Iterate over directory and print some information - c

I want to write a C program that takes as an argument the path to a folder and shows some info about the files it contains.
So far I have written this:
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv){
char* dir_path = argv[1];
char* dir_path_bar = strcat(dir_path, "/");
DIR* dir = opendir(dir_path);
for(struct dirent* entry = readdir(dir); entry != NULL; entry = readdir(dir)){
printf("Next entry is %s\n", entry->d_name);
char* entry_path = strcat(dir_path_bar, entry->d_name);
printf("%s\n", entry_path);
struct stat buf;
stat(entry_path, &buf);
printf("Its inode number is %s\n", entry->d_ino);
printf("Its inode number is %s\n", buf.st_ino);
printf("Its uid is %s\n", buf.st_uid);
printf("Its size is %s bytes\n", buf.st_size);
};
closedir(dir);
}
Which compiles, but the stat call is giving me a SEGFAULT. What is going on?

As others have mentioned, you can't append to argv[1]. You can't keep appending to it inside the loop. And, you can't use %s to output numbers.
Here is your code with the bugs annotated and fixed [using #if 0 to show the old code]:
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int
main(int argc, char **argv)
{
char *dir_path = argv[1];
// NOTE/BUG: argv[1] has a fixed size you can't append to it
#if 0
char *dir_path_bar = strcat(dir_path, "/");
#else
char dir_path_bar[PATH_MAX];
strcpy(dir_path_bar,dir_path);
strcat(dir_path_bar,"/");
#endif
DIR *dir = opendir(dir_path);
#if 1
if (dir == NULL) {
perror(dir_path);
exit(1);
}
#endif
for (struct dirent *entry = readdir(dir); entry != NULL;
entry = readdir(dir)) {
printf("Next entry is %s\n", entry->d_name);
// NOTE/BUG: because you don't reset dir_path_bar, this just keeps appending
// to it
#if 0
char *entry_path = strcat(dir_path_bar, entry->d_name);
#else
char entry_path[PATH_MAX];
strcpy(entry_path,dir_path_bar);
strcat(entry_path,entry->d_name);
#endif
printf("\n");
printf("%s\n", entry_path);
struct stat buf;
stat(entry_path, &buf);
// NOTE/BUG: these need one or more of: %d/%ld/%lld (vs %s)
#if 0
printf("Its inode number is %s\n", entry->d_ino);
printf("Its inode number is %s\n", buf.st_ino);
printf("Its uid is %s\n", buf.st_uid);
printf("Its size is %s bytes\n", buf.st_size);
#else
printf("Its inode number is %ld\n", entry->d_ino);
printf("Its inode number is %ld\n", buf.st_ino);
printf("Its uid is %d\n", buf.st_uid);
printf("Its size is %ld bytes\n", buf.st_size);
#endif
};
closedir(dir);
return 0;
}

Two problems:
You're appending continuously to the input (argv[1]) argument which is undefined behaviour. You can't append to the strings of argv.
Also printing integer values using %s which is undefined as well. %s expects a char * argument but you wanted to print integer values.
You can instead use a temporary buffer and pass it to stat(2):
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
if (argc != 2) {
printf("Usage: %s dir\n", argv[0]);
exit(1);
}
char* dir_path = argv[1];
DIR* dir = opendir(dir_path);
if (!dir) {
perror("opendir");
exit(1);
}
for(struct dirent* entry = readdir(dir); entry != NULL; entry = readdir(dir)) {
char entry_path[PATH_MAX] = {0};
int rc = snprintf(entry_path, sizeof entry_path, "%s/%s", dir_path, entry->d_name);
if ( rc < 0 || rc >= sizeof entry_path) {
fprintf(stderr, "Path truncated for '%s'\n", entry->d_name);
continue;
}
printf("Next entry is: %s\n", entry_path);
struct stat buf;
if (stat(entry_path, &buf) == 0) {
printf("Its inode number is %ju\n", (uintmax_t)entry->d_ino);
printf("Its inode number is %ju\n", (uintmax_t)buf.st_ino);
printf("Its uid is %jd\n", (intmax_t)buf.st_uid);
printf("Its size is %jd bytes\n", (intmax_t)buf.st_size);
} else {
perror("stat");
}
}
closedir(dir);
}
I have also added some error checking.

Not shown in the other 2 earlier answers is a nice avoidance of excessive copying.
When forming the entry_path, only the entry itself needs to be overwritten, not the entire string. This becomes valuable with a long pre-fixed directory string.
dir_path_len = strlen(dir_path);
if (dir_path_len >= PATH_MAX - 1) { return EXIT_FAILURE; } // too long
char entry_path[PATH_MAX];
strcpy(entry_path, dir_path);
strcpy(entry_path + dir_path_len++, "/"); // Can use strcpy() here
DIR *dir = opendir(dir_path);
...
for (struct dirent *entry = readdir(dir); entry != NULL; entry = readdir(dir)) {
printf("Next entry is %s\n", entry->d_name);
entry_len = strlen(entry->d_name);
if (dir_path_len + entry_len >= PATH_MAX) {
continue;
// or
return EXIT_FAILURE; // too long
}
strcpy(path + dir_path_len, entry->d_name); // strcpy(), not strcat()
printf("\n%s\n", entry_path);
struct stat buf;
if (stat(entry_path, &buf) ...
...

Related

Opening a file using relative path

The following code is supposed to work as follows: print the list of the files in a directory, and print the content of each .c file.
it works fine when executed in UNIX for the same directory: ./a.out ./
However, I was not able to make it work for ./a.out ../differentDir execution.
I know that if the absolute path is provided as an argument, I could use argv[1] for that. However, when it is provided in a form of a relative path I am lost.
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUFFSIZE 32768
int main(int argc, char **argv) {
char buf[BUFFSIZE];
DIR *dp;
struct dirent *dirp;
char filename[80];
int name_length;
FILE *fp;
if (argc != 2) {
fprintf(stderr, "usage: %s dir_name\n", argv[0]);
exit(1);
}
if ((dp = opendir(argv[1])) == NULL ) {
fprintf(stderr, "can't open '%s'\n", argv[1]);
exit(1);
}
while ((dirp = readdir(dp)) != NULL ){
printf("%s\n", dirp->d_name);
memset(filename, '\0', sizeof(filename));
strcpy(filename, dirp->d_name);
printf(" ** %s ", filename);
name_length = strlen(filename);
printf(" name_length=%d \n", name_length);
if (findC(filename)) // checking if the file has a .c extension
{
fp=fopen(filename, "r");
if (fp == NULL)
fprintf(stderr, "Can't open .C file!\n");
else
{// if the file was opened successfuly:
do
{
fgets(buf,BUFFSIZE,fp); // reading each line until buffer is full or until reaching whitespace
buf[strlen(buf)-1]='\0'; // removing the trailing whitespace from the buffer
puts(buf);
}
while (!feof(fp));
printf("\n\n");
fclose(fp);
}
}
}
closedir(dp);
return(0);
}
/*FindC method gets a c-string that represents a file name; returns 1 if the file ends with .C extension, else returns 0*/
int findC(char * name)
{
int len = strlen(name);
if (len>=2 && name[len-2]=='.' && tolower(name[len-1])=='c')
return 1;
return 0;
}
Upon opening the file to read, the file pathname needs to also be relative.
// Form prefix for complete relative file name
char filename[MAXPATH];
strcpy(filename, argv[1]);
// append '/' if directory path does not end in '/'
if (TBD_code(filename)) {
strcat(filename, "/");
}
char *end = filename[strlen(filename)];
while ((dirp = readdir(dp)) != NULL ){
printf("%s\n", dirp->d_name);
if (findC(dirp->d_name)) {
// append filename to prefix
strcpy(end, dirp->d_name);
fp=fopen(filename, "r");
...
You can use realpath(argv1...) like in this example. realpath will return the absolute path for a relative path.
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
int main(int argc, char **argv) {
char *path = "../..";
char buff[PATH_MAX + 1]; /* not sure about the "+ 1" */
char *res = realpath(path, buff);
if (res) {
printf("This source is at %s.\n", buff);
} else {
perror("realpath");
exit(EXIT_FAILURE);
}
return 0;
}
To include the desired behavior in your program, you can use realpathin your code:
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <ctype.h>
#include <dirent.h>
#include <string.h>
#define BUFFSIZE 32768
int main(int argc, char **argv) {
char buf[BUFFSIZE];
DIR *dp;
struct dirent *dirp;
char filename[80];
int name_length;
FILE *fp;
char buff[PATH_MAX + 1]; /* not sure about the "+ 1" */
if (argc != 2) {
fprintf(stderr, "usage: %s dir_name\n", argv[0]);
exit(1);
}
char *res = realpath(argv[1], buff);
if ((dp = opendir(res)) == NULL ) {
fprintf(stderr, "can't open '%s'\n", argv[1]);
exit(1);
}
while ((dirp = readdir(dp)) != NULL ){
printf("%s\n", dirp->d_name);
memset(filename, '\0', sizeof(filename));
strcpy(filename, dirp->d_name);
printf(" ** %s ", filename);
name_length = strlen(filename);
printf(" name_length=%d \n", name_length);
if (findC(filename)) // checking if the file has a .c extension
{
fp=fopen(filename, "r");
if (fp == NULL)
fprintf(stderr, "Can't open .C file!\n");
else
{// if the file was opened successfuly:
do
{
fgets(buf,BUFFSIZE,fp); // reading each line until buffer is full or until reaching whitespace
buf[strlen(buf)-1]='\0'; // removing the trailing whitespace from the buffer
puts(buf);
}
while (!feof(fp));
printf("\n\n");
fclose(fp);
}
}
}
closedir(dp);
return(0);
}
/*FindC method gets a c-string that represents a file name; returns 1 if the file ends with .C extension, else returns 0*/
int findC(char * name)
{
int len = strlen(name);
if (len>=2 && name[len-2]=='.' && tolower(name[len-1])=='c')
return 1;
return 0;
}
You could first change to the directory chdir either with relative or absolute path and the get the absolute path via the getcwd
#include <sys/types.h>
#include <string.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUFFSIZE 32768
#define PATH_SIZE 512
int main(int argc, char **argv) {
char buf[BUFFSIZE];
char path[PATH_SIZE];
DIR *dp;
struct dirent *dirp;
char filename[80];
int name_length, r;
FILE *fp;
if (argc != 2) {
fprintf(stderr, "usage: %s dir_name\n", argv[0]);
exit(1);
}
strcpy(path, argv[1]);
r = chdir(path);
if( r != 0 )
{
printf("Invalid path '%s'\n",path);
exit(1);
}
getcwd(path,PATH_SIZE);
if ((dp = opendir(path)) == NULL ) {
fprintf(stderr, "can't open '%s'\n", argv[1]);
exit(1);
}
while ((dirp = readdir(dp)) != NULL ){
printf("%s\n", dirp->d_name);
memset(filename, '\0', sizeof(filename));
strcpy(filename, dirp->d_name);
printf(" ** %s ", filename);
name_length = strlen(filename);
printf(" name_length=%d \n", name_length);
if (findC(filename)) // checking if the file has a .c extension
{
fp=fopen(filename, "r");
if (fp == NULL)
fprintf(stderr, "Can't open .C file!\n");
else
{// if the file was opened successfuly:
do
{
fgets(buf,BUFFSIZE,fp); // reading each line until buffer is full or until reaching whitespace
buf[strlen(buf)-1]='\0'; // removing the trailing whitespace from the buffer
puts(buf);
}
while (!feof(fp));
printf("\n\n");
fclose(fp);
}
}
}
closedir(dp);
return(0);
}
/*FindC method gets a c-string that represents a file name; returns 1 if the file ends with .C extension, else returns 0*/
int findC(char * name)
{
int len = strlen(name);
if (len>=2 && name[len-2]=='.' && tolower(name[len-1])=='c')
return 1;
return 0;
}

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

Iterative folder read using C and GSList

I'm trying to create an iterative program that reads all the folders from a specific starting folder using GSList and C. I haven't managed to find what the flaw in my code is for now.
The problem I'm having is that it reads each folder and all of it's subfolders until in reaches one with more subfolders. After that it just repeats to open only one directory.
The running result is below:
http://pastebin.com/jZMFBrxC
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <glib.h>
#include <glib/gprintf.h>
#include <limits.h>
#include <errno.h>
#include <sys/types.h>
int main(int argc, char *argv[]) {
GSList *list = NULL;
list = g_slist_prepend(list, "/home/ravior/Documente"); /* Folder for searching */
DIR *d;
int index = 0;
while((char *)g_slist_nth_data(list, 0) != NULL) {
gchar *element = g_strdup((char *)g_slist_nth_data(list, 0));
d = opendir(element);
if(!d) {
fprintf(stderr, "Couldn't open '%s' : %s\n", (char *)g_slist_nth_data(list, 0), strerror(errno));
exit(EXIT_FAILURE);
}
printf("\n\nThe opened folder is: %s\n\n", (char *)g_slist_nth_data(list, 0));
while(TRUE) {
struct dirent *entry;
const char *d_name;
entry = readdir(d);
if(!entry) {
break;
}
d_name = entry->d_name;
/* Some code here... */
if(entry->d_type & DT_DIR && strcmp(d_name, "..") != 0 && strcmp(d_name, ".") != 0) {
int path_length;
static char path[PATH_MAX];
path_length = snprintf(path, PATH_MAX, "%s/%s",element, d_name);
if(path_length >= PATH_MAX) {
fprintf(stderr, "Path length has got too long.\n");
exit(EXIT_FAILURE);
}
printf("%s\n", path);
list = g_slist_append(list, path);
index++;
printf("The appended element is: %s\n", (char *)g_slist_nth_data(list, index));
}
}
if(closedir(d)){
fprintf(stderr, "Couldn't close' '%s': %s\n",(char *)g_slist_nth_data(list, 0), strerror(errno));
}
list = g_slist_remove(list, (char *)g_slist_nth_data(list, 0));
free(element);
element = NULL;
index--;
}
g_slist_free(list);
return EXIT_SUCCESS;
}
Any help to solve this is more than appreciated. Also, if you have any other implementation for this problem using C, sharing it will be more than appreciated.
I've managed to figure it out eventually.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <glib.h>
#include <glib/gprintf.h>
#include <limits.h>
#include <errno.h>
#include <sys/types.h>
int main(int argc, char *argv[]) {
GSList *list = NULL;
list = g_slist_prepend(list, "/home/ravior/Documente"); /* Folder for searching */
DIR *d;
int index = 0;
while((char *)g_slist_nth_data(list, 0) != NULL) {
gchar *element = g_strdup((char *)g_slist_nth_data(list, 0));
d = opendir(element);
if(!d) {
fprintf(stderr, "Couldn't open '%s' : %s\n", element, strerror(errno));
exit(EXIT_FAILURE);
}
printf("\n\nThe opened folder is: %s\n\n", element);
while(TRUE) {
struct dirent *entry;
const char *d_name;
entry = readdir(d);
if(!entry) {
break;
}
d_name = entry->d_name;
/* Some code here... */
if(entry->d_type & DT_DIR && strcmp(d_name, "..") != 0 && strcmp(d_name, ".") != 0) {
int path_length;
static char path[PATH_MAX];
path_length = snprintf(path, PATH_MAX, "%s/%s",element, d_name);
if(path_length >= PATH_MAX) {
fprintf(stderr, "Path length has got too long.\n");
exit(EXIT_FAILURE);
}
printf("%s\n", path);
list = g_slist_append(list, strdup(path));
index++;
printf("The appended element is: %s\n", (char *)g_slist_nth_data(list, index));
}
}
if(closedir(d)){
fprintf(stderr, "Couldn't close' '%s': %s\n", element, strerror(errno));
}
list = g_slist_remove(list, (char *)g_slist_nth_data(list, 0));
free(element);
element = NULL;
index--;
}
g_slist_free(list);
return EXIT_SUCCESS;
}
I hope this piece of code will help someone in the future.

readdir()->d_name giving weird values

I am trying to get the name of the parent directory by using this code:
dirp=opendir(cur_spot);
printf("parent name: %s\n", readdir(dirp)->d_name);
closedir(dirp);
cur_spot holds '..'.
i do this in a loop and it keeps climbing up the directories to the root, the sequence of my output it:
.
.bash_logout
.
.
srv
I know that it is traversing correctly because i am checking the inodes along the way.
Do i need to use something different than d_name?
Thanks
I came up with this based on the ideas in the comments under sjs' answer:
#include <dirent.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <sys/stat.h>
#include <limits.h>
#include <errno.h>
int LookupName(const char* parent, ino_t ino, char *name, size_t size)
{
DIR *dp = opendir(parent);
if (!dp) return -1;
int ret = -1;
struct dirent *de;
while (de = readdir(dp))
{
if (de->d_ino == ino)
{
strncpy(name, de->d_name, size);
ret = 0;
break;
}
}
closedir(dp);
if (ret == -1) errno = ENOENT;
return ret;
}
int GetWorkdir(char *workdir, size_t size)
{
struct stat st;
if (stat(".", &st)) return -1;
char path[PATH_MAX];
strncpy(path, "..", sizeof(path));
memset(workdir, '\0', sizeof(workdir));
char name[PATH_MAX];
while (1)
{
if (LookupName(path, st.st_ino, name, sizeof(name))) return -1;
if (!strcmp(name, "..") || !strcmp(name, "."))
{
strncpy(name, "/", sizeof(name));
strncat(name, workdir, sizeof(name));
strncpy(workdir, name, size);
break;
}
if (workdir[0] != '\0')
{
strncat(name, "/", sizeof(name));
}
strncat(name, workdir, sizeof(name));
strncpy(workdir, name, size);
if (stat(path, &st)) return -1;
strncat(path, "/..", sizeof(path));
}
return 0;
}
int main(int argc, char **argv)
{
char workDir[PATH_MAX];
assert(!GetWorkdir(workDir, sizeof(workDir)));
printf("%s\n", workDir);
}
readdir is reading the directory, so when you say
printf("parent name: %s\n", readdir(dirp)->d_name);
you are actually asking to have the name of the first entry inside .. printed for you, not the name of the .. directory.
Depending on what you are trying to do, perhaps parsing the output of getcwd might be a better approach?

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);
}
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.
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)
...
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).
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;
}
If you are using on unix, then you may use the system command.
system("ls -ltr | grep -d");

Resources