Calculating size of Directory in C - c

I want to calculate the size of the directory (path) recursively. In my current code I have a function that identifies if it's a directory or file, if it's a directory it calls the function with the subdirectory (file) and if it's a file it adds to the totalSize variable. However, my current code doesn't return anything meaning that there is an error somewhere.
here is my code -
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
void getsize(const char *path,int* totalSize);
int main()
{
int total = 0;
char path [] = "C:\\Users\\abbas\\Desktop\\Leetcode airplane";
getsize(path,&total);
printf("%d",total);
return total;
}
void getsize(const char *path,int* totalSize)
{
struct dirent *pDirent;
struct stat buf;
struct stat info;
DIR *pDir;
int exists;
char str[100];
pDir = opendir (path);
while ((pDirent = readdir(pDir)) != NULL)
{
stat(pDirent->d_name,&info);
if(S_ISDIR(info.st_mode))
{
strcpy(str,path);
strcat(str,"/");
strcat(str,pDirent->d_name);
getsize(str,totalSize);
}
else
{
strcpy(str,path);
strcat(str,"/");
strcat(str,pDirent->d_name);
exists = stat(str,&buf);
if (exists < 0)
{
continue;
}
else
{
(*totalSize) += buf.st_size;
}
}
}
closedir(pDir);
}

Include string.h.
The arbitrary fixed sizestr[100] is problematic. If you are on Linux include linux/limits.h and use str[PATH_MAX] or even better pathconf(path, _PC_NAME_MAX). In either case you should either ensure the buffer is big enough (using snprintf() for instance), or dynamically allocate the buffer.
You need to exclude . and .. otherwise you end up with an infinite loop (path/../path/..).
stat(pDirent->d_name,&info) fails as you need to stat() path/pDirect->d_name not just pDirect->d_name.
(not fixed) Maybe snprintf(path2, sizeof path2, "%s%s%s", path, PATH_SEP, pDirenv->d_name) instead of strcpy() and strcat()?
Check return values of functions otherwise you are wasting time.
No point of doing two stat() calls on the same path so just use (*totalSize) += buf.st_size;.
(not fixed) On Windows, consider using _stat64() with the address of a struct __stat64 (#AndrewHenle).
I assume you only want the size of files.
(not fixed) It would be more natural if getsize() returned the size instead of using int *totalSize out parameter.
(not fixed) Consider using nftw() (or the older ftw()) to walk the tree.
Note that program now accept path via command line for testing purposes.
#include <dirent.h>
#include <errno.h>
#include <linux/limits.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
const char PATH_SEP =
#ifdef _WIN32
"\\";
#else
"/";
#endif
void getsize(const char *path,int *totalSize) {
struct dirent *pDirent;
DIR *pDir = opendir (path);
while ((pDirent = readdir(pDir)) != NULL) {
if(
!strcmp(pDirent->d_name, ".") ||
!strcmp(pDirent->d_name, "..")
)
continue;
char path2[PATH_MAX];
strcpy(path2, path);
strcat(path2, PATH_SEP);
strcat(path2, pDirent->d_name);
struct stat info;
if(stat(path2, &info) == -1) {
perror("stat");
return;
}
if(S_ISDIR(info.st_mode))
getsize(path2, totalSize);
else if(S_ISREG(info.st_mode))
(*totalSize) += info.st_size;
}
closedir(pDir);
}
int main(argc, char *argv[]) {
if(argc != 2) {
printf("usage: your_program path\n");
return 1;
}
int total = 0;
getsize(argv[1], &total);
printf("%d\n",total);
}
and example test:
$ mkdir -p 1/2
$ dd if=/dev/zero of=1/file count=123
123+0 records in
123+0 records out
62976 bytes (63 kB, 62 KiB) copied, 0.000336838 s, 187 MB/s
$ dd if=/dev/zero of=1/2/file count=234
234+0 records in
234+0 records out
119808 bytes (120 kB, 117 KiB) copied, 0.0015842 s, 75.6 MB/s
$ echo $((62976 + 119808))
182784
$ ./your_program 1
182784

I think the major error of your code lies in the recursive logic.
To quote pp.183 of The C Programming Language:
Each directory always contains entries for itself, called ".",
and its parent, ".."; these must be skipped, or the program will
loop forever.
Therefore, maybe you can try adding the following if test
at the beginning of the while loop:
while ((pDirent = readdir(pDir)) != NULL)
{
if (strcmp(pDirent->d_name, ".") == 0
|| strcmp(pDirent->d_name, "..") == 0)
continue; /* skip self and parent */
/* ... */
}
Still, there might be other errors, but I think this one is the most significant.

Practice safe coding.
Below risks buffer overflow.
// Risky
strcpy(str,path);
strcat(str,"/");
strcat(str,pDirent->d_name);
Had code done,
int len = snprintf(str, sizeof str, "%s/%s", path, pDirent->d_name);
if (len < 0 || (unsigned) len >= sizeof str) {
fprintf(stderr, "Path too long %s/%s\n", path, pDirent->d_name);
exit (-1);
}
Then the code would have readily errored out do to recursion on "." and ".." and led to OP's self-discovery of a key problem.
This make for faster code production and more resilient code. Saves OP time.

Related

How to Find number and folder of files in a directory

I need to find number of files and folders in a directory. I'm using MinGW compiler before this I tried to use d_type but I couldn't compile my code.
And I don't care about "." and" .. " directory. I don't wanna calculate them.
So I write a program like this. This program can easily find how many files and folders in a directory.
But When I give a new Name a folder instead of New Folder, New Folder(1). This programs calculate that folders as a file.
What can I do? I really stuck. I have to find how many files and how many folders...
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
int
main(int argc, char *argv[])
{
int file_count = 0;
int dir_count = 0;
struct dirent * entry;
struct stat filestat;
size_t nfiles = 0, ndirs = 0;
DIR *dp;
if (argc != 2)
{
printf("usage: put directory_name\n");
exit(-1);
}
if ((dp = opendir(argv[1])) == NULL)
{
printf("Error: can't open %s\n", argv[1]);
exit(-2);
}
while ((entry= readdir(dp)) != NULL){
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
continue;
}
stat(entry->d_name,&filestat);
if( S_ISDIR(filestat.st_mode) ){
ndirs++;
}
else
nfiles++;
}
closedir(dp);
printf("%lu Files, %lu Directories\n", nfiles, ndirs);
return(0);
}
entry->d_name will contain the filename only, not the full path, so if call stat() it will fail unless that file happens to exist in the current directory.
Also note that your code is not recursive, so it won't count stuff in the subfolders.

Printing directory: chdir not working

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.

Troubleshoot code implementing LS command like functionalities in C

I have drafted a code snippet that is to emulate the operation of ls -all in a custom shell named My$HELL
The main shell process invokes this code(by calling its executable through execlp).
Following is the code of the executable myls which is to do the work:-
myls.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <time.h>
void search_dir(const char * arg);
void main(int argc, char *argv[])
{
int i;
if (argc==1)
search_dir(".");
for(i=1;i<argc;i++)
search_dir(argv[i]);
}
void search_dir(const char *arg)//Function to read directories and file attributes//
{
DIR *dirp;
struct dirent *dp;//Dirent structure used to read and store directory attributes//
char file_name[256];
char time[50]={"\0"};
struct tm *timeinfo;
struct stat prop_file;//stat function for accessing file attributes//
char type;
if((dirp=opendir(arg))==NULL)
{
perror("opendir");
return;
}
printf("\n\nDirectory \tTime last Modified\tSize\t\t Name\n");
while((dp=readdir(dirp))!=NULL) // Navigates the directory structure
{
if ( stat(dp->d_name,&prop_file)!=0) //Reading file attributes//
{
printf("\n%s:Error in reading the file attributes", dp->d_name );
continue;
}
if ( dp->d_type==8 )
{
type = '-';
}
else
{
type = 'd';
}
timeinfo=localtime(&(prop_file.st_mtime));
strftime(time,20,"%b %d %H:%M", timeinfo);
printf("\n %c\t\t %s\t\t%d\t\t %s",type,time,(int)prop_file.st_size,dp->d_name); //Printing ile attributes//
}
printf("\n");
}
Irrespective of the contents in the directory, the process displays certain fields after which the calling process terminates with a segmentation fault.
A GDB run is also of a little help (for being vague) and search on the error yields little result. Following is the debugged output:-
[~pbox/working/trial]<My$HELL>myls
Executing myls
Directory Time last Modified Size Name
d Aug 14 19:22 4096 ..
d Aug 14 18:42 4096 .
[~pbox/working/trial]<My$HELL>
Program received signal SIGSEGV, Segmentation fault.
strlen () at ../sysdeps/x86_64/strlen.S:106
106 ../sysdeps/x86_64/strlen.S: No such file or directory.
(gdb) Quit
From what I could understand such error are results of illegal variable/pointer assignments. Any help in pointing out the bug is highly appreciated.
Am also appending code segment of the main process from where myls is being called
main.c
.
.
else if(strcmp(command[0],"myls")==0) //command of type char ** stores the user input command check if the first field is 'myls'//
{
printf("Executing myls\n");
strcat(path,"/myls"); //path stores the directory path
result=execvp(path,command); //result of type int
exit(0);
}
.
.
Cheers and thanks in anticipation!!
the following code :
1) cleanly compiles
2) handles errors in an appropriate manner
3) does the job correctly
4) does not follow symbolic links
5) does not display the proper file type for every file
6) when accessing directories that are (in any way) protected
from casual reading, will output an error message
7) properly builds the path+filename before calling stat()
8) properly declares main() function and proper return
9) does not handle any options that are passed in.
10)does not seg fault
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <time.h>
#include <string.h>
void search_dir(const char * arg);
int main(int argc, char *argv[])
{
int i;
char dirBuf[128] = {'\0'};
char * newline = NULL;
if (argc==1)
search_dir(".");
else
{
for(i=1;i<argc;i++)
{
newline = strcpy( dirBuf, argv[i] );
if( strstr( dirBuf, "\n") )
{
*newline = '\0';
}
search_dir(dirBuf);
}
}
return 0;
}
void search_dir(const char *arg)//Function to read directories and file attributes//
{
DIR *dirp;
struct dirent *dp;//Dirent structure used to read and store directory attributes//
char fileName[256];
char fileTime[50]={"\0"};
struct tm *timeinfo;
struct stat prop_file;//stat function for accessing file attributes//
char type;
printf( "%s\n", arg);
if( NULL == (dirp=opendir(arg)) )
{
perror("opendir failed");
return;
}
// implied else, opendir successful
printf("\n\nDirectory \tTime last Modified\tSize\t\t Name\n");
while( NULL != (dp=readdir(dirp)) ) // gets next entry in current directory,
{
strcpy(fileName, arg);
strcat(fileName, "/");
strcat(fileName, dp->d_name);
printf( "\nfileName: %s", fileName);
if ( stat(fileName,&prop_file) ) //Reading file attributes//
{
perror( "stat failed" );
printf("\n%s:Error in reading the file attributes", dp->d_name );
continue;
}
#ifdef _DIRENT_HAVE_D_OFF
// following if/else needs expansion
if ( dp->d_type==8 )
{
type = '-';
}
else
{
type = 'd';
}
#else
type = '?';
#endif
timeinfo=localtime(&(prop_file.st_mtime));
strftime(fileTime, 49, "%b %d %H:%M", timeinfo);
printf("\n %c\t\t %s\t\t%d\t\t %s",
type,
fileTime,
(int)prop_file.st_size,
dp->d_name); //Printing file attributes//
}
printf("\n");
closedir( dirp );
}

In C, is there a way to get the size of multiple files with names matching a string?

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.

opendir: Too many open files

I write this code to print all files in /home/keep with absolution path:
#include <dirent.h>
#include <sys/stat.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
void catDIR(const char *re_path);
int main(int argc, char *argv[])
{
char *top_path = "/home/keep";
catDIR(top_path);
return 0;
}
void catDIR(const char *re_path)
{
DIR *dp;
struct stat file_info;
struct dirent *entry;
if ((dp = opendir(re_path)) == NULL)
{
perror("opendir");
return;
}
while (entry = readdir(dp))
{
if (entry->d_name[0] == '.')
continue;
//get Absolute path
char next_path[PATH_MAX];
strcpy(next_path, re_path);
strcat(next_path, "/");
strcat(next_path, entry->d_name);
lstat(next_path, &file_info);
if (S_ISDIR(file_info.st_mode))
{
catDIR(next_path);
}
else
{
printf("%s/%s\n", re_path, entry->d_name);
}
}
free(dp);
free(entry);
}
When I run it.
Not only print some file's path, but also print some error message:
opendir: Too many open files
I read man 3 opendir, then realize, I had opened too many files.
I do want to know, how to close it? and how to correct this program
You should probably use closedir when you finish iterating through a directory's contents.
Also, you might want to read the directory's listing into an array, close the directory, and then recurse on the array. This might help with traversing very deep directory structures.
Run
cat /proc/sys/fs/file-nr
what does it give ?
output format is : (number of allocated file handlers) - (number of allocated but unused file handlers) - (maximum number of file handlers)
If you get more number of allocated but unused file handlers, it means that you've to close directories as mentioned by #Matthew Iselin.
You can also change system limit.
More info about changing system limit Can be found here.

Resources