0

I'm new to C and trying to recurse through all directories/files from the current working directory and output their information. The problem I'm running into that I can't think of a good way to solve is when there are two folders in the same directory the path gets built wrong the second time. For instance if dir1 and dir2 are in the same path after "/something/dir1" is completed the path should become "/something/dir2" but becomes "/something/dir1/dir2" because of the way I have things written. I thought of just keeping track of the previous path, but I'm not sure of a way to do that without it constantly being rewritten every recursive call.

Update: I have since fixed the original bug I was having and figured I'd post my new code here. The trick I was unaware about was opendir(".") and changedir("..") would actually translate the period into the full current or previous path. As far as changing the type = 8 and type = 4 statements to the more readable S_ISDIR(statbuf.st_mode) and S_ISREG(statbuf.st_mode) statements, they appear not to work at all while the type statements do. Not sure what's wrong with the syntax and the way I tried using them.

Update 2: I solved the S_ISDIR/S_ISREG problem here - How to use S_ISREG() and S_ISDIR() POSIX Macros?

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

void helper(DIR *, struct dirent *, struct stat, char *, int);
void dircheck(DIR *, struct dirent *, struct stat, char *, int);

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

  DIR *dip;
  struct dirent *dit;
  struct stat statbuf;
  char currentPath[FILENAME_MAX];
  int depth = 0; /*Used to correctly space output*/

  dip = opendir(".");
  getcwd(currentPath, FILENAME_MAX);

  while((dit = readdir(dip)) != NULL){

    /*Skips . and ..*/
    if(strcmp(dit->d_name, ".") == 0 || strcmp(dit->d_name, "..") == 0)
      continue;

    stat(currentPath, &statbuf);

    /*Checks if current item is of the type file (type 8)*/
    if(dit->d_type == 8)
      printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);

    /*Checks if current item is of the type directory (type 4)*/
    if(dit->d_type == 4)
      dircheck(dip, dit, statbuf, currentPath, depth);

  }
  return 0;
}

/*Recursively called helper function*/
void helper(DIR *dip, struct dirent *dit, struct stat statbuf, char currentPath[FILENAME_MAX], int depth){
  int i = 0;
  dip = opendir(currentPath);

  while((dit = readdir(dip)) != NULL){

    if(strcmp(dit->d_name, ".") == 0 || strcmp(dit->d_name, "..") == 0)
      continue;

    stat(currentPath, &statbuf);

    if(dit->d_type == 8){
      for(i = 0; i < depth; i++)
        printf("    ");
      printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
    }

    if(dit->d_type == 4)
      dircheck(dip, dit, statbuf, currentPath, depth);

  }
}

void dircheck(DIR *dip, struct dirent *dit, struct stat statbuf, char currentPath[FILENAME_MAX], int depth){
  int i = 0;

  strcat(currentPath, "/");
  strcat(currentPath, dit->d_name);

  /*If two directories exist at the same levelt the path
    is built wrong and needs to be corrected*/
  if((chdir(currentPath)) == -1){
    chdir("..");
    getcwd(currentPath, FILENAME_MAX);
    strcat(currentPath, "/");
    strcat(currentPath, dit->d_name);

    for(i = 0; i < depth; i++)
      printf ("    ");
    printf("%s (subdirectory)\n", dit->d_name);
    depth++;
    helper(dip, dit, statbuf, currentPath, depth);
  }

  else{
    for(i =0; i < depth; i++)
      printf("    ");
    printf("%s (subdirectory)\n", dit->d_name);
    chdir(currentPath);
    depth++;
    helper(dip, dit, statbuf, currentPath, depth);
  }

}
Community
  • 1
  • 1
Zach Alberico
  • 268
  • 3
  • 6
  • 19

2 Answers2

4

Don't needlessly reinvent the wheel. If you're on a unixy system, the nftw library function (part of the XSI option group in POSIX, meaning it's almost universally available) does exactly what you want:

http://pubs.opengroup.org/onlinepubs/9699919799/functions/nftw.html

On the other hand, if you're doing this as a learning exercise, or if you have a (moderately rare) instance where nftw won't suit your needs (for instance, if you need to perform directory recursion from multiple threads simultaneously) proceed to debug your solution.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • I'm doing it more as a learning exercise with system calls, but thank you for letting me know about this library. I'll definitely use it if I need this functionality in the future. – Zach Alberico Feb 07 '11 at 03:57
1

The very first thing I would do is change this:

if(type == 4)

To this:

if(S_ISDIR(statbuf.st_mode))

See the stat manpage for more detail.

Second, it looks like you are never calling closedir. You need to free stuff.

Third... Rather than copy-pasting your opendir/readdir code in two places, it would be valuable to put this redundant work into the same function.

And lastly.. Allocating MAX_PATH on the stack at each recursion will get rather large. You might want to consider using malloc and realloc. But as @R.. mentions above, you should try to re-use these buffers where you can. For a large dir tree this will use a lot of space and be expensive.

asveikau
  • 39,039
  • 2
  • 53
  • 68
  • Thanks a lot, these are helpful changes. Any ideas how I can work out the bug? – Zach Alberico Feb 06 '11 at 23:38
  • The reason the readdir code isn't in a separate function is because of the current bug. This was the path can be reestablished in main which is why it'll work for multiple directories in the first path. It's when it recurses into deeper levels that it becomes a problem. Trying to store the previous path is what I can't figure out considering it gets overwritten every time the recursive call is made. – Zach Alberico Feb 07 '11 at 03:56