2

I've been trying to learn how can I use functions to interact with the paths on the system, but I guess I stucked at the very beginning.

I've searched web and stackoverflow especially and couldn't find a basic implementation like I am trying to do. There are some other questions, which is similar to mine, but I don't find them simple and beginner friendly like mine.

Here is the code, this code just prints the files in given path, and "." and ".."

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>

void listdir(DIR *c);

int main()
{

    DIR* root_d;
    root_d = opendir("/home/foo/Desktop/testx");
    listdir(root_d);

}

void listdir(DIR *c){
    DIR* current = c;
    struct dirent *curr_ent;
    if((curr_ent =readdir(current)) != NULL && curr_ent->d_type == DT_DIR){
        listdir(current);
    }
    printf("%s\n", curr_ent->d_name);
    return;
}

What am I doing wrong?

  • `current` gets assigned `c` but then you pass it to the next `listdir()` invocation. You probably need to take `curr_ent` and `current` combined when you recurse. – Brian Cain Apr 01 '15 at 22:07
  • Please do not reinvent the wheel badly. Use [`nftw()`](http://stackoverflow.com/a/29402705/1475978) instead. It was *designed* to do exactly this -- to walk file trees. – Nominal Animal Apr 01 '15 at 23:39

2 Answers2

3

It's working exactly as it should, you just forgot to exempt . and ... Those two "virtual" directories exist in ALL directories. . is a shortcut for "self", and .. is a shortcut for parent.

e.g. if you did listdir('.'), then you'd just keep looping, because the first thing you'd get upon doing opendir in . is ANOTHER ., which points to the exact same place.

In short, you'd need the following pseudo-code:

if (dir != '.' && dir != '..' && is_dir(dir)) {
    listdir(dir);
}
Marc B
  • 356,200
  • 43
  • 426
  • 500
1

What am I doing wrong?

  1. You haven't added code to ignore . and ...
  2. You are using the same DIR* in the recursive calls.

Here's how I would change your function.

int is_dot_or_dot_dot(char const* name)
{
   return (strcmp(name, ".") == 0 || strcmp(name, "..") == 0 );
}

void listdir(char const* dirname)
{
   char* subdir;
   DIR* dirp = opendir(dirname);
   struct dirent *curr_ent;

   if ( dirp == NULL )
   {
      return;
   }

   while ( (curr_ent = readdir(dirp)) != NULL )
   { 
      // Print the name.
      printf("%s\n", curr_ent->d_name);

      // Traverse sub-directories excluding . and ..
      // Ignore . and ..
      if ( curr_ent->d_type == DT_DIR && ! (is_dot_or_dot_dot(curr_ent->d_name)) )
      {
         // Allocate memory for the subdirectory.
         // 1 additional for the '/' and the second additional for '\0'.
         subdir = malloc(strlen(dirname) + strlen(curr_ent->d_name) + 2);

         // Flesh out the subdirectory name.
         strcpy(subdir, dirname);
         strcat(subdir, "/");
         strcat(subdir, curr_ent->d_name);

         // List the contents of the subdirectory.
         listdir(subdir);

         // Free the allocated memory.
         free(subdir);
      }
   }

   // Close the directory
   closedir(dirp);
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • I didn't understand 2nd one. I actually did, but is there way to use "new" one? Or are there actually a new one ? –  Apr 01 '15 at 22:16
  • 1
    For every sub-directory to traverse, you have use `opendir()` and `closedir()`. – R Sahu Apr 01 '15 at 22:18