feat(user/rsync_tranches):Make more granular
[BK-2020-03.git] / user / bkots
CommitLineData
b64bbf81
SBS
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
3a8398d2
SBS
8declare -ag calendars;
9calendars+=("https://finney.calendar.eternitywall.com");
10calendars+=("https://btc.calendar.catallaxy.com");
11calendars+=("https://alice.btc.calendar.opentimestamps.org");
12calendars+=("https://bob.btc.calendar.opentimestamps.org");
90152692 13declare -a commands # array for storing assembled commands
3434f509 14age_threshold="60"; # min age to add file; seconds;
b64bbf81
SBS
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'
43952687 203bkots 1.0.3
b64bbf81
SBS
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
3434f509
SBS
264 Files modified less than 1 minute ago are ignored.
265
b64bbf81
SBS
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
90152692
SBS
357cmdwrap() {
358 # print command to stderr
359 echo "$@" 1>&2;
360
361 # execute command
362 "$@";
363}; # print and execute string together
364export -f cmdwrap; # export cmdwrap for use in other functions
b64bbf81
SBS
365main() {
366 # Desc: Creates `.ots` file:
367 # - for each file specified in arrayPosArgs array
368 # - for each file in each dir specified in arrayPosArgs array
369 # Output file created alongside each file or in output directory specified by pathDirIn1
370 # Usage: main "$@";
3434f509
SBS
371 # Input: arrayPosArgs array with positional arguments
372 # pathDirOut1 path for output `.ots` files (if pathDirOut1 is specified and is a path)
373 # age_threshold var: mininum age in seconds to timestamp file
374
b64bbf81 375 # Output: file(s) creates `.ots` file alongside specified files
90152692 376 # Depends: find (GNU findutils) 4.8.0, GNU Coreutils 8.32 (sort), GNU Parallel 20210822
b64bbf81
SBS
377 # Ref/Attrib: [1] How to create an array of unique elements from a string/array in bash https://unix.stackexchange.com/a/167194
378 # [2] How to find files containing newlines in their names https://stackoverflow.com/a/21727028
3434f509 379 # [3] Get mtime of specific file using Bash? https://stackoverflow.com/a/4774377
90152692 380 # [4] Search/Replace with string substitution instead of sed. https://www.shellcheck.net/wiki/SC2001
b64bbf81
SBS
381 local -a file_list file_list_pruned;
382 local -a files_to_verify files_to_upgrade files_to_stamp
3a8398d2
SBS
383 local -a files_to_verify_pruned files_to_upgrade_pruned files_to_stamp_pruned
384
b64bbf81
SBS
385 # Process args
386 processArgs "$@";
387
388 # Check dependencies
90152692 389 if ! checkapp ots find parallel; then
b64bbf81
SBS
390 displayMissing;
391 die "FATAL:Missing dependencies.";
392 fi;
393
3434f509
SBS
394 # Check arguments
395 for arg in "${arrayPosArgs[@]}"; do
396 arg="$(readlink -f "$arg")";
397 if ! { [[ -d $arg ]] || [[ -f $arg ]]; }; then
398 die "FATAL:Not a file or dir:arg:$arg";
b64bbf81 399 fi;
3434f509 400 done;
b64bbf81
SBS
401
402 # Display ots details
403 vbm "$(type ots)"; # show how 'ots' is defined
404 #TODO: add option to define 'ots' as a bash function that
405 #populates the ots option '--bitcoin-node FILE' with a
406 #user-specified FILE.
407
408 # Populate file_list
409 vbm "DEBUG:begin populate file_list array";
410 for item in "${arrayPosArgs[@]}"; do
411 vbm "DEBUG:adding to file list:item:$item";
412
413 ## Get full canonicalized path (follow symlinks)
414 item="$(readlink -f "$item")";
415 vbm "DEBUG:item full path:item:$item";
416
417 ## Add to list: files
418 if [[ -f $item ]]; then
419 vbm "DEBUG:is a file:item:$item";
420 file_list+=("$item");
421 vbm "DEBUG:added to file_list:$item";
422 ## Add to list: files in dirs
423 elif [[ -d $item ]]; then
424 vbm "DEBUG:is a dir:item:$item";
425 ### Check for recursive flag
426 if [[ "$option_recursive" == "true" ]]; then
427 vbm "DEBUG:option_recursive:$option_recursive";
428 while read -r line; do
429 file_list+=("$line");
430 vbm "DEBUG:added to file_list:$line";
431 done < <(find "$item" -type f);
432 else
433 while read -r line; do
434 file_list+=("$line");
435 vbm "DEBUG:added to file_list:$line";
436 done < <(find "$item" -maxdepth 1 -type f);
437 fi;
438 else
3434f509 439 die "FATAL:Not a file or dir:item:$item";
b64bbf81
SBS
440 fi;
441 done;
442 if [[ $opVerbose == "true" ]]; then
443 vbm "DEBUG:file_list:";
444 printf "%s\n" "${file_list[@]}";
445 fi;
446
447 # Prune file_list
448 for item in "${file_list[@]}"; do
3434f509
SBS
449 ## Ignore files that end in '.ots.bak'.
450 if [[ $item =~ '.ots.bak'$ ]]; then
90152692 451 vbm "DEBUG:Skipping file ending in '.ots.bak':item:$item";
3434f509
SBS
452 continue; # skip to next item
453 fi;
454
455 ## Ignore dotfiles
b64bbf81 456 if ! [[ $option_include_dotfiles == "true" ]]; then
90152692
SBS
457 ### Ignore files if '/.' contained within canonical path
458 pattern="/\."; # a dot after a forward slash
459 if [[ $item =~ $pattern ]]; then
460 continue;
b64bbf81 461 fi;
90152692
SBS
462
463 # ### Ignore files located beneath a dotfile directory (e.g. '/home/my_repo/.git/config')
464 # unset flag_contains_dotfile_parent;
465 # while read -r line; do
466 # #### Check line from output of get_parent_dirnames
467 # pattern="^\.";
468 # if [[ $line =~ $pattern ]]; then
469 # ##### line starts with '.'
470 # vbm "DEBUG:Dotfile parent detected. Not including in file_list_pruned:$item";
471 # vbm "DEBUG:Dotfile in path:item:$item";
472 # vbm "DEBUG:Dotfile parent:line:$line";
473 # flag_contains_dotfile_parent="true";
474 # break
475 # fi;
476 # done < <(get_parent_dirnames "$item");
477 # if [[ $flag_contains_dotfile_parent == "true" ]]; then
478 # unset flag_contains_dotfile_parent;
479 # continue; # skip to next item (i.e. don't add to file_list_pruned)
480 # fi;
481
482 # ### Ignore dotfiles themselves
483 # item_basename="$(basename "$item")";
484 # pattern="^\.";
485 # if [[ $item_basename =~ $pattern ]]; then
486 # vbm "INFO :Skipping dotfile:item:$item";
487 # continue; # skip to next item
488 # fi;
b64bbf81 489 fi;
3434f509 490
b64bbf81
SBS
491 ## Ignore files with newlines present in filename. See [2].
492 if [[ $item =~ $'\n' ]]; then
90152692 493 vbm "DEBUG:Skipping file name with newline:$item";
b64bbf81
SBS
494 continue; # skip to next item
495 fi;
496
497 ## Ignore files that end in '~'.
498 if [[ $item =~ ~$ ]]; then
90152692 499 vbm "DEBUG:Skipping file ending in tilde:$item";
b64bbf81
SBS
500 continue; # skip to next item
501 fi;
fdf23d9f 502
3434f509
SBS
503 ## Ignore files modified less than age_threshold. See [3].
504 time_now="$(date +%s)"; # epoch seconds
505 item_age="$(stat -c %Y "$item")"; # age in seconds
506 if [[ $(( time_now - item_age )) -lt "$age_threshold" ]]; then
507 yell "INFO :Skipping file modified less than $age_threshold seconds ago:item:$item";
fdf23d9f
SBS
508 continue; # skip to next item
509 fi;
b64bbf81
SBS
510
511 ## Add item to file_list_pruned
512 file_list_pruned+=("$item");
513 done;
514 if [[ $opVerbose == "true" ]]; then
515 vbm "DEBUG:file_list_pruned:";
516 printf "%s\n" "${file_list_pruned[@]}";
517 fi;
518
519 # Decide what actions to take for items in file_list_pruned
520 for item in "${file_list_pruned[@]}"; do
521 vbm "DEBUG:considering action to take for item:$item";
522 unset path_src path_prf dir_parent dir_source;
523
524 ## Check file extension
525 if [[ $item =~ .ots$ ]]; then
526 ### item ends in '.ots'. Item is proof file.
527 vbm "DEBUG:item ends in '.ots'. Item is proof file:item:$item";
528 if [[ -f ${item%.ots} ]]; then
529 #### Proof file (item) is adjacent to source file
530 vbm "DEBUG:Proof file (item) is adjacent to source file.";
531 ##### Upgrade and verify proof file against adjacent source file
532 vbm "DEBUG:Marking proof file to be upgraded and verified.";
533 path_src="${item%.ots}";
534 path_prf="$item";
535 files_to_upgrade+=("$(printf "%s" "$path_prf")");
536 files_to_verify+=("$(printf "%s\n%s" "$path_src" "$path_prf")");
537 else
538 #### Proof file (item) is not adjacent to source file
539 vbm "DEBUG:Proof file (item) is not adjacent to source file.";
540 #### Check if source file in parent dir
541 dir_parent="$(dirname "$(dirname "$item")" )";
542 cand_src_filename="$(basename "$item")";
543 cand_src_path="$dir_parent/$cand_src_filename";
544 if [[ -f "$cand_src_path" ]]; then
545 ##### source file in parent dir
546 vbm "DEBUG:found source file in parent:cand_src_path:$cand_src_path";
547 path_src="$cand_src_path";
548 path_prf="$item";
549 files_to_upgrade+=("$(printf "%s" "$path_prf")");
550 files_to_verify+=("$(printf "%s\n%s" "$path_src" "$path_prf")");
551 else
552 #### Throw non-fatal error
553 vbm "DEBUG:Source file not found for proof file:item:$item";
554 yell "ERROR:Item is proof file but source filei not adjacent in parent dir. item:$item";
555 #### Attempt upgrade only
556 vbm "DEBUG:Marking proof file to be upgraded.";
557 path_prf="$item";
558 files_to_upgrade+=("$(printf "%s" "$path_prf")");
559 fi;
560 fi;
561 else
562 ### item does not end in '.ots'. Item is source file.
563 vbm "DEBUG:item does NOT end in '.ots'. Item is source file.";
564 if [[ -f "$item".ots ]]; then
565 #### Proof file is adjacent to source file (item).
566 vbm "DEBUG:Proof file is adjacent to source file (item).";
567 ##### Upgrade and verify proof file against adjacent source file.
568 vbm "DEBUG:Marking proof file to be upgraded and verified.";
569 path_src="$item";
570 path_prf="$item.ots";
571 files_to_upgrade+=("$(printf "%s" "$path_prf")");
572 files_to_verify+=("$(printf "%s\n%s" "$path_src" "$path_prf")");
573 else
574 #### Proof file is not adjacent to source file (item).
575 #### Check if proof file is in subdir
576 vbm "DEBUG:checking if proof file for source file (item) is in subdir:item:$item";
577 unset flag_proof_in_subdir;
578 dir_item="$(dirname "$item")";
579 cand_prf_filename="$(basename "$item")".ots;
580 while read -r line; do
581 line_basename="$(basename "$line")";
582 if [[ $line_basename == "$cand_prf_filename" ]]; then
583 flag_proof_in_subdir="true";
584 path_prf="$line";
585 vbm "DEBUG:proof found in subdir at:line:$line";
586 break;
587 fi;
588 done < <(find "$dir_item" -mindepth 2 -maxdepth 2 -type f)
589 if [[ $flag_proof_in_subdir == "true" ]]; then
590 ##### Proof file is in subdir
591 vbm "DEBUG:Proof file detected in subdir relative to source file (item)";
592 #path_prf="$path_prf"; # set in while loop
593 path_src="$item";
594 files_to_upgrade+=("$(printf "%s" "$path_prf")");
595 files_to_verify+=("$(printf "%s\n%s" "$path_src" "$path_prf")");
596 else
597 ##### Proof file is not in subdir
598 vbm "DEBUG:Proof file not detected in subdir relative to source file (item).";
599 #### Stamp source file
600 vbm "DEBUG:Marking source file to be stamped.";
601 path_src="$item";
602 files_to_stamp+=("$(printf "%s" "$path_src")")
603 fi;
604 unset flag_proof_in_subdir;
605 fi;
606 fi;
607 done;
608 unset path_src path_prf dir_item dir_parent cand_prf_filename cand_src_filename line_basename cand_src_path
609
610 # Prune action lists.
611 ## Sort and prune file action arrays
612 ### files to upgrade
613 while read -r -d $'\0' line; do
614 vbm "DEBUG:adding to files_to_upgrade_pruned:line:$line";
615 files_to_upgrade_pruned+=("$line");
616 done < <(printf "%s\0" "${files_to_upgrade[@]}" | sort -zu | shuf -z); # See [1]
617 if [[ $opVerbose == "true" ]]; then
618 vbm "DEBUG:files_to_upgrade_pruned:";
619 printf "%s\n" "${files_to_upgrade_pruned[@]}";
620 fi;
621
622 ### files to verify
623 while read -r -d $'\0' line; do
624 vbm "DEBUG:adding to files_to_verify_pruned:line:$line";
625 files_to_verify_pruned+=("$line");
626 done < <(printf "%s\0" "${files_to_verify[@]}" | sort -zu | shuf -z); # See [1]
627 if [[ $opVerbose == "true" ]]; then
628 vbm "DEBUG:files_to_verify_pruned:";
629 printf "%s\n\n" "${files_to_verify_pruned[@]}";
630 fi;
631
632 ### files to stamp
633 while read -r -d $'\0' line; do
634 vbm "DEBUG:adding to files_to_stamp_pruned:line:$line";
635 files_to_stamp_pruned+=("$line");
636 done < <(printf "%s\0" "${files_to_stamp[@]}" | sort -zu | shuf -z); # See [1]
637 if [[ $opVerbose == "true" ]]; then
638 vbm "DEBUG:files_to_stamp_pruned:";
639 printf "%s\n" "${files_to_stamp_pruned[@]}";
640 fi;
641
642 # Act on files
90152692 643 ## Assemble upgrade file commands
b64bbf81
SBS
644 for item in "${files_to_upgrade_pruned[@]}"; do
645 path_prf="$(cut -d $'\n' -f1 < <(echo "$item"))";
90152692 646 path_prf_sesc="${path_prf//\"/\\\"}"; # escape path double quotes. See [4].
b64bbf81
SBS
647 if [[ -z "$path_prf" ]]; then
648 yell "ERROR:blank upgrade item encountered. Skipping:item:$item";
649 continue;
650 fi;
651 vbm "DEBUG:Attempting to upgrade proof file:path_prf:$path_prf";
652 if [[ ! $option_dry_run == "true" ]]; then
fdf23d9f
SBS
653 ### Try upgrade with known calendars in random order
654 while read -r url; do
655 vbm "DEBUG:Upgrading with calendar:url:$url";
90152692
SBS
656 if [[ "$opVerbose" = "true" ]]; then
657 commands+=("cmdwrap ots -v -l $url --no-default-whitelist upgrade \"$path_prf_sesc\"") && break;
658 else
659 commands+=("cmdwrap ots -l $url --no-default-whitelist upgrade \"$path_prf_sesc\"") && break;
660 fi;
661 #ots -l "$url" --no-default-whitelist upgrade "$path_prf" && break;
fdf23d9f 662 done < <(printf "%s\n" "${calendars[@]}" | shuf);
b64bbf81 663 else
b108df29 664 yell "DEBUG:DRY RUN:Not running:\"ots upgrade $path_prf\"";
b64bbf81 665 fi;
b64bbf81
SBS
666 done;
667
90152692 668 ## Assemble verify file commands
b64bbf81
SBS
669 for item in "${files_to_verify_pruned[@]}"; do
670 path_src="$(cut -d $'\n' -f1 < <(echo "$item"))";
671 path_prf="$(cut -d $'\n' -f2 < <(echo "$item"))";
90152692
SBS
672 path_src_sesc="${path_src//\"/\\\"}"; # escape path double quotes. See [4].
673 path_prf_sesc="${path_prf//\"/\\\"}"; # escape path double quotes. See [4].
b64bbf81
SBS
674 if [[ -z "$path_src" ]] || [[ -z "$path_prf" ]]; then
675 yell "ERROR:blank verify item encountered. Skipping:item:$item";
676 continue;
677 fi;
678 vbm "DEBUG:Attempting to verify source file:path_src:$path_src";
679 vbm "DEBUG: against proof file: path_prf:$path_prf";
680 if [[ ! $option_dry_run == "true" ]]; then
fdf23d9f
SBS
681 ### Try verify with known calendars in random order
682 while read -r url; do
683 vbm "DEBUG:Verifying with calendar:url:$url";
90152692
SBS
684 if [[ "$opVerbose" = "true" ]]; then
685 commands+=("cmdwrap ots -v -l $url --no-default-whitelist verify -f \"$path_src_sesc\" \"$path_prf_sesc\"") && break;
686 else
687 commands+=("cmdwrap ots -l $url --no-default-whitelist verify -f \"$path_src_sesc\" \"$path_prf_sesc\"") && break;
688 fi;
689 #ots -l "$url" --no-default-whitelist verify -f "$path_src" "$path_prf" && break;
fdf23d9f 690 done < <(printf "%s\n" "${calendars[@]}" | shuf);
b64bbf81 691 else
b108df29 692 yell "DEBUG:DRY RUN:Not running:\"ots verify -f $path_src $path_prf\"";
b64bbf81 693 fi;
b64bbf81
SBS
694 done;
695
90152692 696 ## Assemble stamp file commands
b64bbf81
SBS
697 for item in "${files_to_stamp_pruned[@]}"; do
698 path_src="$(cut -d $'\n' -f1 < <(echo "$item"))";
90152692 699 path_src_sesc="${path_src//\"/\\\"}"; # escape path double quotes. See [4].
b64bbf81
SBS
700 if [[ -z "$path_src" ]]; then
701 yell "ERROR:blank stamp item encountered. Skipping:item:$item";
702 continue;
703 fi;
704 vbm "DEBUG:Attempting to stamp source file:path_src:$path_src";
705 if [[ ! $option_dry_run == "true" ]]; then
90152692
SBS
706 if [[ "$opVerbose" = "true" ]]; then
707 commands+=("cmdwrap ots -v stamp \"$path_src_sesc\"");
708 else
709 commands+=("cmdwrap ots stamp \"$path_src_sesc\"");
710 fi;
711 #ots stamp "$path_src";
b64bbf81 712 else
b108df29 713 yell "DEBUG:DRY RUN:Not running:\"ots stamp $path_src\"";
b64bbf81 714 fi;
b64bbf81
SBS
715 done;
716
90152692 717 ## Run commands
814a75ea 718 #yell "DEBUG:commands:$(printf "%s\n" "${commands[@]}")";
43952687 719 printf "%s\n" "${commands[@]}" | parallel --group --jobs 25%;
90152692 720
b64bbf81
SBS
721}; # main program
722
723# Run program
724main "$@";
3904c727 725exit 0;