pashage

Yet Another Opinionated Re-engineering of the Unix Password Store
git clone https://git.instinctive.eu/pashage.git
Log | Files | Refs | README | LICENSE

commit e1a62fb6dc4451c5a165e1abc7aee470e8adc871
parent 7ca735c8ed8ddbc3274c935107f3b161a1433fc1
Author: Natasha Kerensikova <natgh@instinctive.eu>
Date:   Fri, 13 Sep 2024 18:30:06 +0000

usage1 is replaced by a derivation form cmd_usage
Diffstat:
Mspec/internal_spec.sh | 20--------------------
Mspec/usage_spec.sh | 205++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/pashage.sh | 72++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
3 files changed, 227 insertions(+), 70 deletions(-)

diff --git a/spec/internal_spec.sh b/spec/internal_spec.sh @@ -219,26 +219,6 @@ Describe 'Internal Helper Functions' End End - Describe 'usage1' - It 'accepts arguments' - PROGRAM=prg - COMMAND=cmd - When run usage1 'flag1' 'flag2' - The error should equal 'Usage: prg cmd flag1 flag2' - The output should be blank - The status should equal 1 - End - - It 'works without argument' - PROGRAM=prg - COMMAND=cmd - When run usage1 - The error should equal 'Usage: prg cmd' - The output should be blank - The status should equal 1 - End - End - Describe 'yesno' Describe 'Without stty' It 'accepts an uppercase N' diff --git a/spec/usage_spec.sh b/spec/usage_spec.sh @@ -221,6 +221,10 @@ Describe 'Command-Line Parsing' End It 'reports a bad option' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_copy -s arg The output should be blank The error should equal 'Usage: prg copy [--force,-f] old-path new-path' @@ -228,6 +232,10 @@ Describe 'Command-Line Parsing' End It 'reports a lack of argument' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_copy src The output should be blank The error should equal 'Usage: prg copy [--force,-f] old-path new-path' @@ -235,6 +243,25 @@ Describe 'Command-Line Parsing' End End + Describe 'cmd_copy_move' + COMMAND=wrong + + It 'reports both commands when confused' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } + result() { %text + #|Usage: prg copy [--force,-f] old-path new-path + #| prg move [--force,-f] old-path new-path + } + When run cmd_copy src + The output should be blank + The error should equal "$(result)" + The status should equal 1 + End + End + Describe 'cmd_delete' COMMAND=delete @@ -289,16 +316,24 @@ Describe 'Command-Line Parsing' End It 'reports a bad option' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_delete -u arg The output should be blank - The error should equal 'Usage: prg delete [--force,-f] pass-name ...' + The error should equal 'Usage: prg delete [--force,-f] pass-name' The status should equal 1 End It 'reports a lack of argument' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_delete The output should be blank - The error should equal 'Usage: prg delete [--force,-f] pass-name ...' + The error should equal 'Usage: prg delete [--force,-f] pass-name' The status should equal 1 End End @@ -334,6 +369,10 @@ Describe 'Command-Line Parsing' End It 'reports a lack of argument' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_edit The output should be blank The error should equal 'Usage: prg edit pass-name' @@ -352,9 +391,13 @@ Describe 'Command-Line Parsing' End It 'reports a lack of argument' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_find The output should be blank - The error should equal 'Usage: prg find [-grepflags] regex' + The error should equal 'Usage: prg find [GREP_OPTIONS] regex' The status should equal 1 End End @@ -363,6 +406,11 @@ Describe 'Command-Line Parsing' COMMAND=generate GENERATED_LENGTH=25 + usage_text() { %text + #|Usage: prg generate [--no-symbols,-n] [--clip,-c | --qrcode,-q] + #| [--in-place,-i | --force,-f] pass-name [pass-length] + } + It 'generates a new entry with default length' result() { %text @@ -574,44 +622,68 @@ Describe 'Command-Line Parsing' End It 'reports incompatible generation long options' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_generate --inplace --force secret The output should be blank - The error should equal 'Usage: prg generate [--no-symbols,-n] [--clip,-c | --qrcode,-q] [--in-place,-i | --force,-f] pass-name [pass-length]' + The error should equal "$(usage_text)" The status should equal 1 End It 'reports incompatible generation short options' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_generate -fi secret The output should be blank - The error should equal 'Usage: prg generate [--no-symbols,-n] [--clip,-c | --qrcode,-q] [--in-place,-i | --force,-f] pass-name [pass-length]' + The error should equal "$(usage_text)" The status should equal 1 End It 'reports incompatible show long options' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_generate --qrcode --clip secret The output should be blank - The error should equal 'Usage: prg generate [--no-symbols,-n] [--clip,-c | --qrcode,-q] [--in-place,-i | --force,-f] pass-name [pass-length]' + The error should equal "$(usage_text)" The status should equal 1 End It 'reports incompatible show short options' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_generate -cq secret The output should be blank - The error should equal 'Usage: prg generate [--no-symbols,-n] [--clip,-c | --qrcode,-q] [--in-place,-i | --force,-f] pass-name [pass-length]' + The error should equal "$(usage_text)" The status should equal 1 End It 'reports a bad option' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_generate --bad secret The output should be blank - The error should equal 'Usage: prg generate [--no-symbols,-n] [--clip,-c | --qrcode,-q] [--in-place,-i | --force,-f] pass-name [pass-length]' + The error should equal "$(usage_text)" The status should equal 1 End It 'reports a lack of argument' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_generate The output should be blank - The error should equal 'Usage: prg generate [--no-symbols,-n] [--clip,-c | --qrcode,-q] [--in-place,-i | --force,-f] pass-name [pass-length]' + The error should equal "$(usage_text)" The status should equal 1 End End @@ -670,9 +742,13 @@ Describe 'Command-Line Parsing' End It 'reports a lack of argument' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_git The output should be blank - The error should equal 'Usage: prg git args ...' + The error should equal 'Usage: prg git git-command-args ...' The status should equal 1 End @@ -777,6 +853,10 @@ Describe 'Command-Line Parsing' End It 'reports a lack of argument' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_grep The output should be blank The error should equal 'Usage: prg grep [GREP_OPTIONS] search-regex' @@ -888,34 +968,50 @@ Describe 'Command-Line Parsing' End It 'reports a bad option' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_init -q arg The output should be blank The error should equal \ - 'Usage: prg init [--path=subfolder,-p subfolder] recipient ...' + 'Usage: prg init [--path=subfolder,-p subfolder] age-recipient ...' The status should equal 1 End It 'reports a missing recipient' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_init -p sub The output should be blank The error should equal \ - 'Usage: prg init [--path=subfolder,-p subfolder] recipient ...' + 'Usage: prg init [--path=subfolder,-p subfolder] age-recipient ...' The status should equal 1 End It 'reports a missing path' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_init -p The output should be blank The error should equal \ - 'Usage: prg init [--path=subfolder,-p subfolder] recipient ...' + 'Usage: prg init [--path=subfolder,-p subfolder] age-recipient ...' The status should equal 1 End It 'reports a lack of argument' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_init The output should be blank The error should equal \ - 'Usage: prg init [--path=subfolder,-p subfolder] recipient ...' + 'Usage: prg init [--path=subfolder,-p subfolder] age-recipient ...' The status should equal 1 End End @@ -1084,6 +1180,10 @@ Describe 'Command-Line Parsing' End It 'reports a bad option' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_insert -u secret The output should be blank The error should equal \ @@ -1092,6 +1192,10 @@ Describe 'Command-Line Parsing' End It 'reports incompatible long options' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_insert --multiline --echo secret The output should be blank The error should equal \ @@ -1100,6 +1204,10 @@ Describe 'Command-Line Parsing' End It 'reports incompatible short options' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_insert -em secret The output should be blank The error should equal \ @@ -1108,6 +1216,10 @@ Describe 'Command-Line Parsing' End It 'reports a lack of argument' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_insert The output should be blank The error should equal \ @@ -1117,6 +1229,7 @@ Describe 'Command-Line Parsing' End Describe 'cmd_list_or_show' + COMMAND= SELECTED_LINE=1 SHOW=text @@ -1130,7 +1243,6 @@ Describe 'Command-Line Parsing' When call cmd_list_or_show The output should be blank The error should equal "$(result)" - The variable COMMAND should equal 'show' End It 'shows multiple entries' @@ -1149,7 +1261,6 @@ Describe 'Command-Line Parsing' When call cmd_list_or_show arg1 arg2 arg3 The output should be blank The error should equal "$(result)" - The variable COMMAND should equal 'show' End It 'shows a flag-like entry' @@ -1162,7 +1273,6 @@ Describe 'Command-Line Parsing' When call cmd_list_or_show -- -c The output should be blank The error should equal "$(result)" - The variable COMMAND should equal 'show' End It 'copies an entry into the clipboard (short option)' @@ -1175,7 +1285,6 @@ Describe 'Command-Line Parsing' When call cmd_list_or_show -c arg The output should be blank The error should equal "$(result)" - The variable COMMAND should equal 'show' End It 'copies an entry into the clipboard (long option)' @@ -1188,7 +1297,6 @@ Describe 'Command-Line Parsing' When call cmd_list_or_show --clip arg The output should be blank The error should equal "$(result)" - The variable COMMAND should equal 'show' End It 'copies a line of an entry into the clipboard (short option)' @@ -1201,7 +1309,6 @@ Describe 'Command-Line Parsing' When call cmd_list_or_show -c2 arg The output should be blank The error should equal "$(result)" - The variable COMMAND should equal 'show' End It 'copies a line of an entry into the clipboard (short option)' @@ -1214,7 +1321,6 @@ Describe 'Command-Line Parsing' When call cmd_list_or_show --clip=2 arg The output should be blank The error should equal "$(result)" - The variable COMMAND should equal 'show' End It 'shows an entry as a QR-code (short option)' @@ -1227,7 +1333,6 @@ Describe 'Command-Line Parsing' When call cmd_list_or_show -q arg The output should be blank The error should equal "$(result)" - The variable COMMAND should equal 'show' End It 'shows an entry as a QR-code (long option)' @@ -1240,7 +1345,6 @@ Describe 'Command-Line Parsing' When call cmd_list_or_show --qrcode arg The output should be blank The error should equal "$(result)" - The variable COMMAND should equal 'show' End It 'shows the line of an entry as a QR-code (short option)' @@ -1253,7 +1357,6 @@ Describe 'Command-Line Parsing' When call cmd_list_or_show -q3 arg The output should be blank The error should equal "$(result)" - The variable COMMAND should equal 'show' End It 'shows the line of an entry as a QR-code (long option)' @@ -1266,14 +1369,52 @@ Describe 'Command-Line Parsing' When call cmd_list_or_show --qrcode=3 arg The output should be blank The error should equal "$(result)" - The variable COMMAND should equal 'show' End - It 'reports a bad option' + It 'reports a bad option for both commands' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } + result() { %text + #|Usage: prg [list] [subfolder] + #| prg [show] [--clip[=line-number],-c[line-number] | + #| --qrcode[=line-number],-q[line-number]] pass-name + } When run cmd_list_or_show -f arg The output should be blank - The error should equal \ - 'Usage: prg show [ --clip[=line-number], -c[line-number] ] [ --qrcode[=line-number], -q[line-number] ] pass-name' + The error should equal "$(result)" + The status should equal 1 + End + + It 'reports a bad option for list command' + COMMAND=list + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } + result() { %text + #|Usage: prg [list] [subfolder] + } + When run cmd_list_or_show -f arg + The output should be blank + The error should equal "$(result)" + The status should equal 1 + End + + It 'reports a bad option for show command' + COMMAND=show + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } + result() { %text + #|Usage: prg [show] [--clip[=line-number],-c[line-number] | + #| --qrcode[=line-number],-q[line-number]] pass-name + } + When run cmd_list_or_show -f arg + The output should be blank + The error should equal "$(result)" The status should equal 1 End End @@ -1348,6 +1489,10 @@ Describe 'Command-Line Parsing' End It 'reports a bad option' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_move -s arg The output should be blank The error should equal 'Usage: prg move [--force,-f] old-path new-path' @@ -1355,6 +1500,10 @@ Describe 'Command-Line Parsing' End It 'reports a lack of argument' + CLIP_TIME='$CLIP_TIME' + GENERATED_LENGTH='$GENERATED_LENGTH' + cat() { @cat; } + sed() { @sed "$@"; } When run cmd_move src The output should be blank The error should equal 'Usage: prg move [--force,-f] old-path new-path' diff --git a/src/pashage.sh b/src/pashage.sh @@ -101,11 +101,6 @@ strlen(){ unset STR } -# Output a usage error message message -usage1() { - die "Usage: ${PROGRAM} ${COMMAND}" "$@" -} - # Ask for confirmation # $1: Prompt yesno() { @@ -1006,7 +1001,13 @@ cmd_copy_move() { done if [ "${PARSE_ERROR}" = yes ] || [ $# -lt 2 ]; then - usage1 "[--force,-f] old-path new-path" + if [ "${COMMAND}" = "c${COMMAND#c}" ]; then + die_usage1 copy + elif [ "${COMMAND}" = "m${COMMAND#m}" ]; then + die_usage1 move + else + die_usage1 copy move + fi fi unset PARSE_ERROR @@ -1048,7 +1049,7 @@ cmd_delete() { done if [ "${PARSE_ERROR}" = yes ] || [ $# -eq 0 ]; then - usage1 "[--force,-f] pass-name ..." + die_usage1 delete fi unset PARSE_ERROR @@ -1058,7 +1059,7 @@ cmd_delete() { } cmd_edit() { - [ $# -eq 0 ] && usage1 "pass-name" + [ $# -eq 0 ] && die_usage1 edit check_sneaky_paths "$@" platform_tmpdir @@ -1070,7 +1071,7 @@ cmd_edit() { cmd_find() { if [ $# -eq 0 ]; then - usage1 "[-grepflags] regex" + die_usage1 find fi do_tree "${PREFIX}" "Search pattern: $*" "$@" @@ -1136,8 +1137,7 @@ cmd_generate() { if [ "${PARSE_ERROR}" = yes ] || [ $# -eq 0 ] || [ $# -gt 2 ] \ || [ "${DECISION}-${OVERWRITE}" = force-yes ] then - usage1 "[--no-symbols,-n] [--clip,-c | --qrcode,-q]" \ - "[--in-place,-i | --force,-f] pass-name [pass-length]" + die_usage1 generate fi unset PARSE_ERROR @@ -1157,7 +1157,7 @@ cmd_generate() { cmd_git() { if [ $# -lt 1 ]; then - usage1 'args ...' + die_usage1 git elif [ -d "${PREFIX}/.git" ]; then platform_tmpdir TMPDIR="${SECURE_TMPDIR}" git -C "${PREFIX}" "$@" @@ -1178,7 +1178,7 @@ cmd_git() { } cmd_grep() { - [ $# -eq 0 ] && usage1 "[GREP_OPTIONS] search-regex" + [ $# -eq 0 ] && die_usage1 grep ( cd "${PREFIX}" && do_grep "" "$@" ) } @@ -1242,7 +1242,7 @@ cmd_init() { done if [ "${PARSE_ERROR}" = yes ] || [ $# -eq 0 ]; then - usage1 "[--path=subfolder,-p subfolder] recipient ..." + die_usage1 init fi check_sneaky_path "${SUBDIR}" @@ -1308,7 +1308,7 @@ cmd_insert() { || [ $# -lt 1 ] \ || [ "${ECHO}${MULTILINE}" = yesyes ] then - usage1 "[--echo,-e | --multiline,-m] [--force,-f] pass-name" + die_usage1 insert fi unset PARSE_ERROR @@ -1319,7 +1319,6 @@ cmd_insert() { } cmd_list_or_show() { - COMMAND=show PARSE_ERROR=no while [ $# -ge 1 ]; do @@ -1358,8 +1357,13 @@ cmd_list_or_show() { done if [ "${PARSE_ERROR}" = yes ]; then - usage1 "[ --clip[=line-number], -c[line-number] ]" \ - "[ --qrcode[=line-number], -q[line-number] ] pass-name" + if [ "${COMMAND}" = "l${COMMAND#l}" ]; then + die_usage1 list + elif [ "${COMMAND}" = "s${COMMAND#s}" ]; then + die_usage1 show + else + die_usage1 list show + fi fi check_sneaky_paths "$@" @@ -1412,7 +1416,7 @@ ${INDENT_PROG} delete [--force,-f] pass-name ${INDNT}Remove existing passwords or directories, optionally forcefully. ${INDENT_PROG} edit pass-name ${INDNT}Insert a new password or edit an existing password using an editor. -${INDENT_PROG} find [-grepflags] regex +${INDENT_PROG} find [GREP_OPTIONS] regex ${INDNT}List passwords that match the given regex. ${INDENT_PROG} generate [--no-symbols,-n] [--clip,-c | --qrcode,-q] ${INDENT_ARGT} [--in-place,-i | --force,-f] pass-name [pass-length] @@ -1423,13 +1427,13 @@ ${INDNT}or display it as a QR-code. ${INDNT}Prompt before overwriting existing password unless forced. ${INDNT}Optionally replace only the first line of an existing file ${INDNT}with a new password. -${INDENT_PROG} git git-command-args... +${INDENT_PROG} git git-command-args ... ${INDNT}If the password store is a git repository, execute a git command ${INDNT}specified by git-command-args. ${INDENT_PROG} gitconfig ${INDNT}If the password store is a git repository, enforce local configuration. -${INDENT_PROG} grep [GREPOPTIONS] search-string -${INDNT}Search for password files matching search-string when decrypted. +${INDENT_PROG} grep [GREP_OPTIONS] search-regex +${INDNT}Search for password files matching search-regex when decrypted. ${INDENT_PROG} help ${INDNT}Show this text. ${INDENT_PROG} init [--path=subfolder,-p subfolder] age-recipient ... @@ -1464,3 +1468,27 @@ cmd_version() { ============================================== EOF } + +# Outputs usage text for a single command on stderr and abort +# $1: comamnd +# ... alternate commands +die_usage1() { + MSG='' + USAGE="$(cmd_usage "START ${PROGRAM}" "END")" + for ARG in "$@"; do + # The line below is actually covered, see + # https://github.com/SimonKagstrom/kcov/issues/164 + PART="$(printf '%s\n' "${USAGE}" \ + | sed -e ': b' \ + -e "/^START.*${ARG}/{;h;N;/END/!b b" \ + -e ';x;q;};d')" + + if [ -z "${MSG}" ]; then + MSG="Usage:${PART#START }" + else + MSG="$(printf '%s\n' "${MSG}" " ${PART#START}")" + fi + done + + die "${MSG}" +}