0

I am working on a version of graphical ls, representing the output of ls with a tree. I have gotten most of my code working but would like to be able to determine what directory to read from in the command line. I have tried using

DIR *d
d = opendir(argv[1]);

But this does not work and results in errors opening files as well as the size and other information not updating for the file.

Any information would be greatly appreciated! Thanks.

#include <sys/types.h>
#include <sys/stat.h>

#include <sys/types.h>
#include <unistd.h>

#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>


#define _GNU_SOURCE
#include <limits.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 *d;

  // Create dirent struct
  struct dirent *dir;

  // Create stat struct
  struct stat buf;

  // current path for file
  char currentPath[FILENAME_MAX];

  // Depth for tree
  int depth = 0; 


  // Checking for correct usage
  if (argc != 2) {
    printf("Usage: './gls <directory_name>'\n");
    exit(0);
  }

  // Checking that file provided in command line actually exists
  if (lstat(argv[(argc - 1)], &buf) < 0) {
    printf("Error: No such file exists.\n");
    return 1;
    exit(0);
  }

  // General resource for printing files: http://stackoverflow.com/questions/4204666/how-to-list-files-in-a-directory-in-a-c-program%20*/

  // Open the current directory
  d = opendir (".");

  if(d == NULL) {
    printf("Error opening directory.\n");
    return 1;
  }

  // Store the current directory into currentPath
  if((getcwd(currentPath, FILENAME_MAX)) == NULL) {
    printf("Error: No such file exists.\n");
    return 1;
  }

  // Iterate through all items in directory
  while((dir = readdir(d)) != NULL){

    // Do not process . and ..
    if(strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0)
      continue;

    // Forms the path for lstat
    getcwd(currentPath, FILENAME_MAX);
    strcat(currentPath, "/");
    strcat(currentPath, dir->d_name);
    if(lstat(currentPath, &buf) == -1){
      perror("stat");
      printf("Error could not open file\n");
    }
    getcwd(currentPath, FILENAME_MAX);

    // Checks if file is a regular file
    if(S_ISREG(buf.st_mode))
    printf("| %s (regular file - %d - !checksum)\n", dir->d_name, (int)buf.st_size);

    // Checks if file is a directory
    else if(S_ISDIR(buf.st_mode)) {
      printf("| %s (directory)\n", dir->d_name);
      dircheck(d, dir, buf, currentPath, depth, argv);
    }

    // Checks if file is a symbolic link
    else if(S_ISLNK(buf.st_mode)) {

      // Resource used for absolute and relative paths: http://www.apiexamples.com/c/stdlib/realpath.html 
      char resolved_path[PATH_MAX];
      realpath(currentPath, resolved_path);         
      printf("| %s (symbolic link - %s)\n", dir->d_name, resolved_path);
    }

    // Checks if file is a FIFO
    else if(S_ISFIFO(buf.st_mode)) {
      printf("| %s (fifo (named pipe))\n", dir->d_name);
    }
  }
  // Close the directory and return 0 for success
  closedir(d);
  return 0;
}

// Recursive helper 
// Resource used for some of helper function and dircheck function: http://stackoverflow.com/questions/4989431/how-to-use-s-isreg-and-s-isdir-posix-macros
void helper(DIR *d, struct dirent *dir, struct stat buf, char currentPath[FILENAME_MAX], int depth, char *argv[]){
  int i = 0;

  // Open directory in currentPath
  if((d = opendir(currentPath)) == NULL)
    printf("Error: Failed to open Directory ==> %s\n", currentPath);

  // Read through directory
  while((dir = readdir(d)) != NULL){

    // If file is . or .. ignore
    if(strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0)
      continue;

    getcwd(currentPath, FILENAME_MAX);
    strcat(currentPath, "/");
    strcat(currentPath, dir->d_name);

    getcwd(currentPath, FILENAME_MAX);

    // If file is a register
    if(S_ISREG(buf.st_mode)){
      for(i = 0; i < depth; i++) {
        printf("    ");
        printf("%s (%d bytes)\n", dir->d_name, (int)buf.st_size);
      }
    }

    // If file is a directory
    if(S_ISDIR(buf.st_mode)) {
      if(strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) {
        dircheck(d, dir, buf, currentPath, depth, argv);
      }
    }
  }

  // Change directory back
    chdir("..");
    closedir(d);
}


// Resource used as guideline in order to create helper and dircheck functions
// http://stackoverflow.com/questions/4989431/how-to-use-s-isreg-and-s-isdir-posix-macros
void dircheck(DIR *d, struct dirent *dir, struct stat buf, char currentPath[FILENAME_MAX], int depth, char *argv[]){

  int i = 0;
  strcat(currentPath, "/");
  strcat(currentPath, dir->d_name);

  // If two directories exists at the same section in the tree
  if((chdir(currentPath)) == -1){
    getcwd(currentPath, FILENAME_MAX);
    strcat(currentPath, "/");
    strcat(currentPath, dir->d_name);

    getcwd(currentPath, FILENAME_MAX);

    // Add --- based on the depth of the tree
    for(i = 0; i <= depth; i++)
      printf ("---");
    printf("| %s (subdirectory)\n", dir->d_name);
    depth++;
    helper(d, dir, buf, currentPath, depth, argv);
  }

  else{
    // Add --- based on the depth of the tree
    for(i =0; i <= depth; i++)
      printf("---");
    printf("| %s (subdirectory)\n", dir->d_name);
    chdir(currentPath);
    depth++;
    helper(d, dir, buf, currentPath, depth, argv);
  }
}
AndieM
  • 39
  • 8
  • 2
    You only only call `stat` once, and you only call it on the directory provided as an argument. That means the value in `statBuf.st_mode` never changes. – Ross Ridge May 19 '16 at 06:03
  • On an unrelated note, if `stat` fails it could be for many other reasons, not just that the file doesn't exist. You need to check [`errno`](http://man7.org/linux/man-pages/man3/errno.3.html) to see what went wrong. – Some programmer dude May 19 '16 at 06:03
  • How can i update the value to check each file separately then? – AndieM May 19 '16 at 06:07
  • You my be interested in the functions `ftw` and `nftw` for handling directory trees. Much less error prone to use the C library functions intended to perform a *file tree walk* rather than rolling your own. – David C. Rankin May 20 '16 at 05:23

2 Answers2

1

You are reading stat before while loop, this is dir in your case. Then for every file in directory you are checking st_mode, but this is not updated nowhere in the while loop.

Marcin Kajzler
  • 2,698
  • 1
  • 9
  • 7
0
 if (stat(argv[(argc - 1)], &statBuf) < 0)

this line just query the dir info, so you will always get directory type. you should query specific file under that dir, so you can change the code like this:

#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>

#include <errno.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>

/* Resource: http://stackoverflow.com/questions/4204666/how-to-list-files-in-a-directory-in-a-c-program */

/* Resource: http://cboard.cprogramming.com/linux-programming/131-stat.html */
#define MAX_FILE_NAME_LEN 256

int main(int argc, char *argv[])
{
  char path[MAX_FILE_NAME_LEN] = {0};
  struct stat statBuf;

  if (argc != 2) {
    printf("Usage: './gls <directory_name>'\n");
    exit(0);
  }


  if (stat(argv[(argc - 1)], &statBuf) < 0) {
    printf("Error: No such file exists.\n");
    exit(0);
  }

  DIR           *d;
  struct dirent *dir;
  //char currentPath[FILENAME_MAX];

  d = opendir(argv[1]);


  while ((dir = readdir(d)) != NULL) {

    //getcwd(currentPath, FILENAME_MAX);
    //strcat(currentPath, "/");
    //strcat(currentPath, dir->d_name);
    //if(stat(currentPath, &statBuf) == -1){
      //printf("N")
    //}
   memset(path, 0, MAX_FILE_NAME_LEN);
   snprintf(path,MAX_FILE_NAME_LEN,"%s%s",argv[1],dir->d_name);
    //getcwd(currentPath, FILENAME_MAX);

   if (stat(path, &statBuf) < 0) {
     printf("Error: No such file exists.\n");
     continue;
     // exit(0);
   }                                                                                    

    if(S_ISREG(statBuf.st_mode)) {

      printf("| %s (regular file - %d - !checksum!)\n", dir->d_name, (int)statBuf.st_size);             /* If regular file */
    }

    if(S_ISDIR(statBuf.st_mode)) {
      printf("| %s (directory - size)\n", dir->d_name);
    }

    if(S_ISLNK(statBuf.st_mode)) {
      printf("| %s (symbolic link - points to !!!\n", dir->d_name);
    }

    if(S_ISFIFO(statBuf.st_mode)) {
      printf("| %s (fifo (named pipe))\n", dir->d_name);
    }
  }

  closedir(d);

  return(0);
}
Sunson
  • 88
  • 1
  • 8
  • When i implemented these changes I now get the error message "Error: NO such file exists multiple times. What exactly does the statement if (stat(path, &statBuf) < 0) mean? – AndieM May 19 '16 at 15:10
  • 'path' combines the dir name and file name,so it's a absolute path name of a file. 'if(stat(path, &statBuf)<0)' means the named file not exist. You can printout the 'path' and see whether the file exists. BTW, this code works for me, and you may check the permission of the dir and files. You can also use 'perror()' to find the exact error – Sunson May 20 '16 at 01:26
  • @AndieM this link may help. http://stackoverflow.com/questions/8524876/c-programming-stat-system-call-error – Sunson May 20 '16 at 01:33