/* SPDX-License-Identifier: Apache-2.0 */ /* Copyright 2026 Thorsten Töpper * * vim:ts=4:sw=4:expandtab */ #include #include #include #include #include #include #include "options.h" #include "trace_macros.h" /* === GLOBAL VARIABLES === */ struct option long_options[] = { { "all", no_argument, 0, 0 }, { "database", required_argument, 0 ,0 }, { "help", no_argument, 0, 0 }, { "kv-storage", required_argument, 0, 0}, { "new-kv", no_argument, 0, 0 }, { "quiet", no_argument, 0, 0 }, { 0, 0, 0, 0 } }; enum operation_modes option_mode = MODE_DEV_MESSED_UP; bool option_clean_kv = false; bool option_quiet = false; bool option_show_hidden_entries = false; char *option_gdbm_db_name = "/tmp/duplicate_finder.gdbm"; char *option_sqlite_db_name = "duplicate_finder.sqlite"; char *exec_name; /* === IMPLEMENTATION === */ void usage(char *executable) { fprintf(stderr, "Call: %s OPTIONS scan|analyze [path]\n", executable); fprintf(stderr, "\nOPTIONS are\n"); /* long name, short name, optional argument, explanation */ fprintf(stderr, " %-25s %2s %10s - %s\n", "--all", "-a", "", "Also process and show files hidden on the filesystem"); fprintf(stderr, " %-25s %2s %10s - %s\n", "--database", "-d", "", "fullname of the sqlite db to be used"); fprintf(stderr, " %-25s %2s %10s - %s\n", "--help", "-h", "", "Show this message and exit"); fprintf(stderr, " %-25s %2s %10s - %s\n", "--kv-storage", "-k", "", "fullname of the gdbm storage file used during filesystem scans"); fprintf(stderr, " %-25s %2s %10s - %s\n", "--new-kv", "", "", "If the kv storage file already exists replace it with a new one"); fprintf(stderr, " %-25s %2s %10s - %s\n", "--quiet", "-q", "", "Don't print error messages or warnings"); } 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 (strcmp("all", option_name) == 0) { option_show_hidden_entries = true; return; } if (option_name == NULL) return; /* options WITHOUT arguments */ if (strcmp("help", option_name) == 0) { usage(exec_name); exit(EXIT_SUCCESS); } if (strcmp("new-kv", option_name) == 0) { option_clean_kv = true; return; } if (strcmp("quiet", option_name) == 0) { option_quiet = 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); } /* No checks regarding validity of the paths here, if the lib can't * work with it, it will react with a error which will be handled. */ if (strcmp("database", option_name) == 0) { option_sqlite_db_name = option_argument; return; } if (strcmp("kv-storage", option_name) == 0) { option_gdbm_db_name = option_argument; 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, "ad:hk:q", long_options, &index); if (c == -1) { break; } switch (c) { case 0: set_option(long_options[index].name, optarg); break; case 'a': option_show_hidden_entries = true; break; case 'd': option_sqlite_db_name = optarg; break; case 'h': usage(exec_name); exit(EXIT_SUCCESS); case 'k': option_gdbm_db_name = optarg; break; case 'q': option_quiet = true; break; case '?': break; default: LOGERR("ERROR: unrecognized option 0x%02X '%c'\n", c, c); usage(exec_name); exit(EXIT_FAILURE); } } if (argc == optind) { LOGERR("ERROR: Too few arguments, at least mode is required, see --help or man\n"); /* No external stuff touched so far, so just quit. */ exit(EXIT_FAILURE); } if (strcmp("scan", argv[optind]) == 0) { option_mode = MODE_SCAN; } else if (strcmp("analyze", argv[optind]) == 0) { option_mode = MODE_ANALYZE_DB; } else { LOGERR("ERROR: No valid mode given see --help or man.\n"); exit(EXIT_FAILURE); } return optind++; }