From 9f30334c544326732698012b3ebe9ffbec0afe0e Mon Sep 17 00:00:00 2001 From: Thorsten Töpper Date: Sat, 21 Jun 2025 23:57:40 +0200 Subject: simple format string: name, size and time fields --- include/options.h | 1 + include/output.h | 1 + src/dir_monitor.c | 6 +- src/options.c | 17 ++++++ src/output.c | 173 +++++++++++++++++++++++++++++++++++++++++++----------- 5 files changed, 163 insertions(+), 35 deletions(-) diff --git a/include/options.h b/include/options.h index 5e951b2..e31fd6a 100644 --- a/include/options.h +++ b/include/options.h @@ -32,6 +32,7 @@ extern bool option_show_hidden_entries; extern bool option_timestamp_long; extern char option_time_field; +extern char *option_format_string; int parse_arguments(int argc, char **argv); void set_option(const char *option_name, char *option_argument); diff --git a/include/output.h b/include/output.h index 35640fe..53f0869 100644 --- a/include/output.h +++ b/include/output.h @@ -23,6 +23,7 @@ int fputc_all_cols(char c, FILE *fdout); int fputc_width_x(char c, size_t x, FILE *fdout); void print_list(struct list_head *list); +void print_list_formatted(const char *format, struct list_head *list); #endif diff --git a/src/dir_monitor.c b/src/dir_monitor.c index 509e99f..46409cd 100644 --- a/src/dir_monitor.c +++ b/src/dir_monitor.c @@ -30,7 +30,11 @@ int main(int argc, char **argv) { } list = get_data_from_directory(argv[path_index]); - print_list(list); + if (option_format_string == NULL) { + print_list(list); + } else { + print_list_formatted(option_format_string, list); + } destroy_list(list); return EXIT_SUCCESS; diff --git a/src/options.c b/src/options.c index 8a2216d..293ca3d 100644 --- a/src/options.c +++ b/src/options.c @@ -19,6 +19,7 @@ /* === GLOBAL VARIABLES === */ struct option long_options[] = { { "help", no_argument, 0, 0 }, + { "format-string", required_argument, 0, 0 }, { "long-timestamp", no_argument, 0, 0 }, { "reverse-sort", no_argument, 0, 0 }, { "show-hidden-entries", no_argument, 0, 0 }, @@ -31,6 +32,7 @@ bool option_sort_reverse_order = false; enum esort_type option_sort_type = SORT_BY_SIZE; bool option_show_hidden_entries = false; bool option_timestamp_long = false; +char *option_format_string = NULL; /* There are three fields regarding time, valid values 'a','c', 'm': * st_atime / st_atim.tv_sec - time of the last access @@ -49,6 +51,8 @@ void usage(char *executable) { /* long name, short name, optional argument, explanation */ fprintf(stderr, " %-25s %2s %10s - %s\n", "--help", "-h", "", "Show this message and exit"); + fprintf(stderr, " %-25s %2s %10s - %s\n", "--format-string", "", "format", + "define columns to print, details below"); fprintf(stderr, " %-25s %2s %10s - %s\n", "--long-timestamp", "-t", "", "Print timestamp in long form yyyymmdd HH:MM:SS ZONE"); fprintf(stderr, " %-25s %2s %10s - %s\n", "--reverse-sort", "", "", @@ -63,6 +67,14 @@ void usage(char *executable) { /* fputc_width_x('-', 72, stderr); */ fprintf(stderr, "\n\n--sort-by variants: name | size | time\n"); + + fprintf(stderr, "\n--format-string: Characters unlike the following are ignored\n"); + fprintf(stderr, " n - name\n"); + fprintf(stderr, " s - size\n"); + fprintf(stderr, " t - time defined by --time-field (default: modification time)\n"); + fprintf(stderr, " A - access time\n"); + fprintf(stderr, " C - change time\n"); + fprintf(stderr, " M - modification time\n"); } @@ -102,6 +114,11 @@ void set_option(const char *option_name, char *option_argument) { exit(EXIT_FAILURE); } + if (strcmp("format-string", option_name) == 0) { + option_format_string = option_argument; + return; + } + if (strcmp("sort-by", option_name) == 0) { if (strncmp("name", option_argument, 4) == 0) { option_sort_type = SORT_BY_NAME; diff --git a/src/output.c b/src/output.c index 53a0ff3..254d73f 100644 --- a/src/output.c +++ b/src/output.c @@ -5,6 +5,7 @@ * vim:ts=4:sw=4:expandtab */ +#include #include #include #include @@ -16,6 +17,20 @@ +/* === DEFINITIONS === */ + +#define out_vsep fputc('|', stdout) + +#define out_print_newline() fputc('\n', stdout) +#define out_print_fname(x) printf(" %s ", x->fname) + +void out_print_size(struct list_node *ptr); +void out_print_time(time_t tv); +void out_print_time_by_option(struct list_node *ptr); + + +/* === IMPLEMENTATION === */ + inline int fputc_all_cols(char c, FILE *fdout) { struct winsize terminal; ioctl(STDOUT_FILENO, TIOCGWINSZ, &terminal); @@ -45,13 +60,57 @@ inline int fputc_width_x(char c, size_t x, FILE *fdout) { } + +/* === FORMATTING RELATED FUNCTIONS === */ +inline void out_print_size(struct list_node *ptr) { + printf(" %8ld %2s ", ((ptr->ln_stat.st_size>=1024) ? (ptr->ln_stat.st_size/1024) : ptr->ln_stat.st_size), + ((ptr->ln_stat.st_size >= 1024) ? "kB" : "")); +} + +inline void out_print_time(time_t tv) { + struct tm *tm = NULL; + char timestamp[128]; + + tm = localtime(&tv); + if (option_timestamp_long) { + strftime(timestamp, sizeof(timestamp), "%Y%m%d %H:%M:%S %Z", tm); + printf(" %20s ", timestamp); + } else { + strftime(timestamp, sizeof(timestamp), "%H:%M:%S", tm); + printf(" %8s ", timestamp); + } +} + +inline void out_print_time_by_option(struct list_node *ptr) { + time_t time_value = 0; + + switch (option_time_field) { + case 'm': + time_value = ptr->ln_stat.st_mtim.tv_sec; + break; + case 'a': + time_value = ptr->ln_stat.st_atim.tv_sec; + break; + case 'c': + time_value = ptr->ln_stat.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); + option_time_field = 'm'; + time_value = ptr->ln_stat.st_mtim.tv_sec; + break; + }; + out_print_time(time_value); +} +/* === END OF FORMATTING FUNCTIONS === */ + + + /* The most simple default output, formatted output is planned */ void print_list(struct list_head *list) { struct list_head *lh = list; struct list_node *ptr; - struct tm *tm = NULL; - time_t time_value = 0; - char timestamp[128]; size_t total_size = 0; if (list == NULL) return; @@ -65,38 +124,11 @@ void print_list(struct list_head *list) { ptr = lh->first; while (ptr != NULL) { - time_value = 0; - - switch (option_time_field) { - case 'm': - time_value = ptr->ln_stat.st_mtim.tv_sec; - break; - case 'a': - time_value = ptr->ln_stat.st_atim.tv_sec; - break; - case 'c': - time_value = ptr->ln_stat.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); - option_time_field = 'm'; - time_value = ptr->ln_stat.st_mtim.tv_sec; - break; - }; - - printf(" %8ld %2s ", ((ptr->ln_stat.st_size>=1024) ? (ptr->ln_stat.st_size/1024) : ptr->ln_stat.st_size), - ((ptr->ln_stat.st_size >= 1024) ? "kB" : "")); + out_print_size(ptr); + out_print_time_by_option(ptr); + out_print_fname(ptr); + out_print_newline(); - tm = localtime(&time_value); - if (option_timestamp_long) { - strftime(timestamp, sizeof(timestamp), "%Y%m%d %H:%M:%S %Z", tm); - printf(" %20s ", timestamp); - } else { - strftime(timestamp, sizeof(timestamp), "%H:%M:%S", tm); - printf(" %8s ", timestamp); - } - printf(" %s\n", ptr->fname); /* Linux: Neither man stat(2) nor stat(3type) declare why struct stat field st_size * would get a negative value assigned. The type off_t masks __off_t or __off64_t * which themselves mask bits/types.h __SLONGWORD_TYPE on x86_64 that's long int. @@ -113,3 +145,76 @@ void print_list(struct list_head *list) { if (lh != list) destroy_list(lh); } + +/* For now with a large walk around common format strings and prevention of attack vectors. + * n - name + * s - file size + * + * Time related + * t - timestamp set by option_time_field + * A - timestamp from atim.tv_sec + * C - timestamp from ctim.tv_sec + * M - timestamp from mtim.tv_sec + */ +void print_list_formatted(const char *format, struct list_head *list) { + struct list_head *lh = list; + struct list_node *ptr; + size_t format_len = 0, i = 0; + + if (format == NULL || format[0] == '\0') { + LOGERR("ERROR: No format string given"); + return; + } + + if (list == NULL || list->first == NULL) { + LOGERR("ERROR: No data to print.\n"); + return; + } + + if (option_sort_reverse_order) { + lh = create_list_sort_reversed(list); + /* be tolerant */ + if (lh == NULL) { + lh = list; + } + } + + /* Note: the maximum length needs to be adjusted if the strings + * shall contain more complicated settings */ + format_len = strnlen(format, 80); + + ptr = lh->first; + while (ptr != NULL) { + for (i=0; i < format_len; i++) { + switch (format[i]) { + case 'n': + out_print_fname(ptr); + break; + case 's': + out_print_size(ptr); + break; + case 't': + out_print_time_by_option(ptr); + break; + case 'A': + out_print_time(ptr->ln_stat.st_atim.tv_sec); + break; + case 'C': + out_print_time(ptr->ln_stat.st_ctim.tv_sec); + break; + case 'M': + out_print_time(ptr->ln_stat.st_mtim.tv_sec); + break; + case ' ': /* just ignore this without warning */ + break; + default: + LOGERR("WARNING: Invalid character 0x%02X ignoring it\n", format[i]); + break; + }; + } + out_print_newline(); + ptr = ptr->next; + } + if (lh != list) destroy_list(lh); +} + -- cgit v1.2.3-70-g09d2