AndieM
AndieM

Reputation: 39

How to read a directory given in the command line and open and print the directories

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);
  }
}

Upvotes: 0

Views: 1204

Answers (2)

Sunson
Sunson

Reputation: 88

 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);
}

Upvotes: 0

Marcin Kajzler
Marcin Kajzler

Reputation: 2968

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.

Upvotes: 1

Related Questions