From 501012e33134b5ce78ae8320a131faf87b0f13ef Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 11 Jun 2025 16:54:20 -0500 Subject: [PATCH] Allow requiring that options precede parameters GNU getopt, at least, provides multiple "scanning modes" that affect how it parses interleaved options and parameters. This exposes the choice to the application using the argsparse library. --- argsparse.sh | 29 ++++++++++++++++++++++++++++- unittest | 10 ++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/argsparse.sh b/argsparse.sh index 24d86d8..9f3d235 100644 --- a/argsparse.sh +++ b/argsparse.sh @@ -1341,6 +1341,33 @@ argsparse_allow_no_argument() { } +# Default behaviour is to continue parsing options after a non-option parameter +# is encountered. +__argsparse_scanning_mode="" + + +## @fn argsparse_options_first() +## @brief Stop parsing options once a non-option parameter is encountered. +## @details Change argsparse behaviour to require that options +## precede non-options. Default is to ignore order. +## @param string if (case-insensitive) "yes", "true" or "1", the value +## is considered as affirmative. Anything else is a negative value. +## @retval 0 unless there's more than one parameter (or none). +## @ingroup ArgsparseParameter +argsparse_options_first() { + [[ $# -eq 1 ]] || return 1 + local param=$1 + case "${param,,}" in + yes|true|1) + __argsparse_scanning_mode="+" + ;; + *) + __argsparse_scanning_mode="" + ;; + esac +} + + ## @brief Internal use only. ## @details The default minimum parameters requirement for command line. ## @ingroup ArgsparseParameter @@ -1760,7 +1787,7 @@ __argsparse_parse_options_no_usage() { # 4. Invoke getopt and replace arguments. if ! getopt_temp=$(getopt -s bash -n "$argsparse_pgm" \ - --longoptions="$longs" "$shorts" "$@") + --longoptions="$longs" "$__argsparse_scanning_mode$shorts" "$@") then # Syntax error on the command implies returning with error. return 1 diff --git a/unittest b/unittest index 12364ed..381ea9b 100755 --- a/unittest +++ b/unittest @@ -171,6 +171,16 @@ parse_option_wrapper() { parse_option_wrapper "short property" -S ) +( + argsparse_use_option option1 "" + + argsparse_options_first yes + parse_option_wrapper "stop at nonoption" --option1 nonoption --invalid + + argsparse_options_first no + TEST=failure parse_option_wrapper "don't stop at nonoption" --option1 nonoption --invalid +) + ( argsparse_use_option =shortcut "" parse_option_wrapper "= in optstring" -s