From 92073747bedbb2e8aabbb5154b5045400ce5ed33 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Thu, 22 Dec 2016 12:39:11 +1300 Subject: Fork keep() to ksh Mostly works. --- ISSUES.markdown | 2 + ksh/kshrc.d/keep.ksh | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 ksh/kshrc.d/keep.ksh diff --git a/ISSUES.markdown b/ISSUES.markdown index 0949c0b6..db98183f 100644 --- a/ISSUES.markdown +++ b/ISSUES.markdown @@ -16,3 +16,5 @@ Known issues * dr(1df) is probably more practical in awk * How come commands I fix with the fc builtin always seem to exit 1 even if they succeed? Did I do that or is it Bash? +* mksh(1) when running keep() prints out variables with a "typeset" prefix + that seems to mask them when read back in diff --git a/ksh/kshrc.d/keep.ksh b/ksh/kshrc.d/keep.ksh new file mode 100644 index 00000000..90fd632f --- /dev/null +++ b/ksh/kshrc.d/keep.ksh @@ -0,0 +1,152 @@ +# +# keep -- Main function for kshkeep; provided with a list of NAMEs, whether +# shell functions or variables, writes the current definition of each NAME to a +# directory $KSHKEEP (defaults to ~/.kshkeep.d) with a .ksh suffix, each of +# which is reloaded each time this file is called. This allows you to quickly +# arrange to keep that useful shell function or variable you made inline on +# subsequent logins. +# +# Consider a shell function declared inline with the NAME 'ayy': +# +# $ ayy() { printf '%s\n' lmao ; } +# $ ayy +# lmao +# $ keep ayy +# $ keep +# ayy +# $ exit +# +# Then, on next login, the function is redefined for you: +# +# $ ayy +# lmao +# +# To get rid of it: +# +# $ keep -d ayy +# +function keep { + + # Name self + typeset self + self=keep + + # Figure out the directory to which we're reading and writing these scripts + typeset kshkeep + kshkeep=${KSHKEEP:-"$HOME"/.kshkeep.d} + mkdir -p -- "$kshkeep" || return + + # Parse options + typeset opt delete + typeset OPTERR OPTIND OPTARG + while getopts 'dh' opt ; do + case $opt in + + # -d given; means delete the keepfiles for the given names + d) + delete=1 + ;; + + # -h given; means show help + h) + cat <&2 + return 2 + ;; + esac + done + shift "$((OPTIND-1))" + + # If any arguments left, we must be either keeping or deleting + if (($#)) ; then + + # Start keeping count of any errors + typeset -i errors + errors=0 + + # Iterate through the NAMEs given + typeset name + for name ; do + + # Check NAMEs for validity + case $name in + + # NAME must start with letters or an underscore, and contain no + # characters besides letters, numbers, or underscores + *[!a-zA-Z0-9_]*|[!a-zA-Z_]*) + printf 'ksh: %s: %s not a valid NAME\n' \ + "$self" "$name" >&2 + ((errors++)) + ;; + + # NAME is valid, proceed + *) + + # If -d was given, delete the keep files for the NAME + if ((delete)) ; then + rm -- "$kshkeep"/"$name".ksh || + ((errors++)) + + # Save a function + elif [[ $(whence -v "$name" 2>/dev/null) == *' is a function' ]] ; then + typeset -f -- "$name" >"$kshkeep"/"$name".ksh || + ((errors++)) + + # Save a variable + elif [[ -n "$name" ]] ; then + typeset -p -- "$name" >"$kshkeep"/"$name".ksh || + ((errors++)) + fi + ;; + esac + done + + # Return 1 if we accrued any errors, 0 otherwise + return "$((errors > 0))" + fi + + # Deleting is an error, since we need at least one argument + if ((delete)) ; then + printf 'ksh: %s: must specify at least one NAME to delete\n' \ + "$self" >&2 + return 2 + fi + + # Otherwise the user must want us to print all the NAMEs kept + ( + typeset keep + for keep in "$kshkeep"/*.ksh ; do + [[ -f "$keep" ]] || break + keep=${keep##*/} + keep=${keep%.ksh} + printf '%s\n' "$keep" + done + ) +} + +# Load any existing scripts in kshkeep +for kshkeep in "${KSHKEEP:-"$HOME"/.kshkeep.d}"/*.ksh ; do + [[ -e $kshkeep ]] && source "$kshkeep" +done +unset -v kshkeep -- cgit v1.2.3