]> zdv2.bktei.com Git - BK-2020-03.git/blob - user/bkots
chore(unitproc/find_erotica.sh):Update nouns
[BK-2020-03.git] / user / bkots
1 #!/usr/bin/env bash
2 # Desc: Recursively timestamp files with OpenTimestamp
3 # Usage: bkots [PATH]
4 # Version: 0.2.0
5
6 # Define variables
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");
20
21 # Declare functions
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
25 checkapp() {
26 # Desc: If arg is a command, save result in assoc array 'appRollCall'
27 # Usage: checkapp arg1 arg2 arg3 ...
28 # Version: 0.1.1
29 # Input: global assoc. array 'appRollCall'
30 # Output: adds/updates key(value) to global assoc array 'appRollCall'
31 # Depends: bash 5.0.3
32 local returnState
33
34 #===Process Args===
35 for arg in "$@"; do
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;
39 else
40 appRollCall[$arg]="false"; returnState="false";
41 fi;
42 done;
43
44 #===Determine function return code===
45 if [ "$returnState" = "true" ]; then
46 return 0;
47 else
48 return 1;
49 fi;
50 } # Check that app exists
51 checkfile() {
52 # Desc: If arg is a file path, save result in assoc array 'fileRollCall'
53 # Usage: checkfile arg1 arg2 arg3 ...
54 # Version: 0.1.1
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
58 # Depends: bash 5.0.3
59 local returnState
60
61 #===Process Args===
62 for arg in "$@"; do
63 if [ -f "$arg" ]; then
64 fileRollCall["$arg"]="true";
65 if ! [ "$returnState" = "false" ]; then returnState="true"; fi;
66 else
67 fileRollCall["$arg"]="false"; returnState="false";
68 fi;
69 done;
70
71 #===Determine function return code===
72 if [ "$returnState" = "true" ]; then
73 return 0;
74 else
75 return 1;
76 fi;
77 } # Check that file exists
78 checkdir() {
79 # Desc: If arg is a dir path, save result in assoc array 'dirRollCall'
80 # Usage: checkdir arg1 arg2 arg3 ...
81 # Version 0.1.2
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
85 # Depends: Bash 5.0.3
86 local returnState
87
88 #===Process Args===
89 for arg in "$@"; do
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
95 else
96 dirRollCall["$arg"]="false"; returnState="false";
97 fi
98 done
99
100 #===Determine function return code===
101 if [ "$returnState" = "true" ]; then
102 return 0;
103 else
104 return 1;
105 fi
106 } # Check that dir exists
107 displayMissing() {
108 # Desc: Displays missing apps, files, and dirs
109 # Usage: displayMissing
110 # Version 1.0.0
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
117
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 ";
127 appMissing="true";
128 fi;
129 done;
130 if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing.
131 echo "$missingApps" 1>&2;
132 fi;
133 unset value;
134 #===END Display Missing Apps===
135
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 ";
144 fileMissing="true";
145 fi;
146 done;
147 if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing.
148 echo "$missingFiles" 1>&2;
149 fi;
150 unset value;
151 #===END Display Missing Files===
152
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 ";
161 dirMissing="true";
162 fi;
163 done;
164 if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing.
165 echo "$missingDirs" 1>&2;
166 fi;
167 unset value;
168 #===END Display Missing Directories===
169
170 #==END Display errors==
171 #==BEGIN Determine function return code===
172 if [ "$appMissing" == "true" ] || [ "$fileMissing" == "true" ] || [ "$dirMissing" == "true" ]; then
173 return 1;
174 else
175 return 0;
176 fi
177 #==END Determine function return code===
178 } # Display missing apps, files, dirs
179 vbm() {
180 # Description: Prints verbose message ("vbm") to stderr if opVerbose is set to "true".
181 # Usage: vbm "DEBUG :verbose message here"
182 # Version 0.2.0
183 # Input: arg1: string
184 # vars: opVerbose
185 # Output: stderr
186 # Depends: bash 5.0.3, GNU-coreutils 8.30 (echo, date)
187
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.
191 fi
192
193 # End function
194 return 0; # Function finished.
195 } # Displays message if opVerbose true
196 showVersion() {
197 # Desc: Displays script version and license information.
198 # Usage: showVersion
199 # Version: 0.0.1
200 # Input: scriptVersion var containing version string
201 # Output: stdout
202 # Depends: vbm(), yell, GNU-coreutils 8.30
203
204 # Initialize function
205 vbm "DEBUG:showVersion function called."
206
207 cat <<'EOF'
208 bkots 2.1.1
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.
213
214 GNU Coreutils 8.32
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.
219 EOF
220
221 # End function
222 vbm "DEBUG:showVersion function ended."
223 return 0; # Function finished.
224 } # Display script version.
225 showUsage() {
226 # Desc: Display script usage information
227 # Usage: showUsage
228 # Version 0.0.1
229 # Input: none
230 # Output: stdout
231 # Depends: GNU-coreutils 8.30 (cat)
232 cat <<'EOF'
233 USAGE:
234 bkots [ options ] [PATH...]
235
236 POSITIONAL ARGUMENTS:
237 PATH Path(s) of file(s) or directory(ies)
238
239 OPTIONS:
240 --dry-run
241 Do everything except run 'ots' commands.
242 -h, --help
243 Display help information.
244 --include-dotfiles
245 Include files and directories starting with '.' (not
246 included by default).
247 -j, --jobs
248 Specify simultaneous job count (default: 2)
249 -r, --recursive
250 Consider files in dirs recursively.
251 --version
252 Display script version.
253 -v, --verbose
254 Display debugging info.
255 --
256 Mark end of options. Interpret remaining arguments as
257 positional arguments.
258
259 DESCRIPTION:
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.
264
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').
270
271 Files modified less than 1 minute ago are ignored.
272
273 EXAMPLES:
274 bkots -v foo.txt
275 bkots foo.txt bar.pdf /home/username/Pictures/
276 EOF
277 } # Display information on how to use this script.
278 count_jobs() {
279 # Desc: Count and return total number of jobs
280 # Usage: count_jobs
281 # Input: None.
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;
285 # Version: 0.0.1
286
287 local job_count;
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;
291 echo "$job_count";
292 }; # Return number of background jobs
293 wait_for_jobslot() {
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";
300 sleep 1;
301 done;
302 return 0;
303 };
304 processArgs() {
305 # Desc: Processes arguments provided to script.
306 # Usage: processArgs "$@"
307 # Version: 1.0.0
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
312 # Depends:
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
319 # Ref./Attrib.:
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
322
323 # Initialize function
324 vbm "DEBUG:processArgs function called."
325
326 # Perform work
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].
330 case "$1" in
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
340 max_job_count="$2";
341 vbm "STATUS:Max job count set to:$max_job_count";
342 shift;
343 else
344 showUsage;
345 die "FATAL:Invalid job count:$2";
346 fi;;
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].
353 shift;
354 for arg in "$@"; do
355 vbm "DEBUG:adding to arrayPosArgs:$arg";
356 arrayPosArgs+=("$arg");
357 done;
358 break;;
359 -*) showUsage; yell "ERROR: Unrecognized option."; exit 1;; # Display usage
360 *) # Assume remaining arguments are positional arguments
361 for arg in "$@"; do
362 vbm "DEBUG:adding to arrayPosArgs:$arg";
363 arrayPosArgs+=("$arg");
364 done;
365 break;;
366 #*) showUsage; yell "ERROR: Unrecognized argument."; exit 1;; # Handle unrecognized options. See [1].
367 esac
368 shift
369 done
370
371 # End function
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
380 # Version: 0.0.1
381 # Depends: yell(), die(), must()
382 local path
383
384 # Check input
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;
387
388 # Process path
389 path="$1";
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;
396 echo "$name_base";
397 done;
398 }; # Output parent dirnames to stdout
399 main() {
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
404 # Usage: main "$@";
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
408
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
417
418 # Process args
419 processArgs "$@";
420
421 # Check dependencies
422 if ! checkapp ots find parallel; then
423 displayMissing;
424 die "FATAL:Missing dependencies.";
425 fi;
426
427 # Check arguments
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";
432 fi;
433 done;
434
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.
440
441 # Populate file_list
442 vbm "DEBUG:begin populate file_list array";
443 for item in "${arrayPosArgs[@]}"; do
444 vbm "DEBUG:adding to file list:item:$item";
445
446 ## Get full canonicalized path (follow symlinks)
447 item="$(readlink -f "$item")";
448 vbm "DEBUG:item full path:item:$item";
449
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);
465 else
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);
470 fi;
471 else
472 die "FATAL:Not a file or dir:item:$item";
473 fi;
474 done;
475 if [[ $opVerbose == "true" ]]; then
476 vbm "DEBUG:file_list:";
477 printf "%s\n" "${file_list[@]}";
478 fi;
479
480 # Prune 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
486 fi;
487
488 ## Ignore dotfiles
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
493 continue;
494 fi;
495
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
500 # pattern="^\.";
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";
507 # break
508 # fi;
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)
513 # fi;
514
515 # ### Ignore dotfiles themselves
516 # item_basename="$(basename "$item")";
517 # pattern="^\.";
518 # if [[ $item_basename =~ $pattern ]]; then
519 # vbm "INFO :Skipping dotfile:item:$item";
520 # continue; # skip to next item
521 # fi;
522 fi;
523
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
528 fi;
529
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
534 fi;
535
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
542 fi;
543
544 ## Add item to file_list_pruned
545 file_list_pruned+=("$item");
546 done;
547 if [[ $opVerbose == "true" ]]; then
548 vbm "DEBUG:file_list_pruned:";
549 printf "%s\n" "${file_list_pruned[@]}";
550 fi;
551
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;
556
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}";
567 path_prf="$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 (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";
581 path_prf="$item";
582 files_to_upgrade+=("$(printf "%s" "$path_prf")");
583 files_to_verify+=("$(printf "%s\n%s" "$path_src" "$path_prf")");
584 else
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.";
590 path_prf="$item";
591 files_to_upgrade+=("$(printf "%s" "$path_prf")");
592 fi;
593 fi;
594 else
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.";
602 path_src="$item";
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")");
606 else
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";
617 path_prf="$line";
618 vbm "DEBUG:proof found in subdir at:line:$line";
619 break;
620 fi;
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
626 path_src="$item";
627 files_to_upgrade+=("$(printf "%s" "$path_prf")");
628 files_to_verify+=("$(printf "%s\n%s" "$path_src" "$path_prf")");
629 else
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.";
634 path_src="$item";
635 files_to_stamp+=("$(printf "%s" "$path_src")")
636 fi;
637 unset flag_proof_in_subdir;
638 fi;
639 fi;
640 done;
641 unset path_src path_prf dir_item dir_parent cand_prf_filename cand_src_filename line_basename cand_src_path
642
643 # Prune action lists.
644 ## Sort and prune file action arrays
645 ### files to upgrade
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[@]}";
653 fi;
654
655 ### files to verify
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[@]}";
663 fi;
664
665 ### files to stamp
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[@]}";
673 fi;
674
675 # Act on files
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
683 fi;
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";
689
690 #### assemble command
691 local -a cmd_temp;
692 cmd_temp=("ots");
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;
697
698 #### execute command
699 "${cmd_temp[@]}";
700 unset cmd_temp;
701 break;
702 #ots -l "$url" --no-default-whitelist upgrade "$path_prf" && break;
703 done < <(printf "%s\n" "${calendars[@]}" | shuf);
704 else
705 yell "DEBUG:DRY RUN:Not running:\"ots upgrade $path_prf\"";
706 fi;
707 } &
708 done;
709
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
718 fi;
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";
725
726 #### assemble command
727 local -a cmd_temp;
728 cmd_temp=("ots");
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;
733
734 #### execute command
735 "${cmd_temp[@]}";
736 unset cmd_temp;
737 break;
738 #ots -l "$url" --no-default-whitelist verify -f "$path_src" "$path_prf" && break;
739 done < <(printf "%s\n" "${calendars[@]}" | shuf);
740 else
741 yell "DEBUG:DRY RUN:Not running:\"ots verify -f $path_src $path_prf\"";
742 fi;
743 } &
744 done;
745
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
753 fi;
754 vbm "DEBUG:Attempting to stamp source file:path_src:$path_src";
755 if [[ ! $option_dry_run == "true" ]]; then
756
757 #### assemble command
758 local -a cmd_temp;
759 cmd_temp=("ots");
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;
763
764 #### execute command
765 if "${cmd_temp[@]}"; then
766 sleep "$stamp_throttle" || die "FATAL:Invalid stamp throttle.";
767 else
768 yell "ERROR:Could not stamp file with command:$(declare -p cmd_temp)";
769 sleep "$stamp_throttle_fail" || die "FATAL:Invalid stamp throttle.";
770 fi;
771
772 unset cmd_temp;
773 #ots stamp "$path_src";
774 else
775 yell "DEBUG:DRY RUN:Not running:\"ots stamp $path_src\"";
776 fi;
777 } &
778 done;
779
780 ## Wait for jobs to finish.
781 wait;
782 }; # main program
783
784 # Run program
785 main "$@";
786 exit 0;