nunos
nunos

Reputation: 21409

Listing directories in Linux from C

I am trying to simulate linux command ls using linux api from c. Looking at the code it does make sense, but when I run it I get "stat error: No such file or directory". I have checked that opendir is working ok. I think the problem is in stat, which is returning -1 even though I think it should return 0.

What am I missing?

Thanks for your help.

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

int main(int argc, char *argv[])
{
 DIR *dirp;
 struct dirent *direntp;
 struct stat stat_buf;
 char *str;

 if (argc != 2)
 {
  fprintf( stderr, "Usage: %s dir_name\n", argv[0]);
  exit(1);
 }

 if ((dirp = opendir( argv[1])) == NULL)
 {
  perror(argv[1]);
  exit(2);
 }

 while ((direntp = readdir( dirp)) != NULL)
 {
  if (stat(direntp->d_name, &stat_buf)==-1)
  {
   perror("stat ERROR");
   exit(3);
  }
  if (S_ISREG(stat_buf.st_mode)) str = "regular";
  else if (S_ISDIR(stat_buf.st_mode)) str = "directory";
  else str = "other";
  printf("%-25s - %s\n", direntp->d_name, str);
 }

 closedir(dirp);
 exit(0);
}

Upvotes: 1

Views: 5496

Answers (5)

Sabri Mevis
Sabri Mevis

Reputation: 2431

Why dont you try this? Just give the path to argv[1] like this /home/sabri/Desktop/Test

int main(int argc, char *argv[]) 
{
    struct dirent *direntp;
    DIR *dirp;

    if (argc != 2) 
    {
       fprintf(stderr, "Usage: %s directory_name\n", argv[0]);
       return 1;
    }

if ((dirp = opendir(argv[1])) == NULL) 
{
    perror ("Failed to open directory");
    return 1;
}

while ((direntp = readdir(dirp)) != NULL)
    printf("%s\n", direntp->d_name);

while ((closedir(dirp) == -1) && (errno == EINTR)) ;

return 0;

}

Upvotes: 1

caf
caf

Reputation: 239311

Others have suggested building a full path for stat(), or using chdir(). Both those will work (although they are subject to a race condition, if the directory is renamed while you are in the middle of reading it).

An alternative, which is not subject to the race condition, and is therefore arguably more "correct", is to use fstatat(). Just replace your existing stat() call with:

fstatat(dirfd(dirp), direntp->d_name, &stat_buf, 0)

(The chdir() method can be made race-condition-free too: either by using fchdir(dirfd(dirp)) instead of chdir(), or by changing directory to argv[1] and then opening "." with opendir(). The pathname construction method can't be made race-condition-free).

Upvotes: 1

Omnifarious
Omnifarious

Reputation: 56078

It's because you aren't stating the actual file. It's in a different directory. If you want the real filename, combine argv[1] and direntp->d_name with a '/' between them.

Also, hungarian naming is icky, even the minor bit like 'p' on the end. If you have so many variables you need to keep track of their types in their names you're doing something wrong.

Here is a revised version of your program:

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#include <sys/param.h>

int main(int argc, char *argv[])
{
 DIR *dirp;
 struct dirent *direntp;
 struct stat stat_buf;
 char *str;
 char fullpath[MAXPATHLEN + 1];
 size_t dirnamelen;

 if (argc != 2)
 {
  fprintf( stderr, "Usage: %s dir_name\n", argv[0]);
  exit(1);
 }
 strncpy(fullpath, argv[1], MAXPATHLEN - 1); /* account for trailing '/' */
 fullpath[MAXPATHLEN - 1] = '\0';
 dirnamelen = strlen(fullpath);
 if (strlen(argv[1]) > dirnamelen) {
  fprintf( stderr, "Directory name is too long: %s", argv[1] );
  exit(2);
 }

 fullpath[dirnamelen++] = '/';
 fullpath[dirnamelen] = '\0';

 if ((dirp = opendir( argv[1])) == NULL)
 {
  perror(argv[1]);
  exit(2);
 }

 while ((direntp = readdir( dirp)) != NULL)
 {
  fullpath[dirnamelen] = '\0';
  if ((dirnamelen + strlen(direntp->d_name)) > MAXPATHLEN) {
   fprintf(stderr, "File %s + directory %s is too long.", direntp->d_name, fullpath);
   continue;
  } else {
   /* strncpy is mild overkill because the if statement has verified that
      there's enough space.  */
   strncpy(fullpath + dirnamelen, direntp->d_name, MAXPATHLEN - dirnamelen);
   fullpath[MAXPATHLEN] = '\0';
  }
  if (stat(fullpath, &stat_buf)==-1)
  {
   perror("stat ERROR");
   exit(3);
  }
  if (S_ISREG(stat_buf.st_mode)) str = "regular";
  else if (S_ISDIR(stat_buf.st_mode)) str = "directory";
  else str = "other";
  printf("%-25s - %s\n", direntp->d_name, str);
 }

 closedir(dirp);
 exit(0);
}

Note that I use MAXPATHLEN (from <limits.h>) and carefully check to make sure there aren't any buffer overflows. You should do the same in your code.

Edit: Changed code to use strn family functions for added safety.

Upvotes: 3

Tuomas Pelkonen
Tuomas Pelkonen

Reputation: 7831

Add

#include <unistd.h>
...
chdir(argv[1]);

or call stat with the full pathname like this

...
char fullpath[MAXPATHLEN];
snprintf(fullpath, sizeof(fullpath), "%s/%s", argv[1], direntp->d_name);
if (stat(fullpath, &stat_buf) == -1)
...

Upvotes: 2

Sachin
Sachin

Reputation: 21921

If you are using on unix, then you may use the system command.

system("ls -ltr | grep -d");

Upvotes: -2

Related Questions