LCOV - code coverage report
Current view: top level - src - ft_config.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 49.3 % 296 146
Test Date: 2025-10-15 21:43:52 Functions: 64.7 % 17 11

            Line data    Source code
       1              : #include <apr_getopt.h>
       2              : #include <apr_strings.h>
       3              : #include <apr_user.h>
       4              : #include <pcre.h>
       5              : #include <unistd.h>
       6              : #include <grp.h>
       7              : 
       8              : #include "config.h"
       9              : #include "debug.h"
      10              : #include "ft_config.h"
      11              : #include "ft_system.h"
      12              : #include "ft_types.h"
      13              : #include "human_size.h"
      14              : #include "key_hash.h"
      15              : 
      16              : /* Forward declarations for key functions defined in ftwin.c */
      17              : const void *ft_fsize_get_key(const void *opaque);
      18              : const void *ft_gids_get_key(const void *opaque);
      19              : 
      20         1192 : int ft_file_cmp(const void *param1, const void *param2)
      21              : {
      22         1192 :     const ft_file_t *file1 = (const ft_file_t *) param1;
      23         1192 :     const ft_file_t *file2 = (const ft_file_t *) param2;
      24              : 
      25         1192 :     if (file1->size < file2->size) {
      26          310 :         return -1;
      27              :     }
      28          882 :     if (file2->size < file1->size) {
      29          635 :         return 1;
      30              :     }
      31              : 
      32          247 :     return 0;
      33              : }
      34              : 
      35              : /* Default ignore patterns */
      36              : static const char *const default_ignores[] = {
      37              :     /* VCS */
      38              :     ".git/", ".hg/", ".svn/",
      39              :     /* Build Artifacts */
      40              :     "build/", "dist/", "out/", "target/", "bin/",
      41              :     "*.o", "*.class", "*.pyc", "*.pyo",
      42              :     /* Dependency Caches */
      43              :     "node_modules/", "vendor/", ".venv/",
      44              :     /* OS & Editor Artifacts */
      45              :     ".DS_Store", "Thumbs.db", "*.swp", "*~", ".idea/", ".vscode/",
      46              :     NULL
      47              : };
      48              : 
      49            1 : static apr_status_t ft_pcre_free_cleanup(void *pcre_space)
      50              : {
      51            1 :     pcre_free(pcre_space);
      52            1 :     return APR_SUCCESS;
      53              : }
      54              : 
      55            1 : static pcre *ft_pcre_compile(const char *regex, int caseless, apr_pool_t *pool)
      56              : {
      57            1 :     const char *errptr = NULL;
      58            1 :     int erroffset = 0;
      59            1 :     int options = PCRE_DOLLAR_ENDONLY | PCRE_DOTALL;
      60            1 :     pcre *result = NULL;
      61              : 
      62            1 :     if (caseless) {
      63            0 :         options |= PCRE_CASELESS;
      64              :     }
      65              : 
      66            1 :     result = pcre_compile(regex, options, &errptr, &erroffset, NULL);
      67            1 :     if (NULL == result) {
      68            0 :         DEBUG_ERR("can't parse %s at [%.*s] for -e / --regex-ignore-file: %s", regex, erroffset, regex, errptr);
      69              :     }
      70              :     else {
      71            1 :         apr_pool_cleanup_register(pool, result, ft_pcre_free_cleanup, apr_pool_cleanup_null);
      72              :     }
      73              : 
      74              : 
      75            1 :     return result;
      76              : }
      77              : 
      78              : static const int MAX_GIDS = 256;
      79              : 
      80           17 : static apr_status_t fill_gids_ht(const char *username, napr_hash_t *gids, apr_pool_t *pool)
      81           17 : {
      82           17 :     gid_t list[MAX_GIDS];
      83           17 :     apr_uint32_t hash_value = 0;
      84           17 :     int nb_gid = 0;
      85              : 
      86           17 :     memset(list, 0, sizeof(list));
      87           17 :     nb_gid = getgroups((int) (sizeof(list) / sizeof(gid_t)), list);
      88           17 :     if (nb_gid < 0) {
      89            0 :         DEBUG_ERR("error calling getgroups()");
      90            0 :         return APR_EGENERAL;
      91              :     }
      92              :     /*
      93              :      * According to getgroups manpage:
      94              :      * It is unspecified whether the effective group ID of the calling process
      95              :      * is included in the returned list.  (Thus, an application should also
      96              :      * call getegid(2) and add or remove the resulting value.)
      97              :      */
      98           17 :     if (nb_gid < (sizeof(list) / sizeof(gid_t))) {
      99           17 :         list[nb_gid] = getegid();
     100           17 :         nb_gid++;
     101              :     }
     102              : 
     103          119 :     for (int idx = 0; idx < nb_gid; idx++) {
     104          102 :         ft_gid_t *gid = napr_hash_search(gids, &(list[idx]), sizeof(gid_t), &hash_value);
     105          102 :         if (NULL == gid) {
     106           85 :             gid = apr_palloc(pool, sizeof(struct ft_gid_t));
     107           85 :             gid->val = list[idx];
     108           85 :             napr_hash_set(gids, gid, hash_value);
     109              :         }
     110              :     }
     111              : 
     112           17 :     return APR_SUCCESS;
     113              : }
     114              : 
     115            0 : static void ft_hash_add_ignore_list(napr_hash_t *hash, const char *file_list)
     116              : {
     117            0 :     const char *filename = NULL;
     118            0 :     const char *end = NULL;
     119            0 :     apr_uint32_t hash_value = 0;
     120            0 :     apr_pool_t *pool = NULL;
     121            0 :     char *tmp = NULL;
     122              : 
     123            0 :     pool = napr_hash_pool_get(hash);
     124            0 :     filename = file_list;
     125              :     do {
     126            0 :         end = strchr(filename, ',');
     127            0 :         if (NULL != end) {
     128            0 :             tmp = apr_pstrndup(pool, filename, end - filename);
     129              :         }
     130              :         else {
     131            0 :             tmp = apr_pstrdup(pool, filename);
     132              :         }
     133            0 :         napr_hash_search(hash, tmp, strlen(tmp), &hash_value);
     134            0 :         napr_hash_set(hash, tmp, hash_value);
     135              : 
     136            0 :         if (NULL != end) {
     137            0 :             filename = end + 1;
     138              :         }
     139            0 :     } while ((NULL != end) && ('\0' != *filename));
     140            0 : }
     141              : 
     142           17 : static void ft_load_defaults(ft_conf_t *conf)
     143              : {
     144          374 :     for (int idx = 0; default_ignores[idx] != NULL; idx++) {
     145          357 :         ft_ignore_add_pattern_str(conf->global_ignores, default_ignores[idx]);
     146              :     }
     147           17 : }
     148              : 
     149            0 : static void version(void)
     150              : {
     151            0 :     (void) fprintf(stdout, PACKAGE_STRING "\n");
     152            0 :     (void) fprintf(stdout, "Copyright (C) 2007 François Pesce\n");
     153            0 :     (void) fprintf(stdout, "Licensed under the Apache License, Version 2.0 (the \"License\");\n");
     154            0 :     (void) fprintf(stdout, "you may not use this file except in compliance with the License.\n");
     155            0 :     (void) fprintf(stdout, "You may obtain a copy of the License at\n");
     156            0 :     (void) fprintf(stdout, "\n");
     157            0 :     (void) fprintf(stdout, "\thttp://www.apache.org/licenses/LICENSE-2.0\n");
     158            0 :     (void) fprintf(stdout, "\n");
     159            0 :     (void) fprintf(stdout, "Unless required by applicable law or agreed to in writing, software\n");
     160            0 :     (void) fprintf(stdout, "distributed under the License is distributed on an \"AS IS\" BASIS,\n");
     161            0 :     (void) fprintf(stdout, "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n");
     162            0 :     (void) fprintf(stdout, "See the License for the specific language governing permissions and\n");
     163            0 :     (void) fprintf(stdout, "limitations under the License.\n\n");
     164            0 :     (void) fprintf(stdout, "Report bugs to " PACKAGE_BUGREPORT "\n");
     165            0 : }
     166              : 
     167            0 : static void usage(const char *name, const apr_getopt_option_t *opt_option)
     168              : {
     169            0 :     (void) fprintf(stdout, PACKAGE_STRING "\n");
     170            0 :     (void) fprintf(stdout, "Usage: %s [OPTION]... [FILES or DIRECTORIES]...\n", name);
     171            0 :     (void) fprintf(stdout, "Find identical files passed as parameter or recursively found in directories.\n");
     172            0 :     (void) fprintf(stdout, "\n");
     173            0 :     (void) fprintf(stdout, "Mandatory arguments to long options are mandatory for short options too.\n");
     174            0 :     (void) fprintf(stdout, "\n");
     175              : 
     176            0 :     for (int idx = 0; NULL != opt_option[idx].name; idx++) {
     177            0 :         (void) fprintf(stdout, "-%c,\t--%s\t%s\n", opt_option[idx].optch, opt_option[idx].name, opt_option[idx].description);
     178              :     }
     179            0 : }
     180              : 
     181            0 : static void print_usage_and_exit(const char *name, const apr_getopt_option_t *opt_option, const char *error_msg,
     182              :                                  const char *arg)
     183              : {
     184            0 :     if (error_msg) {
     185            0 :         (void) fprintf(stderr, "Error: %s %s\n\n", error_msg, arg);
     186              :     }
     187            0 :     usage(name, opt_option);
     188            0 :     exit(EXIT_FAILURE);
     189              : }
     190              : 
     191              : static const int HASH_STR_BUCKET_SIZE = 32;
     192              : static const int HASH_STR_MAX_ENTRIES = 8;
     193              : static const int HASH_SIZE_BUCKET_SIZE = 4096;
     194              : static const int HASH_SIZE_MAX_ENTRIES = 8;
     195              : static const apr_off_t EXCESS_SIZE_DEFAULT = (50LL * 1024 * 1024);
     196              : 
     197           17 : ft_conf_t *ft_config_create(apr_pool_t *pool)
     198              : {
     199           17 :     apr_uint32_t hash_value = 0;
     200           17 :     ft_conf_t *conf = apr_pcalloc(pool, sizeof(ft_conf_t));
     201              : 
     202           17 :     conf->pool = pool;
     203           17 :     conf->heap = napr_heap_make(pool, ft_file_cmp);
     204           17 :     conf->ig_files = napr_hash_str_make(pool, HASH_STR_BUCKET_SIZE, HASH_STR_MAX_ENTRIES);
     205           17 :     conf->sizes = napr_hash_make(pool, HASH_SIZE_BUCKET_SIZE, HASH_SIZE_MAX_ENTRIES, ft_fsize_get_key,
     206              :                                  ft_fsize_get_key_len, apr_off_t_key_cmp, apr_off_t_key_hash);
     207           17 :     conf->gids = napr_hash_make(pool, HASH_SIZE_BUCKET_SIZE, HASH_SIZE_MAX_ENTRIES, ft_gids_get_key, ft_gid_get_key_len,
     208              :                                 gid_t_key_cmp, gid_t_key_hash);
     209              : 
     210              :     /* To avoid endless loop, ignore looping directory ;) */
     211           17 :     napr_hash_search(conf->ig_files, ".", 1, &hash_value);
     212           17 :     napr_hash_set(conf->ig_files, ".", hash_value);
     213           17 :     napr_hash_search(conf->ig_files, "..", 2, &hash_value);
     214           17 :     napr_hash_set(conf->ig_files, "..", hash_value);
     215              : 
     216           17 :     conf->ig_regex = NULL;
     217           17 :     conf->wl_regex = NULL;
     218           17 :     conf->ar_regex = NULL;
     219           17 :     conf->p_path = NULL;
     220           17 :     conf->p_path_len = 0;
     221           17 :     conf->minsize = 0;
     222           17 :     conf->maxsize = 0;
     223           17 :     conf->sep = '\n';
     224           17 :     conf->excess_size = (apr_off_t) EXCESS_SIZE_DEFAULT;
     225           17 :     conf->num_threads = ft_get_cpu_cores();  /* Default to number of CPU cores */
     226           17 :     conf->respect_gitignore = 1;     /* Respect .gitignore by default */
     227           17 :     conf->global_ignores = ft_ignore_context_create(pool, NULL, "/");      /* Initialize global ignores */
     228           17 :     ft_load_defaults(conf);     /* Load default ignore patterns */
     229           17 :     conf->mask = OPTION_RECSD;
     230           17 :     conf->threshold = PUZZLE_CVEC_SIMILARITY_LOWER_THRESHOLD;
     231              : 
     232           17 :     return conf;
     233              : }
     234              : 
     235              : static const double DEFAULT_THRESHOLD = 0.5;
     236              : 
     237              : static const apr_getopt_option_t opt_option[] = {
     238              :     {"hidden", 'a', FALSE, "do not ignore hidden files."},
     239              :     {"case-unsensitive", 'c', FALSE, "this option applies to regex match."},
     240              :     {"display-size", 'd', FALSE, "\tdisplay size before duplicates (human-readable)."},
     241              :     {"dry-run", 'n', FALSE, "\tonly print the operations that would be done."},
     242              :     {"regex-ignore-file", 'e', TRUE, "filenames that match this are ignored."},
     243              :     {"follow-symlink", 'f', FALSE, "follow symbolic links."},
     244              :     {"help", 'h', FALSE, "\t\tdisplay usage."},
     245              :     {"image-cmp", 'I', FALSE, "\twill run ftwin in image cmp mode (using libpuzzle)."},
     246              :     {"image-threshold", 'T', TRUE,
     247              :      "will change the image similarity threshold\n\t\t\t\t (default is [1], accepted [2/3/4/5])."},
     248              :     {"ignore-list", 'i', TRUE, "\tcomma-separated list of file names to ignore."},
     249              : #if HAVE_JANSSON
     250              :     {"json", 'J', FALSE, "\t\toutput results in machine-readable JSON format."},
     251              : #endif
     252              :     {"minimal-length", 'm', TRUE, "minimum size of file to process."},
     253              :     {"max-size", 'M', TRUE, "maximum size of file to process."},
     254              :     {"optimize-memory", 'o', FALSE, "reduce memory usage, but increase process time."},
     255              :     {"priority-path", 'p', TRUE, "\tfile in this path are displayed first when\n\t\t\t\tduplicates are reported."},
     256              :     {"recurse-subdir", 'r', FALSE, "recurse subdirectories (default: on)."},
     257              :     {"no-recurse", 'R', FALSE, "do not recurse in subdirectories."},
     258              :     {"separator", 's', TRUE, "\tseparator character between twins, default: \\n."},
     259              :     {"tar-cmp", 't', FALSE, "\twill process files archived in .tar default: off."},
     260              :     {"threads", 'j', TRUE, "\tnumber of threads for parallel hashing (default: CPU cores)."},
     261              :     {"verbose", 'v', FALSE, "\tdisplay a progress bar."},
     262              :     {"version", 'V', FALSE, "\tdisplay version."},
     263              :     {"whitelist-regex-file", 'w', TRUE, "filenames that doesn't match this are ignored."},
     264              :     {"excessive-size", 'x', TRUE, "excessive size of file that switch off mmap use."},
     265              :     {NULL, 0, 0, NULL},         /* end (a.k.a. sentinel) */
     266              : };
     267              : 
     268              : /**
     269              :  * @brief A structure to hold pointers to the various regex string options.
     270              :  *
     271              :  * This is used to avoid passing multiple `char **` arguments to helper
     272              :  * functions, which can be error-prone (swappable parameters).
     273              :  */
     274              : struct regex_options
     275              : {
     276              :     char **ignore_regex;     /**< Pointer to the ignore regex string. */
     277              :     char **whitelist_regex;  /**< Pointer to the whitelist regex string. */
     278              :     char **archive_regex;    /**< Pointer to the archive regex string. */
     279              : };
     280              : 
     281              : /* Forward declarations for helper functions */
     282              : 
     283              : /**
     284              :  * @brief Maps a command-line option character to its corresponding flag and value.
     285              :  */
     286              : typedef struct
     287              : {
     288              :     int option_char;    /**< The single-character option, e.g., 'a'. */
     289              :     int option_flag;    /**< The flag to set, e.g., OPTION_SHOW_HIDDEN. */
     290              :     int value;          /**< The value to set (1 for on, 0 for off). */
     291              : } flag_option_mapping_t;
     292              : 
     293              : static void handle_flag_option(int option, ft_conf_t *conf);
     294              : static void handle_string_option(int option, const char *optarg, ft_conf_t *conf, struct regex_options *opts);
     295              : static void handle_numeric_option(int option, const char *optarg, ft_conf_t *conf, const char *name,
     296              :                                   const apr_getopt_option_t *opt_option);
     297              : static void handle_special_option(int option, const char *optarg, ft_conf_t *conf, struct regex_options *opts,
     298              :                                   const char *name, const apr_getopt_option_t *opt_option);
     299              : 
     300           17 : static void process_options(int option, const char *optarg, ft_conf_t *conf, struct regex_options *opts, const char *name)
     301              : {
     302           17 :     switch (option) {
     303              :         /* Simple Flags */
     304            2 :     case 'a':
     305              :     case 'c':
     306              :     case 'd':
     307              :     case 'n':
     308              :     case 'f':
     309              :     case 'o':
     310              :     case 'r':
     311              :     case 'R':
     312              :     case 'v':
     313            2 :         handle_flag_option(option, conf);
     314            2 :         break;
     315              : 
     316              :         /* String Arguments */
     317            0 :     case 'e':
     318              :     case 'i':
     319              :     case 'p':
     320              :     case 's':
     321              :     case 'w':
     322            0 :         handle_string_option(option, optarg, conf, opts);
     323            0 :         break;
     324              : 
     325              :         /* Numeric Arguments */
     326           13 :     case 'j':
     327              :     case 'm':
     328              :     case 'M':
     329              :     case 'x':
     330           13 :         handle_numeric_option(option, optarg, conf, name, opt_option);
     331           13 :         break;
     332              : 
     333              :         /* Special/Complex Cases */
     334            2 :     case 'h':
     335              :     case 'V':
     336              :     case 'I':
     337              :     case 'T':
     338              :     case 'J':
     339              :     case 't':
     340            2 :         handle_special_option(option, optarg, conf, opts, name, opt_option);
     341            2 :         break;
     342              : 
     343            0 :     default:
     344              :         /* Should not happen. */
     345            0 :         break;
     346              :     }
     347           17 : }
     348              : 
     349              : static const flag_option_mapping_t flag_mappings[] = {
     350              :     {'a', OPTION_SHOW_HIDDEN, 1},
     351              :     {'c', OPTION_ICASE, 1},
     352              :     {'d', OPTION_SIZED, 1},
     353              :     {'n', OPTION_DRY_RUN, 1},
     354              :     {'f', OPTION_FSYML, 1},
     355              :     {'o', OPTION_OPMEM, 1},
     356              :     {'r', OPTION_RECSD, 1},
     357              :     {'R', OPTION_RECSD, 0}
     358              : };
     359              : 
     360            2 : static void handle_flag_option(int option, ft_conf_t *conf)
     361              : {
     362            9 :     for (size_t idx = 0; idx < sizeof(flag_mappings) / sizeof(flag_mappings[0]); ++idx) {
     363            9 :         if (flag_mappings[idx].option_char == option) {
     364            2 :             set_option(&conf->mask, flag_mappings[idx].option_flag, flag_mappings[idx].value);
     365            2 :             return;
     366              :         }
     367              :     }
     368              : 
     369            0 :     if (option == 'v') {
     370              :         /* The verbose flag is a special case: it should only be set if JSON output is not enabled. */
     371            0 :         if (!is_option_set(conf->mask, OPTION_JSON)) {
     372            0 :             set_option(&conf->mask, OPTION_VERBO, 1);
     373              :         }
     374              :     }
     375              : }
     376              : 
     377            0 : static void handle_string_option(int option, const char *optarg, ft_conf_t *conf, struct regex_options *opts)
     378              : {
     379            0 :     switch (option) {
     380            0 :     case 'e':
     381            0 :         *(opts->ignore_regex) = apr_pstrdup(conf->pool, optarg);
     382            0 :         break;
     383            0 :     case 'i':
     384            0 :         ft_hash_add_ignore_list(conf->ig_files, optarg);
     385            0 :         break;
     386            0 :     case 'p':
     387            0 :         conf->p_path = apr_pstrdup(conf->pool, optarg);
     388            0 :         conf->p_path_len = strlen(conf->p_path);
     389            0 :         break;
     390            0 :     case 's':
     391            0 :         conf->sep = *optarg;
     392            0 :         break;
     393            0 :     case 'w':
     394            0 :         *(opts->whitelist_regex) = apr_pstrdup(conf->pool, optarg);
     395            0 :         break;
     396            0 :     default:
     397              :         /* Should not happen. */
     398            0 :         break;
     399              :     }
     400            0 : }
     401              : 
     402           13 : static void handle_numeric_option(int option, const char *optarg, ft_conf_t *conf, const char *name,
     403              :                                   const apr_getopt_option_t *opt_option)
     404              : {
     405           13 :     switch (option) {
     406           11 :     case 'j':{
     407           11 :             char *endptr = NULL;
     408           11 :             long threads = strtol(optarg, &endptr, BASE_TEN);
     409           11 :             if (*endptr != '\0' || threads < 1 || threads > MAX_THREADS) {
     410            0 :                 print_usage_and_exit(name, opt_option, "Invalid number of threads (must be 1-256):", optarg);
     411              :             }
     412           11 :             conf->num_threads = (unsigned int) threads;
     413           11 :             break;
     414              :         }
     415            1 :     case 'm':
     416            1 :         conf->minsize = parse_human_size(optarg);
     417            1 :         if (conf->minsize < 0) {
     418            0 :             print_usage_and_exit(name, opt_option, "Invalid size for --minimal-length:", optarg);
     419              :         }
     420            1 :         break;
     421            1 :     case 'M':
     422            1 :         conf->maxsize = parse_human_size(optarg);
     423            1 :         if (conf->maxsize < 0) {
     424            0 :             print_usage_and_exit(name, opt_option, "Invalid size for --max-size:", optarg);
     425              :         }
     426            1 :         break;
     427            0 :     case 'x':
     428            0 :         conf->excess_size = (apr_off_t) strtoul(optarg, NULL, BASE_TEN);
     429            0 :         if (ULONG_MAX == conf->minsize) {
     430            0 :             print_usage_and_exit(name, opt_option, "can't parse for -x / --excessive-size", optarg);
     431              :         }
     432            0 :         break;
     433            0 :     default:
     434              :         /* Should not happen. */
     435            0 :         break;
     436              :     }
     437           13 : }
     438              : 
     439              : /**
     440              :  * @brief Handles image-specific command-line options ('I' and 'T').
     441              :  *
     442              :  * This function centralizes the logic for image comparison options,
     443              :  * reducing the complexity of the main option handling switch.
     444              :  */
     445            0 : static void handle_image_options(int option, const char *optarg, ft_conf_t *conf, char **wregex, const char *name,
     446              :                                  const apr_getopt_option_t *opt_option)
     447              : {
     448            0 :     switch (option) {
     449            0 :     case 'I':
     450            0 :         set_option(&conf->mask, OPTION_ICASE, 1);
     451            0 :         set_option(&conf->mask, OPTION_PUZZL, 1);
     452            0 :         *wregex = apr_pstrdup(conf->pool, ".*\\.(gif|png|jpe?g)$");
     453            0 :         break;
     454            0 :     case 'T':
     455            0 :         switch (*optarg) {
     456            0 :         case '1':
     457            0 :             conf->threshold = PUZZLE_CVEC_SIMILARITY_LOWER_THRESHOLD;
     458            0 :             break;
     459            0 :         case '2':
     460            0 :             conf->threshold = PUZZLE_CVEC_SIMILARITY_LOW_THRESHOLD;
     461            0 :             break;
     462            0 :         case '3':
     463            0 :             conf->threshold = DEFAULT_THRESHOLD;
     464            0 :             break;
     465            0 :         case '4':
     466            0 :             conf->threshold = PUZZLE_CVEC_SIMILARITY_THRESHOLD;
     467            0 :             break;
     468            0 :         case '5':
     469            0 :             conf->threshold = PUZZLE_CVEC_SIMILARITY_HIGH_THRESHOLD;
     470            0 :             break;
     471            0 :         default:
     472            0 :             print_usage_and_exit(name, opt_option, "invalid threshold:", optarg);
     473              :         }
     474            0 :         break;
     475            0 :     default:
     476              :         /* Should not be reached */
     477            0 :         break;
     478              :     }
     479            0 : }
     480              : 
     481            2 : static void handle_special_option(int option, const char *optarg, ft_conf_t *conf, struct regex_options *opts,
     482              :                                   const char *name, const apr_getopt_option_t *opt_option)
     483              : {
     484            2 :     switch (option) {
     485            0 :     case 'h':
     486            0 :         usage(name, opt_option);
     487            0 :         exit(0);
     488            0 :     case 'V':
     489            0 :         version();
     490            0 :         exit(0);
     491            0 :     case 'I':
     492              :     case 'T':
     493            0 :         handle_image_options(option, optarg, conf, opts->whitelist_regex, name, opt_option);
     494            0 :         break;
     495              : #if HAVE_JANSSON
     496            1 :     case 'J':
     497            1 :         set_option(&conf->mask, OPTION_JSON, 1);
     498            1 :         if (is_option_set(conf->mask, OPTION_VERBO)) {
     499            0 :             (void) fprintf(stderr, "Warning: Verbose mode disabled for JSON output.\n");
     500            0 :             set_option(&conf->mask, OPTION_VERBO, 0);
     501              :         }
     502            1 :         break;
     503              : #endif
     504            1 :     case 't':
     505            1 :         set_option(&conf->mask, OPTION_UNTAR, 1);
     506            1 :         *(opts->archive_regex) = apr_pstrdup(conf->pool, ".*\\.(tar\\.gz|tgz|tar\\.bz2|tbz2|tar\\.xz|txz|zip|rar|7z|tar)$");
     507            1 :         break;
     508            0 :     default:
     509              :         /* Should not happen. */
     510            0 :         break;
     511              :     }
     512            2 : }
     513              : 
     514           17 : apr_status_t ft_config_parse_args(ft_conf_t *conf, int argc, const char **argv, int *first_arg_index)
     515           17 : {
     516           17 :     char errbuf[ERROR_BUFFER_SIZE];
     517           17 :     char *regex_str = NULL;
     518           17 :     char *wregex_str = NULL;
     519           17 :     char *arregex_str = NULL;
     520           17 :     struct regex_options opts = { &regex_str, &wregex_str, &arregex_str };
     521           17 :     apr_getopt_t *opt_state = NULL;
     522           17 :     const char *optarg = NULL;
     523           17 :     int option = 0;
     524           17 :     apr_status_t status = APR_SUCCESS;
     525              : 
     526           17 :     memset(errbuf, 0, sizeof(errbuf));
     527           17 :     status = apr_getopt_init(&opt_state, conf->pool, argc, argv);
     528           17 :     if (APR_SUCCESS != status) {
     529            0 :         DEBUG_ERR("error calling apr_getopt_init: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
     530            0 :         return status;
     531              :     }
     532              : 
     533           34 :     while (APR_SUCCESS == (status = apr_getopt_long(opt_state, opt_option, &option, &optarg))) {
     534           17 :         process_options(option, optarg, conf, &opts, argv[0]);
     535              :     }
     536              : 
     537           17 :     status = apr_uid_current(&(conf->userid), &(conf->groupid), conf->pool);
     538           17 :     if (APR_SUCCESS != status) {
     539            0 :         DEBUG_ERR("error calling apr_uid_current: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
     540            0 :         return status;
     541              :     }
     542              : 
     543           17 :     status = apr_uid_name_get(&(conf->username), conf->userid, conf->pool);
     544           17 :     if (APR_SUCCESS != status) {
     545            0 :         DEBUG_ERR("error calling apr_uid_name_get: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
     546            0 :         return status;
     547              :     }
     548              : 
     549           17 :     status = fill_gids_ht(conf->username, conf->gids, conf->pool);
     550           17 :     if (APR_SUCCESS != status) {
     551            0 :         DEBUG_ERR("error calling fill_gids_ht: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
     552            0 :         return status;
     553              :     }
     554              : 
     555           17 :     if (NULL != regex_str) {
     556            0 :         conf->ig_regex = ft_pcre_compile(regex_str, is_option_set(conf->mask, OPTION_ICASE), conf->pool);
     557            0 :         if (NULL == conf->ig_regex) {
     558            0 :             return APR_EGENERAL;
     559              :         }
     560              :     }
     561              : 
     562           17 :     if (NULL != wregex_str) {
     563            0 :         conf->wl_regex = ft_pcre_compile(wregex_str, is_option_set(conf->mask, OPTION_ICASE), conf->pool);
     564            0 :         if (NULL == conf->wl_regex) {
     565            0 :             return APR_EGENERAL;
     566              :         }
     567              :     }
     568              : 
     569           17 :     if (NULL != arregex_str) {
     570            1 :         conf->ar_regex = ft_pcre_compile(arregex_str, is_option_set(conf->mask, OPTION_ICASE), conf->pool);
     571            1 :         if (NULL == conf->ar_regex) {
     572            0 :             return APR_EGENERAL;
     573              :         }
     574              :     }
     575              : 
     576              :     /* Return the index of the first non-option argument */
     577           17 :     if (first_arg_index != NULL) {
     578           17 :         *first_arg_index = opt_state->ind;
     579              :     }
     580              : 
     581           17 :     return APR_SUCCESS;
     582              : }
        

Generated by: LCOV version 2.0-1