Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
577 views
in Technique[技术] by (71.8m points)

c - How to use S_ISREG() and S_ISDIR() POSIX Macros?

This is a C program I wrote to recursively navigate and output directories and regular files. It compiles and runs fine on my Linux machine. But on Solaris, the dit->d_type == 8 check and the other similar ones don't work because there is no d_type field. An answer I've read to this problem is to use the S_ISREG() and S_ISDIR() macros, but they don't work at all the way I have them in my code currently. I commented out the lines that work on my Linux machine.

#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, char **);
void dircheck(DIR *, struct dirent *, struct stat, char *, int, char **);

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*/

  /*Open Current Directory*/
  if((dip = opendir(".")) == NULL)
    return errno;

  /*Store Current Working Directory in currentPath*/
  if((getcwd(currentPath, FILENAME_MAX)) == NULL)
    return errno;

  /*Read all items in directory*/
  while((dit = readdir(dip)) != NULL){
    /*Skips . and ..*/
    if(strcmp(dit->d_name, ".") == 0 || strcmp(dit->d_name, "..") == 0)
      continue;

    if(stat(currentPath, &statbuf) == -1){
      perror("stat");
      return errno;
    }

    /*Checks if current item is of the type file (type 8) and no command line arguments
      if(dit->d_type == 8 && argv[1] == NULL)*/
    if(S_ISREG(statbuf.st_mode) && argv[1] == NULL)
      printf("%s (%d bytes)
", dit->d_name, (int)statbuf.st_size);

      /*If a command line argument is given, checks for filename match
    if(dit->d_type == 8 && argv[1] != NULL)*/
    if(S_ISREG(statbuf.st_mode) && argv[1] != NULL)
      if(strcmp(dit->d_name, argv[1]) == 0)
        printf("%s (%d bytes)
", dit->d_name, (int)statbuf.st_size);

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

  }
  closedir(dip);
  return 0;
}

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

  if((dip = opendir(currentPath)) == NULL)
    printf("Error: Failed to open Directory ==> %s
", 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 && argv[1] == NULL){*/
    if(S_ISREG(statbuf.st_mode) && argv[1] == NULL){
      for(i = 0; i < depth; i++)
        printf("    ");
      printf("%s (%d bytes)
", dit->d_name, (int)statbuf.st_size);
    }

    /*if(dit->d_type == 8 && argv[1] != NULL){*/
    if(S_ISREG(statbuf.st_mode) && argv[1] != NULL){
      if(strcmp(dit->d_name, argv[1]) == 0){
    for(i = 0; i < depth; i++)
      printf("    ");
    printf("%s (%d bytes)
", dit->d_name, (int)statbuf.st_size);
      }
    }

    /*if(dit->d_type == 4)*/
    if(S_ISDIR(statbuf.st_mode))
      dircheck(dip, dit, statbuf, currentPath, depth, argv);

  }
}

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

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

  /*If two directories exist at the same level 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)
", dit->d_name);
    depth++;
    helper(dip, dit, statbuf, currentPath, depth, argv);
  }

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

}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You're using S_ISREG() and S_ISDIR() correctly, you're just using them on the wrong thing.

In your while((dit = readdir(dip)) != NULL) loop in main, you're calling stat on currentPath over and over again without changing currentPath:

if(stat(currentPath, &statbuf) == -1) {
    perror("stat");
    return errno;
}

Shouldn't you be appending a slash and dit->d_name to currentPath to get the full path to the file that you want to stat? Methinks that similar changes to your other stat calls are also needed.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...