0

I am supposed to go through each file or directory for a given directory, but I can't get it to work calculate the sum of bytes occupied for every file in the directory.

Here is the code that I've written so far. (Also I'm not sure how to let the user type in the directory; I tried cin or getline but it didn't work.)

#include <string>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>

using namespace std;

int main (void)
{
    int reg = 0;
    int der = 0;
    size_t size = 0;
    DIR * dirp;
    struct dirent *entry;
    struct stat file_stats;

    dirp = opendir("/usr/share/");
    while ((entry = readdir(dirp)) != NULL) {
        if (entry->d_type == DT_REG)
        {
            reg++;
            stat(entry->d_name, &file_stats);  
            size += (unsigned int) file_stats.st_size;

        }
        else if (entry->d_type == DT_DIR)
        {        
           der++;
        }
       }
    closedir(dirp);
  printf("The total number of directories in directory is %d \n", reg);
  printf("The total number of files in directory is %d \n", der);
  printf("The total number of bytes occupied by all files in directory is %zu\n", size);
  return 0;
}

When I run it from the /usr/share/ it always returns 0, but it works when I change the directory to the current directory.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Jin K
  • 11
  • 2
  • 1
    Please ask a specific question: Does your code not compile? Does it not output what you expect? Etc. Also note that since C++17, the C++ standard library offers `` as an alternative to the POSIX-specific functions you are using. – walnut Feb 25 '20 at 04:30
  • Please don't put `struct` in front of a type name in a variable declaration in C++. This is necessary in C, but redundant in C++ and may sometimes cause problems. – walnut Feb 25 '20 at 04:31
  • Yep, I've fixed that but unless it's a current directory like "." it won't show the sum of the byte size. – Jin K Feb 25 '20 at 05:14

1 Answers1

1

Readdir returns filenames within the directory, so entry->d_name will return terminfo instead of /usr/share/terminfo. If you had checked the return value of stat you would see that it returns -1 for nearly every item returned by readdir.

The easy solution is to just chdir to /usr/share before walking the files, as the relative filenames will then correctly be resolved.

Another easy solution is to use fstatat:

The fstatat() system call is equivalent to stat() and lstat() except in the case where the path specifies a relative path. In this case the sta tus is retrieved from a file relative to the directory associated with the file descriptor fd instead of the current working directory.

So you can simply call

fstatat(dirfd(dirp), entry->d_name, &file_stats, AT_SYMLINK_NOFOLLOW)

(but check for error return values this time!)

Finally, you tagged your question as C++, so here's a C++17 answer:

#include <filesystem>
#include <iostream>

namespace fs = std::filesystem;

int main() {
    int total = 0, directories = 0;
    fs::path p;
    std::cin >> p;
    for (auto& file: fs::directory_iterator(p)) {
        if (file.is_regular_file()) {
            total += file.file_size();
        } else if (file.is_directory()) {
            directories++;
        }
    }
    std::cout << "Directory " << p << " contains " << total << " bytes, " << directories << " directories." << std::endl;
}
Botje
  • 26,269
  • 3
  • 31
  • 41