#!/bin/sh #shellcheck disable=SC2039 CHARMENU_FILE="${CHARMENU_FILE:-}" charmenu() { # # Present "1-char-menu" (but actually N-char) or its parts # # Usage: # CHARMENU_FILE=/path/to/file # charmenu [options] full [ARG]... # charmenu [options] items # charmenu [options] prompt # charmenu [options] has ITEM # # Read file in $CHARMENU_FILE and display either `full` # menu, `items` menu (e.g. "y,n,q,?") or just `prompt`. # # Options: # # -f FILE, --file FILE Read contents of menu freom FILE # instead of variable `$CHARMENU_FILE`. # # -p PROMPT, --prompt PROMPT Set prompt string to PROMPT. # If PROMPT contains '%s', it will be replaced by 'items' # menu (eg. "y,n,q"). Note that you most probably want # to include space as last character. Default: "What # next [%s]? ". # # -i, --ignore-case Ignore case of user reply. Default: # case is respected. # # -d DLM, --delimiter DLM Use delimiter DLM when printing # 'items' menu. Default: "," (comma). # # Format of menu file is one item per line, where item is # delimited from its description by single colon. Leading # and trailing spaces and spaces around the colon are # stripped. # # Simple example menu file: # # a: abort # r: retry # f: fail # # You can use printf symbols like `%s` in menu and then # call `charmenu full arg1 arg2...` to have your arguments # expanded in the full menu. More fleshed out example: # # . "$(sfpath)" || exit 3 # shellfu import charmenu # shellfu import pretty # # cat <menu # r: re-run test %s # c: clean screen # d: download results to %s # q: quit # EOM # # CHARMENU_FILE=menu # running=true # while $running; do # charmenu full "mytest" "some storage" # read -r response # case $response in # r) rerun_test ;; # c) reset ;; # d) download_results ;; # q) running=false ;; # *) warn "invalid response: $response" # esac # done # clean_up # # `charmenu has ITEM` exits with zero if ITEM is a valid # menu item. With abort/retry/fail example above, calling # `if charmenu has a; echo OK; fi` would print "OK".. # local mfile # path to menu file local prompt # prompt text local delim # delimiter (in 'items' menu) local cmd # command do execute local cases # "-i" in case-insensitive mode local menu # loaded menu text local alist # loaded list of items mfile=$CHARMENU_FILE prompt="What next [%s]? " delim=, while true; do case $1 in -d|--delimiter) delim="$2"; shift 2 || return 2 ;; -f|--file) mfile="$2"; shift 2 || return 2 ;; -i|--ignore-case) cases="-i"; shift ;; -p|--prompt) prompt="$2"; shift 2 || return 2 ;; full|items|prompt|has) cmd=$1; shift; break ;; *) mkusage "full [PRINTF_ARG...]" \ "items [DELIM]" \ "prompt [PRINTF_FMT]" \ "has ITEM" ;; esac done menu="$(cat "$mfile")" alist="$(echo "$menu" | cut -d: -f1 | sed -re 's/^ +//; s/ +$//')" #shellcheck disable=SC2059 case $cmd in full) # print the full menu, with any printf notation expanded printf "$menu\n" "$@" ;; items) # print just the list of actions echo "$alist" paste -sd"$delim" ;; prompt) # print prompt for user printf "$prompt" "$(charmenu items)" ;; has) # check if response is valid ("on the menu") local res="$1" test -n "$res" || return 1 # "" is not valid echo "$alist" | grep $cases -e "^$res$" return $? ;; esac }