#include /* for printf */ #include /* for struct dirent */ #include /* for struct dirent */ #include /* for lstat, struct stat */ #include /* for malloc */ #include /* for strdup */ #include #include #include "error_handling.h" typedef struct _ckdu_tree_entry { char * name; off_t size; mode_t mode; struct _ckdu_tree_entry * next; struct _ckdu_tree_entry * child; } ckdu_tree_entry; char * malloc_path_join(const char *dirname, const char *basename) { size_t const len_dirname = strlen(dirname); size_t const len_basename = strlen(basename); char * const target = malloc(len_dirname + 1 + len_basename + 1); if (!target) { errno = ENOMEM; return NULL; } memcpy(target, dirname, len_dirname); target[len_dirname] = '/'; memcpy(target + len_dirname + 1, basename, len_basename); target[len_dirname + 1 + len_basename] = '\0'; return target; } void initialize_tree_entry(ckdu_tree_entry * entry, const char * dirname, const char * basename) { char * fullpath; struct stat props; assert(entry); assert(dirname); assert(basename); entry->name = strdup(basename); fullpath = malloc_path_join(dirname, basename); assert(fullpath); if (lstat(fullpath, &props)) { handle_stat_error(errno, dirname, basename); } free(fullpath); entry->size = props.st_size; entry->mode = props.st_mode; assert(entry->name); entry->next = NULL; entry->child = NULL; } int is_nonlink_directory(const ckdu_tree_entry * root) { return S_ISDIR(root->mode) && ! S_ISLNK(root->mode); } void crawl_tree(ckdu_tree_entry * root, const char * dirname) { DIR * dir; struct dirent * entry; ckdu_tree_entry * previous = NULL; ckdu_tree_entry * current; errno = 0; dir = opendir(dirname); if (! dir) { handle_opendir_error(errno, dirname); return; } do { errno = 0; entry = readdir(dir); if (! entry) { if (errno) { handle_readdir_error(errno, dirname); return; } break; } if (! strcmp(entry->d_name, ".") || ! strcmp(entry->d_name, "..")) { continue; } current = malloc(sizeof(ckdu_tree_entry)); assert(current); initialize_tree_entry(current, dirname, entry->d_name); if (! previous) { root->child = current; } else { previous->next = current; } previous = current; if (is_nonlink_directory(current)) { char * const fullpath = malloc_path_join(dirname, current->name); assert(fullpath); crawl_tree(current, fullpath); free(fullpath); } } while (dir); closedir(dir); } void print_tree_indent(const ckdu_tree_entry * root, const char * indent) { const ckdu_tree_entry * current = root->child; const char * const slash_or_not = is_nonlink_directory(root) ? "/" : ""; printf("%10u %s%s%s\n", (unsigned int)root->size, indent, root->name, slash_or_not); while (current) { size_t const len = strlen(indent); char * const child_indent = malloc(len + 2 + 1); assert(child_indent); strncpy(child_indent, indent, len); strncpy(child_indent + len, " ", 2); child_indent[len + 2] = '\0'; print_tree_indent(current, child_indent); free(child_indent); current = current->next; } } void print_tree(const ckdu_tree_entry * root) { print_tree_indent(root, ""); } int main() { ckdu_tree_entry entry; initialize_tree_entry(&entry, ".", "."); crawl_tree(&entry, "."); print_tree(&entry); return 0; }