2

I am writing a simple C program that receives a directory as an argument and displays the files in this directory and also his subdirectories. I wrote a "recursive" function for doing that. But for an unknown reason, my program fails at the stat function. Here is my program :

    #define _POSIX_SOURCE 1

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


void display_directory(char* path){

  DIR* directory = opendir(path);
  if( directory == NULL){
    printf("opendir failure for %s\n", path);
    exit(1);
  }


 struct dirent* dirent;
 struct stat stat_info;

 while((dirent = readdir(directory)) != NULL){
   printf("[%s]\n", dirent->d_name);
   if(stat(dirent -> d_name, &stat_info) == -1){
     printf("readdir error for %s\n", dirent->d_name);
     exit(1);
   }
   if(S_ISREG(stat_info.st_mode)){
       printf("File: %s \n", dirent -> d_name); 
   }
   if(S_ISDIR(stat_info.st_mode)){
     if(strncmp(dirent->d_name, "..",2)){
       printf("Directory : %s\n", dirent->d_name);
       display_directory(dirent->d_name);
     }  
    }

 }

 closedir(directory);
}

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

char* path;

if(argc > 1){
 path = argv[1];
} else {
 path = ".";
}

display_directory(path);

 return EXIT_SUCCESS;
}

For instance, if in my directory A, I have a1, a2, a3 and .., it reads first the .. directory, and when it reads the directory a1, the stat function fails.

Can someone tells me what is not correct with my code.

[EDIT] I included <errno.h> as many of you suggest and after running the program, I have the error Too many open files.

    #define _POSIX_SOURCE 1

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


void display_directory(char* path){

  DIR* directory = opendir(path);
  if( directory == NULL){
    printf("opendir failure for %s --> %s\n", path, strerror(errno));
    exit(1);
  }


 struct dirent* dirent;
 struct stat stat_info;

 while((dirent = readdir(directory)) != NULL){
   printf("[%s]\n", dirent->d_name);
   if(stat(dirent->d_name, &stat_info)){
     printf("readdir error for %s ---> %s\n", dirent->d_name, strerror(errno));
     continue;
   }
   if(S_ISREG(stat_info.st_mode)){
       printf("Fichier : %s \n", dirent->d_name); 
   }
   if(S_ISDIR(stat_info.st_mode)){
     if(strncmp(dirent->d_name, "..",2)){
       printf("Directory : %s\n", dirent->d_name);
       display_directory(dirent->d_name);
     }  
    }

 }

 closedir(directory);
}

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

char* path;

if (argc > 2) { 
 fprintf(stderr, "Usage: %s [directory]\n", argv[0]); 
 exit(1); 
}

path = argv[1];


display_directory(path);

 return EXIT_SUCCESS;
}

The output of the program :

[..]
[mykill.c]
readdir error for mykill.c ---> No such file or directory
[.]
Directory : .
[..]
[.]
Directory : .
[..]
[.]
Directory : .
[..]

...
...
Directory : .
opendir failure for . --> Too many open files

mykill.c is a file in the directory that was passed as an argument.

Dimitri
  • 8,122
  • 19
  • 71
  • 128
  • 1
    Did you look at `errno`? – Oliver Charlesworth Sep 19 '11 at 23:32
  • Don't use spaces around the primary operators `a.b` or `a->b`. It looks very weird to an experienced coder. – Jonathan Leffler Sep 19 '11 at 23:35
  • 1
    A better design for your main function would process all the arguments on the command line: `if (argc == 1) display_directory("."); else { for (int i = 1; i < argc; i++) display_directory(argv[i]); }`. – Jonathan Leffler Sep 19 '11 at 23:37
  • @OliCharlesworth Nop I dit not look at errno. – Dimitri Sep 19 '11 at 23:38
  • @JonathanLeffler Yeah, but my program only takes one argument. – Dimitri Sep 19 '11 at 23:39
  • In that case, it should have `if (argc > 2) { fprintf(stderr, "Usage: %s [directory]\n", argv[0]); exit(1); }` added. Now it only takes one argument. – Jonathan Leffler Sep 19 '11 at 23:42
  • Include `` and print out `errno` in your printfs, e.g. `printf("opendir failure for %s: \n", path, strerror(errno)); `. What does it say ? Also, at what level of recursion do you fail ? opendir/stat is relative to the current working directory with your code, so if you recurse with only the directory name, and not the path leading to the directory, things will fail. You have to append the parent directory, so you end up calling stat with "A/a1" and not just "a1" /or call chdir() at each level of recursion – nos Sep 19 '11 at 23:47
  • Re edit: That's not the **complete and unedited** output of your program. Show us the **complete and unedited** output. Also, update the source code to be exactly what you have. – zwol Sep 19 '11 at 23:48
  • Okay, now we have the output. (You did edit it, but I'll forgive you, because it appears you just deleted a bunch of repetition.) Based on what you see there, what do you think is the problem? (Hint: you have two problems.) – zwol Sep 19 '11 at 23:59
  • The first problem that I see is that I have an infinite loop while reading the '.' and the '..' directory.I do not understand the second problem. the condition "if(S_ISREG(stat_info.st_mode)){" is never met. – Dimitri Sep 20 '11 at 00:06
  • Ok, another hint: if you run the program *inside* the directory containing `mykill.c`, instead of passing that path as a command line argument, what happens? – zwol Sep 20 '11 at 00:17

4 Answers4

5

I have a pretty good idea what's wrong, but I want to tell you how to debug this for yourself, first. Change this code ...

if(stat(dirent -> d_name, &stat_info) == -1){
  printf("readdir error for %s\n", dirent->d_name);
  exit(1);
}

... to read instead ...

if (stat(dirent->d_name, &stat_info)) {
    printf("%s: %s\n", dirent->d_name, strerror(errno));
    continue;
}

You will need to add to the include list

#include <errno.h>

Run the program again. If you don't see from the output what the problem is, then edit the COMPLETE, UNEDITED output into your question and we'll go from there.

zwol
  • 135,547
  • 38
  • 252
  • 361
1

You are making stat only with filename (without full path), add full path to the filename or change working directory before calling stat.

1
if(S_ISDIR(stat_info.st_mode)){
 if( !strcmp(dirent->d_name, ".")) continue;
 if( !strcmp(dirent->d_name, "..")) continue;

 printf("Directory : %s\n", dirent->d_name);
 display_directory(dirent->d_name);
}
wildplasser
  • 43,142
  • 8
  • 66
  • 109
0

Use nftw().

Matt Joiner
  • 112,946
  • 110
  • 377
  • 526