about summary refs log tree commit diff
path: root/Completion
diff options
context:
space:
mode:
authorOliver Kiddle <opk@zsh.org>2021-11-24 23:07:18 +0100
committerOliver Kiddle <opk@zsh.org>2021-11-24 23:09:38 +0100
commit16d5d6a9dae526355caf16f2de9d57d84b5d9993 (patch)
tree6ab30a2ad2596523b85aa553ffa8a4544632fd7a /Completion
parent138f5bf144589a59ef009f26332652291cd0a944 (diff)
downloadzsh-16d5d6a9dae526355caf16f2de9d57d84b5d9993.tar.gz
zsh-16d5d6a9dae526355caf16f2de9d57d84b5d9993.tar.xz
zsh-16d5d6a9dae526355caf16f2de9d57d84b5d9993.zip
49597: add a helper for completing numbers with unit suffixes and separate out defaults, ranges and units in completion descriptions
Diffstat (limited to 'Completion')
-rw-r--r--Completion/BSD/Command/_ipfw4
-rw-r--r--Completion/Base/Core/_description8
-rw-r--r--Completion/Base/Utility/_numbers87
-rw-r--r--Completion/Linux/Command/_btrfs8
-rw-r--r--Completion/Unix/Command/_dd14
-rw-r--r--Completion/Unix/Command/_git6
-rw-r--r--Completion/Unix/Command/_head22
-rw-r--r--Completion/Unix/Command/_killall12
-rw-r--r--Completion/Unix/Command/_pv18
-rw-r--r--Completion/Unix/Command/_rclone10
-rw-r--r--Completion/Unix/Command/_rsync10
-rw-r--r--Completion/Unix/Command/_stdbuf6
-rw-r--r--Completion/Unix/Command/_tail22
-rw-r--r--Completion/Unix/Command/_timeout2
-rw-r--r--Completion/Unix/Command/_zfs20
-rw-r--r--Completion/X/Command/_xset4
16 files changed, 165 insertions, 88 deletions
diff --git a/Completion/BSD/Command/_ipfw b/Completion/BSD/Command/_ipfw
index 2354a70fe..49d0ef1e8 100644
--- a/Completion/BSD/Command/_ipfw
+++ b/Completion/BSD/Command/_ipfw
@@ -249,8 +249,8 @@ actions=(
     $'/(pipe|queue|sched)[ \t\0]/' -'pqs=${match%?}' ':dummynet-commands:dummynet configuration:$ca pipe queue sched'
     $word ': _message -e numbers number'
     $word ':options:config:$ca config'
-    \( $'/bw[ \t\0]/' \( $'/<->/' ': _guard "[0-9]#" bandwidth'
-         $word ':units:unit:compadd -M "m:{a-z}={A-Z}" {K,M,G}{bit,Byte}/s'
+    \( $'/bw[ \t\0]/'
+      \( $word ':bandwidths: :_numbers -M "m:{a-z}={A-Z}" bandwidth {K,M,G}{bit,Byte}/s'
       \| $word ':devices:device:_net_interfaces -qS " "' \)
     \| $'/delay[ \t\0]/' $word ': _message -e numbers "propagation delay (ms)"'
     \| $'/burst[ \t\0]/' $word ': _message -e numbers "size (bytes)"'
diff --git a/Completion/Base/Core/_description b/Completion/Base/Core/_description
index 5b54484c7..368b41ee2 100644
--- a/Completion/Base/Core/_description
+++ b/Completion/Base/Core/_description
@@ -2,6 +2,7 @@
 
 local name nopt xopt format gname hidden hide match opts tag
 local -a ign gropt sort
+local -a match mbegin mend
 
 opts=()
 
@@ -78,6 +79,13 @@ shift 2
 if [[ -z "$1" && $# -eq 1 ]]; then
   format=
 elif [[ -n "$format" ]]; then
+  if [[ -z $2 ]]; then
+    argv+=( h:${1%%( ##\((#b)([^\)]#[^0-9-][^\)]#)(#B)\)|)( ##\((#b)([0-9-]##)(#B)\)|)( ##\[(#b)([^\]]##)(#B)\]|)} )
+    [[ -n $match[1] ]] && argv+=( m:$match[1] )
+    [[ -n $match[2] ]] && argv+=( r:$match[2] )
+    [[ -n $match[3] ]] && argv+=( o:$match[3] )
+  fi
+
   zformat -F format "$format" "d:$1" "${(@)argv[2,-1]}"
 fi
 
diff --git a/Completion/Base/Utility/_numbers b/Completion/Base/Utility/_numbers
new file mode 100644
index 000000000..97bb8b4c8
--- /dev/null
+++ b/Completion/Base/Utility/_numbers
@@ -0,0 +1,87 @@
+#autoload
+
+# Usage: _numbers [compadd options] [-t tag] [-f|-N] [-u units] [-l min] [-m max] \
+#                 [-d default] ["description"] [unit-suffix...]
+
+#   -t : specify a tag (defaults to 'numbers')
+#   -u : indicate the units, e.g. seconds
+#   -l : lowest possible value
+#   -m : maximum possible value
+#   -d : default value
+#   -N : allow negative numbers (implied by range including a negative)
+#   -f : allow decimals (float)
+
+# For a unit-suffix, an initial colon indicates a unit that asserts the default
+# otherwise, colons allow for descriptions, e.g:
+
+#   :s:seconds m:minutes h:hours
+
+# unit-suffixes are not sorted by the completion system when listed
+# Specify them in order of magnitude, this tends to be ascending unless
+# the default is of a higher magnitude, in which case, descending.
+# So for, example
+#   bytes kB MB GB
+#   s ms us ns
+# Where the compadd options include matching control or suffixes, these
+# are applied to the units
+
+# For each unit-suffix, the format style is looked up with the
+# unit-suffixes tag and the results concatenated. Specs used are:
+#   x : the suffix
+#   X : suffix description
+#   d : indicate suffix is for the default unit
+#   i : list index
+#   r : reverse list index
+# The latter three of these are useful with ternary expressions.
+
+# _description is called with the x token set to make the completed
+# list of suffixes available to the normal format style
+
+local desc tag range suffixes suffix suffixfmt pat='<->' partial=''
+local -a expl formats
+local -a default max min keep tags units
+local -i i
+local -A opts
+
+zparseopts -K -D -A opts M+:=keep q:=keep s+:=keep S+:=keep J+: V+: 1 2 o+: n F: x+: X+: \
+  t:=tags u:=units l:=min m:=max d:=default f=type e=type N=type
+
+desc="${1:-number}" tag="${tags[2]:-numbers}"
+(( $# )) && shift
+
+[[ -n ${(M)type:#-f} ]] && pat='(<->.[0-9]#|[0-9]#.<->|<->)' partial='(|.)'
+[[ -n ${(M)type:#-N} || $min[2] = -* || $max[2] = -* ]] && \
+    pat="(|-)$pat" partial="(|-)$partial"
+
+if (( $#argv )) && compset -P "$pat"; then
+  zstyle -s ":completion:${curcontext}:units" list-separator sep || sep=--
+  _description -V units expl unit
+  disp=( ${${argv#:}/:/ $sep } )
+  compadd -M 'r:|/=* r:|=*' -d disp "$keep[@]" "$expl[@]" - ${${argv#:}%%:*}
+  return
+elif [[ -prefix $~pat || $PREFIX = $~partial ]]; then
+  formats=( "h:$desc" )
+  (( $#units )) && formats+=( m:${units[2]} ) desc+=" ($units[2])"
+  (( $#min )) && range="$min[2]-"
+  (( $#max )) && range="${range:--}$max[2]"
+  [[ -n $range ]] && formats+=( r:$range ) desc+=" ($range)"
+  (( $#default )) && formats+=( o:${default[2]} ) desc+=" [$default[2]]"
+
+  zstyle -s ":completion:${curcontext}:unit-suffixes" format suffixfmt || \
+      suffixfmt='%(d.%U.)%x%(d.%u.)%(r..|)'
+  for ((i=0;i<$#;i++)); do
+    zformat -f suffix "$suffixfmt" "x:${${argv[i+1]#:}%%:*}" \
+        "X:${${argv[i+1]#:}#*:}" "d:${#${argv[i+1]}[1]#:}" \
+	i:i r:$(( $# - i - 1))
+    suffixes+="$suffix"
+  done
+  [[ -n $suffixes ]] && formats+=( x:$suffixes )
+
+  _comp_mesg=yes
+  _description -x $tag expl "$desc" $formats
+  [[ $compstate[insert] = *unambiguous* ]] && compstate[insert]=
+  compadd "$expl[@]"
+  return 0
+fi
+
+return 1
diff --git a/Completion/Linux/Command/_btrfs b/Completion/Linux/Command/_btrfs
index bb0f724e6..65cf067aa 100644
--- a/Completion/Linux/Command/_btrfs
+++ b/Completion/Linux/Command/_btrfs
@@ -147,16 +147,16 @@ while (( $#state )); do
             '--tbytes[show sizes in TiB, or TB with --si]'
           )
         ;|
-        filesystem:resize) args+=( '1:size:_guard "(|+|-)[0-9]#[GKM]"' '2:path:->mounts' );;
+        filesystem:resize) args+=( '1: :_numbers -u bytes -N size K M G T P E' '2:path:->mounts' );;
         filesystem:defragment)
           args+=( '!-v'
             '-r[defragment files recursively]'
             '-c+[compress files while defragmenting]::compression algorithm:(zlib lzo zstd)'
             '-r[defragment files recursively]'
             '-f[flush after defragmenting]'
-            '-s[start position]:byte position'
-            '-l[defragment limited number of bytes]:length (bytes)'
-            '-t[defragment only files over a certain size]:minimum size (bytes) [32M]'
+            '-s[start position]: :_numbers -u bytes -d "beginning of file" offset K M G T P E'
+            '-l[defragment limited number of bytes]: :_numbers -u bytes length K M G T P E'
+            '-t[defragment only extents up to a certain size]: :_numbers -u bytes -d 32M "maximum extent size" K M G T P E'
             '*:file:_files'
           )
         ;;
diff --git a/Completion/Unix/Command/_dd b/Completion/Unix/Command/_dd
index e5c5e63ce..10682bc8e 100644
--- a/Completion/Unix/Command/_dd
+++ b/Completion/Unix/Command/_dd
@@ -1,18 +1,19 @@
 #compdef dd gdd
 
-local -a vals conv flags
+local -a vals conv flags units
 local variant
 
+units=( w:word b:block k:1024 m g t )
 _pick_variant -r variant gnu=GNU $OSTYPE --version
 
 vals=(
-  '(ibs obs)bs[block size]:block size (bytes)'
-  'cbs[conversion buffer size]:buffer size (bytes)'
+  '(ibs obs)bs[block size]: :_numbers -u bytes "block size" $units'
+  'cbs[conversion buffer size]: :_numbers -u bytes "buffer size" $units'
   'conv[specify conversions to apply]: :_values -s , conversion $conv'
   'count[number of input blocks to copy]:blocks'
-  '(bs)ibs[input block size]:block size (bytes)'
+  '(bs)ibs[input block size]: :_numbers -u bytes -d 512 "block size" $units'
   'if[specify input file]:input file:_tilde_files'
-  '(bs)obs[output block size]:block size (bytes)'
+  '(bs)obs[output block size]: :_numbers -u bytes -d 512 "block size" $units'
   'of[specify output file]:output file:_tilde_files'
   'seek[output blocks initially skipped]:blocks'
   'skip[input blocks initially skipped]:blocks'
@@ -63,7 +64,7 @@ case $variant in
   freebsd*)
     vals+=(
       'fillchar[specify padding character]:character'
-      'speed[limit copying speed]:speed (bytes/second)'
+      'speed[limit copying speed]: :_numbers -u bytes/second speed $units'
     )
     conv+=(
       '(pareven parnone parodd parset)'{pareven,parnone,parodd,parset}
@@ -75,6 +76,7 @@ case $variant in
     )
     flags+=( fullblock noatime nocache count_bytes skip_bytes seek_bytes )
     conv+=( excl nocreat fdatasync fsync )
+    units=( c:1 w:2 b:512 kB:1000 K:1024 MB:1000^2 M:1024\^2 GB G TB T PB P EB E ZB Z YB Y )
   ;;
   netbsd*)
     vals+=(
diff --git a/Completion/Unix/Command/_git b/Completion/Unix/Command/_git
index 7c7fb22bc..c757376b0 100644
--- a/Completion/Unix/Command/_git
+++ b/Completion/Unix/Command/_git
@@ -3720,8 +3720,8 @@ _git-fast-import () {
                                                            now\:"use current time and timezone"' \
     '--done[terminate with error if there is no "done" command at the end of the stream]' \
     '--force[force updating modified existing branches]' \
-    '--max-pack-size=-[maximum size of each packfile]: : __git_guard_bytes' \
-    '--big-file-threshold=-[maximum size of blob to create deltas for]: : __git_guard_bytes' \
+    '--max-pack-size=-[maximum size of each packfile]: : __git_guard_bytes -d unlimited size' \
+    '--big-file-threshold=-[maximum size of blob to create deltas for]: : __git_guard_bytes -d 512m size' \
     '--depth=-[maximum delta depth for blob and tree deltification]: :__git_guard_number "maximum delta depth"' \
     '--active-branches=-[maximum number of branches to maintain active at once]: :__git_guard_number "maximum number of branches"' \
     '--export-marks=-[dump internal marks table when complete]: :_files' \
@@ -7506,7 +7506,7 @@ __git_guard_number () {
 
 (( $+functions[__git_guard_bytes] )) ||
 __git_guard_bytes () {
-  _guard '[[:digit:]]#([kKmMgG]|)' ${*:-size}
+  _numbers -u bytes ${*:-size} k m g
 }
 
 (( $+functions[__git_datetimes] )) ||
diff --git a/Completion/Unix/Command/_head b/Completion/Unix/Command/_head
index f25c97c83..0771b1e4d 100644
--- a/Completion/Unix/Command/_head
+++ b/Completion/Unix/Command/_head
@@ -32,20 +32,14 @@ _arguments -C -s -S $opts : $args '*:file:_files' && return 0
 
 case $state in
   (number)
-    local mlt sign digit
-    mlt='multiplier:multiplier:((b\:512 K\:1024 KB\:1000 M\:1024\^2'
-    mlt+=' MB\:1000\^2 G\:1024\^3 GB\:1000\^3 T\:1024\^4 TB\:1000\^4))'
-    sign='sign:sign:((-\:"print all but the last specified bytes/lines"'
-    sign+=' +\:"print the first specified bytes/lines (default)"))'
-    digit='digits:digit:(0 1 2 3 4 5 6 7 8 9)'
-    if compset -P '(-|+|)[0-9]##'; then
-      _alternative $mlt $digit && ret=0
-    elif [[ -z $PREFIX ]]; then
-      _alternative $sign $digit && ret=0
-    elif compset -P '(+|-)'; then
-      _alternative $digit && ret=0
-    fi
-    ;;
+    local alts
+    [[ -z $PREFIX ]] && alts=(
+      'sign:sign:((-\:"print all but the last specified bytes/lines" +\:"print the first specified bytes/lines (default)"))'
+    )
+    compset -P '+'
+    alts+=( 'numbers: :_numbers -N $state_descr b\:512 K\:1024 KB\:1000 M\:1024\^2 MB\:1000\^2 G\:1024\^3 GB\:1000\^3 T\:1024\^4 TB\:1000\^4' )
+    _alternative $alts && ret=0
+  ;;
 esac
 
 return ret
diff --git a/Completion/Unix/Command/_killall b/Completion/Unix/Command/_killall
index 36accb2e0..3ddd36341 100644
--- a/Completion/Unix/Command/_killall
+++ b/Completion/Unix/Command/_killall
@@ -38,15 +38,9 @@ if _pick_variant psmisc=PSmisc unix --version; then
 
   case $state in
     (time)
-      local -a units=( 's:seconds' 'm:minutes' 'h:hours' 'd:days'
-			'w:weeks' 'M:months' 'y:years' )
-      if compset -P '[0-9]##(|.[0-9]#)'; then
-	_alternative 'float-numbers:: _message "float number"' \
-		    'units:: _describe unit units' && ret=0
-      else
-	_message 'float number and unit' && ret=0
-      fi
-      ;;
+      _numbers -fN age 's:seconds' 'm:minutes' 'h:hours' 'd:days' \
+          'w:weeks' 'M:months' 'y:years'
+    ;;
   esac
 
   return ret
diff --git a/Completion/Unix/Command/_pv b/Completion/Unix/Command/_pv
index 68f8e8586..d02d3a35d 100644
--- a/Completion/Unix/Command/_pv
+++ b/Completion/Unix/Command/_pv
@@ -25,7 +25,7 @@ _arguments -s -S $args \
   '(-q --quiet)'{-q,--quiet}"[don't output any transfer information at all, useful with -L]" \
   '(-W --wait)'{-W,--wait}'[display nothing until first byte transferred]' \
   '(-D --delay-start -R --remote)'{-D+,--delay-start=}'[display nothing until delay has passed]:delay (seconds)' \
-  '(-s --size)'{-s+,--size=}'[set estimated data size]:size (bytes):->size-unit' \
+  '(-s --size)'{-s+,--size=}'[set estimated data size]: :_numbers -u bytes size K M G T' \
   '(-l --line-mode -R --remote)'{-l,--line-mode}'[count lines instead of bytes]' \
   '(-0 --null -l --line-mode)'{-0,--null}'[lines are null-terminated]' \
   '(-i --interval)'{-i+,--interval=}'[update every after specified interval]:interval (seconds) [1]' \
@@ -34,8 +34,8 @@ _arguments -s -S $args \
   '(-N --name)'{-N+,--name=}'[prefix visual information with given name]:name' \
   '(-f --force -R --remote)'{-f,--force}'[output even if standard error is not a terminal]' \
   '(-c --cursor -R --remote)'{-c,--cursor}'[use cursor positioning escape sequences]' \
-  '(-L --rate-limit)'{-L+,--rate-limit=}'[limit transfer rate]:rate (bytes per second):->size-unit' \
-  '(-B --buffer-size)'{-B+,--buffer-size=}'[use a buffer size of given size]:size (bytes):->size-unit' \
+  '(-L --rate-limit)'{-L+,--rate-limit=}'[limit transfer rate]: :_numbers -u "bytes per second" rate K M G T' \
+  '(-B --buffer-size)'{-B+,--buffer-size=}'[use a buffer size of given size]: :_numbers -u bytes size K M G T' \
   '(-C --no-splice)'{-C,--no-splice}'[never use splice(), always use read/write]' \
   '(-R --remote)*'{-E,--skip-errors}"[skip read errors in input${Edesc}]" \
   '(-S --stop-at-size -R --remote)'{-S,--stop-at-size}'[stop after --size bytes have been transferred]' \
@@ -70,18 +70,6 @@ case $state in
       _pids $suf && ret=0
     fi
   ;;
-  size-unit)
-    if compset -P '<->'; then
-      _tags values units
-    else
-      _tags values
-    fi
-    while _tags; do
-      _requested values && _message -e values "$state_descr" && ret=0
-      _requested units expl unit compadd -o nosort - K M G T && ret=0
-      (( ret )) || break
-    done
-  ;;
 esac
 
 return ret
diff --git a/Completion/Unix/Command/_rclone b/Completion/Unix/Command/_rclone
index 27b4dd926..a2e3429f5 100644
--- a/Completion/Unix/Command/_rclone
+++ b/Completion/Unix/Command/_rclone
@@ -62,7 +62,7 @@ _arguments -C \
   '--backup-dir[make backups into hierarchy based at specified directory]:directory:_directories' \
   '--bind[specify socal address to bind to for outgoing connections]:IPv4, IPv6 or name' \
   '--buffer-size[specify in memory buffer size when reading files for each --transfer]:size [16M]' \
-  '--bwlimit[specify bandwidth limit]:BwTimetable (kBytes/s or b|k|M|G suffix)' \
+  '--bwlimit[specify bandwidth limit]: :_numbers -u kBytes/s limit b k M G' \
   '--cache-dir[specify directory rclone will use for caching]:directory [~/.cache/rclone]:_directories' \
   '--checkers[specify number of checkers to run in parallel]:number [8]': \
   '(-c --checksum)'{-c,--checksum}'[skip based on checksum & size, not mod-time & size]' \
@@ -99,15 +99,15 @@ _arguments -C \
   '--log-format[specify comma separated list of log format options]:string ["date,time"]' \
   '--log-level[specify log level]:string [NOTICE]:(DEBUG INFO NOTICE ERROR)'  \
   '--low-level-retries[number of low level retries to do]:int [10]' \
-  '--max-age[only transfer files younger than this in s or suffix ms|s|m|h|d|w|M|y]:duration [default off]' \
+  '--max-age[only transfer files younger than specified age]: :_numbers -u seconds age ms\:milliseconds \:s\:seconds m\:minutes h\:hours d\:days w\:weeks M\:months y\:years' \
   '--max-backlog[maximum number of objects in sync or check backlog]:int [10000]' \
   '--max-delete[when synchronizing, limit the number of deletes]:delete limit [-1]' \
   '--max-depth[limit the recursion depth]:depth [-1]' \
-  '--max-size[only transfer files smaller than this in k or suffix b|k|M|G]:int [default off]' \
+  '--max-size[only transfer files smaller than specified size]: :_numbers -u kBytes size \:k M G' \
   '--max-transfer[maximum size of data to transfer]:int [default off]' \
   '--memprofile[write memory profile to file]:file:_files' \
-  '--min-age[only transfer files older than this in s or suffix ms|s|m|h|d|w|M|y]:duration [default off]' \
-  '--min-size[only transfer files bigger than this in k or suffix b|k|M|G]:int [default off]' \
+  '--min-age[only transfer files older than specified age]: :_numbers -u seconds age ms\:milliseconds \:s\:seconds m\:minutes h\:hours d\:days w\:weeks M\:months y\:years' \
+  '--min-size[only transfer files bigger than specified size]: :_numbers -u kBytes size \:k M G' \
   '--modify-window[specify max time delta to be considered the same]:duration [1ns]' \
   '--multi-thread-cutoff[use multi-threaded downloads for files above specified size]:size (250M)' \
   '--multi-thread-streams[specify max number of streams to use for multi-threaded downloads]:number (4)' \
diff --git a/Completion/Unix/Command/_rsync b/Completion/Unix/Command/_rsync
index b1a4f6046..eb906e974 100644
--- a/Completion/Unix/Command/_rsync
+++ b/Completion/Unix/Command/_rsync
@@ -99,7 +99,7 @@ _rsync() {
   _arguments -s \
     '*'{-v,--verbose}'[increase verbosity]' \
     {--no-v,--no-verbose}'[turn off --verbose]' \
-    '--bwlimit=[limit I/O bandwidth]:limit (KiB per second)' \
+    '--bwlimit=[limit I/O bandwidth]: :_numbers -f -u "KiB per second" -d 1g limit B K M G T P' \
     '--outbuf=[set output buffering]:buffering:(none line block)' \
     '--port=[specify alternate port number]:port:(873)' \
     '--address=[bind to the specified address]:bind address:_bind_addresses' \
@@ -181,7 +181,7 @@ _rsync() {
     {--no-W,--no-whole-file}'[turn off --whole-file]' \
     '(--cc --checksum-choice)'{--cc,--checksum-choice}'=[choose the checksum algorithms]:algorithm:_sequence -n 2 compadd - auto md4 md5 none' \
     '(-x --one-file-system)'{-x,--one-file-system}"[don't cross filesystem boundaries]" \
-    '(-B --block-size)'{-B+,--block-size=}'[force a fixed checksum block-size]:block size (bytes)' \
+    '(-B --block-size)'{-B+,--block-size=}'[force a fixed checksum block-size]: :_numbers -f -u bytes -d 1g "block size" B K M G T P' \
     '(-e --rsh)'{-e+,--rsh=}'[specify the remote shell to use]:remote-shell command:(rsh ssh)' \
     '--rsync-path=[specify path to rsync on the remote machine]:remote command' \
     '--ignore-existing[ignore files that already exist on receiving side]' \
@@ -199,10 +199,10 @@ _rsync() {
     '--force-change[affect user-/system-immutable files/dirs]' \
     '--force-uchange[affect user-immutable files/dirs]' \
     '--force-schange[affect system-immutable files/dirs]' \
-    '--max-delete=[do not delete more than NUM files]:number' \
-    '--max-size=[do not transfer any file larger than specified size]:number' \
+    "--max-delete=[don't delete more than NUM files]: :_numbers -f -u bytes size B K M G T P" \
+    "--max-size=[don't transfer any file larger than specified size]: :_numbers -f -u bytes size B K M G T P" \
     '--min-size=[do not transfer any file smaller than specified size]:number' \
-    '--max-alloc=[set limit to individual memory allocation]:size (bytes) [1g]' \
+    '--max-alloc=[set limit to individual memory allocation]: :_numbers -f -u bytes -d 1g size B K M G T P' \
     '(-P)--partial[keep partially transferred files]' \
     '--no-partial[turn off --partial]' \
     '--partial-dir=[put a partially transferred file into specified directory]:directory:_directories' \
diff --git a/Completion/Unix/Command/_stdbuf b/Completion/Unix/Command/_stdbuf
index a18938ee1..4b7d98ba0 100644
--- a/Completion/Unix/Command/_stdbuf
+++ b/Completion/Unix/Command/_stdbuf
@@ -7,7 +7,9 @@ short=( -e -i -o )
 long=( --error --input --output )
 buf=( err in out )
 
-opt='[set initial buffering for std${buf[i]}]:mode or size:((0\:unbuffered L\:line\ buffered'
+opt='[set initial buffering for std${buf[i]}]: : _alternative
+  "sizes\: \: _numbers -u bytes size k M G"
+  "modes\:mode\:((0\:unbuffered L\:line\ buffered'
 if _pick_variant gnu=GNU freebsd --version; then
   gnu=1
   args=(
@@ -17,7 +19,7 @@ if _pick_variant gnu=GNU freebsd --version; then
 else
   opt+=' B\:fully\ buffered'
 fi
-opt+='))'
+opt+='))"'
 
 for ((i=1;i<=3;i++)); do
   args+=( "(${long[i]})${short[i]}+${(e)opt}" )
diff --git a/Completion/Unix/Command/_tail b/Completion/Unix/Command/_tail
index 6d6e9b2d5..e54a0b06e 100644
--- a/Completion/Unix/Command/_tail
+++ b/Completion/Unix/Command/_tail
@@ -53,20 +53,14 @@ _arguments -C -s -S $opts : $args '*:file:_files' && return
 
 case $state in
   (number)
-    local mlt sign digit
-    mlt='multipliers:multiplier:((b\:512 K\:1024 KB\:1000 M\:1024\^2'
-    mlt+=' MB\:1000\^2 G\:1024\^3 GB\:1000\^3 T\:1024\^4 TB\:1000\^4))'
-    sign='signs:sign:((+\:"start at the specified byte/line"'
-    sign+=' -\:"output the last specified bytes/lines (default)"))'
-    digit='digits:digit:(0 1 2 3 4 5 6 7 8 9)'
-    if compset -P '(-|+|)[0-9]##'; then
-      _alternative $mlt $digit && ret=0
-    elif [[ -z $PREFIX ]]; then
-      _alternative $sign $digit && ret=0
-    elif compset -P '(+|-)'; then
-      _alternative $digit && ret=0
-    fi
-    ;;
+    local alts
+    [[ -z $PREFIX ]] && alts=(
+      'sign:sign:((-\:"print all but the last specified bytes/lines" +\:"print the first specified bytes/lines (default)"))'
+    )
+    compset -P '+'
+    alts+=( 'numbers: :_numbers -N $state_descr b\:512 K\:1024 KB\:1000 M\:1024\^2 MB\:1000\^2 G\:1024\^3 GB\:1000\^3 T\:1024\^4 TB\:1000\^4' )
+    _alternative $alts && ret=0
+  ;;
 esac
 
 return ret
diff --git a/Completion/Unix/Command/_timeout b/Completion/Unix/Command/_timeout
index 2235fa5ec..c041283ac 100644
--- a/Completion/Unix/Command/_timeout
+++ b/Completion/Unix/Command/_timeout
@@ -16,5 +16,5 @@ _arguments -S -A "-" $args \
   "--foreground[don't propagate timeout to the command children]" \
   '(-s --signal)'{-s,--signal}'[specify the signal to send on timeout]:signal:_signals' \
   '(-k --kill-after)'{-k,--kill-after}'[followup first signal with SIGKILL if command persists after specified time]:time' \
-  '1: :_guard "[0-9.]#([smhd]|)" duration' \
+  '1: :_numbers -f -u seconds duration :s:seconds m:minutes h:hours d:days' \
   '*:::command:_normal'
diff --git a/Completion/Unix/Command/_zfs b/Completion/Unix/Command/_zfs
index 452e1160d..51da9170b 100644
--- a/Completion/Unix/Command/_zfs
+++ b/Completion/Unix/Command/_zfs
@@ -162,12 +162,20 @@ _zfs() {
 		"multilevel:value:(on off)"
 		"nbmand:value:(on off)"
 		"primarycache:value:(all none metadata)"
-		"quota:number or 'none':{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ $PREFIX == quota= ]]; then _wanted none expl 'number or none' compadd none; else _wanted none expl 'quota' compadd none; fi}"
+		"quota: : _alternative \
+				'sizes: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes size :B {k,M,G,T,P,E,Z}{,B}' \
+				'properties:property:(none)'"
 		"readonly:value:(on off)"
 		"recordsize:value:(512 1K 2K 4K 8K 16K 32K 64K 128K 256K 512K 1M)"
-		"refquota:number or 'none':{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ $PREFIX == refquota= ]]; then _wanted none expl 'number or none' compadd none; else _wanted none expl 'refquota' compadd none; fi}"
-		"refreservation:number or 'none':{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ $PREFIX == refreservation= ]]; then _wanted none expl 'number or none' compadd none; else _wanted none expl 'refreservation' compadd none; fi}"
-		"reservation:value:{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ $PREFIX == reservation= ]]; then _wanted none expl 'number or none' compadd none; else _wanted none expl 'reservation' compadd none; fi}"
+		"refquota: : _alternative \
+				'sizes: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes size :B {k,M,G,T,P,E,Z}{,B}' \
+				'properties:property:(none)'"
+		"refreservation: : _alternative \
+				'sizes: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes size :B {k,M,G,T,P,E,Z}{,B}' \
+				'properties:property:(auto none)'"
+		"reservation: : _alternative \
+				'sizes: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes size :B {k,M,G,T,P,E,Z}{,B}' \
+				'properties:property:(none)'"
 		"rstchown:value:(on off)"
 		"secondarycache:value:(all none metadata)"
 		"setuid:value:(on off)"
@@ -238,8 +246,8 @@ _zfs() {
 			':filesystem:_zfs_dataset -t fs -e "parent dataset"' \
 			- set2 \
 			'-s[Create sparse volume]' \
-			'-b[Set volblocksize]:blocksize:' \
-			'-V[Set size]:size:' \
+			'-b+[set volblocksize]: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes blocksize :B {k,M,G,T,P,E,Z}{,B}' \
+			'-V+[set size]: :_numbers -M "m\:{a-zA-Z}={A-Za-z}" -u bytes size :B {k,M,G,T,P,E,Z}{,B}' \
 			':volume:_zfs_dataset -t fs -e "parent dataset"'
 		;;
 
diff --git a/Completion/X/Command/_xset b/Completion/X/Command/_xset
index b35a6466b..adda47f01 100644
--- a/Completion/X/Command/_xset
+++ b/Completion/X/Command/_xset
@@ -91,8 +91,8 @@ _regex_arguments _xset_parse \
     \( "/(blank|noblank|expose|noexpose|default|on|activate|reset)$nul/" \
        ':option-s:screen saver:(blank noblank expose noexpose default on activate reset off)' \
     \| "/off$nul/" \( "/off$nul/" ':option-s-off-period:period off:(off)' \| \) \
-    \| "/[0-9]##$nul/" ':option-s-timeout:length:' \
-      \( "/[0-9]##$nul/" ':option-s-period:period:' \
+    \| "/[0-9]##$nul/" ':option-s-timeout: :_numbers -u seconds length' \
+      \( "/[0-9]##$nul/" ':option-s-period: :_numbers -u seconds period' \
       \| \) \
     \| \) \
   \| "/-r$nul/" "$guard" \