feat(bkgpslog):Add functions, group code into functions
[EVA-2020-02.git] / exec / bkgpslog
index 92890646ebb2bb5562f01365f6153a01811f9287..6daa5414b82c5c3d1561569e61262469b8dc0f58 100755 (executable)
@@ -6,7 +6,7 @@
 
 #==BEGIN Define script parameters==
 ## Logging Behavior parameters
 
 #==BEGIN Define script parameters==
 ## Logging Behavior parameters
-BUFFER_TTL="60"; # time between file writes
+BUFFER_TTL="300"; # time between file writes
 SCRIPT_TTL="day"; # (day|hour)
 #### TZ="UTC"; export TZ; # Default time zone; overridden by '--time-zone=[str]' option
 DIR_TMP_DEFAULT="/dev/shm"; # Default parent of working directory
 SCRIPT_TTL="day"; # (day|hour)
 #### TZ="UTC"; export TZ; # Default time zone; overridden by '--time-zone=[str]' option
 DIR_TMP_DEFAULT="/dev/shm"; # Default parent of working directory
@@ -14,14 +14,16 @@ DIR_TMP_DEFAULT="/dev/shm"; # Default parent of working directory
 SCRIPT_TIME_START=$(date +%Y%m%dT%H%M%S.%N);
 PATH="$HOME/.local/bin:$PATH";   # Add "$(systemd-path user-binaries)" path in case apps saved there
 SCRIPT_HOSTNAME=$(hostname);     # Save hostname of system running this script.
 SCRIPT_TIME_START=$(date +%Y%m%dT%H%M%S.%N);
 PATH="$HOME/.local/bin:$PATH";   # Add "$(systemd-path user-binaries)" path in case apps saved there
 SCRIPT_HOSTNAME=$(hostname);     # Save hostname of system running this script.
-SCRIPT_VERSION="bkgpslog 0.2.1"; # Define version of script.
-SCRIPT_NAME="$(basename "$0")";  # Define basename of script file.
+SCRIPT_VERSION="0.3.9";          # Define version of script.
+SCRIPT_NAME="bkgpslog";          # Define basename of script file.
+SCRIPT_URL="https://gitlab.com/baltakatei/ninfacyzga-01"; # Define wesite hosting this script.
+AGE_VERSION="1.0.0-beta2";       # Define version of age (encryption program)
+AGE_URL="https://github.com/FiloSottile/age/releases/tag/v1.0.0-beta2"; # Define website hosting age.
 
 declare -Ag appRollCall # Associative array for storing app status
 declare -Ag fileRollCall # Associative array for storing file status
 declare -Ag dirRollCall # Associative array for storing dir status
 
 declare -Ag appRollCall # Associative array for storing app status
 declare -Ag fileRollCall # Associative array for storing file status
 declare -Ag dirRollCall # Associative array for storing dir status
-declare -a recPubKeys # for processArguments function
-declare recipients # for main function
+declare -a argRecPubKeys # for processArguments function
 
 ## Initialize variables
 OPTION_VERBOSE=""; OPTION_ENCRYPT=""; OPTION_COMPRESS=""; OPTION_TMPDIR="";
 
 ## Initialize variables
 OPTION_VERBOSE=""; OPTION_ENCRYPT=""; OPTION_COMPRESS=""; OPTION_TMPDIR="";
@@ -144,35 +146,31 @@ showUsage() {
     echoerr "OPTIONS:"
     echoerr "    -h, --help"
     echoerr "            Display help information."
     echoerr "OPTIONS:"
     echoerr "    -h, --help"
     echoerr "            Display help information."
-    echoerr
     echoerr "    --version"
     echoerr "            Display script version."
     echoerr "    --version"
     echoerr "            Display script version."
-    echoerr
     echoerr "    -v, --verbose"
     echoerr "            Display debugging info."
     echoerr "    -v, --verbose"
     echoerr "            Display debugging info."
-    echoerr
     echoerr "    -e, --encrypt"
     echoerr "            Encrypt output."
     echoerr "    -e, --encrypt"
     echoerr "            Encrypt output."
-    echoerr
     echoerr "    -r, --recipient [ pubkey string ]"
     echoerr "            Specify recipient. May be age or ssh pubkey."
     echoerr "            See https://github.com/FiloSottile/age"
     echoerr "    -r, --recipient [ pubkey string ]"
     echoerr "            Specify recipient. May be age or ssh pubkey."
     echoerr "            See https://github.com/FiloSottile/age"
-    echoerr
     echoerr "    -o, --output [ directory ]"
     echoerr "            Specify output directory to save logs."
     echoerr "    -o, --output [ directory ]"
     echoerr "            Specify output directory to save logs."
-    echoerr
     echoerr "    -c, --compress"
     echoerr "            Compress output with gzip (before encryption if enabled)."
     echoerr "    -c, --compress"
     echoerr "            Compress output with gzip (before encryption if enabled)."
-    echoerr
     echoerr "    -z, --time-zone"
     echoerr "            Specify time zone. (ex: \"America/New_York\")"
     echoerr "    -z, --time-zone"
     echoerr "            Specify time zone. (ex: \"America/New_York\")"
-    echoerr
     echoerr "    -t, --temp-dir"
     echoerr "            Specify parent directory for temporary working directory."
     echoerr "            Default: \"/dev/shm\""
     echoerr "    -t, --temp-dir"
     echoerr "            Specify parent directory for temporary working directory."
     echoerr "            Default: \"/dev/shm\""
+    echoerr "    -R, --recipient-dir"
+    echoerr "            Specify directory containing files whose first lines are"
+    echoerr "            to be interpreted as pubkey strings (see \'-r\' option)."
     echoerr
     echoerr "EXAMPLE: (bash script lines)"
     echoerr
     echoerr "EXAMPLE: (bash script lines)"
-    echoerr "/bin/bash bkgpslog -e -c \\"
+    echoerr "/bin/bash bkgpslog -v -e -c \\"
+    echoerr "-z \"UTC\" -t \"/dev/shm\" \\"
     echoerr "-r age1mrmfnwhtlprn4jquex0ukmwcm7y2nxlphuzgsgv8ew2k9mewy3rs8u7su5 \\"
     echoerr "-r age1ala848kqrvxc88rzaauc6vc5v0fqrvef9dxyk79m0vjea3hagclswu0lgq \\"
     echoerr "-o ~/Sync/Location"
     echoerr "-r age1mrmfnwhtlprn4jquex0ukmwcm7y2nxlphuzgsgv8ew2k9mewy3rs8u7su5 \\"
     echoerr "-r age1ala848kqrvxc88rzaauc6vc5v0fqrvef9dxyk79m0vjea3hagclswu0lgq \\"
     echoerr "-o ~/Sync/Location"
@@ -213,10 +211,11 @@ processArguments() {
            -o | --output) if [ -d "$2" ]; then DIR_OUT="$2"; vbm "DEBUG:DIR_OUT:$DIR_OUT"; shift; fi ;; # Define output directory.
            -e | --encrypt) OPTION_ENCRYPT="true"; vbm "DEBUG:Encrypted output mode enabled.";;
            -r | --recipient) # Add 'age' recipient via public key string
            -o | --output) if [ -d "$2" ]; then DIR_OUT="$2"; vbm "DEBUG:DIR_OUT:$DIR_OUT"; shift; fi ;; # Define output directory.
            -e | --encrypt) OPTION_ENCRYPT="true"; vbm "DEBUG:Encrypted output mode enabled.";;
            -r | --recipient) # Add 'age' recipient via public key string
-               recPubKeys+=("$2"); vbm "STATUS:pubkey added:""$2"; shift;;
+               argRecPubKeys+=("$2"); vbm "STATUS:pubkey added:""$2"; shift;;
            -c | --compress) OPTION_COMPRESS="true"; vbm "DEBUG:Compressed output mode enabled.";;
            -z | --time-zone) try setTimeZoneEV "$2"; shift;;
            -c | --compress) OPTION_COMPRESS="true"; vbm "DEBUG:Compressed output mode enabled.";;
            -z | --time-zone) try setTimeZoneEV "$2"; shift;;
-           -t | --temp-dir) OPTION_TMPDIR="true" && TMP_DIR_PRIORITY="$2"; shift;;
+           -t | --temp-dir) OPTION_TMPDIR="true" && argTmpDirPriority="$2"; shift;;
+           -R | --recipient-dir) OPTION_RECDIR="true" && argRecDir="$2"; shift;;
            *) echoerr "ERROR: Unrecognized argument: $1"; echoerr "STATUS:All arguments:$*"; exit 1;; # Handle unrecognized options.
        esac
        shift
            *) echoerr "ERROR: Unrecognized argument: $1"; echoerr "STATUS:All arguments:$*"; exit 1;; # Handle unrecognized options.
        esac
        shift
@@ -331,76 +330,122 @@ timeUntilNextHour(){
 } # Report seconds until next hour
 dateTimeShort(){
     # Desc: Timestamp without separators (YYYYmmddTHHMMSS+zzzz)
 } # Report seconds until next hour
 dateTimeShort(){
     # Desc: Timestamp without separators (YYYYmmddTHHMMSS+zzzz)
-    # Usage: dateTimeShort
+    # Usage: dateTimeShort ([str date])
+    # Version 1.1.0
+    # Input: arg1: 'date'-parsable timestamp string (optional)
     # Output: stdout: timestamp (ISO-8601, no separators)
     # Output: stdout: timestamp (ISO-8601, no separators)
+    # Depends: yell
     local TIME_CURRENT TIME_CURRENT_SHORT
     local TIME_CURRENT TIME_CURRENT_SHORT
+
+    argTime="$1";
+    # Get Current Time
     TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
     TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
-    TIME_CURRENT_SHORT="$(date -d "$TIME_CURRENT" +%Y%m%dT%H%M%S%z)"; # Produce separator-less current timestamp with resolution 1 second.
+    # Decide to parse current or supplied date
+    ## Check if time argument empty
+    if [[ -z "$argTime" ]]; then
+       ## T: Time argument empty, use current time
+       TIME_INPUT="$TIME_CURRENT";
+    else
+       ## F: Time argument exists, validate time
+       if date --date="$argTime" 1>/dev/null 2>&1; then
+           ### T: Time argument is valid; use it
+           TIME_INPUT="$argTime";
+       else
+           ### F: Time argument not valid; exit
+           yell "ERROR:Invalid time argument supplied. Exiting."; exit 1;
+       fi
+    fi
+    # Construct and deliver separator-les date string
+    TIME_CURRENT_SHORT="$(date -d "$TIME_INPUT" +%Y%m%dT%H%M%S%z)";
     echo "$TIME_CURRENT_SHORT";
 } # Get YYYYmmddTHHMMSS±zzzz
 dateShort(){
     # Desc: Date without separators (YYYYmmdd)
     echo "$TIME_CURRENT_SHORT";
 } # Get YYYYmmddTHHMMSS±zzzz
 dateShort(){
     # Desc: Date without separators (YYYYmmdd)
-    # Usage: dateShort
+    # Usage: dateShort ([str date])
+    # Version: 1.1.0
+    # Input: arg1: 'date'-parsable timestamp string (optional)
     # Output: stdout: date (ISO-8601, no separators)
     # Output: stdout: date (ISO-8601, no separators)
+    # Depends: yell
     local TIME_CURRENT DATE_CURRENT_SHORT
     local TIME_CURRENT DATE_CURRENT_SHORT
+
+    argTime="$1";
+    # Get Current Time
     TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
     TIME_CURRENT="$(date --iso-8601=seconds)" ; # Produce `date`-parsable current timestamp with resolution of 1 second.
-    DATE_CURRENT_SHORT="$(date -d "$TIME_CURRENT" +%Y%m%d)"; # Produce separator-less current date with resolution 1 day.
+    # Decide to parse current or supplied date
+    ## Check if time argument empty
+    if [[ -z "$argTime" ]]; then
+       ## T: Time argument empty, use current time
+       TIME_INPUT="$TIME_CURRENT";
+    else
+       ## F: Time argument exists, validate time
+       if date --date="$argTime" 1>/dev/null 2>&1; then
+           ### T: Time argument is valid; use it
+           TIME_INPUT="$argTime";
+       else
+           ### F: Time argument not valid; exit
+           yell "ERROR:Invalid time argument supplied. Exiting."; exit 1;
+       fi
+    fi
+    # Construct and deliver separator-les date string    
+    DATE_CURRENT_SHORT="$(date -d "$TIME_INPUT" +%Y%m%d)"; # Produce separator-less current date with resolution 1 day.
     echo "$DATE_CURRENT_SHORT";
 } # Get YYYYmmdd
 timeDuration(){
     echo "$DATE_CURRENT_SHORT";
 } # Get YYYYmmdd
 timeDuration(){
-    # Desc: Output approximate time duration string before given time (default:current date)
+    # Desc: Given seconds, output ISO-8601 duration string
     # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information
     # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2)
     # Ref/Attrib: ISO-8601:2004(E), §4.4.4.2 Representations of time intervals by duration and context information
     # Note: "1 month" ("P1M") is assumed to be "30 days" (see ISO-8601:2004(E), §2.2.1.2)
-    # Usage: timeDuration [arg1] ([arg2])
-    # Version: 1.0.1
+    # Usage: timeDuration [1:seconds] ([2:precision])
+    # Version: 1.0.3
     # Input: arg1: seconds as base 10 integer >= 0  (ex: 3601)
     #        arg2: precision level (optional; default=2)
     # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S")
     # Input: arg1: seconds as base 10 integer >= 0  (ex: 3601)
     #        arg2: precision level (optional; default=2)
     # Output: stdout: ISO-8601 duration string (ex: "P1H1S", "P2Y10M15DT10H30M20S")
+    #         exit code 0: success
+    #         exit code 1: error_input
+    #         exit code 2: error_unknown
     # Example: 'timeDuration 111111 3' yields 'P1DT6H51M'
     # Depends: date 8 (gnucoreutils), yell, 
     # Example: 'timeDuration 111111 3' yields 'P1DT6H51M'
     # Depends: date 8 (gnucoreutils), yell, 
-    local returnState ARG1 ARG2 remainder precision witherPrecision
+    local returnState argSeconds argPrecision remainder precision witherPrecision
     local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
     local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
     local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
     
     local fullYears fullMonths fullDays fullHours fullMinutes fullSeconds
     local displayYears displayMonths displayDays displayHours displayMinutes displaySeconds
     local hasYears hasMonths hasDays hasHours hasMinutes hasSeconds
     
-    ARG1="$1";
-    ARG2="$2";
+    argSeconds="$1"; # read arg1 (seconds)
+    argPrecision="$2"; # read arg2 (precision)
     precision=2; # set default precision
     precision=2; # set default precision
-    returnState="true"; # set default return state
 
     # Check that between one and two arguments is supplied
     if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then
        yell "ERROR:Invalid number of arguments:$# . Exiting.";
 
     # Check that between one and two arguments is supplied
     if ! { [[ $# -ge 1 ]] && [[ $# -le 2 ]]; }; then
        yell "ERROR:Invalid number of arguments:$# . Exiting.";
-       returnState="ERROR_INPUT"; fi
+       returnState="error_input"; fi
 
 
-    # Check that arg1 provided
+    # Check that argSeconds provided
     if [[ $# -ge 1 ]]; then
     if [[ $# -ge 1 ]]; then
-       ## Check that arg1 is a positive integer
-       if [[ "$ARG1" =~ ^[[:digit:]]+$ ]]; then
+       ## Check that argSeconds is a positive integer
+       if [[ "$argSeconds" =~ ^[[:digit:]]+$ ]]; then
            :
        else
            :
        else
-           yell "ERROR:ARG1 not a digit.";
-           returnState="ERROR_INPUT";
+           yell "ERROR:argSeconds not a digit.";
+           returnState="error_input";
        fi
     else
        yell "ERROR:No argument provided. Exiting.";
        exit 1;
     fi
 
        fi
     else
        yell "ERROR:No argument provided. Exiting.";
        exit 1;
     fi
 
-    # Consider whether arg2 was provided
+    # Consider whether argPrecision was provided
     if  [[ $# -eq 2 ]]; then
     if  [[ $# -eq 2 ]]; then
-       # Check that the second arg is a positive integer
-       if [[ "$ARG2" =~ ^[[:digit:]]+$ ]] && [[ "$ARG2" -gt 0 ]]; then
-       precision="$ARG2";
+       # Check that argPrecision is a positive integer
+       if [[ "$argPrecision" =~ ^[[:digit:]]+$ ]] && [[ "$argPrecision" -gt 0 ]]; then
+       precision="$argPrecision";
        else
        else
-           yell "ERROR:ARG2 not a positive integer. (is $ARG2 ). Leaving early.";
-           returnState="ERROR_INPUT";
+           yell "ERROR:argPrecision not a positive integer. (is $argPrecision ). Leaving early.";
+           returnState="error_input";
        fi;
     else
        :
     fi;
     
        fi;
     else
        :
     fi;
     
-    remainder="$ARG1" ; # seconds
+    remainder="$argSeconds" ; # seconds
     ## Calculate full years Y, update remainder
     fullYears=$(( remainder / (365*24*60*60) ));
     remainder=$(( remainder - (fullYears*365*24*60*60) ));
     ## Calculate full years Y, update remainder
     fullYears=$(( remainder / (365*24*60*60) ));
     remainder=$(( remainder - (fullYears*365*24*60*60) ));
@@ -500,8 +545,6 @@ timeDuration(){
     fi;
     if $witherPrecision; then ((precision--)); fi;
 
     fi;
     if $witherPrecision; then ((precision--)); fi;
 
-
-
     ## Determine whether or not the "T" separator is needed to separate date and time elements
     if ( $displayHours || $displayMinutes || $displaySeconds); then
        displayDateTime="true"; else displayDateTime="false"; fi
     ## Determine whether or not the "T" separator is needed to separate date and time elements
     if ( $displayHours || $displayMinutes || $displaySeconds); then
        displayDateTime="true"; else displayDateTime="false"; fi
@@ -524,14 +567,17 @@ timeDuration(){
        OUTPUT=$OUTPUT$fullSeconds"S"; fi
 
     ## Output duration string to stdout
        OUTPUT=$OUTPUT$fullSeconds"S"; fi
 
     ## Output duration string to stdout
-    if [[ "$returnState" = "true" ]]; then echo "$OUTPUT"; fi
+    echo "$OUTPUT" && returnState="true";
 
     #===Determine function return code===
     if [ "$returnState" = "true" ]; then
        return 0;
 
     #===Determine function return code===
     if [ "$returnState" = "true" ]; then
        return 0;
-    else
-       echo "$returnState" 1>&2;
+    elif [ "$returnState" = "error_input" ]; then
+       yell "ERROR:input";
        return 1;
        return 1;
+    else
+       yell "ERROR:Unknown";
+       return 2;
     fi
 
 } # Get duration (ex: PT10M4S )
     fi
 
 } # Get duration (ex: PT10M4S )
@@ -663,7 +709,8 @@ checkMakeTar() {
 } # checks if arg1 is tar; creates one otherwise
 appendArgTar(){
     # Desc: Writes first argument to temporary file with arguments as options, then appends file to tar
 } # checks if arg1 is tar; creates one otherwise
 appendArgTar(){
     # Desc: Writes first argument to temporary file with arguments as options, then appends file to tar
-    # Usage: writeArg "$(echo "Data to be written.")" [name of file to be inserted] [tar path] [temp dir] ([cmd1] [cmd2] [cmd3] [cmd4]...)
+    # Usage: appendArgTar "$(echo "Data to be written.")" [name of file to be inserted] [tar path] [temp dir] ([cmd1] [cmd2] [cmd3] [cmd4]...)
+    # Version: 1.0.3
     # Input: arg1: data to be written
     #        arg2: file name of file to be inserted into tar
     #        arg3: tar archive path (must exist first)
     # Input: arg1: data to be written
     #        arg2: file name of file to be inserted into tar
     #        arg3: tar archive path (must exist first)
@@ -675,10 +722,11 @@ appendArgTar(){
     #          appendArgTar "$(cat /tmp/largefile2.gpg)" "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
     #          appendArgTar "$(cat /tmp/largefile3.gpg)" "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
     # Depends: bash 5
     #          appendArgTar "$(cat /tmp/largefile2.gpg)" "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
     #          appendArgTar "$(cat /tmp/largefile3.gpg)" "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
     # Depends: bash 5
+    # Ref/Attrib: Using 'eval' to construct command strings https://askubuntu.com/a/476533
 
     # Save function name
 
     # Save function name
-    local FN="${FUNCNAME[0]}"
-    yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
+    local FN="${FUNCNAME[0]}";
+    #yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
     
     # Set file name
     if ! [ -z "$2" ]; then FILENAME="$2"; else yell "ERROR:$FN:Not enough arguments."; exit 1; fi
     
     # Set file name
     if ! [ -z "$2" ]; then FILENAME="$2"; else yell "ERROR:$FN:Not enough arguments."; exit 1; fi
@@ -695,130 +743,361 @@ appendArgTar(){
     if ! [ -z "$7" ]; then CMD3="$7"; else CMD3="tee /dev/null "; fi # command string 3
     if ! [ -z "$8" ]; then CMD4="$8"; else CMD4="tee /dev/null "; fi # command string 4
 
     if ! [ -z "$7" ]; then CMD3="$7"; else CMD3="tee /dev/null "; fi # command string 3
     if ! [ -z "$8" ]; then CMD4="$8"; else CMD4="tee /dev/null "; fi # command string 4
 
-    # Debug
-    yell "STATUS:$FN:CMD1:$CMD1"
-    yell "STATUS:$FN:CMD2:$CMD2"
-    yell "STATUS:$FN:CMD3:$CMD3"
-    yell "STATUS:$FN:CMD4:$CMD4"
-    yell "STATUS:$FN:FILENAME:$FILENAME"
-    yell "STATUS:$FN:TAR_PATH:$TAR_PATH"
-    yell "STATUS:$FN:TMP_DIR:$TMP_DIR"
+    # Input command
+    CMD0="echo \"\$1\""
+
+    # # Debug
+    # yell "DEBUG:STATUS:$FN:CMD0:$CMD0"
+    # yell "DEBUG:STATUS:$FN:CMD1:$CMD1"
+    # yell "DEBUG:STATUS:$FN:CMD2:$CMD2"
+    # yell "DEBUG:STATUS:$FN:CMD3:$CMD3"
+    # yell "DEBUG:STATUS:$FN:CMD4:$CMD4"
+    # yell "DEBUG:STATUS:$FN:FILENAME:$FILENAME"
+    # yell "DEBUG:STATUS:$FN:TAR_PATH:$TAR_PATH"
+    # yell "DEBUG:STATUS:$FN:TMP_DIR:$TMP_DIR"
+    
     # Write to temporary working dir
     # Write to temporary working dir
-    echo "$1" | $CMD1 | $CMD2 | $CMD3 | $CMD4 > "$TMP_DIR"/"$FILENAME";
+    eval "$CMD0"" | ""$CMD1"" | ""$CMD2"" | ""$CMD3"" | ""$CMD4" > "$TMP_DIR"/"$FILENAME";
 
     # Append to tar
     try tar --append --directory="$TMP_DIR" --file="$TAR_PATH" "$FILENAME";
 
     # Append to tar
     try tar --append --directory="$TMP_DIR" --file="$TAR_PATH" "$FILENAME";
-    yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
+    #yell "DEBUG:STATUS:$FN:Finished appendArgTar()."
 } # Append Bash var to file appended to Tar archive
 } # Append Bash var to file appended to Tar archive
+appendFileTar(){
+    # Desc: Processes first file and then appends to tar
+    # Usage: appendFileTar [file path] [name of file to be inserted] [tar path] [temp dir] ([cmd1] [cmd2] [cmd3] [cmd4]...)
+    # Version: 1.0.2
+    # Input: arg1: path of file to be (processed and) written
+    #        arg2: name to use for file inserted into tar
+    #        arg3: tar archive path (must exist first)
+    #        arg4: temporary working dir
+    #        arg5+: command strings (ex: "gpsbabel -i nmea -f - -o kml -F - ")
+    # Output: file written to disk
+    # Example: decrypt multiple large files in parallel
+    #          appendFileTar /tmp/largefile1.gpg "largefile1" $HOME/archive.tar /tmp "gpg --decrypt" &
+    #          appendFileTar /tmp/largefile2.gpg "largefile2" $HOME/archive.tar /tmp "gpg --decrypt" &
+    #          appendFileTar /tmp/largefile3.gpg "largefile3" $HOME/archive.tar /tmp "gpg --decrypt" &
+    # Depends: bash 5
+
+    # Save function name
+    local FN="${FUNCNAME[0]}";
+    #yell "DEBUG:STATUS:$FN:Finished appendFileTar()."
+    
+    # Set file name
+    if ! [ -z "$2" ]; then FILENAME="$2"; else yell "ERROR:$FN:Not enough arguments."; exit 1; fi
+    # Check tar path is a file
+    if [ -f "$3" ]; then TAR_PATH="$3"; else yell "ERROR:$FN:Tar archive arg not a file."; exit 1; fi
+    # Check temp dir arg
+    if ! [ -z "$4" ]; then TMP_DIR="$4"; else yell "ERROR:$FN:No temporary working dir set."; exit 1; fi
+    # Set command strings
+    if ! [ -z "$5" ]; then CMD1="$5"; else CMD1="tee /dev/null "; fi # command string 1
+    if ! [ -z "$6" ]; then CMD2="$6"; else CMD2="tee /dev/null "; fi # command string 2
+    if ! [ -z "$7" ]; then CMD3="$7"; else CMD3="tee /dev/null "; fi # command string 3
+    if ! [ -z "$8" ]; then CMD4="$8"; else CMD4="tee /dev/null "; fi # command string 4
+
+    # Input command string
+    CMD0="cat \"\$1\""
+    
+    # # Debug
+    # yell "DEBUG:STATUS:$FN:CMD0:$CMD0"
+    # yell "DEBUG:STATUS:$FN:CMD1:$CMD1"
+    # yell "DEBUG:STATUS:$FN:CMD2:$CMD2"
+    # yell "DEBUG:STATUS:$FN:CMD3:$CMD3"
+    # yell "DEBUG:STATUS:$FN:CMD4:$CMD4"
+    # yell "DEBUG:STATUS:$FN:FILENAME:$FILENAME"
+    # yell "DEBUG:STATUS:$FN:TAR_PATH:$TAR_PATH"
+    # yell "DEBUG:STATUS:$FN:TMP_DIR:$TMP_DIR"
+    
+    # Write to temporary working dir
+    eval "$CMD0 | $CMD1 | $CMD2 | $CMD3 | $CMD4" > "$TMP_DIR"/"$FILENAME";
+
+    # Append to tar
+    try tar --append --directory="$TMP_DIR" --file="$TAR_PATH" "$FILENAME";
+    #yell "DEBUG:STATUS:$FN:Finished appendFileTar()."
+} # Append file to Tar archive
+checkAgePubkey() {
+    # Desc: Checks if string is an age-compatible pubkey
+    # Usage: checkAgePubkey [str pubkey]
+    # Version: 0.1.2
+    # Input: arg1: string
+    # Output: return code 0: string is age-compatible pubkey
+    #         return code 1: string is NOT an age-compatible pubkey
+    #         age stderr (ex: there is stderr if invalid string provided)
+    # Depends: age (v0.1.0-beta2; https://github.com/FiloSottile/age/releases/tag/v1.0.0-beta2 )
+    
+    argPubkey="$1";
+    
+    if echo "test" | age -a -r "$argPubkey" 1>/dev/null; then
+       return 0;
+    else
+       return 1;
+    fi;
+} # Check age pubkey
+validateInput() {
+    # Desc: Validates Input
+    # Usage: validateInput [str input] [str input type]
+    # Version: 0.2.1
+    # Input: arg1: string to validate
+    #        arg2: string specifying input type (ex:"ssh_pubkey")
+    # Output: return code 0: if input string matched specified string type
+    # Depends: bash 5, yell
+
+    # Save function name
+    local FN="${FUNCNAME[0]}";
+
+    # Process arguments
+    argInput="$1";
+    argType="$2";
+    if [[ $# -gt 2 ]]; then yell "ERROR:$0:$FN:Too many arguments."; exit 1; fi;
+
+    # Check for blank
+    if [[ -z "$argInput" ]]; then return 1; fi
+    
+    # Define input types
+    ## ssh_pubkey
+    ### Check for alnum/dash base64 (ex: "ssh-rsa AAAAB3NzaC1yc2EAAA")
+    if [[ "$argType" = "ssh_pubkey" ]]; then
+       if [[ "$argInput" =~ ^[[:alnum:]-]*[\ ]*[[:alnum:]+/=]*$ ]]; then
+       return 0; fi; fi;
+
+    ## age_pubkey
+    ### Check for age1[:bech32:]
+    if [[ "$argType" = "age_pubkey" ]]; then
+       if [[ "$argInput" =~ ^age1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]*$ ]]; then
+           return 0; fi; fi
+
+    # Return error if no condition matched.
+    return 1;
+} # Validates strings
 magicWriteVersion() {
     # Desc: Appends time-stamped VERSION to PATHOUT_TAR
     # Usage: magicWriteVersion
 magicWriteVersion() {
     # Desc: Appends time-stamped VERSION to PATHOUT_TAR
     # Usage: magicWriteVersion
+    # Version: 0.1.0
     # Input: CONTENT_VERSION, FILEOUT_VERSION, PATHOUT_TAR, DIR_TMP
     # Input: CONTENT_VERSION, FILEOUT_VERSION, PATHOUT_TAR, DIR_TMP
+    # Input: SCRIPT_VERSION, SCRIPT_URL, AGE_VERSION, AGE_URL, SCRIPT_HOSTNAME
+    # Output: appends tar PATHOUT_TAR
     # Depends: dateTimeShort, appendArgTar
     # Depends: dateTimeShort, appendArgTar
-    local CONTENT_VERSION
+    local CONTENT_VERSION pubKeyIndex
     
     
-    # Generate VERSION file in BashVar
+    # Set VERSION file name
     FILEOUT_VERSION="$(dateTimeShort)..VERSION";
     FILEOUT_VERSION="$(dateTimeShort)..VERSION";
-    CONTENT_VERSION="VERSION=$SCRIPT_VERSION";
-    CONTENT_VERSION="$CONTENT_VERSION""\n""SCRIPT_NAME=SCRIPT_NAME";
-    CONTENT_VERSION="$CONTENT_VERSION""\n""DATE=$(date --iso-8601=seconds)";
-    CONTENT_VERSION="$CONTENT_VERSION""\n""HOSTNAME=$SCRIPT_HOSTNAME";
+
+    # Gather VERSION data in CONTENT_VERSION
+    CONTENT_VERSION="SCRIPT_VERSION=$SCRIPT_VERSION";
+    #CONTENT_VERSION="$CONTENT_VERSION""\\n";
+    CONTENT_VERSION="$CONTENT_VERSION""\\n""SCRIPT_NAME=$SCRIPT_NAME";
+    CONTENT_VERSION="$CONTENT_VERSION""\\n""SCRIPT_URL=$SCRIPT_URL";
+    CONTENT_VERSION="$CONTENT_VERSION""\\n""AGE_VERSION=$AGE_VERSION";
+    CONTENT_VERSION="$CONTENT_VERSION""\\n""AGE_URL=$AGE_URL";
+    CONTENT_VERSION="$CONTENT_VERSION""\\n""DATE=$(date --iso-8601=seconds)";
+    CONTENT_VERSION="$CONTENT_VERSION""\\n""HOSTNAME=$SCRIPT_HOSTNAME";
+    ## Add list of recipient pubkeys
+    for pubkey in "${recPubKeysValid[@]}"; do
+       ((pubKeyIndex++))
+       CONTENT_VERSION="$CONTENT_VERSION""\\n""PUBKEY_$pubKeyIndex=$pubkey";
+    done
+    ## Process newline escapes
     CONTENT_VERSION="$(echo -e "$CONTENT_VERSION")"
 
     CONTENT_VERSION="$(echo -e "$CONTENT_VERSION")"
 
-    # Create BashVar as file FILEOUT_VERSION and write-append to PATHOUT_TAR
+    # Write CONTENT_VERSION as file FILEOUT_VERSION and write-append to PATHOUT_TAR
     appendArgTar "$CONTENT_VERSION" "$FILEOUT_VERSION" "$PATHOUT_TAR" "$DIR_TMP";
     appendArgTar "$CONTENT_VERSION" "$FILEOUT_VERSION" "$PATHOUT_TAR" "$DIR_TMP";
+    
 } # bkgpslog: write version data to PATHOUT_TAR via appendArgTar()
 } # bkgpslog: write version data to PATHOUT_TAR via appendArgTar()
-magicWriteBuffer() {
+magicGatherWriteBuffer() {
     # Desc: bkgpslog-specific meta function for writing data to DIR_TMP then appending each file to PATHOUT_TAR
     # Desc: bkgpslog-specific meta function for writing data to DIR_TMP then appending each file to PATHOUT_TAR
-    # Inputs: PATHOUT_TAR FILEOUT_{NMEA,GPX,KML} CMD_CONV_{NMEA,GPX,KML} CMD_{COMPRESS,ENCRYPT} DIR_TMP
+    # Inputs: PATHOUT_TAR FILEOUT_{NMEA,GPX,KML} CMD_CONV_{NMEA,GPX,KML} CMD_{COMPRESS,ENCRYPT} DIR_TMP,
+    # Inputs: BUFFER_TTL bufferTTL_STR SCRIPT_HOSTNAME CMD_COMPRESS_SUFFIX CMD_ENCRYPT_SUFFIX
     # Depends: yell, try, vbm, appendArgTar, tar
     local FN="${FUNCNAME[0]}";
     wait; # Wait to avoid collision with older magicWriteBuffer() instances (see https://www.tldp.org/LDP/abs/html/x9644.html )
     # Depends: yell, try, vbm, appendArgTar, tar
     local FN="${FUNCNAME[0]}";
     wait; # Wait to avoid collision with older magicWriteBuffer() instances (see https://www.tldp.org/LDP/abs/html/x9644.html )
+    # Create buffer file with unique name
+    PATHOUT_BUFFER="$DIR_TMP/buffer$SECONDS";
+    # Fill buffer
+    timeout "$BUFFER_TTL"s gpspipe -r -o "$PATHOUT_BUFFER" ;
+    timeBufferStart="$(dateTimeShort  "$(date --date="$BUFFER_TTL seconds ago")")"; # Note start time
     vbm "DEBUG:STATUS:$FN:Started magicWriteBuffer().";
     vbm "DEBUG:STATUS:$FN:Started magicWriteBuffer().";
-
+    # Determine file paths (time is start of buffer period)
+    FILEOUT_BASENAME="$timeBufferStart""--""$bufferTTL_STR""..""$SCRIPT_HOSTNAME""_location" && vbm "STATUS:Set FILEOUT_BASENAME to:$FILEOUT_BASENAME";
+    ## Files saved to DIR_TMP
+    FILEOUT_NMEA="$FILEOUT_BASENAME".nmea"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_NMEA to:$FILEOUT_NMEA";
+    FILEOUT_GPX="$FILEOUT_BASENAME".gpx"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_GPX to:$FILEOUT_GPX";
+    FILEOUT_KML="$FILEOUT_BASENAME".kml"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_KML to:$FILEOUT_KML";
+    PATHOUT_NMEA="$DIR_TMP"/"$FILEOUT_NMEA" && vbm "STATUS:Set PATHOUT_NMEA to:$PATHOUT_NMEA";
+    PATHOUT_GPX="$DIR_TMP"/"$FILEOUT_GPX" && vbm "STATUS:Set PATHOUT_GPX to:$PATHOUT_GPX";
+    PATHOUT_KML="$DIR_TMP"/"$FILEOUT_KML" && vbm "STATUS:Set PATHOUT_KML to:$PATHOUT_KML";
+    ## Files saved to disk (DIR_OUT)
+    ### one file per day (Ex: "20200731..hostname_location.[.gpx.gz].tar")
+    PATHOUT_TAR="$DIR_OUT"/"$(dateShort "$(date --date="$BUFFER_TTL seconds ago")")".."$SCRIPT_HOSTNAME""_location""$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX".tar && \
+       vbm "STATUS:Set PATHOUT_TAR to:$PATHOUT_TAR";
+    # DEBUG: check vars
+    vbm "STATUS:DIR_TMP     :$DIR_TMP";
+    vbm "STATUS:PATHOUT_TAR :$PATHOUT_TAR";
+    vbm "STATUS:PATHOUT_NMEA:$PATHOUT_NMEA";
+    vbm "STATUS:PATHOUT_GPX:$PATHOUT_GPX";
+    vbm "STATUS:PATHOUT_KML:$PATHOUT_KML";
+    
+    
     # Validate PATHOUT_TAR as tar.
     checkMakeTar "$PATHOUT_TAR";
     ## Add VERSION file if checkMakeTar had to create a tar (exited 1) or replace one (exited 2)
     if [[ $? -eq 1 ]] || [[ $? -eq 2 ]]; then magicWriteVersion; fi
 
     # Write bufferBash to PATHOUT_TAR
     # Validate PATHOUT_TAR as tar.
     checkMakeTar "$PATHOUT_TAR";
     ## Add VERSION file if checkMakeTar had to create a tar (exited 1) or replace one (exited 2)
     if [[ $? -eq 1 ]] || [[ $? -eq 2 ]]; then magicWriteVersion; fi
 
     # Write bufferBash to PATHOUT_TAR
-    appendArgTar "$bufferBash" "$FILEOUT_NMEA" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_NMEA" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write NMEA data
-    appendArgTar "$bufferBash" "$FILEOUT_GPX" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_GPX" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write GPX file
-    appendArgTar "$bufferBash" "$FILEOUT_KML" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_KML" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write KML file
-    # Remove secured chunks from DIR_TMP
-    try rm "$PATHOUT_NMEA" "$PATHOUT_GPX" "$PATHOUT_KML";
-    yell "DEBUG:STATUS:$FN:Finished magicWriteBuffer().";
-} # bkgpslog write function
+    appendFileTar "$PATHOUT_BUFFER" "$FILEOUT_NMEA" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_NMEA" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write NMEA data
+    appendFileTar "$PATHOUT_BUFFER" "$FILEOUT_GPX" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_GPX" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write GPX file
+    appendFileTar "$PATHOUT_BUFFER" "$FILEOUT_KML" "$PATHOUT_TAR" "$DIR_TMP" "$CMD_CONV_KML" "$CMD_COMPRESS" "$CMD_ENCRYPT"; # Write KML file
 
 
-main() {
-    processArguments "$@" # Process arguments.
+    # Remove secured chunks from DIR_TMP
+    rm "$PATHOUT_BUFFER" "$PATHOUT_NMEA" "$PATHOUT_GPX" "$PATHOUT_KML";
+    vbm "DEBUG:STATUS:$FN:Finished magicWriteBuffer().";
+} # write buffer to disk
+magicParseRecipientDir() {
+    # Desc: Updates recPubKeysValid with pubkeys in dir specified by '-R' option ("recipient directory")
+    # Inputs: vars: OPTION_RECDIR, argRecDir, 
+    #         arry: recPubKeysValid
+    # Outputs: arry: recPubKeysValid (modified with pubkeys in argRecDir if pubkeys valid)
+    # Depends: processArguments,
+    local recFileLine updateRecipients recipientDir
+    declare -a candRecPubKeysValid
     
     
-    # Determine working directory
-    ## Set DIR_TMP_PARENT to user-specified value if specified
-    if [[ "$OPTION_TMPDIR" = "true" ]]; then
-           if [[ -d "$TMP_DIR_PRIORITY" ]]; then
-           DIR_TMP_PARENT="$OPTION_TMPDIR"; 
+    if [[ "$OPTION_RECDIR" = "true" ]]; then
+       ### Check that argRecDir is a directory.
+       if [[ -d "$argRecDir" ]]; then
+           recipientDir="$argRecDir";
+           #### Initialize variable indicating outcome of pubkey review
+           unset updateRecipients
+           #### Add existing recipients
+           candRecPubKeysValid=(${recPubKeysValid[@]});
+           #### Parse files in recipientDir
+           for file in "$recipientDir"/*; do
+               ##### Read first line of each file
+               recFileLine="$(cat "$file" | head -n1)";
+               ##### check if first line is a valid pubkey
+               if checkAgePubkey "$recFileLine" && \
+                       ( validateInput "$recFileLine" "ssh_pubkey" || validateInput "$recFileLine" "age_pubkey"); then
+                   ###### T: add candidate pubkey to candRecPubKeysValid
+                   candRecPubKeysValid+=("$recFileLine");
+               else
+                   ###### F: throw warning;
+                   yell "ERROR:Invalid recipient file detected. Not modifying recipient list."
+                   updateRecipients="false";
+               fi;
+           done
+           #### Write updated recPubKeysValid array to recPubKeysValid if no failure detected
+           if ! updateRecipients="false"; then
+               recPubKeysValid=(${candRecPubKeysValid[@]});
+           fi;
        else
        else
-           yell "WARNING:Specified temporary working directory not valid:$OPTION_TMPDIR";
-           exit 1;
-       fi
-    fi
-
-    ## Set DIR_TMP_PARENT to default or fallback otherwise
-    if [[ -d "$DIR_TMP_DEFAULT" ]]; then
-       DIR_TMP_PARENT="$DIR_TMP_DEFAULT";
-    elif [[ -d /tmp ]]; then
-       yell "WARNING:/dev/shm not available. Falling back to /tmp .";
-       DIR_TMP_PARENT="/tmp";
-    else
-       yell "ERROR:No valid working directory available. Exiting.";
-       exit 1;
-    fi
+           yell "ERROR:$0:Recipient directory $argRecDir does not exist. Exiting."; exit 1;
+       fi;
+    fi;
+} # Update recPubKeysValid with argRecDir
+magicParseRecipientArgs() {
+    # Desc: Parses recipient arguments specified by '-r' option
+    # Input:  vars: OPTION_ENCRYPT from processArguments()
+    #         arry: argRecPubKeys from processArguments()
+    # Output: vars: CMD_ENCRYPT, CMD_ENCRYPT_SUFFIX
+    #         arry: recPubKeysValid
+    # Depends: checkapp(), checkAgePubkey(), validateInput(), processArguments()
+    local recipients
 
 
-    ## Set DIR_TMP using DIR_TMP_PARENT and nonce (SCRIPT_TIME_START)
-    DIR_TMP="$DIR_TMP_PARENT"/"$SCRIPT_TIME_START""..bkgpslog" && vbm "DEBUG:Set DIR_TMP to:$DIR_TMP"; # Note: removed at end of main().
-    
-    # Set output encryption and compression option strings
     if [[ "$OPTION_ENCRYPT" = "true" ]]; then # Check if encryption option active.
        if checkapp age; then # Check that age is available.
     if [[ "$OPTION_ENCRYPT" = "true" ]]; then # Check if encryption option active.
        if checkapp age; then # Check that age is available.
-           for pubkey in "${recPubKeys[@]}"; do # Validate recipient pubkey strings by forming test message
-               vbm "DEBUG:Testing pubkey string:$pubkey"
-               if echo "butts" | age -a -r "$pubkey" 1>/dev/null; then
-                   # Form age recipient string
-                   recipients="$recipients""-r $pubkey ";
+           for pubkey in "${argRecPubKeys[@]}"; do # Validate recipient pubkey strings by forming test message
+               vbm "DEBUG:Testing pubkey string:$pubkey";
+               if checkAgePubkey "$pubkey" && \
+                       ( validateInput "$pubkey" "ssh_pubkey" || validateInput "$pubkey" "age_pubkey"); then
+                   #### Form age recipient string
+                   recipients="$recipients""-r '$pubkey' ";
                    vbm "STATUS:Added pubkey for forming age recipient string:""$pubkey";
                    vbm "DEBUG:recipients:""$recipients";
                    vbm "STATUS:Added pubkey for forming age recipient string:""$pubkey";
                    vbm "DEBUG:recipients:""$recipients";
+                   #### Add validated pubkey to recPubKeysValid array
+                   recPubKeysValid+=("$pubkey") && vbm "DEBUG:recPubkeysValid:pubkey added:$pubkey";
                else
                    yell "ERROR:Exit code ""$?"". Invalid recipient pubkey string. Exiting."; exit 1;
                fi
            done
                else
                    yell "ERROR:Exit code ""$?"". Invalid recipient pubkey string. Exiting."; exit 1;
                fi
            done
-           vbm "DEBUG:Finished processing recPubKeys array";
-           # Form age command string
-           CMD_ENCRYPT="age ""$recipients ";
-           CMD_ENCRYPT_SUFFIX=".age";
+           vbm "DEBUG:Finished processing argRecPubKeys array";
+
+           ##  Form age command string
+           CMD_ENCRYPT="age ""$recipients " && vbm "CMD_ENCRYPT:$CMD_ENCRYPT";
+           CMD_ENCRYPT_SUFFIX=".age" && vbm "CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
        else
            yell "ERROR:Encryption enabled but \"age\" not found. Exiting."; exit 1;
        fi
     else
        else
            yell "ERROR:Encryption enabled but \"age\" not found. Exiting."; exit 1;
        fi
     else
-       CMD_ENCRYPT="tee /dev/null ";
-       CMD_ENCRYPT_SUFFIX="";
+       CMD_ENCRYPT="tee /dev/null " && vbm "CMD_ENCRYPT:$CMD_ENCRYPT";
+       CMD_ENCRYPT_SUFFIX="" && vbm "CMD_ENCRYPT_SUFFIX:$CMD_ENCRYPT_SUFFIX";
        vbm "DEBUG:Encryption not enabled."
        vbm "DEBUG:Encryption not enabled."
-    fi
+    fi    
+} # Populate recPubKeysValid with argRecPubKeys; form encryption cmd string and filename suffix
+magicParseCompressionArg() {
+    # Desc: Parses compression arguments specified by '-c' option
+    # Input:  vars: OPTION_COMPRESS
+    # Output: CMD_COMPRESS, CMD_COMPRESS_SUFFIX
+    # Depends: checkapp(), vbm(), gzip, 
     if [[ "$OPTION_COMPRESS" = "true" ]]; then # Check if compression option active
        if checkapp gzip; then # Check if gzip available
     if [[ "$OPTION_COMPRESS" = "true" ]]; then # Check if compression option active
        if checkapp gzip; then # Check if gzip available
-           CMD_COMPRESS="gzip ";
-           CMD_COMPRESS_SUFFIX=".gz";
+           CMD_COMPRESS="gzip " && vbm "CMD_COMPRESS:$CMD_COMPRESS";
+           CMD_COMPRESS_SUFFIX=".gz" && vbm "CMD_COMPRESS_SUFFIX:$CMD_COMPRESS_SUFFIX";
        else
            yell "ERROR:Compression enabled but \"gzip\" not found. Exiting."; exit 1;
        fi
     else
        else
            yell "ERROR:Compression enabled but \"gzip\" not found. Exiting."; exit 1;
        fi
     else
-       CMD_COMPRESS="tee /dev/null ";
-       CMD_COMPRESS_SUFFIX="";
+       CMD_COMPRESS="tee /dev/null " && vbm "CMD_COMPRESS:$CMD_COMPRESS";
+       CMD_COMPRESS_SUFFIX="" && vbm "CMD_COMPRESS_SUFFIX:$CMD_COMPRESS_SUFFIX";
        vbm "DEBUG:Compression not enabled.";
        vbm "DEBUG:Compression not enabled.";
-    fi
+    fi    
+} # Form compression cmd string and filename suffix
+magicInitWorkingDir() {
+    # Desc: Determine temporary working directory from defaults or user input
+    # Input:  vars: OPTION_TEMPDIR, argTmpDirPriority, DIR_TMP_DEFAULT
+    # Input:  vars: SCRIPT_TIME_START
+    # Output: vars: DIR_TMP
+    # Depends: processArguments(), vbm(), yell()
+    # Parse '-t' option (user-specified temporary working dir)
+    ## Set DIR_TMP_PARENT to user-specified value if specified
+    local DIR_TMP_PARENT
+    
+    if [[ "$OPTION_TMPDIR" = "true" ]]; then
+       if [[ -d "$argTempDirPriority" ]]; then
+           DIR_TMP_PARENT="$argTempDirPriority"; 
+       else
+           yell "WARNING:Specified temporary working directory not valid:$OPTION_TMPDIR";
+           exit 1; # Exit since user requires a specific temp dir and it is not available.
+       fi;
+    else
+    ## Set DIR_TMP_PARENT to default or fallback otherwise
+       if [[ -d "$DIR_TMP_DEFAULT" ]]; then
+           DIR_TMP_PARENT="$DIR_TMP_DEFAULT";
+       elif [[ -d /tmp ]]; then
+           yell "WARNING:$DIR_TMP_DEFAULT not available. Falling back to /tmp .";
+           DIR_TMP_PARENT="/tmp";
+       else
+           yell "ERROR:No valid working directory available. Exiting.";
+           exit 1;
+       fi
+    fi;
+    ## Set DIR_TMP using DIR_TMP_PARENT and nonce (SCRIPT_TIME_START)
+    DIR_TMP="$DIR_TMP_PARENT"/"$SCRIPT_TIME_START""..bkgpslog" && vbm "DEBUG:Set DIR_TMP to:$DIR_TMP"; # Note: removed at end of main().    
+} # Sets working dir
+
+main() {
+    # Process arguments
+    processArguments "$@";
+    ## Act upon arguments
+    ### Determine working directory
+    magicInitWorkingDir;
+    ### Set output encryption and compression option strings
+    #### React to "-r" ("encryption recipients") option
+    magicParseRecipientArgs;
+    #### React to "-c" ("compression") option
+    magicParseCompressionArg;
+    #### React to "-R" ("recipient directory") option
+    magicParseRecipientDir;
 
 
-    # Check that critical apps and dirs are available, displag missing ones.
-    if ! checkapp gpspipe tar && ! checkdir "$DIR_OUT" "/dev/shm"; then
+    # Check that critical apps and dirs are available, display missing ones.
+    if ! checkapp gpspipe tar && ! checkdir "$DIR_OUT" "DIR_TMP"; then
        yell "ERROR:Critical components missing.";
        displayMissing; yell "Exiting."; exit 1; fi
 
        yell "ERROR:Critical components missing.";
        displayMissing; yell "Exiting."; exit 1; fi
 
@@ -839,47 +1118,20 @@ main() {
     checkMakeTar "$PATHOUT_TAR" && vbm "DEBUG:Confirmed or Created to be a tar:$PATHOUT_TAR";
     ## Append VERSION file to PATHOUT_TAR
     magicWriteVersion;
     checkMakeTar "$PATHOUT_TAR" && vbm "DEBUG:Confirmed or Created to be a tar:$PATHOUT_TAR";
     ## Append VERSION file to PATHOUT_TAR
     magicWriteVersion;
-    
-    # Record gps data until script lifespan ends
-    declare debugCounter; debugCounter="0"; # set debug counter
+
+    # Define GPS conversion commands
+    CMD_CONV_NMEA="tee /dev/null " && vbm "STATUS:Set CMD_CONV_NMEA to:$CMD_CONV_NMEA"; # tee as passthrough
+    CMD_CONV_GPX="gpsbabel -i nmea -f - -o gpx -F - " && vbm "STATUS:Set CMD_CONV_GPX to:$CMD_CONV_GPX"; # convert NMEA to GPX
+    CMD_CONV_KML="gpsbabel -i nmea -f - -o kml -F - " && vbm "STATUS:Set CMD_CONV_KML to:$CMD_CONV_KML"; # convert NMEA to KML
+
+    # MAIN LOOP:Record gps data until script lifespan ends
     while [[ "$SECONDS" -lt "$scriptTTL" ]]; do
     while [[ "$SECONDS" -lt "$scriptTTL" ]]; do
-       timeBufferStart="$(dateTimeShort)"; # Note start time
-       # Determine file paths (time is start of buffer period)
-       FILEOUT_BASENAME="$timeBufferStart""--""$bufferTTL_STR""..""$SCRIPT_HOSTNAME""_location" && vbm "STATUS:Set FILEOUT_BASENAME to:$FILEOUT_BASENAME";
-       ## Files saved to DIR_TMP
-       FILEOUT_NMEA="$FILEOUT_BASENAME".nmea"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_NMEA to:$FILEOUT_NMEA";
-       FILEOUT_GPX="$FILEOUT_BASENAME".gpx"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_GPX to:$FILEOUT_GPX";
-       FILEOUT_KML="$FILEOUT_BASENAME".kml"$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX" && vbm "STATUS:Set FILEOUT_KML to:$FILEOUT_KML";
-       PATHOUT_NMEA="$DIR_TMP"/"$FILEOUT_NMEA" && vbm "STATUS:Set PATHOUT_NMEA to:$PATHOUT_NMEA";
-       PATHOUT_GPX="$DIR_TMP"/"$FILEOUT_GPX" && vbm "STATUS:Set PATHOUT_GPX to:$PATHOUT_GPX";
-       PATHOUT_KML="$DIR_TMP"/"$FILEOUT_KML" && vbm "STATUS:Set PATHOUT_KML to:$PATHOUT_KML";
-       ## Files saved to disk (DIR_OUT)
-       ### one file per day (Ex: "20200731..hostname_location.[.gpx.gz].tar")
-       PATHOUT_TAR="$DIR_OUT"/"$(dateShort)".."$SCRIPT_HOSTNAME""_location""$CMD_COMPRESS_SUFFIX""$CMD_ENCRYPT_SUFFIX".tar && \
-           vbm "STATUS:Set PATHOUT_TAR to:$PATHOUT_TAR";
-       # Define GPS conversion commands
-       CMD_CONV_NMEA="tee /dev/null " && vbm "STATUS:Set CMD_CONV_NMEA to:$CMD_CONV_NMEA"; # tee as passthrough
-       CMD_CONV_GPX="gpsbabel -i nmea -f - -o gpx -F - " && vbm "STATUS:Set CMD_CONV_GPX to:$CMD_CONV_GPX"; # convert NMEA to GPX
-       CMD_CONV_KML="gpsbabel -i nmea -f - -o kml -F - " && vbm "STATUS:Set CMD_CONV_KML to:$CMD_CONV_KML"; # convert NMEA to KML
-       # Fill Bash variable buffer
-       bufferBash="$(timeout "$BUFFER_TTL""s" gpspipe -r)" && vbm "STATUS:Successfully filled bufferBash variable with gpspipe data."; # Record gpspipe nmea data to buffer for bufferTTL seconds
-       # Process bufferBash, save secured chunk set to DIR_TMP
-       vbm "STATUS:Beginning to save data to $DIR_TMP";
-       magicWriteBuffer &
-       # Append each secured chunk in memory dir (DIR_TMP) to file on disk (PATHOUT_TAR in DIR_OUT)
-       vbm "STATUS:DIR_TMP     :$DIR_TMP";
-       vbm "STATUS:PATHOUT_TAR :$PATHOUT_TAR";
-       vbm "STATUS:PATHOUT_NMEA:$PATHOUT_NMEA";
-       vbm "STATUS:PATHOUT_GPX:$PATHOUT_GPX";
-       vbm "STATUS:PATHOUT_KML:$PATHOUT_KML";
-
-       # Reset buffer and filenames
-       unset bufferBash FILEOUT_BASENAME PATHOUT_NMEA PATHOUT_GPX PATHOUT_KML PATHOUT_TAR timeBufferStart;
-       vbm "DEBUG:Completed buffer session $debugCounter ." 1>&2;
-       ((debugCounter++))
-    done
+       magicGatherWriteBuffer &
+       sleep "$BUFFER_TTL";
+    done    
 
 
-    # Remove DIR_TMP
+    # Cleanup
+    ## Remove DIR_TMP
     try rm -r "$DIR_TMP";
 
     vbm "STATUS:Main function finished.";
     try rm -r "$DIR_TMP";
 
     vbm "STATUS:Main function finished.";