# readreclx: a no-binary readrec substitute for Bash 4.3+ # Just source this file in your scripts # Created by Luxferre in 2023, released into public domain rrlx_emit() { # args: UPEF flag, field name, buffer value local upef="$1" local fieldname="$2" local buf="$3" local -n realval="$fieldname" # get the field name reference if [[ -n "$realval" ]]; then # this variable already exists if [[ "$(declare -ap "$fieldname" 2>/dev/null)" =~ ^declare\ -a.* ]]; then if (( "$upef" == 1 && "${#realval[@]}" > 0 )); then # update the last entry in the array realval[-1]="$buf" # I love Bash 4.3+ else realval+=("$buf") # just append the value fi else # it's a simple variable if [[ "$upef" == 1 ]]; then # just update the previous variable declare -g "$fieldname"="$buf" else # convert the variable into an array and append the new value declare -ag "$fieldname" # now realval should point to the array realval+=("$buf") # append the value fi fi else # just create a single-value variable declare -g "$fieldname"="$buf" fi } readreclx() { local buf='' # value buffer local fieldname='' # fieldname buffer local L='' # input string buffer local litf=0 # literal mode flag local upef=0 # flag to update the previously emitted field local LF=$'\n' # line feed character cache while read -r -d "$LF" L; do # read line by line if [[ "$litf" == 1 ]]; then # we're in the literal mode buf="${buf}${L}" # append the contents else [[ -z "$L" ]] && break # line empty, end of record [[ "$L" =~ ^#.* ]] && continue # it's a comment, skip if [[ "$L" =~ ^\+.* ]]; then # newline syntax L="${L#+}" # remove the leading + L="${L# }" # remove the single leading whitespace buf="${buf}${LF}${L}" # add it after the newline upef=1 # set UPEF flag else # any other values upef=0 # reset UPEF flag IFS=: read -r -s fieldname buf < <(printf '%s' "$L") if [[ "$fieldname" =~ ^[a-zA-Z%][a-zA-Z0-9_]*$ ]]; then # valid field # remove the starting whitespace or tab if the buffer starts with it if [[ "$buf" == $' '* ]]; then buf="${buf:1}" elif [[ "$buf" == $'\t'* ]]; then buf="${buf:1}" fi else printf '%s\n' 'Invalid field name detected, discarding!' 1>&2 fieldname='' buf='' fi fi fi # now, check if we need to maintain the literal mode if [[ "$buf" == *$'\\' ]]; then # buffer ends with a backspace litf=1 # set the literal mode buf="${buf:0:-1}" # remove the backspace from buffer else # finally, we have the name in fieldname and the value in buf litf=0 # unset the literal mode if [[ ! -z "$fieldname" ]]; then # perform the actual var assignment rrlx_emit "$upef" "$fieldname" "$buf" fi fi done }