FAQ > Accessing a directory and all the files within it


Match word(s).

If you have any questions or comments,
please visit us on the Forums.

FAQ > How do I... (Level 3) > Accessing a directory and all the files within it

This item was added on: 2003/02/27

To access a directory, and the files within it, is a very compiler/OS specific task. It's not practicable to show how to do this for all variations, so here are some examples for various compilers.

The basic principle is the same for each, start by attaching to the base directory, and then the first file within it. Then repeatedly ask for the "next" file, if it's a directory, move into it, and start over. Recursion is used to achieve this, and this code provides a good example of that technique which is nice to follow through.

This first example, adapted from a post by Salem, if I remember correctly, works on Borland 5.5 command line compiler (and possibly other versions). It simply performs a recursive search of all directories on the current drive, looking for files matching the name provided by the user. The filename comparison function uses strcmp(), which is case sensitive, so watch out for that if you use this code.

Instead of printing each matching file, the walker() function could be adapted to take a function pointer as an argument, and then call that function for each matching file. This would make the code more versatile, but naturally, it's up to you what you do with it.


#include <stdio.h> 
#include <dir.h> 
#include <string.h> 
#define ALL_ATTS  (FA_DIREC | FA_ARCH) 

void walker(const char *, const char *);

void walker(const char *path, const char *findme)
{
  struct ffblk  finder;
  unsigned int  res;

  chdir(path);

  for (res = findfirst("*.*", &finder, ALL_ATTS); res == 0; res = findnext(&finder))
  {
    if (strcmp(finder.ff_name, ".") == 0) continue;   /* current dir */
    if (strcmp(finder.ff_name, "..") == 0) continue;  /* parent dir  */

    /* 
     * If its a directory, examine it
     * else compare the filename with the one we're looking for
     */
    if (finder.ff_attrib & FA_DIREC)
    {
      char newpath[MAXPATH];
      strcpy(newpath, path);
      strcat(newpath, "\\");
      strcat(newpath, finder.ff_name);
      chdir(finder.ff_name);
      walker(newpath, findme);
      chdir("..");
    }
    else
    {
      if (strcmp(finder.ff_name, findme) == 0)
      {
        printf("Found in: %s\n", path);
      }
    }
  }
}

int main(void)
{
  const char *root = "\\";
  char buf[BUFSIZ];
  
  printf ("This program will find a file on the current drive.\n"
          "Enter the name of the file to look for: ");

  fflush(stdout);  

  if (fgets(buf, sizeof(buf), stdin))
  {
    strtok(buf, "\n");  /* Remove the newline character */
    walker(root, buf);
  }
  
  return(0);
}

/*
 * Program output:
 This program will find a file on the current drive.
 Enter the name of the file to look for: MyFile.txt
 Found in: \\Temp\MySubDir
 *
 */


This next example supplied by vVv was written for the *nix FAQ, so it should be suitable for POSIX compliant compilers/systems. It also compiles on Borland 5.5


/*
 * This program displays the names of all files in the current directory.
 */

#include <dirent.h> 
#include <stdio.h> 

int main(void)
{
  DIR           *d;
  struct dirent *dir;
  d = opendir(".");
  if (d)
  {
    while ((dir = readdir(d)) != NULL)
    {
      printf("%s\n", dir->d_name);
    }

    closedir(d);
  }

  return(0);
}


The following is an expanded example for Unix systems.

#include <dirent.h> 
#include <sys/types.h> 
#include <sys/param.h> 
#include <sys/stat.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <string.h> 

int walker( char *searching, char *result ) {
  DIR           *d;
  struct dirent *dir;
  d = opendir( "." );
  if( d == NULL ) {
    return 1;
  }
  while( ( dir = readdir( d ) ) ) {
    if( strcmp( dir->d_name, "." ) == 0 || 
        strcmp( dir->d_name, ".." ) == 0 ) {
      continue;
    }
    if( dir->d_type == DT_DIR ) {
      chdir( dir->d_name );
      walker( searching, result );
      chdir( ".." );
    } else {
      if( strcmp( dir->d_name, searching ) == 0 ) {
        int  len;
        getcwd( result, MAXPATHLEN );
        len = strlen( result );
        snprintf( result + len, MAXPATHLEN - len, "/%s", dir->d_name );
        break;
      }
    }
  }
  closedir( d );
  return *result == 0;
}

int main( ) {
  char buf[MAXPATHLEN] = { 0 };
  if( walker( "lame", buf ) == 0 ) {
    printf( "Found: %s\n", buf );
  } else {
    puts( "Not found" );
  }
  return 0;
}

And now a Win32 example


// xtree.cpp : Defines the entry point for the console application.
//
#include <stdio.h> 
#include <stdlib.h> 
#include <windows.h> 

void errormessage(void)
{
  LPVOID  lpMsgBuf;
  FormatMessage
  (
    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL,
    GetLastError(),
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),  // Default language
    (LPTSTR) & lpMsgBuf,
    0,
    NULL
  );
  fprintf(stderr, "%s\n", (char*)lpMsgBuf);
  LocalFree(lpMsgBuf);
}

void format_time(FILETIME *t, char *buff)
{
  FILETIME    localtime;
  SYSTEMTIME  sysloc_time;
  FileTimeToLocalFileTime(t, &localtime);
  FileTimeToSystemTime(&localtime, &sysloc_time);
  sprintf
  (
    buff,
    "%4d/%02d/%02d %02d:%02d:%02d",
    sysloc_time.wYear,
    sysloc_time.wMonth,
    sysloc_time.wDay,
    sysloc_time.wHour,
    sysloc_time.wMinute,
    sysloc_time.wSecond
  );
}

void format_attr(DWORD attr, char *buff)
{
  *buff++ = attr & FILE_ATTRIBUTE_ARCHIVE ? 'A' : '-';
  *buff++ = attr & FILE_ATTRIBUTE_SYSTEM ? 'S' : '-';
  *buff++ = attr & FILE_ATTRIBUTE_HIDDEN ? 'H' : '-';
  *buff++ = attr & FILE_ATTRIBUTE_READONLY ? 'R' : '-';
  *buff++ = attr & FILE_ATTRIBUTE_DIRECTORY ? 'D' : '-';
  *buff++ = attr & FILE_ATTRIBUTE_ENCRYPTED ? 'E' : '-';
  *buff++ = attr & FILE_ATTRIBUTE_COMPRESSED ? 'C' : '-';
  *buff = '\0';
}

struct flist
{
  int             num_entries;
  int             max_entries;
  WIN32_FIND_DATA *files;
};
void addfile(flist *list, WIN32_FIND_DATA data)
{
  if (list->num_entries == list->max_entries)
  {
    int             newsize = list->max_entries == 0 ? 16 : list->max_entries * 2;
    WIN32_FIND_DATA *temp = (WIN32_FIND_DATA *) realloc(list->files, newsize * sizeof(WIN32_FIND_DATA));
    if (temp == NULL)
    {
      fprintf(stderr, "Out of memory\n");
      exit(1);
    }
    else
    {
      list->max_entries = newsize;
      list->files = temp;
    }
  }

  list->files[list->num_entries++] = data;
}

int sortfiles(const void *a, const void *b)
{
  const WIN32_FIND_DATA *pa = (WIN32_FIND_DATA *) a;
  const WIN32_FIND_DATA *pb = (WIN32_FIND_DATA *) b;
  int                   a_is_dir = pa->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
  int                   b_is_dir = pb->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
  if (a_is_dir ^ b_is_dir)
  {
    // one of each, prefer the directories first
    if (a_is_dir) return(-1);
    if (b_is_dir) return(+1);
    return(0);
  }
  else
  {
    // both files, or both directories - return the strcmp result
    return(strcmp(pa->cFileName, pb->cFileName));
  }
}

void doit(char *root)
{
  flist           list = { 0, 0, NULL };
  HANDLE          h;
  WIN32_FIND_DATA info;
  int             i;

  // build a list of files
  h = FindFirstFile("*.*", &info);
  if (h != INVALID_HANDLE_VALUE)
  {
    do
    {
      if (!(strcmp(info.cFileName, ".") == 0 || strcmp(info.cFileName, "..") == 0))
      {
        addfile(&list, info);
      }
    } while (FindNextFile(h, &info));
    if (GetLastError() != ERROR_NO_MORE_FILES) errormessage();
    FindClose(h);
  }
  else
  {
    errormessage();
  }

  // sort them
  qsort(list.files, list.num_entries, sizeof(list.files[0]), sortfiles);

  // print out in sorted order
  int numdirs = 0;
  for (i = 0; i < list.num_entries; i++)
  {
    char  t1[50], t2[50], t3[50], a[10];
    format_time(&list.files[i].ftCreationTime, t1);
    format_time(&list.files[i].ftLastAccessTime, t2);
    format_time(&list.files[i].ftLastWriteTime, t3);
    format_attr(list.files[i].dwFileAttributes, a);
    if (list.files[i].dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    {
      // 'null' date for directory access times, which change each time
      // we run this tool
      sprintf(t2, "%4d/%02d/%02d %02d:%02d:%02d", 2000, 1, 1, 0, 0, 0);
    }

    printf("%s %10ld %s %s %s %s\\%s\n", a, list.files[i].nFileSizeLow, t1, t2, t3, root, list.files[i].cFileName);
    if (list.files[i].dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) numdirs++;
  }

  // now process all the sub-dirs
  // free all the files first, to save a bit of space
  // the sort function will have put them all at the end.
  list.files = (WIN32_FIND_DATA *) realloc(list.files, numdirs * sizeof(WIN32_FIND_DATA));
  for (i = 0; i < numdirs; i++)
  {
    char  newroot[MAX_PATH];
    sprintf(newroot, "%s\\%s", root, list.files[i].cFileName);
    SetCurrentDirectory(list.files[i].cFileName);
    doit(newroot);
    SetCurrentDirectory("..");
  }

  // free the remainder
  free(list.files);
}

void banner(void)
{
  //       12345678901234567890
  printf("Attribs ");
  printf("      Size ");
  printf("       CreationTime ");
  printf("     LastAccessTime ");
  printf("      LastWriteTime ");
  printf("Filename\n");
}

int main(int argc, char *argv[])
{
  if (argc > 1)
  {
    char  olddir[MAX_PATH];
    if (GetCurrentDirectory(MAX_PATH, olddir) == 0)
    {
      errormessage();
      exit(1);
    }

    if (!SetCurrentDirectory(argv[1]))
    {
      errormessage();
      exit(1);
    }

    banner();
    doit(".");
    SetCurrentDirectory(olddir);
  }
  else
  {
    banner();
    doit(".");
  }

  return(0);
}

Credit: Salem, vVv and Hammer

Script provided by SmartCGIs