How to use S_ISREG() and S_ISDIR() POSIX Macros?
Asked Answered
C

2

14

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)\n", 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)\n", 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\n", 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)\n", 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)\n", 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)\n", dit->d_name);
    depth++;
    helper(dip, dit, statbuf, currentPath, depth, argv);
  }

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

}
Cavalla answered 14/2, 2011 at 6:28 Comment(3)
I've restored the code to the original because the question didn't make sense any more. It's great that you contribute working code, but that belongs in an answer, not in the question. If you feel that it's worth posting complete code that implements mu's solution, please post it as an answer.Edibles
Is this the same as tree?Mazurka
For a complete version of an answer look here.Borst
D
17

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.

Dinka answered 14/2, 2011 at 6:50 Comment(3)
Thanks, that does seem to be the issue. Any ideas on how to do the change without everything else breaking? Simply using strcat to add a / and dit->d_name every iteration builds the path wrong, but also messes up the rest of the code.Cavalla
Actually just think I figured it out, I'll post updated code in a moment to help other people in a similar situation. Thank you.Cavalla
using absolute path to stat, solved a lot of issuesGardiner
D
6

[Posted on behalf of fossuser] Thanks to "mu is too short" I was able to fix the bug. Here is my working code has been edited in for those looking for a nice example (since I couldn't find any others online).

#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;

    /*Correctly forms the path for stat and then resets it for rest of algorithm*/
    getcwd(currentPath, FILENAME_MAX);
    strcat(currentPath, "/");
    strcat(currentPath, dit->d_name);
    if(stat(currentPath, &statbuf) == -1){
      perror("stat");
      return errno;
    }
    getcwd(currentPath, FILENAME_MAX);


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

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

    /*Checks if current item is of the type directory (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\n", currentPath);

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

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

    strcat(currentPath, "/");
    strcat(currentPath, dit->d_name);
    stat(currentPath, &statbuf);
    getcwd(currentPath, FILENAME_MAX);

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

    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)\n", dit->d_name, (int)statbuf.st_size);
      }
    }

    if(S_ISDIR(statbuf.st_mode))
      dircheck(dip, dit, statbuf, currentPath, depth, argv);
  }
  /*Changing back here is necessary because of how stat is done*/
    chdir("..");
    closedir(dip);
}

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)\n", dit->d_name);
    depth++;
    helper(dip, dit, statbuf, currentPath, depth, argv);
  }

  else{
    for(i =0; i < depth; i++)
      printf("    ");
    printf("%s (subdirectory)\n", dit->d_name);
    chdir(currentPath);
    depth++;
    helper(dip, dit, statbuf, currentPath, depth, argv);
  }
}
Dicast answered 14/2, 2011 at 6:28 Comment(1)
attempts to open a library object as a directory, and then segfaults: Error: Failed to open Directory ==> <some path>/SDL_shaders_gl.lo ... Segmentation fault (core dumped)Inigo

© 2022 - 2024 — McMap. All rights reserved.