This question already has answers here:
stat() error 'No such file or directory' when file name is returned by readdir()
(2 answers)
Closed 2 years ago.
I wrote a program that print the directory name or file name. It's easy but I got something trouble.
It couldn't distinguish directory and file type. I know and I used stat.st_mode to finish it. But something is wrong:
When I use gdb to check the st_mode value, I found it was 0, except "." and "..", so here is the question: why st_mode is 0?
and that is my code:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
int main(void)
{
DIR *pDir = opendir("MyDirectory");
struct dirent *pDirent;
struct stat vStat;
if (pDir == NULL)
{
printf("Can't open the directory \"MyDirectory\"");
exit(1);
}
while ((pDirent = readdir(pDir)) != NULL)
{
stat(pDirent->d_name, &vStat);
if (S_ISDIR(vStat.st_mode))
printf("Directory: %s\n", pDirent->d_name);
else
printf("File: %s\n", pDirent->d_name);
}
closedir(pDir);
return 0;
}
Classic readdir mistake: pDirent->d_name is the name of the directory entry, not a path to the file. It's "1", "4-5.c", etc. So your stat calls are looking for a file with that name in the current directory, not under MyDirectory.
Check the return value of stat. You'll see that it's ENOENT — except for . and .., which exist in the current directory as well. When stat fails, the content of the stat structure is undefined.
If you're calling opendir in a directory other than ., then to do pretty much anything useful with the returned names, you need to build a full path. Copy the path you passed to opendir to a buffer with enough room for a slash and file name in addition, and copy each file name to that buffer. Proof-of-concept code (error checking omitted, etc.):
char *directory = "MyDirectory";
size_t directory_length = strlen(directory);
char *path = malloc(directory_length + 1 + NAME_MAX);
strcpy(path, directory);
path[directory_length] = '/';
while ((pDirent = readdir(pDir)) != NULL) {
strcpy(path + directory_length + 1, pDirent->d_name);
if (stat(path, &vStat) == -1) {
perror(path);
continue;
}
…
}
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I'm working on a c programming mimicking UNIX shell and I wonder if there's a way to find an executable file(command file) in a specific directory
By executable file, I am assuming you are looking for any file that is not a directory that has the execution flag set.
One way you could go about achieving your goal is to:
Get a directory stream corresponding to the directory by using the opendir.
Iterate through the directory stream using readdir .
At each iteration, find the absolute path of the file, which can be computed by concatenating the directory path and file name (can be obtained by accessing the d_name of the dirent struct), pass this path to stat, and use bit mask specified in inode documentation to check for the execution permission.
Code:
#include <stdio.h>
#include <sys/types.h> //opendir, stat
#include <dirent.h> //opendir
#include <errno.h>
#include <string.h>
#include <sys/stat.h> //stat
#include <stdlib.h> //free, malloc
int main(int argc, char** argv) {
if (argc < 2) {
fprintf(stderr, "Usage: ./a.out path_to_a_directory \n");
return 1;
}
char* path_to_directory = argv[1];
int path_length = strlen(path_to_directory);
int modified = 0;
//Modify path so that a dash is at the end.
if (path_to_directory[path_length - 1] != '/') {
char* modified_path = (char *) malloc(sizeof(char) * (strlen(path_to_directory) + 2));
//Copies the null character as well.
strcpy(modified_path, path_to_directory);
modified_path[path_length] = '/';
modified_path[path_length + 1] = '\0';
path_to_directory = modified_path;
//Set flag to true so that the dynamically allocated memory is freed later.
modified = 1;
}
//Get the directory stream corresponding to the directory.
DIR* in_directory_stream = opendir(path_to_directory);
if (in_directory_stream == NULL) {
fprintf(stderr, "Error: the specified directory cannot be found or opened. \n", errno);
if (modified) free(path_to_directory);
return 1;
}
dirent* entry = NULL;
printf("Files that are executable by at least one of the permission classes (owner, group, others) are: \n");
while ((entry = readdir(in_directory_stream)) != NULL) {
//All directories contain . and .., which corresponds to current and parent directory respectively,
//in unix systems. Since we are looking for only executable files, we can ignore them.
if (!strcmp(".", entry->d_name)) {
continue;
}
if (!strcmp("..", entry->d_name)) {
continue;
}
//Get file information.
struct stat entry_info;
/* Create the absolute path of the entry.
* Without it, as mentioned by Shawn below,
* stat will look for a file with the entry's name in current working directory
* instead of the specified directory.
*/
char* entry_absolute_path = (char *) malloc(sizeof(char) * (strlen(path_to_directory)
+ strlen(entry->d_name) + 1));
strcat(entry_absolute_path, path_to_directory);
strcat(entry_absolute_path, entry->d_name);
if (stat(entry_absolute_path, &entry_info) == -1) {
fprintf(stderr, "Error in obtaining file information about %s\n", entry->d_name);
} else {
// Check if the file is not a directory and
// is executable by one of the permission classes (owner, group, others).
if (((entry_info.st_mode & S_IFMT) != S_IFDIR) &&
((entry_info.st_mode & S_IXUSR) || (entry_info.st_mode & S_IXGRP)
|| (entry_info.st_mode & S_IXOTH))) {
printf("%s\n", entry->d_name);
}
}
free(entry_absolute_path);
}
//Close directory stream.
closedir(in_directory_stream);
if (modified) free(path_to_directory);
return 0;
}
EDIT: Corrected the program to pass the absolute path of the file to stat. Prior to this edit, the stat was given only the file name resulting in the program searching only the current working directory. Error identified by Shawn
I'm trying to open a directory and access all it's files and sub-directories and also the sub-directories files and so on(recursion).
I know i can access the files and the sub-directories by using the opendir call, but i was wondering if there is a way to do so by using the open() system call(and how?), or does the open system call only refers to files?
#include <stdio.h>
#include <dirent.h>
int main(void)
{
struct dirent *de; // Pointer for directory entry
// opendir() returns a pointer of DIR type.
DIR *dr = opendir(".");
if (dr == NULL) // opendir returns NULL if couldn't open directory
{
printf("Could not open current directory" );
return 0;
}
while ((de = readdir(dr)) != NULL)
printf("%s\n", de->d_name);
closedir(dr);
return 0;
}
the following code gives me the names of the files in my directory and the names of the sub-folders, but how can I differ a file from a sub-folder so i can use recursion to access the files in the sub-folder?
any help would be appreciated
you will need to have the struct stat and the macro S_ISDIR from the , if you want to check if it is a file you can use the same method but with the macro S_ISREG.
Also when you use structures is better to allocate memory before using them.
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
int main(void)
{
struct dirent *de = malloc(sizeof(struct dirent)); // Pointer for directory entry
struct stat *info; = malloc(sizeof(struct stat));
// opendir() returns a pointer of DIR type.
DIR *dr = opendir(".");
if (dr == NULL) // opendir returns NULL if couldn't open directory
{
printf("Could not open current directory" );
return 0;
}
while ((de = readdir(dr)) != NULL)
{
if((S_ISDIR(info->st_mode)
printf("Directory:%s \n", de->d_name);
else printf("File:"%s \n,de->d_name);
}
closedir(dr);
return 0;
}
I was wondering if someone could tell me what I'm doing wrong. This code is supposed to walk though all the directories and files and print them out exactly the same way the UNIX utility FIND does. But for some reason I cant get chdir to change the working directory. I'm trying to limit the number of file descriptors im using.
MAIN
#include <stdio.h>
#include "sfind.h"
#include <unistd.h>
#include <dirent.h>
int main(int argv, char *argc[]){
char cwd[1024]; /* current working directory limit*/
char *path = NULL;
DIR *dp = NULL;
if (getcwd(cwd, sizeof(cwd)) != NULL){ /*allow us to grab the current working directory*/
fprintf(stdout, "Current working dir: %s\n", cwd);
}
else{
perror("getcwd() error");
}
dp = opendir(cwd);
path = ".";
directoryList(dp,path);
return 0;
}
Directory Method Definition
#include <stdio.h>
#include <stdlib.h>
#include "sfind.h"
#include <unistd.h>
#include <limits.h>
#include <dirent.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
void directoryList(DIR *dp, char *path){
char newPath[PATH_MAX] = {0};/*To store new path*/
struct dirent *element; /*get file name*/
struct stat statbuf;/*determine type of file*/
int status = 0; /*My base case should be once the directory I'm in runs outs out of files I should return;*/
if(dp == NULL){
fprintf(stderr,"FILE DID NOT OPEN!");
exit(-1);
}
/*change the current file directory even if its the first one*/
if((status = chdir(path)) == -1){
printf("ERROOR!");
}/*change the current working directory whether that the same on or not*/
while((element = readdir(dp)) != NULL) /*while current file directory pointer is not equal to zero*/{
/* from here we only have two cases once were reading from the directory either is a file or directory!*/
/*using lstat*/
lstat(element->d_name,&statbuf);
if((S_ISDIR(statbuf.st_mode))) /*is of type directory*/{
if((strcmp(".",element->d_name) == 0) || (strcmp("..",element->d_name) == 0))
continue;
/*create new directory name*/
newPath[0] = '\0';
strcat(newPath,path);/* this will give us the "."*/
strcat(newPath,"/");
strcat(newPath,element->d_name);
printf("%s\n", newPath);
directoryList(dp,newPath); /*recursion*/
file*/
}
else /*Its a file!*/{
printf("%s/%s\n",path,element->d_name);
}
}
}
The issue seems to be with the call to readdir(dp)...
Even though you change the current working directory, you don't update the dp pointer to open the new folder.
Here's a poor-man's working example (I wouldn't do it this way, but it works for small trees).
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
void directoryList(DIR *dp, char *path) {
char newPath[PATH_MAX] = {0}; /*To store new path*/
struct dirent *element; /*get file name*/
struct stat statbuf; /*determine type of file*/
int status = 0; /*My base case should be once the directory I'm in runs outs
out of files I should return;*/
DIR *dp_tmp;
if (dp == NULL) {
fprintf(stderr, "FILE DID NOT OPEN!");
exit(-1);
}
while ((element = readdir(dp)) !=
NULL) /*while current file directory pointer is not equal to zero*/ {
/* from here we only have two cases once were reading from the directory
* either is a file or directory!*/
/*using lstat*/
lstat(element->d_name, &statbuf);
if ((S_ISDIR(statbuf.st_mode))) /*is of type directory*/ {
if ((strcmp(".", element->d_name) == 0) ||
(strcmp("..", element->d_name) == 0))
continue;
/*create new directory name*/
newPath[0] = '\0';
strcat(newPath, path); /* this will give us the "."*/
strcat(newPath, "/");
strcat(newPath, element->d_name);
printf("%s\n", newPath);
if ((dp_tmp = opendir(newPath)) == NULL) {
perror("hmm?! ");
exit(1);
}
directoryList(dp_tmp, newPath); /*recursion*/
} else /*Its a file!*/ {
printf("* %s/%s\n", path, element->d_name);
}
}
closedir(dp);
}
int main(void) {
char cwd[1024]; /* current working directory limit*/
char *path = NULL;
DIR *dp = NULL;
if (getcwd(cwd, sizeof(cwd)) !=
NULL) { /*allow us to grab the current working directory*/
fprintf(stdout, "Current working dir: %s\n", cwd);
} else {
perror("getcwd() error");
}
dp = opendir(cwd);
path = ".";
directoryList(dp, path);
return 0;
}
EDIT:
To answer the question in the comment...
Open directories (that should be closed using closedir) are different (and quite unrelated) to the current working directory.
The current working directory is mostly used to resolve the path to any file/folder you're referencing.
Open directory pointers (DIR *) are just pointers to data in the memory. That data relates to a specific directory and you can open a number of directories at the same time.
EDIT2:
A few people in the comments recommended nftw (file tree walk) which is a great alternative to doing it yourself.
If this isn't a learning project, I would recommend it's use.
However, note that POSIX.1-2008 marks ftw as obsolete, so make sure to use the nftw flavor.
Is your goal to learn to implement this yourself, or do you just want results? Because you should take a look at fts.h if you want some very powerful stuff to implement something like find.
This question already has answers here:
stat() error 'No such file or directory' when file name is returned by readdir()
(2 answers)
Closed 2 years ago.
I wrote a program that print the directory name or file name. It's easy but I got something trouble.
It couldn't distinguish directory and file type. I know and I used stat.st_mode to finish it. But something is wrong:
When I use gdb to check the st_mode value, I found it was 0, except "." and "..", so here is the question: why st_mode is 0?
and that is my code:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
int main(void)
{
DIR *pDir = opendir("MyDirectory");
struct dirent *pDirent;
struct stat vStat;
if (pDir == NULL)
{
printf("Can't open the directory \"MyDirectory\"");
exit(1);
}
while ((pDirent = readdir(pDir)) != NULL)
{
stat(pDirent->d_name, &vStat);
if (S_ISDIR(vStat.st_mode))
printf("Directory: %s\n", pDirent->d_name);
else
printf("File: %s\n", pDirent->d_name);
}
closedir(pDir);
return 0;
}
Classic readdir mistake: pDirent->d_name is the name of the directory entry, not a path to the file. It's "1", "4-5.c", etc. So your stat calls are looking for a file with that name in the current directory, not under MyDirectory.
Check the return value of stat. You'll see that it's ENOENT — except for . and .., which exist in the current directory as well. When stat fails, the content of the stat structure is undefined.
If you're calling opendir in a directory other than ., then to do pretty much anything useful with the returned names, you need to build a full path. Copy the path you passed to opendir to a buffer with enough room for a slash and file name in addition, and copy each file name to that buffer. Proof-of-concept code (error checking omitted, etc.):
char *directory = "MyDirectory";
size_t directory_length = strlen(directory);
char *path = malloc(directory_length + 1 + NAME_MAX);
strcpy(path, directory);
path[directory_length] = '/';
while ((pDirent = readdir(pDir)) != NULL) {
strcpy(path + directory_length + 1, pDirent->d_name);
if (stat(path, &vStat) == -1) {
perror(path);
continue;
}
…
}
I have files something like this:
file1_a_etc.txt,
file1_b_etc.txt
file2_a_z.txt
file2_b_z.txt
I want to get the size of files with "a" i.e. file2_a_z.txt & file1_a_etc.txt
I have got a large number of files this way, so cant specify each name individually.
I am a beginner at C.
I know how to read the size of a single file. And I am working on windows.
#include <stdio.h>
#include <sys/stat.h> // For struct stat and stat()
struct stat attr;
void main()
{
if(stat("filename.txt", &attr) == 0)
{
float x;
x=(attr.st_size)/1048576.0; //1MB=1048576 bytes
printf("Filesize: %.2f MB", x);
}
else
{
// couldn't open the file
printf("Couldn't get file attributes...");
}
}
For Windows console there is function _findfirst. For first parameter put *a*.txt.
You need to iterate over the files in a given directory while searching for the substring in each file name.
This answer, under the section (Unix/Linux), specifies how to iterate over each filename while comparing for an exact match, you can modify the strcmp function call to strstr to look for a substring.
You could make an Array of strings to store all filenames. Then you can use the strchr function to test, if an 'a' or other character is the name. The use of this function is explained e.g at http://www.tutorialspoint.com/ansi_c/c_strchr.htm
Reading directories programmatically can be done with readdir.
You could do something like this:
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
static void lookup(const char *dir)
{
DIR *dirp;
struct dirent *dp;
if ((dirp = opendir(dir)) == NULL) {
perror("couldn't open '.'");
return;
}
do {
errno = 0;
if ((dp = readdir(dirp)) != NULL) {
if (strstr(dp->d_name, "_a_") == NULL)
continue;
(void) printf("found %s\n", dp->d_name);
// Add code to handle the file
}
} while (dp != NULL);
if (errno != 0)
perror("error reading directory");
(void) closedir(dirp);
return;
}
readdir is part of POSIX.1-2001, which is supported by unix/linux-type systems (including OS/X) but only some windows compilers. If you are programming in windows you may have to use another solution.