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