summary refs log tree commit diff
path: root/Completion
diff options
context:
space:
mode:
Diffstat (limited to 'Completion')
-rw-r--r--Completion/Unix/Command/_zfs1911
-rw-r--r--Completion/Unix/Command/_zpool311
-rw-r--r--Completion/Unix/Type/_zfs_dataset15
-rw-r--r--Completion/Unix/Type/_zfs_keysource_props15
4 files changed, 1359 insertions, 893 deletions
diff --git a/Completion/Unix/Command/_zfs b/Completion/Unix/Command/_zfs
index 51da9170b..be4a64b33 100644
--- a/Completion/Unix/Command/_zfs
+++ b/Completion/Unix/Command/_zfs
@@ -1,561 +1,1350 @@
-#compdef zfs
-# Synced with the S11U1 man page
-
-_zfs() {
-	local context state line expl implementation
-	typeset -A opt_args
-	local -a subcmds rw_properties rw_propnames ro_properties create_properties
-	local -a share_nfs_ro_properties share_nfs_rw_properties
-	local -a share_smb_ro_properties share_smb_rw_properties
-	local -a share_ro_properties share_rw_properties
-	local -a difffields delegatable_perms
-
-	_pick_variant -r implementation -c 'zpool upgrade -v' openzfs='This system supports ZFS pool feature flags' solaris
-
-	subcmds=(
-		"create" "destroy" "clone" "promote" "rename" "snapshot"
-		"rollback" "list" "set" "get" "inherit" "mount" "unmount"
-		"share" "unshare" "send" "receive" "allow" "unallow"
-		"upgrade" "userspace" "groupspace" "hold" "holds" "release"
-		"diff" "key" "help"
-	)
-
-  [[ $OSTYPE == freebsd<7->.* ]] && subcmds+=(jail unjail)
-
-	share_nfs_ro_properties=(
-		"share.nfs.all"
-	)
-
-	share_nfs_rw_properties=(
-		"share.nfs:value:(on off)"
-		"share.nfs.aclok:value:(on off)"
-		"share.nfs.acflfab:value:(on off)"
-		"share.nfs.anon:uid:"
-		"share.nfs.charset.euc-cn:access-list:"
-		"share.nfs.charset.euc-jpms:access-list:"
-		"share.nfs.charset.euc-kr:access-list:"
-		"share.nfs.charset.euc-tw:access-list:"
-		"share.nfs.charset.iso8859-1:access-list:"
-		"share.nfs.charset.iso8859-2:access-list:"
-		"share.nfs.charset.iso8859-5:access-list:"
-		"share.nfs.charset.iso8859-6:access-list:"
-		"share.nfs.charset.iso8859-7:access-list:"
-		"share.nfs.charset.iso8859-8:access-list:"
-		"share.nfs.charset.iso8859-9:access-list:"
-		"share.nfs.charset.iso8859-13:access-list:"
-		"share.nfs.charset.iso8859-15:access-list:"
-		"share.nfs.charset.koi8-r:access-list:"
-		"share.nfs.index:file:_files"
-		"share.nfs.log:nfslog.conf tag:"
-		"share.nfs.nosub:value:(on off)"
-		"share.nfs.nosuid:value:(on off)"
-		"share.nfs.public:value:(on off)"
-		"share.nfs.sec:security-mode-list:"
-		"share.nfs.sec.default.none:access-list:"
-		"share.nfs.sec.default.ro:access-list:"
-		"share.nfs.sec.default.root:access-list:"
-		"share.nfs.sec.default.root_mapping:uid:"
-		"share.nfs.sec.default.rw:access-list:"
-		"share.nfs.sec.default.window:seconds"
-		"share.nfs.sec.dh.none:access-list:"
-		"share.nfs.sec.dh.ro:access-list:"
-		"share.nfs.sec.dh.root:access-list:"
-		"share.nfs.sec.dh.root_mapping:uid:"
-		"share.nfs.sec.dh.rw:access-list:"
-		"share.nfs.sec.dh.window:seconds"
-		"share.nfs.sec.krb5.none:access-list:"
-		"share.nfs.sec.krb5.ro:access-list:"
-		"share.nfs.sec.krb5.root:access-list:"
-		"share.nfs.sec.krb5.root_mapping:uid:"
-		"share.nfs.sec.krb5.rw:access-list:"
-		"share.nfs.sec.krb5.window:seconds"
-		"share.nfs.sec.krb5i.none:access-list:"
-		"share.nfs.sec.krb5i.ro:access-list:"
-		"share.nfs.sec.krb5i.root:access-list:"
-		"share.nfs.sec.krb5i.root_mapping:uid:"
-		"share.nfs.sec.krb5i.rw:access-list:"
-		"share.nfs.sec.krb5i.window:seconds"
-		"share.nfs.sec.krb5p.none:access-list:"
-		"share.nfs.sec.krb5p.ro:access-list:"
-		"share.nfs.sec.krb5p.root:access-list:"
-		"share.nfs.sec.krb5p.root_mapping:uid:"
-		"share.nfs.sec.krb5p.rw:access-list:"
-		"share.nfs.sec.krb5p.window:seconds"
-		"share.nfs.sec.none.none:access-list:"
-		"share.nfs.sec.none.ro:access-list:"
-		"share.nfs.sec.none.root:access-list:"
-		"share.nfs.sec.none.root_mapping:uid:"
-		"share.nfs.sec.none.rw:access-list:"
-		"share.nfs.sec.none.window:seconds"
-		"share.nfs.sec.sys.none:access-list:"
-		"share.nfs.sec.sys.ro:access-list:"
-		"share.nfs.sec.sys.root:access-list:"
-		"share.nfs.sec.sys.root_mapping:uid:"
-		"share.nfs.sec.sys.rw:access-list:"
-		"share.nfs.sec.sys.window:seconds"
-	)
-
-	share_smb_ro_properties=(
-		"share.smb.all"
-	)
-
-	share_smb_rw_properties=(
-		"share.smb:value:(on off)"
-		"share.smb.ad-container"
-		"share.smb.abe"
-		"share.smb.csc:value:(disabled manual auto vdo)"
-		"share.smb.catia:value:(on off)"
-		"share.smb.dfsroot:value:(on off)"
-		"share.smb.guestok:value:(on off)"
-		"share.smb.ro:access-list:"
-		"share.smb.rw:access-list:"
-		"share.smb.none:access-list:"
-	)
-
-	share_ro_properties=(
-		"share.all"
-		"share.fs"
-		"share.name"
-		"share.point"
-		"share.protocols"
-		"share.state"
-		$share_nfs_ro_properties
-		$share_smb_ro_properties
-	)
-
-	share_rw_properties=(
-		"share.desc:description:"
-		"share.noauto:value:(on off)"
-		"share.path:path:"
-		$share_nfs_rw_properties
-		$share_smb_rw_properties
-	)
-
-	# TODO: userused@ and groupused@ could have more extensive handling
-	ro_properties=(
-		"name" "type" "creation" "space" "used" "available" "referenced"
-		"compressratio" "mounted" "origin" "usedbychildren"
-		"usedbydataset" "usedbyrefreservation" "usedbysnapshots"
-		"defer_destroy" "userused@" "userrefs" "groupused@"
-		"keychangedate" "keystatus" "rekeydate"
-		$share_ro_properties
-	)
-
-	# TODO: Be cleverer about what values can be set.  Is there any way to
-	# set the sorting for *size properties to false by default?
-	rw_properties=(
-		"aclinherit:value:(discard noallow restricted passthrough passthrough-x)"
-		"atime:value:(on off)"
-		"canmount:value:(on off noauto)"
-		"checksum:value:(on off fletcher2 fletcher4 sha256 sha256+mac)"
-		"compression:value:(on off lzjb lz4 gzip gzip-{1..9} zle)"
-		"copies:value:(1 2 3)"
-		"dedup:value:(on off verify sha256 sha256,verify)"
-		"devices:value:(on off)"
-		"encryption:value:(off on aes128-ccm aes-192-ccm aes-256-ccm aes-128-gcm aes-192-gcm aes-256-gcm)"
-		"exec:value:(on off)"
-		"groupquota@:value:" # TODO: complete group=size|none
-		"keysource:value:_zfs_keysource_props"
-		"logbias:value:(latency throughput)"
-		"mlslabel:value:(none)" # TODO: list sensitivity labels
-		"mountpoint:path, 'legacy', or 'none':{if [[ -prefix /* ]]; then _path_files -/; else _wanted mountpoints expl 'mountpoint (type \"/\" to start completing paths)' compadd legacy none; fi}"
-		"multilevel:value:(on off)"
-		"nbmand:value:(on off)"
-		"primarycache:value:(all none metadata)"
-		"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: : _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)"
-		"shadow:value:" # TODO: complete URI|none
-		"share:share properties:"
-		"snapdir:value:(hidden visible)"
-		"sync:value:(standard always disabled)"
-		"userquota@:value:" # TODO: complete user=size|none
-		"version:value:(1 2 3 4 current)"
-		"volsize:value:" # <size>
-		"vscan:value:(on off)"
-		"xattr:value:(on off)"
-		"zoned:value:(on off)"
-		$share_rw_properties
-	)
-
-		if [[ "$OSTYPE" == "linux-gnu" ]]; then
-			rw_properties+=("acltype:value:(off noacl posixacl)")
-		elif [[ "$implementation" == "solaris" ]]; then
-			rw_properties+=("aclmode:value:(discard mask passthrough)")
-		else
-			rw_properties+=("aclmode:value:(discard groupmask passthrough restricted)")
-		fi
-
-
-	create_properties=(
-		$rw_properties
-		"casesensitivity:value:(sensitive insensitive mixed)"
-		"normalization:value:(none formC formD formKC formKD)"
-		"utf8only:value:(on off)"
-		"volblocksize:value:(512 1K 2K 4K 8K 16K 32K 64K 128K 256K 512K 1M)"
-	)
-
-	delegatable_perms=(
-		"allow" "clone" "create" "destroy" "diff" "hold" "key"
-		"keychange" "mount" "promote" "receive" "release" "rename"
-		"rollback" "send" "share" "snapshot"
-		"groupused" "userused" "userprop"
-		${create_properties%%:*}
-	)
-
-	rw_propnames=( ${rw_properties%%:*} )
-
-	difffields=(
-		object parent size links linkschange name oldname user group
-		ctime mtime atime crtime
-	)
-
-	if [[ $service == "zfs" ]]; then
-		_arguments -C -A "-*" \
-			'-\?[Help]' \
-			'*::command:->subcmd' && return 0
-
-		if (( CURRENT == 1 )); then
-			_wanted commands expl "zfs subcommand" compadd -a subcmds
-			return
-		fi
-		service="$words[1]"
-		curcontext="${curcontext%:*}=$service:"
-	fi
-
-	case $service in
-	("create")
-		_arguments -A "-*" \
-			'-p[Create parent datasets]' \
-			'*-o[Set initial properties]:property:_values -s , "property" $create_properties' \
-			- set1 \
-			':filesystem:_zfs_dataset -t fs -e "parent dataset"' \
-			- set2 \
-			'-s[Create sparse volume]' \
-			'-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"'
-		;;
-
-	("destroy")
-		_arguments -A "-*" \
-			'-r[Recursively destroy all children]' \
-			'-R[Recursively destroy all dependents]' \
-			- set1 \
-			'-d[delete or mark deferred]' \
-			':snapshot:_zfs_dataset -t snap' \
-			- set2 \
-			'-f[Force unmounts]' \
-			':filesystem/volume/snapshot:_zfs_dataset -t fs -t vol'
-		;;
-
-	(snap(|shot))
-		_arguments -A "-*" \
-			'-r[Recursively snapshot all descendant datasets]' \
-			'*-o[Set property]:property:_values -s , "property" $create_properties' \
-			':filesystem/volume:_zfs_dataset -t fs -t vol -S@'
-		;;
-
-	("rollback")
-		_arguments -A "-*" \
-			'-r[Recursively destroy more recent snapshots]' \
-			'-R[Recursively destroy more recent snapshots and clones]' \
-			'-f[Force unmounts]' \
-			':snapshot:_zfs_dataset -t snap'
-		;;
-
-	("clone")
-		# XXX needs to bail if there are no snapshots
-		_arguments -A "-*" \
-			'-p[Create parent datasets]' \
-			'-K[Create encryption key]' \
-			'*-o[Set property]:property:_values -s , "property" $create_properties' \
-			':snapshot:_zfs_dataset -t snap' \
-			':filesystem/volume:_zfs_dataset -t fs -e "parent dataset"'
-		;;
-
-	("promote")
-		_arguments \
-			':filesystem:_zfs_dataset -t clone' \
-		;;
-
-	("rename")
-		_arguments -A "-*" \
-			'(-r)-p[Create parent datasets]' \
-			'(-p)-r[Recursively rename snapshots of all descendent datasets]' \
-			':dataset:_zfs_dataset -r1' \
-			':dataset:_zfs_dataset -r2'
-		;;
-
-	("list")
-		_arguments -A "-*" \
-			'-r[Recursively display children]' \
-			'-H[Scripting mode]' \
-			'-d[Depth]:value:' \
-			'-o[Properties to list]:property:_values -s , "property" $ro_properties $rw_propnames' \
-			'*-s[Sort key (ascending)]:property:_values "property" $ro_properties $rw_propnames' \
-			'*-S[Sort key (descending)]:property:_values "property" $ro_properties $rw_propnames' \
-			'-t[Dataset types to list]:dataset type:_values -s , "dataset type" all filesystem snapshot volume' \
-			'*:filesystem/volume/snapshot/path:_zfs_dataset -p'
-		;;
-
-	("set")
-		_arguments \
-			'-r[Recursively apply value]' \
-			':property:_values -s , "property" $rw_properties' \
-			'*:filesystem/volume:_zfs_dataset -t fs -t vol'
-		;;
-
-	("get")
-		_arguments -A "-*" \
-			"-r[Recursively display children's properties]" \
-			'-d[Depth]:value:' \
-			'-H[Scripting mode]' \
-			'-p[Display numbers exactly]' \
-			'-s[Specify sources]:source:_values -s , "source" local default inherited temporary none' \
-			'-o[Specify fields]:field:_values -s , "field" name property value source' \
-			':property:_values -s , "property" $ro_properties $rw_propnames all' \
-			'*:filesystem/volume/snapshot:_zfs_dataset'
-		;;
-
-	("inherit")
-		_arguments -A "-*" \
-			'-r[Recursively inherit property for all children]' \
-			'-S[Revert to received property value]' \
-			':property:_values -s , "property" $ro_properties $rw_properties' \
-			'*:filesystem/volume:_zfs_dataset -t fs -t vol'
-		;;
-
-	("userspace"|"groupspace")
-		_arguments -A "-*" \
-			'-n[Print numeric ID]' \
-			'-i[Translate SID to POSIX ID]' \
-			'-H[Tab-delimited output with no headers]' \
-			'-p[Parseable mode]' \
-			'-o[Properties to list]:property:_values -s , "property" type name used quota' \
-			'*-s[Sort key (ascending)]:property:_values "property" type name used quota' \
-			'*-S[Sort key (descending)]:property:_values "property" type name used quota' \
-			'-t[Types to list]:type:_values -s , "type" all posixuser smbuser posixgroup smbgroup' \
-			'*:filesystem/volume/snapshot:_zfs_dataset'
-		;;
-
-	("mount")
-		_arguments -A "-*" \
-			'-o[Mount options]:mount options:_values -s , "option" {,no}{devices,exec,setuid} ro rw' \
-			'-O[Overlay mount]' \
-			'-v[Report mount progress]' \
-			- set1 \
-			':filesystem:_zfs_dataset -t fs' \
-			- set2 \
-			'-a[Mount all available ZFS filesystems]'
-		;;
-
-	("unmount")
-		_arguments -A "-*" \
-			- set1 \
-			'-f[Force unmount]' \
-			':filesystem:_zfs_dataset -t fs -t mtpt' \
-			- set2 \
-			'-a[Unmount all ZFS filesystems]'
-		;;
-
-	("share")
-		_arguments -A "-*" \
-			- set1 \
-			'-a[Share all available ZFS filesystems]' \
-			- set2 \
-			'-r[Share filesystems recursively]' \
-			':filesystem:_zfs_dataset -t fs' \
-			- set3 \
-			'*-o[Create a share with these properties]:property:_values -w "share properties" $share_rw_properties' \
-			'-u[Create a share without sharing it]' \
-			':filesystem:_zfs_dataset -t fs' \
-			- set4 \
-			':filesystem:_zfs_dataset -t fs -t mtpt -t share'
-		;;
-
-	("unshare")
-		_arguments -A "-*" \
-			- set1 \
-			'-a[Unshare all shared ZFS filesystems]' \
-			- set2 \
-			'-r[Unshare filesystems recursively]' \
-			':filesystem:_zfs_dataset -t fs' \
-			- set3 \
-			':filesystem:_zfs_dataset -t fs -t mtpt -t share'
-		;;
-
-	("send")
-		_arguments -A "-*" \
-			'-b' \
-			'-i[Generate an incremental stream]:snapshot:_zfs_dataset -t snap' \
-			'-D[Perform dedup processing]' \
-			'-p[Send properties]' \
-			'-v[Verbose]' \
-			- set1 \
-			'-I[Generate an incremental stream with intermediary snapshots]:snapshot:_zfs_dataset -t snap' \
-			'-R[Generate a replication stream package]' \
-			':snapshot:_zfs_dataset -t snap' \
-			- set2 \
-			'-c[Create a self-contained stream]' \
-			'-r[Generate a recursive stream package]' \
-			':snapshot:_zfs_dataset -t snap'
-		;;
-
-	("receive")
-		_arguments -A "-*" \
-			'-v[Verbose]' \
-			'-n[Do not receive the stream]' \
-			'-F[Force a rollback if necessary]' \
-			'-u[Filesystem is not mounted]' \
-			'-o[Include property change in the stream]::' \
-			'-x[Exclude property change from the stream]:property:' \
-			- set1 \
-			':filesystem/volume/snapshot:_zfs_dataset' \
-			- set2 \
-			'(-e)-d[Set path prefix from stream, excluding only pool name]' \
-			'(-d)-e[Set path prefix from stream, using last path element]' \
-			'-:filesystem:_zfs_dataset -t fs'
-		;;
-
-	("allow")
-		_arguments -A "-*" \
-			'(1 -g -e -c -s)-u[delegate to user]:user:_users' \
-			'(1 -u -e -c -s)-g[delegate to group]:group:_groups' \
-			'(1 -g -u -c -s)-e[delegate to everyone]' \
-			'(1 -u -g -e -l -d -s)-c[set permissions for newly-created descendant filesystems]' \
-			'(1 -u -g -e -l -d -c)-s[define or modify permission sets]:permission set' \
-			'(1 -c -s)-l[allow for named dataset]' \
-			'(1 -c -s)-d[allow for descendent datasets]' \
-			'1::filesystem/volume:_zfs_dataset -t fs -t vol' \
-			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
-			':filesystem/volume:_zfs_dataset -t fs -t vol' \
-		;;
-
-	("unallow")
-		_arguments -A "-*" \
-			'-r[Recursive removal]' \
-			- set1 \
-			'-s[Remove permissions from or delete a permission set]:permission set:' \
-			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
-			':filesystem/volume:_zfs_dataset -t fs -t vol' \
-			- set2 \
-			'(-g)-u[User]:user:_users' \
-			'(-u)-g[Group]:group:_groups' \
-			'-l[Allow for named dataset]' \
-			'-d[Allow for descendent datasets]' \
-			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
-			':filesystem/volume:_zfs_dataset -t fs -t vol' \
-			- set3 \
-			'-e[Everyone]' \
-			'-l[Allow for named dataset]' \
-			'-d[Allow for descendent datasets]' \
-			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
-			':filesystem/volume:_zfs_dataset -t fs -t vol' \
-			- set4 \
-			'-c[Create-time permissions]' \
-			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
-			':filesystem/volume:_zfs_dataset -t fs -t vol'
-		;;
-
-	("upgrade")
-		_arguments -A "-*" \
-			- set1 \
-			'-v[Verbose]' \
-			- set2 \
-			'-a[Upgrade all filesystems on all pools]' \
-			'-r[Upgrade descendent filesystems, too]' \
-			'-V[Upgrade to specified version]:version:(1 2)' \
-			- set3 \
-			'-r[Upgrade descendent filesystems, too]' \
-			'-V[Upgrade to specified version]:version:(1 2)' \
-			':filesystem:_zfs_dataset -t fs'
-		;;
-
-	("hold")
-		_arguments -A "-*" \
-			'-r[Apply hold recursively]' \
-			':tag:' \
-			':snapshot:_zfs_dataset -t snap'
-		;;
-
-	("holds")
-		_arguments -A "-*" \
-			'-r[List holds recursively]' \
-			':snapshot:_zfs_dataset -t snap'
-		;;
-
-	("release")
-		_arguments -A "-*" \
-			'-r[Release holds recursively]' \
-			':tag:' \
-			':snapshot:_zfs_dataset -t snap'
-		;;
-
-	("diff")
-		_arguments -A "-*" \
-			'-F[Add column for filetype character]' \
-			'-H[Parseable output]' \
-			'-e[Only show new and changed files]' \
-			'*-o[Show fields]:field:_values "field" $difffields' \
-			'-t[Add column for ctime]' \
-			- set1 \
-			':snapshot:_zfs_dataset -t snap' \
-			':snapshot or filesystem:_zfs_dataset -t snap -t fs' \
-			- set2 \
-			'-E[Show difference from empty]' \
-			':snapshot or filesystem:_zfs_dataset -t snap -t fs'
-		;;
-
-	("key")
-		_arguments -A "-*" \
-			- set1 \
-			'-a[Apply to all datasets in all pools]' \
-			'(-u -K -f)-l[Load the encryption key]' \
-			'(-l -K)-u[Unload the encryption key]' \
-			'(-l -u -f)-K[Create a new data encryption key]' \
-			'(-l -K)-f[Unmount the dataset before unloading the encryption key]' \
-			'-r[Apply recursively]' \
-			':filesystem or volume:_zfs_dataset -t fs -t vol' \
-			- set2 \
-			'-c[Change the encryption key]' \
-			'-o[Change a property]:property:_zfs_keysource_props' \
-			':filesystem or volume:_zfs_dataset -t fs -t vol'
-		;;
-
-	("jail"|"unjail")
-		_arguments \
-			'1: : _jails' \
-			'2:filesystem:_zfs_dataset -t fs'
-		;;
-
-	("help")
-		_arguments -A "-*" \
-			- set1 \
-			':command:($subcmds $delegatable_perms $ro_properties ${rw_properties%%:*} properties)' \
-			- set2 \
-			'-l[Display property information]' \
-			': :(properties)'
-		;;
-
-	(*)
-		_message "unknown zfs subcommand: $service"
-		;;
-	esac
-}
-
-_zfs "$@"
+#compdef zfs zdb zpool
+
+local curcontext="$curcontext" implementation nm="$compstate[nmatches]"
+local -a state curstate line state_descr expl alts args
+typeset -A opt_args val_args
+local MATCH MBEGIN MEND
+local -a subcmds
+local -a share_nfs_ro_properties share_nfs_rw_properties
+local -a share_smb_ro_properties share_smb_rw_properties
+local -a share_ro_properties share_rw_properties
+local -a difffields delegatable_perms key_properties
+local -a ds_types sum_algorithms comp_algorithms dedup_algorithms
+
+local -a ds_propnames ro_ds_props rw_ds_props ci_ds_props # dataset properties
+local -a po_propnames ro_po_props rw_po_props ci_po_props # pool properties
+
+_pick_variant -r implementation -c 'zpool upgrade -v' openzfs='This system supports ZFS pool feature flags' solaris
+
+ds_types=( filesystem snapshot volume all )
+sum_algorithms=( on off fletcher2 fletcher4 sha256 )
+comp_algorithms=( on off lzjb lz4 gzip gzip-{1..9} zle )
+dedup_algorithms=( on off verify sha256 sha256,verify )
+
+ro_po_props=( # readonly
+  'all[all properties]'
+  'allocated[space allocated]'
+  'capacity[space used (percentage)]'
+  'dedupratio[deduplication ratio]'
+  'free[space unallocated]'
+  'health[health status]'
+  'size[total size]'
+)
+ci_po_props=( # only set at create or import
+  'altroot[alternate root directory]:path:_directories'
+  'guid[unique identifier]:identifier'
+  'readonly[whether the pool can be modified]:value:(on off)'
+)
+rw_po_props=(
+  'autoexpand[automatic pool expansion]:value:(on off)'
+  'autoreplace[automatic device replacement]:value:(on off)'
+  'bootfs[default bootable dataset]:dataset:_zfs_dataset'
+  'cachefile[pool configuration cache file location]:value'
+  'dedupditto[threshold for number of copies]:value [0]'
+  'delegation[delegated administration]:value:(on off)'
+  'failmode[failure-mode behavior]:value:(wait continue panic)'
+  "listshares[show shares in 'zfs list']:value:(on off)"
+  "listsnaps[show snapshots in 'zfs list']:value:(on off)"
+  'version[pool version]:version'
+)
+
+# TODO: userused@ and groupused@ could have more extensive handling
+ro_ds_props=(
+  name type creation space used available referenced compressratio mounted
+  origin usedbychildren usedbydataset usedbyrefreservation usedbysnapshots
+  defer_destroy userused@ userrefs groupused@ keystatus
+)
+ci_ds_props=(
+  'casesensitivity:value:(sensitive insensitive mixed)'
+  'normalization:value:(none formC formD formKC formKD)'
+  'utf8only:value:(on off)'
+)
+rw_ds_props=(
+  'aclinherit:value:(discard noallow restricted passthrough passthrough-x)'
+  'atime:value:(on off)'
+  'canmount:value:(on off noauto)'
+  "checksum:value:($sum_algorithms)"
+  "compression:value:($comp_algorithms)"
+  'copies:value:(1 2 3)'
+  "dedup:value:($dedup_algorithms)"
+  'devices:value:(on off)'
+  'encryption:value:(off on aes128-ccm aes-192-ccm aes-256-ccm aes-128-gcm aes-192-gcm aes-256-gcm)'
+  'exec:value:(on off)'
+  'groupquota@'
+  'logbias:value:(latency throughput)'
+  "mountpoint: : _alternative \
+      'properties:property:(none legacy)' \
+      'paths:mountpoint:_directories -W / -P /'"
+  'multilevel:value:(on off)'
+  'nbmand:value:(on off)'
+  'primarycache:value:(all none metadata)'
+  'quota: :->quotas'
+  'readonly:value:(on off)'
+  'recordsize:value:(512 1K 2K 4K 8K 16K 32K 64K 128K 256K 512K 1M)'
+  'refquota: :->quotas'
+  "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: :->quotas'
+  'rstchown:value:(on off)'
+  'secondarycache:value:(all none metadata)'
+  'setuid:value:(on off)'
+  'shadow:value' # TODO: complete URI|none
+  'share:share properties'
+  'snapdir:value:(hidden visible)'
+  'sync:value:(standard always disabled)'
+  'userquota@'
+  'version:value'
+  'volsize:size:_numbers -M "m:{a-zA-Z}={A-Za-z}" -u bytes size :B {k,M,G,T,P,E,Z}{,B}'
+)
+
+case $service:$implementation in
+  *:openzfs)
+    ds_types+=( bookmark )
+    sum_algorithms+=( noparity sha512 skein edonr )
+    comp_algorithms+=( zstd zstd-{1..19} zstd-fast zstd-fast-{{1..9}{,0},100,500,1000} )
+    dedup_algorithms+=( {sha512,skein}{,\,verify} edonr,verify )
+    share_rw_properties=( sharesmb:option sharenfs:option )
+    ro_po_props+=(
+      'expandsize[uninitialized space within the pool]'
+      'fragmentation[amount of fragmentation in the pool]'
+      'freeing[amount of space remaining to be reclaimed]'
+      'used[amount of storage space used within the pool]'
+      'load_guid[unique identifier generated when pool is loaded]'
+    )
+    ci_po_props+=(
+      'ashift[pool sector size exponent]:exponent:((9\:512 10\:1024 11\:2048 12\:4096 13\:8192 14\:16384 15\:32768 16\:65536))'
+    )
+    rw_po_props+=(
+      'autotrim[periodically trim recently freed space]:value:(on off)'
+      'comment[text string that is available even if the pool becomes faulted]:value'
+      'multihost[perform pool activity check during import]:value:(on off)'
+    )
+    rw_ds_props+=(
+      'aclmode:value:(discard groupmask passthrough restricted)'
+      'acltype:value:(off noacl nfsv4 posix posixacl)'
+      'mlslabel:value:(none)' # TODO: list sensitivity labels
+      'redundant_metadata:value:(all most)'
+      'vscan:value:(on off)'
+      'xattr:value:(on off dir sa)'
+      "filesystem_limit: :{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ -prefix n ]]; then compadd none; else _message -e limits 'number or none'; fi}"
+      "snapshot_limit: :{if [[ -prefix [0-9]## ]]; then _message -e 'number'; elif [[ -prefix n ]]; then compadd none; else _message -e limits 'number or none'; fi}"
+      'volmode:mode:((
+        default\:use\ system-wide\ tunable
+        full\:expose\ as\ block\ devices
+        geom\:expose\ as\ block\ devices
+        dev\:hide\ partitions
+        none\:not\ exposed\ outside\ zfs
+      ))'
+    )
+    ro_ds_props+=(
+      createtxg clones filesystem_count guid logicalreferenced logicalused
+      receive_resume_token refcompressratio snapshot_count volblocksize written
+    )
+    delegatable_perms=(
+      bookmark load-key change-key userobjquota userobjused groupobjquota
+      groupobjused projectused projectquota projectobjused projectobjquota
+    )
+  ;|
+  *:solaris)
+    ds_types+=( share )
+    sum_algorithms+=( sha256+mac )
+    share_nfs_ro_properties=( share.nfs.all )
+    share_nfs_rw_properties=(
+      'share.nfs:value:(on off)'
+      'share.nfs.aclok:value:(on off)'
+      'share.nfs.aclfab:value:(on off)'
+      'share.nfs.anon:uid'
+      'share.nfs.charset.'{cp932,euc-{cn,jpns,kr,tw},iso8859-{1,2,5,6,7,8,9,13,15},koi8-r,shift_jis}':access-list'
+      'share.nfs.index:file:_files'
+      'share.nfs.labeled:value:(on off)'
+      'share.nfs.noaclfab:value:(on off)'
+      'share.nfs.log:nfslog.conf tag'
+      'share.nfs.nosub:value:(on off)'
+      'share.nfs.nosuid:value:(on off)'
+      'share.nfs.public:value:(on off)'
+      'share.nfs.sec:security-mode-list'
+      'share.nfs.sec.'{default,dh,krb5{,i,p},none,sys}.{ro,root,rw}':access-list'
+      'share.nfs.sec.'{default,dh,krb5{,i,p},none,sys}.root_mapping':uid'
+      'share.nfs.sec.'{default,dh,krb5{,i,p},none,sys}.window':credential lifetime (seconds)'
+      'share.nfs.sec.sys.resvport:value:(on off)'
+    )
+    share_smb_ro_properties=( share.smb.all )
+    share_smb_rw_properties=(
+      'share.smb:value:(on off)'
+      'share.smb.abe'
+      'share.smb.ad-container'
+      'share.smb.catia:value:(on off)'
+      'share.smb.csc:value:(disabled manual auto vdo)'
+      'share.smb.dfsroot:value:(on off)'
+      'share.smb.encrypt:value:(on off)'
+      'share.smb.guestok:value:(on off)'
+      'share.smb.oplocks:value:(disabled enabled)'
+      'share.smb.cont_avail:value:(on off)'
+      'share.smb.'{none,ro,rw}':access-list'
+    )
+    share_ro_properties=(
+      share.all share.fs share.name share.point share.protocols share.state
+      $share_nfs_ro_properties $share_smb_ro_properties
+    )
+    share_rw_properties=(
+      'share.desc:description'
+      'share.auto:value:(on off)'
+      'share.autoname:value'
+      'share.nfs.cksum:value'
+      'share.path:path'
+      $share_nfs_rw_properties $share_smb_rw_properties
+    )
+    ro_po_props+=(
+      'lastscrub[start time of the last successful scrub]'
+    )
+    rw_po_props+=(
+      'clustered[pool is imported as a global pool in Oracle Solaris Cluster]:value:(on off)'
+      'scrubinternal[time interval between scheduled scrubs]:interval'
+    )
+    ro_ds_props+=( keychangedate rekeydate effective{read,write}limit )
+    rw_ds_props+=(
+      'aclmode:value:(discard mask passthrough)'
+      "defaultreadlimit: : _alternative \
+          'sizes: :_guard \[0-9\]\#\(\|\[BKMGTPEZ\]\) size\ \(bytes\ per\ second\)' \
+          'properties:property:(none)'"
+      "defaultwritelimit: : _alternative \
+          'sizes: :_guard \[0-9\]\#\(\|\[BKMGTPEZ\]\) size\ \(bytes\ per\ second\)' \
+          'properties:property:(none)'"
+      'defaultuserquota:->quotas'
+      'defaultgroupquota: :->quotas'
+      'keysource:value:->keysources'
+    )
+    ci_ds_props+=(
+      'volblocksize:value:compadd -o nosort 512 1K 2K 4K 8K 16K 32K 64K 128K 256K 512K 1M'
+    )
+    difffields=(
+      object parent size links linkschange name oldname user group
+      ctime mtime atime crtime mountpoint dataset_name
+    )
+    delegatable_perms=( key keychange )
+  ;|
+  zfs:openzfs)
+    subcmds+=(
+      bookmark change-key load-key program project projectspace redact
+      unload-key wait
+    )
+  ;|
+  zpool:openzfs)
+    subcmds+=(
+      checkpoint events labelclear initialize reopen resilver sync trim wait
+      version
+    )
+  ;|
+  zfs:solaris)
+    subcmds+=( key help )
+  ;|
+  zpool:solaris)
+    subcmds+=( help label monitor )
+  ;|
+
+  zfs:*)
+    subcmds+=(
+      create destroy clone promote rename snapshot rollback list set get
+      inherit mount unmount share unshare send receive allow unallow upgrade
+      userspace groupspace hold holds release diff
+    )
+    [[ $OSTYPE = freebsd<7->.* ]] && subcmds+=( jail unjail )
+  ;;
+  zpool:*)
+    subcmds+=(
+      add attach clear create destroy detach export get history import iostat
+      list offline online reguid remove replace scrub set split status upgrade
+    )
+  ;;
+esac
+
+case $OSTYPE in
+  solaris*)
+    rw_ds_props+=( 'zoned:value:(on off)' )
+  ;;
+  freebsd*)
+    [[ $OSTYPE = freebsd<-12>.* ]] && subcmds+=( remap )
+    rw_ds_props+=( 'jailed:value:(on off)' )
+  ;;
+  linux-gnu)
+    rw_ds_props+=( 'relatime:value:(on off)' )
+    ci_ds_props+=(
+      {,fs,def,root}'context:SELinux context:_selinux_contexts'
+    )
+  ;;
+esac
+
+delegatable_perms+=(
+  allow clone create destroy diff hold key keychange mount promote receive
+  release rename rollback send share snapshot groupquota groupused userprop
+  userused ${ci_ds_props%%:*}
+)
+
+key_properties=(
+  'keylocation:location [prompt]:_files -P file\:// -W /'
+  'keyformat:format:(raw hex passphrase)'
+  'pbkdf2iters:iterations [350000]'
+)
+
+ro_ds_props+=( $share_ro_properties )
+rw_ds_props+=( $share_rw_properties )
+ci_ds_props+=( $rw_ds_props )
+
+ds_propnames=( ${rw_ds_props%%:*} )
+po_propnames=( ${ro_po_props%%:*} ${ci_po_props%%:*} ${rw_po_props%%:*} )
+
+
+case $service in
+  zfs|zpool)
+    _arguments -C -A "-*" \
+      '-?[display usage information]' \
+      '*::command:->subcmd' && return 0
+
+    if (( CURRENT == 1 )); then
+      _wanted commands expl "subcommand" compadd -a subcmds
+      return
+    fi
+    curcontext="${curcontext%:*}-$words[1]:"
+  ;;
+  zdb)
+    if [[ $implementation = openzfs ]]; then
+      args=(
+        '-mm[also display free space histogram associated with each metaslab]'
+        {-mmm,-MM}'[display more free space information]'
+        {-mmmm,-MMM}'[display every spacemap record]'
+        '-DD[display a histogram of deduplication statistics]'
+        '-DDD[display deduplication statistics independently for each table]'
+        '-DDDD[dump the contents of the deduplication tables describing duplicate blocks]'
+        '-DDDDD[also dump the contents of the deduplication tables describing unique blocks]'
+        '-E+[decode and display block from a given embedded block pointer]:word'
+        '(-l)-ll+[like -l but display L2ARC log blocks]:device:_files'
+        '(-l -ll)-lll+[like -l but display every configuration, unique or not]:device:_files'
+        "-q[don't print labels (with -l)]"
+        '-k[examine the checkpointed state of the pool]'
+        '-M[display the offset, spacemap, and free space of each metaslab]' \
+        '-O+[look up the specified path inside of the dataset]:dataset:_zfs_dataset:path:_files'
+        '-o+[set the given global libzpool variable]:variable'
+        '-r+[copy the specified path inside of the dataset to the specified destination]:dataset:_zfs_dataset:path:_files:destination:_files'
+        '-x+[copy all blocks accessed to files in the specified directory]:directory:_directories'
+        '-V[attempt verbatim import]'
+        '-Y[attempt all possible combinations when reconstructing indirect split blocks]'
+        '-y[perform validation for livelists that are being deleted]'
+      )
+    else
+      args=(
+        '-?[display usage information]'
+        '-M+[dump MOS contents]:contents: _values -s , raw_config all objset dir pool_props metaslab sync_bplist dtl config spares l2cache history errlog_scrub errlog_last bpmap-vdev bpmap_defer_obj dtl-scan ddt2'
+        '-r[dump datasets recursively]'
+        '-z[report zombies only]'
+        '-V[verify DDT xtree block data]'
+        "-a[don't import l2arc cache data]"
+        '-f[attempt to force import (with -e)]'
+        '-w+[specify directory to save shadow copy of all accessed disk locations]: :_directories'
+        '-x+[set kernel tunable]:tunable'
+        '-G[dump the contents of the zfs_dbgmsg buffer before exiting]'
+        '-I[limit the number of outstanding checksum I/Os to the specified value]'
+      )
+    fi
+    _arguments -A "-*" -S $args \
+      '(-C)-b[display block statistics]' \
+      '(-C)*-c[verify checksum of metadata blocks]' \
+      '(-b -c -d)-C[display configuration information]' \
+      '(-C)*-d[display dataset information]' \
+      '-h[display pool history]' \
+      '-i[display intent log (ZIL) information]' \
+      '-l+[read the vdev labels from the specified device]:device:_files' \
+      '-m[display the offset, spacemap, and free space of each metaslab]' \
+      '-s[report statistics on zdb I/O]' \
+      '*-u[also display the uberblocks on the device (with -l)]' \
+      '*-v[enable verbose output]' \
+      '-D[display deduplication statistics]' \
+      '-S[simulate the effects of deduplication, displaying constructed DDT as with -DD]' \
+      '-L[disable leak detection and the loading of space maps]' \
+      '-R+[read and display a block from the specified device]:device' \
+      "-A[don't abort should any assertion fail]" \
+      "-AA[enable panic recovery]" \
+      '-F[try progressively older transactions until pool is readable]' \
+      '-U+[specify cache file to use]:cache file [/etc/zfs/zpool.cache]:_files' \
+      '-X[attempt "extreme" transaction rewind]' \
+      '-e[operate on an exported pool]' \
+      '-p[specify path under which to search for devices (with -e)]:path:_files' \
+      '-P[use exact (parsable) numeric output]' \
+      '-t+[specify the highest transaction to use when searching for uberblocks]:transaction' \
+      '1:pool:_zfs_pool'
+    return
+  ;;
+esac
+
+case $service:$words[1] in
+  zfs:create)
+    [[ $implementation = openzfs ]] && args=(
+      '-P[print machine-parsable verbose information about the created dataset]'
+      '-n[do a dry-run, no dataset will be created]'
+      '-v[print verbose information about the created dataset]'
+    )
+    _arguments -C -A "-*" -S $args \
+      '-p[create parent datasets]' \
+      '*-o+[set initial propertyvalue]:property:->create-properties' \
+      - set1 \
+      ':filesystem:_zfs_dataset -t fs -e "parent dataset"' \
+      - set2 \
+      '-s[create sparse volume]' \
+      '-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"'
+  ;;
+
+  zfs:destroy)
+    if [[ $implementation = openzfs ]]; then
+      args=(
+        '-n[do a dry-run, no data will be deleted]'
+        '-p[print machine-parsable verbose information about the deleted data]'
+        '-v[print verbose information about the deleted data]'
+      )
+    else
+      args=( '-s[destroy snapshots synchronously - only return when blocks freed]' )
+    fi
+    _arguments -A "-*" -S $args \
+      '-r[recursively destroy all children]' \
+      '-R[recursively destroy all dependents]' \
+      '(-f)-d[delete or mark deferred]' \
+      '(-d)-f[force unmounts]' \
+      ':dataset:_zfs_dataset -t fs -t vol ${=${opt_args[(i)-f]:--t snap}:/-f/} ${=${opt_args[(i)-*]:--t bookmark}:/-?/}'
+  ;;
+
+  zfs:snap(|shot))
+    _arguments -C -A "-*" -S \
+      '-r[recursively snapshot all descendant datasets]' \
+      '*-o+[set property]:property:->create-properties' \
+      ':filesystem/volume:_zfs_dataset -t fs -t vol -S@'
+  ;;
+
+  zfs:rollback)
+    _arguments -A "-*" -S \
+      '-r[recursively destroy more recent snapshots]' \
+      '-R[recursively destroy more recent snapshots and clones]' \
+      '-f[force unmounts]' \
+      ':snapshot:_zfs_dataset -t snap'
+  ;;
+
+  zfs:clone)
+    [[ $implementation = solaris ]] && args+=(
+    '-K[create encryption key]'
+  )
+  _arguments -C -A "-*" -S $args \
+    '-p[create parent datasets]' \
+    '*-o+[set property]:property:->create-properties' \
+    ':snapshot:_zfs_dataset -t snap' \
+    ':filesystem/volume:_zfs_dataset -t fs -e "parent dataset"'
+  ;;
+
+  zfs:promote)
+    _arguments \
+      ':filesystem:_zfs_dataset -t clone' \
+  ;;
+
+  zfs:rename)
+    [[ $implementation = openzfs ]] && args=(
+      '(-r -u)-f[force unmount any filesystems]'
+      "(-r -f)-u[don't remount file systems during rename]"
+    )
+    _arguments -A "-*" -S $args \
+      '(-r)-p[create parent datasets]' \
+      '(-p -u -f)-r[recursively rename snapshots of all descendent datasets]' \
+      ':dataset:_zfs_dataset -r1' \
+      ':dataset:_zfs_dataset -r2'
+  ;;
+
+  zfs:bookmark)
+    _arguments \
+      ':snapshot or bookmark:_zfs_dataset -t snap -t bookmark' \
+      ':bookmark'
+  ;;
+
+  zfs:program)
+    _arguments -A "-*" -S \
+      '-j[display channel program output in JSON format]' \
+      '-n[execute a read-only channel program]' \
+      '-t+[limit the number of Lua instructions to execute]:instruction limit' \
+      '-m+[specify memory limit]:memory limit (bytes) [10MB]' \
+      ':pool:_zfs_pool' \
+      ':script:_files' \
+      '*: :_default'
+  ;;
+
+  zfs:list)
+    if [[ $implementation = solaris ]]; then
+      args=( '-I+[specify dataset states to display instead of normal datasets]:dataset state:_sequence compadd - receiving resumable hidden all' )
+    else
+      args=( '-p[use exact (parsable) numeric output]' )
+    fi
+    _arguments -A "-*" -S $args \
+      '(-d)-r[recursively display children]' \
+      '-H[suppress printing of headers]' \
+      '(-r)-d+[depth]:value' \
+      '-o+[specify properties to list]: :_values -s , "property" $ro_ds_props $ds_propnames' \
+      '*-s+[specify sort key (ascending)]: :_values "property" $ro_ds_props $ds_propnames' \
+      '*-S+[specify sort key (descending)]: :_values "property" $ro_ds_props $ds_propnames' \
+      '-t+[specify dataset types to list]: :_values -s , "dataset type" $ds_types' \
+      '*:filesystem/volume/snapshot/path:_zfs_dataset -p'
+  ;;
+
+  zfs:set)
+    [[ $implementation = solaris ]] && args=(
+      '-r[recursively apply value]' \
+    )
+    _arguments -C -A "-*" -S $args \
+      ':property:->set-properties' \
+      '*:filesystem/volume:_zfs_dataset -t fs -t vol'
+  ;;
+
+  zfs:get)
+    if [[ $implementation == openzfs ]]; then
+      args=( '-t+[specify dataset types to display]: :_values -s , "dataset type" $ds_types' )
+    else
+      args=( '-e[expand property sublists to any depth]' )
+    fi
+    _arguments -A "-*" -S $args \
+      "(-d)-r[recursively display children's properties]" \
+      '(-r)-d+[depth]:value' \
+      '-H[suppress printing of headers]' \
+      '-p[use exact (parsable) numeric output]' \
+      '-s+[specify sources]: :_values -s , "source" local default inherited received temporary none' \
+      '-o+[specify fields]: :_values -s , "field" name property received value source' \
+      ':property:_values -s , "property" $ro_ds_props $ds_propnames all' \
+      '*:filesystem/volume/snapshot:_zfs_dataset'
+  ;;
+
+  zfs:inherit)
+    _arguments -C -A "-*" -S \
+      '-r[recursively inherit property for all children]' \
+      '-S[revert to received property value]' \
+      ':property:_values "property" $ro_ds_props ${rw_ds_props%%:*}' \
+      '*:filesystem/volume:_zfs_dataset -t fs -t vol'
+  ;;
+
+  zfs:remap)
+    _arguments \
+      ':filesystem or volume:_zfs_dataset -t fs -t vol'
+  ;;
+
+  zfs:upgrade)
+    _arguments -A "-*" -S \
+      '(- :)-v[display supported ZFS versions]' \
+      '(-v :)-a[upgrade all filesystems on all pools]' \
+      '(-v)-r[upgrade descendent filesystems, too]' \
+      '(-v)-V+[upgrade to specified version]:version' \
+      '(-a -v):filesystem:_zfs_dataset -t fs'
+  ;;
+
+  zfs:(user|group)space)
+    args=(
+      '-n[print numeric ID]'
+      '-i[translate SID to POSIX ID]'
+    )
+  ;& # fall-through
+  zfs:projectspace)
+    [[ $implementation = solaris ]] && args+=(
+      '(- *)'{-h,--help}'[display usage information]'
+    )
+    _arguments -A "-*" -S $args \
+      '-H[suppress printing of headers, tab-delimit columns]' \
+      '-p[use exact (parsable) numeric output]' \
+      '-o+[specify properties to list]:property:_values -s , "property" type name used quota' \
+      '*-s+[specify sort key (ascending)]: :_values "property" type name used quota' \
+      '*-S+[specify sort key (descending)]: :_values "property" type name used quota' \
+      '-t+[specify types to list]:type:_values -s , "type" all posixuser smbuser posixgroup smbgroup' \
+      '*:filesystem/volume/snapshot:_zfs_dataset'
+  ;;
+
+  zfs:project)
+    _arguments -A "-*" -S \
+      '(-r -C -k -p -s)-d[act on the directory project ID and inherit flag, not its children]' \
+      '(-d)-r[act on subdirectories recursively]' \
+      '(-0 -c -d -s)-C[clear project inherit flag and/or ID on the file(s) or directories]' \
+      '(-0 -c -d -p -s)-k[keep the project ID unchanged]' \
+      '(-k -C -s)-c[check project ID and inherit flag on the file(s) or directories]' \
+      '(-k -C -s)-0[print file name with a trailing NUL instead of newline]' \
+      '(-k)-p+[specify project ID]:project ID' \
+      '(-0 -c -k -C)-s[set project inherit flag on the given file(s) or directories]' \
+      '*:file:_files'
+  ;;
+
+  zfs:mount)
+    [[ $OSTYPE != freebsd* ]] && args=( '-O[overlay mount]' )
+    [[ $implementation = openzfs ]] && args+=(
+    '-l[load keys for encrypted filesystems as they are being mounted]'
+    )
+    _arguments -A "-*" -S $args \
+      '-o+[specify temporary file system options]: :_values -s , "option" {,no}{atime,dev,exec,relatime,suid,xattr} ro rw' \
+      '-v[report mount progress]' \
+      '-f[force mount]' \
+      '(:)-a[mount all available ZFS filesystems]' \
+      '(-a):filesystem:_zfs_dataset -t fs'
+  ;;
+
+  zfs:u(|n)mount)
+    [[ $implementation = openzfs ]] && args+=(
+      '-u[unload keys for any unmounted encryption roots]'
+    )
+    _arguments -A "-*" -S $args \
+      '-f[force unmount]' \
+      '(:)-a[unmount all ZFS filesystems]' \
+      '(-a):dataset or mountpoint:_zfs_dataset -t fs -t mtpt'
+  ;;
+
+  zfs:share)
+    [[ $implementation = solaris ]] && args=(
+      - set2 \
+      '-r[share filesystems recursively]' \
+      ':dataset:_zfs_dataset -t fs' \
+      - set3 \
+      '*-o+[create a share with specified properties]: :_values -w "share properties" $share_rw_properties' \
+      '-u[create a share without sharing it]' \
+      ':dataset:_zfs_dataset -t fs' \
+    )
+    _arguments -A "-*" -S \
+      - set1 \
+      '-a[share all available ZFS filesystems]' \
+      $args \
+      - set4 \
+      ':dataset or mountpoint:_zfs_dataset -t fs -t mtpt -t share'
+  ;;
+
+  zfs:unshare)
+    [[ $implementation = solaris ]] && args=(
+      - set2
+      '-r[unshare filesystems recursively]'
+      ':filesystem:_zfs_dataset -t fs'
+    )
+    _arguments -A "-*" -S $args \
+      - set1 \
+      '-a[unshare all shared ZFS filesystems]' \
+      - set3 \
+      ':filesystem:_zfs_dataset -t fs -t mtpt -t share'
+  ;;
+
+  zfs:send)
+    if [[ $implementation = openzfs ]]; then
+      args=(
+        '(-L --large-block)'{-L,--large-block}'[generate a stream which may contain blocks larger than 128KB]'
+        '(-P --parsable)'{-P,--parsable}'[print machine-parsable verbose information about the stream generated]'
+        '(-e --embed)'{-e,--embed}'[more compact stream for blocks stored with the embedded_data feature]'
+        '(-c --compressed)'{-c,--compressed}'[more compact stream for compressed blocks]'
+        '(-h --holds)'{-h,--holds}'[send snapshot holds]'
+        '-V[set the process title to a per-second report of how much data has been send]'
+        '-t[create a send stream that resumes an interrupted receive]:resume token'
+        '(-w --raw)'{-w,--raw}'[keep encrypted data exactly as it exists on disk]'
+        - redact
+        '(-h -V -t -w --raw)--redact[generate a redacted send stream]'
+        - saved
+        '(-S --saved)'{-S,--saved}'[generate stream from partially received dataset]'
+      )
+    else
+      args=(
+        '-w+[send compressed filesystem blocks as compressed in the stream]:compression:(compress none)'
+        '-m+[limit amount of memory used by deduplication processing]: :_numbers -u bytes "memory size" K M G'
+        '-s+[set stream options]:token:(streamsize check nocheck memsize)'
+        '-C[read a receive checkpoint from stdin]'
+        '-c[create a self-contained stream]'
+        '(-R)-r[generate a recursive stream package]'
+      )
+    fi
+    _arguments -A "-*" -S \
+      '-b[send only received property values]' \
+      '(-I)-i[generate an incremental stream]:snapshot:_zfs_dataset -t snap' \
+      '-D[perform dedup processing]' \
+      "-n[don't send the stream]" \
+      '-p[send properties]' \
+      '-v[verbose]' \
+      '(-i)-I[generate an incremental stream with intermediary snapshots]:snapshot:_zfs_dataset -t snap' \
+      '(-r)-R[generate a replication stream package]' \
+      ':snapshot:_zfs_dataset -t snap -t bookmark' \
+      $args
+  ;;
+
+  zfs:redact)
+    _arguments \
+      ':snapshot:_zfs_dataset -t snap' \
+      ':bookmark:_zfs_dataset -t bookmark' \
+      ':redaction snapshot:_zfs_dataset -t snap'
+  ;;
+
+  zfs:(receive|recv))
+    if [[ $implementation = openzfs ]]; then
+      args=(
+        '-h[skip the receive of holds]'
+        '-s[if the receive is interrupted, save the partially received state]'
+        '(- set2)-A[abort an interrupted zfs recv -s, deleting its saved partially received state]'
+      )
+      [[ $OSTYPE != linux* ]] && args+=(
+        '-M[force an unmount of the file system while receiving a snapshot]'
+      )
+    else
+      args=( '(-)-C[write a receive checkpoint to stdout]' )
+    fi
+    _arguments -A "-*" -S $args \
+      '-v[verbose]' \
+      "-n[don't receive the stream]" \
+      '-F[force a rollback if necessary]' \
+      '-u[filesystem is not mounted]' \
+      '-o[include property change in the stream]:property' \
+      '-x[exclude property change from the stream]:property' \
+      - set1 \
+      ':filesystem/volume/snapshot:_zfs_dataset' \
+      - set2 \
+      '(-e)-d[set path prefix from stream, excluding only pool name]' \
+      '(-d)-e[set path prefix from stream, using last path element]' \
+      ':filesystem:_zfs_dataset -t fs'
+  ;;
+
+  zfs:allow)
+    _arguments -C -A "-*" -S \
+      '(-g -e -c -s)-u[delegate to user]' \
+      '(-u -e -c -s)-g[delegate to group]' \
+      '(1 -g -u -c -s)-e[delegate to everyone]' \
+      '(1 -u -g -e -l -d -s)-c[set permissions for newly-created descendant filesystems]' \
+      '(-u -g -e -l -d -c)-s[define or modify permission sets]:permission set' \
+      '(-c -s)-l[allow for named dataset]' \
+      '(-c -s)-d[allow for descendent datasets]' \
+      '1: :->first' \
+      ':permission list:_values -s , "permission or set" $delegatable_perms' \
+      ':filesystem/volume:_zfs_dataset -t fs -t vol'
+
+    if [[ -n $state ]]; then
+      case $opt_args[(I)-[ugs]] in
+        ^-[ug]) alts+=( 'permission-sets: :_guard "(|@*)" "permission set"' ) ;|
+        ^-[gs]) alts+=( 'users:user:_users' ) ;|
+        ^-[us]) alts+=( 'groups:group:_groups' ) ;|
+        '')
+          alts+=(
+            'all:everyone:(everyone)'
+            'filesystems:filesystem/volume:_zfs_dataset -t fs -t vol'
+          )
+        ;;
+      esac
+      _alternative $alts
+    fi
+  ;;
+
+  zfs:unallow)
+    _arguments -A "-*" -S \
+      '-r[recursive removal]' \
+      '(-e -g -s -c)-u[user]' \
+      '(-e -u -s -c)-g[group]' \
+      '(1 -g -u -s -c)-e[everyone]' \
+      '(1 -u -g -e -s -l -d)-c[create-time permissions]' \
+      '(-e -u -g -c)-s[remove permissions from or delete a permission set]:permission set' \
+      '(-c -s)-l[allow for named dataset]' \
+      '(-c -s)-d[allow for descendent datasets]' \
+      '1: :->first' \
+      '::permissions or sets:_values -s , "permission or set" $delegatable_perms' \
+      ':filesystem/volume:_zfs_dataset -t fs -t vol'
+
+    if [[ -n $state ]]; then
+      case $opt_args[(I)-[ugs]] in
+        ^-[ug]) alts+=( 'permission-sets: :_guard "(|@*)" "permission set"' ) ;|
+        ^-[gs]) alts+=( 'users:user:_users' ) ;|
+        ^-[us]) alts+=( 'groups:group:_groups' ) ;|
+        '') alts+=( 'all:everyone:(everyone)' ) ;;
+      esac
+      _alternative $alts
+    fi
+  ;;
+
+  zfs:hold)
+    _arguments -A "-*" -S \
+      '-r[apply hold recursively]' \
+      ':tag' \
+      ':snapshot:_zfs_dataset -t snap'
+  ;;
+
+  zfs:holds)
+    [[ $implementation = openzfs ]] && args=(
+      '-H[suppress printing of headers, tab-delimit columns]'
+    )
+    [[ $OSTYPE = freebsd<-12>.* ]] && args+=(
+      # features were lost with the openzfs rebase
+      '-p[use exact (parsable) numeric output]'
+      '(-r)-d+[depth]:value'
+    )
+    _arguments -A "-*" -S $args \
+      '(-d)-r[list holds recursively]' \
+      ':snapshot:_zfs_dataset -t snap'
+  ;;
+
+  zfs:release)
+    _arguments -A "-*" -S \
+      '-r[release holds recursively]' \
+      ':tag' \
+      ':snapshot:_zfs_dataset -t snap'
+  ;;
+
+  zfs:diff)
+    [[ $implementation = solaris ]] && args=(
+      '(-E)-e[only show new and changed files, no deleted]'
+      '*-o+[show specified fields]:field:_values "field" $difffields'
+      '-q[silence warnings for missing snapshots on recursive datasets]'
+      '-N[enumerate new child datasets (with -r)]'
+      '(1 -e)-E[show difference from empty]'
+    )
+    _arguments -A "-*" -S $args \
+      '-F[add column for filetype character, similar to ls(1)]' \
+      '-H[suppress printing of headers and arrows, tab-delimit columns]' \
+      '-t[add column for ctime]' \
+      '(-E)1:snapshot:_zfs_dataset -t snap' \
+      '2:snapshot or filesystem:_zfs_dataset -t snap -t fs'
+  ;;
+
+  zfs:wait)
+    _arguments -A "-*" -S \
+      '-t[specify background activity]:activity:(deleteq)' \
+      ':filesystem:_zfs_dataset'
+  ;;
+
+  zfs:key)
+    _arguments -C -A "-*" -S \
+      '-t+[only apply to given dataset type]: :_values -s , "dataset type" $ds_types' \
+      '(-u -c -K -f -o)-l[load the encryption key]' \
+      "(-u -c -K -f -o)-M[don't mount file systems after loading their keys]" \
+      "(-u -c -K -f -o)-S[don't share file systems after loading their keys]" \
+      '(-l -c -K -o -M -S)-u[unload the encryption key]' \
+      '(-l -c -K -o -M -S)-f[force unmount the dataset before unloading the encryption key]' \
+      '(-l -u -K -f -M -S)-c[change the encryption key]' \
+      '(-l -u -K -f -M -S)-o+[change a property]:property:->keysources' \
+      '(-l -c -u -f -o -M -S)-K[create a new data encryption key]' \
+      '(1 -r)-a[apply to all datasets in all pools]' \
+      '(-a)-r[apply recursively]' \
+      ':filesystem or volume:_zfs_dataset -t fs -t vol'
+  ;;
+
+  zfs:load-key)
+    _arguments -A "-*" -S \
+      "-L+[specify location of user's encryption key]:key location [prompt]:_files -P file\:// -W /" \
+      '(:)-a[load keys for all encryption roots in all imported pools]' \
+      '-n[do a dry-run, simply check that the provided key is correct]' \
+      '-r[load keys for datasets recursively]' \
+      '(-a):filesystem or volume:_zfs_dataset -t fs -t vol'
+  ;;
+
+  zfs:unload-key)
+    _arguments -A "-*" -S \
+      '(:)-a[unload keys for all encryption roots in all imported pools]' \
+      '-r[unload keys for datasets recursively]' \
+      '(-a):filesystem or volume:_zfs_dataset -t fs -t vol'
+  ;;
+
+  zfs:change-key)
+    _arguments -A "-*" -S \
+      '(-o)-i[make filesystem inherit key from its parent]' \
+      '-l[ensure key is loaded before attempting to change it]' \
+      '(-i)*-o+[change encryption key property]: :_values -s , "property" $key_properties' \
+      ':filesystem or volume:_zfs_dataset -t fs -t vol'
+  ;;
+
+  zfs:jail|zfs:unjail)
+    _arguments \
+      '1: : _jails' \
+      '2:filesystem:_zfs_dataset -t fs'
+  ;;
+
+  zfs:help)
+    _arguments -A "-*" -S \
+      - set1 \
+      ':command:($subcmds $delegatable_perms $ro_ds_props ${rw_ds_props%%:*} properties)' \
+      - set2 \
+      '(2)-l[display property information]' \
+      ':help topic:(property)' \
+      ':property:($delegatable_perms $ro_ds_props ${rw_ds_props%%:*})'
+  ;;
+
+  zpool:help)
+    _arguments -A "-*" -S \
+      - commands \
+      ':command:($subcmds)' \
+      - properties \
+      '(2)-l[display property information]' \
+      ':help topic:(property)' \
+      ':property:(${po_propnames%%\[*})'
+  ;;
+
+  zpool:add)
+    if [[ $implementation = openzfs ]]; then
+      args=(
+        '-g[display vdev, GUIDs instead of the normal device names]'
+        '-L[display real paths for vdevs resolving all symbolic links]'
+        '-o+[set given pool properties]: :_values -s , "property" "${(@M)ci_po_props\:#ashift*}"' \
+        '-P[display real paths for vdevs instead of only the last component of the path]'
+      )
+    elif [[ $implementation = solaris ]]; then
+      args=( '-l[display configuration in /dev/chassis location form]' )
+    fi
+    _arguments -A "-*" -S $args \
+      '-f[force use of in-use devices]' \
+      '-n[display configuration without modifying pool]' \
+      ':pool:_zfs_pool' \
+      '*:virtual device:->virtual-devices'
+  ;;
+
+  zpool:attach)
+    if [[ $implementation = openzfs ]]; then
+      args=(
+        '-w[wait until new device has finished resilvering before returning]'
+        '-s[reconstruct sequentially to restore redundancy as quickly as possible]'
+        '-o+[set given pool properties]: :_values -s , "property" "${(@M)ci_po_props\:#ashift*}"'
+      )
+    fi
+    _arguments -A "-*" -S $args \
+      '-f[force attach, even if in use]' \
+      ':pool:_zfs_pool' \
+      ':virtual device:->pool-devices' \
+      ':virtual device:->disk-devices'
+  ;;
+
+  zpool:checkpoint)
+    _arguments -A "-*" -S \
+      '(-d --discard)'{-d,--discard}'[discard an existing checkpoint from the pool]' \
+      '(-w --wait)'{-w,--wait}'[wait until the checkpoint has finished being discarded before returning]' \
+      ':pool:_zfs_pool'
+  ;;
+
+  zpool:clear)
+    [[ $implementation = solaris ]] && args=(
+      '-f[ignore fmadm acquit and fmadm repair failures]'
+    )
+    _arguments -C -A "-*" -S $args \
+      '-F[discard transactions to allow pool opening]' \
+      '-n[with -F, check if discarding transactions would work]' \
+      '-X[(undocumented) extreme rewind of transactions]' \
+      ':pool:_zfs_pool' \
+      '*:virtual device:->pool-devices'
+  ;;
+
+  zpool:create)
+    if [[ $implementation = openzfs ]]; then
+      args=(
+        "-d[don't enable any features on the new pool]"
+      )
+    else
+      args=(
+        '-B[create EFI boot partition on whole disks]'
+        '-l[display configuration in /dev/chassis location form]'
+        "-N[create pool but don't mount or share]"
+      )
+    fi
+    _arguments -C -A "-*" -S $args \
+      '-o+[set pool property at creation time]:property:->newpool-properties' \
+      '-O+[set dataset property at creation time]:property:->create-properties' \
+      '-f[force use of in-use devices]' \
+      '-n[display configuration without creating pool]' \
+      '-R+[use alternate root]:alternate root:_directories' \
+      '-m+[set mountpoint for root dataset]:mountpoint' \
+      '-t+[use a temporary pool name]:pool name' \
+      ':pool :_guard "^-*" "pool name"' \
+      '*: :->virtual-devices'
+  ;;
+
+  zpool:destroy)
+    _arguments -A "-*" -S \
+      '-f[force active datasets to be unmounted]' \
+      ':pool:_zfs_pool'
+  ;;
+
+  zpool:detach)
+    _arguments -C \
+      ':pool:_zfs_pool' \
+      ':virtual device:->pool-devices'
+  ;;
+
+  zpool:events)
+    _arguments -A "-*" -S \
+      '(- 1)-c[clear all previous events]' \
+      '-f[follow mode - continue running, showing new events]' \
+      '-H[suppress headers and tab-delimit fields]' \
+      '-v[print the entire payload for each event]' \
+      '(-c)1:pool:_zfs_pool'
+  ;;
+
+  zpool:export)
+    [[ $implementation = openzfs ]] && args=( '(*)-a[export all pools]' )
+    _arguments -A "-*" -S $args \
+      '-f[forcefully unmount all datasets]' \
+      '*:pool:_zfs_pool'
+  ;;
+
+  zpool:get)
+    [[ $implementation = solaris ]] && args=(
+      '-s+[specify sources to display]: :_values -s "source" local default none'
+    )
+    _arguments -A "-*" -S $args \
+      '-H[suppress headers and tab-delimit fields]' \
+      '-p[display numbers in parseable (exact) values]' \
+      '-o+[specify fields to display]: : _values -s , field name property value source' \
+      ':property:_values -s , "property" $po_propnames' \
+      '*:pool:_zfs_pool'
+  ;;
+
+  zpool:history)
+    _arguments -A "-*" -S \
+      '-i[display internal events]' \
+      '-l[long format]' \
+      '*:pool:_zfs_pool'
+  ;;
+
+  zpool:import)
+    # TODO: -o should complete mount options, too
+    if [[ $implementation = openzfs ]]; then
+      args=(
+        '-t[new pool name is temporary]'
+        '-l[request encryption keys for all encrypted datasets]'
+        '--rewind-to-checkpoint[rewind pool to the checkpointed state]'
+        '-s[scan using the default search path]'
+        '(-F -X)-T[specify the txg to use for rollback]'
+      )
+    else
+      args=(
+        '(-a)-t+[use a temporary pool name]:pool name'
+        '-l[display configuration in /dev/chassis location form]'
+      )
+    fi
+    _arguments -C -A "-*" -S $args \
+      '(1 2 -t)-a[search for and import all pools found]' \
+      '-D[destroyed pools only]' \
+      '(-d)*-c+[use cache file]:cache file:_files' \
+      '(-c -D)*-d+[search for devices or files in directory]:directory:_files -/' \
+      '-F[recovery mode: discard transactions if required]' \
+      '-X[(undocumented) extreme rewind of transactions]' \
+      '!-V' \
+      '-f[force import]' \
+      '-m[ignore missing log devices]' \
+      '-N[import pool without mounting any filesystems]' \
+      "-n[with -F; don't perform input]" \
+      '-R+[specify alternate root]:alternate root:_files -/' \
+      '-o+[set pool or dataset property]:property:->import-properties' \
+      '1:pool name or id:_zfs_pool' \
+      '2::new pool name'
+  ;;
+
+  zpool:initialize)
+    _arguments -A "-*" -S \
+      '(-s --suspend -c --cancel)'{-c,--cancel}'[cancel initializing on specified devices]' \
+      '(-s --suspend -c --cancel)'{-s,--suspend}'[suspend initializing on specified devices]' \
+      '(-w --wait)'{-w,--wait}'[wait until devices have finished initializing before returning]' \
+      ':pool:_zfs_pool' \
+      '*:device:pool-devices'
+  ;;
+
+  zpool:iostat)
+    if [[ $implementation = openzfs ]]; then
+      args=(
+        '-c[run scripts on each vdev]:script:_files -W "($ZPOOL_SCRIPTS_PATH /etc/zfs/zpool.d ~/.zpool.d)"'
+        '-g[display vdev GUIDs instead of normal device names]'
+        '-H[suppress headers and tab-delimit fields]'
+        '-L[display real paths for vdevs resolving all symbolic links]'
+        '-n[print headers only once]'
+        '-p[display numbers in parsable (exact) values and times in nanoseconds]'
+        '-P[display full paths for vdevs instead of only the last component of the path]'
+        "-r[print request size histograms for the leaf vdev's IO]"
+        '-y[omit statistics since boot]'
+        '-w[display latency histograms]'
+        '-l[include average latency statistics]'
+        '-q[include active queue statistics]'
+      )
+    else
+      args=( '-l[display configuration in /dev/chassis location form]' )
+    fi
+    _arguments -A "-*" -S $args \
+      '-T+[display a timestamp]:format:((d\:standard u\:internal))' \
+      '-v[verbose statistics]' \
+      '*::pool:_zfs_pool' \
+      '::interval' \
+      '::count'
+  ;;
+
+  zpool:label)
+    _arguments -C -A "-*" -S \
+      '(-c)*-d+[specify path in which to search for devices or files]:path:_directories' \
+      '(-d)-c+[read configuration from specified cache file]:cache file:_files' \
+      '(-R)-C[clear ZFS metadata on an inactive pool or device]' \
+      '(-C)-R[recover ZFS metadata for a pool]' \
+      '1::pool:_zfs_pool' \
+      '2:device:->pool-devices'
+  ;;
+
+  zpool:labelclear)
+    _arguments -A "-*" -S \
+      '-f[treat exported or foreign devices as inactive]' \
+      '*:virtual device:_files'
+  ;;
+
+  zpool:list)
+    [[ $implementation = openzfs ]] && args=(
+      '-g[display vdev GUIDs instead of normal device names]'
+      '-L[display real paths for vdevs resolving all symbolic links]'
+      '-p[display numbers in parsable (exact) values]'
+      '-P[display full paths for vdevs instead of only the last component of the path]'
+      '-v[report usage statistics for individual vdevs within the pool]'
+    )
+    _arguments -A "-*" -S $args \
+      '-H[suppress headers and tab-delimit fields]' \
+      '-T+[display a timestamp]:format:((d\:standard u\:internal))' \
+      '-o+[specify fields to list]: :_values -s , "field" $po_propnames' \
+      '::pool:_zfs_pool'
+  ;;
+
+  zpool:monitor)
+    _arguments -A "-*" -S \
+      '-t+[specify provider]:provider:(send receive scrub resilver ddtmigrate destroy)' \
+      '-o+[specify fields]: :_values -s , field done other pctdone pool provider speed starttime tag timeleft timestmp total' \
+      '-T+[display a timestamp]:format:((d\:standard u\:internal))' \
+      '-p[use machine-parseable output format]' \
+      '1:pool:_zfs_pool' \
+      '2:interval' \
+      '3:count'
+  ;;
+
+  zpool:offline)
+    [[ $implementation = openzfs ]] && args=(
+      '-f[force disk into faulted state]'
+    )
+    _arguments -C -A "-*" -S $args \
+      '-t[offline until next reboot]' \
+      ':pool:_zfs_pool' \
+      '*:virtual device:->pool-devices'
+  ;;
+
+  zpool:online)
+    _arguments -C -A "-*" -S \
+      '-e[expand device to use all available space]' \
+      ':pool:_zfs_pool' \
+      '*:virtual device:->pool-devices'
+  ;;
+
+  zpool:reopen)
+    _arguments -A "-*" -S \
+      "-n[don't restart an in-progress scrub operation]" \
+      '1:pool:_zfs_pool'
+  ;;
+
+  zpool:reguid)
+    _zfs_pool
+  ;;
+
+  zpool:remove)
+    [[ $implementation = openzfs ]] && args=(
+      '(-s)-w[wait until removal has completed before returning]'
+    )
+    _arguments -C -A "-*" -S $args \
+      "(-s)-n[don't perform the removal, display mapping table memory use]" \
+      '(-s)-p[with -n, display numbers in parseable (exact) values]' \
+      '(- *)-s[stop and cancel an in-progress removal]' \
+      '1:pool:_zfs_pool' \
+      '*:device:->pool-devices'
+  ;;
+
+  zpool:replace)
+    [[ $implementation = openzfs ]] && args=(
+      '-w[wait until replacement has completed before returning]'
+      '-s[reconstruct sequentially to restore redundancy as quickly as possible]'
+      '-o+[set given pool properties]: :_values -s , "property" "${(@M)ci_po_props\:#ashift*}"'
+    )
+    _arguments -A "-*" -S $args \
+      '-f[force attach, even if in use]' \
+      ':pool:_zfs_pool' \
+      ':virtual device:_files' \
+      '::virtual device:_files'
+  ;;
+
+  zpool:(resilver|sync))
+    _arguments \
+      '*:pool:_zfs_pool'
+  ;;
+
+  zpool:scrub)
+    [[ $implementation = openzfs ]] && args=(
+      '(-s)-p[pause scrubbing]'
+      '-w[wait until scrub has completed before returning]'
+    )
+    _arguments -A "-*" -S $args \
+      '(-p)-s[stop scrubbing]' \
+      '*:pool:_zfs_pool'
+  ;;
+
+  zpool:set)
+    _arguments -C -A "-*" -S \
+      ':property:->set-pool-properties' \
+      '*:pool:_zfs_pool'
+  ;;
+
+  zpool:split)
+    if [[ $implementation = solaris ]]; then
+      args=( '-l[display configuration in /dev/chassis location form]' )
+    else
+      args=(
+        '-g[display vdev GUIDs instead of normal device names]'
+        '-L[display real paths for vdevs resolving all symbolic links]'
+        '-l[request encryption keys for encrypted datasets]'
+        '-P[display full paths for vdevs instead of only the last component of the path]'
+      )
+    fi
+    _arguments -C -A "-*" -S $args \
+      '-R+[specify alternate root]:alternate root:_files -/' \
+      '-n[display configuration without splitting]' \
+      '-o+[set pool or dataset property]:property:->import-properties' \
+      ':pool name or id:_zfs_pool' \
+      ':new pool name' \
+      '*:virtual device:->pool-devices'
+  ;;
+
+  zpool:status)
+    if [[ $implementation = openzfs ]]; then
+      args=(
+        '-D[display a histogram of deduplication statistics]'
+        '-c[run scripts on each vdev]:script:_files -W "($ZPOOL_SCRIPTS_PATH /etc/zfs/zpool.d ~/.zpool.d)"'
+        '-i[display vdev initialization status]'
+        '-g[display vdev GUIDs instead of the normal device names]'
+        '-L[display real paths for vdevs resolving all symbolic links]'
+        '-p[display numbers in parsable (exact) values and times in nanoseconds]'
+        '-P[display full paths for vdevs instead of only the last component of the path]'
+        '-s[display the number of leaf VDEV slow IOs]'
+        '-t[display vdev TRIM status]'
+      )
+    else
+      args=( '-l[display configuration in /dev/chassis location form]' )
+    fi
+    _arguments -A "-*" -S $args\
+      '-v[verbose information]' \
+      '-x[show only unhealthy pools]' \
+      '-T+[display a timestamp]:format:((d\:standard u\:internal))' \
+      '*::pool:_zfs_pool' \
+      ':: :_guard "[0-9]#" interval' \
+      ':: :_guard "[0-9]#" count'
+  ;;
+
+  zpool:trim)
+    _arguments -C -A "-*" -S \
+      '(-d --secure)'{-d,--secure}'[initiate a secure TRIM]' \
+      '(-r --rate)'{-r,--rate}'[set rate at which the TRIM operation progresses]:rate (bytes per second)' \
+      '(-c --cancel)'{-c,--cancel}'[cancel trimming]' \
+      '(-s --suspend)'{-s,--suspend}'[suspend trimming]' \
+      '(-w --wait)'{-w,--wait}'[wait until devices are done being trimmed]' \
+      '1:pool:_zfs_pool' \
+      '*:device:->pool-devices'
+  ;;
+
+  zpool:upgrade)
+    _arguments -A "-*" -S \
+      '(- *)-v[display ZFS versions and descriptions]'
+      "(-v)-V+[upgrade to given version]:version" \
+      '(-v *)-a[upgrade all pools]' \
+      '(-a -v)*:pool:_zfs_pool'
+  ;;
+
+  zpool:wait)
+    _arguments -A "-*" -S \
+      '-H[suppress printing of headers, tab-delimit columns]' \
+      '-P[use exact (parsable) numeric output]' \
+      '-t+[specify background activity]: : _values -s , activity discard free initialize replace remove resilver scrub trim' \
+      '-T+[display a timestamp]:format:((d\:standard u\:internal))' \
+      ':pool:_zfs_pool' \
+      ':interval'
+  ;;
+
+  *)
+    _default
+  ;;
+esac
+
+while (( $#state )); do
+  curstate=$state
+  state=()
+  case $curstate in
+    virtual-devices)
+      local -a vdevtypes
+      vdevtypes=( mirror raidz{,1,2,3} spare log cache )
+      if [[ $implementation = openzfs ]]; then
+        vdevtypes+=( draid{,1,2,3} dedup special )
+      else
+        vdevtypes+=( meta )
+      fi
+      # cache can't be a mirror
+      [[ $words[CURRENT-1] != cache ]] && alts=(
+        'vdev-types:vdev type:compadd -a vdevtypes'
+      )
+      [[ -prefix / ]] || alts+=(
+        'disk-vdevs:disk vdev:_files -g "*(-%)" -W /dev'
+      )
+      _alternative $alts 'file-vdevs:file vdev:_files -W / -P /'
+    ;;
+
+    pool-devices)
+      local -a devices
+      devices=( ${${${(M)${(f)"$(_call_program devices zpool status $line[1])"}:#$'\t' *}##[[:blank:]]#}%%[[:blank:]]*} )
+      if (( $#devices )); then
+        _description devices expl "$state_descr"
+        compadd "$expl[@]" -a devices
+        break
+      fi
+    ;& # fall-through if we found none
+
+    disk-devices)
+      [[ -prefix / ]] || alts=(
+        'disk-vdevs:disk vdev:_files -g "*(-%)" -W /dev'
+      )
+      _alternative $alts 'file-vdevs:file vdev:_files -W / -P /'
+    ;;
+
+    keysources)
+      local -a suf
+
+      compset -S ",*" || suf=(-S ,)
+      if compset -P 1 "*,"; then
+        _alternative \
+          'zfs-keylocator-prompt:"prompt" locator:(prompt)' \
+          'zfs-keylocator-file:file locator:_files' \
+          'zfs-keylocator-pkcs11: : _message -e zfs-keylocator-pkcs11 "PKCS#11 locator"' \
+          'zfs-keylocator-https: : _message -e zfs-keylocator-https "HTTPS URL locator"'
+      else
+        _description keysource-formats expl "keysource format"
+        compadd $suf -q "$expl[@]" "$@" raw hex passphrase
+      fi
+    ;;
+
+    quotas)
+      _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)'
+    ;;
+
+    import-properties) args=( $ci_ds_props $rw_ds_props $ci_po_props ) ;|
+    create-properties) args=( $ci_ds_props ) ;|
+    set-properties) args=( $rw_ds_props ) ;|
+    newpool-properties) args=( $rw_po_props $ci_po_props ) ;|
+    set-pool-properties) args=( $rw_po_props ) ;|
+
+    *-properties)
+      if compset -P 1 '(#m)*@'; then
+        if compset -P 1 '*='; then
+          case $MATCH in
+            *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)'
+            ;;
+          esac
+        else
+          case $MATCH in
+            user*@) _users -S = ;;
+            group*@) _groups -S = ;;
+            project*@) _message -e projects project ;;
+          esac
+        fi
+      else
+        _wanted values expl "$state_descr" compadd -S@ ${${(M)args:#*@}%@}
+        _values -C "$state_descr" ${args:#*@}
+      fi
+    ;;
+  esac
+done
+
+[[ nm -ne "$compstate[nmatches]" ]]
diff --git a/Completion/Unix/Command/_zpool b/Completion/Unix/Command/_zpool
deleted file mode 100644
index d9c2caa52..000000000
--- a/Completion/Unix/Command/_zpool
+++ /dev/null
@@ -1,311 +0,0 @@
-#compdef zpool
-# Synced with the S11U1 man page
-
-_zpool() {
-	local context state line expl implementation
-	local -a subcmds fields ro_props rw_props versions create_properties_dataset
-
-	_pick_variant -r implementation -c 'zpool upgrade -v' openzfs='This system supports ZFS pool feature flags' solaris
-
-	subcmds=(
-		create destroy add remove list iostat status online
-		offline clear attach detach replace scrub import export
-		upgrade history get set split help
-	)
-
-	if [[ $implementation = openzfs ]] && [[ $OSTYPE != solaris* ]]; then
-		subcmds+=( labelclear initialize )
-	fi
-
-	versions=(
-		${${${(M)"${(f)$(_call_program versions zpool upgrade -v)}":#[[:space:]]#<->*}##[[:space:]]}%%[[:space:]]*}
-	)
-
-	ro_props=(
-		"all[All properties]"
-		"allocated[Space allocated]"
-		"capacity[Space used (percentage)]"
-		"dedupratio[Deduplication ratio]"
-		"free[Space unallocated]"
-		"guid[Unique identifier]"
-		"health[Health status]"
-		"size[Total size]"
-	)
-
-	rw_props=(
-		"altroot[Alternate root directory]:value:"
-		"autoexpand[Automatic pool expansion]:value:(on off)"
-		"autoreplace[Automatic device replacement]:value:(on off)"
-		"bootfs[Default bootable dataset]:value:"
-		"cachefile[Pool configuration cache file location]:value:"
-		"dedupditto[Threshold for number of copies]:value:"
-		"delegation[Delegated administration]:value:(on off)"
-		"failmode[Failure-mode behavior]:value:(wait continue panic)"
-		"listshares[Show shares in 'zfs list']:value:(on off)"
-		"listsnaps[Show snapshots in 'zfs list']:value:(on off)"
-		"readonly[Controls whether the pool can be modified]:value:(on off)"
-		"version[Pool version]:version:($versions)"
-	)
-
-	fields=( ${ro_props%%:*} ${rw_props%%:*} )
-
-	create_properties_dataset=(
-		"aclinherit:value:(discard noallow restricted passthrough passthrough-x)"
-		"aclmode:value:(discard mask passthrough)"
-		"atime:value:(on off)"
-		"canmount:value:(on off noauto)"
-		"checksum:value:(on off fletcher2 fletcher4 sha256 sha256+mac)"
-		"compression:value:(on off lzjb gzip gzip-{1..9} zle)"
-		"copies:value:(1 2 3)"
-		"dedup:value:(on off verify sha256 sha256,verify)"
-		"devices:value:(on off)"
-		"encryption:value:(off on aes128-ccm aes-192-ccm aes-256-ccm aes-128-gcm aes-192-gcm aes-256-gcm)"
-		"exec:value:(on off)"
-		"groupquota@:value:" # TODO: complete group=size|none
-		"keysource:value:_zfs_keysource_props"
-		"logbias:value:(latency throughput)"
-		"mlslabel:value:(none)" # TODO: list sensitivity labels
-		"mountpoint:path, 'legacy', or 'none':{if [[ -prefix /* ]]; then _path_files -/; else _wanted mountpoints expl 'mountpoint (type \"/\" to start completing paths)' compadd legacy none; fi}"
-		"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}"
-		"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}"
-		"rstchown:value:(on off)"
-		"secondarycache:value:(all none metadata)"
-		"setuid:value:(on off)"
-		"shadow:value:" # TODO: complete URI|none
-		"share:share properties:"
-		"sharenfs:value:(on off)"
-		"sharesmb:value:(on off)"
-		"snapdir:value:(hidden visible)"
-		"sync:value:(standard always disabled)"
-		"userquota@:value:" # TODO: complete user=size|none
-		"version:value:(1 2 3 4 current)"
-		"volsize:value:" # <size>
-		"vscan:value:(on off)"
-		"xattr:value:(on off)"
-		"zoned:value:(on off)"
-	)
-
-	if [[ $service == "zpool" ]]; then
-		_arguments -C \
-			'-\?[show help information]' \
-			'1:subcommand:compadd -a subcmds' \
-			'*:: :->subcmd' && return
-
-		service="$words[1]"
-		curcontext="${curcontext%:*}-$service:"
-	fi
-
-	case $service in
-	(help)
-		_arguments -A "-*" \
-			- set1 \
-			':command/property:($subcmds ${fields%%\[*} properties)' \
-			- set2 \
-			'-l[Display property information]' \
-			': :(properties)'
-		;;
-
-	(clear)
-		_arguments -A "-*" \
-			'-F[Discard transactions to allow pool opening]' \
-			'-f[Ignore fmadm acquit and fmadm repair failures]' \
-			'-n[With -F, check if discarding transactions would work]' \
-			':pool name:_zfs_pool' \
-			'*:virtual device:_files'
-		;;
-
-	(create)
-		# TODO: investigate better vdev handling
-		_arguments -A "-*" \
-			'-B[Create EFI boot partition on whole disks]' \
-			'-o[Set pool property at creation time]:property:_values -s , "property" $rw_props' \
-			'-O[Set dataset property at creation time]:property:_values -s , "property" $create_properties_dataset' \
-			'-f[Force use of in-use devices]' \
-			'-l[Display configuration in /dev/chassis location form]' \
-			'-n[Display configuration without creating pool]' \
-			'-R[Use alternate root]:alternate root:_files -/' \
-			'-m[Set mountpoint for root dataset]:mountpoint:' \
-			':pool name:' \
-			'*:virtual device:_files'
-		;;
-
-	(destroy)
-		_arguments -A "-*" \
-			'-f[Force active datasets to be unmounted]' \
-			':pool name:_zfs_pool'
-		;;
-
-	(add)
-		_arguments -A "-*" \
-			'-f[Force use of in-use devices]' \
-			'-l[Display configuration in /dev/chassis location form]' \
-			'-n[Display configuration without modifying pool]' \
-			':pool name:_zfs_pool' \
-			'*:virtual device:_files'
-		;;
-
-	(list)
-		_arguments \
-			'-H[Scripted mode]' \
-			'-T[timestamp]:value:(u d)' \
-			'-o[Fields to list]:field:_values -s , "field" $fields' \
-			'::pool name:_zfs_pool'
-		;;
-
-	(initialize)
-		_arguments -A "-*" \
-			'(-c --cancel)'{-c,--cancel}'[cancel initializing on specified devices]' \
-			'(-s --suspend)'{-s,--suspend}'[suspend initializing on specified devices]' \
-			':pool name:_zfs_pool' \
-			'*:device:_files'
-		;;
-
-	(iostat)
-		_arguments -A "-*" \
-			'-l[Display configuration in /dev/chassis location form]' \
-			'-T[timestamp]:value:(u d)' \
-			'-v[Verbose statistics]' \
-			'*::pool name:_zfs_pool' \
-			'::interval:' \
-			'::count:'
-		;;
-
-	(labelclear)
-		_arguments -A "-*" \
-			'-f[treat exported or foreign devices as inactive]' \
-			'*:virtual device:_files'
-		;;
-
-	(status)
-		_arguments -A "-*" \
-			'-l[Display configuration in /dev/chassis location form]' \
-			'-v[Verbose information]' \
-			'-x[Show only unhealthy pools]' \
-			'-T[timestamp]:value:(u d)' \
-			'*::pool name:_zfs_pool'
-		;;
-
-	(offline)
-		_arguments -A "-*" \
-			'-t[Offline until next reboot]' \
-			':pool name:_zfs_pool' \
-			'*:virtual device:_files'
-		;;
-
-	(online)
-		_arguments \
-			'-e[Expand device to use all available space]' \
-			':pool name:_zfs_pool' \
-			'*:virtual device:_files'
-		;;
-
-	(attach)
-		# TODO: first device should choose first from existing.
-		_arguments \
-			'-f[Force attach, even if in use]' \
-			':pool name:_zfs_pool' \
-			':virtual device:_files' \
-			':virtual device:_files'
-		;;
-
-	(detach)
-		_arguments \
-			':pool name:_zfs_pool' \
-			':virtual device:_files'
-		;;
-
-	(replace)
-		_arguments -A "-*" \
-			'-f[Force attach, even if in use]' \
-			':pool name:_zfs_pool' \
-			':virtual device:_files' \
-			'::virtual device:_files'
-		;;
-
-	(scrub)
-		_arguments -A "-*" \
-			'-s[Stop scrubbing]' \
-			'*:pool name:_zfs_pool'
-		;;
-
-	(export)
-		_arguments -A "-*" \
-			'-f[Forcefully unmount all datasets]' \
-			'*:pool name:_zfs_pool'
-		;;
-
-	(import)
-		# TODO: -o should complete mount options, too
-		_arguments -A "-*" \
-			'-D[Destroyed pools]' \
-			'(-d)*-c[Use cache file]:cache file:_files' \
-			'(-c -D)*-d[Search for devices or files in directory]:directory:_files -/' \
-			'-F[Recovery mode: discard transactions if required]' \
-			'-f[Force import]' \
-			'-l[Display configuration in /dev/chassis location form]' \
-			'-m[Ignore missing log devices]' \
-			'-N[Import pool without mounting any filesystems]' \
-			'-n[With -F; do not perform input]' \
-			'-R[Alternate root]:alternate root:_files -/' \
-			'-o[Set pool or dataset property]:property:_values -s , "property" $create_properties_dataset $rw_props' \
-			- set1 \
-			'*:pool name or id:_zfs_pool' \
-			'::new pool name:' \
-			- set2 \
-			'-N[Do not mount any filesystems]' \
-			'-a[All pools]'
-		;;
-
-	(get)
-		_arguments -A "-*" \
-			':property:_values -s , "property" $fields' \
-			'*:pool name:_zfs_pool'
-		;;
-
-	(set)
-		_arguments -A "-*" \
-			':property:_values -s , "property" $rw_props' \
-			'*:pool name:_zfs_pool'
-		;;
-
-	(split)
-		_arguments -A "-*" \
-			'-R[Alternate root]:alternate root:_files -/' \
-			'-l[Display configuration in /dev/chassis location form]' \
-			'-n[Display configuration without splitting]' \
-			'-o[Set pool or dataset property]:property:_values -s , "property" $create_properties_dataset $rw_props' \
-			':pool name or id:_zfs_pool' \
-			':new pool name:' \
-			'*::virtual device:_files -/'
-		;;
-
-	(upgrade)
-		_arguments -A "-*" \
-			- set1 \
-			'-v[Display ZFS versions and descriptions]' \
-			- set2 \
-			"-V[Upgrade to given version]:version:($versions)" \
-			'-a[Upgrade all pools]' \
-			'*:pool name:_zfs_pool'
-		;;
-
-	(history)
-		_arguments -A "-*" \
-			'-i[Display internal events]' \
-			'-l[Long format]' \
-			'*:pool name:_zfs_pool'
-		;;
-
-	(*)
-		_message "unknown zpool subcommand: $service"
-		;;
-	esac
-}
-
-_zpool "$@"
diff --git a/Completion/Unix/Type/_zfs_dataset b/Completion/Unix/Type/_zfs_dataset
index 63384afc6..7edcfd5d7 100644
--- a/Completion/Unix/Type/_zfs_dataset
+++ b/Completion/Unix/Type/_zfs_dataset
@@ -11,10 +11,12 @@ local expl_type
 # -t takes arguments (what kinds of datasets) and can appear multiple times
 zparseopts -D -E e:=expl_type_arr p=paths_allowed r1=rsrc r2=rdst t+:=type
 
-[[ -n $type[(r)fs] ]]    && typearg=( filesystem )
-[[ -n $type[(r)vol] ]]   && typearg=( $typearg volume )
-[[ -n $type[(r)snap] ]]  && typearg=( $typearg snapshot )
-[[ -n $type[(r)share] ]]  && typearg=( $typearg share )
+[[ -n $type[(r)fs] ]] && typearg=( filesystem )
+[[ -n $type[(r)vol] ]] && typearg+=( volume )
+[[ -n $type[(r)snap] ]] && typearg+=( snapshot )
+[[ -n $type[(r)share] && $implementation = solaris ]] && typearg+=( share )
+[[ -n $type[(r)bookmark] && $implementation = openzfs ]] &&
+    typearg+=( bookmark )
 if [[ -n $typearg ]]; then
 	typearg=( -t ${(j:,:)typearg} )
 # We know we're in zfs list if paths_allowed is non-empty.
@@ -58,7 +60,7 @@ if [[ ${#rdst} -gt 0 ]]; then
 fi
 
 if [[ -n $type[(r)clone] ]]; then
-	datasetlist=( ${(f)"$(zfs list -H -o name,origin -t filesystem 2>/dev/null | awk -F $'\t' "\$2 != \"-\" {print \$1}")":#no cloned filesystems available} )
+  datasetlist=( ${(f)"$(zfs list -H -o name,origin -t filesystem 2>/dev/null | awk -F$'\t' "\$2 != \"-\" {print \$1}")":#no cloned filesystems available} )
 else
 	datasetlist=( ${(f)"$(zfs list -H -o name $typearg 2>/dev/null)":#no datasets available} )
 fi
@@ -74,4 +76,5 @@ if [[ -n $expl_type_arr[2] ]]; then
 	expl_type=$expl_type_arr[2]
 fi
 
-_wanted dataset expl "$expl_type" _multi_parts "$@" -q / datasetlist
+_description datasets expl "$expl_type"
+_multi_parts "$@" "$expl[@]" -q / datasetlist
diff --git a/Completion/Unix/Type/_zfs_keysource_props b/Completion/Unix/Type/_zfs_keysource_props
deleted file mode 100644
index 01f63257a..000000000
--- a/Completion/Unix/Type/_zfs_keysource_props
+++ /dev/null
@@ -1,15 +0,0 @@
-#autoload
-
-local -a suf
-local expl
-
-compset -S ",*" || suf=(-S ,)
-if compset -P 1 "*,"; then
-	_alternative "zfs-keylocator-prompt:\"prompt\" locator:(prompt)" \
-		"zfs-keylocator-file:file locator:_path_files" \
-		"zfs-keylocator-pkcs11:PKCS#11 locator: " \
-		"zfs-keylocator-https:HTTPS URL locator: "
-else
-	_description format expl "keysource format"
-	compadd $suf -q "$expl[@]" "$@" raw hex passphrase
-fi