Writing a recursive ls in C

Recursion can become very useful for writing system utilities, especially in systems programming languages like C. In the following example, we will create a C program which recursively prints out files from a given directory. It uses a system library called “direct.h” that contains constructs that facilitate directory traversal.

#include <stdio.h>
#include <dirent.h>
#include <string.h>
#define MAX_PATH 1024

void lsrec(char *dir)
{
   char path[MAX_PATH];
   struct dirent *dp;
   DIR *dfd;

   if ((dfd = opendir(dir)) == NULL) {
      fprintf(stderr, "lsrec: can't open %s\n", dir);
      return;
   }

   while ((dp = readdir(dfd)) != NULL) {
      if (dp->d_type == DT_DIR){
         path[0] = '\0';
         if (strcmp(dp->d_name, ".") == 0 || 
             strcmp(dp->d_name, "..") == 0)
            continue;
         sprintf(path, "%s/%s", dir, dp->d_name);
         lsrec(path);
      }
      else
         printf("%s/%s\n", dir, dp->d_name);
      }
   closedir(dfd);
}

int main(int argc, char *argv[])
{
   if (argc == 1)
      lsrec(".");
   else
      lsrec(argv[1]);
   return 0;
}

The function opendir() opens the directory stream, and returns a DIR pointer, similar to the FILE pointer returned by fopen(). Testing if it is NULL determines if the directory exists or not (remember to closedir() when finished). To make the program useful, we have to use the function readdir() to read the directory. It returns a pointer to a structure (dp) representing the directory entry at the current position in the directory stream, specified by dfd. This function is called repeatedly until NULL is returned, indicating no further entries are available in the directory.

The structure dirent is often customized depending on the file system. One key member worthy of access is d_name, which represent the directory entry’s filename. Another one, and one used in this program is d_type, which signifies the type of the entry.  In this case, dp->d_type is compared against DT_DIR to determine if it is a directory. To compare against a file, one would use DT_REG. On OSX, DT_DIR=4, and DT_REG=8. The values returned, aren’t always the same on every OS.

If dp is a directory, then a check is made to see if it is “.” (current) or “..” (parent) directories. If so, they are bypassed. A new path is created, and then lsrec() is called recursively, moving down the directory path. If dp is a normal file, it will print it out, including its path.

 

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.