From 84dde7c7b622737a34526ac2a5ba29ddc6c072d8 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 14 May 2022 20:34:28 -0700 Subject: Jan Brieg: 50212 (and discussion): Add "bright" color variants --- Functions/Misc/colors | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'Functions/Misc') diff --git a/Functions/Misc/colors b/Functions/Misc/colors index 5e9d77d10..5c9924adb 100644 --- a/Functions/Misc/colors +++ b/Functions/Misc/colors @@ -63,6 +63,16 @@ color=( # 63 double-overline-or-left # 64 stress # 65 no-ideogram-marking + +# Bright color codes (xterm extension) + 90 bright-gray 100 bg-bright-gray + 91 bright-red 101 bg-bright-red + 92 bright-green 102 bg-bright-green + 93 bright-yellow 103 bg-bright-yellow + 94 bright-blue 104 bg-bright-blue + 95 bright-magenta 105 bg-bright-magenta + 96 bright-cyan 106 bg-bright-cyan + 97 bright-white 107 bg-bright-white ) # A word about black and white: The "normal" shade of white is really a @@ -79,7 +89,7 @@ for k in ${(k)color}; do color[${color[$k]}]=$k; done # Add "fg-" keys for all the text colors, for clarity. -for k in ${color[(I)3?]}; do color[fg-${color[$k]}]=$k; done +for k in ${color[(I)[39]?]}; do color[fg-${color[$k]}]=$k; done # This is inaccurate, but the prompt theme system needs it. @@ -89,7 +99,11 @@ for k in grey gray; do color[bg-$k]=${color[bg-black]} done -# Assistance for the color-blind. +# Assistance for the colo(u)r-blind. + +for k in '' fg- bg-; do + color[${k}bright-grey]=${color[${k}bright-gray]} +done colour=(${(kv)color}) # A case where ksh namerefs would be useful ... -- cgit v1.2.3 From 92da264eea88582d51c9932b6233863298f79016 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 26 May 2022 09:32:33 +0100 Subject: 50286: avoid zed error if nounset is in effect --- ChangeLog | 4 ++++ Functions/Misc/zed | 14 ++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index 3fbd9d9b5..6b65e7996 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2022-05-26 Peter Stephenson + + * 50286: Functions/Misc/zed: avoid error if nounset is in effect. + 2022-05-25 Norbert Lange * github #94: Completion/Linux/Command/_modutils: Support .zst diff --git a/Functions/Misc/zed b/Functions/Misc/zed index 7d0d590db..bb075512c 100644 --- a/Functions/Misc/zed +++ b/Functions/Misc/zed @@ -14,15 +14,17 @@ local var opts zed_file_name integer TMOUT=0 okargs=1 fun hist bind local -a expand -zparseopts -D -A opts f h b x: +zparseopts -D -A opts f h b x: || return 1 fun=$+opts[-f] hist=$+opts[-h] bind=$+opts[-b] -if [[ $opts[-x] == <-> ]]; then - expand=(-x $opts[-x]) -elif (( $+opts[-x] )); then - print -r "Integer expected after -x: $opts[-x]" >&2 - return 1 +if (( $+opts[-x] )); then + if [[ $opts[-x] == <-> ]]; then + expand=(-x $opts[-x]) + else + print -r "Integer expected after -x: $opts[-x]" >&2 + return 1 + fi fi [[ $0 = fned ]] && fun=1 -- cgit v1.2.3 From 52761c94185f8405d8069c1d4bacc8b5210a1850 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 8 Jun 2022 20:41:24 -0700 Subject: 50335: simplify "wait" usage, fix signal handling - remove the preliminary "wait" for all the process - remove "nomonitor" (because it was only needed for that "wait") - explicitly adds traps to exit for tty-generated signals plus TERM - capture the signal trap context and restore it in background jobs - wrap in an "always" block to clean up local helper functions - update comments to note another buglet and drop support for zsh 4.x. --- ChangeLog | 5 +++++ Functions/Misc/zargs | 40 ++++++++++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 10 deletions(-) (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index 71f879776..c13d8a163 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-06-08 Bart Schaefer + + * 50335: Functions/Misc/zargs: simplify "wait" usage, fix signal + handling for functions used as the command. + 2022-06-07 Peter Stephenson * 50339: Doc/Zsh/options.yo, Src/text.c, Test/C04funcdef.ztst: diff --git a/Functions/Misc/zargs b/Functions/Misc/zargs index 81916a3ac..782d6811e 100644 --- a/Functions/Misc/zargs +++ b/Functions/Misc/zargs @@ -39,9 +39,14 @@ # # "Killed by a signal" is determined by the usual shell rule that $? is # the signal number plus 128, so zargs can be fooled by a command that -# explicitly exits with 129+. Also, zsh prior to 4.1.x returns 1 rather -# than 127 for "command not found" so this function incorrectly returns -# 123 in that case if used with zsh 4.0.x. +# explicitly exits with 129+. If the command passed to zargs is a shell +# function which uses "exit" instead of "return", zsh interprets 129+ as +# a signal sent to the process group and may terminate zargs with that +# status. This is avoided when running zargs -P 2 or greater. +# +# ZARGS_VERSION 1.5 is the last to support zsh 4.x. Also, zsh prior to +# 4.1.x returns 1 rather than 127 for "command not found" so zargs +# incorrectly returned 123 in that case if used with zsh 4.0.x. # # Because of "wait" limitations, --max-procs spawns max-procs jobs, then # waits for all of those, then spawns another batch, etc. @@ -71,6 +76,10 @@ # * The use of SIGUSR1 and SIGUSR2 to change the number of parallel jobs # is not supported. +{ # Begin "always" block to reset locally defined functions + +local ZARGS_VERSION="1.8" + # First, capture the current setopts as "sticky emulation" if zmodload zsh/parameter then @@ -84,11 +93,20 @@ else emulate $(emulate -l) -c '_zarun() { eval "$@" }' fi +local _zaTRAPS="$(trap)" +_zatraps() { + # In children, these traps may be reset to default behavior, even + # if the calling shell has traps. Restore to surrounding context, + # but assure that if zargs itself is signaled, children will exit. + [[ -o interactive ]] && + function TRAP{HUP,INT,QUIT,TERM} { exit $((128 + $1)) } + [[ -n "$_zaTRAPS" ]] && eval "$_zaTRAPS" + unset _zaTRAPS +} + emulate -L zsh || return 1 local -a opts eof n s l P i -local ZARGS_VERSION="1.7" - if zparseopts -a opts -D -- \ -eof::=eof e::=eof \ -exit x \ @@ -193,14 +211,14 @@ then (( c = $#command - 1 )) else command=( print -r -- ) fi -local wait bg -local execute=' +local bg execute=' if (( $opts[(I)-(-interactive|p)] )) then read -q "?$call?..." || continue elif (( $opts[(I)-(-verbose|t)] )) then print -u2 -r -- "$call" fi _zarun "{ + _zatraps \"\${call[@]}\" } $bg"' local ret=0 analyze=' @@ -263,12 +281,10 @@ fi if (( P != 1 && ARGC > 1 )) then - # These setopts are necessary for "wait" on multiple jobs to work. - setopt nonotify nomonitor + setopt nonotify # Do not report each exiting job local -a _zajobs local j bg='& _zajobs+=( $! )' - wait='wait' analyze=' for j in $_zajobs; do wait $j @@ -316,4 +332,8 @@ return $ret ) +} always { + builtin unfunction _zarun _zatraps +} + # } -- cgit v1.2.3 From 6e827d8f9a50653aa1905d8aff8cc91e6e2423c4 Mon Sep 17 00:00:00 2001 From: Julian Prein Date: Mon, 19 Sep 2022 02:02:18 +0000 Subject: 50648: Use $ZCALC_HISTORY where appropriate --- ChangeLog | 5 +++++ Doc/Zsh/contrib.yo | 7 +++++-- Functions/Misc/zcalc | 4 +++- 3 files changed, 13 insertions(+), 3 deletions(-) (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index 375172ec9..48c65d01b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-09-25 Peter Stephenson + + * 50648: Functions/Misc/zcalc: Julian Prein: Use ZCALC_HISTFILE + where defined for zcalc history. + 2022-09-21 Jun-ichi Takimoto * Nicholas Vinson: 50641: aczsh.m4, configure.ac: use 'int main()' diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index 0ef59dbc9..96de5aa9b 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -3972,8 +3972,11 @@ calculation is stored. For example, the result of the calculation on the line preceded by `tt(4> )' is available as tt($4). The last value calculated is available as tt(ans). Full command line editing, including the history of previous calculations, is available; the history is saved in -the file tt(~/.zcalc_history). To exit, enter a blank line or type `tt(:q)' -on its own (`tt(q)' is allowed for historical compatibility). +the file tt($ZCALC_HISTFILE). If tt($ZCALC_HISTFILE) is unset, +tt($ZDOTDIR/.zcalc_history) is used instead, which in turn falls backs to +tt($HOME/.zcalc_history) if tt($ZDOTDIR) is unset. To exit, enter a blank +line or type `tt(:q)' on its own (`tt(q)' is allowed for historical +compatibility). A line ending with a single backslash is treated in the same fashion as it is in command line editing: the backslash is removed, the diff --git a/Functions/Misc/zcalc b/Functions/Misc/zcalc index 480373345..6cd2822c9 100644 --- a/Functions/Misc/zcalc +++ b/Functions/Misc/zcalc @@ -124,8 +124,10 @@ integer _rpn_mode _matched _show_stack _i _n integer _max_stack _push local -a _expressions stack + # We use our own history file with an automatic pop on exit. -history -ap "${ZDOTDIR:-$HOME}/.zcalc_history" +history -ap "${ZCALC_HISTFILE:-${ZDOTDIR:-$HOME}/.zcalc_history}" + _forms=( '%2$g' '%.*g' '%.*f' '%.*E' '') -- cgit v1.2.3 From b82e8e10355aba96cf3cf4e75bae71a6a3f8b235 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 17 Oct 2022 16:21:01 +0100 Subject: 50786: Make match etc. local when used in styles. Avoids side effects of add-zle-hook-widget. --- ChangeLog | 5 +++++ Functions/Misc/add-zle-hook-widget | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index cb47acd1b..61390fc9d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-10-17 Peter Stephenson + + * 50786: Functions/Misc/add-zle-hook-widget: make match + etc. local when used in styles. + 2022-10-17 Jun-ichi Takimoto * Wesley Schwengle: 50736: Src/Zle/compmatch.c: silence diff --git a/Functions/Misc/add-zle-hook-widget b/Functions/Misc/add-zle-hook-widget index 4d8049083..4293a07dd 100644 --- a/Functions/Misc/add-zle-hook-widget +++ b/Functions/Misc/add-zle-hook-widget @@ -39,7 +39,7 @@ zstyle zle-hook types ${hooktypes#zle-} # Relying on multifuncdef option here function azhw:${^hooktypes} { - local -a hook_widgets + local -a hook_widgets match mbegin mend local hook # Values of these styles look like number:name # and we run them in number order @@ -58,7 +58,7 @@ function azhw:${^hooktypes} { # Redefine ourself with the setup left out function add-zle-hook-widget { - local -a hooktypes + local -a hooktypes match mbegin mend zstyle -a zle-hook types hooktypes # This part copied from add-zsh-hook -- cgit v1.2.3 From f9bb03cd72cdfe878e6ef51ef292c3a6b2550b7d Mon Sep 17 00:00:00 2001 From: Atte Peltomäki Date: Tue, 6 Dec 2022 20:40:21 -0800 Subject: 51088: fix standards reference in comment --- ChangeLog | 4 ++++ Functions/Misc/colors | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index 8caeecd81..5e710bb7c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2022-12-06 Bart Schaefer + + * Atte Peltomäki: 51088: Functions/Misc/colors: typo in comment + 2022-12-03 Bart Schaefer * Philippe Altherr: 51094: Src/exec.c, Src/loop.c: consistent diff --git a/Functions/Misc/colors b/Functions/Misc/colors index 5c9924adb..8a0cec383 100644 --- a/Functions/Misc/colors +++ b/Functions/Misc/colors @@ -44,7 +44,7 @@ color=( 35 magenta 45 bg-magenta 36 cyan 46 bg-cyan 37 white 47 bg-white -# 38 iso-8316-6 # 48 bg-iso-8316-6 +# 38 iso-8613-6 # 48 bg-iso-8613-6 39 default 49 bg-default # Other codes: -- cgit v1.2.3 From dd13048b3b8cf710f44424ce9fedc2b56c31fde3 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 13 Mar 2023 19:46:39 -0700 Subject: 51572: fix "shift" error when running standalone --- ChangeLog | 2 ++ Functions/Misc/run-help | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index e80b09833..7ecc36a67 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2023-03-13 Bart Schaefer + * 51572: Functions/Misc/run-help: fix error when running standalone + * Sven Joachim: 51563: Completion/Debian/Command/_sbuild: unmatched " 2023-03-11 Bart Schaefer diff --git a/Functions/Misc/run-help b/Functions/Misc/run-help index d52c1b032..14d09bd65 100644 --- a/Functions/Misc/run-help +++ b/Functions/Misc/run-help @@ -98,13 +98,13 @@ do if whence "run-help-$1:t" >/dev/null then local cmd_args - builtin getln cmd_args + builtin getln cmd_args && builtin print -z "$cmd_args" - cmd_args=( ${(z)cmd_args} ) + cmd_args=( ${(z)${cmd_args:-"$*"}} ) # Discard the command itself & everything before it. shift $cmd_args[(i)${run_help_orig_cmd:-$1}] cmd_args || - return + continue # Discard options, parameter assignments & paths. cmd_args=( ${cmd_args[@]:#([-+]*|*=*|*/*|\~*)} ) -- cgit v1.2.3 From b2421219836d571b4f0ffa9568740922fa7005b8 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 10 May 2023 21:17:51 -0700 Subject: 51593: improve search for command name after skipping prefix assignments This is aimed mostly at use of run-help as a standalone function rather than as a widget. When run-help is invoked outside widget context, there's no source line to search for the original command name, so this attempts searching the arguments. --- ChangeLog | 5 +++++ Functions/Misc/run-help | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index e7e84a04b..956cb7eeb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-05-10 Bart Schaefer + + * 51593: Functions/Misc/run-help: improve search for original + command name after skipping prefix assignments + 2023-05-08 Jun-ichi Takimoto * 51692: Test/C02cond.ztst: do not skip tests for [[ -r file ]] diff --git a/Functions/Misc/run-help b/Functions/Misc/run-help index 14d09bd65..462044b72 100644 --- a/Functions/Misc/run-help +++ b/Functions/Misc/run-help @@ -58,11 +58,11 @@ do case $what in (*( is an alias for (noglob|nocorrect))*) [[ ${what[(w)7]:t} != ${what[(w)1]} ]] && - run_help_orig_cmd=${what[(w)1]} run-help ${what[(w)7]:t} + run_help_orig_cmd=${what[(w)1]} run-help ${what[(w)7]:t} ${(z)${what[(w)8,-1]}} ;; (*( is an alias)*) [[ ${what[(w)6]:t} != ${what[(w)1]} ]] && - run_help_orig_cmd=${what[(w)1]} run-help ${what[(w)6]:t} + run_help_orig_cmd=${what[(w)1]} run-help ${what[(w)6]:t} ${(z)${what[(w)7,-1]}} ;; (*( is a * function)) case ${what[(w)1]} in @@ -103,7 +103,7 @@ do cmd_args=( ${(z)${cmd_args:-"$*"}} ) # Discard the command itself & everything before it. - shift $cmd_args[(i)${run_help_orig_cmd:-$1}] cmd_args || + shift $cmd_args[(i)(${run_help_orig_cmd}|$1)] cmd_args 2>/dev/null || continue # Discard options, parameter assignments & paths. -- cgit v1.2.3 From 4f6a1b3717bb84b8243b13c4ec9171945893c934 Mon Sep 17 00:00:00 2001 From: Stephane Chazelas Date: Thu, 22 Jun 2023 12:52:12 -0700 Subject: 51813: differentiate empty $2 from omitted $2 in version comparisons --- ChangeLog | 7 ++++++- Functions/Misc/is-at-least | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index e89ffee1b..fc3818213 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-06-22 Bart Schaefer + + * Stephane: 51813: Functions/Misc/is-at-least: differentiate empty + $2 from omitted $2 to avoid wrong comparisons against ZSH_VERSION. + 2023-06-20 Jun-ichi Takimoto * 51877: Src/Modules/pcre.mdd, configure.ac: do not build pcre @@ -17,7 +22,7 @@ 2023-06-06 Peter Stephenson - * Stephan: 51818: Protect another ':'. + * Stephane: 51818: Protect another ':'. * Stephane: 51817: Protect some use of ':' from history modifier interpreation. diff --git a/Functions/Misc/is-at-least b/Functions/Misc/is-at-least index d4ff3552a..5985684be 100644 --- a/Functions/Misc/is-at-least +++ b/Functions/Misc/is-at-least @@ -24,8 +24,14 @@ emulate -L zsh local IFS=".-" min_cnt=0 ver_cnt=0 part min_ver version order +: ${2=$ZSH_VERSION} + +# sort out the easy corner cases first +[[ $1 = $2 ]] && return 0 # same version +[[ -n $2 ]] || return 1 # no version + min_ver=(${=1}) -version=(${=2:-$ZSH_VERSION} 0) +version=(${=2} 0) while (( $min_cnt <= ${#min_ver} )); do while [[ "$part" != <-> ]]; do -- cgit v1.2.3 From 2a854aae481e7eb064729db28a71ed0efb2f33e6 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 27 Aug 2023 15:22:14 -0700 Subject: 52028: improvements to _shadow / _unshadow, plus helper and doc --- ChangeLog | 4 +++ Completion/Base/Utility/_shadow | 66 ++++++++++++++++++++++++++++------------- Doc/Zsh/compsys.yo | 53 +++++++++++++++++++-------------- Doc/Zsh/contrib.yo | 21 +++++++++++++ Functions/Misc/mkshadow | 11 +++++++ 5 files changed, 112 insertions(+), 43 deletions(-) create mode 100644 Functions/Misc/mkshadow (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index bb6afe127..412dbda61 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-08-27 Bart Schaefer + * 52028: Completion/Base/Utility/_shadow, Doc/Zsh/compsys.yo, + Doc/Zsh/contrib.yo, Functions/Misc/mkshadow: improve _shadow + and _unshadow, add helper function and update documentation + * Robert Woods: 52053: Src/utils.c: whitelist capability CAP_WAKE_ALARM in 'privasserted' function diff --git a/Completion/Base/Utility/_shadow b/Completion/Base/Utility/_shadow index 5b0f79c36..b5a8acb24 100644 --- a/Completion/Base/Utility/_shadow +++ b/Completion/Base/Utility/_shadow @@ -8,7 +8,7 @@ # } # # Invoke callers of fname # } always { -# _unshadow fname +# _unshadow # } ## Alternate usage: # { @@ -19,7 +19,7 @@ # } # # Invoke callers of fname # } always { -# _unshadow -s suffix fname +# _unshadow # } ## @@ -33,36 +33,62 @@ zmodload zsh/parameter # Or what? # This probably never comes up, but protect ourself from recursive call # chains that may duplicate the top elements of $funcstack by creating # a counter of _shadow calls and using it to make shadow names unique. -typeset -gHi _shadowdepth=0 +builtin typeset -gHi .shadow.depth=0 +builtin typeset -gHa .shadow.stack # Create a copy of each fname so that a caller may redefine _shadow() { - local -A fsfx=( -s ${funcstack[2]}:${functrace[2]}:$((_shadowdepth+1)) ) - local fname + emulate -L zsh + local -A fsfx=( -s ${funcstack[2]}:${functrace[2]}:$((.shadow.depth+1)) ) + local fname shadowname + local -a fnames zparseopts -K -A fsfx -D s: for fname; do - local shadowname=${fname}@${fsfx[-s]} - (( ${+functions[$fname]} )) && - builtin functions -c $fname $shadowname + shadowname=${fname}@${fsfx[-s]} + if (( ${+functions[$shadowname]} )) + then + # Called again with the same -s, just ignore it + continue + elif (( ${+functions[$fname]} )) + then + builtin functions -c -- $fname $shadowname + fnames+=(f@$fname) + elif (( ${+builtins[$fname]} )) + then + eval "function -- $shadowname { builtin $fname \"\$@\" }" + fnames+=(b@$fname) + else + eval "function -- $shadowname { command $fname \"\$@\" }" + fnames+=(c@$fname) + fi done - ((_shadowdepth++)) + [[ -z $REPLY ]] && REPLY=${fsfx[-s]} + builtin set -A .shadow.stack ${fsfx[-s]} $fnames -- ${.shadow.stack} + ((.shadow.depth++)) } # Remove the redefined function and shadowing name _unshadow() { - local -A fsfx=( -s ${funcstack[2]}:${functrace[2]}:${_shadowdepth} ) - local fname - zparseopts -K -A fsfx -D s: - for fname; do - local shadowname=${fname}@${fsfx[-s]} - if (( ${+functions[$shadowname]} )); then - builtin functions -c $shadowname $fname - builtin unfunction $shadowname - elif (( ${+functions[$fname]} )); then - builtin unfunction $fname + emulate -L zsh + local fname shadowname fsfx=${.shadow.stack[1]} + local -a fnames + [[ -n $fsfx ]] || return 1 + shift .shadow.stack + while [[ ${.shadow.stack[1]?no shadows} != -- ]]; do + fname=${.shadow.stack[1]#?@} + shadowname=${fname}@${fsfx} + if (( ${+functions[$fname]} )); then + builtin unfunction -- $fname fi + case ${.shadow.stack[1]} in + (f@*) builtin functions -c -- $shadowname $fname ;& + ([bc]@*) builtin unfunction -- $shadowname ;; + esac + shift .shadow.stack done - ((_shadowdepth--)) + [[ -z $REPLY ]] && REPLY=$fsfx + shift .shadow.stack + ((.shadow.depth--)) } # This is tricky. When we call _shadow recursively from autoload, diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo index 33baeab49..3f708eb5a 100644 --- a/Doc/Zsh/compsys.yo +++ b/Doc/Zsh/compsys.yo @@ -5229,13 +5229,12 @@ and hence is not normally called explicitly. ) findex(_shadow) findex(_unshadow) -xitem(tt(_shadow) [ tt(-s) var(suffix) ] var(command_name) ...) -item(tt(_unshadow) [ tt(-s) var(suffix) ] var(command_name) ...)( +xitem(tt(_shadow) [ tt(-s) var(suffix) ] [ -- ] var(command_name) ...) +item(tt(_unshadow))( The tt(_shadow) function creates a copy of each of the shell functions in the var(command_name) arguments. The original functions can then -be replaced by new implementations. A later call to tt(_unshadow), -with the same var(command_name) list, removes the new implementations, -if any, and restores the originals. +be replaced by new implementations. A later call to tt(_unshadow) +removes the new implementations, if any, and restores the originals. Recommended usage is to pair tt(_shadow) and tt(_unshadow) calls by use of an `tt(always)' block: @@ -5246,30 +5245,38 @@ example({ } # Invoke callers of fname } always { - _unshadow fname + _unshadow }) -Any var(command_name) may instead be a builtin, but in that case no -copy is created. The expectation is that an initial tt(_shadow) is -followed by creating a wrapper function, and therafter any nested or -recursive calls thus copy and replace the wrapper function. +The var(suffix), if supplied, is prepended by an `tt(@)' character and +then appended to each var(command_name) to create the copy. Thus +example(_shadow -s XX foo) +creates a function named `tt(foo@XX)'. This provides a well-known +name for the original implementation if the new implementation needs +to call it as a wrapper. If a nested call to tt(_shadow) uses the +same var(suffix), em(no new copy is made). The presumption thus is +that suffixes and new implementations correspond one to one. + +If var(command_name) is a builtin or external command, and there has been +no preceding tt(_shadow) replacement made, the function so created calls +the shadowed name prefixed by the tt(builtin) or tt(command) keywords as +appropriate. example({ - _shadow compadd - compadd LPAR()RPAR() { builtin compadd -O tmparr "$@" } + _shadow -s wrap compadd + compadd LPAR()RPAR() { + # compadd@wrap runs builtin compadd + compadd@wrap -O tmparr "$@" } } always { - _unshadow compadd + _unshadow }) -The var(suffix), if supplied, is prepended by an `tt(@)' character and -then appended to each var(command_name) to create the copy. Thus -example(_shadow -s XX foo) -creates a function named `tt(foo@XX)' (unless `tt(foo)' is a builtin). -Note that a nested call to tt(_shadow) with the same var(suffix) may -result in name collisions and unexpected results, but this provides a -well-known name for the original function if the new implementation -needs to call it as a wrapper. The same var(suffix) must be used in -the call to tt(_unshadow). When no var(suffix) is present, -tt(_shadow) creates a unique suffix to avoid name collisions. +When no var(suffix) argument is present, tt(_shadow) creates a unique +suffix to avoid name collisions. + +Arguments of tt(_unshadow) are ignored. Every listed var(command_name) +for the most recent call to tt(_shadow) is removed. This differs from +an early implementation that required tt(_unshadow) to receive the +same var(suffix) and var(command_name) list as tt(_shadow). ) findex(_store_cache) item(tt(_store_cache) var(cache_identifier) var(param) ...)( diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index 96de5aa9b..ef11d77ad 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -4336,6 +4336,27 @@ example(is-at-least 3.1.6-15 && setopt NO_GLOBAL_RCS is-at-least 3.1.0 && setopt HIST_REDUCE_BLANKS is-at-least 2.6-17 || print "You can't use is-at-least here.") ) +findex(mkshadow) +findex(rmshadow) +xitem(tt(mkshadow) [ tt(-s) var(suffix) ] [ -- ] var(command_name) ...) +item(tt(rmshadow))( +These functions are an interface to the tt(_shadow) and tt(_unshadow) +completion utilities to make them more easily accessible in other +contexts. Usage is exactly as for the completion utility: +example({ + mkshadow fname + function fname { + # Do your new thing + } + # Invoke callers of fname +} always { + rmshadow +}) + +Upon return, the value of tt($REPLY) is the suffix used to create a +copy of the original var(command_name), so var(command_name)tt(@$REPLY) +invokes that original. +) findex(nslookup) item(tt(nslookup) [ var(arg) ... ])( This wrapper function for the tt(nslookup) command requires the diff --git a/Functions/Misc/mkshadow b/Functions/Misc/mkshadow new file mode 100644 index 000000000..2ae3a0f2c --- /dev/null +++ b/Functions/Misc/mkshadow @@ -0,0 +1,11 @@ +#autoload +# Front-end to the completion helper _shadow for use outside completion. +# This just forces proper autoload of _shadow/_unshadow and calls them. + +autoload _shadow +mkshadow() { unset REPLY; _shadow "$@" } +rmshadow() { unset REPLY; _unshadow } + +# Bootstrap because of autoload special case +unset REPLY +_shadow "$@" -- cgit v1.2.3 From f093b41f09ab18459a75c86c0a896aa36432803b Mon Sep 17 00:00:00 2001 From: Jörg Sommer Date: Tue, 23 May 2023 18:25:41 +0200 Subject: 51776: run-help-openssl: Reduce code and use new manpages Openssl switches the naming of manpages to the common style openssl-$SUBCOMMAND, e.g. openssl-enc. For backward compatibility try to show the manpage with the old name if the new one doesn't exist. --- ChangeLog | 3 +++ Functions/Misc/run-help-openssl | 8 +------- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index f03fd0d5c..eeb4cd618 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-09-20 Oliver Kiddle + * Jörg Sommer: 51776: Functions/Misc/run-help-openssl: + Reduce code and use new manpages + * Wim de With: 51857: Completion/Linux/Command/_fusermount: Include fusermount3 in fusermount completions diff --git a/Functions/Misc/run-help-openssl b/Functions/Misc/run-help-openssl index c528418c8..e4e45070e 100644 --- a/Functions/Misc/run-help-openssl +++ b/Functions/Misc/run-help-openssl @@ -1,7 +1 @@ - -if [ $# -eq 0 ]; then - man openssl -else - man $1 -fi - +man openssl${1:+-$1} || man ${1:-openssl} -- cgit v1.2.3 From 9eb2b047035273891c66f6d0fc23edb22d6bfad5 Mon Sep 17 00:00:00 2001 From: Jörg Sommer Date: Thu, 18 May 2023 00:12:45 +0200 Subject: 51747: ip accepts the reduction of link to l. --- ChangeLog | 3 +++ Functions/Misc/run-help-ip | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index eeb4cd618..e12ed92e0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-09-20 Oliver Kiddle + * Jörg Sommer: 51747: Functions/Misc/run-help-ip: + ip accepts the reduction of link to l + * Jörg Sommer: 51776: Functions/Misc/run-help-openssl: Reduce code and use new manpages diff --git a/Functions/Misc/run-help-ip b/Functions/Misc/run-help-ip index b811ce352..f635cce71 100644 --- a/Functions/Misc/run-help-ip +++ b/Functions/Misc/run-help-ip @@ -18,7 +18,7 @@ case $1 in (addrl*) man ip-addrlabel ;; (a*) man ip-address ;; (l2*) man ip-l2tp ;; - (li*) man ip-link ;; + (l*) man ip-link ;; (ma*) man ip-maddress ;; (mo*) man ip-monitor ;; (mr*) man ip-mroute ;; -- cgit v1.2.3 From 60479a7a180a837df4d973b5b9b50f4cd566bef9 Mon Sep 17 00:00:00 2001 From: Jörg Sommer Date: Wed, 31 May 2023 19:45:59 +0200 Subject: 51812: run-help for docker, perf, podman, ssh, svnadmin --- ChangeLog | 7 +++++++ Functions/Misc/run-help-docker | 9 +++++++++ Functions/Misc/run-help-perf | 1 + Functions/Misc/run-help-podman | 9 +++++++++ Functions/Misc/run-help-ssh | 6 ++++++ Functions/Misc/run-help-svnadmin | 1 + 6 files changed, 33 insertions(+) create mode 100644 Functions/Misc/run-help-docker create mode 100644 Functions/Misc/run-help-perf create mode 100644 Functions/Misc/run-help-podman create mode 100644 Functions/Misc/run-help-ssh create mode 100644 Functions/Misc/run-help-svnadmin (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index b7d49ace2..a54ffdfe2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2023-10-10 Oliver Kiddle + + * Jörg Sommer: 51812: Functions/Misc/run-help-docker, + Functions/Misc/run-help-perf, Functions/Misc/run-help-podman, + Functions/Misc/run-help-ssh, Functions/Misc/run-help-svnadmin: + run-help for docker, perf, podman, ssh, svnadmin + 2023-10-05 Bart Schaefer * 52204: Src/parse.c: fix unmeta() thinko from 52193 diff --git a/Functions/Misc/run-help-docker b/Functions/Misc/run-help-docker new file mode 100644 index 000000000..09a8a221a --- /dev/null +++ b/Functions/Misc/run-help-docker @@ -0,0 +1,9 @@ +if [[ $# == 0 ]] +then + man docker +elif [[ $# > 1 && $1 == (builder|checkpoint|config|container|context|image|manifest|network|node|plugin|secret|service|stack|swarm|system|trust|volume) ]] +then + man docker-$1-$2 +else + man docker-$1 +fi diff --git a/Functions/Misc/run-help-perf b/Functions/Misc/run-help-perf new file mode 100644 index 000000000..2e0695af2 --- /dev/null +++ b/Functions/Misc/run-help-perf @@ -0,0 +1 @@ +man perf${1:+-$1} diff --git a/Functions/Misc/run-help-podman b/Functions/Misc/run-help-podman new file mode 100644 index 000000000..64d9cd83f --- /dev/null +++ b/Functions/Misc/run-help-podman @@ -0,0 +1,9 @@ +if [[ $# == 0 ]] +then + man podman +elif [[ $# > 1 && $1 == (container|generate|healthcheck|image|kube|machine|manifest|network|pod|secret|system|volume) ]] +then + man podman-$1-$2 +else + man podman-$1 +fi diff --git a/Functions/Misc/run-help-ssh b/Functions/Misc/run-help-ssh new file mode 100644 index 000000000..9c48596ff --- /dev/null +++ b/Functions/Misc/run-help-ssh @@ -0,0 +1,6 @@ +if [[ $# < 2 ]] +then + man ssh +else + run-help $2 +fi diff --git a/Functions/Misc/run-help-svnadmin b/Functions/Misc/run-help-svnadmin new file mode 100644 index 000000000..dbddd6396 --- /dev/null +++ b/Functions/Misc/run-help-svnadmin @@ -0,0 +1 @@ +svnadmin help $1 | ${=PAGER:-more} -- cgit v1.2.3 From a920e368b87f24e95b015eb091e1112c378c2939 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 11 Oct 2023 00:37:35 +0200 Subject: unposted (cf. 52166): remove obsolete helper for svk --- ChangeLog | 3 +++ Functions/Misc/run-help-svk | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) delete mode 100644 Functions/Misc/run-help-svk (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index a54ffdfe2..3e9b6ffa0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-10-10 Oliver Kiddle + * unposted (cf. 52166): Functions/Misc/run-help-svk: + remove obsolete helper for svk + * Jörg Sommer: 51812: Functions/Misc/run-help-docker, Functions/Misc/run-help-perf, Functions/Misc/run-help-podman, Functions/Misc/run-help-ssh, Functions/Misc/run-help-svnadmin: diff --git a/Functions/Misc/run-help-svk b/Functions/Misc/run-help-svk deleted file mode 100644 index 782538246..000000000 --- a/Functions/Misc/run-help-svk +++ /dev/null @@ -1 +0,0 @@ -svk help $1 | ${=PAGER:-more} -- cgit v1.2.3 From d6e4ddd4d48b6ac9c0a29b95e0e2fc0e6012d725 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Fri, 5 Jan 2024 20:38:58 -0800 Subject: 52465: use NULL_GLOB when expanding zmv input pattern to avoid NOMATCH exit --- ChangeLog | 5 +++++ Doc/Zsh/contrib.yo | 8 ++++---- Functions/Misc/zmv | 8 +++++++- 3 files changed, 16 insertions(+), 5 deletions(-) (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index e7b785c7f..1c7e7786f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-01-05 Bart Schaefer + + * 52465: Doc/Zsh/contrib.yo, Functions/Misc/zmv: use NULL_GLOB + when expanding the input pattern to avoid NOMATCH exit + 2023-12-06 Jun-ichi Takimoto * 52413: Completion/Unix/Command/_iconv: support Citrus version diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index f43ac2257..e1781a5e1 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -4668,10 +4668,10 @@ renames `tt(foo.lis)' to `tt(foo.txt)', `tt(my.old.stuff.lis)' to The pattern is always treated as an tt(EXTENDED_GLOB) pattern. Any file whose name is not changed by the substitution is simply ignored. Any -error (a substitution resulted in an empty string, two substitutions gave -the same result, the destination was an existing regular file and tt(-f) -was not given) causes the entire function to abort without doing -anything. +error (no files matched the var(srcpat), substitution resulted in an empty +string, two substitutions gave the same result, the destination was an +existing regular file and tt(-f) was not given) causes the entire function +to abort without doing anything. In addition to pattern replacement, the variable tt($f) can be referred to in the second (replacement) argument. This makes it possible to diff --git a/Functions/Misc/zmv b/Functions/Misc/zmv index 269fe5ba5..5c03e9ea1 100644 --- a/Functions/Misc/zmv +++ b/Functions/Misc/zmv @@ -236,12 +236,18 @@ if [[ $pat = (#b)(*)\((\*\*##/)\)(*) ]]; then else fpat=$pat fi -files=(${~fpat}) [[ -n $hasglobqual ]] && pat=$opat errs=() +() { + # (#qN) breaks bareglobqual -Q option, so: + setopt localoptions nullglob + files=(${~fpat}) +} +(( ${#files} )) || errs=( "no files matched \`$fpat'" ) + for f in $files; do if [[ $pat = (#b)(*)\(\*\*##/\)(*) ]]; then # This looks like a recursive glob. This isn't good enough, -- cgit v1.2.3 From 2744208ab3eab1741157e01484d08095bd8ef528 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 3 Feb 2024 20:10:52 -0800 Subject: unposted: elaboration on Roman's "slurp" implementation from zsh-users --- ChangeLog | 3 +++ Functions/Misc/zslurp | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 Functions/Misc/zslurp (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index 386ef3ab9..238c12c16 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-02-03 Bart Schaefer + * unposted: cf. Roman in users/29472: Functions/Misc/zslurp: + Efficient lossless read of stdin into $REPLY + * unposted: Src/Zle/zle_tricky.c, Src/parse.c, Src/pattern.c: Record as comments some notes about namespace usage exceptions. diff --git a/Functions/Misc/zslurp b/Functions/Misc/zslurp new file mode 100644 index 000000000..84df0c948 --- /dev/null +++ b/Functions/Misc/zslurp @@ -0,0 +1,31 @@ +#!/bin/zsh -f + +# Read stdin verbatim and as efficiently as possible into $REPLY, +# stopping without any change to $REPLY in the event of any error. +# Benchmarked by Roman Perepelitsa in zsh-users/29472 + +# Although this function faithfully records the input stream, later +# references to $REPLY with the multibyte option back in effect will +# (re-)interpret the content as multibyte characters. This may not be +# what is desired. + +emulate -L zsh -o no_multibyte + +### Alternate formulation, faster on bigger files +# # /dev/fd/0 is treated specially by -f so also check /dev/fd +# if [[ -d /dev/fd && -f /dev/fd/0 ]] && zmodload zsh/mapfile +# then +# local +h -Ar mapfile +# typeset -g REPLY="${mapfile[/dev/fd/0]}" && return +# fi +# # else fall through to read from pipe/socket + +zmodload zsh/system || return +local -a content +local -i i=0 +while true; do + sysread 'content[++i]' && continue + (( $? == 5 )) || return + break +done +typeset -g REPLY=${(j::)content} -- cgit v1.2.3 From 300ce96080b0679038db946ef34ac5c2d26646b5 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Fri, 24 May 2024 19:24:23 -0700 Subject: 52910: Improve handling of **/ patterns --- ChangeLog | 2 ++ Functions/Misc/zmv | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index 642b40821..7f3412f8e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2024-05-24 Bart Schaefer + * 52910: Functions/Misc/zmv: Improve handling of **/ patterns + * 52904: Completion/Unix/Command/_git: Improve quoting of paths passed via _call_program to "git ls-files" diff --git a/Functions/Misc/zmv b/Functions/Misc/zmv index 5c03e9ea1..2002af5a6 100644 --- a/Functions/Misc/zmv +++ b/Functions/Misc/zmv @@ -249,13 +249,13 @@ errs=() (( ${#files} )) || errs=( "no files matched \`$fpat'" ) for f in $files; do - if [[ $pat = (#b)(*)\(\*\*##/\)(*) ]]; then + if [[ $pat = (#b)(*)(\(\*\*##/\)|\*\*##/)(*) ]]; then # This looks like a recursive glob. This isn't good enough, - # because we should really enforce that $match[1] and $match[2] + # because we should really enforce that $match[1] and $match[3] # don't match slashes unless they were explicitly given. But # it's a start. It's fine for the classic case where (**/) is # at the start of the pattern. - pat="$match[1](*/|)$match[2]" + pat="$match[1](*/|)$match[3]" fi [[ -e $f && $f = (#b)${~pat} ]] || continue set -- "$match[@]" -- cgit v1.2.3 From 84ef0c523878625feeed8cd0a5c142929d8b4d06 Mon Sep 17 00:00:00 2001 From: dana Date: Sun, 27 Apr 2025 07:58:23 -0500 Subject: 53516: add zgetopt contrib function --- ChangeLog | 3 + Doc/Zsh/contrib.yo | 78 +++++++++++++++++++ Functions/Misc/zgetopt | 198 +++++++++++++++++++++++++++++++++++++++++++++++ NEWS | 5 +- Test/Z04zgetopt.ztst | 206 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 489 insertions(+), 1 deletion(-) create mode 100755 Functions/Misc/zgetopt create mode 100644 Test/Z04zgetopt.ztst (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index 57050830f..de8fc6f3d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2025-04-27 dana + * 53516: Doc/Zsh/contrib.yo, Functions/Misc/zgetopt, NEWS, + Test/Z04zgetopt.ztst: add zgetopt contrib function + * 53515: Test/B01cd.ztst: correct ztst documentation error 2025-04-20 Eric Cook diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index c1bea6022..030b63029 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -4672,6 +4672,84 @@ Same as tt(zmv -C) and tt(zmv -L), respectively. These functions do not appear in the zsh distribution, but can be created by linking tt(zmv) to the names tt(zcp) and tt(zln) in some directory in your tt(fpath). ) +findex(zgetopt) +item(tt(zgetopt) [ tt(-a) ] [ tt(-A) var(array) ] [ tt(-l) var(spec) ] [ tt(-n) var(name) ] [ tt(-o) var(spec) ] tt(--) [ var(args) ])( +This is a wrapper around tt(zparseopts) (from tt(zsh/zutil)) which +provides an interface similar to the util-linux implementation of +tt(getopt+LPAR()1+RPAR()) (sometimes called `GNU tt(getopt)'). It +simplifies GNU-style argument parsing (including permutation) and +can make it easier to write functions and scripts with complex APIs, +particularly ones where the order of options is significant. + +The typical usage pattern is as follows: + +example(zgetopt -o abc: -l aaa,bbb,ccc: -- "$@" || return +while (( $# )); do + case $1 in + -a|--aaa+RPAR() ...; shift ;; # handle -a + -b|--bbb+RPAR() ...; shift ;; # handle -b + -c|--ccc+RPAR() ...; shift 2 ;; # handle -c and arg + --+RPAR() ...; shift; break ;; # end of options + esac +done +# handle operands) + +It can also be called as a stand-alone script from other shells +using the more traditional print-and-eval pattern: + +example(args="$( zgetopt -n myscript -o abc: -l aaa,bbb,ccc: -- "$@" )" || return +eval set -- "$args" +while [ $# -ne 0 ]; do ...; done) + +Options: + +startsitem() +sitem(tt(-A var(array)))(When called as a function, assign the parsed +arguments to the named array var(array). Defaults to tt(argv), which +overwrites the caller's positional parameters. Has no meaning when +called as a script, in which case the parsed and quoted arguments are +always printed to standard output. An empty string forces the +printing behaviour in either mode.) +sitem(tt(-a))(Use Sun-style single-hyphenated long options instead of +GNU-style double-hyphenated ones (tt(-foo) vs tt(--foo)). Note that +long options with optional optargs can't always be distinguished +accurately from short options with optional optargs when using this +option. Also, due to limitations of tt(zparseopts), a Sun-style long +option whose name is only one character long is always treated as a +short option.) +sitem(tt(-l var(spec)))(Specify long options to recognise when +parsing. These should be given using just the option name (no +dashes), suffixed by `tt(:)' or `tt(::)' if it takes a mandatory or +optional argument respectively. Multiple options can be defined +either by separating them by commas or by supplying -l again. +Example: tt(-l foo,bar: -l baz)) +sitem(tt(-n var(name)))(Specify the name to use in the error message +if argument parsing fails. Defaults to the name of the nearest +calling function or the base name of tt($ZSH_ARGZERO). Note that +errors related to the usage of tt(zgetopt) itself are always reported +as coming from tt(zgetopt).) +sitem(tt(-o var(spec)))(Specify short options to recognise when +parsing. These should be given as a single string, in the same format +used by the tt(getopts) built-in or the tt(getopt+LPAR()3+RPAR()) +library function, again using `tt(:)' or `tt(::)' to indicate a +mandatory or optional argument. The spec may be prefixed with `tt(+)' +to indicate that option parsing should stop at the first non-option +argument (equivalent to setting the environment variable +tt(POSIXLY_CORRECT)). Example: tt(-o ab:cd::)) +endsitem() + +At least one of tt(-o) or tt(-l) must be given. The function's own +options should be followed by zero or more arguments to parse. It is +critical that these be separated explicitly by `tt(--)', as in the +above examples, to ensure that the function can accurately +distinguish the arguments it's meant to parse from its own. + +Refer to the manual for util-linux's tt(getopt+LPAR()1+RPAR()) for +more information about the way arguments are parsed and results are +returned. Note however that this function is not intended to be a +complete re-implementation. In particular, it omits all +portability/compatibility features. +) item(tt(zkbd))( See `Keyboard Definition' ifzman(above)\ diff --git a/Functions/Misc/zgetopt b/Functions/Misc/zgetopt new file mode 100755 index 000000000..5fc1e7725 --- /dev/null +++ b/Functions/Misc/zgetopt @@ -0,0 +1,198 @@ +#!/bin/zsh -f + +# Wrapper around zparseopts which gives it an interface similar to util-linux's +# getopt(1). See zshcontrib(1) for documentation + +emulate -L zsh -o extended_glob +zmodload -i zsh/zutil || return 3 + +# Very stupid and brittle internal wrapper around zparseopts used to insert the +# caller name into its error messages, allowing us to implement --name. This +# MUST be called with -v, since argv has the options to zparseopts itself +__zgetopt_zparseopts() { + local __err __ret + + __err=$( zparseopts "$@" 2>&1 ) + __ret=$? + + zparseopts "$@" &> /dev/null && return + + # Raw error message should look like this: + # zgetopt_zparseopts:zparseopts:3: bad option: -x + [[ -n $__err ]] && print -ru2 - ${__err/#*:zparseopts:<->:/$name:} + return __ret +} + +local optspec pat i posix=0 +local -a match mbegin mend optvv argvv +local -a array alt lopts sopts name +local -a specs no_arg_opts req_arg_opts opt_arg_opts tmp + +# Same as leading + in short-opts spec +(( $+POSIXLY_CORRECT )) && posix=1 + +# This 0=... makes any error message we get here look a little nicer when we're +# called as a script. Unfortunately the function name overrides $0 in +# zwarnnam() in other scenarios, so this can't be used to implement --name +0=${0:t} zparseopts -D -F -G - \ + {A,-array}:-=array \ + {a,-alternative}=alt \ + {l,-longoptions,-long-options}+:-=lopts \ + {n,-name}:-=name \ + {o,-options}:-=sopts \ +|| { + print -ru2 "usage: ${0:t} [-A ] [-a] [-l ] [-n ] [-o ] -- " + return 2 +} + +# Default to the caller's name +(( $#name )) && name=( "${(@)name/#(-n|--name=)/}" ) +[[ -n $name ]] || name=( ${funcstack[2]:-${ZSH_ARGZERO:t}} ) + +(( $#array )) && array=( "${(@)array/#(-A|--array=)/}" ) + +if [[ $ZSH_EVAL_CONTEXT != toplevel ]]; then + [[ $array == *[^A-Za-z0-9_.]* ]] && { + print -ru2 - "${0:t}: invalid array name: $array" + return 2 + } + (( $#array )) || array=( argv ) + +elif [[ -n $array ]]; then + print -ru2 - "${0:t}: -A option not meaningful unless called as function" + return 2 +fi + +# getopt requires a short option spec; we'll require either short or long +(( $#sopts || $#lopts )) || { + print -ru2 - "${0:t}: missing option spec" + return 2 +} + +optspec=${(@)sopts/#(-o|--options=)/} +sopts=( ) + +for (( i = 1; i <= $#optspec; i++ )); do + # Leading '+': Act POSIXLY_CORRECT + if [[ $i == 1 && $optspec[i] == + ]]; then + posix=1 + # Leading '-': Should leave operands interspersed with options, but this is + # not really possible with zparseopts + elif [[ $i == 1 && $optspec[i] == - ]]; then + print -ru2 - "${0:t}: optspec with leading - (disable operand collection) not supported" + return 2 + # Special characters: [+=\\] because they're special to zparseopts, ':' + # because it's special to getopt, '-' because it's the parsing terminator + elif [[ $optspec[i] == [+:=\\-] ]]; then + print -ru2 - "${0:t}: invalid short-option name: $optspec[i]" + return 2 + # 'a' + elif [[ $optspec[i+1] != : ]]; then + sopts+=( $optspec[i] ) + # 'a:' + elif [[ $optspec[i+2] != : ]]; then + sopts+=( $optspec[i]: ) + (( i += 1 )) + # 'a::' + elif [[ $optspec[i+3] != : ]]; then + sopts+=( $optspec[i]:: ) + (( i += 2 )) + fi +done + +lopts=( ${(@)lopts/#(-l|--long(|-)options=)/} ) +lopts=( ${(@s<,>)lopts} ) + +# Don't allow characters that are special to zparseopts in long-option specs. +# See above +pat='(*[+=\\]*|:*|*:::##|*:[^:]*)' +[[ -n ${(@M)lopts:#$~pat} ]] && { + print -ru2 - "${0:t}: invalid long-option spec: ${${(@M)lopts:#$~pat}[1]}" + return 2 +} + +(( $#alt )) || lopts=( ${(@)lopts/#/-} ) + +specs=( $sopts $lopts ) + +# Used below to identify options with optional optargs +no_arg_opts=( ${(@)${(@M)specs:#*[^:]}/#/-} ) +req_arg_opts=( ${(@)${(@)${(@M)specs:#*[^:]:}/#/-}/%:#} ) +opt_arg_opts=( ${(@)${(@)${(@M)specs:#*::}/#/-}/%:#} ) + +# getopt returns all instances of each option given, so add + +specs=( ${(@)specs/%(#b)(:#)/+$match[1]} ) + +# POSIXLY_CORRECT: Stop parsing options after first non-option argument +if (( posix )); then + tmp=( "$@" ) + __zgetopt_zparseopts -D -F -G -a optvv -v tmp - $specs || return 1 + argvv=( "${(@)tmp}" ) + +# Default: Permute options following non-option arguments +else + tmp=( "$@" ) + __zgetopt_zparseopts -D -E -F -G -a optvv -v tmp - $specs || return 1 + argv=( "${(@)tmp}" ) + # -D + -E leaves an explicit -- in argv where-ever it might appear + local seen + while (( $# )); do + [[ -z $seen && $1 == -- ]] && seen=1 && shift && continue + argvv+=( "$1" ) + shift + done +fi + +# getopt outputs all optargs as separate parameters, even missing optional ones, +# so we scan through and add/separate those if needed. This can't be perfectly +# accurate if Sun-style (-a) long options are used with optional optargs -- e.g. +# if you have specs a:: and abc::, then argument -abc=d is ambiguous. We don't +# guarantee which one is prioritised +(( $#opt_arg_opts )) && { + local cur next + local -a old_optvv=( "${(@)optvv}" ) + optvv=( ) + + for (( i = 1; i <= $#old_optvv; i++ )); do + cur=$old_optvv[i] + next=$old_optvv[i+1] + # Option with no optarg + if [[ -n ${no_arg_opts[(r)$cur]} ]]; then + optvv+=( $cur ) + # Option with required optarg -- will appear in next element + elif [[ -n ${req_arg_opts[(r)$cur]} ]]; then + optvv+=( $cur "$next" ) + (( i++ )) + # Long option with optional optarg -- will appear in same element delimited + # by '=' (even if missing) + elif [[ $cur == *=* && -n ${opt_arg_opts[(r)${cur%%=*}]} ]]; then + optvv+=( ${cur%%=*} "${cur#*=}" ) + # Short option with optional optarg -- will appear in same element with no + # delimiter (thus the option appears alone if the optarg is missing) + elif [[ -n ${opt_arg_opts[(r)${(M)cur#-?}]} ]]; then + optvv+=( ${(M)cur#-?} "${cur#-?}" ) + # ??? + else + print -ru2 - "${0:t}: parse error, please report!" + print -ru2 - "${0:t}: specs: ${(j< >)${(@q+)specs}}" + print -ru2 - "${0:t}: old_optvv: ${(j< >)${(@q+)old_optvv}}" + print -ru2 - "${0:t}: cur: $cur" + optvv+=( $cur ) # I guess? + fi + done +} + +if [[ -n $array ]]; then + # Use EXIT trap to assign in caller's context + trap "$array=( ${(j< >)${(@q+)optvv}} -- ${(j< >)${(@q+)argvv}} )" EXIT + +elif [[ $ZSH_EVAL_CONTEXT != toplevel ]]; then + print -r - "${(@q+)optvv}" -- "${(@q+)argvv}" + +# If called as a script, use unconditional single-quoting. This is ugly but it's +# the closest to what getopt does and it offers compatibility with legacy shells +else + print -r - "${(@qq)optvv}" -- "${(@qq)argvv}" +fi + +return 0 diff --git a/NEWS b/NEWS index 7c5e1e06b..a1e74b9fb 100644 --- a/NEWS +++ b/NEWS @@ -51,7 +51,7 @@ result via assignment to the named param rather than always via $REPLY. The shell now uses monotonic time instead of wall time for most internal time tracking, making it immune to system clock changes due to NTP, etc. For the most part this is transparent to users. However, as a -side effect, some features like $SECONDS and the time builtin gained +side effect, some features like $SECONDS and the time keyword gained (nominal) nanosecond precision. The zsh/zutil module's zparseopts builtin learnt a -v option which can @@ -60,6 +60,9 @@ be used to specify the array of arguments to parse instead of $@. The zparseopts builtin also learnt a -G option which enables GNU-style argument parsing ('--opt=arg', etc.). +A new contrib function zgetopt was added. It wraps `zparseopts -G` to +provide an interface similar to util-linux's getopt(1). + The module zsh/pcre has been updated to use the pcre2 library. The new zsh/random module defines an SRANDOM parameter, zrand_float() diff --git a/Test/Z04zgetopt.ztst b/Test/Z04zgetopt.ztst new file mode 100644 index 000000000..c2bc22be0 --- /dev/null +++ b/Test/Z04zgetopt.ztst @@ -0,0 +1,206 @@ +%prep + + autoload -Uz zgetopt + +%test + + zgetopt -A '' -- a b c + zgetopt -A '' -o '' -- a b c + zgetopt -A '' -l '' -- a b c +0:-o or -l required +?zgetopt: missing option spec +>-- a b c +>-- a b c + + zgetopt -A '' -o - -- a b c + zgetopt -A '' -o -a -- a b c + zgetopt -A '' -o a- -- a b c + zgetopt -A '' -o a+ -- a b c + zgetopt -A '' -o a= -- a b c + zgetopt -A '' -o a\\ -- a b c + zgetopt -A '' -o :a -- a b c + zgetopt -A '' -o a::: -- a b c + zgetopt -A '' -o '' -- a b c + zgetopt -A '' -o + -- a b c +0:weird short-option specs +?zgetopt: optspec with leading - (disable operand collection) not supported +?zgetopt: optspec with leading - (disable operand collection) not supported +?zgetopt: invalid short-option name: - +?zgetopt: invalid short-option name: + +?zgetopt: invalid short-option name: = +?zgetopt: invalid short-option name: \ +?zgetopt: invalid short-option name: : +?zgetopt: invalid short-option name: : +>-- a b c +>-- a b c + + zgetopt -A '' -l a,+ -- a b c + zgetopt -A '' -l a,= -- a b c + zgetopt -A '' -l a,\\ -- a b c + zgetopt -A '' -l a,: -- a b c + zgetopt -A '' -l a,:b -- a b c + zgetopt -A '' -l a,b:b -- a b c + zgetopt -A '' -l a,b::: -- a b c + zgetopt -A '' -l '' -- a b c + zgetopt -A '' -l , -- a b c + zgetopt -A '' -l a,,,,,b -- a b c + zgetopt -A '' -l - -- a b c --- +0:weird long-option specs +?zgetopt: invalid long-option spec: + +?zgetopt: invalid long-option spec: = +?zgetopt: invalid long-option spec: \ +?zgetopt: invalid long-option spec: : +?zgetopt: invalid long-option spec: :b +?zgetopt: invalid long-option spec: b:b +?zgetopt: invalid long-option spec: b::: +>-- a b c +>-- a b c +>-- a b c +>--- -- a b c + + zgetopt -A '' -o ab:c:: -- a b c + zgetopt -A '' -o ab:c:: -- -a + zgetopt -A '' -o ab:c:: -- -a a b c + zgetopt -A '' -o ab:c:: -- -a a -b c + zgetopt -A '' -o ab:c:: -- -a a -b -c + zgetopt -A '' -o ab:c:: -- -a a -b -c d + zgetopt -A '' -o ab:c:: -- -a a -b -c -c + zgetopt -A '' -o ab:c:: -- -a a -b -c -c d + zgetopt -A '' -o ab:c:: -- -a a -b -c -cd +0:short options +>-- a b c +>-a -- +>-a -- a b c +>-a -b c -- a +>-a -b -c -- a +>-a -b -c -- a d +>-a -b -c -c '' -- a +>-a -b -c -c '' -- a d +>-a -b -c -c d -- a + + zgetopt -A '' -l aaa,bbb:,ccc:: -- a b c + zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa + zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a b c + zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb c + zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb=c + zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb --ccc + zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb --ccc d + zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb --ccc --ccc + zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb --ccc --ccc d + zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb --ccc --ccc=d +0:long options +>-- a b c +>--aaa -- +>--aaa -- a b c +>--aaa --bbb c -- a +>--aaa --bbb c -- a +>--aaa --bbb --ccc -- a +>--aaa --bbb --ccc -- a d +>--aaa --bbb --ccc --ccc '' -- a +>--aaa --bbb --ccc --ccc '' -- a d +>--aaa --bbb --ccc --ccc d -- a + + zgetopt -A '' -al aaa,bbb:,ccc:: -- a b c + zgetopt -A '' -al aaa,bbb:,ccc:: -- --aaa a b c + zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa + zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a b c + zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb c + zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb=c + zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb -ccc + zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb -ccc d + zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb -ccc -ccc + zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb -ccc -ccc d + zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb -ccc -ccc=d +0:long options with -a (Sun style) +>-- a b c +?(eval): bad option: --aaa +>-aaa -- +>-aaa -- a b c +>-aaa -bbb c -- a +>-aaa -bbb c -- a +>-aaa -bbb -ccc -- a +>-aaa -bbb -ccc -- a d +>-aaa -bbb -ccc -ccc '' -- a +>-aaa -bbb -ccc -ccc '' -- a d +>-aaa -bbb -ccc -ccc d -- a + + zgetopt -A '' -al a: -- -a=b +0:single-character long option with -a +>-a '=b' -- + + zgetopt -A '' -o '' +0:zero args to parse +>-- + + zgetopt -A '' -o '' -- -- a b c + zgetopt -A '' -o '' -- a b -- c + zgetopt -A '' -o '' -- a b c -- + zgetopt -A '' -o c -- a b -- -c + zgetopt -A '' -o c -- a b - -c +0:parsing terminator +>-- a b c +>-- a b c +>-- a b c +>-- a b -c +>-c -- a b - + + zgetopt -A '' -o a -- a -a b + zgetopt -A '' -o +a -- a -a b + POSIXLY_CORRECT=1 zgetopt -A '' -o a -- a -a b +0:POSIXLY_CORRECT +>-a -- a b +>-- a -a b +>-- a -a b + + zgetopt -A '' -o '' -- $'\a\'\a' +0:function-mode quoting style +>-- $'\C-G\'\C-G' + + zgetopt -A '' -o '' -- a -a b + zgetopt -A '' -o '' -- a --a b +1:bad options +?(eval): bad option: -a +?(eval): bad option: --a + + zgetopt -A '' ; echo $? # missing spec + zgetopt -A '' -o '' -x ; echo $? # bad option to zgetopt + zgetopt -A '' -o '' -- -y; echo $? # bad option to parse +0:return status +*?zgetopt: missing option spec +*>2 +*?zgetopt:zparseopts:*: bad option: -x +*?usage:* +*>2 +*?\(eval\): bad option: -y +*>1 + + () { zgetopt -o a -- "$@"; typeset -p argv } -a b c + () { local -a v; zgetopt -A v -o a -- "$@"; typeset -p argv v } -a b c +0:array output +>typeset -g -a argv=( -a -- b c ) +>typeset -g -a argv=( -a b c ) +>typeset -a v=( -a -- b c ) + + zgetopt -A '' -o a: -- -x + zgetopt -A '' -o a: -- -a + () { zgetopt -A '' -o a: -- "$@"; : } -x + func() { zgetopt -A '' -o a: -- "$@"; : }; func -x + f1() { zgetopt -A '' -o a: -- "$@"; : }; f2() { f1 "$@" }; f2 -x +0:automatic name +?(eval): bad option: -x +?(eval): missing argument for option: -a +?(anon): bad option: -x +?func: bad option: -x +?f1: bad option: -x + + zgetopt -n aaa -A '' -o a: -- -x + zgetopt -n aaa -A '' -o a: -- -a + () { zgetopt -n bbb -A '' -o a: -- "$@"; : } -x + func() { zgetopt -n ccc -A '' -o a: -- "$@"; : }; func -x + f1() { zgetopt -n ddd -A '' -o a: -- "$@"; : }; f2() { f1 "$@" }; f2 -x +0:manual name with -n +?aaa: bad option: -x +?aaa: missing argument for option: -a +?bbb: bad option: -x +?ccc: bad option: -x +?ddd: bad option: -x -- cgit v1.2.3 From 80de57d5a63870e61a3d48efa1406ac05bbd2b5b Mon Sep 17 00:00:00 2001 From: dana Date: Mon, 28 Apr 2025 16:18:42 -0500 Subject: 53527: remove zgetopt reverts most of 84ef0c523, 0e369f37d, and 9b68cf38f feature was not ready. may be re-added after 5.10 release --- ChangeLog | 5 ++ Doc/Zsh/contrib.yo | 79 ------------------- Functions/Misc/zgetopt | 198 ----------------------------------------------- NEWS | 3 - Test/Z04zgetopt.ztst | 206 ------------------------------------------------- 5 files changed, 5 insertions(+), 486 deletions(-) delete mode 100755 Functions/Misc/zgetopt delete mode 100644 Test/Z04zgetopt.ztst (limited to 'Functions/Misc') diff --git a/ChangeLog b/ChangeLog index 441a5dd5a..cdcf5d5e8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2025-04-28 dana + + * 53527: Doc/Zsh/contrib.yo, Functions/Misc/zgetopt, NEWS, + Test/Z04zgetopt.ztst: remove zgetopt (feature not ready) + 2025-04-27 dana * unposted: Doc/Zsh/contrib.yo, Doc/Zsh/mod_zutil.yo: diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index 7822460e8..c1bea6022 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -4672,85 +4672,6 @@ Same as tt(zmv -C) and tt(zmv -L), respectively. These functions do not appear in the zsh distribution, but can be created by linking tt(zmv) to the names tt(zcp) and tt(zln) in some directory in your tt(fpath). ) -findex(zgetopt) -item(tt(zgetopt) [ tt(-a) ] [ tt(-A) var(array) ] [ tt(-l) var(spec) ] [ tt(-n) var(name) ] [ tt(-o) var(spec) ] tt(--) [ var(args) ])( -This is a wrapper around tt(zparseopts) (from tt(zsh/zutil)) which -provides an interface similar to the util-linux implementation of -tt(getopt+LPAR()1+RPAR()) (sometimes called `GNU tt(getopt)'). It -simplifies GNU-style argument parsing (including permutation) and -can make it easier to write functions and scripts with complex APIs, -particularly ones where the order of options is significant. - -The typical usage pattern is as follows: - -example(zgetopt -o abc: -l aaa,bbb,ccc: -- "$@" || return -while (( $# )); do - case $1 in - -a|--aaa+RPAR() ...; shift ;; # handle -a - -b|--bbb+RPAR() ...; shift ;; # handle -b - -c|--ccc+RPAR() ...; shift 2 ;; # handle -c and arg - --+RPAR() ...; shift; break ;; # end of options - esac -done -# handle operands) - -It can also be called as a stand-alone script from other shells -using the more traditional print-and-eval pattern: - -example(args="$( zgetopt -n myscript -o abc: -l aaa,bbb,ccc: -- "$@" )" || return -eval set -- "$args" -while [ $# -ne 0 ]; do ...; done) - -Options: - -startsitem() -sitem(tt(-A var(array)))(When called as a function, assign the parsed -arguments to the named array var(array). Defaults to tt(argv), which -overwrites the caller's positional parameters. Has no meaning when -called as a script, in which case the parsed and quoted arguments are -always printed to standard output. An empty string forces the -printing behaviour in either mode.) -sitem(tt(-a))(Use `alternative'-style single-hyphenated long options -instead of GNU-style double-hyphenated ones (tt(-foo) vs tt(--foo)). -Note that long options with optional optargs can't always be -distinguished accurately from short options with optional optargs -when using this option. Also, due to limitations of tt(zparseopts), -a single-hyphenated long option whose name is only one character long -is always treated as a short option.) -sitem(tt(-l var(spec)))(Specify long options to recognise when -parsing. These should be given using just the option name (no -dashes), suffixed by `tt(:)' or `tt(::)' if it takes a mandatory or -optional argument respectively. Multiple options can be defined -either by separating them by commas or by supplying -l again. -Example: tt(-l foo,bar: -l baz)) -sitem(tt(-n var(name)))(Specify the name to use in the error message -if argument parsing fails. Defaults to the name of the nearest -calling function or the base name of tt($ZSH_ARGZERO). Note that -errors related to the usage of tt(zgetopt) itself are always reported -as coming from tt(zgetopt).) -sitem(tt(-o var(spec)))(Specify short options to recognise when -parsing. These should be given as a single string, in the same format -used by the tt(getopts) built-in or the tt(getopt+LPAR()3+RPAR()) -library function, again using `tt(:)' or `tt(::)' to indicate a -mandatory or optional argument. The spec may be prefixed with `tt(+)' -to indicate that option parsing should stop at the first non-option -argument (equivalent to setting the environment variable -tt(POSIXLY_CORRECT)). Example: tt(-o ab:cd::)) -endsitem() - -At least one of tt(-o) or tt(-l) must be given. The function's own -options should be followed by zero or more arguments to parse. It is -critical that these be separated explicitly by `tt(--)', as in the -above examples, to ensure that the function can accurately -distinguish the arguments it's meant to parse from its own. - -Refer to the manual for util-linux's tt(getopt+LPAR()1+RPAR()) for -more information about the way arguments are parsed and results are -returned. Note however that this function is not intended to be a -complete re-implementation. In particular, it omits all -portability/compatibility features. Also, like tt(zparseopts) -itself, it does not support abbreviating long options. -) item(tt(zkbd))( See `Keyboard Definition' ifzman(above)\ diff --git a/Functions/Misc/zgetopt b/Functions/Misc/zgetopt deleted file mode 100755 index 5fc1e7725..000000000 --- a/Functions/Misc/zgetopt +++ /dev/null @@ -1,198 +0,0 @@ -#!/bin/zsh -f - -# Wrapper around zparseopts which gives it an interface similar to util-linux's -# getopt(1). See zshcontrib(1) for documentation - -emulate -L zsh -o extended_glob -zmodload -i zsh/zutil || return 3 - -# Very stupid and brittle internal wrapper around zparseopts used to insert the -# caller name into its error messages, allowing us to implement --name. This -# MUST be called with -v, since argv has the options to zparseopts itself -__zgetopt_zparseopts() { - local __err __ret - - __err=$( zparseopts "$@" 2>&1 ) - __ret=$? - - zparseopts "$@" &> /dev/null && return - - # Raw error message should look like this: - # zgetopt_zparseopts:zparseopts:3: bad option: -x - [[ -n $__err ]] && print -ru2 - ${__err/#*:zparseopts:<->:/$name:} - return __ret -} - -local optspec pat i posix=0 -local -a match mbegin mend optvv argvv -local -a array alt lopts sopts name -local -a specs no_arg_opts req_arg_opts opt_arg_opts tmp - -# Same as leading + in short-opts spec -(( $+POSIXLY_CORRECT )) && posix=1 - -# This 0=... makes any error message we get here look a little nicer when we're -# called as a script. Unfortunately the function name overrides $0 in -# zwarnnam() in other scenarios, so this can't be used to implement --name -0=${0:t} zparseopts -D -F -G - \ - {A,-array}:-=array \ - {a,-alternative}=alt \ - {l,-longoptions,-long-options}+:-=lopts \ - {n,-name}:-=name \ - {o,-options}:-=sopts \ -|| { - print -ru2 "usage: ${0:t} [-A ] [-a] [-l ] [-n ] [-o ] -- " - return 2 -} - -# Default to the caller's name -(( $#name )) && name=( "${(@)name/#(-n|--name=)/}" ) -[[ -n $name ]] || name=( ${funcstack[2]:-${ZSH_ARGZERO:t}} ) - -(( $#array )) && array=( "${(@)array/#(-A|--array=)/}" ) - -if [[ $ZSH_EVAL_CONTEXT != toplevel ]]; then - [[ $array == *[^A-Za-z0-9_.]* ]] && { - print -ru2 - "${0:t}: invalid array name: $array" - return 2 - } - (( $#array )) || array=( argv ) - -elif [[ -n $array ]]; then - print -ru2 - "${0:t}: -A option not meaningful unless called as function" - return 2 -fi - -# getopt requires a short option spec; we'll require either short or long -(( $#sopts || $#lopts )) || { - print -ru2 - "${0:t}: missing option spec" - return 2 -} - -optspec=${(@)sopts/#(-o|--options=)/} -sopts=( ) - -for (( i = 1; i <= $#optspec; i++ )); do - # Leading '+': Act POSIXLY_CORRECT - if [[ $i == 1 && $optspec[i] == + ]]; then - posix=1 - # Leading '-': Should leave operands interspersed with options, but this is - # not really possible with zparseopts - elif [[ $i == 1 && $optspec[i] == - ]]; then - print -ru2 - "${0:t}: optspec with leading - (disable operand collection) not supported" - return 2 - # Special characters: [+=\\] because they're special to zparseopts, ':' - # because it's special to getopt, '-' because it's the parsing terminator - elif [[ $optspec[i] == [+:=\\-] ]]; then - print -ru2 - "${0:t}: invalid short-option name: $optspec[i]" - return 2 - # 'a' - elif [[ $optspec[i+1] != : ]]; then - sopts+=( $optspec[i] ) - # 'a:' - elif [[ $optspec[i+2] != : ]]; then - sopts+=( $optspec[i]: ) - (( i += 1 )) - # 'a::' - elif [[ $optspec[i+3] != : ]]; then - sopts+=( $optspec[i]:: ) - (( i += 2 )) - fi -done - -lopts=( ${(@)lopts/#(-l|--long(|-)options=)/} ) -lopts=( ${(@s<,>)lopts} ) - -# Don't allow characters that are special to zparseopts in long-option specs. -# See above -pat='(*[+=\\]*|:*|*:::##|*:[^:]*)' -[[ -n ${(@M)lopts:#$~pat} ]] && { - print -ru2 - "${0:t}: invalid long-option spec: ${${(@M)lopts:#$~pat}[1]}" - return 2 -} - -(( $#alt )) || lopts=( ${(@)lopts/#/-} ) - -specs=( $sopts $lopts ) - -# Used below to identify options with optional optargs -no_arg_opts=( ${(@)${(@M)specs:#*[^:]}/#/-} ) -req_arg_opts=( ${(@)${(@)${(@M)specs:#*[^:]:}/#/-}/%:#} ) -opt_arg_opts=( ${(@)${(@)${(@M)specs:#*::}/#/-}/%:#} ) - -# getopt returns all instances of each option given, so add + -specs=( ${(@)specs/%(#b)(:#)/+$match[1]} ) - -# POSIXLY_CORRECT: Stop parsing options after first non-option argument -if (( posix )); then - tmp=( "$@" ) - __zgetopt_zparseopts -D -F -G -a optvv -v tmp - $specs || return 1 - argvv=( "${(@)tmp}" ) - -# Default: Permute options following non-option arguments -else - tmp=( "$@" ) - __zgetopt_zparseopts -D -E -F -G -a optvv -v tmp - $specs || return 1 - argv=( "${(@)tmp}" ) - # -D + -E leaves an explicit -- in argv where-ever it might appear - local seen - while (( $# )); do - [[ -z $seen && $1 == -- ]] && seen=1 && shift && continue - argvv+=( "$1" ) - shift - done -fi - -# getopt outputs all optargs as separate parameters, even missing optional ones, -# so we scan through and add/separate those if needed. This can't be perfectly -# accurate if Sun-style (-a) long options are used with optional optargs -- e.g. -# if you have specs a:: and abc::, then argument -abc=d is ambiguous. We don't -# guarantee which one is prioritised -(( $#opt_arg_opts )) && { - local cur next - local -a old_optvv=( "${(@)optvv}" ) - optvv=( ) - - for (( i = 1; i <= $#old_optvv; i++ )); do - cur=$old_optvv[i] - next=$old_optvv[i+1] - # Option with no optarg - if [[ -n ${no_arg_opts[(r)$cur]} ]]; then - optvv+=( $cur ) - # Option with required optarg -- will appear in next element - elif [[ -n ${req_arg_opts[(r)$cur]} ]]; then - optvv+=( $cur "$next" ) - (( i++ )) - # Long option with optional optarg -- will appear in same element delimited - # by '=' (even if missing) - elif [[ $cur == *=* && -n ${opt_arg_opts[(r)${cur%%=*}]} ]]; then - optvv+=( ${cur%%=*} "${cur#*=}" ) - # Short option with optional optarg -- will appear in same element with no - # delimiter (thus the option appears alone if the optarg is missing) - elif [[ -n ${opt_arg_opts[(r)${(M)cur#-?}]} ]]; then - optvv+=( ${(M)cur#-?} "${cur#-?}" ) - # ??? - else - print -ru2 - "${0:t}: parse error, please report!" - print -ru2 - "${0:t}: specs: ${(j< >)${(@q+)specs}}" - print -ru2 - "${0:t}: old_optvv: ${(j< >)${(@q+)old_optvv}}" - print -ru2 - "${0:t}: cur: $cur" - optvv+=( $cur ) # I guess? - fi - done -} - -if [[ -n $array ]]; then - # Use EXIT trap to assign in caller's context - trap "$array=( ${(j< >)${(@q+)optvv}} -- ${(j< >)${(@q+)argvv}} )" EXIT - -elif [[ $ZSH_EVAL_CONTEXT != toplevel ]]; then - print -r - "${(@q+)optvv}" -- "${(@q+)argvv}" - -# If called as a script, use unconditional single-quoting. This is ugly but it's -# the closest to what getopt does and it offers compatibility with legacy shells -else - print -r - "${(@qq)optvv}" -- "${(@qq)argvv}" -fi - -return 0 diff --git a/NEWS b/NEWS index a1e74b9fb..d5f6ab620 100644 --- a/NEWS +++ b/NEWS @@ -60,9 +60,6 @@ be used to specify the array of arguments to parse instead of $@. The zparseopts builtin also learnt a -G option which enables GNU-style argument parsing ('--opt=arg', etc.). -A new contrib function zgetopt was added. It wraps `zparseopts -G` to -provide an interface similar to util-linux's getopt(1). - The module zsh/pcre has been updated to use the pcre2 library. The new zsh/random module defines an SRANDOM parameter, zrand_float() diff --git a/Test/Z04zgetopt.ztst b/Test/Z04zgetopt.ztst deleted file mode 100644 index c2bc22be0..000000000 --- a/Test/Z04zgetopt.ztst +++ /dev/null @@ -1,206 +0,0 @@ -%prep - - autoload -Uz zgetopt - -%test - - zgetopt -A '' -- a b c - zgetopt -A '' -o '' -- a b c - zgetopt -A '' -l '' -- a b c -0:-o or -l required -?zgetopt: missing option spec ->-- a b c ->-- a b c - - zgetopt -A '' -o - -- a b c - zgetopt -A '' -o -a -- a b c - zgetopt -A '' -o a- -- a b c - zgetopt -A '' -o a+ -- a b c - zgetopt -A '' -o a= -- a b c - zgetopt -A '' -o a\\ -- a b c - zgetopt -A '' -o :a -- a b c - zgetopt -A '' -o a::: -- a b c - zgetopt -A '' -o '' -- a b c - zgetopt -A '' -o + -- a b c -0:weird short-option specs -?zgetopt: optspec with leading - (disable operand collection) not supported -?zgetopt: optspec with leading - (disable operand collection) not supported -?zgetopt: invalid short-option name: - -?zgetopt: invalid short-option name: + -?zgetopt: invalid short-option name: = -?zgetopt: invalid short-option name: \ -?zgetopt: invalid short-option name: : -?zgetopt: invalid short-option name: : ->-- a b c ->-- a b c - - zgetopt -A '' -l a,+ -- a b c - zgetopt -A '' -l a,= -- a b c - zgetopt -A '' -l a,\\ -- a b c - zgetopt -A '' -l a,: -- a b c - zgetopt -A '' -l a,:b -- a b c - zgetopt -A '' -l a,b:b -- a b c - zgetopt -A '' -l a,b::: -- a b c - zgetopt -A '' -l '' -- a b c - zgetopt -A '' -l , -- a b c - zgetopt -A '' -l a,,,,,b -- a b c - zgetopt -A '' -l - -- a b c --- -0:weird long-option specs -?zgetopt: invalid long-option spec: + -?zgetopt: invalid long-option spec: = -?zgetopt: invalid long-option spec: \ -?zgetopt: invalid long-option spec: : -?zgetopt: invalid long-option spec: :b -?zgetopt: invalid long-option spec: b:b -?zgetopt: invalid long-option spec: b::: ->-- a b c ->-- a b c ->-- a b c ->--- -- a b c - - zgetopt -A '' -o ab:c:: -- a b c - zgetopt -A '' -o ab:c:: -- -a - zgetopt -A '' -o ab:c:: -- -a a b c - zgetopt -A '' -o ab:c:: -- -a a -b c - zgetopt -A '' -o ab:c:: -- -a a -b -c - zgetopt -A '' -o ab:c:: -- -a a -b -c d - zgetopt -A '' -o ab:c:: -- -a a -b -c -c - zgetopt -A '' -o ab:c:: -- -a a -b -c -c d - zgetopt -A '' -o ab:c:: -- -a a -b -c -cd -0:short options ->-- a b c ->-a -- ->-a -- a b c ->-a -b c -- a ->-a -b -c -- a ->-a -b -c -- a d ->-a -b -c -c '' -- a ->-a -b -c -c '' -- a d ->-a -b -c -c d -- a - - zgetopt -A '' -l aaa,bbb:,ccc:: -- a b c - zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa - zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a b c - zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb c - zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb=c - zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb --ccc - zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb --ccc d - zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb --ccc --ccc - zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb --ccc --ccc d - zgetopt -A '' -l aaa,bbb:,ccc:: -- --aaa a --bbb --ccc --ccc=d -0:long options ->-- a b c ->--aaa -- ->--aaa -- a b c ->--aaa --bbb c -- a ->--aaa --bbb c -- a ->--aaa --bbb --ccc -- a ->--aaa --bbb --ccc -- a d ->--aaa --bbb --ccc --ccc '' -- a ->--aaa --bbb --ccc --ccc '' -- a d ->--aaa --bbb --ccc --ccc d -- a - - zgetopt -A '' -al aaa,bbb:,ccc:: -- a b c - zgetopt -A '' -al aaa,bbb:,ccc:: -- --aaa a b c - zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa - zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a b c - zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb c - zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb=c - zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb -ccc - zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb -ccc d - zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb -ccc -ccc - zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb -ccc -ccc d - zgetopt -A '' -al aaa,bbb:,ccc:: -- -aaa a -bbb -ccc -ccc=d -0:long options with -a (Sun style) ->-- a b c -?(eval): bad option: --aaa ->-aaa -- ->-aaa -- a b c ->-aaa -bbb c -- a ->-aaa -bbb c -- a ->-aaa -bbb -ccc -- a ->-aaa -bbb -ccc -- a d ->-aaa -bbb -ccc -ccc '' -- a ->-aaa -bbb -ccc -ccc '' -- a d ->-aaa -bbb -ccc -ccc d -- a - - zgetopt -A '' -al a: -- -a=b -0:single-character long option with -a ->-a '=b' -- - - zgetopt -A '' -o '' -0:zero args to parse ->-- - - zgetopt -A '' -o '' -- -- a b c - zgetopt -A '' -o '' -- a b -- c - zgetopt -A '' -o '' -- a b c -- - zgetopt -A '' -o c -- a b -- -c - zgetopt -A '' -o c -- a b - -c -0:parsing terminator ->-- a b c ->-- a b c ->-- a b c ->-- a b -c ->-c -- a b - - - zgetopt -A '' -o a -- a -a b - zgetopt -A '' -o +a -- a -a b - POSIXLY_CORRECT=1 zgetopt -A '' -o a -- a -a b -0:POSIXLY_CORRECT ->-a -- a b ->-- a -a b ->-- a -a b - - zgetopt -A '' -o '' -- $'\a\'\a' -0:function-mode quoting style ->-- $'\C-G\'\C-G' - - zgetopt -A '' -o '' -- a -a b - zgetopt -A '' -o '' -- a --a b -1:bad options -?(eval): bad option: -a -?(eval): bad option: --a - - zgetopt -A '' ; echo $? # missing spec - zgetopt -A '' -o '' -x ; echo $? # bad option to zgetopt - zgetopt -A '' -o '' -- -y; echo $? # bad option to parse -0:return status -*?zgetopt: missing option spec -*>2 -*?zgetopt:zparseopts:*: bad option: -x -*?usage:* -*>2 -*?\(eval\): bad option: -y -*>1 - - () { zgetopt -o a -- "$@"; typeset -p argv } -a b c - () { local -a v; zgetopt -A v -o a -- "$@"; typeset -p argv v } -a b c -0:array output ->typeset -g -a argv=( -a -- b c ) ->typeset -g -a argv=( -a b c ) ->typeset -a v=( -a -- b c ) - - zgetopt -A '' -o a: -- -x - zgetopt -A '' -o a: -- -a - () { zgetopt -A '' -o a: -- "$@"; : } -x - func() { zgetopt -A '' -o a: -- "$@"; : }; func -x - f1() { zgetopt -A '' -o a: -- "$@"; : }; f2() { f1 "$@" }; f2 -x -0:automatic name -?(eval): bad option: -x -?(eval): missing argument for option: -a -?(anon): bad option: -x -?func: bad option: -x -?f1: bad option: -x - - zgetopt -n aaa -A '' -o a: -- -x - zgetopt -n aaa -A '' -o a: -- -a - () { zgetopt -n bbb -A '' -o a: -- "$@"; : } -x - func() { zgetopt -n ccc -A '' -o a: -- "$@"; : }; func -x - f1() { zgetopt -n ddd -A '' -o a: -- "$@"; : }; f2() { f1 "$@" }; f2 -x -0:manual name with -n -?aaa: bad option: -x -?aaa: missing argument for option: -a -?bbb: bad option: -x -?ccc: bad option: -x -?ddd: bad option: -x -- cgit v1.2.3