diff options
-rw-r--r-- | ChangeLog | 79 | ||||
-rw-r--r-- | Completion/Base/Utility/_alternative | 4 | ||||
-rw-r--r-- | Completion/Base/Utility/_multi_parts | 16 | ||||
-rw-r--r-- | Completion/Linux/Command/_setsid | 12 | ||||
-rw-r--r-- | Completion/Unix/Command/_composer | 850 | ||||
-rw-r--r-- | Completion/Unix/Command/_git | 12 | ||||
-rw-r--r-- | Completion/Unix/Command/_ipsec | 179 | ||||
-rw-r--r-- | Completion/Unix/Command/_swanctl | 225 | ||||
-rw-r--r-- | Completion/Unix/Type/_urls | 2 | ||||
-rw-r--r-- | Completion/Zsh/Command/_exec | 9 | ||||
-rw-r--r-- | Completion/Zsh/Command/_precommand | 2 | ||||
-rw-r--r-- | Config/version.mk | 4 | ||||
-rw-r--r-- | Doc/Zsh/builtins.yo | 2 | ||||
-rw-r--r-- | Doc/Zsh/options.yo | 4 | ||||
-rw-r--r-- | Etc/creating-a-release.txt | 4 | ||||
-rw-r--r-- | NEWS | 35 | ||||
-rw-r--r-- | README | 4 | ||||
-rw-r--r-- | Src/Modules/datetime.c | 2 | ||||
-rw-r--r-- | Src/Zle/compctl.c | 9 | ||||
-rw-r--r-- | Src/jobs.c | 23 | ||||
-rw-r--r-- | Src/math.c | 2 | ||||
-rw-r--r-- | Src/prompt.c | 30 | ||||
-rw-r--r-- | Src/subst.c | 2 | ||||
-rw-r--r-- | Src/utils.c | 23 | ||||
-rw-r--r-- | Test/A05execution.ztst | 53 | ||||
-rw-r--r-- | Test/C01arith.ztst | 16 | ||||
-rw-r--r-- | Test/D04parameter.ztst | 8 | ||||
-rw-r--r-- | Test/V09datetime.ztst | 21 | ||||
-rw-r--r-- | Test/X04zlehighlight.ztst | 154 |
29 files changed, 1737 insertions, 49 deletions
@@ -1,3 +1,76 @@ +2018-01-05 dana <dana@dana.is> + + * unposted: Config/version.mk, NEWS: Update for 5.6.2-test-3 + +2018-01-04 dana <dana@dana.is> + + * 43974: Completion/Unix/Command/_git: Always offer -F/-m for + `git tag` + + * 43970, 43971: Etc/creating-a-release.txt, NEWS: Document some + changes in 5.{4,5,6} not previously mentioned, clarify that + process in release instructions + +2019-01-03 Peter Stephenson <p.stephenson@samsung.com> + + * 43969: Src/Zle/compctl.c: untokenize Dash for compctl with + GLOB_SUBST set. + +2018-01-01 dana <dana@dana.is> + + * 43960: Completion/Linux/Command/_setsid, + Completion/Zsh/Command/_exec, Completion/Zsh/Command/_precommand: + Add full completion for exec and setsid + + * 43959: Completion/Unix/Command/_ipsec, + Completion/Unix/Command/_swanctl, Completion/Unix/Type/_urls: Add + completion for strongSwan, &al.; complete unix:// URIs + +2018-12-31 dana <dana@dana.is> + + * 43962: Test/X04zlehighlight.ztst: Fix module_path + +2018-12-30 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * Sebastian: users/23809: Src/prompt.c, + Test/X04zlehighlight.ztst: Add ZLE_HIGHLITHT extensions to + termcap for colours > 7, add tests. + + * 43944: Martijn: Doc/Zsh/options.yo, README, Src/math.c, + Test/C01arith.ztst: apply NO_UNSET consistently to arithmetic. + + * unposted: Test/D04parameter.ztst: test for 43938. + +2018-12-30 dana <dana@dana.is> + + * 43914 (tweaked): Completion/Unix/Command/_composer: Add + completion for Composer + + * 43930: Completion/Base/Utility/_multi_parts: Improve performance + + * unposted (per 43938): Src/utils.c: Avoid segfault when + unmetafying empty string + +2018-12-29 dana <dana@dana.is> + + * 43953 (tweaked per 43954): Src/utils.c, Test/V09datetime.ztst: + Fix rounding/truncation error in %. time-format specifier + +2018-12-24 dana <dana@dana.is> + + * 43935 (tweaked): Src/Modules/datetime.c, Test/V09datetime.ztst: + Fix test error on 32-bit machines, reject too-large nanosecond + values given to strftime + +2018-12-23 dana <dana@dana.is> + + * 43928: Completion/Base/Utility/_alternative: Evaluate (...) + action syntax as with _arguments + +2018-12-21 Bart Schaefer <schaefer@zsh.org> + + * 43921: ${\var} should be an error + 2018-12-19 Peter Stephenson <p.w.stephenson@ntlworld.com> * unposted: Config/version.mk, Etc/FAQ.yo, README: update for @@ -6960,6 +7033,12 @@ Test/B02typeset.ztst, Test/V10private.ztst: typeset -p can now output arrays on one line. + * 37303: Test/E01options.ztst: test WARN_CREATE_GLOBAL negative + cases. + + * 37302: Src/exec.c, Src/params.c: suppress WARN_CREATE_GLOBAL + warnings after a fork + * unposted: Src/exec.c, Src/init.c: fix up init_io() calls in previous change. diff --git a/Completion/Base/Utility/_alternative b/Completion/Base/Utility/_alternative index bfb34a604..3c61d1125 100644 --- a/Completion/Base/Utility/_alternative +++ b/Completion/Base/Utility/_alternative @@ -43,8 +43,10 @@ while _tags; do # Anything inside `(...)' is added directly. + eval ws\=\( "${action[2,-2]}" \) + _all_labels "${def%%:*}" expl "$descr" \ - compadd "$subopts[@]" - ${=action[2,-2]} + compadd "$subopts[@]" -a - ws elif [[ "$action" = \{*\} ]]; then # A string in braces is evaluated. diff --git a/Completion/Base/Utility/_multi_parts b/Completion/Base/Utility/_multi_parts index 3e2f36c9c..12ff965ed 100644 --- a/Completion/Base/Utility/_multi_parts +++ b/Completion/Base/Utility/_multi_parts @@ -127,8 +127,7 @@ while true; do return fi elif (( $#tmp1 )); then - local ret=1 tt - local -a mm + local ret=1 # More than one match. First we get all strings that match the # rest from the line. @@ -145,11 +144,14 @@ while true; do SUFFIX="$suf" fi - for tt in $tmp1 - do - mm+=( "${(@M)matches:#$tt*}" ) - done - matches=( $mm ) + # The purpose of this check (or one purpose, anyway) seems to be to ensure + # that the suffix for the current segment on the command line doesn't + # match across segments. For example, we want $matches for a<TAB>c to + # include abc/d, but not abd/c. If we don't have anything on the command + # line for this segment, though, we can skip it. (The difference is only + # noticeable when there are a huge number of possibilities) + [[ -n $pre$suf ]] && + matches=( ${(@M)matches:#(${(j<|>)~${(@b)tmp1}})*} ) if ! zstyle -t ":completion:${curcontext}:" expand suffix || [[ -n "$menu" || -z "$compstate[insert]" ]]; then diff --git a/Completion/Linux/Command/_setsid b/Completion/Linux/Command/_setsid new file mode 100644 index 000000000..a8107e582 --- /dev/null +++ b/Completion/Linux/Command/_setsid @@ -0,0 +1,12 @@ +#compdef setsid + +[[ $service == setsid ]] && precommands+=( setsid ) + +_arguments -s -S -A '-*' : \ + '(: * -)'{-h,--help}'[display help information]' \ + '(: * -)'{-V,--version}'[display version information]' \ + '(-c --ctty)'{-c,--ctty}'[set controlling terminal to current one]' \ + '(-f --fork)'{-f,--fork}'[always fork]' \ + '(-w --wait)'{-w,--wait}'[wait for program to exit, and use same return code]' \ + '1: :_path_commands' \ + '*:: : _normal' diff --git a/Completion/Unix/Command/_composer b/Completion/Unix/Command/_composer new file mode 100644 index 000000000..2b9f2cd32 --- /dev/null +++ b/Completion/Unix/Command/_composer @@ -0,0 +1,850 @@ +#compdef composer composer.phar + +# Notes: +# - With some re-arranging, this function could be used as a base for completing +# any Symfony Console application. It's worth mentioning that most Console +# applications provide their help output in structured XML and JSON formats, +# and a helper function could be written to parse these and produce basic +# completion similar to what `_arguments --` does. But it wouldn't be fully +# featured like this +# - Completing arbitrary package names from Packagist, even with caching enabled +# (which it is by default here), can be very slow; it may even hang the shell. +# To disable the fetching of remote packages and use only local cache/JSON +# information for completion, set the fetch-packages style as follows: +# zstyle ':completion:*:composer:*' fetch-packages no +# - @todo We don't complete custom commands (including script aliases). This is +# easy to do in the general case, but it probably requires some clever caching +# to avoid introducing a noticeable lag to every completion operation, due to +# the way command resolution works and the fact that discovering custom +# commands requires making slow calls to Composer +# - @todo We don't complete version constraints + +# Check cache validity +__composer_cache_policy() { + # Invalidate if the cache is over a week old + [[ -n $1(#qmw+1N) ]] && return 0 + + __composer_update_work_dir + + # Invalidate if the current project JSON file is newer than the cache + [[ -e $_composer_work_dir/composer.json ]] && + [[ $_composer_work_dir/composer.json -nt $1 ]] +} + +# _call_program wrapper (same eval/quoting rules apply) +# $1 => tag +# $2 ... => composer arguments +(( $+functions[__composer_call] )) || +__composer_call() { + local -a cmd + + __composer_update_work_dir + + cmd=( ${_composer_cmd:-composer} -d${_composer_work_dir:-${(q)PWD}} ) + (( _composer_is_global )) && cmd+=( global ) + + _call_program $1 $cmd "${@[2,-1]}" +} + +# Resolve potentially abbreviated/aliased command name to canonical name +# $1 => name of scalar parameter to set +# $2 => provided command name +(( $+functions[__composer_resolve_cmd] )) || +__composer_resolve_cmd() { + local __i __ret=1 + local -a __cmds __tmp=( ${(@)_composer_cmds%%:*} ) + + __cmds=( $__tmp[(r)$2] ) + (( $#__cmds )) || __cmds=( ${(M)__tmp:#$2*} ) + + if (( $#__cmds == 1 )); then + 2=$__cmds[1] + (( $+_composer_cmd_aliases[$2] )) && 2=$_composer_cmd_aliases[$2] + __ret=0 + else + 3=$__cmds[1] + + # An ambiguous prefix match isn't ambiguous if all the matches are aliases + # of each other + for (( __i = 2; __i <= $#__cmds; __i++ )); do + if [[ $_composer_cmd_aliases[$__cmds[__i]] == $3 ]]; then + __cmds[__i]=() + elif [[ $_composer_cmd_aliases[$3] == $__cmds[__i] ]]; then + 3=$__cmds[__i] + __cmds[__i]=() + fi + done + + if (( $#__cmds == 1 )); then + 2=$3 + __ret=0 + elif (( $#__cmds )); then + _message -e ambiguous-commands "ambiguous command: $2 ($__cmds)" + else + # @todo Too annoying without handling custom commands + : _message -e unrecognized-commands "unrecognized command: $2" + fi + fi + : ${(P)1::=$2} + return __ret +} + +# Remove already-used global options (this is a bit silly admittedly) +# $1 ... => options given (e.g. ${(k)opt_args}) +(( $+functions[__composer_prune_global_opts] )) || +__composer_prune_global_opts() { + local opt + local -a excls specs remove + + for opt in $@; do + specs=( ${(M)_composer_global_opts:#(*[\*\)]|)${opt}[=+-]#\[*} ) + excls=( ${=${(@)${(@M)specs#\([^\)]##\)}//[ \(\)]/ }} ) + remove+=( + # Don't remove used options like *-v + ${specs:#(*\)|)\*${opt}[=+-]#\[*} + # But do remove them if a used option excludes them + ${(M)_composer_global_opts:#(*[\*\)]|)(${(j<|>)~${(@b)excls}})[=+-]#\[*} + ) + done + + _composer_global_opts=( ${_composer_global_opts:#(${(j<|>)~${(@b)remove}})} ) +} + +# Update the working directory from opt_args/PWD. This is a little irritating to +# deal with; for now we're just calling it anywhere it might be important +(( $+functions[__composer_update_work_dir] )) || +__composer_update_work_dir() { + if [[ -n ${(v)opt_args[(i)(-d|--working-dir)]} ]]; then + eval _composer_work_dir=${(v)opt_args[(i)(-d|--working-dir)]} + elif [[ -z $_composer_work_dir ]]; then + _composer_work_dir=$PWD + fi +} + +# Complete local/vendored binaries +(( $+functions[__composer_binaries] )) || +__composer_binaries() { + local -a expl tmp + + tmp=( ${(f)"$( __composer_call exec-list exec -l )"} ) + tmp=( ${(@)tmp%%[[:space:]]##} ) + [[ $tmp[1] == *: ]] && tmp[1]=() + tmp=( ${(@)tmp##[\*-][[:space:]]#} ) + tmp=( ${(@)tmp%%[[:space:]]##\(local\)} ) + + _wanted -x commands expl binary compadd -a "$@" - tmp +} + +# Complete commands +(( $+functions[__composer_commands] )) || +__composer_commands() { + _describe -t commands command _composer_cmds +} + +# Complete package licences +(( $+functions[__composer_licenses] )) || +__composer_licenses() { + # These are just the ones the Composer documentation recommends; the full list + # of supported identifiers can be found at https://spdx.org/licenses/. If + # other functions need to complete licences in the future, it might be wise to + # break this out into a _licenses type + local -a tmp=( + 'Apache-2.0:Apache License 2.0' + 'BSD-2-Clause:BSD 2-Clause "Simplified" License' + 'BSD-3-Clause:BSD 3-Clause "New" or "Revised" License' + 'BSD-4-Clause:BSD 4-Clause "Original" or "Old" License' + 'GPL-2.0-only:GNU General Public License v2.0 only' + 'GPL-2.0-or-later:GNU General Public License v2.0 or later' + 'GPL-3.0-only:GNU General Public License v3.0 only' + 'GPL-3.0-or-later:GNU General Public License v3.0 or later' + 'LGPL-2.1-only:GNU Lesser General Public License v2.1 only' + 'LGPL-2.1-or-later:GNU Lesser General Public License v2.1 or later' + 'LGPL-3.0-only:GNU Lesser General Public License v3.0 only' + 'LGPL-3.0-or-later:GNU Lesser General Public License v3.0 or later' + 'MIT:MIT License' + 'proprietary:proprietary/closed-source license' + ) + _describe -t licenses 'package license' tmp +} + +# Complete packages +# --pairs => complete as package:constraint pairs +# --vendor => complete only vendored (installed) packages +(( $+functions[__composer_packages] )) || +__composer_packages() { + local cwd + local -a pairs vendor home_dirs pkgs + + __composer_update_work_dir + + zparseopts -D -E - -vendor=vendor -pairs=pairs + + (( $#pairs )) && compset -P '*[:= ]' && { + _message -e versions 'version constraint' + return + } + + home_dirs=( + $COMPOSER_HOME(#q/N) + $HOME/.composer(#q/N) + $HOME/.config/composer(#q/N) + ${XDG_CONFIG_HOME:-/@err@}/composer(#q/N) + ) + + pkgs=( $_composer_work_dir/vendor/*/*(#q/N) ) + + # Trying to work out the path to the vendor directory when we're global is + # tedious, so we'll just take everything we can find + (( ! $#vendor || _composer_is_global )) && + pkgs+=( $^home_dirs/vendor/*/*(#q/N) ) + + (( $#vendor )) || pkgs+=( + ${COMPOSER_CACHE_DIR:-/@err@}/files/*/*(#q/N) + $^home_dirs/cache/files/*/*(#q/N) + ) + + pkgs=( ${(@M)pkgs%%[^/]##/[^/]##} ) + + (( $#vendor )) || { + pkgs+=( + ${(f)"$( + _call_program packages-json \ + command grep -soE ${(qq):-'"[^/]+\\?/[^/]+"\s*:'} -- \ + ${(qq)_composer_work_dir}/composer.json + )"//[$':" \t\\']/} + ${(@)${(f)"$( + _call_program packages-lock \ + command grep -soE ${(qq):-'"name"\s*:\s*"[^/]+\\?/[^/]+"'} -- \ + ${(qq)_composer_work_dir}/composer.lock + )"//\"name\"/}//[$':" \t\\']/} + ) + + zstyle -T ":completion:*:*:$service:*" fetch-packages && { + { (( ! $#_composer_cache_pkgs )) || _cache_invalid composer-pkgs } && + ! _retrieve_cache composer-pkgs && { + _composer_cache_pkgs=( ${(f)"$( + __composer_call packages-fetch show -aN | + LC_ALL=C tr -d '\t ' # Seems faster than ${...//.../} here + )"} ) + _store_cache composer-pkgs _composer_cache_pkgs + } + pkgs+=( $_composer_cache_pkgs ) + } + } + + (( $#pkgs )) || { + _message -e packages package + return + } + + if [[ $PREFIX == */* ]]; then + _description packages expl "${PREFIX%%/*}/* package" + else + _description packages expl 'package vendor' + fi + _multi_parts "${(@)expl}" "$@" / pkgs +} + +# Complete package repositories +(( $+functions[__composer_repositories] )) || +__composer_repositories() { + _alternative \ + 'urls:repository URL:_urls' \ + 'files:repository JSON configuration file:_files -g "*.json(#q-.)"' \ + 'json:repository JSON configuration object:' +} + +# Complete composer.json scripts +(( $+functions[__composer_scripts] )) || +__composer_scripts() { + local -a expl tmp + + tmp=( ${(f)"$( __composer_call run-script-list run-script -l )"} ) + tmp=( ${(@)tmp##[[:space:]]##} ) + tmp=( ${(@)tmp%%[[:space:]]##} ) + [[ $tmp[1] == *: ]] && tmp[1]=() + tmp=( ${(@)tmp##[\*-][[:space:]]#} ) + tmp=( ${(@)tmp%%[[:space:]]*} ) + + _wanted -x commands expl script compadd -a "$@" - tmp +} + +# Complete package stabilities +(( $+functions[__composer_stabilities] )) || +__composer_stabilities() { + local -a expl + _wanted stabilities expl 'package stability' compadd "$@" - \ + stable RC beta alpha dev +} + +# Complete package types +(( $+functions[__composer_types] )) || +__composer_types() { + local -a expl + # Only the first four here are official types listed in the documentation; the + # others are popular custom types + _wanted types expl 'package type' compadd "$@" - \ + composer-plugin library metapackage project \ + cakephp-plugin cantao-module drupal-module magento2-module package \ + phpcodesniffer-standard silverstripe-module symfony-bundle \ + typo3-cms-extension wordpress-plugin yii2-extension +} + +(( $+functions[_composer_archive] )) || +_composer_archive() { + _arguments -s -S : \ + $_composer_global_opts \ + '--dir=[specify output directory]:output directory:_files -/' \ + '--file=[specify output file name]:output file name (without extension):_files' \ + '(-f --format)'{-f+,--format=}'[specify archive format]:archive format:(tar zip)' \ + '1:: :__composer_packages' \ + '2:: :_guard "^-*" "version constraint"' +} + +(( $+functions[_composer_check-platform-reqs] )) || +_composer_check-platform-reqs() { + _arguments -s -S : \ + $_composer_global_opts \ + '--no-dev[do not check require-dev package requirements]' +} + +(( $+functions[_composer_config] )) || +_composer_config() { + local ret=1 + local -a context expl line state state_descr cmd tmp + local -A opt_args + + # -a and -f can be used together, in which case auth.json will be looked up in + # the base directory of the -f file. -f and -g can't be used together, but -f + # can still be used to specify config.json with `composer global config` + _arguments -s -S : \ + $_composer_global_opts \ + + '(a)' '(A l u : *)'{-a,--auth}'[edit auth.json (with -e)]' \ + + '(A)' '(a e u)--absolute[display absolute *-dir setting paths]' \ + + '(e)' '(A l u : *)'{-e,--editor}'[open configuration in $EDITOR]' \ + + '(f)' '(g)'{-f+,--file=}'[specify {composer,config}.json path]:configuration file:_files' \ + + '(g)' {-g,--global}'[use global config.json]' \ + + '(l)' '(a e u : *)'{-l,--list}'[list configuration settings]' \ + + '(u)' '(a A e l *)--unset[unset specified setting key]' \ + + k '(a e l)1: :->key' \ + + v '(a e l u)*:: :->val' \ + && ret=0 + __composer_update_work_dir + + case $state in + key) + # `composer config` doesn't seem to actually respect -d... + tmp=( ${(v)opt_args[(i)([^-]##-|)(-f|--file)]} ) + cmd=( config -f$^tmp $opt_args[(i)([^-]##-|)(-g|--global)] -l ) + + tmp=( ${(@M)${(f)"$( __composer_call config-list $cmd )"}#\[*\]} ) + tmp=( ${(@)tmp//[ \]\[]/} ) + + _wanted setting-keys expl 'setting key' compadd -a "$@" - tmp && ret=0 + ;; + val) + case $words[1] in + *[.-]dirs#|home) + _wanted setting-values expl 'setting value' _files -/ "$@" && ret=0 + ;; + *[.-]domains#) + _wanted setting-values expl 'setting value' _hosts "$@" && ret=0 + ;; + *[.-]urls#) + _wanted setting-values expl 'setting value' _urls "$@" && ret=0 + ;; + *) + # If we wanted we could get specific about booleans, etc., here + _wanted setting-values expl 'setting value' _default "$@" && ret=0 + ;; + esac + ;; + esac + + return ret +} + +(( $+functions[_composer_create-project] )) || +_composer_create-project() { + _arguments -s -S : \ + $_composer_global_opts \ + '(--no-dev)--dev[install require-dev packages]' \ + '--ignore-platform-reqs[ignore PHP platform requirements]' \ + '(--remove-vcs)--keep-vcs[do not remove VCS directory]' \ + '!(--no-plugins)--no-custom-installers' \ + '(--dev)--no-dev[do not install require-dev packages]' \ + '--no-install[skip installation of package dependencies]' \ + '--no-progress[do not display download progress]' \ + '--no-secure-http[do not use HTTPS]' \ + '--no-scripts[prevent execution of scripts defined in root package]' \ + '(--prefer-source)--prefer-dist[prefer installation from dist]' \ + '(--prefer-dist)--prefer-source[prefer installation from source]' \ + '(--keep-vcs)--remove-vcs[force removal of VCS directory]' \ + '--repository=[specify package repository]: :__composer_repositories' \ + '!(--repository)--repository_url:repository URL:_urls' \ + '(-s --stability)'{-s+,--stability=}'[specify minimum stability]: :__composer_stabilities' \ + '1: :__composer_packages' \ + '2::project directory:_files -/' \ + '3:: :_guard "^-*" "version constraint"' +} + +(( $+functions[_composer_depends] )) || +_composer_depends() { + _arguments -s -S : \ + $_composer_global_opts \ + '(-r --recursive)'{-r,--recursive}'[resolve recursively up to root package]' \ + '(-t --tree)'{-t,--tree}'[display in tree format]' \ + '1: :__composer_packages --vendor' \ + '2:: :_guard "^-*" "version constraint"' +} + +(( $+functions[_composer_dump-autoload] )) || +_composer_dump-autoload() { + _arguments -s -S : \ + $_composer_global_opts \ + '--apcu[use APCu to cache found/not-found classes]' \ + '--no-dev[ignore autoload-dev rules]' \ + '--no-scripts[prevent execution of scripts defined in composer.json]' \ + + '(a)' '(o)'{-a,--classmap-authoritative}'[autoload from class maps only (implies -o)]' \ + + '(o)' {-o,--optimize}'[use class maps for PSR-0/4 packages]' +} + +(( $+functions[_composer_exec] )) || +_composer_exec() { + local ret=1 + local -a context line state state_descr + local -A opt_args + + _arguments -s -S : \ + $_composer_global_opts \ + '(: * -l --list)'{-l,--list}'[display available binaries]' \ + '1: :__composer_binaries' \ + '*: :->next' \ + && ret=0 + + # Can't use *:: here, it won't complete subsequent options + [[ $state == next ]] && { + shift words + (( CURRENT-- )) + unset _composer_cmd + _normal && ret=0 + } + + return ret +} + +(( $+functions[_composer_global] )) || +_composer_global() { + _composer_is_global=1 + _composer "$@" +} + +(( $+functions[_composer_help] )) || +_composer_help() { + _arguments -s -S : \ + $_composer_global_opts \ + '--format=[specify output format]:output format [txt]:(json md txt xml)' \ + '--raw[output raw help (with text format)]' \ + '1: :__composer_commands' +} + +(( $+functions[_composer_home] )) || +_composer_home() { + _arguments -s -S : \ + $_composer_global_opts \ + '(-H --homepage)'{-H,--homepage}'[use home page instead of repository]' \ + '(-s --show)'{-s,--show}'[display URL only (do not open)]' \ + '*: :__composer_packages' \ +} + +(( $+functions[_composer_init] )) || +_composer_init() { + _arguments -s -S : \ + $_composer_global_opts \ + '--author=[specify package author]:package author' \ + '--description=[specify package description]:package description' \ + '--homepage=[specify package home page]:package home page:_urls' \ + '(-l --license)'{-l+,--license=}'[specify package license]: :__composer_licenses' \ + '--name=[specify package name]: :__composer_packages' \ + '*--repository=[specify custom package repository]: :__composer_repositories' \ + '*--require=[specify package to require]: :__composer_packages --pairs' \ + '*--require-dev=[specify package to require for development]: :__composer_packages --pairs' \ + '(-s --stability)'{-s+,--stability=}'[specify minimum stability]: :__composer_stabilities' \ + '--type=-[specify package type]:: :__composer_types' +} + +(( $+functions[_composer_install] )) || +_composer_install() { + _arguments -s -S : \ + $_composer_global_opts \ + '(--no-dev)--dev[install require-dev packages]' \ + '--dry-run[do not actually install (implies -v)]' \ + '--ignore-platform-reqs[ignore PHP platform requirements]' \ + '!(--no-plugins)--no-custom-installers' \ + '(--dev)--no-dev[do not install require-dev packages]' \ + '--no-progress[do not display download progress]' \ + '--no-scripts[prevent execution of scripts defined in composer.json]' \ + '--no-suggest[do not display package suggestions]' \ + '(--prefer-source)--prefer-dist[prefer installation from dist]' \ + '(--prefer-dist)--prefer-source[prefer installation from source]' \ + + '(a)' '(n o)'{-a,--classmap-authoritative}'[autoload from class maps only (implies -o)]' \ + + '(A)' '(n)--apcu-autoloader[use APCu to cache found/not-found classes]' \ + + '(n)' '(a A o)--no-autoload[skip autoloader generation]' \ + + '(o)' '(n)'{-o,--optimize}'[use class maps for PSR-0/4 packages]' +} + +(( $+functions[_composer_licenses] )) || +_composer_licenses() { + _arguments -s -S : \ + $_composer_global_opts \ + '--no-dev[ignore require-dev packages]' \ + '--format=[specify output format]:output format [text]:(json text)' +} + +(( $+functions[_composer_list] )) || +_composer_list() { + _arguments -s -S : \ + $_composer_global_opts \ + '--format=[specify output format]:output format [txt]:(json md txt xml)' \ + '--raw[output raw help (with text format)]' \ + '1: :_guard "^-*" namespace' +} + +(( $+functions[_composer_outdated] )) || +_composer_outdated() { + _arguments -s -S : \ + $_composer_global_opts \ + '--format=[specify output format]:output format [text]:(json text)' \ + '--strict[return non-zero exit code if there are outdated packages]' \ + '1: :__composer_packages --vendor' \ + + '(a)' '(D I)'{-a,--all}'[display all installed packages]' \ + + '(D)' '(a)'{-D,--direct}'[display only packages directly required by root package]' \ + + '(I)' '(a)*--ignore=[ignore specified package (with -o)]: :__composer_packages' \ + + '(m)' '(a)'{-m,--minor-only}'[display only packages with minor semver updates (with -o)]' \ + + '(o)' '!(a)'{-o,--outdated} +} + +(( $+functions[_composer_prohibits] )) || +_composer_prohibits() { + _arguments -s -S : \ + $_composer_global_opts \ + '(-r --recursive)'{-r,--recursive}'[resolve recursively up to root package]' \ + '(-t --tree)'{-t,--tree}'[display in tree format]' \ + '1: :__composer_packages --vendor' \ + '2:: :_guard "^-*" "version constraint"' +} + +(( $+functions[_composer_remove] )) || +_composer_remove() { + _arguments -s -S : \ + $_composer_global_opts \ + '--dev[remove package from require-dev]' \ + '--ignore-platform-reqs[ignore PHP platform requirements]' \ + '(--dev)--no-dev[do not install require-dev packages]' \ + '--no-progress[do not display download progress]' \ + '--no-scripts[prevent execution of scripts defined in composer.json]' \ + '(--update-no-dev)--no-update[do not update dependencies]' \ + '(--update-with-dependencies)--no-update-with-dependencies[do not update inherited dependencies]' \ + '(--no-update)--update-no-dev[update dependencies with --no-dev option]' \ + '(--no-update-with-dependencies)--update-with-dependencies[update inherited dependencies]' \ + '*: :__composer_packages --vendor' \ + + '(a)' '(n o)'{-a,--classmap-authoritative}'[autoload from class maps only (implies -o)]' \ + + '(A)' '(n)--apcu-autoloader[use APCu to cache found/not-found classes]' \ + + '(n)' '(a A o)--no-autoload[skip autoloader generation]' \ + + '(o)' '(n)'{-o,--optimize}'[use class maps for PSR-0/4 packages]' +} + +(( $+functions[_composer_require] )) || +_composer_require() { + _arguments -s -S : \ + $_composer_global_opts \ + '--dev[add package to require-dev]' \ + '--ignore-platform-reqs[ignore PHP platform requirements]' \ + '--no-progress[do not display download progress]' \ + '--no-scripts[prevent execution of scripts defined in composer.json]' \ + '--no-suggest[do not display package suggestions]' \ + '(--prefer-source)--prefer-dist[prefer installation from dist]' \ + '--prefer-lowest[prefer lowest versions of dependencies]' \ + '(--prefer-dist)--prefer-source[prefer installation from source]' \ + '--prefer-stable[prefer stable versions of dependencies]' \ + '--sort-packages[sort packages when adding/updating dependencies]' \ + '(--no-update)--update-no-dev[update dependencies with --no-dev option]' \ + '*: :__composer_packages --pairs' \ + + '(a)' '(n o)'{-a,--classmap-authoritative}'[autoload from class maps only (implies -o)]' \ + + '(A)' '(n)--apcu-autoloader[use APCu to cache found/not-found classes]' \ + + '(n)' '(a A o)--no-autoload[skip autoloader generation]' \ + + '(o)' '(n)'{-o,--optimize}'[use class maps for PSR-0/4 packages]' \ + + '(u)' \ + '(--update-no-dev)--no-update[do not update dependencies]' \ + '--update-with-dependencies[update inherited dependencies, except root requirements]' \ + '--update-with-all-dependencies[update all inherited dependencies]' +} + +(( $+functions[_composer_run-script] )) || +_composer_run-script() { + local ret=1 + local -a context line state state_descr + local -A opt_args + + _arguments -s -S : \ + $_composer_global_opts \ + '(--no-dev)--dev[enable dev mode]' \ + '(: * -l --list)'{-l,--list}'[display available scripts]' \ + '(--dev)--no-dev[disable dev mode]' \ + '--timeout=[specify script timeout]:timeout (seconds)' \ + '1: :__composer_scripts' \ + '*: :->next' \ + && ret=0 + + # Can't use *:: here, it won't complete subsequent options + [[ $state == next ]] && { + shift words + (( CURRENT-- )) + unset _composer_cmd + _normal && ret=0 + } + + return ret +} + +(( $+functions[_composer_search] )) || +_composer_search() { + _arguments -s -S : \ + $_composer_global_opts \ + '(-N --only-name)'{-N,--only-name}'[search package names only]' \ + '(-t --type)'{-t+,--type=}'[search for specified package type]: :__composer_types' \ + '*: :__composer_packages' +} + +(( $+functions[_composer_self-update] )) || +_composer_self-update() { + _arguments -s -S : \ + $_composer_global_opts \ + + '(c)' \ + '--preview[force update to preview channel]' \ + '--snapshot[force update to snapshot channel]' \ + '--stable[force update to stable channel]' \ + + '(C)' '(u)--set-channel-only[set channel as default and exit]' \ + + u \ + '(C -r --rollback)--clean-backups[delete old back-ups during update]' \ + '(C)--no-progress[do not display download progress]' \ + '(c C -r --rollback)'{-r,--rollback}'[roll back to earlier installation]' \ + '(C)--update-keys[prompt for key update]' \ + '(C)1: :_guard "^-*" "Composer version"' +} + +(( $+functions[_composer_show] )) || +_composer_show() { + local ret=1 + local -a context line state state_descr + local -A opt_args + + _arguments -s -S : \ + $_composer_global_opts \ + '--format=[specify output format]:output format [text]:(json text)' \ + '--strict[return non-zero exit code if there are outdated packages]' \ + '1: :->pkgs' \ + '2:: :_guard "^-*" "version constraint"' \ + + '(a)' '(D I s t)'{-a,--available}'[display only available packages]' \ + + '(A)' '(D I s t)--all[display all packages]' \ + + '(D)' '(a A p s)'{-D,--direct}'[display only packages directly required by root package]' \ + + '(i)' '(s)'{-i,--installed}'[display only installed packages]' \ + + '(I)' '(a A s t)*--ignore=[ignore specified package (with -o)]: :__composer_packages' \ + + '(l)' '(s t)'{-l,--latest}'[display only latest version of installed packages]' \ + + '(m)' '(a A s t)'{-m,--minor-only}'[display only packages with minor semver updates (with -o)]' \ + + '(N)' '(P)'{-N,--name-only}'[display package names only]' \ + + '(o)' '(l t s)'{-o,--outdated}'[like -l, but display only outdated packages]' \ + + '(p)' '(D)'{-p,--platform}'[display only PHP platform packages]' \ + + '(P)' '(a A N)'{-P,--path}'[display package file paths]' \ + + '(s)' '(a A I l o t)'{-s,--self}'[display root package information]' \ + + '(t)' '(a A I l o s)'{-t,--tree}'[display in tree format]' \ + && ret=0 + + [[ $state == pkgs ]] && + if [[ -n $opt_args[(i)[aA]-*] ]]; then + __composer_packages && ret=0 + else + __composer_packages --vendor && ret=0 + fi + + return ret +} + +(( $+functions[_composer_suggests] )) || +_composer_suggests() { + _arguments -s -S : \ + $_composer_global_opts \ + '(--by-suggestion)--by-package[group results by suggesting package]' \ + '(--by-package)--by-suggestion[group results by suggested package]' \ + '--no-dev[exclude suggestions from require-dev packages]' \ + '*: :__composer_packages --vendor' +} + +(( $+functions[_composer_update] )) || +_composer_update() { + _arguments -s -S : \ + $_composer_global_opts \ + '(--no-dev)--dev[install require-dev packages]' \ + '--dry-run[do not actually update (implies -v)]' \ + '(-i -n --interactive --no-interaction)'{-i,--interactive}'[update with interactive interface]' \ + '--ignore-platform-reqs[ignore PHP platform requirements]' \ + '--lock[update composer.lock hash only]' \ + '!(--no-plugins)--no-custom-installers' \ + '(--dev)--no-dev[do not install require-dev packages]' \ + '--no-progress[do not display download progress]' \ + '--no-scripts[prevent execution of scripts defined in composer.json]' \ + '--no-suggest[do not display package suggestions]' \ + '(--prefer-source)--prefer-dist[prefer installation from dist]' \ + '--prefer-lowest[prefer lowest versions of dependencies]' \ + '(--prefer-dist)--prefer-source[prefer installation from source]' \ + '--prefer-stable[prefer stable versions of dependencies]' \ + '--root-reqs[update only root-package dependencies]' \ + '*:: :__composer_packages --vendor' \ + + '(a)' '(n o)'{-a,--classmap-authoritative}'[autoload from class maps only (implies -o)]' \ + + '(A)' '(n)--apcu-autoloader[use APCu to cache found/not-found classes]' \ + + '(n)' '(a A o)--no-autoload[skip autoloader generation]' \ + + '(o)' '(n)'{-o,--optimize}'[use class maps for PSR-0/4 packages]' \ + + '(u)' \ + '--with-dependencies[update dependencies of whitelisted packages, except root requirements]' \ + '--with-all-dependencies[update all dependencies of whitelisted packages]' +} + +(( $+functions[_composer_validate] )) || +_composer_validate() { + _arguments -s -S : \ + $_composer_global_opts \ + '(-A --with-dependencies)'{-A,--with-dependencies}'[validate composer.json of installed dependencies]' \ + '--no-check-all[do not validate completely]' \ + '--no-check-lock[ignore out-of-date composer.lock]' \ + '--no-check-publish[ignore publish errors]' \ + '--strict[return non-zero exit code for warnings as well as errors]' \ + '1::composer.json file:_files -g "*.json(#q-.)"' +} + +_composer() { + local ret=1 tmp helps cmd + local -a context line state state_descr + local -A opt_args + + # These are meant for use by helper functions; we check for set-ness first in + # case of `composer global` + (( $+_composer_cmd )) || { + local _composer_cmd=$words[1] _composer_is_global=0 _composer_work_dir= + local -a _composer_cmds _composer_global_opts _composer_cache_pkgs + local -A _composer_cmd_aliases + + # alias -> canonical (mappings derived from source) + _composer_cmd_aliases=( + browse home + clearcache clear-cache + dumpautoload dump-autoload + i install + info show + selfupdate self-update + u update + upgrade update + why depends + why-not prohibits + ) + # Official commands (see note at top about custom ones) + _composer_cmds=( + about:'display short information about Composer' + archive:'create archive of project/package' + check-platform-reqs:'check that platform requirements are satisfied' + clear{-,}cache:'clear internal package cache' + config:'set configuration options' + create-project:'create new project from package' + {depends,why}:'display packages depending on package' + diagnose:'diagnose system problems' + dump{-,}autoload:'dump class autoloader' + exec:'execute vendored binary/script' + global:'run command in global Composer directory' + help:'display help information' + {home,browse}:'open package home page or repository in browser' + init:'create basic composer.json' + {install,i}:'install packages in composer.{lock,json}' + licenses:'display dependency license information' + list:'display supported commands' + outdated:'display outdated packages' + {prohibits,why-not}:'display packages preventing package from being installed' + remove:'remove and uninstall required package' + require:'add and install required package' + run-script:'run scripts in composer.json' + search:'search packages' + self{-,}update:'upgrade Composer' + {show,info}:'display package information' + status:'display locally modified packages' + suggests:'display package suggestions' + {update,u,upgrade}:'upgrade packages in composer.json and update composer.lock' + validate:'validate composer.{json,lock}' + ) + # Global options (can be used both before and after a command) + _composer_global_opts=( + '(-h --help)'{-h,--help}'[display help information]' + '(: * -)'{-V,--version}'[display version information]' + + # Symfony handles -vv and -vvv specially + '(-q -v --quiet --verbose)'{-q,--quiet}'[reduce output verbosity]' + '(-q --quiet --verbose)*-v[increase output verbosity]' + '(-q --quiet --verbose)--verbose[increase output verbosity]' + + '(--no-ansi)--ansi[force ANSI (color) output]' + '(--ansi)--no-ansi[disable ANSI (color) output]' + + '(-n --no-interaction)'{-n,--no-interaction}'[run non-interactively]' + + '--no-plugins[disable plug-ins]' + '--profile[display timing and memory usage information]' + '(-d --working-dir)'{-d+,--working-dir=}'[specify working directory]:working directory:_files -/' + ) + + # Use caching by default; Composer is very slow + zstyle -s ":completion:*:*:$service:*" cache-policy tmp + [[ -n $tmp ]] || + zstyle ":completion:*:*:$service:*" cache-policy __composer_cache_policy + + zstyle -s ":completion:*:*:$service:*" use-cache tmp + [[ -n $tmp ]] || + zstyle ":completion:*:*:$service:*" use-cache yes + } + + # Symfony's Application class naively intercepts --version and --help no + # matter where they appear on the command line. --version is handled + # sufficiently by the global option spec above; for --help, we need to + # simulate the help command + [[ -n $words[(r)(-h|--help)] ]] && { + helps=${#${(M)words[2,CURRENT]:#(-h|--help)}} + words=( "$words[1]" help "${(@)words[2,CURRENT]:#(-h|--help)}" ) + (( CURRENT -= helps - 1 )) + __composer_prune_global_opts -h --help + } + + _arguments -s -S -A '-*' : \ + $_composer_global_opts \ + '1: :__composer_commands' \ + '*:: :->next' \ + && ret=0 + __composer_update_work_dir + + [[ $state == next ]] && { + # Resolve abbreviated/aliased command names and ensure that $words[1] works + # as expected in our helper functions + __composer_resolve_cmd cmd $words[1] + words[1]=$_composer_cmd + + # Don't offer global options again after they've been given + __composer_prune_global_opts ${(k)opt_args} + # This is intentionally done twice + [[ $cmd == help ]] && __composer_prune_global_opts -h --help + + if (( $+functions[_composer_$cmd] )); then + _composer_$cmd "$@" && ret=0 + else + _arguments -s -S : $_composer_global_opts && ret=0 + fi + } + + return ret +} + +_composer "$@" diff --git a/Completion/Unix/Command/_git b/Completion/Unix/Command/_git index 093464625..0ebbbe861 100644 --- a/Completion/Unix/Command/_git +++ b/Completion/Unix/Command/_git @@ -2017,15 +2017,6 @@ _git-subtree () { (( $+functions[_git-tag] )) || _git-tag () { - local -a message_opts - - if (( words[(I)-[asu]] )); then - message_opts=( - '(-m --message -F --file)'{-m+,--message=}'[specify tag message]:message' - '(-m --message -F --file)'{-F+,--file=}'[read tag message from given file]:message file:_files' - ) - fi - _arguments \ - creation \ '(-a --annotate -s --sign -u --local-user)'{-a,--annotate}'[create an unsigned, annotated tag]' \ @@ -2035,7 +2026,8 @@ _git-tag () { '(-f --force)'{-f,--force}'[replace existing tag]' \ '--create-reflog[create a reflog]' \ '--cleanup=[cleanup message]:mode:((verbatim\:"no cleanup" whitespace\:"remove leading and trailing whitespace" strip\:"remove leading and trailing whitespace and comments"))' \ - $message_opts \ + '(-m --message -F --file)'{-F+,--file=}'[read tag message from given file]:message file:_files' \ + '(-m --message -F --file)'{-m+,--message=}'[specify tag message]:message' \ ': :__git_tags' \ ':: :__git_commits' \ - deletion \ diff --git a/Completion/Unix/Command/_ipsec b/Completion/Unix/Command/_ipsec new file mode 100644 index 000000000..631d2bc9a --- /dev/null +++ b/Completion/Unix/Command/_ipsec @@ -0,0 +1,179 @@ +#compdef ipsec strongswan + +# Completion for the ipsec script (aka strongswan on some systems) provided by +# FreeS/WAN, Openswan, Libreswan, and strongSwan. See also strongSwan's swanctl. +# +# As with swanctl, elevated privileges are usually required to complete SA names +# and the like; consider setting the gain-privileges style as follows: +# zstyle ':completion:*:(ipsec|strongswan)/*' gain-privileges yes +# +# @todo We don't complete pool names or virtual IPs + +# Complete connection (IKE SA) names and optionally SA/instance names +# --instances => also complete SA/instance names +(( $+functions[_ipsec_connections] )) || +_ipsec_connections() { + local -a instances tmp ipsec_conns ipsec_insts + + zparseopts -D -E -a instances - -instances + + tmp=( ${(f)${"$( + _call_program -p ipsec-status ${_ipsec_cmd:-$words[1]} statusall + )"#*$'\n'[[:space:]]#[Cc]onnections:[[:space:]]#$'\n'}} ) + tmp=( ${(@M)tmp:#[[:space:]]#[^[:space:]]##:[[:space:]]##?*} ) + tmp=( ${(@)${(@)tmp##[[:space:]]##}%%:*} ) + + ipsec_conns=( ${(@)tmp%%['[{']<->['}]']} ) + ipsec_insts=( ${(@M)tmp:#*['[{']<->['}]']} ) + + (( $#ipsec_conns )) || { + _message -e connections 'connection name' + return + } + + tmp=( 'connections:connection name:compadd -a - ipsec_conns' ) + (( $#instances && $#ipsec_insts )) && + tmp+=( 'instances:connection SA/instance name:compadd -a - ipsec_insts' ) + + _alternative $tmp +} + +# Complete arguments to /usr/lib/ipsec/starter. This is rarely invoked directly, +# and there is almost no documentation on it, but the start/restart commands +# pass options to it +(( $+functions[_ipsec_starter] )) || +_ipsec_starter() { + _arguments : \ + '(--nofork)--attach-gdb[start daemon under gdb (implies --nofork)]' \ + '--auto-update[specify select time-out]:select time-out (seconds)' \ + '--conf[specify path to ipsec.conf]:ipsec.conf file:_files' \ + '--daemon[specify daemon name]:daemon name' \ + '--nofork[do not fork daemon]' \ + + '(d)' \ + '--debug[set log level 2]' \ + '--debug-more[set log level 3]' \ + '--debug-all[set log level 4]' \ + '--nolog[set log level 0]' +} + +_ipsec() { + local ret=1 variant _ipsec_cmd=$words[1] + local -a context line state state_descr tmp + local -A opt_args + + _pick_variant -r variant \ + freeswan='(#i)frees/#wan' \ + libreswan='(#i)libreswan' \ + openswan='(#i)openswan' \ + strongswan='(#i)strongswan' \ + unix \ + --version + + # Provide only basic completion for non-strongSwan implementations + if [[ $variant == unix ]]; then + _default + return + elif [[ $variant == (free|libre|open)* ]]; then + tmp=( ${(f)"$( _call_program ipsec-help $words[1] --help )"} ) + tmp=( ${(@M)tmp:#($' '|$'\t')*} ) + tmp=( ${(@)tmp:#*[':/<>()[]']*} ) + tmp=( ${(f)${(F)tmp//[[:space:]]##/$'\n'}} ) + + _arguments -S -A '-*' : \ + '(: * -)--help[display help information]' \ + '(: * -)--version[display version information]' \ + "(-)1:command:(${(j< >)${(@q-)tmp}})" \ + '(-)2: :_default' + return + fi + + _arguments -A '-*' \ + '(: * -)--confdir[display path to configuration directory (IPSEC_CONFDIR)]' \ + '(: * -)--copyright[display copyright information]' \ + '(: * -)--directory[display path to libexec/utility directory (IPSEC_DIR)]' \ + '(: * -)--help[display help information]' \ + '(: * -)--piddir[display path to PID directory (IPSEC_PIDDIR)]' \ + '(: * -)--version[display version information]' \ + '(: * -)--versioncode[display brief version information]' \ + '1:command:(( + down\:"terminate IPsec connection/SA" + down-srcip\:"terminate IKE SAs by client virtual IP" + leases\:"display IP address/pool status" + listaacerts\:"display X.509 authorization authority certificates" + listacerts\:"display X.509 attribute certificates" + listalgs\:"display loaded algorithms" + listall\:"execute all list commands" + listcacerts\:"display X.509 certificate authority certificates" + listcainfos\:"display certificate authority information" + listcerts\:"display X.509/OpenPGP certificates" + listcounters\:"display IKE counter information" + listcrls\:"display certificate revocation lists" + listgroups\:"display groups for user authorization profiles" + listocsp\:"display OCSP revocation information" + listocspcerts\:"display X.509 OCSP signer certificates" + listplugins\:"display loaded plug-in features" + listpubkeys\:"display RSA public keys" + purgecerts\:"purge cached certificates" + purgecrl\:"purge cached certificate revocation lists" + purgeike\:"purge IKE SAs without a quick mode or CHILD_SA" + purgeocsp\:"purge cached OCSP information" + reload\:"reload entire configuration (send SIGUSR1)" + rereadacerts\:"re-read attribute certificates" + rereadaacerts\:"flush and re-read authorization authority certificates" + rereadall\:"execute all re-read commands" + rereadcacerts\:"flush and re-read certificate authority certificates" + rereadcrls\:"re-read certificate revocation lists" + rereadocspcerts\:"re-read OCSP certificates" + rereadsecrets\:"flush and re-read secrets" + resetcounters\:"reset IKE counter information" + restart\:"equivalent to stop + start" + route\:"insert kernel IPsec policy for connection" + start\:"start IKE daemon" + status\:"display concise connection status" + statusall\:"display detailed connection status" + stop\:"terminate all IPsec connections and stop IKE daemon" + stroke\:"issue stroke command" + unroute\:"remove kernel IPsec policy for connection" + up\:"bring up IPsec connection" + update\:"reload changes in configuration (send SIGHUP)" + ))' \ + '*:: :->next' \ + && ret=0 + + [[ $state == next ]] && + case $words[1] in + down) + _arguments : '1: :_ipsec_connections --instances' && ret=0 + ;; + listcounters|resetcounters|route|status|statusall|unroute|up) + _arguments : '1: :_ipsec_connections' && ret=0 + ;; + down-srcip) + _arguments : \ + '1:virtual IP address (start)' \ + '2::virtual IP address (end)' \ + && ret=0 + ;; + leases) + _arguments : '1:pool name' '2::virtual IP address' && ret=0 + ;; + list*~list(counters|plugins)) + _arguments : '--utc[use UTC for time fields]' && ret=0 + ;; + start|restart) + _ipsec_starter && ret=0 + ;; + stroke) + _arguments -s -S -A '-*' \ + '(: * -)'{-h,--help}'[display help information]' \ + '(-d --daemon)'{-d+,--daemon=}'[specify daemon name]:daemon name' \ + '1: :_guard "^-*" "stroke command"' \ + '*:stroke command argument:_default' \ + && ret=0 + ;; + esac + + return ret +} + +_ipsec "$@" diff --git a/Completion/Unix/Command/_swanctl b/Completion/Unix/Command/_swanctl new file mode 100644 index 000000000..ba2f5402d --- /dev/null +++ b/Completion/Unix/Command/_swanctl @@ -0,0 +1,225 @@ +#compdef swanctl + +# Completion for strongSwan's swanctl. See also ipsec, which is deprecated but +# still supported by strongSwan, and also used by Openswan/Libreswan. +# +# Note that in most cases elevated privileges are required to connect to the +# VICI socket, so the gain-privileges style may be necessary to complete SA +# names and the like: zstyle ':completion:*:swanctl/*' gain-privileges yes +# +# Other notes: +# - One of swanctl's selling points is that it can provide 'raw' structured +# responses for scripting, etc. In practice, though, the output formatted for +# humans seems easier to 'parse' from the shell than the plist-like raw output +# - @todo We don't complete authority names, pool names, peer IPs, etc. + +# Complete connection names, SA names, or SA unique IDs. The distinctions +# between concepts like 'connections' and 'SAs' are very blurry here, partially +# for convenience and partially due to author confusion +# --child => complete only child/CHILD_SA names/IDs +# --ids => complete unique SA IDs rather than connection/SA names +# --ike => complete only connection/IKE_SA names/IDs +(( $+functions[_swanctl_connections] )) || +_swanctl_connections() { + local i which + local -a expl tmp matches + local -A opts + + zparseopts -D -E -A opts - -child -ids -ike + + tmp=( ${(@M)${(f)"$( + _call_program -p swanctl-sas $words[1] -l + )"}:#[^:]##: \#<->*} ) + (( $+opts[--ids] )) || tmp+=( ${(@M)${(f)"$( + _call_program -p swanctl-conns $words[1] -L + )"}:#[^:]##: ([A-Z][A-Za-z0-9]#|),*} ) + + for i in $tmp; do + if (( $+opts[--child] )) && [[ $i != [[:space:]]* ]]; then + continue + elif (( $+opts[--ike] )) && [[ $i == [[:space:]]* ]]; then + continue + fi + + # <name>: #<unique id>, ... + i=${i//[#:,]/ } + + if (( $+opts[--ids] )); then + matches+=( "${i[(w)2]}:${i[(w)1]} #${i[(w)2]}" ) + else + matches+=( ${i[(w)1]} ) + fi + done + + if (( $+opts[--ids] )); then + matches=( ${(onu)matches} ) + if (( $+opts[--child] )); then + which=CHILD_ + elif (( $+opts[--ike] )); then + which=IKE_ + fi + _describe -x2Vt sa-ids "${which}SA unique ID" matches + else + if (( $+opts[--child] )); then + which='child ' + elif (( $+opts[--ike] )); then + which='IKE ' + fi + _wanted -x connections expl "${which}connection/SA name" compadd - $matches + fi +} + +_swanctl() { + # Although swanctl will correctly parse multiple short options in the first + # word, as in `swanctl -lh`, it won't actually *do* anything with the + # subsequent options -- so we'll require that they be separated. Also, --help + # doesn't take any further options, so just stop if we've got that + if (( CURRENT == 2 )) || [[ $words[2] == (-h*|--help) ]]; then + _arguments : \ + '(-)'{-a,--load-pools}'[(re)load pool configuration]' \ + '(-)'{-A,--list-pools}'[display loaded pool configurations]' \ + '(-)'{-b,--load-authorities}'[(re)load authority configuration]' \ + '(-)'{-B,--list-authorities}'[display loaded authority configurations]' \ + '(-)'{-c,--load-conns}'[(re)load connection configuration]' \ + '(-)'{-C,--counters}'[display or reset IKE event counters]' \ + '(-)'{-d,--redirect}'[redirect IKE_SA]' \ + '(-)'{-f,--flush-certs}'[flush cached certificates]' \ + '(-)'{-g,--list-algs}'[display loaded algorithms]' \ + '(-)'{-h,--help}'[display help information]' \ + '(-)'{-i,--initiate}'[initiate a connection]' \ + '(-)'{-l,--list-sas}'[display currently active IKE_SAs]' \ + '(-)'{-L,--list-conns}'[display loaded configurations]' \ + '(-)'{-m,--monitor-sa}'[monitor for IKE_SA and CHILD_SA changes]' \ + '(-)'{-p,--install}'[install trap or shunt policy]' \ + '(-)'{-P,--list-pols}'[display currently installed policies]' \ + '(-)'{-q,--load-all}'[load credentials, authorities, pools, and connections]' \ + '(-)'{-r,--reload-settings}'[reload daemon strongswan.conf]' \ + '(-)'{-R,--rekey}'[rekey SA]' \ + '(-)'{-s,--load-creds}'[(re)load credentials]' \ + '(-)'{-S,--stats}'[display daemon statistics]' \ + '(-)'{-t,--terminate}'[terminate connection]' \ + '(-)'{-T,--log}'[trace logging output]' \ + '(-)'{-u,--uninstall}'[uninstall trap or shunt policy]' \ + '(-)'{-v,--version}'[display version information]' \ + '(-)'{-x,--list-certs}'[display stored certificates]' + return + fi + + local ret=1 cmd + local -a args cert_flags cert_types + + cert_flags=( aa any ca none ocsp ) + cert_types=( ocsp_response pubkey x509 x509_ac x509_crl ) + + if [[ $words[2] == -[^-]* ]]; then + cmd=${(M)words[2]#??} + else + cmd=$words[2] + fi + words=( $words[1] "${(@)words[3,-1]}" ) + (( CURRENT-- )) + + # Technically, only -v, -u, and -+ are truly global command options. However, + # in practice, all commands also support -h, -P, and -r + args=( + '(: * -)'{-h,--help}'[display help information]' + '(-P -r --pretty --raw)'{-P,--pretty}'[dump raw response message in pretty print]' + '(-P -r --pretty --raw)'{-r,--raw}'[dump raw response message]' + # https://wiki.strongswan.org/projects/strongswan/wiki/LoggerConfiguration + # https://github.com/strongswan/strongswan/blob/master/src/libstrongswan/utils/debug.h + '(-v --debug)'{-v+,--debug=}'[specify debug level]:debug level [1]:(( + -1\:"absolutely silent (SILENT)" + 0\:"basic auditing (AUDIT)" + 1\:"generic control flow with errors (CTRL)" + 2\:"detailed control flow (DIAG)" + 3\:"raw binary blobs (RAW)" + 4\:"sensitive data (PRIVATE)" + ))' + '(-u --uri)'{-u+,--uri=}'[specify service URI to connect to]:VICI service URI:_urls' + '(-+ --options)'{'-\++',--options=}'[read command-line options from specified file]:options file:_files' + ) + + case $cmd in + -A|--list-pools) args+=( + '(-n --name)'{-n+,--name=}'[filter by specified pool name]:pool name' + '(-l --leases)'{-l,--leases}'[display leases of each pool]' + ) ;; + -B|--list-authorities) args+=( + '(-n --name)'{-n+,--name=}'[filter by specified authority name]:authority name' + ) ;; + -C|--counters) args+=( + '(-a -n --all --name)'{-a,--all}'[display/reset counters for all tracked connections]' + '(-a -n --all --name)'{-n+,--name=}'[specify connection name]: :_swanctl_connections --ike' + '(-r --reset)'{-r,--reset}'[reset counters]' + ) ;; + -d|--redirect) args+=( + '(-d --peer-id)'{-d+,--peer-id=}'[redirect by IKE_SA matching specified peer identity]:peer identity' + '(-g --gateway)'{-g+,--gateway=}'[redirect to specified gateway]:target gateway' + '(-i --ike)'{-i+,--ike=}'[redirect by specified IKE_SA name]: :_swanctl_connections --ike' + '(-I --ike-id)'{-I+,--ike-id=}'[redirect by specified IKE_SA unique ID]: :_swanctl_connections --ids --ike' + '(-p --peer-ip)'{-p+,--peer-ip=}'[redirect by IKE_SA matching specified peer IP]:peer IP address' + ) ;; + -f|--flush-certs) args+=( + '(-t --type)'{-t+,--type=}"[filter by specified certificate type]:certificate type:( + ${(j< >)${(@q-)cert_types}} + )" + ) ;; + -i|--initiate) args+=( + '(-c --child)'{-c+,--child=}'[specify CHILD_SA name]: :_swanctl_connections --child' + '(-i --ike)'{-i+,--ike=}"[specify CHILD_SA's connection name]: :_swanctl_connections --ike" + '(-t --timeout)'{-t+,--timeout=}'[specify timeout before detaching]:timeout (seconds)' + ) ;; + -l|--list-sas) args+=( + '(-i --ike)'{-i+,--ike=}'[filter by specified IKE_SA name]: :_swanctl_connections --ike' + '(-I --ike-id)'{-I+,--ike-id=}'[filter by specified IKE_SA unique ID]: :_swanctl_connections --ids --ike' + '(-n --noblock)'{-n,--noblock}'[do not wait for IKE_SAs in use]' + ) ;; + -p|-u|--install|--uninstall) args+=( + '(-c --child)'{-c+,--child=}'[specify CHILD_SA name]: :_swanctl_connections --child' + '(-i --ike)'{-i+,--ike=}"[specify CHILD_SA's connection name]: :_swanctl_connections --ike" + ) ;; + -P|--list-pols) args+=( + '(-c --child)'{-c+,--child=}'[filter by specified CHILD_SA name]: :_swanctl_connections --child' + '(-d --drop)'{-d,--drop}'[list drop policies]' + '(-p --pass)'{-p,--pass}'[list bypass policies]' + '(-t --trap)'{-t,--trap}'[list trap policies]' + ) ;; + -q|-s|--load-all|--load-creds) args+=( + '(-c --clear)'{-c,--clear}'[clear previously loaded credentials]' + '(-n --noprompt)'{-n,--noprompt}'[do not prompt for passwords]' + ) ;; + -R|--rekey) args+=( + '(-c --child)'{-c+,--child=}'[rekey by specified CHILD_SA name]: :_swanctl_connections --child' + '(-C --child-id)'{-C+,--child-id=}'[rekey by specified CHILD_SA unique ID]: :_swanctl_connections --ids --child' + '(-i --ike)'{-i+,--ike=}'[rekey by specified IKE_SA name]: :_swanctl_connections --ike' + '(-I --ike-id)'{-I+,--ike-id=}'[rekey by specified IKE_SA unique ID]: :_swanctl_connections --ids --ike' + ) ;; + -t|--terminate) args+=( + '(-t --timeout)'{-t+,--timeout=}'[specify timeout before detaching]:timeout (seconds)' + '(-c --child)'{-c+,--child=}'[terminate by specified CHILD_SA name]: :_swanctl_connections --child' + '(-C --child-id)'{-C+,--child-id=}'[terminate by specified CHILD_SA unique ID]: :_swanctl_connections --ids --child' + '(-i --ike)'{-i+,--ike=}'[terminate by specified IKE_SA name]: :_swanctl_connections --ike' + '(-I --ike-id)'{-I+,--ike-id=}'[terminate by specified IKE_SA unique ID]: :_swanctl_connections --ids --ike' + ) ;; + -v|--version) args+=( + '(-d --daemon)'{-d,--daemon}'[query daemon version]' + ) ;; + -x|--list-certs) args+=( + '(-f --flag)'{-f+,--flag=}"[filter by specified X.509 certificate flag]:certificate flag:( + ${(j< >)${(@q-)cert_flags}} + )" + '(-p --pem)'{-p,--pem}'[display PEM encoding of certificate]' + '(-s --subject)'{-s+,--subject=}'[filter by specified certificate subject]:certificate subject' + '(-S --short)'{-S,--short}'[omit some certificate details]' + '(-t --type)'{-t+,--type=}"[filter by specified certificate type]:certificate type:( + ${(j< >)${(@q-)cert_types}} + )" + '(-u --utc)'{-u,--utc}'[use UTC for time fields]' + ) ;; + esac + + _arguments -s -S : $args && ret=0 + return ret +} + +_swanctl "$@" diff --git a/Completion/Unix/Type/_urls b/Completion/Unix/Type/_urls index e81eaac05..5d4990442 100644 --- a/Completion/Unix/Type/_urls +++ b/Completion/Unix/Type/_urls @@ -81,7 +81,7 @@ case "$scheme" in return fi ;; - file) + file|unix) [[ -prefix //(127.0.0.1|localhost)/ ]] && compset -P '//(127.0.0.1|localhost)' [[ -prefix /// ]] && compset -P // if ! compset -P //; then diff --git a/Completion/Zsh/Command/_exec b/Completion/Zsh/Command/_exec new file mode 100644 index 000000000..8de341a02 --- /dev/null +++ b/Completion/Zsh/Command/_exec @@ -0,0 +1,9 @@ +#compdef exec + +[[ $service == exec ]] && precommands+=( exec ) + +_arguments -s -S -A '-*' : \ + '-a+[set argv\[0\] to specified string]:argv[0] string' \ + '-c[clear environment]' \ + '-l[simulate login shell (prepend - to argv\[0\])]' \ + '*:: : _normal' diff --git a/Completion/Zsh/Command/_precommand b/Completion/Zsh/Command/_precommand index f57e668c6..c9eef78af 100644 --- a/Completion/Zsh/Command/_precommand +++ b/Completion/Zsh/Command/_precommand @@ -1,4 +1,4 @@ -#compdef - nohup eval time rusage noglob nocorrect exec catchsegv aoss hilite eatmydata setsid +#compdef - nohup eval time rusage noglob nocorrect catchsegv aoss hilite eatmydata # precommands is made local in _main_complete precommands+=($words[1]) diff --git a/Config/version.mk b/Config/version.mk index 099b9bb58..8d59d6dc4 100644 --- a/Config/version.mk +++ b/Config/version.mk @@ -27,5 +27,5 @@ # This must also serve as a shell script, so do not add spaces around the # `=' signs. -VERSION=5.6.2-test-2 -VERSION_DATE='December 19, 2018' +VERSION=5.6.2-test-3 +VERSION_DATE='January 5, 2019' diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index fd29ca3a5..cc9832379 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -2362,6 +2362,8 @@ then all currently active child processes are waited for. Each var(job) can be either a job specification or the process ID of a job in the job table. The exit status from this command is that of the job waited for. +If var(job) represents an unknown job or process ID, a warning is printed +(unless the tt(POSIX_BUILTINS) option is set) and the exit status is 127. It is possible to wait for recent processes (specified by process ID, not by job) that were running in the background even if the process has diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo index 25b3d5736..bc182eb7b 100644 --- a/Doc/Zsh/options.yo +++ b/Doc/Zsh/options.yo @@ -753,7 +753,9 @@ pindex(NOUNSET) cindex(parameters, substituting unset) cindex(unset parameters, substituting) item(tt(UNSET) (tt(PLUS()u), ksh: tt(PLUS()u)) <K> <S> <Z>)( -Treat unset parameters as if they were empty when substituting. +Treat unset parameters as if they were empty when substituting, and as if +they were zero when reading their values in arithmetic expansion and +arithmetic commands. Otherwise they are treated as an error. ) pindex(WARN_CREATE_GLOBAL) diff --git a/Etc/creating-a-release.txt b/Etc/creating-a-release.txt index 2445151f5..fd52da86a 100644 --- a/Etc/creating-a-release.txt +++ b/Etc/creating-a-release.txt @@ -11,6 +11,10 @@ To create a zsh release: README NEWS + README should document compatibility-breaking changes. Generally, NEWS should + document new features and major bug fixes (but not routine fixes or changes to + completion/contrib functions). + - Commit those changes with an "unposted" ChangeLog entry. git commit -am "Test release: 5.5.1-test-1." && @@ -13,6 +13,10 @@ highlighting. On 88 and 256 colour terminals, a new zsh/nearcolor module allows colours specified with hex triplets to be matched against the nearest available colour. +The zsh/datetime module's strftime builtin now accepts an argument +specifying the nanoseconds time component; both arguments can be omitted +to use the current time. + Changes from 5.6.1 to 5.6.2 --------------------------- @@ -63,13 +67,23 @@ In shell patterns, [[:blank:]] now honours the locale instead of matching exclusively on space and tab, like for the other POSIX character classes or for extended regular expressions. -Nanosecond precision on file times is supported in the module +The zsh/system module now provides the PID of the last process +substitution via $sysparams[procsubstpid]. + +Time formatting via the %D prompt escape now offers nanosecond +precision with the %. and %N format specifiers. Additionally, +nanosecond precision on file times is supported in the module zsh/stat. +The zsh/mathfunc module now includes a log2() function. + +The parameter ZLE_RECURSIVE has been added to indicate the +current ZLE recursion level. + Changes from 5.5 to 5.5.1 ------------------------- -Apart from a fix for a configuration problem finding singal names from +Apart from a fix for a configuration problem finding signal names from (some) recent versions of glibc, there are only minor changes. Changes from 5.4.2 to 5.5 @@ -89,9 +103,19 @@ typeset -A assoc=([key1]=val1 [key2]=val2) is allowed for compatibility with other shells. In the case of normal arrays the new syntax can be mixed with the old. +The %E, %S, and %U TIMEFMT specifiers now support m and u prefixes +(e.g., %mE) to output times in milliseconds and microseconds, +respectively. + +The option CHECK_RUNNING_JOBS was added to control whether zsh should +check for running jobs in addition to suspended ones with CHECK_JOBS. +It is enabled by default. + Changes from 5.3.1 to 5.4.2 --------------------------- +There are only minor changes between 5.4 and 5.4.2. + The 'exec' and 'command' precommand modifiers, and options to them, are now parsed after parameter expansion. Previously, both the modifier and any options to it were parsed between alias expansion and parameter @@ -111,6 +135,13 @@ turned on for an individual function with "functions -W". zmodload now has an option -s to be silent on a failure to find a module but still print other errors. +The autoload builtin gained several new features to load functions from a +file path determined at the time of definition rather than at the time of +loading. + +The zsh/parameter module now exposes the file paths associated with +shell functions via the parameters functions_source and +dis_functions_source. Changes from 5.2 to 5.3.1 ------------------------- @@ -42,6 +42,10 @@ array. The gen-applied-string hook is unaffected; it still receives the patches in reverse order, from last applied to first applied. +2) The option NO_UNSET now also applies when reading values from +variables without a preceding '$' sign in shell arithmetic expansion +and in the double-parentheses and 'let' arithmetic commands. + Incompatibilities between 5.5.1 and 5.6.2 ------------------------------------------ diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c index 18c7fb58e..521c15a5b 100644 --- a/Src/Modules/datetime.c +++ b/Src/Modules/datetime.c @@ -148,7 +148,7 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func)) } else if (*argv[2] == '\0' || *endptr != '\0') { zwarnnam(nam, "%s: invalid decimal number", argv[2]); return 1; - } else if (ts.tv_nsec < 0) { + } else if (ts.tv_nsec < 0 || ts.tv_nsec > 999999999) { zwarnnam(nam, "%s: invalid nanosecond value", argv[2]); return 1; } diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 87d13afc1..fe87409cb 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -3256,6 +3256,15 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) rpl = strlen(rpre); rsl = strlen(rsuf); } + else + { + for (p = rpre; *p; ++p) + if (*p == Dash) + *p = '-'; + for (p = rsuf; *p; ++p) + if (*p == Dash) + *p = '-'; + } untokenize(lpre); untokenize(lsuf); diff --git a/Src/jobs.c b/Src/jobs.c index ed9f81f26..73d7f26da 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -1910,7 +1910,7 @@ getjob(const char *s, const char *prog) /* "%%", "%+" and "%" all represent the current job */ if (*s == '%' || *s == '+' || !*s) { if (curjob == -1) { - if (prog) + if (prog && !isset(POSIXBUILTINS)) zwarnnam(prog, "no current job"); returnval = -1; goto done; @@ -1921,7 +1921,7 @@ getjob(const char *s, const char *prog) /* "%-" represents the previous job */ if (*s == '-') { if (prevjob == -1) { - if (prog) + if (prog && !isset(POSIXBUILTINS)) zwarnnam(prog, "no previous job"); returnval = -1; goto done; @@ -1944,7 +1944,7 @@ getjob(const char *s, const char *prog) returnval = jobnum; goto done; } - if (prog) + if (prog && !isset(POSIXBUILTINS)) zwarnnam(prog, "%%%s: no such job", s); returnval = -1; goto done; @@ -1962,7 +1962,7 @@ getjob(const char *s, const char *prog) returnval = jobnum; goto done; } - if (prog) + if (prog && !isset(POSIXBUILTINS)) zwarnnam(prog, "job not found: %s", s); returnval = -1; goto done; @@ -1976,7 +1976,8 @@ getjob(const char *s, const char *prog) } /* if we get here, it is because none of the above succeeded and went to done */ - zwarnnam(prog, "job not found: %s", s); + if (!isset(POSIXBUILTINS)) + zwarnnam(prog, "job not found: %s", s); returnval = -1; done: return returnval; @@ -2375,9 +2376,10 @@ bin_fg(char *name, char **argv, Options ops, int func) } } } else if ((retval = getbgstatus(pid)) < 0) { - zwarnnam(name, "pid %d is not a child of this shell", pid); + if (!isset(POSIXBUILTINS)) + zwarnnam(name, "pid %d is not a child of this shell", pid); /* presumably lastval2 doesn't tell us a heck of a lot? */ - retval = 1; + retval = 127; } thisjob = ocj; continue; @@ -2391,15 +2393,16 @@ bin_fg(char *name, char **argv, Options ops, int func) job = (*argv) ? getjob(*argv, name) : firstjob; firstjob = -1; if (job == -1) { - retval = 1; + retval = 127; break; } jstat = oldjobtab ? oldjobtab[job].stat : jobtab[job].stat; if (!(jstat & STAT_INUSE) || (jstat & STAT_NOPRINT)) { - zwarnnam(name, "%s: no such job", *argv); + if (!isset(POSIXBUILTINS)) + zwarnnam(name, "%s: no such job", *argv); unqueue_signals(); - return 1; + return 127; } /* If AUTO_CONTINUE is set (automatically make stopped jobs running * on disown), we actually do a bg and then delete the job table entry. */ diff --git a/Src/math.c b/Src/math.c index b08e05cb4..a38770073 100644 --- a/Src/math.c +++ b/Src/math.c @@ -342,6 +342,8 @@ getmathparam(struct mathvalue *mptr) mptr->pval = (Value)zhalloc(sizeof(struct value)); if (!getvalue(mptr->pval, &s, 1)) { + if (unset(UNSET)) + zerr("%s: parameter not set", mptr->lval); mptr->pval = NULL; if (isset(FORCEFLOAT)) { result.type = MN_FLOAT; diff --git a/Src/prompt.c b/Src/prompt.c index 568bfc2a9..135aca942 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -1764,7 +1764,12 @@ output_colour(int colour, int fg_bg, int use_tc, int truecol, char *buf) /* length of hex triplet always 7, don't need sprintf to count */ atrlen += buf ? sprintf(ptr, "#%02x%02x%02x", colour >> 16, (colour >> 8) & 0xff, colour & 0xff) : 7; - /* colour should only be > 7 if using termcap but let's be safe */ + /* colour should only be > 7 if using termcap but let's be safe. Update: + * currently other places in code don't always imply that colour > 7 => + * using-termcap - if zle_highlight will be non-default, then it will be + * used instead of termcap even for colour > 7. Here this just emits the + * color number, so it works fine for both zle_highlight and tercap cases + */ } else if (use_tc || colour > 7) { char digbuf[DIGBUFSIZE]; sprintf(digbuf, "%d", colour); @@ -1995,6 +2000,7 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) char *ptr; int do_free, is_prompt = (flags & TSC_PROMPT) ? 1 : 0; int colour, tc, def, use_termcap, use_truecolor; + int is_default_zle_highlight = 1; if (fg_bg == COL_SEQ_FG) { colour = txtchangeget(atr, TXT_ATTR_FG_COL); @@ -2010,6 +2016,15 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) use_termcap = txtchangeisset(atr, TXT_ATTR_BG_TERMCAP); } + /* Test if current zle_highlight settings are customized, or + * the typical "standard" codes */ + if (0 != strcmp(fg_bg_sequences[fg_bg].start, fg_bg == COL_SEQ_FG ? "\e[3" : "\e[4") || + 0 != strcmp(fg_bg_sequences[fg_bg].def, "9") || /* the same in-fix for both FG and BG */ + 0 != strcmp(fg_bg_sequences[fg_bg].end, "m") /* the same suffix for both FG and BG */ + ) { + is_default_zle_highlight = 0; + } + /* * If we're not restoring the default, and either have a * colour value that is too large for ANSI, or have been told @@ -2020,7 +2035,7 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) * highlighting variables, so much of this shouldn't be * necessary at this point, but we might as well be safe. */ - if (!def && !use_truecolor && (colour > 7 || use_termcap)) { + if (!def && !use_truecolor && (is_default_zle_highlight && (colour > 7 || use_termcap))) { /* * We can if it's available, and either we couldn't get * the maximum number of colours, or the colour is in range. @@ -2046,9 +2061,10 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) } /* * Nope, that didn't work. - * If 0 to 7, assume standard ANSI works, otherwise it won't. + * If 0 to 7, assume standard ANSI works, if 8 to 255, assume + * typical 256-color escapes works, otherwise it won't. */ - if (colour > 7) + if (colour > 255) return; } @@ -2057,6 +2073,10 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) allocate_colour_buffer(); } + /* Build the reset-code: .start + .def + . end + * or the typical true-color code: .start + 8;2;%d;%d;%d + .end + * or the typical 256-color code: .start + 8;5;%d + .end + */ strcpy(colseq_buf, fg_bg_sequences[fg_bg].start); ptr = colseq_buf + strlen(colseq_buf); @@ -2067,6 +2087,8 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) } else if (use_truecolor) { ptr += sprintf(ptr, "8;2;%d;%d;%d", colour >> 16, (colour >> 8) & 0xff, colour & 0xff); + } else if (colour > 7 && colour <= 255) { + ptr += sprintf(ptr, "8;5;%d", colour); } else *ptr++ = colour + '0'; strcpy(ptr, fg_bg_sequences[fg_bg].end); diff --git a/Src/subst.c b/Src/subst.c index ff6750a59..60eb33390 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -2338,7 +2338,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, zerr("bad substitution"); return NULL; } - } else if (inbrace && inull(*s)) { + } else if (inbrace && inull(*s) && *s != Bnull) { /* * Handles things like ${(f)"$(<file)"} by skipping * the double quotes. We don't need to know what was diff --git a/Src/utils.c b/Src/utils.c index e43a3cdb4..32f600858 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -3334,19 +3334,28 @@ morefmt: #endif switch (*fmt++) { case '.': - if (ztrftimebuf(&bufsize, digs)) - return -1; + { + long fnsec = nsec; if (digs > 9) digs = 9; + if (ztrftimebuf(&bufsize, digs)) + return -1; if (digs < 9) { int trunc; - for (trunc = 8 - digs; trunc; trunc--) - nsec /= 10; - nsec = (nsec + 8) / 10; + long max = 100000000; + for (trunc = 8 - digs; trunc; trunc--) { + max /= 10; + fnsec /= 10; + } + max -= 1; + fnsec = (fnsec + 5) / 10; + if (fnsec > max) + fnsec = max; } - sprintf(buf, "%0*ld", digs, nsec); + sprintf(buf, "%0*ld", digs, fnsec); buf += digs; break; + } case '\0': /* Guard against premature end of string */ *buf++ = '%'; @@ -3837,7 +3846,7 @@ sepjoin(char **s, char *sep, int heap) char sepbuf[2]; if (!*s) - return heap ? "" : ztrdup(""); + return heap ? dupstring("") : ztrdup(""); if (!sep) { /* optimise common case that ifs[0] is space */ if (ifs && *ifs != ' ') { diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst index 5d3d460df..edc561582 100644 --- a/Test/A05execution.ztst +++ b/Test/A05execution.ztst @@ -341,3 +341,56 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline >17 >19 +# Test 'wait' for unknown job/process ID. + wait 1 + echo $? + wait %% + echo $? + wait %+ + echo $? + wait %- + echo $? + wait %1 + echo $? + wait %foo + echo $? + wait %\?bar +127:'wait' exit status and warning for unknown ID +>127 +>127 +>127 +>127 +>127 +>127 +?(eval):wait:1: pid 1 is not a child of this shell +?(eval):wait:3: %%: no such job +?(eval):wait:5: %+: no such job +?(eval):wait:7: %-: no such job +?(eval):wait:9: %1: no such job +?(eval):wait:11: job not found: foo +?(eval):wait:13: job not found: ?bar + +# Test 'wait' for unknown job/process ID (POSIX mode). + (setopt POSIX_BUILTINS + wait 1 + echo $? + wait %% + echo $? + wait %+ + echo $? + wait %- + echo $? + wait %1 + echo $? + wait %foo + echo $? + wait %\?bar) +127:'wait' exit status for unknown ID (POSIX mode) +>127 +>0 +>127 +>127 +>127 +>127 +# TBD: the 0 above is believed to be bogus and should also be turned +# into 127 when the ccorresponding bug is fixed in the main shell. diff --git a/Test/C01arith.ztst b/Test/C01arith.ztst index f1364ab36..9dfc065c8 100644 --- a/Test/C01arith.ztst +++ b/Test/C01arith.ztst @@ -471,3 +471,19 @@ print $(( -2#101-16#f )) 0: Unary minus doesn't apply to base but to number as a whole. >-20 + + ( set -o nounset + true $(( noexist + 1 )) + echo 'should never get here' ) +1:Arithmetic, NO_UNSET part 1 +?(eval):2: noexist: parameter not set + + ( setopt nounset + (( noexist++ )) ) +2:Arithmetic, NO_UNSET part 2 +?(eval):2: noexist: parameter not set + + ( unsetopt unset + let noexist==0 ) +1:Arithmetic, NO_UNSET part 3 +?(eval):2: noexist: parameter not set diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 7ab2384aa..1ec650352 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -2423,6 +2423,8 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 >: ` backtick >: word +#' (to deconfuse emacs shell script mode) + ( setopt KSH_ARRAYS typeset -A ksh_assoc @@ -2437,3 +2439,9 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 0:double-quoted nested evaluation of empty string >0 0 0 0 >4 4 4 4 + + () { + local -a x + : <<< ${(F)x/y} + } +0:Separation / join logic regresssion test diff --git a/Test/V09datetime.ztst b/Test/V09datetime.ztst index 22d560750..9f67ecec3 100644 --- a/Test/V09datetime.ztst +++ b/Test/V09datetime.ztst @@ -90,7 +90,7 @@ strftime '%Y-%m-%d %H:%M:%S.%3.' 1012615322 strftime '%Y-%m-%d %H:%M:%S.%3.' 1012615322 0 strftime '%Y-%m-%d %H:%M:%S.%3.' 1012615322 2 - strftime '%Y-%m-%d %H:%M:%S.%3.' 1012615322 $(( 222 * (10 ** 9) )) + strftime '%Y-%m-%d %H:%M:%S.%3.' 1012615322 $(( 222 * (10 ** 6) )) 0:optional nanoseconds >2002-02-02 02:02:02.000 >2002-02-02 02:02:02.000 @@ -103,6 +103,9 @@ strftime '%Y' 1012615322 '' 2> /dev/null 1:empty nanoseconds not allowed + strftime '%N' 1012615322 $(( 10 ** 9 )) 2> /dev/null +1:too-large nanoseconds not allowed + strftime '%N' 1012615322 ${(l<64><9>):-} 2> /dev/null 1:overflowed nanoseconds not allowed @@ -111,3 +114,19 @@ strftime -r '%Y' 2> /dev/null 1:-r timestring not optional + + # This tests rounding up and the use of repeated %.s + strftime '%Y-%m-%d %H:%M:%S.%3..%3.' 1012615322 $(( 999_999 )) + # These test the ceiling on rounding up + for 1 in %. %1. %3. %6. %9. %12.; do + print -rn - "$1 " + strftime "%Y-%m-%d %H:%M:%S.$1" 1012615322 $(( 999_999_999 )) + done +0:%. truncation +>2002-02-02 02:02:02.001.001 +>%. 2002-02-02 02:02:02.999 +>%1. 2002-02-02 02:02:02.9 +>%3. 2002-02-02 02:02:02.999 +>%6. 2002-02-02 02:02:02.999999 +>%9. 2002-02-02 02:02:02.999999999 +>%12. 2002-02-02 02:02:02.999999999 diff --git a/Test/X04zlehighlight.ztst b/Test/X04zlehighlight.ztst new file mode 100644 index 000000000..e14517490 --- /dev/null +++ b/Test/X04zlehighlight.ztst @@ -0,0 +1,154 @@ +# Tests for region_highlight, true-color support, near-color support +# Version 0.7 2018-12-06 +%prep + + export TERM=xterm-256color + if [[ ${+termcap} != 1 || ${termcap[Co]} != <-> || ${termcap[Co]} -lt 256 ]]; then + ZTST_unimplemented="no termcap module OR termcap doesn't support 256 or more colors" + elif [[ $OSTYPE == cygwin ]]; then + ZTST_unimplemented='the zsh/zpty module does not work on Cygwin' + elif zmodload zsh/zpty 2> /dev/null; then + zpty_start() { + export PS1= PS2= + zpty -d + zpty zsh "${(q)ZTST_testdir}/../Src/zsh -fiV +Z" + zpty -w zsh "module_path=( ${(j< >)${(@q-)module_path}} \$module_path )" + zpty -w zsh 'zle_highlight=( fg_start_code:"CDE|3" fg_end_code:"|" bg_start_code:"BCDE|4" bg_end_code:"|" )' + } + zpty_input() { + zpty ${${(M)2:#nonl}:+-n} -w zsh "$1" + } + zpty_enable_zle() { + zpty -w zsh "tcfunc() { REPLY=""; }" + # This line will not be echoed back, behaving like ! -o zle + zpty -w zsh "setopt zle; zle -T tc tcfunc; unset zle_bracketed_paste" + } + zpty_line() { + setopt localoptions extendedglob noshwordsplit + local REPLY cm=$'\r' + integer i + for (( i = 0; i < ${1:-1}; ++i )); do + zpty -r zsh REPLY + # P is for "preserve", and induces keeping some + # expected color codes to test region_highlight, + # etc. - the color codes are made a regular text. + [[ "$2" = "p" ]] && { + REPLY=${REPLY//(#b)$'\x1b'\[([0-9;]##m)/${match[1]}} + } || { + REPLY=${REPLY//$'\x1b'\[[0-9;]##m/} # remove all [0-9]...m codes + } + # Fix e^Mexit - match ((?)\r(?)), if \2 == \3, then replace with \2 + # otherwise replace with \1 stripped out of leading/trailing [[:space:]] + REPLY=${REPLY//(#b)((?(#c0,1))$cm(?(#c0,1)))/${${${(M)match[2]:#${match[3]}}:+${match[2]}}:-${${match[1]##[[:space:]]##}%%[[:space:]]##}}} + [[ -n "$REPLY" ]] && print -r -- ${${REPLY%%[[:space:]]##}##[[:space:]]##} + done + } + zpty_stop() { + setopt localoptions extendedglob + local REPLY cm=$'\r' + # To early Ctrl-D might happen, it was happening when debug + # logs were writing to a file slowing down Zle a little + LANG=C sleep 0.333 + # Zle is active, can use Ctrl-D to exit + zpty -n -w zsh $'\C-d' + # zpty gives no output when piped without these braces (?) + # The while loop with // substitution is to convert `e^Mexit' + # into `exit' (see zpty_line). The sed commands remove escapes + { zpty -r zsh } | sed -e $'/[^\t\r ]/!d' -e $'s/\r$//' -e $'s/\x1b\\[[0-9;]*m//g' | while read REPLY; do REPLY=${REPLY//(#b)((?(#c0,1))$cm(?(#c0,1)))/${${${(M)match[2]:#${match[3]}}:+${match[2]}}:-${${match[1]##[[:space:]]##}%%[[:space:]]##}}}; print -rn -- "$REPLY"; done + zpty -d + : + } + else + ZTST_unimplemented='the zsh/zpty module is not available' + fi + +%test + + zpty_start + zpty_input 'rh_widget() { BUFFER="true word2 word3"; region_highlight+=( "0 4 fg=196" ); rh2; }' + zpty_input 'rh2() { region_highlight=( "2 3 standout" ); };' # note the =, not += + zpty_input 'zle -N rh_widget' + zpty_input 'bindkey "\C-a" rh_widget' + zpty_enable_zle + zpty_input $'\C-a' # emits newline, which executes BUFFER="true" command + zpty_line 1 p # the line of interest, preserving escapes ("p") + zpty_stop +0:region highlight - standout overlapping on other region_highlight entry +>0m27m24mtr7mu27me word2 word3 + + zpty_start + zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=green" ); }' + zpty_input 'zle -N rh_widget' + zpty_input 'bindkey "\C-a" rh_widget' + zpty_enable_zle + zpty_input $'\C-a' # emits newline, which executes BUFFER="true" command + zpty_line 1 p # the line of interest, preserving escapes ("p") + zpty_stop +0:basic region_highlight with 8 colors +>0m27m24mCDE|32|trueCDE|39| + + zpty_start + zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=#040810" ); }' + zpty_input 'zle -N rh_widget' + zpty_input 'bindkey "\C-a" rh_widget' + zpty_enable_zle + zpty_input $'\C-a' # emits newline, which executes BUFFER="true" command + zpty_line 1 p # the line of interest, preserving escapes ("p") + zpty_stop +0:basic region_highlight with true-color (hex-triplets) +>0m27m24mCDE|38;2;4;8;16|trueCDE|39| + + zpty_start + zpty_input 'zmodload zsh/nearcolor' + zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=#040810" ); }' + zpty_input 'zle -N rh_widget' + zpty_input 'bindkey "\C-a" rh_widget' + zpty_enable_zle + zpty_input $'\C-a' # emits newline, which executes BUFFER="true" command + zpty_line 1 p # the line of interest, preserving escapes ("p") + zpty_stop +0:basic region_highlight with near-color (hex-triplets at input) +>0m27m24mCDE|38;5;232|trueCDE|39| + + zpty_start + zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=green" ); rh2; }' + zpty_input 'rh2() { region_highlight+=( "1 2 fg=red" ); }' # `r' in red; the above line would be too long + zpty_input 'zle -N rh_widget' + zpty_input 'bindkey "\C-a" rh_widget' + zpty_enable_zle + zpty_input $'\C-a' # emits newline, which executes BUFFER="true" command + zpty_line 1 p # the line of interest, preserving escapes ("p") + zpty_stop +0:overlapping region_highlight with 8 colors +>0m27m24mCDE|32|tCDE|31|rCDE|39|CDE|32|ueCDE|39| + + zpty_start + zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=#00cc00" ); rh2; }' + zpty_input 'rh2() { region_highlight+=( "1 2 fg=#cc0000" ); }' # `r' in red; the above line would be too long + zpty_input 'zle -N rh_widget' + zpty_input 'bindkey "\C-a" rh_widget' + zpty_enable_zle + zpty_input $'\C-a' # emits newline, which executes BUFFER="true" command + zpty_line 1 p # the line of interest, preserving escapes ("p") + zpty_stop +0:overlapping region_highlight with true-color +>0m27m24mCDE|38;2;0;204;0|tCDE|38;2;204;0;0|rCDE|39|CDE|38;2;0;204;0|ueCDE|39| + + zpty_start + zpty_input 'zmodload zsh/nearcolor' + zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=#00cc00" ); rh2; }' + zpty_input 'rh2() { region_highlight+=( "1 2 fg=#cc0000" ); }' # `r' in red; the above line would be too long + zpty_input 'zle -N rh_widget' + zpty_input 'bindkey "\C-a" rh_widget' + zpty_enable_zle + zpty_input $'\C-a' # emits newline, which executes BUFFER="true" command + zpty_line 1 p # the line of interest, preserving escapes ("p") + zpty_stop +0:overlapping region_highlight with near-color (hex-triplets at input) +>0m27m24mCDE|38;5;40|tCDE|38;5;160|rCDE|39|CDE|38;5;40|ueCDE|39| + +%clean + + zmodload -ui zsh/zpty + +# vim:ft=zsh |