1

I have function to check file existing:

int file_exists(char *filename)
{
    struct stat buffer;
    int i = stat(filename, &buffer);
    if (i == 0) {
        return 1;
    }
    return 0;
}

and it works fine with full path, but I want to check if file exist in user home directory and if not - in /etc directory. How to do it?

Bill Lynch
  • 80,138
  • 16
  • 128
  • 173
Nips
  • 13,162
  • 23
  • 65
  • 103
  • 3
    The last 4 lines of your function body are equivalent to `return !i;`. – undur_gongor Jul 10 '13 at 22:41
  • 1
    @Nips: This function returns non-zero if the given path refers to a directory instead of a regular file. If you want it to return non-zero only if the path refers to a regular file, then you need to check the `st_mode` member of the `struct stat`. For example, `return (stat(filename, &buffer) == 0 && S_ISREG(buffer.st_mode));` – Adam Rosenfield Jul 10 '13 at 23:09

4 Answers4

2
int 
file_exists(const char *basename)
{
    static const char *dirs[] = { "/etc", "/home/username" };
    char buf[MAX_PATH_LENGTH + 1];
    size_t i;
    struct stat dummy;

    for (i = 0; i < sizeof dirs / sizeof dirs[0]; ++i) {
        (void)snprintf(buf, sizeof buf, "%s/%s", dirs[i], basename);
        if (!stat(buf, &dummy)) return 1; 
    }

    return 0;
}

Note that this will fail (i.e. search for the wrong file) if the path plus the file name are longer than MAX_PATH_LENGTH characters. You can check the return value of snprintf to catch that.

If you want to avoid finding directories/sockets/..., you can change the if line to

if (!stat(buf, &dummy) && S_ISREG(dummy.st_mode)) return 1; 

as pointed out by Adam Rosenfield. In that case, you probably want to rename dummy to something more appropriate (maybe sb).

undur_gongor
  • 15,657
  • 5
  • 63
  • 75
0

Use the errno value, it will be ENOENT if the file doesn't exist.

#include <errno.h>
int file_exists(char *filename)
{
    struct stat buffer;
    int i = stat(filename, &buffer);
    if (i == 0) {
       return 1;
    }
    if (errno == ENOENT)
       printf("%s : file not found\n", buffer);
    return 0;
}

For your problem: use chdir if you don't want to have to concatenate the directory with your filename.

undur_gongor
  • 15,657
  • 5
  • 63
  • 75
nouney
  • 4,363
  • 19
  • 31
0

Only take "filename" as input, then use strcat to create the full path name - something like below:

int file_exists(char *filename)
{
    struct stat buffer;
    char fullpath[50], *path;
    path = "/home/Nips/"
    if ((strlen(path) + strlen(filename)) >= 50) {
        printf("filename too long!\n");
        return 0;
    }
    strcpy(fullpath, path);
    strcat(fullpath, filename);
    if (stat(fullpath, &buffer) == 0) {
        return 1;
    }
    if (errno == ENOENT) {
        path = "/etc/";
        strcpy(fullpath, path);
        strcat(fullpath, filename);
        if (stat(fullpath, &buffer) == 0) {
            return 1;
        }
        if (errno == ENOENT) {
            printf("File not found!\n");
            return 0;
        }
    }
    printf("Error finding File!\n");
    return 0;
}

Note: Code isn't tested, this just to give you a hint. Do proper sanity checking while you implement. Hope this will give you some idea!

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
rakib_
  • 136,911
  • 4
  • 20
  • 26
  • Ok, I've updated my answer with overflow checking for filename, now it's safe. And since we know the size in advance, so it's no that particularly required to use strncat(). Thanks! – rakib_ Jul 10 '13 at 22:52
  • 2
    Using `strcat()` to initialize `fullpath` is wrong. `fullpath` is not initialized at all before `strcat()` is called the first time, so the behavior is undefined. Use `strcpy()` instead (or better, `strncpy()`) when initializing `fullpath` with a path, then use `strcat()` (or `strncat()`) only to append the `filename` to `fullpath`. – Remy Lebeau Jul 10 '13 at 22:57
  • Ok, I've take you suggestion of making fullpath 0 and then using strcat(), but, I'm not at all agree with you that strcat() is unsafe here. We *know* the size in advance, we know what we're doing. Since, everyone wants strncat(), so I've made the change. – rakib_ Jul 10 '13 at 23:08
  • 1
    Your original use of `strcat()` was unsafe. Your use of `strncat()` is now equally unsafe, because you are passing the wrong value in the last parameter of `strncat()`. It expects the max length of the **destination** buffer (`fullpath`) so it knows the bounds it should not exceed, but you are passing it the length of the **source** buffer instead (`path`, `filename`, `etc` - and worse, in the last `strncat()` call, you are not even using a length from the correct source buffer). Don't say you know what you are doing, because you clearly **DO NOT** know what you are doing. – Remy Lebeau Jul 11 '13 at 00:38
  • And then to top it off, you are using all this effort to create a value for `fullpath`, and then you are not even passing `fullpath` to `stat()` at all, you are passing the original `filename` instead! I have made corrections to your code. – Remy Lebeau Jul 11 '13 at 00:51
  • Thanks for improving the answer. Regarding, the wrong value in last parameter in strncat(), haven't you noticed that there was a check to make sure fullpath can't be over size 50. And, that's what I meant by I *KNOW* what I'm doing. – rakib_ Jul 11 '13 at 04:18
  • Regarding, passing fullpath instead of filename is, it was copy pasted from questioner code snippet, I also made a note on my answer. Well, the whole point of this question was - "how to check if file exist in specific directories?", So, I tried to solve to this particular problem, didn't emphasize on things like whether user should use strcat() or strncat(), I hope user knows it well and I hope, you understand my point. Thanks! – rakib_ Jul 11 '13 at 04:19
0

Always provide full address to your path, or ./ on the current directory.

Also, according to this similar question, the access(pathname, mode) function does what you need. Use it instead. It returns 0 on success, or -1 in case of failure. Since access() does exactly what you need, you don't have to re-implement it.

Note, the most common modes are the following:

F_OK, for checking if the file exists.

R_OK, for reading.

W_OK, for writing.

X_OK, for reading, writing and executing permissions.

Also, if for example you need to check both reading and writing permission, you can have a bitwise or between R_OK and W_OK (or even between each three R_OK, W_OK or X_OK)

For further information, type man access on your terminal, or visit the link above.

Beware: access() is part of unistd.h library, that is POSIX standard. If you need its use under other systems you may need to include different libraries.

Community
  • 1
  • 1
Acsor
  • 1,011
  • 2
  • 13
  • 26