I'm stuck on unix system programming and am beginner about it. Assume that there is a directory which is X. There are a file(text1.txt) and another directory which is Y in X. Last, there are two files(text2.noExtension) and text3.noExtension) and another directory which is Z in Y. My goal is that read files and enter directories until there is no directory. Candidly, I really don't have any idea how to go on.
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
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 want to list all directories and sub-directories, try something recursive. E.g.:
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void listOfDir(char * dirname, int level)
{
struct dirent *direntp;
DIR *dirp;
char *subdirname;
if ((dirp = opendir(dirname)) == NULL)
{
return;
}
while ((direntp = readdir(dirp)) != NULL)
{
if(strcmp(direntp->d_name, ".")==0 || strcmp(direntp->d_name, "..")==0)
continue; // skip current and parent directories
printf("%*c%s\n", level, '>', direntp->d_name);
if( direntp->d_type == 4)
{
// build child dir name and call listOfDir
subdirname = (char*)malloc(strlen(direntp->d_name) + strlen(dirname) + 2);
strcpy(subdirname, dirname);
strcat(subdirname, "/");
strcat(subdirname, direntp->d_name);
listOfDir(subdirname, level+1);
free(subdirname);
}
}
closedir(dirp);
}
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;
}
listOfDir(argv[1], 1);
return 0;
}
Expression like printf("%*c", level, '>') just make indents for elach level of nesting
Related
WHAT I HAVE TO DO
I have to find, starting from a directory, a file located in one of all directories that have as root the directory passed in input.
Something as shell command find.
INPUT/OUTPUT
Having this in input:
./myfind /home/claudio/Scrivania file.txt
I need something as this in output, absolute path and last modify date ecc:
/home/claudio/Scrivania/SistemiOperativi/file.txt Tue Mar 30 19:51:54 2021
MY CODE
It doesn't print anything.
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <errno.h>
#if !defined(NAME_MAX)
#define NAME_MAX 256
#endif
void find(const char* passed_dir_name, const char* passed_file_name) {
if (chdir(passed_dir_name) == -1) {
perror("FATAL ERROR CHANGING DIRECTORY");
exit(EXIT_FAILURE);
}
DIR* current_directory;
if ((current_directory = opendir(".")) == NULL) {
perror("FATAL ERROR OPENING CURRENT WORKING DIRECTORY");
exit(EXIT_FAILURE);
}
char* buf;
if ((buf = calloc(NAME_MAX, sizeof(char))) == NULL) {
perror("FATAL ERROR ALLOCATING MEMORY");
exit(EXIT_FAILURE);
}
struct dirent* dir;
while ((dir = readdir(current_directory)) != NULL) {
struct stat statbuf;
stat(dir->d_name, &statbuf);
if (S_ISDIR(statbuf.st_mode)) {
if (strncmp(dir->d_name, ".", 1) != 0 && strncmp(dir->d_name, "..", 2) != 0) {
find(dir->d_name, passed_file_name);
}
} else {
if (strncmp(dir->d_name, passed_file_name, strlen(passed_file_name) == 0)) {
if (getcwd(buf, NAME_MAX) == NULL) {
perror("FATAL ERROR");
exit(EXIT_FAILURE);
}
fprintf(stdout, "%s/%s %s", buf, dir->d_name, ctime(&statbuf.st_mtime));
}
}
}
if (closedir(current_directory) == -1) {
perror("FATAL ERROR CLOSING CURRENT WORKING DIRECTORY");
exit(EXIT_FAILURE);
}
chdir("..");
free(buf);
}
int main(int argc, char** argv) {
if (argc != 3) {
fprintf(stderr, "ERROR: RUn as ./myfind directory\n");
exit(EXIT_FAILURE);
}
const char* dir = argv[1];
const char* file = argv[2];
struct stat statbuf;
stat(dir, &statbuf);
if(!S_ISDIR(statbuf.st_mode)) {
fprintf(stderr, "FATAL ERROR: %s IS NOT A DIRECTORY\n", dir);
exit(EXIT_FAILURE);
}
find(dir, file);
exit(EXIT_SUCCESS);
}
Your parentheses are wrong in:
if (strncmp(dir->d_name, passed_file_name, strlen(passed_file_name) == 0))
You need to write:
if (strncmp(dir->d_name, passed_file_name, strlen(passed_file_name)) == 0)
Since strncmp(x, y, 0) will always return 0, the condition is never being met.
But note that there's no point in using strncmp here at all. strncmp is only needed if you don't know that one of your entries is a null terminated string. You have a guarante that d_name is null-terminated, and if passed_file_name is not, then strlen is going to be problematic. You might as well just write strcmp(dir->d_name, passed_file_name).
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);
}
My C code for recursively listing directories and files get executed multiple times. I am not sure how to fix it and why it keeps happening... It is not infinite its just like 10 times shows the current directory.
void printdir(char *dir, int depth)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
int spaces = depth;
dp = opendir(dir);
while((entry = readdir(dp))) {
lstat(entry->d_name,&statbuf);
if(S_ISDIR(statbuf.st_mode)) {
/* Found a directory, but ignore . and .. */
if(strcmp(".",entry->d_name) == 0 ||
strcmp("..",entry->d_name) == 0)
continue;
printf("%*s%s/\n",spaces,"",entry->d_name);
/* Recurse at a new indent level */
printdir(entry->d_name,depth+1);
}
else printf("%*s%s\n",spaces,"",entry->d_name);
}
closedir(dp);
}
int print_file(char *file, char *dir, struct stat buf, int showinode, int showlong, int showRec)
{
if (showinode)
printf("%lld ", buf.st_ino);
if (showlong)
print_long(file, dir, buf);
if (showRec)
printdir(dir, 0);
else
printf("%s\n", file);
return 0;
}
Here's a recursive function that lists the directories it comes across, using openat(), fdopendir(), fstatat() to avoid string-operations on paths (and, possibly, race-conditions on the directory-tree):
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
int sanerecursivedirsearch(int dirfd)
{
DIR *curdir = fdopendir(dirfd);
if (!curdir)
{
perror("fdopendir()");
close(dirfd);
return -1;
}
struct dirent *direp;
while (!!(direp = readdir(curdir)))
{
if (!strcmp(direp->d_name, "..") || !strcmp(direp->d_name, "."))
continue;
struct stat statbuf;
fstatat(dirfd, direp->d_name, &statbuf, 0);
if (S_ISDIR(statbuf.st_mode))
{
int newfd = openat(dirfd, direp->d_name,
O_RDONLY | O_DIRECTORY);
if (newfd == -1)
{
perror("openat()");
continue;
}
printf("directory found:\t%s\n", direp->d_name);
sanerecursivedirsearch(newfd);
}
}
closedir(curdir);
return 0;
}
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "insufficient command-line arguments");
exit(EXIT_FAILURE);
}
int fd = openat(AT_FDCWD, argv[1],
O_RDONLY | O_DIRECTORY);
if (fd == -1)
{
perror("openat()");
exit(EXIT_FAILURE);
}
sanerecursivedirsearch(fd);
return 0;
}
I am trying to get the number of directories in a folder except the files in but I cannot get the correct result. Somebody help me to solve this problem? Especially what should I sent to the isDirectory() function?
int listFilesIndir(char *currDir)
{
struct dirent *direntp;
DIR *dirp;
int x ,y =0 ;
if ((dirp = opendir(currDir)) == NULL)
{
perror ("Failed to open directory");
return 1;
}
while ((direntp = readdir(dirp)) != NULL)
{
printf("%s\n", direntp->d_name);
x= isDirectory(dirp);
if(x != 0)
y++;
}
printf("direc Num : %d\n",y );
while ((closedir(dirp) == -1) && (errno == EINTR)) ;
return 0;
}
int isDirectory(char *path)
{
struct stat statbuf;
if (stat(path, &statbuf) == -1)
return 0;
else
return S_ISDIR(statbuf.st_mode);
}
You're sending a directory stream to the function, and treating it like a path.
Linux and some other Unix systems include a way to get this info directly:
while ((direntp = readdir(dirp)) != NULL)
{
printf("%s\n", direntp->d_name);
if (direntp->d_type == DT_DIR)
y++;
}
Otherwise, make sure you send the right details to the function, i.e.
x= isDirectory(direntp->d_name);
The call for your function is wrong.
x= isDirectory(dirp);
While the prototype of function is:
int isDirectory(char *path)
It need a string as parameter, but you give it a "DIR *dirp;". I changed the code as:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int listFilesIndir(char *currDir)
{
struct dirent *direntp;
DIR *dirp;
int x ,y =0 ;
if ((dirp = opendir(currDir)) == NULL)
{
perror ("Failed to open directory");
return 1;
}
while ((direntp = readdir(dirp)) != NULL)
{
printf("%s\n", direntp->d_name);
if(direntp->d_type == DT_DIR)
y++;
}
printf("direc Num : %d\n",y );
while ((closedir(dirp) == -1) && (errno == EINTR)) ;
return 0;
}
int main(int argc, char **argv){
if(argc == 2){
// Check whether the argv[1] is a directory firstly.
listFilesIndir(argv[1]);
}
else{
printf("Usage: %s directory", argv[0]);
}
return 0;
}
I tested it on my Linux server. And it works well. SO #teppic is right. But pay attention, in the code, the number of directory includes two specific ".." (parent directory) and "." (current directory). If you do not want to include it, you could change:
printf("direc Num : %d\n",y );
into:
printf("direc Num : %d\n",y-2 );
Hope it helps!
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");