minor(user/bkots):fix verbose comment
[BK-2020-03.git] / user / bkots
1 #!/usr/bin/env bash
2
3 # Define variables
4 declare -Ag appRollCall # Associative array for storing app status
5 declare -Ag fileRollCall # Associative array for storing file status
6 declare -Ag dirRollCall # Associative array for storing dir status
7 declare -ag arrayPosArgs # Associative array for processArgs() function
8 declare -g ots_delay; ots_delay=1 # minimum time in seconds between ots operations
9 declare -ag calendars;
10 calendars+=("https://finney.calendar.eternitywall.com");
11 calendars+=("https://btc.calendar.catallaxy.com");
12 calendars+=("https://alice.btc.calendar.opentimestamps.org");
13 calendars+=("https://bob.btc.calendar.opentimestamps.org");
14
15 # Declare functions
16 yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
17 die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status
18 try() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
19 checkapp() {
20 # Desc: If arg is a command, save result in assoc array 'appRollCall'
21 # Usage: checkapp arg1 arg2 arg3 ...
22 # Version: 0.1.1
23 # Input: global assoc. array 'appRollCall'
24 # Output: adds/updates key(value) to global assoc array 'appRollCall'
25 # Depends: bash 5.0.3
26 local returnState
27
28 #===Process Args===
29 for arg in "$@"; do
30 if command -v "$arg" 1>/dev/null 2>&1; then # Check if arg is a valid command
31 appRollCall[$arg]="true";
32 if ! [ "$returnState" = "false" ]; then returnState="true"; fi;
33 else
34 appRollCall[$arg]="false"; returnState="false";
35 fi;
36 done;
37
38 #===Determine function return code===
39 if [ "$returnState" = "true" ]; then
40 return 0;
41 else
42 return 1;
43 fi;
44 } # Check that app exists
45 checkfile() {
46 # Desc: If arg is a file path, save result in assoc array 'fileRollCall'
47 # Usage: checkfile arg1 arg2 arg3 ...
48 # Version: 0.1.1
49 # Input: global assoc. array 'fileRollCall'
50 # Output: adds/updates key(value) to global assoc array 'fileRollCall';
51 # Output: returns 0 if app found, 1 otherwise
52 # Depends: bash 5.0.3
53 local returnState
54
55 #===Process Args===
56 for arg in "$@"; do
57 if [ -f "$arg" ]; then
58 fileRollCall["$arg"]="true";
59 if ! [ "$returnState" = "false" ]; then returnState="true"; fi;
60 else
61 fileRollCall["$arg"]="false"; returnState="false";
62 fi;
63 done;
64
65 #===Determine function return code===
66 if [ "$returnState" = "true" ]; then
67 return 0;
68 else
69 return 1;
70 fi;
71 } # Check that file exists
72 checkdir() {
73 # Desc: If arg is a dir path, save result in assoc array 'dirRollCall'
74 # Usage: checkdir arg1 arg2 arg3 ...
75 # Version 0.1.2
76 # Input: global assoc. array 'dirRollCall'
77 # Output: adds/updates key(value) to global assoc array 'dirRollCall';
78 # Output: returns 0 if all args are dirs; 1 otherwise
79 # Depends: Bash 5.0.3
80 local returnState
81
82 #===Process Args===
83 for arg in "$@"; do
84 if [ -z "$arg" ]; then
85 dirRollCall["(Unspecified Dirname(s))"]="false"; returnState="false";
86 elif [ -d "$arg" ]; then
87 dirRollCall["$arg"]="true";
88 if ! [ "$returnState" = "false" ]; then returnState="true"; fi
89 else
90 dirRollCall["$arg"]="false"; returnState="false";
91 fi
92 done
93
94 #===Determine function return code===
95 if [ "$returnState" = "true" ]; then
96 return 0;
97 else
98 return 1;
99 fi
100 } # Check that dir exists
101 displayMissing() {
102 # Desc: Displays missing apps, files, and dirs
103 # Usage: displayMissing
104 # Version 1.0.0
105 # Input: associative arrays: appRollCall, fileRollCall, dirRollCall
106 # Output: stderr: messages indicating missing apps, file, or dirs
107 # Output: returns exit code 0 if nothing missing; 1 otherwise
108 # Depends: bash 5, checkAppFileDir()
109 local missingApps value appMissing missingFiles fileMissing
110 local missingDirs dirMissing
111
112 #==BEGIN Display errors==
113 #===BEGIN Display Missing Apps===
114 missingApps="Missing apps :";
115 #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done
116 for key in "${!appRollCall[@]}"; do
117 value="${appRollCall[$key]}";
118 if [ "$value" = "false" ]; then
119 #echo "DEBUG:Missing apps: $key => $value";
120 missingApps="$missingApps""$key ";
121 appMissing="true";
122 fi;
123 done;
124 if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing.
125 echo "$missingApps" 1>&2;
126 fi;
127 unset value;
128 #===END Display Missing Apps===
129
130 #===BEGIN Display Missing Files===
131 missingFiles="Missing files:";
132 #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done
133 for key in "${!fileRollCall[@]}"; do
134 value="${fileRollCall[$key]}";
135 if [ "$value" = "false" ]; then
136 #echo "DEBUG:Missing files: $key => $value";
137 missingFiles="$missingFiles""$key ";
138 fileMissing="true";
139 fi;
140 done;
141 if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing.
142 echo "$missingFiles" 1>&2;
143 fi;
144 unset value;
145 #===END Display Missing Files===
146
147 #===BEGIN Display Missing Directories===
148 missingDirs="Missing dirs:";
149 #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done
150 for key in "${!dirRollCall[@]}"; do
151 value="${dirRollCall[$key]}";
152 if [ "$value" = "false" ]; then
153 #echo "DEBUG:Missing dirs: $key => $value";
154 missingDirs="$missingDirs""$key ";
155 dirMissing="true";
156 fi;
157 done;
158 if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing.
159 echo "$missingDirs" 1>&2;
160 fi;
161 unset value;
162 #===END Display Missing Directories===
163
164 #==END Display errors==
165 #==BEGIN Determine function return code===
166 if [ "$appMissing" == "true" ] || [ "$fileMissing" == "true" ] || [ "$dirMissing" == "true" ]; then
167 return 1;
168 else
169 return 0;
170 fi
171 #==END Determine function return code===
172 } # Display missing apps, files, dirs
173 vbm() {
174 # Description: Prints verbose message ("vbm") to stderr if opVerbose is set to "true".
175 # Usage: vbm "DEBUG :verbose message here"
176 # Version 0.2.0
177 # Input: arg1: string
178 # vars: opVerbose
179 # Output: stderr
180 # Depends: bash 5.0.3, GNU-coreutils 8.30 (echo, date)
181
182 if [ "$opVerbose" = "true" ]; then
183 functionTime="$(date --iso-8601=ns)"; # Save current time in nano seconds.
184 echo "[$functionTime]:$0:""$*" 1>&2; # Display argument text.
185 fi
186
187 # End function
188 return 0; # Function finished.
189 } # Displays message if opVerbose true
190 showVersion() {
191 # Desc: Displays script version and license information.
192 # Usage: showVersion
193 # Version: 0.0.1
194 # Input: scriptVersion var containing version string
195 # Output: stdout
196 # Depends: vbm(), yell, GNU-coreutils 8.30
197
198 # Initialize function
199 vbm "DEBUG:showVersion function called."
200
201 cat <<'EOF'
202 bkots 0.0.6
203 Copyright (C) 2022 Steven Baltakatei Sandoval
204 License GPLv3: GNU GPL version 3
205 This is free software; you are free to change and redistribute it.
206 There is NO WARRANTY, to the extent permitted by law.
207
208 GNU Coreutils 8.32
209 Copyright (C) 2020 Free Software Foundation, Inc.
210 License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
211 This is free software: you are free to change and redistribute it.
212 There is NO WARRANTY, to the extent permitted by law.
213 EOF
214
215 # End function
216 vbm "DEBUG:showVersion function ended."
217 return 0; # Function finished.
218 } # Display script version.
219 showUsage() {
220 # Desc: Display script usage information
221 # Usage: showUsage
222 # Version 0.0.1
223 # Input: none
224 # Output: stdout
225 # Depends: GNU-coreutils 8.30 (cat)
226 cat <<'EOF'
227 USAGE:
228 bkots [ options ] [PATH...]
229
230 POSITIONAL ARGUMENTS:
231 PATH Path(s) of file(s) or directory(ies)
232
233 OPTIONS:
234 --dry-run
235 Do everything except run 'ots' commands.
236 -h, --help
237 Display help information.
238 --include-dotfiles
239 Include files and directories starting with '.' (not
240 included by default).
241 -r, --recursive
242 Consider files in dirs recursively.
243 --version
244 Display script version.
245 -v, --verbose
246 Display debugging info.
247 --
248 Mark end of options. Interpret remaining arguments as
249 positional arguments.
250
251 DESCRIPTION:
252 Scans files by file paths or directory paths provided by
253 positional arguments to see if Open Timestamps '.ots' file
254 exists. If so, attempt to upgrade and verify the '.ots'
255 file. If no '.ots' file exists, attempt to create one.
256
257 Files with a dotfile parent directory located anywhere in the
258 file path are ignored by default. (e.g. 'HEAD' in
259 '/home/user/diary/.git/logs/HEAD' because of '.git'). Dotfiles
260 themselves are also ignored by default
261 (e.g. '/home/user/.gitconfig').
262
263 EXAMPLES:
264 bkots -v foo.txt
265 bkots foo.txt bar.pdf /home/username/Pictures/
266 EOF
267 } # Display information on how to use this script.
268 processArgs() {
269 # Desc: Processes arguments provided to script.
270 # Usage: processArgs "$@"
271 # Version: 1.0.0
272 # Input: "$@" (list of arguments provided to the function)
273 # Output: Sets following variables used by other functions:
274 # opVerbose Indicates verbose mode enable status. (ex: "true", "false")
275 # arrayPosArgs Array of remaining positional argments
276 # Depends:
277 # yell() Displays messages to stderr.
278 # vbm() Displays messsages to stderr if opVerbose set to "true".
279 # showUsage() Displays usage information about parent script.
280 # showVersion() Displays version about parent script.
281 # arrayPosArgs Global array for storing non-option positional arguments (i.e. arguments following the `--` option).
282 # External dependencies: bash (5.1.16), echo
283 # Ref./Attrib.:
284 # [1]: Marco Aurelio (2014-05-08). "echo that outputs to stderr". https://stackoverflow.com/a/23550347
285 # [2]: "Handling positional parameters" (2018-05-12). https://wiki.bash-hackers.org/scripting/posparams
286
287 # Initialize function
288 vbm "DEBUG:processArgs function called."
289
290 # Perform work
291 while [ ! $# -eq 0 ]; do # While number of arguments ($#) is not (!) equal to (-eq) zero (0).
292 #yell "DEBUG:Starting processArgs while loop." # Debug stderr message. See [1].
293 #yell "DEBUG:Provided arguments are:""$*" # Debug stderr message. See [1].
294 case "$1" in
295 --dry-run) # Do not run ots commands
296 option_dry_run="true";
297 vbm "DEBUG:Option enabled:dry run";;
298 -h | --help) showUsage; exit 1;; # Display usage.
299 --include-dotfiles) # Include dotfiles
300 option_include_dotfiles="true";
301 vbm "DEBUG:Option enabled:include dotfiles";;
302 -r | --recursive) # Specify recursive option
303 option_recursive="true";
304 vbm "DEBUG:option enabled:include files in dirs recursively";;
305 --version) showVersion; exit 1;; # Show version
306 -v | --verbose) opVerbose="true"; vbm "DEBUG:Verbose mode enabled.";; # Enable verbose mode. See [1].
307 --) # End of all options. See [2].
308 shift;
309 for arg in "$@"; do
310 vbm "DEBUG:adding to arrayPosArgs:$arg";
311 arrayPosArgs+=("$arg");
312 done;
313 break;;
314 -*) showUsage; yell "ERROR: Unrecognized option."; exit 1;; # Display usage
315 *) # Assume remaining arguments are positional arguments
316 for arg in "$@"; do
317 vbm "DEBUG:adding to arrayPosArgs:$arg";
318 arrayPosArgs+=("$arg");
319 done;
320 break;;
321 #*) showUsage; yell "ERROR: Unrecognized argument."; exit 1;; # Handle unrecognized options. See [1].
322 esac
323 shift
324 done
325
326 # End function
327 vbm "DEBUG:processArgs function ended."
328 return 0; # Function finished.
329 }; # Evaluate script options from positional arguments (ex: $1, $2, $3, etc.).
330 get_parent_dirnames() {
331 # Desc: Provides newline-delimited list of each parent dir of a file or dir
332 # Usage: get_parent_dirnames arg1
333 # Input: arg1 input path
334 # Output: stdout newline-delimited list of parent dirs
335 # Version: 0.0.1
336 # Depends: yell(), die(), try()
337 local path
338
339 # Check input
340 if [[ $# -ne 1 ]]; then die "FATAL:Incorrect number of arguments:$#"; fi;
341 if ! { [[ -f $1 ]] || [[ -d $1 ]]; }; then die "FATAL:Not a file or dir:$1"; fi;
342
343 # Process path
344 path="$1";
345 while [[ -f $path ]] || [[ -d $path ]]; do
346 path="$(dirname "$path")";
347 name_base_previous="$name_base";
348 name_base="$(basename "$path")";
349 ## Check for stop condition (dirname returns same result as previous iteration)
350 if [[ $name_base == "$name_base_previous" ]]; then break; fi;
351 echo "$name_base";
352 done;
353 }; # Output parent dirnames to stdout
354 main() {
355 # Desc: Creates `.ots` file:
356 # - for each file specified in arrayPosArgs array
357 # - for each file in each dir specified in arrayPosArgs array
358 # Output file created alongside each file or in output directory specified by pathDirIn1
359 # Usage: main "$@";
360 # Input: arrayPosArgs array with positional arguments
361 # pathDirOut1 path for output `.ots` files (if pathDirOut1 is specified and is a path)
362 # Output: file(s) creates `.ots` file alongside specified files
363 # Depends: find (GNU findutils) 4.8.0, GNU Coreutils 8.32 (sort)
364 # Ref/Attrib: [1] How to create an array of unique elements from a string/array in bash https://unix.stackexchange.com/a/167194
365 # [2] How to find files containing newlines in their names https://stackoverflow.com/a/21727028
366 local -a file_list file_list_pruned;
367 local -a files_to_verify files_to_upgrade files_to_stamp
368 local -a files_to_verify_pruned files_to_upgrade_pruned files_to_stamp_pruned
369
370 # Process args
371 processArgs "$@";
372
373 # Check dependencies
374 if ! checkapp ots find; then
375 displayMissing;
376 die "FATAL:Missing dependencies.";
377 fi;
378
379 # Check arguments
380 ## Mark if output dir option specified
381 if [[ -v pathDirOut1 ]]; then
382 vbm "DEBUG:output directory specified:pathDirOut1:$pathDirOut1";
383 if [[ -d $pathDirOut1 ]]; then
384 vbm "DEBUG:pathDirOut1:$pathDirOut1";
385 config_output_dir="true";
386 else
387 die "ERROR:Not a dir:$pathDirOut1";
388 fi;
389 fi;
390
391 # Display ots details
392 vbm "$(type ots)"; # show how 'ots' is defined
393 #TODO: add option to define 'ots' as a bash function that
394 #populates the ots option '--bitcoin-node FILE' with a
395 #user-specified FILE.
396
397 # Populate file_list
398 vbm "DEBUG:begin populate file_list array";
399 for item in "${arrayPosArgs[@]}"; do
400 vbm "DEBUG:adding to file list:item:$item";
401
402 ## Get full canonicalized path (follow symlinks)
403 item="$(readlink -f "$item")";
404 vbm "DEBUG:item full path:item:$item";
405
406 ## Add to list: files
407 if [[ -f $item ]]; then
408 vbm "DEBUG:is a file:item:$item";
409 file_list+=("$item");
410 vbm "DEBUG:added to file_list:$item";
411 ## Add to list: files in dirs
412 elif [[ -d $item ]]; then
413 vbm "DEBUG:is a dir:item:$item";
414 ### Check for recursive flag
415 if [[ "$option_recursive" == "true" ]]; then
416 vbm "DEBUG:option_recursive:$option_recursive";
417 while read -r line; do
418 file_list+=("$line");
419 vbm "DEBUG:added to file_list:$line";
420 done < <(find "$item" -type f);
421 else
422 while read -r line; do
423 file_list+=("$line");
424 vbm "DEBUG:added to file_list:$line";
425 done < <(find "$item" -maxdepth 1 -type f);
426 fi;
427 else
428 die "ERROR:Not a file or dir:item:$item";
429 fi;
430 done;
431 if [[ $opVerbose == "true" ]]; then
432 vbm "DEBUG:file_list:";
433 printf "%s\n" "${file_list[@]}";
434 fi;
435
436 # Prune file_list
437 for item in "${file_list[@]}"; do
438 if ! [[ $option_include_dotfiles == "true" ]]; then
439 ## Ignore files located beneath a dotfile directory (e.g. '/home/my_repo/.git/config')
440 unset flag_contains_dotfile_parent;
441 while read -r line; do
442 ### Check line from output of get_parent_dirnames
443 pattern="^\.";
444 if [[ $line =~ $pattern ]]; then
445 #### line starts with '.'
446 vbm "DEBUG:Dotfile parent detected. Not including in file_list_pruned:$item";
447 vbm "DEBUG:Dotfile in path:item:$item";
448 vbm "DEBUG:Dotfile parent:line:$line";
449 flag_contains_dotfile_parent="true";
450 break
451 fi;
452 done < <(get_parent_dirnames "$item");
453 if [[ $flag_contains_dotfile_parent == "true" ]]; then
454 unset flag_contains_dotfile_parent;
455 continue; # skip to next item (i.e. don't add to file_list_pruned)
456 fi;
457
458 ## Ignore dotfiles themselves
459 item_basename="$(basename "$item")";
460 pattern="^\.";
461 if [[ $item_basename =~ $pattern ]]; then
462 vbm "INFO :Skipping dotfile:item:$item";
463 continue; # skip to next item
464 fi;
465 fi;
466
467 ## Ignore files with newlines present in filename. See [2].
468 if [[ $item =~ $'\n' ]]; then
469 yell "INFO :Skipping file name with newline:$item";
470 continue; # skip to next item
471 fi;
472
473 ## Ignore files that end in '~'.
474 if [[ $item =~ ~$ ]]; then
475 yell "INFO :Skipping file ending in tilde:$item";
476 continue; # skip to next item
477 fi;
478
479 ## Ignore files that end in '.ots.bak'.
480 if [[ $item =~ '.ots.bak'$ ]]; then
481 yell "INFO :Skipping file ending in '.ots.bak':item:$item";
482 continue; # skip to next item
483 fi;
484
485 ## Add item to file_list_pruned
486 file_list_pruned+=("$item");
487 done;
488 if [[ $opVerbose == "true" ]]; then
489 vbm "DEBUG:file_list_pruned:";
490 printf "%s\n" "${file_list_pruned[@]}";
491 fi;
492
493 # Decide what actions to take for items in file_list_pruned
494 for item in "${file_list_pruned[@]}"; do
495 vbm "DEBUG:considering action to take for item:$item";
496 unset path_src path_prf dir_parent dir_source;
497
498 ## Check file extension
499 if [[ $item =~ .ots$ ]]; then
500 ### item ends in '.ots'. Item is proof file.
501 vbm "DEBUG:item ends in '.ots'. Item is proof file:item:$item";
502 if [[ -f ${item%.ots} ]]; then
503 #### Proof file (item) is adjacent to source file
504 vbm "DEBUG:Proof file (item) is adjacent to source file.";
505 ##### Upgrade and verify proof file against adjacent source file
506 vbm "DEBUG:Marking proof file to be upgraded and verified.";
507 path_src="${item%.ots}";
508 path_prf="$item";
509 files_to_upgrade+=("$(printf "%s" "$path_prf")");
510 files_to_verify+=("$(printf "%s\n%s" "$path_src" "$path_prf")");
511 else
512 #### Proof file (item) is not adjacent to source file
513 vbm "DEBUG:Proof file (item) is not adjacent to source file.";
514 #### Check if source file in parent dir
515 dir_parent="$(dirname "$(dirname "$item")" )";
516 cand_src_filename="$(basename "$item")";
517 cand_src_path="$dir_parent/$cand_src_filename";
518 if [[ -f "$cand_src_path" ]]; then
519 ##### source file in parent dir
520 vbm "DEBUG:found source file in parent:cand_src_path:$cand_src_path";
521 path_src="$cand_src_path";
522 path_prf="$item";
523 files_to_upgrade+=("$(printf "%s" "$path_prf")");
524 files_to_verify+=("$(printf "%s\n%s" "$path_src" "$path_prf")");
525 else
526 #### Throw non-fatal error
527 vbm "DEBUG:Source file not found for proof file:item:$item";
528 yell "ERROR:Item is proof file but source filei not adjacent in parent dir. item:$item";
529 #### Attempt upgrade only
530 vbm "DEBUG:Marking proof file to be upgraded.";
531 path_prf="$item";
532 files_to_upgrade+=("$(printf "%s" "$path_prf")");
533 fi;
534 fi;
535 else
536 ### item does not end in '.ots'. Item is source file.
537 vbm "DEBUG:item does NOT end in '.ots'. Item is source file.";
538 if [[ -f "$item".ots ]]; then
539 #### Proof file is adjacent to source file (item).
540 vbm "DEBUG:Proof file is adjacent to source file (item).";
541 ##### Upgrade and verify proof file against adjacent source file.
542 vbm "DEBUG:Marking proof file to be upgraded and verified.";
543 path_src="$item";
544 path_prf="$item.ots";
545 files_to_upgrade+=("$(printf "%s" "$path_prf")");
546 files_to_verify+=("$(printf "%s\n%s" "$path_src" "$path_prf")");
547 else
548 #### Proof file is not adjacent to source file (item).
549 #### Check if proof file is in subdir
550 vbm "DEBUG:checking if proof file for source file (item) is in subdir:item:$item";
551 unset flag_proof_in_subdir;
552 dir_item="$(dirname "$item")";
553 cand_prf_filename="$(basename "$item")".ots;
554 while read -r line; do
555 line_basename="$(basename "$line")";
556 if [[ $line_basename == "$cand_prf_filename" ]]; then
557 flag_proof_in_subdir="true";
558 path_prf="$line";
559 vbm "DEBUG:proof found in subdir at:line:$line";
560 break;
561 fi;
562 done < <(find "$dir_item" -mindepth 2 -maxdepth 2 -type f)
563 if [[ $flag_proof_in_subdir == "true" ]]; then
564 ##### Proof file is in subdir
565 vbm "DEBUG:Proof file detected in subdir relative to source file (item)";
566 #path_prf="$path_prf"; # set in while loop
567 path_src="$item";
568 files_to_upgrade+=("$(printf "%s" "$path_prf")");
569 files_to_verify+=("$(printf "%s\n%s" "$path_src" "$path_prf")");
570 else
571 ##### Proof file is not in subdir
572 vbm "DEBUG:Proof file not detected in subdir relative to source file (item).";
573 #### Stamp source file
574 vbm "DEBUG:Marking source file to be stamped.";
575 path_src="$item";
576 files_to_stamp+=("$(printf "%s" "$path_src")")
577 fi;
578 unset flag_proof_in_subdir;
579 fi;
580 fi;
581 done;
582 unset path_src path_prf dir_item dir_parent cand_prf_filename cand_src_filename line_basename cand_src_path
583
584 # Prune action lists.
585 ## Sort and prune file action arrays
586 ### files to upgrade
587 while read -r -d $'\0' line; do
588 vbm "DEBUG:adding to files_to_upgrade_pruned:line:$line";
589 files_to_upgrade_pruned+=("$line");
590 done < <(printf "%s\0" "${files_to_upgrade[@]}" | sort -zu | shuf -z); # See [1]
591 if [[ $opVerbose == "true" ]]; then
592 vbm "DEBUG:files_to_upgrade_pruned:";
593 printf "%s\n" "${files_to_upgrade_pruned[@]}";
594 fi;
595
596 ### files to verify
597 while read -r -d $'\0' line; do
598 vbm "DEBUG:adding to files_to_verify_pruned:line:$line";
599 files_to_verify_pruned+=("$line");
600 done < <(printf "%s\0" "${files_to_verify[@]}" | sort -zu | shuf -z); # See [1]
601 if [[ $opVerbose == "true" ]]; then
602 vbm "DEBUG:files_to_verify_pruned:";
603 printf "%s\n\n" "${files_to_verify_pruned[@]}";
604 fi;
605
606 ### files to stamp
607 while read -r -d $'\0' line; do
608 vbm "DEBUG:adding to files_to_stamp_pruned:line:$line";
609 files_to_stamp_pruned+=("$line");
610 done < <(printf "%s\0" "${files_to_stamp[@]}" | sort -zu | shuf -z); # See [1]
611 if [[ $opVerbose == "true" ]]; then
612 vbm "DEBUG:files_to_stamp_pruned:";
613 printf "%s\n" "${files_to_stamp_pruned[@]}";
614 fi;
615
616 # Act on files
617 ## Upgrade files
618 for item in "${files_to_upgrade_pruned[@]}"; do
619 path_prf="$(cut -d $'\n' -f1 < <(echo "$item"))";
620 if [[ -z "$path_prf" ]]; then
621 yell "ERROR:blank upgrade item encountered. Skipping:item:$item";
622 continue;
623 fi;
624 vbm "DEBUG:Attempting to upgrade proof file:path_prf:$path_prf";
625 if [[ ! $option_dry_run == "true" ]]; then
626 ### Try upgrade with known calendars in random order
627 while read -r url; do
628 vbm "DEBUG:Upgrading with calendar:url:$url";
629 ots -l "$url" --no-default-whitelist upgrade "$path_prf" && break;
630 done < <(printf "%s\n" "${calendars[@]}" | shuf);
631 else
632 yell "DEBUG:DRY RUN:Not running:\"ots upgrade $path_prf\"";
633 fi;
634 #sleep "$ots_delay";
635 done;
636
637 ## Verify files
638 for item in "${files_to_verify_pruned[@]}"; do
639 path_src="$(cut -d $'\n' -f1 < <(echo "$item"))";
640 path_prf="$(cut -d $'\n' -f2 < <(echo "$item"))";
641 if [[ -z "$path_src" ]] || [[ -z "$path_prf" ]]; then
642 yell "ERROR:blank verify item encountered. Skipping:item:$item";
643 continue;
644 fi;
645 vbm "DEBUG:Attempting to verify source file:path_src:$path_src";
646 vbm "DEBUG: against proof file: path_prf:$path_prf";
647 if [[ ! $option_dry_run == "true" ]]; then
648 ### Try verify with known calendars in random order
649 while read -r url; do
650 vbm "DEBUG:Verifying with calendar:url:$url";
651 ots -l "$url" --no-default-whitelist verify -f "$path_src" "$path_prf" && break;
652 done < <(printf "%s\n" "${calendars[@]}" | shuf);
653 else
654 yell "DEBUG:DRY RUN:Not running:\"ots verify -f $path_src $path_prf\"";
655 fi;
656 #sleep "$ots_delay";
657 done;
658
659 ## Stamp files
660 for item in "${files_to_stamp_pruned[@]}"; do
661 path_src="$(cut -d $'\n' -f1 < <(echo "$item"))";
662 if [[ -z "$path_src" ]]; then
663 yell "ERROR:blank stamp item encountered. Skipping:item:$item";
664 continue;
665 fi;
666 vbm "DEBUG:Attempting to stamp source file:path_src:$path_src";
667 if [[ ! $option_dry_run == "true" ]]; then
668 ots stamp "$item";
669 else
670 yell "DEBUG:DRY RUN:Not running:\"ots stamp $item\"";
671 fi;
672 sleep "$ots_delay";
673 done;
674
675 }; # main program
676
677 # Run program
678 main "$@";