Problem reading directories in C
Asked Answered
P

4

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.

Propman answered 19/9, 2011 at 23:31 Comment(11)
Did you look at errno?Dogged
Don't use spaces around the primary operators a.b or a->b. It looks very weird to an experienced coder.Zonation
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]); }.Zonation
@OliCharlesworth Nop I dit not look at errno.Propman
@JonathanLeffler Yeah, but my program only takes one argument.Propman
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.Zonation
Include <errno.h> 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 recursionAjani
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.Donn
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.)Donn
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.Propman
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?Donn
N
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);
}
Nidia answered 20/9, 2011 at 11:2 Comment(0)
D
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.

Donn answered 19/9, 2011 at 23:37 Comment(0)
N
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);
}
Nidia answered 20/9, 2011 at 11:2 Comment(0)
U
1

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

Underfeed answered 16/9, 2013 at 17:52 Comment(0)
R
0

Use nftw().

Richmal answered 20/9, 2011 at 2:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.