C program: determine which directory a file is in - c

What would be a good method to determine which directory a file is in? I would use realpath(), but that returns the absolute path of the file or directory to which a symlink is pointing.
For instance, if the argument is the basename of a file, and lstat() returns 0, I can confirm that the file exists. But for the purposes of the program I'm working on, I need to determine which directory that file is in.
The project is on GH, so I don't mind posting code here if it helps answer the question. Thanks!
UPDATE: Here are some specifics:
The code is near L64. If the file, dir, or symlink is in .local/share/Trash.test/files/, I need to find the corresponding trashinfo file in .local/share/Trash.test/info/.trashinfo. Normally I truncate the return value realpath() at files/, then append info/, then append the basename and .trashinfo ext. and after that, it does what I need. But when I try to get the realpath of the symlink, the absolute path to it is returned, but it's the path to what the symlink points to (e.g. /home/andy/temp/.local/share/Trash.test/files/dnsmasq -> /usr/share/doc/dnsmasq

No, realpath() does not return the absolute path of the file or directory to which a symlink is pointing. realpath() returns the absolute path of an existing file given a relative path, and as an additional benefit, if a symlink is involved in the relative path, then the symlink will be resolved.
A plain file name in the current directory is also a relative path. So, realpath() is the function you need.
Also: why not try it before posting a question?

there is no unambiguous answer to the question of "which directory hold this file". in unix-like filesystems, there can be any number of file names in any number of directories that all refer to the exact same file (hard links).
even realpath only says that it will give you one fully qualified pathname that refers to the file.

This is how I solved what I was trying to do.
This does some boundary checking. Not relevant to the problem (I see I need to add another check or two though).
buf_check (argv[restore_request], PATH_MAX);
argv[restore_request] is the file to be restored. This can be an absolute or relative path, or it can just be a basename, depending on the current working directory.
strcpy (file.relative_path, argv[restore_request]);
file.base_name = basename (argv[restore_request]);
This places a '\0' in the string: str[strlen(str) - strlen (file.base_name)], in effect, chopping off the basename.
truncate_str (file.relative_path, strlen (file.base_name));
I believe the remaining lines are self-explanatory
strcpy (file.info, file.relative_path);
strcat (file.info, "../info/");
strcat (file.info, file.base_name);
strcat (file.info, DOT_TRASHINFO);
Now the path to the info file can be found, which was the desired result.
I apologize that my question was not clear and didn't provide enough details to properly illustrate my goal. I will make it a point to write better questions in the future. Thanks to all who took the time to give me feedback.

Related

An absolute path in fopen()

I am trying to make my c program more dynamic. It should opens a file with fopen(). Apparently, fopen does not read absolute paths. For example It can't read this path:
fopen("/Documents/projects/cs50_radio/broadcast/source/deadinside.mp3", "r")
returns NULL
;however,
fopen("deadinside.mp3", "r");
returns the expected pointer
I was wondering if there is a possible way to read such a path which might be independent from the current working directory in other cases ?
fopen() can take absolute paths as arguments. Are you working on a unix/linux based OS or on windows? Likely what is happening is you've got the path wrong. If you're on a mac, which it looks like you are, the correct path might be
~/Documents/projects/cs50_radio/broadcast/source/deadinside.mp3
But you can verify by cd'ing into the directory and typing pwd
If you're on windows, your path is definitely wrong, as windows would look more like this:
C:\Documents\projects\cs50_radio\broadcast\source\deadinside.mp3

How can I search for a file within a directory and its sub-directories in c?

How can I search for a file within a directory and its sub-directories in C?
I'm not allowed to use find and I must use opendir , readdir and stat.
I want to perform something like the command ls -ln if the file indeed exists.
For traversing the directories, you will need: opendir(3), readdir(3), and closedir(3).
For checking the type of file (to see if it's a directory and if you should recursively search within it) you will need stat(2).
You will want to check
(struct stat).st_mode & S_IFDIR
to see if the file is a directory. See <sys/stat.h> for more information.
If we try to write a small piece of code in C then we can do this search activity easily.
Suppose you need to search abc.txt in a /home/Jack/ then just open a file stream and pass the file path as a parameter.
Now when this statement will be executed, it will try to open the existing file. This API will return non zero if the file exists otherwise it is returned -1 or zero.
You've already provided the basic answer: opendir/readdir/closedir. As you walk the directory entries, you check whether each refers to a file or a directory. Those that refer to directories, you traverse as well (typically recursively). For those that refer to files, you compare their names to the file(s) you're looking for, and see if you've found it.
One other minor detail: you probably also want to check for symbolic links. A symbolic link can (for example) refer to a parent directory, which could/can lead to infinite recursion. You may want to ignore symbolic links completely, or you may want to keep a list of directories you've already at least started to traverse, and resolve/traverse what's in the symbolic link only if it's not already in the list.

What corner cases must we consider when parsing $PATH on Linux?

I'm working on a C application that has to walk $PATH to find full pathnames for binaries, and the only allowed dependency is glibc (i.e. no calling external programs like which). In the normal case, this just entails splitting getenv("PATH") by colons and checking each directory one by one, but I want to be sure I cover all of the possible corner cases. What gotchas should I look out for? In particular, are relative paths, paths starting with ~ meant to be expanded to $HOME, or paths containing the : char allowed?
One thing that once surprised me is that the empty string in PATH means the current directory. Two adjacent colons or a colon at the end or beginning of PATH means the current directory is included. This is documented in man bash for instance.
It also is in the POSIX specification.
So
PATH=:/bin
PATH=/bin:
PATH=/bin::/usr/bin
All mean the current directory is in PATH
I'm not sure this is a problem with Linux in general, but make sure that your code works if PATH has some funky (like, UTF-8) encoding to deal with directories with fancy letters. I suspect this might depend on the filesystem encoding.
I remember working on a bug report of some russian guy who had fancy letters in his user name (and hence, his home directory name which appeared in PATH).
This is minor but I'll added it since it hasn't already been mentioned. $PATH can include both absolute and relative paths. If your crawling the paths list by chdir(2)ing into each directory, you need to keep track of the original working directory (getcwd(3)) and chdir(2) back to it at each iteration of the crawl.
The existing answers cover most of it, but it's worth covering parts of the question that wasn't answered yet:
$ and ~ are not special in the value of $PATH.
If $PATH is not set at all, execvp() will use a default value.

How to get the parent directory of the current folder in a C program?

I am trying to get the parent directory of the current folder in which i have the program.
I need to include in the C program I have. I tried doing it through string methods and solve it, but I feel there can be a better and simpler way. Eg: If his path is “C:\Application\Config”, then I want to get - “C:\Application” the just parent path.
Can some one please help me with this?
Thanks,
Priyanka
To in-place truncate a string at its last backslash:
char pathname[MAX_PATH];
GetCurrentDirectory(MAX_PATH, pathname);
char* last_backslash = strrchr(pathname, '\\');
if (last_backslash)
{
*last_backslash = '\0';
}
Sometimes just adding \.. will suffice if you are not afraid by MAX_PATH.
It's difficult to answer your question since you haven't really specified what you want to -do- with the path once you have it. If you want to change to the new directory, that's easy, you just use whatever function you'd normally use to change directory but pass it ".." instead of a full path - that's because on all sane filesystems, ".." is a 'magic' directory which exists inside all other directories and refers to the parent thereof.
If you want to perform some string function on the new directory before jumping to it, your problem instantly becomes a lot more difficult to solve. The way I'd go about doing it mirrors RichieHindle's solution - strip the current directory away from the full path then you're left with the parent directory's path with which you can muck about to your heart's content.
In Windows OS, the API function you need is called GetCurrentDirectory().
http://msdn.microsoft.com/en-us/library/aa364934%28v=vs.85%29.aspx

What can I do if getcwd() and getenv("PWD") don't match?

I have a build system tool that is using getcwd() to get the current working directory. That's great, except that sometimes people have spaces in their paths, which isn't supported by the build system. You'd think that you could just make a symbolic link:
ln -s "Directory With Spaces" DirectoryWithoutSpaces
And then be happy. But unfortunately for me, getcwd() resolves all the symbolic links. I tried to use getenv("PWD"), but it is not pointing at the same path as I get back from getcwd(). I blame make -C for not updating the environment variable, I think. Right now, getcwd() gives me back a path like this:
/Users/carl/Directory With Spaces/Some/Other/Directories
And getenv("PWD") gives me:
/Users/carl/DirectoryWithoutSpaces
So - is there any function like getcwd() that doesn't resolve the symbolic links?
Edit:
I changed
make -C Some/Other/Directories
to
cd Some/Other/Directories ; make
And then getenv("PWD") works.. If there's no other solution, I can use that.
According to the Advanced Programming in the UNIX Environment bible by Stevens, p.112:
Since the kernel must maintain knowledge of the current working directory, we should be able to fetch its current value. Unfortunately, all the kernel maintains for each process is the i-node number and device identification for the current working directory. The kernel does not maintain the full pathname of the directory.
Sorry, looks like you do need to work around this in another way.
There is no way for getcwd() to determine the path you followed via symbolic links. The basic implementation of getcwd() stats the current directory '.', and then opens the parent directory '..' and scans the entries until it finds the directory name with the same inode number as '.' has. It then repeats the process upwards until it finds the root directory, at which point it has the full path. At no point does it ever traverse a symbolic link. So the goal of having getcwd() calculate the path followed via symlinks is impossible, whether it is implemented as a system call or as a library function.
The best resolution is to ensure that the build system handles path names containing spaces. That means quoting pathnames passed through the shell. C programs don't care about the spaces in the name; it is only when a program like the shell interprets the strings that you run into problems. (Compilers implemented as shell scripts that run pre-processors often have problems with pathnames that contain spaces - speaking from experience.)

Resources