Last active
December 9, 2025 13:12
-
-
Save skull-squadron/955f0a1e21e1356c1a6ba4f48afcd935 to your computer and use it in GitHub Desktop.
dufff - Faster and betterer than `du -sh` and `find -printf . | wc -c`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* Output like: | |
| $ duff some/dir | |
| files: 86095 | |
| dirs: 2799 | |
| other: 0 | |
| bytes: 7.81 GiB (8383140523) | |
| $ | |
| */ | |
| #include <dirent.h> | |
| #include <inttypes.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include <unistd.h> | |
| #include <locale.h> | |
| static const char *const UNITS[] = {"", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}; | |
| #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) | |
| #define BYTES_FMT PRIu64 | |
| typedef uint64_t bytes_t; | |
| // Evil globals variables, now for everyone to share | |
| uint32_t dirs = 0; | |
| uint32_t files = 0; | |
| uint32_t other = 0; | |
| bytes_t bytes = 0; | |
| struct dirent *entry; | |
| struct stat st; | |
| static int count_files(const char *dir) { | |
| DIR *d = opendir(dir); | |
| if (!d) { | |
| perror("opendir"); | |
| fprintf(stderr, "Could not open directory '%s'\n", dir); | |
| return -1; | |
| } | |
| ++dirs; | |
| while ((entry = readdir(d))) { | |
| switch (entry->d_type) { | |
| case DT_DIR: { | |
| if (entry->d_name[0] == '.') | |
| if (entry->d_name[1] == '\0' || (entry->d_name[1] == '.' && entry->d_name[2] == '\0')) | |
| continue; | |
| const size_t dir_len = strlen(dir); | |
| const size_t entry_len = strlen(entry->d_name); | |
| char new_path[dir_len + 1 /* / */ + entry_len + 1 /* \0 */]; | |
| memcpy(new_path, dir, dir_len); | |
| new_path[dir_len] = '/'; | |
| memcpy(&new_path[dir_len + 1], entry->d_name, entry_len + 1); | |
| if (count_files(new_path) != 0) { | |
| closedir(d); | |
| return -1; | |
| } | |
| } break; | |
| case DT_REG: { | |
| ++files; | |
| const size_t dir_len = strlen(dir); | |
| const size_t entry_len = strlen(entry->d_name); | |
| char new_path[dir_len + entry_len + 2]; | |
| memcpy(new_path, dir, dir_len); | |
| new_path[dir_len] = '/'; | |
| memcpy(&new_path[dir_len + 1], entry->d_name, entry_len + 1); | |
| if (lstat(new_path, &st) != 0) { | |
| perror("lstat"); | |
| fprintf(stderr, "Could not lstat('%s')\n", new_path); | |
| closedir(d); | |
| return -1; | |
| } | |
| bytes += st.st_size; | |
| } break; | |
| default: | |
| ++other; | |
| break; | |
| } | |
| } | |
| closedir(d); | |
| return 0; | |
| } | |
| static void humanize_bytes(bytes_t bytes, char *output) { | |
| size_t unit_index = 0; | |
| double humanized_size = (double)bytes; | |
| for (; humanized_size >= 1024 && unit_index < ARRAY_SIZE(UNITS) - 1; ++unit_index) | |
| humanized_size /= 1024; | |
| sprintf(output, "%.2f%*s", humanized_size, (unit_index == 0) ? 0 : 4, | |
| UNITS[unit_index]); | |
| } | |
| int main(int argc, char *argv[]) { | |
| char humanized[60]; | |
| const char *const dir_name = (argc < 2) ? "." : argv[1]; | |
| if (count_files(dir_name) != 0) exit(EXIT_FAILURE); | |
| setlocale(LC_NUMERIC, ""); | |
| humanize_bytes(bytes, humanized); | |
| printf( | |
| "files: %" PRIu32 "\n" | |
| " dirs: %" PRIu32 "\n" | |
| "other: %" PRIu32 "\n" | |
| "bytes: %s (%" BYTES_FMT ")\n", | |
| files, dirs, other, humanized, bytes); | |
| return EXIT_SUCCESS; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| CC ?= cc | |
| CFLAGS ?= -DNDEBUG -std=c99 -O3 | |
| dufff: dufff.c | |
| $(CC) $(CFLAGS) dufff.c -o dufff |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Added locale support so French, Italian, etc. see proper decimal seps.