dirDepth.c

/**
   @file      dirDepth.c
   @author    Mitch Richling <http://www.mitchr.me/>
   @Copyright Copyright 1995 by Mitch Richling.  All rights reserved.
   @brief     Depth first traversal of UNIX directory trees@EOL
   @Keywords  UNIX readdir depth first directory traversal
   @Std       ISOC POSIX UNIX98 BSD4.3 SYSV3

              This C program is intended to illustrate how to
              traverse a directory tree depth first.  That is to say,
              that all files at a particular level of the tree are
              stated before the program descends.  It simply prints
              out the names of all files found.
              
              This program reads all the file names in a directory and
              places any subdirectory names into a linked list to be
              traversed later.  This program has rather elegant
              implementations using ANSI C++ and the STL instead of
              the linked lists used here (dirDepth.cc).
              
              chdir'ing instead of constructing strings might be
              faster on some systems.  On many systems a memcpy
              function will operate faster than a strcpy/strcat.  The
              lengths of the strings are computed anyway, so a memcpy
              is the way to go.
              
              Note that this implementation doesn't suffer from
              any limitations on the number of open directory
              descriptors -- this implementation only has one open
              directory descriptor at any time.
              
              The functions used are all POSIX or ISO C++ and thus
              this program will run on most BSD and SRV
              implementations of UNIX.

   @Tested    
              - Solaris 2.8
              - MacOS X.2
              - Linux (RH 7.3)
*/

#include <stdio.h>              /* I/O lib         ISOC  */
#include <string.h>             /* Strings         ISOC  */
#include <sys/types.h>          /* UNIX types      POSIX */
#include <dirent.h>             /* UNIX dirs       POSIX */
#include <errno.h>              /* error stf       POSIX */
#include <utime.h>              /* utime           POSIX */
#include <sys/stat.h>           /* UNIX stat       POSIX */
#include <time.h>               /* time            ISOC  */
#include <stdlib.h>             /* Standard Lib    ISOC  */

/* This struct is used to hold directory entries that we must traverse
   later.  We must store them someplace, or else we would have to
   repeatedly rescan the current directory looking for the next
   directory.  Note the nextNode member.  This is to facilitate the
   construction of a linked list. */
struct myStatStruct {
  char *d_name;
  struct myStatStruct *nextNode;
};

int main(int argc, char *argv[]);

int main(int argc, char *argv[]) {
  DIR *dp;                               /** The directory pointer. */
  struct dirent *dep;                    /** The directory entry. */
  struct stat s;                         /** Used to stat each file. */
  char *name;                            /** Holds the complete path name of the current file. */
  struct myStatStruct *lastDir;          /** Points to the last node in the linked list. */
  struct myStatStruct *workingDir;       /** Points to the current working directory. */
  struct myStatStruct *tmpDir;           /** Used to hold the current workingDir just before deallocation. */
  long len_d_name, len_workingDirName;   /** Integers used to hold the lengths of file names. */

  /* Stat the root of the tree we are to traverse. */
  if((argc == 2) && (lstat(argv[1], &s) < 0)) {
    printf("ERROR: Bad first argument\n");
    exit(1);
  } /* end if */
  /* Now that we have stat'ed the thing, we add it as the first node
     of our linked list of directories thus initializing our linked
     list. */
  workingDir = (struct myStatStruct *)malloc(sizeof(struct myStatStruct));
  workingDir->d_name = (char *)malloc(strlen(argv[1]) + 1);
  strcpy(workingDir->d_name, argv[1]);
  workingDir->nextNode = NULL;
  lastDir = workingDir;

  while(workingDir != NULL) {
    if((dp = opendir(workingDir->d_name)) == NULL) {
      printf("ERROR: dirDepth: %s: %s\n", workingDir->d_name, strerror(errno));
    } else {
      /* We have opened the directory, now we read each file name from
         it and store the file names that correspond to sub
         directories in our linked list for traversal later. */
      len_workingDirName = strlen(workingDir->d_name);
      while((dep = readdir(dp)) != NULL) {
        /* Make sure we don't process .. and . */
        if((strcmp(dep->d_name, "..") != 0) && (strcmp(dep->d_name, ".") != 0)) {
          /* Allocate space for and construct the complete path name. */
          len_d_name = strlen(dep->d_name);
          name = (char *)malloc(len_d_name + len_workingDirName + 2);
          strcpy(name, workingDir->d_name);
          strcat(name, "/");
          strcat(name, dep->d_name);
          /* Now that we have a complete path name, let's stat the file in question. */
          if(lstat(name, &s) < 0) {
            printf("ERROR: dirDepth: %s: %s\n", name, strerror(errno));
            free(name);
          } else {
            /* We have successfully stat'ed the file, now let's process it: */
            if(S_ISDIR(s.st_mode)) {    /* Process directories. */
              /* This is a directory, so we must add it to our linked list of directories to traverse. */
              if((lastDir->nextNode = (struct myStatStruct *)malloc(sizeof(struct myStatStruct))) == NULL) {
                printf("ERROR: dirDepth: malloc: %s\n", strerror(errno));
                return 1;
              } /* end if */
              lastDir->nextNode->d_name = name;
              lastDir->nextNode->nextNode = NULL;
              lastDir = lastDir->nextNode;
              printf(" D: %s\n", name);
            } else {            /* Process non-directories. */
              printf("ND: %s\n", name);
              /* Free up the space for the pathname. */
              free(name);
            } /* end if/else */
          } /* end if/else */
        } /* end if */
      } /* end while */
      closedir(dp);
    } /* end if/else */
    /* We have finished walking the current directory, so we remove it
       from the linked list and update the workingDir pointer so that
       it points to the next directory in the list. */
    tmpDir = workingDir;
    workingDir = workingDir->nextNode;
    free(tmpDir->d_name);
    free(tmpDir);
  } /* end while */

  return 0;
} /* end func main */

Generated by GNU Enscript 1.6.5.2.