/* SPDX-License-Identifier: Apache-2.0 */ /* Copyright 2025 Thorsten Töpper * * dir_monitor - print specified stat() information from entries of a given * directory to stdout. Call it via watch for repeated output to a terminal. * * vim:ts=4:sw=4:expandtab */ #include #include #include #include #include #include #include "output.h" #include "list_management.h" #include "options.h" /* === DEFINITIONS === */ struct list_head *get_data_from_directory(char *path); /* === IMPLEMENTATION === */ /* This function contains the only time critical code. The loop over * the directory content. * * Note for future: * I myself use the dir_monitor in combination with watch on a directory * containing temporary data, it may happen that a run into an error occurs, * but it's not tragic. To reduce the chance of the error the first step * would be to optimize the loop with putting the data into a list in stack * form and moving the nodes into a sorted list afterwards. */ struct list_head *get_data_from_directory(char *path) { char *fullpath = NULL, *fname_in_path = NULL; DIR *dir = NULL; size_t l; struct dirent *de = NULL; struct list_node *tmp = NULL; struct list_head *list = NULL; struct stat stat_res; if (path == NULL) { LOGERR("ERROR: No path given.\n"); return NULL; } l = strlen(path); /* 258 filename and possibly required PATH_SEP and of course string termination */ if ((fullpath = calloc(l+258, sizeof(char))) == NULL) { LOGERR("ERROR: Failed to allocate memory for list head.\n"); return NULL; } sprintf(fullpath, "%s%c", path, ((path[l-1] == PATH_SEP) ? '\0' : PATH_SEP)); fname_in_path = (fullpath[l] == PATH_SEP) ? &(fullpath[l+1]) : &(fullpath[l]) ; if ((dir = opendir(path)) == NULL) { LOGERR("ERROR: Failed to open directory '%s': %s (errno %d)\n", path, strerror(errno), errno); return NULL; } /* TBH: In case those few bytes are not available, the process will be killed anyways */ if ((list = calloc(1, sizeof(struct list_head))) == NULL) { LOGERR("ERROR: Failed to allocate memory for list head.\n"); closedir(dir); return NULL; } errno = 0; /* Currently the code is specific UNIXoid, needs to be adjusted in case someone ports it. */ while ((de = readdir(dir)) != NULL) { if (de->d_name[0] == '.' && option_show_hidden_entries == false) continue; sprintf(fname_in_path, "%s", de->d_name); DBGTRC("DEBUG: fullpath: '%s'\n", fullpath); if (lstat(fullpath, &stat_res) != 0) { LOGERR("ERROR: lstat call on '%s' failed: %s (errno %d)\n", fullpath, strerror(errno), errno); continue; } if ((tmp = create_node(de->d_name, &stat_res)) == NULL) { LOGERR("ERROR: Skipping entry %s\n", de->d_name); continue; } /* SORT_BY_SIZE is the default value, always check it first. */ switch (option_sort_type) { case SORT_BY_SIZE: insert_sorted_by_size(list, tmp); break; case SORT_BY_TIME: insert_sorted_by_time(list, tmp); break; }; } free(fullpath); closedir(dir); return list; } int main(int argc, char **argv) { struct list_head *list = NULL; int path_index = 1; if (argc < 2) { usage(argv[0]); return EXIT_FAILURE; } path_index = parse_arguments(argc, argv); if (path_index == argc) { usage(argv[0]); return EXIT_FAILURE; } list = get_data_from_directory(argv[path_index]); print_list(list); destroy_list(list); return EXIT_SUCCESS; }