I am trying to implement the ls
command and am getting stuck in the -i
option using the stat()
function to list inode numbers.
The format is ./myls [options] [list of files]
If I run ./myls -i .
I get correct output (this prints files in the current directory)
./myls -i
1446018 myls.c
1441809 myls
1445497 something
and if I run ./myls ..
I get correct output (printing files in parent directory)
./myls ..
Assignment2
Assignment3
Assignment4
Assignment1
But if combine them with parent directory and run ./myls -i ..
I get the error message
/myls -i ..
Error finding file: No such file or directory
"Error finding file" is an error message I am printing out in my ls
function.
//printf("%s", name);
if (stat(name, fileStat) != 0) {
error("Error finding file");
}
If I uncomment the printf
statement right before this error message, I get the following
/myls -i ..
Assignment2
Error finding file: No such file or directory
So it is getting the name of the first file of the parent directory but the stat function fails. Anyone know how I can fix this?
I read somewhere that it might be failing because it is only passing the name of the file and not its path. But this works in the current directory so this may not be the issue. If it is, then how would I pass the path and not just the name of file?
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int i = 0, R = 0, l = 0;
int anyFiles = 0;
void error(char *msg) {
perror(msg);
exit(0);
}
void ls(char *path) {
DIR *d;
struct dirent *dir;
d = opendir(path);
if (!d || d == NULL)
error("Unable to read directory");
char name[256]; //to store file name of each file in directory
while ((dir = readdir(d)) != NULL) {
strcpy(name, dir -> d_name);
if (name[0] == '.' || strncmp(name, "..", 2) == 0)
continue;
if (i || R || l) {
struct stat *fileStat;
//printf("%s\n",name);
if (stat(name, fileStat) != 0) {
error("Error finding file");
}
if (i) {
printf("%lu\t\t%s\n", fileStat->st_ino, name);
continue;
}
/*else if (R) {
continue;
}
else if (l) {
continue;
}*/
}
printf("%s\n", name);
}
closedir(d);
}
void process(int args, char *argsList[]) {
int j;
for (j = 1; j < args; j ) {
if (argsList[j][0] == '-') {
int k;
for (k = 1; k < (strlen(argsList[j])); k ) {
if (argsList[j][k] == 'i')
i = 1;
else if (argsList[j][k] == 'R')
R = 1;
else if (argsList[j][k] == 'l')
l = 1;
else
error("option not supported");
}
}
else if (argsList[j][0] != '-') {
ls(argsList[j]);
anyFiles = 1;
}
}
if (anyFiles == 0)
ls(".");
}
int main(int argc, char *argv[]) {
if (argc == 1)
ls(".");
else if (argc > 1) {
process(argc, argv);
}
return 0;
}
CodePudding user response:
The name passed to stat
in stat(name, fileStat)
is relative to the open directory. You must either construct the actual name from path
and dir->d_name
or use statat()
. It works for "."
because the relative paths happen to be relative to the current directory.
Furthermore, the stat
structure should be declared with automatic storage, not as an uninitialized pointer.
Here is a modified version with many other issues fixed:
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int flag_a, flag_i, flag_R, flag_l;
int ls(const char *path) {
int status = 0;
DIR *d = opendir(path);
if (d == NULL) {
fprintf(stderr, "cannot open directory %s: %s\n",
path, strerror(errno));
return 1;
}
struct stat fileStat;
char name[1024]; //to store the path name of each file in directory
int has_dirs = 0;
struct dirent *dir;
while ((dir = readdir(d)) != NULL) {
if (*dir->d_name == '.' && !flag_a)
continue;
if (flag_i flag_l flag_R) {
snprintf(name, sizeof name, "%s/%s", path, dir->d_name);
if (stat(name, &fileStat) != 0) {
fprintf(stderr, "cannot stat file %s: %s\n",
name, strerror(errno));
status = 1;
continue;
}
if (flag_i) {
printf("%lu\t", (unsigned long)fileStat.st_ino);
}
if (flag_l) {
printf("%lu\t", (unsigned long)fileStat.st_size);
/* should also output mode and date */
}
printf("%s\n", dir->d_name);
if (flag_R && S_ISDIR(fileStat.st_mode)) {
has_dirs ;
}
} else {
printf("%s\n", dir->d_name);
}
}
if (flag_R && has_dirs) {
rewinddir(d);
while ((dir = readdir(d)) != NULL) {
if (*dir->d_name == '.') {
if (!flag_a)
continue;
if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."))
continue;
}
snprintf(name, sizeof name, "%s/%s", path, dir->d_name);
if (stat(name, &fileStat) != 0) {
fprintf(stderr, "cannot stat file %s: %s\n",
name, strerror(errno));
status = 1;
continue;
}
if (S_ISDIR(fileStat.st_mode)) {
printf("\n%s:\n", name);
status |= ls(name);
}
}
}
closedir(d);
return status;
}
int process(int args, char *argsList[]) {
int status = 0;
int anyFiles = 0;
for (int j = 1; j < args; j ) {
char *arg = argsList[j];
if (*arg == '-') {
for (int k = 1; arg[k] != '\0'; k ) {
switch (arg[k]) {
case 'a': flag_a = 1; continue;
case 'i': flag_i = 1; continue;
case 'R': flag_R = 1; continue;
case 'l': flag_l = 1; continue;
default: fprintf(stderr, "option not supported: -%c\n", arg[k]);
return 1;
}
}
} else {
status |= ls(arg);
anyFiles = 1;
}
}
if (anyFiles == 0)
status |= ls(".");
return status;
}
int main(int argc, char *argv[]) {
return process(argc, argv);
}
CodePudding user response:
Refer to the question, Correct use of Stat on C After declaring fileStat, you need to allocate memory for it: add the line
struct stat *fileStat;
fileStat = malloc(sizeof(struct stat));
....
....
free(fileStat);