aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2016-08-19 15:59:46 +1200
committerTom Ryder <tom@sanctum.geek.nz>2016-08-19 16:00:35 +1200
commit475e31ca38054b5abb51fc4eae7c654fe573b72d (patch)
tree6a8e9696ffa4ca564939dff329605593a794c533
parentCorrect stws(1)'s self-identifying (diff)
downloaddotfiles-475e31ca38054b5abb51fc4eae7c654fe573b72d.tar.gz
dotfiles-475e31ca38054b5abb51fc4eae7c654fe573b72d.zip
Translate apf(1) to POSIX sh
We lose the ability to include newlines in options, but probably a pretty good tradeoff, especially since it makes the *rc files hand-editable in theory. Also add skipping comments and blank lines. Update ISSUES.markdown. There are two more Bash scripts left; one of them, han(1), does actually require Bash, though.
-rw-r--r--ISSUES.markdown1
-rw-r--r--README.markdown2
-rwxr-xr-xbin/apf62
-rw-r--r--man/man1/apf.174
4 files changed, 80 insertions, 59 deletions
diff --git a/ISSUES.markdown b/ISSUES.markdown
index 72b87ea8..b94ab582 100644
--- a/ISSUES.markdown
+++ b/ISSUES.markdown
@@ -13,7 +13,6 @@ Known issues
- Mostly done now:
[tom@conan:~/.dotfiles/bin](git:master)$ grep bash *
- apf:#!/usr/bin/env bash
eds:#!/usr/bin/env bash
han:#!/usr/bin/env bash
diff --git a/README.markdown b/README.markdown
index b443d618..47197122 100644
--- a/README.markdown
+++ b/README.markdown
@@ -380,7 +380,7 @@ Installed by the `install-bin` target:
for unfolding HTTP headers, but it should work for most RFC 822
formats.
* `apf(1)` prepends arguments to a command with ones read from a file,
- intended as a framework for shell functions.
+ intended as a framework for shell wrappers or functions.
* `ax(1)` evaluates an awk expression given on the command line; this is
intended as a quick way to test how Awk would interpret a given expression.
* `bel(1)` prints a terminal bell character.
diff --git a/bin/apf b/bin/apf
index 733d67fc..0a0cbeea 100755
--- a/bin/apf
+++ b/bin/apf
@@ -1,30 +1,52 @@
-#!/usr/bin/env bash
-# Prepend arguments from a file to a command call
+#!/bin/sh
+# Prepend arguments from a file to the given arguments for a command
self=apf
-# Give up completely if no BASH_VERSINFO (<2.0)
-[ -n "$BASH_VERSINFO" ] || exit
-
-# Require at least two arguments, give usage otherwise
-if (($# < 2)) ; then
+# Require at least two arguments
+if [ "$#" -lt 2 ] ; then
printf >&2 '%s: Need an arguments file and a command\n' "$self"
exit 2
fi
# First argument is the file containing the null-delimited arguments
-argfile=$1
-shift
+argf=$1 cmd=$2
+shift 2
-# Read all the null-delimited arguments from the file
-declare -a args
-while IFS= read -rd '' arg ; do
- args[${#args[@]}]=$arg
-done < "$argfile"
+# If the file exists, we'll read it. If it doesn't, this is not an error (think
+# personal config files like ~/.vimrc)
+if [ -f "$argf" ] ; then
-# Next argument is the command to run
-cmd=$1
-shift
+ # Create a temporary directory with name in $td, and handle POSIX-ish traps to
+ # remove it when the script exits.
+ td=
+ cleanup() {
+ [ -n "$td" ] && rm -fr -- "$td"
+ if [ "$1" != EXIT ] ; then
+ trap - "$1"
+ kill "-$1" "$$"
+ fi
+ }
+ for sig in EXIT HUP INT TERM ; do
+ # shellcheck disable=SC2064
+ trap "cleanup $sig" "$sig"
+ done
+ td=$(mktd "$self") || exit
+
+ # Write the arguments in reverse to a temporary file
+ revf=$td/revf
+ sed '1!G;$!{h;d}' "$argf" > "$revf" || exit
+
+ # Stack up all the arguments from the file. Skip blank lines and comments.
+ # An empty file is also fine.
+ while IFS= read -r arg ; do
+ case $arg in
+ '#'*) continue ;;
+ *[![:space:]]*) ;;
+ *) continue ;;
+ esac
+ set -- "$arg" "$@"
+ done < "$revf"
+fi
-# Run the command with the retrieved arguments first, then the rest of the
-# command line as passed to the function
-command "$cmd" "${args[@]}" "$@"
+# Run the command with the changed arguments
+exec "$cmd" "$@"
diff --git a/man/man1/apf.1 b/man/man1/apf.1
index 67b3b25a..7cb2ab28 100644
--- a/man/man1/apf.1
+++ b/man/man1/apf.1
@@ -7,30 +7,34 @@
foorc
foo --bar baz
.SH DESCRIPTION
-Add null-delimited arguments read from a file to a command's arguments before
-running it. This is intended as a way of implementing *rc files for interactive
-shell calls to programs that don't support such files, without having to use
-broken environment variables (e.g. GREP_OPTIONS); this enables you to, for
-example, use arguments with shell metacharacters and spaces in them that you do
-not want expanded.
-
+Add newline-delimited arguments read from a file to a command's arguments
+(before any given ones) before running it. This is intended as a quick way of
+implementing *rc files for interactive shell calls to programs that don't
+support such files, without having to use broken environment variables like GNU
+grep(1)'s GREP_OPTIONS.
+.P
+This enables you to use arguments with shell metacharacters and spaces in them
+that you do not want expanded. The only exception is that you cannot have
+newlines in any of the arguments. This was done to keep POSIX sh(1)
+compatibility.
+.P
For example, given this simple program in our $PATH, printargs:
-
- $ cat ~/.local/bin/printargs
+.P
+ $ cat /usr/bin/printargs
#!/bin/sh
- printf '%s\n' "$@"
-
+ printf '%s\\n' "$@"
+.P
Which just prints its arguments:
-
+.P
$ printargs a b c
a
b
c
-
+.P
We could do this:
-
- $ printf '%s\0' -f --flag --option '? foo bar *' > "$HOME"/.printargsrc
-
+.P
+ $ printf '%s\\n' -f --flag --option '? foo bar *' > "$HOME"/.printargsrc
+.P
$ apf "$HOME"/.printargsrc printargs a b c
-f
--flag
@@ -39,27 +43,23 @@ We could do this:
a
b
c
-
-We could then make a permanent wrapper function with:
-
+.P
+We could then make a permanent wrapper script in two line:
+.P
+ $ cat >~/.local/bin/printargs
+ #!/bin/sh
+ exec apf "$HOME"/.printargsrc /usr/bin/printargs
+ ^D
+ $ chmod +x ~/.local/bin/printargs
+.P
+Or just a shell function, if it's needed interactively:
+.P
$ printargs() { apf "$HOME"/.printargsrc printargs "$@" ; }
-
- $ printargs a b c
- -f
- --flag
- --option
- ? foo bar *
- a
- b
- c
-
- $ printf '%s\n' !-2:q >> "$HOME"/.bashrc
-
-This means you can edit the options in the *rc file, and don't have to redefine
-a wrapper function.
-
-If you actually want those options to *always* be added, regardless of whether
-you're in an interactive shell, you really should make an actual wrapper
-script.
+.P
+It's not considered an error if the file doesn't exist or is empty. If it's a
+directory or otherwise not byte-readable, an error will be printed to stderr,
+but execution of the called program will continue anyway. Blank lines or lines
+beginning with # are also ignored. Both leading and trailing whitespace is
+preserved.
.SH AUTHOR
Tom Ryder <tom@sanctum.geek.nz>