Rob
Rob

Reputation: 3

C - Recursive call to traverse sub-directory and print all files + sizes (using stat)

I have a program that recursively prints the cwd size plus containing file sizes and repeats for each sub directory.


Recursive directory traversal function: (Note the reason for printf in this function and passing two strings is that the output needs to be in a special format so I can't just output the actual filepath. Also I am just learning about system calls to work with directories in ubuntu so if you have any comments on improvements on the code I would appreciate them (style or using something more simple to accomplish the same).

#include <sys/stat.h>
#include <limits.h>
#include <dirent.h>
#include <libgen.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define TRUE 1 
#define FALSE 0
#define FIRST_ARG 1
#define SECOND_ARG 2
char* fileName;
FILE* fileToRead;
DIR* directoryToRead;
int printFileSize(char*);
int printWorkingSize(char*, char*);
int printSize(char*, char*);
int printDir(char*, char*);
int printCurrentDir(char*, char*);
int bytesToKbytes(long long, char*);
int main(int argc, char** argv) {
    if(argc == FIRST_ARG) {
        char currentDir[PATH_MAX + 1] = "";
        char* currentDirectory;
        directoryToRead = opendir (".");
        if (directoryToRead == NULL){
            exit (EXIT_FAILURE);
        }
        closedir(directoryToRead);
        printCurrentDir(currentDirectory, ".");
    }
return 0;
}

int printCurrentDir(char* name, char* path) {
    struct dirent *dir;
    struct stat statBuffer;
    char fileName[PATH_MAX + 1];
    char filePath[PATH_MAX + 1];
    DIR* openDir;
    if((openDir = opendir(path)) == NULL) {
        printf("Could not open %s\n", path);
    }    
    stat(path, &statBuffer);
    if(strcmp(path, ".") == 0) {
        printf("%lld .\n", (long long)statBuffer.st_size);
    }   else {
            printf("%lld %s\n", (long long)statBuffer.st_size, name);
    }
    while (TRUE) {  // go through contents of current directory
        dir = readdir(openDir);
        if(!dir) {
            break;
        }
        if((strcmp(dir->d_name, "..") == 0) || (strcmp(dir->d_name,".") == 0)) {
            continue;
        }
        if(name == NULL) {
            strcpy(fileName, dir->d_name);
        } else {
        strcpy(fileName, name);
        strcat(fileName, "/");
        strcat(fileName, dir->d_name);
        }
        strcpy(filePath, path);
        strcat(filePath, "/");
        strcat(filePath, dir->d_name);
        if(dir->d_type == DT_DIR) {  // if the next file is a directory
            if(!printCurrentDir(fileName, filePath)) {
                return FALSE;
            }
        } 
        else if(!printWorkingSize(fileName, filePath)) {
            return FALSE;
        }
    }
    return TRUE;
}

//output file size in bytes followed by name-> (char* file)
int printWorkingSize(char* file, char* path) {
    struct stat statBuffer;
    stat(path, &statBuffer);
    char result[PATH_MAX];
    if(bytesToKbytes((long long)statBuffer.st_size, result) == FALSE) {
        sprintf(result, "%lld", (long long)statBuffer.st_size);
    }
    printf("%5s %s\n", result, path);
    return TRUE;
        }


// convert bytes to kilobytes if more than 5 digits
int bytesToKbytes(long long bytes, char* result) {
    if(!(bytes > 99999)) {
        return FALSE;
    }
    char size[PATH_MAX];
    sprintf(size, "%lld", bytes);
    size[3] = 'K';
    size[4] = '\0';
    strcpy(result, size);
    return TRUE;
}

Upvotes: 0

Views: 4759

Answers (2)

It is operating system specific. On Linux and POSIX, you should simply use the nftw(3) library function, which is recursively reading the directories and already calling stat(2) for you.

Upvotes: 1

Serge Ballesta
Serge Ballesta

Reputation: 148910

The problem is effectively in the way you use bytesToKbytes. It returns a boolean to indicate if it wrote anything in result and :

  • you do not preload anything in result before calling bytesToKbytes
  • you do not test return value to write anything in resul if size was less than 99999

You could use :

int printWorkingSize(char* file, char* path) {
    struct stat statBuffer;
    stat(path, &statBuffer);
    char result[PATH_MAX];
    if (! bytesToKbytes((long long)statBuffer.st_size, result)) {
        sprintf(result, "%lld", (long long)statBuffer.st_size);
    }
    printf("%5s %s\n", result, path);
    return TRUE;
}

You could also change bytesToKbytes to put length in bytes into resul if size is less than 99999

Upvotes: 0

Related Questions