From da7aba41e7b43dc49b6fca111fe63a9e1fed4386 Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Mon, 25 Sep 2023 08:40:36 +0900 Subject: [PATCH 1/2] feat(_comp_compgen): support `-P prefix` with auto-adjusted `cur` The Bash builtin `compgen -P prefix ... -- "$cur"` prepends the prefix AFTER filtering completions using `$cur`. However, we usually want to filter completions with "$cur" starting with the prefix. To properly handle such a situation, one first needs to check if the current content of "$cur" is compatible. Then, one needs to modify $cur to remove the prefix part, generate completions, and prepends the prefix to the generated completions. This pattern is used frequently in the codebase, so it is good to handle it within `_comp_compgen`. This patch implements the option `-P` of `_comp_compgen`. When a non-empty string is specified to the `-P` option, it performs the necessary operations: the check and adjustment of $cur, the proper filtering by the prefix string, and prepending of the prefix string. --- bash_completion | 70 ++++++++++++++++++++++++++++++-- doc/api-and-naming.md | 12 +++--- test/t/unit/test_unit_compgen.py | 46 +++++++++++++++++++++ 3 files changed, 118 insertions(+), 10 deletions(-) diff --git a/bash_completion b/bash_completion index 12172ab3a29..d3d1f284f8e 100644 --- a/bash_completion +++ b/bash_completion @@ -439,6 +439,7 @@ _comp_split() # @var[in] $? # @var[in] _var # @var[in] _append +# @var[in] _upvars # @return original $? _comp_compgen__error_fallback() { @@ -449,6 +450,10 @@ _comp_compgen__error_fallback() else eval -- "$_var=()" fi + if ((${#_upvars[@]})); then + _comp_unlocal "${_upvars[@]}" + _upvars=() + fi return "$_status" } @@ -478,6 +483,13 @@ _comp_compgen__error_fallback() # is ${cur-}. # -R The same as -c ''. Use raw outputs without filtering. # -C dir Evaluate compgen/generator in the specified directory. +# -P pref Prepend the prefix to the generated completions. Unlike `compgen +# -P prefix`, this prefix is subject to filtering by `cur`. When a +# non-empty prefix is specified, first `cur` is tested whether it is +# consistent with the prefix. Then, `cur` is reduced for the part +# excluding the prefix, and the normal completion generation is +# performed. Finally, the prefix is prepended to generated +# completions. # @var[in,opt] cur Used as the default value of a prefix to filter the # completions. # @@ -562,6 +574,7 @@ _comp_compgen() local _var= local _cur=${_comp_compgen__cur-${cur-}} local _dir="" + local _prefix="" local _ifs=$' \t\n' _has_ifs="" local _icmd="" _xcmd="" local -a _upvars=() @@ -572,7 +585,7 @@ _comp_compgen() shopt -u nocasematch fi local OPTIND=1 OPTARG="" OPTERR=0 _opt - while getopts ':av:U:Rc:C:lF:i:x:' _opt "$@"; do + while getopts ':av:U:Rc:C:P:lF:i:x:' _opt "$@"; do case $_opt in a) _append=set ;; v) @@ -601,6 +614,7 @@ _comp_compgen() fi _dir=$OPTARG ;; + P) _prefix=$OPTARG ;; l) _has_ifs=set _ifs=$'\n' ;; F) _has_ifs=set _ifs=$OPTARG ;; [ix]) @@ -636,6 +650,19 @@ _comp_compgen() [[ $_append ]] || _append=${_comp_compgen__append-} fi + local _prefix_fail="" + if [[ $_prefix ]]; then + if [[ $_cur == "$_prefix"* ]]; then + _cur=${_cur#"$_prefix"} + elif [[ $_prefix == "$_cur"* ]]; then + _cur="" + else + # No completions are generated because the current word does not match + # the prefix. + _prefix_fail=set + fi + fi + if [[ $1 != -* ]]; then # usage: _comp_compgen [options] NAME args if [[ $_has_ifs ]]; then @@ -657,6 +684,11 @@ _comp_compgen() fi shift + if [[ $_prefix_fail ]]; then + _comp_compgen__error_fallback + return 1 + fi + _comp_compgen__call_generator "$@" else # usage: _comp_compgen [options] -- [compgen_options] @@ -680,6 +712,11 @@ _comp_compgen() return 2 fi + if [[ $_prefix_fail ]]; then + _comp_compgen__error_fallback + return 1 + fi + _comp_compgen__call_builtin "$@" fi } @@ -694,8 +731,6 @@ _comp_compgen() # @var[in] _var _comp_compgen__call_generator() { - ((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}" - if [[ $_dir ]]; then local _original_pwd=$PWD local PWD=${PWD-} OLDPWD=${OLDPWD-} @@ -708,14 +743,28 @@ _comp_compgen__call_generator() } fi + ((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}" + local _comp_compgen__append=$_append local _comp_compgen__var=$_var local _comp_compgen__cur=$_cur cur=$_cur + if [[ $_prefix ]]; then + local -a tmp=() + local _comp_compgen__var=tmp + local _comp_compgen__append="" + fi # Note: we use $1 as a part of a function name, and we use $2... as # arguments to the function if any. # shellcheck disable=SC2145 "${_generator[@]}" "$@" local _status=$? + if [[ $_prefix ]]; then + local _i + for _i in "${!tmp[@]}"; do + tmp[_i]=$_prefix${tmp[_i]} + done + _comp_compgen -RU tmp ${_append:+-a} -v "$_var" -- -W '"${tmp[@]}"' + fi # Go back to the original directory. # Note: Failure of this line results in the change of the current @@ -770,6 +819,13 @@ if ((BASH_VERSINFO[0] > 5 || BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3)); t ((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}" ((${#_result[@]})) || return + + if [[ $_prefix ]]; then + local _i + for _i in "${!_result[@]}"; do + _result[_i]=$_prefix${_result[_i]} + done + fi if [[ $_append ]]; then eval -- "$_var+=(\"\${_result[@]}\")" else @@ -794,7 +850,13 @@ else } ((${#_upvars[@]})) && _comp_unlocal "${_upvars[@]}" - _comp_split -l ${_append:+-a} "$_var" "$_result" + + if [[ $_prefix ]]; then + _comp_split -l ${_append:+-a} "$_var" \ + "$(IFS=$'\n' compgen -W '$_result' -P "$_prefix")" + else + _comp_split -l ${_append:+-a} "$_var" "$_result" + fi } fi diff --git a/doc/api-and-naming.md b/doc/api-and-naming.md index 824e351e154..c82179365ce 100644 --- a/doc/api-and-naming.md +++ b/doc/api-and-naming.md @@ -143,12 +143,12 @@ concept is later extended to also mean "eXported". The generator functions, which have names of the form `_comp_compgen_NAME`, are used to generate completion candidates. A generator function is supposed to be -called by `_comp_compgen [OPTS] NAME ARGS` where `OPTS = -aRl|-v var|-c cur|-C -dir|-F sep` are the options to modify the behavior (see the code comment of -`_comp_compgen` for details). When there are no `opts`, the generator function -is supposed to be directly called as `_comp_compgen_NAME ARGS`. The result is -stored in the target variable (which is `COMPREPLY` by default but can be -specified by `-v var` in `OPTS`). +called by `_comp_compgen [OPTS] NAME ARGS` where `OPTS = -aR|-v var|-c cur|-C +dir|-U var|-P pref` are the options to modify the behavior (see the code +comment of `_comp_compgen` for details). When there are no `opts`, the +generator function is supposed to be directly called as `_comp_compgen_NAME +ARGS`. The result is stored in the target variable (which is `COMPREPLY` by +default but can be specified by `-v var` in `OPTS`). ### Implementing a generator function diff --git a/test/t/unit/test_unit_compgen.py b/test/t/unit/test_unit_compgen.py index fb04d7472af..310609dc6e0 100644 --- a/test/t/unit/test_unit_compgen.py +++ b/test/t/unit/test_unit_compgen.py @@ -172,3 +172,49 @@ def test_9_inherit_a(self, bash, functions): bash, "_comp__test_compgen gen9", want_output=True ) assert output.strip() == "<11><11>" + + def test_10_option_P_unmatching_prefix(self, bash, functions): + output = assert_bash_exec( + bash, + "_comp__test_compgen -c 'x' -P 'prefix,' -- -W 'alpha apple beta lemon'", + want_output=True, + ) + assert output.strip() == "" + + def test_10_option_P_incomplete_prefix(self, bash, functions): + output = assert_bash_exec( + bash, + "_comp__test_compgen -c 'pre' -P 'prefix,' -- -W 'alpha apple beta lemon'", + want_output=True, + ) + assert ( + output.strip() + == "" + ) + + def test_10_option_P_exact_prefix(self, bash, functions): + output = assert_bash_exec( + bash, + "_comp__test_compgen -c 'prefix,' -P 'prefix,' -- -W 'alpha apple beta lemon'", + want_output=True, + ) + assert ( + output.strip() + == "" + ) + + def test_10_option_P_starts_with_prefix(self, bash, functions): + output = assert_bash_exec( + bash, + "_comp__test_compgen -c 'prefix,a' -P 'prefix,' -- -W 'alpha apple beta lemon'", + want_output=True, + ) + assert output.strip() == "" + + def test_10_option_P_no_match(self, bash, functions): + output = assert_bash_exec( + bash, + "_comp__test_compgen -c 'prefix,x' -P 'prefix,' -- -W 'alpha apple beta lemon'", + want_output=True, + ) + assert output.strip() == "" From 86a7ae238309bde7d2f38a9c4aa0485442215b7f Mon Sep 17 00:00:00 2001 From: Koichi Murase Date: Sun, 6 Apr 2025 22:20:27 +0900 Subject: [PATCH 2/2] refactor(*): use `_comp_compgen -P` * refactor(dot,gprof,protoc,ssh,7z): use `_comp_compgen -P` for optargs * refactor(_comp_compgen_tilde): use `_comp_compgen -P` for `~...` * refactor(curl,strings): use `_comp_compgen -P` for `@...` * refactor(tshark): use `_comp_compgen -P` for `-[oX]-...` * refactor({,u}mount,kcov,lintian): use `_comp_compgen -P` for `*,*` * refactor(upgradepkg): use `_comp_compgen -P` for `*%*` * refactor(_mount{,.linux}): use `_comp_compgen -P` for `//host/...` * refactor(xgamma): use `_comp_compgen -P` for `-display:*.*` --- bash_completion | 2 +- completions/7z | 36 ++++---- completions/_mount | 2 +- completions/_mount.linux | 185 +++++++++++++++++++------------------- completions/_umount.linux | 22 ++--- completions/curl | 7 +- completions/dot | 7 +- completions/gprof | 5 +- completions/kcov | 22 +++-- completions/lintian | 15 ++-- completions/protoc | 6 +- completions/ssh | 8 +- completions/strings | 7 +- completions/tshark | 6 +- completions/upgradepkg | 10 +-- completions/xgamma | 4 +- 16 files changed, 158 insertions(+), 186 deletions(-) diff --git a/bash_completion b/bash_completion index d3d1f284f8e..9d745ba8d10 100644 --- a/bash_completion +++ b/bash_completion @@ -1914,7 +1914,7 @@ _comp_compgen_tilde() { if [[ ${cur-} == \~* && $cur != */* ]]; then # Try generate ~username completions - if _comp_compgen -c "${cur#\~}" -- -P '~' -u; then + if _comp_compgen -P '~' -- -u; then # 2>/dev/null for direct invocation, e.g. in the # _comp_compgen_tilde unit test compopt -o filenames 2>/dev/null diff --git a/completions/7z b/completions/7z index 027f84be652..7333ba605e7 100644 --- a/completions/7z +++ b/completions/7z @@ -15,20 +15,17 @@ _comp_cmd_7z() case $cur in -ao*) - _comp_compgen -c "${cur:3}" -- -P"${cur:0:3}" -W 'a s t u' + _comp_compgen -P "-ao" -- -W 'a s t u' return ;; -?(a)[ix]*) - local opt - if [[ $cur == -a[ix]* ]]; then - opt=${cur:0:3} cur=${cur:3} - else - opt=${cur:0:2} cur=${cur:2} - fi if [[ $cur != *[@\!]* ]]; then - _comp_compgen -- -P"$opt" -W '@ ! r@ r-@ r0@ r! r-! r0!' - elif [[ $cur == ?(r@(-|0|))@* ]]; then - _comp_compgen -c "${cur#*@}" -- -P"${opt}${cur%%@*}@" -f + [[ $cur =~ ^-a?[ix] ]] + local prefix=${BASH_REMATCH-} + _comp_compgen -P "$prefix" -- -W '@ ! r@ r-@ r0@ r! r-! r0!' + elif [[ $cur =~ ^-a?[ix](r|r-|r0)?@ ]]; then + local prefix=${BASH_REMATCH-} + _comp_compgen -P "$prefix" -- -f compopt -o filenames fi return @@ -43,31 +40,30 @@ _comp_cmd_7z() ;; -o* | -w?*) compopt -o filenames - _comp_compgen -c "${cur:2}" -- -d -P"${cur:0:2}" -S/ + _comp_compgen -P "${cur:0:2}" -- -d -S/ compopt -o nospace return ;; -r?*) - _comp_compgen -c "${cur:2}" -- -P"${cur:0:2}" -W '- 0' + _comp_compgen -P "-r" -- -W '- 0' return ;; -scs*) - _comp_compgen -c "${cur:4}" -- -P"${cur:0:4}" -W 'UTF-8 WIN DOS' + _comp_compgen -P "-scs" -- -W 'UTF-8 WIN DOS' return ;; -ssc?*) - _comp_compgen -c "${cur:4}" -- -P"${cur:0:4}" -W '-' + _comp_compgen -P "-ssc" -- -W '-' return ;; -t*) if [[ $mode == w ]]; then - _comp_compgen -c "${cur:2}" -- -P"${cur:0:2}" -W '7z bzip2 gzip - swfc tar wim xz zip' + _comp_compgen -P "-t" -- -W '7z bzip2 gzip swfc tar wim xz zip' else - _comp_compgen -c "${cur:2}" -- -P"${cur:0:2}" -W '7z apm arj - bzip2 cab chm cpio cramfs deb dmg elf fat flv gzip hfs iso - lzh lzma lzma86 macho mbr mslz mub nsis ntfs pe ppmd rar - rpm squashfs swf swfc tar udf vhd wim xar xz z zip' + _comp_compgen -P "-t" -- -W '7z apm arj bzip2 cab chm cpio + cramfs deb dmg elf fat flv gzip hfs iso lzh lzma lzma86 + macho mbr mslz mub nsis ntfs pe ppmd rar rpm squashfs swf + swfc tar udf vhd wim xar xz z zip' fi return ;; diff --git a/completions/_mount b/completions/_mount index ab385f11288..23ef20515b1 100644 --- a/completions/_mount +++ b/completions/_mount @@ -44,7 +44,7 @@ _comp_cmd_mount() host=${cur#//} host=${host%%/*} if [[ $host ]]; then - _comp_compgen -c "${cur#//"$host"}" split -P "//$host" -- "$( + _comp_compgen -P "//$host" split -- "$( smbclient -d 0 -NL "$host" 2>/dev/null | command sed -ne '/^[[:blank:]]*Sharename/,/^$/p' | command sed -ne '3,$s|^[^A-Za-z]*\([^[:blank:]]*\).*$|/\1|p' diff --git a/completions/_mount.linux b/completions/_mount.linux index 3d4c30794b4..4ad2ae23401 100644 --- a/completions/_mount.linux +++ b/completions/_mount.linux @@ -8,24 +8,18 @@ _comp_cmd_mount() local cur prev words cword comp_args _comp_initialize -n =: -- "$@" || return - local split="" case "$prev" in -t | --types) # find /lib/modules/$(uname -r)/ -type f -path '*/fs/*.ko' -printf '%f\n' | cut -d. -f1 # FIXME: no - if [[ $cur == ?*,* ]]; then - prev="${cur%,*}" - cur="${cur##*,}" - split=set - fi - _comp_compgen -- -W 'auto adfs affs autofs btrfs cifs coda cramfs - davfs debugfs devpts efs ext2 ext3 ext4 fuse hfs hfsplus hpfs - iso9660 jffs2 jfs minix msdos ncpfs nfs nfs4 ntfs ntfs-3g proc - qnx4 ramfs reiserfs romfs squashfs smbfs sysv tmpfs ubifs udf - ufs umsdos usbfs vfat xfs' - _comp_compgen -a fstypes - [[ $split ]] && ((${#COMPREPLY[@]})) && - _comp_compgen -Rv COMPREPLY -- -P "$prev," -W '"${COMPREPLY[@]}"' + [[ $cur =~ ^.+, ]] + local prefix=${BASH_REMATCH-} + _comp_compgen -P "$prefix" -- -W 'auto adfs affs autofs btrfs cifs + coda cramfs davfs debugfs devpts efs ext2 ext3 ext4 fuse hfs + hfsplus hpfs iso9660 jffs2 jfs minix msdos ncpfs nfs nfs4 ntfs + ntfs-3g proc qnx4 ramfs reiserfs romfs squashfs smbfs sysv + tmpfs ubifs udf ufs umsdos usbfs vfat xfs' + _comp_compgen -aP "$prefix" fstypes return ;; --bind | -B | --rbind | -R) @@ -66,61 +60,58 @@ _comp_cmd_mount() # no is not a real fstype, reset to "auto" [[ $fstype == no?* ]] && fstype=auto # split options list - if [[ $cur == ?*,* ]]; then - prev="${cur%,*}" - cur="${cur##*,}" - split=set - fi + [[ $cur =~ ^.+, ]] + local prefix=${BASH_REMATCH-} # no completion if $cur is opt=smth - [[ $cur == *=* ]] && return + [[ ${cur##*,} == *=* ]] && return # mount options - _comp_compgen -- -W 'loop {,a}sync {,no}atime {,no}auto - {,fs,def,root}context= defaults {,no}dev {,no}diratime dirsync - {,no}exec group {,no}iversion {,no}mand _netdev nofail - {,no}relatime {,no}strictatime {,no}suid owner remount ro rw - {,no}user users' + _comp_compgen -P "$prefix" -- -W 'loop {,a}sync {,no}atime + {,no}auto {,fs,def,root}context= defaults {,no}dev + {,no}diratime dirsync {,no}exec group {,no}iversion {,no}mand + _netdev nofail {,no}relatime {,no}strictatime {,no}suid owner + remount ro rw {,no}user users' case "$fstype" in adfs | auto) - _comp_compgen -a -- -W '{u,g}id= {own,oth}mask=' + _comp_compgen -aP "$prefix" -- -W '{u,g}id= {own,oth}mask=' ;;& affs | auto) - _comp_compgen -a -- -W '{u,g}id= set{u,g}id= mode= protect - usemp verbose prefix= volume= reserved= root= bs= - {,no,usr,grp}quota' + _comp_compgen -aP "$prefix" -- -W '{u,g}id= set{u,g}id= + mode= protect usemp verbose prefix= volume= reserved= + root= bs= {,no,usr,grp}quota' ;;& btrfs | auto) - _comp_compgen -a -- -W 'degraded subvol= subvolid= device= - nodatasum nodatacow nobarrier max_inline= alloc_start= - thread_pool= compress= compress-force= ssd noacl - notreelog flushoncommit metadata_ratio= - {,no}space_cache clear_cache user_subvol_rm_allowed - autodefrag inode_cache' + _comp_compgen -aP "$prefix" -- -W 'degraded subvol= + subvolid= device= nodatasum nodatacow nobarrier + max_inline= alloc_start= thread_pool= compress= + compress-force= ssd noacl notreelog flushoncommit + metadata_ratio= {,no}space_cache clear_cache + user_subvol_rm_allowed autodefrag inode_cache' ;;& cifs | auto) - _comp_compgen -a -- -W 'user= password= credentials= - {u,g}id= force{u,g}id port= servern= netbiosname= - {file,dir}_mode= ip= domain= guest iocharset - {,no}setuids {,no,dyn}perm directio {,no}mapchars - {,no}intr hard soft noacl nocase sec= nobrl sfu - {,no}serverino nounix nouser_xattr {r,w}size= + _comp_compgen -aP "$prefix" -- -W 'user= password= + credentials= {u,g}id= force{u,g}id port= servern= + netbiosname= {file,dir}_mode= ip= domain= guest + iocharset {,no}setuids {,no,dyn}perm directio + {,no}mapchars {,no}intr hard soft noacl nocase sec= + nobrl sfu {,no}serverino nounix nouser_xattr {r,w}size= rwpidforward backup{u,g}id cache=' ;;& davfs | auto) - _comp_compgen -a -- -W 'conf= {file,dir}_mode= {u,g}id= - username=' + _comp_compgen -aP "$prefix" -- -W 'conf= {file,dir}_mode= + {u,g}id= username=' ;;& ext[2-4] | auto) - _comp_compgen -a -- -W '{,no}acl bsddf minixdf check= debug - errors= {,no}grpid {bsd,sysv}groups {,no,usr,grp}quota - nobh nouid32 oldalloc orlov res{u,g}id= sb= - {,no}user_xattr' + _comp_compgen -aP "$prefix" -- -W '{,no}acl bsddf minixdf + check= debug errors= {,no}grpid {bsd,sysv}groups + {,no,usr,grp}quota nobh nouid32 oldalloc orlov + res{u,g}id= sb= {,no}user_xattr' ;;& ext[34] | auto) - _comp_compgen -a -- -W 'journal= journal_dev= norecovery - noload data= barrier= commit=' + _comp_compgen -aP "$prefix" -- -W 'journal= journal_dev= + norecovery noload data= barrier= commit=' ;;& ext4 | auto) - _comp_compgen -a -- -W 'journal_checksum + _comp_compgen -aP "$prefix" -- -W 'journal_checksum journal_async_commit nobarrier inode_readahead= stripe= {,no}delalloc abort {max,min}_batch_time= journal_ioprio= {,no}auto_da_alloc {,no}discard nouid32 @@ -128,83 +119,87 @@ _comp_cmd_mount() max_dir_size_kb= i_version' ;;& msdos | umsdos | vfat | auto) - _comp_compgen -a -- -W 'blocksize= {u,g}id= {u,d,f}mask= - allow_utime= check= codepage= conv= cvf_format= - cvf_option= debug fat= iocharset= tz= quiet showexec - sys_immutable flush usefree {,no}dots dotsOK=' + _comp_compgen -aP "$prefix" -- -W 'blocksize= {u,g}id= + {u,d,f}mask= allow_utime= check= codepage= conv= + cvf_format= cvf_option= debug fat= iocharset= tz= quiet + showexec sys_immutable flush usefree {,no}dots dotsOK=' ;;& vfat | auto) - _comp_compgen -a -- -W 'uni_xlate posix nonumtail utf8 - shortname=' + _comp_compgen -aP "$prefix" -- -W 'uni_xlate posix + nonumtail utf8 shortname=' ;;& iso9660 | auto) - _comp_compgen -a -- -W 'norock nojoliet check= {u,g}id= - map= mode= unhide block= conv= cruft session= sbsector= - iocharset= utf8' + _comp_compgen -aP "$prefix" -- -W 'norock nojoliet check= + {u,g}id= map= mode= unhide block= conv= cruft session= + sbsector= iocharset= utf8' ;;& jffs2 | auto) - _comp_compgen -a -- -W 'compr= rp_size=' + _comp_compgen -aP "$prefix" -- -W 'compr= rp_size=' ;;& jfs | auto) - _comp_compgen -a -- -W 'iocharset= resize= {,no}integrity - errors= {,no,usr,grp}quota' + _comp_compgen -aP "$prefix" -- -W 'iocharset= resize= + {,no}integrity errors= {,no,usr,grp}quota' ;;& nfs | nfs4 | auto) - _comp_compgen -a -- -W 'soft hard timeo= retrans= {r,w}size= - {,no}ac acreg{min,max}= acdir{min,max}= actimeo= bg fg - retry= sec= {,no}sharecache {,no}resvport lookupcache= - proto= port= {,no}intr {,no}cto {,nfs}vers=' + _comp_compgen -aP "$prefix" -- -W 'soft hard timeo= + retrans= {r,w}size= {,no}ac acreg{min,max}= + acdir{min,max}= actimeo= bg fg retry= sec= + {,no}sharecache {,no}resvport lookupcache= proto= port= + {,no}intr {,no}cto {,nfs}vers=' ;;& nfs | auto) - _comp_compgen -a -- -W 'udp tcp rdma mount{port,proto,host}= - mountvers= namlen={,no}lock {,no}acl {,no}rdirplus - {,no}fsc' + _comp_compgen -aP "$prefix" -- -W 'udp tcp rdma + mount{port,proto,host}= mountvers= namlen={,no}lock + {,no}acl {,no}rdirplus {,no}fsc' ;;& nfs4 | auto) - _comp_compgen -a -- -W 'clientaddr= {,no}migration' + _comp_compgen -aP "$prefix" -- -W 'clientaddr= + {,no}migration' ;;& ntfs-3g) - _comp_compgen -a -- -W '{u,g}id= {u,f,d}mask= usermapping= - permissions inherit locale= force {,no}recover - ignore_case remove_hiberfile show_sys_files - hide_{hid,dot}_files windows_names allow_other - max_read= silent no_def_opts streams_interface= - user_xattr efs_raw {,no}compression debug no_detach' + _comp_compgen -aP "$prefix" -- -W '{u,g}id= {u,f,d}mask= + usermapping= permissions inherit locale= force + {,no}recover ignore_case remove_hiberfile + show_sys_files hide_{hid,dot}_files windows_names + allow_other max_read= silent no_def_opts + streams_interface= user_xattr efs_raw {,no}compression + debug no_detach' ;;& proc | auto) - _comp_compgen -a -- -W '{u,g}id=' + _comp_compgen -aP "$prefix" -- -W '{u,g}id=' ;;& reiserfs | auto) - _comp_compgen -a -- -W 'conv hash= + _comp_compgen -aP "$prefix" -- -W 'conv hash= {,no_un}hashed_relocation noborder nolog notail replayonly resize= user_xattr acl barrier=' ;;& tmpfs | auto) - _comp_compgen -a -- -W 'size= nr_blocks= nr_inodes= mode= - {u,g}id= mpol=' + _comp_compgen -aP "$prefix" -- -W 'size= nr_blocks= + nr_inodes= mode= {u,g}id= mpol=' ;;& udf | auto) - _comp_compgen -a -- -W '{u,g}id= umask= unhide undelete - nostrict iocharset bs= novrs session= anchor= volume= - partition= lastblock= fileset= rootdir=' + _comp_compgen -aP "$prefix" -- -W '{u,g}id= umask= unhide + undelete nostrict iocharset bs= novrs session= anchor= + volume= partition= lastblock= fileset= rootdir=' ;;& usbfs | auto) - _comp_compgen -a -- -W 'dev{u,g}id= devmode= bus{u,g}id= - busmode= list{u,g}id= listmode=' + _comp_compgen -aP "$prefix" -- -W 'dev{u,g}id= devmode= + bus{u,g}id= busmode= list{u,g}id= listmode=' ;;& xfs | auto) - _comp_compgen -a -- -W 'allocsize= {,no}attr2 barrier dmapi - {,no}grpid {bsd,sysv}groups ihashsize= {,no}ikeep - inode{32,64} {,no}largeio logbufs= logbsize= logdev= - rtdev= mtpt= noalign norecovery nouuid osyncisosync - {u,g,p}qnoenforce {,u,usr,g,grp,p,prj}quota sunit= - swidth= swalloc' + _comp_compgen -aP "$prefix" -- -W 'allocsize= {,no}attr2 + barrier dmapi {,no}grpid {bsd,sysv}groups ihashsize= + {,no}ikeep inode{32,64} {,no}largeio logbufs= logbsize= + logdev= rtdev= mtpt= noalign norecovery nouuid + osyncisosync {u,g,p}qnoenforce + {,u,usr,g,grp,p,prj}quota sunit= swidth= swalloc' ;;& esac # COMP_WORDBREAKS is a real pain in the ass - prev="${prev##*["$COMP_WORDBREAKS"]}" - [[ $split ]] && ((${COMPREPLY[@]})) && - _comp_compgen -Rv COMPREPLY -- -P "$prev," -W '"${COMPREPLY[@]}"' + if ((${#COMPREPLY[@]})) && [[ $prefix == *["$COMP_WORDBREAKS"]* ]]; then + local tmp=${prefix%["$COMP_WORDBREAKS"]*} + COMPREPLY=("${COMPREPLY[@]#"$tmp"?}") + fi [[ ${COMPREPLY-} == *= ]] && compopt -o nospace return ;; @@ -236,7 +231,7 @@ _comp_cmd_mount() host=${cur#//} host=${host%%/*} if [[ $host ]]; then - _comp_compgen -c "${cur#//"$host"}" split -P "//$host" -- "$( + _comp_compgen -P "//$host" split -- "$( smbclient -d 0 -NL "$host" 2>/dev/null | command sed -ne '/^[[:blank:]]*Sharename/,/^$/p' | command sed -ne '3,$s|^[^A-Za-z]*\([^[:blank:]]*\).*$|/\1|p' diff --git a/completions/_umount.linux b/completions/_umount.linux index 5540e8eac65..d5692f46af1 100644 --- a/completions/_umount.linux +++ b/completions/_umount.linux @@ -98,20 +98,14 @@ _comp_cmd_umount() case "$prev" in -t) # FIXME: no - local split="" - if [[ $cur == ?*,* ]]; then - prev="${cur%,*}" - cur="${cur##*,}" - split=set - fi - _comp_compgen -- -W 'adfs affs autofs btrfs cifs coda cramfs - debugfs devpts efs ext2 ext3 ext4 fuse hfs hfsplus hpfs iso9660 - jfs minix msdos ncpfs nfs nfs4 ntfs ntfs-3g proc qnx4 ramfs - reiserfs romfs squashfs smbfs sysv tmpfs ubifs udf ufs umsdos - usbfs vfat xfs' - _comp_compgen -a fstypes - [[ $split ]] && ((${#COMPREPLY[@]})) && - _comp_compgen -Rv COMPREPLY -- -P "$prev," -W '"${COMPREPLY[@]}"' + [[ $cur =~ ^.+, ]] + local prefix=${BASH_REMATCH-} + _comp_compgen -P "$prefix" -- -W 'adfs affs autofs btrfs cifs coda + cramfs debugfs devpts efs ext2 ext3 ext4 fuse hfs hfsplus hpfs + iso9660 jfs minix msdos ncpfs nfs nfs4 ntfs ntfs-3g proc qnx4 + ramfs reiserfs romfs squashfs smbfs sysv tmpfs ubifs udf ufs + umsdos usbfs vfat xfs' + _comp_compgen -aP "$prefix" fstypes return ;; -O) diff --git a/completions/curl b/completions/curl index 96e4b221edd..dc61bc4ccf6 100644 --- a/completions/curl +++ b/completions/curl @@ -53,13 +53,12 @@ _comp_cmd_curl() --data | --data-ascii | --data-binary | --data-urlencode | --json | \ --header | --proxy-header | --write-out | -${noargopts}[dHw]) if [[ $cur == \@* ]]; then - _comp_compgen -c "${cur:1}" filedir - _comp_compgen -a -c "${cur:1}" -- -W '-' - if [[ ${#COMPREPLY[@]} -eq 1 && -d ${COMPREPLY[0]} ]]; then + _comp_compgen -P @ filedir + _comp_compgen -aP @ -- -W '-' + if [[ ${#COMPREPLY[@]} -eq 1 && -d ${COMPREPLY[0]#@} ]]; then COMPREPLY[0]+=/ compopt -o nospace fi - COMPREPLY=("${COMPREPLY[@]/#/@}") fi return ;; diff --git a/completions/dot b/completions/dot index dcb84369b28..8f1a56366a8 100644 --- a/completions/dot +++ b/completions/dot @@ -13,21 +13,20 @@ _comp_cmd_dot() ;; -T*) # generate langs - _comp_compgen -c "${cur#-T}" split -P "-T" -- "$( + _comp_compgen -P "-T" split -- "$( "$1" -TNON_EXISTENT 2>&1 | command sed -ne 's/.*one of://p' )" return ;; -K*) # generate layouts - _comp_compgen -c "${cur#-K}" split -P "-K" -- "$( + _comp_compgen -P "-K" split -- "$( "$1" -KNON_EXISTENT 2>&1 | command sed -ne 's/.*one of://p' )" return ;; -o*) - _comp_compgen -c "${cur#-o}" filedir && - COMPREPLY=("${COMPREPLY[@]/#/-o}") + _comp_compgen -P "-o" filedir return ;; esac diff --git a/completions/gprof b/completions/gprof index cb186330c95..798df5a3358 100644 --- a/completions/gprof +++ b/completions/gprof @@ -10,12 +10,11 @@ _comp_cmd_gprof() return ;; -S*) - _comp_compgen -c "${cur:2}" filedir - ((${#COMPREPLY[@]})) && COMPREPLY=("${COMPREPLY[@]/#/-S}") + _comp_compgen -P "-S" filedir return ;; -O*) - _comp_compgen -c "${cur:2}" -- -P -O -W 'auto bsd 4.4bsd magic + _comp_compgen -P "-O" -- -W 'auto bsd 4.4bsd magic prof' return ;; diff --git a/completions/kcov b/completions/kcov index 20a62f22c5e..573a48aa86a 100644 --- a/completions/kcov +++ b/completions/kcov @@ -28,17 +28,23 @@ _comp_cmd_kcov() return ;; --limits | -l) - if [[ $cur == ?*,* ]]; then - prev="${cur%,*}" - cur="${cur##*,}" - _comp_compgen -- -W "{0..100}" - ((${#COMPREPLY[@]} == 1)) && - _comp_compgen -Rv COMPREPLY -- -P "$prev," -W '"$COMPREPLY"' + # Complete the option argument of the form "--limits low,high". + local prefix + if [[ $cur =~ ^.+, ]]; then + prefix=$BASH_REMATCH else - _comp_compgen -- -W "{0..100}" - ((${#COMPREPLY[@]} == 1)) && COMPREPLY=("${COMPREPLY/%/,}") + prefix="" compopt -o nospace fi + _comp_compgen -P "$prefix" -- -W "{0..100}" && + if ((${#COMPREPLY[@]} == 1)); then + # When we complete the "low" part, we suffix a comma. + [[ $prefix ]] || COMPREPLY=("${COMPREPLY[@]/%/,}") + else + # When the result is not unique, we do not show the prefix + # part on the list. + COMPREPLY=("${COMPREPLY[@]##*,}") + fi return ;; --title | -t | --include-pattern | --exclude-pattern | --path-strip-level) diff --git a/completions/lintian b/completions/lintian index 51402b2a86f..ac3fb7af316 100644 --- a/completions/lintian +++ b/completions/lintian @@ -12,10 +12,9 @@ _comp_cmd_lintian__tags() tags=$(command sed -e "s/\<$item\>//g" <<<"$tags") done _comp_compgen -aR -- -W "$tags" - elif [[ $cur == *,* ]]; then - _comp_compgen -ac "${cur##*,}" -- -P "${cur%,*}," -W "$tags" else - _comp_compgen -a -- -W "$tags" + [[ $cur == ^.*, ]] + _comp_compgen -aP "${BASH_REMATCH-}" -- -W "$tags" fi } @@ -37,10 +36,9 @@ _comp_cmd_lintian__checks() done done _comp_compgen -aR -- -W "$checks" - elif [[ $cur == *,* ]]; then - _comp_compgen -ac "${cur##*,}" -- -P "${cur%,*}," -W "$checks" else - _comp_compgen -a -- -W "$checks" + [[ $cur == ^.*, ]] + _comp_compgen -aP "${BASH_REMATCH-}" -- -W "$checks" fi } @@ -57,10 +55,9 @@ _comp_cmd_lintian__infos() infos=$(command sed -e "s/\<$item\>//g" <<<"$infos") done _comp_compgen -aR -- -W "$infos" - elif [[ $cur == *,* ]]; then - _comp_compgen -ac "${cur##*,}" -- -P "${cur%,*}," -W "$infos" else - _comp_compgen -a -- -W "$infos" + [[ $cur == ^.*, ]] + _comp_compgen -aP "${BASH_REAMTCH-}" -- -W "$infos" fi } diff --git a/completions/protoc b/completions/protoc index ac93e1cab74..9f8c1b54d9f 100644 --- a/completions/protoc +++ b/completions/protoc @@ -31,13 +31,11 @@ _comp_cmd_protoc() case $cur in -o*) - _comp_compgen -c "${cur:2}" filedir - ((${#COMPREPLY[@]})) && COMPREPLY=("${COMPREPLY[@]/#/-o}") + _comp_compgen -P "-o" filedir return ;; -I*) - _comp_compgen -c "${cur:2}" filedir -d - ((${#COMPREPLY[@]})) && COMPREPLY=("${COMPREPLY[@]/#/-I}") + _comp_compgen -P "-I" filedir -d return ;; -*) diff --git a/completions/ssh b/completions/ssh index db30ba340f7..90ea9c403a8 100644 --- a/completions/ssh +++ b/completions/ssh @@ -371,9 +371,7 @@ _comp_cmd_ssh() esac if [[ $cur == -F* ]]; then - _comp_compgen -c "${cur#-F}" filedir - # Prefix completions with '-F' - COMPREPLY=("${COMPREPLY[@]/#/-F}") + _comp_compgen -P "-F" filedir elif [[ $cur == -* ]]; then _comp_compgen_usage else @@ -447,9 +445,7 @@ _comp_cmd_sftp() esac if [[ $cur == -F* ]]; then - _comp_compgen -c "${cur#-F}" filedir - # Prefix completions with '-F' - COMPREPLY=("${COMPREPLY[@]/#/-F}") + _comp_compgen -P "-F" filedir elif [[ $cur == -* ]]; then _comp_compgen_usage else diff --git a/completions/strings b/completions/strings index 52464a80d62..228da782d38 100644 --- a/completions/strings +++ b/completions/strings @@ -37,13 +37,10 @@ _comp_cmd_strings() command sed -e "s/\[-number\]//")" [[ ${COMPREPLY-} == *= ]] && compopt -o nospace return - elif [[ $cur == @* ]]; then - _comp_compgen -c "${cur:1}" filedir && - COMPREPLY=("${COMPREPLY[@]/#/@}") - return fi - _comp_compgen_filedir + [[ $cur =~ ^@ ]] + _comp_compgen -P "${BASH_REMATCH-}" filedir } && complete -F _comp_cmd_strings strings diff --git a/completions/tshark b/completions/tshark index b7e2f6a398c..9da06391b78 100644 --- a/completions/tshark +++ b/completions/tshark @@ -26,8 +26,7 @@ _comp_cmd_tshark() _comp_cmd_tshark__prefs="$("$1" -G defaultprefs 2>/dev/null | command sed -ne 's/^#\{0,1\}\([a-z0-9_.-]\{1,\}:\).*/\1/p' | tr '\n' ' ')" : ${prefix:=} - _comp_compgen -c "${cur:${#prefix}}" -- -P "$prefix" \ - -W "$_comp_cmd_tshark__prefs" + _comp_compgen -P "$prefix" -- -W "$_comp_cmd_tshark__prefs" [[ ${COMPREPLY-} == *: ]] && compopt -o nospace fi return @@ -101,8 +100,7 @@ _comp_cmd_tshark() if [[ ${cur:${#prefix}} == lua_script:* ]]; then _comp_compgen -c "${cur#*:}" filedir lua else - _comp_compgen -c "${cur:${#prefix}}" -- -P "$prefix" \ - -W 'lua_script:' + _comp_compgen -P "$prefix" -- -W 'lua_script:' [[ ${COMPREPLY-} == *: ]] && compopt -o nospace fi return diff --git a/completions/upgradepkg b/completions/upgradepkg index 0012f6282c3..30f6fb9e232 100644 --- a/completions/upgradepkg +++ b/completions/upgradepkg @@ -10,13 +10,11 @@ _comp_cmd_upgradepkg() return fi - if [[ $cur == ?*%* ]]; then - prev="${cur%%?(\\)%*}" - cur="${cur#*%}" - local nofiles="" + if [[ $cur =~ ^[^%]+% ]]; then + local prefix=$BASH_REMATCH nofiles="" compopt -o filenames - _comp_compgen -- -P "$prev%" -f -X "!*.t[bgxl]z" || nofiles=set - _comp_compgen -a -- -P "$prev%" -S '/' -d + _comp_compgen -P "$prefix" -- -f -X "!*.t[bgxl]z" || nofiles=set + _comp_compgen -aP "$prefix" -- -S '/' -d [[ $nofiles ]] && compopt -o nospace return fi diff --git a/completions/xgamma b/completions/xgamma index 2a0c40973f5..a265cd5ae79 100644 --- a/completions/xgamma +++ b/completions/xgamma @@ -32,8 +32,8 @@ _comp_cmd_xgamma() # local screen numbers local t screens=$(xrandr --query 2>/dev/null | command sed -ne \ '/^Screen /s|^Screen \{1,\}\(.*\):.*$|\1|p' 2>/dev/null) - t="${cur#:}" - _comp_compgen -c "${cur##*.}" -- -P "${t%.*}." -W '$screens' + cur="${cur#:}" + _comp_compgen -P "${cur%.*}." -- -W '$screens' elif [[ $cur != *:* ]]; then # complete hostnames _comp_compgen_known_hosts -c -- "$cur"