Thread: Recursive DIR problems

  1. #1
    Registered User
    Join Date
    Mar 2010
    Posts
    90

    Recursive DIR problems

    Hello,
    I do recursive dir scanning for .c files in tree on linux but that one don't work on windows because of that:
    Code:
    if (entry->d_type & DT_DIR)
    So I have to change code and now I have this:
    Code:
    static void
    list_dir(const char *dir_name)
    {
        DIR *d;
        d = opendir(dir_name);
    
        while (1)
        {
            struct dirent *entry;
            struct stat statbuf;
            const char *d_name;
    
            entry = readdir(d);
            d_name = entry->d_name;
            stat(d_name, &statbuf);
    
            if(S_ISDIR(statbuf.st_mode) != 0)
            {
                if (strcmp (d_name, "..") != 0 && strcmp (d_name, ".") != 0)
                {
                    int path_length;
                    char path[FILENAME_MAX];
                    path_length = snprintf (path, FILENAME_MAX, "%s/%s", dir_name, d_name);
                    if (path_length >= FILENAME_MAX) exit(EXIT_FAILURE);
                    list_dir (path);
                }
            }
    	
    	if (S_ISREG(statbuf.st_mode) != 0)
            {
                char fullname[FILENAME_MAX] = {0};
                sprintf(fullname, "%s/%s", dir_name, d_name);
    
                if (strlen(fullname) >= 2)
                {
                    if (strcmp (".c", & (fullname[strlen(fullname) - 2])) == 0)
                    {
    		// it is c file
                    }
                }
            }
        }
        if (closedir(d)) exit(EXIT_FAILURE);
    }
    That code crashes after several loops when come to first file...
    Anybody see why and how to fix it?

  2. #2
    Registered User
    Join Date
    Dec 2007
    Posts
    2,675
    Code:
    entry = readdir(d);
    d_name = entry->d_name;
    Where is your check to be sure entry is actually a valid pointer? You know, that readdir succeeded?

    readdir(3): read directory - Linux man page

    The only fields in the dirent structure that are mandated by POSIX.1 are: d_name[], of unspecified size, with at most NAME_MAX characters preceding the terminating null byte; and (as an XSI extension) d_ino. The other fields are unstandardized, and not present on all systems; see NOTES below for some further details.
    Same thing with the call to opendir and stat. You just can't assume every system call succeeds.

  3. #3
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    Quote Originally Posted by nime View Post
    I do recursive dir scanning for .c files ... windows
    Although it's not portable, most "windows" guys will recommend you use FindFirstFile() ... FindNextFile() ... . Link to MSDN article:

    Listing the Files in a Directory (Windows)

    This can get more complicated if you need to access directories with security attributes. You'll need functions like OpenProcessToken(), GetTokenInformation(), LookupPrivilegeName(), AdjustTokenPrivileges(), ... Link for AdjustTokenPrivileges(), you can find the others by searching msdn and the function name.

    AdjustTokenPrivileges function (Windows)

  4. #4
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    POSIX C libraries provide nftw(), which makes directory tree traversals much easier.

    However, BSD C libraries (since 4.4BSD, about 1994 or thereabouts) and GNU C libraries (since 1999) have long provided fts() to help programmers implement exactly this kind of code efficiently. You need sixty lines, including comments, to write a full program that searches for and lists all regular files ending in .c in all directories specified on the command line:
    Code:
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fts.h>
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    
    int main(int argc, char *argv[])
    {
        FTS    *tree;
        FTSENT *item;
    
        if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
            fprintf(stderr, "\n");
            fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
            fprintf(stderr, "       %s FILES-OR-DIRECTORIES...\n", argv[0]);
            fprintf(stderr, "\n");
            return EXIT_FAILURE;
        }
    
        tree = fts_open(argv, FTS_LOGICAL | FTS_COMFOLLOW, NULL);
        if (!tree) {
            fprintf(stderr, "%s.\n", strerror(errno));
            return EXIT_FAILURE;
        }
    
        while (1) {
    
            errno = 0;
            item = fts_read(tree);
            if (!item)
                break;
    
            /* Only consider regular files. */
            if (item->fts_info != FTS_F)
                continue;
    
            /* Require .c suffix. */
            if (item->fts_namelen < 3 ||
                item->fts_name[item->fts_namelen - 2] != '.' ||
                item->fts_name[item->fts_namelen - 1] != 'c')
                continue;
    
            /* Print the access path, and file size. */
            printf("%s (%.0f bytes)\n", item->fts_accpath, (double)item->fts_statp->st_size);
    
        }
        if (errno) {
            fprintf(stderr, "%s.\n", strerror(errno));
            return EXIT_FAILURE;
        }
        
        if (fts_close(tree)) {
            fprintf(stderr, "%s.\n", strerror(errno));
            return EXIT_FAILURE;
        }
    
        return EXIT_SUCCESS;
    }
    You might find the Walk a directory/Recursively, C at Rosetta Code interesting.

  5. #5
    Registered User
    Join Date
    Mar 2010
    Posts
    90
    Thank's for replies but since noone point to right direction, I have to solve a problem by myself.

    This is very simple but efficient and potrable code for recursive directory scanning for c files which will work in most "normal" cases...

    Code:
    static void list_dir(char *dir_name)
    {
        DIR *dp;
        struct dirent *ep;
        char abs_filename[FILENAME_MAX];
        dp = opendir (dir_name);
        if (dp != NULL)
        {
            while ((ep = readdir(dp)))
            {
                struct stat stFileInfo;
                snprintf(abs_filename, FILENAME_MAX, "%s/%s", dir_name, ep->d_name);
                if (stat(abs_filename, &stFileInfo) < 0) perror(abs_filename);
    
                if(S_ISDIR(stFileInfo.st_mode))
                {
                    if(strcmp(ep->d_name, ".") && strcmp(ep->d_name, ".."))
                    {
                        //printf("%s directory\n", abs_filename);
                        list_dir(abs_filename);
                    }
                }
                else if(S_ISREG(stFileInfo.st_mode))
                {
                    int len = strlen(abs_filename);
                    if (len >= 2)
                    {
                        if (strcmp (".c", & (abs_filename[len - 2])) == 0)
                        {
                           //printf("c file %s\n", abs_filename);
                        }
                    }
                }
            }
        }
        else
            perror("Couldn't open the directory");
    
        if (dp != NULL) closedir(dp);
    }
    Last edited by nime; 05-27-2013 at 03:15 AM.

  6. #6
    Registered User
    Join Date
    Mar 2010
    Posts
    90
    However, it would be interesting to know under...
    Code:
               if(S_ISDIR(stFileInfo.st_mode))
                {...
    how deep we are from initial directory, like 1, 2 or more subdirectory level.

    For example if we start to search at /media/progs/ then /media/progs/dir would give 1, /media/progs/dir/portable would give 2, /media/progs/strings/mid also 2, /media/progs/guis/gtk/checkbox 3 etc...

    Any idea how to achieve that?

  7. #7
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Provide a second argument of type int to your function. The first call of your function needs to supply the value zero. Every call within your function does so with the value it received plus one.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  8. #8
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    Quote Originally Posted by nime View Post
    However, it would be interesting to know under... how deep we are from initial directory.
    You could use a static variable in list_dir(), and increment it each time you recursively call list_dir. Are you using gcc for windows? I'm wondering how you get opendir() and readdir() to work with windows (visual c / c++ doesn't have those functions, but there may be a .c file or library to emulate them).

    I've done a similar program, but I have two loops in list_dir(), the first loop would display all instances of ".c" files in the current directory, the second loop would search for sub-directories and call list_dir() for each directory encountered. This would group all files in the same directory together instead of being separated by any sub-directories that occur between files.

  9. #9
    Registered User
    Join Date
    Mar 2010
    Posts
    90
    Quote Originally Posted by rcgldr View Post
    Are you using gcc for windows? I'm wondering how you get opendir() and readdir() to work with windows (visual c / c++ doesn't have those functions, but there may be a .c file or library to emulate them).
    Hi rcgldr,
    Yes, I'm using gcc. That way programs are highly portable to linux if I don't use win api, winforms and so...

    What I don't understand is that some folders have one subdir and some may have up to four, so how can I count "position" from initial dir by simply increasing integer when I don't know where loop currently is?
    Probably that can be done by string manipulation, comparision or reading slashes but I search for a better or more elegant solution.

  10. #10
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    Quote Originally Posted by nime View Post
    What I don't understand is that some folders have one subdir and some may have up to four, so how can I count "position" from initial dir by simply increasing integer when I don't know where loop currently is?
    Sorry, I didn't fully explain it. Add a static variable to list_dir, for example dir_level. Initialize it to zero (actually just declaring it as static will initialize it to zero). Increment dir_level before each recursive call to list_dir() and decrement it just after each recursive call to list_dir():

    Code:
    static void list_dir(char *dir_name)
    {
    static int dir_level;
    /* ... */
                        dir_level++;
                        list_dir(abs_filename);
                        dir_level--;
    /* ... */
    }

  11. #11
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by nime View Post
    Probably that can be done by string manipulation, comparision or reading slashes but I search for a better or more elegant solution.
    Clearly you have dismissed my previous suggestion. You can go for static variables if you really want, but that is generally considered very bad style in C.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  12. #12
    Registered User
    Join Date
    Mar 2010
    Posts
    90
    @rcgldr
    What a nice idea. It works wery well, thank you!

  13. #13
    Registered User
    Join Date
    Mar 2010
    Posts
    90
    Sorry grumpy, I was not understand what you suggest.
    Why is in this case using of static int inside this function bad?

  14. #14
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    Quote Originally Posted by grumpy View Post
    You can go for static variables if you really want, but that is generally considered very bad style in C.
    If a static variable is an issue, you can pass dir_level as a parameter:

    Code:
    static void list_dir(char *dir_name, int dir_level)
    {
    /* ... */
                        list_dir(abs_filename, dir_level+1);
    /* ... */
    }
    
    int main(...)
    {
    /* ... */
                        list_dir(dirname, 0);
    /* ... */
    }
    I normally use static variables to avoid filling up the stack, but in this case, one more integer on the stack for each nested call to list_dir() isn't much overhead.
    Last edited by rcgldr; 05-27-2013 at 08:21 AM.

  15. #15
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by nime View Post
    Sorry grumpy, I was not understand what you suggest.
    rcgldr has given you the answer to that in his/her last post. I believe you would have learned more by reasoning it out.
    Quote Originally Posted by nime View Post
    Why is in this case using of static int inside this function bad?
    The short answer is that it limits scalability and maintainability of your function - and of programs that call it - in the long run. As an example, the approach will not work in multi-threaded programs, if two threads attempt to use your function at once.

    If you look around (on various forum sites, or using google) you will find heaps of information about the disadvantages of static variables. (There are also some advantages but, generally, they are considered to be outweighed by the disadvantages.)


    Quote Originally Posted by rcgldr View Post
    I normally use static variables to avoid filling up the stack, but in this case, one more integer on the stack for each nested call to list_dir() isn't much overhead.
    That would be one of the weakest justifications I've ever seen for using a static. In case like this one - a recursive function - a single integer per recursive call is usually significantly less than than the function call overhead, per call. In this case, that recursive function creates an array of char on the stack as well.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Problems with recursive structure
    By heinz55 in forum C Programming
    Replies: 11
    Last Post: 09-29-2012, 08:42 AM
  2. Converting recursive function to tail recursive
    By ajacobs365 in forum C Programming
    Replies: 1
    Last Post: 10-30-2011, 08:15 AM
  3. merge sort: recursive is fasfter than non-recursive
    By rio_cat in forum C Programming
    Replies: 8
    Last Post: 12-04-2006, 12:52 AM
  4. recursive function problems
    By jomns in forum C++ Programming
    Replies: 6
    Last Post: 01-16-2004, 11:04 AM
  5. Recursive Solution to Any Maze And Stack Overflow Problems
    By PunkyBunny300 in forum C Programming
    Replies: 14
    Last Post: 12-14-2002, 07:00 PM

Tags for this Thread