2 # Desc: Recursively timestamp files with OpenTimestamp
7 declare -g max_job_count
="2"; # default max job count
8 declare -g age_threshold
="60"; # min age to add file; seconds;
9 declare -g stamp_throttle
="0.1"; # seconds delay between stamps
10 declare -g stamp_throttle_fail
="1"; # seconds delay if stamp errors
11 declare -Ag appRollCall
# Associative array for storing app status
12 declare -Ag fileRollCall
# Associative array for storing file status
13 declare -Ag dirRollCall
# Associative array for storing dir status
14 declare -ag arrayPosArgs
# Associative array for processArgs() function
15 declare -ag calendars
;
16 calendars
+=("https://finney.calendar.eternitywall.com");
17 calendars
+=("https://btc.calendar.catallaxy.com");
18 calendars
+=("https://alice.btc.calendar.opentimestamps.org");
19 calendars
+=("https://bob.btc.calendar.opentimestamps.org");
22 yell
() { echo "$0: $*" >&2; } # print script path and all args to stderr
23 die
() { yell
"$*"; exit 111; } # same as yell() but non-zero exit status
24 must
() { "$@" || die
"cannot $*"; } # runs args as command, reports args if command fails
26 # Desc: If arg is a command, save result in assoc array 'appRollCall'
27 # Usage: checkapp arg1 arg2 arg3 ...
29 # Input: global assoc. array 'appRollCall'
30 # Output: adds/updates key(value) to global assoc array 'appRollCall'
36 if command -v "$arg" 1>/dev
/null
2>&1; then # Check if arg is a valid command
37 appRollCall
[$arg]="true";
38 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi;
40 appRollCall
[$arg]="false"; returnState
="false";
44 #===Determine function return code===
45 if [ "$returnState" = "true" ]; then
50 } # Check that app exists
52 # Desc: If arg is a file path, save result in assoc array 'fileRollCall'
53 # Usage: checkfile arg1 arg2 arg3 ...
55 # Input: global assoc. array 'fileRollCall'
56 # Output: adds/updates key(value) to global assoc array 'fileRollCall';
57 # Output: returns 0 if app found, 1 otherwise
63 if [ -f "$arg" ]; then
64 fileRollCall
["$arg"]="true";
65 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi;
67 fileRollCall
["$arg"]="false"; returnState
="false";
71 #===Determine function return code===
72 if [ "$returnState" = "true" ]; then
77 } # Check that file exists
79 # Desc: If arg is a dir path, save result in assoc array 'dirRollCall'
80 # Usage: checkdir arg1 arg2 arg3 ...
82 # Input: global assoc. array 'dirRollCall'
83 # Output: adds/updates key(value) to global assoc array 'dirRollCall';
84 # Output: returns 0 if all args are dirs; 1 otherwise
90 if [ -z "$arg" ]; then
91 dirRollCall
["(Unspecified Dirname(s))"]="false"; returnState
="false";
92 elif [ -d "$arg" ]; then
93 dirRollCall
["$arg"]="true";
94 if ! [ "$returnState" = "false" ]; then returnState
="true"; fi
96 dirRollCall
["$arg"]="false"; returnState
="false";
100 #===Determine function return code===
101 if [ "$returnState" = "true" ]; then
106 } # Check that dir exists
108 # Desc: Displays missing apps, files, and dirs
109 # Usage: displayMissing
111 # Input: associative arrays: appRollCall, fileRollCall, dirRollCall
112 # Output: stderr: messages indicating missing apps, file, or dirs
113 # Output: returns exit code 0 if nothing missing; 1 otherwise
114 # Depends: bash 5, checkAppFileDir()
115 local missingApps value appMissing missingFiles fileMissing
116 local missingDirs dirMissing
118 #==BEGIN Display errors==
119 #===BEGIN Display Missing Apps===
120 missingApps
="Missing apps :";
121 #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done
122 for key
in "${!appRollCall[@]}"; do
123 value
="${appRollCall[$key]}";
124 if [ "$value" = "false" ]; then
125 #echo "DEBUG:Missing apps: $key => $value";
126 missingApps
="$missingApps""$key ";
130 if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing.
131 echo "$missingApps" 1>&2;
134 #===END Display Missing Apps===
136 #===BEGIN Display Missing Files===
137 missingFiles
="Missing files:";
138 #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done
139 for key
in "${!fileRollCall[@]}"; do
140 value
="${fileRollCall[$key]}";
141 if [ "$value" = "false" ]; then
142 #echo "DEBUG:Missing files: $key => $value";
143 missingFiles
="$missingFiles""$key ";
147 if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing.
148 echo "$missingFiles" 1>&2;
151 #===END Display Missing Files===
153 #===BEGIN Display Missing Directories===
154 missingDirs
="Missing dirs:";
155 #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done
156 for key
in "${!dirRollCall[@]}"; do
157 value
="${dirRollCall[$key]}";
158 if [ "$value" = "false" ]; then
159 #echo "DEBUG:Missing dirs: $key => $value";
160 missingDirs
="$missingDirs""$key ";
164 if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing.
165 echo "$missingDirs" 1>&2;
168 #===END Display Missing Directories===
170 #==END Display errors==
171 #==BEGIN Determine function return code===
172 if [ "$appMissing" == "true" ] ||
[ "$fileMissing" == "true" ] ||
[ "$dirMissing" == "true" ]; then
177 #==END Determine function return code===
178 } # Display missing apps, files, dirs
180 # Description: Prints verbose message ("vbm") to stderr if opVerbose is set to "true".
181 # Usage: vbm "DEBUG :verbose message here"
183 # Input: arg1: string
186 # Depends: bash 5.0.3, GNU-coreutils 8.30 (echo, date)
188 if [ "$opVerbose" = "true" ]; then
189 functionTime
="$(date --iso-8601=ns)"; # Save current time in nano seconds.
190 echo "[$functionTime]:$0:""$*" 1>&2; # Display argument text.
194 return 0; # Function finished.
195 } # Displays message if opVerbose true
197 # Desc: Displays script version and license information.
200 # Input: scriptVersion var containing version string
202 # Depends: vbm(), yell, GNU-coreutils 8.30
204 # Initialize function
205 vbm
"DEBUG:showVersion function called."
209 Copyright (C) 2022 Steven Baltakatei Sandoval
210 License GPLv3: GNU GPL version 3
211 This is free software; you are free to change and redistribute it.
212 There is NO WARRANTY, to the extent permitted by law.
215 Copyright (C) 2020 Free Software Foundation, Inc.
216 License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
217 This is free software: you are free to change and redistribute it.
218 There is NO WARRANTY, to the extent permitted by law.
222 vbm
"DEBUG:showVersion function ended."
223 return 0; # Function finished.
224 } # Display script version.
226 # Desc: Display script usage information
231 # Depends: GNU-coreutils 8.30 (cat)
234 bkots [ options ] [PATH...]
236 POSITIONAL ARGUMENTS:
237 PATH Path(s) of file(s) or directory(ies)
241 Do everything except run 'ots' commands.
243 Display help information.
245 Include files and directories starting with '.' (not
246 included by default).
248 Specify simultaneous job count (default: 2)
250 Consider files in dirs recursively.
252 Display script version.
254 Display debugging info.
256 Mark end of options. Interpret remaining arguments as
257 positional arguments.
260 Scans files by file paths or directory paths provided by
261 positional arguments to see if Open Timestamps '.ots' file
262 exists. If so, attempt to upgrade and verify the '.ots'
263 file. If no '.ots' file exists, attempt to create one.
265 Files with a dotfile parent directory located anywhere in the
266 file path are ignored by default. (e.g. 'HEAD' in
267 '/home/user/diary/.git/logs/HEAD' because of '.git'). Dotfiles
268 themselves are also ignored by default
269 (e.g. '/home/user/.gitconfig').
271 Files modified less than 1 minute ago are ignored.
275 bkots foo.txt bar.pdf /home/username/Pictures/
277 } # Display information on how to use this script.
279 # Desc: Count and return total number of jobs
282 # Output: stdout integer number of jobs
283 # Depends: Bash 5.1.16
284 # Example: while [[$(count_jobs) -gt 0]]; do echo "Working..."; sleep 1; done;
288 job_count
="$(jobs -r | wc -l | tr -d ' ' )";
289 #yell "DEBUG:job_count:$job_count";
290 if [[ -z $job_count ]]; then job_count
="0"; fi;
292 }; # Return number of background jobs
294 # Desc: Does not return until count_jobs() falls below $max_job_count
295 # Input: var max_job_count
296 # Output: return code 0
297 # Depends: count_jobs(), yell();
298 while [[ $
(count_jobs
) -ge $max_job_count ]]; do
299 printf "\r%d:Working..." "$SECONDS";
305 # Desc: Processes arguments provided to script.
306 # Usage: processArgs "$@"
308 # Input: "$@" (list of arguments provided to the function)
309 # Output: Sets following variables used by other functions:
310 # opVerbose Indicates verbose mode enable status. (ex: "true", "false")
311 # arrayPosArgs Array of remaining positional argments
313 # yell() Displays messages to stderr.
314 # vbm() Displays messsages to stderr if opVerbose set to "true".
315 # showUsage() Displays usage information about parent script.
316 # showVersion() Displays version about parent script.
317 # arrayPosArgs Global array for storing non-option positional arguments (i.e. arguments following the `--` option).
318 # External dependencies: bash (5.1.16), echo
320 # [1]: Marco Aurelio (2014-05-08). "echo that outputs to stderr". https://stackoverflow.com/a/23550347
321 # [2]: "Handling positional parameters" (2018-05-12). https://wiki.bash-hackers.org/scripting/posparams
323 # Initialize function
324 vbm
"DEBUG:processArgs function called."
327 while [ ! $# -eq 0 ]; do # While number of arguments ($#) is not (!) equal to (-eq) zero (0).
328 #yell "DEBUG:Starting processArgs while loop." # Debug stderr message. See [1].
329 #yell "DEBUG:Provided arguments are:""$*" # Debug stderr message. See [1].
331 --dry-run) # Do not run ots commands
332 option_dry_run
="true";
333 vbm
"DEBUG:Option enabled:dry run";;
334 -h |
--help) showUsage
; exit 1;; # Display usage.
335 --include-dotfiles) # Include dotfiles
336 option_include_dotfiles
="true";
337 vbm
"DEBUG:Option enabled:include dotfiles";;
338 -j |
--jobs) # Specify max_job_count
339 if [[ -n "$2" ]] && [[ "$2" =~ ^
[0-9]+$
]]; then
341 vbm
"STATUS:Max job count set to:$max_job_count";
345 die
"FATAL:Invalid job count:$2";
347 -r |
--recursive) # Specify recursive option
348 option_recursive
="true";
349 vbm
"DEBUG:option enabled:include files in dirs recursively";;
350 --version) showVersion
; exit 1;; # Show version
351 -v |
--verbose) opVerbose
="true"; vbm
"DEBUG:Verbose mode enabled.";; # Enable verbose mode. See [1].
352 --) # End of all options. See [2].
355 vbm
"DEBUG:adding to arrayPosArgs:$arg";
356 arrayPosArgs
+=("$arg");
359 -*) showUsage
; yell
"ERROR: Unrecognized option."; exit 1;; # Display usage
360 *) # Assume remaining arguments are positional arguments
362 vbm
"DEBUG:adding to arrayPosArgs:$arg";
363 arrayPosArgs
+=("$arg");
366 #*) showUsage; yell "ERROR: Unrecognized argument."; exit 1;; # Handle unrecognized options. See [1].
372 vbm
"DEBUG:processArgs function ended."
373 return 0; # Function finished.
374 }; # Evaluate script options from positional arguments (ex: $1, $2, $3, etc.).
375 get_parent_dirnames
() {
376 # Desc: Provides newline-delimited list of each parent dir of a file or dir
377 # Usage: get_parent_dirnames arg1
378 # Input: arg1 input path
379 # Output: stdout newline-delimited list of parent dirs
381 # Depends: yell(), die(), must()
385 if [[ $# -ne 1 ]]; then die
"FATAL:Incorrect number of arguments:$#"; fi;
386 if ! { [[ -f $1 ]] ||
[[ -d $1 ]]; }; then die
"FATAL:Not a file or dir:$1"; fi;
390 while [[ -f $path ]] ||
[[ -d $path ]]; do
391 path
="$(dirname "$path")";
392 name_base_previous
="$name_base";
393 name_base
="$(basename "$path")";
394 ## Check for stop condition (dirname returns same result as previous iteration)
395 if [[ $name_base == "$name_base_previous" ]]; then break; fi;
398 }; # Output parent dirnames to stdout
400 # Desc: Creates `.ots` file:
401 # - for each file specified in arrayPosArgs array
402 # - for each file in each dir specified in arrayPosArgs array
403 # Output file created alongside each file or in output directory specified by pathDirIn1
405 # Input: arrayPosArgs array with positional arguments
406 # pathDirOut1 path for output `.ots` files (if pathDirOut1 is specified and is a path)
407 # age_threshold var: mininum age in seconds to timestamp file
409 # Output: file(s) creates `.ots` file alongside specified files
410 # Depends: find (GNU findutils) 4.8.0, GNU Coreutils 8.32 (sort), GNU Parallel 20210822
411 # Ref/Attrib: [1] How to create an array of unique elements from a string/array in bash https://unix.stackexchange.com/a/167194
412 # [2] How to find files containing newlines in their names https://stackoverflow.com/a/21727028
413 # [3] Get mtime of specific file using Bash? https://stackoverflow.com/a/4774377
414 local -a file_list file_list_pruned
;
415 local -a files_to_verify files_to_upgrade files_to_stamp
416 local -a files_to_verify_pruned files_to_upgrade_pruned files_to_stamp_pruned
422 if ! checkapp ots
find parallel
; then
424 die
"FATAL:Missing dependencies.";
428 for arg
in "${arrayPosArgs[@]}"; do
429 arg
="$(readlink -f "$arg")";
430 if ! { [[ -d $arg ]] ||
[[ -f $arg ]]; }; then
431 die
"FATAL:Not a file or dir:arg:$arg";
435 # Display ots details
436 vbm
"$(type ots)"; # show how 'ots' is defined
437 #TODO: add option to define 'ots' as a bash function that
438 #populates the ots option '--bitcoin-node FILE' with a
439 #user-specified FILE.
442 vbm
"DEBUG:begin populate file_list array";
443 for item
in "${arrayPosArgs[@]}"; do
444 vbm
"DEBUG:adding to file list:item:$item";
446 ## Get full canonicalized path (follow symlinks)
447 item
="$(readlink -f "$item")";
448 vbm
"DEBUG:item full path:item:$item";
450 ## Add to list: files
451 if [[ -f $item ]]; then
452 vbm
"DEBUG:is a file:item:$item";
453 file_list
+=("$item");
454 vbm
"DEBUG:added to file_list:$item";
455 ## Add to list: files in dirs
456 elif [[ -d $item ]]; then
457 vbm
"DEBUG:is a dir:item:$item";
458 ### Check for recursive flag
459 if [[ "$option_recursive" == "true" ]]; then
460 vbm
"DEBUG:option_recursive:$option_recursive";
461 while read -r line
; do
462 file_list
+=("$line");
463 vbm
"DEBUG:added to file_list:$line";
464 done < <(find "$item" -type f
);
466 while read -r line
; do
467 file_list
+=("$line");
468 vbm
"DEBUG:added to file_list:$line";
469 done < <(find "$item" -maxdepth 1 -type f
);
472 die
"FATAL:Not a file or dir:item:$item";
475 if [[ $opVerbose == "true" ]]; then
476 vbm
"DEBUG:file_list:";
477 printf "%s\n" "${file_list[@]}";
481 for item
in "${file_list[@]}"; do
482 ## Ignore files that end in '.ots.bak'.
483 if [[ $item =~
'.ots.bak'$
]]; then
484 vbm
"DEBUG:Skipping file ending in '.ots.bak':item:$item";
485 continue; # skip to next item
489 if ! [[ $option_include_dotfiles == "true" ]]; then
490 ### Ignore files if '/.' contained within canonical path
491 pattern
="/\."; # a dot after a forward slash
492 if [[ $item =~
$pattern ]]; then
496 # ### Ignore files located beneath a dotfile directory (e.g. '/home/my_repo/.git/config')
497 # unset flag_contains_dotfile_parent;
498 # while read -r line; do
499 # #### Check line from output of get_parent_dirnames
501 # if [[ $line =~ $pattern ]]; then
502 # ##### line starts with '.'
503 # vbm "DEBUG:Dotfile parent detected. Not including in file_list_pruned:$item";
504 # vbm "DEBUG:Dotfile in path:item:$item";
505 # vbm "DEBUG:Dotfile parent:line:$line";
506 # flag_contains_dotfile_parent="true";
509 # done < <(get_parent_dirnames "$item");
510 # if [[ $flag_contains_dotfile_parent == "true" ]]; then
511 # unset flag_contains_dotfile_parent;
512 # continue; # skip to next item (i.e. don't add to file_list_pruned)
515 # ### Ignore dotfiles themselves
516 # item_basename="$(basename "$item")";
518 # if [[ $item_basename =~ $pattern ]]; then
519 # vbm "INFO :Skipping dotfile:item:$item";
520 # continue; # skip to next item
524 ## Ignore files with newlines present in filename. See [2].
525 if [[ $item =~ $
'\n' ]]; then
526 vbm
"DEBUG:Skipping file name with newline:$item";
527 continue; # skip to next item
530 ## Ignore files that end in '~'.
531 if [[ $item =~ ~$
]]; then
532 vbm
"DEBUG:Skipping file ending in tilde:$item";
533 continue; # skip to next item
536 ## Ignore files modified less than age_threshold. See [3].
537 time_now
="$(date +%s)"; # epoch seconds
538 item_age
="$(stat -c %Y "$item")"; # age in seconds
539 if [[ $
(( time_now
- item_age
)) -lt "$age_threshold" ]]; then
540 yell
"INFO :Skipping file modified less than $age_threshold seconds ago:item:$item";
541 continue; # skip to next item
544 ## Add item to file_list_pruned
545 file_list_pruned
+=("$item");
547 if [[ $opVerbose == "true" ]]; then
548 vbm
"DEBUG:file_list_pruned:";
549 printf "%s\n" "${file_list_pruned[@]}";
552 # Decide what actions to take for items in file_list_pruned
553 for item
in "${file_list_pruned[@]}"; do
554 vbm
"DEBUG:considering action to take for item:$item";
555 unset path_src path_prf dir_parent dir_source
;
557 ## Check file extension
558 if [[ $item =~ .ots$
]]; then
559 ### item ends in '.ots'. Item is proof file.
560 vbm
"DEBUG:item ends in '.ots'. Item is proof file:item:$item";
561 if [[ -f ${item%.ots} ]]; then
562 #### Proof file (item) is adjacent to source file
563 vbm
"DEBUG:Proof file (item) is adjacent to source file.";
564 ##### Upgrade and verify proof file against adjacent source file
565 vbm
"DEBUG:Marking proof file to be upgraded and verified.";
566 path_src
="${item%.ots}";
568 files_to_upgrade
+=("$(printf "%s
" "$path_prf")");
569 files_to_verify
+=("$(printf "%s
\n%s
" "$path_src" "$path_prf")");
571 #### Proof file (item) is not adjacent to source file
572 vbm
"DEBUG:Proof file (item) is not adjacent to source file.";
573 #### Check if source file in parent dir
574 dir_parent
="$(dirname "$
(dirname "$item")" )";
575 cand_src_filename
="$(basename "$item")";
576 cand_src_path
="$dir_parent/$cand_src_filename";
577 if [[ -f "$cand_src_path" ]]; then
578 ##### source file in parent dir
579 vbm
"DEBUG:found source file in parent:cand_src_path:$cand_src_path";
580 path_src
="$cand_src_path";
582 files_to_upgrade
+=("$(printf "%s
" "$path_prf")");
583 files_to_verify
+=("$(printf "%s
\n%s
" "$path_src" "$path_prf")");
585 #### Throw non-fatal error
586 vbm
"DEBUG:Source file not found for proof file:item:$item";
587 yell
"ERROR:Item is proof file but source filei not adjacent in parent dir. item:$item";
588 #### Attempt upgrade only
589 vbm
"DEBUG:Marking proof file to be upgraded.";
591 files_to_upgrade
+=("$(printf "%s
" "$path_prf")");
595 ### item does not end in '.ots'. Item is source file.
596 vbm
"DEBUG:item does NOT end in '.ots'. Item is source file.";
597 if [[ -f "$item".ots
]]; then
598 #### Proof file is adjacent to source file (item).
599 vbm
"DEBUG:Proof file is adjacent to source file (item).";
600 ##### Upgrade and verify proof file against adjacent source file.
601 vbm
"DEBUG:Marking proof file to be upgraded and verified.";
603 path_prf
="$item.ots";
604 files_to_upgrade
+=("$(printf "%s
" "$path_prf")");
605 files_to_verify
+=("$(printf "%s
\n%s
" "$path_src" "$path_prf")");
607 #### Proof file is not adjacent to source file (item).
608 #### Check if proof file is in subdir
609 vbm
"DEBUG:checking if proof file for source file (item) is in subdir:item:$item";
610 unset flag_proof_in_subdir
;
611 dir_item
="$(dirname "$item")";
612 cand_prf_filename
="$(basename "$item")".ots
;
613 while read -r line
; do
614 line_basename
="$(basename "$line")";
615 if [[ $line_basename == "$cand_prf_filename" ]]; then
616 flag_proof_in_subdir
="true";
618 vbm
"DEBUG:proof found in subdir at:line:$line";
621 done < <(find "$dir_item" -mindepth 2 -maxdepth 2 -type f
)
622 if [[ $flag_proof_in_subdir == "true" ]]; then
623 ##### Proof file is in subdir
624 vbm
"DEBUG:Proof file detected in subdir relative to source file (item)";
625 #path_prf="$path_prf"; # set in while loop
627 files_to_upgrade
+=("$(printf "%s
" "$path_prf")");
628 files_to_verify
+=("$(printf "%s
\n%s
" "$path_src" "$path_prf")");
630 ##### Proof file is not in subdir
631 vbm
"DEBUG:Proof file not detected in subdir relative to source file (item).";
632 #### Stamp source file
633 vbm
"DEBUG:Marking source file to be stamped.";
635 files_to_stamp
+=("$(printf "%s
" "$path_src")")
637 unset flag_proof_in_subdir
;
641 unset path_src path_prf dir_item dir_parent cand_prf_filename cand_src_filename line_basename cand_src_path
643 # Prune action lists.
644 ## Sort and prune file action arrays
646 while read -r -d $
'\0' line
; do
647 vbm
"DEBUG:adding to files_to_upgrade_pruned:line:$line";
648 files_to_upgrade_pruned
+=("$line");
649 done < <(printf "%s\0" "${files_to_upgrade[@]}" |
sort -zu | shuf
-z); # See [1]
650 if [[ $opVerbose == "true" ]]; then
651 vbm
"DEBUG:files_to_upgrade_pruned:";
652 printf "%s\n" "${files_to_upgrade_pruned[@]}";
656 while read -r -d $
'\0' line
; do
657 vbm
"DEBUG:adding to files_to_verify_pruned:line:$line";
658 files_to_verify_pruned
+=("$line");
659 done < <(printf "%s\0" "${files_to_verify[@]}" |
sort -zu | shuf
-z); # See [1]
660 if [[ $opVerbose == "true" ]]; then
661 vbm
"DEBUG:files_to_verify_pruned:";
662 printf "%s\n\n" "${files_to_verify_pruned[@]}";
666 while read -r -d $
'\0' line
; do
667 vbm
"DEBUG:adding to files_to_stamp_pruned:line:$line";
668 files_to_stamp_pruned
+=("$line");
669 done < <(printf "%s\0" "${files_to_stamp[@]}" |
sort -zu | shuf
-z); # See [1]
670 if [[ $opVerbose == "true" ]]; then
671 vbm
"DEBUG:files_to_stamp_pruned:";
672 printf "%s\n" "${files_to_stamp_pruned[@]}";
676 ## Assemble and execute upgrade file commands
677 for item
in "${files_to_upgrade_pruned[@]}"; do
678 wait_for_jobslot
&& {
679 path_prf
="$(cut -d $'\n' -f1 < <(echo "$item"))";
680 if [[ -z "$path_prf" ]]; then
681 yell
"ERROR:blank upgrade item encountered. Skipping:item:$item";
682 return 1; # would have been `continue` were it not in a subshell
684 vbm
"DEBUG:Attempting to upgrade proof file:path_prf:$path_prf";
685 if [[ ! $option_dry_run == "true" ]]; then
686 ### Try upgrade with known calendars in random order
687 while read -r url
; do
688 vbm
"DEBUG:Upgrading with calendar:url:$url";
690 #### assemble command
693 if [[ "$opVerbose" = "true" ]]; then cmd_temp
+=("-v"); fi;
694 cmd_temp
+=("-l" "$url" "--no-default-whitelist");
695 cmd_temp
+=("upgrade" "$path_prf");
696 if [[ "$opVerbose" = "true" ]]; then declare -p cmd_temp
; fi;
702 #ots -l "$url" --no-default-whitelist upgrade "$path_prf" && break;
703 done < <(printf "%s\n" "${calendars[@]}" | shuf
);
705 yell
"DEBUG:DRY RUN:Not running:\"ots upgrade $path_prf\"";
710 ## Assemble and execute verify file commands
711 for item
in "${files_to_verify_pruned[@]}"; do
712 wait_for_jobslot
&& {
713 path_src
="$(cut -d $'\n' -f1 < <(echo "$item"))";
714 path_prf
="$(cut -d $'\n' -f2 < <(echo "$item"))";
715 if [[ -z "$path_src" ]] ||
[[ -z "$path_prf" ]]; then
716 yell
"ERROR:blank verify item encountered. Skipping:item:$item";
717 return 1; # would have been `continue` were it not in a subshell
719 vbm
"DEBUG:Attempting to verify source file:path_src:$path_src";
720 vbm
"DEBUG: against proof file: path_prf:$path_prf";
721 if [[ ! $option_dry_run == "true" ]]; then
722 ### Try verify with known calendars in random order
723 while read -r url
; do
724 vbm
"DEBUG:Verifying with calendar:url:$url";
726 #### assemble command
729 if [[ "$opVerbose" = "true" ]]; then cmd_temp
+=("-v"); fi;
730 cmd_temp
+=("-l" "$url" "--no-default-whitelist");
731 cmd_temp
+=("verify" "-f" "$path_src" "$path_prf");
732 if [[ "$opVerbose" = "true" ]]; then declare -p cmd_temp
; fi;
738 #ots -l "$url" --no-default-whitelist verify -f "$path_src" "$path_prf" && break;
739 done < <(printf "%s\n" "${calendars[@]}" | shuf
);
741 yell
"DEBUG:DRY RUN:Not running:\"ots verify -f $path_src $path_prf\"";
746 ## Assemble and execute stamp file commands
747 for item
in "${files_to_stamp_pruned[@]}"; do
748 wait_for_jobslot
&& {
749 path_src
="$(cut -d $'\n' -f1 < <(echo "$item"))";
750 if [[ -z "$path_src" ]]; then
751 yell
"ERROR:blank stamp item encountered. Skipping:item:$item";
752 return 1; # would have been `continue` were it not in a subshell
754 vbm
"DEBUG:Attempting to stamp source file:path_src:$path_src";
755 if [[ ! $option_dry_run == "true" ]]; then
757 #### assemble command
760 if [[ "$opVerbose" = "true" ]]; then cmd_temp
+=("-v"); fi;
761 cmd_temp
+=("stamp" "$path_src");
762 if [[ "$opVerbose" = "true" ]]; then declare -p cmd_temp
; fi;
765 if "${cmd_temp[@]}"; then
766 sleep "$stamp_throttle" || die
"FATAL:Invalid stamp throttle.";
768 yell
"ERROR:Could not stamp file with command:$(declare -p cmd_temp)";
769 sleep "$stamp_throttle_fail" || die
"FATAL:Invalid stamp throttle.";
773 #ots stamp "$path_src";
775 yell
"DEBUG:DRY RUN:Not running:\"ots stamp $path_src\"";
780 ## Wait for jobs to finish.