aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt7
-rw-r--r--include/list_management.h35
-rw-r--r--include/options.h44
-rw-r--r--include/output.h29
-rw-r--r--src/dir_monitor.c331
-rw-r--r--src/list_management.c131
-rw-r--r--src/options.c131
-rw-r--r--src/output.c87
8 files changed, 466 insertions, 329 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0e9238a..163f941 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,18 +10,23 @@ set (CMAKE_C_STANDARD 17)
add_compile_options(-Wall)
-set(SOURCE_DM src/dir_monitor.c)
+set(SOURCE_DM src/dir_monitor.c src/list_management.c src/options.c src/output.c)
add_executable(dir_monitor_debug ${SOURCE_DM})
+target_include_directories(dir_monitor_debug PRIVATE include)
target_compile_options(dir_monitor_debug PUBLIC -g -DDEBUGBUILD -Werror)
add_executable(dir_monitor_debug_asan ${SOURCE_DM})
+target_include_directories(dir_monitor_debug_asan PRIVATE include)
target_compile_options(dir_monitor_debug_asan PUBLIC -g -DDEBUGBUILD -fsanitize=address -Werror)
target_link_libraries(dir_monitor_debug_asan asan)
add_executable(dir_monitor_asan ${SOURCE_DM})
+target_include_directories(dir_monitor_asan PRIVATE include)
target_compile_options(dir_monitor_asan PUBLIC -fsanitize=address)
target_link_libraries(dir_monitor_asan asan)
add_executable(dir_monitor ${SOURCE_DM})
+target_include_directories(dir_monitor PRIVATE include)
+target_compile_options(dir_monitor_debug PUBLIC -O2 -Werror)
diff --git a/include/list_management.h b/include/list_management.h
new file mode 100644
index 0000000..0a887c7
--- /dev/null
+++ b/include/list_management.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/* Copyright 2025 Thorsten Töpper
+ *
+ * list_management - data handling related code
+ *
+ * vim:ts=4:sw=4:expandtab
+ */
+
+#ifndef LIST_MANAGEMENT_H
+#define LIST_MANAGEMENT_H
+
+#include <time.h>
+
+/* === DEFINITIONS === */
+struct list_node {
+ struct list_node *next;
+ size_t fsize;
+ char fname[256];
+ time_t ftime; /* can be creation, access or modification */
+};
+
+struct list_head {
+ struct list_node *first;
+};
+
+struct list_node *create_node(char *fname, size_t fsize, time_t ftime);
+struct list_head *create_list_sort_reversed(struct list_head *list);
+void destroy_list(struct list_head *list);
+int insert_sorted_by_size(struct list_head *list, struct list_node *node);
+int insert_sorted_by_time(struct list_head *list, struct list_node *node);
+
+
+#endif
+
diff --git a/include/options.h b/include/options.h
new file mode 100644
index 0000000..70f22e4
--- /dev/null
+++ b/include/options.h
@@ -0,0 +1,44 @@
+/* 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
+ */
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+#include <stdbool.h>
+
+#include "output.h"
+#include "list_management.h"
+
+
+
+/* === DEFINITIONS === */
+#define PATH_SEP '/'
+
+enum esort_type { SORT_BY_SIZE, SORT_BY_TIME };
+
+
+/* === GLOBAL VARIABLES === */
+extern bool option_sort_reverse_order;
+extern enum esort_type option_sort_type;
+extern bool option_show_hidden_entries;
+extern bool option_timestamp_long;
+
+
+int parse_arguments(int argc, char **argv);
+void set_option(const char *option_name, char *option_argument);
+/* In theory a part of output, but it's easier to place it near the struct
+ * containing the options in options.c */
+void usage(char *executable);
+
+
+
+
+#endif
+
diff --git a/include/output.h b/include/output.h
new file mode 100644
index 0000000..22f3885
--- /dev/null
+++ b/include/output.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/* Copyright 2025 Thorsten Töpper
+ *
+ * output related functions and definitions
+ *
+ * vim:ts=4:sw=4:expandtab
+ */
+#ifndef OUTPUT_H
+#define OUTPUT_H
+#include <stdio.h>
+
+#include "list_management.h"
+
+
+#define LOGERR(...) {fprintf(stderr, "[%s:%d] %s: ", __FILE__, __LINE__, __func__); fprintf(stderr, __VA_ARGS__);}
+
+#ifdef DEBUGBUILD
+#define DBGTRC(...) LOGERR(__VA_ARGS__)
+#else
+#define DBGTRC(...)
+#endif
+
+
+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);
+#endif
+
diff --git a/src/dir_monitor.c b/src/dir_monitor.c
index 5323ce8..b2257ce 100644
--- a/src/dir_monitor.c
+++ b/src/dir_monitor.c
@@ -10,254 +10,22 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <getopt.h>
-#include <stdint.h>
-#include <stdbool.h>
#include <errno.h>
-#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
-#include <time.h>
-#include <sys/ioctl.h>
-
-#define LOGERR(...) {fprintf(stderr, "[%s:%d] %s: ", __FILE__, __LINE__, __func__); fprintf(stderr, __VA_ARGS__);}
-
-#ifdef DEBUGBUILD
-#define DBGTRC(...) LOGERR(__VA_ARGS__)
-#else
-#define DBGTRC(...)
-#endif
+#include "output.h"
+#include "list_management.h"
+#include "options.h"
/* === DEFINITIONS === */
-#define PATH_SEP '/'
-
-enum esort_type { SORT_BY_SIZE, SORT_BY_TIME };
-struct list_node {
- struct list_node *next;
- size_t fsize;
- char fname[256];
- time_t ftime; /* can be creation, access or modification */
-};
-
-struct list_head {
- struct list_node *first;
-};
-struct list_node *create_node(char *fname, size_t fsize, time_t ftime);
-struct list_head *create_list_sort_reversed(struct list_head *list);
-void destroy_list(struct list_head *list);
-int fputc_all_cols(char c, FILE *fdout);
-int fputc_width_x(char c, size_t x, FILE *fdout);
struct list_head *get_data_from_directory(char *path);
-int insert_sorted_by_size(struct list_head *list, struct list_node *node);
-int insert_sorted_by_time(struct list_head *list, struct list_node *node);
-int parse_arguments(int argc, char **argv);
-void print_list(struct list_head *list);
-void set_option(const char *option_name, char *option_argument);
-void usage(char *executable);
-
-
-
-/* === GLOBAL VARIABLES === */
-struct option long_options[] = {
- { "reverse-sort", no_argument, 0, 0 },
- { "show-hidden-entries", no_argument, 0, 0 },
- { "sort-by", required_argument, 0, 0 },
- { "long-timestamp", no_argument, 0, 0 },
- { 0, 0, 0, 0 }
-};
-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;
/* === IMPLEMENTATION === */
-inline struct list_node *create_node(char *fname, size_t fsize, time_t ftime) {
- struct list_node *node = NULL;
-
- if (fname == NULL || fname[0] == '\0') {
- LOGERR("ERROR: No valid filename 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;
- }
-
- node->next = NULL;
- node->fsize = fsize;
- node->ftime = ftime;
- strncpy(node->fname, fname, 256);
- 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);
-}
-
-
-inline int fputc_all_cols(char c, FILE *fdout) {
- struct winsize terminal;
- ioctl(STDOUT_FILENO, TIOCGWINSZ, &terminal);
- return fputc_width_x(c, ((terminal.ws_col == 0) ? 72 : terminal.ws_col), fdout);
-}
-
-
-/* Not the most performant way, but during output the time critical tasks are already done. */
-inline int fputc_width_x(char c, size_t x, FILE *fdout) {
- size_t i=0;
-
- if (fdout == NULL) {
- LOGERR("ERROR: No output stream given.\n");
- return EOF;
- }
-
- if ( ! isprint(c) ) { c = '?'; }
-
- for (i=0; i<x; i++) {
- if (fputc(c, fdout) == EOF) {
- LOGERR("ERROR: Failed to write char 0x%02X to stream %d\n", c, fdout->_fileno);
- return EOF;
- }
- }
-
- return 1;
-}
-
-/* dirty... */
-#define INSERT_BY_NUMERIC_FIELD(list, node, field) \
-{ struct list_node *ptr = NULL; \
- if (node->field > list->first->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->field > ptr->next->field) { \
- node->next = ptr->next; ptr->next = node; \
- return 0; } \
- ptr = ptr->next; \
- } \
-}
-
-
-/* TODO: later when, other parameters are actively used move the action to a template */
-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, fsize);
- 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;
- }
- INSERT_BY_NUMERIC_FIELD(list, node, ftime);
- return -3;
-}
-#undef INSERT_BY_NUMERIC_FIELD
-
-/* 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;
- char timestamp[128];
- size_t total_size = 0;
-
- if (list == NULL) return;
- if (option_sort_reverse_order) {
- lh = create_list_sort_reversed(list);
- /* be tolerant */
- if (lh == NULL) {
- lh = list;
- }
- }
-
- ptr = lh->first;
- while (ptr != NULL) {
- printf(" %8lu %2s ", ((ptr->fsize>=1024) ? (ptr->fsize/1024) : ptr->fsize),
- ((ptr->fsize >= 1024) ? "kB" : ""));
-
- tm = localtime(&ptr->ftime);
- 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);
- total_size += ptr->fsize;
- ptr = ptr->next;
- }
- fputc_all_cols('=', stdout);
- printf("\nTotal size: %lu %s\n", ((total_size>1024) ? total_size/1024 : total_size),
- ((total_size >= 1024) ? "kB" : ""));
-
- if (lh != list) destroy_list(lh);
-}
-
-
-
-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->fsize, ptr->ftime);
- 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.
@@ -339,99 +107,6 @@ struct list_head *get_data_from_directory(char *path) {
}
-void set_option(const char *option_name, char *option_argument) {
-
- DBGTRC("DEBUG: called with option_name '%s' and option_argument '%s'\n",
- option_name, option_argument);
-
- if (option_name == NULL)
- return;
-
- /* options WITHOUT arguments */
- if (strcmp(option_name, "show-hidden-entries") == 0) {
- option_show_hidden_entries = true;
- return;
- }
-
- if (strcmp(option_name, "long-timestamp") == 0) {
- option_timestamp_long = true;
- return;
- }
-
- if (strcmp("reverse-sort", option_name) == 0) {
- option_sort_reverse_order = true;
- return;
- }
-
- /* options WITH arguments */
- if (option_argument == NULL || option_argument[0] == '\0') {
- LOGERR("ERROR: option_name %s with missing option_argument\n",
- option_name);
- return;
- }
-
- if (strcmp(option_name, "sort-by") == 0) {
- if (strncmp("size", option_argument, 4) == 0) {
- option_sort_type = SORT_BY_SIZE;
- } else if (strncmp("time", option_argument, 4) == 0) {
- option_sort_type = SORT_BY_TIME;
- } else {
- LOGERR("WARNING: '%s' is an invalig argument for %s\n",
- option_argument, option_name);
- }
- return;
- }
-
- LOGERR("ERROR: Option '%s' not recognized\n.", option_name);
-}
-
-int parse_arguments(int argc, char **argv) {
- int c = 0, index;
-
- while(1) {
- index = 0;
- c = getopt_long(argc, argv, "tv", long_options, &index);
-
- if (c == -1) {
- break;
- }
-
- switch (c) {
- case 0:
- set_option(long_options[index].name, optarg);
- break;
- case 't':
- option_timestamp_long = true;
- break;
- case 'v':
- option_show_hidden_entries = true;
- break;
- case '?':
- break;
- default:
- LOGERR("ERROR: unrecognized option 0x%02X '%c'\n", c, c);
- break;
- }
- }
-
- return optind;
-}
-
-
-void usage(char *executable) {
- fprintf(stderr, "Call: %s OPTIONS path_to_open\n", executable);
- fprintf(stderr, "\nOPTIONS are\n");
- /* long name, short name, optional argument, explanation */
- 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", "", "",
- "Sort reversed");
- fprintf(stderr, " %-25s %2s %10s %s\n", "--show-hidden-entries", "-v", "",
- "Show hidden entries in the directory");
- fprintf(stderr, " %-25s %2s %10s %s\n", "--sort-by", "", "size|time",
- "Sort either by time or size");
-}
-
int main(int argc, char **argv) {
struct list_head *list = NULL;
int path_index = 1;
diff --git a/src/list_management.c b/src/list_management.c
new file mode 100644
index 0000000..634b884
--- /dev/null
+++ b/src/list_management.c
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/* Copyright 2025 Thorsten Töpper
+ *
+ * vim:ts=4:sw=4:expandtab
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "output.h"
+#include "list_management.h"
+
+
+
+/* === IMPLEMENTATION === */
+inline struct list_node *create_node(char *fname, size_t fsize, time_t ftime) {
+ struct list_node *node = NULL;
+
+ if (fname == NULL || fname[0] == '\0') {
+ LOGERR("ERROR: No valid filename 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;
+ }
+
+ node->next = NULL;
+ node->fsize = fsize;
+ node->ftime = ftime;
+ /* With strncpy and strict compiler options it complained. A file name
+ * can't be longer than 256 bytes, including the string terminating \0
+ * byte at the end. As the code no longer's in the same file...*/
+ memcpy(node->fname, fname, 255);
+ node->fname[255] = '\0';
+ 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->field > list->first->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->field > ptr->next->field) { \
+ node->next = ptr->next; ptr->next = node; \
+ return 0; } \
+ ptr = ptr->next; \
+ } \
+}
+
+
+/* TODO: later when, other parameters are actively used move the action to a template */
+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, fsize);
+ 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;
+ }
+ INSERT_BY_NUMERIC_FIELD(list, node, ftime);
+ return -3;
+}
+#undef INSERT_BY_NUMERIC_FIELD
+
+
+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->fsize, ptr->ftime);
+ if (cpynode == NULL) {
+ destroy_list(lh);
+ return NULL;
+ }
+ cpynode->next = lh->first;
+ lh->first = cpynode;
+ ptr = ptr->next;
+ }
+
+ return lh;
+}
+
diff --git a/src/options.c b/src/options.c
new file mode 100644
index 0000000..848eaf5
--- /dev/null
+++ b/src/options.c
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/* Copyright 2025 Thorsten Töpper
+ *
+ * options -
+ *
+ * vim:ts=4:sw=4:expandtab
+ */
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdbool.h>
+
+
+#include "output.h"
+#include "options.h"
+
+
+
+
+
+/* === GLOBAL VARIABLES === */
+struct option long_options[] = {
+ { "reverse-sort", no_argument, 0, 0 },
+ { "show-hidden-entries", no_argument, 0, 0 },
+ { "sort-by", required_argument, 0, 0 },
+ { "long-timestamp", no_argument, 0, 0 },
+ { 0, 0, 0, 0 }
+};
+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;
+
+
+
+/* === IMPLEMENTATION === */
+
+void usage(char *executable) {
+ fprintf(stderr, "Call: %s OPTIONS path_to_open\n", executable);
+ fprintf(stderr, "\nOPTIONS are\n");
+ /* long name, short name, optional argument, explanation */
+ 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", "", "",
+ "Sort reversed");
+ fprintf(stderr, " %-25s %2s %10s %s\n", "--show-hidden-entries", "-v", "",
+ "Show hidden entries in the directory");
+ fprintf(stderr, " %-25s %2s %10s %s\n", "--sort-by", "", "size|time",
+ "Sort either by time or size");
+}
+
+
+void set_option(const char *option_name, char *option_argument) {
+
+ DBGTRC("DEBUG: called with option_name '%s' and option_argument '%s'\n",
+ option_name, option_argument);
+
+ if (option_name == NULL)
+ return;
+
+ /* options WITHOUT arguments */
+ if (strcmp(option_name, "show-hidden-entries") == 0) {
+ option_show_hidden_entries = true;
+ return;
+ }
+
+ if (strcmp(option_name, "long-timestamp") == 0) {
+ option_timestamp_long = true;
+ return;
+ }
+
+ if (strcmp("reverse-sort", option_name) == 0) {
+ option_sort_reverse_order = true;
+ return;
+ }
+
+ /* options WITH arguments */
+ if (option_argument == NULL || option_argument[0] == '\0') {
+ LOGERR("ERROR: option_name %s with missing option_argument\n",
+ option_name);
+ return;
+ }
+
+ if (strcmp(option_name, "sort-by") == 0) {
+ if (strncmp("size", option_argument, 4) == 0) {
+ option_sort_type = SORT_BY_SIZE;
+ } else if (strncmp("time", option_argument, 4) == 0) {
+ option_sort_type = SORT_BY_TIME;
+ } else {
+ LOGERR("WARNING: '%s' is an invalig argument for %s\n",
+ option_argument, option_name);
+ }
+ return;
+ }
+
+ LOGERR("ERROR: Option '%s' not recognized\n.", option_name);
+}
+
+int parse_arguments(int argc, char **argv) {
+ int c = 0, index;
+
+ while(1) {
+ index = 0;
+ c = getopt_long(argc, argv, "tv", long_options, &index);
+
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 0:
+ set_option(long_options[index].name, optarg);
+ break;
+ case 't':
+ option_timestamp_long = true;
+ break;
+ case 'v':
+ option_show_hidden_entries = true;
+ break;
+ case '?':
+ break;
+ default:
+ LOGERR("ERROR: unrecognized option 0x%02X '%c'\n", c, c);
+ break;
+ }
+ }
+
+ return optind;
+}
+
diff --git a/src/output.c b/src/output.c
new file mode 100644
index 0000000..9dd274b
--- /dev/null
+++ b/src/output.c
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/* Copyright 2025 Thorsten Töpper
+ *
+ * vim:ts=4:sw=4:expandtab
+ */
+
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/ioctl.h>
+
+#include "output.h"
+#include "options.h"
+
+
+
+inline int fputc_all_cols(char c, FILE *fdout) {
+ struct winsize terminal;
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &terminal);
+ return fputc_width_x(c, ((terminal.ws_col == 0) ? 72 : terminal.ws_col), fdout);
+}
+
+
+/* Not the most performant way, but during output the time critical tasks are already done. */
+inline int fputc_width_x(char c, size_t x, FILE *fdout) {
+ size_t i=0;
+
+ if (fdout == NULL) {
+ LOGERR("ERROR: No output stream given.\n");
+ return EOF;
+ }
+
+ if ( ! isprint(c) ) { c = '?'; }
+
+ for (i=0; i<x; i++) {
+ if (fputc(c, fdout) == EOF) {
+ LOGERR("ERROR: Failed to write char 0x%02X to stream %d\n", c, fdout->_fileno);
+ return EOF;
+ }
+ }
+
+ return 1;
+}
+
+
+/* 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;
+ char timestamp[128];
+ size_t total_size = 0;
+
+ if (list == NULL) return;
+ if (option_sort_reverse_order) {
+ lh = create_list_sort_reversed(list);
+ /* be tolerant */
+ if (lh == NULL) {
+ lh = list;
+ }
+ }
+
+ ptr = lh->first;
+ while (ptr != NULL) {
+ printf(" %8lu %2s ", ((ptr->fsize>=1024) ? (ptr->fsize/1024) : ptr->fsize),
+ ((ptr->fsize >= 1024) ? "kB" : ""));
+
+ tm = localtime(&ptr->ftime);
+ 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);
+ total_size += ptr->fsize;
+ ptr = ptr->next;
+ }
+ fputc_all_cols('=', stdout);
+ printf("\nTotal size: %lu %s\n", ((total_size>1024) ? total_size/1024 : total_size),
+ ((total_size >= 1024) ? "kB" : ""));
+
+ if (lh != list) destroy_list(lh);
+}
+