# shellcheck shell=dash # vim: ft=sh # substitute replaces all occurances of one string with another in the provided # file, writing to the specified output file. substitute() { local input="$1" local output="$2" shift 2 # TODO: real parsing local search="$1" local replace="$2" #-v RS="$(printf "\0")" \ awk \ -v srch="$search" \ -v repl="$replace" \ -v RS="" \ -v ORS="" \ '{ gsub(srch, repl, $0); print $0 }' \ < "$input" > "$output" } # substituteInPlace works like substitute but operates in-place on the provided # file substituteInPlace() { local fpath="$1" shift local tmpfile tmpfile="$(mktemp)" if ! substitute "$fpath" "$tmpfile" "$@"; then rm "$tmpfile" return $? fi # Overwrite now that we've succeeded mv "$tmpfile" "$fpath" } # Shell-quotes an arbitrary string. # # From: http://www.etalabs.net/sh_tricks.html # # This function simply replaces every instance of «'» (single quote) within # the string with «'\''» (single quote, backslash, single quote, single # quote), then puts single quotes at the beginning and end of the string. # Since the only character whose meaning is special within single quotes is # the single quote character itself, this is totally safe. Trailing # newlines are handled correctly, and the single quote at the end doubles # as a safety character to prevent command substitution from clobbering the # trailing newlines, should one want to do something like: # quoted=$(quote "$var") quote() { printf "%s\n" "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/" } # Returns whether or not a string matches a glob; similar to the bash [[ test, # but portable. fnmatch() { # We explicitly want this to act as a glob # shellcheck disable=SC2254 case "$2" in $1) return 0 ;; *) return 1 ;; esac } # Portable equivalent to GNU's `date +"%s"` # # From: http://www.etalabs.net/sh_tricks.html epochseconds() { # This is horrible, but... it tells 'date' to generate a string that # contains shell math to generate the actual epoch date. # # TODO(andrew-d): break down and verify the math here echo $(( $(TZ=GMT0 date +"((%Y-1600)*365+(%Y-1600)/4-(%Y-1600)/100+(%Y-1600)/400+1%j-1000-135140)*86400+(1%H-100)*3600+(1%M-100)*60+(1%S-100)") )) } # user_homedir returns the home directory for the provided user user_homedir() { local user="$1" awk \ -v u="$user" \ -v FS=':' \ '$1==u {print $6}' \ /etc/passwd } # sponge acts like sponge(1) from moreutils, but is portable. # # It "soaks up" all input from stdin before writing it to the specified output; # helpful for modifying a file in-place with a utility that does not support # it. sponge() { local append=false while getopts 'a' opt; do case $opt in a) append=true ;; *) echo "unknown option: $opt" >&2; exit 1;; esac done shift "$(( OPTIND - 1 ))" local outfile="$1" local tmpfile tmpfile="$(mktemp "$(dirname "$outfile")/tmp-sponge.XXXXXXXX")" && cat >"$tmpfile" && if "$append"; then cat "$tmpfile" >>"$outfile" else if [ -f "$outfile" ]; then # NOTE: the stat() call here isn't exactly portable, but both GNU # coreutils and busybox support it, so close enough? Let's # double-check that it returns something sensible just to be sure. local oldperms oldperms="$(stat -c '%a' "$outfile")" # Should be a string of length 3 with only digits if [ "${#oldperms}" = "3" ] && [ -z "$(echo "$oldperms" | tr -d '0-9')" ]; then chmod "$oldperms" "$tmpfile" else printf "warning (bad: %s): not setting permssions on: %s\n" "${oldperms}" "$outfile" >&2 fi fi if [ -f "$outfile" ]; then mv "$tmpfile" "$outfile" elif [ -n "$outfile" ] && [ ! -e "$outfile" ]; then cat "$tmpfile" >"$outfile" else cat "$tmpfile" fi fi && rm -f "$tmpfile" }