Accessing Directories in C

落花浮王杯 提交于 2019-11-28 13:05:48

One problem is that a directory is also a type of file, and can be normally fopen()ed. You want to call lstat() on each file to check whether it is a directory. Like this:

struct stat st;
lstat(dent->d_name, &st);
if(S_ISDIR(st.st_mode))
   printf("\t DIRECTORY\n");
else
   printf("\t FILE\n");

But this error should lead to all entries being displayed as files. Do you have read permissions for the files in this directory? What is the value of errno after the fopen() call?

Gilles

Suppose that root/test contains a file called foo. The call to dent=readdir(dir) sets dent->d_name to "foo". You already have debugging output that shows this: printf(dent->d_name)¹. Then you try to open foo with fopen, but the file is actually root/test/foo. So this fails every time (unless you happen to also have a file called foo in the current directory).

There are two ways to open the right file:

  • Construct the full name of the file by concatenating the argument to opendir with the file name. Something like:

    /*before */
    size_t dir_length = strlen(DIR_PATH);
    char *filename = malloc(dir_length + NAME_MAX + 2); /*error checking omitted*/
    strcpy(filename, DIR_PATH);
    filename[dir_length] = '/';
    filename[dir_length+1] = 0;
    while ((dent = readdir(dir)) != NULL) {
        strcpy(filename + dir_length + 1, dent->d_name);
        /*now call lstat, fopen, etc. on filename*/
    
  • Change into the directory you're listing. For example, change the opendir call to

    chdir(DIR_PATH); /*error checking omitted*/
    dir = opendir(".");
    

    You have to remember to save the previous directory with getcwd and chdir back to it afterwards. This method is not recommended in production software because it is possible to have a current directory that you can't chdir back into due to permissions.

slacker has already explained why fopen can't be used to test if a file is a directory.

¹ which by the way should be puts(dent->d_name) or even better fputs(dent->d_name, stderr): your original printf call would break if a file name contains %, which is not a big problem for debugging output but is a bad habit to get into.

its a combination of the two answers by slacker and Gilles. use lstat but don't use it like slacker said. You need to send lstat the full path not just dent->d_name. And just so you know lstat requires you include sys/stat.h>
if you look at the man page for lstat there is a test program at the bottom or just look at mine.

Here is my program that tries to mimick "ls" on linux. Note: escape sequence colors doesn't work in windows just in case you were worried about portability.

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

int main(int argc,char* argv[]){
 char blue[] = { 0x1b, '[', '1', ';', '3', '4', 'm', 0 };
 char normal[]={ 0x1b, '[', '0', ';', '3', '9', 'm', 0 };
 char green[]= { 0x1b, '[', '0', ';', '3', '2', 'm', 0 };
 char red[]=   { 0x1b, '[', '0', ';', '3', '1', 'm', 0 };
 char cyan[]=  { 0x1b, '[', '0', ';', '3', '6', 'm', 0 };
 DIR* myDirectory;
 char *path=NULL;
 size_t size=100;
 int result;
 char* fullpath;
 if (argc >=3){
   std::cout<<"Usage: myls <path>"<<std::endl;
   return -1;
 }
 if (argc >= 2){
   myDirectory=opendir(argv[1]);
   if (errno==ENOENT){
   std::cout<<"error: file does not exist"<<std::endl;
   return -1;
   }
   path=argv[1];
 if (path[strlen(path)-1]!='/')
strcat(path,"/");
 }
 else if(argc==1){
   path=getcwd(path,size);
   strcat(path,"/");
   myDirectory=opendir(path);
 }
 struct stat fileProperties;
 struct dirent* directory;
 do{
    directory=readdir(myDirectory);
    if (directory!=NULL){
    fullpath=new char[strlen(path)+strlen(directory->d_name)+2];
    strcat(fullpath,path);
    strcat(fullpath,directory->d_name);
    result=lstat(fullpath,&fileProperties);
    //std::cout<<result<<fullpath;
    switch (fileProperties.st_mode & S_IFMT){
      case S_IFDIR: std::cout<<blue;
            break;
      case S_IFLNK: std::cout<<cyan; break;
      case S_IFREG: std::cout<<normal;
      default:  std::cout<<normal;
        if (fileProperties.st_mode & S_IXUSR)
            std::cout<<green;
        break;
      }

      std::cout<<directory->d_name<<"\n";
      std::cout<<normal;
     }
  }while(directory!=NULL);
  std::cout<<normal<<'\n';
  closedir(myDirectory);
  delete[] fullpath;
  return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!