/* SPDX-License-Identifier: Apache-2.0 */ /* Copyright 2025 Thorsten Töpper * * vim:ts=4:sw=4:expandtab */ #include #include #include #include #include #include #include "output.h" #include "data_management.h" #include "options.h" /* === IMPLEMENTATION === */ inline struct list_node *create_node(char *fname, struct stat *ln_stat) { struct list_node *node = NULL; size_t length = 0; #ifdef DEBUGBUILD static size_t count = 0; DBGTRC("DEBUG: Called fname '%s', attempting to create node #%lu\n", fname, ++count); #endif if (fname == NULL || fname[0] == '\0') { LOGERR("ERROR: No valid filename given\n"); return NULL; } if (ln_stat == NULL) { LOGERR("ERROR: No valid file information given\n"); return NULL; } if ((node = calloc(1, sizeof(struct list_node))) == NULL) { LOGERR("ERROR: Failed allocate memory for node: %s\n", strerror(errno)); return NULL; } length = strnlen(fname,256); if (length>255) { LOGERR("ERROR: strnlen(\"%s\",256) == %lu, terminating with '\\0' at char[255].\n", fname, length); length = 255; } node->next = NULL; explicit_bzero(node->fname, 256); memcpy(node->fname, fname, length); memcpy(&(node->ln_stat), ln_stat, sizeof(struct stat)); return node; } inline void destroy_list(struct list_head *list) { struct list_node *ptr = NULL; if (list == NULL) return; while (list->first != NULL) { ptr = list->first->next; if (ptr == NULL) { free(list->first); break; } list->first->next = ptr->next; free(ptr); } free(list); } /* dirty... */ #define INSERT_BY_NUMERIC_FIELD(list, node, field) \ { struct list_node *ptr = NULL; \ if (node->ln_stat.field > list->first->ln_stat.field) { \ node->next = list->first; list->first = node; return 0; } \ ptr = list->first; \ while (ptr != NULL) { \ if (ptr->next == NULL) { ptr->next = node; return 0; } \ if (node->ln_stat.field > ptr->next->ln_stat.field) { \ node->next = ptr->next; ptr->next = node; \ return 0; } \ ptr = ptr->next; \ } \ } inline int insert_sorted_by_size(struct list_head *list, struct list_node *node) { if (list == NULL) { LOGERR("ERROR: No list given.\n"); return -1; } if (node == NULL) { LOGERR("ERROR: No node given.\n"); return -2; } if (list->first == NULL) { list->first = node; return 0; } INSERT_BY_NUMERIC_FIELD(list, node, st_size); return -3; } inline int insert_sorted_by_time(struct list_head *list, struct list_node *node) { if (list == NULL) { LOGERR("ERROR: No list given.\n"); return -1; } if (node == NULL) { LOGERR("ERROR: No node given.\n"); return -2; } if (list->first == NULL) { list->first = node; return 0; } switch (option_time_field) { case 'm': /* default setting first */ INSERT_BY_NUMERIC_FIELD(list, node, st_mtim.tv_sec); break; case 'a': INSERT_BY_NUMERIC_FIELD(list, node, st_atim.tv_sec); break; case 'c': INSERT_BY_NUMERIC_FIELD(list, node, st_ctim.tv_sec); break; default: LOGERR("WARNING: option_time_field has invalid value %c 0x%02X - going by default m\n", option_time_field, option_time_field); INSERT_BY_NUMERIC_FIELD(list, node, st_mtim.tv_sec); break; }; return -3; } inline int insert_sorted_by_name(struct list_head *list, struct list_node *node) { struct list_node *ptr = NULL; if (list == NULL) { LOGERR("ERROR: No list given.\n"); return -1; } if (node == NULL) { LOGERR("ERROR: No node given.\n"); return -2; } if (list->first == NULL) { list->first = node; return 0; } if (strncmp(node->fname, list->first->fname, 255) < 0) { node->next = list->first; list->first = node; return 0; } ptr = list->first; while (ptr != NULL) { if (ptr->next == NULL) { ptr->next = node; return 0; } if (strncmp(node->fname, ptr->next->fname, 255) < 0) { node->next = ptr->next; ptr->next = node; return 0; } ptr = ptr->next; } return -3; } inline struct list_head *create_list_sort_reversed(struct list_head *list) { struct list_head *lh = NULL; struct list_node *cpynode = NULL, *ptr = NULL; if (list == NULL) { LOGERR("ERROR: No list given\n"); return NULL; } if ((lh = calloc(1, sizeof(struct list_head))) == NULL) { LOGERR("ERROR: Failed to allocate memory for a struct list_head\n"); return NULL; } lh->first = NULL; ptr = list->first; while (ptr != NULL) { cpynode = create_node(ptr->fname, &(ptr->ln_stat)); if (cpynode == NULL) { destroy_list(lh); return NULL; } cpynode->next = lh->first; lh->first = cpynode; ptr = ptr->next; } return lh; } /* This function contains the only time critical code. The loop over * the directory content. */ 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 *lnstack = NULL, *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); free(fullpath); 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); free(fullpath); 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; } /* Performance: Put every node on the stack and do the sorting afterwards */ tmp->next = lnstack; lnstack = tmp; } free(fullpath); closedir(dir); DBGTRC("DEBUG: Finished reading data from directory\n"); while (lnstack != NULL) { tmp = lnstack; lnstack = lnstack->next; tmp->next = NULL; /* 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; case SORT_BY_NAME: insert_sorted_by_name(list, tmp); break; default: /* Never should be the case, but as so often, better safe than sorry */ LOGERR("ERROR: No valid sort option set: %d\n", option_sort_type); exit(EXIT_FAILURE); }; } return list; }