Counting number of files and folders in directory - c

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

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

rename() only works for directory that program is running in?

I am trying to rename a bunch of files in a user specified directory, but it only seems to be working when the user specifies the directory that the program is running from. For example, when running from the command line:
./a.out . "NewName.txt" will work, while
./a.out .. "NewName.txt" will not work. Is there a reason for this? It's on Linux, by the way.
int main(int argc, char** argv){
char* dirpath = argv[1];
char* newName = argv[2];
DIR *d;
struct dirent *dir;
d = opendir(dirpath);
if (d){
while ((dir = readdir(d)) != NULL){
char* filename = dir->d_name;
if (rename(filename,newName) == 0){
printf("Renaming %s -> %s\n",filename,newName);
} else {
printf("Could not rename %s\n",filename);
}
}
}
closedir(d);
}
I have also tried (while running the program from outside of Desktop):
if (rename("~/Desktop/test.txt","~/Desktop/test2.txt") == 0){
printf("Renaming %s -> %s\n",filename,newName);
} else {
printf("Could not rename %s\n",filename);
}
and it still fails.
While readdir() is reading file names from the other directory, your program's current directory is still in a different location. Unless you prefix the source file name with the path to the directory (and the destination file name too) you're trying to rename non-existent files in the current directory, in general.
In pseudo-code:
dir = opendir(remote_directory)
foreach name from dir
rename "remote_directory/name" to "remote_directory/othername"
end for
Note that the pseudo-code works if 'remote_directory' happens to be ., the current directory; you don't need to special-case that code.
I believe that your main problem is that the result from readdir is just the filename. It doesn't include the directory. You need to paste the directory name and the filename from dir->d_name together in your program.
From the documentation:
The old argument points to the pathname of the file to be renamed.
The new argument points to the new pathname of the file.
If the new argument does not resolve to an existing directory entry for a
file of type directory and the new argument contains at least one non-<slash>
character and ends with one or more trailing <slash> characters after all symbolic
links have been processed, rename() shall fail
Looks like you're not referring to an existing element when you use any path other than '.', which is likely why it's failing.
Check the specific errno value to see why.

listing the files in a directory and delete them in C/C++

In a "C" code I would like to list all the files in a directory and delete the oldest one. How do I do that?
Can I use popen for that or we have any other solutions??
Thanks,
From the tag, I assume that you want to do this in a POSIX compliant system. In this case a code snippet for listing files in a folder would look like this:
#include <dirent.h>
#include <sys/types.h>
#include <stdio.h>
DIR* dp;
struct dirent* ep;
char* path = "/home/mydir";
dp = opendir(path);
if (dp != NULL)
{
printf("Dir content:\n");
while(ep = readdir(dp))
{
printf("%s\n", ep->d_name);
}
}
closedir(dp);
To check file creation or modification time, use stat (man 2 stat). For removing file, just use function remove(const char* path)
On Linux (and indeed, any POSIX system), you read a directory by calling opendir() / readdir() / closedir(). You can then call stat() on each directory entry to determine if it's a file, and what its access / modification / status-change times are.
If your definition of "oldest" depends on the creation time of the file, then you're on shaky ground - traditionally UNIX didn't record the creation time. On Linux, some recent filesystems do provide it through the extended attribute file.crtime (which you access using getxattr() from sys/xattr.h), but you'll have to handle the common case where that attribute doesn't exist.
You can scan the directory using readdir and opendir
or, if you want to traverse (recursively) a file hierarchy fts or nftw. Don't forget to ignore the entries for the current directory "." and the parent ".." one. You probably want to use the stat syscall too.

How to check if a dir exists?

How would I go about checking if a FILE is a directory? I have
if (file == NULL) {
fprintf(stderr, "%s: No such file\n", argv[1]);
return 1;
}
and that checks if the node exists at all, but I want to know if it's a dir or a file.
Filenames themselves don't carry any information about whether they exist or not or whether they are a directory with them - someone could change it out from under you. What you want to do is run a library call, namely stat(2), which reports back if the file exists or not and what it is. From the man page,
[ENOENT] The named file does not exist.
So there's an error code which reports (in errno) that the file does not exist. If it does exist, you may wish to check that it is actually a directory and not a regular file. You do this by checking st_mode in the struct returned:
The status information word st_mode has the following bits:
...
#define S_IFDIR 0040000 /* directory */
Check the manpage for further information.
struct stat st;
if(stat("/directory",&st) == 0)
printf(" /directory is present\n");
use opendir to try and open it as a directory. If that returns a null pointer it's clearly not a directory :)
Here's a snippet for your question:
#include <stdio.h>
#include <dirent.h>
...
DIR *dip;
if ((dip = opendir(argv[1])) == NULL)
{
printf("not a directory");
}
else closedir(dip);
If you're using *nix, stat().

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