/* 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 "options.h" /* === GLOBAL VARIABLES === */ struct option long_options[] = { { "help", no_argument, 0, 0 }, { "format-string", required_argument, 0, 0 }, { "long-timestamp", no_argument, 0, 0 }, { "print-boxed", no_argument, 0, 0 }, { "print-header", no_argument, 0, 0 }, { "reverse-sort", no_argument, 0, 0 }, { "show-hidden-entries", no_argument, 0, 0 }, { "sort-by", required_argument, 0, 0 }, { "time-field", required_argument, 0, 0 }, { 0, 0, 0, 0 } }; bool option_sort_reverse_order = false; enum esort_type option_sort_type = SORT_BY_SIZE; bool option_print_boxed_table = false; bool option_print_header = false; 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 * st_ctime / st_ctim.tv_sec - time of the last change to the inode * st_mtime / st_mtim.tv_sec - time of last modification of the content */ char option_time_field = 'm'; char *exec_name; /* === 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", "--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", "--print-boxed", "", "", "Print a boxed table"); fprintf(stderr, " %-25s %2s %10s - %s\n", "--print-header", "-H", "", "Print a header above the columns"); fprintf(stderr, " %-25s %2s %10s - %s\n", "--reverse-sort", "", "", "Sort reversed"); fprintf(stderr, " %-25s %2s %10s - %s\n", "--show-hidden-entries", "-a", "", "Show hidden entries in the directory"); fprintf(stderr, " %-25s %2s %10s - %s\n", "--sort-by", "", "variant", "Sort either by size or time"); fprintf(stderr, " %-25s %2s %10s - %s\n", "--time-field", "", "a|c|m", "Sort by (a)ccess, (c)hange or (m)odification time. Default: m"); /* 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, " A - access time\n"); fprintf(stderr, " C - change time\n"); fprintf(stderr, " G - group name\n"); fprintf(stderr, " g - group id\n"); fprintf(stderr, " M - modification time\n"); fprintf(stderr, " n - file name\n"); fprintf(stderr, " p - permissions\n"); fprintf(stderr, " s - size\n"); fprintf(stderr, " T - type\n"); fprintf(stderr, " t - time defined by --time-field (default: modification time)\n"); fprintf(stderr, " U - user name\n"); fprintf(stderr, " u - user id\n"); } 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("help", option_name) == 0) { usage(exec_name); exit(EXIT_SUCCESS); } if (strcmp("long-timestamp", option_name) == 0) { option_timestamp_long = true; return; } if (strcmp("reverse-sort", option_name) == 0) { option_sort_reverse_order = true; return; } if (strcmp("print-boxed", option_name) == 0) { option_print_boxed_table = true; return; } if (strcmp("print-header", option_name) == 0) { option_print_header = true; return; } if (strcmp("show-hidden-entries", option_name) == 0) { option_show_hidden_entries = 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); 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; } else 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 invalid argument for %s\n", option_argument, option_name); } return; } if (strcmp("time-field", option_name) == 0) { switch (option_argument[0]) { case 'a': option_time_field = 'a'; break; case 'c': option_time_field = 'c'; break; case 'm': option_time_field = 'm'; break; default: /* Keep in mind: isgraph ignores space, but an explicit call --time-field ' ' is strange */ LOGERR("WARNING: --time-field: invalid value %c 0x%02X - falling back to default m\n", ((isgraph(option_argument[0])) ? option_argument[0] : ' '), option_argument[0]); option_time_field = 'm'; break; }; return; } LOGERR("ERROR: Option '%s' not recognized\n.", option_name); } int parse_arguments(int argc, char **argv) { int c = 0, index; /* exec_name is a file internal global variable for --help in set_option() */ exec_name = argv[0]; while(1) { index = 0; c = getopt_long(argc, argv, "aHht", long_options, &index); if (c == -1) { break; } switch (c) { case 0: set_option(long_options[index].name, optarg); break; case 'h': usage(exec_name); exit(EXIT_SUCCESS); case 'H': option_print_header = true; break; case 't': option_timestamp_long = true; break; case 'a': option_show_hidden_entries = true; break; case '?': break; default: LOGERR("ERROR: unrecognized option 0x%02X '%c'\n", c, c); usage(exec_name); exit(EXIT_FAILURE); } } return optind; }