Is there a way that I can write a C function that gives me the file size for each file in a directory tree? (similar to the output of du -a)?
I don't have trouble getting any one of the file sizes, but I run into trouble recursing through directories within the main directory.
Is there a way that I can write a C function that gives me the file size for each file in a directory tree?
Yes, there is. You can use the <dirent.h> API to traverse a directory:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <dirent.h>
#include <sys/stat.h>
void recursive_dump(DIR *dir, const char *base)
{
struct dirent *ent;
for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) {
if (ent->d_name[0] == '.') {
continue;
}
char fname[PATH_MAX];
snprintf(fname, sizeof(fname), "%s/%s", base, ent->d_name);
struct stat st;
stat(fname, &st);
if (S_ISREG(st.st_mode)) {
printf("Size of %s is %llu\n", fname, st.st_size);
} else {
DIR *ch = opendir(fname);
if (ch != NULL) {
recursive_dump(ch, fname);
closedir(ch);
}
}
}
}
int main(int argc, char *argv[])
{
DIR *dir = opendir(argv[1]);
recursive_dump(dir, argv[1]);
closedir(dir);
return 0;
}
Yes. You need to use opendir and stat. See 'man 3 opendir', and 'man 2 stat'.
In a nutshell:
#include <dirent.h>
#include <sys/stat.h>
// etc...
void the_du_c_function() {
struct dirent direntBuf;
struct dirent* dirEntry = 0;
const char* theDir = ".";
DIR* dir = opendir(theDir);
while (readdir_r(dir,&direntBuf,dirEntry) && dirEntry) {
struct stat filestat;
char filename[1024];
snprintf(filename,sizeof(filename),"%s/%s",theDir,dirEntry.d_name);
stat(filename,&filestat);
fprintf(stdout,"%s - %u bytes\n",filename,filestat.st_size);
}
}
I just typed that code segment. I did not compile it, but that's the gist of it.
Related
I am trying to create a C program that will take a directory name as input in main and pass that open directory to the function. I have both components working seperately but cannot figure out how to pass the open directory to the function.Also I am struggling to find the oldest file.
Any help would be appreciated.
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, char* argv[]) {
DIR *d;
struct dirent *dir;
if(argc==2){
char* path = argv[1];
d = opendir(path);
if (d) {
while ((dir = readdir(d)) != NULL) {
printf("%s\n", dir->d_name);
}
closedir(d);
}
return(0);
}else{
printf("Wrong number of arguments");
return 1;
}
}
void getFileCreationTime(char *path) {
struct stat attr;
stat(path, &attr);
printf("Last modified time: %s", ctime(&attr.st_mtime));
}
int recent(DIR *d, char *file_name, char *dir_name)
{
struct dirent *entry;
int files = 0;
while( (entry=readdir(entry)) )
{
files++;
printf("File %3d: %s\n",files, entry->d_name);
printf("File %3d: %i\n",files, entry->d_reclen);
getFileCreationTime(".");
}
closedir(entry);
return(0);
}
I believe your issue is in the recent function where you are referencing the incorrect variable (entry instead of d). Following is a snippet of code with a revision of the variable entry.
int recent(DIR *d, char *file_name, char *dir_name)
{
struct dirent *entry;
int files = 0;
while( (entry=readdir(d)) ) /* Revised this line to utilize "d" */
{
files++;
printf("File %3d: %s\n",files, entry->d_name);
printf("File %3d: %i\n",files, entry->d_reclen);
getFileCreationTime(".");
}
closedir(d); /* Revised this line to utilize "d" */
return(0);
}
When I ran the code with this revision, I received the following terminal output.
#Dev:~/C_Programs/Console/Directory/bin/Release$ ./Directory /home/craig/C_Programs/Console/Directory
main.c
bin
obj
..
.
Directory.cbp
Give that a try and see if it meets the spirit of your project.
I am trying to open a directory and read the files and folders and add '/' at the end of if it is a folder. This is my current code.
#include <dirent.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int isDir(const char* target)
{
struct stat statbuf;
stat(target, &statbuf);
return S_ISDIR(statbuf.st_mode);
}
int main(int argc, char * argv[])
{
puts("");
struct stat sfile;
struct dirent *dp;
char * dir;
if(argc != 2)
dir = getenv("PWD"); //default
else
dir = argv[1];
DIR *dirp = opendir(dir);
dp = readdir(dirp);
while ( dp != NULL )
{
stat(dp->d_name,&sfile);
//opendir(dp->d_name);
printf("Size : %ld ",sfile.st_size);
if (isDir(dp->d_name))
{
printf("%s/\n",dp->d_name);
}
else
{
printf("%s \n",dp->d_name);
}
dp = readdir(dirp);
}
printf("\n");
closedir(dirp);
}
This works when I pass no arguments, ie, it is reading files and folders in the current working directory. When I pass a directory as argument, it fails to detect further folders in that as directories. Curiously, when tried using errno when I try to open using opendir(), all the contents are detected as directories.
int isDir2(const char* target)
{
opendir(target);
if(errno == ENOTDIR)
{
puts("Not directory");
return 0;
}
else
{
puts("Directory"); //only this is getting printed
return 1;
}
}
In either case, when both '.' and '..' are detected as directories but the rest as either directories or files depending on whether I am using opendir()'s errno or S_ISDIR.
Your isDir function isn't actually checking the return value from stat to validate that it succeeded. And since your statbuf variable is uninitialized, it's undefined behavior.
Better:
int isDir(const char* target)
{
struct stat statbuf = {0};
int result = 0;
if (stat(target, &statbuf) != -1)
{
result = S_ISDIR(statbuf.st_mode);
}
return result;
}
Further, you are invoking opendir without assigning it's return value:
while ( dp != NULL )
{
stat(dp->d_name,&sfile);
opendir(dp->d_name); // WHERE'S THE RESULT?
You need to rethink your logic in this while loop with regards to invoking opendir, readdir, and don't forget to invoke closedir.
So the issue was of working directories. When I checked what the stat function in the isDir() function was returning, it was returning -1. It wasn't able to find the file/folder because it's current working directory was not the folder it was given, rather the cwd where the program was run. What I did was change the directory to the given folder and it works.
The code now:
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
int isDir(const char* target)
{
struct stat statbuf = {0};
int result = 0;
int err = stat(target, &statbuf);
if (err != -1)
{
result = S_ISDIR(statbuf.st_mode);
}
return result;
}
int main(int argc, char * argv[])
{
puts("");
struct stat sfile;
struct dirent *dp;
char * dir;
if(argc != 2)
dir = getenv("PWD"); //default
else
dir = argv[1];
DIR *dirp = opendir(dir);
dp = readdir(dirp);
chdir(dir);
while ( dp != NULL )
{
stat(dp->d_name,&sfile);
printf("Size : %ld ",sfile.st_size);
if (isDir(dp->d_name))
{
printf("%s/\n",dp->d_name);
}
else
{
printf("%s \n",dp->d_name);
}
dp = readdir(dirp);
}
printf("\n");
closedir(dirp);
}
I cannot locate argv[i] in /bin (or /sbin), package only checks the directory it was run from. How do I check for argv[i] in /bin and /sbin?
I am currently working on my own package manager and I am writing it in pure C. I am currently writing the check to see if the package(s) passed (using argv[]) are already installed. The issue I am having is that when I run for the check my code only checks in the directory it was run from and I need it to check /bin and /sbin (I am going to handle to checking of /sbin) and I am trying to get it to check /bin, but am having zero luck. I cannot seem to get this to work, each time they just check the current working directory and I need them to check /bin. I can't figure this out, has anyone by chance done this in pure C before? Thank you
These are all the methods I have tried so far, none of them work...
using stat()
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <dirent.h>
int main(int argc, char *argv[])
{
struct dirent *de = calloc(1, sizeof(struct dirent));
DIR *dr = opendir("/bin"); /* directory to open */
short i;
struct stat *program = calloc(1, sizeof(struct stat));
if (dr == NULL) {
printf("directory could not be opened");
return 0;
}
while ((de = readdir(dr)) != NULL) {
for (i = 1; i < argc; i++) {
if (stat(argv[i], program) == 0) {
printf("found\n");
closedir(dr);
}
else {
printf("not found\n");
closedir(dr);
}
}
}
}
using realpath
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
int main(int argc, char *argv[])
{
struct dirent *de = calloc(1, sizeof(struct dirent));
DIR *dr = opendir("/bin"); /* directory to open */
short i;
char *res = realpath(argv[i], NULL);
if (dr == NULL) {
printf("directory could not be opened");
return 0;
}
while ((de = readdir(dr)) != NULL) {
for (i = 1; i < argc; i++) {
if (res == NULL) {
printf("found\n");
closedir(dr);
}
else {
printf("not found\n");
closedir(dr);
}
}
}
}
using strcmp
#include <stdio.h>
#include <string.h>
#include <dirent.h>
int main(int argc, char *argv[])
{
struct dirent *de;
DIR *dr = opendir("/bin"); /* directory to open */
short i;
struct stat program;
if (dr == NULL) {
printf("directory could not be opened");
return 0;
}
while ((de = readdir(dr)) != NULL) {
for (i = 1; i < argc; i++) {
if (strcmp(de->d_name, argv[i]) == 0) {
printf("found\n");
closedir(dr);
}
else {
printf("not found\n");
closedir(dr);
}
}
}
}
I am expecting them all to work as follows:
check echo
// it would go to /bin and find echo and then print
found
but when I run them they only check the current working directory, so for example:
check connection.c
// finds connection.c in the same directory
found
Solution
Naha! I found a way to do it! So using the function chdir() I can run stat() in /bin like so:
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
short i;
struct stat *program = calloc(1, sizeof(struct stat));
for (i = 1; i < argc; i++) {
chdir("/bin"); /* move to /bin */
if (chdir("/bin") != 0)
return 1;
if (stat(argv[i], program) == 0)
return 0;
else
return 1;
}
}
I tried simplifying your code just to output the contents of the folder, I get the contents of the '/bin' folder each time.
As a general rule of thumb, I like to get my code to compile without warnings, test it does what I expect it to, then work on the next bit.
The 'struct stat program' was causing the code not to compile, my guess is you were running an old version of the code.
#include <stdio.h>
#include <dirent.h>
#include <string.h>
int main(int argc, char *argv[])
{
struct dirent *de;
DIR *dr = opendir("/bin"); /* directory to open */
short i;
// struct stat program;
if (dr == NULL) {
printf("directory could not be opened");
return 0;
}
while ((de = readdir(dr)) != NULL) {
printf(de->d_name);
printf("\n");
}
}
If I understand you correctly then you’re confusing the semantics of opendir/readdir with those of chdir:
opendir does not change the working directory. This means that, e.g. realpath("echo", resolved_path) won’t resolve echo as /bin/echo but as $PWD/echo (where $PWD is the environment variable holding your current working directory).
That said, as a general rule you should not chdir inside your process. Instead, you can construct the path explicitly … e.g. via sprintf:
const char *binpath = "/bin";
char *fullpath = malloc(strlen(binpath) + 1 + strlen(argv[1]) + 1);
sprintf(fullpath, "%s/%s", binpath, argv[1]);
… and then stat the result (no need to dynamically allocate your struct stat either):
struct stat st;
if (stat(fullpath, &st) != 0) {
fprintf(stderr, "error in stat(): %s", strerror(errno));
}
printf("%s has inode %ju\n", fullpath, (unsigned long) st.st_ino);
The canonical way of determining a file's type is to use the commented
out code in this snippet:
// Return the number of files in dirName. Ignore directories and links.
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
int fCt = 0;
struct dirent *dir;
DIR *d;
d = opendir(argv[1]);
if (d == NULL) {
printf("%s was not opened!\n", argv[1]);
exit(0);
}
// Count all of the files.
while ((dir = readdir(d)) != NULL) {
// struct stat buf;
// stat(dir->d_name, &buf);
// if (S_ISREG(buf.st_mode)) { fCt++; }
if (dir->d_type == 8) { fCt++; }
}
return fCt;
}
The element buf.st_mode returns 41ED (hex), 16877 (decimal) for both directories and regular files. S_ISREG fails to find the right bit set for both types.
Note that the line:
if (dir->d_type == 8) { fCt++; }
returns an accurate file count.
Why did the commented out method fail?
I have a probleme with a program.
I need to take file name in a folder and put it in a variable.
I tried that:
#define _POSIX_SOURCE
#include <dirent.h>
#include <errno.h>
#include <sys/types.h>
#undef _POSIX_SOURCE
#include <stdio.h>
int main()
{
DIR *dir;
struct dirent *file;
char fileName;
dir = opendir("../../incoming");
while ((file = readdir(dir)) != NULL)
printf(" %s\n", file->d_name);
fileName = file->d_name;
printf(fileName);
closedir(dir);
return 0;
}
thx
Not very clear what you wanted, I prefer to think you want read the file name into your varible 'fileName' and then handle that varible...
Correct 2 parts:
fileName type should be same as the struct member for assign.
the while loop......
int main(){
DIR *dir;
struct dirent *file;
char fileName[255];
dir = opendir("../../incoming");
while ((file = readdir(dir)) != NULL)
{
printf(" %s\n", file->d_name);
strncpy(fileName, file->d_name, 254);
fileName[254] = '\0';
printf("%s\n", fileName);
}
closedir(dir);
return 0;
}
You need to declare a character array of sufficient size and copy the contents of the file->d_name into it if you want to save it past the call to closedir().
If you want to simply print the name,
printf("%s\n", file->d_name);
would accomplish that.