Line data Source code
1 : #include "precizer.h"
2 : #include <sysexits.h>
3 :
4 : #ifdef TESTITALL
5 : static bool missing_arguments = false;
6 : #endif
7 :
8 : /**
9 : *
10 : * @brief Parse arguments with argp lib
11 : *
12 : */
13 :
14 : const char *argp_program_version = APP_NAME " " APP_VERSION;
15 :
16 : /* Program documentation. */
17 : static char doc[] =
18 : "\n" APP_NAME " " APP_VERSION " — verify file checksums at scale\n\n"
19 : BOLD APP_NAME RESET " is a lightweight and blazing-fast CLI application designed for file integrity verification and comparison, making it particularly useful for checking synchronization results. The program recursively traverses directories, generating a database of files and their checksums for quick and efficient comparisons.\n"
20 : "\n"
21 : "Built for both embedded platforms and large-scale clustered mainframes, " BOLD APP_NAME RESET " helps detect synchronization errors by comparing files and their checksums across different sources. It can also be used to analyze historical changes by comparing databases generated at different points in time from the same source.\n"
22 : "\n"
23 : "Glory to Ukraine!\n"
24 : "\vSIMPLE EXAMPLE\n"
25 : "\n"
26 : "Consider two hosts with large disks containing identical content mounted at /mnt1 and /mnt2 respectively. The task is to verify content identity and identify any differences.\n"
27 : "\n"
28 : "1. Run the program on the first machine with host name, for example “host1”:\n"
29 : "\n"
30 : APP_NAME " --progress /mnt1\n"
31 : "\n"
32 : "The program recursively traverses all directories starting from /mnt1 and the host1.db database will be created in the current directory. The --progress option visualizes progress and will show the amount of space and the number of files being examined.\n"
33 : "\n"
34 : "2. Run the program on a second machine with a host name, for example host2:\n"
35 : "\n"
36 : APP_NAME " --progress /mnt2\n"
37 : "\n"
38 : "As a result, the host2.db database will be created in the current directory.\n"
39 : "\n"
40 : "3. Transfer the host1.db and host2.db files to either machine and run the program with the appropriate parameters to compare the databases:\n"
41 : "\n"
42 : APP_NAME " --compare host1.db host2.db\n"
43 : "\n"
44 : "The following information will be displayed on the screen:\n"
45 : "\n"
46 : "* Which files are missing on “host1” but present on “host2” and vice versa.\n"
47 : "* For which files, present on both hosts, the checksums do NOT match.\n"
48 : "\n"
49 : "Note that " APP_NAME " writes only relative paths to the database. The example file “/mnt1/abc/def/aaa.txt” will be written to the database as “abc/def/aaa.txt” without /mnt1. The same thing will happen with the file “/mnt2/abc/def/aaa.txt”. Despite different mount points and different sources the files can be compared with each other under the same names “abc/def/aaa.txt” with the corresponding checksums.\n"
50 : "\n"
51 : "All other technical details could be found in README file of the project";
52 :
53 : /* A description of the arguments we accept. */
54 : static char args_doc[] = "PATH";
55 :
56 : /* The options we understand. */
57 : static struct argp_option options[] = {
58 : { 0,0,0,0,"Build database options:",2},
59 : {"lock-checksum",'k',"PCRE2_REGEXP",0,"Relative path to be treated as immutable archival data. PCRE2 regular expressions can be used to "
60 : "select files or directories whose checksums are written once to the database and never updated "
61 : "again. If no matching files exist in the database yet, their entries and checksums will still be "
62 : "created normally when they are scanned for the first time. All paths for the regular expression "
63 : "must be specified as relative, the same way as for the "
64 : BOLD "--ignore" RESET " option. For these entries, the "
65 : BOLD "--update" RESET " option will not recalculate checksums; any difference in file size, timestamps, or content will "
66 : "always be reported as data corruption instead of a reason to generate a new checksum. Multiple "
67 : "regular expressions can be specified using multiple "
68 : BOLD "--lock-checksum" RESET " options.\n"
69 : "Example:\n"
70 : BOLD APP_NAME " --update --lock-checksum=\"^archive/2077/.*\" /mnt/storage" RESET "\n",0},
71 : {"rehash-locked",'h',0,0,"Force a full SHA512 rehash for every file that is already stored "
72 : "in the database and protected by any "
73 : BOLD "--lock-checksum" RESET " pattern. "
74 : "This option must always be used together with "
75 : BOLD "--lock-checksum" RESET "; it has no effect otherwise. During an update pass, "
76 : "every locked entry found on disk is read again, its checksum is recomputed, and the "
77 : "result is compared with the value stored in the database. This provides an extra "
78 : "validation layer for immutable archives at the cost of additional I/O and CPU time, "
79 : "ensuring that frozen records are validated even when file size or timestamps alone are "
80 : "not sufficient indicators. "
81 : "The option purposefully ignores "
82 : BOLD "--watch-timestamps" RESET " — timestamp tracking can be on or off, the rehash will "
83 : "still take place. If the recomputed checksum and file size match the database entry, "
84 : "the record stays consistent but differing ctime/mtime values in the database are "
85 : "synchronized with the on-disk timestamps. If the checksum differs, the file is "
86 : "reported as corrupted just like any other locked entry.\n"
87 : "Example:\n"
88 : BOLD APP_NAME " --update --lock-checksum=\"^archive/2024/.*\" --rehash-locked /mnt/storage" RESET "\n",0},
89 : {"ignore",'e',"PCRE2_REGEXP",0,"Relative path to ignore. PCRE2 regular expressions could be used to specify "
90 : "a pattern to ignore files or directories. Attention! All paths for the regular expression must be specified as relative. To understand what a relative path looks like, just run traverses without the "
91 : BOLD "--ignore" RESET " option and look how the terminal will display "
92 : "relative paths that are written to the database.\n"
93 : "Example:\n"
94 : BOLD APP_NAME " --ignore=\"^diff2/1/.*\" tests/examples/diffs" RESET "\n"
95 : "In this example, the starting path for the traversing "
96 : "is ./tests/examples/diffs and the relative path to ignore will "
97 : "be ./tests/examples/diffs/diff2/1/ and all subdirectories (/.*).\n"
98 : "Multiple regular expressions for ignore could be specified using many "
99 : BOLD "--ignore" RESET " options at once.\n"
100 : "Example:\n"
101 : BOLD APP_NAME " --ignore=\"diff2/1/.*\" --ignore=\"diff2/2/.*\" tests/examples/diffs" RESET "\n",0 },
102 : {"include",'i',"PCRE2_REGEXP",0,"Relative path to be included. PCRE2 regular expressions. Include these relative paths even if they were excluded via the " BOLD "--ignore" RESET " option. Multiple regular expressions could be specified.\n",0 },
103 : {"db-clean-ignored",'C',0,0,"The database is protected from accidental changes by default. The option " BOLD "--db-clean-ignored" RESET " must be specified additionally in order to remove from the database mention of files that matches the regular expression passed through the " BOLD "--ignore=PCRE2_REGEXP" RESET " option(s).\n",0},
104 : {"watch-timestamps",'T',0,0,"Consider file metadata changes (creation and modification timestamps) in addition to file size when detecting changes. By default, only file size changes trigger rescanning. When this option is enabled, any changes to file timestamps or size will cause the file to be rescanned and its checksum updated in the primary database.\n",0},
105 : {"maxdepth",'m',"NUMBER",0,"Recursion depth limit. The depth of the traversal, numbered from 0 to N, where a file could be found. Representing the maximum of the starting point (from root) of the traversal. The root itself is numbered 0. " BOLD "--maxdepth=0" RESET " completely disable recursion.\n",0},
106 : {"dry-run",'n',0,0,"Perform a trial run with no changes made. The option will not affect " BOLD "--compare" RESET "\n",0},
107 : {"start-device-only",'o',0,0,"This option prevents directory traversal from descending into directories that have a different device number than the file from which the descent began.\n",0 },
108 : {"force",'f',0,0,"Use this option only in case when the PATHs that were written into the database as a result of the last scanning really need to be renewed. Warning! If this option will be used in incorrect way, information about files and their checksums against the database would be lost.\n",0},
109 : {"update",'u',0,0,"Updates the database to reflect file system changes (new, modified and deleted files). Must be used with the same initial PATH that was used when creating the database, as existing records will be replaced with data from the specified location. This option modifies database consistency. Use with caution, especially in automated scripts, as incorrect usage may lead to loss of file checksums and metadata.\n",0 },
110 : {"database",'d',"FILE",0,"Database filename. Defaults to ${HOST}.db, where HOST is the local hostname.\n",0 },
111 : {"check-level",'l',"FULL|QUICK",0,"Select database validation level: 'quick' for basic structure check, 'full' (default) for comprehensive integrity verification.\n",0 },
112 : { 0,0,0,0,"Compare databases options:",1},
113 : {"compare",'c',0,0,"Compare two databases from different sources. Requires two additional arguments specifying paths to database files, e.g.:\n" BOLD APP_NAME " --compare database1.db database2.db" RESET "\n",0 },
114 : { 0,0,0,0,"Visualizations options:\n",-1},
115 : {"silent",'s',0,0,"Don't produce any output. The option will not affect " BOLD "--compare" RESET,0 },
116 : {"verbose",'v',0,0,"Produce verbose output.",0 },
117 : {"progress",'p',0,0,"Enabling this option displays progress information but requires an initial count of files and the space they occupy to estimate execution time. The program first traverses all specified directories, counting files, folders, and symlinks before proceeding with file analysis. This initial traversal may take a significant amount of time. It is strongly recommended not to use this option when calling the program from a script.",0 },
118 : {0}
119 : };
120 :
121 : /* Parse a single option. */
122 1460 : static error_t parse_opt(
123 : int key,
124 : char *arg,
125 : struct argp_state *state)
126 : {
127 1460 : char *ptr = NULL;
128 1460 : long int argument_value = -1;
129 :
130 1460 : switch(key)
131 : {
132 148 : case 'd':
133 : // Full path to DB file
134 148 : config->db_primary_file_path = strdup(arg);
135 :
136 148 : if(config->db_primary_file_path == NULL)
137 : {
138 0 : argp_failure(state,1,0,"ERROR: Memory allocation for db_file_path failed!");
139 0 : exit(ARGP_ERR_UNKNOWN);
140 : }
141 :
142 : // Name of DB file only
143 148 : config->db_file_name = strdup(basename(arg));
144 :
145 148 : if(config->db_file_name == NULL)
146 : {
147 0 : free(config->db_primary_file_path); // Free previously allocated memory
148 0 : config->db_primary_file_path = NULL; // Set to NULL after freeing
149 0 : argp_failure(state,1,0,"ERROR: Memory allocation for db_file_name failed!");
150 0 : exit(ARGP_ERR_UNKNOWN);
151 : }
152 148 : break;
153 22 : case 'e':
154 22 : (void)add_string_to_array(&config->ignore,arg);
155 22 : break;
156 10 : case 'n':
157 10 : config->dry_run = true;
158 10 : break;
159 4 : case 'i':
160 4 : (void)add_string_to_array(&config->include,arg);
161 4 : break;
162 28 : case 'k':
163 28 : (void)add_string_to_array(&config->lock_checksum,arg);
164 28 : break;
165 4 : case 'h':
166 4 : config->rehash_locked = true;
167 4 : break;
168 36 : case 'c':
169 36 : config->compare = true;
170 36 : break;
171 2 : case 'o':
172 2 : config->start_device_only = true;
173 2 : break;
174 12 : case 'C':
175 12 : config->db_clean_ignored = true;
176 12 : break;
177 4 : case 'm':
178 4 : argument_value = strtol(arg,&ptr,10);
179 :
180 : // Validate if lont int could be casted to short int
181 : // and the argument contains a digit only
182 4 : if(argument_value >= 0 && argument_value <= 32767 && *ptr == '\0')
183 : {
184 4 : config->maxdepth = (short int)argument_value;
185 : } else {
186 0 : argp_failure(state,1,0,"ERROR: Wrong --maxdepth (-m) value. Should be an integer from 0 to 32767. See --help for more information");
187 0 : exit(ARGP_ERR_UNKNOWN);
188 : }
189 4 : break;
190 40 : case 'p':
191 40 : config->progress = true;
192 40 : break;
193 16 : case 'T':
194 16 : config->watch_timestamps = true;
195 16 : break;
196 88 : case 'u':
197 88 : config->update = true;
198 88 : break;
199 2 : case 'f':
200 2 : config->force = true;
201 2 : break;
202 2 : case 'l':
203 :
204 2 : if(0 == strncasecmp(arg,"QUICK",sizeof("QUICK")))
205 : {
206 2 : config->db_check_level = QUICK;
207 0 : } else if(0 == strncasecmp(arg,"FULL",sizeof("FULL"))){
208 0 : config->db_check_level = FULL;
209 : } else {
210 0 : return(ARGP_ERR_UNKNOWN);
211 : }
212 2 : break;
213 22 : case 's':
214 : // Global variable
215 22 : rational_logger_mode = SILENT;
216 22 : break;
217 4 : case 'v':
218 : // Global variable
219 4 : rational_logger_mode = VERBOSE;
220 4 : config->verbose = true;
221 4 : break;
222 2 : case ARGP_KEY_NO_ARGS:
223 : #ifdef TESTITALL
224 1 : missing_arguments = true;
225 1 : state->flags |= ARGP_NO_EXIT;
226 1 : argp_usage(state);
227 1 : return(EX_USAGE);
228 : #else
229 1 : argp_usage(state);
230 : #endif
231 0 : break;
232 202 : case ARGP_KEY_ARG:
233 202 : config->paths = &state->argv[state->next - 1];
234 202 : state->next = state->argc;
235 202 : break;
236 202 : case ARGP_KEY_END:
237 :
238 202 : if(config->compare == true)
239 : {
240 36 : if(state->arg_num < 2)
241 : {
242 0 : argp_failure(state,1,0,"ERROR: Too few arguments\n--compare require two arguments with paths to database files. See --help for more information");
243 36 : } else if(state->arg_num > 2){
244 0 : argp_failure(state,1,0,"ERROR: Too many arguments\n--compare require just two arguments with paths to database files. See --help for more information");
245 : }
246 166 : } else if(state->arg_num > 1){
247 0 : slog(TRACE,"Caution: multiple PATH arguments received. Multipath mode activated. It’s important to note that when comparison mode is enabled, the ORDER of the paths must be identical for the database comparison to work correctly. Number of paths: %d\n",state->arg_num);
248 : }
249 202 : break;
250 610 : default:
251 610 : return(ARGP_ERR_UNKNOWN);
252 : }
253 :
254 848 : return(0);
255 : }
256 :
257 : /* Our argp parser. */
258 : static struct argp argp = {
259 : options,parse_opt,args_doc,doc,0,0,0
260 : };
261 :
262 204 : Return parse_arguments(
263 : const int argc,
264 : char *argv[])
265 : {
266 : /// The status that will be passed to return() before exiting.
267 : /// By default, the function worked without errors.
268 204 : Return status = SUCCESS;
269 :
270 : #ifdef TESTITALL
271 102 : missing_arguments = false;
272 : #endif
273 :
274 : /* Parse our arguments; every option seen by parse_opt will be
275 : reflected in arguments. */
276 204 : argp_parse(&argp,argc,argv,
277 :
278 : #ifdef TESTITALL
279 : ARGP_NO_EXIT,
280 : #else
281 : 0,
282 : #endif
283 : 0,0);
284 :
285 : #ifdef TESTITALL
286 102 : if(true == missing_arguments)
287 : {
288 1 : status = (Return)EX_USAGE;
289 : }
290 : #endif
291 :
292 203 : if(config->paths != NULL)
293 : {
294 440 : for(int i = 0; config->paths[i]; i++)
295 : {
296 : // Remove unnecessary trailing slash at the end of the directory path
297 238 : remove_trailing_slash(config->paths[i]);
298 : }
299 : }
300 :
301 203 : if(config->compare == true)
302 : {
303 36 : if(config->paths != NULL)
304 : {
305 : // The array with database names
306 36 : config->db_file_paths = config->paths;
307 :
308 108 : for(int i = 0; config->db_file_paths[i] && (SUCCESS == status); i++)
309 : {
310 : // Create a copy of the path string for basename
311 72 : char *tmp = strdup(config->db_file_paths[i]);
312 :
313 72 : if(tmp == NULL)
314 : {
315 0 : report("Failed to duplicate string: %s",config->db_file_paths[i]);
316 0 : status = FAILURE;
317 0 : break;
318 : }
319 :
320 : // Get basename and handle possible NULL return
321 72 : const char *db_file_basename = basename(tmp);
322 :
323 72 : if(db_file_basename == NULL)
324 : {
325 0 : report("basename failed for path: %s",tmp);
326 0 : free(tmp);
327 0 : status = FAILURE;
328 0 : break;
329 : }
330 :
331 72 : status = add_string_to_array(&config->db_file_names,db_file_basename);
332 72 : free(tmp);
333 :
334 72 : if(SUCCESS != status)
335 : {
336 0 : break;
337 : }
338 : }
339 : }
340 : }
341 :
342 203 : if(SUCCESS != status)
343 : {
344 1 : provide(status);
345 : }
346 :
347 : /* Testing mode */
348 : {
349 202 : slog(TESTING,"rational_logger_mode=%s\n",rational_reconvert(rational_logger_mode));
350 :
351 202 : if(config->paths != NULL)
352 : {
353 202 : slog(TESTING,"argument:paths=");
354 :
355 440 : for(int i = 0; config->paths[i]; i++)
356 : {
357 238 : slog(TESTING|UNDECOR,i == 0 ? "%s" : ", %s",config->paths[i]);
358 : }
359 202 : slog(TESTING|UNDECOR,"\n");
360 : }
361 :
362 202 : if(config->db_primary_file_path != NULL)
363 : {
364 148 : slog(TESTING,"argument:database=%s\n",config->db_primary_file_path);
365 : }
366 :
367 202 : if(config->db_file_name != NULL)
368 : {
369 148 : slog(TESTING,"argument:db_file_name=%s\n",config->db_file_name);
370 : }
371 :
372 202 : if(config->db_file_paths != NULL)
373 : {
374 36 : slog(TESTING,"argument:db_file_paths=");
375 :
376 108 : for(int i = 0; config->db_file_paths[i]; i++)
377 : {
378 72 : slog(TESTING|UNDECOR,i == 0 ? "%s" : ", %s",config->db_file_paths[i]);
379 : }
380 36 : slog(TESTING|UNDECOR,"\n");
381 : }
382 :
383 202 : if(config->db_file_names != NULL)
384 : {
385 36 : slog(TESTING,"argument:db_file_names=");
386 :
387 108 : for(int i = 0; config->db_file_names[i]; i++)
388 : {
389 72 : slog(TESTING|UNDECOR,i == 0 ? "%s" : ", %s",config->db_file_names[i]);
390 : }
391 36 : slog(TESTING|UNDECOR,"\n");
392 : }
393 :
394 202 : if(config->ignore != NULL)
395 : {
396 18 : slog(TESTING,"argument:ignore=");
397 :
398 : // Print the contents of the string array
399 40 : for(int i = 0; config->ignore[i] != NULL; ++i)
400 : {
401 22 : slog(TESTING|UNDECOR,i == 0 ? "%s" : ", %s",config->ignore[i]);
402 : }
403 18 : slog(TESTING|UNDECOR,"\n");
404 : }
405 :
406 202 : if(config->include != NULL)
407 : {
408 2 : slog(TESTING,"argument:include=");
409 :
410 : // Print the contents of the string array
411 6 : for(int i = 0; config->include[i] != NULL; ++i)
412 : {
413 4 : slog(TESTING|UNDECOR,i == 0 ? "%s" : ", %s",config->include[i]);
414 : }
415 2 : slog(TESTING|UNDECOR,"\n");
416 : }
417 :
418 202 : if(config->lock_checksum != NULL)
419 : {
420 22 : slog(TESTING,"argument:lock-checksum=");
421 :
422 : // Print the contents of the string array
423 50 : for(int i = 0; config->lock_checksum[i] != NULL; ++i)
424 : {
425 28 : slog(TESTING|UNDECOR,i == 0 ? "%s" : ", %s",config->lock_checksum[i]);
426 : }
427 22 : slog(TESTING|UNDECOR,"\n");
428 : }
429 :
430 202 : if(config->db_check_level != FULL)
431 : {
432 2 : slog(TESTING,"argument:check-level=%s\n",config->db_check_level == QUICK ? "QUICK" : "FULL");
433 : }
434 :
435 202 : if(config->maxdepth > 0)
436 : {
437 2 : slog(TESTING,"argument:maxdepth=%d\n",config->maxdepth);
438 : }
439 :
440 202 : if(config->verbose)
441 : {
442 4 : slog(TESTING,"argument:verbose=%s\n",config->verbose ? "yes" : "no");
443 : }
444 :
445 202 : if(config->watch_timestamps)
446 : {
447 16 : slog(TESTING,"argument:watch-timestamps=%s\n",config->watch_timestamps ? "yes" : "no");
448 : }
449 :
450 202 : if(config->rehash_locked == true)
451 : {
452 4 : slog(TESTING,"argument:rehash-locked=%s\n",config->rehash_locked ? "yes" : "no");
453 : }
454 :
455 202 : if(config->force)
456 : {
457 2 : slog(TESTING,"argument:force=%s\n",config->force ? "yes" : "no");
458 : }
459 :
460 202 : if(config->update)
461 : {
462 88 : slog(TESTING,"argument:update=%s\n",config->update ? "yes" : "no");
463 : }
464 :
465 202 : if(config->progress)
466 : {
467 40 : slog(TESTING,"argument:progress=%s\n",config->progress ? "yes" : "no");
468 : }
469 :
470 202 : if(config->compare)
471 : {
472 36 : slog(TESTING,"argument:compare=%s\n",config->compare ? "yes" : "no");
473 : }
474 :
475 202 : if(config->db_clean_ignored)
476 : {
477 12 : slog(TESTING,"argument:db-clean-ignored=%s\n",config->db_clean_ignored ? "yes" : "no");
478 : }
479 :
480 202 : if(config->dry_run)
481 : {
482 10 : slog(TESTING,"argument:dry-run=%s\n",config->dry_run ? "yes" : "no");
483 : }
484 :
485 202 : if(config->start_device_only)
486 : {
487 2 : slog(TESTING,"argument:start-device-only=%s\n",config->start_device_only ? "yes" : "no");
488 : }
489 :
490 : }
491 :
492 : /* Verbose mode */
493 : {
494 202 : slog(VERBOSE,"Configuration: ");
495 202 : slog(VERBOSE|UNDECOR,"rational_logger_mode=%s\n",rational_reconvert(rational_logger_mode));
496 :
497 202 : if(config->paths != NULL)
498 : {
499 202 : slog(VERBOSE|UNDECOR,"paths=");
500 :
501 440 : for(int i = 0; config->paths[i]; i++)
502 : {
503 238 : slog(VERBOSE|UNDECOR,i == 0 ? "%s" : ", %s",config->paths[i]);
504 : }
505 202 : slog(VERBOSE|UNDECOR,"; ");
506 : }
507 :
508 202 : if(config->db_primary_file_path != NULL)
509 : {
510 148 : slog(VERBOSE|UNDECOR,"database=%s; ",config->db_primary_file_path);
511 : }
512 :
513 202 : if(config->db_file_name != NULL)
514 : {
515 148 : slog(VERBOSE|UNDECOR,"db_file_name=%s; ",config->db_file_name);
516 : }
517 :
518 202 : if(config->db_file_paths != NULL)
519 : {
520 36 : slog(VERBOSE|UNDECOR,"db_file_paths=");
521 :
522 108 : for(int i = 0; config->db_file_paths[i]; i++)
523 : {
524 72 : slog(VERBOSE|UNDECOR,i == 0 ? "%s" : ", %s",config->db_file_paths[i]);
525 : }
526 36 : slog(VERBOSE|UNDECOR,"; ");
527 : }
528 :
529 202 : if(config->db_file_names != NULL)
530 : {
531 36 : slog(VERBOSE|UNDECOR,"db_file_names=");
532 :
533 108 : for(int i = 0; config->db_file_names[i]; i++)
534 : {
535 72 : slog(VERBOSE|UNDECOR,i == 0 ? "%s" : ", %s",config->db_file_names[i]);
536 : }
537 36 : slog(VERBOSE|UNDECOR,"; ");
538 : }
539 :
540 202 : if(config->ignore != NULL)
541 : {
542 18 : slog(VERBOSE|UNDECOR,"ignore=");
543 :
544 : // Print the contents of the string array
545 40 : for(int i = 0; config->ignore[i] != NULL; ++i)
546 : {
547 22 : slog(VERBOSE|UNDECOR,i == 0 ? "%s" : ", %s",config->ignore[i]);
548 : }
549 18 : slog(VERBOSE|UNDECOR,"; ");
550 : }
551 :
552 202 : if(config->include != NULL)
553 : {
554 2 : slog(VERBOSE|UNDECOR,"include=");
555 :
556 : // Print the contents of the string array
557 6 : for(int i = 0; config->include[i] != NULL; ++i)
558 : {
559 4 : slog(VERBOSE|UNDECOR,i == 0 ? "%s" : ", %s",config->include[i]);
560 : }
561 2 : slog(VERBOSE|UNDECOR,"; ");
562 : }
563 :
564 202 : if(config->lock_checksum != NULL)
565 : {
566 22 : slog(VERBOSE|UNDECOR,"lock-checksum=");
567 :
568 : // Print the contents of the string array
569 50 : for(int i = 0; config->lock_checksum[i] != NULL; ++i)
570 : {
571 28 : slog(VERBOSE|UNDECOR,i == 0 ? "%s" : ", %s",config->lock_checksum[i]);
572 : }
573 22 : slog(VERBOSE|UNDECOR,"; ");
574 : }
575 :
576 202 : slog(VERBOSE|UNDECOR,"verbose=%s; maxdepth=%d; silent=no; force=%s; update=%s; watch-timestamps=%s; rehash-locked=%s; progress=%s; compare=%s, db-clean-ignored=%s, dry-run=%s, start-device-only=%s, check-level=%s, rational_logger_mode=%s",
577 : config->verbose ? "yes" : "no",
578 : config->maxdepth,
579 : config->force ? "yes" : "no",
580 : config->update ? "yes" : "no",
581 : config->watch_timestamps ? "yes" : "no",
582 : config->rehash_locked ? "yes" : "no",
583 : config->progress ? "yes" : "no",
584 : config->compare ? "yes" : "no",
585 : config->db_clean_ignored ? "yes" : "no",
586 : config->dry_run ? "yes" : "no",
587 : config->start_device_only ? "yes" : "no",
588 : config->db_check_level == QUICK ? "QUICK" : "FULL",
589 : rational_reconvert(rational_logger_mode));
590 202 : slog(VERBOSE|UNDECOR,"\n");
591 : }
592 :
593 202 : slog(TRACE,"Argument parsing is complete\n");
594 :
595 202 : provide(status);
596 : }
|