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 = { ®ex_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 : }
|