stat function returns empty struct [duplicate] - c

This question already has an answer here:
Issue with S_ISDIR() result in C (maybe because stat() isn't setting its struct properly?)
(1 answer)
Closed 7 years ago.
studentsDir = opendir(lineValues);
while ((entry = readdir(studentsDir)) != NULL) {
stat(path, &dirData);
if (S_ISDIR(dirData.st_mode) && (entry->d_name[0] != '.')
&& (entry->d_name[1] != '.')) {
Im searching in a directory for folders. The problem is that stat returns dirdata with empty values for most of the folders

the code needs:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
the prototype for the function is:
int stat(const char *path, struct stat *buf);
so you also need:
struct stat dirData;
and the *path variable needs to be the actual path (can be relative or absolute), with the file name, to the target file.
Nothing in the posted code indicates any of the above has been included in the code.
and 'entry' is a pointer to a 'struct dirent' where the current file/directory name needs to be appended to 'path' before stat() uses it.
'entry' needs to be checked to assure it is not NULL before using it.
the struct dirent contains the d_name[256] field that will contain
the name of a current file (or directory) (but not the whole path)
so each time the code steps into a (sub) directory,
(which the posted code is not doing)
then the path needs to have the string from d_name[]
appended to the path string

Related

How to get the details of the files in the current working directory in C?

I'm trying to display the files of my current working directory in C, however, I also need to include the details of these files such as the date created and their file sizes.
Is there a function that does this in C?
So far here is my code:
int main(
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d) {
while ((dir = readdir(d)) != NULL) {
printf("%s\n", dir->d_name);
}
closedir(d);
}
}
File information is handled not by dirent functions, but by stat().
Note stat() requires a pathname; combining the directory path with the file name from the dirent structure (using strncat) will give you the necessary argument. In this particular case, since file information is only needed for files in the current working directory, the path can be left off and the file name can be used directly as the argument (stat() will then look for the file in the current directory).
The stat struct has the various entries you're interested in, such as st_size for file size and st_ctime for change time (note the latter is a UNIX time, which you'll likely want to convert to a readable string).

Counting number of files and folders in directory

How can I count the number of files and folders in a directory using C. I have no clue. I couldn't write single line code.
I do not care about current directory . and the parent directory..
I have to give a pathname for example “C:/Users/me/Documents/Example” while I'm running the program.
And I need a output like " There are 2 folders and 4 files in this directory".
for linux os
The code below shall list all files and sub-directories in dir_path (for current directory use dir_path = ".").
DESCRIPTION
This description qouted from man7.org link
struct dirent *readdir(DIR *dirp);
readdir()
The readdir() function returns a pointer to a dirent structure
representing the next directory entry in the directory stream pointed
to by dirp. It returns NULL on reaching the end of the directory
stream or if an error occurred. for further details go to the link above for man7.org.
#include <stdio.h>
#include <dirent.h>
int main(void)
{
/* de is Pointer for directory entry */
struct dirent *de;
const char* dir_path = "C:/Users/me/Documents/Example";
/*opendir() returns a pointer of DIR type.*/
DIR *dr = opendir(dir_path);
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;
}
for windows os
for windows use the header file: fileapi.h see microsoft docs here: fileapi.h
this question answered before in SO in the link below using the FindFirstFile, FindNextFile and FindClose functions.
please review the answer in the link: list directory in windows in C programming language

How to determine files and directories in parent/other directories

I found the answer to another question here to be very helpful.
There seems to be a limitation of the sys/stat.h library as when I tried to look in other directories everything was seen as a directory.
I was wondering if anyone knew of another system function or why it sees anything outside the current working directory as only a directory.
I appreciate any help anyone has to offer as this is perplexing me and various searches have turned up no help.
The code I made to test this is:
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
int main(void) {
int status;
struct stat st_buf;
struct dirent *dirInfo;
DIR *selDir;
selDir = opendir("../");
// ^ or wherever you want to look
while ((dirInfo = readdir(selDir))) {
status = stat (dirInfo->d_name, &st_buf);
if (S_ISREG (st_buf.st_mode)) {
printf ("%s is a regular file.\n", dirInfo->d_name);
}
if (S_ISDIR (st_buf.st_mode)) {
printf ("%s is a directory.\n", dirInfo->d_name);
}
}
return 0;
}
You need to check the status of the stat call; it is failing.
The trouble is that you're looking for a file the_file in the current directory when it is actually only found in ../the_file. The readdir() function gives you the name relative to the other directory, but stat() works w.r.t the current directory.
To make it work, you'd have to do the equivalent of:
char fullname[1024];
snprintf(fullname, sizeof(fullname), "%s/%s", "..", dirInfo->d_name);
if (stat(fullname, &st_buf) == 0)
...report on success...
else
...report on failure...
If you printed out stat, you'll notice there's an error (File not found).
This is because stat takes the path to the file, but you're just providing the file name.
You then call IS_REG on garbage values.
So, suppose you have a file ../test.txt
You call stat on test.txt...That isn't in directory ./test.txt, but you still print out the results from IS_REG.

readdir looping more times than number of files present in the directory [duplicate]

This question already has answers here:
List regular files only (without directory) problem
(2 answers)
Closed 10 years ago.
My goal is to count the number of files in a directory. After searching around, I found a piece of code which iterates over each file in a directory. But the issue is that it's looping extra times, 2 times extra to be more precise.
So for
int main(void)
{
DIR *d;
struct dirent *dir;
char *ary[10000];
char fullpath[256];
d = opendir("D:\\frames\\");
if (d)
{
int count = 1;
while ((dir = readdir(d)) != NULL)
{
snprintf(fullpath, sizeof(fullpath), "%s%d%s", "D:\\frames\\", count, ".jpg");
int fs = fsize(fullpath);
printf("%s\t%d\n", fullpath, fs); // using this line just for output purposes
count++;
}
closedir(d);
}
getchar();
return(0);
}
My folder contains 500 files, but the output is shown till 502
UPDATE
I modified the code to read as
struct stat buf;
if ( S_ISREG(buf.st_mode) ) // <-- I'm assuming this says "if it is a file"
{
snprintf(fullpath, sizeof(fullpath), "%s%d%s", "D:\\frames\\", count, ".jpg");
int fs = fsize(fullpath);
printf("%s\t%d\n", fullpath, fs);
}
But I'm getting storage size of "buf" isn't known. I also tried doing struct stat buf[100], but that didn't help either.
As pointed out in comments, you're also getting the two directories named . and .., which skews your count.
In Linux, you can use the d_type field of the struct dirent to filter them out, but the documentation says:
The only fields in the dirent structure that are mandated by POSIX.1 are: d_name[], of unspecified size, with at most NAME_MAX characters preceding the terminating null byte; and (as an XSI extension) d_ino. The other fields are unstandardized, and not present on all systems; see NOTES below for some further details.
So, assuming you're on Windows you probably don't have d_type. Then you can use some other call instead, for instance stat(). You can of course filter out based on name too, but if you want to skip directories anyway that is a more robust and general solution.
You need to call _stat()/stat() on the file name you want info for.
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WINDOWS
# define STAT _stat
#else
# define STAT stat
#endif
...
char * filename = ... /* let it point to some file's name */
struct STAT buffer = {0};
if (STAT(filename, &buffer)
... /* error */
else
{
if (S_ISREG(buffer.st_mode))
{
... /* getting here, means `filename` referrs to a ordinary file */
}
}

Delete files while reading directory with readdir()

My code is something like this:
DIR* pDir = opendir("/path/to/my/dir");
struct dirent pFile = NULL;
while ((pFile = readdir())) {
// Check if it is a .zip file
if (subrstr(pFile->d_name,".zip") {
// It is a .zip file, delete it, and the matching log file
char zipname[200];
snprintf(zipname, sizeof(zipname), "/path/to/my/dir/%s", pFile->d_name);
unlink(zipname);
char* logname = subsstr(zipname, 0, strlen(pFile->d_name)-4); // Strip of .zip
logname = appendstring(&logname, ".log"); // Append .log
unlink(logname);
}
closedir(pDir);
(this code is untested and purely an example)
The point is: Is it allowed to delete a file in a directory while looping through the directory with readdir()?
Or will readdir() still find the deleted .log file?
Quote from POSIX readdir:
If a file is removed from or added to
the directory after the most recent
call to opendir() or rewinddir(),
whether a subsequent call to readdir()
returns an entry for that file is
unspecified.
So, my guess is ... it depends.
It depends on the OS, on the time of day, on the relative order of the files added/deleted, ...
And, as a further point, between the time the readdir() function returns and you try to unlink() the file, some other process could have deleted that file and your unlink() fails.
Edit
I tested with this program:
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
struct dirent *de;
DIR *dd;
/* create files `one.zip` and `one.log` before entering the readdir() loop */
printf("creating `one.log` and `one.zip`\n");
system("touch one.log"); /* assume it worked */
system("touch one.zip"); /* assume it worked */
dd = opendir("."); /* assume it worked */
while ((de = readdir(dd)) != NULL) {
printf("found %s\n", de->d_name);
if (strstr(de->d_name, ".zip")) {
char logname[1200];
size_t i;
if (*de->d_name == 'o') {
/* create `two.zip` and `two.log` when the program finds `one.zip` */
printf("creating `two.zip` and `two.log`\n");
system("touch two.zip"); /* assume it worked */
system("touch two.log"); /* assume it worked */
}
printf("unlinking %s\n", de->d_name);
if (unlink(de->d_name)) perror("unlink");
strcpy(logname, de->d_name);
i = strlen(logname);
logname[i-3] = 'l';
logname[i-2] = 'o';
logname[i-1] = 'g';
printf("unlinking %s\n", logname);
if (unlink(logname)) perror("unlink");
}
}
closedir(dd); /* assume it worked */
return 0;
}
On my computer, readdir() finds deleted files and does not find files created between opendir() and readdir(). But it may be different on another computer; it may be different on my computer if I compile with different options; it may be different if I upgrade the kernel; ...
I'm testing my new Linux reference book. The Linux Programming Interface by Michael Kerrisk and it says the following:
SUSv3 explicitly notes that it is unspecified whether readdir() will return a filename that has been added to or removed from since the last since the last call to opendir() or rewinddir(). All filenames that have been neither added nor removed since the last such call are guaranteed to be returned.
I think that what is unspecified is what happens to dirents not yet scanned. Once an entry has been returned, it is 100% guaranteed that it will not be returned anymore whether or not you unlink the current dirent.
Also note the guarantee provided by the second sentence. Since you are leaving alone the other files and only unlinking the current entry for the zip file, SUSv3 guarantees that all the other files will be returned. What happens to the log file is undefined. it may or may not be returned by readdir() but in your case, it shouldn't be harmful.
The reason why I have explored the question it is to find an efficient way to close file descriptors in a child process before exec().
The suggested way in APUE from Stevens is to do the following:
int max_open = sysconf(_SC_OPEN_MAX);
for (int i = 0; i < max_open; ++i)
close(i);
but I am thinking using code similar to what is found in the OP to scan /dev/fd/ directory to know exactly which fds I need to close. (Special note to myself, skip over dirfd contained in the DIR handle.)
I found the following page describe the solution of this problem.
https://support.apple.com/kb/TA21420

Resources