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