diff options
| author | dana <dana@dana.is> | 2026-05-04 20:16:03 -0500 |
|---|---|---|
| committer | dana <dana@dana.is> | 2026-05-06 21:44:56 -0500 |
| commit | 08a487691c417fb24fde8b51fe9bd27bbcd0a63a (patch) | |
| tree | 4df1267c984ccfa03af8346f46cb48374a20784c | |
| parent | 54454: _values: auto-remove argument separator (diff) | |
| download | zsh-08a487691c417fb24fde8b51fe9bd27bbcd0a63a.tar zsh-08a487691c417fb24fde8b51fe9bd27bbcd0a63a.tar.gz zsh-08a487691c417fb24fde8b51fe9bd27bbcd0a63a.tar.bz2 zsh-08a487691c417fb24fde8b51fe9bd27bbcd0a63a.tar.lz zsh-08a487691c417fb24fde8b51fe9bd27bbcd0a63a.tar.xz zsh-08a487691c417fb24fde8b51fe9bd27bbcd0a63a.tar.zst zsh-08a487691c417fb24fde8b51fe9bd27bbcd0a63a.zip | |
54471: getopts: don't look for +o with posix_builtins, add -p
| -rw-r--r-- | ChangeLog | 4 | ||||
| -rw-r--r-- | Doc/Zsh/builtins.yo | 30 | ||||
| -rw-r--r-- | Doc/Zsh/options.yo | 9 | ||||
| -rw-r--r-- | NEWS | 3 | ||||
| -rw-r--r-- | README | 6 | ||||
| -rw-r--r-- | Src/builtin.c | 17 | ||||
| -rw-r--r-- | Test/B10getopts.ztst | 61 |
7 files changed, 109 insertions, 21 deletions
@@ -1,5 +1,9 @@ 2026-05-06 dana <dana@dana.is> + * 54471: Doc/Zsh/builtins.yo, Doc/Zsh/options.yo, NEWS, README, + Src/builtin.c, Test/B10getopts.ztst: getopts: don't look for +o + with posix_builtins, add -p + * 54454: Completion/Base/Utility/_values: auto-remove argument separator diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index c63d66e0c..095bc5783 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -988,16 +988,16 @@ tt(read -zr). ) findex(getopts) cindex(options, processing) -item(tt(getopts) var(optstring) var(name) [ var(arg) ... ])( +item(tt(getopts) [ tt(-p) ] var(optstring) var(name) [ var(arg) ... ])( Checks the var(arg)s for legal options. If the var(arg)s are omitted, -use the positional parameters. A valid option argument +the positional parameters are used. A valid option argument begins with a `tt(PLUS())' or a `tt(-)'. An argument not beginning with a `tt(PLUS())' or a `tt(-)', or the argument `tt(-)tt(-)', ends the options. Note that a single `tt(-)' is not considered a valid option argument. var(optstring) contains the letters that tt(getopts) recognizes. If a letter is followed by a `tt(:)', that option -requires an argument. The options can be -separated from the argument by blanks. +requires an argument. The argument may appear either immediately +following the option in the same word, or in the next word. Each time it is invoked, tt(getopts) places the option letter it finds in the shell parameter var(name), prepended with a `tt(PLUS())' when @@ -1007,15 +1007,17 @@ is stored in tt(OPTARG). vindex(OPTIND, use of) vindex(OPTARG, use of) +When the terminator `tt(-)tt(-)' is encountered, tt(OPTIND) is +incremented such that it can be tt(shift)ed away with the options. This +is not true of other non-option arguments, including a single `tt(-)'. + The first option to be examined may be changed by explicitly assigning to tt(OPTIND). tt(OPTIND) has an initial value of tt(1), and is normally set to tt(1) upon entry to a shell function and restored -upon exit. (The tt(POSIX_BUILTINS) option disables this, and also changes -the way the value is calculated to match other shells.) tt(OPTARG) -is not reset and retains its value from the most recent call to -tt(getopts). If either of tt(OPTIND) or tt(OPTARG) is explicitly -unset, it remains unset, and the index or option argument is not -stored. The option itself is still stored in var(name) in this case. +upon exit. tt(OPTARG) is not reset and retains its value from the most +recent call to tt(getopts). If either of tt(OPTIND) or tt(OPTARG) is +explicitly unset, it remains unset, and the index or option argument is +not stored. The option itself is still stored in var(name) in this case. A leading `tt(:)' in var(optstring) causes tt(getopts) to store the letter of any invalid option in tt(OPTARG), and to set var(name) to @@ -1023,6 +1025,14 @@ letter of any invalid option in tt(OPTARG), and to set var(name) to missing. Otherwise, tt(getopts) sets var(name) to `tt(?)' and prints an error message when an option is invalid. The exit status is nonzero when there are no more options. + +When either the tt(POSIX_BUILTINS) option is enabled or the tt(-p) +option is given to tt(getopts) itself, tt(getopts) behaves in a more +POSIX-compatible manner. Specifically, handling of +`tt(PLUS())'-prefixed options is disabled (they are treated as +non-options) and the value of tt(OPTIND) is calculated differently. +Additionally, with tt(POSIX_BUILTINS) (but not with tt(-p)), the value +of tt(OPTIND) is not kept local to the calling function. ) findex(hash) item(tt(hash) [ tt(-Ldfmrv) ] [ var(name)[tt(=)var(value)] ] ...)( diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo index 77dfb3fdb..3e745ec3d 100644 --- a/Doc/Zsh/options.yo +++ b/Doc/Zsh/options.yo @@ -2255,13 +2255,12 @@ Furthermore, functions and shell builtins are not executed after an tt(exec) prefix; the command to be executed must be an external command found in the path. -Furthermore, the tt(getopts) builtin behaves in a POSIX-compatible -fashion in that the associated variable tt(OPTIND) is not made -local to functions, and its value is calculated differently to match -other shells. - Moreover, the warning and special exit code from tt([[ -o )var(non_existent_option)tt( ]]) are suppressed. + +Other builtins may be affected as described in +ifzman(zmanref(zshbuiltins))\ +ifnzman(noderef(Shell Builtin Commands)). ) pindex(POSIX_IDENTIFIERS) pindex(NO_POSIX_IDENTIFIERS) @@ -86,6 +86,9 @@ to use outside of completion contexts. The zsh/mathfunc module now provides isnan() and isinf() functions. +The getopts builtin learnt a -p option to make it behave like the +POSIX_BUILTINS option has been temporarily enabled. + Changes since 5.8.1 ------------------- @@ -154,6 +154,12 @@ supported by _arguments, bringing it in line with the documentation. As a consequence, a leading '!' in a value name must now be escaped if it should be taken literally, as in: _values desc '!hidden' '\!literal' +The getopts builtin no longer interprets arguments beginning with a + +as potential options during parsing when the POSIX_BUILTINS option is +enabled. Additionally, as a consequence of learning the related -p +option, an opt spec beginning with a hyphen must be guarded by - or --. +(Note that the effect of a hyphen in the opt spec is unspecified.) + Incompatibilities between 5.8.1 and 5.9 --------------------------------------- diff --git a/Src/builtin.c b/Src/builtin.c index 4b11aed60..266fcf924 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -76,7 +76,7 @@ static struct builtin builtins[] = BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlp:%rtux", "E"), BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "ckmMstTuUWx:z", NULL), BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"), - BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL), + BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, "p", NULL), BUILTIN("hash", BINF_MAGICEQUALS, bin_hash, 0, -1, 0, "Ldfmrv", NULL), #ifdef ZSH_HASH_DEBUG @@ -5669,13 +5669,18 @@ int optcind; /**/ int -bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int func)) +bin_getopts(UNUSED(char *name), char **argv, Options ops, UNUSED(int func)) { - int lenstr, lenoptstr, quiet, lenoptbuf; + int lenstr, lenoptstr, quiet, lenoptbuf, posix; char *optstr = unmetafy(*argv++, &lenoptstr), *var = *argv++; char **args = (*argv) ? argv : pparams; char *str, optbuf[2] = " ", *p, opch; + // note that resetting + restoring OPTIND happens in doshfunc(), so using -p + // or enabling POSIX_BUILTINS inside a function that calls getopts is not + // exactly the same as enabling POSIX_BUILTINS before the function is called + posix = isset(POSIXBUILTINS) || OPT_ISSET(ops, 'p'); + /* zoptind keeps count of the current argument number. The * * user can set it to zero to start a new option parse. */ if (zoptind < 1) { @@ -5703,7 +5708,7 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun str = unmetafy(dupstring(args[zoptind - 1]), &lenstr); } if(!optcind) { - if(lenstr < 2 || (*str != '-' && *str != '+')) + if (lenstr < 2 || (*str != '-' && (posix || *str != '+'))) return 1; if(lenstr == 2 && str[0] == '-' && str[1] == '-') { zoptind++; @@ -5723,7 +5728,7 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) { p = "?"; /* Keep OPTIND correct if the user doesn't return after the error */ - if (isset(POSIXBUILTINS)) { + if (posix) { optcind = 0; zoptind++; } @@ -5744,7 +5749,7 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun if(optcind == lenstr) { if(!args[zoptind]) { /* Fix OPTIND as above */ - if (isset(POSIXBUILTINS)) { + if (posix) { optcind = 0; zoptind++; } diff --git a/Test/B10getopts.ztst b/Test/B10getopts.ztst index e50d177c7..5ababebcc 100644 --- a/Test/B10getopts.ztst +++ b/Test/B10getopts.ztst @@ -125,3 +125,64 @@ 0:OPTIND calculation with and without POSIX_BUILTINS (workers/42248) >no_posix_builtins: <1><1><2><1><1><3><5><7><6> >posix_builtins: <1><1><2><2><2><3><6><7><7> + + for 1 in no_posix_builtins posix_builtins; do ( + setopt $1 + print -rn - $1: + set -- -a -b +b -c + while getopts :abc opt; do + case $opt in + a|b|+b|c) print -rn - " $opt" ;; + ?) print -rn - " ?$OPTARG" ;; + esac + done + print + ); done +0:POSIX_BUILTINS disables '+' variant handling +>no_posix_builtins: a b +b c +>posix_builtins: a b + + ( + setopt no_posix_builtins + for 1 in -a +a -x +x; do + () { local opt; getopts :abc opt; print -r - $opt } $1 + done + for 1 in -a +a -x +x; do + () { local opt; getopts -p :abc opt; print -r - $opt } $1 + done + ) +0:-p works like POSIX_BUILTINS +>a +>+a +>? +>? +>a +> +>? +> + + # not enough arguments + () { getopts } + () { getopts '' } + () { getopts x } + () { getopts - x } + () { getopts -p - x } + () { getopts -p -- x } + # invalid option to getopts + () { getopts -x x } + () { getopts -x x y } + () { getopts -x - x y } + # guarded spec + () { getopts - -x y } + # argv on command line + () { getopts x y a b c } +-:option parsing +?(anon):getopts: not enough arguments +?(anon):getopts: not enough arguments +?(anon):getopts: not enough arguments +?(anon):getopts: not enough arguments +?(anon):getopts: not enough arguments +?(anon):getopts: not enough arguments +?(anon):getopts: bad option: -x +?(anon):getopts: bad option: -x +?(anon):getopts: bad option: -x |
