about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog3187
-rw-r--r--Completion/BSD/Command/_bsdconfig12
-rw-r--r--Completion/BSD/Command/_chflags96
-rw-r--r--Completion/BSD/Command/_cu2
-rw-r--r--Completion/BSD/Command/_fetch69
-rw-r--r--Completion/BSD/Command/_freebsd-update13
-rw-r--r--Completion/BSD/Command/_fstat11
-rw-r--r--Completion/BSD/Command/_gstat6
-rw-r--r--Completion/BSD/Command/_jexec4
-rw-r--r--Completion/BSD/Command/_jot82
-rw-r--r--Completion/BSD/Command/_kld11
-rw-r--r--Completion/BSD/Command/_portmaster2
-rw-r--r--Completion/BSD/Command/_procstat37
-rw-r--r--Completion/BSD/Command/_sockstat1
-rw-r--r--Completion/BSD/Command/_sysrc53
-rw-r--r--Completion/BSD/Command/_systat3
-rw-r--r--Completion/Base/Completer/_expand9
-rw-r--r--Completion/Base/Completer/_external_pwds8
-rw-r--r--Completion/Base/Completer/_user_expand11
-rw-r--r--Completion/Base/Core/_main_complete3
-rw-r--r--Completion/Base/Core/_message3
-rw-r--r--Completion/Base/Core/_normal4
-rw-r--r--Completion/Base/Utility/_arguments22
-rw-r--r--Completion/Base/Utility/_call_program16
-rw-r--r--Completion/Base/Utility/_values7
-rw-r--r--Completion/Darwin/Type/_retrieve_mac_apps6
-rw-r--r--Completion/Debian/Command/_a2utils4
-rw-r--r--Completion/Debian/Command/_apt80
-rw-r--r--Completion/Debian/Command/_bts77
-rw-r--r--Completion/Debian/Command/_bug3
-rw-r--r--Completion/Debian/Command/_dchroot1
-rw-r--r--Completion/Debian/Command/_dchroot-dsa1
-rw-r--r--Completion/Debian/Command/_dcut6
-rw-r--r--Completion/Debian/Command/_debfoster4
-rw-r--r--Completion/Debian/Command/_git-buildpackage4
-rw-r--r--Completion/Debian/Command/_lighttpd8
-rw-r--r--Completion/Debian/Command/_lintian6
-rw-r--r--Completion/Debian/Command/_make-kpkg2
-rw-r--r--Completion/Debian/Command/_schroot2
-rw-r--r--Completion/Debian/Command/_wajig2
-rw-r--r--Completion/Debian/Type/_deb_architectures2
-rw-r--r--Completion/Debian/Type/_deb_packages2
-rw-r--r--Completion/Debian/Type/_debbugs_bugnumber11
-rw-r--r--Completion/Linux/Command/_cpupower119
-rw-r--r--Completion/Linux/Command/_ethtool2
-rw-r--r--Completion/Linux/Command/_fusermount2
-rw-r--r--Completion/Linux/Command/_gpasswd19
-rw-r--r--Completion/Linux/Command/_iconvconfig13
-rw-r--r--Completion/Linux/Command/_iwconfig4
-rw-r--r--Completion/Linux/Command/_lsblk57
-rw-r--r--Completion/Linux/Command/_lsusb30
-rw-r--r--Completion/Linux/Command/_ltrace35
-rw-r--r--Completion/Linux/Command/_mdadm10
-rw-r--r--Completion/Linux/Command/_modutils2
-rw-r--r--Completion/Linux/Command/_pidof24
-rw-r--r--Completion/Linux/Command/_pkgtool2
-rw-r--r--Completion/Linux/Command/_ss11
-rw-r--r--Completion/Linux/Command/_sshfs68
-rw-r--r--Completion/Linux/Command/_strace210
-rw-r--r--Completion/Linux/Command/_sysstat37
-rw-r--r--Completion/Linux/Command/_vserver2
-rw-r--r--Completion/Mandriva/Command/_rebootin2
-rw-r--r--Completion/Redhat/Command/_rpm2
-rw-r--r--Completion/Redhat/Command/_scl5
-rw-r--r--Completion/Redhat/Command/_yum604
-rw-r--r--Completion/Solaris/Command/_beadm21
-rw-r--r--Completion/Solaris/Command/_coreadm83
-rw-r--r--Completion/Solaris/Command/_dladm1355
-rw-r--r--Completion/Solaris/Command/_dumpadm39
-rw-r--r--Completion/Solaris/Command/_flowadm48
-rw-r--r--Completion/Solaris/Command/_fmadm188
-rw-r--r--Completion/Solaris/Command/_pfexec3
-rw-r--r--Completion/Solaris/Command/_svcadm2
-rw-r--r--Completion/Solaris/Command/_svccfg2
-rw-r--r--Completion/Solaris/Command/_svcs52
-rw-r--r--Completion/Solaris/Command/_zlogin1
-rw-r--r--Completion/Solaris/Type/_svcs_fmri2
-rw-r--r--Completion/Unix/Command/_adb6
-rw-r--r--Completion/Unix/Command/_ant5
-rw-r--r--Completion/Unix/Command/_arping22
-rw-r--r--Completion/Unix/Command/_attr8
-rw-r--r--Completion/Unix/Command/_augeas5
-rw-r--r--Completion/Unix/Command/_awk138
-rw-r--r--Completion/Unix/Command/_basename27
-rw-r--r--Completion/Unix/Command/_bzr2
-rw-r--r--Completion/Unix/Command/_cat27
-rw-r--r--Completion/Unix/Command/_cdrdao6
-rw-r--r--Completion/Unix/Command/_chown4
-rw-r--r--Completion/Unix/Command/_cp3
-rw-r--r--Completion/Unix/Command/_cpio11
-rw-r--r--Completion/Unix/Command/_cscope29
-rw-r--r--Completion/Unix/Command/_cvs5
-rw-r--r--Completion/Unix/Command/_darcs2
-rw-r--r--Completion/Unix/Command/_date98
-rw-r--r--Completion/Unix/Command/_dbus13
-rw-r--r--Completion/Unix/Command/_devtodo4
-rw-r--r--Completion/Unix/Command/_df13
-rw-r--r--Completion/Unix/Command/_dmesg32
-rw-r--r--Completion/Unix/Command/_dmidecode17
-rw-r--r--Completion/Unix/Command/_doas22
-rw-r--r--Completion/Unix/Command/_dsh3
-rw-r--r--Completion/Unix/Command/_entr9
-rw-r--r--Completion/Unix/Command/_env53
-rw-r--r--Completion/Unix/Command/_espeak73
-rw-r--r--Completion/Unix/Command/_feh4
-rw-r--r--Completion/Unix/Command/_find20
-rw-r--r--Completion/Unix/Command/_fmt60
-rw-r--r--Completion/Unix/Command/_fsh1
-rw-r--r--Completion/Unix/Command/_gcc4
-rw-r--r--Completion/Unix/Command/_gdb2
-rw-r--r--Completion/Unix/Command/_getent2
-rw-r--r--Completion/Unix/Command/_git1652
-rw-r--r--Completion/Unix/Command/_global6
-rw-r--r--Completion/Unix/Command/_gpg2
-rw-r--r--Completion/Unix/Command/_gphoto2113
-rw-r--r--Completion/Unix/Command/_graphicsmagick4
-rw-r--r--Completion/Unix/Command/_grep91
-rw-r--r--Completion/Unix/Command/_groff2
-rw-r--r--Completion/Unix/Command/_growisofs8
-rw-r--r--Completion/Unix/Command/_gs2
-rw-r--r--Completion/Unix/Command/_gsettings47
-rw-r--r--Completion/Unix/Command/_head2
-rw-r--r--Completion/Unix/Command/_hg2
-rw-r--r--Completion/Unix/Command/_iftop21
-rw-r--r--Completion/Unix/Command/_imagemagick4
-rw-r--r--Completion/Unix/Command/_initctl4
-rw-r--r--Completion/Unix/Command/_iostat132
-rw-r--r--Completion/Unix/Command/_ip12
-rw-r--r--Completion/Unix/Command/_java6
-rw-r--r--Completion/Unix/Command/_joe2
-rw-r--r--Completion/Unix/Command/_kvno10
-rw-r--r--Completion/Unix/Command/_less7
-rw-r--r--Completion/Unix/Command/_lha2
-rw-r--r--Completion/Unix/Command/_libvirt238
-rw-r--r--Completion/Unix/Command/_ln1
-rw-r--r--Completion/Unix/Command/_locale84
-rw-r--r--Completion/Unix/Command/_localedef92
-rw-r--r--Completion/Unix/Command/_locate192
-rw-r--r--Completion/Unix/Command/_look34
-rw-r--r--Completion/Unix/Command/_lp2
-rw-r--r--Completion/Unix/Command/_ls113
-rw-r--r--Completion/Unix/Command/_lsof2
-rw-r--r--Completion/Unix/Command/_lzop3
-rw-r--r--Completion/Unix/Command/_make24
-rw-r--r--Completion/Unix/Command/_man60
-rw-r--r--Completion/Unix/Command/_md5sum5
-rw-r--r--Completion/Unix/Command/_mencal2
-rw-r--r--Completion/Unix/Command/_module22
-rw-r--r--Completion/Unix/Command/_mosh1
-rw-r--r--Completion/Unix/Command/_mount20
-rw-r--r--Completion/Unix/Command/_mpc2
-rw-r--r--Completion/Unix/Command/_mutt7
-rw-r--r--Completion/Unix/Command/_mysql_utils2
-rw-r--r--Completion/Unix/Command/_nkf2
-rw-r--r--Completion/Unix/Command/_nm1
-rw-r--r--Completion/Unix/Command/_nmap4
-rw-r--r--Completion/Unix/Command/_openstack192
-rw-r--r--Completion/Unix/Command/_paste19
-rw-r--r--Completion/Unix/Command/_patch31
-rw-r--r--Completion/Unix/Command/_patchutils4
-rw-r--r--Completion/Unix/Command/_pbm2
-rw-r--r--Completion/Unix/Command/_perforce948
-rw-r--r--Completion/Unix/Command/_perl2
-rw-r--r--Completion/Unix/Command/_pgrep3
-rw-r--r--Completion/Unix/Command/_php2
-rw-r--r--Completion/Unix/Command/_picocom2
-rw-r--r--Completion/Unix/Command/_pkg-config37
-rw-r--r--Completion/Unix/Command/_postfix153
-rw-r--r--Completion/Unix/Command/_prove2
-rw-r--r--Completion/Unix/Command/_rake2
-rw-r--r--Completion/Unix/Command/_rar2
-rw-r--r--Completion/Unix/Command/_readelf4
-rw-r--r--Completion/Unix/Command/_rlogin5
-rw-r--r--Completion/Unix/Command/_rm22
-rw-r--r--Completion/Unix/Command/_rrdtool26
-rw-r--r--Completion/Unix/Command/_rsync23
-rw-r--r--Completion/Unix/Command/_ruby7
-rw-r--r--Completion/Unix/Command/_screen2
-rw-r--r--Completion/Unix/Command/_script71
-rw-r--r--Completion/Unix/Command/_sed6
-rw-r--r--Completion/Unix/Command/_service49
-rw-r--r--Completion/Unix/Command/_sisu2
-rw-r--r--Completion/Unix/Command/_sort14
-rw-r--r--Completion/Unix/Command/_sqsh2
-rw-r--r--Completion/Unix/Command/_ssh155
-rw-r--r--Completion/Unix/Command/_stgit12
-rw-r--r--Completion/Unix/Command/_strip7
-rw-r--r--Completion/Unix/Command/_su127
-rw-r--r--Completion/Unix/Command/_subversion68
-rw-r--r--Completion/Unix/Command/_sudo6
-rw-r--r--Completion/Unix/Command/_surfraw2
-rw-r--r--Completion/Unix/Command/_swaks40
-rw-r--r--Completion/Unix/Command/_swift123
-rw-r--r--Completion/Unix/Command/_sysctl26
-rw-r--r--Completion/Unix/Command/_tail2
-rw-r--r--Completion/Unix/Command/_tcpdump200
-rw-r--r--Completion/Unix/Command/_texinfo243
-rw-r--r--Completion/Unix/Command/_tin2
-rw-r--r--Completion/Unix/Command/_tmux1831
-rw-r--r--Completion/Unix/Command/_top177
-rw-r--r--Completion/Unix/Command/_touch45
-rw-r--r--Completion/Unix/Command/_tree101
-rw-r--r--Completion/Unix/Command/_truss76
-rw-r--r--Completion/Unix/Command/_uniq4
-rw-r--r--Completion/Unix/Command/_units6
-rw-r--r--Completion/Unix/Command/_user_admin206
-rw-r--r--Completion/Unix/Command/_vim45
-rw-r--r--Completion/Unix/Command/_vmstat26
-rw-r--r--Completion/Unix/Command/_w3m4
-rw-r--r--Completion/Unix/Command/_wget3
-rw-r--r--Completion/Unix/Command/_wiggle2
-rw-r--r--Completion/Unix/Command/_xargs1
-rw-r--r--Completion/Unix/Command/_xmlsoft7
-rw-r--r--Completion/Unix/Command/_xxd18
-rw-r--r--Completion/Unix/Command/_xz2
-rw-r--r--Completion/Unix/Command/_yafc28
-rw-r--r--Completion/Unix/Command/_zfs34
-rw-r--r--Completion/Unix/Command/_zip119
-rw-r--r--Completion/Unix/Command/_zpool2
-rw-r--r--Completion/Unix/Type/_absolute_command_paths12
-rw-r--r--Completion/Unix/Type/_baudrates (renamed from Completion/Unix/Type/_baudrate)58
-rw-r--r--Completion/Unix/Type/_canonical_paths93
-rw-r--r--Completion/Unix/Type/_date_formats21
-rw-r--r--Completion/Unix/Type/_dates7
-rw-r--r--Completion/Unix/Type/_diff_options27
-rw-r--r--Completion/Unix/Type/_email_addresses2
-rw-r--r--Completion/Unix/Type/_files3
-rw-r--r--Completion/Unix/Type/_hosts24
-rw-r--r--Completion/Unix/Type/_path_files12
-rw-r--r--Completion/Unix/Type/_pspdf2
-rw-r--r--Completion/Unix/Type/_remote_files19
-rw-r--r--Completion/Unix/Type/_signals13
-rw-r--r--Completion/Unix/Type/_sys_calls20
-rw-r--r--Completion/Unix/Type/_tilde_files3
-rw-r--r--Completion/Unix/Type/_zfs_dataset8
-rw-r--r--Completion/X/Command/_evince30
-rw-r--r--Completion/X/Command/_okular4
-rw-r--r--Completion/X/Command/_rdesktop79
-rw-r--r--Completion/X/Command/_setxkbmap8
-rw-r--r--Completion/X/Command/_x_utils28
-rw-r--r--Completion/X/Type/_xft_fonts2
-rw-r--r--Completion/Zsh/Command/_bindkey7
-rw-r--r--Completion/Zsh/Command/_fc9
-rw-r--r--Completion/Zsh/Command/_precommand2
-rw-r--r--Completion/Zsh/Command/_print42
-rw-r--r--Completion/Zsh/Command/_strftime2
-rw-r--r--Completion/Zsh/Command/_typeset26
-rw-r--r--Completion/Zsh/Command/_vared2
-rw-r--r--Completion/Zsh/Command/_zed8
-rw-r--r--Completion/Zsh/Command/_zle10
-rw-r--r--Completion/Zsh/Command/_zstyle112
-rw-r--r--Completion/Zsh/Context/_brace_parameter5
-rw-r--r--Completion/Zsh/Context/_condition3
-rw-r--r--Completion/Zsh/Context/_subscript6
-rw-r--r--Completion/Zsh/Context/_value1
-rw-r--r--Completion/Zsh/Function/_add-zle-hook-widget36
-rw-r--r--Completion/Zsh/Function/_add-zsh-hook6
-rw-r--r--Completion/Zsh/Function/_zargs2
-rw-r--r--Completion/Zsh/Type/_arrays2
-rw-r--r--Completion/Zsh/Type/_completers14
-rw-r--r--Completion/Zsh/Type/_globflags3
-rw-r--r--Completion/Zsh/Type/_globquals25
-rw-r--r--Completion/Zsh/Type/_history_modifiers5
-rw-r--r--Completion/Zsh/Type/_module_math_func2
-rw-r--r--Completion/Zsh/Type/_ps12348
-rw-r--r--Completion/Zsh/Type/_user_math_func2
-rw-r--r--Completion/Zsh/Type/_vars2
-rw-r--r--Completion/Zsh/Type/_widgets9
-rw-r--r--Completion/compaudit6
-rw-r--r--Completion/compdump5
-rw-r--r--Completion/compinit5
-rw-r--r--Config/version.mk4
-rw-r--r--Doc/Zsh/builtins.yo117
-rw-r--r--Doc/Zsh/compsys.yo302
-rw-r--r--Doc/Zsh/compwid.yo7
-rw-r--r--Doc/Zsh/cond.yo9
-rw-r--r--Doc/Zsh/contrib.yo307
-rw-r--r--Doc/Zsh/exec.yo15
-rw-r--r--Doc/Zsh/expn.yo93
-rw-r--r--Doc/Zsh/grammar.yo3
-rw-r--r--Doc/Zsh/jobs.yo4
-rw-r--r--Doc/Zsh/mod_complist.yo5
-rw-r--r--Doc/Zsh/mod_curses.yo18
-rw-r--r--Doc/Zsh/mod_db_gdbm.yo11
-rw-r--r--Doc/Zsh/mod_parameter.yo29
-rw-r--r--Doc/Zsh/mod_sched.yo5
-rw-r--r--Doc/Zsh/mod_zleparameter.yo16
-rw-r--r--Doc/Zsh/options.yo76
-rw-r--r--Doc/Zsh/params.yo52
-rw-r--r--Doc/Zsh/restricted.yo10
-rw-r--r--Doc/Zsh/roadmap.yo3
-rw-r--r--Doc/Zsh/zle.yo91
-rw-r--r--Etc/BUGS42
-rw-r--r--Etc/ChangeLog-4.32
-rw-r--r--Etc/FAQ.yo4
-rw-r--r--Etc/zsh-development-guide79
-rw-r--r--Functions/MIME/zsh-mime-handler2
-rw-r--r--Functions/Math/.distfiles2
-rw-r--r--Functions/Math/zmathfunc34
-rw-r--r--Functions/Misc/add-zle-hook-widget186
-rw-r--r--Functions/Misc/add-zsh-hook2
-rw-r--r--Functions/Misc/run-help-ip4
-rw-r--r--Functions/Misc/zcalc345
-rw-r--r--Functions/Misc/zed4
-rw-r--r--Functions/VCS_Info/Backends/VCS_INFO_get_data_git64
-rw-r--r--Functions/VCS_Info/Backends/VCS_INFO_get_data_hg57
-rw-r--r--Functions/VCS_Info/VCS_INFO_hexdump16
-rw-r--r--Functions/VCS_Info/VCS_INFO_patch2subject67
-rw-r--r--Functions/VCS_Info/VCS_INFO_quilt64
-rw-r--r--Functions/VCS_Info/VCS_INFO_set-patch-format79
-rw-r--r--Functions/VCS_Info/vcs_info3
-rw-r--r--Functions/Zle/bracketed-paste-magic50
-rw-r--r--Functions/Zle/bracketed-paste-url-magic4
-rw-r--r--Functions/Zle/delete-whole-word-match15
-rw-r--r--Functions/Zle/expand-absolute-path2
-rw-r--r--Functions/Zle/history-beginning-search-menu2
-rw-r--r--Functions/Zle/insert-unicode-char4
-rw-r--r--Functions/Zle/match-words-by-style53
-rw-r--r--Functions/Zle/select-bracketed2
-rw-r--r--Functions/Zle/select-word-match120
-rw-r--r--Functions/Zle/surround7
-rw-r--r--Functions/Zle/vi-pipe39
-rw-r--r--Functions/Zle/zcalc-auto-insert3
-rw-r--r--LICENCE2
-rw-r--r--Makefile.in3
-rw-r--r--Misc/vcs_info-examples2
-rw-r--r--NEWS59
-rw-r--r--README136
-rw-r--r--Src/Builtins/sched.c6
-rw-r--r--Src/Modules/curses.c104
-rw-r--r--Src/Modules/datetime.c8
-rw-r--r--Src/Modules/db_gdbm.c537
-rw-r--r--Src/Modules/db_gdbm.mdd2
-rw-r--r--Src/Modules/example.c3
-rw-r--r--Src/Modules/mathfunc.c4
-rw-r--r--Src/Modules/parameter.c137
-rw-r--r--Src/Modules/regex.c8
-rw-r--r--Src/Modules/system.c36
-rw-r--r--Src/Modules/system.mdd7
-rw-r--r--Src/Modules/tcp.c3
-rw-r--r--Src/Modules/terminfo.c2
-rw-r--r--Src/Modules/zpty.c16
-rw-r--r--Src/Modules/zutil.c24
-rw-r--r--Src/Zle/comp.h33
-rw-r--r--Src/Zle/compcore.c56
-rw-r--r--Src/Zle/compctl.c61
-rw-r--r--Src/Zle/complete.c81
-rw-r--r--Src/Zle/complist.c24
-rw-r--r--Src/Zle/compmatch.c215
-rw-r--r--Src/Zle/compresult.c5
-rw-r--r--Src/Zle/computil.c499
-rw-r--r--Src/Zle/iwidgets.list2
-rw-r--r--Src/Zle/textobjects.c18
-rw-r--r--Src/Zle/zle.h14
-rw-r--r--Src/Zle/zle_hist.c17
-rw-r--r--Src/Zle/zle_keymap.c38
-rw-r--r--Src/Zle/zle_main.c106
-rw-r--r--Src/Zle/zle_misc.c10
-rw-r--r--Src/Zle/zle_params.c128
-rw-r--r--Src/Zle/zle_refresh.c17
-rw-r--r--Src/Zle/zle_thingy.c42
-rw-r--r--Src/Zle/zle_tricky.c53
-rw-r--r--Src/Zle/zle_utils.c14
-rw-r--r--Src/Zle/zle_vi.c193
-rw-r--r--Src/Zle/zle_word.c90
-rw-r--r--Src/builtin.c484
-rw-r--r--Src/compat.c362
-rw-r--r--Src/cond.c30
-rw-r--r--Src/exec.c789
-rw-r--r--Src/glob.c96
-rw-r--r--Src/hashtable.c187
-rw-r--r--Src/hist.c50
-rw-r--r--Src/init.c35
-rw-r--r--Src/input.c27
-rw-r--r--Src/jobs.c80
-rw-r--r--Src/lex.c42
-rw-r--r--Src/linklist.c29
-rw-r--r--Src/loop.c35
-rw-r--r--Src/math.c39
-rw-r--r--Src/mem.c34
-rw-r--r--Src/module.c3
-rw-r--r--Src/options.c4
-rw-r--r--Src/params.c635
-rw-r--r--Src/parse.c119
-rw-r--r--Src/pattern.c12
-rw-r--r--Src/prompt.c23
-rw-r--r--Src/signals.c43
-rw-r--r--Src/signals.h31
-rw-r--r--Src/string.c31
-rw-r--r--Src/subst.c201
-rw-r--r--Src/text.c7
-rw-r--r--Src/utils.c321
-rw-r--r--Src/watch.c107
-rw-r--r--Src/wcwidth9.h1325
-rw-r--r--Src/zsh.h120
-rw-r--r--Src/zsh_system.h8
-rw-r--r--Src/ztype.h6
-rw-r--r--Test/A01grammar.ztst88
-rw-r--r--Test/A02alias.ztst33
-rw-r--r--Test/A04redirect.ztst36
-rw-r--r--Test/A05execution.ztst13
-rw-r--r--Test/A06assign.ztst206
-rw-r--r--Test/B02typeset.ztst14
-rw-r--r--Test/B03print.ztst28
-rw-r--r--Test/B09hash.ztst8
-rw-r--r--Test/C02cond.ztst39
-rw-r--r--Test/C03traps.ztst266
-rw-r--r--Test/C04funcdef.ztst200
-rw-r--r--Test/D01prompt.ztst18
-rw-r--r--Test/D02glob.ztst47
-rw-r--r--Test/D04parameter.ztst260
-rw-r--r--Test/D06subscript.ztst7
-rw-r--r--Test/D07multibyte.ztst56
-rw-r--r--Test/D08cmdsubst.ztst24
-rw-r--r--Test/E01options.ztst118
-rw-r--r--Test/E02xtrace.ztst23
-rw-r--r--Test/V01zmodload.ztst74
-rw-r--r--Test/V06parameter.ztst42
-rw-r--r--Test/V09datetime.ztst14
-rw-r--r--Test/V10private.ztst16
-rw-r--r--Test/V11db_gdbm.ztst327
-rw-r--r--Test/X02zlevi.ztst27
-rw-r--r--Test/Y01completion.ztst24
-rw-r--r--Test/Y03arguments.ztst536
-rwxr-xr-xTest/ztst.zsh26
-rw-r--r--Util/check-tmux-state2
-rwxr-xr-xUtil/helpfiles2
-rw-r--r--configure.ac76
428 files changed, 22947 insertions, 7316 deletions
diff --git a/ChangeLog b/ChangeLog
index 4355fd360..15a02afa1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,3178 @@
+2017-05-28  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Doc/Zsh/mod_complist.yo: clarify ZLS_COLORS pattern
+	matching contexts
+
+	* 41159 (tweaked): Completion/Unix/Command/_ssh: handle "Include"
+	and "HostName" lines in ~/.ssh/config
+
+2017-05-24  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Sebastian: 41146: Src/Modules/db_gdbm.c: be more careful about
+	freeing strings with embedded nulls.
+
+	* Sebastian: 40898: Src/Modules/db_gdbm.c: fix GDBM error handling.
+
+2017-05-23  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Stephane: 41142: Src/Modules/system.c: ensure close-on-exec is
+	applied to moved file descriptor.
+
+2017-05-22  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Marko Myllenen: 41087: Completion/Unix/Command/_kvno: Update
+	completion to MIT krb 1.14.
+
+	* Marko Myllenen: 41086: Completion/Unix/Command/_libvirt:
+	update completion.
+
+	* Marko Myllenen: 41085: Completion/Unix/Command/_openstack:
+	update completion.
+
+	*  Jörg Sommer: 41128: Doc/Zsh/compsys.yo: typo.
+
+2017-05-19  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* users/22707: Doc/Zsh/compsys.yo: Document use of
+	accept-exact-dirs for allowing completion after "magic"
+	directories.
+
+2017-05-18  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 41113 (tweaked): Src/hashtable,c, Src/hist.c, Src/zsh.h: Save
+	and restore state of linking of current history line into history
+	ring, to avoid an attempt to free the current history line.
+
+2017-05-12  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 41090: Src/Zle/zle_refresh.c, Src/compat.c, Src/pattern.c,
+	Src/utils.c, Src/wcwidth9.h, Src/zsh.h, Src/ztype.h,
+	configure.ac: Replace iswprint() if unicode9 support is enabled.
+	Enable unicode9 if wcwidth() and/or iswprint() is broken.
+
+2017-05-11  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 41096: Src/string.c: Fix dupstring_wlen() for unmetafied
+	string.  It's not safe to assume null termination.
+
+2017-05-09  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* unposted: Test/D02glob.ztst: Adding comment to test changed
+	line number in output.
+
+2017-05-09  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 41081: Src/utils.c, Test/D02glob.ztst: Symlink expansion
+	didn't test all buffer length calculations.
+
+	* 41078: Src/prompt.c, Test/D01prompt.ztst: Empty psvar could
+	cause bad reference in prompt expansion.
+
+2017-05-08  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 41059: Completion/compinit: use 2>&- to avoid error in
+	restricted shell.
+
+	* 41073: Src/parse.c, Test/A04redirect.ztst: off-by-one error
+	checking for {varid} syntax.
+
+	* users/22688: Src/parse.c, Test/A04redirect.ztst: Allow mixing
+	of redirections and arguments after anonymous functions.
+
+	* 41060: Src/parse.c, Test/A04redirect.ztst: combination
+	of HERE document and |& was broken by miscounting wordcode owing
+	to missing flag.
+
+2017-05-04  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 41038: NEWS: Document recent, backwards-compatible precommand
+	modifiers changes.
+
+2017-05-03  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 41043: Src/exec.c: Close pipes in shell if disowning
+	backgrounded job with &!.
+
+2017-05-02  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* unposted: Functions/Zle/insert-unicode-char: use typeset -g to
+	avoid warnnestedvar warning.
+
+2017-04-28  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 41020: Src/exec.c, Test/A01grammar.ztst: "command -p" was
+	broken by 41008, also add more tests for precommand modifiers.
+
+2017-04-27  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 41012: Src/builtin.c, Src/exec.c, Src/signals.c,
+	Test/C03traps.ztst: Fix early exit from nested functions in EXIT
+	trap.  Drive-by fix of testing for need to exit if exiting when
+	already in EXIT trap for main shell --- we should just leave
+	immediately.
+
+2017-04-27  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 41016: Test/A01grammar.ztst: test that quoted precommand
+	modifiers now work.
+
+2017-04-26  Oliver Kiddle  <opk@zsh.org>
+
+	* unposted: Completion/Unix/Command/_dbus: fix syntax on line end
+
+	* 40965 (tweaked c.f. 40968): Completion/BSD/Command/_portmaster,
+	Completion/Debian/Command/_a2utils, Completion/Debian/Command/_apt,
+	Completion/Debian/Command/_lighttpd,
+	Completion/Debian/Command/_lintian,
+	Completion/Debian/Command/_wajig,
+	Completion/Debian/Type/_deb_architectures,
+	Completion/Debian/Type/_debbugs_bugnumber,
+	Completion/Linux/Command/_ethtool,
+	Completion/Solaris/Command/_svcadm,
+	Completion/Solaris/Command/_svccfg,
+	Completion/Solaris/Type/_svcs_fmri,
+	Completion/Unix/Command/_cdrdao, Completion/Unix/Command/_darcs,
+	Completion/Unix/Command/_iftop, Completion/Unix/Command/_lha,
+	Completion/Unix/Command/_lsof, Completion/Unix/Command/_pkg-config,
+	Completion/Unix/Command/_rrdtool, Completion/Unix/Command/_stgit,
+	Completion/Unix/Command/_tcpdump, Completion/Unix/Command/_texinfo,
+	Completion/Unix/Command/_units, Completion/Unix/Command/_yafc,
+	Completion/Unix/Type/_absolute_command_paths,
+	Completion/X/Command/_setxkbmap, Completion/X/Type/_xft_fonts,
+	Completion/Zsh/Command/_fc, Completion/Zsh/Context/_value,
+	Completion/Zsh/Function/_add-zle-hook-widget,
+	Completion/Zsh/Function/_add-zsh-hook:
+	fix for missing local declarations of expl
+
+2017-04-26  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 41008: Src/exec.c, Src/linklist.c, Test/A01grammar.ztst,
+	Test/E01options.ztst: Handle expansions when analysing
+	precommand modifiers.
+
+2017-04-26  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 41006: Completion/Unix/Command/_ls: add new options for BSDs,
+	fix a few problems on Linux, etc.
+
+2017-04-23  Daniel Hahler  <zsh@thequod.de>
+
+	* 40943: Completion/Unix/Command/_git: __git_recent_commits: prefer
+	recent commit objects.
+
+2017-04-23  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 40995: Src/lex.c, Test/D08cmdsubst.ztst: we need to expand
+	aliases when identifiying the end of a command substitution as
+	sometimes we can hit a parse error before.
+
+2017-04-23  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 40994: Src/utils.c: unmeta_one() need not count Meta
+
+2017-04-21  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40990: Src/params.c: When starting in sh emulation, don't
+	link PATH-style parameters to array equivalents.  Don't
+	check linkage when exporting colon-separated parameter.
+
+2017-04-18  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40973: Completion/Unix/Type/_remote_files: Fix completion of
+	remote files that start with a hyphen/minus.
+
+2017-04-15  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40977: Src/init.c: "emulate" disallows "--help" and "--version"
+
+2017-04-10  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Orlov Sergey: 40935: Src/utils.c: User names need metafying as
+	they can contain multibyte characters on some systems.
+
+2017-04-07  Bart Schaefer  <schaefer@zsh.org>
+
+	* 40940: Src/cond.c: untokenize names of condition features
+	before attempting to look up the definition from a module.
+	Necessary because of 40760 (tokenization of '-' as Dash).
+
+2017-04-05  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* users/22653: Doc/Zsh/options.yo: Clarify REMATCH_PCRE
+	semantics.
+
+2017-04-04  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 40933: Src/exec.c: we need job text in sourced files in case
+	of suspending.
+
+2017-04-03  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40932: Src/params.c, Test/D06subscript.ztst: parameter
+	subscripts need to count parentheses to avoid terminating early.
+
+2017-04-02  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40929 (replaces 40598): Src/subst.c: paramsubst() should always
+	return scalar when PREFORK_SINGLE was passed in from prefork()
+
+2017-04-01  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* Sebastian: 40782: Completion/Unix/Type/_hosts: avoid dependency
+	on zsh/regex module
+
+2017-03-30  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Dag-Erling Smørgrav: 40915: Completion/BSD/Command/_kld: fix
+	breakage.
+
+	* Sebastian: 40909: Test/D04parameter.ztst: stress test for
+	parameter substitution.
+
+2017-03-27  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40906: Doc/Zsh/expn.yo: array subst needs [*] or [@] with
+	KSH_ARRAYS.  Note this for (k) flag.
+
+2017-03-24  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Alexandre Rames: 40878: Completion/Unix/Command/_stgit:
+	additional squash command completion.
+
+	* Aaron Schrab: 40892: Functions/Misc/run-help-ip: subcommand
+	matching for ip help.
+
+2017-03-23  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 40891: Src/glob.c, Test/D04parameter.ztst: another similar fix
+	for zero-length matches at the end of a string.
+
+2017-03-23  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* users/22601: Src/glob.c, Test/D04parameter.ztst: problem matching
+	pattern against zero-length string in parameter substitutions.
+
+2017-03-21  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40875 (Martin Krafft): change description of REC_EXACT option.
+
+	* 40821: Test/V11db_gdbm.ztst: Note this contains UTF-8
+	characters and fix name.
+
+2017-03-20  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted (after 40859): Doc/Zsh/contrib.yo: Fix yodl warning.
+
+2017-03-19  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40859: Doc/Zsh/contrib.yo: vcs_info docs: Use proper internal
+	links through texinfo nodes.
+
+2017-03-18  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Doc/Zsh/contrib.yo: vcs_info quilt: Fix documentation
+	markup typo.
+
+2017-03-16  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40855: Completion/Unix/Command/_dmidecode: Fix _arguments
+	syntax error.
+
+2017-03-14  Oliver Kiddle  <opk@zsh.org>
+
+	* 40843: Completion/Unix/Command/_ruby: update options for
+	ruby 2.4.0p0
+
+	* 40842: Completion/Unix/Command/_basename,
+	Completion/Unix/Command/_cat, Completion/Unix/Command/_date,
+	Completion/Unix/Command/_df, Completion/Unix/Command/_fmt,
+	Completion/Unix/Command/_locate, Completion/Unix/Command/_ls,
+	Completion/Unix/Command/_nm, Completion/Unix/Command/_paste,
+	Completion/Unix/Command/_readelf, Completion/Unix/Command/_sed,
+	Completion/Unix/Command/_strip: update completions for coreutils
+	and similar utilities, also improving BSD and Solaris support
+
+2017-03-14  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40818: Completion/Unix/Command/_git: _git-checkout: When
+	completing local heads, prefer recently-checked-out ones. (after
+	38592)
+
+	* 40817: Completion/Unix/Command/_git: __git_recent_branches:
+	Retrieve less data, but faster.
+
+	* 40822: Doc/Zsh/contrib.yo: vcs_info quilt: Document the
+	'.quilt-foo' zstyle context element. (Compare users/20807.)
+
+2017-03-12  Oliver Kiddle  <opk@zsh.org>
+
+	* Wieland Hoffmann: 40837: Completion/Unix/Command/_pgrep:
+	add -w on Linux
+
+2017-03-12  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40816: Doc/Zsh/builtins.yo, Test/D01prompt.ztst: Document
+	interaction of 'print -P' and 'print -f'.
+
+2017-03-12  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 40820: Test/V09datetime.ztst: add a few tests of strftime,
+	related to 40681
+
+2017-03-11  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40832: Src/subst.c: fix $x:P when PWD=/
+
+2017-03-11  John Leuenhagen  <john@zlima12.com>
+
+	* unposted (github pull request #15):
+	Completion/Unix/Command/_ip: fix a small typo in `ip`
+	corrections file
+
+2017-03-10  Oliver Kiddle  <opk@zsh.org>
+
+	* 40824: Completion/Unix/Command/_grep: completion handling
+	of option deviations between different systems
+
+	* 40823: Completion/Linux/Command/_sshfs: update for sshfs 2.8
+
+2017-03-10  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40819: Src/glob.c: Fix IS_DASH() test in BRACE_CCL handling.
+
+2017-03-10  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* unposted: Src/Modules/tcp.c: silence compiler warnings on Cygwin
+
+	* unposted: Src/hashtable.c: declare file local variables as static
+
+2017-03-09  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* Fabian Klotzl: 40808: Completion/Linux/Command/_mdadm,
+	Completion/Unix/Command/_git, Completion/Unix/Command/_grep,
+	Completion/Unix/Command/_hg, Completion/Unix/Command/_java,
+	Completion/Unix/Command/_ls, Completion/Unix/Command/_mysql_utils,
+	Completion/Unix/Command/_rake, Completion/Unix/Command/_user_admin,
+	Completion/Unix/Command/_wget, Completion/Unix/Command/_zpool:
+	fix typos where (x,y) should have been (x y) in _arguments syntax
+
+	* 40801: Completion/Unix/Command/_mount: turns out that work of
+	art removed by 33963 was necessary after all: re-fix completion
+	of mount points with spaces in the name
+
+2017-03-09  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40805: (combined with the previous change):
+	Test/D04parameter.ztst: Check that $- and ${-} produce a
+	plausible result.
+
+	* Sebastian: 40803 as modified in 40804 and 40806:
+	Test/D04parameter.ztst: check for the (z) split flag on some
+	partly binary data that might be problematic.
+
+	* 40796: Src/exec.c: We don't want magic '=' expansion if we are
+	already parsing a separate variable name and value.
+
+2017-03-08  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40799: Src/params.c: fix $- expansion partly broken by 40760
+
+	* 40763: Src/Zle/compmatch.c, Src/Zle/computil.c, Src/utils.c:
+	count wide characters and Cmatcher pointers more sanely in
+	cfp_matcher_pats(), and count characters in pattern_match()
+	the same way to stay in sync; might not fix wide-char matching
+	in completion matcher-lists but should avoid wild pointer crash
+
+2017-03-08  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40745 + 40753: Src/init.c, Src/params.c: Fix 'unset
+	ZLE_RPROMPT_INDENT' not restoring the default behaviour.
+
+	* 40744: Doc/Zsh/grammar.yo: Document the SHORT_LOOPS 'function'
+	syntax.
+
+2017-03-07  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Sebastian: 40706: Test/V11db_gdbm.ztst: Fix ordering
+	dependence in gdbm test.
+
+	* 40760: Src/cond.c, Src/exec.c, Src/glob.c, Src/lex.c,
+	Src/math.c, Src/parse.c, Src/pattern.c, Src/subst.c,
+	Src/utils.c, Src/zsh.h, Test/D02glob.ztst: Always tokenise '-'
+	to Dash to eliminate niggles with range matches in complicated
+	contexts.  Match both - or Dash in contexts that don't care.
+
+2017-03-07  Mikael Magnusson  <mikachu@gmail.com>
+
+	* 40780: Completion/Unix/Command/_mount: Don't use =~ for simple
+	prefix match
+
+2017-03-05  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* Sebastian: 40726: Doc/Zsh/mod_curses.yo, Src/Modules/curses.c,
+	configure.ac: add "zcurses resize" for sane terminal size change
+
+2017-03-04  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* Sebastian: 40781: Src/params.c, Test/A06assign.ztst: optimize
+	array assignment, similar to 39995 for string assignment
+
+	* 40654: Src/exec.c: exit cleanly from special POSIXBUILTINS in
+	subshells
+
+2017-03-04  Oliver Kiddle  <opk@zsh.org>
+
+	* unposted (github): Christoffer Aasted:
+	Completion/Unix/Command/_ant: allow -Dproperty=/path_complete
+
+	* unposted (from Ferenc- via github): Completion/Unix/Command/_gcc:
+	Add newer C++ standard options to gcc completion
+
+	* unposted: Completion/Unix/Command/_tmux: complete 'tiled' layout
+
+	* unposted: Completion/Unix/Command/_xz: fix argument to --format
+
+	* 40715: Completion/Unix/Command/_git: update for git 2.12.0
+
+	* 40597: Completion/BSD/Command/_sysrc,
+	Completion/Base/Utility/_values: be flexible about order of
+	options to _values
+
+2017-03-03  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Sebastian: 40170: Src/Modules/curses.c: Fix up error number
+	resetting in curses module.  This appears to resolve an issue
+	mentioned in comments but attributed elsewhere, so remove
+	confusion here.
+
+	* 40173: Test/V11db_gdbm.ztst: don't report an error if gdbm
+	module doesn't load as this simply causes the test to be skipped.
+
+	* 40702: Doc/Zsh/zle.yo, Src/Zle/zle_params.c: add
+	KEYS_QUEUED_COUNT variable to ZLE parameters.
+
+2017-03-02  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 40681: Src/Modules/datetime.c: strftime builtin should
+	return 1 if zstrftime() returns -1.
+
+2017-03-01  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40622 (typos fixed): Doc/Zsh/builtins.yo, Src/builtin.c,
+	Src/math.c, Test/C04funcdef.ztst: add functions -Ms for
+	mathematical functions with string arguments.
+
+	* Sebastian: 40562: Test/V11db_gdbm.ztst: this was missed out of
+	the previous commit.
+
+2017-02-28  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40641: Doc/Zsh/expn.yo, Doc/Zsh/roadmap.yo: docs: patterns:
+	Add '|' to the overview. Clarify that it short-circuits.
+
+2017-02-26  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40650: Src/Modules/parameter.c: redo 40508 and 40626 with
+	comments explaining what is actually going on
+
+2017-02-25  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40644: Src/Modules/parameter.c, Test/V06parameter.ztst:
+	Revert 40626 which broke tests.
+
+2017-02-25  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40640 (plus doc typo fixed): Doc/Zsh/expn.yo, Src/subst.c: the
+	(A) parameter flag forces array result even if assignment syntax
+	is not used
+
+2017-02-25  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Completion/Zsh/Context/_brace_parameter: Port 40617
+	to the completion.
+
+	* 40626: Src/Modules/parameter.c, Test/V06parameter.ztst:
+	Make $options re-settable.  [reverted in 40644]
+
+2017-02-23  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted (cf. 40617): Doc/Zsh/expn.yo: clarify description of
+	the ${(A)name=value} and ${(AA)name=value} parameter flag uses.
+
+	* 40624 (cf. Danek Duvall, 40563): Src/signals.c: conditionally
+	handle WIFCONTINUED to properly set SP_RUNNING process status
+
+2017-02-23  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 40604: configure.ac, Src/watch.c: revert to the old method if
+	getutent() is not available
+
+2017-02-21  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* Julien Nicoulaud: 40586:
+	Functions/VCS_Info/Backends/VCS_INFO_get_data_git: discard stderr
+
+2017-02-20  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Test/ztst.zsh: use "diff -a" in case special characters
+	were written to the test output
+
+	* 40598: Src/subst.c: paramsubst() should always return scalar
+	when PREFORK_SINGLE was passed in from prefork()
+
+	* Martijn Dekker: 40565 (tweaked): test cases for assigning array
+	to scalar with various combinations of SHWORDSPLIT and IFS
+
+2017-02-19  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40593: Src/subst.c: SHWORDSPLIT + unset IFS should cause default
+	splitting of $@ and other array references with (@) or [@]
+
+	* 40576 (tweaked): Src/exec.c: entersubsh(): small improvement to
+	loop that resets trap handlers; unblock any signals that were
+	blocked for trap handling
+
+2017-02-19  Oliver Kiddle  <opk@zsh.org>
+
+	* 40569: Completion/Unix/Command/_gphoto2: update to gphoto2 2.5.11
+
+2017-02-17  Peter Stephenson  <p.stephenson@samsung.com>
+
+	*  Øystein Walle: 40568: REMATCH_PCRE option is not enabled by
+	default.
+
+	* Sebastian: 40558, 40562: Doc/Zsh/mod_db_gdbm.yo,
+	Src/Modules/db_gdbm.c, Src/Modules/db_gdbm.mdd: General
+	improvements to zsh/db/gdbm module.
+
+2017-02-13  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40539: Fabian Klotzl: Completion/Unix/Command/_gcc: typo in MIPS
+	branch of argument selection
+
+	* 40524: Src/Modules/example.c: do not free a null array
+
+2017-02-13  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40537: Doc/Zsh/builtins.yo: document the foregoing.
+
+	* Sebastian: 40536: Src/builtin.c, Src/zsh.h: prepend directory
+	of function autoload with absolute path to fpath if loading a
+	function by relative path.
+
+2017-02-10  Oliver Kiddle  <opk@zsh.org>
+
+	* 40512: Completion/Unix/Command/_entr: new entr completion
+
+	* unposted: Completion/Linux/Command/_lsusb: using a colon in
+	the tag name was not such a good idea
+
+2017-02-09  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40510: Misc/vcs_info-examples, README: vcs_info: Update the
+	$psvar episode with '%'-unescaping. (Follow-up to 40492.)
+
+	* 40492: Doc/Zsh/contrib.yo, Etc/BUGS,
+	Functions/VCS_Info/Backends/VCS_INFO_get_data_hg,
+	Functions/VCS_Info/VCS_INFO_set-patch-format, README: vcs_info:
+	Escape '%' signs in payloads.
+
+2017-02-08  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Completion/Unix/Command/_subversion: _svn: Fix
+	'--show-revs' completion.
+
+	* 40508: Src/Modules/parameter.c, Test/V06parameter.ztst:
+	Make $functions re-settable.
+
+2017-02-07  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Sebastian: 40507: Src/Modules/db_gdbm.c: remove extraneous
+	null byte creating records.
+
+2017-02-07  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40494: Completion/Unix/Command/_git: Use slashes matchspec
+	for references (as already used for branch names).
+
+2017-02-07  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40493/0002: Completion/Unix/Command/_git: _git-checkout:
+	Reorder default completions.
+
+2017-02-07  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40493/0001: Completion/Unix/Command/_git: _git-checkout:
+	No functional change.
+
+2017-02-06  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Jan Matejek: 40434: Completion/Unix/Command/_patchutils:
+	update.
+
+2017-02-04  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Doc/Zsh/contrib.yo,
+	Functions/VCS_Info/Backends/VCS_INFO_get_data_git: vcs_info git:
+	Fix typo in manual.
+
+2017-02-03  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40480: Functions/VCS_Info/Backends/VCS_INFO_get_data_git,
+	Functions/VCS_Info/Backends/VCS_INFO_get_data_hg,
+	Functions/VCS_Info/VCS_INFO_quilt,
+	Functions/VCS_Info/VCS_INFO_set-patch-format: vcs_info
+	set-patch-format: Eliminate the remaining code duplication.
+
+	* 40479: Functions/VCS_Info/Backends/VCS_INFO_get_data_git,
+	Functions/VCS_Info/Backends/VCS_INFO_get_data_hg,
+	Functions/VCS_Info/VCS_INFO_quilt,
+	Functions/VCS_Info/VCS_INFO_set-patch-format: vcs_info
+	set-patch-format: Prepare for more code sharing between the
+	callers.
+
+	* 40481: Functions/VCS_Info/Backends/VCS_INFO_get_data_hg:
+	vcs_info hg: Pass arguments to the set-patch-format hook.
+
+	* 40478: Functions/VCS_Info/VCS_INFO_set-patch-format: vcs_info
+	set-patch-format: Guard against empty variable elision.
+
+	* 40476: Doc/Zsh/contrib.yo, Functions/VCS_Info/VCS_INFO_quilt:
+	vcs_info $backend_misc: Document at the right point, provide
+	in quilt 'standalone' mode.
+
+2017-02-02  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40486: Src/Modules/regex.c, Src/params.c: don't warn on
+	creation of MATCH etc. from regex test as this is implicit.
+
+2017-02-01  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40483 (cf. Eric Freese: 40482): Src/Modules/zpty.c: Remove zpty
+	exit hook from forked processes
+
+2017-02-01  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40460: Src/params.c, Test/E01options.ztst: WARN_NESTED_VAR:
+	Don't warn when assigning to a slice of an existing array
+
+2017-02-01  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 40470: Test/D07multibyte.ztst: make the test work also on
+	OSs which always use ASCII collation (e.g. macOS).
+
+2017-01-31  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40466: Makefile.in: update depencencies so autoheader is run
+	after autoconf when configuration input files change.
+
+2017-01-30  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40469: Src/Zle/complete.c: change strategy from 40453 to use
+	patcompile(PAT_HEAPDUP) instead of signal queueing.
+
+2017-01-30  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40465: Test/C04funcdef.ztst, Test/V06parameter.ztst: use
+	method from cd tests to output current directory.
+
+2017-01-29  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	*  Zhiming Wang: 40450, 40451: Completion/Unix/Command/_swift,
+	Completion/Unix/Command/_openstack: Add new swift language
+	completion and attempt to resolve against openstack swift command.
+
+2017-01-28  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40453: Src/Modules/zpty.c, Src/Modules/zutil.c,
+	Src/Zle/compctl.c, Src/Zle/complete.c, Src/Zle/computil.c,
+	Src/Zle/zle_hist.c, Src/builtin.c, Src/cond.c, Src/glob.c,
+	Src/loop.c, Src/options.c, Src/parse.c: signal handler safety
+	for callers of patcompile(PAT_STATIC), which is not re-entrant.
+
+	* 40439: Src/zsh.h: PAT_HEAPDUP definition just for clarity
+
+2017-01-28  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 40440: ../Doc/Zsh/mod_parameter.yo, Modules/parameter.c,
+	hashtable.c, ../Test/C04funcdef.ztst, ../Test/V06parameter.ztst:
+	Add $functions_source to zsh/parameter to help find where
+	functions where loaded from.
+
+2017-01-27  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Zach Whaley: 40200: Completion/Unix/Command/_perforce: update
+	for latest Perforce versions.
+
+	* 40425: configure.ac, Src/watch.c: HAVE_* tests for getutxent
+	etc.
+
+2017-01-26  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 40423: Src/params.c, Test/E01options.ztst: similar for type
+	conversion the other way.
+
+	* 40422: Src/params.c, Test/E01options.ztst: more
+	WARN_NESTED_VAR cases that were broken in the original patch.
+
+2017-01-25  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40413: Src/params.c, Test/E01options.ztst: no WARN_NESTED_VAR
+	warning on bogus parameter created for subscripted assignment.
+
+2017-01-25  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40403/0004: Functions/VCS_Info/Backends/VCS_INFO_get_data_git,
+	Functions/VCS_Info/Backends/VCS_INFO_get_data_hg,
+	Functions/VCS_Info/VCS_INFO_quilt,
+	Functions/VCS_Info/VCS_INFO_set-patch-format: vcs_info
+	set-patch-format helper: Part #4.
+
+	* 40403/0003: Functions/VCS_Info/Backends/VCS_INFO_get_data_git,
+	Functions/VCS_Info/Backends/VCS_INFO_get_data_hg,
+	Functions/VCS_Info/VCS_INFO_quilt,
+	Functions/VCS_Info/VCS_INFO_set-patch-format: vcs_info
+	set-patch-format helper: Part #3.
+
+	* 40403/0002: Functions/VCS_Info/Backends/VCS_INFO_get_data_git,
+	Functions/VCS_Info/Backends/VCS_INFO_get_data_hg,
+	Functions/VCS_Info/VCS_INFO_quilt,
+	Functions/VCS_Info/VCS_INFO_set-patch-format: vcs_info
+	set-patch-format helper: Part #2.
+
+	* 40403/0001: Functions/VCS_Info/Backends/VCS_INFO_get_data_git,
+	Functions/VCS_Info/Backends/VCS_INFO_get_data_hg,
+	Functions/VCS_Info/VCS_INFO_quilt,
+	Functions/VCS_Info/VCS_INFO_set-patch-format,
+	Functions/VCS_Info/vcs_info: vcs_info set-patch-format helper:
+	Part #1.
+
+	* 40401: Functions/VCS_Info/Backends/VCS_INFO_get_data_git:
+	vcs_info git: Fix the %c patch-format expando.
+
+	* 40396: Doc/Zsh/contrib.yo: vcs_info quilt: More documentation
+	of '%Q' and 'use-quilt'.
+
+	* 40392: Functions/VCS_Info/VCS_INFO_patch2subject: vcs_info
+	patch2subject: Support `git show` output.
+
+2017-01-24  Mikael Magnusson  <mikachu@gmail.com>
+
+	* posted: Test/D07multibyte.ztst: Make D07 recognize more
+	spellings of pl_PL.UTF-8
+
+2017-01-24  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40404: Src/builtin.c: quoting of commands in whence should
+	only apply to whence -v.
+
+2017-01-23  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40391: Completion/compinit, Doc/Zsh/builtins.yo,
+	Doc/Zsh/options.yo, Src/builtin.c, Src/exec.c, Src/options.c,
+	Src/params.c, Src/zsh.h, Test/E01options.ztst: Add
+	WARN_NESTED_VAR option and functions -W to turn it on similarly
+	to functions -T.
+
+2017-01-23  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Etc/BUGS: Record users/20807 vcs_info quilt issue.
+
+2017-01-18  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 43080: Test/C04funcdef.ztst: a few more tests for autoload
+	with absolute path.
+
+	* 40375: Src/builtin.c, Src/subst.c: autoload with explicit path
+	mustn't trash already loaded function.  Also drive-by removal of
+	duplicated duplication in =cmd expansion.
+
+2017-01-17  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* unposted: Completion/Zsh/Command/_typeset: autoload ~... also
+	completes file.
+
+2017-01-16  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40372: Completion/compinit: turn off POSIX_IDENTIFIERS option
+	for completion.
+
+	* 40369: Src/builtin.c: whence -v should probably quote commands
+	since it quotes functions.
+
+	* 40353 (plus improvement to whence -v): Src/exec.c,
+	Src/hashtable.c, Src/signals.c: use directory cache where
+	possible for all functions loaded from directory including
+	fpath.
+
+2017-01-15  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 40362: Src/Zle/computil.c: need duplicated values for setting
+	parameter in compvalues.  Was causing obscure but surprisingly
+	rare crashes in value completion.
+
+2017-01-13  Eric Cook  <llua@gmx.com>
+
+	* earnestly: 40355: Completion/Unix/Command/_mpc: improve
+	playlist completion
+
+2017-01-13  Oliver Kiddle  <opk@zsh.org>
+
+	* 40345: Completion/Linux/Command/_lsusb: update lsusb completion
+
+	* 40344: Completion/Linux/Command/_lsblk: new lsblk completion
+
+2017-01-12  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* unposted: Src/hashtable.c: one missing *name = NULL.
+
+	* 40342: Src/builtin.c, Src/exec.c, Src/hashtable.c,
+	Src/signals.c, Test/C04funcdef.ztst: add directory name cache
+	for directories recorded for autoload files.
+
+2017-01-12  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40335: Src/builtin.c, Src/exec.c, Src/zsh.h,
+	Test/C04funcdef.ztst: be more careful autoload filename is
+	directory, not source location.
+
+	* Jens Elkner: 40333: Src/watch.c: Fix the utmpx interface for
+	watch as otherwise it failed on some OSes.
+
+2017-01-11  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 40332: Completion/Zsh/Command/_typeset: completion for new
+	autoload features.
+
+2017-01-11  Oliver Kiddle  <opk@zsh.org>
+
+	* 40321: Doc/Zsh/compsys.yo, Src/Zle/computil.c,
+	Test/Y03arguments.ztst: _arguments option groups
+
+2017-01-11  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* unposted: Src/builtin.c, Src/exec.c: be more careful to free
+	memory when updating filename in struct shfunc.
+
+	* 40327 (with minor fixes): Doc/Zsh/builtins.yo,
+	README,Src/builtin.c, Src/exec.c, Src/hashtable.c, Src/parse.c,
+	Src/zsh.h, Test/C04funcdef.ztst: add ability to autoload
+	function from file using full path, with additional related
+	autoload options -r, -R, -d and extension to -X.
+
+2017-01-10  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 40305: Src/Zle/complist.c, Src/Zle/zle_main.c,
+	Src/Zle/zle_refresh.c, Src/Zle/zle_thingy.c: fix some problems
+	redisplaying command line after interrupts.
+
+	* 40306 with documentation additions: Doc/Zsh/options.yo,
+	README, Src/input.c, Src/options.c, Src/parse.c, Src/zsh.h,
+	Test/A02alias.ztst: Add ALIAS_FUNC_DEF option and make
+	the default behaviour to disallow functions where the
+	name is expanded as an alias (unless part of a complete
+	function definition within the alias).
+
+2017-01-10  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40303: Completion/Debian/Command/_bts: Add more subcommands.
+
+2017-01-10  Phil Pennock  <zsh-workers+phil.pennock@spodhuis.org>
+
+	* 40318 (in part): Doc/Zsh/builtins.yo: Document echo \c
+	behaviour.
+
+2017-01-10  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40302: Completion/Unix/Command/_swaks: New _swaks completion
+	(common options only).
+
+2017-01-08  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: LICENCE: Update year to $now (2017).
+
+2017-01-06  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* m0viefreak: 40285: Src/Zle/zle_hist.c: more care needed
+	managing patterns in history isearch if there are hooks
+	around.
+
+2017-01-05  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40270 (after 39995): Src/params.c: Add cross-reference for
+	robustness.
+
+	* 40264: Completion/Unix/Command/_man: Complete all sections
+	after '-a'.
+
+	* users/22320: Etc/BUGS: Add 40240, label 40106.
+
+2017-01-05  Oliver Kiddle  <opk@zsh.org>
+
+	* 40269: Src/Zle/computil.c, Test/Y03arguments.ztst:
+	handle option exclusion within current word for clumped options
+
+2017-01-04  Oliver Kiddle  <opk@zsh.org>
+
+	* 40227: Src/Zle/computil.c, Test/Y03arguments.ztst: new approach
+	to 39611 (_arguments sets and rest arguments starting with a dash)
+
+	* 40226: Src/Zle/computil.c, Test/Y03arguments.ztst:
+	tidy up some of the _arguments set code
+
+2017-01-03  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40265: Src/pattern.c: fix continuing problems with Meta characters
+	in pattern that resolves to a string by copying source string.
+	Triggered by string that (correctly) turned Dash into '-'.
+
+	* Paulo Andrade: 40260: Src/prompt.c: Set newly allocated
+	space in prompt buffer to zero as it may be tested.
+
+2017-01-01  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* users/22319: Src/subst.c: ${ary1:^ary2} should not change
+	the isarr state of the expansion of ary1 unless ary1 is made
+	from a scalar, lest semantics of (@) in double quotes be lost.
+
+2016-12-30  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40248: Src/hist.c: suppress errors from zshaddhistoryhook,
+	and do not call it if no history entry will be written
+
+2016-12-28  Sebastian Gniazdowski  <psprint@fastmail.com>
+
+	* 40231: Src/params.c: Optimise setarrvalue().
+
+2016-12-28  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40232: configure.ac: Remove SH_USE_BSD_ECHO autoconf test.
+
+2016-12-27  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted (see users/22287): Completion/compinit: _comp_options
+	needs to disable ERR_RETURN along with ERR_EXIT
+
+2016-12-24  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* Oliver: 40118: Functions/Zle/bracketed-paste-magic: relocate
+	BUFFER/CURSOR reset to work around "fc -p" issue (alternate fix
+	replacing 40115).
+
+	* unposted: Functions/Zle/bracketed-paste-magic: revert 40115,
+	thus restoring 38579.
+
+2016-12-22  Oliver Kiddle  <opk@zsh.org>
+
+	* 40162: Src/Zle/computil.c, Test/Y03arguments.ztst: _arguments
+	support for a match spec in combination with sets
+
+2016-12-22  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Config/version.mk: Post-release version bump to
+	5.3.1-dev-0.
+
+2016-12-21  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* unposted: Config/version.mk, Etc/FAQ.yo, NEWS, README:
+	release 5.3.1:
+
+2016-12-18  Baptiste Daroussin  <bapt@FreeBSD.org>
+
+	* 40210: Completion/Unix/Command/_sysctl: Add support modern
+	FreeBSD and drop support for FreeBSD < 5
+
+2016-12-18  Baptiste Daroussin  <bapt@gandi.net>
+
+	* 40209: Completion/BSD/Command/_chflags: Fix typo in chflags
+	completion
+
+2016-12-16  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40149: Functions/VCS_Info/Backends/VCS_INFO_get_data_git:
+	vcs_info git: Avoid a fork.
+
+	* 40203: Test/B03print.ztst: Resolve 'printf --' question from
+	grandparent commit (thanks Chet).
+
+2016-12-15  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40198: Test/V10private.ztst: output of B02typeset differs when
+	(( UID == 0 )) so do not attempt to redo that test in that case
+
+2016-12-14  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40179: Src/builtin.c: fix handling of "printf -" and "printf --"
+
+	* unposted: Test/B03print.ztst: regression for 40179 / 37467
+
+2016-12-12  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* unposted: Config/version.mk: update to 5.3-dev-0 to avoid
+	clash with installed 5.3.
+
+2016-12-10  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40134: Src/builtin.c, Src/Zle/compmatch.c: silence spurious
+	compiler warnings.
+
+2016-12-10  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40139: Test/Y03arguments.ztst: tests: Convert comments to
+	diagnostics; test backslashes. (after 40129)
+
+	* users/22182: Completion/Zsh/Context/_brace_parameter: Propagate
+	22161 docs patch to completion descriptions.
+
+2016-12-09  Oliver Kiddle  <opk@zsh.org>
+
+	* 40137: Doc/Zsh/compsys.yo: document _external_pwds
+
+2016-12-09  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* unposted: Config/version.mk: 5.2-test-3.
+
+	* users/22161: Doc/Zsh/expn.yo: the (E) parameter flag is one
+	after every other use of indexing.
+
+2016-12-09  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40117: Doc/Zsh/compsys.yo, Src/input.c: Revert a hunk of
+	40035 that changed semantics incorrectly.
+
+2016-12-09  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* unposted: Test/D07multibyte.ztst: minor typos.
+
+	* 40138: Test/D07multibyte.ztst: Put the regex test last as it
+	has an additional dependency, and note this in a failure
+	message.
+
+2016-12-09  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40126: Functions/Misc/zed: zed (fned): When editing a new
+	function. escape its name.
+
+2016-12-08  Oliver Kiddle  <opk@zsh.org>
+
+	* 40130: NEWS: mention some changes since 5.2
+
+	* 40129: Src/Zle/computil.c, Test/Y03arguments.ztst:
+	revert 39611, add code comments and test cases for _arguments
+
+	* c.f. 40119: Src/zsh.h: correct typo in comment
+
+	* 40114: Completion/Zsh/Context/_brace_parameter: complete
+	:/ with other parameter operators
+
+2016-12-06  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* Zhiming Wang: 40115: Functions/Zle/bracketed-paste-magic:
+	revert 38579 due to bug restoring BUFFER after history search
+
+	* 40110: Test/B02typeset.ztst: ignore strerror text in test
+	output, it differs by OS
+
+	* 40110: Test/C02cond.ztst: discard stderr when looking for
+	a path to the "mount" command, to avoid spurious test failure
+
+2016-12-06  Mikael Magnusson  <mikachu@gmail.com>
+
+	* 40107: Completion/Linux/Command/_fusermount,
+	Completion/Unix/Command/_mount: fix quoting
+
+2016-12-06  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* unposted: Config/version.mk: 5.2-test-2.
+
+2016-12-05  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40102: Test/ztst.zsh, Test/C02cond.ztst: create test IO
+	files in newly created temporary directory.
+
+	* 40100: Test/C03traps.ztst: Check ERR_EXIT and ERR_RETURN on
+	anonymous functions: should exit on function return even if
+	suppressed internally.
+
+	* 40097: Src/exec.c, Test/C03traps.ztst: Don't trigger ERR_EXIT
+	or ERR_RETURN on non-zero status after current shell group, either.
+
+	* 40096: Src/exec.c, Src/loop.c, Src/zsh.h, Test/C03traps.ztst:
+	Don't trigger ERR_EXIT or ERR_RETURN on non-zero status after
+	shell construct.
+
+2016-12-04  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Functions/VCS_Info/Backends/VCS_INFO_get_data_git:
+	vcs_info git: Handle rebase-apply sequences with >=10000 patches.
+
+	* 40074: Functions/VCS_Info/Backends/VCS_INFO_get_data_git:
+	vcs_info git: rfc822-unfold rebase-apply patch subjects when
+	msg-clean is unavailable.
+
+2016-12-04  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 40088: Test/D04parameter.ztst: attempt to make output from
+	interactive test more robust.
+
+2016-12-03  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Test/D04parameter.ztst: regression test for 40071
+
+	* 40071: Src/subst.c: change Dash back to "-" before evaluating
+	named directory expansions
+
+2016-12-03  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40068: Src/params.c, Test/B02typeset.ztst: Abort execution
+	when setuid/setgid fail.
+
+	* 40067: Src/Zle/comp.h, Src/Zle/complete.c, Src/params.c:
+	internal: Document 'cmatcher', parse_cmatcher(), 'comptoend',
+	unsetparam_pm(), and getindex().
+
+2016-12-03  Daniel Hahler  <zsh@thequod.de>
+
+	*  40029: Functions/VCS_Info/Backends/VCS_INFO_get_data_git:
+	Get subject of current patch in rebase-apply mode
+
+2016-12-02  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* unposted: Config/version.mk: 5.2-test-1.
+
+	* 40066: Etc/FAQ.yo, README: updates for 5.3 release.
+
+2016-12-01  Oliver Kiddle  <opk@zsh.org>
+
+	* 40055: Completion/Unix/Command/_git: update for git 2.11.0
+
+	* 40053: Completion/Base/Completer/_external_pwds: exclude
+	current directory of active zsh from completion matches
+
+2016-12-01  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Functions/VCS_Info/VCS_INFO_patch2subject,
+	Functions/VCS_Info/VCS_INFO_quilt, Functions/VCS_Info/vcs_info:
+	vcs_info: Break out VCS_INFO_quilt-patch2subject into
+	VCS_INFO_patch2subject
+
+2016-12-01  Mikael Magnusson  <mikachu@gmail.com>
+
+	* 40024: Completion/Unix/Type/_path_files: Update _path_files
+	since quoting requirements changed in 39412, this case only
+	triggers when accept-exact-dirs is set.
+
+2016-12-01  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 40050: Src/compat.c: prepend /**/ to global functions and #ifdefs
+
+2016-11-30  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40049: NEWS: notes on Unicode 9 (40037).
+
+	* 40043: Src/Zle/computil.c: fix indentation (recent gcc warns
+	about this).
+
+2016-11-30  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40036: Completion/Debian/Command/_bts: Complete removal
+	syntaxes correctly.
+
+2016-11-29  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 40034: Src/subst.c: clear badcshglob when ignoring errors
+
+	* unposted: README: example describing 40032
+
+	* 40032: Src/params.c: consistency in handling of subscript
+	slices outside the bounds of an array parameter
+
+2016-11-29  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 40037: Joshua Rubin: Unicode 9 character width support.
+	Needs enabling with --enable-unicode9.
+
+	* 40035: Eitan Adler: Cosmetic fixes mostly for duplication in
+	comments and documentation.
+
+	* 40026: Src/Zle/zle_tricky.c: More care with redirection
+	completion.  Fixes for completion after > in "!> ." that
+	should add to sanity.
+
+2016-11-29  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Completion/Zsh/Context/_subscript: Complete the
+	$foo[(f)] flag.
+
+	* unposted: Completion/Unix/Command/_git: _git-config: Fix
+	user.email completion to complete only bare email addresses.
+
+2016-11-28  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 40011: Completion/Base/Core/_main_complete,
+	Completion/Solaris/Command/_pfexec,
+	Completion/Solaris/Command/_zlogin,
+	Completion/Unix/Command/_chown, Completion/Unix/Command/_doas,
+	Completion/Unix/Command/_sudo: Make $_comp_priv_prefix only
+	declared when required, and use that to have chgrp offer all
+	groups under doas, ssh, etc as well.
+
+	* 40010: Src/builtin.c, Test/A02alias.ztst: builtins: Say
+	'bad option: +x', not 'bad option: -x', when +x was passed.
+
+	* 40009: Src/hashtable.c: alias -L: Emit aliases that begin
+	with a plus sign correctly.
+
+2016-11-24  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 40013: Completion/Unix/Type/_date_formats,
+	Completion/Zsh/Command/_strftime: process zsh-specific time
+	format extensions correctly
+
+2016-11-25  Oliver Kiddle  <opk@zsh.org>
+
+	* unposted: Functions/Zle/surround: set new vichange flag
+
+	* 40020: Completion/Unix/Command/_cscope: new completion
+
+2016-11-25  Laurent Arnoud  <laurent@spkdev.net>
+
+	* 40018 + 40019: Completion/Debian/Command/_apt: Add missing
+	commands to Debian apt completion
+
+2016-11-24  Oliver Kiddle  <opk@zsh.org>
+
+	* unposted: Completion/Unix/Command/_date: fix typo (=+ to +=)
+
+	* 40004 (tweaked): Completion/Redhat/Command/_yum: fix cache
+	mechanism, complete groups and other minor improvements
+
+	* unposted: Src/Zle/zle_params.c: fix to compile on Solaris where
+	curses.h has a #define for reg to register
+
+	* 40003: Src/Zle/zle_params.c, Doc/Zsh/zle.yo: include "0-"9
+	vi buffers in the registers associative array
+
+2016-11-24  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 40005: Completion/Unix/Command/_date,
+	Completion/Unix/Type/_date_formats: call _date_formats from _date.
+	Update both files for darwin etc.
+
+2016-11-24  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39982: README, Src/utils.c: $SPROMPT: Don't accept a spelling
+	correction at space/tab.
+
+2016-11-23  Bart Schaefer  <schaefer@zsh.org>
+
+	* unposted: Src/Zle/zle_main.c: clear ERRFLAG_ERROR before
+	invoking immortal widget (cf. 39934)
+
+2016-11-23  Oliver Kiddle  <opk@zsh.org>
+
+	* unposted: Test/Y03arguments.ztst: partial match test case
+
+	* unposted: Src/builtin.c, Test/B03print.ztst: fix printf -v
+	to an array without format string reuse
+
+2016-11-21  Oliver Kiddle  <opk@zsh.org>
+
+	* 39993: Test/Y01completion.ztst: Tests for 39981.
+
+2016-11-21  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39981: Src/Zle/compresult.c: Make compstate[to_end] skip
+	suffix and hidden suffix, but not ignored suffix.
+
+2016-11-20  Oliver Kiddle  <opk@zsh.org>
+
+	* 39986, 39989: Src/Zle/zle.h, Src/Zle/zle_keymap.c,
+	Src/Zle/zle_main.c, Src/Zle/zle_misc.c, Src/Zle/zle_thingy.c,
+	Src/Zle/zle_utils.c, Src/Zle/zle_vi.c, Test/X02zlevi.ztst,
+	Doc/Zsh/zle.yo, Functions/Zle/vi-pipe: make vi-repeat-change
+	work better with shell based widgets and save old change
+	when recording a new change in case the new one fails
+
+	* 39974: Completion/Unix/Command/_ssh: complete shared
+	libraries for -e and -s options to ssh-add
+
+2016-11-20  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* Guillaume Maudoux: 39900 (doc slightly tweaked): Src/params.c,
+	Doc/Zsh/params.yo: Add TERMINFO_DIRS special colon-separated
+	array, not tied.
+
+	* 39995 (from 39977): Src/params.c, Test/A06assign.ztst:
+	optimisation of string assignment if length is unchanged.
+
+	* Mikel Ward: 39978: Doc/Zsh/jobs.yo: more accurate
+	documentation of searching for job by string.
+
+2016-11-20  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* Sebastian: 39992: Src/params.c: setarrvalue: Allocate
+	a correctly-sized array.
+
+	* unposted (after 39952): Src/Zle/zle_params.c: Restore C89
+	compatibility.
+
+	* 39985: Doc/Zsh/params.yo: Document the [nyae] answers to the
+	spelling correction prompt, $SPROMPT.
+
+2016-11-18  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39916 + 39930 + tweak: Completion/Unix/Command/_git: Complete
+	options and values for -c.
+
+2016-11-17  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 39958: Src/Zle/compctl.c, Src/builtin.c, Src/compat.c,
+	Src/exec.c, Src/glob.c, Src/hist.c, Src/utils.c: Add spare byte
+	to PATH_MAX allocation to allow for possible null.
+
+2016-11-17  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39921: Completion/Unix/Command/_git: __git_recent_branches:
+	Remove erroneous parsing of partial ref names as tags.
+
+	* 39922: Completion/Unix/Command/_git: __git_recent_branches:
+	Fix an 'assertion' failure when two branches (refs) point to
+	the same commit.
+
+	* 39917: Completion/Zsh/Command/_precommand: Complete setsid(1)
+	as a precommand
+
+	* 39915: Src/exec.c, Test/E01options.ztst: whence: Honor
+	PATH_DIRS option for arguments that start with './' or '../'.
+
+2016-11-17  Oliver Kiddle  <opk@zsh.org>
+
+	* 39962: Src/Zle/zle_keymap.c: bind vi case conversion widgets
+	u/U in visual mode and g~ from normal mode
+
+	* 39959: Src/Zle/zle_vi.c, Test/X02zlevi.ztst: when repeating
+	vi changes advance through the numbered killring registers.
+	Also fix numeric arguments with vi-repeat-change.
+
+	* 39952: Src/Zle/zle_params.c, Doc/Zsh/zle.yo: add registers
+	special parameter to provide access to the vi register
+	buffers from a zle widget function
+
+2016-11-16  Oliver Kiddle  <opk@zsh.org>
+
+	* 39945: Src/Zle/compcore.c, Completion/Base/Core/_message,
+	Test/Y03arguments.ztst: allow further tab presses to move on
+	to menu completion even when compstate[insert] is emptied
+
+2016-11-15  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39949: Src/subst.c, Src/utils.c, Test/B09hash.ztst: "-" is
+	allowed in named directories, so needs a special case when
+	sh-tokenized for possible ranges.
+
+	* 39947: Test/D04parameter.ztst: Test out-of-rantge multiple
+	array subscripts with and without (@).
+
+2016-11-14  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39943: Src/utils.c: no need to compute arrlen() in arrdup_max()
+	when max == 0.
+
+2016-11-15  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 39937: Src/params.c: fix a problem introduced by 39886.
+	$a[i,j] should become an empty array if i>j.
+
+2016-11-14  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39906: Src/utils.c: More optimisation of multibyte handling
+	for US-ASCII along existing lines.
+
+2016-11-13  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Src/Zle/zle_utils.c: comment in zlecallhook() points
+	to redrawhook() just in case
+
+	* 39934: Src/Zle/zle_main.c: if a widget execution fails, try to
+	execute a corresponding immortal widget instead.
+
+	* 39933: Src/Zle/zle_main.c: more of zlecallhook() in redrawhook(),
+	add commentary on some of the differences
+
+	* 39929: Doc/Zsh/zle.yo: clarify execute{,-last}-named-cmd
+
+2016-11-12  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Etc/zsh-development-guide: add list of standard
+	module hooks with corresponding macro names
+
+2016-11-12  Oliver Kiddle  <opk@zsh.org>
+
+	* Paul Seyfert: 39920: Completion/Redhat/Command/_yum:
+	extend yum completion to cover more yum commands
+
+2016-11-10  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39893: Src/params.c: use arrdup_max() to show explicitly the
+	difference in two code branches; no functional change
+
+2016-11-11  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39874/0002 plus size=0 handling: Src/mem.c: zshcalloc: Remove
+	code duplication. No functional change.
+
+	* 39874/0001: Src/params.c: setarrvalue: Remove needless
+	initialization.
+
+2016-11-10  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39901: Src/exec.c, Test/C03traps.ztst: Exiting the left hand
+	side of a pipeline shouldn't trigger EXIT trap.
+
+2016-11-09  Oliver Kiddle  <opk@zsh.org>
+
+	* 39890: Completion/Debian/Command/_git-buildpackage,
+	Completion/Debian/Command/_lintian,
+	Completion/Debian/Command/_make-kpkg,
+	Completion/Linux/Command/_iwconfig,
+	Completion/Debian/Command/_dcut, Completion/Linux/Command/_mdadm,
+	Completion/Solaris/Command/_dladm, Completion/Unix/Command/_adb,
+	Completion/Unix/Command/_attr, Completion/Unix/Command/_awk,
+	Completion/Unix/Command/_bzr, Completion/Unix/Command/_cdrdao,
+	Completion/Unix/Command/_devtodo, Completion/Unix/Command/_feh,
+	Completion/Unix/Command/_global, Completion/Unix/Command/_groff,
+	Completion/Unix/Command/_growisofs,
+	Completion/Unix/Command/_initctl, Completion/Unix/Command/_iostat,
+	Completion/Unix/Command/_ip, Completion/Unix/Command/_java,
+	Completion/Unix/Command/_localedef, Completion/Unix/Command/_make,
+	Completion/Unix/Command/_mencal, Completion/Unix/Command/_module,
+	Completion/Unix/Command/_nkf, Completion/Unix/Command/_nmap,
+	Completion/Unix/Command/_pbm, Completion/Unix/Command/_rar,
+	Completion/Unix/Command/_sisu, Completion/Unix/Command/_sqsh,
+	Completion/Unix/Command/_surfraw, Completion/Unix/Command/_units,
+	Completion/Unix/Command/_wiggle: fix typos and spellings
+
+	* 39888: Completion/Unix/Command/_tmux: update for tmux 2.3
+
+	* 39884: Functions/Zle/history-beginning-search-menu:
+	fix for history lines containing a pipe character
+
+2016-11-09  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39882: Doc/Zsh/expn.yo: mention ${name:/pattern/replacement}
+	in the itemized synopsis with the related forms; clarify doc.
+
+2016-11-09  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39887: Src/params.c: no need to conditionalise assignment of
+	strlen in getstrvalue().
+
+	* 39886 based on 39877 (Sebastian): Src/params.c, Src/utils.c:
+	add arrdup_max() so as not to duplicate entire arrays unnecessarily.
+
+	* Sebastian: 39875: Src/params.c, Src/string.c: add
+	dupstring_glen() to avoid redundant strlen() calls.
+
+2016-11-09  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39853: Completion/Unix/Command/_subversion: Accept long
+	options in the '--foo=bar' syntax.
+
+2016-11-08  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Sebastian: 39871: cut down number of strlen()s in getstrvalue().
+
+2016-11-08  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 39857: Completion/Unix/Command/_top: add support for darwin,
+	with improvements on linux etc.
+
+2016-11-08  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39870: Src/builtin.c, Test/C03traps.ztst: ensure exit trap can
+	always run.
+
+	* unposted: Src/utils.c: follow up to 39867: don't need test
+	against zero any more.
+
+	* Sebastian: 39869: can transfer ownership of core of array when
+	assigning.
+
+	* 39867: Src/utils.c: cast to unsigned char to compare against
+	0x7f.
+
+2016-11-07  Peter Stephenson  <p.stephenson@samsung.com>
+
+	*  Felix Neumärker: Completion/Unix/Command/_module: follow
+	symbolic links and use _multi_parts for paths.
+
+	* zsh-users/22083: Doc/Zsh/expn.yo: attempt to explain a bit
+	better what a "word" is in parameter substitution.
+
+2016-11-07  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39840: Completion/Unix/Command/_cpio: Declare variable's
+	type correctly.
+
+2016-11-07  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 39728: Src/zsh_system.h, Src/Modules/datetime.c: define
+	_GNU_SOURCE on Cygwin
+
+	* 39855: Completion/BSD/Command/_jot: new completion
+
+2016-11-07  Frank Terbeck  <ft@bewatermyfriend.org>
+
+	* 39849: Completion/Unix/Command/_tmux: _tmux: Remove
+	set-remain-on-exit
+
+2016-11-06  Frank Terbeck  <ft@bewatermyfriend.org>
+
+	* 39847: Util/check-tmux-state: Update helper function name
+	pattern
+
+	* 39848: Completion/Unix/Command/_tmux: _tmux: Update options to
+	latest tmux
+
+2016-11-05  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Completion/compaudit: tweak replacement getent
+	to handle /etc/hosts format.
+
+	* Thomas Bereknyei, Mic92: 39831: Completion/compaudit: find
+	"getent" by the usual command search, not just /usr/bin/.
+
+	* unposted: Test/A05execution.ztst:  tests for 39839, 39844
+
+	* 39844: Src/loop.c: fix "while" condition as per 39839
+
+	* 39839: Src/loop.c: fix "return" from "if" condition
+
+	* 39838: Src/builtin.c: another missing unqueue_signals()
+
+2016-11-04  Oliver Kiddle  <opk@zsh.org>
+
+	* 39829: Src/Zle/compcore.c: when compstate[insert] is emptied
+	by the completion function and AUTO_MENU is set, start menu
+	completion on the second tab press rather than the third
+
+	* 39834: Src/builtin.c, Completion/Zsh/Command/_print,
+	Completion/Zsh/Command/_fc, Completion/Zsh/Type/_ps1234:
+	when print used with -v and -l, include a final
+	newline; complete print -S option and make further use
+	of the new printf to an array feature
+
+	* 39389: Src/builtin.c, Test/B03print.ztst, Doc/Zsh/builtins.yo,
+	Completion/Zsh/Type/_globquals, Completion/Zsh/Command/_print:
+	when printf -v is used with an array use separate elements each
+	time the format is reused
+
+2016-11-04  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* users/22080: Doc/Zsh/zle.yo: bracketed-paste: Third time's
+	a charm.
+
+2016-11-04  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Sebastian: 39825: Src/utils.c: Optimise mb_metastrlenend() for
+	the common case of 7-bit character.
+
+2016-11-04  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 39818 (Oliver Kiddle): Completion/Unix/Command/_awk: add nawk
+
+2016-11-03  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39824: Test/X02zlevi.ztst: test case for 39811
+
+2016-11-03  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* unposted: Src/Zle/zle_keymap.c: comment explaining putting
+	back tail end of key buffer into input.
+
+2016-11-03  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39806: Src/Zle/zle_vi.c: internal: Document zle's vichgbuf.
+
+	* 39805: Src/Zle/comp.h, Src/Zle/compcore.c: internal: Document
+	some compadd internals.
+
+	* 39804: Src/Zle/comp.h, Src/Zle/complete.c: internal: Document
+	matchspec flags.
+
+	* 39803: Src/Zle/complete.c: internal: Document bin_compadd().
+
+	* 39802: Src/Zle/compcore.c, Src/Zle/complete.c: internal:
+	Document and simplify multiquote().
+
+	* users/22063: Doc/Zsh/zle.yo: bracketed-paste: Document ability
+	to specify a vi register. (after users/22036)
+
+2016-11-03  Daniel Hahler  <zsh@thequod.de>
+
+	* 39822: Completion/Unix/Command/_git: remove "-A '-*'" with
+	_arguments for some commands.
+
+2016-11-03  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Completion/Unix/Type/_remote_files: fix typo that
+	was causing handling of "--" argument to fail
+
+	* 39820: Src/Zle/zle_vi.c: vi-repeat handles multi-key bindings
+
+2016-11-03  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39815: Src/lex.c, Test/D04parameter.ztst: read input to end
+	on parse error in $(...) inside a string.
+
+2016-11-02  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39813: Src/Zle/zle_keymap.c: fix keybuflen after ungetting
+	extra keys from the end of keybuf in getkeymapcmd().
+
+	* 39811: Src/Zle/zle_vi.c: vi-repeat-change must not be the
+	"motion" of vi-change, lest it infinitely repeat itself.
+
+2016-11-01  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 39731: Completion/Unix/Command/_awk: add support for gawk
+	ver.3 and 4
+
+2016-11-01  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39787: Test/Y01completion.ztst: Add a regression test for
+	39412.
+
+2016-10-31  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39797: Completion/Unix/Type/_canonical_paths: obsolete the
+	helper functions fixed by the previous two changes, replacing
+	them with the new ${var:P} modifier.
+
+	* 39795: Completion/Unix/Type/_canonical_paths: preserve "cd -"
+	behavior around directory shuffling in _canonical_paths_pwd
+
+	* 39792: Completion/Unix/Type/_canonical_paths: improve handling
+	of relative paths in _canonical_paths_add_paths
+
+2016-10-31  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39786: Functions/VCS_Info/Backends/VCS_INFO_get_data_hg,
+	Functions/VCS_Info/VCS_INFO_hexdump, Functions/VCS_Info/vcs_info:
+	vcs_info (hg): Use native hexdump implementation instead of
+	external command.
+
+2016-10-30  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 39777: Src/parse.c, Test/D08cmdsubst.ztst: $() is a valid
+	empty command substitution.
+
+2016-10-29  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39758: Src/builtin.c, Src/params.c: revise 39704 to output
+	"typeset" for array and hash parameters, even when exported; for
+	POSIXBUILTINS, "export var" does not implicitly set $var, and its
+	export state is preserved when assigned (but not when explicitly
+	unset).
+
+2016-10-28  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* users/22036: Doc/Zsh/zle.yo: bracketed-paste: Document
+	cutbuffer behaviour; clarify.
+
+2016-10-27  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Functions/Zle/bracketed-paste-magic: handle empty
+	pastes and "zle bracketed-paste variablename" form.
+
+2016-10-27  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Doc/Zsh/compsys.yo, Doc/Zsh/contrib.yo: Minor
+	documentation fixes (markup, grammar, etc).
+
+	* 39739: Completion/Unix/Command/_head,
+	Completion/Unix/Command/_tail: Complete negative integers for
+	-n/-c in the GNU variant.  (after 39479)
+
+2016-10-25  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39725: Src/Zle/complist.c: more metafication of patterns.
+
+2016-10-25  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39723: Src/Zle/complist.c, Src/glob.c: metafy string to be
+	used in pattern for complist.  Also skip metafied characters
+	when tokenizing (typically this has no effect, however).
+
+2016-10-24  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: NEWS, README: update for 39704.
+	
+	* 39704: Src/params.c, Test/B02typeset.ztst, Test/B03print.ztst,
+	Test/V10private.ztst: the output of "typeset -p" uses "export"
+	commands or the "-g" option for parameters that are not local to
+	the current scope.
+
+2016-10-24  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39706: Completion/Unix/Type/_tilde_files, Doc/Zsh/compsys.yo:
+	_tilde_files: Offer named directories. Document.
+
+	* 39708: Completion/Unix/Type/_email_addresses,
+	Doc/Zsh/compsys.yo: Document _email_addresses.
+
+	* 39707: Doc/Zsh/compsys.yo: Document _dir_list.
+
+2016-10-23  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 39715: Src/Zle/complist.c: handle multibyte characters when
+	highlighting completion listing.
+
+2016-10-22  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39710 (cf. Alex George: 39709): Completion/Base/Completer/_expand,
+	Completion/Base/Completer/_user_expand: handle the %o format in the
+	"all-expansions" tag; allow a single unique match to appear in the
+	"expansions" tag (cf. users/21955).
+
+2016-10-20  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39688: Src/pattern.c: Alternative fix: next pointer is
+	irrelevant anyway as there's only one P_EXACTLY in a P_PURES.
+
+	* 39683: Src/pattern.c: Update scan pointer in pattern after
+	possible reallocation for meta handling.
+
+2016-10-19  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39680: Src/mem.c: correctly handle case of popping last arena
+
+2016-10-19  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39678: Src/builtin.c, Test/D07multibyte.ztst: printf %q
+	argument needs metafying and result unmetafying.
+
+2016-10-18  m0viefreak  <m0viefreak.cm@googlemail.com>
+
+	* 39590: Src/Zle/compresult.c: zle: Call zle-line-pre-redraw
+	after inserting a completion result
+
+2016-10-18  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39654: Completion/Unix/Command/_make: (based on patch by
+	Baptiste Daroussin) Better support bmake, FreeBSD's default
+	make(1) implementation.
+
+	* 39657: Completion/Zsh/Command/_zstyle: Complete the -g,
+	-s,-b,-a, -t,-T, -m options.
+
+	* 39657: Completion/Zsh/Command/_zstyle: When completing a
+	style for an unrecognised context, complete all known styles.
+
+	* 39657: Completion/Zsh/Command/_zstyle,
+	Completion/Zsh/Type/_completers, Doc/Zsh/compsys.yo: Make a
+	helper function global.
+
+2016-10-16  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39500: Src/Zle/computil.c: compdescribe: perform
+	on-screen-width computation using the "nice" length, since the
+	matches are rendered by nice*() functions by compadd
+
+2016-10-13  Oliver Kiddle  <opk@zsh.org>
+
+	* 39624: Completion/Unix/Command/_yafc: remove space before
+	compdef that prevents function being used plus other tweaks
+
+	* 39623: Completion/X/Command/_evince: new completion
+
+2016-10-13  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39625: Martijn Dekker: Src/loop.c, Test/A01grammar.ztst: case
+	needs to reset lastval if no pattern matched.
+
+2016-10-13  Oliver Kiddle  <opk@zsh.org>
+
+	* 39622: Test/Y03arguments.ztst: add test cases for recent
+	_arguments fixes, exclusion lists and -A/-S
+
+	* 39611: Src/Zle/computil.c: with _arguments sets completion
+	stopped if one of the rest arguments starts with a dash
+
+	* 39533: Matthew Martin: Completion/Unix/Command/_doas:
+	add the new -L option, sort options and add some exclusions
+
+2016-10-11  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 36108: Src/builtin.c: command -[vV] assumed -p.
+
+2016-10-10  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39599: Src/loop.c, Test/A01grammar.ztst: Don't reset status
+	before "case" execution.
+
+2016-10-07  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39522: Doc/Zsh/contrib.yo: add-zsh-hook/add-zle-hook-widget:
+	Promise append semantics.
+
+	* 39489: Completion/Unix/Type/_path_files: Interpret -P as
+	literally, rather than as a pattern.
+
+	* 39479 + 39481 minus _path_files hunk (see
+	39489): Completion/Debian/Command/_debfoster,
+	Completion/Linux/Command/_modutils,
+	Completion/Redhat/Command/_rpm, Completion/Unix/Command/_ant,
+	Completion/Unix/Command/_cpio, Completion/Unix/Command/_cvs,
+	Completion/Unix/Command/_dbus, Completion/Unix/Command/_git,
+	Completion/Unix/Command/_graphicsmagick,
+	Completion/Unix/Command/_growisofs, Completion/Unix/Command/_gs,
+	Completion/Unix/Command/_gsettings,
+	Completion/Unix/Command/_head,
+	Completion/Unix/Command/_imagemagick,
+	Completion/Unix/Command/_java, Completion/Unix/Command/_lp,
+	Completion/Unix/Command/_lzop, Completion/Unix/Command/_mount,
+	Completion/Unix/Command/_perl, Completion/Unix/Command/_php,
+	Completion/Unix/Command/_rlogin, Completion/Unix/Command/_ssh,
+	Completion/Unix/Command/_tail, Completion/Unix/Command/_w3m,
+	Completion/X/Command/_rdesktop, Completion/X/Command/_x_utils,
+	Completion/Zsh/Command/_fc, Completion/Zsh/Type/_arrays,
+	Completion/Zsh/Type/_globflags, Completion/Zsh/Type/_ps1234,
+	Completion/Zsh/Type/_vars: Completion: audit 'compset -P'
+	calls to use shortest match where applicable, plus random
+	drive-by tweaks.
+
+2016-10-06  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Julien Cretin: 39579: Src/subst.c: position of \0
+	with optional quoting in parameter substitution was wrongly
+	calculated.
+
+	* 39578: Test/C03traps.ztst: Test some more ERR_RETURN cases
+	involving "&&" and functions.
+
+2016-10-05  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39571: Src/exec.c, Test/C03traps.ztst: "&&" inside a shell
+	function could mess up ERR_EXIT outside.
+
+	* 39568: Src/exec.c, Test/C03traps.ztst: "! <complex-command>"
+	should suppress ERR_EXIT inside the complex command.
+
+	* 39566: README, Doc/Zsh/exec.yo, Src/exec.c,
+	Test/C04funcdef.ztst: change the behaviour of
+	command_not_found_handler to make it easier to handle a non-zero
+	return status naturally.
+
+2016-10-04  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39561: Src/mem.c: missing unqueue_signals() when ZSH_HEAP_DEBUG
+
+2016-10-03  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39550: Src/Zle/zle_main.c: reset signal queue in recursiveedit()
+
+	* 39548: Src/signals.c, Src/signals.h: DEBUG for queueing_enabled
+
+	* 39547: Src/Zle/zle_main.c: handle zero delta in calc_timeout()
+
+2016-10-03  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39545: Src/builtin.c, Src/exec.c, Src/hist.c, Src/init.c,
+	Src/mem.c, Src/module.c, Src/params.c, Src/prompt.c,
+	Src/Zle/computil.c, Src/Zle/zle_main.c: Add some missing
+	unqueue_signals().
+
+	* 39521: Src/exec.c, Src/zsh.h, Test/A01grammar.ztst: Refactor
+	start of execcmd().  This allows execpline2() easier access to
+	the state at the start of execuation.
+
+2016-10-02  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 39540: Src/exec.c, Test/C03traps.ztst: "! command" should
+	suppress ERR_EXIT and ERR_RETURN.
+
+2016-09-30  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39495: Doc/Zsh/contrib.yo, Functions/Misc/add-zle-hook-widget:
+	add-zle-hook-widget: Add end-of-options guard to hook invocation.
+
+	* 39480: Completion/Debian/Command/_bug: _reportbug: Complete
+	absolute filenames, too.
+
+2016-09-30  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Doc/Zsh/builtins.yo: update or remove references to
+	typeset behavior obsoleted by 35586.
+
+	* 39509: Src/builtins.c: in the event the current directory has
+	been removed, use chasedots semantics for "cd ..".
+
+2016-09-30  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39519: Src/params.c: setting PM_EXPORT was missing in
+	USE_SET_UNSET_ENV case.
+
+	* 39517: back off 39502, this isn't a robust fix.
+
+	* 39498: Src/parmas.c,Src/zsh.h: use PRIVILEGED option to
+	decide on problematic parameter imports.
+
+2016-09-30  Mikael Magnusson  <mikachu@gmail.com>
+
+	* 39452 + comment from 39432: Src/Zle/zle_main.c: Call the
+	pre-redraw hook if there is text in the buffer on init (from
+	popping or zle-line-init setting $BUFFER)
+
+2016-09-30  Oliver Kiddle  <opk@zsh.org>
+
+	* 39514: Completion/Unix/Command/_git: handle --git-dir
+
+	* 39513: Completion/Unix/Command/_doas: new doas completion
+
+	* 39464: Completion/Solaris/Command/_fmadm: fix where lack
+	of $state check caused unwanted filename completion
+
+2016-09-29  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39507: Doc/Zsh/params.yo, Src/exec.c: TMPSUFFIX for =(...)
+
+	* 39470: Src/exec.c, Src/utils.c: failure to open a supposedly
+	unique temp file name should result in an error; band-aid for
+	signal-related race conditions in temp file name generation
+
+2016-09-29  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39502: Src/exec.c, Test/A01grammar.ztst: need to fork for
+	assignment in LHS of pipeline.
+
+	* unposted: Src/Zle/compctl.c, Src/Zle/zle_tricky.c: update
+	findcmd() call here, too.
+
+	* 39493: Src/builtin.c, Src/exec.c, Src/subst.c,
+	Test/A01grammar.ztst: make "command" with multiple options work
+	better and ensure "command -p" with "-v" or "-V" checks for a
+	builtin and then using the system default command path.
+
+2016-09-28  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Martijn Dekker: 39463: configure.ac: another way of getting
+	the default system path.
+
+	* 39460: Src/params.c, Src/zsh.h: don't import PS4 if running as
+	root.  Currently no known exploit with this in zsh as xtrace
+	needs an explicit option setting, so this is just precautionary.
+
+2016-09-27  Oliver Kiddle  <opk@zsh.org>
+
+	* Marko Myllynen: 39453: Completion/Unix/Command/_openstack:
+	new openstack completions
+
+	* 39369 (tweaked cf 39371): Src/Zle/zle_thingy.c: when calling
+	an internal widget, set bindk because some widgets use it to
+	determine how they should act
+
+2016-09-27  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39437: Src/exec.c: use list_pipe_pid in assignment for clarity
+
+2016-09-26  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* Martijn Dekker: 39448: Src/loop.c: reset REPLY in select when
+	needed on user action.
+
+2016-09-25  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 39436: Src/exec.c, Src/signals.c: Don't execute builtin if
+	interrupted; set lastval to 128 + SIGINT on interrupt.
+
+	* 39435: Src/exec.c: Don't set gleader of SUBJOB immediately if
+	SUPERJOB has no processes.
+
+2016-09-24  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39423: Completion/Zsh/Command/_zed: Support the '--'
+	end-of-options mark.
+
+	* 39423: Functions/VCS_Info/Backends/VCS_INFO_get_data_git:
+	vcs_info git: Produce nicer applied-string messages for 'exec'
+	actions.
+
+2016-09-23  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39412: Src/Zle/computil.c: Fix directory completion when
+	$PWD:h contains parentheses, which are interpreted as globbing
+	metacharacters.
+
+2016-09-22  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39410: Completion/Darwin/Type/_retrieve_mac_apps,
+	Completion/Unix/Command/_dsh, Completion/Unix/Type/_dates:
+	Fix unintentional history modifiers.
+
+2016-09-22  Adam Gibbins  <adam@adamgibbins.com>
+
+	* unposted: Completion/Unix/Command/_gpg: Correct typo
+
+2016-09-21  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Completion/Darwin/Type/_retrieve_mac_apps: add
+	missing final colon in zstyle context lookup
+
+	* users/21955: Completion/Base/Completer/_user_expand: add
+	missing final colon in zstyle context lookup; allow a single
+	unique match to appear in "expansions" tag
+
+2016-09-21  Oliver Kiddle  <opk@zsh.org>
+
+	* 39370: Completion/Zsh/Command/_typeset,
+	Completion/Zsh/Command/_zle: complete -w and -K options to zle
+	following the widget name and numeric base to integer
+
+	* 39407: Completion/Linux/Command/_cpupower: cleanup minor
+	issues; updated through to version 4.8
+
+2016-09-20  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39385: Completion/Debian/Command/_bts: Complete more argument
+	types for 'cache' and 'show'.
+
+	* 39383: Src/Zle/compcore.c: compadd: Restrict previous patch
+	to the case where $PREFIX includes characters both from the -P
+	prefix and from the (unprefixed) candidate completion word.
+
+	* 39372: Src/Zle/compcore.c: compadd: Match -P prefix
+	all-or-nothing rather than greedily.
+
+2016-09-19  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39381: Src/exec.c: handle save/restore of variable values when
+	"typeset"-related reserved words are prefixed by an assignment
+
+2016-09-19  Mikael Magnusson  <mikachu@gmail.com>
+
+	* 39351: Functions/Zle/bracketed-paste-url-magic: Handle magnet
+	links too, and allow for schemes without //
+
+2016-09-18  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39374: Completion/Debian/Command/_apt: Complete package
+	versions in the 'packagename=<version>' syntax.
+
+2016-09-17  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39356 (tweaked, c.f. Bart 39358): Etc/BUGS: Remove fixed items,
+	add 'compset -q' item from workers/39306.
+
+2016-09-16  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 39362: Src/exec.c: forked zsh in pipeline handling always
+	starts a new process group, avoiding double STOP.
+
+2016-09-16  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39359: Src/exec.c, Src/jobs.c, Src/signals.c: Further fix on
+	top of 39331 for remaining race.  Ensure process group of forked
+	superjob is sane.
+
+	* 39331: Src/exec.c, Src/jobs.c, Src/zsh.h: Partially fix problem
+	occurring when a subjop in the RHS of a pipeline needs to be
+	picked up by a forked zsh after ^Z when the original superjob
+	(LHS of pipeline) has already exited.  Still race-prone.
+
+2016-09-16  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Completion/Unix/Command/_postfix: Correct quoting
+	in _call_program invocations.
+
+	* 39312 (tweaked): Completion/Unix/Command/_postfix: _postfix
+	(postconf): Complete only applicable parameters for -X and -#.
+
+	* 39310/0010: Src/Zle/compmatch.c: internals: match_str:
+	Simplify expression.
+
+	* 39310/0009: Src/Zle/compmatch.c: internals: match_str:
+	Downscope local variable 't'.
+
+	* 39310/0008: Src/Zle/compmatch.c: internals: match_str:
+	Document several local variables.
+
+	* 39310/0007: Src/Zle/compmatch.c: internals: match_str: Rename
+	and constify local variables 'oll', 'olw'.
+
+	* 39310/0006: Src/Zle/compmatch.c: internals: match_str:
+	Downscope local variable 'bpc'.
+
+	* 39310/0005: Src/Zle/compmatch.c: internals: match_str:
+	Constify some local variables.
+
+	* 39310/0004: Src/Zle/compmatch.c: internals: match_str: Document
+	'savl'.
+
+	* 39310/0003: Src/Zle/compmatch.c: internals: match_str: Document
+	'savw'. Avoid magic number.
+
+	* 39310/0002: Src/Zle/compmatch.c: internals: match_str:
+	Simplify by removing 'zoff'.
+
+	* 39310/0001: Src/Zle/compmatch.c: internals: match_str:
+	Document some local variables. See 39123.
+
+	* 39311: Completion/Unix/Command/_tmux: _tmux-capture-pane:
+	Tweak specifications of -S/-E and description strings.
+
+2016-09-15  Oliver Kiddle  <opk@zsh.org>
+
+	* 39333: Completion/Base/Utility/_call_program,
+	Doc/Zsh/compsys.yo, Completion/Solaris/Command/_pfexec,
+	Completion/Unix/Command/_sudo: include name of command used to
+	gain priviliges in context for command and gain-priviliges styles
+
+	* 39332: Doc/Zsh/cond.yo, Src/cond.c, Src/params.c, Src/parse.c,
+	Completion/Zsh/Context/_condition, Test/C02cond.ztst: support
+	ksh's [[ -v varname ]] condition for checking if variables are set
+
+	* unposted: Src/parse.c: remove duplicated assignment
+
+2016-09-14  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* unposted: Doc/Zsh/expn.yo: small documentation tweaks.
+
+	* zsh-users/21903: Src/subst.c, Src/zsh.h,
+	Test/D04parameter.ztst: ${...?...} in interactive shell should
+	abort to top level, not just current command.
+
+2016-09-14  Marko Myllynen  <myllynen@redhat.com>
+
+	* unposted: Completion/Unix/Command/_libvirt: Add another
+	noglob. (Follow-up to 39199)
+
+2016-09-14  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39199: Completion/Unix/Command/_libvirt: Support libvirt URLs
+	that contain a literal question mark under noglob.
+
+	* unposted: Completion/Unix/Command/_git: Permit multiple
+	-c options.
+
+2016-09-13  Oliver Kiddle  <opk@zsh.org>
+
+	* 39307: Completion/Unix/Command/_vim,
+	Completion/Unix/Command/_xxd: update options up to vim 8
+
+	* 39299: Completion/Base/Completer/_external_pwds: better
+	Freebsd support in _external_pwds using procstat
+
+2016-09-13  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39292: Config/version.mk, Src/cond.c, Src/parse.c, Src/text.c,
+	Src/zsh.h, Test/C02cond.ztst, Test/D01prompt.ztst,
+	Test/E02xtrace.ztst: Distinguish "=" and "==" tests in output to
+	avoid confusion.  Update version number to 5.2-dev-2 (unposted:
+	update date, too).
+
+	* 39305: Src/exec.c: error handling on substitution for here
+	document was illogical.
+
+2016-09-12  Oliver Kiddle  <opk@zsh.org>
+
+	* 39295: Completion/Unix/Type/_remote_files: allow '--' to
+	appear in the command line passed as an argument
+
+	* 39287: Completion/Unix/Command/_graphicsmagick,
+	Completion/Unix/Command/_imagemagick, Completion/X/Command/_okular:
+	add .tif as a valid extension for TIFF files
+
+2016-09-12  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Doc/Zsh/mod_sched.yo: reference use of widgets to
+	effect editor changes from a sched command
+
+	* 39297: Doc/Zsh/compsys.yo: more of 39284, it's painful to add
+	texi nodes in yodl
+
+2016-09-12  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39255: Doc/Zsh/cond.yo: = in conditions is not actually going
+	to be removed.
+
+2016-09-11  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39268: Src/Modules/system.c: "zsystem flock -t 0 ..." tries only
+	once to flock and immediately returns success or failure
+
+2016-09-11  Vin Shelton  <ethersoft@gmail.com>
+
+	* 39284: Doc/Zsh/compsys.yo: Added Completion System Variables as
+	a menu item
+
+2016-09-11  Oliver Kiddle  <opk@zsh.org>
+
+	* 39261 (tweaked cf. Daniel: 39275): Doc/Zsh/compsys.yo:
+	Completion/Base/Core/_main_complete,
+	Completion/Base/Utility/_call_program,
+	Completion/Debian/Command/_dchroot,
+	Completion/Debian/Command/_dchroot-dsa,
+	Completion/Debian/Command/_schroot,
+	Completion/Solaris/Command/_pfexec,
+	Completion/Solaris/Command/_zlogin, Completion/Unix/Command/_dsh,
+	Completion/Unix/Command/_fsh, Completion/Unix/Command/_libvirt,
+	Completion/Unix/Command/_mosh, Completion/Unix/Command/_rlogin,
+	Completion/Unix/Command/_ssh, Completion/Unix/Command/_sudo:
+	don't unconditionally gain privileges with sudo for completion
+
+2016-09-11  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39252: Src/Zle/compcore.c, Src/Zle/compctl.c,
+	Src/Zle/computil.c, Src/Zle/zle_misc.c, Src/Zle/zle_tricky.c,
+
+	Src/builtin.c, Src/subst.c, Src/text.c, Src/utils.c: internal:
+	quotestring: Drop the 'e' parameter, which no caller uses.
+
+2016-09-09  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Test/D04parameter.ztst: Add tests for ${(q)} being
+	aware of the EQUALS option.
+
+2016-09-08  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39236 (plus typo fix): Doc/Zsh/compsys.yo, README: docs: Tweak
+	_arguments $opt_args documentation added in 39173.
+
+2016-09-08  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39222: Test/V01zmodload.ztst: test use of module loaded math
+	functions with zsh/systeme.
+
+2016-09-07  Danek Duvall  <duvall@comfychair.org>
+
+	* 39194: Completion/Unix/Command/_man: _man (Solaris): Ignore
+	man-index. Support multiple sections in the -s flag's argument.
+
+2016-09-07  Oliver Kiddle  <opk@zsh.org>
+
+	* unposted: Completion/Unix/Command/_rm: fix to use ;|
+	style fall-throughs in the case statement
+
+	* 39209: Matthew Martin: Completion/BSD/Command/_chflags:
+	Update options
+
+	* 39208: Matthew Martin: Completion/BSD/Command/_chflags:
+	Update flags
+
+	* 39223: Matthew Martin: Completion/BSD/Command/_chflags:
+	Introduce addflags function to reduce redundancy
+
+	* 39193: Completion/Unix/Type/_remote_files: don't pass options
+	from after -- on to compadd
+
+2016-09-07  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 39185: Src/lex.c: wordbeg only set for ZLE if not alias.
+	Fixes crash on completion in some obscure alias expansions.
+
+2016-09-07  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Matthew Martin: 39221: Completion/Unix/Command/_rm: update for
+	BSD.
+
+	* 39218: Src/module.c: fix feature enables with math functions
+	present.  This caused parameters to be miscounted.
+
+	* 39217: Src/Modules/system.mdd: adapt zsh.mdd to avoid problems
+	with GCC preprocessor in error names.
+
+2016-09-06  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* m0viefreak: 38153 (cf. 39135): Completion/Base/Utility/_arguments:
+	change the way long options are examined for "=" signs to more
+	accurately identify options that take arguments
+
+2016-09-06  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39174: Completion/Unix/Command/_ssh: Fix completion of
+	ProxyCommand option.
+
+	* 39173: Doc/Zsh/compsys.yo, README, Src/Zle/computil.c:
+	_arguments: Escape colons and backslashes in $opt_args
+	unambiguously.
+
+	* 39171: Completion/Unix/Command/_libvirt: Apply
+	$opt_args-unescaping.
+
+	* unposted: Completion/Unix/Command/_libvirt: Escape $words
+	elements to avoid their interpretation as a pattern against
+	$_cache_virsh_cmds.
+
+2016-09-06  Marko Myllynen  <myllynen@redhat.com>
+
+	* unposted: Completion/Unix/Command/_libvirt: Fix leakage of
+	$word (after 39179).
+
+2016-09-06  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39181: Src/builtin.c, Src/zsh.h, Src/Zle/complete.c: Add
+	PM_SINGLE for single instance and use for compstate.
+
+2016-09-06  Marko Myllynen  <myllynen@redhat.com>
+
+	* 39179 (plus tweak): Completion/Unix/Command/_libvirt: More
+	_libvirt (virsh) completion tweaks
+
+2016-09-05  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* Teubel György: 39167: Src/init.c, Doc/Zsh/params.yo: Make $ENV
+	more like POSIX.
+
+2016-09-04  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Src/Zle/comp.h, Src/Zle/complete.c: internals:
+	Document zshcompwid(1) parameter implementations.
+
+2016-09-03  Oliver Kiddle  <opk@zsh.org>
+
+	* Marko Myllynen: 39158: Completion/Unix/Command/_libvirt:
+	cleanup following review comments of function
+
+	* 39165: Completion/Unix/Command/_dmidecode,
+	Completion/Unix/Command/_ant, Completion/Linux/Command/_ss,
+	Completion/Unix/Command/_espeak, Completion/Unix/Command/_git,
+	Completion/Unix/Command/_prove, Completion/Unix/Command/_tin,
+	Completion/Unix/Command/_patch, Completion/Unix/Command/_look,
+	Completion/Unix/Command/_md5sum: update options
+
+2016-09-02  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Completion/Unix/Command/_postfix: _postfix_queue_id:
+	Unbreak completion of short queue ids of held/active messages,
+	broken in 39108.
+
+	* 39154: Completion/Unix/Type/_files: -/ with list-dirs-first
+	resulted in a bad pattern, thus aborting completion partway.
+
+2016-08-31  Oliver Kiddle  <opk@zsh.org>
+
+	* Marko Myllynen: 39118: Completion/Unix/Command/_libvirt:
+	(almost) complete virsh completions
+
+2016-08-31  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39141: Test/D04parameter.ztst: use zsh in SHLVL test to remove
+	ambiguous behaviour of sh.
+
+	* Stephane: 39125: Src/exec.c, Test/D04parameter.ztst: More care
+	decrementing SHLVL on exec; not needed in subshells.
+
+2016-08-31  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39122: Completion/Unix/Command/_git: __git_recent_branches:
+	Silence warning on an edge case.
+
+2016-08-30  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39131: Functions/Misc/add-zle-hook-widget: return on error
+	needs to be at the outer scope.
+
+2016-08-30  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39108 (tweaked): Completion/Unix/Command/_postfix: Support
+	$enable_long_queue_ids.
+
+	* 39110: Completion/Unix/Command/_postfix: Complete 'postconf'.
+
+	* 39107: Completion/Unix/Command/_postfix: Complete
+	'postqueue'. Also minor tweaks to 'postsuper'.
+
+	* 39109: Completion/Unix/Command/_postfix: Use mailq to obtain
+	queue id's.
+
+2016-08-29  Mikael Magnusson  <mikachu@gmail.com>
+
+	* 39112: Completion/X/Command/_x_utils: Use state_descr
+
+2016-08-28  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39115: Src/subst.c, Test/D04parameter.ztst: repair forced
+	joining when (@) and (j) are used together (broken by 39019)
+
+2016-08-27  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39105: Completion/Unix/Type/_absolute_command_paths:
+	_hashed_absolute_command_paths: Make the display string the
+	basename.
+
+	* 39103: Completion/Unix/Type/_absolute_command_paths:
+	_typed-in_absolute_command_paths: Better handle non-empty
+	arguments that don't start with a slash.
+
+	* 39070: Completion/Unix/Command/_mount,
+	Completion/Unix/Type/_canonical_paths: umount: Complete /f/b<TAB>
+	→ /foo/bar (for absolute path arguments only, for now)
+
+2016-08-26  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39104: Src/exec.c: do not hash relative paths in findcmd()
+
+2016-08-25  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39102: Completion/Unix/Command/_git: __git_recent_branches:
+	Optimise.
+
+2016-08-24  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39094: Completion/Unix/Command/_git: _git-config: When an
+	option is unknown, complete its value to the set value.
+
+2016-08-23  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 39087: Src/builtin.c, Src/utils.c: fix 'conditionally
+	uninitialized' variables
+
+	* 39086: Src/mem.c: declare file local variables as 'static'
+
+2016-08-22  Oliver Kiddle  <opk@zsh.org>
+
+	* unposted (c.f. Mikael: 39078): Completion/Unix/Command/_git:
+	fix for git blame terms
+
+2016-08-22  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Completion/Unix/Command/_git: _git-config:
+	sendemail.smtpserver: Correct tags.
+
+	* 39046 + 39061: Completion/Base/Completer/_external_pwds,
+	Completion/Zsh/Type/_history_modifiers, Doc/Zsh/contrib.yo,
+	Doc/Zsh/expn.yo, Functions/MIME/zsh-mime-handler,
+	Functions/VCS_Info/VCS_INFO_quilt,
+	Functions/Zle/expand-absolute-path, NEWS, Src/params.c,
+	Src/subst.c, Src/utils.c, Test/D02glob.ztst: New :P history
+	modifier.
+
+2016-08-20  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 39064: configure.ac, Src/Modules/mathfuc.c: use scalbn() instead
+	of scalb()
+
+2016-08-20  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 39074: Completion/Unix/Command/_ip: _ip-neighbour: Fix typo in
+	'lladdr'.
+
+2016-08-19  Oliver Kiddle  <opk@zsh.org>
+
+	* 39072: Completion/Unix/Command/_git: minor tweaks
+
+	* 39071: Completion/Solaris/Command/_beadm,
+	Completion/Solaris/Command/_svcs, Completion/Unix/Command/_sort
+	Completion/Solaris/Command/_coreadm,
+	Completion/Solaris/Command/_dladm,
+	Completion/Solaris/Command/_dumpadm,
+	Completion/Solaris/Command/_flowadm,
+	Completion/Solaris/Command/_fmadm: update for Solaris 11u3
+
+	* 39067: Completion/Unix/Type/_diff_options:
+	Completion/Linux/Command/_gpasswd, Completion/Linux/Command/_ss,
+	Completion/Redhat/Command/_scl, Completion/Unix/Command/_arping,
+	Completion/Unix/Command/_augeas, Completion/Unix/Command/_dbus,
+	Completion/Unix/Command/_grep, Completion/Unix/Command/_less,
+	Completion/Unix/Command/_make, Completion/Unix/Command/_mutt,
+	Completion/Unix/Command/_rsync, Completion/Unix/Command/_wget,
+	Completion/Unix/Command/_sed, Completion/Unix/Command/_sort,
+	Completion/Unix/Command/_ssh, Completion/Unix/Command/_tmux,
+	Completion/Unix/Command/_tree, Completion/Unix/Command/_uniq,
+	Completion/Unix/Command/_user_admin,
+	Completion/Unix/Command/_xargs, Completion/Unix/Command/_zip,
+	Completion/Unix/Command/_readelf: update options
+
+2016-08-18  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Completion/Unix/Type/_canonical_paths: Port docstring
+	changes from 39044.
+
+	* 39044 (in part, see thread): Doc/Zsh/compsys.yo: Document
+	_canonical_paths in the manual, too.
+
+2016-08-15  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39039: Doc/Zsh/zle.yo: fix documentation for transpose-words.
+
+2016-08-13  Oliver Kiddle  <opk@zsh.org>
+
+	* 39036: Completion/Unix/Command/_git: update up to git 2.9.2
+
+	* 39026: Src/Zle/computil.c: pattern specified with _arguments'
+	-A option shouldn't be checked against words after the cursor
+
+2016-08-12  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39035: Src/subst.c, Test/D04parameter.ztst: ${(A)name=word}
+	should expand as an array even when there is only one element.
+
+2016-08-12  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 39031: Src/Zle/zle_word.c: Ensure variables are initialised in
+	transpose-words.
+
+	* 39029: Doc/Zsh/zle.yo: document previous change.
+
+	* Han Pingtian: 38983: Src/Zle/zle_word.c: make transpose-words
+	handle numeric arguments sensibly.
+
+2016-08-12  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39028: Src/subst.c, Test/D04parameter.ztst: more join/split
+	cases fixed and tested.
+
+2016-08-10  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39019 (cf. PWS 39013): Src/subst.c, Test/D04parameter.ztst:
+	fix SHWORDSPLIT regression introduced by workers/29313; add
+	test cases for more join/split combinations.
+
+2016-08-10  Mikael Magnusson  <mikachu@gmail.com>
+
+	* 39014: Src/zsh_system.h, configure.ac: Use special OpenBSD
+	interface to get correct rand() behavior
+
+2016-08-06  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Src/Zle/zle_thingy.c: fix typo in comment
+
+	* unposted: Etc/zsh-development-guide: fix formatting in module
+	discussion; add mention of widgets and keymaps for modules.
+
+2016-08-05  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* workers/38995 (in part): Src/Zle/computil.c: compfiles:
+	Add reverse-engineered documentation breadcrumbs.
+
+	* 38996: Completion/Unix/Command/_man: Support _correct_word.
+
+	* 38994: Completion/Unix/Command/_man: Fix two bugs when
+	completing manpage filenames in separate-sections mode.
+
+	* 38993: Completion/Unix/Command/_man: Drop (b): it's incorrect
+	when $sect contains '|'.
+
+	* 38991: Src/exec.c, Test/C04funcdef.ztst: Make 'whence -v
+	autoloaded-function' shows the defining filename.
+
+	* 38990: Completion/Debian/Type/_debbugs_bugnumber: Track bts's
+	data dir migration.
+
+2016-08-04  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* unposted: Doc/Zsh/zle.yo: fix format
+
+2016-08-03  Oliver Kiddle  <opk@zsh.org>
+
+	* 38989: Completion/BSD/Command/_freebsd-update,
+	Completion/BSD/Command/_sockstat, Completion/Unix/Command/_cp,
+	Completion/Unix/Command/_getent: minor completion updates for BSD
+
+	* 38986: Completion/Unix/Command/_gsettings: new completion
+
+2016-08-01  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 38981: Completion/Unix/Command/_man: Followup to 37634:
+	unbreak OpenBSD 'man 3p' and Linux $MANSECT.
+
+2016-08-01  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* Andy Spencer: 38982: Completion/Linux/Command/_cpupower: move
+	to correct folder.
+
+	* Andy Spencer: 38976: Completion/Linux/_cpupower: new
+	completion.
+
+2016-08-01  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 38971: Src/Modules/terminfo.c, Src/Modules/zutil.c,
+	Src/builtin.c, Src/params.c, Src/prompt.c, Src/utils.c: Start
+	using the new arrlen_ge() / arrlen_le() helpers.
+
+	* 38973: Src/params.c, Src/subst.c, Src/utils.c: Optimize
+	indexing array parameters.
+
+	* 38964: Completion/Unix/Command/_git: _git-config: Complete
+	option names present in the config file.
+
+	* 38963: Completion/Unix/Command/_git: _git-config: Run
+	gettable-options earlier and in all codepaths.
+
+	* 38961: Completion/Unix/Command/_git: _git-config: No functional
+	change: rename $git_options_static to $git_options in preparation
+	for the after-next commit.
+
+2016-07-31  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Completion/Unix/Command/_git: Clean up superfluous
+	and missing backslashes.
+
+	* 38967: Completion/Unix/Type/_hosts: Don't complete wildcard
+	entries from ~/.ssh/known_hosts.
+
+	* 38966: Completion/Unix/Command/_subversion: _svnadmin: Complete
+	positional arguments for 'hotcopy', 'setlog', 'setrevprop',
+	'delrevprop'.
+
+	* 38965: Completion/Unix/Command/_subversion: _svnadmin: Complete
+	'freeze' as a precommand.
+
+	* 38962: Completion/Unix/Command/_git: _git-config: Document
+	more line noise.
+
+	* 38959: Doc/Zsh/compsys.yo: Document 38956 (_widgets).
+
+2016-07-29  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Completion/Unix/Command/_subversion: _svnadmin:
+	Tweak state description.
+
+2016-07-28  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 38927: Src/Zle/zle_main.c: zle-line-pre-redraw: Set $WIDGET
+	like other special widgets do.
+
+	* unposted: Functions/Misc/add-zle-hook-widget,
+	Functions/Misc/add-zsh-hook: Avoid $0 for POSIX_ARGZERO
+	compatibility.
+
+2016-07-28  Oliver Kiddle  <opk@zsh.org>
+
+	* 38957: Functions/Zle/select-word-match,
+	Completion/Zsh/Command/_zstyle, Doc/Zsh/contrib.yo:
+	make use of updates to match-words-by-style and better support
+	completion of word-style styles for zstyle
+
+	* 38956: Completion/Zsh/Command/_bindkey,
+	Completion/Zsh/Command/_vared, Completion/Zsh/Command/_zle,
+	Completion/Zsh/Function/_add-zle-hook-widget,
+	Completion/Zsh/Type/_widgets: factor out zle widget completion
+	into its own function
+
+	* unposted: Functions/Zle/vi-pipe: fix for visual mode
+
+2016-07-28  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 38953: Doc/Zsh/contrib.yo,
+	Functions/Zle/delete-whole-word-match,
+	Functions/Zle/match-words-by-style: Fix some problems with
+	match-words-by-style and add keyword retrieval of matched data.
+
+	* users/21793: README, Src/glob.c: remove ancient undocumented
+	pre-"f" glob qualifer feature that unqualified integers were
+	treated as octal file mode.
+
+2016-07-27  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Functions/Misc/add-zle-hook-widget: Prefix function's
+	name to its error messages.
+
+	* unposted (after 38939): Completion/Unix/Command/_git:
+	_git-rebase: Unbreak.
+
+	* 38914 (tweaked): Completion/Unix/Command/_pkg-config: Add
+	options, complete *.pc files for positional arguments.
+
+2016-07-26  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 38945: Doc/Zsh/expn.yo: additional notes on :a behaviour.
+
+2016-07-24  Oliver Kiddle  <opk@zsh.org>
+
+	* 38943: Completion/Unix/Command/_texinfo: update options
+	and improve completion of Info files, nodes and menu items
+
+	* 38939: Completion/Unix/Command/_git: add missing options,
+	in particular those for GPG signing a push
+
+	* 38936: Functions/Zle/vi-pipe: fix for option compatibility
+
+	* 38929: Doc/Zsh/contrib.yo, Functions/Zle/select-word-match:
+	new vim style text object using match-words-by-style mechanism
+
+	* 38935: Matthew Martin: Completion/Unix/Command/_tcpdump:
+	update for Free and Open BSD
+
+2016-07-23  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Completion/Unix/Command/_git: _git-grep: Stop
+	leaking the parameter $i.
+
+	* unposted: Completion/Debian/Type/_deb_packages: Minor
+	optimization.
+
+	* 38913: Completion/Unix/Command/_pkg-config: Complete variables
+	for --variable.
+
+	* 38911: Doc/Zsh/compsys.yo: _arguments: Clarify documentation of
+	'-s -w' switch
+
+2016-07-23  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 38923 (plus expanded comment): Src/jobs.c: zwaitjob() continues
+	waiting for children that may have ignored the interrupt signal,
+	even if the current shell has been interrupted.
+
+2016-07-22  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* users/21779: Doc/Zsh/expn.yo: Clarify documentation of the
+	':a' word modifier.
+
+	* 38890: Completion/Zsh/Function/_add-zle-hook-widget,
+	Completion/Zsh/Function/_add-zsh-hook: _add-zle-hook-widget:
+	New completion.
+
+2016-07-21  Oliver Kiddle  <opk@zsh.org>
+
+	* 38904: Completion/Base/Core/_normal,
+	Completion/Unix/Command/_iostat, Completion/Unix/Command/_top:
+	fix return status in _normal and functions erroneously using
+	it for default completion
+
+	* 38894: Marko Myllynen: Completion/Unix/Command/_libvirt:
+	completion for virt-admin and libvirt client/server IDs/names
+
+2016-07-21  Felipe Sateler  <fsateler@debian.org>
+
+	* 38901: Completion/Debian/Command/_schroot: Add -r/--run-session
+
+2016-07-20  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 38853: Src/glob.c: use strchr().
+
+2016-07-20  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* users/21777: Doc/Zsh/expn.yo: Clarify documentation of the
+	':A' word modifier.
+
+2016-07-19  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 38862: Src/Modules/datetime.c: strptime(3) requires _XOPEN_SOURCE
+	on Cygwin (newlib-2.4 or later)
+
+2016-07-18  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 38879: Src/glob.c, Test/D07multibyte.ztst: Ensure file names
+	are sorted unmetafied.  Test using Polish UTF-8 collation
+	sequence.
+
+2016-07-18  Mikael Magnusson  <mikachu@gmail.com>
+
+	* 38785 (plus tweak): Completion/Zsh/Command/_print: add -v
+
+2016-07-17  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Functions/Misc/add-zle-hook-widget:  Move from Zle/.
+	
+	* 38866: Doc/Zsh/contrib.yo: update add-zle-hook-widget for 38850.
+
+	* 38866 (+ tweak 38872): Functions/Zle/add-zle-hook-widget: fix
+	edge case handling, wrap in anonymous function for kshautoload
+	management.
+
+2016-07-17  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* users/21752: Doc/Zsh/mod_zleparameter.yo: Extend
+	zsh/zleparameter's $widgets' parameter's documentation.
+
+	* unposted (cf. users/21737):: Completion/Unix/Command/_git:
+	_git-subtree: Record technical debt in comment.
+
+	* users/21750 (after users/21551): Completion/Unix/Command/_git:
+	_git-subtree: Tweak -m,-P completions
+
+2016-07-17  Oliver Kiddle  <opk@zsh.org>
+
+	* 38868: Completion/BSD/Command/_freebsd-update,
+	Completion/BSD/Command/_bsdconfig, Completion/BSD/Command/_fetch,
+	Completion/BSD/Command/_fstat, Completion/BSD/Command/_gstat,
+	Completion/BSD/Command/_jexec, Completion/BSD/Command/_kld,
+	Completion/BSD/Command/_procstat, Completion/BSD/Command/_sysrc,
+	Completion/BSD/Command/_systat, Completion/Unix/Command/_cat,
+	Completion/Unix/Command/_sysctl, Completion/Unix/Command/_vmstat,
+	Completion/Unix/Command/_zfs, Completion/Unix/Type/_zfs_dataset:
+	update completions for new options on FreeBSD
+
+	* 38867: Completion/Linux/Command/_ltrace,
+	Completion/Linux/Command/_strace, Completion/Unix/Command/_truss,
+	Completion/Unix/Type/_sys_calls: update strace completion
+	factoring out system calls and new truss and ltrace completions
+
+	* Marko Myllynen: 38837 (tweaked c.f. 38826):
+	Completion/Unix/Command/_libvirt: new virsh completion
+
+	* 38845: Src/Zle/zle_main.c, Doc/Zsh/zle.yo: reset region_active
+	before entering zle - it was on exit but before zle-line-finish
+	Also reword documentation on region to better cover vi mode
+
+2016-07-13  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 38850: Functions/Zle/add-zle-hook-widget: Simplify indexing
+	scheme to store hooks strictly in the order they are added;
+	better handling of edge cases and autoloading/sourcing file.
+
+2016-07-13  Eric Cook  <llua@gmx.com>
+
+	* 38833: Completion/Unix/Command/_iostat
+	separate iostat completion from Completion/Linux/Command/_sysstat
+
+2016-07-08  Oliver Kiddle  <opk@zsh.org>
+
+	* 38812: Completion/BSD/Command/_chflags,
+	Completion/Unix/Command/_dmesg, Completion/Unix/Command/_env,
+	Completion/Unix/Command/_ln, Completion/Unix/Command/_xmlsoft,
+	Completion/Unix/Command/_ls, Completion/Unix/Command/_service,
+	Completion/Unix/Command/_touch, Completion/Unix/Command/_script,
+	Completion/X/Command/_x_utils, Completion/Zsh/Command/_vared:
+	a few new completions and update some command options
+
+	* 38810: Src/Zle/textobjects.c: fix cursor positioning
+	and repeated invocations when widgets used from emacs mode
+
+	* 38809: Src/zsh.h, Src/prompt.c, Src/Zle/zle_refresh.c:
+	fix tracking of colour attributes and restore them when
+	turning bold off
+
+	* unposted: Doc/Zsh/builtins.yo, Doc/Zsh/compsys.yo,
+	Doc/Zsh/options.yo, Doc/Zsh/zle.yo: fix duplicated words
+
+2016-07-08  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 38796: Functions/Zle/match-words-by-style: fix subword
+	matching on last character of subword.
+
+2016-07-05  Oliver Kiddle  <opk@zsh.org>
+
+	* arno: 38780: Completion/X/Command/_setxkbmap: include
+	variants containing non alphanumeric symbols in completion
+
+	* Jordan Klassen: users/21551 (tweaked per users/21560):
+	Completion/Unix/Command/_git: new git subtree completion
+
+2016-07-05  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 38728: Test/D02glob.ztst: Tests: Add tests for the ':a' and
+	':A' modifiers.
+
+2016-07-04  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 38783: Doc/Zsh/contrib.yo, Functions/Misc/zcalc: tweaks for
+	variable and stack interation in RPN mode.
+
+2016-06-29  Oliver Kiddle  <opk@zsh.org>
+
+	* 38770: Src/Zle/zle_keymap.c, Src/Zle/zle_vi.c, Doc/Zsh/zle.yo,
+	Doc/Zsh/contrib.yo, Functions/Zle/vi-pipe: vi upper/lowercase
+	widgets and shell widget example that reads a vi movement
+
+	* 38752: Src/builtin.c: add comments to explain use of stdout
+	instead of stderr for the which builtin
+
+2016-06-27  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 38760: Completion/Unix/Command/_git: _git-config: Stop trying
+	to execute the empty string command name upon completing values
+	for an unknown option.
+
+2016-06-25  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Completion/Unix/Command/_git: _git-config: Document
+	some line noise.
+
+	* unposted: Completion/Unix/Command/_git: _git-config: Fix
+	syntax error in 'tag.sort' completion.
+
+	* 38652: Test/ztst.zsh: test harness: Emit unified diffs instead
+	of context diffs
+
+2016-06-22  Oliver Kiddle  <opk@zsh.org>
+
+	* 38749: Functions/Zle/surround, Functions/Zle/select-bracketed:
+	fixes for case at start of a line and for ksharrays
+
+2016-06-22  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* unposted: Src/lex.c: remove unused and no longer defined flag
+	from experiments for previous fix.
+
+	* 38746: Src/lex.c, Test/A02alias.ztst: Delay marking
+	a suffix alias as free until the last minute.
+
+2016-06-22  Oliver Kiddle  <opk@zsh.org>
+
+	* 38714: Src/Zle/complete.c, Doc/Zsh/compsys.yo,
+	Doc/Zsh/compwid.yo: add x: syntax to match specs to make it
+	possible to disable match specs hardcoded in completion functions
+
+	* 38735: Completion/Unix/Command/_find,
+	Completion/Zsh/Type/_globquals: support verbose style to allow
+	clearer but less compact descriptions for time specifiers
+
+	* 38733: Completion/X/Command/_rdesktop: completion for xfreerdp
+
+2016-06-21  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 38737: Functions/Misc/zcalc, Doc/Zsh/contrib.yo: document some
+	zcalc variable usage and make other variables start with "_".
+
+	* 38736: Completion/Zsh/Type/_module_math_func,
+	Completion/Zsh/Type/_user_math_func, Doc/Zsh/contrib.yo,
+	Functions/Misc/zcalc: various RPN mode enhancments for zcalc.
+
+	* 38734: Src/loop.c, Test/A01grammar.ztst: fix final case
+	clauses terminating with ;&.
+
+	* 38692: Doc/Zsh/restricted.yo, Src/params.c: IFS can't be
+	changed in restricted mode.
+
+2016-06-19  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 38715: Doc/Zsh/contrib.yo, Functions/Zle/add-zle-hook-widget:
+	assorted ksharrays fixes; assign an index to any hook that is
+	added without one, to preserve append ordering
+
+2016-06-18  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Functions/Misc/zed: localoptions noksharrays
+
+	* unposted: Doc/Zsh/options.yo: when ksharrays is set, braces are
+	required for all parameter expansions with colon-modifiers
+
+2016-06-18  Oliver Kiddle  <opk@zsh.org>
+
+	* 38713: Completion/Unix/Type/_dates: don't add calendar matches
+	when not immediately entering menu selection
+
+	* 38707: Completion/Zsh/Type/_globquals,
+	Completion/Unix/Type/_files, Completion/Unix/Type/_path_files:
+	strip suffix for glob qualifier completion
+
+	* 38703: Completion/Unix/Command/_su: support su options on macOS
+
+2016-06-16  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 38693: Doc/Zsh/contrib.yo, Functions/Misc/zcalc,
+	Functions/Zle/zcalc-auto-insert: Add RPN mode to zcalc.
+
+	* unposted: Doc/Zsh/params.yo: fix parentheses for getrusage().
+
+2016-06-16  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* 38684: Doc/Zsh/contrib.yo: fix format
+
+2016-06-14  Eric Cook  <llua@gmx.com>
+
+	* 38676: Marko Myllynen: Completion/Linux/Command/_pidof:
+	Completion for pidof(1)
+
+	* 38680: Marko Myllynen: Completion/Unix/Command/_localedef:
+	Completion for localedef(1)
+
+2016-06-14  Doug Kearns  <dougkearns@gmail.com>
+
+	* unposted: Completion/Unix/Command/_cvs: remove unused parameter
+
+2016-06-13  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* users/21632: Doc/Zsh/params.yo, Src/jobs.c: REPORTMEMORY
+	parameter gives minimum size to trigger usage report.
+
+2016-06-13  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 38653 + 38657: Doc/Zsh/builtins.yo, Src/exec.c,
+	Test/E02xtrace.ztst: 'functions -T' tracing: recurse into
+	anonymous functions.
+
+	* 38651: Completion/Unix/Command/_git: Escape parameter arguments
+	to _call_program.
+
+	* 38665: Completion/Unix/Command/_git: config option completion:
+	Quote properly.
+
+2016-06-12  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 38670: Doc/Zsh/contrib.yo, Functions/Zle/add-zle-hook-widget:
+	New function for managing ZLE special widgets, modeled after
+	Functions/Misc/add-zsh-hook.
+
+2016-06-09  Oliver Kiddle  <opk@zsh.org>
+
+	* 38641: Completion/Base/Utility/_values: allow for values which
+	resemble compadd options
+
+	* 38639: Completion/Unix/Command/_su: fix username completion
+	after -, update options and get user shell with getent
+
+2016-06-07  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 38624: Completion/Unix/Command/_git: Optimize the last commit's
+	__git_recent_branches__names as suggested by Matthew.
+
+	* 38592 (plus tweak): Completion/Unix/Command/_git: New recent
+	branches completion, unused. (Joint with Nils Luxton)
+
+2016-06-07  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 38632: Completion/Linux/Command/_pkgtool: remove trailing space
+	on #compdef line for compinit parsing
+
+	* 38630: Src/builtin.c: fix infinite loop of "hash ="
+
+2016-06-06  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 38610: Functions/VCS_Info/VCS_INFO_quilt: vcs_info quilt:
+	Extract a patch subject, 2.0.
+
+	* 38609: Functions/VCS_Info/VCS_INFO_quilt: vcs_info quilt:
+	Factor out a helper function. No functional change.
+
+2016-06-05  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 38622: Src/jobs.c: consistent handling of "--" in "kill" builtin
+
+2016-06-04  Eric Cook <llua@gmx.com>
+
+	* 38547: Completion/compdump: only autoload functions
+	when they exist in fpath.
+
+2016-06-04  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 38593: Doc/Zsh/compsys.yo: _describe: Make documentation
+	more greppable.
+
+2016-06-04  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted (cf. 38612): Src/params.c: remove overeager DPUTS()
+
+	* 38599: Src/subst.c: skip the "no such named directory" warning
+	when NO_EXEC is in effect
+
+2016-06-03  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* users/21609: Completion/Unix/Type/_path_files: do not treat
+	tilde-expansions as quoted when inside command substitutions
+
+2016-06-03  Marko Myllynen  <myllynen@redhat.com>
+
+	* 38587: Completion/Linux/Command/_iconvconfig: Fix iconvconfig
+	completion --output handling
+
+2016-06-03  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Src/subst.c: internal: Document modify().
+
+2016-06-03  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 38586: Src/Modules/parameter.c, Test/D07multibyte.ztst:
+	Don't unmetafy values for $functions when passing back
+	within parameter framework.
+
+2016-06-03  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* 38577: Completion/Unix/Type/_signals: Don't complete SIGEXIT
+	from _kill.
+
+	* 38576: Completion/Unix/Command/_git: Also detect
+	_git-${thirdparty} functions in $fpath that are symlinks.
+
+2016-06-02  Eric Cook <llua@gmx.com>
+
+	* 38567: Marko Myllynen: Completion/Unix/Command/_locale:
+	completion for locale(1)
+
+	* 38440: Marko Myllynen: Completion/Linux/Command/_iconvconfig:
+	completion for linux's iconvconfig(8)
+
+2016-06-02  Oliver Kiddle  <opk@zsh.org>
+
+	* 38540: Src/Zle/zle_utils.c: fix undo problem by not moving
+	the current change when only undoing a history line change
+
+2016-06-02  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* users/21603: Src/utils.c: Don't do second attempt at read/poll
+	from terminal if interrupted.
+
+2016-05-22  Peter Stephenson  <p.w.stephenson@ntlworld.com>
+
+	* 38515: Matthew Martin: Completion/Zsh/Context/_subscript:
+	missing -- after print and optimised array add.
+
+	* 38514: Matthew Martin: Completion/Linux/Command/_vserver:
+	remove unnecessary ls.
+
+	* 38513: Matthew Martin: Src/Builtins/sched.c: cast time to long
+	long where possible.
+
+2015-05-12  Clint Adams  <clint@zsh.org>
+
+	* 38482: Completion/Debian/Command/_apt: complete available packages
+	for apt list.
+
+2016-05-10  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 38468: Src/lex.c: wb,we values in gotword() needed assignment in
+	additional case to avoid core dump (bug introduced by 38248)
+
+	* 38463: Src/Zle/zle_keymap.c: use immortal widgets in .safe keymap
+
+2016-05-10  Daniel Shahaf  <d.s@daniel.shahaf.name>
+
+	* unposted: Functions/Math/.distfiles, Functions/Math/zmathfunc:
+	Commit forgotten part of users/21256.
+
+	* 38453: Completion/Debian/Command/_apt: Complete
+	${package}/${release} for 'source' and 'build-dep', too.
+
+	* 38452: Completion/Debian/Command/_apt: Complete releases by
+	codename ('jessie', 'sid', etc).
+
+	* 38451: Completion/Debian/Command/_apt: Complete 'apt-get
+	download'.
+
+2016-03-10  Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
+
+	* Marko Myllynen: 38458: Doc/Zsh/compsys.yo: mention the option
+	-i of _multi_parts
+
+2016-05-09  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* Jun T.: 38445 (+tweak): Test/C02cond.ztst: subsequent tests rely
+	on a file touched by the -N test, so it must always be touched even
+	when the filesystem is noatime
+
+	* 38432: Src/zsh_system.h: avoid nonstandard setenv() on __APPLE__
+
+2016-05-09  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* unposted: Functions/Zle/bracketed-paste-magic: update documentary
+	comments to remove references to self-insert-unmeta.
+
+2016-05-09  Frank Terbeck  <ft@bewatermyfriend.org>
+
+	* 38422: Completion/Unix/Type/_baudrate,
+	Completion/Unix/Type/_baudrates: _baudrate -> _baudrates
+
+	* 38426: Completion/Unix/Type/_baudrates: _baudrates: Use 2 space
+	indentation
+
+	* 38424: Completion/BSD/Command/_cu, Completion/Unix/Command/_gdb,
+	Completion/Unix/Command/_joe, Completion/Unix/Command/_picocom,
+	Completion/Unix/Command/_screen: Use _baudrates helper instead of
+	_baudrate
+
+	* 38421: Completion/Unix/Type/_baudrates: _baudrates: Fit better
+	into the general completion framework
+
+	* 38425: Completion/BSD/Command/_cu: _cu: Remove old -d option of
+	_baudrates
+
+	* 38420: Completion/Unix/Type/_baudrates: _baudrates: Make style
+	lookups fit better with the rest of compsys
+
 2016-05-06  Daniel Shahaf  <d.s@daniel.shahaf.name>
 
 	* 38401: Completion/Debian/Type/_deb_packages: New completion
@@ -142,9 +3317,9 @@
 
 2016-04-07  Barton E. Schaefer  <schaefer@zsh.org>
 
-	* 38248: Src/Zle/zle_tricky.c: fix word position calculation
-	when completing on or just before a redirection operator; the
-	completion result is still in need of some repair
+	* 38248: Src/lex.c, Src/Zle/zle_tricky.c: fix word position
+	calculation when completing on or just before a redirection
+	operator; the completion result is still in need of some repair
 
 2016-04-03  Barton E. Schaefer  <schaefer@zsh.org>
 
@@ -1540,7 +4715,7 @@
 
 2015-09-28  Barton E. Schaefer  <schaefer@zsh.org>
 
-	* 36669: Src/lex.c: fix ${(z)...} of an an incomplete math
+	* 36669: Src/lex.c: fix ${(z)...} of an incomplete math
 	expression by restoring "((" at the front of the token
 
 2015-09-28  Daniel Shahaf  <d.s@daniel.shahaf.name>
@@ -1742,7 +4917,7 @@
 
 2015-09-11  Daniel Shahaf  <d.s@daniel.shahaf.name>
 
-	* 36443: Doc/Zsh/zle.yo Src/Zle/zle_refresh.c: 
+	* 36443: Doc/Zsh/zle.yo Src/Zle/zle_refresh.c:
 	Highlight pasted/yanked text as standout by default (i.e., when
 	$zle_highlight doesn't specify otherwise).
 
@@ -7534,7 +10709,7 @@
 2013-07-20  Peter Stephenson  <p.w.stephenson@ntlworld.com>
 
 	* 31545: Src/exec.c, Src/parse.c: if FD_CLOEXEC is available,
-	so mark dump file file descriptors, avoiding possible
+	so mark dump file descriptors, avoiding possible
 	multiple use of file descriptors.
 
 2013-07-19  Peter Stephenson  <p.w.stephenson@ntlworld.com>
diff --git a/Completion/BSD/Command/_bsdconfig b/Completion/BSD/Command/_bsdconfig
index 8c7597e0f..f88255575 100644
--- a/Completion/BSD/Command/_bsdconfig
+++ b/Completion/BSD/Command/_bsdconfig
@@ -2,6 +2,7 @@
 
 local -a shortcuts
 shortcuts=(
+  api
   'console:utilities to customize the behavior of the system console'
   'defaultrouter:default router/gateway'
   'diskmgmt:manage disk partitions and/or labels using sade(8)'
@@ -12,6 +13,7 @@ shortcuts=(
   'groupedit:edit/view groups'
   'groupmgmt:utilities to add/change/view/delete group accounts'
   'hostname:set hostname/domainname'
+  includes
   'kern_securelevel:set kern.securelevel variable'
   'mouse:utilities for configuring, exploring, and enabling console mouse support'
   'mouse_disable:disable mouse support'
@@ -43,13 +45,19 @@ shortcuts=(
   'userdel:delete users'
   'useredit:edit/view users'
   'usermgmt:utilities to add/edit/view/delete user accounts'
+  vt_font
+  vt_keymap
+  vt_repeat
+  vt_saver
+  vt_screenmap
+  vt_ttys
 )
 
 _arguments -s -w -A '-*' : \
   '-d[debug mode]' \
   '-D[send debug info to file]: :{ compset -P 1 +; _files }' \
   '-f[load file as script then exit]: : _files' \
-  '-h[print usage then exit]' \
+  '-h[display help information]' \
   '-S[secure X11 mode]' \
   '-X[use Xdialog(1)]' \
-  '1:bsdconfig(8) menus:(( $shortcuts ))'
+  '1:bsdconfig(8) menu:(( $shortcuts ))'
diff --git a/Completion/BSD/Command/_chflags b/Completion/BSD/Command/_chflags
index c9f26249d..075782cd9 100644
--- a/Completion/BSD/Command/_chflags
+++ b/Completion/BSD/Command/_chflags
@@ -1,47 +1,79 @@
 #compdef chflags
 
-local flags own='-g *(-u$EUID)'
-
-flags=(
-  '(noopaque)opaque[set the opaque flag]'
-  '(opaque)noopaque[unset the opaque flag]'
-  '(dump)nodump[set the nodump flag]'
-  '(nodump)dump[unset the nodump flag]'
-  '(nouappnd)uappnd[set the user append-only flag]'
-  '(uappnd)nouappnd[unset the user append-only flag]'
-  '(nouchg)uchg[set the user immutable flag]'
-  '(uchg)nouchg[unset the user immutable flag]'
-)
+local flags args own='-g *(-u$EUID)'
+
+addflags() {
+  for 1 2; do
+    if [[ $1 = no* ]]; then
+      flags+=("(${1#no})$1[set the $2 flag]"
+               "($1)${1#no}[unset the $2 flag]")
+    else
+      flags+=("(no$1)$1[set the $2 flag]"
+               "($1)no$1[unset the $2 flag]")
+    fi
+  done
+}
+
+addflags \
+  uappnd 'user append-only' \
+  uchg 'user immutable'
 
 if (( ! EUID )); then
-  flags=( $flags[@]
-    '(noarch)arch[set the archived flag]'
-    '(arch)noarch[unset the archived flag]'
-    '(nosappnd)sappnd[set the system append-only flag]'
-    '(sappnd)nosappnd[unset the system append-only flag]'
-    '(noschg)schg[set the system immutable flag]'
-    '(schg)noschg[unset the system immutable flag]'
-  )
+  addflags \
+    arch archived \
+    nodump nodump \
+    sappnd 'system append-only' \
+    schg 'system immutable'
   unset own
 fi
 
-if [[ $OSTYPE = (freebsd|dragonfly|darwin)* ]]; then
-  flags=( $flags[@]
-    '(nouunlnk)uunlnk[set the user undeletable flag]'
-    '(uunlnk)nouunlnk[unset the user undeletable flag]'
-    '(nohidden)hidden[set the hidden flag]'
-    '(hidden)nohidden[unset the hidden flag]'
-  )
-  (( EUID )) || flags=( $flags[@]
-    '(nosunlnk)sunlnk[set the system undeletable flag]'
-    '(sunlnk)nosunlnk[unset the system undeletable flag]'
+if [[ $OSTYPE = (darwin|dragonfly|freebsd|netbsd)* ]]; then
+  addflags opaque opaque
+
+  if [[ $OSTYPE = darwin* ]]; then
+    addflags hidden hidden
+  fi
+
+  if [[ $OSTYPE = (dragonfly|freebsd)* ]]; then
+    addflags uunlnk 'user undeletable'
+    (( EUID )) || addflags sunlnk 'system undeletable'
+  fi
+
+  [[ $OSTYPE = dragonflybsd* ]] && {
+    addflags \
+      cache XXX \
+      nouhistory 'user nohistory'
+
+    (( EUID )) || addflags \
+      noscache XXX \
+      noshistory 'system nohistory'
+  }
+
+  [[ $OSTYPE = freebsd* ]] && addflags \
+    uarch archive \
+    uhidden hidden \
+    uoffline offline \
+    urdonly 'DOS, Windows and CIFS readonly' \
+    ureparse 'Windows reparse point' \
+    usparse 'sparse file' \
+    usystem 'DOS, Windows and CIFS system'
+
+fi
+
+if [[ $OSTYPE = (darwin|dragonfly|freebsd)* ]]; then
+  args=(
+    "-f[don't display diagnostic messages]"
+    '-v[verbose output]'
   )
 fi
 
-_arguments -s -A "-*" \
+_arguments -s -A "-*" : $args \
+  - opth \
+  '-h[act on symlinks]' \
+  - optR \
+  '-R[recurse directories]' \
   '(-L -P)-H[follow symlinks on the command line (specify with -R)]' \
   '(-H -P)-L[follow all symlinks (specify with -R)]' \
   '(-L -H)-P[do not follow symlinks (specify with -R)]' \
-  '-R[recurse directories]' \
   ':file flag:_values -s , "file flags" $flags[@]' \
   '*:file:_files "$own"'
diff --git a/Completion/BSD/Command/_cu b/Completion/BSD/Command/_cu
index 4b9f25d1f..8c0d1efef 100644
--- a/Completion/BSD/Command/_cu
+++ b/Completion/BSD/Command/_cu
@@ -3,5 +3,5 @@
 _arguments -s -A '-*' \
   '-d[do not block waiting for a carrier to be detected]' \
   '-l[line to use]:line:(/dev/(cuaU#<->|ttyS<->)(N%c))' \
-  '-s[line speed]:line speed:_baudrate -d "line speed"' \
+  '-s[line speed]:line speed:_baudrates' \
   '(-*)1:host:'
diff --git a/Completion/BSD/Command/_fetch b/Completion/BSD/Command/_fetch
index 3136763a0..ac1264bf8 100644
--- a/Completion/BSD/Command/_fetch
+++ b/Completion/BSD/Command/_fetch
@@ -1,30 +1,43 @@
 #compdef fetch
-# Deprecated arguments are removed from the completion
 
-_arguments -s \
-	'-1[stop and return exit code 0 at the first successfully retrieved file]' \
-	'-4[forces fetch to use IPv4 addresses only]' \
-	'-6[forces fetch to use IPv6 addresses only]' \
-	'-A[do not automatically follow "temporary" (302) redirects]' \
-	'-a[automatically retry the transfer upon soft failures]' \
-	'-B[specify the read buffer size in bytes]:bytes:' \
-	'-d[use a direct connection even if a proxy is configured]' \
-	'-F[in combination with the -r flag, forces a restart]' \
-	'-l[if the target is a file-scheme URL, make a symbolic link to the target]' \
-	'-M' \
-	'-m[mirror mode]' \
-	'-N[use file instead of ~/.netrc to look up login names and pass- words for FTP sites]' \
-	'-n[do not preserve the modification time]' \
-	'-o[set the output file name]:file:_files' \
-	'-P' \
-	'-p[use passive FTP]' \
-	'-q[quiet mode]' \
-	'-R[do not delete the output file in any circumstances]' \
-	'-r[restart a previously interrupted transfer]' \
-	'-S[require the file size reported by the server to match the specified value]' \
-	'-s[print the size in bytes, without fetching it]' \
-	'-T[set timeout value]:seconds:' \
-	'-U[when using passive FTP, allocate the port for the data connection from the low port range]' \
-	'-v[increase verbosity level]' \
-	'-w[wait successive retries]:seconds:' \
-	'*:URL to fetch:_urls'
+# Deprecated arguments are prefixed with ! so they aren't listed but their arguments are completed
+_arguments -s -S \
+  '(-1 --one-file)'{-1,--one-file}'[stop and return exit code 0 at the first successfully retrieved file]' \
+  '(-4 --ipv4-only)'{-4,--ipv4-only}'[forces fetch to use IPv4 addresses only]' \
+  '(-6 --ipv6-only)'{-6,--ipv6-only}'[forces fetch to use IPv6 addresses only]' \
+  '(-A --no-redirect)'{-A,--no-redirect}"[don't automatically follow "temporary" (302) redirects]" \
+  '(-a --retry)'{-a,--retry}'[automatically retry the transfer upon soft failures]' \
+  '(-B --buffer-size)'{-B+,--buffer-size=}'[specify the read buffer size in bytes]:buffer size (bytes)' \
+  '--bind-address=[specify address to which outgoing connections will be bound]:host:_hosts' \
+  '--ca-cert=[specify certificate bundle containing trusted CA certificates]:file:_files' \
+  '--ca-path=[specify directory containing trusted CA hashes]:path:_directories' \
+  '--cert=[specify PEM encoded client key for authentication]:file:_files -g "*.pem(-.)"' \
+  '--crl=[specify certificate revocation list file]:file:_files' \
+  '(-d --direct)'{-d,--direct}'[use a direct connection even if a proxy is configured]' \
+  '(-F --force-restart)'{-F,--force-restart}'[in combination with the -r flag, forces a restart]' \
+  '(-i --if-modified-since)'{-i+,--if-modified-since=}'[only retrieve if remote file newer than specified local file]:file:_files' \
+  '--key=[specify PEM encoded client key]:key file:_files -g "*.pem(-.)"' \
+  '(-l --symlink)'{-l,--symlink}'[if the target is a file-scheme URL, make a symbolic link to the target]' \
+  '-M' \
+  '(-m --mirror -r --restart)'{-m,--mirror}'[mirror mode]' \
+  '(-N --netrc)'{-N+,--netrc=}'[use file instead of ~/.netrc to look up login names and pass- words for FTP sites]' \
+  '(-n --no-mtime)'{-n,--no-mtime}"[don't preserve the modification time]" \
+  '--no-passive[force FTP code to use active mode]' \
+  '--no-proxy=[hosts on which to disable proxoes]:host:_sequence _hosts' \
+  '--no-sslv3' '--no-tlsv1' --no-verify-hostname --no-verify-peer \
+  '(-o --output)'{-o+,--output=}'[set the output file name]:file:_files' \
+  '-P' \
+  '(-p --passive)'{-p,--passive}'[use passive FTP]' \
+  '--referer=:URL:_urls' \
+  '(-q --quiet)'{-q,--quiet}'[quiet mode]' \
+  '(-R --keep-output)'{-R,--keep-output}"[don't delete the output file in any circumstances]" \
+  '(-r --restart -m --mirror)'{-r,--restart}'[restart a previously interrupted transfer]' \
+  '(-S --require-size)'{-S+,--require-size=}'[require the file size reported by the server to match the specified value]' \
+  '(-s --print-size)'{-s,--print-size}'[print the size in bytes, without fetching it]' \
+  '(-T --timeout)'{-T+,--timeout=}'[set timeout value]:seconds:' \
+  '(-U --passive-portrange-default)'{-U,--passive-portrange-default}'[when using passive FTP, allocate the port for the data connection from the low port range]' \
+  '--user-agent=:user agent' \
+  '(-v --verbose)'{-v,--verbose}'[increase verbosity level]' \
+  '(-w --retry-delay)'{-w+,--retry-delay=}'[wait successive retries]:delay (seconds)' \
+  '!(*)-h+:host:_hosts' '!(*)-f+:file:_files' '!(*)-c+:remote directory:_directories' \
+  '*:URL to fetch:_urls'
diff --git a/Completion/BSD/Command/_freebsd-update b/Completion/BSD/Command/_freebsd-update
index 11235bad0..d42457272 100644
--- a/Completion/BSD/Command/_freebsd-update
+++ b/Completion/BSD/Command/_freebsd-update
@@ -7,14 +7,17 @@ flags=(
   '(install rollback)upgrade[fetch files necessary for upgrading to a new release]'
   '(upgrade rollback)install[install the most recently fetched updates or upgrade]'
   '(upgrade install)rollback[uninstall the most recently installed updates]'
+  'IDS[compare the system against an index of "known good" files]'
 )
 
-_arguments -s \
+_arguments \
   '-b[operate on a system mounted at basedir]:basedir:_files -/' \
   '-d[store working files in workdir]:workdir:_files -/' \
   '-f[read configuration options from conffile]:conf file:_files' \
-  '-k[trust an RSA key with SHA256 of KEY]:RSA key:' \
-  '-r[specify the new release]:new release:' \
+  '-F[force freebsd-update fetch to proceed where it normally would not]' \
+  '-k[trust an RSA key with SHA256 of KEY]:RSA key' \
+  '-r[specify the new release]:new release' \
   '-s[fetch files from the specified server or server pool]:server:_hosts' \
-  '-f[mail output of cron command, if any, to address]:address:' \
-  ':command:_values -S " " -w "commands" $flags[@]'
+  '-t[mail output of cron command, if any, to address]:address' \
+  '--currently-running[assume specified release as current]:release' \
+  '*:command:_values -S " " -w "commands" $flags[@]'
diff --git a/Completion/BSD/Command/_fstat b/Completion/BSD/Command/_fstat
index 3e8d61733..5487e6c0f 100644
--- a/Completion/BSD/Command/_fstat
+++ b/Completion/BSD/Command/_fstat
@@ -1,15 +1,12 @@
 #compdef fstat
 
-local pids
-pids=(${${${(f)"$(/usr/bin/procstat -ah)"}/[[:space:]]#/}/[[:space:]]*[[:space:]](ELF[[:digit:]]#[[:space:]]|-[[:space:]]#)/:})
-
 _arguments -s \
 '-f[restrict examination to files open in the same file systems as the named file arguments]' \
-'-M[extract values associated with the name list from the specified core]:core:_files' \
-'-N[extract the name list from the specified system]:system:' \
+'-M+[extract values associated with the name list from the specified core]:core:_files' \
+'-N+[extract the name list from the specified system]:system' \
 '-m[include memory-mapped files in the listing]' \
 '-n[numerical format]' \
-'-p[report all files open by the specified process]:process id:(($pids))' \
-'-u[report all files open by the specified user]:user:_users' \
+'-p+[report all files open by the specified process]:process id:_pids' \
+'-u+[report all files open by the specified user]:user:_users' \
 '-v[verbose mode]' \
 '*:files:_files'
diff --git a/Completion/BSD/Command/_gstat b/Completion/BSD/Command/_gstat
index 55b7db74d..7baaf0d62 100644
--- a/Completion/BSD/Command/_gstat
+++ b/Completion/BSD/Command/_gstat
@@ -1,11 +1,11 @@
 #compdef gstat
 
-_arguments -s -w : \
+_arguments -s : \
   '-a[only display providers that are at least 0.1% busy]' \
   '-b[batch mode]' \
   '-c[enable the display geom(4) consumers]' \
   '-d[enable the display delete operations]' \
-  '-f[filter by regex]:regex' \
+  '-f+[filter by regex]:regex' \
   '-o[enable the display for other operations]' \
-  '-I[display refresh rate]:interval' \
+  '-I+[display refresh rate]:interval (ms)' \
   '-p[only display physical providers]'
diff --git a/Completion/BSD/Command/_jexec b/Completion/BSD/Command/_jexec
index f065ea1e0..85829d10e 100644
--- a/Completion/BSD/Command/_jexec
+++ b/Completion/BSD/Command/_jexec
@@ -2,6 +2,7 @@
 
 _jexec_normal() {
   local PATH=$PATH
+  local -a _comp_priv_prefix
   # relative paths are relative to the jail's root
   path=( "$(command jls -j $words[1] path)"/$^path )
   shift 1 words; (( CURRENT-- ))
@@ -9,7 +10,8 @@ _jexec_normal() {
 }
 
 _jexec() {
-  _arguments -s -w -A "-*" : \
+  _arguments -s -S -A "-*" : \
+    '-l[execute in a clean environment]' \
     '(-U)-u[host environment user whom command runs as]:host user:_users' \
     '(-u)-U[jail environment user whom command runs as]:jail user:_users' \
     '1:jail:_jails' \
diff --git a/Completion/BSD/Command/_jot b/Completion/BSD/Command/_jot
new file mode 100644
index 000000000..e8abcb7d1
--- /dev/null
+++ b/Completion/BSD/Command/_jot
@@ -0,0 +1,82 @@
+#compdef jot
+
+local curcontext="$curcontext" state state_descr line ret=1
+local -A opt_args
+
+_arguments -s -C : \
+  '(-b)-r[generate random data instead of the default sequential data]' \
+  '(-c -p -r -w)-b+[just print the specified word repeatedly]:word:' \
+  '(-b -c -p)-w+[specify output format as in printf]:output format:->format' \
+  '(-b -p -w)-c[same as -w %c]' \
+  '-s+[separate the data by the specified string instead of newline]:string:' \
+  '-n[do not print the final newline]' \
+  '(-b -c -w)-p+[specify number of digits after decimal point]:precision:' \
+  '(-)1: :_guard "^-*" "number of data"' \
+  '2::lower bound:' \
+  '3::upper bound:' \
+  '4:: :->lastarg' && ret=0
+
+case $state in
+  (format)
+    local -a flags digits conversions
+    flags=(
+      '#:alternative form'
+      ' :leave a blank before a positive number'
+      '+:always place a sign before a number'
+      '-:left adjust'
+    )
+    digits=(0 1 2 3 4 5 6 7 8 9 .)
+    conversions=(
+      {d,i}':signed decimal'
+      'o:unsigned octal'
+      'u:unsigned decimal'
+      {x,X}':unsigned hexadecimal'
+      'l'{d,i}':long signed decimal'
+      'lo:long unsigned octal'
+      'lu:long unsigned decimal'
+      'l'{x,X}':long unsigned hexadecimal'
+      'f:double number'
+      {e,E}':double number in scientific notation'
+      {g,G}':use %f or %e, whichever is more appropriate'
+      'c:character'
+    )
+
+    compset -P '([^%]#%%)#'
+    if compset -P '[^%]#%'; then
+      # flags: (|\#)(| )(|+|-)
+      compset -P '(\#|\\\#)' && flags=( ${(@)flags:#\#*} )
+      compset -P '( |\\ )' && flags=( ${(@)flags:#(\#| )*} )
+      compset -P '(+|-)' || compset -P '[0-9]##' && flags=()
+      # width.precision: [0-9]#(|.[0-9]#)
+      if compset -P '.'; then
+	flags=()
+	digits=( ${(@)digits:#.} )
+      fi
+      compset -P '[0-9]#'
+      # conversion specifier: ((|l)[diouxX]|[feEgGc])
+      if compset -P '((|l)[diouxX]|[feEgGc])'; then
+	_message -e format-rest 'rest of the format' && ret=0
+      else
+	local opts=( -S '' )
+	_alternative -O opts 'flags:flag:(( $flags ))' \
+	    "width-precision:width.precision:($digits)" \
+	    'conversion-specifiers:conversion specifier:(( $conversions ))' \
+	    && ret=0
+      fi
+    else
+      local expl
+      compset -P '*'
+      _wanted start-specification expl 'start specification' \
+				compadd -S '' % && ret=0
+    fi
+    ;;
+  (lastarg)
+    if (( $+opt_args[-r] )); then
+      _message -e seeds 'random number seed' && ret=0
+    else
+      _message -e steps 'step size' && ret=0
+    fi
+    ;;
+esac
+
+return ret
diff --git a/Completion/BSD/Command/_kld b/Completion/BSD/Command/_kld
index 34f26f1e9..42fdc2bd0 100644
--- a/Completion/BSD/Command/_kld
+++ b/Completion/BSD/Command/_kld
@@ -24,14 +24,17 @@ _kld() {
   unset _cache_sysctlvars
 
   case "$service" in
-  kldload)
-    _arguments -s \
+    kldload)
+      _arguments -s -S -A "-*" \
+        "-n[don't try to load module if already loaded]" \
         '-v[be verbose]' \
+        '-q[silence any extraneous warnings]' \
         '*:module to load:_kld_module'
     ;;
 
-  kldunload)
-    _arguments -s \
+    kldunload)
+      _arguments -s -S -A "-*" \
+        '-f[force the unload]' \
         '-v[be verbose]' \
         '(-n)-i:module id to unload:_kld_unload_id' \
         '(-i)-n:module to unload:_kld_unload' \
diff --git a/Completion/BSD/Command/_portmaster b/Completion/BSD/Command/_portmaster
index 4c16e2d1b..48390c9ad 100644
--- a/Completion/BSD/Command/_portmaster
+++ b/Completion/BSD/Command/_portmaster
@@ -5,7 +5,7 @@ _portmaster_pkgs() {
 }
 
 _portmaster_ports() {
-  local ret=1 _fbsd_ports _fbsd_cat
+  local expl ret=1 _fbsd_ports _fbsd_cat
    _fbsd_cat=(${PORTSDIR:-/usr/ports}/[a-z]*(/:t))
    if [[ $PREFIX != */* ]] ; then
      _wanted cat_packages expl 'category/ports' compadd -S '/' $_fbsd_cat
diff --git a/Completion/BSD/Command/_procstat b/Completion/BSD/Command/_procstat
index f8bc54290..e16057635 100644
--- a/Completion/BSD/Command/_procstat
+++ b/Completion/BSD/Command/_procstat
@@ -1,17 +1,24 @@
 #compdef procstat
 
-local pids
-#get list of pids and associated process name as comment
-pids=(${${${(f)"$(/usr/bin/procstat -ah)"}/[[:space:]]#/}/[[:space:]]*[[:space:]](ELF[[:digit:]]#[[:space:]]|-[[:space:]]#)/:})
-
-_arguments -s \
-'-b[display binary information for the process]' \
-'-c[display command line arguments for the process]' \
-'-f[display file descriptor information for the process]' \
-'-k[display the stacks of kernel threads in the process]' \
-'-s[display security credential information for the process]' \
-'-t[display thread information for the process]' \
-'-v[display virtual memory mappings for the process]' \
-'-h[suppress table headers]' \
-'-a[all processes]' \
-':process id:(($pids))'
+_arguments -s -A "-*" -S \
+  '-b[show binary information]' \
+  '-c[show command line arguments]' \
+  '-e[show environment variables]' \
+  '-f[show file descriptor information]' \
+  '-i[show signal pending and disposition]' \
+  '-k[show stacks of kernel threads]' \
+  '-l[show resource limits]' \
+  '-r[show resource usage]' \
+  '-s[show security credential information]' \
+  '-S[show cpuset information]' \
+  '-t[show thread information]' \
+  '-v[show virtual memory mappings]' \
+  '-x[show ELF auxiliary vector]' \
+  '-h[suppress table headers]' \
+  '(1)-a[all processes]' \
+  '-w+[repeat information after specified interval]:delay (seconds)' \
+  '-C[print additional capability information for file descriptors]' \
+  '-H[statistics per-thread rather than per-process]' \
+  '-n[numeric form for signals]' \
+  '1::core file:_path_files -g "*core*(-.)"' \
+  '*:process id:_pids'
diff --git a/Completion/BSD/Command/_sockstat b/Completion/BSD/Command/_sockstat
index daad946fc..913f3eb05 100644
--- a/Completion/BSD/Command/_sockstat
+++ b/Completion/BSD/Command/_sockstat
@@ -28,6 +28,7 @@ case $OSTYPE in
       '*-j[show sockets belonging to JID]: : _jails -0 -o jid'
       '-L[exclude loopback]'
       '*-P+[specify protocol]: : _values -s , protocols $protocols'
+      '-s[display protocol state if applicable]'
     )
   ;;
   netbsd*)
diff --git a/Completion/BSD/Command/_sysrc b/Completion/BSD/Command/_sysrc
index d8bc4ef0e..246d73bd3 100644
--- a/Completion/BSD/Command/_sysrc
+++ b/Completion/BSD/Command/_sysrc
@@ -1,39 +1,50 @@
 #compdef sysrc
+
 _sysrc_caching_policy() {
   local -a oldp
   oldp=( "$1"(Nm+1) )
   (( $#oldp ))
 }
 
-
 _sysrc() {
-  _arguments -A '-*' : \
+  local curcontext="$curcontext" state line ret=1
+  typeset -A opt_args
+  local -a rc_conf_vars
+
+  _arguments -C -s -A '-*' : \
+    '(-l)-e[print variables as sh(1) compatible syntax]' \
+    '-E[list only existing files]' \
+    '(-l)-q[quiet mode]' \
+    '(-L)-s+[process additional rc.conf.d entries for specified service name]:service name:_services' \
+    '(-L)*-f+[operate on specified file(s), not \`rc_conf_files'\'']: : _files' \
+    '(-l)-v[verbose mode]' \
+    '*:configuration variable:->confvars' \
+    - set \
+    '(-A)-a[list all non-default configuration variables]' \
+    '(-a)-A[list all configuration variables]' \
     '-c[check only, return success if vars are set]' \
     '-d[print variable(s) description]' \
     '-D[print default value(s) only]' \
-    '-e[print variables as sh(1) compatible syntax]' \
-    '*-f[operate on specified file(s), not \`rc_conf_files'\'']: : _files' \
     '-F[print only the last rc.conf(5) file each directive is in]' \
-    '-h[print short usage message]' \
-    '--help[print full usage message]' \
+    '(- *)-h[print short usage message]' \
+    '(- *)--help[print full usage message]' \
     '-i[ignore unknown variables]' \
-    '-j[jail to operate within]:jails:_jails' \
+    '-j+[jail to operate within]:jails:_jails' \
     '-n[print only variable values]' \
     '-N[print only variable names]' \
-    '-q[quiet mode]' \
-    '-R[specify an alternative root]:alternative root:_files -/' \
-    '-v[verbose mode]' \
-    '--version[print version information]' \
+    '-R+[specify an alternative root]:alternative root:_files -/' \
+    '(- *)--version[print version information]' \
     '-x[remove specified variables from specified file(s)]' \
-    '*:configuration variable:->confvars' \
-    - set1 \
-    '-a[list all non-default configuration variables]' \
-    - set2 \
-    '-A[list all configuration variables]'
+    - lists \
+    '(-e -q -v)-l[list config files used at startup]' \
+    - lista \
+    '(-s -f)-L[list all configuration files including rc.conf.d entries]' && ret=0
 
   if [[ $state  == confvars ]]; then
-    local k v opt curcontext="${curcontext%:*}:values"; local -a rc_conf_vars
-    if [[ -prefix *=* ]]; then
+    local k v opt
+    if (( $+opt_args[lista--L] )); then
+      _services && ret=0
+    elif [[ -prefix *=* ]]; then
       # do you really want to go down this hole?
       _message -e values value
     else
@@ -65,13 +76,15 @@ _sysrc() {
 
       if (( $#rc_conf_vars )); then
         if [[ $opt == N ]]; then
-          _values -w -C variable ${^rc_conf_vars%%\[*}'::value'
+          _values -w variable ${^rc_conf_vars%%\[*}'::value' && ret=0
         else
-          _values -w -C variable ${^rc_conf_vars}'::value'
+          _values -w variable ${^rc_conf_vars}'::value' && ret=0
         fi
       fi
     fi
   fi
+
+  return ret
 }
 
 _sysrc "$@"
diff --git a/Completion/BSD/Command/_systat b/Completion/BSD/Command/_systat
index c8348c7f7..35b842188 100644
--- a/Completion/BSD/Command/_systat
+++ b/Completion/BSD/Command/_systat
@@ -1,12 +1,13 @@
 #compdef systat
 
-local -a screens args opts
+local -a screens opts
 local pre
 case $OSTYPE in
   freebsd*)
     pre=-
     screens=(
       'pigs:processes consuming the most CPU time'
+      'sctp:SCTP statistics'
       icmp{6,}':ICMP statistics'
       ip{6,}':IP and UDP statistics'
       'tcp:TCP statistics'
diff --git a/Completion/Base/Completer/_expand b/Completion/Base/Completer/_expand
index e52144cb7..a6e30e891 100644
--- a/Completion/Base/Completer/_expand
+++ b/Completion/Base/Completer/_expand
@@ -181,7 +181,7 @@ if [[ -z "$compstate[insert]" ]] ;then
 else
   _tags all-expansions expansions original
 
-  if [[ $#exp -gt 1 ]] && _requested expansions; then
+  if [[ $#exp -ge 1 ]] && _requested expansions; then
     local i j normal space dir
 
     if [[ "$sort" = menu ]]; then
@@ -207,9 +207,14 @@ else
     (( $#space ))  && compadd "$expl[@]" -UQ -qS " " -a space
     (( $#normal )) && compadd "$expl[@]" -UQ -qS "" -a normal
   fi
-  if _requested all-expansions expl 'all expansions'; then
+  if _requested all-expansions; then
     local disp dstr
 
+    if [[ "$sort" = menu ]]; then
+      _description all-expansions expl 'all expansions' "o:$word"
+    else
+      _description -V all-expansions expl 'all expansions' "o:$word"
+    fi
     if [[ "${#${exp}}" -ge COLUMNS ]]; then
       disp=( -ld dstr )
       dstr=( "${(r:COLUMNS-5:)exp} ..." )
diff --git a/Completion/Base/Completer/_external_pwds b/Completion/Base/Completer/_external_pwds
index 4ad50f02b..dfc1abe16 100644
--- a/Completion/Base/Completer/_external_pwds
+++ b/Completion/Base/Completer/_external_pwds
@@ -22,9 +22,13 @@ case $OSTYPE in
     )
   ;;
   linux*)
-    dirs=( /proc/${^$(pidof zsh):#$$}/cwd(N:A) )
+    dirs=( /proc/${^$(pidof -- -zsh zsh):#$$}/cwd(N:P) )
     dirs=( $^dirs(N^@) )
   ;;
+  freebsd*)
+    dirs=( $(pgrep -U $UID -x zsh) )
+    dirs=( $(procstat -h -f $dirs|awk '{if ($3 == "cwd") print $NF}') )
+  ;;
   *)
     if (( $+commands[lsof] )); then
       dirs=( ${${${(M)${(f)"$(lsof -a -u $EUID -c zsh -p \^$$ -d cwd -F n -w
@@ -32,7 +36,7 @@ case $OSTYPE in
     fi
   ;;
 esac
-dirs=( ${(D)dirs} )
+dirs=( ${(D)dirs:#$PWD} )
 
 compstate[pattern_match]='*'
 _wanted directories expl 'current directory from other shell' \
diff --git a/Completion/Base/Completer/_user_expand b/Completion/Base/Completer/_user_expand
index cf3d172f0..ee39bb176 100644
--- a/Completion/Base/Completer/_user_expand
+++ b/Completion/Base/Completer/_user_expand
@@ -27,7 +27,7 @@ exp=("$word")
 
 # Now look for user completions.
 
-zstyle -a ":completion:${curcontext}" user-expand specs || return 1
+zstyle -a ":completion:${curcontext}:" user-expand specs || return 1
 
 for spec in $specs; do
   REPLY=
@@ -95,7 +95,7 @@ if [[ -z "$compstate[insert]" ]] ;then
 else
   _tags all-expansions expansions original
 
-  if [[ $#exp -gt 1 ]] && _requested expansions; then
+  if [[ $#exp -ge 1 ]] && _requested expansions; then
     local i j normal space dir
 
     if [[ "$sort" = menu ]]; then
@@ -121,9 +121,14 @@ else
     (( $#space ))  && compadd "$expl[@]" -UQ -qS " " -a space
     (( $#normal )) && compadd "$expl[@]" -UQ -qS "" -a normal
   fi
-  if _requested all-expansions expl "all expansions${REPLY:+: $REPLY}"; then
+  if _requested all-expansions; then
     local disp dstr
 
+    if [[ "$sort" = menu ]]; then
+      _description all-expansions expl "all expansions${REPLY:+: $REPLY}" "o:$word"
+    else
+      _description -V all-expansions expl "all expansions${REPLY:+: $REPLY}" "o:$word"
+    fi
     if [[ "${#${exp}}" -ge COLUMNS ]]; then
       disp=( -ld dstr )
       dstr=( "${(r:COLUMNS-5:)exp} ..." )
diff --git a/Completion/Base/Core/_main_complete b/Completion/Base/Core/_main_complete
index 9c4cfac85..aa2486236 100644
--- a/Completion/Base/Core/_main_complete
+++ b/Completion/Base/Core/_main_complete
@@ -38,6 +38,9 @@ local func funcs ret=1 tmp _compskip format nm call match min max i num\
       _saved_colors="$ZLS_COLORS" \
       _saved_colors_set=${+ZLS_COLORS} \
       _ambiguous_color=''
+# Hide any '_comp_priv_prefix' variable that happens to be defined in the calling scope.
+local _comp_priv_prefix
+unset _comp_priv_prefix
 
 # _precommand sets this to indicate we are following a precommand modifier
 local -a precommands
diff --git a/Completion/Base/Core/_message b/Completion/Base/Core/_message
index 13c83989f..4d5645eaf 100644
--- a/Completion/Base/Core/_message
+++ b/Completion/Base/Core/_message
@@ -18,7 +18,8 @@ if [[ "$1" = -e ]]; then
     ret=0
   done
 
-  (( $compstate[nmatches] )) || compstate[insert]=
+  (( ! $compstate[nmatches] )) && [[ $compstate[insert] = *unambiguous* ]] &&
+      compstate[insert]=
 
   return ret
 fi
diff --git a/Completion/Base/Core/_normal b/Completion/Base/Core/_normal
index 539b3781f..dd607d2b2 100644
--- a/Completion/Base/Core/_normal
+++ b/Completion/Base/Core/_normal
@@ -30,9 +30,9 @@ if [[ CURRENT -eq 1 ]]; then
   curcontext="${curcontext%:*:*}:-command-:"
 
   comp="$_comps[-command-]"
-  [[ -n "$comp" ]] && eval "$comp" && ret=0
+  [[ -n "$comp" ]] && eval "$comp" && return
 
-  return ret
+  return 1
 fi
 
 _set_command
diff --git a/Completion/Base/Utility/_arguments b/Completion/Base/Utility/_arguments
index 687c0c4ed..d2c0d33de 100644
--- a/Completion/Base/Utility/_arguments
+++ b/Completion/Base/Utility/_arguments
@@ -105,7 +105,10 @@ if (( long )); then
 	 continue
        else
 	 # Still no comment, add the previous options anyway.
-	 lopts+=("${tmp[@]}")
+         # Add a ':' after the option anyways, to make the matching of
+         # the options lateron work as intended.
+         # It will be removed again later.
+	 lopts+=("${^tmp[@]}":)
 	 tmp=()
        fi
      fi
@@ -147,7 +150,7 @@ if (( long )); then
    done
    # Tidy up any remaining uncommented options.
    if (( ${#tmp} )); then
-     lopts+=("${tmp[@]}")
+     lopts+=("${^tmp[@]}":)
    fi
 
     # Remove options also described by user-defined specs.
@@ -220,19 +223,22 @@ if (( long )); then
 
       # Ignore :descriptions at the ends of lopts for matching this;
       # they aren't in the patterns.
-      tmp=("${(@M)lopts:##$~pattern(|:*)}")
-      lopts=("${(@)lopts:##$~pattern(|:*)}")
+      tmp=("${(@M)lopts:##$~pattern:*}")
+      lopts=("${(@)lopts:##$~pattern:*}")
 
       (( $#tmp )) || continue
 
       opt=''
 
+      # Clean suffix ':' added earlier
+      tmp=("${(@)tmp%:}")
+
       # If there are option strings with a `[=', we take these to get an
       # optional argument.
 
-      tmpo=("${(@M)tmp:#*\[\=*}")
+      tmpo=("${(@M)tmp:#[^:]##\[\=*}")
       if (( $#tmpo )); then
-        tmp=("${(@)tmp:#*\[\=*}")
+        tmp=("${(@)tmp:#[^:]##\[\=*}")
 
 	for opt in "$tmpo[@]"; do
 	  # Look for --option:description and turn it into
@@ -263,9 +269,9 @@ if (( long )); then
       # Basically the same as the foregoing.
       # TODO: could they be combined?
 
-      tmpo=("${(@M)tmp:#*\=*}")
+      tmpo=("${(@M)tmp:#[^:]##\=*}")
       if (( $#tmpo )); then
-        tmp=("${(@)tmp:#*\=*}")
+        tmp=("${(@)tmp:#[^:]##\=*}")
 
 	for opt in "$tmpo[@]"; do
 	  if [[ $opt = (#b)(*):([^:]#) ]]; then
diff --git a/Completion/Base/Utility/_call_program b/Completion/Base/Utility/_call_program
index 010e09476..9a44f2d8e 100644
--- a/Completion/Base/Utility/_call_program
+++ b/Completion/Base/Utility/_call_program
@@ -1,6 +1,16 @@
 #autoload +X
 
-local tmp err_fd=-1
+local curcontext="${curcontext}" tmp err_fd=-1
+local -a prefix
+
+if [[ "$1" = -p ]]; then
+  shift
+  if (( $#_comp_priv_prefix )); then
+    curcontext="${curcontext%:*}/${${(@M)_comp_priv_prefix:#^*[^\\]=*}[1]}:"
+    zstyle -t ":completion:${curcontext}:${1}" gain-privileges &&
+	prefix=( $_comp_priv_prefix )
+  fi
+fi
 
 if (( ${debug_fd:--1} > 2 )) || [[ ! -t 2 ]]
 then exec {err_fd}>&2	# debug_fd is saved stderr, 2 is trace or redirect
@@ -13,10 +23,10 @@ if zstyle -s ":completion:${curcontext}:${1}" command tmp; then
   if [[ "$tmp" = -* ]]; then
     eval "$tmp[2,-1]" "$argv[2,-1]"
   else
-    eval "$tmp"
+    eval $prefix "$tmp"
   fi
 else
-  eval "$argv[2,-1]"
+  eval $prefix "$argv[2,-1]"
 fi 2>&$err_fd
 
 } always {
diff --git a/Completion/Base/Utility/_values b/Completion/Base/Utility/_values
index ab0e46a9c..6e38e00f4 100644
--- a/Completion/Base/Utility/_values
+++ b/Completion/Base/Utility/_values
@@ -1,13 +1,14 @@
 #autoload
 
-local subopts opt usecc garbage
+local subopts opt usecc garbage keep
 
 subopts=()
-zparseopts -D -E -a garbage C=usecc O:=subopts M: J: V: 1 2 n F: X:
+zparseopts -D -a garbage s+:=keep S+:=keep w+=keep C=usecc O:=subopts \
+    M: J: V: 1 2 n F: X:
 
 (( $#subopts )) && subopts=( "${(@P)subopts[2]}" )
 
-if compvalues -i "$@"; then
+if compvalues -i "$keep[@]" "$@"; then
 
   local noargs args opts descr action expl sep argsep subc test='*'
   local oldcontext="$curcontext"
diff --git a/Completion/Darwin/Type/_retrieve_mac_apps b/Completion/Darwin/Type/_retrieve_mac_apps
index e9a30e006..f24d36c3c 100644
--- a/Completion/Darwin/Type/_retrieve_mac_apps
+++ b/Completion/Darwin/Type/_retrieve_mac_apps
@@ -71,7 +71,7 @@ _retrieve_mac_apps() {
 
     # Get application search method
     typeset retrieve
-    if ! zstyle -s ":completion:*:*:$service:commands" search-method retrieve
+    if ! zstyle -s ":completion:*:*:${service}:commands" search-method retrieve
     then
       if [[ -d /.Spotlight-V100 ]]; then
         # / is indexed to use Spotlight
@@ -80,12 +80,12 @@ _retrieve_mac_apps() {
         # Fall back to the old way
         retrieve=_mac_apps_old_retrieve 
       fi
-      zstyle ":completion:*:*:$service:commands" search-method $retrieve
+      zstyle ":completion:*:*:${service}:commands" search-method $retrieve
     fi
 
     # Get root directories to search applications
     typeset -a app_dir_root
-    if ! zstyle -a ":completion:${curcontext}" application-path app_dir_root
+    if ! zstyle -a ":completion:${curcontext}:" application-path app_dir_root
     then
       if [[ $retrieve = _mac_apps_old_retrieve ]]; then
         app_dir_root=( {,/Developer,/Network,"$HOME"}/{Applications*(N),Desktop} )
diff --git a/Completion/Debian/Command/_a2utils b/Completion/Debian/Command/_a2utils
index 46126282f..8ee30ecf1 100644
--- a/Completion/Debian/Command/_a2utils
+++ b/Completion/Debian/Command/_a2utils
@@ -1,6 +1,6 @@
 #compdef a2ensite a2dissite a2enmod a2dismod
 
-local -a mods
+local -a expl mods
 
 case "$service" in
     a2ensite)
@@ -21,4 +21,4 @@ case "$service" in
 	;;
 esac
 
-return 0
+return
diff --git a/Completion/Debian/Command/_apt b/Completion/Debian/Command/_apt
index 0bc20c6df..074fb0164 100644
--- a/Completion/Debian/Command/_apt
+++ b/Completion/Debian/Command/_apt
@@ -399,20 +399,32 @@ _apt-cmd () {
     -o,--option:arbitem \
     -t,--target-release:release \
     -- \
-    /$'list\0'/ \| \
+    /$'list\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
     /$'search\0'/ /$'[^\0]#\0'/ ':strings:pattern:' \| \
-    /$'show\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \
+    /$'showsrc\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
+    /$'show\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
+    /$'depends\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
+    /$'rdepends\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
+    /$'policy\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
     /$'update\0'/ \| \
     \( \
-    /$'install\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \
-    /$'[^\0/]#/'/ /$'[^\0/]#\0'/ ':release name::_apt_releases' \) \| \
+    /$'install\0'\|$'download\0'\|$'source\0'\|$'build-dep\0'/ \
+      /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \
+       \( \
+       /$'[^\0/]#/'/ /$'[^\0/]#\0'/ ':release name::_apt_releases' \| \
+       /$'[^\0=]#='/ /$'[^\0=]#\0'/ ':package version::_apt_versions_of_binary_package' \
+       \)  \
+    \) \| \
     /$'remove\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" installed' \# \| \
     /$'purge\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" installed' \# \| \
     /$'upgrade\0'/ \| \
+    /$'autoclean\0'/ \| \
+    /$'changelog\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
     /$'autoremove\0'/ \| \
     /$'full-upgrade\0'/ \| \
+    /$'dist-upgrade\0'/ \| \
     /$'edit-sources\0'/ \| \
-    /"[]"/	':argument-1::compadd "$expl_action[@]" list search show update install remove upgrade full-upgrade edit-sources autoremove purge'
+    /"[]"/	':argument-1::compadd "$expl_action[@]" list search showsrc show depends rdepends policy update install download source build-dep remove upgrade full-upgrade dist-upgrade edit-sources autoclean changelog autoremove purge'
 
   _apt-cmd () {
     local expl_action expl_packages
@@ -462,8 +474,13 @@ _apt-get () {
     /$'update\0'/ \| \
     /$'upgrade\0'/ \| \
     \( \
-    /$'install\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \
-    /$'[^\0/]#/'/ /$'[^\0/]#\0'/ ':release name::_apt_releases' \) \| \
+    /$'install\0'\|$'download\0'\|$'source\0'\|$'build-dep\0'/ \
+      /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \
+       \( \
+       /$'[^\0/]#/'/ /$'[^\0/]#\0'/ ':release name::_apt_releases' \| \
+       /$'[^\0=]#='/ /$'[^\0=]#\0'/ ':package version::_apt_versions_of_binary_package' \
+       \)  \
+    \) \| \
     /$'remove\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" installed' \# \| \
     /$'purge\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" installed' \# \| \
     /$'dist-upgrade\0'/ \| \
@@ -472,13 +489,11 @@ _apt-get () {
     /$'autoclean\0'/ \| \
     /$'changelog\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
     /$'check\0'/ \| \
-    /$'source\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
-    /$'build-dep\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
     /$'autoremove\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" installed' \# \| \
     /$'help\0/' \| \
     /$'markauto\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" installed' \# \| \
     /$'unmarkauto\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" installed' \# \| \
-    /"[]"/	':argument-1::compadd "$expl_action[@]" update upgrade install remove purge dist-upgrade dselect-upgrade clean autoclean changelog check source build-dep autoremove help markauto unmarkauto'
+    /"[]"/	':argument-1::compadd "$expl_action[@]" update upgrade install remove purge dist-upgrade dselect-upgrade clean autoclean changelog check source build-dep autoremove help markauto unmarkauto download'
 
   _apt-get () {
     local expl_action expl_packages
@@ -510,7 +525,7 @@ _apt-cache () {
     --installed:bool \
     -- \
     /$'help\0'/ \| \
-    /$'add\0'/ /$'[^\0]#\0'/ ':files:index files:_files "$expl[@]"' \# \| \
+    /$'add\0'/ /$'[^\0]#\0'/ ':files:index files:_files' \# \| \
     /$'gencaches\0'/ \| \
     /$'showpkg\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
     /$'showsrc\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
@@ -580,10 +595,10 @@ _apt-config () {
     /$'shell\0'/ \
       \( \
 	/$'[^\0]#\0'/ ':parameters:shell variable to assign:_parameters' \
-	/$'[^\0]#\0'/ ':values:configuration key:compadd "$expl[@]" - ${${(f)"$(apt-config dump 2>&1)"}% *}' \
+	/$'[^\0]#\0'/ ':values:configuration key:compadd - ${${(f)"$(apt-config dump 2>&1)"}% *}' \
       \) \# \| \
     /$'dump\0'/ \| \
-    /"[]"/	':argument-1:action:compadd "$expl[@]" shell dump'
+    /"[]"/	':argument-1:action:compadd shell dump'
 
   _apt-config "$@"
 }
@@ -592,8 +607,10 @@ _apt_releases_update () {
   if ( [[ ${+_apt_releases} -eq 0 ]] ||
       _cache_invalid APT_releases ) && ! _retrieve_cache APT_releases;
   then
+    local -a tmp=("${(f)$(apt-cache policy)}")
     _apt_releases=(
-${${${${(M)${(f)"$(apt-cache policy)"}:#*release*}#*a=}%%,*}:#now}
+${${${${(M)tmp:#*release*}#*a=}%%,*}:#now}
+${${${${(M)tmp:#*release*n=*}#*n=}%%,*}:#now}
     )
     typeset -U _apt_releases
     _store_cache APT_releases _apt_releases
@@ -613,6 +630,41 @@ _apt_releases () {
   _tags apt-releases && compadd -a _apt_releases
 }
 
+# Complete versions of the package named in $match[1].
+#
+# Interpret the package name as a binary package (even if there is
+# a source package by that name, too).
+_apt_versions_of_binary_package() {
+  local package_name=${match[1]%=} # $match was set by _regex_arguments
+  local line
+  local name version source
+  local -a kv
+
+  for line in ${(f)"$(_call_program versions-of-package "apt-cache madison $package_name")"}; do
+    # If $package_name is a source package name, we'll have lines of the form
+    #   $package_name | $version | ...Sources
+    #
+    # If $package_name is a binary package name, we'll have lines of the form
+    #   $package_name | $version | ...Packages
+    #   $src_package_name | $version | ...Sources
+    for name version source in "${(@s. | .)line}"; do
+      # Remove leading/trailing whitespace
+      name=${name// }
+      version=${version// }
+      source=${${source# ##}% ##}
+
+      # Skip source packages
+      if [[ $name != $package_name ]] || [[ $source != *Packages* ]]; then
+        continue
+      fi
+
+      kv+=( "${version//:/\\:}:$source" )
+    done
+  done
+
+  _describe -t apt-package-versions "package versions" kv "$@"
+}
+
 _apt_caching_policy () {
   local -a oldp
 
diff --git a/Completion/Debian/Command/_bts b/Completion/Debian/Command/_bts
index 75085a9af..70b95ef96 100644
--- a/Completion/Debian/Command/_bts
+++ b/Completion/Debian/Command/_bts
@@ -17,21 +17,23 @@ _arguments -A "-*" \
   '(--no-conf --noconf)*'{--no-conf,--noconf}'[do not read any config files]' \
   '*:subcommand and args:->subcmds' && return 0
 
-compset -N '-[^0-9]#' && first=0
+compset -N '-[^0-9]##' && first=0
 compset -N '[,.]' && first=0
 [[ $first -eq 0 ]] || compset -n 2
 
 if [[ CURRENT -eq 1 ]]; then
-  _wanted cmd expl 'bts command' compadd show bugs close reopen retitle \
+  _wanted cmd expl 'bts command' compadd show bugs reopen retitle \
       reassign merge unmerge tag tags severity forwarded notforwarded help \
       clone submitter found notfound block unblock user usertag usertags \
       package owner noowner reportspam cache cleancache claim unclaim \
-      subscribe unsubscribe fixed notfixed affects
+      subscribe unsubscribe fixed notfixed affects spamreport status \
+      select done archive unarchive summary forcemerge limit listcachedbugs \
+      version
   return
 fi
 
 case "$words[1]" in
-  (close|unmerge|notforwarded|noowner|reportspam)
+  (unmerge|notforwarded|noowner|reportspam|spamreport|archive|unarchive)
     if [[ CURRENT -eq 2 ]]; then
       _debbugs_bugnumber
     else
@@ -41,8 +43,9 @@ case "$words[1]" in
   (show|bugs)
     if [[ CURRENT -eq 2 ]]; then
       _alternative \
-	'packages:package:_deb_packages avail' \
-        'emails:package maintainer:compadd $DEBEMAIL'
+        'packages:package:_deb_packages avail' \
+        'emails:package maintainer:compadd $DEBEMAIL' \
+        'bugnum:bug number:_debbugs_bugnumber'
     else
       _wanted sep expl 'separator' compadd -S ' ' , .
     fi
@@ -74,7 +77,10 @@ case "$words[1]" in
      _wanted sep expl 'separator' compadd -S ' ' , .
     fi
   ;;
-  merge)
+  (status)
+    # TODO: some additional syntaxes aren't being completed.
+  ;&
+  (merge|forcemerge)
     _debbugs_bugnumber
     if [[ CURRENT -gt 2 ]]; then
      _wanted sep expl 'separator' compadd -S ' ' , .
@@ -199,14 +205,18 @@ case "$words[1]" in
   ;;
   (cache)
      _alternative \
-       'package:package:_deb_packages avail' \
-       'email:email address:_email_addresses -c' \
+       'source-packages:source package:_deb_packages -P "src:" source' \
+       'package:binary package:_deb_packages avail' \
+       'email:email address:_email_addresses -c -P "from:"' \
+       'bugnum:bug number:_debbugs_bugnumber' \
        'rc:rc:compadd release-critical'
   ;;
   (cleancache)
      _alternative \
-       'package:package:_deb_packages avail' \
-       'email:email address:_email_addresses -c' \
+       'source-packages:source package:_deb_packages -P "src:" source' \
+       'package:binary package:_deb_packages avail' \
+       'email:email address:_email_addresses -c -P "from:"' \
+       'bugnum:bug number:_debbugs_bugnumber' \
        'all:all:compadd ALL'
   ;;
   (claim|unclaim)
@@ -229,6 +239,51 @@ case "$words[1]" in
       _wanted package expl 'package' _deb_packages avail
     fi
   ;;
+  (summary)
+    case $CURRENT in
+      (2) _debbugs_bugnumber;;
+      (3) _message -e message-number 'message number';&
+      (4) _wanted sep expl 'separator' compadd -S ' ' , .;;
+    esac
+  ;;
+  (close|done)
+    case $CURRENT in
+      (2) _debbugs_bugnumber;;
+      (3) _message -e version 'version';&
+      (4) _wanted sep expl 'separator' compadd -S ' ' , .;;
+    esac
+  ;;
+  (select)
+    _values -S : -w "select field" \
+      '*package[binary package]: :_deb_packages avail' \
+      '*source[source package]: :_deb_packages source' \
+      '*maintainer:email address of the maintainer:_email_addresses -c' \
+      '*submitter:email address of the submitter:_email_addresses -c' \
+      '*severity:severity:(wishlist minor normal important serious grave critical)' \
+      '*tag[tags applied to the bug]:tags:' \
+      '*owner:owner:_email_addresses -c' \
+      '*correspondent:email address of a correspondent:_email_addresses -c' \
+      '*affects:affected package:_deb_packages avail' \
+      '*users:namespaces of usertags:_email_addresses -c' \
+      '*archive:whether to search archived bugs:((0:no 1:yes both:both))'
+      # undocumented: bugs
+    _wanted sep expl 'separator' compadd -S ' ' , .
+  ;;
+  (limit)
+    _values -S : -w "limit field" \
+      '*submitter[email address of the submitter]:submitter:_email_addresses -c' \
+      '*date[bug submission timestamp]:unix timestamp' \
+      '*subject[subject of the bug]:bug subject:' \
+      '*msgid[message-id of the initial bug report]:message-id:' \
+      '*package[binary package]: :_deb_packages avail' \
+      '*source[source package]: :_deb_packages source' \
+      '*tag[tags applied to the bug]:tags:' \
+      '*severity:severity:(wishlist minor normal important serious grave critical)' \
+      '*owner:owner:_email_addresses -c' \
+      '*affects:affected package:_deb_packages avail' \
+      '*archive:whether to search archived bugs:((0:no 1:yes both:both))'
+    _wanted sep expl 'separator' compadd -S ' ' , .
+  ;;
   help)
   ;&
   *) _wanted sep expl 'separator' compadd -S ' ' , .
diff --git a/Completion/Debian/Command/_bug b/Completion/Debian/Command/_bug
index 2dc14d488..436d294f9 100644
--- a/Completion/Debian/Command/_bug
+++ b/Completion/Debian/Command/_bug
@@ -7,7 +7,6 @@ _bug_commonargs=(
   '-m[maintainer-only]'
   '-p[print to stdout instead of mail]'
   '-h[help]'
-  '*:package:_deb_packages installed'
 )
 
 _rb_commonargs=(
@@ -29,6 +28,7 @@ case "$service" in
       '-v[version]' \
       '-x[do not cc submitter]' \
       '-z[send configs verbatim]' \
+      '*:package:_deb_packages installed' \
       "$_bug_commonargs[@]"
   ;;
   reportbug)
@@ -92,6 +92,7 @@ case "$service" in
       '(--paranoid)--no-paranoid' \
       '--body=:message body string' \
       '--body-file=:message body file:_files' \
+      '*:package: _alternative "packages::_deb_packages installed" "files::{ [[ \$PREFIX[1] == / ]] && _files }"' \
       "$_bug_commonargs[@]" \
       "$_rb_commonargs[@]"
   ;;
diff --git a/Completion/Debian/Command/_dchroot b/Completion/Debian/Command/_dchroot
index c26e5691a..2a6f5d808 100644
--- a/Completion/Debian/Command/_dchroot
+++ b/Completion/Debian/Command/_dchroot
@@ -2,6 +2,7 @@
 
 local expl context state line
 typeset -A opt_args
+local -a _comp_priv_prefix
 
 _arguments -S \
        '(-h --help)'{-h,--help}'[help]' \
diff --git a/Completion/Debian/Command/_dchroot-dsa b/Completion/Debian/Command/_dchroot-dsa
index d4668b553..e8e981b84 100644
--- a/Completion/Debian/Command/_dchroot-dsa
+++ b/Completion/Debian/Command/_dchroot-dsa
@@ -2,6 +2,7 @@
 
 local expl context state line
 typeset -A opt_args
+local -a _comp_priv_prefix
 
 _arguments -S \
        '(-h --help)'{-h,--help}'[help]' \
diff --git a/Completion/Debian/Command/_dcut b/Completion/Debian/Command/_dcut
index 210d54429..fb1ac7e4f 100644
--- a/Completion/Debian/Command/_dcut
+++ b/Completion/Debian/Command/_dcut
@@ -106,7 +106,7 @@ function _dcut() {
     (rm)
       # dcut rm arguments
       opts=(
-        '--searchdirs[search in all directores for the given files]'
+        '--searchdirs[search in all directories for the given files]'
         '1::file to be deleted:_files'
       )
       ;;
@@ -128,7 +128,7 @@ function _dcut() {
       opts=(
         '--uid[full name and address or GPG fingerprint of the Debian Maintainer]'
         '*--allow[grant permission to upload a source package]:source package'
-        '*--deny[remove permission to upload a source pckage]:source package'
+        '*--deny[remove permission to upload a source package]:source package'
       )
       ;;
     (reschedule)
@@ -142,7 +142,7 @@ function _dcut() {
       # dcut rm arguments
       opts=(
         '*'{-f,--filename}'[file name to be removed]:file name:_files'
-        '--searchdirs[search in all directores for the given files]'
+        '--searchdirs[search in all directories for the given files]'
       )
       ;;
     (upload)
diff --git a/Completion/Debian/Command/_debfoster b/Completion/Debian/Command/_debfoster
index 154d0e913..08a1078e2 100644
--- a/Completion/Debian/Command/_debfoster
+++ b/Completion/Debian/Command/_debfoster
@@ -26,10 +26,10 @@ _arguments -C \
   && ret=0
 
 if [[ -n "$state" ]]; then
-  if compset -P '*='; then
+  if compset -P 1 '*='; then
     case "$IPREFIX" in
     *(#i)(install|remove|info)cmd*)
-      _wanted values expl 'command string' _command && ret=0
+      _wanted values expl 'command string' _cmdstring && ret=0
       ;;
     *(#i)(keeperfile|dpkg(status|available))*)
       _wanted values expl 'metadata file' _files && ret=0
diff --git a/Completion/Debian/Command/_git-buildpackage b/Completion/Debian/Command/_git-buildpackage
index a2dc65689..c38edc1cc 100644
--- a/Completion/Debian/Command/_git-buildpackage
+++ b/Completion/Debian/Command/_git-buildpackage
@@ -4,7 +4,7 @@
 _arguments \
   '--version[show program version number and exit]' \
   '--help[show help message and exit]' \
-  '--git-ignore-new[build with uncommited changes in the source tree]' \
+  '--git-ignore-new[build with uncommitted changes in the source tree]' \
   '--git-no-ignore-new[negates --git-ignore-new]' \
   '--git-verbose[verbose command execution]' \
   '--git-tag[create a tag after a successful build]' \
@@ -40,4 +40,4 @@ _arguments \
   '--git-dont-purge[retain exported package build directory]' \
   '--git-overlay[extract orig tarball when using export-dir option]' \
   '--git-no-overlay[negates --git-overlay]' \
-  '*:Other options:_dpkg-buildpackage'
+  '*:other options:_dpkg-buildpackage'
diff --git a/Completion/Debian/Command/_lighttpd b/Completion/Debian/Command/_lighttpd
index 7f4385b90..c24b42d13 100644
--- a/Completion/Debian/Command/_lighttpd
+++ b/Completion/Debian/Command/_lighttpd
@@ -1,16 +1,16 @@
 #compdef lighty-enable-mod lighty-disable-mod
 
-local -a mods
+local -a mods expl
 
 case "$service" in
     lighty-enable-mod)
 		mods=( `echo /etc/lighttpd/conf-available/*.conf(N:r:t) | sed -e 's/\b[0-9][0-9]-//g'` )
-		_wanted mods expl mods compadd -a mods
+		_wanted mods expl mod compadd -a mods
 	;;
     lighty-disable-mod)
 		mods=( `echo /etc/lighttpd/conf-enabled/*.conf(N:r:t) | sed -e 's/\b[0-9][0-9]-//g'` )
-		_wanted mods expl mods compadd -a mods
+		_wanted mods expl mod compadd -a mods
 	;;
 esac
 
-return 0
+return
diff --git a/Completion/Debian/Command/_lintian b/Completion/Debian/Command/_lintian
index 773e7a182..d60acc9ed 100644
--- a/Completion/Debian/Command/_lintian
+++ b/Completion/Debian/Command/_lintian
@@ -1,6 +1,6 @@
 #compdef lintian lintian-info
 
-local line cmds ret=1
+local curcontext="$curcontext" state line expl cmds ret=1
 
 case "$service" in
    (lintian)
@@ -23,7 +23,7 @@ case "$service" in
       '(-o --no-override)'{-o,--no-override}'[do not use the overrides file]' \
       '--show-overrides[output tags that have been overridden]' \
       '--color:when:(never always auto)' \
-      '(-U --unpack-info)'{-U,--unpack-info}'[collect informations]:infos:_values -s , "collectibles" changelog-file copyright-file debfiles debian-readme diffstat doc-base-files file-info init.d md5sums menu-files objdump-info override-file scripts source-control-file' \
+      '(-U --unpack-info)'{-U,--unpack-info}'[collect information]:info:_values -s , "collectibles" changelog-file copyright-file debfiles debian-readme diffstat doc-base-files file-info init.d md5sums menu-files objdump-info override-file scripts source-control-file' \
       '(-m --md5sums)'{-m,--md5sums}'[check md5sums when processing a .changes file]' \
       '--allow-root[override warning when run with superuser privileges]' \
       '--cfg:config file:_files' \
@@ -55,7 +55,7 @@ case "$service" in
         (args)
           case $line[1] in
             -t|--tags)
-              _wanted tag expl 'tag' compadd $(command awk '/^Tag:/ { print $2 }' /usr/share/lintian/checks/*.desc) && ret=0
+              _wanted tags expl 'tag' compadd $(command awk '/^Tag:/ { print $2 }' /usr/share/lintian/checks/*.desc) && ret=0
             ;;
           esac
         ;;
diff --git a/Completion/Debian/Command/_make-kpkg b/Completion/Debian/Command/_make-kpkg
index 55adf882f..252627a79 100644
--- a/Completion/Debian/Command/_make-kpkg
+++ b/Completion/Debian/Command/_make-kpkg
@@ -40,7 +40,7 @@ _arguments -C \
         build\:"compiles the kernel" \
         modules\:"build all add-on modules" \
         modules-config\:"configure all add-on modules" \
-        modules-image\:"build all add-on modules, but wirhou source and diff files" \
+        modules-image\:"build all add-on modules, but without source and diff files" \
         modules-clean\:"clean add-on modules" \
         configure\:"configure the kernel" \
         debian\:"creates the debian/ directory" \
diff --git a/Completion/Debian/Command/_schroot b/Completion/Debian/Command/_schroot
index f10236860..117df45ef 100644
--- a/Completion/Debian/Command/_schroot
+++ b/Completion/Debian/Command/_schroot
@@ -2,6 +2,7 @@
 
 local expl context state line
 typeset -A opt_args
+local -a _comp_priv_prefix
 
 _arguments -S \
        '(-h --help)'{-h,--help}'[help]' \
@@ -20,6 +21,7 @@ _arguments -S \
        '(-v --verbose)'{-v,--verbose}'[verbose]' \
        '(-V --version)'{-V,--version}'[version]' \
        '(-b --begin-session)'{-b,--begin-session}'[begin a session; returns a session ID]' \
+       '(-r --run-session)'{-r,--run-session}'[run an existing session]' \
        '--recover-session[recover an existing session]' \
        '(-e --end-session)'{-e,--end-session}'[end an existing session]' \
        '(-f --force)'{-f,--force}'[force operation]' \
diff --git a/Completion/Debian/Command/_wajig b/Completion/Debian/Command/_wajig
index 350eee658..26d08cfd7 100644
--- a/Completion/Debian/Command/_wajig
+++ b/Completion/Debian/Command/_wajig
@@ -1,6 +1,6 @@
 #compdef wajig
 
-local curcontext="$curcontext" state line cmds argno ret=1
+local curcontext="$curcontext" state line expl cmds argno ret=1
 
 _arguments -C -s \
   '(- 1 *)'{-h,--help}'[print usage message]' \
diff --git a/Completion/Debian/Type/_deb_architectures b/Completion/Debian/Type/_deb_architectures
index 22c43dd3e..1429112a8 100644
--- a/Completion/Debian/Type/_deb_architectures
+++ b/Completion/Debian/Type/_deb_architectures
@@ -1,6 +1,6 @@
 #autoload
 
-local extra
+local extra expl
 zparseopts -E -D -a extra a:
 
 _description architectures expl 'architecture'
diff --git a/Completion/Debian/Type/_deb_packages b/Completion/Debian/Type/_deb_packages
index d82515cef..bdc0cb00c 100644
--- a/Completion/Debian/Type/_deb_packages
+++ b/Completion/Debian/Type/_deb_packages
@@ -122,7 +122,7 @@ _deb_packages () {
 
   typeset -gH $cachevar
 
-  _tags packages && compadd "$expl[@]" - "${(@P)cachevar}"
+  _tags packages && compadd "$expl[@]" -a - $cachevar
 }
 
 _debs_caching_policy () {
diff --git a/Completion/Debian/Type/_debbugs_bugnumber b/Completion/Debian/Type/_debbugs_bugnumber
index 2c10998ac..85e0c60d6 100644
--- a/Completion/Debian/Type/_debbugs_bugnumber
+++ b/Completion/Debian/Type/_debbugs_bugnumber
@@ -1,4 +1,11 @@
 #autoload
 # TODO: use _describe with some basic metadata (e.g., bug title/package/version)
-[[ $PREFIX$SUFFIX == [0-9]# ]] &&
-_wanted -x bugnum expl 'bug number' compadd ~/.devscripts_cache/bts/<->.(html|mbox)(N:t:r)
+
+local expl
+
+[[ $PREFIX$SUFFIX == [0-9]# ]] || return 1
+
+# The cache directory moved; try both locations.
+# ### TODO: Use 'bts listcachedbugs'?
+local -a cachedirs=( ~/.devscripts_cache/bts ~/.cache/devscripts/bts )
+_wanted -x bugnum expl 'bug number' compadd -- $^cachedirs/<->.(html|mbox)(N:t:r)
diff --git a/Completion/Linux/Command/_cpupower b/Completion/Linux/Command/_cpupower
new file mode 100644
index 000000000..6713323cb
--- /dev/null
+++ b/Completion/Linux/Command/_cpupower
@@ -0,0 +1,119 @@
+#compdef cpupower
+
+local curcontext="$curcontext" ret=1
+local -a state line expl cmds args
+typeset -A opt_args
+
+_arguments -C \
+  '(- :)'{-h,--help}'[print help information]' \
+  '(- :)'{-v,--version}'[print version information]' \
+  '(-d --debug)'{-d,--debug}'[enable debug output]' \
+  '(-c --cpu)'{-c,--cpu}'[limit values to specific processor cores]:cpu' \
+  ':cmd:->cmds' \
+  '*::arg:->args' && ret=0
+
+cmds=(
+  'frequency-info:show current frequency info'
+  'frequency-set:set frequency parameters'
+  'idle-info:show current idle state info'
+  'idle-set:set idle state parameters'
+  'info:show global power parameters'
+  'set:set global power parameters'
+  'monitor:report frequency and idle statistics'
+  'help:print usage information'
+)
+case $state in
+  cmds)
+    _describe command cmds && ret=0
+  ;;
+  args)
+    curcontext="${curcontext%:*}-$words[1]"
+    case ${words[1]} in
+      help)
+        _describe command cmds
+        return
+      ;;
+      frequency-info)
+        args=(
+          '(-m --human)'{-m,--human}'[use human readable output]'
+          '(-n --no-rounding)'{-n,--no-rounding}'[disable rounding of values]'
+          - '(info)'
+          {-e,--debug}'[print debug info]'
+          {-f,--freq}'[show current frequency]'
+          {-w,--hwfreq}'[show current hardware frequency]'
+          {-l,--hwlimits}'[show min/max frequency allowed]'
+          {-d,--driver}'[show the kernel driver in use]'
+          {-p,--policy}'[show the current cpufreq policy]'
+          {-g,--governors}'[show available governers]'
+          {-r,--related-cpus}'[show cpus that run at the same frequency]'
+          {-a,--affected-cpus}'[show software controlled cpus]'
+          {-s,--stats}'[show cpufreq statistics]'
+          {-y,--latency}'[show frequency change latency]'
+        )
+        [[ -n $opt_args[(i)-(c|-cpu)] ]] || args+=(
+          {-o,--proc}'[print old style proc info]'
+        )
+      ;;
+      frequency-set)
+        args=(
+          '(-d --min)'{-d+,--min}'[new minimum frequency]:frequency:->frequencies'
+          '(-u --max)'{-u+,--max}'[new maximum frequency]:frequency:->frequencies'
+          '(-g --governor)'{-g+,--governor}'[new cpufreq governor]:governor:->governors'
+          '(-)'{-f+,--freq}'[new frequency for userspace governor]:frequency:->frequencies'
+          '(-r --related)'{-r,--related}'[modify all hardware related cpus]'
+        )
+      ;;
+      idle-info)
+        args=(
+          '(-f --silent)'{-f,--silent}'[print summary only]'
+          '(-e --proc)'{-e,--proc}'[print old style proc info (deprecated)]'
+        )
+      ;;
+      idle-set)
+        args=(
+          '(-d --disable)'{-d+,--disable}'[disable specific sleep state]:state no'
+          '(-e --enable)'{-e+,--enable}'[enable specific sleep state]:state no'
+          '(-D --disable-by-latency)'{-D+,--disable-by-latency}'[disable state based on latency]:latency'
+          '(-E --enable-all)'{-E,--enable-all}'[enable all idle states]'
+        )
+      ;;
+      info)
+        args=(
+          '(-b --perf-bias)'{-b,--perf-bias}'[show intel performance bias value]'
+        )
+      ;;
+      set)
+        args=(
+          '(-b --perf-bias)'{-b+,--perf-bias}'[set intel performance bias value]:performance bias'
+        )
+      ;;
+      monitor)
+        args=(
+          '(-)-l[list available monitors]'
+          '-m+[display specified monitors]:monitor:->monitors'
+          '-i+[measurement interval]:interval (seconds)'
+          '-c[schedule on every core]'
+          '-v[increase verbosity]'
+          '*:::command: _normal'
+        )
+      ;;
+    esac
+    _arguments -C -s "$args[@]" && ret=0
+    case $state in
+      frequencies)
+        _wanted -x frequencies expl frequency compadd $(cpupower frequency-info |
+               sed -n 's/ //g; s/,/ /g; s/availablefrequencysteps://p') && ret=0
+      ;;
+      governors)
+        _wanted governors expl 'scaling governor' compadd \
+            ${=${"$(_call_program governors cpupower frequency-info -g)"}##*:} && ret=0
+      ;;
+      monitors)
+        _sequence _wanted monitors expl 'monitor' compadd - ${${${(M)${(f)"$(_call_program monitors \
+            cpupower monitor -l)"}:#Monitor *}#*\"}%%\"*} && ret=0
+      ;;
+    esac
+  ;;
+esac
+
+return ret
diff --git a/Completion/Linux/Command/_ethtool b/Completion/Linux/Command/_ethtool
index 5d607741f..71f5ed3bf 100644
--- a/Completion/Linux/Command/_ethtool
+++ b/Completion/Linux/Command/_ethtool
@@ -1,6 +1,6 @@
 #compdef ethtool
 
-local -a cmds
+local -a expl cmds
 
 if [[ $CURRENT -ge 4 ]]; then
 	case $words[CURRENT-1] in
diff --git a/Completion/Linux/Command/_fusermount b/Completion/Linux/Command/_fusermount
index d3d1647fa..02cb57237 100644
--- a/Completion/Linux/Command/_fusermount
+++ b/Completion/Linux/Command/_fusermount
@@ -20,7 +20,7 @@ case "$state" in
     _files -/
   else
     mtpts=(${${${"${(f)$(< /etc/mtab)}"}#* }%% *})
-    _canonical_paths mounted 'mounted filesystem' $mtpts
+    _canonical_paths mounted 'mounted filesystem' "${(@g::)mtpts}"
   fi
   ;;
 esac
diff --git a/Completion/Linux/Command/_gpasswd b/Completion/Linux/Command/_gpasswd
index 9b4bedec6..24fe361b0 100644
--- a/Completion/Linux/Command/_gpasswd
+++ b/Completion/Linux/Command/_gpasswd
@@ -1,21 +1,12 @@
 #compdef gpasswd
-local curcontext=$curcontext state state_descr line
-typeset -A opt_args
 
-_arguments -C -w -s \
+_arguments -s \
   '(-a --add -d --delete)'{-a,--add}'[add user to group]: : _users' \
   '(-d --delete -a --add)'{-d,--delete}'[remove user from group]: : _users' \
-  '(-h --help)'{-h,--help}'[display help]' \
-  '(-Q --root)'{-Q,--root}'[directory to chroot into]: : _files -/' \
+  '(-)'{-h,--help}'[display help]' \
+  '(-Q --root)'{-Q,--root}'[specify directory to chroot into]: : _files -/' \
   '(-r --remove-password)'{-r,--remove-password}'[remove the group password]' \
   '(-R --restrict)'{-R,--restrict}'[restrict access to GROUP to its members]' \
-  '(-M --members -A --administrators)'{-M,--members}'[set the list of members of GROUP]: :->users' \
-  '(-A --administrators -M --members)'{-A,--administrators}'[set the list of admins for GROUP]: :->users' \
+  '(-M --members -A --administrators)'{-M,--members}'[set the list of members of GROUP]: :_sequence _users' \
+  '(-A --administrators -M --members)'{-A,--administrators}'[set the list of admins for GROUP]: :_sequence _users' \
   '1: : _groups'
-
-if [[ $state == users ]]; then
-  local -a ignore
-  compset -P '*,'; compset -S ',*'
-  ignore=( ${(s:,:)IPREFIX} ${(s:,:)ISUFFIX} )
-  _users -F ignore -qS ,
-fi
diff --git a/Completion/Linux/Command/_iconvconfig b/Completion/Linux/Command/_iconvconfig
new file mode 100644
index 000000000..a10e134e1
--- /dev/null
+++ b/Completion/Linux/Command/_iconvconfig
@@ -0,0 +1,13 @@
+#compdef iconvconfig
+
+local exargs="-? --help --usage -V --version"
+
+_arguments -S -s \
+    "($exargs)--nostdlib[do not search system directory]" \
+    "(-o --output $exargs)"{-o+,--output=}'[specify output file]:output file:_files' \
+    "($exargs)--prefix=[specify system dir prefix]:prefix:_files" \
+    '(- *)'{-\?,--help}'[display help information]' \
+    '(- *)--usage[display a short usage message]' \
+    '(- *)'{-V,--version}'[print program version]' \
+    '*:directory:_files -/' \
+    && return 0
diff --git a/Completion/Linux/Command/_iwconfig b/Completion/Linux/Command/_iwconfig
index 30892fb6d..07c028be4 100644
--- a/Completion/Linux/Command/_iwconfig
+++ b/Completion/Linux/Command/_iwconfig
@@ -50,7 +50,7 @@ if [[ -n "$state" ]]; then
       	'essid[set the network name]' \
 	'(nwid domain)'{nwid,domain}'[set the network ID]' \
 	'(freq channel)'{freq,channel}'[set the operating frequency or channel]' \
-	'sens[set the sensitivity threhold]' \
+	'sens[set the sensitivity threshold]' \
       	'mode[set operating mode]' \
 	'ap[register with given access point]' \
 	'(nick nickname)'nick{,name}'[set the nickname]' \
@@ -62,7 +62,7 @@ if [[ -n "$state" ]]; then
 	'txpower[set transmit power]' \
 	'retry[set number of retries]' \
 	'modu[force a specific set of modulations]' \
-	'commit[apply changes imediately]' && ret=0
+	'commit[apply changes immediately]' && ret=0
     ;;
   esac
 fi
diff --git a/Completion/Linux/Command/_lsblk b/Completion/Linux/Command/_lsblk
new file mode 100644
index 000000000..e1863cd06
--- /dev/null
+++ b/Completion/Linux/Command/_lsblk
@@ -0,0 +1,57 @@
+#compdef lsblk
+
+local sep ret=1
+local -a values dedup suf=( -qS , )
+local curcontext="$curcontext" state line expl
+typeset -A opt_args
+
+_arguments -C -s -S \
+  '(H -a --all)'{-a,--all}'[print all devices]' \
+  '(H -b --bytes)'{-b,--bytes}'[print size in bytes rather than in human readable format]' \
+  '(H -d --nodeps)'{-d,--nodeps}"[don't print slaves or holders]" \
+  '(H -I --include)*'{-e,--exclude}'[exclude devices by major number]:major device number:->majorlist' \
+  '(H -e --exclude)*'{-I+,--include=}'[show only devices with specified major numbers]:major device number:->majorlist' \
+  '(H -n --noheadings)'{-n,--noheadings}"[don't print headings]" \
+  '(H -p --paths)'{-p,--paths}'[print complete device path]' \
+  '(H -s --inverse)'{-s,--inverse}'[reverse dependency order]' \
+  '(H -x --sort)'{-x+,--sort=}'[sort output by specified column]:column:->columns' \
+  '*:device:_files -g "*(-%b)" -P / -W /' \
+  + fields \
+  '(H -D --discard -o --output -O --output-all)'{-D,--discard}'[output discard capabilities]' \
+  '(H -f --fs -o --output -O --output-all)'{-f,--fs}'[output info about filesystems]' \
+  '(H -m --perms -o --output -O --output-all)'{-m,--perms}'[output info about permissions]' \
+  '(H -S --scsi -o --output -O --output-all)'{-S,--scsi}'[output info about SCSI devices]' \
+  '(H -t --topology -o --output -O --output-all)'{-t,--topology}'[output info about topology]' \
+  '(H fields)'{-o+,--output=}'[specify output columns]:output column:->columnlist' \
+  '(H fields)'{-O,--output-all}'[output all columns]' \
+  + '(format)' \
+  '(H)'{-i,--ascii}'[output ascii characters only]' \
+  '(H)'{-J,--json}'[use JSON output format]' \
+  '(H)'{-l,--list}'[use list format output]' \
+  '(H)'{-P,--pairs}'[use key="value" output format]' \
+  '(H)'{-r,--raw}'[use raw output format]' \
+  + 'H' \
+  '(* -)'{-h,--help}'[display help information]' \
+  '(* -)'{-V,--version}'[display version information]' && ret=0
+
+case $state in
+  *list)
+    dedup=( ${(Ms.,.)PREFIX##*,} ${(Ms.,.)SUFFIX%%,*} )
+    compset -S ',*' && suf=()
+    compset -P '*,'
+  ;|
+  column*)
+    values=(
+      ${${${${(f)"$(_call_program columns lsblk -h)"}[(r)Available*,-3]## #}[2,-1]//:/\\:}/  /:}
+    )
+    _describe -t fields column values -M 'm:{a-z}={A-Z}' $suf -F dedup && ret=0
+  ;;
+  major*)
+    zstyle -s ":completion:${curcontext}:" list-separator sep || sep=--
+    values=( ${${${(f)"$(</proc/devices)"}[(r)Block*,-1]## #}[2,-1]/ /:} )
+    zformat -a values " $sep " $values
+    _wanted -V devices expl 'major device number' compadd $suf -d values -F dedup ${values%% *} && ret=0
+  ;;
+esac
+
+return ret
diff --git a/Completion/Linux/Command/_lsusb b/Completion/Linux/Command/_lsusb
index 17240e03d..129309b8b 100644
--- a/Completion/Linux/Command/_lsusb
+++ b/Completion/Linux/Command/_lsusb
@@ -1,19 +1,21 @@
 #compdef lsusb
 
-local context state line usbidsline vendorid pair
+local usbidsline vendorid pair ret=1
+local curcontext="$curcontext" state line expl
 typeset -A opt_args
 
-_arguments \
-  '(-v --verbose)'{-v,--verbose}'[be verbose]' \
-  '-s:bus and/or devnum to show:' \
-  '-d:vendor and product to show:->vendorproduct' \
-  '-D:device to show:_files' \
-  '-t[dump the physical USB device hierarchy as a tree]' \
-  '(-V --version)'{-V,--version}'[print version info and exit]' && return 0
+_arguments -C \
+  '(-v --verbose -t --tree)'{-v,--verbose}'[be verbose]' \
+  '-s+[filter devices by bus and/or device number]:bus and/or devnum to show' \
+  '-d+[filter devices by vendor/product ID]:vendor and product to show:->vendorproduct' \
+  '-D+[display only specified device]:device:_files -g "*(-%)" -P / -W /' \
+  '(-t --tree -v --verbose)'{-t,--tree}'[dump the physical USB device hierarchy as a tree]' \
+  '(-)'{-V,--version}'[print version information]' \
+  '(-)'{-h,--help}'[print help information]' && ret=0
 
-  if [[ ${+_lsusb_vendors} -eq 0 ]]; then
-    typeset -A _lsusb_vendors _lsusb_devices
-  while IFS="" read usbidsline
+if [[ -n $state && ${+_lsusb_vendors} -eq 0 ]]; then
+  typeset -A _lsusb_vendors _lsusb_devices
+  cat /usr/share/(misc|hwdata)/usb.ids | while IFS="" read usbidsline
   do
     case "$usbidsline" in
       ((#b)([0-9a-f]##) ##(*))
@@ -25,7 +27,9 @@ _arguments \
         _lsusb_devices[${pair}]="$match[2]"
       ;;
     esac
-  done < /usr/share/misc/usb.ids
+  done
 fi
 
-compadd -k _lsusb_devices
+_wanted products expl 'vendor ID:product ID' compadd -k _lsusb_devices && ret=0
+
+return ret
diff --git a/Completion/Linux/Command/_ltrace b/Completion/Linux/Command/_ltrace
new file mode 100644
index 000000000..e48d8ec98
--- /dev/null
+++ b/Completion/Linux/Command/_ltrace
@@ -0,0 +1,35 @@
+#compdef ltrace
+
+local root hlp="-h --help -V --version"
+
+(( EUID )) && root='!'
+
+_arguments -s -S $args \
+  "(-c -a --align $hlp)"{-a+,--align=}"[align return values in a secific column]:column [$((COLUMNS*5/8))]" \
+  "(-c $hlp)-A+[specify maximum number of array elements to print]:elements" \
+  "(-c -b --no-signals $hlp)"{-b,--no-signals}"[don't print signals]" \
+  "(-a --align -A -b --no-signals -i -n --indent -r -s -t -tt -ttt -T $hlp)-c[count time and calls, and report a summary on exit]" \
+  "(-C --demangle $hlp)"{-C,--demangle}'[decode low-level symbol names into user-level names]' \
+  "(-D --debug $hlp)"{-D+,--debug=}'[enable debugging]:mask:(help 77)' \
+  "($hlp)*-e+[modify which library calls to trace]:filter" \
+  "($hlp)-f[trace child processes]" \
+  "($hlp)*"{-F+,--config=}'[load alternate configuration file]:file:_files' \
+  '(- 1 *)'{-h,--help}'[display help information]' \
+  "(-c $hlp)-i[print instruction pointer at time of call]" \
+  "(-l --library $hlp)"{-l+,--library=}'[only trace symbols implemented by specified library]:library:_files' \
+  "($hlp)-L[don't display library calls]" \
+  "(-c -n --indent $hlp)"{-n+,--indent=}'[specify indent for each level of call nesting]:spaces' \
+  "(-o --output $hlp)"{-o+,--output=}'[write the trace output to specified file]:file:_files' \
+  "(: $hlp)-p+[attach to the process with specified process ID and begin tracing]:process ID:_pids" \
+  "(-c $hlp)-r[print relative timestamps]" \
+  "(-c $hlp)-s+[specify the maximum string size to print]:maximum string size [32]" \
+  "($hlp)-S[trace system calls as well as library calls]" \
+  "(-c -ttt $hlp)-t[prefix each line of the trace with the time of day]" \
+  "(-c -ttt -tt $hlp)-tt[prefix each line of the trace with the time of day including the microseconds]" \
+  "(-c -tt -t $hlp)-ttt[prefix each line of the trace with the number of seconds and microseconds since the epoch]" \
+  "(-c $hlp)-T[show the time spent in each call]" \
+  "${root}-u+[run as specified user]:user:_users" \
+  '(- 1 *)'{-V,--version}'[display version information]' \
+  "($hlp)*-x+[modify which static functions to trace]:filter" \
+  '(-):command name: _command_names -e' \
+  '*::arguments:_normal'
diff --git a/Completion/Linux/Command/_mdadm b/Completion/Linux/Command/_mdadm
index 940eb6887..b6dce7ccb 100644
--- a/Completion/Linux/Command/_mdadm
+++ b/Completion/Linux/Command/_mdadm
@@ -56,15 +56,15 @@ _layouts () {
         	                'f9[far copies]'
 			;;
 		faulty)
-			_values -S \  "Failure mode" \
+			_values -S \  "failure mode" \
 				{write-transient,wt}'[write-transient]' \
 				{read-transient,rt}'[read-transient]' \
-				{write-presistent,wp}'[write-presistent]' \
-				{read-presistent,rp}'[read-presistent]' \
+				{write-persistent,wp}'[write-persistent]' \
+				{read-persistent,rp}'[read-persistent]' \
 				write-all'[write-all]' \
 				{read-fixable,rf}'[read-fixable]' \
 				{clear,none}'[remove any pending or periodic failure modes]' \
-				flush'[clear any persistant faults]'
+				flush'[clear any persistent faults]'
 	esac
 }
 
@@ -78,7 +78,7 @@ if (( $+words[(r)-(A|-assemble)] )); then
 		'(--scan -s)'{--scan,-s}'[scan config file for missing information]'
 		'(--run -R)'{--run,-R}'[try to start the array even if not enough devices for a full array are present]'
 		'(--force -f)'{--force,-f}'[assemble the array even if some superblocks appear out-of-date]'
-		'(--update,-U)'{--update=,-U}'[update superblock]::update the superblock:(sparc2.2 summaries uuid resync byteorder super-minor)'
+		'(--update -U)'{--update=,-U}'[update superblock]::update the superblock:(sparc2.2 summaries uuid resync byteorder super-minor)'
 	)
 fi
 
diff --git a/Completion/Linux/Command/_modutils b/Completion/Linux/Command/_modutils
index 0732aa106..7de97f60e 100644
--- a/Completion/Linux/Command/_modutils
+++ b/Completion/Linux/Command/_modutils
@@ -123,7 +123,7 @@ _modutils() {
 	    ;;
 
 	params)
-	    if compset -P '*='; then
+	    if compset -P 1 '*='; then
 		_message -e value 'parameter value'
 	    else
 		local params
diff --git a/Completion/Linux/Command/_pidof b/Completion/Linux/Command/_pidof
new file mode 100644
index 000000000..6605e7e67
--- /dev/null
+++ b/Completion/Linux/Command/_pidof
@@ -0,0 +1,24 @@
+#compdef pidof
+
+local curcontext="$curcontext" state line expl ret=1
+typeset -A opt_args
+
+local exargs="-h --help -V --version"
+_arguments -C -s -w \
+  '(- *)'{-h,--help}'[display help information]' \
+  '(- *)'{-V,--version}'[print program version]' \
+  "(-s --single-shot $exargs)"{-s,--single-shot}'[return one PID only]' \
+  "(-c --check-root $exargs)"{-c,--check-root}'[omit processes with different root]' \
+  "(-x $exargs)"-x'[include shells running named scripts]' \
+  "($exargs)"\*{-o+,--omit-pid}'[omit processes with PIDs]:pids:_sequence -s , _pids' \
+  '*:process:->procnames' \
+  && return 0
+
+case $state in
+  procnames)
+    # Handle defunct processes and "avahi-daemon:"
+    _wanted process-names expl process compadd ${${${${${(@)${(f)"$(ps -N --ppid 2 -p 2 o args=)"}%% *}##*/}%:}#\[}%]} && ret=0
+  ;;
+esac
+
+return ret
diff --git a/Completion/Linux/Command/_pkgtool b/Completion/Linux/Command/_pkgtool
index 916e9e33f..cb14099b9 100644
--- a/Completion/Linux/Command/_pkgtool
+++ b/Completion/Linux/Command/_pkgtool
@@ -1,4 +1,4 @@
-#compdef installpkg upgradepkg removepkg pkgtool explodepkg makepkg 
+#compdef installpkg upgradepkg removepkg pkgtool explodepkg makepkg
 
 local expl
 
diff --git a/Completion/Linux/Command/_ss b/Completion/Linux/Command/_ss
index 95aa798db..90d83a4c4 100644
--- a/Completion/Linux/Command/_ss
+++ b/Completion/Linux/Command/_ss
@@ -19,6 +19,11 @@ _arguments -C -s \
   "($info -p --processes)"{-p,--processes}'[show process using each socket]' \
   "($info -i --info)"{-i,--info}'[show internal TCP information]' \
   "($info -s --summary)"{-s,--summary}'[print summary statistics]' \
+  "($info -b --bpf)"{-b,--bpf}'[show bpf filter socket information]' \
+  "($info -E --events)"{-E,--events}'[continually display sockets as they are destroyed]' \
+  "($info -Z --context)"{-Z,--context}'[display process SELinux security contexts]' \
+  "($info -z --contexts)"{-z,--contexts}'[display process and socket SELinux security contexts]' \
+  "($info -N --net)"{-N,--net}'[switch to specified network namespace]:network namespace' \
   "($info -4 --ipv4 -6 --ipv6)"{-4,--ipv4}'[display only IP version 4 sockets]' \
   "($info -4 --ipv4 -6 --ipv6)"{-6,--ipv6}'[display only IP version 6 sockets]' \
   "($info -0 --packet)"{-0,--packet}'[display PACKET sockets]' \
@@ -27,8 +32,10 @@ _arguments -C -s \
   "($info -d --dccp)"{-d,--dccp}'[display DCCP sockets]' \
   "($info -w --raw)"{-w,--raw}'[display RAW sockets]' \
   "($info -x --unix)"{-x,--unix}'[display Unix domain sockets]' \
-  "($info -f --family)"{-f,--family}'[display sockets of specified type]:family:(unix inet inet6 link netlink)' \
-  "($info -A --query --socket)"{-A,--query,--socket}'[specify socket tables to show]: :_values -s , socket\ table all inet tcp udp raw unix packet netlink unix_dgram unix_stream packet_raw packet_dgram' \
+  "($info -f --family)"{-f,--family}'[display sockets of specified type]:family:(unix inet inet6 link netlink unix)' \
+  "($info -K --kill)"{-K,--kill}'[forcibly close sockets, display what was closed]' \
+  "($info -H --no-header)"{-H,--no-header}'[suppress header line]' \
+  "($info -A --query --socket)"{-A,--query,--socket}'[specify socket tables to show]: :_values -s , socket\ table all inet tcp udp raw unix packet netlink unix_dgram unix_stream unix_seqpacket packet_raw packet_dgram' \
   "($info -D)"{-D,--diag=}'[dump raw info to file]:file:_files' \
   "($info -F)"{-F,--filter=}'[read filter information from a file]:file:_files' \
   "($info)*: :->filter" && ret=0
diff --git a/Completion/Linux/Command/_sshfs b/Completion/Linux/Command/_sshfs
index 534e806e3..fe976288d 100644
--- a/Completion/Linux/Command/_sshfs
+++ b/Completion/Linux/Command/_sshfs
@@ -1,39 +1,65 @@
 #compdef sshfs
 
-local context state state_descr line
+local curcontext="$curcontext" state state_descr line
 typeset -A opt_args
-local curcontext="$curcontext"
 integer ret=1
 
 _arguments -C \
-  '-V[version]' \
-  '-p:tcp port:' \
-  '-C[compression]' \
-  '-o:options:->options' \
-  '-d[debug]' \
+  '(-)'{-h,--help}'[display help information]' \
+  '(-)'{-V,--version}'[display version information]' \
+  '-p[specify TCP port]:tcp port:_ports' \
+  '-C[enable compression]' \
+  '-F[specify ssh config file]:file:_files' \
+  '-o[specify mount options]:options:->options' \
+  '(-f)-d[enable debug output]' \
   '-f[foreground]' \
   '-s[disable multithreaded operation]' \
-  '-r[mount read-only]' \
-  '-h[help]' \
   ':remote directory:_user_at_host -S:' \
   ':mountpoint:_files -/' && ret=0
 
 if [[ $state == options ]]; then
   _values -s , "sshfs or fuse or mount options" \
-    reconnect sshfs_sync no_readahead sshfs_debug \
+    debug reconnect delay_connect sshfs_sync no_readahead sync_readdir sshfs_debug \
     'cache:cache setting:(yes no)' \
-    cache_timeout:seconds: \
-    cache_stat_timeout:seconds: \
-    cache_dir_timeout:seconds: \
-    cache_link_timeout:seconds: \
+    'cache_max_size:size [10000]' \
+    'cache_timeout:timeout (seconds) [20]' \
+    cache_{stat,dir,link}_timeout:'timeout (seconds)' \
+    'cache_clean_interval:interval [60]' \
+    'cache_min_clean_interval:interval [5]' \
+    'workaround:workaround:(none all rename delaysrv truncate nobuflimit)' \
+    'idmap:user/group mapping:(none user file)' \
+    uidfile:file:_files \
+    gidfile:file:_files \
+    'nomap:type:(ignore error)' \
     'ssh_command:ssh command:_command_names' \
-    directport:port: \
-    'SSHOPT:ssh option:' \
-    default_permissions allow_other allow_root kernel_cache large_read direct_io \
-    max_read:size: \
-    hard_remove debug \
-    fs_name:name: \
-    use_ino readdir_ino && ret=0
+    'ssh_protocol:version:(1 2)' \
+    sftp_server:path:_files \
+    directport:port:_ports \
+    slave disable_hardlink transform_symlinks follow_symlinks no_check_root password_stdin \
+    allow_other allow_root auto_unmount nonempty default_permissions \
+    fsname:filesystem\ name \
+    subtype:filesystem\ type \
+    large_read \
+    max_read:max\ size \
+    hard_remove use_ino readdir_ino direct_io kernel_cache auto_cache \
+    'umask:permissions' \
+    'uid:owner' 'gid:group' \
+    'entry_timeout:timeout (seconds) [1]' \
+    'negative_timeout:timeout (seconds) [0]' \
+    'attr_timeout:timeout (seconds) [1]' \
+    'ac_attr_timeout:timeout (seconds) [= attr_timeout]' \
+    noforget \
+    'remember:time (seconds)' \
+    nopath intr \
+    'intr_signal:signal [10]' \
+    modules:module \
+    max_write:size \
+    max_readahead:readahead \
+    max_background:number \
+    congestion_threshold:threshold \
+    async_read sync_read atomic_o_trunc big_writes no_remote_lock no_remote_flock \
+    no_remote_posix_lock splice_write splice_move splice_read \
+    from_code:charset to_code:charset subdir:_directories rellinks && ret=0
 fi
 
 return ret
diff --git a/Completion/Linux/Command/_strace b/Completion/Linux/Command/_strace
index d6dabfd24..cbf95d6c4 100644
--- a/Completion/Linux/Command/_strace
+++ b/Completion/Linux/Command/_strace
@@ -1,119 +1,101 @@
-#compdef strace
+#compdef strace strace64
 
-# TODO:
-#	- make _sys_calls system-dependent
-#	- allow negated calls (e.g. -e!write)
-_sys_calls () {
-	local expl
-	local -a sys_calls
+local curcontext="$curcontext" state line root expl ret=1
+typeset -A opt_args
 
-	sys_calls=(_llseek _newselect _sysctl accept access acct
-		adjtimex afs_syscall alarm bdflush bind break brk cacheflush
-		capget capset chdir chmod chown chown32 chroot clone close connect
-		creat create_module delete_module dup dup2 execve exit fchdir
-		fchmod fchown fchown32 fcntl fcntl64 fdatasync flock fork fstat
-		fstat64 fstatfs fsync ftime ftruncate ftruncate64 get_kernel_syms
-		getcwd getdents getdents64 getegid getegid32 geteuid geteuid32
-		getgid getgid32 getgroups getgroups32 getitimer getpagesize getpeername
-		getpmsg getpgid getpgrp getpid getppid getpriority getresgid getresgid32
-		getresuid getresuid32 getrlimit getrusage getsid getsockname getsockopt
-		gettid gettimeofday getuid getuid32 gtty idle init_module ioctl ioperm
-		iopl ipc kill lchown lchown32 link listen lock lseek lstat lstat64
-		madvise mincore mkdir mknod mlock mlockall mmap modify_ldt mount mprotect
-		mpx mremap msync munlock munlockall munmap nanosleep nfsservctl nice
-		oldfstat oldlstat oldolduname oldstat oldumount olduname open pause
-		personality phys pipe pivot_root poll prctl pread prof profil ptrace
-		putpmsg pwrite query_module quotactl read readahead readdir readlink
-		readv reboot recv recvfrom recvmsg rename rmdir rt_sigaction
-		rt_sigpending rt_sigprocmask rt_sigqueueinfo rt_sigreturn rt_sigsuspend
-		rt_sigtimedwait sched_get_priority_max sched_get_priority_min
-		sched_getparam sched_getscheduler sched_rr_get_interval sched_setparam
-		sched_setscheduler sched_yield security select sendfile send sendmsg sendto
-		setdomainname setfsgid setfsgid32 setfsuid setfsuid32 setgid setgid32
-		setgroups setgroups32 sethostname setitimer setpgid setpriority setregid
-		setregid32 setresgid setresgid32 setresuid setresuid32 setreuid setreuid32
-		setrlimit setsid setsockopt settimeofday setuid setuid32 setup sgetmask
-		shutdown sigaction sigaltstack signal sigpending sigprocmask sigreturn
-		sigsuspend socket socketcall socketpair ssetmask stat stat64 statfs stime
-		stty swapoff swapon symlink sync sysfs sysinfo syslog time times truncate
-		truncate64 ulimit umask umount uname unlink uselib ustat utime vfork vhangup
-		vm86 vm86old wait4 waitpid write writev)
+(( EUID )) && root='!'
 
-	for t in ${(s:,:)${PREFIX}}; do
-		sys_calls=( ${sys_calls:#$t} )
-	done
-	compset -P '*,'
-	_wanted sys_calls expl 'System calls' compadd -qS , -a sys_calls
-}
+_arguments -C -s \
+  '-a+[align return values in a specific column]:column number [40]' \
+  '(-c)-i[print instruction pointer at time of syscall]' \
+  '-o+[write the trace output to the file]:output file:->file-pipe' \
+  '-q[suppress messages about attaching, detaching etc.]' \
+  '(-q)-qq[suppress messages about process exit status]' \
+  '(-c)-r[print a relative timestamp upon entry to each system call]' \
+  '-s+[specify the maximum string size to print]:maximum string size [32]' \
+  '(-c -ttt)-t[prefix each line of the trace with the time of day]' \
+  '(-c -ttt -tt)-tt[prefix each line of the trace with the time of day including the microseconds]' \
+  '(-c -tt -t)-ttt[prefix each line of the trace with the number of seconds and microseconds since the epoch]' \
+  '(-c)-T[show the time spent in system calls]' \
+  '(-xx)-x[print all non-ASCII strings in hexadecimal string format]' \
+  '(-x)-xx[print all strings in hexadecimal string format]' \
+  '(-c -yy)-y[print paths associated with file descriptor arguments]' \
+  '(-c -y)-yy[print protocol specific information associated with socket file descriptors]' \
+  '(-C -i -k -r -ff -t -tt -ttt -T -y -yy)-c[count time, calls, and errors for each system call and report a summary]' \
+  '(-c)-C[count time, calls, and errors for each system call and report a summary in addition to regular output]' \
+  '-O+[overhead for tracing system calls]:overhead (microseconds)' \
+  '-S+[sort the output of the histogram (-c option) by the specified criterion]:sort criterion:(time calls name nothing)' \
+  '-w[summarise syscall latency]' \
+  '*-e+[select events to trace or how to trace]:system call:->expressions' \
+  '*-P+[trace only system calls accessing given path]:path:_files' \
+  '-b+[detach from process on specified syscall]:syscall:(execve)' \
+  '-f[trace child processes as they are created by currently traced processes]' \
+  '(-c -C)-ff[write each process trace to <filename>.<pid> (when using -o <filename>]' \
+  '-D[run tracer as detached grandchild, keeping traced process as direct child of calling process]' \
+  '-I+[when strace can be interrupted by signals]:interruptible:((1\:"no signals are blocked" 2\:"fatal signals are blocked while decoding syscall (default)" 3\:"fatal signals are always blocked (default with -o)" 4\:"fatal signals and SIGTSTP are always blocked"))' \
+  '*-E+[set or remove exported environment variable]:variable:->envars' \
+  "${root}-u+[run as specified user]:user:_users" \
+  '(:)*-p+[attach to the process with specified process ID and begin tracing]:process ID:_pids' \
+  '-d[show debug output of strace itself on standard error]' \
+  '-v[print unabbreviated versions of environment, stat, termios, etc. calls]' \
+  '(- 1 *)-h[display help information]' \
+  '(- 1 *)-V[display version information]' \
+  '(-c)-k[obtain stack trace between each syscall]' \
+  '(-):command name: _command_names -e' \
+  '*::arguments:_normal' && ret=0
 
-_sets () {
-	_alternative \
-		'special:special values:(all none)' \
-		'calls::_sys_calls'
-}
+case $state in
+  expressions)
+    _values -C -S = 'qualifying expression' \
+      'trace[trace specified set of system calls only]:system calls:->syscalls' \
+      'abbrev[abbreviate the output from printing each member of large structures]:system call:_sequence _sys_calls -a -n' \
+      'verbose[dereference structures for the specified set of system calls]:system call:_sequence _sys_calls -a -n' \
+      'raw[print raw, undecoded arguments for the specified set of system calls]:system call:_sequence _sys_calls -a -n' \
+      'signal[trace only the specified subset of signals]:signal:_sequence _signals -s -M "B\:!="' \
+      'read[perform a full hex and ASCII dump of all the data read from listed file descriptors]:file descriptor:_sequence _file_descriptors' \
+      'write[perform a full hex and ASCII dump of all the data written to listed file descriptors]:file descriptor:_sequence _file_descriptors' && ret=0
+    if [[ $words[CURRENT] != *=* || $state = syscalls ]]; then
+      local dedup sets suf="-qS,"
+      compset -P '!'
+      dedup=( ${(Ms.,.)PREFIX##*,} ${(Ms.,.)SUFFIX%%,*} )
+      compset -S ',*' || suf=""
+      compset -P '*,'
+      sets=(
+	'file:trace all system calls which take a file name as an argument'
+	'process:trace all system calls which involve process management'
+	'network:trace all the network related system calls'
+	'signal:trace all signal related system calls'
+	'ipc:trace all IPC related system calls'
+	'desc:trace all file descriptor related system calls'
+	'memory:trace all memory mapping related system calls'
+      )
+      _alternative \
+	"related system call:sets: _describe -t traces 'related system call' sets -F dedup $suf" \
+	"system call:syscalls:_sys_calls -a -n $suf -F dedup" && ret=0
+    fi
+  ;;
+  file-pipe)
+    compset -P '\\'
+    if (( ! $+opt_args[-ff] )) && compset -P '(!|\|)'; then
+      compset -q
+      if (( CURRENT == 1 )); then
+	_command_names -e && ret=0
+      else
+	_normal && ret=0
+      fi
+    else
+      _files && ret=0
+    fi
+  ;;
+  envars)
+    if [[ -prefix *=* ]]; then
+      compstate[parameter]="${PREFIX%%\=*}"
+      compset -P 1 '*='
+      _value && ret=0
+    else
+      _parameters -qS= -g "*export*" && ret=0
+    fi
+  ;;
+esac
 
-_traces () {
-	local expl
-	traces=('file:trace all system calls which take a file name as an argument'
-		'process:trace all system calls which involve process management'
-		'network:trace all the network related system calls'
-		'signal:trace all signal related system calls'
-		'ipc:trace all IPC related system calls'
-		'desc:trace all file descriptor related system calls')
-	compset -P '*,'
-	_describe -t traces 'Related system calls' traces -qS ,
-}
-
-_traces_sets () {
-	_alternative \
-		'traces::_traces' \
-		'sets::_sets'
-}
-
-_expression () {
-	_values -S = "Qualifying expression" \
-		'trace[trace specified set of system calls only]:system calls:_traces_sets' \
-		'abbrev[abbreviate the output from printing each member of large structures]:system calls:_sets' \
-		'verbose[dereference structures for the specified set of system calls]:system calls:_sets' \
-		'raw[print raw, undecoded arguments for the specified set of system calls]:system calls:_sets' \
-		'signal[trace only the specified subset of signals]:signal:{compset -P "*,"; _signals -s -qS ,}' \
-		'read[perform a full hex and ASCII dump of all the data read from listed file descriptors]:file descriptors:{compset -P "*,"; _file_descriptors -qS ,}' \
-		'write[perform a full hex and ASCII dump of all the data written to listed file descriptors]:file descriptors:{compset -P "*,"; _file_descriptors -qS ,}'
-	if [[ -z "$words[CURRENT]" || -n "${words[CURRENT]:#*=*}" ]]; then
-		_traces_sets
-	fi
-}
-
-_arguments \
-	'()-c[count time, calls, and errors for each system call and report a summary]' \
-	'()-C[count time, calls, and errors for each system call and report a summary in addition to regular output]' \
-	'-d[show some debugging output of strace itself on the standard error]' \
-	'-D[run tracer as detached grandchild, keeping traced process as direct child of calling process]' \
-	'-f[trace child processes as they are created by currently traced processes]' \
-	'-ff[write each process trace to <filename>.<pid> (when using -o <filename>]' \
-	'(-c -d -f -ff -i -q -r -t -tt -ttt -T -v -V -x -xx -a -e -o -O -p -s -S -u -E)-h[print help]' \
-	'-i[print the instruction pointer at the time of the system call]' \
-	'-q[suppress messages about attaching, detaching etc.]' \
-	'-r[print a relative timestamp upon entry to each system call]' \
-	'(-ttt)-t[prefix each line of the trace with the time of day]' \
-	'(-ttt -tt)-tt[prefix each line of the trace with the time of day including the microseconds]' \
-	'(-tt -t)-ttt[prefix each line of the trace with the number of seconds and microseconds since the epoch]' \
-	'-T[show the time spent in system calls]' \
-	'-y[print paths associated with file descriptor arguments]' \
-	'-v[print unabbreviated versions of environment, stat, termios, etc. calls]' \
-	'(-c -d -f -ff -h -i -q -r -t -tt -ttt -T -v -x -xx -a -e -o -O -p -s -S -u -E)-V[print the version number of strace]' \
-	'(-xx)-x[print all non-ASCII strings in hexadecimal string format]' \
-	'(-x)-xx[print all strings in hexadecimal string format]' \
-	'-I+[when strace can be interrupted by signals]:interruptible:((1\:"no signals are blocked" 2\:"fatal signals are blocked while decoding syscall (default)" 3\:"fatal signals are always blocked (default if '\''-o FILE PROG'\''" 4\:"fatal signals and SIGTSTP are always blocked"))' \
-	'*-P+[trace only system calls accessing given path]:path:_files' \
-	'-a+[align return values in a specific column (default 40)]:column number' \
-	'*-e+[select events to trace or how to trace]:system call:_expression' \
-	'-o+[write the trace output to the file]:output file:_files' \
-	'-O+[overhead for tracing system calls]:overhead microseconds' \
-	'(:)-p+[attach to the process with specified process ID and begin tracing]:process ID:_pids' \
-	'-s+[specify the maximum string size to print (default 32)]:maximum string size' \
-	'-S+[sort the output of the histogram (-c option) by the specified criterion]:sort by:(time calls name nothing)' \
-	'-u+[run as specified user]:user:_users' \
-	'*-E+[remove variable from the inherited list of environment or define a value]:variable:_printenv' \
-	'(-):command name: _command_names -e' \
-	'*::arguments:_normal'
+return ret
diff --git a/Completion/Linux/Command/_sysstat b/Completion/Linux/Command/_sysstat
index 2a7128c23..e976b4705 100644
--- a/Completion/Linux/Command/_sysstat
+++ b/Completion/Linux/Command/_sysstat
@@ -1,4 +1,4 @@
-#compdef -P mpstat (|cifs)iostat isag sadf sar pidstat
+#compdef mpstat cifsiostat isag sadf sar pidstat
 # -V can appear with other options, so (- *) isn't needed.
 #TODO:
 # sysstat-nfsiostat - there seems to be two nfsiostat(1)s. one from oracle and one by redhat.
@@ -10,24 +10,8 @@ _mpstat() {
     '-P[specify processor number]:processor: _values -s "," processor ON ALL {1..$(_call_program processors getconf _NPROCESSORS_ONLN)}' \
     '-u[report CPU utilization]' \
     '-V[print version number]' \
-    '1:interval' \
-    '2:count'
-}
-
-_iostat() {
-  _arguments : \
-    '-c[display CPU utilization report]' \
-    '-d[display device utilization report]' \
-    '-T[only display global statistics for group_name]' \
-    '-g[display statistics for a group of devices]:group name' \
-    '-h[human readable device utilization report]' \
-    '-j[display persistent device name]' \
-    '(-m)-k[display statistics in kB/s]' \
-    '(-k)-m[display statistics in MB/s]' \
-    '-N[display registered device mapper names]' \
-    '::device:_files -W /dev -g "*(-%)"' \
-    ': :_guard "[0-9]#" "interval"' \
-    ':count'
+    '1: : _guard "^-*" interval' \
+    '2: : _guard "^-*" count'
 }
 
 _cifsiostat() {
@@ -37,8 +21,8 @@ _cifsiostat() {
     '(-k)-m[display statistics in MB/s]' \
     '-t[print timestamp for each report]' \
     '-V[print version number]' \
-    '1:interval' \
-    '2:count'
+    '1: : _guard "^-*" interval' \
+    '2: : _guard "^-*" count'
 }
 
 _isag() {
@@ -68,8 +52,8 @@ _sadf() {
       '(-t -T)-U[display in seconds since epoch (UTC)]' \
       '-V[print version number]' \
       '(-j -d -p)-x[output file in XML]' \
-      '1:interval' \
-      '2:count' \
+      '1: : _guard "^-*" interval' \
+      '2: : _guard "^-*" count' \
       '3:data file:_files' && ret=0
   else
     _arguments : '*::sar: _sar' && ret=0
@@ -107,8 +91,8 @@ _sar() {
     '-W[report swapping statistics]' \
     '-w[report task creation and system switching activity]' \
     '-y[report TTY device activity]' \
-    '1:interval' \
-    '2:count'
+    '1: : _guard "^-*" interval' \
+    '2: : _guard "^-*" count'
 }
 
 _pidstat() {
@@ -128,7 +112,8 @@ _pidstat() {
     '-V[print version number]' \
     '-v[display values from kernel table]' \
     '-w[report task switching activity]' \
-    ':interval' ':count'
+    '1: : _guard "^-*" interval' \
+    '2: : _guard "^-*" count'
 }
 
 _sysstat() {
diff --git a/Completion/Linux/Command/_vserver b/Completion/Linux/Command/_vserver
index 2401387e5..36a61c45a 100644
--- a/Completion/Linux/Command/_vserver
+++ b/Completion/Linux/Command/_vserver
@@ -95,7 +95,7 @@ _vserver_cache_cfgdir() {
 _vserver_cache_vsnames() {
   if [[ "$_cache_vserver_vsnames_initialized" != true ]]; then
     typeset -ga _cache_vserver_vsnames
-    _cache_vserver_vsnames=( $(ls -d $_cache_vserver_cfgdir/*/ | sed -e s,$_cache_vserver_cfgdir,, | tr -d '/') )
+    _cache_vserver_vsnames=( $_cache_vserver_cfgdir/*(/:t) )
     _cache_vserver_vsnames_initialized=true
   fi
 }
diff --git a/Completion/Mandriva/Command/_rebootin b/Completion/Mandriva/Command/_rebootin
index 3f30b2591..284ff08f1 100644
--- a/Completion/Mandriva/Command/_rebootin
+++ b/Completion/Mandriva/Command/_rebootin
@@ -2,7 +2,7 @@
 
 local context state line expl
 typeset -A opt_args
-local loader=$(sudo detectloader -q)
+local loader=${$(_call_program -p entries detectloader -q):-GRUB}
 
 _arguments -s \
     '-n[no immediate reboot just set the flags for next reboot]' \
diff --git a/Completion/Redhat/Command/_rpm b/Completion/Redhat/Command/_rpm
index b2157bd48..b24213e3c 100644
--- a/Completion/Redhat/Command/_rpm
+++ b/Completion/Redhat/Command/_rpm
@@ -298,7 +298,7 @@ _rpm () {
 	  ${(f)"$(_call_program capabilities rpm -qa --queryformat '%\{requirename}\\n' 2>/dev/null)"} && ret=0
       ;;
     relocate)
-      if compset -P '*='; then
+      if compset -P 1 '*='; then
 	_description directories expl 'new path'
       else
 	_description directories expl 'old path'
diff --git a/Completion/Redhat/Command/_scl b/Completion/Redhat/Command/_scl
index e1a4b69db..b7bba2b13 100644
--- a/Completion/Redhat/Command/_scl
+++ b/Completion/Redhat/Command/_scl
@@ -8,7 +8,8 @@ local -a state line force alts cmd
 
 _arguments -C $force \
   '(- 1)'{-l,--list}'[list installed software collections or packages within a collection]' \
-  '(-l --list)1:action:(enable register deregister)' \
+  '(-l --list)1:action:(enable run load unload list-collections list-packages man register deregister)' \
+  '(- *)--help' \
   '(-)*:collections:->collections' && ret=0
 
 if [[ -n $state ]]; then
@@ -31,7 +32,7 @@ if [[ -n $state ]]; then
     cmd='command:command:_cmdstring'
 
   _alternative 'collections:collection:compadd
-      $(_call_program collections ${words[1]} -l)' \
+      $(_call_program collections "${words[1]} list-collections || ${words[1]} -l")' \
       $cmd && ret=0
 fi
 
diff --git a/Completion/Redhat/Command/_yum b/Completion/Redhat/Command/_yum
index f4538060e..4698a3159 100644
--- a/Completion/Redhat/Command/_yum
+++ b/Completion/Redhat/Command/_yum
@@ -1,305 +1,475 @@
 #compdef yum
 
 # Main dispatcher
-_yum() 
-{
-	local curcontext="$curcontext" state lstate line
-
-	_arguments -s \
-	   '(- *)'{-h,--help}'[show the help message]' \
-	   '(-t --tolerant)'{-t,--tolerant}'[be tolerant of errors]' \
-	   '(-C --cacheonly)'{-C,--cacheonly}'[run entirely from cache]' \
-	   '(-c --config)'{-c,--config=}'[config file location]:Yum conf file:_files' \
-	   '(-R --randomwait)'{-R,--randomwait=}'[maximum command wait time (in minutes)]:max wait time' \
-	   '(-d --debuglevel)'{-d,--debuglevel=}'[debug level (0-10)]:debug level' \
-	   '(-e --errorlevel)'{-e,--errorlevel=}'[error level (0-10)]:error level' \
-	   '(-y --assumeyes)'{-y,--assumeyes}'[answer yes for all questions]' \
-	   '--installroot=[set install root]:install root:_files -/' \
-	   '*--enablerepo=[enable or or more repositories]:repos to enable:_yum_disabled_repos_list' \
-	   '*--disablerepo=[disable one or more repositories]:disable repos:_yum_enabled_repos_list' \
-	   {*-x,*--exclude=}'[exclude package(s) by name or glob]:exclude packages' \
-	   '--version[show yum version]' \
-	   '--obsoletes[enable obsoletes processing during updates]' \
-	   '--nogpgcheck[disable gpg signature checking]' \
-	   '--noplugins[disable yum plugins]' \
-	   '--disablepresto[disable Presto plugin and don''''t download any deltarpms]' \
-	   '*::yum command:_yum_command'
-}
-
-(( $+functions[_yum_command] )) || _yum_command() 
+_yum() {
+  local curcontext="$curcontext" state lstate line
+
+  _arguments -s \
+    '(- *)'{-h,--help}'[show the help message]' \
+    '(-t --tolerant)'{-t,--tolerant}'[be tolerant of errors]' \
+    '(-C --cacheonly)'{-C,--cacheonly}'[run entirely from cache]' \
+    '(-c --config)'{-c,--config=}'[config file location]:Yum conf file:_files' \
+    '(-R --randomwait)'{-R,--randomwait=}'[specify maximum command wait time]:max wait time (minutes)' \
+    '(-d --debuglevel)'{-d,--debuglevel=}'[debug level (0-10)]:debug level' \
+    '--showduplicates[show duplicates, in repos, in list/search commands]' \
+    '(-e --errorlevel)'{-e,--errorlevel=}'[error level (0-10)]:error level' \
+    '--rpmverbosity=[specify rpm debug level]:debug level' \
+    '(-q --quiet -v --verbose)'{-q,--quiet}'[quiet operation]' \
+    '(-q --quiet -v --verbose)'{-v,--verbose}'[verbose operation]' \
+    '(-y --assumeyes --assumeno)'{-y,--assumeyes}'[answer yes to all questions]' \
+    '(-y --assumeyes --assumeno)--assumeno[answer no to all questions]' \
+    '(- *)--version[display version information]' \
+    '--installroot=[set install root]:install root:_files -/' \
+    '*--enablerepo=[enable or or more repositories]:repository:_sequence _yum_disabled_repos' \
+    '*--disablerepo=[disable one or more repositories]:repository:_sequence _yum_enabled_repos' \
+    \*{-x,--exclude=}'[exclude package by name or glob]:package:_yum_all_pkgs' \
+    '--disableexcludes=[disable exclude]:exclude:(all main)' \
+    '--disableincludes=[disable include]:include' \
+    '--obsoletes[enable obsoletes processing during updates]' \
+    '--noplugins[disable yum plugins]' \
+    '--nogpgcheck[disable gpg signature checking]' \
+    '--disableplugin=[disable plugin]:plugin' \
+    '--enableplugin=[enable plugin]:plugin' \
+    '--skip-broken[skip packages with depsolving problems]' \
+    '--color=[control whether color is used]:(always auto never)' \
+    '--releasever=[set value of $releasever in yum config and repo files]:value' \
+    "--downloadonly[don't update, just download]" \
+    '--downloaddir=[specify alternate directory to store packages]:directort:_directories' \
+    '--setopt=[set arbitrary config and repo options]:option' \
+    '--bugfix[include bugfix relevant packages in updates]' \
+    '--security[include security relevant packages in updates]' \
+    '--advisory=[include packages needed to fix the given advisory]:advisory' \
+    '--bzs=[include packages needed to fix the given BZ]:BZ' \
+    '--cves=[include packages needed to fix the given CVE]:CVE' \
+    '--sec-severity=[include security relevant packages matching specific severity]:severity' \
+    {*-x,*--exclude=}'[exclude package(s) by name or glob]:exclude packages' \
+    '*::yum command:_yum_command'
+}
+
+(( $+functions[_yum_command] )) || _yum_command()
 {
-  	local -a _yum_cmds
-  	_yum_cmds=(
-		"install:install the latest version of a package or group of packages"
-		"erase:remove an installed package (with its dependencies)"
-		"remove:remove an installed package (with its dependencies)"
-		"clean:clean local yum cache"
-		"deplist:gives a list of all dependencies for a package"
-		"check-update:check if any updates are available"
-		"info:get description of available packages"
-		"list:is used to list various information about packages"
-		"groupinfo:get info on package groups"
-		"groupinstall:install a package group or groups"
-		"groupremove:remove a package group or groups"
-		"grouplist:list package groups"
-		"groupupdate:update a package group or groups"
-		"localinstall:install packages with local rpm files"
-		"localupdate:update packages with local rpm files"
-		"makecache:makes a local yum cache"
-		"provides:find out which package provides some feature or file"
-		"whatprovides:find out which package provides some feature or file"
-		"resolvedep:list packages providing the specified dependencies"
-		"search:find any packages matching pattern"
-		"shell:enter the 'yum shell'"
-		"update:update one or more packages"
-		"upgrade:upgrade one or more packages"
-  	)
-
-  	if (( CURRENT == 1 )); then
-		_describe -t commands 'yum command' _yum_cmds || compadd "$@"
-  	else
-    	local curcontext="$curcontext"
-
-	    cmd="${${_yum_cmds[(r)$words[1]:*]%%:*}}"
-		# Deal with any aliases
-		case $cmd in
-			remove) cmd="erase";;
-			whatprovides) cmd="provides";;
-			upgrade) cmd="update";;
-		esac
-		
-    	if (( $#cmd )); then
-    		curcontext="${curcontext%:*:*}:yum-${cmd}:"
-	
-	      	local update_policy
-		  	zstyle -s ":completion:${curcontext}:" cache-policy update_policy
-		  	if [[ -z "$update_policy" ]]; then
-				zstyle ":completion:${curcontext}:" cache-policy _yum_caching_policy
-	  		fi
-
-      		_call_function ret _yum_$cmd || _message 'no more arguments'
-    	else
-      		_message "unknown yum command: $words[1]"
-    	fi
-    	return ret
-  	fi
+  local -a _yum_cmds
+  _yum_cmds=(
+  "install:install the latest version of a package or group of packages"
+  "erase:remove an installed package (with its dependencies)"
+  "remove:remove an installed package (with its dependencies)"
+  "clean:clean local yum cache"
+  "deplist:gives a list of all dependencies for a package"
+  "check-update:check if any updates are available"
+  "info:get description of available packages"
+  "list:is used to list various information about packages"
+  "groupinfo:get info on package groups"
+  "groupinstall:install a package group or groups"
+  "groupremove:remove a package group or groups"
+  "grouplist:list package groups"
+  "groupupdate:update a package group or groups"
+  "localinstall:install packages with local rpm files"
+  "localupdate:update packages with local rpm files"
+  "makecache:makes a local yum cache"
+  "provides:find out which package provides some feature or file"
+  "whatprovides:find out which package provides some feature or file"
+  "resolvedep:list packages providing the specified dependencies"
+  "search:find any packages matching pattern"
+  "shell:enter the 'yum shell'"
+  "update:update one or more packages"
+  "upgrade:upgrade one or more packages"
+  "update-to:update one or more packages taking obsoletes into account"
+  "upgrade-to:upgrade one or more packages taking obsoletes into account"
+  "history:view past transactions"
+  "help:produce help for all or given command"
+  "load-transaction:load a saved transaction from a textfile"
+  "load-ts:load a saved transaction from a textfile"
+  "check:check for problems in the rpmdb"
+  "reinstall:reinstall a package"
+  "downgrade:downgrade a package"
+  "repolist:display the configured software repositories"
+  "distribution-synchronization:synchronize installed packages to the latest available versions"
+  "distro-sync:synchronize installed packages to the latest available versions"
+  )
+
+  if (( CURRENT == 1 )); then
+    _describe -t commands 'yum command' _yum_cmds
+  else
+    local curcontext="$curcontext" ret=1
+
+    cmd="${${_yum_cmds[(r)$words[1]:*]%%:*}}"
+    # Deal with any aliases
+    case $cmd in
+      remove) cmd="erase";;
+      whatprovides) cmd="provides";;
+      upgrade) cmd="update";;
+      upgrade-to) cmd="update";;
+      update-to) cmd="update";;
+      load-ts) cmd="load-transaction";;
+      distro-sync) cmd="distribution-synchronization";;
+    esac
+
+    if (( $#cmd )); then
+      curcontext="${curcontext%:*:*}:yum-${cmd}:"
+
+      local update_policy
+      zstyle -s ":completion:${curcontext}:" cache-policy update_policy
+      if [[ -z "$update_policy" ]]; then
+        zstyle ":completion:${curcontext}:" cache-policy _yum_caching_policy
+      fi
+
+      if [ "$cmd" = "help" ]; then
+        if (( CURRENT == 2 )); then
+          _describe -t commands 'yum commands' _yum_cmds && ret=0
+        else
+          # help takes one argument
+          _message 'no more arguments'
+        fi
+      elif ! _call_function ret _yum-$cmd; then
+	_default && ret=0
+      fi
+    else
+      # fallback to default completion for unknown commands
+      _default && ret=0
+    fi
+    return ret
+  fi
+}
+
+# Expand next argument after 'yum check'
+_yum-check() {
+    ##chkargs=("dependencies" "duplicates" "obsoletes" "provides" "all") # according to man page
+    ##chkargs=("dependencies" "duplicates" "all") # according to help
+    #chkargs=("dependencies" "duplicates" "provides" "all") # what works for me
+  _values -w 'check argument' dependencies duplicates provides all
+}
+
+# Expand next argument after 'yum repolist'
+_yum-repolist() {
+  _yum_get_repos
+  _alternative \
+    'filters:filter:(all enabled disabled)' \
+    'repositories:repository:compadd -a yum_enabled_repos yum_disabled_repos'
+}
+
+_yum_ids() {
+  # the last argument will be the first valid transaction ID to be suggested
+  # all other arguments are forwarded to compadd
+  #
+  # maxid is the last transaction ID known to yum
+  local maxid
+
+  maxid=${${(M)${(f)"$(_call_program transactions yum history stats)"}:#Transactions:*}##* }
+
+  # `$@' are the arguments
+  # `${(@)@[...]}' selects a subrange from $@
+  # `${(@)@[1,-2]}' are all except the last argument
+  # `$@[$#]' is the last argument, e.g. the first suggestable ID
+  compadd "${(@)@[1,-2]:/-J/-V}" -M "B:0=" {$@[$#]..$maxid}
+}
+
+_yum_ranges() {
+  if compset -P 1 '*..'; then
+    _yum_ids "$@" ${(S)IPREFIX#..}
+  elif compset -S '..*'; then
+    _yum_ids "$@" 1
+  else
+    _yum_ids "$@" -S '..' 1
+  fi
+}
+
+# Expand next argument after 'yum history'
+_yum-history() {
+  local expl
+  if (( CURRENT == 2 )); then
+    _wanted commands expl "yum history command" compadd info list \
+      packages-list packages-info summary addon-info redo undo \
+      roll-back new sync stats
+  elif (( CURRENT == 3 )); then
+    local -a ID_commands
+    local -a ID_range_commands
+    local -a package_commands
+    local -a alts
+    alts=()
+    #ID_commands=('summary' 'info' 'list' 'stats' 'addon-info')
+    ID_commands=('addon-info')
+    ID_range_commands=('summary' 'info' 'list' 'stats')
+    package_commands=('summary' 'info' 'list' 'stats' 'packages-list' 'packages-info')
+    #package_commands=('packages-list' 'packages-info')
+    # packages-list, packages-info   : needs package name
+    # summary, info, list, stats     : ID, ID range, package name
+    # addon-info                     : ID
+    # redo, undo, roll-back, sync    : unknown
+
+    [[ -n "${ID_commands[(r)$words[2]]}" ]] && alts+=('special:special ID:(last)')
+    [[ -n "${ID_commands[(r)$words[2]]}" ]] && alts+=('regular:ID:_yum_ids 1')
+    [[ -n "${ID_range_commands[(r)$words[2]]}" ]] && alts+=('regular:transaction range:_yum_ranges')
+    [[ -n "${ID_range_commands[(r)$words[2]]}" ]] && alts+=('special:all transactions:(all)')
+    [[ -n "${package_commands[(r)$words[2]]}" ]] && alts+=("package:package:_yum_act_on_installed_pkgs")
+
+    if (( ${+alts[1]} )) ; then
+      _alternative "$alts[@]"
+    else
+      _default
+    fi
+  elif (( CURRENT == 4 )); then
+    if [ "$words[2]" = "addon-info" ]; then
+      _wanted arguments expl 'additional option' compadd saved_tx
+    fi
+  fi
 }
 
+
 # Fills the all pkg cache
 _yum_all_pkgs()
 {
-	if ( [[ ${+_all_pkgs} -eq 0 ]] || _cache_invalid ALL ) &&
-		! _retrieve_cache ALL;
-	then
-		_all_pkgs=( $(yum -C list all | sed 's/\s.*//' | grep '\.' 2>/dev/null) )
-		_store_cache ALL _all_pkgs
-	fi
+  (( $+_yum_all_pkgs[1] )) && return
+  if _cache_invalid yum-all-packages || ! _retrieve_cache yum-all-packages; then
+    _yum_all_pkgs=( ${(M)${${(f)"$(_call_program packages yum -C list all)"}// *}:#*.*} )
+    _store_cache yum-all-packages _yum_all_pkgs
+  fi
 }
 
 # Fills the installed pkg cache
 _yum_installed_pkgs()
 {
-	if ( [[ ${+_installed_pkgs} -eq 0 ]] || _cache_invalid INSTALLED ) &&
-		! _retrieve_cache INSTALLED;
-	then
-		_installed_pkgs=( $(yum -C list installed | sed 's/\s.*//' | grep '\.' 2>/dev/null) )
-		_store_cache INSTALLED _installed_pkgs
-	fi
+  (( $+_yum_installed_pkgs[1] )) && return
+  if _cache_invalid yum-installed || ! _retrieve_cache yum-installed; then
+    _yum_installed_pkgs=( ${(M)${${(f)"$(_call_program packages yum -C list installed)"}// *}:#*.*} )
+    _store_cache yum-installed _yum_installed_pkgs
+  fi
 }
 
 # Fills the available pkg cache
 _yum_available_pkgs()
 {
-	if ( [[ ${+_available_pkgs} -eq 0 ]] || _cache_invalid AVAILABLE ) &&
-		! _retrieve_cache AVAILABLE;
-	then
-		_available_pkgs=( $(yum -C list available | sed 's/\s.*//' | grep '\.' 2>/dev/null) )
-		_store_cache AVAILABLE _available_pkgs
-	fi
+  (( $+_yum_available_pkgs[1] )) && return
+  if _cache_invalid yum-available || ! _retrieve_cache yum-available; then
+    _yum_available_pkgs=( ${(M)${${(f)"$(_call_program packages yum -C list available)"}// *}:#*.*} )
+    _store_cache yum-available _yum_available_pkgs
+  fi
 }
 
 # Fills the upgrade pkg cache
 _yum_upgrade_pkgs()
 {
-	if ( [[ ${+_upgrade_pkgs} -eq 0 ]] || _cache_invalid UPGRADE ) &&
-		! _retrieve_cache UPGRADE;
-	then
-		_upgrade_pkgs=( $(yum -C list upgrade | sed 's/\s.*//' | grep '\.' 2>/dev/null) )
-		_store_cache UPGRADE _upgrade_pkgs
-	fi
+  (( $+_yum_upgrade_pkgs[1] )) && return
+  if _cache_invalid yum-upgrade || ! _retrieve_cache yum-upgrade; then
+    _yum_upgrade_pkgs=( ${(M)${${(f)"$(_call_program packages yum -C list upgrade)"}// *}:#*.*} )
+    _store_cache yum-upgrade _yum_upgrade_pkgs
+  fi
 }
 
 # Gets the list of defined repos
-yum_repos() {
-    local trepo
-    local -a tarray
-    tarray=( $(egrep -h '(^\[.*\]|^enabled.*=)' /etc/yum.repos.d/*.repo /etc/yum.conf | sed -e 's/ //g' | sed -e 's/\[//g' | sed -e 's/\].*$//g' 2>/dev/null) )
-    local -i eindex=0
-    local -i dindex=0
-    for line in $tarray; do
-        if [[ "$line" = "enabled=1" ]]; then
-            enabled_yum_repos=($enabled_yum_repos $trepo)
-        elif [[ "$line" = "enabled=0" ]]; then
-            disabled_yum_repos=($disabled_yum_repos $trepo)
-        elif [[ "$line" != "main" ]]; then
-            trepo=$line
-        fi
-    done
+_yum_get_repos() {
+  local trepo
+  local -a tarray
+  tarray=( $(egrep -h '(^\[.*\]|^enabled.*=)' /etc/yum.repos.d/*.repo /etc/yum.conf | sed -e 's/ //g' | sed -e 's/\[//g' | sed -e 's/\].*$//g' 2>/dev/null) )
+  local -i eindex=0
+  local -i dindex=0
+  for line in $tarray; do
+    if [[ "$line" = "enabled=1" ]]; then
+      yum_enabled_repos=($enabled_yum_repos $trepo)
+    elif [[ "$line" = "enabled=0" ]]; then
+      yum_disabled_repos=($yum_disabled_repos $trepo)
+    elif [[ "$line" != "main" ]]; then
+      trepo=$line
+    fi
+  done
 }
 
-(( $+functions[_yum_disabled_repos_list] )) || _yum_disabled_repos_list()
-{
-	compset -P '*,'
-	compset -S ',*'
-	yum_repos			
-	compadd "$@" -a -- disabled_yum_repos
+_yum_disabled_repos() {
+  _yum_get_repos
+  compadd "$@" -a -- yum_disabled_repos
+}
+
+_yum_enabled_repos() {
+  _yum_get_repos
+  compadd "$@" -a -- yum_enabled_repos
+}
+
+# Suggest installed packages
+_yum_act_on_installed_pkgs() {
+  local expl
+  _yum_installed_pkgs
+  _wanted packages expl 'package' compadd "$@" -a -- _yum_installed_pkgs
 }
 
-(( $+functions[_yum_enabled_repos_list] )) || _yum_enabled_repos_list()
+# Completion function for distribution-synchronization|distro-sync
+(( $+functions[_yum-distribution-synchronization] )) || _yum-distribution-synchronization()
 {
-	compset -P '*,'
-	compset -S ',*'
-	yum_repos			
-	compadd "$@" -a -- enabled_yum_repos
+  _yum_act_on_installed_pkgs "$@"
 }
 
 # Completion function for erase|remove
-(( $+functions[_yum_erase] )) || _yum_erase()
+(( $+functions[_yum-erase] )) || _yum-erase()
+{
+  _yum_act_on_installed_pkgs "$@"
+}
+
+# Completion function for downgrade
+(( $+functions[_yum-downgrade] )) || _yum-downgrade()
+{
+  _yum_act_on_installed_pkgs "$@"
+}
+
+# Completion function for reinstall
+(( $+functions[_yum-reinstall] )) || _yum-reinstall()
+{
+  _yum_act_on_installed_pkgs "$@"
+}
+
+(( $+functions[_yum-groupinfo] )) || _yum-groupinfo()
+{
+  local expl
+  _wanted groups expl group compadd \
+      ${${(M)${(f)"$(_call_program groups yum grouplist -C)"}:#   *}#   }
+}
+
+(( $+functions[_yum-groupinstall] )) || _yum-groupinstall()
+{
+  local expl
+  _wanted groups expl group compadd \
+      ${${(M)${(f)"$(_call_program groups yum grouplist -C)"}:#   *}#   }
+}
+
+(( $+functions[_yum-groupremove] )) || _yum-groupremove()
+{
+  local expl
+  _wanted groups expl group compadd \
+      ${${(M)${(f)"$(_call_program groups yum grouplist -C installed)"}:#   *}#   }
+}
+
+(( $+functions[_yum-groupupdate] )) || _yum-groupupdate()
 {
-	_yum_installed_pkgs
-	compadd "$@" -a -- _installed_pkgs
+  local expl
+  _wanted groups expl group compadd \
+      ${${(M)${(f)"$(_call_program groups yum grouplist -C installed)"}:#   *}#   }
 }
 
 # Completion function for install
-(( $+functions[_yum_install] )) || _yum_install()
+(( $+functions[_yum-install] )) || _yum-install()
 {
-        if ! [[ $PREFIX == */* ]]; then
-          _yum_available_pkgs
-        fi
+  if ! [[ $PREFIX == */* ]]; then
+    _yum_available_pkgs
+  fi
 
-        local ret=1
-        _tags files packages
-        while _tags; do
-          if _requested files; then
-            compadd "$@" -a -- _available_pkgs
-          fi
-          if _requested packages; then
-            _call_function - _yum_localinstall
-          fi
-          (( ret )) || break
-        done
-        return ret
+  local ret=1
+  _tags files packages
+  while _tags; do
+    if _requested files; then
+      compadd "$@" -a -- _yum_available_pkgs
+    fi
+    if _requested packages; then
+      _call_function - _yum_localinstall
+    fi
+    (( ret )) || break
+  done
+  return ret
+}
+
+# Completion function for load-transaction
+(( $+functions[_yum-load-transaction] )) || _yum-load-transaction()
+{
+  _files
 }
 
 # Completion function for localinstall
-(( $+functions[_yum_localinstall] )) || _yum_localinstall()
+(( $+functions[_yum-localinstall] )) || _yum-localinstall()
 {
-	_files -/ -g '(#i)*.rpm(-.)'
+  _files -/ -g '(#i)*.rpm(-.)'
 }
 
 # Completion function for localupdate
-(( $+functions[_yum_localupdate] )) || _yum_localupdate()
+(( $+functions[_yum-localupdate] )) || _yum-localupdate()
 {
-	_files -/ -g '(#i)*.rpm(-.)'
+  _files -/ -g '(#i)*.rpm(-.)'
 }
 
 # Completion function for update/upgrade
-(( $+functions[_yum_update] )) || _yum_update()
+(( $+functions[_yum-update] )) || _yum-update()
 {
-	_yum_upgrade_pkgs
-	compadd "$@" -a -- _upgrade_pkgs
+  local expl
+  _yum_upgrade_pkgs
+  _wanted packages expl package compadd "$@" -a _yum_upgrade_pkgs
 }
 
 # Completion function for deplist
-(( $+functions[_yum_deplist] )) || _yum_deplist()
+(( $+functions[_yum-deplist] )) || _yum-deplist()
 {
-	_yum_available_pkgs
-	compadd "$@" -a -- _available_pkgs
+  local expl
+  _yum_available_pkgs
+  _wanted packages expl package compadd "$@" -a _yum_available_pkgs
 }
 
 _yum_all()
 {
-	_yum_all_pkgs
-	compadd "$@" -a -- _all_pkgs
+  _yum_all_pkgs
+  compadd "$@" -a -- _yum_all_pkgs
 }
+
 _yum_list_or_info()
 {
-	local -a listlist
-	listlist=(
-		"all:all packages in repositories"
-		"available:packages available in repositories"
-		"updates:packages with updates available"
-		"installed:installed packages"
-		"extras:packages installed that are not available in any yum repository"
-		"obsoletes:packages installed that are obsoleted"
-		"recent:packages recently added to repositories"
-	)
-	
-  	if (( CURRENT == 2 )); then
-		_describe -t yum-list-subcmds "Yum info/list sub-commands" listlist || _yum_all
-	else
-	    local subcmd
-		subcmd="${${listlist[(r)$words[2]:*]%%:*}}"
-		# offer packages selected by the subcommand
-		case $subcmd in
-			all) _yum_all;;
-			installed) _yum_erase;;
-			available) _yum_install;;
-			updates) _yum_update;;
-		esac
-	fi
+  local -a listlist
+  listlist=(
+    "all:all packages in repositories"
+    "available:packages available in repositories"
+    "updates:packages with updates available"
+    "installed:installed packages"
+    "extras:packages installed that are not available in any yum repository"
+    "obsoletes:packages installed that are obsoleted"
+    "recent:packages recently added to repositories"
+  )
+
+  if (( CURRENT == 2 )); then
+    _describe -t filters "filter" listlist || _yum_all
+  else
+    # offer packages selected by the subcommand
+    case $words[2] in
+      installed) _yum-erase;;
+      available) _yum-install;;
+      updates) _yum-update;;
+      *extras|obsoletes|recent)
+        _wanted packages expl package compadd \
+	    ${(M)${${(f)"$(_call_program packages yum -C list $words[2])"}// *}:#*.*}
+      ;;
+      *) _yum_all;;
+    esac
+  fi
 }
 
 # Completion function for list
-(( $+functions[_yum_list] )) || _yum_list()
+(( $+functions[_yum-list] )) || _yum-list()
 {
-	_yum_list_or_info
+  _yum_list_or_info
 }
 
 # Completion function for info
-(( $+functions[_yum_info] )) || _yum_info()
+(( $+functions[_yum-info] )) || _yum-info()
 {
-	_yum_list_or_info
+  _yum_list_or_info
 }
 
 # Completion function for provides|whatprovides
-(( $+functions[_yum_provides] )) || _yum_provides()
+(( $+functions[_yum-provides] )) || _yum-provides()
 {
-	_files	
+  _files	
 }
 
-# Completion function for resolvedep
-(( $+functions[_yum_resolvedep] )) || _yum_resolvedep()
+# Completion function for clean
+(( $+functions[_yum-clean] )) || _yum-clean()
 {
-	_files	
+  _values -w "Yum clean sub-commands" \
+    "all[all cache]" \
+    "cache[all cache]" \
+    "dbcache[DB cache]" \
+    "headers[cache headers]" \
+    "packages[cache packages]" \
+    "metadata[cache meta-data]"
 }
 
-# Completion function for clean
-(( $+functions[_yum_clean] )) || _yum_clean()
-{
-	local -a cleanlist
-	cleanlist=(
-		"all:all cache"
-		"cache:all cache"
-		"dbcache:DB cache"
-		"headers:cache headers"
-		"packages:cache packages"
-		"metadata:cache meta-data"
-	)
-	
-  	if (( CURRENT == 2 )); then
-		_describe -t yum-clean-subcmds "Yum clean sub-commands" cleanlist 
-	fi
-}
-
-_yum_caching_policy() 
-{
+_yum_caching_policy() {
   local _yumrepomds
   local -a oldp
 
   # rebuild if cache is more than a week old
-  oldp=( "$1"(mw+1) )
-  (( $#oldp )) && return 0
+  oldp=( "$1"(mw-1) )
+  (( $#oldp )) || return 0
 
   _yumrepomds=( /var/cache/yum/**/repomd.xml )
 
diff --git a/Completion/Solaris/Command/_beadm b/Completion/Solaris/Command/_beadm
index 8422653d8..cdb324e87 100644
--- a/Completion/Solaris/Command/_beadm
+++ b/Completion/Solaris/Command/_beadm
@@ -1,9 +1,10 @@
 #compdef beadm
 
-local cmd expl args
+local cmd expl args cmds
 
 if (( CURRENT == 2 )); then
-  _wanted subcommands expl 'subcommand' compadd \
+  [[ $OSTYPE = solaris* ]] && cmds=( set-policy )
+  _wanted subcommands expl 'subcommand' compadd $cmds \
       activate create destroy list mount rename unmount
   return
 fi
@@ -24,15 +25,16 @@ case $cmd in
       '*-o[property]:zfs property'
       '-p[create new BE in specified zfs pool]:zfs pool:_zfs_pool'
     )
-    _arguments -A "-*" $args \
+    _arguments -A "-*" -s $args \
       '-e[base BE]:BE name or snapshot:_be_name -t all' \
       ':new BE name:'
   ;;
   destroy)
     [[ $OSTYPE = solaris* ]] && args=(
       '-f[unmount BE if necessary]'
+      '(:)-O[destroy all orphaned boot environments]'
     )
-    _arguments -A "-*" \
+    _arguments -A "-*" $args \
       "-F[don't prompt for verification]" \
       ':BE or BE snapshot:_be_name'
   ;;
@@ -42,14 +44,16 @@ case $cmd in
     elif [[ $OSTYPE = freebsd* ]]; then
       args=( '-D[display space usage of boot environment]' )
     fi
-    _arguments -A "-*" \
+    _arguments -A "-*" -s $args \
       '-a[list subordinate filesystems and snapshots]' \
       '-s[list snapshots]' \
       '-H[parseable format]' \
       ':boot environment:_be_name'
   ;;
   mount)
-    _arguments -A "-*" \
+    [[ $OSTYPE = solaris* ]] &&
+        args=( '-b[mount associated boot pool dataset]' )
+    _arguments -A "-*" $args \
       ':BE name:_be_name' \
       ':mountpoint:_path_files -/'
   ;;
@@ -58,6 +62,11 @@ case $cmd in
       ':existing boot environment name:_be_name' \
       ':new boot environment:'
   ;;
+  set-policy)
+    _arguments -A "-*" \
+      '*-n[specify policy]:policy:(static -static noevict -noevict)' \
+      '*:BE name:_be_name'
+  ;;
   u(n|)mount)
     _arguments -A "-*" \
       '-f[force unmount]' \
diff --git a/Completion/Solaris/Command/_coreadm b/Completion/Solaris/Command/_coreadm
index c37e2b077..7262e6423 100644
--- a/Completion/Solaris/Command/_coreadm
+++ b/Completion/Solaris/Command/_coreadm
@@ -1,48 +1,45 @@
 #compdef coreadm
 
-_coreadm() {
-	local -a content option
+local -a content option
 
-	content=(
-		"anon[anonymous private mappings]"
-		"ctf[CTF type information]"
-		"data[writable private file mappings]"
-		"dism[DISM mappings]"
-		"heap[process heap]"
-		"ism[ISM mappings]"
-		"rodata[read-only private file mappings]"
-		"shanon[anonymous shared mappings]"
-		"shfile[file-backed shared mappings]"
-		"shm[System V shared memory]"
-		"stack[process stack]"
-		"symtab[symbol table sections for loaded files]"
-		"text[readable and executable private file mappings]"
-	)
+content=(
+  "anon[anonymous private mappings]"
+  "ctf[CTF type information]"
+  "data[writable private file mappings]"
+  "dism[DISM mappings]"
+  "heap[process heap]"
+  "ism[ISM mappings]"
+  "rodata[read-only private file mappings]"
+  "shanon[anonymous shared mappings]"
+  "shfile[file-backed shared mappings]"
+  "shm[System V shared memory]"
+  "stack[process stack]"
+  "symtab[symbol table sections for loaded files]"
+  "text[readable and executable private file mappings]"
+)
 
-	option=(
-		"global"\:"Allow global core dumps"
-		"global-setid"\:"Allow set-id global core dumps"
-		"log"\:"Generate a syslog message on global core dump"
-		"process"\:"Allow per-process core dumps"
-		"proc-setid"\:"Allow set-id per-process core dumps"
-	)
+option=(
+  'global:allow global core dumps'
+  'process:allow per-process core dumps'
+  'global-setid:allow set-id global core dumps'
+  'proc-setid:allow set-id per-process core dumps'
+  'log:generate a syslog message on global core dump'
+  'kzone:allow kernel zone core dumps'
+)
 
-	# _values doesn't quite work for us here -- the separator can be either
-	# "+" or "-"
-	_arguments -s \
-		- set1 \
-		'-g[global core file name pattern]:' \
-		'-G[global core file content]:content:_values -s + "content" $content' \
-		'-i[per-process core file name pattern]:' \
-		'-I[per-process core file content]:content:_values -s + "content" $content' \
-		'*-d[disable core option]:option:(($option))' \
-		'*-e[enable core option]:option:(($option))' \
-		- set2 \
-		'-p[PID-specific per-process core file name pattern]:' \
-		'-P[PID-specific per-process core file content]:content:_values -s + "content" $content' \
-		'*:pids:_pids' \
-		- set3 \
-		'-u[update options from coreadm.conf]'
-}
-
-_coreadm "$@"
+# _values doesn't quite work for us here -- the separator can be either
+# "+" or "-"
+_arguments -s \
+  - set1 \
+  '-g[global core file name pattern]:' \
+  '-G[global core file content]:content:_values -s + "content" $content' \
+  '-i[per-process core file name pattern]:' \
+  '-I[per-process core file content]:content:_values -s + "content" $content' \
+  '*-d[disable core option]:option:(($option))' \
+  '*-e[enable core option]:option:(($option))' \
+  - set2 \
+  '-p[PID-specific per-process core file name pattern]:' \
+  '-P[PID-specific per-process core file content]:content:_values -s + "content" $content' \
+  '*:pids:_pids' \
+  - set3 \
+  '-u[update options from coreadm.conf]'
diff --git a/Completion/Solaris/Command/_dladm b/Completion/Solaris/Command/_dladm
index 5e5ab0172..cb598de03 100644
--- a/Completion/Solaris/Command/_dladm
+++ b/Completion/Solaris/Command/_dladm
@@ -1,723 +1,734 @@
 #compdef dladm
-# Synced with the S11U1 build 19 man page
 
 _dladm_links() {
-	compadd "$@" - $(dladm show-link -p -o link)
+  compadd "$@" - $(_call_program links dladm show-link -p -o link)
 }
 
 _dladm_devs() {
-	compadd "$@" - $(dladm show-phys -p -o device)
+  compadd "$@" - $(_call_program devices dladm show-phys -p -o device)
 }
 
 _dladm_aggrs() {
-	compadd "$@" - $(dladm show-aggr -p -o link)
+  compadd "$@" - $(_call_program aggregations dladm show-aggr -p -o link)
 }
 
 _dladm_aggr_ports() {
-	compadd "$@" - $(dladm show-aggr -p -x -o ports)
+  compadd "$@" - $(_call_program ports dladm show-aggr -p -x -o ports)
 }
 
 _dladm_vlans() {
-	compadd "$@" - $(dladm show-vlan -p -o link)
+  compadd "$@" - $(_call_program vlans dladm show-vlan -p -o link)
 }
 
 _dladm_wifi_links() {
-	compadd "$@" - $(dladm show-wifi -p -o link)
+  compadd "$@" - $(_call_program wifi-links dladm show-wifi -p -o link)
 }
 
 _dladm_wifi_nets() {
-	compadd "$@" - ${(f)"$(dladm scan-wifi -p -o essid)"}
+  compadd "$@" - ${(f)"$(_call_program networks dladm scan-wifi -p -o essid)"}
 }
 
 _dladm_secobjs() {
-	compadd "$@" - ${(f)"$(dladm show-secobj -p -o object)"}
+  compadd "$@" - ${(f)"$(_call_program secure-objects dladm show-secobj -p -o object)"}
 }
 
 _dladm_ethers() {
-	compadd "$@" - $(dladm show-ether -p -o link)
+  compadd "$@" - $(_call_program ethers dladm show-ether -p -o link)
 }
 
 _dladm_vnics() {
-	compadd "$@" - $(dladm show-vnic -p -o link)
+  compadd "$@" - $(_call_program vnics dladm show-vnic -p -o link)
 }
 
 _dladm_etherstubs() {
-	compadd "$@" - $(dladm show-etherstub)
+  compadd "$@" - $(_call_program etherstubs dladm show-etherstub)
 }
 
 _dladm_bridges() {
-	compadd "$@" - $(dladm show-bridge -p -o bridge)
+  compadd "$@" - $(_call_program bridges dladm show-bridge -p -o bridge)
 }
 
 _dladm_iptuns() {
-	compadd "$@" - $(dladm show-iptun -p -o link)
+  compadd "$@" - $(_call_program ip-tunnels dladm show-iptun -p -o link)
 }
 
 _dladm_parts() {
-	compadd "$@" - $(dladm show-part -p -o link)
+  compadd "$@" - $(_call_program parts dladm show-part -p -o link)
 }
 
 _dladm_iblinks() {
-	compadd "$@" - $(dladm show-ib -p -o link)
+  compadd "$@" - $(_call_program iblinks dladm show-ib -p -o link)
 }
 
 _dladm() {
-	local context state line expl
-	typeset -A opt_args
-	local -a subcmds
-	local -a linkprops linkprops_general linkprops_nonvlanvnic linkprops_wifi
-	local -a linkprops_ether linkprops_ib linkprops_iptun
-	local -a link_properties link_stats_properties vnic_properties
-	local -a aggr_properties aggr_lacp_properties aggr_ext_properties
-	local -a vlan_properties wifi_properties wifi_connect_properties
-	local -a ether_properties linkprop_properties secobj_properties
-	local -a bridge_properties bridge_stats_properties bridge_link_properties
-	local -a bridge_link_stats_properties bridge_fwd_properties
-	local -a bridge_fwd_properties bridge_trill_properties
-	local -a iptun_properties tunnel_values part_properties ib_properties
-
-	# TODO: some subcommands can take multiple comma-separated targets
-	# TODO: some option sets may be different based on other commandline flags
-	# TODO: some subcommands may take different arguments based on options
-
-	subcmds=(
-		"help" "show-ether" "show-ib" "show-usage"
-		{"rename","show"}"-link"
-		{"add","create","delete","modify","remove","show"}"-aggr"
-		{"connect","disconnect","scan","show"}"-wifi"
-		{"reset","set","show"}"-linkprop"
-		{"create","delete","show"}"-secobj"
-		{"create","delete","modify","show"}"-vlan"
-		{"delete","show"}"-phys"
-		{"create","delete","modify","show"}"-vnic"
-		{"create","delete","show"}"-etherstub"
-		{"create","modify","delete","add","remove","show"}"-bridge"
-		{"create","modify","delete","show"}"-iptun"
-		{"create","delete","show"}"-part"
-	)
-
-	if [[ $service == "dladm" ]]; then
-		_arguments -C -A "-*" \
-			'-\?[Help]' \
-			'*::command:->subcmd' && return 0
-
-		if (( CURRENT == 1 )); then
-			_wanted commands expl "dladm subcommand" compadd -a subcmds
-			return
-		fi
-		service="$words[1]"
-		curcontext="${curcontext%:*}=$service:"
-	fi
-
-	link_properties=( "link" "zone" "class" "mtu" "state" "over" )
-	link_stats_properties=( "link" "ipackets" "rbytes" "ierrors" "opackets" "obytes" "oerrors" )
-
-	aggr_properties=( "link" "policy" "addrpolicy" "lacpactivity" "lacptimer" "mode" "flags" )
-	aggr_lacp_properties=( "link" "port" "aggregatable" "sync" "coll" "dist" "defaulted" "expired" )
-	aggr_ext_properties=( "link" "port" "speed" "duplex" "state" "address" "portstate" )
-
-	vlan_properties=( "link" "vid" "over" "flags" )
-
-	wifi_connect_properties=( "link" "essid" "bssid" "sec" "mode" "strength" "speed" "bsstype" )
-	wifi_properties=( $wifi_connect_properties "status" "auth" )
-
-	ether_properties=( "link" "ptype" "state" "auto" "speed-duplex" "pause" "rem_fault" )
-
-	linkprop_properties=( "link" "property" "value" "default" "possible" )
-
-	secobj_properties=( "object" "class" )
-
-	vnic_properties=( "link" "over" "speed" "macaddr" "macaddrtype" )
-
-	bridge_properties=( "bridge"  "address" "priority" "bmaxage" "bhellotime" "bfwddelay" 
-		"forceproto" "tctime" "tccount" "tchange" "desroot" "rootcost" "rootport"
-		"maxage" "hellotime" "fwddelay" "holdtime" )
-	bridge_stats_properties=( "bridge" "drops" "forwards" "mbcast" "recv" "sent" "unknown" )
-	bridge_link_properties=( "link" "index" "state" "uptime" "opercost" "operp2p" "operedge" 
-		"desroot" "descost" "desbridge" "desport" "tcack" )
-	bridge_link_stats_properties=( "link" "cfgbpdu" "tcnbpdu" "rstpbpdu" "txbpdu" "drops" "recv" "xmit" )
-	bridge_fwd_properties=( "dest" "age" "flags" "output" )
-	bridge_trill_properties=( "nick" "flags" "link" "nexthop" )
-
-	iptun_properties=( "link" "type" "flags" "local" "remote" )
-	tunnel_values=( "local:address/host: " "remote:address/host: " )
-
-	part_properties=( "link" "pkey" "over" "state" "flags" )
-
-	ib_properties=( "link" "hcaguid" "portguid" "port" "state" "pkeys" )
-
-	linkprops_general=(
-		"autopush:streams modules:"
-		"cos:value:(0 1 2 3 4 5 6 7)"
-		"cpus:processors:"
-		"cpus-effective"
-		"etsbw-lcl:percentage:"
-		"etsbw-lcl-advice"
-		"etsbw-lcl-effective"
-		"etsbw-rmt-effective"
-		"lro:value:(off on auto)"
-		"lro-effective"
-		"mac-address:MAC address:"
-		"maxbw:bandwith:"
-		"pool:pools:"
-		"pool-effective"
-		"priority:priority:(high medium low)"
-		"rxringsavail"
-		"rxrings:value:"
-		"rxhwclntavail"
-		"txringsavail"
-		"txrings:value:"
-		"txhwclntavail"
-		"forward:value:(0 1)"
-		"stp_priority:value:"
-		"stp_cost:value:"
-		"stp_edge:value:(0 1)"
-		"stp_p2p:value:(true false auto)"
-		"stp_mcheck:value:(0 1)"
-		"protection:value:(mac-nospoof ip-nospoof dhcp-nospoof restricted)"
-		"vsi-mgrid:IPv6 address:"
-		"vsi-mgrid-effective"
-		"vsi-mgrid-enc:encoding:(oracle_v1 none)"
-		"vsi-mgrid-enc-effective"
-		"vsi-typeid:value:"
-		"vsi-typeid-effective"
-		"vsi-vers:value:"
-		"vsi-vers-effective"
-		"zone:value:_zones"
-	)
-	linkprops_nonvlanvnic=(
-		"default_tag:value:"
-		"learn_decay:value:"
-		"learn_limit:value:"
-		"rxfanout:value:"
-		"rxfanout-effective:value:"
-		"stp:value:(0 1)"
-	)
-	linkprops_wifi=(
-		"channel:value:"
-		"powermode:value:(off max fast)"
-		"radio:value:(on off)"
-		"speed:value:"
-	)
-	linkprops_ether=(
-		"duplex"
-		"state"
-		"adv_autoneg_cap"
-		"adv_10gfdx_cap"
-		"adv_1000fdx_cap"
-		"adv_1000hdx_cap"
-		"adv_100fdx_cap"
-		"adv_100hdx_cap"
-		"adv_10fdx_cap"
-		"adv_10hdx_cap"
-		"en_10gfdx_cap:value:(0 1)"
-		"en_1000fdx_cap:value:(0 1)"
-		"en_1000hdx_cap:value:(0 1)"
-		"en_100fdx_cap:value:(0 1)"
-		"en_100hdx_cap:value:(0 1)"
-		"en_10fdx_cap:value:(0 1)"
-		"en_10hdx_cap:value:(0 1)"
-		"flowctrl:value:(auto no rx tx pfc bi)"
-		"flowctrl-effective"
-		"gvrp-timeout:value:"
-		"mtu:value:"
-		"ntcs"
-		"pfcmap:value:"
-		"pfcmap-lcl-effective"
-		"pfcmap-rmt-effective"
-		"speed"
-		"tagmode:value:(normal vlanonly)"
-		"vlan-announce:value:(off gvrp)"
-	)
-	linkprops_ib=(
-		"linkmode:value:(cm ud)"
-	)
-	linkprops_iptun=(
-		"hoplimit:value:"
-		"encaplimit:value:"
-	)
-	linkprops=(
-		$linkprops_general $linkprops_wifi $linkprops_ether
-		$linkprops_ib $linkprops_iptun
-	)
-
-	case $service in
-	("help")
-		_arguments ':subcommand:($subcmds)'
-		;;
-
-	("show-link")
-		_arguments -A "-*" \
-			'(-P --persistent)'{-P,--persistent}'[Display persistent link configuration]' \
-			'(-p --parseable)'{-p,--parseable}'[Parseable output]' \
-			'-Z[Display ZONE column in output]' \
-			'-z[zone]:zonename:_values -s , "zone" $(zoneadm list)' \
-			- set1 \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $link_properties' \
-			- set2 \
-			'(-s --statistics)'{-s,--statistics}'[Display link statistics]' \
-			'(-i --interval)'{-i,--interval}'[Specify an interval]:interval:' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $link_stats_properties' \
-			':link name:_dladm_links'
-		;;
-
-	("rename-link")
-		_arguments -A "-*" \
-			'-R[Root directory]:directory:_path_files -/' \
-			':old link name:_dladm_links' \
-			':new link name:'
-		;;
-
-	("show-phys")
-		_arguments -A "-*" \
-			'-D[Show Data Center Bridging information]:featureset:(ets pfc)' \
-			'-H[Show hardware resource usage]' \
-			'-L[Display location information]' \
-			'(-P --persistent)'{-P,--persistent}'[Display persistent link configuration]' \
-			'(-p --parseable)'{-p,--parseable}'[Parseable output]' \
-			'-m[Display MAC address information]' \
-			'-Z[Display ZONE column in output]' \
-			'-z[zone]:zonename:_values -s , "zone" $(zoneadm list)' \
-			'(-s --statistics)'{-s,--statistics}'[Display link statistics]' \
-			'(-i --interval)'{-i,--interval}'[Specify an interval]:interval:' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" link media state speed duplex device' \
-			':physical link name:_dladm_devs'
-		;;
-
-	("delete-phys")
-		_arguments -A "-*" \
-			':physical link name:_dladm_devs'
-		;;
-
-	("create-aggr")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Aggregation should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-l --link)'{-l,--link}'[Component link]:link:_dladm_links' \
-			'(-m --mode)'{-m,--mode}'[Aggregation mode]:mode:(dlmp trunk)' \
-			'(-P --policy)'{-P,--policy}'[Port selection policy]:policy:_values -s , "policy" L2 L3 L4' \
-			'(-L --lacp-mode)'{-L,--lacp-mode}'[LACP mode]:lacp mode:(off active passive)' \
-			'(-T --lacp-timer)'{-T,--lacp-timer}'[LACP timer]:lacp timer:(short long)' \
-			'(-u --unicast)'{-u,--unicast}'[Unicast address]:unicast address:' \
-			':aggregate link name:'
-		;;
-
-	("modify-aggr")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Aggregation should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-l --link)'{-l,--link}'[Component link]:link:_dladm_links' \
-			'(-m --mode)'{-m,--mode}'[Aggregation mode]:mode:(dlmp trunk)' \
-			'(-P --policy)'{-P,--policy}'[Port selection policy]:policy:_values -s , "policy" L2 L3 L4' \
-			'(-L --lacp-mode)'{-L,--lacp-mode}'[LACP mode]:lacp mode:(off active passive)' \
-			'(-T --lacp-timer)'{-T,--lacp-timer}'[LACP timer]:lacp timer:(short long)' \
-			'(-u --unicast)'{-u,--unicast}'[Unicast address]:unicast address:' \
-			':aggregate link name:_dladm_aggrs'
-		;;
-
-	("delete-aggr")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Deletion should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			':aggregate link name:_dladm_aggrs'
-		;;
-
-	("add-aggr")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Aggregation should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-l --link)'{-l,--link}'[Component link]:link:_dladm_links' \
-			':aggregate link name:_dladm_aggrs'
-		;;
-
-	("remove-aggr")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Aggregation should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-l --link)'{-l,--link}'[Component link]:link:_dladm_aggr_ports' \
-			':aggregate link name:_dladm_aggrs'
-		;;
-
-	("show-aggr")
-		_arguments -A "-*" \
-			'(-P --persistent)'{-P,--persistent}'[Display persistent link configuration]' \
-			'(-p --parseable)'{-p,--parseable}'[Parseable output]' \
-			'(-s --statistics)'{-s,--statistics}'[Display link statistics]' \
-			'(-i --interval)'{-i,--interval}'[Specify an interval]:interval:' \
-			'-Z[Display ZONE column in output]' \
-			'-z[zone]:zonename:_values -s , "zone" $(zoneadm list)' \
-			':aggregate link name:_dladm_links' \
-			- set1 \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $aggr_properties' \
-			- lacp \
-			'(-L --lacp)'{-L,--lacp}'[LACP information]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $aggr_lacp_properties' \
-			- extended \
-			'(-x --extended)'{-x,--extended}'[Extended information]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $aggr_ext_properties'
-		;;
-
-	("create-vlan")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[VLAN should be temporary]' \
-			'(-f --force)'{-f,--force}'[Force VLAN creation]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-l --link)'{-l,--link}'[Component link]:link:_dladm_links' \
-			'-v[VLAN ID]:id:' \
-			':VLAN link name:'
-		;;
-
-	("delete-vlan")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Deletion should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			':VLAN link name:_dladm_vlans'
-		;;
-
-	("modify-vlan")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[VLAN should be temporary]' \
-			'(-f --force)'{-f,--force}'[Force VLAN creation]' \
-			'(-R --root-dir)'{-R,--root-dir}'[Root directory]:directory:_path_files -/' \
-			'(-l --link)'{-l,--link}'[Component link]:link:_dladm_links' \
-			'-v[VLAN ID]:id:' \
-			- set1 \
-			'-L[Source link]:link:_dladm_links' \
-			- set2 \
-			':VLAN link name:'
-		;;
-
-	("show-vlan")
-		_arguments -A "-*" \
-			'(-P --persistent)'{-P,--persistent}'[Display persistent link configuration]' \
-			'(-p --parseable)'{-p,--parseable}'[Parseable output]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $vlan_properties' \
-			'-Z[Display ZONE column in output]' \
-			'-z[zone]:zonename:_values -s , "zone" $(zoneadm list)' \
-			':VLAN link name:_dladm_vlans'
-		;;
-
-	("scan-wifi")
-		_arguments -A "-*" \
-			'(-p --parseable)'{-p,--parseable}'[Parseable output]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $wifi_connect_properties' \
-			':wireless link name:_dladm_wifi_links'
-		;;
-
-	("connect-wifi")
-		_arguments -A "-*" \
-			'(-e --essid)'{-e,--essid}'[ESSID name]:network:_dladm_wifi_nets' \
-			'(-b --bsstype)'{-b,--bsstype}'[BSS type]:' \
-			'(-m --mode)'{-m,--mode}'[802.11 mode]:802.11 mode:(a b g n)' \
-			'(-k --key)'{-k,--key}'[Key name]:key:_dladm_secobjs' \
-			'(-s --sec)'{-s,--sec}'[Security mode]:security mode:(none wep wpa)' \
-			'(-a --auth)'{-a,--auth}'[Authentication mode]:authentication mode:(open shared)' \
-			'(-c --create-ibss)'{-c,--create-ibss}'[Create an ad-hoc network]' \
-			'(-T --timeout)'{-T,--timeout}'[Association timeout]:association timeout:(forever)' \
-			':wireless link name:_dladm_wifi_links'
-		;;
-
-	("disconnect-wifi")
-		_arguments -A "-*" \
-			- set1 \
-			'(-a --all-links)'{-a,--all-links}'[All links]' \
-			- set2 \
-			':wireless link name:_dladm_wifi_links'
-		;;
-
-	("show-wifi")
-		_arguments -A "-*" \
-			'(-p --parseable)'{-p,--parseable}'[Parseable output]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $wifi_properties' \
-			'-Z[Display ZONE column in output]' \
-			'-z[zone]:zonename:_values -s , "zone" $(zoneadm list)' \
-			':wireless link name:_dladm_wifi_links'
-		;;
-
-	("show-ether")
-		_arguments -A "-*" \
-			'(-p --parseable)'{-p,--parseable}'[Parseable output]' \
-			'(-x --extended)'{-x,--extended}'[Extended output]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $ether_properties' \
-			'-P[protocol]:protocol:(ecp vdp)' \
-			'-Z[Display ZONE column in output]' \
-			'-z[zone]:zonename:_values -s , "zone" $(zoneadm list)' \
-			':ethernet link name:_dladm_ethers'
-		;;
-
-	("set-linkprop")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Change should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-p --prop)'{-p,--prop}'[Properties]:property:_values -s , "property" ${(M)linkprops\:#*\:*}' \
-			':link name:_dladm_links'
-		;;
-
-	("reset-linkprop")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Change should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-p --prop)'{-p,--prop}'[Properties]:property:_values -s , "property" ${${(M)linkprops\:#*\:*}%%\:*}' \
-			':link name:_dladm_links'
-		;;
-
-	("show-linkprop")
-		_arguments -A "-*" \
-			'(-P --persistent)'{-P,--persistent}'[Display persistent link properties]' \
-			'(-c --parseable)'{-c,--parseable}'[Parseable output]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $linkprop_properties' \
-			'(-p --prop)'{-p,--prop}'[Properties]:property:_values -s , "property" ${linkprops%%\:*}' \
-			'-Z[Display ZONE column in output]' \
-			'-z[zone]:zonename:_values -s , "zone" $(zoneadm list)' \
-			':link name:_dladm_links'
-		;;
-
-	("create-secobj")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Creation should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-c --class)'{-c,--class}'[Class]:class:(wep wpa)' \
-			'(-f --file)'{-f,--file}'[File containing object value]:file:_path_files' \
-			':object name:'
-		;;
-
-	("delete-secobj")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Deletion should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			':object name:_dladm_secobjs'
-		;;
-
-	("show-secobj")
-		_arguments -A "-*" \
-			'(-P --persistent)'{-P,--persistent}'[Display persistent object information]' \
-			'(-p --parseable)'{-p,--parseable}'[Parseable output]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $secobj_properties' \
-			':object name:_dladm_secobjs'
-		;;
-
-	("create-vnic")
-		# TODO: MAC address completion could be richer
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Creation should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-l --link)'{-l,--link}'[Component link]:link:_dladm_links' \
-			'(-m --mac-address)'{-m,--mac-address}'[MAC address]:address:(factory random auto vrrp)' \
-			'-v[VLAN ID]:id:' \
-			'(-p --prop)'{-p,--prop}'[Property values]:value:_values -s , "property" ${(M)linkprops_general\:#*\:*}' \
-			':VNIC name:'
-		;;
-
-	("delete-vnic")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Deletion should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			':VNIC name:_dladm_vnics'
-		;;
-
-	("modify-vnic")
-		# TODO: MAC address completion could be richer
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Creation should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-l --link)'{-l,--link}'[Component link]:link:_dladm_links' \
-			'(-m --mac-address)'{-m,--mac-address}'[MAC address]:address:(factory random auto vrrp)' \
-			'-v[VLAN ID]:id:' \
-			'(-p --prop)'{-p,--prop}'[Property values]:value:_values -s , "property" ${(M)linkprops_general\:#*\:*}' \
-			- set1 \
-			'-L[Source link]:link:_dladm_links' \
-			- set2 \
-			':VNIC name:'
-		;;
-
-	("show-vnic")
-		_arguments -A "-*" \
-			'(-P --persistent)'{-P,--persistent}'[Display persistent object information]' \
-			'(-p --parseable)'{-p,--parseable}'[Parseable output]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $vnic_properties' \
-			'(-l --link)'{-l,--link}'[Limit to VNICs on link]:link:_dladm_links' \
-			'(-s --statistics)'{-s,--statistics}'[Display VNIC statistics]' \
-			'(-i --interval)'{-i,--interval}'[Specify an interval]:interval:' \
-			'-Z[Display ZONE column in output]' \
-			'-z[zone]:zonename:_values -s , "zone" $(zoneadm list)' \
-			':VNIC name:_dladm_vnics'
-		;;
-
-	("create-etherstub")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Creation should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			':etherstub name:'
-		;;
-
-	("delete-etherstub")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Deletion should be temporary]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			':etherstub name:_dladm_etherstubs'
-		;;
-
-	("show-etherstub")
-		_arguments -A "-*" \
-			'-Z[Display ZONE column in output]' \
-			'-z[zone]:zonename:_values -s , "zone" $(zoneadm list)' \
-			':etherstub name:_dladm_etherstubs'
-		;;
-
-	("show-usage")
-		_arguments -A "-*" \
-			'(-f --file)'{-f,--file}'[Read records from file]:file:_path_files' \
-			'(-F --format)'{-F,--format}'[Plotfile format]:plotfile format:(gnuplot)' \
-			'(-p --plot)'{-p,--plot}'[Write plot to file]:' \
-			'(-e --start)'{-e,--start}'[Start time]:date/time (MM/DD/YYYY,hh\:mm\:ss)' \
-			'(-s --stop)'{-s,--stop}'[Stop time]:date/time (MM/DD/YYYY,hh\:mm\:ss)' \
-			':link name:_dladm_links'
-		;;
-
-	("create-bridge")
-		_arguments -A "-*" \
-			'(-P --protect)'{-P,--protect}'[Specify a protection method]:protection method:(stp trill)' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-p --priority)'{-p,--priority}'[Specify the bridge priority]:value' \
-			'(-m --max-age)'{-m,--max-age}'[Specify the max age for config info]:value' \
-			'(-h --hello-time)'{-h,--hello-time}'[Specify the hello time]:value' \
-			'(-d --forward-delay)'{-d,--forward-delay}'[Specify the forward delay]:value' \
-			'(-f --force-protocol)'{-f,--force-protocol}'[Specify forced maximum supported protocol]:value' \
-			'*'{-l,--link}'[Specify link to add]:link:_dladm_links' \
-			':bridge name:'
-		;;
-
-
-	("modify-bridge")
-		_arguments -A "-*" \
-			'(-P --protect)'{-P,--protect}'[Specify a protection method]:protection method:(stp trill)' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-p --priority)'{-p,--priority}'[Specify the bridge priority]:value' \
-			'(-m --max-age)'{-m,--max-age}'[Specify the max age for config info]:value' \
-			'(-h --hello-time)'{-h,--hello-time}'[Specify the hello time]:value' \
-			'(-d --forward-delay)'{-d,--forward-delay}'[Specify the forward delay]:value' \
-			'(-f --force-protocol)'{-f,--force-protocol}'[Specify forced maximum supported protocol]:value' \
-			':bridge name:_dladm_bridges'
-		;;
-
-
-	("delete-bridge")
-		_arguments -A "-*" \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			':bridge name:_dladm_bridges'
-		;;
-
-
-	("add-bridge"|"remove-bridge")
-		_arguments -A "-*" \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'*'{-l,--link}'[Specify link to add]:link:_dladm_links' \
-			':bridge name:_dladm_bridges'
-		;;
-
-
-	("show-bridge")
-		# XXX $bridge_stats_properties get added into -o completions for set1
-		# XXX $bridge_link_stats_properties get added into -o completions for set3
-		_arguments -A "-*" \
-			'(-p --parseable)'{-p,--parseable}'[Parseable output]' \
-			- set1 \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $bridge_properties' \
-			- set2 \
-			'(-s --statistics)'{-s,--statistics}'[Display statistics]' \
-			'(-i --interval)'{-i,--interval}'[Specify an interval]:seconds' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $bridge_stats_properties' \
-			- set3 \
-			'(-l --link)'{-l,--link}'[Display link status or statistics]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $bridge_link_properties' \
-			- set4 \
-			'(-s --statistics)'{-s,--statistics}'[Display statistics]' \
-			'(-l --link)'{-l,--link}'[Display link status or statistics]' \
-			'(-i --interval)'{-i,--interval}'[Specify an interval]:seconds' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $bridge_link_stats_properties' \
-			- set5 \
-			'(-f --forwarding)'{-f,--forwarding}'[Display forwarding entries]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $bridge_fwd_properties' \
-			- set6 \
-			'(-t --trill)'{-t,--trill}'[Display TRILL nickname entries]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $bridge_trill_properties' \
-			':bridge name:_dladm_bridges'
-		;;
-
-
-	("create-iptun")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Temporary tunnel]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-T --type)'{-T,--type}'[Tunnel type]:tunnel type:(ipv4 ipv6 6to4)' \
-			'(-a --address)'{-a,--address}'[Endpoint addresses]:address/host:_values -s , "address/host" $tunnel_values' \
-			':tunnel name:'
-		;;
-
-	("modify-iptun")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Temporary modification]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-a --address)'{-a,--address}'[Endpoint addresses]:address/host:_values -s , "address/host" $tunnel_values' \
-			':tunnel name:_dladm_iptuns'
-		;;
-
-	("delete-iptun")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Temporary deletion]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			':tunnel name:_dladm_iptuns'
-		;;
-
-	("show-iptun")
-		_arguments -A "-*" \
-			'(-P --persistent)'{-P,--persistent}'[Display persistent tunnel configuration]' \
-			'(-p --parseable)'{-p,--parseable}'[Parseable output]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $iptun_properties' \
-			'-Z[Display ZONE column in output]' \
-			'-z[zone]:zonename:_values -s , "zone" $(zoneadm list)' \
-			':tunnel name:_dladm_iptuns'
-		;;
-
-	("create-part")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Temporary partition]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			'(-f --force)'{-f,--force}'[Force partition creation]' \
-			'(-l --link)'{-l,--link}'[IP-over-IB physical link name]:IB link:_dladm_iblinks' \
-			'(-p --prop)'{-p,--prop}'[Set link properties]:link property:_values -s , "property" ${(M)linkprops_nonvlanvnic\:#*\:*} ${(M)linkprops_general\:#*\:*}' \
-			'(-P --pkey)'{-P,--pkey}'[Set parition key]:hex number:' \
-			':partition link name:'
-		;;
-
-	("delete-part")
-		_arguments -A "-*" \
-			'(-t --temporary)'{-t,--temporary}'[Temporary deletion]' \
-			'(-R --root)'{-R,--root}'[Root directory]:directory:_path_files -/' \
-			':link name:_dladm_parts'
-		;;
-
-	("show-part")
-		_arguments -A "-*" \
-			'(-P --persistent)'{-P,--persistent}'[Display persistent partition configuration]' \
-			'(-p --parseable)'{-p,--parseable}'[Parseable output]' \
-			'(-l --link)'{-l,--link}'[Information for this link]:link name:_dladm_iblinks' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $part_properties' \
-			':partition link name:_dladm_parts'
-		;;
-
-	("show-ib")
-		_arguments -A "-*" \
-			'(-P --persistent)'{-P,--persistent}'[Display persistent partition configuration]' \
-			'(-p --parseable)'{-p,--parseable}'[Parseable output]' \
-			'(-o --output)'{-o,--output}'[Properties to display]:property:_values -s , "property" $ib_properties' \
-			':IB link name:_dladm_iblinks'
-		;;
-
-	(*)
-		_message "unknown dladm subcommand: $service"
-		;;
-	esac
+  local curcontext="$curcontext" state line expl
+  typeset -A opt_args
+  local -a subcmds
+  local -a linkprops linkprops_general linkprops_nonvlanvnic linkprops_wifi
+  local -a linkprops_ether linkprops_ib linkprops_iptun
+  local -a link_properties link_stats_properties vnic_properties
+  local -a aggr_properties aggr_lacp_properties aggr_ext_properties
+  local -a vlan_properties wifi_properties wifi_connect_properties
+  local -a ether_properties linkprop_properties secobj_properties
+  local -a bridge_properties bridge_stats_properties bridge_link_properties
+  local -a bridge_link_stats_properties bridge_fwd_properties
+  local -a bridge_fwd_properties bridge_trill_properties
+  local -a iptun_properties tunnel_values part_properties ib_properties
+
+  # TODO: some subcommands can take multiple comma-separated targets
+  # TODO: some option sets may be different based on other commandline flags
+  # TODO: some subcommands may take different arguments based on options
+
+  subcmds=(
+    help show-ether show-ib
+    {add,create,delete,modify,remove,show}-{aggr,bridge}
+    {rename,show}-link
+    {connect,disconnect,scan,show}-wifi
+    {reset,set,show}-linkprop
+    {create,delete,modify,show}-vlan
+    {delete,show}-phys
+    {create,delete,modify,show}-{vnic,iptun}
+    {create,delete,show}-{etherstub,secobj,part,cap}
+  )
+
+  if [[ $service == "dladm" ]]; then
+    _arguments -C -A "-*" \
+      '-\?[display help information]' \
+      '*::command:->subcmd' && return 0
+
+    if (( CURRENT == 1 )); then
+      _wanted commands expl "dladm subcommand" compadd -M 'r:|-=* r:|=*' -a subcmds
+      return
+    fi
+    service="$words[1]"
+    curcontext="${curcontext%:*}-$service:"
+  fi
+
+  link_properties=( link zone class mtu state over )
+  link_stats_properties=( link ipackets rbytes ierrors opackets obytes oerrors )
+
+  aggr_properties=( link policy addrpolicy lacpactivity lacptimer mode flags )
+  aggr_lacp_properties=( link port aggregatable sync coll dist defaulted expired )
+  aggr_ext_properties=( link port speed duplex state address portstate )
+
+  vlan_properties=( link vid over flags )
+
+  wifi_connect_properties=( link essid bssid sec mode strength speed bsstype )
+  wifi_properties=( $wifi_connect_properties status auth )
+
+  ether_properties=( link ptype state auto speed-duplex pause rem_fault )
+
+  linkprop_properties=( link property value default possible )
+
+  secobj_properties=( object class )
+
+  vnic_properties=( link over speed macaddr macaddrtype )
+
+  bridge_properties=( bridge  address priority bmaxage bhellotime bfwddelay
+    forceproto tctime tccount tchange desroot rootcost rootport
+    maxage hellotime fwddelay holdtime )
+  bridge_stats_properties=( bridge drops forwards mbcast recv sent unknown )
+  bridge_link_properties=( link index state uptime opercost operp2p operedge
+    desroot descost desbridge desport tcack )
+  bridge_link_stats_properties=( link cfgbpdu tcnbpdu rstpbpdu txbpdu drops recv xmit )
+  bridge_fwd_properties=( dest age flags output )
+  bridge_trill_properties=( nick flags link nexthop )
+
+  iptun_properties=( link type flags local remote )
+  tunnel_values=( 'local:address/host:' 'remote:address/host:' )
+
+  part_properties=( link pkey over state flags )
+
+  ib_properties=( link hcaguid portguid port state pkeys )
+
+  linkprops_general=(
+    'autopush:streams modules'
+    'cos:value:(0 1 2 3 4 5 6 7)'
+    'cpus:processors'
+    'cpus-effective'
+    'etsbw-lcl:percentage:'
+    'etsbw-lcl-advice'
+    'etsbw-lcl-effective'
+    'etsbw-rmt-effective'
+    'lro:value:(off on auto)'
+    'lro-effective'
+    'mac-address:MAC address'
+    'maxbw:bandwith'
+    'pool:pools'
+    'pool-effective'
+    'priority:priority:(high medium low)'
+    'rxringsavail'
+    'rxrings:value'
+    'rxhwclntavail'
+    'txringsavail'
+    'txrings:value'
+    'txhwclntavail'
+    'forward:value:(0 1)'
+    'stp_priority:value'
+    'stp_cost:value'
+    'stp_edge:value:(0 1)'
+    'stp_p2p:value:(true false auto)'
+    'stp_mcheck:value:(0 1)'
+    'protection:value:(mac-nospoof ip-nospoof dhcp-nospoof restricted)'
+    'vsi-mgrid:IPv6 address'
+    'vsi-mgrid-effective'
+    'vsi-mgrid-enc:encoding:(oracle_v1 none)'
+    'vsi-mgrid-enc-effective'
+    'vsi-typeid:value'
+    'vsi-typeid-effective'
+    'vsi-vers:value'
+    'vsi-vers-effective'
+    'zone:zone:_zones'
+  )
+  linkprops_nonvlanvnic=(
+    'default_tag:value'
+    'learn_decay:value'
+    'learn_limit:value'
+    'rxfanout:value'
+    'rxfanout-effective:value'
+    'stp:value:(0 1)'
+  )
+  linkprops_wifi=(
+    'channel:value'
+    'powermode:value:(off max fast)'
+    'radio:value:(on off)'
+    'speed:value'
+  )
+  linkprops_ether=(
+    'duplex'
+    'state'
+    'adv_autoneg_cap'
+    'adv_10gfdx_cap'
+    'adv_1000fdx_cap'
+    'adv_1000hdx_cap'
+    'adv_100fdx_cap'
+    'adv_100hdx_cap'
+    'adv_10fdx_cap'
+    'adv_10hdx_cap'
+    'en_10gfdx_cap:value:(0 1)'
+    'en_1000fdx_cap:value:(0 1)'
+    'en_1000hdx_cap:value:(0 1)'
+    'en_100fdx_cap:value:(0 1)'
+    'en_100hdx_cap:value:(0 1)'
+    'en_10fdx_cap:value:(0 1)'
+    'en_10hdx_cap:value:(0 1)'
+    'flowctrl:value:(auto no rx tx pfc bi)'
+    'flowctrl-effective'
+    'gvrp-timeout:value'
+    'mtu:value'
+    'ntcs'
+    'pfcmap:value'
+    'pfcmap-lcl-effective'
+    'pfcmap-rmt-effective'
+    'speed'
+    'tagmode:value:(normal vlanonly)'
+    'vlan-announce:value:(off gvrp)'
+  )
+  linkprops_ib=(
+    "linkmode:value:(cm ud)"
+  )
+  linkprops_iptun=(
+    "hoplimit:value"
+    "encaplimit:value"
+  )
+  linkprops=(
+    $linkprops_general $linkprops_wifi $linkprops_ether
+    $linkprops_ib $linkprops_iptun
+  )
+
+  case $service in
+  (help)
+    _wanted commands expl "dladm subcommand" compadd -M 'r:|-=* r:|=*' -a subcmds
+  ;;
+
+  (show-link)
+    _arguments -A "-*" \
+      '(-P --persistent)'{-P,--persistent}'[display persistent link configuration]' \
+      '(-p --parseable)'{-p,--parseable}'[parseable output]' \
+      '-Z[display ZONE column in output]' \
+      '-z[zone]:zone:_sequence _zones' \
+      - set1 \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $link_properties' \
+      - set2 \
+      '(-s --statistics)'{-s,--statistics}'[display link statistics]' \
+      '(-i --interval)'{-i,--interval}'[specify an interval]:interval:' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $link_stats_properties' \
+      ':link name:_dladm_links'
+  ;;
+
+  (rename-link)
+    _arguments -A "-*" \
+      '-R[root directory]:directory:_path_files -/' \
+      ':old link name:_dladm_links' \
+      ':new link name:'
+  ;;
+
+  (show-phys)
+    _arguments -A "-*" \
+      '-D[show Data Center Bridging information]:featureset:(ets pfc)' \
+      '-H[show hardware resource usage]' \
+      '-L[display location information]' \
+      '(-P --persistent)'{-P,--persistent}'[display persistent link configuration]' \
+      '(-p --parseable)'{-p,--parseable}'[parseable output]' \
+      '-m[display MAC address information]' \
+      '-Z[display ZONE column in output]' \
+      '-z[zone]:zone:_sequence _zones' \
+      '(-s --statistics)'{-s,--statistics}'[display link statistics]' \
+      '(-i --interval)'{-i,--interval}'[specify an interval]:interval:' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" link media state speed duplex device' \
+      ':physical link name:_dladm_devs'
+  ;;
+
+  (delete-phys)
+    _arguments -A "-*" \
+      ':physical link name:_dladm_devs'
+  ;;
+
+  (create-aggr)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[aggregation should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-l --link)'{-l,--link}'[component link]:link:_dladm_links' \
+      '(-m --mode)'{-m,--mode}'[aggregation mode]:mode:(dlmp trunk)' \
+      '(-P --policy)'{-P,--policy}'[port selection policy]:policy:_values -s , "policy" L2 L3 L4' \
+      '(-L --lacp-mode)'{-L,--lacp-mode}'[LACP mode]:lacp mode:(off active passive)' \
+      '(-T --lacp-timer)'{-T,--lacp-timer}'[LACP timer]:lacp timer:(short long)' \
+      '(-u --unicast)'{-u,--unicast}'[unicast address]:unicast address:' \
+      ':aggregate link name:'
+  ;;
+
+  (modify-aggr)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[aggregation should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-l --link)'{-l,--link}'[component link]:link:_dladm_links' \
+      '(-m --mode)'{-m,--mode}'[aggregation mode]:mode:(dlmp trunk)' \
+      '(-P --policy)'{-P,--policy}'[port selection policy]:policy:_values -s , "policy" L2 L3 L4' \
+      '(-L --lacp-mode)'{-L,--lacp-mode}'[LACP mode]:lacp mode:(off active passive)' \
+      '(-T --lacp-timer)'{-T,--lacp-timer}'[LACP timer]:lacp timer:(short long)' \
+      '(-u --unicast)'{-u,--unicast}'[unicast address]:unicast address:' \
+      ':aggregate link name:_dladm_aggrs'
+  ;;
+
+  (delete-aggr)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[deletion should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      ':aggregate link name:_dladm_aggrs'
+  ;;
+
+  (add-aggr)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[aggregation should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-l --link)'{-l,--link}'[component link]:link:_dladm_links' \
+      ':aggregate link name:_dladm_aggrs'
+  ;;
+
+  (remove-aggr)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[aggregation should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-l --link)'{-l,--link}'[component link]:link:_dladm_aggr_ports' \
+      ':aggregate link name:_dladm_aggrs'
+  ;;
+
+  (show-aggr)
+    _arguments -A "-*" \
+      '(-P --persistent)'{-P,--persistent}'[display persistent link configuration]' \
+      '(-p --parseable)'{-p,--parseable}'[parseable output]' \
+      '(-s --statistics)'{-s,--statistics}'[display link statistics]' \
+      '(-i --interval)'{-i,--interval}'[specify an interval]:interval:' \
+      '-Z[display ZONE column in output]' \
+      '-z[zone]:zone:_sequence _zones' \
+      ':aggregate link name:_dladm_links' \
+      - set1 \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $aggr_properties' \
+      - lacp \
+      '(-L --lacp)'{-L,--lacp}'[LACP information]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $aggr_lacp_properties' \
+      - extended \
+      '(-x --extended)'{-x,--extended}'[extended information]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $aggr_ext_properties'
+  ;;
+
+  (create-vlan)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[VLAN should be temporary]' \
+      '(-f --force)'{-f,--force}'[force VLAN creation]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-l --link)'{-l,--link}'[component link]:link:_dladm_links' \
+      '-v[VLAN ID]:id:' \
+      ':VLAN link name:'
+  ;;
+
+  (delete-vlan)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[deletion should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      ':VLAN link name:_dladm_vlans'
+  ;;
+
+  (modify-vlan)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[VLAN should be temporary]' \
+      '(-f --force)'{-f,--force}'[force VLAN creation]' \
+      '(-R --root-dir)'{-R,--root-dir}'[root directory]:directory:_path_files -/' \
+      '(-l --link)'{-l,--link}'[component link]:link:_dladm_links' \
+      '-v[VLAN ID]:id:' \
+      - set1 \
+      '-L[source link]:link:_dladm_links' \
+      - set2 \
+      ':VLAN link name:'
+  ;;
+
+  (show-vlan)
+    _arguments -A "-*" \
+      '(-P --persistent)'{-P,--persistent}'[display persistent link configuration]' \
+      '(-p --parseable)'{-p,--parseable}'[parseable output]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $vlan_properties' \
+      '-Z[display ZONE column in output]' \
+      '-z[zone]:zone:_sequence _zones' \
+      ':VLAN link name:_dladm_vlans'
+  ;;
+
+  (scan-wifi)
+    _arguments -A "-*" \
+      '(-p --parseable)'{-p,--parseable}'[parseable output]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $wifi_connect_properties' \
+      ':wireless link name:_dladm_wifi_links'
+  ;;
+
+  (connect-wifi)
+    _arguments -A "-*" \
+      '(-e --essid)'{-e,--essid}'[ESSID name]:network:_dladm_wifi_nets' \
+      '(-b --bsstype)'{-b,--bsstype}'[BSS type]:' \
+      '(-m --mode)'{-m,--mode}'[802.11 mode]:802.11 mode:(a b g n)' \
+      '(-k --key)'{-k,--key}'[key name]:key:_sequence _dladm_secobjs' \
+      '(-s --sec)'{-s,--sec}'[security mode]:security mode:(none wep wpa)' \
+      '(-a --auth)'{-a,--auth}'[authentication mode]:authentication mode:(open shared)' \
+      '(-c --create-ibss)'{-c,--create-ibss}'[create an ad-hoc network]' \
+      '(-T --timeout)'{-T,--timeout}'[association timeout]:association timeout:(forever)' \
+      ':wireless link name:_dladm_wifi_links'
+  ;;
+
+  (disconnect-wifi)
+    _arguments -A "-*" \
+      - set1 \
+      '(-a --all-links)'{-a,--all-links}'[all links]' \
+      - set2 \
+      ':wireless link name:_dladm_wifi_links'
+  ;;
+
+  (show-wifi)
+    _arguments -A "-*" \
+      '(-p --parseable)'{-p,--parseable}'[parseable output]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $wifi_properties' \
+      '-Z[display ZONE column in output]' \
+      '-z[zone]:zone:_sequence _zones' \
+      ':wireless link name:_dladm_wifi_links'
+  ;;
+
+  (show-ether)
+    _arguments -A "-*" \
+      '(-p --parseable)'{-p,--parseable}'[parseable output]' \
+      '(-x --extended)'{-x,--extended}'[extended output]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $ether_properties' \
+      '-P[protocol]:protocol:(ecp vdp)' \
+      '-Z[display ZONE column in output]' \
+      '-z[zone]:zone:_sequence _zones' \
+      ':ethernet link name:_dladm_ethers'
+  ;;
+
+  (set-linkprop)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[change should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-p --prop)'{-p,--prop}'[properties]:property:_values -s , "property" ${(M)linkprops\:#*\:*}' \
+      ':link name:_dladm_links'
+  ;;
+
+  (reset-linkprop)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[change should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-p --prop)'{-p,--prop}'[properties]:property:_values -s , "property" ${${(M)linkprops\:#*\:*}%%\:*}' \
+      ':link name:_dladm_links'
+  ;;
+
+  (show-linkprop)
+    _arguments -A "-*" \
+      '(-P --persistent)'{-P,--persistent}'[display persistent link properties]' \
+      '(-c --parseable)'{-c,--parseable}'[parseable output]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $linkprop_properties' \
+      '(-p --prop)'{-p,--prop}'[properties]:property:_values -s , "property" ${linkprops%%\:*}' \
+      '-Z[display ZONE column in output]' \
+      '-z[zone]:zone:_sequence _zones' \
+      ':link name:_dladm_links'
+  ;;
+
+  (create-secobj)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[creation should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-c --class)'{-c,--class}'[class]:class:(wep wpa)' \
+      '(-f --file)'{-f,--file}'[file containing object value]:file:_path_files' \
+      ':object name:'
+  ;;
+
+  (delete-secobj)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[deletion should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      ':object name:_sequence _dladm_secobjs'
+  ;;
+
+  (show-secobj)
+    _arguments -A "-*" \
+      '(-P --persistent)'{-P,--persistent}'[display persistent object information]' \
+      '(-p --parseable)'{-p,--parseable}'[parseable output]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $secobj_properties' \
+      ':object name:_sequence _dladm_secobjs'
+  ;;
+
+  (create-vnic)
+    # TODO: MAC address completion could be richer
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[creation should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-l --link)'{-l,--link}'[component link]:link:_dladm_links' \
+      '(-m --mac-address)'{-m,--mac-address}'[MAC address]:address:(factory random auto vrrp)' \
+      '-v[VLAN ID]:id:' \
+      '(-p --prop)'{-p,--prop}'[property values]:value:_values -s , "property" ${(M)linkprops_general\:#*\:*}' \
+      ':VNIC name:'
+  ;;
+
+  (delete-vnic)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[deletion should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      ':VNIC name:_dladm_vnics'
+  ;;
+
+  (modify-vnic)
+    # TODO: MAC address completion could be richer
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[creation should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-l --link)'{-l,--link}'[component link]:link:_dladm_links' \
+      '(-m --mac-address)'{-m,--mac-address}'[MAC address]:address:(factory random auto vrrp)' \
+      '-v[VLAN ID]:id:' \
+      '(-p --prop)'{-p,--prop}'[property values]:value:_values -s , "property" ${(M)linkprops_general\:#*\:*}' \
+      - set1 \
+      '-L[source link]:link:_dladm_links' \
+      - set2 \
+      ':VNIC name:'
+  ;;
+
+  (show-vnic)
+    _arguments -A "-*" \
+      '(-P --persistent)'{-P,--persistent}'[display persistent object information]' \
+      '(-p --parseable)'{-p,--parseable}'[parseable output]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $vnic_properties' \
+      '(-l --link)'{-l,--link}'[limit to VNICs on link]:link:_dladm_links' \
+      '(-s --statistics)'{-s,--statistics}'[display VNIC statistics]' \
+      '(-i --interval)'{-i,--interval}'[specify an interval]:interval:' \
+      '-Z[display ZONE column in output]' \
+      '-v[display all VLAN information]' \
+      '-z[zone]:zone:_sequence _zones' \
+      ':VNIC name:_dladm_vnics'
+  ;;
+
+  (create-etherstub)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[creation should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      ':etherstub name:'
+  ;;
+
+  (delete-etherstub)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[deletion should be temporary]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      ':etherstub name:_dladm_etherstubs'
+  ;;
+
+  (show-etherstub)
+    _arguments -A "-*" \
+      '-Z[display ZONE column in output]' \
+      '-z[zone]:zone:_sequence _zones' \
+      ':etherstub name:_dladm_etherstubs'
+  ;;
+
+  (show-usage)
+    _arguments -A "-*" \
+      '(-f --file)'{-f,--file}'[read records from file]:file:_path_files' \
+      '(-F --format)'{-F,--format}'[plotfile format]:plotfile format:(gnuplot)' \
+      '(-p --plot)'{-p,--plot}'[write plot to file]:' \
+      '(-e --start)'{-e,--start}'[start time]:date/time (MM/DD/YYYY,hh\:mm\:ss)' \
+      '(-s --stop)'{-s,--stop}'[stop time]:date/time (MM/DD/YYYY,hh\:mm\:ss)' \
+      ':link name:_dladm_links'
+  ;;
+
+  (create-bridge)
+    _arguments -A "-*" \
+      '(-P --protect)'{-P,--protect}'[specify a protection method]:protection method:(stp trill)' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-p --priority)'{-p,--priority}'[specify the bridge priority]:value' \
+      '(-m --max-age)'{-m,--max-age}'[specify the max age for config info]:value' \
+      '(-h --hello-time)'{-h,--hello-time}'[specify the hello time]:value' \
+      '(-d --forward-delay)'{-d,--forward-delay}'[specify the forward delay]:value' \
+      '(-f --force-protocol)'{-f,--force-protocol}'[specify forced maximum supported protocol]:value' \
+      '*'{-l,--link}'[specify link to add]:link:_dladm_links' \
+      ':bridge name:'
+  ;;
+
+
+  (modify-bridge)
+    _arguments -A "-*" \
+      '(-P --protect)'{-P,--protect}'[specify a protection method]:protection method:(stp trill)' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-p --priority)'{-p,--priority}'[specify the bridge priority]:value' \
+      '(-m --max-age)'{-m,--max-age}'[specify the max age for config info]:value' \
+      '(-h --hello-time)'{-h,--hello-time}'[specify the hello time]:value' \
+      '(-d --forward-delay)'{-d,--forward-delay}'[specify the forward delay]:value' \
+      '(-f --force-protocol)'{-f,--force-protocol}'[specify forced maximum supported protocol]:value' \
+      ':bridge name:_dladm_bridges'
+  ;;
+
+
+  (delete-bridge)
+    _arguments -A "-*" \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      ':bridge name:_dladm_bridges'
+  ;;
+
+
+  (add-bridge|remove-bridge)
+    _arguments -A "-*" \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '*'{-l,--link}'[specify link to add]:link:_dladm_links' \
+      ':bridge name:_dladm_bridges'
+  ;;
+
+
+  (show-bridge)
+    # XXX $bridge_stats_properties get added into -o completions for set1
+    # XXX $bridge_link_stats_properties get added into -o completions for set3
+    _arguments -A "-*" \
+      '(-p --parseable)'{-p,--parseable}'[parseable output]' \
+      - set1 \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $bridge_properties' \
+      - set2 \
+      '(-s --statistics)'{-s,--statistics}'[display statistics]' \
+      '(-i --interval)'{-i,--interval}'[specify an interval]:seconds' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $bridge_stats_properties' \
+      - set3 \
+      '(-l --link)'{-l,--link}'[display link status or statistics]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $bridge_link_properties' \
+      - set4 \
+      '(-s --statistics)'{-s,--statistics}'[display statistics]' \
+      '(-l --link)'{-l,--link}'[display link status or statistics]' \
+      '(-i --interval)'{-i,--interval}'[specify an interval]:seconds' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $bridge_link_stats_properties' \
+      - set5 \
+      '(-f --forwarding)'{-f,--forwarding}'[display forwarding entries]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $bridge_fwd_properties' \
+      - set6 \
+      '(-t --trill)'{-t,--trill}'[display TRILL nickname entries]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $bridge_trill_properties' \
+      ':bridge name:_dladm_bridges'
+    ;
+
+
+  (create-iptun)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[temporary tunnel]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-T --type)'{-T,--type}'[tunnel type]:tunnel type:(ipv4 ipv6 6to4)' \
+      '(-a --address)'{-a,--address}'[endpoint addresses]:address/host:_values -s , "address/host" $tunnel_values' \
+      ':tunnel name'
+  ;;
+
+  (modify-iptun)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[temporary modification]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-a --address)'{-a,--address}'[endpoint addresses]:address/host:_values -s , "address/host" $tunnel_values' \
+      ':tunnel name:_dladm_iptuns'
+  ;;
+
+  (delete-iptun)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[temporary deletion]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      ':tunnel name:_dladm_iptuns'
+  ;;
+
+  (show-iptun)
+    _arguments -A "-*" \
+      '(-P --persistent)'{-P,--persistent}'[display persistent tunnel configuration]' \
+      '(-p --parseable)'{-p,--parseable}'[parseable output]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $iptun_properties' \
+      '-Z[display ZONE column in output]' \
+      '-z[zone]:zone:_sequence _zones' \
+      ':tunnel name:_dladm_iptuns'
+  ;;
+
+  (create-part)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[temporary partition]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      '(-f --force)'{-f,--force}'[force partition creation]' \
+      '(-l --link)'{-l,--link}'[IP-over-IB physical link name]:IB link:_dladm_iblinks' \
+      '(-p --prop)'{-p,--prop}'[set link properties]:link property:_values -s , "property" ${(M)linkprops_nonvlanvnic\:#*\:*} ${(M)linkprops_general\:#*\:*}' \
+      '(-P --pkey)'{-P,--pkey}'[set partition key]:hex number' \
+      ':partition link name:'
+  ;;
+
+  (delete-part)
+    _arguments -A "-*" \
+      '(-t --temporary)'{-t,--temporary}'[temporary deletion]' \
+      '(-R --root)'{-R,--root}'[root directory]:directory:_path_files -/' \
+      ':link name:_dladm_parts'
+  ;;
+
+  (show-part)
+    _arguments -A "-*" \
+      '(-P --persistent)'{-P,--persistent}'[display persistent partition configuration]' \
+      '(-p --parseable)'{-p,--parseable}'[parseable output]' \
+      '(-l --link)'{-l,--link}'[information for this link]:link name:_dladm_iblinks' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $part_properties' \
+      ':partition link name:_dladm_parts'
+  ;;
+
+  (show-ib)
+    _arguments -A "-*" \
+      '(-P --persistent)'{-P,--persistent}'[display persistent partition configuration]' \
+      '(-p --parseable)'{-p,--parseable}'[parseable output]' \
+      '(-o --output)'{-o,--output}'[properties to display]:property:_values -s , "property" $ib_properties' \
+      ':IB link name:_dladm_iblinks'
+  ;;
+
+  ((create|delete)-cap)
+    _arguments -A "-*" \
+      '(-R --root)'{-R,--root}'[specify root directory]:directory:_directories' \
+      '(-t --temporary)'{-t,--temporary}'[capture  datalink is temporary\: until next reboot]' \
+      ':cap link'
+  ;;
+
+  (show-cap)
+    _arguments -A "-*" \
+      '(-P --persistent)'{-P,--persistent}'[show persistent datalink configuration]' \
+      '(-p --parseable)'{-p,--parseable}'[output using a stable machine-parseable format]' \
+      '(-o --output)'{-o,--output}'[specify output fields]: _values -s , "field" LINK ZONE TYPE MTU' \
+      ':cap link'
+  ;;
+
+  (*)
+    _default
+  ;;
+  esac
 }
 
 _dladm "$@"
diff --git a/Completion/Solaris/Command/_dumpadm b/Completion/Solaris/Command/_dumpadm
index 02c63f6a7..44d681ae0 100644
--- a/Completion/Solaris/Command/_dumpadm
+++ b/Completion/Solaris/Command/_dumpadm
@@ -1,23 +1,22 @@
 #compdef dumpadm
 
-_dumpadm() {
-	local -a content
+local -a content
 
-	content=(
-		"kernel"\:"Kernel memory pages only"
-		"all"\:"All memory pages"
-		"curproc"\:"Kernel memory pages plus curproc pages"
-	)
-
-	_arguments -s \
-		'-n[dont run savecore on reboot]' \
-		'-u[update dump configuration from dumpadm.conf]' \
-		'-y[run savecore on reboot]' \
-		'-c[set dump content]:dump content:(($content))' \
-		'-d[set dump device]:block devices:_files -g "*(-%b)"' \
-		'-m[set minfree size ]:' \
-		'-s[set the savecore directory]:directory:_files -/' \
-		'-r[alternate root directory]:directory:_files -/'
-}
-
-_dumpadm "$@"
+content=(
+  'kernel:kernel memory pages only'
+  'all:all memory pages'
+  'curproc:kernel memory pages plus curproc pages'
+  'allproc:kernel memory pages and all process pages'
+)
+_arguments -s \
+  '-e[print estimate of disk space required to store compressed crash dump]' \
+  "-n[don't run savecore on reboot]" \
+  '-p[produce machine parseable output]' \
+  '-u[update dump configuration from dumpadm.conf]' \
+  '-y[run savecore on reboot]' \
+  '-c[set dump content]:dump content:(($content))' \
+  '-d[set dump device]: : _alternative "tokens\:token\:(swap none)" "files\:block device\:_files -g \*\(-%b\)"' \
+  '-m[set minfree size]:size' \
+  '-s[set the savecore directory]:directory:_files -/' \
+  '-r[alternate root directory]:directory:_files -/' \
+  '-z[enable saving core files in a compressed format]:compression:(on off)'
diff --git a/Completion/Solaris/Command/_flowadm b/Completion/Solaris/Command/_flowadm
index 53a9f2210..e4d682ffa 100644
--- a/Completion/Solaris/Command/_flowadm
+++ b/Completion/Solaris/Command/_flowadm
@@ -1,41 +1,18 @@
 #compdef flowadm
-# Synced with the S11U1 build 19 man page
-
-_flowadm() {
 
 local -a subcmds tr props
 local expl
 
 _flowadm_flow(){
-	compadd "$@" - $(flowadm show-flow -p -o flow)
-}
-
-_flowadm_flow_int(){
-	compadd "$@" - $(flowadm show-flow -p -o flow)
-	compadd "$@" - $(dladm show-phys -p -o device)
+  compadd "$@" - $(_call_program flows flowadm show-flow -p -o flow)
 }
 
 subcmds=(
-	"help"
-	"show-flow"
-	"add-flow"
-	"remove-flow"
-	"set-flowprop"
-	"reset-flowprop"
-	"show-flowprop"
-)
-	
-tr=(
-	"tcp"
-	"udp"
-	"sctp"
-	"icmp"
-	"icmpv6"
-)
-
-props=(
-	"maxbw"
+  help show-flow add-flow match-flow remove-flow set-flowprop
+  reset-flowprop show-flowprop
 )
+tr=( tcp udp sctp icmp icmpv6 )
+props=( maxbw )
 
 if [[ $service == "flowadm" ]]; then
 	_arguments -C -A "-*" \
@@ -46,7 +23,7 @@ if [[ $service == "flowadm" ]]; then
 		return
 	fi
 	service="$words[1]"
-	curcontext="${curcontext%:*}=$service:"
+	curcontext="${curcontext%:*}-$service:"
 fi
 case $service in
 
@@ -65,6 +42,16 @@ case $service in
 		':flow:_flowadm_flow' \
 	;;
 
+	(match-flow)
+	_arguments -A "-*" \
+		'-o[specify field to display]:field:(flow link ipaddr proto port dsfield)' \
+		'-p[parsable output]' \
+		'-P[persistent flow property information]' \
+		'-a[specify attribute]:attribute:(local_ip= remote_ip= transport=$tr local_port= dsfield=)' \
+		'(:)-l[display information for link]:link or flow:_net_interfaces' \
+		'(-l):flow:_flowadm_flow' \
+	;;
+
 	("add-flow")
 	_arguments -A "-*" \
 		'-t[temporary changes - do not persist across reboots]' \
@@ -110,6 +97,3 @@ case $service in
 	;;
 
 esac
-}
-
-_flowadm "$@"
diff --git a/Completion/Solaris/Command/_fmadm b/Completion/Solaris/Command/_fmadm
index 56dac4f96..54e22bde4 100644
--- a/Completion/Solaris/Command/_fmadm
+++ b/Completion/Solaris/Command/_fmadm
@@ -1,8 +1,7 @@
 #compdef fmadm
-# Synced with the Nevada build 168 man page
 
 _fm_modules() {
-	compadd "$@" - $(fmadm config 2> /dev/null | awk 'NR == 1 {continue} {print $1}')
+  compadd "$@" - ${${(f)"$(_call_program modules fmadm config)"}[2,-1]// */}
 }
 
 _fm_faulted_fmris() {
@@ -14,7 +13,7 @@ _fm_faulted_uuids() {
 }
 
 _fm_faulted_labels() {
-	local q='"'
+	local line q='"'
 	compadd "$@" - $(fmadm faulty -f 2> /dev/null | while read line; do
 		if [[ $line == "----"* ]]; then
 			read line
@@ -25,87 +24,102 @@ _fm_faulted_labels() {
 	done)
 }
 
-_fmadm() {
-	local context state line expl
-	local -A opt_args
-	local -a subcmds
-
-	# TODO: lookup-alias and remove-alias need completion based on the
-	# output of list-alias, but I have no examples of that output.
-
-	subcmds=(
-		"acquit" "config" "faulty" "flush" "load" "unload"
-		"repaired" "replaced" "reset" "rotate"
-		"add-alias" "remove-alias" "lookup-alias" "list-alias" "sync-alias"
-	)
-
-	if [[ $service == "fmadm" ]]; then
-		_arguments -C -A "-*" \
-			'-q[Quite mode]' \
-			'*::command:->subcmd' && return 0
-
-		if (( CURRENT == 1 )); then
-			_wanted commands expl "fmadm subcommand" compadd -a subcmds
-			return
-		fi
-		service="$words[1]"
-		curcontext="${curcontext%:*}=$service:"
-	fi
-
-	case $service in
-	("acquit")
-		_alternative \
-			"fmadm-acquit-label:label:_fm_faulted_labels" \
-			"fmadm-acquit-uuid:uuid:_fm_faulted_uuids" \
-			"fmadm-acquit-fmri:fmri:_fm_faulted_fmris"
-		;;
-
-	("config")
-		;;
-
-	("faulty")
-		_arguments -A "-*" \
-			'-a[Display all faults]' \
-			'-f[Display faulty FRUs]' \
-			'-g[Group faults]' \
-			'-i[Display persistent cache IDs]' \
-			'-n[Limit output to n entries]:number:' \
-			'-p[Page output]' \
-			'-r[Display resources]' \
-			'-s[Display one-line summaries]' \
-			'-u[Only display fault with given uuid]:uuid:_fm_faulted_uuids' \
-			'-v[Display full output]'
-		;;
-
-	("flush")
-		_fm_faulted_fmris
-		;;
-
-	("load")
-		_path_files -g "/*"
-		;;
-
-	("unload")
-		_fm_modules
-		;;
-
-	("repaired"|"replaced")
-		_alternative \
-			"fmadm-acquit-label:label:_fm_faulted_labels" \
-			"fmadm-acquit-fmri:fmri:_fm_faulted_fmris"
-		;;
-
-	("reset")
-		_arguments -A "-*" \
-			'-s[Reset named SERD]:serd:' \
-			':module:_fm_modules'
-		;;
-
-	("rotate")
-		_values "logfile" "errlog" "fltlog" "infolog" "infolog_hival"
-		;;
-
-	esac
-}
-
-_fmadm "$@"
+local curcontext="$curcontext" state line expl ret=1
+local -A opt_args
+local -a subcmds args
+
+# TODO: lookup-alias and remove-alias need completion based on the
+# output of list-alias, but I have no examples of that output.
+
+subcmds=(
+  faulty list acquit replaced repaired list-alert clear
+  list-defect list-fault
+  add-alias remove-alias lookup-alias list-alias sync-alias
+  config load unload reset rotate flush
+)
+
+_arguments -C -A "-*" \
+  '-q[quiet mode]' \
+  '1:fmadm subcommand:compadd -M "r:|-=* r:|=*" -a subcmds' \
+  '*::command:->subcmd' && ret=0
+
+[[ -z $state ]] && return ret
+service="$words[1]"
+curcontext="${curcontext%:*}-$service:"
+
+case $service in
+  (faulty|list|list-alert|list-defect|list-fault)
+    args+=(
+      '-f[display faulty FRUs]'
+      '-i[display persistent cache IDs]'
+      '-p[page output]'
+      '-r[display resources]'
+      '-s[display one-line summaries]'
+      '-u[only display fault with given uuid]:uuid:_fm_faulted_uuids'
+      '-v[display full output]'
+    )
+  ;|
+
+  (list|faulty)
+    _arguments -A "-*" $args \
+      '-a[display all faults]' \
+      '-g[group faults]' \
+      '-n[limit output to specified number of entries]:number'
+  ;;
+
+  (list-alert|list-defect|list-fault)
+    _arguments -A "-*" $args \
+      '-a[display all resources]'
+  ;;
+
+  (acquit)
+    _alternative \
+      'fmadm-acquit-label:label:_fm_faulted_labels' \
+      'fmadm-acquit-uuid:uuid:_fm_faulted_uuids' \
+      'fmadm-acquit-fmri:fmri:_fm_faulted_fmris' && ret=0
+  ;;
+
+  (repaired|replaced)
+    _alternative \
+      'fmadm-acquit-label:label:_fm_faulted_labels' \
+      'fmadm-acquit-fmri:fmri:_fm_faulted_fmris' && ret=0
+  ;;
+
+  (add-alias)
+    _arguments :chassis :alias-id :comment && ret=0
+  ;;
+
+  (remove-alias|lookup-alias)
+    _message -e aliases 'chassis or alias-id'
+  ;;
+
+  (load)
+    _directories && ret=0
+  ;;
+
+  (unload)
+    _fm_modules && ret=0
+  ;;
+
+  (reset)
+    _arguments -A "-*" \
+      '-s[reset named SERD]:serd' \
+      ':module:_fm_modules' && ret=0
+  ;;
+
+  (rotate)
+    _values 'logfile' errlog fltlog infolog infolog_hival && ret=0
+  ;;
+
+  (flush)
+    _fm_faulted_fmris && ret=0
+  ;;
+
+  (list-alias|config|sync-aliases) _message 'no more arguments' ;;
+
+  (*) # fallback to defaults for any new or unhandled subcommand
+    _default && ret=0
+  ;;
+esac
+
+return ret
diff --git a/Completion/Solaris/Command/_pfexec b/Completion/Solaris/Command/_pfexec
index 227336223..2519c3cdc 100644
--- a/Completion/Solaris/Command/_pfexec
+++ b/Completion/Solaris/Command/_pfexec
@@ -22,10 +22,11 @@ _privset() {
 }
 
 _pfexec() {
+	local -a _comp_priv_prefix
  	_arguments \
 		'-P[privileges to acquire]:privspec:_privset' \
  		'(-):command name: _command_names -e' \
- 		'*::arguments: _normal'
+		'*::arguments:{ _comp_priv_prefix=( pfexec ${(kv)opt_args[-P]} ) ; _normal }'
 }
 
 _pfexec "$@"
diff --git a/Completion/Solaris/Command/_svcadm b/Completion/Solaris/Command/_svcadm
index 347e25e2a..c9826f2eb 100644
--- a/Completion/Solaris/Command/_svcadm
+++ b/Completion/Solaris/Command/_svcadm
@@ -1,7 +1,7 @@
 #compdef svcadm
 
 _svcadm() {
-	local context state line subcmds
+	local curcontext="$curcontext" state line expl subcmds
 	typeset -A opt_args
 
 	subcmds=( enable disable restart refresh mark delegate clear milestone )
diff --git a/Completion/Solaris/Command/_svccfg b/Completion/Solaris/Command/_svccfg
index 08c5e4bcd..d31682e77 100644
--- a/Completion/Solaris/Command/_svccfg
+++ b/Completion/Solaris/Command/_svccfg
@@ -14,7 +14,7 @@ _svccfg_properties() {
 	# Get all the property names for the FMRI
 	props=( ${${${(f)"$(svccfg -s $fmri describe)"}:# *}%% *} )
 
-	_multi_parts "$expl[@]" - / props
+	_multi_parts "$@" - / props
 }
 
 _svccfg() {
diff --git a/Completion/Solaris/Command/_svcs b/Completion/Solaris/Command/_svcs
index 7c148fad4..c1f01ad17 100644
--- a/Completion/Solaris/Command/_svcs
+++ b/Completion/Solaris/Command/_svcs
@@ -1,32 +1,28 @@
 #compdef svcs
 
-_svcs() {
-	local -a cols
+local -a cols
 
-	cols=(
-		ctid\:"Contract ID" desc\:"Description" fmri\:"FMRI"
-		inst\:"Instance name" nsta\:"Next state (abbr)" nstate\:"Next state"
-		scope\:"Scope name" svc\:"Service name" sta\:"State (abbr)"
-		state\:"State" stime\:"Start time"
-	)
+cols=(
+  ctid\:"contract id" desc\:"description" fmri\:"fmri"
+  inst\:"instance name" nsta\:"next state (abbr)" nstate\:"next state"
+  scope\:"scope name" svc\:"service name" sta\:"state (abbr)"
+  state\:"state" stime\:"start time" astate\:"name for current auxiliary state"
+  nrun\:"time of next scheduled run for periodic services"
+  lrun\:"time of the last run for periodic services"
+)
 
-	_arguments -s \
-		'(-l -x -d -D)-a[list all instances]' \
-		'(-l -x -D -a -R)-d[list dependencies]' \
-		'(-l -x -d -a -R)-D[list dependents]' \
-		'(-l -x)-H[suppress header line]' \
-		'(-x -d -D -a -R -s)-l[print detailed status about services and instances]' \
-		'(-l -x)-o[display specific columns]:column:_values -s , "column" ${^cols/\:/[}\]' \
-		'(-l -x)-p[list processes]' \
-		'(-l -x -d -D)-R[list services with the given restarter]:instance FMRI:_svcs_fmri -i' \
-		'(-l -x)-s[sort by a column]:column:(($cols))' \
-		'(-l -x)-S[reverse sort by a column]:column:(($cols))' \
-		'-v[verbose columns]' \
-		'(-l)-x[display explanation for service states]' \
-		'(-l -x)-\?[print help]' \
-		'*:FMRI or pattern:_svcs_fmri -i'
-}
-
-_svcs "$@"
-
-# vi:tw=0
+_arguments -s \
+  '(-l -x -d -D)-a[list all instances]' \
+  '(-l -x -D -a -R)-d[list dependencies]' \
+  '(-l -x -d -a -R)-D[list dependents]' \
+  '(-l -x)-H[suppress header line]' \
+  '(-x -d -D -a -R -s)-l[print detailed status about services and instances]' \
+  '(-l -x)-o[display specific columns]:column:_values -s , "column" ${^cols/\:/[}\]' \
+  '(-l -x)-p[list processes]' \
+  '(-l -x -d -D)-R[list services with the given restarter]:instance FMRI:_svcs_fmri -i' \
+  '(-l -x)-s[sort by a column]:column:(($cols))' \
+  '(-l -x)-S[reverse sort by a column]:column:(($cols))' \
+  '-v[verbose columns]' \
+  '(-l)-x[display explanation for service states]' \
+  '(-l -x)-\?[print help]' \
+  '*:FMRI or pattern:_svcs_fmri -i'
diff --git a/Completion/Solaris/Command/_zlogin b/Completion/Solaris/Command/_zlogin
index 04018eb87..74cbafe82 100644
--- a/Completion/Solaris/Command/_zlogin
+++ b/Completion/Solaris/Command/_zlogin
@@ -2,6 +2,7 @@
 # Synced with the Nevada build 162 man page
 
 _zlogin() {
+        local -a _comp_priv_prefix
 	_arguments -s \
 		'-E[Disable escape character]' \
 		'-e[Specify escape character]:character:' \
diff --git a/Completion/Solaris/Type/_svcs_fmri b/Completion/Solaris/Type/_svcs_fmri
index 80d3516c0..ffade6985 100644
--- a/Completion/Solaris/Type/_svcs_fmri
+++ b/Completion/Solaris/Type/_svcs_fmri
@@ -2,7 +2,7 @@
 
 _svcs_fmri() {
 	local type="$argv[$#]"
-	local fmri_abbrevs m i
+	local fmri_abbrevs m i expl
 	typeset -a -g _smf_fmris
 
 	local update_policy
diff --git a/Completion/Unix/Command/_adb b/Completion/Unix/Command/_adb
index 879e26045..6b56d1748 100644
--- a/Completion/Unix/Command/_adb
+++ b/Completion/Unix/Command/_adb
@@ -217,7 +217,7 @@ _adb_pm_list () {
     (permissions)
       _arguments -s '-g[organize by group]' \
       		    '-f[print all information]' \
-      		    '-d[only list dangerous pemissions]' \
+		    '-d[only list dangerous permissions]' \
       		    '-u[only list user visible permissions]' \
       		    '-s[short summary]' \
       		    ':'
@@ -282,7 +282,7 @@ _adb_activity_manager_handler () {
       _arguments -s '-r[print raw results]' \
       		    '-e[set argument NAME to VALUE]:<NAME> <VALUE>:' \
       		    '-p[write profiling data to FILE]:<FILE>:' \
-      		    '-w[wait for instrumenation to finish]' \
+		    '-w[wait for instrumentation to finish]' \
       		    ':'
       ;;
     (profile)
@@ -307,7 +307,7 @@ _adb_package_manager_handler () {
       (( $+functions[_adb_installed_packages] )) && _adb_installed_packages
       ;;
     (setInstallLocation)
-      _wanted set_installlcation expl 'install location' compadd -d "(0:auto 1:internal 2:external)" 0 1 2
+      _wanted install-locations expl 'install location' compadd -d "(0:auto 1:internal 2:external)" 0 1 2
       ;;
     (getInstallLocation)
       ;;
diff --git a/Completion/Unix/Command/_ant b/Completion/Unix/Command/_ant
index ee9f7d910..080ce6857 100644
--- a/Completion/Unix/Command/_ant
+++ b/Completion/Unix/Command/_ant
@@ -41,6 +41,7 @@ _arguments -C \
   '(- *)-version[display version information]' \
   '(- *)-diagnostics[print information helpful to diagnosis or report problems]' \
   '(-q -quiet)'{-q,-quiet}'[be extra quiet]' \
+  '(-s -silent)'{-s,-silent}'[print nothing but task outputs and build failures]' \
   '(-v -verbose)'{-v,-verbose}'[be extra verbose]' \
   '(-d -debug)'{-d,-debug}'[print debugging information]' \
   '(-e -emacs)'{-e,-emacs}'[produce logging information without adornments]' \
@@ -50,7 +51,7 @@ _arguments -C \
   '*-listener[add an instance of specified class as a project listener]:class:->class' \
   '-noinput[do not allow interactive input]' \
   '(-f -file -buildfile -s -find)'{-f,-file,-buildfile}'[use specified build file]:build file:_files -g "*.xml(-.)"' \
-  '*-D[specify property with value to use]:property:->property' \
+  '*-D+[specify property with value to use]:property:->property' \
   '(-k -keep-going)'{-keep-going,-k}'[execute all targets that do not depend on failed target(s)]' \
   '-propertyfile[load all properties from specified file with -D properties taking precedence]:property file:_files -g "*.properties(-.)"' \
   '-inputhandler[specify class which will handle input requests]:class:->class' \
@@ -82,7 +83,7 @@ case $state in
       "classpath:$state:_path_files -r': ' -/" && ret=0
   ;;
   property)
-    if compset -P '*='; then
+    if compset -P 1 '*='; then
       _default && ret=0
     else
       _message -e properties 'property name'
diff --git a/Completion/Unix/Command/_arping b/Completion/Unix/Command/_arping
index 5edda2123..bd9bc8ccc 100644
--- a/Completion/Unix/Command/_arping
+++ b/Completion/Unix/Command/_arping
@@ -1,6 +1,6 @@
 #compdef arping
 
-if _pick_variant iputils=iputils unix -V; then
+if _pick_variant iputils=iputils thabets -V; then
 
 _arguments -s \
   '-f[quit on first reply]' \
@@ -18,25 +18,35 @@ _arguments -s \
 
 else
 
-_arguments -s \
-  '(-)-h[display help information]' \
+_arguments -s -S  -A "-*" \
+  '(-)'{-h,--help}'[display help information]' \
   '-q[only display error messages]' \
   '-a[audible ping]' \
   '*-v[verbose output]' \
   '-r[raw output: only the MAC/IP address displayed]' \
   '-R[like -r but shows "the other one"]' \
   '-d[find duplicate replies]' \
+  '-D[display answers as exclamation marks and missing packets as dots]' \
+  '-e[like -a but beep when there is no reply]' \
+  "-F[don't try to be smart about the interface name]" \
+  '-m+[specify type of timestamp for incoming packets]:timestamp type' \
   '(:)-B[255.255.255.255]' \
   '-0[source 0.0.0.0]' \
   '-b[source 255.255.255.255]' \
-  '-S[set source IP]:source IP address:_hosts' \
-  '-T[target IP]:target IP address:_hosts' \
+  '-S+[set source IP]:source IP address:_hosts' \
+  '-T+[target IP]:target IP address:_hosts' \
   '-p[turn on promiscuous mode]' \
   '-s[set source MAC address]:source MAC address' \
   '-t[set target MAC address]:target MAC address' \
   '-c[send this many requests]:count' \
-  '-I[interface]:interface:_net_interfaces' \
+  '-C[only wait for specified replies]:reply count' \
+  '-i+[interface]:interface:_net_interfaces' \
   '-A[only count addresses matching requested address]' \
+  '-P[send ARP replies instead of requests]' \
+  '-u[show index=received/sent instead of just index=received when pinging MACs]' \
+  '-U[send unsolicited ARP]' \
+  '-w+[specify time to wait between pings (ms)]:time (ms)' \
+  '-W+[specify time to wait between pings (seconds)]:time (seconds)' \
   '(-B):address:_hosts'
  
 fi
diff --git a/Completion/Unix/Command/_attr b/Completion/Unix/Command/_attr
index c7ca1b850..df1fb7da4 100644
--- a/Completion/Unix/Command/_attr
+++ b/Completion/Unix/Command/_attr
@@ -21,11 +21,11 @@ case $service in
       '(-d --dump -n --name)'{-n+,--name=}'[dump the value of the named extended attribute]' \
       '(-n --name -d --dump)'{-d,--dump}'[dump the values of all extended attributes]' \
       '(-e --encoding)'{-e+,--encoding=}'[encode values after retrieving them]:encoding:(text hex base64)' \
-      '(-h --no-derference)'{-h,--no-dereference}'[do not follow symbolic links]' \
+      '(-h --no-dereference)'{-h,--no-dereference}"[don't follow symbolic links]" \
       '(-m --match)'{-m+,--match=}'[only include attributes with names matching regex]:regular expression' \
-      '--absolute-names[do not string leasing slash characters]' \
+      "--absolute-names[don't strip leading slash characters]" \
       '--only-values[dump only attribute values]' \
-      '(-R --recursive)'{-R,--recursive}'[list attributes of all files and directories recurively]' \
+      '(-R --recursive)'{-R,--recursive}'[list attributes of all files and directories recursively]' \
       '(-P --physical -L --logical)'{-L,--logical}'[follow all symbolic links]' \
       '(-L --logical -P --physical)'{-P,--physical}'[skip all symbolic links]' \
       '(* -)--version[display version information]' \
@@ -39,7 +39,7 @@ case $service in
       '(-v --value)'{-v+,--value=}'[specify value for the attribute]' \
       '(-x --remove)'{-x+,--remove=}'[remove specified extended attribute]' \
       '(-n --name -v --value)--restore[restore extended attributes from dump file]:dump file:_files' \
-      '(-h --no-derference)'{-h,--no-dereference}'[do not follow symbolic links]' \
+      '(-h --no-dereference)'{-h,--no-dereference}"[don't follow symbolic links]" \
       '(* -)--version[display version information]' \
       '(* -)--help[display help information]' \
       '*:file:_files'
diff --git a/Completion/Unix/Command/_augeas b/Completion/Unix/Command/_augeas
index 622fa3467..f9c052122 100644
--- a/Completion/Unix/Command/_augeas
+++ b/Completion/Unix/Command/_augeas
@@ -9,6 +9,8 @@ _arguments -C -s \
   '(-n --new)'{-n,--new}'[leave files untouched but save changes with a .augnew extension]' \
   '(-r --root)'{-r,--root=}'[specify filesystem root]:root directory:_files -/' \
   \*{-I+,--include=}'[add directory containing lenses to search path]:directory:_files -/' \
+  \*{-t+,--transform=}'[add a file transform]:xfm' \
+  '(-l --load-file)'{-l+,--load-file=}'[load individual file in the tree]:file:_files' \
   '(-f --file *)'{-f+,--file=}'[read commands from specified file]:file:_files' \
   '(-i --interactive)'{-i,--interactive}'[read commands from the terminal]' \
   '(-e --echo)'{-e,--echo}'[echo commands read from a file or stdin]' \
@@ -17,6 +19,7 @@ _arguments -C -s \
   '(-L --noload)'{-L,--noload}'[do not load any files into the tree on startup]' \
   '(-A --noautoload)'{-A,--noautoload}'[do not autoload modules from the search path]' \
   '--span[load span positions for nodes related to a file]' \
+  '--timing[after executing each command, show how long it took]' \
   '(- *)--version[print version information]' \
   '(-)'{-h,--help}'[print help information]' \
   '*:: :->subcommands' && ret=0
@@ -25,7 +28,7 @@ _arguments -C -s \
 
 if [[ CURRENT -eq 1 || $words[1] == help ]]; then
   local -a subcmds
-  subcmds=( ${${${(f)"$(_call_program subcommands augtool help 2>/dev/null)"}[2,-2]## #}// #- /:} )
+  subcmds=( ${${${(M)${(f)"$(_call_program subcommands augtool help 2>/dev/null)"}:#  *}## #}// #- /:} )
   _describe -t subcommands "augtool command" subcmds && ret=0
   return ret
 fi
diff --git a/Completion/Unix/Command/_awk b/Completion/Unix/Command/_awk
index c493c3b04..9050c2f7d 100644
--- a/Completion/Unix/Command/_awk
+++ b/Completion/Unix/Command/_awk
@@ -1,21 +1,119 @@
-#compdef awk
-
-# completions for awk
-# This only aims to complete POSIX awk options, as described in
-# awk(P). Most awk implementations, such as gawk and mawk, will have
-# additional options that this does not complete. Also, currently
-# this completion does not allow everything that POSIX allows. For
-# example, awk(P) states that the user may specify assignments
-# without using the -v option; this does not support that.
+#compdef awk gawk nawk
+
+# For gawk ver.3 and 4, in addition to POSIX.
+#
+# gawk's options '-W ...' (such as '-W help') are not supported.
+# gawk3 has some synonyms for long options (e.g., --compat is a synonym
+# for --traditional). These are not supported either.
 #
-# In addition, the "program text" completion is not perfect. For
-# instance, type "awk -" and then hit tab. You will be presented
-# both with the dashed options and with the "program text" option.
-# Fixing this is beyond my current _arguments expertise--help
-# appreciated.
-
-_arguments -S -s '-F+[define input field separator to be an extended regular expression]:extended regular expression:' \
-    '*-v+[assign values to variables]:assignment:' \
-    '(1)-f+[program file]:program file:_files' \
-    '1:program text:' \
-    '*:input files:_files'
+# 'gawk -f<TAB>' will complete files in AWKPATH in addition to those in
+# the current directory. If this is annoying, you may try
+#   zstyle ':completion:*:*:gawk:option-f-1:*' tag-order program-files
+
+local variant curcontext="$curcontext" state state_descr line ret=1
+local -A opt_args
+local -a args
+
+_pick_variant -r variant gawk4='GNU Awk 4' gawk3='GNU Awk 3' posix --version
+
+args=(
+  {-F+,--field-separator}'[define input field separator by extended regex]:extended regular expression:'
+  '*'{-v+,--assign}'[assign values to variables]:assignment:'
+  '(1)*'{-f+,--file}'[read program file]:program file:->script'
+  '1: :_guard "^-*" "program text"'
+  '*:input files:_files'
+)
+
+case $variant in
+  (gawk*)
+    args+=(
+      {-c,--traditional}'[run in compatibility mode]'
+      '(- : *)'{-C,--copyright}'[print copyright info and exit]'
+      {-d-,--dump-variables=-}'[print a sorted list of global variables]::output file:_files'
+      {-e,--source}'[pass program text in arg]:program text:'
+      '(1)'{-E+,--exec}'[like -f, but safer for CGI]:program file:->script'
+      '(- : *)'{-h,--help}'[print usage message and exit]'
+      {-L-,--lint=-}'[warn about dubious or non-portable constructs]::flag:((fatal\:"treat warnings as fatal error" invalid\:"warn only about things that are actually invalid"))'
+      {-n,--non-decimal-data}'[auto-detect octal/hexadecimal values in input]'
+      {-N,--use-lc-numeric}"[force use of locale's decimal point character]"
+      {-O,--optimize}'[enable optimization]'
+      {-p-,--profile=-}'[output profiling data to file]::output file:_files'
+      {-P,--posix}'[run in strict POSIX mode]'
+      {-r,--re-interval}'[enable interval expressions in regex matching]'
+      {-t,--lint-old}'[warn about non-portable constructs]'
+      '(- : *)'{-V,--version}'[print version info and exit]'
+    )
+    ;|
+  (gawk4)
+    args+=(
+      {-b,--characters-as-bytes}'[treat all input data as single-byte characters]'
+      {-D-,--debug=-}'[enable debugging]::debugger command file:_files'
+      {-g,--gen-pot}'[scan awk program and generate .po file on stdout]'
+      '*'{-i+,--include}'[load source library]:library file:->script'
+      '*'{-l+,--load}'[load dynamic extension]:extension:->extension'
+      {-M,--bignum}'[select arbitrary-precision arithmetic on numbers]'
+      {-o-,--pretty-print=-}'[pretty-print awk program]::output file:_files'
+      {-S,--sandbox}'[disable system(), redirections and dynamic extensions]'
+    )
+    ;;
+  (gawk3)
+    # one letter options are new in gawk4
+    args=( ${args:#(|\*)(|\(*\))-[cCdEhLnNtOpPreV]*} )
+    args+=(
+      '--gen-po[scan awk program and generate .po file on stdout]'
+    )
+    ;;
+  (*)
+    # remove long options
+    args=( ${args:#*--*} )
+esac
+
+_arguments -S -s -C : $args && ret=0
+
+# Complete files in . (current directory) and AWKPATH/AWKLIBPATH.
+# Use different tag/description for files in . even if . is in AWKPATH.
+_files_in_curdir_or_path() {
+  local expl pat1 pat2
+  if [[ -n $6 ]]; then  # $6 = 'so', 'dll' or ''
+    pat1="-g *.$6"
+    pat2="-g *.$6"
+  fi
+  if [[ $words[CURRENT] == */* || $variant != gawk* || \
+	-n $opt_args[(I)(-c|--traditional|-P|--posix)] ]]; then
+    _wanted $2 expl $3 _files $pat1 && ret=0
+  else
+    local prog='BEGIN {print ENVIRON["'$1'"]}'
+    local -aU paths
+    # split AWKPATH into paths, and replace null element by '.'.
+    paths=( "${(@)${(@s.:.)$(_call_program get-awk-env \
+			    $words[1] ${(q)prog})}:/#%/.}" )
+    if (( $paths[(I).] )); then
+      # If '.' is in paths, remove it; we will handle it separately
+      paths=( ${(@)paths:#.} )
+    else
+      # If '.' is not in paths, we should not complete files in '.'
+      pat1='-g *(-/)'
+    fi
+    if (( $#paths )); then
+      _alternative "${2}:${3}:_files ${(b)pat1}" \
+		  "${4}:${5}:_files -W paths ${(b)pat2}" && ret=0
+    else
+      _wanted $2 expl $3 _files $pat1 && ret=0
+    fi
+  fi
+}
+
+case $state in
+  (script)
+    _files_in_curdir_or_path AWKPATH program-files 'program file' \
+			    library-files 'library in AWKPATH'
+    ;;
+  (extension)
+    local ext=so
+    [[ $OSTYPE == cygwin* ]] && ext=dll
+    _files_in_curdir_or_path AWKLIBPATH extensions 'extension' \
+			    library-files 'extension in AWKLIBPATH' $ext
+    ;;
+esac
+
+return ret
diff --git a/Completion/Unix/Command/_basename b/Completion/Unix/Command/_basename
new file mode 100644
index 000000000..a826b56b0
--- /dev/null
+++ b/Completion/Unix/Command/_basename
@@ -0,0 +1,27 @@
+#compdef basename gbasename
+
+local args variant
+_pick_variant -r variant gnu=GNU $OSTYPE --version
+
+case $variant in
+  gnu)
+    args=( -s -S -A "-*"
+      '(2 -a --multiple)'{-a,--multiple}'[support multiple arguments, handling each]'
+      '(2 -a --multiple -s --suffix)'{-s+,--suffix=}'[remove a trailing suffix]:suffix'
+      '(-z --zero)'{-z,--zero}'[separate output with NUL rather than newline]'
+      '(- *)--version[display version information]'
+      '(- *)--help[display help information]'
+    )
+  ;;
+  darwin*|dragonfly*|freebsd*)
+    args=( -s -S -A "-*"
+      '(2)-a[support multiple arguments, handling each]'
+      '(-a 2)-s+[remove a trailing suffix]:suffix'
+    )
+  ;;
+esac
+
+_arguments $args \
+  '1:file:_files' \
+  '(*)2:suffix' \
+  '*:file:_files'
diff --git a/Completion/Unix/Command/_bzr b/Completion/Unix/Command/_bzr
index 28ebd145c..1b755b4ec 100644
--- a/Completion/Unix/Command/_bzr
+++ b/Completion/Unix/Command/_bzr
@@ -375,7 +375,7 @@ case $cmd in
 (gannotate|gblame|gpraise)
     args+=(
 	'--all[show annotations on all lines]'
-	'--plain[do not hightlight annotation lines]'
+	"--plain[don't highlight annotation lines]"
 	'*:files:_bzr_versionedFiles'
 	)
     ;;
diff --git a/Completion/Unix/Command/_cat b/Completion/Unix/Command/_cat
index e223d90d9..46180f2c8 100644
--- a/Completion/Unix/Command/_cat
+++ b/Completion/Unix/Command/_cat
@@ -19,8 +19,9 @@ if _pick_variant gnu=GNU unix --version; then
     '*:files:_files'
   )
 
-elif [[ "$OSTYPE" == (freebsd|dragonfly|darwin)* ]]; then
+elif [[ "$OSTYPE" == (*bsd|dragonfly|darwin)* ]]; then
   args=(
+    -A "-*"
     '(-n)-b[number non-blank output lines]'
     '(-v)-e[display $ at the end of each line (implies -v)]'
     '-n[number all output lines]'
@@ -28,9 +29,27 @@ elif [[ "$OSTYPE" == (freebsd|dragonfly|darwin)* ]]; then
     '(-v)-t[display tab as ^I (implies -v)]'
     '-u[do not buffer output]'
     '-v[display non-printing chars as ^X or M-a]'
-    '(-)*:files:_files'
+    '*:files:_files'
+  )
+  [[ $OSTYPE = (free|net)bsd* ]] && args+=(
+    '-l[set a lock on the stdout file descriptor]'
+  )
+  [[ $OSTYPE = netbsd* ]] && args+=(
+    '-B+[read with buffer of specified size]:size (bytes)'
+    '-f[only attempt to display regular files]'
+  )
+elif [[ $OSTYPE = solaris* ]]; then
+  args=(
+    -A "-*"
+    '(-b)-n[number all output lines]'
+    '(-n)-b[number non-blank output lines]'
+    "-u[don't buffer output]"
+    '-s[be silent about non-existent files]'
+    '-v[display non-printing chars as ^X or M-a]'
+    '-e[display $ at the end of each line (requires -v)]'
+    '-t[display tab as ^I and formfeeds and ^L (requires -v)]'
+    '*:files:_files'
   )
-
 else
   # POSIX reqires '-u', and most OSes may support '-n'
   args=(
@@ -40,4 +59,4 @@ else
   )
 fi
 
-_arguments -s -S : $args
+_arguments -s -S $args
diff --git a/Completion/Unix/Command/_cdrdao b/Completion/Unix/Command/_cdrdao
index ad1bf4034..ceb86267c 100644
--- a/Completion/Unix/Command/_cdrdao
+++ b/Completion/Unix/Command/_cdrdao
@@ -211,7 +211,7 @@ __cdrdao-simulate-or-write () {
     $_cdrdao_common_toc_device_args \
     $_cdrdao_speed_arg \
     '--multi[do not close the session after successful write]' \
-    '--overburn[allow overburing of medium]' \
+    '--overburn[allow overburning of medium]' \
     '--full-burn[force burning to the outer disk edge]' \
     $_cdrdao_capacity_arg \
     $_cdrdao_eject_arg \
@@ -262,14 +262,14 @@ _cdrdao-copy () {
 __cdrdao-device () {
   # Use cdrdao scanbus and also check what OS we're running under and provide
   # additional stuff, like devices (/dev/sg0)
-  local -a devices
+  local -a expl devices
   devices=(${${(f)"$(_call_program devices cdrdao scanbus -v 0 2>&1)"}%% :*})
 
   _wanted devices expl 'device' compadd -a devices
 }
 
 __cdrdao-drivers () {
-  local suf
+  local expl suf
   local -Ua drivers
   drivers=(${(f)"$(_call_program drivers cut -d'\|' -f4 /usr/share/cdrdao/drivers -s)"})
   if compset -P \*:; then
diff --git a/Completion/Unix/Command/_chown b/Completion/Unix/Command/_chown
index 641b5a773..5750c65ab 100644
--- a/Completion/Unix/Command/_chown
+++ b/Completion/Unix/Command/_chown
@@ -45,8 +45,8 @@ _arguments -C -s "$args[@]" '*:files:->files' && ret=0
 case $state in
   owner)
     if [[ $service = chgrp ]] || compset -P '*[:.]'; then
-      if (( EGID && $+commands[groups] && ! $+funcstack[(r)_sudo] )); then  # except for root
-       _wanted groups expl 'group' compadd $(groups) && return 0
+      if (( EGID && $+commands[groups] && ! $+_comp_priv_prefix )); then  # except for sudo
+       _wanted groups expl 'group' compadd -- $(groups) && return 0
       fi
       _groups && ret=0
     else
diff --git a/Completion/Unix/Command/_cp b/Completion/Unix/Command/_cp
index 7087b4e6c..5c3f37983 100644
--- a/Completion/Unix/Command/_cp
+++ b/Completion/Unix/Command/_cp
@@ -57,7 +57,8 @@ else
     '(dragonfly|freebsd)*' '-l[link files instead of copying]' \
     '(darwin|dragonfly|freebsd|netbsd)*' '-v[show file names as they are copied]' \
     'darwin*' "-X[don't copy extended attributes or resource forks]" \
-    '(dragonfly|freebsd)*' "-x[don't traverse file systems]"
+    '(dragonfly|freebsd)*' "-x[don't traverse file systems]" \
+    'freebsd<10->.*' '-s[make symbolic links instead of copies of non-directories]'
   do
     [[ $OSTYPE = $~pattern ]] && args+=( $arg )
   done
diff --git a/Completion/Unix/Command/_cpio b/Completion/Unix/Command/_cpio
index 6b07a214a..699b1d8f2 100644
--- a/Completion/Unix/Command/_cpio
+++ b/Completion/Unix/Command/_cpio
@@ -1,6 +1,7 @@
 #compdef cpio
 
-local args ig curcontext="$curcontext" state line
+local -a args
+local ig curcontext="$curcontext" state line
 local expl ret=1
 local fmts='(bar bin odc newc crc tar ustar hpbin hpodc)'
 
@@ -106,10 +107,10 @@ fi
 _arguments -C -s "$args[@]" && ret=0
 
 if [[ $state = afile ]]; then
-  if compset -P '*:'; then
-    # TODO: doesn't need to be rsh.
-    _wanted files expl 'remote files' \
-       compadd $(rsh ${words[CURRENT]%:*} echo ${words[CURRENT]#*:}\*) && ret=0
+  if [[ $ig != gnu ]]; then
+    _files && ret=0
+  elif compset -P 1 '*:'; then
+    _remote_files -- ssh && ret=0
   elif compset -P '*@'; then
     _wanted hosts expl 'remote host name' _hosts && ret=0
   else
diff --git a/Completion/Unix/Command/_cscope b/Completion/Unix/Command/_cscope
new file mode 100644
index 000000000..8c3839d10
--- /dev/null
+++ b/Completion/Unix/Command/_cscope
@@ -0,0 +1,29 @@
+#compdef cscope
+
+_arguments -s -S \
+  '-b[build the cross-reference only]' \
+  '-C[ignore letter case when searching]' \
+  "-c[use only ASCII characters in the cross-ref file (don't compress)]" \
+  "-d[don't update the cross-reference]" \
+  '-e[suppress the <Ctrl>-e command prompt between files]' \
+  '-F+[read symbol reference lines from specified file]:symbol-reference file:_files' \
+  '-f+[specify cross-reference file]:cross-reference file [cscope.out]:_files' \
+  '(-)'{-h,--help}'[display help information]' \
+  '*-I+[specify directory to search for #include files]:include directory:_directories' \
+  '-i+[specify file containing a list if files to browse]:file [cscope.file]:_files' \
+  "-k[kernel mode - don't use /usr/include for #include files]" \
+  '-L[do a single search with line-oriented output]' \
+  '-l[line-oriented interface]' \
+  -{0,1,2,3,4,5,6,7,8,9}'+:pattern' \
+  '-P+[prepend path to relative file names in pre-built cross-ref file]:path:_directories' \
+  '-p+[specify number of path components to display]:path components [1]' \
+  '-q[build an inverted index for quick symbol searching]' \
+  '-R[recurse directories for files]' \
+  '-s+[specify directory to search for additional source files]:directory:_directories' \
+  '-T[use only the first eight characters to match against C symbols]' \
+  '-U[check file time stamps]' \
+  '-u[unconditionally build the cross-reference file]' \
+  '-v[be more verbose in line mode]' \
+  '-X[remove the cscope reference file and inverted indexes at end]' \
+  '(-)'{-V,--version}'[display version information]' \
+  '*:file:_files'
diff --git a/Completion/Unix/Command/_cvs b/Completion/Unix/Command/_cvs
index 31997ec09..0552d2175 100644
--- a/Completion/Unix/Command/_cvs
+++ b/Completion/Unix/Command/_cvs
@@ -3,10 +3,7 @@
 # redefine _cvs.
 
 _cvs() {
-  local extra
-
   # "+Qqrwtnlvb:T:e:d:Hfz:s:xa"
-
   _arguments -s \
     '-R[read only access]' \
     '-a[authenticate]' \
@@ -598,7 +595,7 @@ _cvs_tempdir() {
 
 (( $+functions[_cvs_user_variable] )) ||
 _cvs_user_variable() {
-  if compset -P '*='; then
+  if compset -P 1 '*='; then
     _default
   else
     _message -e variables "variable"
diff --git a/Completion/Unix/Command/_darcs b/Completion/Unix/Command/_darcs
index d40ecda28..74734711d 100644
--- a/Completion/Unix/Command/_darcs
+++ b/Completion/Unix/Command/_darcs
@@ -13,6 +13,8 @@
 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ## GNU General Public License for more details.
 
+local expl
+
 if (($CURRENT == 2)); then
   # We're completing the first word after "darcs" -- the command.
   _wanted command expl 'darcs command' \
diff --git a/Completion/Unix/Command/_date b/Completion/Unix/Command/_date
index ff3bac398..a3e933710 100644
--- a/Completion/Unix/Command/_date
+++ b/Completion/Unix/Command/_date
@@ -1,57 +1,99 @@
 #compdef date gdate
 
-local -a args alts
+local curcontext="$curcontext" state state_descr line ret=1
+local -A opt_args
+local -a opts args
+
+opts=( -s -w -C )
 
 if _pick_variant gnu="Free Software Foundation" unix --version; then
+  local d='(-d --date -f --file -r --reference -s --set)'
+  local f='(-I --iso-8601 -R --rfc-email --rfc-3339)'
   args=(
-    '-d[output specified date]:time string'
-    '-f[output dates specified in file]:file:_files'
-    '-I-[iso-8601]:precision:(date hours minutes seconds)'
-    '-r[reference]:file:_files'
-    '-R[RFC2822 format]'
-    '-s[set]:time string'
-    '--rfc-3339=-[output date and time in RFC 3339 format]:output type:(date seconds ns)'
-    --
-    '*=FILE*:file:_files'
-    '*=DATEFILE*:date file:_files'
+    $d{-d+,--date=}'[output date specified by string]:time string'
+    '--debug[annotate parsed date and warn about questionable usage]'
+    $d{-f+,--file=}'[output dates specified in file]:file:_files'
+    $d{-r+,--reference=}'[output last modification time of specified file]:file:_files'
+    $d{-s+,--set=}'[set time]:time string'
+    $f{-I-,--iso-8601=-}'[display in ISO 8601 format]::precision:(date hours minutes seconds ns)'
+    $f{-R,--rfc-email}'[display in RFC5322 format]'
+    $f'--rfc-3339=-[display in RFC 3339 format]:precision:(date seconds ns)'
+    '(-u --utc --universal)'{-u,--utc,--universal}'[display or set time in UTC]'
+    '(- :)--help[output help and exit]'
+    '(- :)--version[output version info and exit]'
   )
 else
+  args=( '-u[display or set time in UTC]' )
   case "$OSTYPE" in
     solaris*)
-      args=( '-a:adjustment' )
+      args+=( '-a:adjustment' )
     ;;
-    darwin*|dragonfly*|netbsd*|openbsd*)
-      args+=( '-r[specify reference time]:seconds since epoch' )
-    ;|
-    freebsd*|darwin*|dragonfly*|netbsd*|openbsd*)
-      args=(
-	'-n[only set time on current machine]'
-	'-d:daylight saving time value'
+    freebsd*|dragonfly*|darwin*|netbsd*|openbsd*)
+      opts+=( -A '-*' )
+      args+=(
 	'-j[do not try to set date]'
-	'-t:minutes west of GMT'
+	'2:format or date:->fmt_date'
+      )
+    ;|
+    freebsd*|dragonfly*|darwin*|netbsd*)
+      args+=( '-n[only set time on current machine]' )
+    ;|
+    freebsd*|dragonfly*|darwin*|openbsd*)
+      args+=(
+	'-d+:daylight saving time value'
+	'-t+:minutes west of GMT'
+      )
+    ;|
+    dragonfly*|darwin*|netbsd*|openbsd*)
+      args+=(
+	'-r+[output date specified by reference time]:seconds since epoch'
       )
     ;|
     freebsd*)
+      local -a alts
       alts=(
 	'seconds:sec:_guard "(0x[0-9a-fA-F]#|[0-9]#)" "seconds since epoch"'
 	'files:file:_files'
       )
       args+=(
-	'-r[reference time: file modification or literal time]:reference: _alternative $alts'
-	'-R[RFC2822 format]'
+	'-r+[reference time: file modification or literal time]:reference: _alternative $alts'
       )
     ;|
     freebsd*|dragonfly*|darwin*)
-      args+=( '-f:parsing format' '-v:adjustment value' )
+      args+=(
+	'-f+[use specified format for input]:parsing format:_date_formats:new date:'
+	'*-v+[adjust and print (but not set) date]:[+-]value[ymwdHMS]:'
+      )
     ;;
+    freebsd*|dragonfly*)
+      args+=( '-R[display in RFC2822 format]' )
+    ;|
+    openbsd*|netbsd*) args+=( '-a[gradually skew]' )
+    ;|
     openbsd*)
       args+=( '-z[specify timezone for output]:time zone:_time_zone')
     ;|
-    openbsd*|netbsd*) args=( '-a[gradually skew]' ) ;;
+    netbsd*)
+      args+=( '-d[output date specified by string]:time string:' )
+    ;;
   esac
 fi
 
-_arguments \
-  '-u[display or set time in UTC]' \
-  ': :_guard "^--*" "format or date"' \
-  "$args[@]"
+_arguments $opts : $args \
+  '1:format or date:->fmt_date' && ret=0
+
+case $state in
+  (fmt_date)
+    local expl
+    if compset -P '+'; then
+      _wanted date-formats expl 'output format' _date_formats && ret=0
+    elif [[ $words[CURRENT] != -* ]]; then
+      # TODO: in most cases it should be possible to determine which
+      # (or both or neither) of the +format and/or date is allowed
+      # depending on the options already on the command line
+      _message -e date-formats '+format or date' && ret=0
+    fi
+    ;;
+esac
+
+return ret
diff --git a/Completion/Unix/Command/_dbus b/Completion/Unix/Command/_dbus
index bdd7613f0..3f106cb5c 100644
--- a/Completion/Unix/Command/_dbus
+++ b/Completion/Unix/Command/_dbus
@@ -13,24 +13,27 @@ case $service in
       '--print-reply=-::format:(literal)' \
       '--reply-timeout=-:timeout (ms)' \
       '--type=-:type:(method_call signal)' \
+      '(* -)--help' \
       ':object path:->objectpaths' \
       ':message name:->methods' \
       ':content:->contents' && ret=0
   ;;
   dbus-monitor)
     _arguments -A "--*" -C \
-      '(--session)--system' '(--system)--session' \
+      '*:watch expression:->expressions' \
+      - '(bus)' \
+      --system --session \
       '--address=-:bus address:->addresses' \
-      '(--profile)--monitor' '(--monitor)--profile' \
-      '*:watch expression:->expressions' && ret=0
+      - '(format)' \
+      --monitor --profile --pcap --binary && ret=0
   ;;
 esac
 
 case $state in
   addresses)
     compset -P '*;'
-    if compset -P '*='; then
-      _files  && ret=0
+    if compset -P 1 '*='; then
+      _files && ret=0
     else
       _message -e addresses address
     fi
diff --git a/Completion/Unix/Command/_devtodo b/Completion/Unix/Command/_devtodo
index dbc64f870..2380399b9 100644
--- a/Completion/Unix/Command/_devtodo
+++ b/Completion/Unix/Command/_devtodo
@@ -32,11 +32,11 @@ arg_generic=(
 	'--help[display help]'
 	'--version[display version]'
 	'--title[todo title]:string: '
-	'--date-format[strftime time formet]:time string: '
+	'--date-format[strftime time format]:time string:_date_formats'
 	'*--format[define format]:format:_todo_format'
 	'*--use-format[output format]:format:_todo_format'
 	'--sort[sort database]:sort expression:_todo_sort'
-	'--paranoid[paranoid parmissions etc]'
+	'--paranoid[paranoid permissions etc]'
 	'--database-loaders[loader order]:database loader: '
 	'--backup[backup database]:count: '
 	'--timeout[display timeout]:seconds: '
diff --git a/Completion/Unix/Command/_df b/Completion/Unix/Command/_df
index a98180a2c..677b8c727 100644
--- a/Completion/Unix/Command/_df
+++ b/Completion/Unix/Command/_df
@@ -5,12 +5,6 @@ local -A opt_args
 
 if _pick_variant gnu=GNU unix --version; then
   args=(
-    '(-B --block-size -k)'{-B+,--block-size=}'[specify block size]:size (bytes)'
-    '(-B --block-size -k)-k[like --block-size=1K]'
-    '(-P --portability)'{-P,--portability}'[use the POSIX output format]'
-    '(-h --human-readable -H --si)'{-h,--human-readable}'[print sizes in human readable format]'
-    '(-h --human-readable -H --si)'{-H,--si}'[human readable fomat, but use powers of 1000 not 1024]'
-    '(-i --inodes)'{-i,--inodes}'[list inode information instead of block usage]'
     '--total[produce a grand total]'
     '(-T --print-type)'{-T,--print-type}'[print file system type]'
     '(-a --all)'{-a,--all}'[include dummy file systems]'
@@ -23,6 +17,13 @@ if _pick_variant gnu=GNU unix --version; then
     '(- : *)--help[display help and exit]'
     '(- : *)--version[output version information and exit]'
     '*:files:_files'
+    - '(format)'
+    {-B+,--block-size=}'[specify block size]:size (bytes)'
+    '-k[like --block-size=1K]'
+    {-P,--portability}'[use the POSIX output format]'
+    {-h,--human-readable}'[print sizes in human readable format]'
+    {-H,--si}'[human readable format, but use powers of 1000 not 1024]'
+    {-i,--inodes}'[list inode information instead of block usage]'
   )
 elif [[ "$OSTYPE" == (darwin|freebsd|dragonfly)* ]]; then
   args=(
diff --git a/Completion/Unix/Command/_dmesg b/Completion/Unix/Command/_dmesg
new file mode 100644
index 000000000..3dd059214
--- /dev/null
+++ b/Completion/Unix/Command/_dmesg
@@ -0,0 +1,32 @@
+#compdef dmesg
+
+local args
+
+case $OSTYPE in
+  linux*) args=( -S --  ) ;;
+  darwin*|dragonfly*|*bsd*)
+    args=(
+      '-M[extract values from core]:core file:_files'
+      '-N[extract name list from kernel image]:system:_files'
+    )
+  ;|
+  dragonfly*|freebsd*)
+    args=(
+      '-a[show all data, including syslog and console]'
+      '-c[clear the buffer after printing]'
+    )
+  ;|
+  openbsd*)
+    args+=(
+      '-s[show console message buffer instead]'
+    )
+  ;;
+  dragonfly*)
+    args+=(
+      '\*-f[follow buffer, displaying new data as it arrives]'
+      '-n[use specified kernel core]:number'
+    )
+  ;;
+esac
+
+_arguments -s $args
diff --git a/Completion/Unix/Command/_dmidecode b/Completion/Unix/Command/_dmidecode
index 0ff56113d..b48608dae 100644
--- a/Completion/Unix/Command/_dmidecode
+++ b/Completion/Unix/Command/_dmidecode
@@ -1,10 +1,13 @@
 #compdef dmidecode
 
-_arguments \
-  '(-d --dev-mem)'{-d,--dev-mem}':memory device:_files' \
+_arguments -s \
+  '(-d --dev-mem --from-dump)'{-d+,--dev-mem=}'[read memory from specified file]:memory device [/dev/mem]:_files' \
+  '(-)'{-h,--help}'[display usage information]' \
   '(-q --quiet -u --dump)'{-q,--quiet}'[be less verbose]' \
-  '(-t --type -u --dump -s --string)'{-s,--string}':DMI string:(bios-vendor bios-version bios-release-date system-manufacturer system-product-name system-version system-serial-number system-uuid baseboard-manufacturer baseboard-product-name baseboard-version baseboard-serial-number baseboard-asset-tag chassis-manufacturer chassis-type chassis-version chassis-serial-number chassis-asset-tag processor-family processor-manufacturer processor-version processor-frequency)' \
-  '(-t --type)'{-t,--type}':types to display:(bios system baseboard chassis processor memory cache connector slot)' \
-  '(-q --quiet -u --dump -s --string)'{-u,--dump}'[do not decode]' \
-  '(-h --help)'{-h,--help}'[display usage information]' \
-  '(-V --version)'{-V,--version}'[display version information]'
+  '(--type -u --dump --dump-bin -s --string)'{-s+,--string=}':DMI string:(bios-vendor bios-version bios-release-date system-manufacturer system-product-name system-version system-serial-number system-uuid baseboard-manufacturer baseboard-product-name baseboard-version baseboard-serial-number baseboard-asset-tag chassis-manufacturer chassis-type chassis-version chassis-serial-number chassis-asset-tag processor-family processor-manufacturer processor-version processor-frequency)' \
+  '(-s --string --dump-bin)*'{-t+,--type=}'[only display entries of specified type]:entry type:(bios system baseboard chassis processor memory cache connector slot)' \
+  '(-q --quiet -u --dump -s --string)'{-u,--dump}"[don't decode entries]" \
+  '--dump-bin=[dump DMI data to a binary file]:file:_files' \
+  '(-d --dev-mem)--from-dump=[read DMI data from a binary file]:file:_files' \
+  "--no-sysfs[don't attempt to read DMI data from sysfs files]" \
+  '(-)'{-V,--version}'[display version information]'
diff --git a/Completion/Unix/Command/_doas b/Completion/Unix/Command/_doas
new file mode 100644
index 000000000..94395557c
--- /dev/null
+++ b/Completion/Unix/Command/_doas
@@ -0,0 +1,22 @@
+#compdef doas
+
+local environ e cmd
+local -a _comp_priv_prefix
+
+zstyle -a ":completion:${curcontext}:" environ environ
+
+for e in "${environ[@]}"
+do local -x "$e"
+done
+
+cmd="$words[1]"
+_arguments -s -S -A '-*' : \
+  - optL \
+  '-L[clear any persisted authorizations]' \
+  - default \
+  '-a+[specify authentication style]:authentication style' \
+  '(-n -s)-C+[check config file and report on command matching]:config:_files' \
+  '(-C)-n[non-interactive: fail rather than prompt for a password]' \
+  '(-C *)-s[run a shell]' \
+  '-u+[run command as specified user]:user:_users' \
+  '*::arguments:{ _comp_priv_prefix=( $cmd -n ${(kv)opt_args[-u]} ) ; _normal }'
diff --git a/Completion/Unix/Command/_dsh b/Completion/Unix/Command/_dsh
index 688e024ce..fc7680062 100644
--- a/Completion/Unix/Command/_dsh
+++ b/Completion/Unix/Command/_dsh
@@ -2,6 +2,7 @@
 
 local curcontext="$curcontext" state line expl
 typeset -A opt_args
+local -a _comp_priv_prefix
 
 _arguments -s -C -S \
   '(-v --verbose -q --quiet)'{-v,--verbose}'[verbose output]' \
@@ -24,7 +25,7 @@ _arguments -s -C -S \
   '*::args: _normal' && return
 
 if [[ $state = groups ]]; then
-  if ! zstyle -s ":completion:$curcontext:dsh-groups" dsh-groups grp; then
+  if ! zstyle -s ":completion:${curcontext}:dsh-groups" dsh-groups grp; then
     [[ -e ~/.dsh/group ]] && grp="~/.dsh/group" || return 1
   fi
   _path_files -W ~/.dsh/group && return
diff --git a/Completion/Unix/Command/_entr b/Completion/Unix/Command/_entr
new file mode 100644
index 000000000..8a830ae71
--- /dev/null
+++ b/Completion/Unix/Command/_entr
@@ -0,0 +1,9 @@
+#compdef entr
+
+_arguments -s -S \
+  '-c[execute clear before invoking utility]' \
+  '-d[track directories and exit if a new file is added]' \
+  '-p[postpone first execution of the utility]' \
+  '-r[reload a persistent child process]' \
+  '(-):command name:_command_names -e' \
+  '*::arguments:_normal'
diff --git a/Completion/Unix/Command/_env b/Completion/Unix/Command/_env
index 96261bba8..9b2a1e011 100644
--- a/Completion/Unix/Command/_env
+++ b/Completion/Unix/Command/_env
@@ -1,13 +1,46 @@
 #compdef env
 
-if _pick_variant gnu=Free\ Soft unix --version; then
-  _arguments \
-    '(--ignore-environment -i)'{-i,--ignore-environment}'[start with empty environment]' \
-    '*'{-u,--unset=}'[remove variable from the environment]:env var to remove:compadd ${(k)parameters[(R)*export*]}' \
-    '--help[help]' \
-    '--version[version]' \
-    '(-):command: _command_names -e' \
-    '*::arguments: _normal'
-else
-  _precommand
+local context state line variant args ret=1
+
+_pick_variant -r variant gnu=Free\ Soft $OSTYPE --version
+case $variant in
+  gnu)
+    args=(
+      '(-)'{-i,--ignore-environment}'[start with empty environment]'
+      '(--ignore-environment -i --help --version)*'{-u,--unset=}'[remove variable from the environment]:env var to remove:_parameters -g "*export*"'
+      '(- *)--help[display help information]'
+      '(- *)--version[display version information]'
+    )
+  ;;
+  freebsd*)
+    args=(
+      '(-i)*-u[remove variable from the environment]:env var to remove:_parameters -g "*export*"'
+      '-P[specify alternate executable search PATH]:path:_dir_list'
+      '-S[perform word splitting]'
+      '*-v[verbose output]'
+    )
+  ;&
+  *)
+    if (( $words[(i)-] < CURRENT )); then
+      words[(i)-]=()
+      (( CURRENT-- ))
+    else
+      args+=(
+	'(-i)'{-,-i}'[start with empty environment]'
+      )
+    fi
+  ;;
+esac
+
+_arguments $args \
+  '*::arguments:->normal' && ret=0
+
+if [[ -n $state ]]; then
+  while [[ $words[1] = *=* ]]; do
+    shift words
+    (( CURRENT-- ))
+  done
+  _normal && ret=0
 fi
+
+return ret
diff --git a/Completion/Unix/Command/_espeak b/Completion/Unix/Command/_espeak
index d868c7912..4ab443bba 100644
--- a/Completion/Unix/Command/_espeak
+++ b/Completion/Unix/Command/_espeak
@@ -1,33 +1,46 @@
 #compdef espeak
 
-#TODO: complete non-existing filenames for -w and --phonout
-#TODO: describe special cases for -k
-#TODO: complete --punct better?
+local curcontext="$curcontext" state line expl ret=1
+typeset -A opt_args
 
-_arguments \
-    '-h[help]' \
-    '-f[file to speak]:text file:_files' \
-    '--stdin[speak from stdin]' \
-    '-q[quiet, no sound output]' \
-    '-a[amplitude]:integer:(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
-18 19 20)' \
-    '-l[line length]:integer: ' \
-    '-p[pitch]:integer:(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
-19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
-43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
-67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
-91 92 93 94 95 96 97 98 99)' \
-    '-s[speed]:words per minute: ' \
-    '-v[voice]:voice name:(afrikaans bosnian catalan czech welsh-test danish-test german greek default en-scottish english lancashire english_rp english_wmids english-us en-westindies esperanto spanish spanish-latin-american finnish french french greek-ancient hindi-test croatian hungarian armenian armenian-west indonesian-test icelandic-test italian test/jbo kurdish latin latvian macedonian-test dutch-test norwegian-test test/pap polish brazil portugal romanian russian_test slovak albanian serbian swedish swahihi-test tamil turkish vietnam-test Mandarin cantonese-test)' \
-    '-b[8-bit text]' \
-    '-m[SSML or other XML text]' \
-    '-w[wav output]:filename: ' \
-    '-x[output phoneme mnemonics]' \
-    '-X[output phoneme mnemonics and translation trace]' \
-    '--stdout[output speech to stdout]' \
-    '-k[capital letter indication]:integer:' \
-    '--punct=-::characters: ' \
-    '--voices=-[list available voices]::language code:(af bs ca cs cy da de el en en-sc en-uk en-uk-north en-uk-rp en-uk-wmids en-us en-wi eo es es-la fi fr fr-be grc hi hr hu hy hy id is it jbo ku la lv mk nl no pap pl pt pt-pt ro ru sk sq sr sv sw ta tr vi zh zh-yue)' \
-    '--path=-[espeak-data path]:path:_files -/' \
-    '--compile=-[compile]::voicename or debug: ' \
-    '--phonout=-[output filename for -x/-X]:filename: ' 
+_arguments -C \
+  '(1 --stdin)-f+[specify file to speak]:text file:_files' \
+  '(1 -f)--stdin[speak from stdin]' \
+  '(-q)-a+[specify amplitude]:amplitude (0-200) [100]' \
+  '(-q)-g+[specify pause between words]:pause (10ms) [1]' \
+  '-k[capital letter indication]:integer:((1\:sound 2\:the\ word\ "capitals"))' \
+  '-l[specify line length below which clause is ended]:length' \
+  '(-q)-p+[specify pitch adjustment]:adjustment (0-99)' \
+  '(-q)-s+[specify speed]:speed (words per minute) [175]' \
+  '(-q)-v+[voice]:voice name:->voices' \
+  '(--stdout -q)-w+[write speech to wav file]:wav file:_files -g "*.wav(-.)"' \
+  '-b+[specify input encoding]:encoding:((1\:UTF-8 2\:8-bit 3\:16-bit))' \
+  '-m[interpret SSML markup, ignore other XML]' \
+  '(-a -g -p -s -v -w -z --split --stdout)-q[quiet, no sound output]' \
+  '(-x --ipa)-x[output phoneme mnemonics]' \
+  '(-X --ipa)-X[output phoneme mnemonics and translation trace]' \
+  '(-q)-z[suppress final sentence pause at the end of the text]' \
+  '--compile=-[compile pronunciation rules and dictionary]::voicename or debug' \
+  '(-x -X)--ipa=-[output phonemes using International Phonetic Alphabet]::options:((1\:use\ ties 2\:use\ ZWJ 3\:separate\ with\ _))' \
+  '--path=[specify espeak-data path]:path:_files -/' \
+  '--pho[output mbrola phoneme data]' \
+  '--phonout=[output filename for -x/-X/--ipa]:filename:_files' \
+  '--punct=-[speak names of punctuation characters]::characters' \
+  '(-q --stdout)--split=[periodically start new wav file]:period (minutes)' \
+  '(-w -q --split)--stdout[output speech to stdout]' \
+  '--voices=-[list available voices]::language code:->languages' \
+  '(-)'{-h,--help}'[display help information]' \
+  '(-)--version[display version information]' && ret=0
+
+case $state in
+  voices)
+    _wanted voices expl voice compadd \
+        ${${${(f)"$(_call_program voices $words[1] --voices)"}[2,-1]#?(#c22)}%% *} && ret=0
+  ;;
+  languages)
+    _wanted languages expl language compadd \
+        ${${${(f)"$(_call_program voices $words[1] --voices)"}[2,-1]#?(#c4)}%% *} && ret=0
+  ;;
+esac
+
+return ret
diff --git a/Completion/Unix/Command/_feh b/Completion/Unix/Command/_feh
index 322b1a33c..a094e33c7 100644
--- a/Completion/Unix/Command/_feh
+++ b/Completion/Unix/Command/_feh
@@ -77,7 +77,7 @@ argument_postfix=(
 	'recursive'   '[recurse into subdirectories]'
 	'randomize'   '[randomize file list before displaying]'
 	'filelist'    '[read file list from this file]:file:_files'
-	'preload'     '[eliminate unlaodable images before displaying]'
+	'preload'     '[eliminate unloadable images before displaying]'
 	'full-screen' '[make the window fullscreen]'
 	'geometry'    '[limit window size]:geometry: '
 	'auto-zoom'   '[zoom picture to screen size]'
@@ -114,7 +114,7 @@ argument_postfix=(
 	'thumb-width' '[montage mode: Thumbnail width]:pixels: '
 	'thumb-height' '[montage mode: Thumbnail height]:pixels: '
 	'limit-width' '[montage mode: Limit montage width]:pixels: '
-	'limit-height' '[montage mode: Limit montage heihgt]:pixels: '
+	'limit-height' '[montage mode: Limit montage height]:pixels: '
 	'bg'          '[montage mode: Background image]:file:_files'
 	'alpha'       '[montage mode: Thumbnail transparency level]:integer: '
 	'font'        '[index mode: Thumbnail info font]:font: '
diff --git a/Completion/Unix/Command/_find b/Completion/Unix/Command/_find
index e736f32cb..3854d6cce 100644
--- a/Completion/Unix/Command/_find
+++ b/Completion/Unix/Command/_find
@@ -1,7 +1,7 @@
 #compdef find gfind
 
-local curcontext="$curcontext" state_descr variant
-local -a state line args alts
+local curcontext="$curcontext" state_descr variant default
+local -a state line args alts disp smatch
 
 _pick_variant -r variant gnu=GNU $OSTYPE -version
 
@@ -101,7 +101,7 @@ case $variant in
       '-D[print diagnostics]:debug option:(help tree search stat rates opt exec)'
       '-O+[enable query optimisation]:level:(1 2 3)'
       '*-daystart'
-      '-regextype:regexp syntax:(emacs posix-awk posix-basic posix-egrep posix-extended)'
+      '-regextype:regexp syntax:(help findutils-default awk egrep ed emacs gnu-awk grep posix-awk posix-basic posix-egrep posix-extended posix-minimal-basic sed)'
       '*-warn'
       '*-nowarn'
       '*-xautofs'
@@ -147,13 +147,21 @@ _arguments -C $args \
   '*-user:user:_users' \
   '*-xdev' \
   '*-a' '*-o' \
-  '*:directory:_files -/'
+  '(-D -E -H -L -O -P -f -s -x --help --version)*:directory:_files -/'
 
 if [[ $state = times ]]; then
   if ! compset -P '[+-]' || [[ -prefix '[0-9]' ]]; then
-    disp=( 'before' 'exactly' 'since' )
     compstate[list]+=' packed'
-    alts=( "senses:sense:compadd -V times -S '' -d disp - + '' -" )
+    if zstyle -t ":completion:${curcontext}:senses" verbose; then
+      zstyle -s ":completion:${curcontext}:senses" list-separator sep || sep=--
+      default=" [default exactly]"
+      disp=( "- $sep before" "+ $sep since" )
+      smatch=( - + )
+    else
+      disp=( before exactly since )
+      smatch=( - '' + )
+    fi
+    alts=( "senses:sense${default}:compadd -V times -S '' -d disp -a smatch" )
   fi
   alts+=( "times:${state_descr}:_dates -f d" )
   _alternative $alts
diff --git a/Completion/Unix/Command/_fmt b/Completion/Unix/Command/_fmt
new file mode 100644
index 000000000..759396637
--- /dev/null
+++ b/Completion/Unix/Command/_fmt
@@ -0,0 +1,60 @@
+#compdef fmt
+
+local variant
+local -a args
+local copt="[preserve indentation of first two lines]"
+local wopt="[specify maximum line width]:width [75]"
+local sopt="[don't join short lines\: split only]"
+
+args=( -A "-*" "(1 2)-w+$wopt" '*:file:_files' )
+_pick_variant -r variant gnu=GNU unix --version
+case $variant in
+  gnu)
+    args=(
+      '(-c --crown-margin)'{-c,--crown-margin}$copt
+      '(-w --width)'{-w+,--width=}$wopt
+      '(-p --prefix)'{-p+,--prefix=}'[only reformat lines with specified prefix]:prefix'
+      '(-s --split-only)'{-s,--split-only}$sopt
+      '(-t --tagged-paragraph)'{-t,--tagged-paragraph}'[indentation of first line different from second]'
+      '(-u --uniform-spacing)'{-u,--uniform-spacing}'[use one space between words, two after sentences]'
+      '(-g --goal)'{-g,--goal=}'[specify goal width]:goal width [93% of width]'
+      '(- *)--help[display help information]'
+      '(- *)--version[display version information]'
+      '*:file:_files'
+    )
+  ;;
+  solaris*)
+    args=(
+      "-c$copt"
+      "-s$sopt"
+    )
+  ;;
+  netbsd*)
+    args+=(
+      '-C[center the text]'
+      '(1 2)-g+[specify goal width]:goal width'
+      '(1 2)-m+[specify maximum width]:maximum width'
+      '-r[format all lines]'
+    )
+  ;|
+  darwin*|dragonfly*|freebsd*|openbsd*)
+    args+=(
+      '-c[center the text line by line]'
+      '-m[sensible formatting of mail header lines]'
+      '-n[format lines beginning with a . (dot) character]'
+      "-p[change in indentation doesn't start new paragraph]"
+      '-s[collapse whitespace inside lines]'
+      '-d+[specify sentence-ending characters]:sentence ends [.?!]'
+      '-l+[replace initial spaces with tabs]:tab width [8]'
+      '-t+[specify tab width of input files]:tab width [8]'
+    )
+  ;& # fall-through
+  netbsd*)
+    args+=( ':: :_guard "[0-9]#" goal width' )
+    (( ${(M)#words[1,CURRENT-1]:#[0-9]##} )) && args+=(
+      ':: :_guard "[0-9]#" maximum width'
+    )
+  ;;
+esac
+
+_arguments -s -S $args
diff --git a/Completion/Unix/Command/_fsh b/Completion/Unix/Command/_fsh
index d9ced5feb..c39373117 100644
--- a/Completion/Unix/Command/_fsh
+++ b/Completion/Unix/Command/_fsh
@@ -1,6 +1,7 @@
 #compdef fsh
 
 local curcontext="$curcontext" state line ret=1
+local -a _comp_priv_prefix
 
 _arguments -C \
   '(- : *)'{-h,--help}'[display help information]' \
diff --git a/Completion/Unix/Command/_gcc b/Completion/Unix/Command/_gcc
index 1276054db..e188c7e9f 100644
--- a/Completion/Unix/Command/_gcc
+++ b/Completion/Unix/Command/_gcc
@@ -94,7 +94,7 @@ romp)
   ;;
 mips*)
   args=(
-    '-mcpu=:CPU type:(r2000 r3000 r4000 r4400 r4600 r6000_'
+    '-mcpu=:CPU type:(r2000 r3000 r4000 r4400 r4600 r6000)'
     -mabicalls -membedded-data
     -membedded-pic -mfp32 -mfp64 -mgas -mgp32 -mgp64
     -mgpopt -mhalf-pic -mhard-float -mint64 -mips1
@@ -353,7 +353,7 @@ args+=(
   '-print-file-name=-[Display the full path to library <library>]:library:->library'
   '-print-prog-name=-[Display the full path to compiler component <program>]:program:'
   '*-specs=-[Override built-in specs with the contents of <file>]:file:_files'
-  '-std=-[Assume that the input sources are for <standard>]:standard:(c90 c89 c99 c11 gnu90 gnu89 gnu99 gnu11 c++98 c++03 gnu++98 gnu++03 c++11 gnu++11 c++1y gnu++1y)'
+  '-std=-[assume that the input sources are for specified standard]:standard:(c90 c89 c99 c11 gnu90 gnu89 gnu99 gnu11 c++98 c++03 gnu++98 gnu++03 c++11 gnu++11 c++1y gnu++1y c++14 gnu++14 c++1z gnu++1z)'
   '*-include:include file:_files -g \*.h\(-.\)'
   '*-imacros:macro input file:_files -g \*.h\(-.\)'
   '*-idirafter:second include path directory:_files -/'
diff --git a/Completion/Unix/Command/_gdb b/Completion/Unix/Command/_gdb
index d5eed5ae4..510e6f1fa 100644
--- a/Completion/Unix/Command/_gdb
+++ b/Completion/Unix/Command/_gdb
@@ -37,7 +37,7 @@ else
   (-[csx]) _files && return 0 ;;
   (-e)     _description files expl executable
            _files "$expl[@]" -g '*(-*)' && return 0 ;;
-  (-b)     _baudrate && return 0 ;;
+  (-b)     _baudrates && return 0 ;;
   esac
 
   w=( "${(@)words[2,-1]}" )
diff --git a/Completion/Unix/Command/_getent b/Completion/Unix/Command/_getent
index b9aff6642..7f4e8bc9c 100644
--- a/Completion/Unix/Command/_getent
+++ b/Completion/Unix/Command/_getent
@@ -29,6 +29,8 @@ case $state in
       databases=( "${(@)${(@f)$(_call_program databases $words[1] --help \
                    2>/dev/null)}[(r)Supported*,-1]}" )
       databases=( "${=${(@)databases[2,${(@)databases[(i)]}-1]}}" )
+    elif [[ $OSTYPE = freebsd* ]]; then
+      databases=( ${=${(f)"$(_call_program databases $words[1] 2>&1)"}[-1]} )
     else
       databases=( passwd group hosts ipnodes services protocols ethers networks netmasks )
     fi
diff --git a/Completion/Unix/Command/_git b/Completion/Unix/Command/_git
index 2fa004a5e..a46e3b55d 100644
--- a/Completion/Unix/Command/_git
+++ b/Completion/Unix/Command/_git
@@ -53,7 +53,7 @@ _git-add () {
     ignore_missing='--ignore-missing[check if files (even missing) are ignored in dry run]'
   fi
 
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
     '(-n --dry-run)'{-n,--dry-run}'[do not actually add files; only show which ones would be added]' \
     '(-v --verbose)'{-v,--verbose}'[show files as they are added]' \
     '(-f --force)'{-f,--force}'[allow adding otherwise ignored files]' \
@@ -67,6 +67,7 @@ _git-add () {
     '--refresh[do not add files, but refresh their stat() info in index]' \
     '--ignore-errors[continue adding if an error occurs]' \
     $ignore_missing \
+    '--chmod[override the executable bit of the listed files]:override:(-x +x)' \
     '*:: :->file' && return
 
   case $state in
@@ -101,9 +102,10 @@ _git-am () {
   _arguments -S \
     '(-s --signoff)'{-s,--signoff}'[add Signed-off-by: line to the commit message]' \
     '(-S --gpg-sign --no-gpg-sign)'{-S-,--gpg-sign=-}'[GPG-sign the commit]::key id' \
-    '(-S --gpg-sign --no-gpg-sign)--no-gpg-sign[do not GPG-sign the commit]' \
+    "(-S --gpg-sign --no-gpg-sign)--no-gpg-sign[don't GPG-sign the commit]" \
     '(-k --keep)'{-k,--keep}'[pass -k to git mailinfo]' \
     '--keep-non-patch[pass -b to git mailinfo]' \
+    '(-m --message-id)'{-m,--message-id}'[pass -m flag to git-mailinfo]' \
     '(          --no-keep-cr)--keep-cr[pass --keep-cr to git mailsplit]' \
     '(--keep-cr             )--no-keep-cr[do not pass --keep-cr to git mailsplit]' \
     '(-c --scissors --no-scissors)'{-c,--scissors}'[strip everything before a scissors line]' \
@@ -150,12 +152,12 @@ _git-archive () {
     esac
   fi
 
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
     '--format=-[format of the resulting archive]:archive format:__git_archive_formats' \
     '(- :)'{-l,--list}'[list available archive formats]' \
     '(-v --verbose)'{-v,--verbose}'[report progress to stderr]' \
     '--prefix=-[prepend the given path prefix to each filename]:path prefix:_directories -r ""' \
-    '--output=[write archive to argument instead of stdout]:archive:_files' \
+    '(-o --output)'{-o+,--output=}'[write archive to specified file]:archive:_files' \
     '--worktree-attributes[look for attributes in .gitattributes in working directory too]' \
     $backend_args \
     '--remote=[archive remote repository]:remote repository:__git_any_repositories' \
@@ -190,6 +192,13 @@ _git-bisect () {
   # subcommand might be removed from the UI level.
   local curcontext=$curcontext state line ret=1
   declare -A opt_args
+  local good bad
+
+  if good=$(_call_program commands git bisect terms --term-good); then
+    bad=$(_call_program commands git bisect terms --term-bad)
+  else
+    good=( good old ) bad=( new bad )
+  fi
 
   _arguments -C \
     '--help[display git-bisect manual page]' \
@@ -203,15 +212,16 @@ _git-bisect () {
       commands=(
         help:'display a short usage description'
         start:'reset bisection state and start a new bisection'
-        bad:'mark current or given revision as bad'
-        good:'mark current or given revision as good'
+        ${^bad}:'mark current or given revision as bad'
+        ${^good}:'mark current or given revision as good'
         skip:'choose a nearby commit'
         next:'find next bisection to test and check it out'
         reset:'finish bisection search and return to the given branch (or master)'
         visualize:'show the remaining revisions in gitk'
         view:'show the remaining revisions in gitk'
         replay:'replay a bisection log'
-        log:'show the log of the current bisection'
+        terms:'show currently used good/bad terms'
+        log:'show log of the current bisection'
         run:'run evaluation script')
 
       _describe -t commands command commands && ret=0
@@ -222,6 +232,9 @@ _git-bisect () {
       case $line[1] in
         (start)
           _arguments -C \
+	    --term-{good,old}'=[specify alternate term for good revisions]:term' \
+	    --term-{bad,new}'=[specify alternate term for bad revisions]:term' \
+	    '--no-checkout[set BISECT_HEAD reference instead of doing checkout at each iteration]' \
             ':bad revision:__git_commits' \
             '*: :->revision-or-path' && ret=0
           case $state in
@@ -236,11 +249,7 @@ _git-bisect () {
               ;;
           esac
           ;;
-        (bad)
-          _arguments \
-            ': :__git_commits' && ret=0
-          ;;
-        (good|skip)
+        (${(~j.|.)bad}|${(~j.|.)good}|skip)
           # TODO: skip can take revlists.
           _arguments \
             '*: :__git_commits' && ret=0
@@ -257,12 +266,15 @@ _git-bisect () {
           _arguments \
             '*:: : _normal' && ret=0
           ;;
+        (terms)
+	  _arguments --term-good --term-bad && ret=0
+	  ;;
         (view|visualize)
           local -a log_options revision_options
           __git_setup_log_options
           __git_setup_revision_options
 
-          _arguments -w -C -s \
+          _arguments -C -s \
             $log_options \
             $revision_options && ret=0
         (*)
@@ -279,14 +291,14 @@ _git-bisect () {
 _git-branch () {
   declare l c m d e
 
-  l='--color --no-color -r -a -v --verbose --abbrev --no-abbrev --list'
-  c='-l -f --force -t --track --no-track -u --set-upstream --set-upstream-to --unset-upstream --contains --merged --no-merged'
+  l='--color --no-color -r --remotes -a -v --verbose --abbrev --no-abbrev --list --points-at --sort'
+  c='-l --create-reflog -f --force -t --track --no-track -u --set-upstream --set-upstream-to --unset-upstream --contains --merged --no-merged'
   m='-m --move -M'
   d='-d --delete -D'
   e='--edit-description'
 
   declare -a dependent_creation_args
-  if (( words[(I)-r] == 0 )); then
+  if (( words[(I)(-r|--remotes)] == 0 )); then
     dependent_creation_args=(
       "($l $m $d): :__git_branch_names"
       "::start-point:__git_revisions")
@@ -297,7 +309,7 @@ _git-branch () {
     dependent_creation_args=
     dependent_deletion_args=(
       '-r[delete only remote-tracking branches]')
-    if (( words[(I)-r] )); then
+    if (( words[(I)(-r|--remotes)] )); then
       dependent_deletion_args+='*: :__git_ignore_line_inside_arguments __git_remote_branch_names'
     else
       dependent_deletion_args+='*: :__git_ignore_line_inside_arguments __git_branch_names'
@@ -312,18 +324,18 @@ _git-branch () {
       '::new branch name:__git_branch_names')
   fi
 
-  _arguments -w -S -s \
+  _arguments -S -s \
     "($c $m $d $e --no-color :)--color=-[turn on branch coloring]:: :__git_color_whens" \
     "($c $m $d $e : --color)--no-color[turn off branch coloring]" \
     "($c $m $d $e --no-column)"'--column=[display tag listing in columns]:column.branch option:((always\:"always show in columns" never\:"never show in columns" auto\:"show in columns if the output is to the terminal" column\:"fill columns before rows (default)" row\:"fill rows before columns" plain\:"show in one column" dense\:"make unequal size columns to utilize more space" nodense\:"make equal size columns"))' \
     "($c $m $d $e --column)"'--no-column[do not display in columns]' \
     "($c $m $d $e )*--list[list only branches matching glob]:pattern" \
-    "($c $m    $e  -a)-r[list or delete only remote-tracking branches]" \
-    "($c $m $d $e: -r)-a[list both remote-tracking branches and local branches]" \
-    "($c $m $d $e : -v -vv --verbose)"{-v,-vv--verbose}'[show SHA1 and commit subject line for each head]' \
+    "($c $m    $e  -a)"{-r,--remotes}'[list or delete only remote-tracking branches]' \
+    "($c $m $d $e: -r --remotes)-a[list both remote-tracking branches and local branches]" \
+    "($c $m $d $e : -v -vv --verbose)"{-v,-vv,--verbose}'[show SHA1 and commit subject line for each head]' \
     "($c $m $d $e :)--abbrev=[set minimum SHA1 display-length]: :__git_guard_number length" \
     "($c $m $d $e :)--no-abbrev[do not abbreviate sha1s]" \
-    "($l $m $d $e)-l[create the branch's reflog]" \
+    "($l $m $d $e)"{-l,--create-reflog}"[create the branch's reflog]" \
     "($l $m $d $e -f --force)"{-f,--force}"[force the creation of a new branch]" \
     "($l $m $d $e -t --track)"{-t,--track}"[set up configuration so that pull merges from the start point]" \
     "($l $m $d $e)--no-track[override the branch.autosetupmerge configuration variable]" \
@@ -331,7 +343,7 @@ _git-branch () {
     "($l $m $d $e -u --set-upstream --set-upstream-to --unset-upstream)--unset-upstream[remove upstream configuration]" \
     "($l $m $d $e)--contains=[only list branches which contain the specified commit]: :__git_committishs" \
     "($l $m $d $e)--merged=[only list branches which are fully contained by HEAD]: :__git_committishs" \
-    "($l $m $d $e)--no-merged=[do not list branches which are fully contained by HEAD]: :__git_committishs" \
+    "($l $m $d $e)--no-merged=[don't list branches which are fully contained by HEAD]: :__git_committishs" \
     "($c $l $m $d)--edit-description[edit branch description]" \
     $dependent_creation_args \
     "($l $c $d $m $e)"{-m,--move}"[rename a branch and the corresponding reflog]" \
@@ -340,6 +352,9 @@ _git-branch () {
     "($l $c $m $d $e)"{-d,--delete}"[delete a fully merged branch]" \
     "($l $c $m $d $e)-D[delete a branch]" \
     {-q,--quiet}"[be more quiet]" \
+    '*--sort=[specify field to sort on]: :__git_ref_sort_keys' \
+    '--points-at=[only list tags of the given object]: :__git_commits' \
+    "($c $m $d $e -i --ignore-case)"{-i,--ignore-case}'[sorting and filtering are case-insensitive]' \
     $dependent_deletion_args
 }
 
@@ -376,7 +391,7 @@ _git-bundle () {
             local revision_options
             __git_setup_revision_options
 
-            _arguments -w -S -s \
+            _arguments -S -s \
               $revision_options \
               ': :_files' \
               '*: :__git_commit_ranges2' && ret=0
@@ -429,22 +444,24 @@ _git-checkout () {
   local curcontext=$curcontext state line ret=1
   declare -A opt_args
 
-  _arguments -w -C -s \
-    '(-q --quiet)'{-q,--quiet}'[suppress feedback messages]' \
+  _arguments -C -s \
+    '(-q --quiet --progress)'{-q,--quiet}'[suppress progress reporting]' \
     '(-f --force -m --merge --conflict --patch)'{-f,--force}'[force branch switch/ignore unmerged entries]' \
-    '(-q --quiet        --theirs --patch)--ours[check out stage #2 for unmerged paths]' \
-    '(-q --quiet --ours          --patch)--theirs[check out stage #3 for unmerged paths]' \
-    '(   -B --orphan --ours --theirs --conflict --patch --detach)-b+[create a new branch based at given commit]: :__git_branch_names' \
-    '(-b    --orphan --ours --theirs --conflict --patch --detach)-B+[create or update branch based at given commit]: :__git_branch_names' \
+    '(-q --quiet -2 --ours -3 --theirs --patch)'{-2,--ours}'[check out stage #2 for unmerged paths]' \
+    '(-q --quiet -2 --ours -3 --theirs --patch)'{-3,--theirs}'[check out stage #3 for unmerged paths]' \
+    '(   -B --orphan -2 --ours -3 --theirs --conflict --patch --detach)-b+[create a new branch based at given commit]: :__git_branch_names' \
+    '(-b    --orphan -2 --ours -3 --theirs --conflict --patch --detach)-B+[create or update branch based at given commit]: :__git_branch_names' \
     '(-t --track --orphan --patch --detach)'{-t,--track}'[set up configuration so pull merges from the base commit]' \
     '(--patch)--no-track[override the branch.autosetupmerge configuration variable]' \
     $new_branch_reflog_opt \
     '(-b -B -t --track --patch --orphan)--detach[detach the HEAD at named commit]' \
     '(-b -B -t --track --patch --detach)--orphan=[create a new orphan branch based at given commit]: :__git_branch_names' \
-    '--ignore-skip-worktree-bits[ignores patterns and adds back any files in <paths>]' \
     '(-q --quiet -f --force -m --merge --conflict --patch)'{-m,--merge}'[3way merge current branch, working tree and new branch]' \
     '(-q --quiet -f --force -m --merge --patch)--conflict=[same as --merge, using given merge style]:style:(merge diff3)' \
     '(-)'{-p,--patch}'[interactively select hunks in diff between given tree-ish and working tree]' \
+    "--ignore-skip-worktree-bits[don't limit pathspecs to sparse entries only]" \
+    "--ignore-other-worktrees[don't check if another worktree is holding the given ref]" \
+    '(-q --quiet)--progress[force progress reporting]' \
     '(-)--[start file arguments]' \
     '*:: :->branch-or-tree-ish-or-file' && ret=0
 
@@ -457,28 +474,25 @@ _git-checkout () {
       [[ $line[CURRENT] = -* ]] && return
       if (( CURRENT == 1 )) && [[ -z $opt_args[(I)--] ]]; then
         # TODO: Allow A...B
-        local branch_arg='' \
+        local \
               remote_branch_noprefix_arg='remote-branch-names-noprefix::__git_remote_branch_names_noprefix' \
-              tree_ish_arg='tree-ishs::__git_tree_ishs' \
+              tree_ish_arg='tree-ishs::__git_commits_prefer_recent' \
               file_arg='modified-files::__git_modified_files'
 
         if [[ -n ${opt_args[(I)-b|-B|--orphan|--detach]} ]]; then
-          remote_branch_noprefix_arg=
-          file_arg=
+          _alternative $tree_ish_arg && ret=0
         elif [[ -n $opt_args[(I)--track] ]]; then
-          branch_arg='remote-branches::__git_remote_branch_names'
-          remote_branch_noprefix_arg=
-          tree_ish_arg=
-          file_arg=
+          _alternative remote-branches::__git_remote_branch_names && ret=0
         elif [[ -n ${opt_args[(I)--ours|--theirs|-m|--conflict|--patch]} ]]; then
-          remote_branch_noprefix_arg=
+          _alternative $tree_ish_arg $file_arg && ret=0
+        else
+          _alternative \
+            $file_arg \
+            $tree_ish_arg \
+            $remote_branch_noprefix_arg \
+            && ret=0
         fi
 
-        _alternative \
-          $branch_arg \
-          $remote_branch_noprefix_arg \
-          $tree_ish_arg \
-          $file_arg && ret=0
       elif [[ -n ${opt_args[(I)-b|-B|-t|--track|--orphan|--detach]} ]]; then
         _nothing
       elif [[ -n $line[1] ]] && __git_is_treeish $line[1]; then
@@ -500,19 +514,21 @@ _git-cherry-pick () {
     '(- :)--quit[end revert or cherry-pick sequence]' \
     '(- :)--continue[resume revert or cherry-pick sequence]' \
     '(- :)--abort[cancel revert or cherry-pick sequence]' \
+    '--allow-empty[preserve initially empty commits]' \
     '--allow-empty-message[allow replaying a commit with an empty message]' \
     '--keep-redundant-commits[keep cherry-picked commits that will become empty]' \
     '(-e --edit --ff)'{-e,--edit}'[edit commit before committing the revert]' \
     '(--ff)-x[append information about what commit was cherry-picked]' \
     '(-m --mainline)'{-m+,--mainline=}'[specify mainline when cherry-picking a merge commit]:parent number' \
+    '--rerere-autoupdate[update index with reused conflict resolution if possible]' \
     '(-n --no-commit --ff)'{-n,--no-commit}'[do not make the actually commit]' \
     '(-s --signoff --ff)'{-s,--signoff}'[add Signed-off-by line at the end of the commit message]' \
     '(-S --gpg-sign --no-gpg-sign)'{-S-,--gpg-sign=-}'[GPG-sign the commit]::key id' \
-    '(-S --gpg-sign --no-gpg-sign)--no-gpg-sign[do not GPG-sign the commit]' \
+    "(-S --gpg-sign --no-gpg-sign)--no-gpg-sign[don't GPG-sign the commit]" \
     '*'{-s+,--strategy=}'[use given merge strategy]:merge strategy:__git_merge_strategies' \
-    '*'{-X+,--strategy-option=}'[pass merge-strategy-specific option to merge strategy]' \
+    '*'{-X+,--strategy-option=}'[pass merge-strategy-specific option to merge strategy]:option' \
     '(-e --edit -x -n --no-commit -s --signoff)--ff[fast forward, if possible]' \
-    ': : __git_commit_ranges -O expl:git_commit_opts'
+    '*: : __git_commit_ranges -O expl:git_commit_opts'
 }
 
 (( $+functions[_git-citool] )) ||
@@ -525,12 +541,12 @@ _git-clean () {
   local curcontext=$curcontext state line ret=1
   declare -A opt_args
 
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
     '-d[also remove untracked directories]' \
     '(-f --force)'{-f,--force}'[required when clean.requireForce is true (default)]' \
     '(-i --interactive)'{-i,--interactive}'[show what would be done and clean files interactively]' \
     '(-n --dry-run)'{-n,--dry-run}'[only show what would and what would not be removed]' \
-    '(-q --quiet)'{-q,--quiet}'[only report errors]' \
+    '(-q --quiet)'{-q,--quiet}"[don't print names of files removed]" \
     '*'{-e+,--exclude=}'[skip files matching specified pattern]:pattern' \
     '(-X   )-x[also remove ignored files]' \
     '(   -x)-X[remove only ignored files]' \
@@ -585,12 +601,14 @@ _git-clone () {
   # TODO: Argument to -o should be a remote name.
   # TODO: Argument to -b should complete branch names in the repository being
   # cloned (see __git_references())
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
     '(-l --local --no-local)'{-l,--local}'[clone locally, hardlink refs and objects if possible]' \
     '(-l --local --no-local)--no-local[override --local, as if file:/// URL was given]' \
     '--no-hardlinks[copy files instead of hardlinking when doing a local clone]' \
     '(-s --shared)'{-s,--shared}'[share the objects with the source repository (warning: see man page)]' \
+    '(-j --jobs)'{-j+,--jobs=}'[specify number of submodules cloned in parallel]:jobs' \
     '--reference[reference repository]:repository:_directories' \
+    '--reference-if-able[reference repository]:repository:_directories' \
     '--dissociate[make the newly-created repository independent of the --reference repository]' \
     '(-q --quiet)'{-q,--quiet}'[operate quietly]' \
     '(-v --verbose)'{-v,--verbose}'[always display the progressbar]' \
@@ -604,10 +622,16 @@ _git-clone () {
     '--template=[directory to use as a template for the object database]: :_directories' \
     '*'{-c,--config}'[<key>=<value> set a configuration variable in the newly created repository]' \
     '--depth[create a shallow clone, given number of revisions deep]: :__git_guard_number depth' \
+    '--shallow-since=[shallow clone since a specific time]:time' \
+    '*--shallow-exclude=[shallow clone excluding commits reachable from specified remote revision]:revision' \
     '(--no-single-branch)--single-branch[clone only history leading up to the main branch or the one specified by -b]' \
     '(--single-branch)--no-single-branch[clone history leading up to each branch]' \
+    '--shallow-submodules[any cloned submodules will be shallow]' \
     '--recursive[initialize all contained submodules]' \
+    '--recurse-submodules[initialize submodules in the clone]' \
     '--separate-git-dir[place .git dir outside worktree]:path to .git dir:_path_files -/' \
+    '(-4 --ipv4 -6 --ipv6)'{-4,--ipv4}'[use IPv4 addresses only]' \
+    '(-4 --ipv4 -6 --ipv6)'{-6,--ipv6}'[use IPv6 addresses only]' \
     ': :->repository' \
     ': :_directories' && ret=0
 
@@ -626,7 +650,7 @@ _git-clone () {
 
 (( $+functions[_git-column] )) ||
 _git-column () {
-  _arguments -w -S -s \
+  _arguments -s \
     '--command=[look up layout mode using config vars column.<name> and column.ui]' \
     '--mode=[specify layout mode. See configuration variable column.ui for option syntax]' \
     '--raw-mode=[same as --mode but take mode encoded as a number]' \
@@ -649,19 +673,20 @@ _git-commit () {
   fi
 
   # TODO: --interactive isn't explicitly listed in the documentation.
-  _arguments -w -S -s \
+  _arguments -S -s \
     '(-a --all --interactive -o --only -i --include *)'{-a,--all}'[stage all modified and deleted paths]' \
     '--fixup=[construct a commit message for use with rebase --autosquash]:commit to be amended:__git_recent_commits' \
     '--squash=[construct a commit message for use with rebase --autosquash]:commit to be amended:__git_recent_commits' \
     $reset_author_opt \
-    '(        --porcelain --dry-run)--short[output dry run in short format]' \
-    '(--short             --dry-run)--porcelain[output dry run in porcelain-ready format]' \
-    '(--short --porcelain --dry-run -z --null)'{-z,--null}'[separate dry run entry output with NUL]' \
+    '(        --porcelain --dry-run)--short[dry run with short output format]' \
+    '--branch[show branch information]' \
+    '(--short             --dry-run)--porcelain[dry run with machine-readable output format]' \
+    '(--short --porcelain --dry-run -z --null)'{-z,--null}'[dry run with NULL-separated output format]' \
     {-p,--patch}'[use the interactive patch selection interface to chose which changes to commit]' \
     '(--reset-author)--author[override the author name used in the commit]:author name' \
     '--date=[override the author date used in the commit]:date' \
     '(-s --signoff)'{-s,--signoff}'[add Signed-off-by line at the end of the commit message]' \
-    '(-n --no-verify)'{-n,--no-verify}'[do not look for suspicious lines the commit introduces]' \
+    '(-n --no-verify)'{-n,--no-verify}'[bypass pre-commit and commit-msg hooks]' \
     '--allow-empty[allow recording an empty commit]' \
     '--allow-empty-message[allow recording a commit with an empty message]' \
     '--cleanup=[specify how the commit message should be cleaned up]:mode:((verbatim\:"do not change the commit message at all"
@@ -683,7 +708,7 @@ _git-commit () {
     '(         --no-status)--status[include the output of git status in the commit message template]' \
     '(--status            )--no-status[do not include the output of git status in the commit message template]' \
     '(-S --gpg-sign --no-gpg-sign)'{-S-,--gpg-sign=}'[GPG-sign the commit]::key id' \
-    '(-S --gpg-sign --no-gpg-sign)--no-gpg-sign[do not GPG-sign the commit]' \
+    "(-S --gpg-sign --no-gpg-sign)--no-gpg-sign[don't GPG-sign the commit]" \
     '(-a --all --interactive -o --only -i --include *)--interactive[interactively update paths in the index file]' \
     $amend_opt \
     '*: :__git_ignore_line_inside_arguments __git_changed_files' \
@@ -697,7 +722,7 @@ _git-commit () {
 
 (( $+functions[_git-describe] )) ||
 _git-describe () {
-  _arguments -w -S -s \
+  _arguments -S -s \
     '(*)--dirty=-[describe HEAD, adding mark if dirty]::mark' \
     '--all[use any ref found in "$GIT_DIR/refs/"]' \
     '--tags[use any ref found in "$GIT_DIR/refs/tags"]' \
@@ -722,9 +747,10 @@ _git-diff () {
   __git_setup_diff_options
   __git_setup_diff_stage_options
 
-  _arguments -w -C -s \
+  _arguments -C -s \
     $* \
     $diff_options \
+    '(--exit-code)--quiet[disable all output]' \
     $diff_stage_options \
     '(--cached --staged)'{--cached,--staged}'[show diff between index and named commit]' \
     '(-)--[start file arguments]' \
@@ -818,9 +844,13 @@ _git-fetch () {
   local -a fetch_options
   __git_setup_fetch_options
 
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
     $fetch_options \
-    '--multiple[allow several repository arguments]' \
+    '--shallow-since=[deepen history of shallow repository based on time]:time' \
+    '*--shallow-exclude=[deepen history of shallow clone by excluding revision]:revision' \
+    '--deepen[deepen history of shallow clone]:number of commits' \
+    '(-n --no-tags -t --tags)'{-n,--no-tags}'[disable automatic tag following]' \
+    '(--all -m --multiple)'{-m,--multiple}'[fetch from multiple remotes]' \
     '*:: :->repository-or-group-or-refspec' && ret=0
 
   case $state in
@@ -851,7 +881,7 @@ _git-format-patch () {
   # TODO: -- is wrong.
   # TODO: Should filter out --name-only, --name-status, and --check from
   # $diff_options.
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
     $diff_options \
     '--[limit the number of patches to prepare]: :__git_guard_number "number of patches to prepare"' \
     '(-o --output-directory --stdout)'{-o+,--output-directory=}'[store resulting files in given directory]: :_directories' \
@@ -859,7 +889,7 @@ _git-format-patch () {
     '(-n --numbered -N --no-numbered -k --keep-subject)'{-N,--no-numbered}'[name output in \[PATCH\] format]' \
     '--start-number=[start numbering patches at given number]: :__git_guard_number "patch number"' \
     '--numbered-files[use only number for file name]' \
-    '(-n --numbered -N --no-numbered -k --keep-subject --subject-prefix)'{-k,--keep-subject}'[do not strip/add \[PATCH\] from the first line of the commit message]' \
+    '(-n --numbered -N --no-numbered -k --keep-subject --rfc --subject-prefix)'{-k,--keep-subject}"[don't strip/add \[PATCH\] from the first line of the commit message]" \
     '(-s --signoff)'{-s,--signoff}'[add Signed-off-by: line to the commit message]' \
     '(-o --output-directory)--stdout[output the generated mbox on standard output (implies --mbox)]' \
     '(         --no-attach --inline)--attach=-[create attachments instead of inlining patches]::boundary' \
@@ -871,7 +901,8 @@ _git-format-patch () {
     '--in-reply-to=[make the first mail a reply to the given message]:message id' \
     '--ignore-if-in-upstream[do not include a patch that matches a commit in the given range]' \
     '(-v --reroll-count)'{-v+,--reroll-count=}'[mark the series as the <n>-th iteration of the topic]: :__git_guard_number iteration' \
-    '(-k --keep-subject)--subject-prefix=[use the given prefix instead of \[PATCH\]]:prefix' \
+    '(-k --keep-subject --subject-prefix)--rfc[use \[RFC PATCH\] instead of \[PATCH\]]' \
+    '(-k --keep-subject --rfc)--subject-prefix=[use the given prefix instead of \[PATCH\]]:prefix' \
     '*--to=[add To: header to email headers]: :_email_addresses' \
     '*--cc=[add Cc: header to email headers]: :_email_addresses' \
     '--from=[add From: header to email headers]: :_email_addresses' \
@@ -882,9 +913,10 @@ _git-format-patch () {
     '(--signature                --signature-file)--no-signature[do not add a signature]' \
     '(--signature --no-signature                 )--signature-file=[use contents of file as signature]' \
     '--suffix=[use the given suffix for filenames]:filename suffix' \
-    '--quiet[suppress the output of the names of generated files]' \
+    '(-q --quiet)'{-q,--quiet}'[suppress the output of the names of generated files]' \
     '--no-binary[do not output contents of changes in binary files, only note that they differ]' \
     '--root[treat the revision argument as a range]' \
+    '--zero-commit[output all-zero hash in From header]' \
     ': :->commit-or-commit-range' && ret=0
 
   case $state in
@@ -902,12 +934,12 @@ _git-format-patch () {
 
 (( $+functions[_git-gc] )) ||
 _git-gc () {
-  _arguments -w -S -s \
+  _arguments -S -s \
     '--aggressive[more aggressively optimize]' \
     '--auto[check whether housekeeping is required]' \
     '(        --no-prune)--prune=[prune loose objects older than given date]: :__git_datetimes' \
     '(--prune           )--no-prune[do not prune any loose objects]' \
-    '--quiet[suppress all progress reports]'
+    '(-q --quiet)'{-q,--quiet}'[suppress progress reporting]'
 }
 
 (( $+functions[_git-grep] )) ||
@@ -929,9 +961,11 @@ _git-grep () {
   _arguments -C -A '-*' \
     '(-O --open-files-in-pager --no-index)--cached[search blobs registered in index file instead of working tree]' \
     '(--cached)--no-index[search files in current directory, not just tracked files]' \
-    '(--exclude-standard)--no-exclude-standard[also search in ignored files]' \
+    '(--exclude-standard)--no-exclude-standard[search also in ignored files]' \
     '(--no-exclude-standard)--exclude-standard[exclude files standard ignore mechanisms]' \
-    '--untracked[search in untracked files]' \
+    '--recurse-submodules[recursively search in each submodule]' \
+    "--parent-basename=[prepend parent project's basename to output]:basename" \
+    '--untracked[search also in untracked files]' \
     '(-a --text)'{-a,--text}'[process binary files as if they were text]' \
     '(--textconv --no-textconv)--textconv[honor textconv filter settings]' \
     '(--textconv --no-textconv)--no-textconv[do not honor textconv filter settings]' \
@@ -960,20 +994,21 @@ _git-grep () {
     '(-A --after-context)'{-A+,--after-context=}'[show <num> trailing lines, and separate groups of matches]: :__git_guard_number lines' \
     '(-B --before-context)'{-B+,--before-context=}'[show <num> leading lines, and separate groups of matches]: :__git_guard_number lines' \
     '(-A --after-context -B --before-context -C --context)'{-C+,--context=}'[show <num> leading and trailing lines, and separate groups of matches]: :__git_guard_number lines' \
+    '--threads=[use specified number of threads]:number of threads' \
     '(-p --show-function)'{-p,--show-function}'[show preceding line containing function name of match]' \
     '(-W --function-context)'{-W,--function-context}'[show whole function where a match was found]' \
     '(1)*-f+[read patterns from given file]:pattern file:_files' \
     '(1)*-e+[use the given pattern for matching]:pattern' \
     $pattern_operators \
     '--all-match[all patterns must match]' \
-    ':pattern' \
+    ': :_guard "^-*" pattern' \
     '*:: :->tree-or-file' && ret=0
 
   # TODO: If --cached, --no-index, -O, or --open-files-in-pager was given,
   # don't complete treeishs.
   case $state in
     (tree-or-file)
-      integer first_tree last_tree start end
+      integer first_tree last_tree start end i
 
       (( start = words[(I)(-f|-e)] > 0 ? 1 : 2 ))
       (( end = $#line - 1 ))
@@ -1070,7 +1105,7 @@ _git-gui () {
 
 (( $+functions[_git-init] )) ||
 _git-init () {
-  _arguments -w -S -s \
+  _arguments -S -s \
     '(-q --quiet)'{-q,--quiet}'[do not print any results to stdout]' \
     '--bare[create a bare repository]' \
     '--template=[directory to use as a template for the object database]: :_directories' \
@@ -1088,10 +1123,9 @@ _git-log () {
   __git_setup_log_options
   __git_setup_revision_options
 
-  _arguments -w -C -s \
+  _arguments -C -s \
     $log_options \
     $revision_options \
-    '-L+[trace the evolution of a line range or regex within a file]:range' \
     '(-)--[start file arguments]' \
     '1: :->first-commit-ranges-or-files' \
     '*: :->commit-ranges-or-files' && ret=0
@@ -1134,12 +1168,15 @@ _git-merge () {
   __git_setup_merge_options
   local -a git_commit_opts=(--all --not HEAD --not)
 
-  _arguments -w -S -s \
+  _arguments -S -s \
     $merge_options \
     '-m+[set the commit message to be used for the merge commit]:merge message' \
+    '(--edit --no-edit)-e[open an editor to change the commit message]' \
     '(                    --no-rerere-autoupdate)--rerere-autoupdate[allow the rerere mechanism to update the index]' \
     '(--rerere-autoupdate                       )--no-rerere-autoupdate[do not allow the rerere mechanism to update the index]' \
     '--abort[restore the original branch and abort the merge operation]' \
+    '--continue[continue the current in-progress merge]' \
+    '--progress[force progress reporting]' \
     '*: : __git_commits -O expl:git_commit_opts'
 }
 
@@ -1148,7 +1185,8 @@ _git-mv () {
   local curcontext=$curcontext state line ret=1
   declare -A opt_args
 
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
+    '(-v --verbose)'{-v,--verbose}'[output additional information]' \
     '(-f --force)'{-f,--force}'[rename/move even if targets exist]' \
     '-k[skip rename/move that would lead to errors]' \
     '(-n --dry-run)'{-n,--dry-run}'[only show what would happen]' \
@@ -1189,7 +1227,9 @@ _git-notes () {
         merge:'merge the given notes ref into the current ref'
         show:'show notes for a given object'
         remove:'remove notes for a given object'
-        prune:'remove all notes for non-existing/unreachable objects')
+        prune:'remove all notes for non-existing/unreachable objects'
+        get-ref:'print the current notes ref'
+      )
 
       _describe -t commands command commands && ret=0
       ;;
@@ -1197,14 +1237,14 @@ _git-notes () {
       curcontext=${curcontext%:*}-$line[1]:
 
       case $line[1] in
-        (list|show|edit|remove)
+        (list|show)
           _arguments \
             ': :__git_commits' && ret=0
           ;;
         (add)
           # TODO: Only complete commits that don't have notes already, unless
           # -f or --force has been given.
-          _arguments -w -S -s \
+          _arguments -S -s \
             '*'{-m+,--message=}'[use given note message]:message' \
             '*'{-F+,--file=}'[take note message from given file]:note message file:_files' \
             '(-C --reuse-message)'{-C+,--reuse-message=}'[take note message from given blob object]: :__git_blobs' \
@@ -1214,24 +1254,35 @@ _git-notes () {
           ;;
         (copy)
           # TODO: --for-rewrite is undocumented.
-          _arguments -w -S -s \
+          _arguments -S -s \
             '(-f --force)'{-f,--force}'[replace existing note]' \
             '(:)--stdin[read objects from stdin]' \
             '(:--stdin)--for-rewrite=[load rewriting config for given command]:command:(amend rebase)' \
             ': :__git_commits' \
             ': :__git_commits' && ret=0
           ;;
+	(edit)
+	  _arguments --allow-empty ':object:__git_commits' && ret=0
+	  ;;
         (merge)
-          _arguments -w -S -s \
+          _arguments -S -s \
             '(-s --strategy)--abort[abort an in-progress notes merge]' \
             '(-s --strategy)--commit[finalize an in-progress notes merge]' \
-            {-q,--quiet}'[be quiet]' \
-            {-v,--verbose}'[be more verbose]' \
+	    '(-q --quiet)'{-q,--quiet}'[be quiet]' \
+            '(-v --verbose)'{-v,--verbose}'[be more verbose]' \
             '(--abort --commit)'{-s,--strategy=}'[resolve conflicts using the given strategy]' \
             ': :__git_notes_refs' && ret=0
           ;;
+	(prune)
+	  _arguments -s \
+	    '(-v --verbose)'{-v,--verbose}'[be more verbose]' \
+	    '(-n --dry-run)'{-n,--dry-run}"[don't remove anything, just report what would be deleted]" && ret=0
+          ;;
+	(remove)
+	  _arguments --ignore-missing --stdin ':object:__git_commits' && ret=0
+	  ;;
         (append)
-          _arguments -w -S -s \
+          _arguments -S -s \
             '*'{-m+,--message=}'[use given note message]:message' \
             '*'{-F+,--file=}'[take note message from given file]:note message file:_files' \
             '(-C --reuse-message)'{-C+,--reuse-message=}'[take note message from given blob object]: :__git_blobs' \
@@ -1258,9 +1309,12 @@ _git-pull () {
     $merge_options \
     '(-r --rebase --no-rebase)'{-r=-,--rebase=-}'[perform a rebase after fetching]::rebase after fetching:((true\:"rebase after fetching"
                                                                                                         false\:"merge after fetching"
-                                                                                                        preserve\:"rebase and preserve merges"))' \
+                                                                                                        preserve\:"rebase and preserve merges"
+													interactive\:"allow list of commits to be edited"))' \
     '(-r --rebase            )--no-rebase[do not perform a rebase after fetching]' \
+    '--autostash[automatically stash/stash pop before and after rebase]' \
     $fetch_options \
+    '(--no-tags -t --tags)--no-tags[disable automatic tag following]' \
     ': :__git_any_repositories' \
     '*: :__git_ref_specs_fetchy'
 }
@@ -1268,22 +1322,28 @@ _git-pull () {
 (( $+functions[_git-push] )) ||
 _git-push () {
   local ret=1
+  local -a sign
+  sign=(
+    {yes,true}'\:always,\ and\ fail\ if\ unsupported\ by\ server'
+    {no,false}'\:never'
+    if-asked'\:iff\ supported\ by\ server'
+  )
   # NOTE: For --receive-pack we use _files to complete, even though this will
   # only complete files on the local end, not the remote end.  Still, it may be
   # helpful to get some sort of completion going, perhaps modifying the path
   # later on to match the remote end.
-  _arguments -w -S -s \
+  _arguments -S -s \
     '--all[push all refs under refs/heads/]' \
     '--prune[remove remote branches that do not have a local counterpart]' \
     '--mirror[push all refs under refs/heads/ and refs/tags/ and delete non-existing refs]' \
     '(-n --dry-run)'{-n,--dry-run}'[do everything except actually send the updates]' \
     '--porcelain[produce machine-readable output]' \
-    '--delete[delete all listed refs from the remote repository]' \
+    '(-d --delete)'{-d,--delete}'[delete all listed refs from the remote repository]' \
     '--tags[all tags under refs/tags are pushed]' \
     '--follow-tags[also push missing annotated tags reachable from the pushed refs]' \
     '(--receive-pack --exec)'{--receive-pack=-,--exec=-}'[path to git-receive-pack on remote]:remote git-receive-pack:_files' \
     '(--force-with-lease --no-force-with-lease)*--force-with-lease=-[allow refs that are not ancestors to be updated if current ref matches expected value]::ref and expectation:->lease' \
-    '(--force-with-lease --no-force-with-lease)--no-force-with-lease=-[cancel all previous force-with-lease specifications]' \
+    '(--force-with-lease --no-force-with-lease)--no-force-with-lease[cancel all previous force-with-lease specifications]' \
     '(-f --force)'{-f,--force}'[allow refs that are not ancestors to be updated]' \
     '(:)--repo=[default repository to use]:repository:__git_any_repositories' \
     '(-u --set-upstream)'{-u,--set-upstream}'[add upstream reference for each branch that is up to date or pushed]' \
@@ -1292,9 +1352,19 @@ _git-push () {
     '(-q --quiet -v --verbose --progress)'{-q,--quiet}'[suppress all output]' \
     '(-q --quiet -v --verbose)'{-v,--verbose}'[output additional information]' \
     '(-q --quiet)--progress[output progress information]' \
-    '(--verify)--no-verify[bybass the pre-push hook]' \
-    '--recurse-submodules=[submodule handling]:submodule handling:((check\:"refuse pushing of supermodule if submodule commit cannot be found on the remote"
-                                                                    on-demand\:"push all changed submodules"))' \
+    '(--verify)--no-verify[bypass the pre-push hook]' \
+    '--recurse-submodules=[submodule handling]:submodule handling:((
+        check\:"refuse to push if submodule commit not to be found on remote"
+        on-demand\:"push all changed submodules"
+	only\:"submodules will be recursively pushed while the superproject is left unpushed"
+	no\:"no submodule handling"))' \
+    "(--no-signed --signed)--sign=-[GPG sign the push]::signing enabled:(($^^sign))" \
+    '(--no-signed --sign)--signed[GPG sign the push]' \
+    "(--sign --signed)--no-signed[don't GPG sign the push]" \
+    '--atomic[request atomic transaction on remote side]' \
+    '(-o --push-option)'{-o+,--push-option=}'[transmit string to server to pass to pre/post-receive hooks]:string' \
+    '(-4 --ipv4 -6 --ipv6)'{-4,--ipv4}'[use IPv4 addresses only]' \
+    '(-4 --ipv4 -6 --ipv6)'{-6,--ipv6}'[use IPv6 addresses only]' \
     ': :__git_any_repositories' \
     '*: :__git_ref_specs_pushy' && ret=0
 
@@ -1322,18 +1392,22 @@ _git-rebase () {
       '(--autosquash                )--no-autosquash[do not check for auto-squash boundaries]')
   fi
 
-  _arguments -A '-*' \
-    '(- :)--continue[continue after resolving merge conflict]' \
-    '(- :)--abort[abort current rebase]' \
-    '--keep-empty[keep empty commits in the result]' \
-    '(- :)--skip[skip the current patch]' \
+  _arguments \
+    - actions \
+    '(-)--continue[continue after resolving merge conflict]' \
+    '(-)--abort[abort current rebase]' \
+    '(-)--edit-todo[edit interactive instruction sheet in an editor]' \
+    '(-)--skip[skip the current patch]' \
+    '(-)--quit[abort but keep HEAD where it is]' \
+    - options \
     '(-m --merge)'{-m,--merge}'[use merging strategies to rebase]' \
     '(-S --gpg-sign --no-gpg-sign)'{-S-,--gpg-sign=-}'[GPG-sign the commit]::key id' \
-    '(-S --gpg-sign --no-gpg-sign)--no-gpg-sign[do not GPG-sign the commit]' \
+    "(-S --gpg-sign --no-gpg-sign)--no-gpg-sign[don't GPG-sign the commit]" \
     '*'{-s+,--strategy=}'[use given merge strategy]:merge strategy:__git_merge_strategies' \
-    '*'{-X+,--strategy-option=}'[pass merge-strategy-specific option to merge strategy]' \
+    '*'{-X+,--strategy-option=}'[pass merge-strategy-specific option to merge strategy]:option' \
     '(-q --quiet -v --verbose --stat -n --no-stat)'{-q,--quiet}'[suppress all output]' \
     '(-q --quiet -v --verbose --stat -n --no-stat)'{-v,--verbose}'[output additional information]' \
+    '--rerere-autoupdate[allow rerere to update index with resolved conflicts]' \
     '--no-verify[bypass the pre-rebase hook]' \
     '-C-[ensure that given lines of surrounding context match]: :__git_guard_number "lines of context"' \
     '(-f --force-rebase)'{-f,--force-rebase}'[force rebase even if current branch descends from commit rebasing onto]' \
@@ -1341,13 +1415,15 @@ _git-rebase () {
     '(-i --interactive)--whitespace=-[detect a new or modified line that has whitespace errors]: :__git_apply_whitespace_strategies' \
     '(-i --interactive)--committer-date-is-author-date[use author date as committer date]' \
     '(-i --interactive --ignore-whitespace --whitespace --committer-date-is-author-date)'{-i,--interactive}'[make a list of commits to be rebased and open in $EDITOR]' \
-    '--edit-todo[edit interactive instruction sheet in an editor]' \
     '(-p --preserve-merges --interactive)'{-p,--preserve-merges}'[try to recreate merges instead of ignoring them]' \
     {-x+,--exec=}'[with -i\: append "exec <cmd>" after each line]:command:_command_names -e' \
+    '(-k --keep-empty)'{-k,--keep-empty}'[keep empty commits in the result]' \
     '(1)--root[rebase all reachable commits]' \
     $autosquash_opts \
     '(--autostash --no-autostash)--autostash[stash uncommitted changes before rebasing and apply them afterwards]' \
     '(--autostash --no-autostash)--no-autostash[do not stash uncommitted changes before rebasing and apply them afterwards]' \
+    '--fork-point[use merge-base --fork-point to refine upstream]' \
+    '--ignore-date[use current timestamp for author date]' \
     '--no-ff[cherry-pick all rebased commits with --interactive, otherwise synonymous to --force-rebase]' \
     '--onto=[start new branch with HEAD equal to given revision]:newbase:__git_revisions' \
     ':upstream branch:__git_revisions' \
@@ -1359,16 +1435,15 @@ _git-reset () {
   local curcontext=$curcontext state line ret=1
   typeset -A opt_args
 
-  _arguments -w -C -s \
+  _arguments -C -s -S \
       '(       --mixed --hard --merge --keep -p --patch -- *)--soft[do not touch the index file nor the working tree]' \
       '(--soft         --hard --merge --keep -p --patch -- *)--mixed[reset the index but not the working tree (default)]' \
-      '(--soft         --hard --merge --keep -p --patch -- *)-N[keep --intent-to-add entries in the index]' \
+      '(--soft         --hard --merge --keep -p --patch -- *)'{-N,--intent-to-add}'[record only the fact that removed paths will be added later]' \
       '(--soft --mixed        --merge --keep -p --patch -- *)--hard[match the working tree and index to the given tree]' \
       '(--soft --mixed --hard         --keep -p --patch -- *)--merge[reset out of a conflicted merge]' \
       '(--soft --mixed --hard --merge        -p --patch -- *)--keep[like --hard, but keep local working tree changes]' \
       '(-p --patch)'{-p,--patch}'[select diff hunks to remove from the index]' \
       '(-q --quiet)'{-q,--quiet}'[suppress all output]' \
-      '(- 1)--[start file arguments]' \
       '(--soft --mixed --hard --merge --keep):: :__git_commits' \
       '(--soft --mixed --hard --merge --keep)*:: :->file' && ret=0
 
@@ -1386,17 +1461,20 @@ _git-reset () {
 
 (( $+functions[_git-revert] )) ||
 _git-revert () {
-  _arguments -w -S -s \
+  _arguments -S -s \
     '(- :)--quit[end revert or cherry-pick sequence]' \
     '(- :)--continue[resume revert or cherry-pick sequence]' \
     '(- :)--abort[cancel revert or cherry-pick sequence]' \
     '(-e --edit --no-edit)'{-e,--edit}'[edit the commit before committing the revert]' \
     '(-e --edit --no-edit)--no-edit[do not edit the commit message before committing the revert]' \
     '(-m --mainline)'{-m+,--mainline=}'[pick which parent is mainline]:parent number' \
+    '--rerere-autoupdate[update the index with reused conflict resolution if possible]' \
     '(-n --no-commit)'{-n,--no-commit}'[do not commit the reversion]' \
     '(-s --signoff)'{-s,--signoff}'[add Signed-off-by line at the end of the commit message]' \
+    '--strategy=[use given merge strategy]:merge strategy:__git_merge_strategies' \
+    '*'{-X,--strategy-option=}'[pass merge-strategy-specific option to merge strategy]:option' \
     '(-S --gpg-sign --no-gpg-sign)'{-S-,--gpg-sign=-}'[GPG-sign the commit]::key id' \
-    '(-S --gpg-sign --no-gpg-sign)--no-gpg-sign[do not GPG-sign the commit]' \
+    "(-S --gpg-sign --no-gpg-sign)--no-gpg-sign[don't GPG-sign the commit]" \
     ': :__git_commits'
 }
 
@@ -1405,13 +1483,13 @@ _git-rm () {
   local curcontext=$curcontext state line ret=1
   declare -A opt_args
 
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
     '(-f --force)'{-f,--force}'[override the up-to-date check]' \
     '(-n --dry-run)'{-n,--dry-run}'[do not actually remove the files, just show if they exist in the index]' \
     '-r[allow recursive removal when a leading directory-name is given]' \
     '--cached[only remove files from the index]' \
     '--ignore-unmatch[exit with 0 status even if no files matched]' \
-    '(-q --quiet)'{-q,--quiet}'[suppress all output]' \
+    '(-q --quiet)'{-q,--quiet}"[don't list removed files]" \
     '*:: :->file' && ret=0
 
   case $state in
@@ -1433,7 +1511,7 @@ _git-shortlog () {
 
   # TODO: should take all arguments found in setup_revisions() (probably more
   # or less what git-rev-list takes).
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
     '(: -)'{-h,--help}'[print a short usage message and exit]' \
     '(-n --numbered)'{-n,--numbered}'[sort according to number of commits]' \
     '(-s --summary)'{-s,--summary}'[suppress commit description]' \
@@ -1495,9 +1573,10 @@ _git-show () {
   __git_setup_log_options
   __git_setup_revision_options
 
-  _arguments -w -C -s \
+  _arguments -C -s \
     $log_options \
     $revision_options \
+    '(-q --quiet)'{-q,--quiet}'[suppress diff output]' \
     '*:: :->object' && ret=0
 
   case $state in
@@ -1517,15 +1596,24 @@ _git-show () {
 _git-stash () {
   local curcontext=$curcontext state line ret=1
   declare -A opt_args
+  local -a save_arguments
+
+  save_arguments=(
+    '(-p --patch -a --all -u --include-untracked)'{-p,--patch}'[interactively select hunks from diff between HEAD and working tree to stash]'
+    '(-k --keep-index --no-keep-index)'{-k,--keep-index}'[all changes already added to the index are left intact]'
+    '(-k --keep-index)--no-keep-index[all changes already added to the index are undone]'
+    '(-q --quiet)'{-q,--quiet}'[suppress all output]'
+    '(-p --patch -a --all -u --include-untracked)'{-u,--include-untracked}'[include untracked files]'
+    '(-p --patch -a --all -u --include-untracked)'{-a,--all}'[include ignored files]'
+  )
 
   _arguments -C \
-    ': :->command' \
-    '*:: :->option-or-argument' && ret=0
+    '*::: :->args' \
+    ${save_arguments//#\(/(* } && ret=0
 
-  case $state in
-    (command)
+  if [[ -n $state ]]; then
+    if (( CURRENT == 1 )); then
       local -a commands
-      local -a save_arguments
 
       commands=(
         save:'save your local modifications to a new stash'
@@ -1536,24 +1624,16 @@ _git-stash () {
         branch:'branch off at the commit at which the stash was originally created'
         clear:'remove all the stashed states'
         drop:'remove a single stashed state from the stash list'
-        create:'create a stash without storing it in the ref namespace')
-
-      save_arguments=(
-        '(--keep-index)--patch[interactively select hunks from diff between HEAD and working tree to stash]' \
-          '(             --no-keep-index)--keep-index[all changes already added to the index are left intact]' \
-          '(--keep-index                )--no-keep-index[all changes already added to the index are undone]' \
-          '(-q --quiet)'{-q,--quiet}'[suppress all output]' \
-          '(-u --include-untracked)'{-u,--include-untracked}'[include untracked files]' \
+        create:'create a stash without storing it in the ref namespace'
       )
+
       _describe -t commands command commands && ret=0
-      _arguments -S $save_arguments && ret=0  # "stash" defaults to "save", but without "message".
-      ;;
-    (option-or-argument)
+    else
       curcontext=${curcontext%:*}-$line[1]:
+      compset -n 1
 
       case $line[1] in
         (save)
-          _arguments -S $save_arguments && ret=0
           _arguments -S \
             $save_arguments \
             ':: :_guard "([^-]?#|)" message' && ret=0
@@ -1563,7 +1643,7 @@ _git-stash () {
           __git_setup_log_options
           __git_setup_revision_options
 
-          _arguments -s -w \
+          _arguments -s \
             $log_options \
             $revision_options && ret=0
           ;;
@@ -1571,7 +1651,7 @@ _git-stash () {
           local diff_options
           __git_setup_diff_options
 
-          _arguments -S -s -w \
+          _arguments -S -s \
             $diff_options \
             ':: :__git_stashes' && ret=0
           ;;
@@ -1601,8 +1681,8 @@ _git-stash () {
           _nothing
           ;;
       esac
-      ;;
-  esac
+    fi
+  fi
 
   return ret
 }
@@ -1615,17 +1695,17 @@ _git-status () {
     branch_opts=('(-b --branch)'{-b,--branch}'[show branch and tracking info]')
   fi
 
-  _arguments -w -S -s \
-    '(-s --short)'{-s,--short}'[output in short format]' \
+  _arguments -S -s \
+    '(-s --short --column --no-column)'{-s,--short}'[output in short format]' \
     $branch_opts \
-    '(-s --short)--porcelain[produce machine-readable output]' \
+    '(-s --short)--porcelain=-[produce machine-readable output]:version:(v1)' \
     '(-u --untracked-files)'{-u-,--untracked-files=-}'[show untracked files]::mode:((no\:"show no untracked files" \
                                                                                      normal\:"show untracked files and directories" \
                                                                                      all\:"also show untracked files in untracked directories (default)"))' \
     '--ignore-submodules[ignore changes to submodules]:: :__git_ignore_submodules_whens' \
     '--ignored[show ignored files as well]' \
-    '(--porcelain)-z[use NUL termination on output]' \
-    '(--no-column)--column=-[display in columns]::column.status option:((always\:"always show in columns" never\:"never show in columns" auto\:"show in columns if the output is to the terminal" column\:"fill columns before rows (default)" row\:"fill rows before columns" plain\:"show in one column" dense\:"make unequal size columns to utilize more space" nodense\:"make equal size columns"))' \
+    '(-z --null --column --no-column)'{-z,--null}'[use NUL termination on output]' \
+    '(--no-column -z --null)--column=-[display in columns]::column.status option:((always\:"always show in columns" never\:"never show in columns" auto\:"show in columns if the output is to the terminal" column\:"fill columns before rows (default)" row\:"fill rows before columns" plain\:"show in one column" dense\:"make unequal size columns to utilize more space" nodense\:"make equal size columns"))' \
     '(--column)--no-column[do not display in columns]' \
     '*: :__git_ignore_line_inside_arguments _files'
 }
@@ -1652,6 +1732,7 @@ _git-submodule () {
         update:'update a submodule'
         summary:'show commit summary between given commit and working tree/index'
         foreach:'evaluate shell command in each checked-out submodule'
+	absorbgitdirs:'move the git directory of a submodule into its superprojects'
         sync:'synchronize submodule settings')
 
       _describe -t commands command commands && ret=0
@@ -1687,6 +1768,7 @@ _git-submodule () {
         (deinit)
           _arguments -S \
             '(-f --force)'{-f,--force}'[remove submodule worktree even if local modifications are present]' \
+	    '(*)--all[remove all submodules]' \
             '*: :__git_ignore_line_inside_arguments __git_submodules' && ret=0
           ;;
         (update)
@@ -1694,8 +1776,10 @@ _git-submodule () {
           _arguments -S \
             '(-q --quiet)'{-q,--quiet}'[suppress all output]' \
             '(-N --no-fetch)'{-N,--no-fetch}'[do not fetch new objects from repository]' \
-            '--merge[merge commit recorded in superproject into current branch of submodule]' \
-            '--rebase[rebase current branch onto commit recorded in superproject]' \
+	    '(--merge --rebase)--checkout[checkout commit recorded in the superproject in the submodule on a detached HEAD]' \
+	    '(--checkout --rebase)--merge[merge commit recorded in superproject into current branch of submodule]' \
+	    '(--checkout --merge)--rebase[rebase current branch onto commit recorded in superproject]' \
+	    '--no-recommend-shallow[ignore submodule.<name>.shallow from .gitmodules]' \
             '--reference=[remote repository to clone]: :__git_any_repositories' \
             '--recursive[traverse submodules recursively]' \
             '--remote[use the status of the submodule''s remote-tracking branch]' \
@@ -1706,8 +1790,8 @@ _git-submodule () {
         (summary)
           _arguments -C -A '-*' \
             '(-q --quiet)'{-q,--quiet}'[suppress all output]' \
-            '--cached[use commit stored in the index]' \
-            '--files[compare commit in index with submodule HEAD commit]' \
+            '(--files)--cached[use commit stored in the index]' \
+            '(--cached)--files[compare commit in index with submodule HEAD commit]' \
             '(-n --summary-limit)'{-n,--summary-limit=}'[limit summary size]: :__git_guard_number "limit"' \
             '(-)--[start submodule arguments]' \
             '*:: :->commit-or-submodule' && ret=0
@@ -1737,8 +1821,13 @@ _git-submodule () {
             '(-q --quiet)'{-q,--quiet}'[suppress all output]' \
             '*: :__git_ignore_line_inside_arguments __git_submodules' && ret=0
           ;;
+	(absorbgitdirs)
+	  _arguments -S \
+            '(-q --quiet)'{-q,--quiet}'[suppress all output]' \
+	    '*:path:_directories'
+	;;
         (*)
-          _nothing
+          _default
           ;;
       esac
       ;;
@@ -1747,28 +1836,120 @@ _git-submodule () {
   return ret
 }
 
+(( $+functions[_git-subtree] )) ||
+_git-subtree () {
+  local curcontext="$curcontext" state state_descr line ret=1
+  declare -A opt_args
+
+  # TODO: -P should only complete paths inside the current repository.
+  _arguments -C \
+    '(-q --quiet)'{-q,--quiet}'[suppress progress output]' \
+    '(-P --prefix)'{-P+,--prefix=}'[the path to the subtree in the repository to manipulate]: :_directories' \
+    '-d[show debug messages]' \
+    ': :->command' \
+    '*::: := ->option-or-argument' && ret=0
+
+  case $state in
+    (command)
+      declare -a commands
+
+      commands=(
+        add:'create the subtree by importing its contents'
+        merge:'merge recent changes up to specified commit into the subtree'
+        pull:'fetch from remote repository and merge recent changes into the subtree'
+        push:'does a split and `git push`'
+        split:'extract a new synthetic project history from a subtree')
+
+      _describe -t commands command commands && ret=0
+    ;;
+    (option-or-argument)
+      curcontext=${curcontext%:*}-$line[1]:
+      case $line[1] in
+        (add)
+          _arguments \
+            '(-q --quiet)'{-q,--quiet}'[suppress progress output]' \
+            '(-m --message)'{-m+,--message=}'[use the given message as the commit message for the merge commit]:message' \
+            '(-P --prefix)'{-P+,--prefix=}'[the path to the subtree in the repository to manipulate]: :_directories' \
+            '--squash[import only a single commit from the subproject]' \
+            ': :__git_any_repositories_or_references' \
+            ':: :__git_ref_specs' && ret=0
+          # TODO: the use of __git_ref_specs isn't quite right: it will
+          # complete "foo:bar" values which git-subtree(1) doesn't take.  What
+          # we should complete here is what's on *one* side of the colon in
+          # __git_ref_specs.
+	;;
+        (merge)
+          _arguments -S \
+            '(-q --quiet)'{-q,--quiet}'[suppress progress output]' \
+            '(-P --prefix)'{-P+,--prefix=}'[the path to the subtree in the repository to manipulate]: :_directories' \
+            '(-m --message)'{-m+,--message=}'[use the given message as the commit message for the merge commit]:message' \
+            '--squash[import only a single commit from the subproject]' \
+            ': :__git_references' && ret=0
+	;;
+        (pull)
+          _arguments -S \
+            '(-q --quiet)'{-q,--quiet}'[suppress progress output]' \
+            '(-P --prefix)'{-P+,--prefix=}'[the path to the subtree in the repository to manipulate]: :_directories' \
+            '(-m --message)'{-m+,--message=}'[use the given message as the commit message for the merge commit]:message' \
+            '--squash[import only a single commit from the subproject]' \
+            ': :__git_any_repositories' \
+            ':: :__git_ref_specs' && ret=0
+	;;
+        (push)
+          _arguments -S \
+            '(-q --quiet)'{-q,--quiet}'[suppress progress output]' \
+            '(-P --prefix)'{-P+,--prefix=}'[the path to the subtree in the repository to manipulate]: :_directories' \
+            '(-m --message)'{-m+,--message=}'[use the given message as the commit message for the merge commit]:message' \
+            ': :__git_any_repositories' \
+            ':: :__git_ref_specs' && ret=0
+	;;
+        (split)
+          _arguments -S \
+	    '--annotate[add a prefix to commit message of new commits]:prefix' \
+            '(-q --quiet)'{-q,--quiet}'[suppress progress output]' \
+            '(-P --prefix)'{-P+,--prefix=}'[specify path to the subtree in the repository to manipulate]: :_directories' \
+            '(-b --branch)'{-b,--branch=}'[create a new branch]' \
+            '--onto=[try connecting new tree to an existing one]: :__git_ref_specs' \
+            '(-m --message)'{-m+,--message=}'[specify commit message for the merge]:message' \
+            '--ignore-joins[ignore prior --rejoin commits]' \
+            '--onto=[try connecting new tree to an existing one]: :__git_ref_specs' \
+            '--rejoin[merge the new branch back into HEAD]' \
+            '*: :__git_references' && ret=0
+	;;
+        (*)
+          _default && ret=0
+	;;
+      esac
+    ;;
+  esac
+
+  return ret
+}
+
 (( $+functions[_git-tag] )) ||
 _git-tag () {
   local -a message_opts
 
   if (( words[(I)-[asu]] )); then
     message_opts=(
-      '(   -F)-m+[specify tag message]:message'
-      '(-m   )-F+[read tag message from given file]:message file:_files')
+      '(-m --message -F --file)'{-m+,--message=}'[specify tag message]:message'
+      '(-m --message -F --file)'{-F+,--file=}'[read tag message from given file]:message file:_files'
+    )
   fi
 
-  _arguments -A '-*' \
+  _arguments \
     - creation \
-      '(   -s -u --local-user)-a[create an unsigned, annotated tag]' \
-      '(-a    -u --local-user)-s[create an signed and annotated tag]' \
-      '(-a -s)'{-u+,--local-user=}'[create a tag, annotated and signed with the given key]: :__git_gpg_secret_keys' \
-      '-f[replace existing tag]' \
+      '(-a --annotate -s --sign -u --local-user)'{-a,--annotate}'[create an unsigned, annotated tag]' \
+      '(-a --annotate -s --sign -u --local-user)'{-s,--sign}'[create a signed and annotated tag]' \
+      '(-a --annotate -s --sign)'{-u+,--local-user=}'[create a tag, annotated and signed with the given key]: :__git_gpg_secret_keys' \
+      '(-f --force)'{-f,--force}'[replace existing tag]' \
+      '--create-reflog[create a reflog]' \
       '--cleanup=[cleanup message]:mode:((verbatim\:"no cleanup" whitespace\:"remove leading and trailing whitespace" strip\:"remove leading and trailing whitespace and comments"))' \
       $message_opts \
       ': :__git_tags' \
       ':: :__git_commits' \
     - deletion \
-      '-d[delete tags]' \
+      '(-d --delete)'{-d,--delete}'[delete tags]' \
       '*:: :__git_ignore_line_inside_arguments __git_tags' \
     - listing \
       '-n+[limit line output of annotation]: :__git_guard_number "limit"' \
@@ -1776,15 +1957,82 @@ _git-tag () {
       '(--no-column)--column=-[display tag listing in columns]::column.tag option:((always\:"always show in columns" never\:"never show in columns" auto\:"show in columns if the output is to the terminal" column\:"fill columns before rows (default)" row\:"fill rows before columns" plain\:"show in one column" dense\:"make unequal size columns to utilize more space" nodense\:"make equal size columns"))' \
       '(--column)--no-column[do not display in columns]' \
       '--contains=[only list tags which contain the specified commit]: :__git_commits' \
-      '--points-at=[only list tags of the given object]: :__git_commits' \
+      '--merged=-[print only tags that are merged]:: :__git_commits' \
+      '--no-merged=-[print only tags that are not merged]:: :__git_commits' \
       '--sort=[specify how the tags should be sorted]:mode:((refname\:"lexicographic order"
                                                              version\\\:refname\:"tag names are treated as version numbers"))' \
-      '::pattern' \
+      '--points-at=[only list tags of the given object]: :__git_commits' \
+      '--format=[specify format to use for the output]:format' \
+      '(-i --ignore-case)'{-i,--ignore-case}'[sorting and filtering are case-insensitive]' \
+      ':: :_guard "^-*" pattern' \
     - verification \
-      '-v[verifies gpg signutare of tags]' \
+      '(-v --verify)'{-v,--verify}'[verify gpg signutare of tags]' \
       '*:: :__git_ignore_line_inside_arguments __git_tags'
 }
 
+(( $+functions[_git-worktree] )) ||
+_git-worktree() {
+  local curcontext="$curcontext" state state_descr line ret=1
+  declare -A opt_args
+
+  _arguments -C \
+    ': :->command' \
+    '*::: := ->option-or-argument' && ret=0
+
+  case $state in
+    (command)
+      declare -a commands args
+
+      commands=(
+        add:'create a new working tree'
+        prune:'prune working tree information'
+        list:'list details of each worktree'
+	lock:'prevent a working tree from being pruned'
+	unlock:'allow working tree to be pruned, moved or deleted'
+      )
+
+      _describe -t commands command commands && ret=0
+    ;;
+    (option-or-argument)
+      curcontext=${curcontext%:*}-$line[1]:
+      case $line[1] in
+        (add)
+	  if (( $words[(I)--detach] )); then
+	    args=( ':commit:__git_commits' )
+	  else
+	    args=( ':branch:__git_branch_names' )
+	  fi
+          _arguments \
+	    '(-f --force)'{-f,--force}'[checkout branch even if already checked out in another worktree]' \
+	    '(-B --detach)-b+[create a new branch]: :__git_branch_names' \
+	    '(-b --detach)-B+[create or reset a branch]: :__git_branch_names' \
+	    '(-b -B)--detach[detach HEAD at named commit]' \
+	    '--no-checkout[suppress file checkout in new worktree]' \
+	    ':path:_files' $args && ret=0
+	;;
+        (prune)
+          _arguments \
+	    '(-n --dry-run)'{-n,--dry-run}"[don't remove, show only]" \
+	    '(-v --verbose)'{-v,--verbose}'[report pruned objects]' \
+	    '--expire[expire objects older than specified time]:time' && ret=0
+	;;
+        (list)
+	  _arguments '--porcelain[machine-readable output]' && ret=0
+	;;
+	(lock)
+	  _arguments -C '--reason=[specify reason for locking]:reason' ': :->worktrees' && ret=0
+	  [[ -z $state ]] && return ret
+	;&
+	(unlock)
+	  _wanted directories expl 'working tree' compadd -S ' ' -f -M 'r:|/=* r:|=*' \
+	      ${${(M)${(f)"$(_call_program directories git worktree list --porcelain)"}:#worktree*}#* }
+	;;
+      esac
+    ;;
+  esac
+  return ret
+}
+
 (( $+functions[_gitk] )) ||
 _gitk () {
   _git-log
@@ -1800,7 +2048,7 @@ _tig () {
 (( $+functions[_git-config] )) ||
 _git-config () {
   local name_arg value_arg
-  local curcontext=$curcontext state line expl ret=1
+  local curcontext=$curcontext state line ret=1
   declare -A opt_args
 
   if (( words[(I)--get-regexp] )); then
@@ -1823,41 +2071,77 @@ _git-config () {
     value_arg=': :->value'
   fi
 
-  _arguments -w -C -S -s \
-    '(         --system --local -f --file)--global[use user-global config file]' \
-    '(--global          --local -f --file)--system[use system-wide config file]' \
-    '(--global --system         -f --file)--local[use local config file]' \
-    '(--global --system -f --file)'{-f+,--file=}'[use given config file]:config file:_files' \
+  _arguments -C -S -s \
+    '(         --system --local -f --file --blob)--global[use user-global config file]' \
+    '(--global          --local -f --file --blob)--system[use system-wide config file]' \
+    '(--global --system         -f --file --blob)--local[use local config file]' \
+    '(--global --system --local           --blob)'{-f+,--file=}'[use given config file]:config file:_files' \
+    '(--global --system --local -f --file)--blob=[read config from given blob object]:blob:__git_blobs' \
     '(       --int --bool-or-int --path)--bool[setting is a boolean]' \
     '(--bool       --bool-or-int --path)--int[setting is an integer]' \
     '(--bool --int               --path)--bool-or-int[setting is an integer]' \
     '(--bool --int --bool-or-int       )--path[setting is a path]' \
     '(-z --null)'{-z,--null}'[end values with NUL and newline between key and value]' \
+    '(--get --get-all --get-urlmatch --replace-all --add --unset --unset-all --rename-section --remove-section -e --edit --get-color --get-colorbool)--name-only[show variable names only]' \
+    '(--includes)'--no-includes"[don't respect \"include.*\" directives]" \
+    '(--no-includes)'--includes'[respect "include.*" directives in config files when looking up values]' \
+    '(--global --system --local -f --file --blob --get-urlmatch --replace-all --add --unset --unset-all --rename-section --remove-section -e --edit --get-color --get-colorbool)--show-origin[show origin of config]' \
     $name_arg \
     $value_arg \
     '::value regex' \
     - '(actions)' \
-      '(-z --null)--replace-all[replace all values of the given key]' \
-      '(3 -z --null)--add[add new value without altering any existing ones]' \
-      '(2)--get[get the first matching value of the key]' \
-      '(2)--get-all[get all matching values of the key]' \
-      '(3 --bool --int --bool-or-int --path -z --null)--remove-section[remove the given section]' \
-      '(3 --bool --int --bool-or-int --path -z --null)--rename-section[rename the given section]'  \
-      '(2 --bool --int --bool-or-int --path -z --null)--unset[remove the first matching value of the key]' \
-      '(2 --bool --int --bool-or-int --path -z --null)--unset-all[remove all matching values of the key]' \
-      '(: --bool --int --bool-or-int --path)'{-l,--list}'[list all variables set in config file]' \
+      '(2 --name-only)--get[get the first matching value of the key]' \
+      '(2 --name-only)--get-all[get all matching values of the key]' \
       '(2)--get-regexp[like "--get-all", but interpret "name" as a regular expression]' \
-      '(2 3 --bool --int --bool-or-int --path -z --null)--get-colorbool[check if color should be used]: :->gettable-colorbool-option' \
-      '(2 3 --bool --int --bool-or-int --path -z --null)--get-color[find color setting]: :->gettable-color-option' \
-      '(-e --edit --bool --int --bool-or-int --path -z --null)'{-e,--edit}'[open config file for editing]' \
-      '(--no-includes)'--includes'[respect "include.*" directives in config files when looking up values]' \
-      '(--includes)'--no-includes'[do not respect "include.*" directives]' && ret=0
+      '(--name-only --show-origin)--get-urlmatch[get value specific for the URL]' \
+      '(-z --null --name-only --show-origin)--replace-all[replace all values of the given key]' \
+      '(3 -z --null --name-only --show-origin)--add[add new value without altering any existing ones]' \
+      '(2 --bool --int --bool-or-int --path -z --null --name-only --show-origin)--unset[remove the first matching value of the key]' \
+      '(2 --bool --int --bool-or-int --path -z --null --name-only --show-origin)--unset-all[remove all matching values of the key]' \
+      '(3 --bool --int --bool-or-int --path -z --null --name-only --show-origin)--rename-section[rename the given section]'  \
+      '(3 --bool --int --bool-or-int --path -z --null --name-only --show-origin)--remove-section[remove the given section]' \
+      '(: --bool --int --bool-or-int --path)'{-l,--list}'[list all variables set in config file]' \
+      '(-e --edit --bool --int --bool-or-int --path -z --null --name-only --show-origin)'{-e,--edit}'[open config file for editing]' \
+      '(2 3 --bool --int --bool-or-int --path -z --null --name-only --show-origin)--get-color[find color setting]: :->gettable-color-option' \
+      '(2 3 --bool --int --bool-or-int --path -z --null --name-only --show-origin)--get-colorbool[check if color should be used]: :->gettable-colorbool-option' && ret=0
+  __git_config_option-or-value "$@" && ret=0
+  return ret
+}
+
+(( $+functions[__git_config_option] )) ||
+__git_config_option () {
+  local -A opt_args=()
+  local -a line=( ${words[CURRENT]%%=*} )
+  local state=option
+  __git_config_option-or-value "$@"
+}
+
+(( $+functions[__git_config_value] )) ||
+__git_config_value () {
+  local -A opt_args=()
+  local -a line=( ${words[CURRENT]%%=*} ${words[CURRENT]#*=} )
+  local state=value
+  __git_config_option-or-value "$@"
+}
+
+# Helper to _git-config().  May be called by other functions, too, provided
+# that The caller has set $line, $state, and $opt_args as _git-config() would
+# set them:
+# 
+# - set $line[1] to the option name being completed (even if completing an
+#   option value).
+# - set $opt_args to git-config(1) options, as set by _arguments in
+#   _git-config().
+# - set $state as _arguments in _git-config() would set it.
+(( $+functions[__git_config_option-or-value] )) ||
+__git_config_option-or-value () {
+  local expl ret
 
   # TODO: Add support for merge.*. (merge driver), diff.*. (diff driver), and filter.*. (filter driver) options
   # (see gitattributes(5)).
   # TODO: .path options should take absolute paths.
-  declare -a git_options_static
-  git_options_static=(
+  declare -a git_options
+  git_options=(
     advice.pushNonFastForward:'show advice when git push refuses non-fast-forward refs::->bool:true'
     advice.pushUpdateRejected:'combined setting for advice.push*::->bool:true'
     advice.pushNonFFCurrent:'show advice when git push fails due to a non-fast-forward update to the current branch::->bool:true'
@@ -2334,7 +2618,7 @@ _git-config () {
     'svn-remote.*.pushurl:URL to push to::_urls'
     'svn-remote.*.branches:branch mappings:branch mapping:->string'
     'svn-remote.*.tags:tag mappings:tag mapping:->string'
-    tag.sort:'Default sorting method:->string'
+    tag.sort:'default sorting method:sorting method:->string'
     'tar.*.command:specify a shell command through which the tar output generated by git archive should be piped::_cmdstring'
     'tar.*.remote:enable <format> for use by remote clients via git-upload-archive::->bool'
     tar.umask:'umask to apply::->umask'
@@ -2346,11 +2630,49 @@ _git-config () {
     uploadarchive.allowUnreachable:'allow git-upload-archive to accept an archive requests that ask for unreachable objects::->bool:false'
     'url.*.insteadOf:string to start URLs with:prefix:->string'
     'url.*.pushInsteadOf:string to start URLs to push to with:prefix:->string'
-    user.email:'email address used for commits::_email_addresses'
+    user.email:'email address used for commits::_email_addresses -c'
     user.name:'full name used for commits:name:->string'
     user.signingkey:'default GPG key to use when creating signed tags::__git_gpg_secret_keys'
     web.browser:'web browser to use::__git_browsers')
 
+  declare -a git_present_options # 'present' is an adjective
+  git_present_options=(
+    ${${${(0)"$(_call_program gettable-options git config -z --list)"}%%$'\n'*}//:/\\:}
+  )
+
+  # Add to $git_options options from the config file that aren't already in $git_options.
+  () {
+    local -a -U sections_that_permit_arbitrary_subsection_names=(
+      alias
+      pager
+      pretty
+      remotes
+      ${(u)${(M)${git_options%%:*}:#*[.][*][.]*}%%.*}
+    )
+    local key
+    for key in $git_present_options ; do
+      if (( ${+git_options[(r)(#i)${(b)key}:*]} )); then
+        # $key is already in git_options
+        continue 
+      elif (( ${+sections_that_permit_arbitrary_subsection_names[(r)${(b)key%%.*}]} )); then
+        if [[ $key == *.*.* ]]; then
+          # If $key isn't an instance of a known foo.*.bar:baz $git_options entry...
+          if ! (( ${+git_options[(r)(#i)${(b)key%%.*}.[*].${(b)key##*.}:*]} )); then
+            # ... then add it.
+            git_options+="${key}:unknown option name::->unknown"
+          fi
+        else
+          # $key is of the form "foo.bar" where 'foo' is known
+          # No need to add it; "foo.<TAB>' will find 'bar' via another codepath later
+          # ### TODO: that "other codepath" will probably run git config -z again, redundantly.
+          continue
+        fi
+      else
+        git_options+="${key}:unknown option name::->unknown"
+      fi
+    done
+  }
+
   case $state in
     (section)
       __git_config_sections -b '(|)' '^' section-names 'section name' $* && ret=0
@@ -2371,8 +2693,10 @@ _git-config () {
 
       if compset -P '[^.]##.*.'; then
         declare -a match mbegin mend
-        options+=(${${${${(M)git_options_static:#(#i)${IPREFIX}[^.:]##:*}#(#i)${IPREFIX}}/#(#b)([^:]##:)([^\\:]#(\\?[^\\:]#)#:[^\\:]#(\\?[^\\:]#)#:->bool)/$match[1]whether or not to $match[2]}/#(#b)([^:]##:([^\\:]#(\\?[^\\:]#)#))*/$match[1]})
-        options+=(${${${${(M)git_options_static:#(#i)${IPREFIX%%.*}.\*.[^.:]##:*}#(#i)${IPREFIX%%.*}.\*.}/#(#b)([^:]##:)([^\\:]#(\\?[^\\:]#)#:[^\\:]#(\\?[^\\:]#)#:->bool)/$match[1]whether or not to $match[2]}/#(#b)([^:]##:([^\\:]#(\\?[^\\:]#)#))*/$match[1]})
+        # When completing 'remote.foo.<TAB>', offer 'bar' if $git_options contains 'remote.foo.bar'.
+        options+=(${${${${(M)git_options:#(#i)${IPREFIX}[^.:]##:*}#(#i)${IPREFIX}}/#(#b)([^:]##:)([^\\:]#(\\?[^\\:]#)#:[^\\:]#(\\?[^\\:]#)#:->bool)/$match[1]whether or not to $match[2]}/#(#b)([^:]##:([^\\:]#(\\?[^\\:]#)#))*/$match[1]})
+        # When completing 'remote.foo.<TAB>', offer 'bar' if $git_options contains 'remote.*.bar'.
+        options+=(${${${${(M)git_options:#(#i)${IPREFIX%%.*}.\*.[^.:]##:*}#(#i)${IPREFIX%%.*}.\*.}/#(#b)([^:]##:)([^\\:]#(\\?[^\\:]#)#:[^\\:]#(\\?[^\\:]#)#:->bool)/$match[1]whether or not to $match[2]}/#(#b)([^:]##:([^\\:]#(\\?[^\\:]#)#))*/$match[1]})
 
         declare -a labels
         labels=(
@@ -2406,8 +2730,8 @@ _git-config () {
       elif compset -P '[^.]##.'; then
         local opt
         declare -a match mbegin mend
-        for opt in ${${${${(M)git_options_static:#(#i)${IPREFIX}[^.:]##:*}#(#i)${IPREFIX}}/#(#b)([^:]##:)([^\\:]#(\\?[^\\:]#)#:[^\\:]#(\\?[^\\:]#)#:->bool)/$match[1]whether or not to $match[2]}/#(#b)([^:]##:([^\\:]#(\\?[^\\:]#)#))*/$match[1]}; do
-          if (( ${git_options_static[(I)${opt%%:*}.*]} )); then
+        for opt in ${${${${(M)git_options:#(#i)${IPREFIX}[^.:]##:*}#(#i)${IPREFIX}}/#(#b)([^:]##:)([^\\:]#(\\?[^\\:]#)#:[^\\:]#(\\?[^\\:]#)#:->bool)/$match[1]whether or not to $match[2]}/#(#b)([^:]##:([^\\:]#(\\?[^\\:]#)#))*/$match[1]}; do
+          if (( ${git_options[(I)${opt%%:*}.*]} )); then
             sections_and_options+=$opt
           else
             options+=$opt
@@ -2420,6 +2744,7 @@ _git-config () {
           'gitcvs.ext:ext-connection-method-specific options'
           'gitcvs.pserver:pserver-connection-method-specific options'
           'notes.rewrite:commands to copy notes from original for when rewriting commits')
+        # Set $sections to the applicable subsection names (e.g., 'decorate:...' if $IPREFIX == "color.")
         sections+=(${${(M)subsections:#${IPREFIX}[^.:]##(.|):*}#${IPREFIX}})
 
         # TODO: Is it fine to use functions like this before _describe below,
@@ -2428,6 +2753,8 @@ _git-config () {
         # following functions don't generate any output in the case of
         # multi-level options.
         case $IPREFIX in
+          # Note: If you add a branch to this 'case' statement,
+          # update $sections_that_permit_arbitrary_subsection_names.
           (alias.)
             __git_aliases && ret=0
             ;;
@@ -2485,6 +2812,10 @@ _git-config () {
           (svn-remote.)
             __git_svn-remotes -S . && ret=0
             ;;
+          (*.)
+            local -a existing_subsections=( ${${${(M)git_present_options:#${IPREFIX}*.*}#${IPREFIX}}%.*} )
+            _describe -t existing-subsections "existing subsections" existing_subsections -S . && ret=0
+            ;;
         esac
       else
         sections=(
@@ -2547,6 +2878,13 @@ _git-config () {
           web:'web options'
           svn:'git svn options'
           svn-remote:'git svn remotes')
+        () {
+          local i
+          for i in ${(u)git_present_options%%.*}; do
+            (( ${+sections[(r)(#i)${(b)i}:*]} )) ||
+              sections+="${i}:unknown section name"
+          done
+        }
       fi
 
       # TODO: Add equivalent of -M 'r:|.=* r:|=*' here so that we can complete
@@ -2554,11 +2892,10 @@ _git-config () {
       _describe -t option-names $label \
         sections -M 'm:{[:lower:][:upper:]}={[:upper:][:lower:]}' -S . -- \
         sections_and_options -M 'm:{[:lower:][:upper:]}={[:upper:][:lower:]}' -qS . -- \
-        options -M 'm:{[:lower:][:upper:]}={[:upper:][:lower:]}' && ret=0
+        options -M 'm:{[:lower:][:upper:]}={[:upper:][:lower:]}' "$@" && ret=0
       ;;
     (gettable-option)
-      _wanted git-options expl option compadd -M 'r:|.=* r:|=*' - \
-        ${${${(0)"$(_call_program gettable-options git config -z --list)"}%%$'\n'*}//:/\\:} && ret=0
+      _wanted git-options expl option compadd -M 'r:|.=* r:|=*' -a - git_present_options && ret=0
       ;;
     (gettable-colorbool-option)
       __git_config_sections -b '(|)' -a '(|)' '^color\.[^.]+$' gettable-colorbool-options option && ret=0
@@ -2567,7 +2904,7 @@ _git-config () {
       __git_config_sections -b '(|)' -a '(|)' '^color\.[^.]+\..*$' gettable-color-options option && ret=0
       ;;
     (value)
-      local current=${${(0)"$(_call_program current "git config $opt_args[(I)--system|--global|--local] ${(kv)opt_args[(I)-f|--file]} -z --get '$line[1]'")"}#*$'\n'}
+      local current=${${(0)"$(_call_program current "git config $opt_args[(I)--system|--global|--local]" ${(kv)opt_args[(I)-f|--file]} "-z --get ${(q)line[1]}")"}#*$'\n'}
       case $line[1] in
         (alias.*)
           if [[ -n $current ]]; then
@@ -2595,12 +2932,17 @@ _git-config () {
           ;;
       esac
       local z=$'\0'
+
+      # Set $parts to the $git_options element that corresponds to $line[1]
+      # (the option name whose value is currently being completed).  The elements
+      # of $parts are the colon-separated elements of the $git_options element.
       declare -a parts
-      parts=("${(S@0)${git_options_static[(r)(#i)${line[1]}:*]}//(#b)(*[^\\]|):/$match[1]$z}")
+      parts=("${(S@0)${git_options[(r)(#i)${line[1]}:*]}//(#b)(*[^\\]|):/$match[1]$z}")
       if (( $#parts < 2 )) && [[ $line[1] == [^.]##.*.[^.]## ]]; then
-        parts=("${(S@0)${git_options_static[(r)(#i)${line[1]%%.*}.\*.${line[1]##*.}:*]}//(#b)(*[^\\]|):/$match[1]$z}")
+        parts=("${(S@0)${git_options[(r)(#i)${line[1]%%.*}.\*.${line[1]##*.}:*]}//(#b)(*[^\\]|):/$match[1]$z}")
       fi
-      (( $#parts > 0 )) || return ret
+
+      (( $#parts >= 4 )) || return ret
       case $parts[4] in
         ('->'*)
           case ${parts[4]#->} in
@@ -2927,6 +3269,10 @@ _git-config () {
                 'values:value:(user)' \
                 'umasks: :__git_guard_number umask' && ret=0
               ;;
+            (unknown)
+              _message "$line[1] option value"
+              compadd - $current && ret=0
+              ;;
           esac
           ;;
         (*)
@@ -2946,7 +3292,7 @@ _git-config () {
 (( $+functions[_git-fast-export] )) ||
 _git-fast-export () {
   # TODO: * should be git-rev-arg and git-rev-list arguments.
-  _arguments -w -S -s \
+  _arguments -S -s \
     '--progress=[insert progress statements]: :__git_guard_number interval' \
     '--signed-tags=[specify how to handle signed tags]:action:((verbatim\:"silently export"
                                                                 warn\:"export, but warn"
@@ -2965,6 +3311,9 @@ _git-fast-export () {
     '--use-done-feature[start with a "feature done" stanza, and terminate with a "done" command]' \
     '--no-data[do not output blocb objects, instead referring to them via their SHA-1 hash]' \
     '--full-tree[output full tree for each commit]' \
+    '(--get --get-all)--name-only[show variable names only]' \
+    '*--refspec=[apply refspec to exported refs]:refspec' \
+    '--anonymize[anonymize output]' \
     '*: :__git_commit_ranges'
 }
 
@@ -2977,8 +3326,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' \
+    '--big-file-threshold=-[maximum size of blob to create deltas for]: : __git_guard_bytes' \
     '--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' \
@@ -3020,6 +3369,7 @@ _git-mergetool () {
     '--tool-help[print a list of merge tools that may be used with "--tool"]' \
     '(-y --no-prompt --prompt)'{-y,--no-prompt}'[do not prompt before invocation of merge resolution program]' \
     '(-y --no-prompt)--prompt[prompt before invocation of merge resolution program]' \
+    '-O-[process files in the order specified in file]:order file:_files' \
     '*:conflicted file:_files'
 }
 
@@ -3037,7 +3387,8 @@ _git-prune () {
   _arguments -S \
     '(-n --dry-run)'{-n,--dry-run}'[do not remove anything; just report what would be removed]' \
     '(-v --verbose)'{-v,--verbose}'[report all removed objects]' \
-    '--expire[only expire loose objects older than given date]: :__git_datetimes' \
+    '--progress[show progress]' \
+    '--expire=[only expire loose objects older than specified date]: :__git_datetimes' \
     '*:: :__git_heads'
 }
 
@@ -3068,7 +3419,9 @@ _git-reflog () {
         commands=(
           'expire:prune old reflog entries'
           'delete:delete entries from reflog'
-          'show:show log of ref')
+          'show:show log of ref'
+          'exists:check whether a ref has a reflog'
+	)
 
         _alternative \
           'commands:: _describe -t commands command commands' \
@@ -3096,6 +3449,7 @@ _git-reflog () {
               '(-n --dry-run)'{-n,--dry-run}'[undocumented]' \
               '--updateref[update ref with SHA-1 of top reflog entry after expiring or deleting]' \
               '--rewrite[adjust reflog entries to ensure old SHA-1 points to new SHA-1 of previous entry after expiring or deleting]' \
+              '--verbose[output additional information]' \
               '*:: :->reflog-entry' && ret=0
 
             case $state in
@@ -3110,6 +3464,9 @@ _git-reflog () {
               $revision_options \
               ':: :__git_references' && ret=0
             ;;
+	  (exists)
+	    __git_references && ret=0
+	    ;;
         esac
     esac
 
@@ -3143,14 +3500,16 @@ _git-remote () {
 
       commands=(
         'add:add a new remote'
+	'get-url:retrieves the URLs for a remote'
         'rename:rename a remote and update all associated tracking branches'
-        'rm:remove a remote and all associated tracking branches'
+	{rm,remove}':remove a remote and all associated tracking branches'
         'set-head:set or delete default branch for a remote'
         'set-branches:change list of branches tracked by a remote'
         'set-url:change URL for a remote'
         'show:show information about a given remote'
         'prune:delete all stale tracking branches for a remote'
-        'update:fetch updates for a set of remotes')
+        'update:fetch updates for a set of remotes'
+      )
 
       _describe -t commands command commands && ret=0
       ;;
@@ -3160,13 +3519,13 @@ _git-remote () {
       case $line[1] in
         (add)
           # TODO: -t and --track should really list branches at url.
-          _arguments -w -S -s \
+          _arguments -S -s \
             '(-f --fetch)'{-f,--fetch}'[run git fetch on new remote after it has been created]' \
             '(       --no-tags)--tags[tell git fetch to import every tag from remote repository]' \
             '(--tags          )--no-tags[tell git fetch to not import every tag from remote repository]' \
             '*'{-t,--track=}'[track given branch instead of default glob refspec]: :__git_branch_names' \
             '(-m --master)'{-m,--master=}'[set HEAD of remote to point to given master branch]: :__git_branch_names' \
-            '--mirror[do not use separate remotes]' \
+	    '--mirror[do not use separate remotes]::mirror type:(fetch pull)' \
             ':name:__git_remotes' \
             ':repository:->repository' && ret=0
 	  case $state in
@@ -3178,18 +3537,20 @@ _git-remote () {
 	    ;;
 	  esac
           ;;
+        (get-url)
+          _arguments -S -s \
+            '--push[list push URL instead of fetch URL]' \
+            '--all[list all URLs for the remote]' \
+            ': :__git_remotes' && ret=0
+          ;;
         (rename)
           _arguments \
             ':old name:__git_remotes' \
             ':new name:__git_remotes' && ret=0
           ;;
-        (rm)
-          _arguments \
-            ': :__git_remotes' && ret=0
-          ;;
         (set-head)
           # TODO: Second argument should be a branch at url for remote.
-          _arguments -w -S -s \
+          _arguments -S -s \
             '(- 2)'{-d,--delete}'[delete default branch]' \
             '(- 2)'{-a,--auto}'[determine default branch automatically]' \
             ': :__git_remotes' \
@@ -3197,14 +3558,14 @@ _git-remote () {
           ;;
         (set-branches)
           # TODO: Branches should be at url.
-          _arguments -w -S -s \
+          _arguments -S -s \
             '--add[add branches to those already defined]' \
             ': :__git_remotes' \
             '*: :__git_branch_names' && ret=0
           ;;
         (set-url)
           # TODO: Old URL should be one of those defined for the remote.
-          _arguments -w -S -s \
+          _arguments -S -s \
             '(3)--push[manipulate push URLs instead of fetch URLs]' \
             '--add[add URL to those already defined]' \
             '(3)--delete[delete all matching URLs]' \
@@ -3213,20 +3574,23 @@ _git-remote () {
             ':old url:_urls' && ret=0
           ;;
         (show)
-          _arguments -w -S -s \
+          _arguments -S \
             '-n[do not contact the remote for a list of branches]' \
             '*: :__git_remotes' && ret=0
           ;;
         (prune)
-          _arguments -w -S -s \
+          _arguments -S -s \
             '(-n --dry-run)'{-n,--dry-run}'[do not actually prune, only list what would be done]' \
             '*: :__git_remotes' && ret=0
           ;;
         (update)
-          _arguments -w -S -s \
+          _arguments -S -s \
             '(-p --prune)'{-p,--prune}'[prune all updated remotes]' \
             ': :__git_remote-groups' && ret=0
           ;;
+	(*) # rm, remove and fallback for any new subcommands
+	  __git_remotes && ret=0
+	  ;;
       esac
       ;;
   esac
@@ -3237,32 +3601,40 @@ _git-remote () {
 (( $+functions[_git-repack] )) ||
 _git-repack () {
   # TODO: --quiet is undocumented.
-  _arguments -w -S -s \
-    '(-A)-a[pack all objects into a single pack]' \
-    '(-a)-A[pack all objects into a single pack, but unreachable objects become loose]' \
+  _arguments -s \
+    '(-A --unpack-unreachable)-a[pack all objects into a single pack]' \
+    '(-a -k --keep-unreachable)-A[pack all objects into a single pack, but unreachable objects become loose]' \
     '-d[remove redundant packs after packing]' \
-    '-l[pass --local option to git pack-objects]' \
+    "--unpack-unreachable=[with -A, don't loosen objects older than specified date]:date" \
     '-f[pass --no-reuse-delta option to git pack-objects]' \
     '-F[pass --no-reuse-object option to git pack-objects]' \
+    "-n[don't update server information]" \
     '(-q --quiet)'{-q,--quiet}'[pass -q option to git pack-objects]' \
-    '-n[do not update server information]' \
-    '--window=-[number of objects to consider when doing delta compression]: :__git_guard_number "number of objects"' \
-    '--depth=-[maximum delta depth]: :__git_guard_number "maximum delta depth"' \
-    '--window-memory=-[scale window size dynamically to not use more than N bytes of memory]: :__git_guard_bytes' \
-    '--max-pack-size=-[maximum size of each output packfile]:maximum pack size:__git_guard_bytes'
+    '(-l --local)'{-l,--local}'[pass --local option to git pack-objects]' \
+    '(-b --write-bitmap-index)'{-b,--write-bitmap-index}'[write a bitmap index]' \
+    "--unpack-unreachable=[with -A, don't loosen objects older than specified time]:time" \
+    '(-k --keep-unreachable)'{-k,--keep-unreachable}'[with -a, repack unreachable objects]' \
+    '--window=[number of objects to consider when doing delta compression]:number of objects' \
+    '--window-memory=[scale window size dynamically to not use more than specified amount of memory]: : __git_guard_bytes' \
+    '--depth=[maximum delta depth]:maximum delta depth' \
+    '--max-pack-size=-[maximum size of each output packfile]: : __git_guard_bytes "maximum pack size"' \
+    '--pack-kept-objects[repack objects in packs marked with .keep]'
 }
 
 (( $+functions[_git-replace] )) ||
 _git-replace () {
-  _arguments -w -S -s \
-    '--edit[edit existing object as base a starting point]' \
-    '--graft[rewrite the parents of a commit]' \
-    '(- *)-f[overwrite existing replace ref]' \
-    '(- 2)-d[delete existing replace refs]' \
-    '(- : *)-l[list replace refs]:pattern' \
+  _arguments -S -s \
+    '(-d --delete -l --list -g --graft *)'{-f,--force}'[overwrite existing replace ref]' \
+    "(-d --delete -l --list -g --graft 2 *)--raw[don't pretty-print contents for --edit]" \
+    '(-d --delete -e --edit -g --graft --raw)--format=[use specified format]:format:(short medium long)' \
     ': :__git_objects' \
     ':replacement:__git_objects' \
-    '*: :__git_objects'
+    '*: :__git_objects' \
+    - '(actions)' \
+    '(: * --raw -f --force)'{-l,--list}'[list replace refs]:pattern' \
+    {-d,--delete}'[delete existing replace refs]:*:replacement:__git_objects' \
+    '(* 2 --format)'{-e,--edit}'[edit existing object and replace it with the new one]' \
+    '(--raw --format)'{-g,--graft}'[rewrite the parents of a commit]'
 }
 
 # Ancillary Commands (Interrogators)
@@ -3276,10 +3648,11 @@ _git-blame () {
   __git_setup_revision_options
 
   # TODO: Not sure about __git_cached_files.
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
     '-b[show blank SHA-1 for boundary commits]' \
     '--root[do not treat root commits as boundaries]' \
     '--show-stats[include additional statistics at the end of blame output]' \
+    '--progress[force progress reporting]' \
     '*-L[annotate only the given line range]: :->line-range' \
     '-l[show long rev]' \
     '-t[show raw timestamp]' \
@@ -3297,6 +3670,7 @@ _git-blame () {
     '(-n --show-number)'{-n,--show-number}'[show the line number in the original commit]' \
     '-s[suppress author name and timestamp]' \
     '-w[ignore whitespace when finding lines]' \
+    '--indent-heuristic[use indent-based heuristic to improve diffs]' \
     $revision_options \
     ':: :__git_revisions' \
     ': :__git_cached_files' && ret=0
@@ -3334,7 +3708,7 @@ _git-cherry () {
 _git-count-objects () {
   _arguments \
     '(-v --verbose)'{-v,--verbose}'[also report number of in-pack objects and objects that can be removed]' \
-    {-H,--human-readable}'[Print sizes in human readable format]'
+    {-H,--human-readable}'[print sizes in human readable format]'
 }
 
 (( $+functions[_git-difftool] )) ||
@@ -3349,13 +3723,14 @@ _git-difftool () {
     '--tool-help[print a list of diff tools that may be used with --tool]' \
     '(--symlinks)--no-symlinks[make copies of instead of symlinks to the working tree]' \
     '(---no-symlinks)--symlinks[make symlinks to instead of copies of the working tree]' \
-    '(-g --gui)'{-g,--gui}'[use diff.guitool instead of diff.tool]'
+    '(-g --gui)'{-g,--gui}'[use diff.guitool instead of diff.tool]' \
+    '--trust-exit-code[make git-difftool exit when diff tool returns a non-zero exit code]'
 }
 
 (( $+functions[_git-fsck] )) ||
 _git-fsck () {
   # TODO: -v is undocumented.
-  _arguments -w -S -s \
+  _arguments -S -s \
     '--unreachable[show objects that are unreferenced in the object database]' \
     '(--dangling --no-dangling)--dangling[print dangling objects (default)]' \
     '(--dangling --no-dangling)--no-dangling[do not print dangling objects]' \
@@ -3364,9 +3739,12 @@ _git-fsck () {
     '--cache[consider objects recorded in the index as head nodes for reachability traces]' \
     '--no-reflogs[do not consider commits referenced only by reflog entries to be reachable]' \
     '--full[check all object directories]' \
+    '--connectivity-only[check only connectivity]' \
     '--strict[do strict checking]' \
     '(-v --verbose)'{-v,--verbose}'[output additional information]' \
     '--lost-found[write dangling objects into .git/lost-found]' \
+    '--progress[show progress]' \
+    '--name-objects[show verbose names for reachable objects]' \
     '*: :__git_objects'
 }
 
@@ -3377,13 +3755,13 @@ _git-get-tar-commit-id () {
 
 (( $+functions[_git-help] )) ||
 _git-help () {
-  _arguments -w -S -s \
+  _arguments -S -s \
     '(         -g --guides -i --info -m --man -w --web)'{-a,--all}'[show all available commands]' \
     '(-a --all -g --guides           -m --man -w --web)'{-i,--info}'[show all available commands]' \
     '(-a --all -g --guides -i --info          -w --web)'{-m,--man}'[show all available commands]' \
     '(-a --all -g --guides -i --info -m --man         )'{-w,--web}'[show all available commands]' \
     '(-g --guides)'{-g,--guides}'[prints a list of useful guides on the standard output]' \
-    ': :_git_commands'
+    ': : _alternative commands:command:_git_commands "guides:git guides:(attributes glossary ignore modules revisions tutorial workflows)"'
 }
 
 (( $+functions[_git-instaweb] )) ||
@@ -3391,7 +3769,7 @@ _git-instaweb () {
   local curcontext=$curcontext state line ret=1
   declare -A opt_args
 
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
     '(-l --local)'{-l,--local}'[bind the web server to 127.0.0.1]' \
     '(-d --httpd)'{-d,--httpd=}'[HTTP-daemon command-line that will be executed]:command line' \
     '(-m --module-path)'{-m,--module-path=}'[module path for the Apache HTTP-daemon]:module path:_directories' \
@@ -3432,7 +3810,7 @@ _git-rerere () {
   declare -A opt_args
 
   # TODO: --rerere-autoupdate is undocumented.
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
     '--rerere-autoupdate[register clean resolutions in index]' \
     ': :->command' && ret=0
 
@@ -3474,7 +3852,7 @@ _git-rev-parse () {
       _message 'argument'
     else
       # TODO: Parse option specification?
-      _arguments -w -S -s \
+      _arguments -S -s \
         '(- *)'{-h,--help}'[display usage]' \
         '--keep-dashdash[do not skip first -- option]' \
         '--stop-at-non-option[stop parsing options at first non-option argument]' \
@@ -3531,7 +3909,7 @@ _git-show-branch () {
   local curcontext=$curcontext state line ret=1
   declare -A opt_args
 
-  _arguments -w -C -S -s -A '-*' \
+  _arguments -C -S -s -A '-*' \
     '(--more        --merge-base --independent)--list[do not show any ancestry (--more=-1)]' \
     - branches \
       '(-r --remotes -a --all)'{-r,--remotes}'[show remote-tracking branches]' \
@@ -3570,15 +3948,18 @@ _git-show-branch () {
 
 (( $+functions[_git-verify-commit] )) ||
 _git-verify-commit () {
-  _arguments -w -S -s \
-    '(-v --verbose)'{-v,--verbose}'[print the contents of the commit object before validating it]' \
+  _arguments -S -s \
+    '(-v --verbose)'{-v,--verbose}'[print contents of the commit object before validating it]' \
+    '--raw[print raw gpg status output]' \
     '*: :__git_commits'
 }
 
 (( $+functions[_git-verify-tag] )) ||
 _git-verify-tag () {
-  _arguments -w -S -s \
-    '(-v --verbose)'{-v,--verbose}'[print the contents of the tag object before validating it]' \
+  _arguments -S -s \
+    '(-v --verbose)'{-v,--verbose}'[print contents of the tag object before validating it]' \
+    '--raw[print raw gpg status output]' \
+    '--format=[specify format to use for the output]:format' \
     '*: :__git_tags'
 }
 
@@ -3661,7 +4042,7 @@ _git-cvsimport () {
 
 (( $+functions[_git-cvsserver] )) ||
 _git-cvsserver () {
-  _arguments -w -S -s \
+  _arguments -S -s \
     '--base-path[path to prepend to requested CVSROOT]: :_directories' \
     '--strict-paths[do not allow recursing into subdirectories]' \
     '--export-all[do not check for gitcvs.enabled]' \
@@ -3673,7 +4054,11 @@ _git-cvsserver () {
 
 (( $+functions[_git-imap-send] )) ||
 _git-imap-send () {
-  _message 'no arguments allowed; accepts mailbox file on standard input'
+  _arguments \
+    '--curl[use libcurl to communicate with the IMAP server]' \
+    - '(out)' \
+    {-v,--verbose}'[be more verbose]' \
+    {-q,--quiet}'[be more quiet]'
 }
 
 (( $+functions[_git-quiltimport] )) ||
@@ -3681,7 +4066,8 @@ _git-quiltimport () {
   _arguments -S \
     '(-n --dry-run)'{-n,--dry-run}'[check patches and warn if they cannot be imported]' \
     '--author[default author name and email address to use for patches]: :_email_addresses' \
-    '--patches[set directory containing patches]:patch directory:_directories'
+    '--patches[set directory containing patches]:patch directory:_directories' \
+    '--series[specify quilt series file]:series file:_files'
 }
 
 (( $+functions[_git-request-pull] )) ||
@@ -3699,8 +4085,8 @@ _git-send-email () {
     '--annotate[review and edit each patch before sending it]' \
     '--bcc=[Bcc: value for each email]: :_email_addresses' \
     '--cc=[starting Cc: value for each email]: :_email_addresses' \
-    '--to-cover[Copy the To: list from the first file to the rest]' \
-    '--cc-cover[Copy the Cc: list from the first file to the rest]' \
+    '--to-cover[copy the To: list from the first file to the rest]' \
+    '--cc-cover[copy the Cc: list from the first file to the rest]' \
     '--compose[edit introductory message for patch series]' \
     '--from=[specify sender]:email address:_email_addresses' \
     '--in-reply-to=[specify contents of first In-Reply-To header]:message-id' \
@@ -3759,150 +4145,127 @@ _git-svn () {
       declare -a commands
 
       commands=(
-        init:'initialize an empty git repository with additional svn data'
-        fetch:'fetch revisions from the SVN remote'
+        blame:'show what revision and author last modified each line of a file'
+        branch:'create a branch in the SVN repository'
         clone:'same as init, followed by fetch'
-        rebase:'fetch revs from SVN parent of HEAD and rebase current work on it'
+        commit-diff:'commit diff of two tree-ishs'
+        create-ignore:'recursively finds the svn:ignore property and creates .gitignore files'
         dcommit:'commit diffs from given head onto SVN repository'
-        branch:'create a branch in the SVN repository'
-        tag:'create a tag in the SVN repository'
-        log:'output SVN log-messages'
-        blame:'show what revision and author last modified each line of a file'
+        fetch:'fetch revisions from the SVN remote'
         find-rev:'output git commit corresponding to the given SVN revision'\''s hash'
-        set-tree:'commit given commit or tree to SVN repository'
-        create-ignore:'recursively finds the svn:ignore property and creates .gitignore files'
-        show-ignore:'output corresponding toplevel .gitignore file of svn:ignore'
-        mkdirs:'recreate empty directories that Git cannot track'
-        commit-diff:'commit diff of two tree-ishs'
+        gc:'compress git-svn-related information'
         info:'show information about a file or directory'
-        proplist:'list the SVN properties stored for a file or directory'
+        init:'initialize an empty git repository with additional svn data'
+        log:'output SVN log-messages'
+        migrate:'migrate configuration/metadata/layout from previous versions of git-svn'
+        mkdirs:'recreate empty directories that Git cannot track'
         propget:'get a given SVN property for a file'
+        proplist:'list the SVN properties stored for a file or directory'
+        propset:'set the value of a property on a file or directory - will be set on commit'
+        rebase:'fetch revs from SVN parent of HEAD and rebase current work on it'
+        reset:'undo effect of fetch back to specific revision'
+        set-tree:'commit given commit or tree to SVN repository'
         show-externals:'show the subversion externals'
-        gc:'compress git-svn-related information'
-        reset:'undo effect of fetch back to specific revision')
+        show-ignore:'output svn:ignore in format of a toplevel .gitignore file'
+        tag:'create a tag in the SVN repository'
+      )
 
       _describe -t commands command commands && ret=0
       ;;
     (option-or-argument)
       curcontext=${curcontext%:*}-$line[1]:
-
-      declare -a remote_opts fc_opts init_opts cmt_opts opts
-
-      # TODO: --no-auth-cache is undocumented.
-      # TODO: --config-dir is undocumented.
-      remote_opts=(
-          '--username=[username to use for SVN transport]: :_users'
-          '--ignore-paths[regular expression of paths to not check out]:pattern'
-          '--no-auth-cache[undocumented]'
-          '--config-dir=[undocumented]:configuration directory:_directories')
-
-      # TODO: --repack-flags can be improved by actually completing the legal
-      # flags to git-repack.
-      # TODO: --noMetadata is undocumented.
-      # TODO: --useSvmProps is undocumented.
-      # TODO: --useSvnsyncProps is undocumented.
-      # TODO: --log-window-size is undocumented.
-      # TODO: --no-checkout is undocumented.
-      fc_opts=(
-        '--localtime[store Git commit times in local timezone]'
-        '--use-log-author[use author from the first From: or Signed-Off-By: line, when fetching into git]'
-        '--add-author-from[when committing to svn, append a From: line based on the git commit'\''s author string]'
-        '(                --no-follow-parent)--follow-parent[follow parent commit]'
-        '(--follow-parent                   )--no-follow-parent[do not follow parent commit]'
-        '(-A --authors-file)'{-A,--authors-file}'[specify author-conversion file]:author-conversion file:_files'
-        '--authors-prog=[program used to generate authors]: :_cmdstring'
-        '(-q --quiet)'{-q,--quiet}'[make git-svn less verbose]'
-        '--repack=[repack files (for given number of revisions)]:: :__git_guard_number "revision limit"'
-        '(--repack-flags --repack-args --repack-opts)'{--repack-flags=,--repack-args=,--repack-opts=}'[flags to pass to git-repack]:git-repack flags'
-        '--noMetadata[undocumented]'
-        '--useSvmProps[undocumented]'
-        '--useSvnsyncProps[undocumented]'
-        '--log-window-size=[undocumented]'
-        '--no-checkout[undocumented]'
-        $remote_opts)
-
-      init_opts=(
-          '(-T --trunk)'{-T-,--trunk=}'[set trunk sub-directory]:trunk sub-directory:->subdirectory'
-          '(-t --tags)*'{-t-,--tags=}'[add tags sub-directory]:tags sub-directory:->subdirectory'
-          '(-b --branches)*'{-b-,--branches=}'[add branches sub-directory]:branches sub-directory:->subdirectory'
-          '(-s --stdlayout)'{-s,--stdlayout}'[shorthand for setting trunk, tags, branches as relative paths, the SVN default]'
-          '--no-metadata[set svn-remote.*.noMetadata]'
-          '--use-svm-props[set svn-remote.*.useSvmProps]'
-          '--use-svnsync-props[set svn-remote.*.useSvnsyncProps]'
-          '--rewrite-root=[set svn-remote.*.rewriteRoot]:new root:_urls'
-          '--rewrite-uuid=[set svn-remote.*.rewriteUUID]:uuid'
-          '--prefix=[prefix to use for names of remotes]:path prefix:_directories -r ""'
-          '(               --no-minimize-url)--minimize-url[minimize URLs]'
-          '(--minimize-url                  )--no-minimize-url[do not minimize URLs]'
-          '--shared=[share repository amongst several users]:: :__git_repository_permissions'
-          '--template=[directory to use as a template for the object database]: :_directories'
-          $remote_opts)
-
-      # TODO: -C and --copy-similarity are undocumented.
-      cmt_opts=(
-        '--rmdir[remove empty directories from SVN tree after commit]'
-        '(-e --edit)'{-e,--edit}'[edit commit message before committing]'
-        '-l-[limit number of rename/copy targets to run]: :__git_guard_number'
-        '--find-copies-harder[try harder to find copies]'
-        '(-C --copy-similarity)'{-C-,--copy-similarity=}'[undocumented]: :_guard "[[\:digit:\]]#" number')
-
-      if [[ $line[1] == (fetch|clone) ]]; then
-        arguments+=(
-          '(-r --revision)'{-r,--revision}'[only fetch given revision or revision range]: :__git_svn_revisions'
-          ':: :__git_svn-remotes')
-      fi
-
-      if [[ $line[1] == (fetch|rebase|dcommit) ]]; then
-        # TODO: --fetch-all and --all are undocumented.
-        opts+=(
-          '(--fetch-all --all)'{--fetch-all,--all}'[undocumented]')
-      fi
-
-      if [[ $line[1] == (rebase|dcommit) ]]; then
-        opts+=(
-          '(-m --merge)'{-m,--merge}'[use merging strategies, if necessary]'
-          '*'{-s,--strategy=-}'[use given merge strategy]:merge strategy:__git_merge_strategies')
-      fi
-
-      if [[ $line[1] == (rebase|dcommit|branch) ]]; then
-        opts+=(
-          '(-n --dry-run)'{-n,--dry-run}'[only display what would be done]')
-      fi
-
-      if [[ $line[1] == (rebase|dcommit|log) ]]; then
-        opts+=(
-          '(-v --verbose)'{-v,--verbose}'[display extra information]')
-      fi
+      declare -a opts
 
       case $line[1] in
-        (init)
-          opts+=(
-            $init_opts)
-          ;;
-        (fetch)
-          opts+=(
-            '--parent[fetch only from SVN parent of current HEAD]'
-            $fc_opts)
-          ;;
-        (clone)
-          opts+=(
-            $init_opts
-            $fc_opts
-            ':url:_urls'
-            '::directory:_directories')
-          ;;
-        (rebase)
-          opts+=(
-            '--local[do not fetch remotely, rebase against the last fetched commit from SVN]'
-            $fc_opts)
-          ;;
-        (dcommit)
-          arguments+=(
-            '--no-rebase[do not rebase or reset after committing]'
-            '--commit-url[commit to a different SVN url]:SVN URL:_url'
-            $fc_opts
-            $cmt_opts)
-          ;;
+        (clone|dcommit|fetch|init|migrate|rebase|set-tree)
+	  # TODO: --ignore-refs is undocumented.
+	  # TODO: --no-auth-cache is undocumented.
+	  # TODO: --config-dir is undocumented.
+	  opts+=(
+	    '--config-dir=:configuration directory:_directories'
+	    '--ignore-paths[regular expression of paths to not check out]:perl regex'
+	    '--include-paths[regular expression of paths to check out]:perl regex'
+	    '--ignore-refs:ref'
+	    '--no-auth-cache'
+	    '--username=[username to use for SVN transport]: :_users'
+	  )
+	;|
+        (clone|dcommit|fetch|log|rebase|set-tree)
+	  opts+=(
+	    '(-A --authors-file)'{-A,--authors-file}'[specify author-conversion file]:author-conversion file:_files'
+	  )
+	;|
+        (clone|dcommit|fetch|rebase|set-tree)
+	  # TODO: --repack-flags can be improved by actually completing the legal
+	  # flags to git-repack.
+	  # TODO: --no-checkout is undocumented.
+	  opts+=(
+	    "--add-author-from[when committing to svn, append a From: line based on the git commit's author string]"
+	    '--authors-prog=[specify program used to generate authors]: :_cmdstring'
+	    '(--no-follow-parent)--follow-parent[follow parent commit]'
+	    "(--follow-parent)--no-follow-parent[don't follow parent commit]"
+	    '--localtime[store Git commit times in local timezone]'
+	    '--log-window-size=[fetch specified number of log entries per-request]:entries [100]'
+	    '--no-checkout'
+	    '(-q --quiet)'{-q,--quiet}'[make git-svn less verbose]'
+	    '(--repack-flags --repack-args --repack-opts)'{--repack-flags=,--repack-args=,--repack-opts=}'[flags to pass to git-repack]:git-repack flags'
+	    '--repack=[repack files (for given number of revisions)]:: :__git_guard_number "revision limit"'
+	    '--use-log-author[use author from the first From: or Signed-Off-By: line, when fetching into git]'
+	  )
+	;|
+	(clone|init)
+	  opts+=(
+	    '(-T --trunk)'{-T-,--trunk=}'[set trunk sub-directory]:trunk sub-directory:->subdirectory'
+	    '(-t --tags)*'{-t-,--tags=}'[add tags sub-directory]:tags sub-directory:->subdirectory'
+	    '(-b --branches)*'{-b-,--branches=}'[add branches sub-directory]:branches sub-directory:->subdirectory'
+	    '(-s --stdlayout)'{-s,--stdlayout}'[shorthand for setting trunk, tags, branches as relative paths, the SVN default]'
+	    '--no-metadata[get rid of git-svn-id: lines at the end of every commit]'
+	    '--rewrite-root=[set svn-remote.*.rewriteRoot]:new root:_urls'
+	    '--rewrite-uuid=[set svn-remote.*.rewriteUUID]:uuid'
+	    '--prefix=[prefix to use for names of remotes]:path prefix:_directories -r ""'
+	    '(               --no-minimize-url)--minimize-url[minimize URLs]'
+	    "(--minimize-url                  )--no-minimize-url[don't minimize URLs]"
+	    '--shared=[share repository amongst several users]:: :__git_repository_permissions'
+	    '--template=[directory to use as a template for the object database]: :_directories'
+	    '--use-svm-props[re-map repository URLs and UUIDs from mirrors created with SVN::Mirror]'
+	    '--use-svnsync-props[re-map repository URLs and UUIDs from mirrors created with svnsync]'
+	  )
+	;|
+	(commitdiff|dcommit|set-tree)
+	  # TODO: -C and --copy-similarity are undocumented.
+	  opts+=(
+	    '(-C --copy-similarity)'{-C-,--copy-similarity=}': :_guard "[[\:digit:\]]#" number'
+	    '(-e --edit)'{-e,--edit}'[edit commit message before committing]'
+	    '-l-[limit number of rename/copy targets to run]: :__git_guard_number'
+	    '--find-copies-harder[try harder to find copies]'
+	    '--rmdir[remove empty directories from SVN tree after commit]'
+	    ':: :__git_svn-remotes'
+	  )
+	;|
+	(fetch|clone)
+	  opts+=(
+	    '(-r --revision)'{-r,--revision}'[only fetch given revision or revision range]: :__git_svn_revisions'
+	    ':: :__git_svn-remotes'
+	  )
+        ;|
+        (fetch|rebase|dcommit)
+	  # TODO: --fetch-all and --all are undocumented.
+	  opts+=( '(--fetch-all --all)'{--fetch-all,--all} )
+        ;|
+        (rebase|dcommit)
+	  opts+=(
+	    '(-M -m --merge)'{-M,-m,--merge}'[use merging strategies, if necessary]'
+	    '*'{-s,--strategy=-}'[use given merge strategy]:merge strategy:__git_merge_strategies'
+	  )
+        ;|
+        (rebase|dcommit|branch|tag)
+	  opts+=(
+	    '(-n --dry-run)'{-n,--dry-run}'[only display what would be done]'
+	  )
+        ;|
+        (rebase|dcommit|log)
+	  opts+=( '(-v --verbose)'{-v,--verbose}'[display extra information]' )
+        ;|
         (branch|tag)
           # TODO: -d/--destination should complete output of
           # git config --get-all svn-remote.*.branches
@@ -3912,15 +4275,82 @@ _git-svn () {
           # git config --get-all svn-remote.*.commiturl
           opts+=(
             '(-m --message)'{-m,--message}'[specify the commit message]:message'
-            '(-d --destination)'{-d,--destination}'[location of branch or tag to create in SVN repository]: :_directories'
+            '(-d --destination)'{-d,--destination}"[location of $line[1] to create in SVN repository]: :_directories"
             '--username[specify SVN username to perform commit as]: :_users'
-            '--commit-url[specify URL to connect to destination SVN repository]: :_urls')
-
-          if [[ $line[1] != tag ]]; then
-            opts+=(
-              '(-t --tag)'{-t,--tag}'[create a tag]')
-          fi
-          ;;
+            '--commit-url[specify URL to connect to destination SVN repository]: :_urls'
+	    '--parents[create parent folders]'
+	  )
+	;|
+        (commit-diff|create-ignore|dcommit|show-ignore|mkdirs|proplist|propget|show-externals)
+          # TODO: -r and --revision is undocumented for dcommit, show-ignore and mkdirs.
+          opts+=(
+            '(-r --revision)'{-r,--revision}'[specify SVN revision]: :__git_svn_revisions'
+	  )
+	;|
+	(propset|propget)
+	  opts+=( '1:property:(svn:ignore svn:keywords svn:executable svn:eol-style svn:mime-type svn:externals svn:needs-lock)' )
+        ;|
+
+	# ;| style fall-throughs end; here on each command covered once
+        (blame)
+          opts+=(
+            '--git-format[produce output in git-blame format, with SVN revision numbers instead of git commit hashes]'
+	    '*:file:__git_cached_files'
+	  )
+	;;
+        (branch)
+	  opts+=( '(-t --tag)'{-t,--tag}'[create a tag]' )
+	;;
+        (clone)
+          opts+=(
+	    '--preserve-empty-dirs[create a placeholder file for each empty directory]'
+	    '--placeholder-filename=[specify name of placeholder files created by --preserve-empty-dirs]:filename [.gitignore]:_files'
+            ':url:_urls'
+            '::directory:_directories'
+	  )
+	;;
+        (commit-diff)
+          # TODO: -m and --message is undocumented.
+          # TODO: -F and --file is undocumented.
+          opts+=(
+            '(-m --message)'{-m-,--message=}':message'
+            '(-F --file)'{-F-,--file=}':file:_files'
+	    ':original tree:__git_tree_ishs'
+	    ':new tree result:__git_tree_ishs'
+	    ':target:_urls'
+	  )
+	;;
+        (dcommit)
+	  # TODO: --set-svn-props is undocumented
+          opts+=(
+            '--commit-url[commit to a different SVN url]:SVN URL:_url'
+	    '(-i --interactive)'{-i,--interactive}'[ask for confirmation that a patch should be sent to SVN]'
+	    '--mergeinfo[add specified merge information during the dcommit]:mergeinfo' \
+            "--no-rebase[don't rebase or reset after committing]"
+	    '--set-svn-props:arg'
+	  )
+	;;
+        (fetch)
+          opts+=(
+	    '(-p --parent)'{-p,--parent}'[fetch only from SVN parent of current HEAD]'
+	  )
+	;;
+        (info)
+          opts+=(
+	    '--url[output only value of URL field]'
+	    ':file:__git_cached_files'
+	  )
+	;;
+        (init)
+	  opts+=( ':SVN URL:_urls' ':target directory:_directories' )
+        ;;
+	(find-rev)
+	  opts+=(
+	    '(-A --after -B --before)'{-B,--before}'[with no exact match, find last commit for current branch]'
+	    '(-A --after -B --before)'{-A,--after}'[with no exact match, find closest match searching forwards]'
+	    ':revision: _alternative "svn-revisions\:svn revision number\:__git_svn_revision_numbers -p r" "git-revisions\:git revision\:__git_revisions"'
+	  )
+	;;
         (log)
           declare -a revision_options
           __git_setup_revision_options
@@ -3934,47 +4364,46 @@ _git-svn () {
             '--limit=[like --max-count, but not counting merged/excluded commits]: :__git_guard_number limit'
             '--incremental[give output suitable for concatenation]'
             '--show-commit[output git commit SHA-1, as well]'
-            '--color[undocumented]'
-            '--pager[undocumented]:pager:_cmdstring'
-            '--non-recursive[undocumented]')
-          ;;
-        (blame)
-          opts+=(
-            '--git-format[produce output in git-blame format, with SVN revision numbers instead of git commit hashes]')
-          ;;
-        (set-tree)
-          opts+=(
-            '--stdin[read list of commits to commit from stdin]')
-          ;;
-        (create-ignore|show-ignore|mkdirs|proplist|propget|show-externals)
-          # TODO: -r and --revision is undocumented for show-ignore and mkdirs.
-          opts+=(
-            '(-r --revision)'{-r,--revision}'[specify SVN revision]: :__git_svn_revisions')
-          ;;
-        (commit-diff)
-          # TODO: -m and --message is undocumented.
-          # TODO: -F and --file is undocumented.
-          # TODO: -r and --revision is undocumented.
-          opts+=(
-            '(-m --message)'{-m-,--message=}'[undocumented]:message'
-            '(-F --file)'{-F-,--file=}'[undocumented]: :_files'
-            '(-r --revision)'{-r-,--revision=}'[undocumented]: :__git_svn_revisions')
-          ;;
-        (info)
+            '--color'
+            '--pager:pager:_cmdstring'
+            '--non-recursive'
+	    ':file:__git_cached_files'
+	  )
+	;;
+        (migrate)
+          opts+=( '--minimize' )
+        ;;
+	(propset)
+	  opts+=( ':value' )
+	;&
+	(proplist|propget)
+          opts+=( '*:file:__git_cached_files' )
+	;;
+        (rebase)
           opts+=(
-            '--url[output only value of URL field]')
-          ;;
+	  '(-l --local)'{-l,--local}"[don't fetch remotely, rebase against the last fetched commit from SVN]"
+	    '(--preserve-merges -p)'{--preserve-merges,-p}'[try to recreate merges instead of ignoring them]'
+	  )
+	;;
         (reset)
           opts+=(
             '(-r --revision)'{-r,--revision}'[specify most recent SVN revision to keep]: :__git_svn_revisions'
-            '(-p --parent)'{-p,--parent}'[discard specified revision as well, keeping nearest parent instead]')
-          ;;
+            '(-p --parent)'{-p,--parent}'[discard specified revision as well, keeping nearest parent instead]'
+	  )
+	;;
+        (set-tree)
+          opts+=( '--stdin[read list of commits to commit from stdin]' )
+	;;
+	(create-ignore|gc|mkdirs|show-externals|show-ignore|tag) ;;
+        (*) # fallback to files on any new/unrecognised commands
+          opts+=( '*:file:_files' )
+        ;;
       esac
 
-      _arguments -w -C -S -s \
-        '(-h -H --help)'{-h,-H,--help}'[display usage information]' \
-        '(-V --version)'{-V,--version}'[display version information]' \
-        '--minimize-connections[undocumented]' \
+      _arguments -C -S -s \
+        '(-)'{-h,-H}'[display usage information]' \
+        '(-)'{-V,--version}'[display version information]' \
+        '--minimize-connections' \
         '(-R --svn-remote --remote)'{-R,--svn-remote,--remote}'[svn remote to use]:svn remote:__git_svn-remotes' \
         '(-i --id)'{-i,--id}'[set GIT_SVN_ID]:GIT_SVN_ID' \
         $opts && ret=0
@@ -4001,7 +4430,7 @@ _git-apply () {
   local -a apply_options
   __git_setup_apply_options
 
-  _arguments -w -S -s \
+  _arguments -S -s \
     $apply_options \
     '(--index --cached --reject)'{-3,--3way}'[fall back on 3-way merge if patch fails]' \
     '--stat[output diffstat for input (turns off "apply")]' \
@@ -4016,6 +4445,7 @@ _git-apply () {
     '--unidiff-zero[disable unified-diff-context check]' \
     '--apply[apply patches that would otherwise not be applied]' \
     '--no-add[ignore additions made by the patch]' \
+    '--allow-overlap[allow overlapping hunks]' \
     '--inaccurate-eof[work around missing-new-line-at-EOF bugs]' \
     '(-v --verbose)'{-v,--verbose}'[display progress on stderr]' \
     '--recount[do not trust line counts in hunk headers]' \
@@ -4030,33 +4460,30 @@ _git-checkout-index () {
     z_opt='-z[paths are separated with NUL character when reading from standard input]'
   fi
 
-  _arguments -w -S -s \
+  _arguments -S -s \
     '(-u --index)'{-u,--index}'[update stat information in index]' \
-    '(-q --quiet)'{-q,--quiet}'[do not complain about existing files or missing files]' \
+    '(-q --quiet)'{-q,--quiet}'[no warning for existing files and files not in index]' \
     '(-f --force)'{-f,--force}'[force overwrite of existing files]' \
     '(-a --all --stdin *)'{-a,--all}'[check out all files in index]' \
     '(-n --no-create)'{-n,--no-create}'[do not checkout new files]' \
-    '--prefix=-[prefix to use when creating files]:directory:_directories' \
-    '--stage=-[check out files from named stage]:stage:(1 2 3 all)' \
     '--temp[write content to temporary files]' \
     '(-a --all *)--stdin[read list of paths from the standard input]' \
+    '--prefix=[prefix to use when creating files]:directory:_directories' \
+    '--stage=[check out files from named stage]:stage:(1 2 3 all)' \
     $z_opt \
     '*: :__git_cached_files'
 }
 
 (( $+functions[_git-commit-tree] )) ||
 _git-commit-tree () {
-  if (( CURRENT == 2 )); then
-    _arguments \
-      '-h[display usage]' \
-      ': :__git_trees'
-  elif [[ $words[CURRENT-1] == -p ]]; then
-    local expl
-    _description commits expl 'parent commit'
-    __git_objects $expl
-  else
-    compadd - '-p'
-  fi
+  _arguments \
+    '-h[display usage]' \
+    '*-p+[specify parent commit]:parent commit:__git_objects' \
+    '(-S --gpg-sign --no-gpg-sign)'{-S-,--gpg-sign=-}'[GPG-sign the commit]::key id' \
+    "(-S --gpg-sign --no-gpg-sign)--no-gpg-sign[don't GPG-sign the commit]" \
+    '-F+[read commit log from specified file]:file:_files' \
+    '*-m+[specify paragraph of commit log message]:message' \
+    ': :__git_trees'
 }
 
 (( $+functions[_git-hash-object] )) ||
@@ -4069,6 +4496,7 @@ _git-hash-object () {
     '-w[write object to object database]' \
     '(: --stdin-paths)--stdin[read object from standard input]' \
     '(: --stdin --path)--stdin-paths[read file names from standard input instead of from command line]' \
+    '--literally[just hash any random garbage to create corrupt objects for debugging Git]' \
     '(       --no-filters)--path=[hash object as if it were located at given path]: :_files' \
     '(--path             )--no-filters[hash contents as is, ignoring any input filters]' \
     '(--stdin --stdin-paths):file:_files'
@@ -4092,6 +4520,7 @@ _git-index-pack () {
     '--stdin[read pack from stdin and instead write to specified file]' \
     $stdin_opts \
     '--strict[die if the pack contains broken objects or links]' \
+    '--threads=[specify number of threads to use]:number of threads' \
     ':pack file:_files -g "*.pack(-.)"'
 }
 
@@ -4118,7 +4547,7 @@ _git-merge-file () {
     '(--ours          --union)--theirs[resolve conflicts favoring their side of the lines]' \
     '(--ours --theirs        )--union[resolve conflicts favoring both sides of the lines]' \
     '--marker-size[specify length of conflict markers]: :__git_guard_number "marker length"' \
-    '--diff3[undocumented]' \
+    '--diff3[use a diff3 based merge]' \
     ':current file:_files' \
     ':base file:_files' \
     ':other file:_files'
@@ -4148,7 +4577,7 @@ _git-mktag () {
 
 (( $+functions[_git-mktree] )) ||
 _git-mktree () {
-  _arguments -w -S -s \
+  _arguments -S -s \
     '-z[read NUL-terminated ls-tree -z output]' \
     '--missing[allow missing objects]' \
     '--batch[allow creation of more than one tree]'
@@ -4163,42 +4592,46 @@ _git-pack-objects () {
   fi
 
   # NOTE: --index-version is only used by the Git test suite.
-  # TODO: --reflog is undocumented.
-  # TODO: --keep-unreachable is undocumented.
-  # TODO: --unpack-unreachable is undocumented.
-  _arguments -A '-*' \
-    '(: --max-pack-size)--stdout[write pack contents to standard output]' \
+  _arguments \
+    '(-q --quiet)'{-q,--quiet}"[don't report progress]" \
+    '(-q --quiet --all-progress)--progress[show progress meter]' \
+    '(-q --quiet --progress --all-progress-implied)--all-progress[show progress meter during object writing phase]' \
+    '(-q --quiet --all-progress)--all-progress-implied[like --all-progress, but only if --progress was also passed]' \
+    '(--stdout)--max-pack-size=[specify maximum size of each output pack file]: : __git_guard_bytes "maximum pack size"' \
+    '(--incremental)--local[similar to --incremental, but only ignore unpacked non-local objects]' \
+    '(--local)--incremental[ignore objects that have already been packed]' \
+    '--window=-[limit pack window by objects]: :__git_guard_number "window size"' \
+    '--window-memory=-[specify window size in memory]: : __git_guard_bytes "window size"' \
+    '--depth=-[maximum delta depth]: :__git_guard_number "maximum delta depth"' \
+    "--no-reuse-delta[don't reuse existing deltas, but compute them from scratch]" \
+    "--no-reuse-object[don't reuse existing object data]" \
+    '--delta-base-offset[use delta-base-offset packing]' \
+    '--threads=-[specify number of threads for searching for best delta matches]: :__git_guard_number "number of threads"' \
+    '--non-empty[only create a package if it contains at least one object]' \
     '--revs[read revision arguments from standard input]' \
     '(--revs)--unpacked[limit objects to pack to those not already packed]' \
     '(--revs)--all[include all refs as well as revisions already specified]' \
+    '--reflog[include objects referred by reflog entries]' \
+    '--indexed-objects[include objects referred to by the index]' \
+    '(: --max-pack-size)--stdout[output pack to stdout]' \
     '--include-tag[include unasked-for annotated tags if object they reference is included]' \
-    '--window=-[number of objects to use per delta compression]: :__git_guard_number "window size"' \
-    '--depth=-[maximum delta depth]: :__git_guard_number "maximum delta depth"' \
-    '--window-memory=-[window size in memory]:window size:__git_guard_bytes' \
-    '(--stdout)--max-pack-size=[maximum size of each output packfile]:maximum pack size:__git_guard_bytes' \
+    '(--unpack-unreachable)--keep-unreachable[keep unreachable ]' \
+    '--pack-loose-unreachable[pack loose unreachable objects]' \
+    '(--keep-unreachable)--unpack-unreachable=-[unpack unreachable objects newer than specified time]::time' \
+    '--include-tag[include tag objects that refer to objects to be packed]' \
+    $thin_opt \
+    '--shallow[create packs suitable for shallow fetches]' \
     '--honor-pack-keep[ignore objects in local pack with .keep file]' \
-    '(              --local)--incremental[ignore objects that have already been packed]' \
-    '(--incremental        )--local[similar to --incremental, but only ignore unpacked non-local objects]' \
-    '--non-empty[only create a package if it contains at least one object]' \
-    '(           --all-progress)--progress[display progress on standard error]' \
-    '(--progress                --all-progress-implied)--all-progress[display progress output on standard error, even during write-out phase]' \
-    '(--all-progress)--all-progress-implied[like --all-progress, but only if --progress was also passed]' \
-    '-q[do not report progress]' \
-    '--no-reuse-delta[do not reuse existing deltas, but compute them from scratch]' \
-    '--no-reuse-object[do not reuse existing object data]' \
     '--compression=-[specify compression level]: :__git_compression_levels' \
-    $thin_opt \
-    '--delta-base-offset[use delta-base-offset packing]' \
-    '--threads=-[specify number of threads for searching for best delta matches]: :__git_guard_number "number of threads"' \
     '--keep-true-parents[pack parents hidden by grafts]' \
-    '(                   --unpack-unreachable)--keep-unreachable[undocumented]' \
-    '(--keep-unreachable                     )--unpack-unreachable[undocumented]' \
+    '--use-bitmap-index[use a bitmap index if available to speed up counting objects]' \
+    '--write-bitmap-index[write a bitmap index together with the pack index]' \
     ':base-name:_files'
 }
 
 (( $+functions[_git-prune-packed] )) ||
 _git-prune-packed () {
-  _arguments -w -S -s \
+  _arguments -S -s \
     '(-n --dry-run)'{-n,--dry-run}'[only list objects that would be removed]' \
     '(-q --quiet)'{-q,--quiet}'[do not display progress on standard error]'
 }
@@ -4227,7 +4660,7 @@ _git-read-tree () {
     exclude_per_directory_opt='--exclude-per-directory=-[specify .gitignore file]:.gitignore file:_files'
   fi
 
-  _arguments -w -S -s \
+  _arguments -S -s \
     '(   --reset --prefix)-m[perform a merge, not just a read]' \
     '(-m         --prefix)--reset[perform a merge, not just a read, ignoring unmerged entries]' \
     '(-m --reset          2 3)--prefix=-[read the contents of specified tree-ish under specified directory]:prefix:_directories -r ""' \
@@ -4247,7 +4680,7 @@ _git-read-tree () {
 
 (( $+functions[_git-symbolic-ref] )) ||
 _git-symbolic-ref () {
-  _arguments -w -S -s \
+  _arguments -S -s \
     '(-d --delete)'{-d,--delete}'[delete symbolic ref]' \
     '(-q --quiet)'{-q,--quiet}'[do not issue error if specified name is not a symbolic ref]' \
     '--short[shorten the ref name (eg. refs/heads/master -> master)]' \
@@ -4274,13 +4707,11 @@ _git-update-index () {
   fi
 
   _arguments -S \
-    $refreshables \
     '(-)'{-h,--help}'[display usage information]' \
+    '-q[continue refresh even when index needs update]' \
     '--add[add files not already in index]' \
-    '(         --force-remove)--remove[remove files that are in the index but are missing from the work tree]' \
-    '(--remove               )--force-remove[remove files from both work tree and index]' \
+    '(--force-remove)--remove[remove files that are in the index but are missing from the work tree]' \
     '(-q --unmerged --ignore-missing --really-refresh)--refresh[refresh index]' \
-    '-q[run quietly]' \
     '--ignore-submodules[do not try to update submodules]' \
     '--unmerged[if unmerged changes exists, ignore them instead of exiting]' \
     '--ignore-missing[ignore missing files when refreshing the index]' \
@@ -4295,10 +4726,16 @@ _git-update-index () {
     '(-)'{-g,--again}'[run git-update-index on differing index entries]' \
     '(-)--unresolve[restore "unmerged" or "needs updating" state of files]' \
     '--info-only[only insert files object-IDs into index]' \
-    '(--remove)--force-remove[remove file from index even when working directory has no such file]' \
     '--replace[replace files already in index, if necessary]' \
+    '(--remove)--force-remove[remove named paths even if present in worktree]' \
     '(: -)--stdin[read list of paths from standard input]' \
     '--verbose[report what is being added and removed from the index]' \
+    '--clear-resolve-undo[forget saved unresolved conflicts]' \
+    '--index-version=[write index in specified on-disk format version]:version:(2 3 4)' \
+    '--split-index[enable/disable split index]' \
+    '--untracked-cache[enable/disable untracked cache]' \
+    '--test-untracked-cache[test if the filesystem supports untracked cache]' \
+    '--force-untracked-cache[enable untracked cache without testing the filesystem]' \
     $z_opt \
     '*:: :_files'
 }
@@ -4311,12 +4748,13 @@ _git-update-ref () {
     z_opt='-z[values are separated with NUL character when reading from stdin]'
   fi
 
-  _arguments -w -S -s \
+  _arguments -S -s \
     '-m[update reflog for specified name with specified reason]:reason for update' \
     '(:)-d[delete given reference after verifying its value]:symbolic reference:__git_revisions:old reference:__git_revisions' \
     '(-d --no-deref)--stdin[reads instructions from standard input]' \
     $z_opt \
     '(-d -z --stdin)--no-deref[overwrite ref itself, not what it points to]' \
+    '--create-reflog[create a reflog]' \
     ':symbolic reference:__git_revisions' \
     ':new reference:__git_revisions' \
     '::old reference:__git_revisions'
@@ -4325,7 +4763,7 @@ _git-update-ref () {
 (( $+functions[_git-write-tree] )) ||
 _git-write-tree () {
   # NOTE: --ignore-cache-tree is only used for debugging.
-  _arguments -w -S -s \
+  _arguments -S -s \
     '--missing-ok[ignore objects in index that are missing in object database]' \
     '--prefix=[write tree representing given sub-directory]:sub-directory:_directories -r ""'
 }
@@ -4334,16 +4772,24 @@ _git-write-tree () {
 
 (( $+functions[_git-cat-file] )) ||
 _git-cat-file () {
-  _arguments -w -S -s \
-    '(- 1)-t[show type of given object]' \
-    '(- 1)-s[show size of given object]' \
-    '(- 1)-e[exit with zero status if object exists]' \
-    '(- 1)-p[pretty-print given object]' \
-    '(- 1)--textconv[show content as transformed by a textconv filter]' \
-    '(- :)--batch=-[print SHA1, type, size and contents (or in <format>) of objects given on stdin]:format' \
-    '(- :)--batch-check=-[print SHA1, type and size (or in <format>) of objects given on stdin]:format' \
+  _arguments -S -s \
+    '(-t -s -e -p --allow-unknown-type 1)--textconv[show content as transformed by a textconv filter]' \
+    '(-t -s -e -p --allow-unknown-type 1)--filters[show content as transformed by filters]' \
+    '(-t -s -e -p --allow-unknown-type 1)--path=[use a specific path for --textconv/--filters]:path:_directories' \
+    - query \
+    '(-s -e -p --textconv --filters 1)-t[show type of given object]' \
+    '(-t -e -p --textconv --filters 1)-s[show size of given object]' \
+    '(-e -p --textconv --filters 1)--allow-unknown-type[allow query of broken/corrupt objects of unknown type]' \
+    '(-t -s -p -textconv --filters --allow-unknown-type 1)-e[exit with zero status if object exists]' \
+    '(-t -s -e -textconv --filters --allow-unknown-type 1)-p[pretty-print given object]' \
     '(-):object type:(blob commit tag tree)' \
-    ': :__git_objects'
+    ': :__git_objects' \
+    - batch \
+    '(--batch-check)--batch=-[print SHA1, type, size and contents (or in specified format)]::format' \
+    '(--batch)--batch-check=-[print SHA1, type and size (or in specified format)]::format' \
+    '--follow-symlinks[follow in-tree symlinks (used with --batch or --batch-check)]' \
+    '--batch-all-objects[show all objects with --batch or --batch-check]' \
+    '--buffer[disable flushing of output after each object]'
 }
 
 (( $+functions[_git-diff-files] )) ||
@@ -4352,7 +4798,7 @@ _git-diff-files () {
   __git_setup_revision_options
   __git_setup_diff_stage_options
 
-  _arguments -w -S -s \
+  _arguments -S -s \
     $revision_options \
     $diff_stage_options \
     ': :__git_changed-in-working-tree_files' \
@@ -4371,7 +4817,7 @@ _git-diff-index () {
   # to given tree-ish?  This should be done for git-diff as well, in that case.
   _arguments -S \
     $revision_options \
-    '--cached[do not consider the work tree at all]' \
+    "--cached[don't consider the work tree at all]" \
     '-m[flag non-checked-out files as up-to-date]' \
     ': :__git_tree_ishs' \
     '*: :__git_cached_files'
@@ -4388,7 +4834,7 @@ _git-diff-tree () {
   # NOTE: -r, -t, --root are actually parsed for all
   # __git_setup_revision_options, but only used by this command, so only have
   # them here.
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
     $revision_options \
     '-r[recurse into subdirectories]' \
     '(-r   )-t[disply tree objects in diff output]' \
@@ -4433,10 +4879,15 @@ _git-for-each-ref () {
   # TODO: Better completion for --format: should complete %(field) stuff, that
   # is, %(refname), %(objecttype), %(objectsize), %(objectname) with optional '*'
   # in front.
-  _arguments -w -S -s \
+  _arguments -S -s \
     '--count=[maximum number of refs to iterate over]: :__git_guard_number "maximum number of refs"' \
-    '--sort=[key to sort refs by]: :__git_ref_sort_keys' \
+    '*--sort=[key to sort refs by]: :__git_ref_sort_keys' \
     '--format=-[output format of ref information]:format' \
+    '*--points-at=[print only refs which point at the given object]:object:__git_commits' \
+    '*--merged=[print only refs that are merged]:object:__git_commits' \
+    '*--no-merged=[print only refs that are not merged]:object:__git_commits' \
+    '*--contains=[print only refs which contain the commit]:object:__git_commits' \
+    '--ignore-case[sorting and filtering are case-insensitive]' \
     '(-s --shell -p --perl --python --tcl)'{-s,--shell}'[use string literals suitable for sh]' \
     '(-s --shell -p --perl --python --tcl)'{-p,--perl}'[use string literals suitable for Perl]' \
     '(-s --shell -p --perl          --tcl)'--python'[use string literals suitable for Python]' \
@@ -4449,13 +4900,13 @@ _git-ls-files () {
   local no_empty_directory_opt=
 
   if (( words[(I)--directory] )); then
-    no_empty_directory_opt='--no-empty-directory[do not list empty directories]'
+    no_empty_directory_opt="--no-empty-directory[don't list empty directories]"
   fi
 
   # TODO: --resolve-undo is undocumented.
   # TODO: Replace _files with something more intelligent based on seen options.
   # TODO: Apply excludes like we do for git-clean.
-  _arguments -w -S -s \
+  _arguments -S -s \
     '(-c --cached)'{-c,--cached}'[show cached files in output]' \
     '(-d --deleted)'{-d,--deleted}'[show deleted files in output]' \
     '(-m --modified)'{-m,--modified}'[show modified files in output]' \
@@ -4463,6 +4914,7 @@ _git-ls-files () {
     '(-i --ignored)'{-i,--ignored}'[show ignored files in output]' \
     '(-s --stage --with-tree)'{-s,--stage}'[show stage files in output]' \
     '--directory[if a whole directory is classified as "other", show just its name]' \
+    '--eol[show line endings of files]' \
     $no_empty_directory_opt \
     '(-s --stage -u --unmerged --with-tree)'{-u,--unmerged}'[show unmerged files in output]' \
     '(-k --killed)'{-k,--killed}'[show killed files in output]' \
@@ -4475,19 +4927,24 @@ _git-ls-files () {
     '(-s --stage -u --unmerged)--with-tree=[treat paths removed since given tree-ish as still present]: :__git_tree_ishs' \
     '-v[identify each files status (hmrck?)]' \
     '--full-name[force paths to be output relative to the project top directory]' \
+    '--recurse-submodules[recurse through submodules]' \
     '--abbrev=[set minimum SHA1 display-length]: :__git_guard_number length' \
+    '--debug[show debugging data]' \
     '*:: :_files'
 }
 
 (( $+functions[_git-ls-remote] )) ||
 _git-ls-remote () {
   # TODO: repository needs fixing
-  _arguments -A '-*' \
+  _arguments \
+    '(-q --quiet)'{-q,--quiet}"[don't print remote URL]" \
+    '--upload-pack=[specify path to git-upload-pack on remote side]:remote path' \
     '(-h --heads)'{-h,--heads}'[show only refs under refs/heads]' \
     '(-t --tags)'{-t,--tags}'[show only refs under refs/tags]' \
-    '(-u --upload-pack)'{-u,--upload-pack=-}'[specify path to git-upload-pack on remote side]:remote path' \
+    "--refs[don't show peeled tags]" \
     '--exit-code[exit with status 2 when no matching refs are found in the remote repository]' \
     '--get-url[expand the URL of the given repository taking into account any "url.<base>.insteadOf" config setting]' \
+    '--symref[show underlying ref in addition to the object pointed by it]' \
     ': :__git_any_repositories' \
     '*: :__git_references'
 }
@@ -4497,7 +4954,7 @@ _git-ls-tree () {
   local curcontext=$curcontext state line ret=1
   declare -A opt_args
 
-  _arguments -w -C -S -s \
+  _arguments -C -S -s \
     '(-t)-d[do not show children of given tree (implies -t)]' \
     '-r[recurse into subdirectories]' \
     '-t[show tree entries even when going to recurse them]' \
@@ -4521,7 +4978,7 @@ _git-ls-tree () {
 
 (( $+functions[_git-merge-base] )) ||
 _git-merge-base () {
-  _arguments -w -S -s \
+  _arguments -S -s \
     '(-a --all)'{-a,--all}'[display all common ancestors]' \
     '--octopus[compute best common ancestors of all supplied commits]' \
     '--is-ancestor[tell if A is ancestor of B (by exit status)]' \
@@ -4595,7 +5052,7 @@ _git-show-index () {
 _git-show-ref () {
   _arguments -S \
     - list \
-      '(-h --head)'{-h,--head}'[show the HEAD reference, even if it would normally be filtered out]' \
+      '--head[show the HEAD reference, even if it would normally be filtered out]' \
       '--tags[show only refs/tags]' \
       '--heads[show only refs/heads]' \
       '(-d --dereference)'{-d,--dereference}'[dereference tags into object IDs as well]' \
@@ -4610,7 +5067,7 @@ _git-show-ref () {
 
 (( $+functions[_git-unpack-file] )) ||
 _git-unpack-file () {
-  _arguments -A '-*' \
+  _arguments \
     '(:)-h[display usage information]' \
     '(-): :__git_blobs'
 }
@@ -4627,7 +5084,7 @@ _git-var () {
 
 (( $+functions[_git-verify-pack] )) ||
 _git-verify-pack () {
-  _arguments -w -S -s \
+  _arguments -S -s \
     '(-v --verbose)'{-v,--verbose}'[show objects contained in pack]' \
     '(-s --stat-only)'{-s,--stat-only}'[do not verify pack contents; only display histogram of delta chain length]' \
     '*:index file:_files -g "*.idx"'
@@ -4675,6 +5132,7 @@ _git-fetch-pack () {
   # TODO: Limit * to __git_head_references?
   _arguments -A '-*' \
     '--all[fetch all remote refs]' \
+    '--stdin[take the list of refs from stdin]' \
     '(-q --quiet)'{-q,--quiet}'[make output less verbose]' \
     '(-k --keep)'{-k,--keep}'[do not invoke git-unpack-objects on received data]' \
     '--thin[fetch a thin pack]' \
@@ -4682,6 +5140,7 @@ _git-fetch-pack () {
     '(--upload-pack --exec)'{--upload-pack=-,--exec=-}'[specify path to git-upload-pack on remote side]:remote path' \
     '--depth=-[limit fetching to ancestor-chains not longer than given number]: :__git_guard_number "maximum ancestor-chain length"' \
     '--no-progress[do not display progress]' \
+    '--diag-url' \
     '-v[produce verbose output]' \
     ': :__git_any_repositories' \
     '*: :__git_references'
@@ -4694,26 +5153,38 @@ _git-http-backend () {
 
 (( $+functions[_git-send-pack] )) ||
 _git-send-pack () {
-  # TODO: --mirror is undocumented.
-  # TODO: --stateless-rpc is undocumented.
-  # TODO: --helper-status is undocumented.
+  local -a sign
+  sign=(
+    {yes,true}'\:always,\ and\ fail\ if\ unsupported\ by\ server'
+    {no,false}'\:never'
+    if-asked'\:iff\ supported\ by\ server'
+  )
   _arguments -A '-*' \
+    '(-v --verbose)'{-v,--verbose}'[produce verbose output]' \
+    '(-q --quiet)'{-q,--quiet}'[be more quiet]' \
     '(--receive-pack --exec)'{--receive-pack=-,--exec=-}'[specify path to git-receive-pack on remote side]:remote path' \
+    '--remote[specify remote name]:remote' \
     '--all[update all refs that exist locally]' \
-    '--dry-run[do everything except actually sending the updates]' \
-    '--force[update remote orphaned refs]' \
-    '-v[produce verbose output]' \
+    '(-n --dry-run)'{-n,--dry-run}'[do everything except actually sending the updates]' \
+    '--mirror[mirror all refs]' \
+    '(-f --force)'{-f,--force}'[update remote orphaned refs]' \
+    "(--no-signed --signed)--sign=-[GPG sign the push]::signing enabled:(($^^sign))" \
+    '(--no-signed --sign)--signed[GPG sign the push]' \
+    "(--sign --signed)--no-signed[don't GPG sign the push]" \
+    '--progress[force progress reporting]' \
     '--thin[send a thin pack]' \
-    '--mirror[undocumented]' \
-    '--stateless-rpc[undocumented]' \
-    '--helper-status[undocumented]' \
+    '--atomic[request atomic transaction on remote side]' \
+    '--stateless-rpc[use stateless RPC protocol]' \
+    '--stdin[read refs from stdin]' \
+    '--helper-status[print status from remote helper]' \
+    '--force-with-lease=[require old value of ref to be at specified value]:refname\:expect' \
     ': :__git_any_repositories' \
     '*: :__git_remote_references'
 }
 
 (( $+functions[_git-update-server-info] )) ||
 _git-update-server-info () {
-  _arguments -w -S -s \
+  _arguments -S -s \
     '(-f --force)'{-f,--force}'[update the info files from scratch]'
 }
 
@@ -4751,6 +5222,7 @@ _git-receive-pack () {
   # TODO: --advertise-refs is undocumented.
   # TODO: --stateless-rpc is undocumented.
   _arguments -A '-*' \
+    '(-q --quiet)'{-q,--quiet}'[be quiet]' \
     '--advertise-refs[undocumented]' \
     '--stateless-rpc[undocumented]' \
     ':directory to sync into:_directories'
@@ -4809,13 +5281,11 @@ _git-upload-archive () {
 
 (( $+functions[_git-upload-pack] )) ||
 _git-upload-pack () {
-  # TODO: --advertise-refs is undocumented.
-  # TODO: --stateless-rpc is undocumented.
   _arguments -S -A '-*' \
-    '--strict[do not try <directory>/.git/ if <directory> is not a git directory' \
-    '--timeout=-[interrupt transfer after given number of seconds of inactivity]: :__git_guard_number "inactivity timeout"' \
-    '--advertise-refs[undocumented]' \
-    '--stateless-rpc[undocumented]' \
+    '--stateless-rpc[quit after a single request/response exchange]' \
+    '--advertise-refs[exit immediately after initial ref advertisement]' \
+    "--strict[don't try <directory>/.git/ if <directory> is not a git directory]" \
+    '--timeout=-[interrupt transfer after period of inactivity]: :__git_guard_number "inactivity timeout (seconds)"' \
     ': :_directories'
 }
 
@@ -4875,15 +5345,15 @@ _git-check-ref-format () {
     '(--no-allow-onelevel)--allow-onelevel[accept one-level refnames]' \
     '(--allow-onelevel)--no-allow-onelevel[do not accept one-level refnames]' \
     '--refspec-pattern[interpret <refname> as a reference name pattern for a refspec]' \
-    '--normalize[Normalize refname by removing leading slashes]' \
+    '--normalize[normalize refname by removing leading slashes]' \
     '--branch[expand previous branch syntax]' \
     ': :__git_references'
 }
 
 (( $+functions[_git-fmt-merge-msg] )) ||
 _git-fmt-merge-msg () {
-  _arguments -w -S -s \
-    '(      --no-log)--log[display one-line descriptions from actual commits being merged]' \
+  _arguments -S -s \
+    '(      --no-log)--log=-[display one-line descriptions from actual commits being merged]::number of commits [20]' \
     '(--log         )--no-log[do not display one-line descriptions from actual commits being merged]' \
     '(-m --message)'{-m+,--message=}'[use given message instead of branch names for first line in log message]:message' \
     '(-F --file)'{-F,--file}'[specify list of merged objects from file]: :_files'
@@ -4893,10 +5363,12 @@ _git-fmt-merge-msg () {
 _git-mailinfo () {
   # TODO: --no-inbody-headers is undocumented.
   _arguments -A '-*' \
-    '-k[do not strip/add \[PATCH\] from first line of commit message]' \
+    '(-b)-k[prevent removal of cruft from Subject: header]' \
+    '(-k)-b[limit stripping of bracketed strings to the word PATCH]' \
     '(-u --encoding)-u[encode commit information in UTF-8]' \
     '(-u --encoding)--encoding=-[encode commit information in given encoding]: :__git_encodings' \
     '-n[disable all charset re-coding of metadata]' \
+    '(-m --message-id)'{-m,--message-id}'[copy the Message-ID header at the end of the commit message]' \
     '(           --no-scissors)--scissors[remove everything in body before a scissors line]' \
     '(--scissors              )--no-scissors[do not remove everything in body before a scissors line]' \
     '--no-inbody-headers[undocumented]' \
@@ -4922,7 +5394,9 @@ _git-merge-one-file () {
 
 (( $+functions[_git-patch-id] )) ||
 _git-patch-id () {
-  _message 'no arguments allowed; accepts patch on standard input'
+   _arguments \
+     '--stable[use a sum of hashes unaffected by diff ordering]' \
+     '--unstable[use patch-id compatible with git 1.9 and older]'
 }
 
 # NOTE: git-sh-setup isn't a user command.
@@ -5116,8 +5590,10 @@ _git_commands () {
     stash:'stash away changes to dirty working directory'
     status:'show working-tree status'
     submodule:'initialize, update, or inspect submodules'
-    tag:'create, list, delete or verify tag object signed with GPG')
-
+    subtree:'split repository into subtrees and merge them'
+    tag:'create, list, delete or verify tag object signed with GPG'
+    worktree:'manage multiple working dirs attached to the same repository'
+  )
   ancillary_manipulator_commands=(
     config:'get and set repository or global options'
     fast-export:'data exporter'
@@ -5640,6 +6116,64 @@ __git_commit_objects_prefer_recent () {
   __git_recent_commits $argument_array_names || __git_commit_objects
 }
 
+# This function returns in $reply recently-checked-out refs' names, in order
+# from most to least recent.
+(( $+functions[__git_recent_branches__names] )) ||
+__git_recent_branches__names()
+{
+  # This parameter expansion does the following:
+  # 1. Obtains the last 1000 'checkout' operations from the reflog
+  # 2. Extracts the move-source from each
+  # 3. Eliminates duplicates
+  # 4. Eliminates commit hashes (leaving only ref names)
+  #    [This step is done again by the caller.]
+  #
+  # See workers/38592 for an equivalent long-hand implementation, and the rest
+  # of that thread for why this implementation was chosen instead.
+  #
+  # Note: since we obtain the "from" part of the reflog, we only obtain heads, not tags.
+  reply=(${${(u)${${(M)${(0)"$(_call_program reflog git reflog -1000 -z --pretty='%gs')"}:#(#s)checkout: moving from *}#checkout: moving from }%% *}:#[0-9a-f](#c40)})
+}
+
+(( $+functions[__git_recent_branches] )) ||
+__git_recent_branches() {
+  local -a branches
+  local -A descriptions
+  local -a reply
+  local -aU valid_ref_names_munged=( ${"${(f)"$(_call_program valid-ref-names 'git for-each-ref --format="%(refname)" refs/heads/')"}"#refs/heads/} )
+
+  # 1. Obtain names of recently-checked-out branches from the reflog.
+  # 2. Remove ref names that no longer exist from the list.
+  #    (We must do this because #3 would otherwise croak on them.)
+  __git_recent_branches__names; branches=( ${(@)reply:*valid_ref_names_munged} )
+
+  # 3. Early out if no matches.
+  if ! (( $+branches[1] )); then
+    # This can happen in a fresh repository (immediately after 'clone' or 'init') before
+    # any 'checkout' commands were run in it.
+    return 1
+  fi
+
+  # 4. Obtain log messages for all of them in one shot.
+  # TODO: we'd really like --sort=none here...  but git doesn't support such a thing.
+  # The \n removal is because for-each-ref prints a \n after each entry.
+  descriptions=( ${(0)"$(_call_program all-descriptions "git --no-pager for-each-ref --format='%(refname)%00%(subject)%00'" refs/heads/${(q)^branches} "--")"//$'\n'} )
+
+  # 5. Synthesize the data structure _describe wants.
+  local -a branches_colon_descriptions
+  local branch
+  for branch in ${branches} ; do
+    branches_colon_descriptions+="${branch//:/\:}:${descriptions[refs/heads/${(b)branch}]}"
+  done
+
+  _describe -V -t recent-branches "recent branches" branches_colon_descriptions
+}
+
+(( $+functions[__git_commits_prefer_recent] )) ||
+__git_commits_prefer_recent () {
+  _alternative 'recent-branches::__git_recent_branches' 'commits::__git_commits'
+}
+
 (( $+functions[__git_commits] )) ||
 __git_commits () {
   local -a argument_array_names
@@ -5732,7 +6266,7 @@ __git_recent_commits () {
 
   # Careful: most %d will expand to the empty string.  Quote properly!
   # NOTE: we could use %D directly, but it's not available in git 1.9.1 at least.
-  commits=("${(f)"$(_call_program commits git --no-pager log $commit_opts -20 --format='%h%n%d%n%s\ \(%cr\)%n%p')"}")
+  commits=("${(f)"$(_call_program commits git --no-pager log ${(q)commit_opts} -20 --format='%h%n%d%n%s\ \(%cr\)%n%p')"}")
   __git_command_successful $pipestatus || return 1
 
   for i j k parents in "$commits[@]" ; do
@@ -5792,11 +6326,11 @@ __git_recent_commits () {
   ret=1
   # Resetting expl to avoid it 'leaking' from one line to the next.
   expl=()
+  _describe -V -t commits 'recent commit object name' descr && ret=0
+  expl=()
   _wanted commit-tags expl 'commit tag' compadd "$@" -a - tags && ret=0
   expl=()
   _wanted heads expl 'head' compadd "$@" -a - heads && ret=0
-  expl=()
-  _describe -V -t commits 'recent commit object name' descr && ret=0
   return $ret
 }
 
@@ -5906,8 +6440,7 @@ __git_submodules () {
   local expl
   declare -a submodules
 
-  submodules=(${${${(f)"$(_call_program submodules git submodule 2>/dev/null)"}#?* }%% *})
-  __git_command_successful $pipestatus || return 1
+  submodules=( ${${${(f)"$(_call_program submodules git submodule)"}#?* }%% *} )
 
   _wanted submodules expl submodule compadd "$@" -a - submodules
 }
@@ -5942,7 +6475,7 @@ __git_tags_of_type () {
 
   type=$1; shift
 
-  tags=(${${(M)${(f)"$(_call_program $type-tag-refs "git for-each-ref --format='%(*objecttype)%(objecttype) %(refname)' refs/tags 2>/dev/null")"}:#$type(tag|) *}#$type(tag|) refs/tags/})
+  tags=(${${(M)${(f)"$(_call_program ${(q)type}-tag-refs "git for-each-ref --format='%(*objecttype)%(objecttype) %(refname)' refs/tags 2>/dev/null")"}:#$type(tag|) *}#$type(tag|) refs/tags/})
   __git_command_successful $pipestatus || return 1
 
   _wanted $type-tags expl "$type tag" compadd -M 'r:|/=**' "$@" -a - tags
@@ -5968,9 +6501,10 @@ __git_references () {
     _git_refs_cache_pwd=$PWD
   fi
 
-  _wanted references expl 'reference' compadd -a - _git_refs_cache
+  _wanted references expl 'reference' compadd -M 'r:|/=**' -a - _git_refs_cache
 }
 
+# ### currently unused; are some callers of __git_references supposed to call this function?
 (( $+functions[__git_local_references] )) ||
 __git_local_references () {
   local expl
@@ -5981,7 +6515,7 @@ __git_local_references () {
     _git_local_refs_cache_pwd=$PWD
   fi
 
-  _wanted references expl 'reference' compadd -a - _git_local_refs_cache
+  _wanted references expl 'reference' compadd -M 'r:|/=**' -a - _git_local_refs_cache
 }
 
 (( $+functions[__git_remote_references] )) ||
@@ -6054,12 +6588,12 @@ __git_files () {
   local pref=$gitcdup$gitprefix$PREFIX
 
   # First allow ls-files to pattern-match in case of remote repository
-  files=(${(0)"$(_call_program files git ls-files -z --exclude-standard $opts -- ${pref:+$pref\\\*} 2>/dev/null)"})
+  files=(${(0)"$(_call_program files git ls-files -z --exclude-standard ${(q)opts} -- ${(q)${pref:+$pref\\\*}} 2>/dev/null)"})
   __git_command_successful $pipestatus || return
 
   # If ls-files succeeded but returned nothing, try again with no pattern
   if [[ -z "$files" && -n "$pref" ]]; then
-    files=(${(0)"$(_call_program files git ls-files -z --exclude-standard $opts -- 2>/dev/null)"})
+    files=(${(0)"$(_call_program files git ls-files -z --exclude-standard ${(q)opts} -- 2>/dev/null)"})
     __git_command_successful $pipestatus || return
   fi
 
@@ -6176,7 +6710,7 @@ __git_tree_files () {
   shift
   (( at_least_one_tree_added = 0 ))
   for tree in $*; do
-    tree_files+=(${(ps:\0:)"$(_call_program tree-files git ls-tree -r $extra_args --name-only -z $tree 2>/dev/null)"})
+    tree_files+=(${(ps:\0:)"$(_call_program tree-files git ls-tree -r ${(q)extra_args} --name-only -z ${(q)tree} 2>/dev/null)"})
     __git_command_successful $pipestatus && (( at_least_one_tree_added = 1 ))
   done
 
@@ -6226,6 +6760,13 @@ __git_any_repositories () {
     'remote-repositories::__git_remote_repositories'
 }
 
+(( $+functions[__git_any_repositories_or_references] )) ||
+__git_any_repositories_or_references () {
+  _alternative \
+    'repositories::__git_any_repositories' \
+    'references::__git_references'
+}
+
 # Common Guards
 
 (( $+functions[__git_guard] )) ||
@@ -6247,7 +6788,7 @@ __git_guard () {
 
 __git_guard_branch-name () {
   if [[ -n $PREFIX$SUFFIX ]]; then
-    _call_program check-ref-format git check-ref-format "refs/heads/$PREFIX$SUFFIX" &>/dev/null
+    _call_program check-ref-format git check-ref-format "refs/heads/"${(q)PREFIX}${(q)SUFFIX} &>/dev/null
     (( ${#pipestatus:#0} > 0 )) && return 1
   fi
 
@@ -6277,7 +6818,7 @@ __git_guard_number () {
 
 (( $+functions[__git_guard_bytes] )) ||
 __git_guard_bytes () {
-  _guard '[[:digit:]]#([kKmMgG]|)' $*
+  _guard '[[:digit:]]#([kKmMgG]|)' ${*:-size}
 }
 
 (( $+functions[__git_datetimes] )) ||
@@ -6307,7 +6848,9 @@ __git_setup_log_options () {
     '(--decorate              )--no-decorate[do not print out ref names of any commits that are shown]'
     '(          --no-follow)--follow[follow renames]'
     '(--follow             )--no-follow[do not follow renames]'
-    '--source[show which ref each commit is reached from]')
+    '--source[show which ref each commit is reached from]'
+    '-L+[trace the evolution of a line range or regex within a file]:range'
+  )
 }
 
 (( $+functions[__git_setup_diff_options] )) ||
@@ -6339,7 +6882,8 @@ __git_setup_diff_options () {
     $diff_types'--name-only[show only names of changed files]'
     $diff_types'--name-status[show only names and status of changed files]'
     '--submodule=-[select output format for submodule differences]::format:((short\:"show pairs of commit names"
-                                                                            log\:"list commits like git submodule does"))'
+									     log\:"list commits like git submodule does"
+									     diff\:"show differences"))'
     '(        --no-color --color-words)--color=-[show colored diff]:: :__git_color_whens'
     '(--color            --color-words)--no-color[turn off colored diff]'
     '--word-diff=-[show word diff]::mode:((color\:"highlight changed words using color"
@@ -6354,7 +6898,7 @@ __git_setup_diff_options () {
     '(--full-index)--binary[in addition to --full-index, output binary diffs for git-apply]'
     '--abbrev=[set minimum SHA1 display-length]: :__git_guard_number length'
     '(-B --break-rewrites)'{-B-,--break-rewrites=-}'[break complete rewrite changes into pairs of given size]:: :__git_guard_number size'
-    '(-M --find-renames)'{-M-,--find-renames=-}'[Detect renames with given scope]:: :__git_guard_number size'
+    '(-M --find-renames)'{-M-,--find-renames=-}'[detect renames with given scope]:: :__git_guard_number size'
     '(-C --find-copies)'{-C-,--find-copies=-}'[detect copies as well as renames with given scope]:: :__git_guard_number size'
     '--find-copies-harder[try harder to find copies]'
     '(-D --irreversible-delete)'{-D,--irreversible-delete}'[omit the preimage for deletes]'
@@ -6374,7 +6918,6 @@ __git_setup_diff_options () {
     '--ignore-blank-lines[do not show hunks that add or remove blank lines]'
     '--inter-hunk-context=[combine hunks closer than n lines]:n'
     '--exit-code[report exit code 1 if differences, 0 otherwise]'
-    '(--exit-code)--quiet[disable all output]'
     '(           --no-ext-diff)--ext-diff[allow external diff helper to be executed]'
     '(--ext-diff              )--no-ext-diff[disallow external diff helper to be executed]'
     '(--textconv --no-textconv)--textconv[allow external text conversion filters to be run when comparing binary files]'
@@ -6384,7 +6927,7 @@ __git_setup_diff_options () {
     '(--no-prefix)--dst-prefix=[use given prefix for destination]:prefix'
     '(--src-prefix --dst-prefix)--no-prefix[do not show any source or destination prefix]'
 
-    '(-c,--cc)'{-c,--cc}'[combined diff format for merge commits]'
+    '(-c --cc)'{-c,--cc}'[combined diff format for merge commits]'
 
     # TODO: --output is undocumented.
     '--output[undocumented]:undocumented')
@@ -6499,7 +7042,6 @@ __git_setup_revision_options () {
     '(-v --header)'{--pretty=-,--format=-}'[pretty print commit messages]::format:__git_format_placeholders'
     '(--abbrev-commit --no-abbrev-commit)--abbrev-commit[show only partial prefixes of commit object names]'
     '(--abbrev-commit --no-abbrev-commit)--no-abbrev-commit[show the full 40-byte hexadecimal commit object name]'
-    '(--abbrev --no-abbrev)--abbrev=[set minimum SHA1 display-length (for use with --abbrev-commit)]: :__git_guard_number length'
     '(--abbrev --no-abbrev)--no-abbrev[show the full 40-byte hexadecimal commit object name]'
     '--oneline[shorthand for --pretty=oneline --abbrev-commit]'
     '--encoding=-[output log messages in given encoding]:: :__git_encodings'
@@ -6588,39 +7130,45 @@ __git_setup_merge_options () {
   merge_options=(
     '(         --no-commit)--commit[perform the merge and commit the result]'
     '(--commit            )--no-commit[perform the merge but do not commit the result]'
-    '(         --no-edit)--edit[open an editor to change the commit message]'
-    '(--edit            )--no-edit[do not open an editor to change the commit message]'
+    '(         --no-edit -e)--edit[open an editor to change the commit message]'
+    "(--edit             -e)--no-edit[don't open an editor to change the commit message]"
     '(     --no-ff)--ff[do not generate a merge commit if the merge resolved as a fast-forward]'
     '(--ff        )--no-ff[generate a merge commit even if the merge resolved as a fast-forward]'
-    '(      --no-log)--log[fill in one-line descriptions of the commits being merged in the log message]'
+    '(      --no-log)--log=-[add entries from shortlog to merge commit message]::entries to add'
     '(--log         )--no-log[do not list one-line descriptions of the commits being merged in the log message]'
     '(-n --no-stat)--stat[show a diffstat at the end of the merge]'
     '(--stat -n --no-stat)'{-n,--no-stat}'[do not show diffstat at the end of the merge]'
     '(         --no-squash)--squash[merge, but do not commit]'
     '(--squash            )--no-squash[merge and commit]'
     '--ff-only[refuse to merge unless HEAD is up to date or merge can be resolved as a fast-forward]'
-    '(-S --gpg-sign --no-gpg-sign)'{-S-,--gpg-sign=}'[GPG-sign the commit]::key id' \
-    '(-S --gpg-sign --no-gpg-sign)--no-gpg-sign[do not GPG-sign the commit]' \
+    '(-S --gpg-sign --no-gpg-sign)'{-S-,--gpg-sign=-}'[GPG-sign the commit]::key id'
+    "(-S --gpg-sign --no-gpg-sign)--no-gpg-sign[don't GPG-sign the commit]"
     '*'{-s,--strategy=}'[use given merge strategy]:merge strategy:__git_merge_strategies'
-    '*'{-X,--strategy-option=}'[pass merge-strategy-specific option to merge strategy]'
+    '*'{-X,--strategy-option=}'[pass merge-strategy-specific option to merge strategy]:option'
     '(--verify-signatures)--verify-signatures[verify the commits being merged or abort]'
     '(--no-verify-signatures)--no-verify-signatures[do not verify the commits being merged]'
     '(-q --quiet -v --verbose)'{-q,--quiet}'[suppress all output]'
-    '(-q --quiet -v --verbose)'{-v,--verbose}'[output additional information]')
+    '(-q --quiet -v --verbose)'{-v,--verbose}'[output additional information]'
+    '--allow-unrelated-histories[allow merging unrelated histories]'
+  )
 }
 
 (( $+functions[__git_setup_fetch_options] )) ||
 __git_setup_fetch_options () {
   fetch_options=(
-    '(: *)--all[fetch all remotes]'
+    '(: * -m --multiple)--all[fetch all remotes]'
     '(-a --append)'{-a,--append}'[append ref names and object names of fetched refs to "$GIT_DIR/FETCH_HEAD"]'
+    '(-j --jobs)'{-j+,--jobs=}'[specify number of submodules fetched in parallel]:jobs'
     '--depth=[deepen the history of a shallow repository by the given number of commits]: :__git_guard_number depth'
     '--unshallow[convert a shallow clone to a complete one]'
+    '--update-shallow[accept refs that update .git/shallow]'
+    '--refmap=[specify refspec to map refs to remote tracking branches]:refspec'
+    '(-4 --ipv4 -6 --ipv6)'{-4,--ipv4}'[use IPv4 addresses only]'
+    '(-4 --ipv4 -6 --ipv6)'{-6,--ipv6}'[use IPv6 addresses only]'
     '--dry-run[show what would be done, without making any changes]'
     '(-f --force)'{-f,--force}'[allow refs that are not ancestors to be updated]'
     '(-k --keep)'{-k,--keep}'[keep downloaded pack]'
     '(-p --prune)'{-p,--prune}'[remove any remote tracking branches that no longer exist remotely]'
-    '(-n --no-tags -t --tags)'{-n,--no-tags}'[disable automatic tag following]'
     '(--no-tags -t --tags)'{-t,--tags}'[fetch remote tags]'
     '(-u --update-head-ok)'{-u,--update-head-ok}'[allow updates of current branch head]'
     '--upload-pack=[specify path to git-upload-pack on remote side]:remote path'
@@ -6633,7 +7181,7 @@ __git_setup_fetch_options () {
     '--submodule-prefix=-[prepend <path> to paths printed in informative messages]:submodule prefix path:_files -/'
     '(-q --quiet -v --verbose --progress)'{-q,--quiet}'[suppress all output]'
     '(-q --quiet -v --verbose)'{-v,--verbose}'[output additional information]'
-    '(-q --quiet)--progress[output progress information]')
+    '(-q --quiet)--progress[force progress reporting]')
 }
 
 (( $+functions[__git_setup_apply_options] )) ||
@@ -6661,7 +7209,7 @@ __git_config_get_regexp () {
   [[ -n $opts[-a] ]] || opts[-a]='.[^.]##'
   [[ $1 == -- ]] && shift
 
-  set -A $2 ${${${(0)"$(_call_program ${3:-$2} "git config -z --get-regexp -- '$1'")"}#${~opts[-b]}}%%${~opts[-a]}$'\n'*}
+  set -A $2 ${${${(0)"$(_call_program ${3:-$2} "git config -z --get-regexp -- ${(q)1}")"}#${~opts[-b]}}%%${~opts[-a]}$'\n'*}
 }
 
 (( $+functions[__git_config_sections] )) ||
@@ -6918,7 +7466,7 @@ __git_sendemail_suppresscc_values () {
 
 (( $+functions[__git_sendmail_smtpserver_values] )) ||
 __git_sendmail_smtpserver_values() {
-  _alternative "smtp hosts:host:_hosts" "sendmail command: :_absolute_command_paths"
+  _alternative "hosts:smtp host:_hosts" "commands: :_absolute_command_paths"
 }
 
 (( $+functions[__git_colors] )) ||
@@ -6942,7 +7490,7 @@ _git() {
   if (( CURRENT > 2 )); then
     local -a aliases
     local -A git_aliases
-    local k v
+    local a k v
     aliases=(${(0)"$(_call_program aliases git config -z --get-regexp '\^alias\.')"})
     for a in ${aliases}; do
         k="${${a/$'\n'*}/alias.}"
@@ -6978,7 +7526,7 @@ _git() {
       '(- :)--version[display version information]' \
       '(- :)--help[display help message]' \
       '-C[run as if git was started in given path]: :_directories' \
-      '-c[pass configuration parameter to command]:parameter' \
+      '*-c[pass configuration parameter to command]: :->configuration' \
       '--exec-path=-[path containing core git-programs]:: :_directories' \
       '(: -)--man-path[print the manpath for the man pages for this version of Git and exit]' \
       '(: -)--info-path[print the path where the info files are installed and exit]' \
@@ -7000,7 +7548,7 @@ _git() {
         ;;
       (option-or-argument)
         curcontext=${curcontext%:*:*}:git-$words[1]:
-
+	(( $+opt_args[--git-dir] )) && local -x GIT_DIR=$opt_args[--git-dir]
 	if ! _call_function ret _git-$words[1]; then
 	  if zstyle -T :completion:$curcontext: use-fallback; then
 	    _default && ret=0
@@ -7009,6 +7557,18 @@ _git() {
 	  fi
         fi
         ;;
+      (configuration)
+        if compset -P 1 '*='; then
+          __git_config_value && ret=0
+        else
+          if compset -S '=*'; then
+            __git_config_option && ret=0 # don't move cursor if we completed just the "foo." in "foo.bar.baz=value"
+            compstate[to_end]=''
+          else
+            __git_config_option -S '=' && ret=0
+          fi
+        fi
+        ;;
     esac
   else
     _call_function ret _$service
@@ -7022,7 +7582,7 @@ declare -gUa _git_third_party_commands
 _git_third_party_commands=()
 
 local file
-for file in ${^fpath}/_git-*~(*~|*.zwc)(.N); do
+for file in ${^fpath}/_git-*~(*~|*.zwc)(-.N); do
   local name=${${file:t}#_git-}
   if (( $+_git_third_party_commands[$name] )); then
     continue
diff --git a/Completion/Unix/Command/_global b/Completion/Unix/Command/_global
index c4714cb05..ffd8c0dce 100644
--- a/Completion/Unix/Command/_global
+++ b/Completion/Unix/Command/_global
@@ -14,7 +14,7 @@ _arguments \
   '(--color)--color=-[color matches]::color:(always auto never)' \
   '(-d --definition)'{-d,--definition}'[print locations of definitions]' \
   '(-e --regexp :)'{-e,--regexp}'[specify pattern]:pattern:_global_tags' \
-  '(--encode-path)--encode-path=-[encode path charcters in hexadecimal representation]:format' \
+  '(--encode-path)--encode-path=-[encode path characters in hexadecimal representation]:format' \
   '(-F --first-match)'{-f,--first-match}'[stop searching if tag is found in current tag file]' \
   '(--from-here)--from-here=-[decide tag type by context]:line_path:' \
   '(-G --basic-regexp :)'{-G,--basic-regexp}'[specify basic regexp to use]:word:_global_tags' \
@@ -23,9 +23,9 @@ _arguments \
   '(-i --ignore-case)'{-i,--ignore-case}'[ignore case in patterns]' \
   '(-L --file-list)'{-L,--file-list}'[obtain files from file in addition to the arguments]:file:_files' \
   '(-l --local)'{-l,--local}'[print just objects which exist under the current directory]' \
-  '(--literal)--literal[use leteral search instead of regexp search]' \
+  '(--literal)--literal[use literal search instead of regexp search]' \
   '(-M --match-case)'{-m,--match-case}'[enable case sensitive search]' \
-  '(--match-part)--match-part=-[speficy how path name completion should match]::part:(first last all)' \
+  '(--match-part)--match-part=-[specify how path name completion should match]::part:(first last all)' \
   '(-n --nofilter)'{-n,--nofilter}'[suppress sort filter and path conversion filter]' \
   '(-O --only-other)'{-O,--only-other}'[search only text files]' \
   '(-o --other)'{-o,--other}'[search in other files, not just source files (with -g)]' \
diff --git a/Completion/Unix/Command/_gpg b/Completion/Unix/Command/_gpg
index fe236d5bf..fe6084f20 100644
--- a/Completion/Unix/Command/_gpg
+++ b/Completion/Unix/Command/_gpg
@@ -62,7 +62,7 @@ fi
   '--desig-revoke[generate a designated revocation certificate]'
   '--export[export all key from all keyrings]'
   '--send-keys[send keys to a keyserver]:key attachment:->public-keyids'
-  '--export-secret-keys:key attahment:->secret-keys'
+  '--export-secret-keys:key attachment:->secret-keys'
   '--export-secret-subkeys:key attachment:->secret-keys'
   '--import[import a gpg key from a file]:_files attachment:_files'
   '--fast-import[import a file without build trustdb]:_files attachment:_files'
diff --git a/Completion/Unix/Command/_gphoto2 b/Completion/Unix/Command/_gphoto2
index 314d83e3a..788c9702f 100644
--- a/Completion/Unix/Command/_gphoto2
+++ b/Completion/Unix/Command/_gphoto2
@@ -1,47 +1,96 @@
 #compdef gphoto2
 
-_arguments -s \
+local curcontext="$curcontext" ret=1
+local -a state line values expl
+typeset -A opt_args
+
+_arguments -s -C \
+  '(-)'{-\?,--help}'[display help information]' \
+  '(-)'{-h,--usage}'[display a short usage message]' \
   '--debug[turn on debugging]' \
+  '--debug-loglevel=[set debug level]:debug level:(error debug data all)' \
+  '--debug-logfile=[name of file to write debug info to]:file:_files' \
   '(-q --quiet)'{-q,--quiet}'[quiet output]' \
-  '(-v --version)'{-v,--version}'[display version and exit]' \
-  '(-h --help)'{-h,--help}'[display a short usage message]' \
-  '--list-cameras[list supported camera models]' \
-  '--list-ports[list supported port devices]' \
+  '--hook-script=[hook script to call after downloads, captures, etc.]:file:_files' \
   '--stdout[send file to stdout]' \
   '--stdout-size[print filesize before data]' \
   '--auto-detect[list auto-detected cameras]' \
-  '--port[specify port device]:port' \
-  '--speed[specify serial transfer speed]:speed' \
-  '--camera[specify camera model]:camera model' \
-  '--filename[specify pattern to save file as]:pattern' \
-  '--usbid[override USB IDs (expert only)]:usbid' \
+  '--show-exif=[show EXIF information of JPEG images]:range' \
+  '--show-info=[show image information, like width, height, and capture time]:range' \
+  '--summary[summary of camera status]' \
+  '--manual[camera driver manual]' \
+  '--about[show information about the camera driver]' \
+  '--storage-info[show storage information]' \
+  '--shell[start the gphoto2 shell]' \
+  '(-)'{-v,--version}'[display version and exit]' \
+  '--list-cameras[list supported camera models]' \
+  '--list-ports[list supported port devices]' \
   '(-a --abilities)'{-a,--abilities}'[display camera abilities]' \
-  '(-f --folder)'{-f,--folder}'[specify camera folder]:folder' \
-  '(-R --recurse --no-recurse)'{-R,--recurse}'[recursion (default for download)]' \
-  '(-R --recurse)--no-recurse[no recursion (default for deletion)]' \
+  '--port=[specify port device]:port:->ports' \
+  '--speed=[specify serial transfer speed]:speed' \
+  '--camera=[specify camera model]:camera model:->models' \
+  '--usbid=[override USB IDs (expert only)]:usbid' \
+  '--list-config[list configuration tree]' \
+  '--list-all-config[dump full configuration tree]' \
+  '--get-config=[get configuration value]:string' \
+  '--set-config=[set configuration value or index in choices]:string' \
+  '--set-config-index=[set configuration value index in choices]:string' \
+  '--set-config-value=[set configuration value]:string' \
+  '--config[start curses configuration menu]' \
+  '--reset[reset device port]' \
+  '--keep[keep images on camera after capturing]' \
+  '--keep-raw[keep the RAW images on the camera with --capture-image-and-download]' \
+  '--no-keep[remove images from camera after capturing]' \
+  '--wait-event=-[wait for event from camera]::count' \
+  '--wait-event-and-download=-[wait for event from camera and download new images]::count' \
+  '--capture-preview[capture a quick preview]' \
+  '--show-preview[capture a quick preview and display using ASCII art]' \
+  '(-B --bulb)'{-B,--bulb=}'[set bulb exposure time]:exposure time (seconds)' \
+  '(-F --frames)'{-F,--frames=}'[set number of frames to capture]:frames [unlimited]' \
+  '(-I --interval)'{-I,--interval=}'[set capture interval in seconds]:interval (seconds)' \
+  '--reset-interval[reset capture interval on signal]' \
+  '--capture-image[capture an image]' \
+  '--trigger-capture[trigger capture of an image]' \
+  '--capture-image-and-download[capture an image and download it]' \
+  '--capture-movie=-[capture a movie]:count or seconds' \
+  '--capture-sound[capture an audio clip]' \
+  '--capture-tethered=-[wait for shutter release on the camera and download]::count' \
   '(-l --list-folders)'{-l,--list-folders}'[list folders in folder]' \
   '(-L --list-files)'{-L,--list-files}'[list files in folder]' \
-  '(-m --mkdir)'{-m,--mkdir}'[create a directory]:directory' \
-  '(-r --rmdir)'{-r,--rmdir}'[remove a directory]:directory' \
-  '(-n --num-files)'{-n,--num-files}'[sisplay number of files]' \
-  '(-p --get-file)'{-p,--get-file}'[get files given in range]:range' \
+  '(-m --mkdir)'{-m,--mkdir=}'[create a directory]:directory' \
+  '(-r --rmdir)'{-r,--rmdir=}'[remove a directory]:directory' \
+  '(-n --num-files)'{-n,--num-files}'[display number of files]' \
+  '(-p --get-file)'{-p,--get-file=}'[get files given in range]:range' \
   '(-P --get-all-files)'{-P,--get-all-files}'[get all files from folder]' \
-  '(-t --get-thumbnail)'{-t,--get-thumbnail}'[get thumbnails given in range]:range' \
+  '(-t --get-thumbnail)'{-t,--get-thumbnail=}'[get thumbnails given in range]:range' \
   '(-T --get-all-thumbnails)'{-T,--get-all-thumbnails}'[get all thumbnails from folder]' \
-  '(-r --get-raw-data)'{-r,--get-raw-data}'[get raw data given in range]:range' \
+  '--get-metadata=[get metadata given in range]:range' \
+  '--get-all-metadata[get all metadata from folder]' \
+  '--upload-metadata=[upload metadata for file]:string' \
+  '--get-raw-data=[get raw data given in range]:range' \
   '--get-all-raw-data[get all raw data from folder]' \
-  '--get-audio-data[get audio data given in range]:range' \
+  '--get-audio-data=[get audio data given in range]:range' \
   '--get-all-audio-data[get all audio data from folder]' \
-  '--delete-file[delete files given in range]:range' \
-  '--delete-all-files[delete all files in folder]' \
-  '(-u --upload-file)'{-u,--upload-file}'[upload a file to camera]:file:_files' \
-  '--capture-preview[capture a quick preview]' \
-  '--capture-image[capture an image]' \
-  '--capture-movie[capture a movie]' \
-  '--capture-sound[capture an audio clip]' \
-  '--show-info[show info about given range]:range' \
-  '--summary[summary of camera status]' \
-  '--manual[camera driver manual]' \
-  '--about[show information about the camera driver]' \
-  '--shell[start the gphoto2 shell]'
+  '(-d --delete-file)'{-d,--delete-file=}'[delete files in given range]:range' \
+  '(-D --delete-all-files)'{-D,--delete-all-files}'[delete all files in folder]' \
+  '(-u --upload-file)'{-u,--upload-file=}'[upload a file to camera]:file:_files' \
+  '--filename=[specify pattern to save file as]:pattern:_date_formats' \
+  '(-f --folder)'{-f,--folder=}'[specify camera folder]:folder [/]' \
+  '(-R --recurse --no-recurse)'{-R,--recurse}'[recursion (default for download)]' \
+  '(-R --recurse)--no-recurse[no recursion (default for deletion)]' \
+  '--new[process new files only]' \
+  '--force-overwrite[overwrite files without asking]' \
+  '--skip-existing[skip files that already exist on local filesystem]' && ret=0
+
+case $state in
+  models)
+    _wanted models expl 'camera model' compadd \
+        ${${${(f)"$(_call_program models $words[1] --list-cameras)"}[3,-1]#*\"}%%\"*} && ret=0
+  ;;
+  ports)
+    values=( ${${${(f)"$(gphoto2 --list-ports)"}[4,-1]/:/\\:}/ ##/:} )
+    _describe -t ports 'port' values
+  ;;
+esac
 
+return ret
diff --git a/Completion/Unix/Command/_graphicsmagick b/Completion/Unix/Command/_graphicsmagick
index 9306acd9c..cc541d891 100644
--- a/Completion/Unix/Command/_graphicsmagick
+++ b/Completion/Unix/Command/_graphicsmagick
@@ -3,7 +3,7 @@
 local state line expl formats curcontext="$curcontext"
 typeset -A opt_args
 
-formats=jpg:jpeg:jp2:j2k:jpc:jpx:jpf:tiff:miff:ras:bmp:cgm:dcx:ps:eps:fig:fits:fpx:gif:mpeg:pbm:pgm:ppm:pcd:pcl:pdf:pcx:png:rad:rgb:rgba:rle:sgi:html:shtml:tga:ttf:uil:xcf:xwd:xbm:xpm:yuv
+formats=jpg:jpeg:jp2:j2k:jpc:jpx:jpf:tif:tiff:miff:ras:bmp:cgm:dcx:ps:eps:fig:fits:fpx:gif:mpeg:pbm:pgm:ppm:pcd:pcl:pdf:pcx:png:rad:rgb:rgba:rle:sgi:html:shtml:tga:ttf:uil:xcf:xwd:xbm:xpm:yuv
 
 if (( $# )); then
   _files "$@" -g "*.(#i)(${~formats//:/|})(-.)"
@@ -360,7 +360,7 @@ case "$words[2]" in
       '*:picture file:_imagemagick' && return
 
     if [[ "$state" = profile ]]; then
-      if compset -P '*:'; then
+      if compset -P 1 '*:'; then
 	_files
       else
 	_wanted prefixes expl 'profile type' compadd icc: iptc:
diff --git a/Completion/Unix/Command/_grep b/Completion/Unix/Command/_grep
index d987c2b1e..5f45ce9a8 100644
--- a/Completion/Unix/Command/_grep
+++ b/Completion/Unix/Command/_grep
@@ -1,7 +1,6 @@
-#compdef grep egrep fgrep bsdgrep zgrep, zegrep, zfgrep -value-,GREP_OPTIONS,-default-
-# Ulrik Haugen 2001
+#compdef grep egrep fgrep bsdgrep zgrep zegrep zfgrep ggrep gegrep gfgrep gzgrep gzegrep gzfgrep -value-,GREP_OPTIONS,-default-
 
-local arguments matchers command
+local arguments matchers command variant
 
 if [[ $service = *GREP_OPT* ]]; then
   compset -q
@@ -9,28 +8,29 @@ if [[ $service = *GREP_OPT* ]]; then
   (( CURRENT++ ))
   command=grep
 else
-  arguments=(
-    '(-e --regexp -f --file)1: :_guard "^--*" pattern'
-    '*:files:_files'
-  )
+  arguments=( '(-e --regexp -f --file)1: :_guard "^-*" pattern' )
+  if [[ $service = z* ]]; then
+    arguments+=( '*:files:_files -g "*.gz(-.)"' )
+  else
+    arguments+=( '*:files:_files' )
+  fi
   command="$words[1]"
 fi
 
-if [[ $service != [ef]grep ]]; then
+if [[ $service != (|g)(|z)[ef]grep ]]; then
   matchers='(--extended-regexp --fixed-strings --basic-regexp --perl-regexp -E -F -G -P)'
   arguments+=(
     $matchers{--extended-regexp,-E}'[use extended regular expression]'
     $matchers{--fixed-strings,-F}'[use literal strings]'
     $matchers{--basic-regexp,-G}'[use basic regular expression]'
-    $matchers{--perl-regexp,-P}'[use perl regular expression]'
   )
 fi
 
-arguments=( $arguments[@]
+arguments+=(
   '(--after-context -A)'{--after-context=,-A+}'[specify lines of trailing context]:lines'
   '(--text -a --binary-files -I)'{--text,-a}'[process binary file as if it were text]'
   '(--before-context -B)'{--before-context=,-B+}'[specify lines of leading context]:lines'
-  '(--context,-C)'{--context=,-C-}'[specify lines of context]:lines'
+  '(--context -C)'{--context=,-C-}'[specify lines of context]:lines'
   '(--color --colour)--'{color,colour}'=-[distinguish matching string]::when:(always never auto)'
   '(--byte-offset -b -c)'{--byte-offset,-b}'[print the byte offset with output lines]'
   '(--text -a -I)--binary-files=[specify type to assume for binary files]:file type:(binary without-match text)'
@@ -45,30 +45,75 @@ arguments=( $arguments[@]
   '--line-buffered[flush output on every line]'
   '(--text -a --binary-files)-I[process binary files as if non-matching]'
   '(--ignore-case -i -y)'{--ignore-case,-i,-y}'[case-insensitive]'
-  '(--files-without-match -L --file-with-matches -l --no-filename -h)'{--files-without-match,-L}"[output non-matching files' names only]"
-  '(--files-with-matches -l --files-without-match -L --no-filename -h)'{--files-with-matches,-l}"[output matching files' names only]"
-  '(--max-count -m)'{--max-count=,-m+}'[stop after specified no of matches]:max number of matches'
+  '(--files-without-match -L --file-with-matches -l --no-filename -h -o --only-matching)'{--files-without-match,-L}"[output non-matching files' names only]"
+  '(--files-with-matches -l --files-without-match -L --no-filename -h -o --only-matching)'{--files-with-matches,-l}"[output matching files' names only]"
+  '(--max-count -m)'{--max-count=,-m+}'[stop after specified no of matches in each file]:max number of matches'
   '(--line-number -n -c)'{--line-number,-n}'[prefix output with line numbers]'
-  '(--only-matching -o)'{--only-matching,-o}'[show only matching part of line]'
+  '(--only-matching -o --files-with-matches -l --files-without-match -L)'{--only-matching,-o}'[show only matching part of line]'
   '(--quiet --silent -q)'{--quiet,--silent,-q}'[suppress normal output]'
-  '(--recursive -r -R -d --directories)'{--recursive,-r,-R}'[recurse subdirectories]'
+  '(--recursive -r --dereference-recursive -R -d --directories)'{--recursive,-r}'[recurse subdirectories]'
   '*--include=[examine files matching specified pattern]:file pattern'
   '*--exclude=[skip files matching specified pattern]:file pattern'
-  '*--exclude-from=[skip files matching pattern in specified file]:file:_files'
   '*--exclude-dir=[skip directories matching specified pattern]:directory pattern'
   '(--no-messages -s)'{--no-messages,-s}'[suppress messages about unreadable]'
   '(--version -V)'{--version,-V}'[display version info]'
   '(--invert-match -v)'{--invert-match,-v}'[select non-matching lines]'
   '(--word-regexp -w --line-regexp -x)'{--word-regexp,-w}'[force pattern to match only whole words]'
   '(--line-regexp -x --word-regexp -w)'{--line-regexp,-x}'[force pattern to match only whole lines]'
-  '(--null -Z --no-filename -h)'{--null,-Z}'[print 0 byte after FILE name]'
-  '--help[display help]'
-  '--mmap[memory map input]'
+  '(-)--help[display help information]'
 )
 
-# remove long options?
-_pick_variant -c "$command" gnu=gnu unix --help ||
-    arguments=( ${arguments:#(|*\)(\*|))--*} )
+_pick_variant -r variant -c "$command" gnu=gnu gpl2=2.5.1 unix --version
+case $variant:$OSTYPE in
+  (gnu:*|gpl2:freebsd*))
+    [[ $service != (|g)(|z)[ef]grep ]] && arguments+=(
+      $matchers{--perl-regexp,-P}'[use perl regular expression]'
+    )
+  ;|
+  (gnu:*|gpl2:(free|net)bsd*))
+    arguments+=(
+      '*--exclude-from=[skip files matching pattern in specified file]:file:_files'
+      '(-z --null-data)'{-z,--null-data}'[input data separated by 0 byte, not newline]'
+    )
+  ;|
+  gpl2:freebsd*)
+    arguments+=(
+      '(--null --no-filename -h)--null[print 0 byte after each filename]'
+    )
+  ;|
+  gpl2:(freebsd|darwin)*)
+    arguments+=(
+      '(-Z --decompress -J --bz2decompress)'{-J,--bz2decompress}"[decompress bzip2'ed input before searching]"
+      '(-Z --decompress -J --bz2decompress)'{-Z,--decompress}"[decompress gzip'ed input before searching]"
+    )
+  ;|
+  (gnu:*|gpl2:netbsd*))
+    arguments+=(
+      '(--null -Z --no-filename -h)'{--null,-Z}'[print 0 byte after each filename]'
+    )
+  ;|
+  gnu:*)
+    arguments+=(
+      '(--no-group-separator)--group-separator=[specify separator between blocks of context]:separator [--]'
+      "(--group-separator)--no-group-separator[don't separate context blocks]"
+      '(-T --initial-tab)'{-T,--initial-tab}'[make tabs line up (if needed)]'
+      '(--recursive -r --dereference-recursive -R -d --directories)'{--dereference-recursive,-R}'[recurse subdirectories, following symlinks]'
+    )
+  ;;
+  gpl2:*) arguments=( ${${arguments:#*\)-r}/\)-r/\)-R} ) ;;
+  *:openbsd*)
+    arguments=(
+      ${(M)arguments:#((#s)|*\))--(context|binary-files|line-buffered)*}
+      ${${arguments:#((#s)|*\))(\*|)-[d-]*}/\)-r/\)-R}
+      "-U[search binary files but don't print them]"
+      '-Z[behave as zgrep]'
+    )
+  ;;
+  *)
+    # remove long options and GNU specific short opts, this is right for solaris
+    arguments=( ${arguments:#((#s)|*\))(\*|)-[aABCdDfGHILmorVy-]*} )
+  ;;
+esac
 
 _arguments -S -s $arguments[@]
 
diff --git a/Completion/Unix/Command/_groff b/Completion/Unix/Command/_groff
index 4f3b8c617..d78d65401 100644
--- a/Completion/Unix/Command/_groff
+++ b/Completion/Unix/Command/_groff
@@ -27,7 +27,7 @@ _arguments \
 	'-I[add search dir for soelim]:directory:_files -/' \
 	'-l[send output to spooler program for printing]' \
 	'*-L[pass arg to spooler program]:spooler argument:' \
-	'-N[do not allow newlines within eqn delimeters]' \
+	"-N[don't allow newlines within eqn delimiters]" \
 	'-p[preprocess with pic]' \
 	'*-P[pass option to postprocessor]:option:' \
         '-R[preprocess with refer]' \
diff --git a/Completion/Unix/Command/_growisofs b/Completion/Unix/Command/_growisofs
index 36b45d5ba..741a7516b 100644
--- a/Completion/Unix/Command/_growisofs
+++ b/Completion/Unix/Command/_growisofs
@@ -116,7 +116,7 @@ _mkisofs_sparc_boot_images () {
 _mkisofs_pathspec () {
   local sep
   if (( $words[(I)-graft-points] )); then
-    if ! compset -P '*[^\\]\='; then
+    if ! compset -P 1 '*[^\\]\='; then
       sep='-qS='
     fi
   fi
@@ -218,7 +218,7 @@ else
     '-check-session[check old session for compliance against current rules]' \
     '-copyright[specify the path and file-name of the copyright file]' \
     '-d[omit trailing period from files that do not have a period]' \
-    '-D[do not use deep directory-realocation]' \
+    "-D[don't use deep directory-reallocation]" \
     '-dir-mode[specify the mode of directories]' \
     '-dvd-video[generate a DVD-Video compliant UDF file-system]' \
     '-f[follow symbolic links when generating the file system]' \
@@ -250,7 +250,7 @@ else
     {-nobak,-no-bak}'[do not include backup files on the ISO9660 file-system]' \
     '-force-rr[do not use the automatic Rock Ridge attribute-recognition for previous sessions]' \
     '-no-rr[do not use use the Rock Ridge attributes from previous sessions]' \
-    '-no-split-symlink-components[do not split the symbolic-link compontents]' \
+    "-no-split-symlink-components[don't split the symbolic-link components]" \
     '-no-split-symlink-fields[do not split the symbolic-link fields]' \
     '-o[output the ISO9660-file-system image to the given file]:ISO9660 image:_files' \
     '-pad[pad the end of the image by 150 sectors]' \
@@ -335,7 +335,7 @@ else
 
     case "$state" in
       (devimg)
-      if compset -P \*=; then
+      if compset -P 1 '*='; then
         _files
       else
         _files -g "*(%,@)"
diff --git a/Completion/Unix/Command/_gs b/Completion/Unix/Command/_gs
index 22f3c789b..98ba14912 100644
--- a/Completion/Unix/Command/_gs
+++ b/Completion/Unix/Command/_gs
@@ -32,7 +32,7 @@ else
     fi
     ;;
   sname)
-    if compset -P '*='; then
+    if compset -P 1 '*='; then
       case "$IPREFIX" in
       *DEVICE\=)
         _wanted devices expl 'ghostscript device' \
diff --git a/Completion/Unix/Command/_gsettings b/Completion/Unix/Command/_gsettings
new file mode 100644
index 000000000..72f015729
--- /dev/null
+++ b/Completion/Unix/Command/_gsettings
@@ -0,0 +1,47 @@
+#compdef gsettings
+
+local curcontext="$curcontext" state line expl ret=1
+local subcmds
+
+_arguments \
+  '(- 1 *)--version[show version information]' \
+  '--schemadir[specify location of schemata]:directory:_directories' \
+  ':command:->subcmds' \
+  '*::args:->subargs' && ret=0
+
+if [[ $state = subargs ]]; then
+  curcontext="${curcontext%:*}-$words[1]:"
+  case $words[1] in
+    help) state=subcmds;;
+    get|range|reset|writable|monitor)
+      _arguments ':schema:->schemata' ':key:->keys'
+    ;;
+    set)
+      _arguments ':schema:->schemata' ':key:->keys' ':value'
+    ;;
+    (list|reset)-(keys|recursively|children)) state=schemata ;;
+    *) _default && ret=0 ;;
+  esac
+fi
+
+case $state in
+  subcmds)
+    subcmds=( ${(L)${${(M)${${(f)"$(_call_program commands $service help)"}[(r)Commands:*,-2]}:#  *}#??}/ ##/:} )
+    _describe -t commands 'command' subcmds -M 'r:?|-=* r:|=*' && ret=0
+    state=''
+  ;;
+  schemata)
+    if compset -P 1 '*:'; then
+      _directories && ret=0
+    else
+      _wanted schemata expl 'schema' compadd -M 'r:|.=* r:|=*' \
+          $(_call_program schemata $service list-schemas) && ret=0
+    fi
+  ;;
+  keys)
+    _wanted keys expl 'key' compadd \
+        $(_call_program keys $service list-keys $words[CURRENT-1]) && ret=0
+  ;;
+esac
+
+return ret
diff --git a/Completion/Unix/Command/_head b/Completion/Unix/Command/_head
index 4f956acdc..75fc1f0a1 100644
--- a/Completion/Unix/Command/_head
+++ b/Completion/Unix/Command/_head
@@ -32,7 +32,7 @@ case $state in
     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
+    if compset -P '(-|+|)[0-9]##'; then
       _alternative $mlt $digit && ret=0
     elif [[ -z $PREFIX ]]; then
       _alternative $sign $digit && ret=0
diff --git a/Completion/Unix/Command/_hg b/Completion/Unix/Command/_hg
index 8d31cd377..8eaa457c8 100644
--- a/Completion/Unix/Command/_hg
+++ b/Completion/Unix/Command/_hg
@@ -876,7 +876,7 @@ _hg_cmd_qdiff() {
 
 _hg_cmd_qfold() {
   _arguments -s : $_hg_global_opts $_h_commit_opts \
-  '(--keep,-k)'{-k,--keep}'[keep folded patch files]' \
+  '(--keep -k)'{-k,--keep}'[keep folded patch files]' \
   '*:unapplied patch:_hg_qunapplied'
 }
 
diff --git a/Completion/Unix/Command/_iftop b/Completion/Unix/Command/_iftop
index 41ffb4ea1..05db3fa03 100644
--- a/Completion/Unix/Command/_iftop
+++ b/Completion/Unix/Command/_iftop
@@ -1,9 +1,16 @@
 #compdef iftop
 
 _interfaces () {
-	_wanted interfaces expl 'network interface' \
-		_net_interfaces
-	_values "Pseudo-device that captures on all interfaces" "any"
+  local disp expl sep
+  _description interfaces expl 'network interface'
+  _net_interfaces "$expl[@]"
+  if zstyle -t ":completion:${curcontext}:interfaces" verbose; then
+    zstyle -s ":completion:${curcontext}:interfaces" list-separator sep || sep=--
+    disp=( "any $sep capture on all interfaces" )
+    compadd "$expl[@]" -ld disp any
+  else
+    compadd "$expl[@]" any
+  fi
 }
 
 _arguments \
@@ -14,7 +21,7 @@ _arguments \
   -P'[turn on port display]' \
   -b"[don't display bar graphs of traffic]" \
   -B'[display bandwidth rates in bytes/sec rather than bits/sec]' \
-  -i'[interface]:network interface:_interfaces' \
-  -f'[filter]:BPF filter' \
-  -F'[net/mask]:network/mask' \
-  -c'[config file]:config file:_files'
+  '-i+[interface]:network interface:_interfaces' \
+  '-f+[filter]:BPF filter' \
+  '-F+[net/mask]:network/mask' \
+  '-c+[config file]:config file:_files'
diff --git a/Completion/Unix/Command/_imagemagick b/Completion/Unix/Command/_imagemagick
index 1fc6089c8..c2c9dc478 100644
--- a/Completion/Unix/Command/_imagemagick
+++ b/Completion/Unix/Command/_imagemagick
@@ -12,7 +12,7 @@ typeset -A opt_args
 #
 # and certainly many other things...
 
-formats=(jpg jpeg jp2 j2k jpc jpx jpf tiff miff ras bmp cgm dcx ps eps fig fits fpx gif mpeg pbm pgm ppm pcd pcl pdf pcx png rad rgb rgba rle sgi html shtml tga ttf uil xcf xwd xbm xpm yuv)
+formats=(jpg jpeg jp2 j2k jpc jpx jpf tif tiff miff ras bmp cgm dcx ps eps fig fits fpx gif mpeg pbm pgm ppm pcd pcl pdf pcx png rad rgb rgba rle sgi html shtml tga ttf uil xcf xwd xbm xpm yuv)
 
 if (( $# )); then
   _files "$@" -g "*.(#i)(${(j:|:)formats})(-.)"
@@ -364,7 +364,7 @@ case "$service" in
       '*:picture file:_imagemagick' && return
 
     if [[ "$state" = profile ]]; then
-      if compset -P '*:'; then
+      if compset -P 1 '*:'; then
 	_files
       else
 	_wanted prefixes expl 'profile type' compadd icc: iptc:
diff --git a/Completion/Unix/Command/_initctl b/Completion/Unix/Command/_initctl
index 6505e4298..9d444c6d1 100644
--- a/Completion/Unix/Command/_initctl
+++ b/Completion/Unix/Command/_initctl
@@ -4,7 +4,7 @@
 
 typeset -g -a -U _initctl_events_list _initctl_eventargs_list
 
-# run show-config -e and if possible parse out all events and KEY= argumnts
+# run show-config -e and if possible parse out all events and KEY= arguments
 # otherwise provide some common values
 _initctl_fillarray_events_args ()
 {
@@ -169,7 +169,7 @@ _initctl()
   # define fallback completion function
   local cmd_completion_default=basic
 
-  # depending on which command was used, call different completion funtions
+  # depending on which command was used, call different completion functions
   case $service in
     initctl)
       _arguments "${common_args[@]}" '*::Initctl Commands:_initctl_command'
diff --git a/Completion/Unix/Command/_iostat b/Completion/Unix/Command/_iostat
new file mode 100644
index 000000000..88fc6a103
--- /dev/null
+++ b/Completion/Unix/Command/_iostat
@@ -0,0 +1,132 @@
+#compdef iostat
+
+local -a args
+
+case $OSTYPE:l in
+  *bsd*)
+    args+=(
+      '-c[repeat the display N times]:count'
+      '-C[display CPU statistics]'
+      '-d[display only device statistics]'
+      '-I[display total statistics for a given period, rather than average]'
+      '-M[extract values of the name list from specified file]:core:_files'
+      '-N[extract the name list from the specified file]:system:_files'
+      '-T[display TTY statistics]'
+      '-w[specify the duration of pauses between each display]:duration'
+    )
+    ;|
+  freebsd*)
+    args+=(
+      '-h[top mode]'
+      '-K[display block count in kilobytes, not block size]'
+      '-o[display old-style iostat device statistics]'
+      '-t[specify which type of device to display]: :->devicetype'
+      '-x[show extended disk statistics]'
+      '-z[omit lines for devices with no activity]'
+      '-?[display a usage statement and exit]'
+      '*:drives:( ${${(M)${(f)"$(geom disk list)"}\:#Geom name\:*}#*\: } )'
+    )
+    ;;
+  openbsd*)
+    args+=(
+      '-D[display alternate disk statistics]'
+      '*:drives:( ${${(s.,.)"$(sysctl -n hw.disknames)"}%\:*} )'
+    )
+    ;;
+  netbsd*)
+    args+=(
+      '-D[display alternate disk statistics]'
+      '-x[show extended disk statistics]'
+      '*:drives:( $(sysctl -n hw.disknames) )'
+    )
+    ;;
+  aix*)
+    args=(
+      '(-b -q -Q -f -F)-a[displays the adapter throughput report]'
+      '(-b)-A[displays the legacy asynchronous IO utilization report, and turns off the display of TTY utilization report]'
+      '-b[displays the block I/O device utilization statistics]'
+      '(-b)-d[turns off the display of TTY utilization report or CPU utilization report]'
+      '(-b -f -F)-D[displays the extended tape/drive utilization report]'
+      '(-a -b -D)-f[displays the file system utilization report]'
+      '(-a -b -D)-F[displays the file system utilization report, and turns off other utilization reports]'
+      '(-b)-l[displays the output in long listing mode]'
+      '(-b -t)-m[displays the path utilization report]'
+      '(-b)-p[displays the tape utilization report]'
+      '(-b)-P[displays the POSIX asynchronous IO utilization report]'
+      '(-b)-q[specifies AIO queues and their request counts]'
+      '(-b)-Q[displays a list of all the mounted file systems and the associated queue numbers with their request counts]'
+      '(-b)-R[specifies that the reset of min* and max* values should happen at each interval]'
+      '(-b)-s[specifies the system throughput report]'
+      '(-b)-S[displays the processor statistics that are multiplied by a value of 10]:power'
+      '(-b -m)-t[turns off the display of disk utilization report]'
+      '-T[displays the time stamp]'
+      '(-b)-V[displays valid nonzero statistics]'
+      '(-b)-z[resets the disk input/output statistics]'
+      '(-b)-@[reports I/O activities of a workload partition]:workload:(ALL Global)'
+      '*:drives:_files' # not sure how to enumerate drives on aix
+    )
+    ;;
+  solaris*)
+    args=(
+      '-c[report percentage of time the system spent in user/system mode, dtrace probes and idling]'
+      '-C[report extended disk statistics aggregated by controller id, when used with -x]'
+      '-d[report kilobytes transferred per second, transfers per second and average service time]'
+      '-D[report reads/writes per second and percentage disk utilization]'
+      '-e[display device error summary statistics]'
+      '-E[display all device error statistics]'
+      '-i[display Device ID instead of the Serial No, when used with -E]'
+      '-I[report counts in each interval]'
+      '-l[limit the number of disks included in the report]:number of disks'
+      '-m[report file system mount points]'
+      '-M[display data throughput in MB/sec instead of KB/sec]'
+      '-n[display names in descriptive format]'
+      '-p[report per-partition statistics]'
+      '-P[report per-partition statistics only]'
+      '-r[display data in comma-separated format]'
+      '-s[suppress message related to state changes]'
+      '-t[report the number of characters read and written to terminals per second]'
+      '-T[display a timestamp]:format:(( "u\:internal representation of time" "d\:standard date format" ))'
+      '-X[report statistics for lun.controller, for disks under scsi_vhci(7D) control]'
+      '-x[report extended disk statistics]'
+      '-Y[report statistics for lun.targetport and lun.targetport.controller, for disks under scsi_vhci(7D) control]'
+      '-z[ignore lines where data values are all zeros]'
+      '::device:( ${${${(f)"$(iostat -rx 1 1)"}[3,-1]}%%,*} )'
+    )
+    ;;
+  darwin*)
+    args=(
+      '-C[display CPU statistics]'
+      '-c[number of times to display statistics]'
+      '-d[display only device statistics]'
+      '-l[total statistics for a given time period]'
+      '-K[display block count in kilobytes]'
+      '-n[limit the number of disks included in the report]:number of disks'
+      '-o[display old-style iostat device statistics]'
+      '-T[display TTY statistics]'
+      '-U[display system load averages]'
+      '-w[specify the duration of pauses between each display]:duration'
+      '*::device:_files -W /dev -g "disk*"'
+    )
+    ;;
+  *linux*)
+    args=(
+      '-c[display CPU utilization report]'
+      '-d[display device utilization report]'
+      '-T[only display global statistics for group_name]'
+      '-g[display statistics for a group of devices]:group name'
+      '-h[human readable device utilization report]'
+      '-j[display persistent device name]'
+      '(-m)-k[display statistics in kB/s]'
+      '(-k)-m[display statistics in MB/s]'
+      '-N[display registered device mapper names]'
+      '*::device:_files -W /dev -g "*(-%)"'
+    )
+    ;;
+esac
+
+if (( $#args )); then
+  _arguments -s -w -A '-*' : $args
+  return
+fi
+
+_default
diff --git a/Completion/Unix/Command/_ip b/Completion/Unix/Command/_ip
index 1e3949148..6e1cc3b78 100644
--- a/Completion/Unix/Command/_ip
+++ b/Completion/Unix/Command/_ip
@@ -98,7 +98,7 @@ subcmd_lockmtu=("(" /$'lock\0'/ ":lock:lock:(lock)" "|" ")" $subcmd_number )
 local -a subcmd_nexthop
 _regex_words nexthop 'nexthop route keyword' \
     'via:specify nexthop router:$subcmd_ipaddr' \
-    'dev:sepcify output device:$subcmd_dev' \
+    'dev:specify output device:$subcmd_dev' \
     'weight:specify relative quality of route:$subcmd_number'
 subcmd_nexthop=("$reply[@]" "#")
 
@@ -107,7 +107,7 @@ _regex_words rtprotocol 'route protocol' \
   'redirect:installed from ICMP redirect' \
   'kernel:installed automatically by kernel' \
   'boot:installed during boot sequence' \
-  'static:installed by adminstrator' \
+  'static:installed by administrator' \
   'ra:installed by Router Discovery protocol'
 subcmd_rtprotocol=("$reply[@]")
 
@@ -172,10 +172,10 @@ _regex_words \
   link-set-commands 'link set commands' \
   'dev:specify device:$subcmd_dev' \
   'u*p:change state to up' \
-  'do*wn:change state do down' \
+  'do*wn:change state to down' \
   'ar*p:change ARP flag on device:$subcmd_onoff' \
   'mu*lticast:change MULTICAST flag on device:$subcmd_onoff' \
-  'pr*omisc:set promiscious mode:$subcmd_onoff' \
+  'pr*omisc:set promiscuous mode:$subcmd_onoff' \
   'dy*namic:change DYNAMIC flag on device:$subcmd_onoff' \
   'n*ame:change name of device:$subcmd_string' \
   'txq*ueuelen:specify length of transmit queue:$subcmd_number' \
@@ -285,7 +285,7 @@ local -a neigh_add_cmds
 _regex_words neigh-add-commands "neighbour add command" \
   't*o:add new neighbour IP address:$subcmd_ipaddr' \
   'dev:specify network device:$subcmd_dev' \
-  'l*ladr:specify link layer (MAC) address or null:$subcmd_lladdr' \
+  'l*laddr:specify link layer (MAC) address or null:$subcmd_lladdr' \
   'n*ud:specify neighbour unreachability detection state:$subcmd_nud'
 # to-address without keyword can appear first
 neigh_add_cmds=( "(" $subcmd_ipaddr "|" ")" "$reply[@]" "#")
@@ -490,7 +490,7 @@ maddr_cmds=("$reply[@]")
 # mroute
 #
 local -a mroute_show_cmds
-_regex_words mroute-show-comnands "mroute show command" \
+_regex_words mroute-show-commands "mroute show command" \
   'to:select destination prefix:$subcmd_ipaddr' \
   'iif:select input interface (device):$subcmd_dev' \
   'from:select source prefix:$subcmd_ipaddr'
diff --git a/Completion/Unix/Command/_java b/Completion/Unix/Command/_java
index 2aef15a25..30231c5ee 100644
--- a/Completion/Unix/Command/_java
+++ b/Completion/Unix/Command/_java
@@ -65,13 +65,13 @@ java)
     '-D-[specify a property]:property:->property' \
     \*{-enableassertions,-ea}-::class \
     \*{-disableassertions,-da}-::class \
-    '(-enablesystemassertions,-esa,-disablesystemassertions,-dsa)'{-enablesystemassertions,-esa,-disablesystemassertions,-dsa} \
+    '(-enablesystemassertions -esa -disablesystemassertions -dsa)'{-enablesystemassertions,-esa,-disablesystemassertions,-dsa} \
     '(-verbose:class)-verbose[print class information]' \
     '(-verbose)-verbose\:class[print class information]' \
     '-verbose\:gc[print gc information]' \
     '-verbose\:jni[print JNI information]' \
     '(- 1)-version[print version]' \
-    '-showversion[print version and contrinue]' \
+    '-showversion[print version and continue]' \
     '(- 1)-'{\?,help}'[print help message]' \
     '(- 1)-X-[non-standard java option]:option' \
     '(- 1)-jar[specify a program encapsulated as jar]:jar:_files -g \*.jar\(-.\)' \
@@ -578,7 +578,7 @@ encoding)
   ;;
 
 property)
-  if compset -P '*='; then
+  if compset -P 1 '*='; then
     _default && return
   else
     _message -e property-names 'property name'
diff --git a/Completion/Unix/Command/_joe b/Completion/Unix/Command/_joe
index 91c437ead..592c34a10 100644
--- a/Completion/Unix/Command/_joe
+++ b/Completion/Unix/Command/_joe
@@ -3,7 +3,7 @@
 _arguments \
   '-asis[characters with codes >127 will be displayed non-inverted]' \
   '-backpath[backup file directory]:backup file directory:_files -/' \
-  '-baud[inserts delays for baud rates below 19200]:baud rate:_baudrate' \
+  '-baud[inserts delays for baud rates below 19200]:baud rate:_baudrates' \
   '-beep[beep on command errors or when cursor goes past extremes]' \
   '-columns[sets the number of screen columns]:num of columns' \
   '-csmode[continued search mode]' \
diff --git a/Completion/Unix/Command/_kvno b/Completion/Unix/Command/_kvno
index 782d9e6fc..9664e187b 100644
--- a/Completion/Unix/Command/_kvno
+++ b/Completion/Unix/Command/_kvno
@@ -3,15 +3,17 @@
 local curcontext="$curcontext" state line ret=1
 
 _arguments -C \
-  '(-4)-c[specify credentials cache to use]:cache:_files' \
-  '(-4)-e[specify encryption type]:encryption type' \
+  '-c[specify credentials cache to use]:cache:_files' \
+  '-e[specify encryption type]:encryption type' \
   '-q[suppress printing]' \
   '(- :)-h[display help information]' \
-  '(-c -e)-4[kerberos 4 tickets]' \
+  '-P[acquire credentials for the principal during S4U2Self]' \
+  '-S[specify the service name of the principal]:service' \
+  '-U[specify user for S4U2Self]:user:_users' \
   ':principal:->principal' && ret=0
 
 if [[ $state = principal ]]; then
-  if compset -P host/; then
+  if compset -P '*/'; then
     _hosts && ret=0
   else
     _alternative \
diff --git a/Completion/Unix/Command/_less b/Completion/Unix/Command/_less
index a3ba8f665..9092010f2 100644
--- a/Completion/Unix/Command/_less
+++ b/Completion/Unix/Command/_less
@@ -32,7 +32,8 @@ fi
 
 _arguments -S -s -A "[-+]*"  \
   '(-? --help)'{-\?,--help}'[display summary of less commands]' \
-  '(-a --search-skip-screen)'{-a,--search-skip-screen}'[begin forward searches after last displayed line]' \
+  '(-a --search-skip-screen)'{-a,--search-skip-screen}'[skip current screen in searches]' \
+  '(-A --SEARCH-SKIP-SCREEN)'{-A,--SEARCH-SKIP-SCREEN}"[start searches just after target line]" \
   '(-b --buffers)'{-b+,--buffers=}'[specify amount of buffer space used for each file]:buffer space (kilobytes)' \
   '(-B --auto-buffers)'{-B,--auto-buffers}"[don't automatically allocate buffers for pipes]" \
   '(-C --CLEAR-SCREEN -c --clear-screen)'{-c,--clear-screen}'[repaint screen instead of scrolling]' \
@@ -44,12 +45,14 @@ _arguments -S -s -A "[-+]*"  \
   '(-F --quit-if-one-screen)'{-F,--quit-if-one-screen}'[exit if entire file fits on first screen]' \
   '(-G --HILITE-SEARCH -g --hilite-search)'{-g,--hilite-search}'[highlight only one match for searches]' \
   '(-g --hilite-search -G --HILITE-SEARCH)'{-G,--HILITE-SEARCH}'[disable highlighting of search matches]' \
+  '--old-bot[revert to the old bottom of screen behavior]' \
   '(-h --max-back-scroll)'{-h+,--max-back-scroll=}'[specify backward scroll limit]:backward scroll limit (lines)' \
   '(-I --IGNORE-CASE -i --ignore-case)'{-i,--ignore-case}'[ignore case in searches that lack uppercase]' \
   '(-i --ignore-case -I --IGNORE-CASE)'{-I,--IGNORE-CASE}'[ignore case in all searches]' \
   '(-j --jump-target)'{-j+,--jump-target}'[specify screen position of target lines]:position (line)' \
   '(-J --status-column)'{-J,--status-column}'[display status column on the left]' \
   \*{-k+,--lesskey-file=}'[use specified lesskey file]:lesskey file:_files' \
+  '(-K --quit-on-intr)'{-K,--quit-on-intr}'[exit less in response to ctrl-c]' \
   '(-L --no-lessopen)'{-L,--no-lessopen}'[ignore the LESSOPEN environment variable]' \
   '(-M --LONG-PROMPT -m --long-prompt)'{-m,--long-prompt}'[prompt verbosely]' \
   '(-m --long-prompt -M --LONG-PROMPT)'{-M,--LONG-PROMPT}'[prompt very verbosely]' \
@@ -80,6 +83,8 @@ _arguments -S -s -A "[-+]*"  \
   '(-\" --quotes)'{-\"+,--quotes=}'[change quoting character]:quoting characters' \
   '(-~ --tilde)'{-~,--tilde}"[don't display tildes after end of file]" \
   '(-\# --shift)'{-\#+,--shift=}"[specify amount to move when scrolling horizontally]:number" \
+  '--follow-name[the F command changes file if the input file is renamed]' \
+  '--use-backslash[subsequent options use backslash as escape char]' \
   "$files[@]" && ret=0
 
 
diff --git a/Completion/Unix/Command/_lha b/Completion/Unix/Command/_lha
index c2d5e7d8d..e8d47fcdb 100644
--- a/Completion/Unix/Command/_lha
+++ b/Completion/Unix/Command/_lha
@@ -1,5 +1,7 @@
 #compdef lha
 
+local expl
+
 if (( CURRENT == 2 )); then
     compset -P -
 
diff --git a/Completion/Unix/Command/_libvirt b/Completion/Unix/Command/_libvirt
new file mode 100644
index 000000000..a3ab5a68a
--- /dev/null
+++ b/Completion/Unix/Command/_libvirt
@@ -0,0 +1,238 @@
+#compdef virsh virt-admin virt-host-validate virt-pki-validate virt-xml-validate
+
+local curcontext="$curcontext" state line expl ret=1
+declare -A opt_args
+
+local exargs="-h --help -V -v --version=short --version=long"
+local -a common_opts interact_cmds
+common_opts=(
+  '(- *)'{-h,--help}'[print help information and exit]'
+  '(- *)'{-v,--version=short}'[print short version information and exit]'
+  '(- *)'{-V,--version=long}'[print long version information and exit]'
+  "(-c --connect $exargs)"{-c+,--connect=}'[specify connection URI]:URI:_hosts'
+  "(-d --debug -q --quiet $exargs)"{-d+,--debug=}'[set debug level]:level:(0 1 2 3 4)'
+  "(-l --log $exargs)"{-l+,--log=}'[specify log file]:file:_files'
+  "(-q --quiet -d --debug $exargs)"{-q,--quiet}'[quiet mode]'
+)
+interact_cmds=(cd echo exit quit connect)
+
+typeset -A dom_opts
+dom_opts=(
+  console " "
+  destroy " "
+  edit " "
+  managedsave " "
+  reboot " "
+  reset " "
+  resume --state-paused
+  save " "
+  screenshot " "
+  send-key " "
+  shutdown --state-running
+  start --inactive
+  suspend --state-running
+  ttyconsole " "
+  undefine --inactive
+  vncdisplay " "
+)
+typeset -A iface_opts
+iface_opts=(
+  iface-start --inactive
+)
+typeset -A net_opts
+net_opts=(
+  net-start --inactive
+)
+typeset -A pool_opts
+pool_opts=(
+  pool-start --inactive
+)
+
+case $service in
+  virsh)
+    if (( ! $+_cache_virsh_cmds )); then
+      _cache_virsh_cmds=( ${${${${(f):-"$(_call_program commands virsh help)"}:#*:}/# ##}/ *} )
+      local icmd
+      for icmd in $interact_cmds; do
+        _cache_virsh_cmds[$_cache_virsh_cmds[(i)$icmd]]=()
+      done
+    fi
+    if (( ! $+_cache_virsh_cmd_opts )); then
+      typeset -gA _cache_virsh_cmd_opts
+    fi
+    _arguments -A "-*" -C -S -s -w \
+      "$common_opts[@]" \
+      "(-e --escape $exargs)"{-e+,--escape=}'[set escape sequence for console]:sequence' \
+      "(-k --keepalive-interval $exargs)"{-k+,--keepalive-interval=}'[set keepalive interval]:interval' \
+      "(-K --keepalive-count $exargs)"{-K+,--keepalive-count=}'[set keepalive count]:count' \
+      "(-r --readonly $exargs)"{-r,--readonly}'[connect readonly]' \
+      "(-t --timing $exargs)"{-t,--timing}'[print timing information]' \
+      '1:command:->virsh_cmds' \
+      '*:cmdopt:->virsh_cmd_opts' && return
+      # We accept only virsh command options after the first non-option argument
+      # (i.e., the virsh command itself), this makes it so with the -A "-*" above
+      [[ -z $state ]] && state=virsh_cmd_opts
+  ;;
+  virt-admin)
+    if (( ! $+_cache_virt_admin_cmds )); then
+      _cache_virt_admin_cmds=( ${${${${(f):-"$(_call_program commands virt-admin help)"}:#*:}/# ##}/ *} )
+      local icmd
+      for icmd in $interact_cmds; do
+        _cache_virt_admin_cmds[$_cache_virt_admin_cmds[(i)$icmd]]=()
+      done
+    fi
+    if (( ! $+_cache_virt_admin_cmd_opts )); then
+      typeset -gA _cache_virt_admin_cmd_opts
+    fi
+    _arguments -A "-*" -C -S -s -w \
+      "$common_opts[@]" \
+      '1:command:->virt_admin_cmds' \
+      '*:cmdopt:->virt_admin_cmd_opts' && return
+      # Same as with virsh above
+      [[ -z $state ]] && state=virt_admin_cmd_opts
+  ;;
+  virt-host-validate)
+    _arguments -A "-*" -S \
+      '(- *)'{-h,--help}'[print help information and exit]' \
+      '(- *)'{-v,--version}'[print version information and exit]' \
+      '(- *)'{-q,--quiet}'[quiet mode]' \
+      '1:hv-type:(qemu lxc bhyve)' && return
+  ;;
+  virt-pki-validate)
+    _arguments -A "-*" -S \
+      '(- *)'{-h,--help}'[print help information and exit]' \
+      '(- *)'{-V,--version}'[print version information and exit]' \
+      && return
+  ;;
+  virt-xml-validate)
+    _arguments -A "-*" -S \
+      '(- *)'{-h,--help}'[print help information and exit]' \
+      '(- *)'{-V,--version}'[print version information and exit]' \
+      '1:file:_files -g "*.xml(-.)"' \
+      '2:schema:(domainsnapshot domain network storagepool storagevol nodedev capability nwfilter secret interface)' \
+      && return
+  ;;
+esac
+
+local -a conn_opt
+if [[ -n ${(v)opt_args[(I)-c|--connect]} ]]; then
+  local uri=${(v)opt_args[(I)-c|--connect]}
+  uri=${uri//(#m)\\([\\:])/${MATCH[2]}} # opt_args elements are colon-escaped
+  # For the libvirt remote URI syntax, see:
+  # https://libvirt.org/guide/html/Application_Development_Guide-Architecture-Remote_URIs.html
+  [[ -z ${(Q)uri//([[:alnum:]]|+|:|\/|@|-|\.|\?|=)} ]] && \
+    conn_opt=( -c $uri )
+fi
+
+case $state in
+  virsh_cmds)
+    _wanted commands expl 'virsh command' compadd -a _cache_virsh_cmds && ret=0
+  ;;
+  virsh_cmd_opts)
+    if [[ $words[CURRENT-1] == --(dir|emulatorbin|file|mountpoint|*path|script|source-dev) || $words[CURRENT] == (/*|.*) ]]; then
+      _default
+      return 0
+    fi
+    local cmd word
+    for word in ${words:1}; do
+      [[ -n "${_cache_virsh_cmds[(r)${(b)word}]}" ]] && cmd=$word && break
+    done
+    [[ -z $cmd ]] && return 1
+    local -a values
+    case $words[CURRENT-1] in
+      --domain)
+        values=( $(_call_program domains "noglob virsh $conn_opt list ${dom_opts[$cmd]:-"--all"} --name") )
+        [[ -n $values ]] && _wanted domains expl domain compadd ${=values} && return 0
+        return 1
+      ;;
+      --interface)
+        values=( ${${${${(f):-"$(_call_program interfaces "noglob virsh $conn_opt iface-list ${iface_opts[$cmd]:-"--all"}")"}/ Name*/}:#---*}/  */} )
+        [[ -n $values ]] && _wanted interfaces expl interface compadd ${=values} && return 0
+        return 1
+      ;;
+      --network)
+        values=( $(_call_program networks "noglob virsh $conn_opt net-list ${net_opts[$cmd]:-"--all"} --name") )
+        [[ -n $values ]] && _wanted networks expl network compadd ${=values} && return 0
+        return 1
+      ;;
+      --device)
+        values; values=( $(_call_program devices "noglob virsh $conn_opt nodedev-list") )
+        [[ -n $values ]] && _wanted devices expl device compadd ${=values} && return 0
+        return 1
+      ;;
+      --nwfilter)
+        values=( ${${${${(f):-"$(_call_program nwfilters "noglob virsh $conn_opt nwfilter-list")"}/ UUID*/}:#---*}/  */} )
+        [[ -n $values ]] && _wanted nwfilters expl nwfilter compadd ${=values} && return 0
+        return 1
+      ;;
+      --pool)
+        values=( ${${${${(f):-"$(_call_program pools "noglob virsh $conn_opt pool-list ${pool_opts[$cmd]:-"--all"}")"}/ Name*/}:#---*}/  */} )
+        [[ -n $values ]] && _wanted pools expl pool compadd ${=values} && return 0
+        return 1
+      ;;
+      --secret)
+        values=( ${${${${(f):-"$(_call_program secrets "noglob virsh $conn_opt secret-list")"}/ UUID*/}:#---*}/  */} )
+        [[ -n $values ]] && _wanted secrets expl secret compadd ${=values} && return 0
+        return 1
+      ;;
+      --snapshotname)
+        local dom ; (( ${(k)words[(I)--domain]} > 0 )) && dom=${words[1+${(k)words[(I)--domain]}]}
+        [[ -z $dom ]] && return 1
+        values=( ${${${${(f):-"$(_call_program snapshots "noglob virsh $conn_opt snapshot-list --domain ${(q)dom} 2>/dev/null")"}/ Name*/}:#---*}/  */} )
+        [[ -n $values ]] && _wanted snapshots expl snapshot compadd ${=values} && return 0
+        return 1
+      ;;
+      --vol)
+        local pool ; (( ${(k)words[(I)--pool]} > 0 )) && pool=${words[1+${(k)words[(I)--pool]}]}
+        [[ -z $pool ]] && return 1
+        values=( ${${${${(f):-"$(_call_program volumes "noglob virsh $conn_opt vol-list --pool ${(q)pool} 2>/dev/null")"}/ Name*/}:#---*}/  */} )
+        [[ -n $values ]] && _wanted volumes expl volume compadd ${=values} && return 0
+        return 1
+      ;;
+    esac
+    if [[ $cmd == help ]]; then
+      [[ $words[-1] == -* ]] && _values -w option --command && return 0
+      if [[ $words[-2] == help || $words[-2] == --command ]]; then
+        _wanted commands expl command compadd ${=_cache_virsh_cmds} && return 0
+      fi
+      return 1
+    fi
+    # Allow passing domain without --domain with few of the most used commands
+    if [[ $cmd == (destroy|edit|reboot|reset|start|shutdown) ]]; then
+      if [[ $words[CURRENT-1] == $cmd ]]; then
+        values=( $(_call_program domains "noglob virsh $conn_opt list ${dom_opts[$cmd]:-"--all"} --name") )
+        [[ -n $values ]] && _wanted domains expl domain compadd ${=values} && return 0
+      fi
+    fi
+    [[ -z $_cache_virsh_cmd_opts[$cmd] ]] && \
+      _cache_virsh_cmd_opts[$cmd]=${(M)${${${${=${(f)"$(_call_program options virsh help $cmd 2>&1)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}
+    [[ -n ${=_cache_virsh_cmd_opts[$cmd]} ]] && \
+      _values -w option ${(u)=_cache_virsh_cmd_opts[$cmd]} && ret=0
+  ;;
+  virt_admin_cmds)
+    _wanted commands expl 'virt-admin command' compadd -a _cache_virt_admin_cmds && ret=0
+  ;;
+  virt_admin_cmd_opts)
+    local cmd word
+    for word in ${words:1}; do
+      [[ -n "${_cache_virt_admin_cmds[(r)${(b)word}]}" ]] && cmd=$word && break
+    done
+    [[ -z $cmd ]] && return 1
+    if [[ $words[CURRENT-1] == --server ]]; then
+      _wanted servers expl server compadd ${=${(S)${${(f)$(_call_program -p servers noglob virt-admin ${(Q)conn_opt} srv-list)}##*--- }//[0-9]* }} && return 0
+    fi
+    if [[ $words[CURRENT-1] == --client ]]; then
+      local srv ; (( ${(k)words[(I)--server]} > 0 )) && srv=${words[1+${(k)words[(I)--server]}]}
+      [[ -z $srv ]] && return 1
+      [[ -n ${srv//[[:alnum:]]} ]] && return 1
+      _wanted clients expl client compadd ${=${${(f):-"$(_call_program -p clients noglob virt-admin ${(Q)conn_opt} srv-clients-list --server $srv 2>/dev/null)"}/ [a-z]*}//[^0-9]} && return 0
+    fi
+    [[ -z $_cache_virt_admin_cmd_opts[$cmd] ]] && \
+      _cache_virt_admin_cmd_opts[$cmd]=${(M)${${${${=${(f)"$(_call_program options virt-admin help $cmd 2>&1)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}
+    [[ -n $_cache_virt_admin_cmd_opts[$cmd] ]] && \
+      _values -w option ${(u)=_cache_virt_admin_cmd_opts[$cmd]} && ret=0
+  ;;
+
+esac
+
+return ret
diff --git a/Completion/Unix/Command/_ln b/Completion/Unix/Command/_ln
index c903fee02..efb982aba 100644
--- a/Completion/Unix/Command/_ln
+++ b/Completion/Unix/Command/_ln
@@ -38,6 +38,7 @@ if [[ $variant == gnu ]]; then
     '(-L --logical)'{-L,--logical}'[create hard links to symbolic link references]'
     '(-n --no-dereference)'{-n,--no-dereference}'[treat destination symbolic link to a directory as if it were a normal file]'
     '(-P --physical)'{-P,--physical}'[create hard links directly to symbolic links]'
+    '(-r --relative)'{-r,--relative}'[create symbolic links relative to link location]'
     '(-s --symbolic)'{-s,--symbolic}'[create symbolic links instead of hard links]'
     '(-S --suffix)'{-S,--suffix=}'[override default backup suffix]:suffix'
     '(-t --target-directory)'{-t,--target-directory=}'[specify directory in which to create the links]: :_directories'
diff --git a/Completion/Unix/Command/_locale b/Completion/Unix/Command/_locale
new file mode 100644
index 000000000..59e04a282
--- /dev/null
+++ b/Completion/Unix/Command/_locale
@@ -0,0 +1,84 @@
+#compdef locale
+
+local curcontext="$curcontext" state state_descr line expl ret=1
+typeset -A opt_args; local -a specs aopts
+
+if _pick_variant gnu='(GNU|EGLIBC)' unix --version; then
+
+  local exargs="-? --help --usage -V --version"
+
+  aopts=( -A "-*" -C -S -s )
+  specs=(
+    '(- *)'{-\?,--help}'[display help information]'
+    '(- *)--usage[display a short usage message]'
+    '(- *)'{-V,--version}'[print program version]'
+    - set1
+    "(-a --all-locales $exargs)"{-a,--all-locales}'[list all available locales]'
+    "(-v --verbose $exargs)"{-v,--verbose}'[display additional information]'
+    - set2
+    "(-m --charmaps $exargs)"{-m,--charmaps}'[list all available charmaps]'
+    - set3
+    "(-c --category-name $exargs)"{-c,--category-name}'[print also locale category]'
+    "(-k --keyword-name $exargs)"{-k,--keyword-name}'[print also keyword of each value]'
+    '*:name:->catkey'
+  )
+
+elif [[ $OSTYPE == openbsd* ]]; then
+
+  specs=(
+    '(-m)-a[list all available locales]'
+    '(-a)-m[list all available charmaps]'
+  )
+
+else
+
+  aopts=( -C -A "-*" )
+  specs=(
+    - set1
+    '-a[list all available locales]'
+    - set2
+    '-m[list all available charmaps]'
+    - set3
+    '-c[print also locale category]'
+    '-k[print also keyword of each value]'
+    '*:name:->catkey'
+  )
+fi
+
+_arguments $aopts : $specs && ret=0
+
+if [[ $state == catkey ]]; then
+  typeset -aU cats keys
+
+  case $OSTYPE in
+    *bsd*)
+      # keywords from comparing locale -k list on netbsd and freebsd
+      # the additional keywords freebsd supports will be added via
+      # the reassignment of keys
+      keys+=(
+        charmap decimal_point thousands_sep grouping radixchar thousep int_curr_symbol
+        currency_symbol mon_decimal_point mon_thousands_sep mon_grouping positive_sign
+        negative_sign int_frac_digits frac_digits p_cs_precedes p_sep_by_space n_cs_precedes
+        n_sep_by_space p_sign_posn n_sign_posn int_p_cs_precedes int_p_sep_by_space
+        int_n_cs_precedes int_n_sep_by_space int_p_sign_posn int_n_sign_posn d_t_fmt d_fmt
+        t_fmt am_str pm_str t_fmt_ampm day_1 day_2 day_3 day_4 day_5 day_6 day_7 abday_1
+        abday_2 abday_3 abday_4 abday_5 abday_6 abday_7 mon_1 mon_2 mon_3 mon_4 mon_5 mon_6
+        mon_7 mon_8 mon_9 mon_10 mon_11 mon_12 abmon_1 abmon_2 abmon_3 abmon_4 abmon_5
+        abmon_6 abmon_7 abmon_8 abmon_9 abmon_10 abmon_11 abmon_12 era era_d_fmt era_d_t_fmt
+        era_t_fmt alt_digits yesexpr noexpr yesstr nostr
+      );|
+    (free|net|dragonfly)bsd*) (( words[(I)-k] )) && keys+=(list);;
+    *)
+      cats=( ${${${(f)"$(locale)"}%=*}%(LANG|LANGUAGE|LC_ALL)} )
+    ;;
+  esac
+
+  keys=( $keys ${${(f)"$(locale -k $cats 2>/dev/null)"}%=*} )
+  if [[ $OSTYPE == freebsd* ]] && (( words[(I)list] )); then
+    _message -e values prefix && ret=0
+  else
+    _wanted values expl name compadd "$@" -a - cats keys && ret=0
+  fi
+fi
+
+return ret
diff --git a/Completion/Unix/Command/_localedef b/Completion/Unix/Command/_localedef
new file mode 100644
index 000000000..4d4b4bbe3
--- /dev/null
+++ b/Completion/Unix/Command/_localedef
@@ -0,0 +1,92 @@
+#compdef localedef
+
+local curcontext="$curcontext" state line expl ret=1
+typeset -A opt_args
+
+if _pick_variant gnu='(GNU|EGLIBC)' unix --version; then
+
+  local exargs="-? --help --usage -V --version"
+  _arguments -A "-*" -C -S -s \
+    '(- *)'{-\?,--help}'[display help information]' \
+    '(- *)--usage[display a short usage message]' \
+    '(- *)'{-V,--version}'[print program version]' \
+    "(-A --alias-file $exargs)"{-A+,--alias-file=}'[specify locale alias file]:alias file:_files' \
+    "($exargs)--prefix=[specify path prefix]:prefix:_files" \
+    "(-c --force $exargs)"{-c,--force}'[force write despite of warnings]' \
+    "(-v --verbose $exargs)"{-v,--verbose}'[display additional information]' \
+    "($exargs)--quiet[suppress messages and warnings]" \
+    - set1 \
+    "(-f --charmap $exargs)"{-f+,--charmap=}'[specify locale charmap file]:charmap:->charmap' \
+    "(-i --inputfile $exargs)"{-i+,--inputfile=}'[specify locale definition file]:locale file:_files' \
+    "(-u --repertoire-map $exargs)"{-u+,--repertoire-map=}'[specify repertoire map file]:repertoire map file:_files' \
+    '1:path:_files' \
+    - set2 \
+    "(--list-archive $exargs)--list-archive[list locales in archive]" \
+    - set3 \
+    "(--delete-from-archive $exargs)--delete-from-archive[delete locale from archive]" \
+    '*:locale:->locale' \
+    - set4 \
+    "(--add-to-archive $exargs)--add-to-archive[add locale to archive]" \
+    "(--replace $exargs)--replace[replace locale in archive]" \
+    "(--no-archive $exargs)--no-archive[use subdir not archive]" \
+    '*:compiled path:_files -/' \
+    && return 0
+
+  case "$state" in
+    charmap)
+      if [[ $words[-1] == */* ]]; then
+        _wanted charmaps expl charmap _files && ret=0
+      else
+        typeset -a charmaps
+        charmaps=( ${(f)"$(locale -m)"} )
+        _wanted charmaps expl charmap compadd -a charmaps && ret=0
+      fi
+    ;;
+    locale)
+      typeset -a locales
+      local pref=${opt_args[--prefix]}
+      local p=${pref:+--prefix}
+      locales=( ${(f)"$(localedef --list-archive $p $pref)"} )
+      _wanted locales expl locale compadd -a locales && ret=0
+    ;;
+  esac
+
+  return ret
+
+else
+
+  typeset -a u_opt bsd_opts
+  [[ $OSTYPE != darwin* ]] && u_opt=(
+      '-u+[specify target codeset]:codeset:_files'
+    )
+  [[ $OSTYPE == (freebsd*|dragonfly*) ]] && bsd_opts=(
+      '-D[create BSD-style output]' \
+      '-U[ignore undefined character symbols]' \
+      '-v[verbose debug output]' \
+      '-w+[specify width file]:width file:_files' \
+    )
+
+  _arguments -A "-*" -C \
+    '-c[force write despite of warnings]' \
+    '-f+[specify locale charmap file]:charmap:->charmap' \
+    '-i+[specify locale definition file]:locale file:_files' \
+    $u_opt \
+    $bsd_opts \
+    '1:path:_files' \
+    && return 0
+
+  case "$state" in
+    charmap)
+      if [[ $words[-1] == */* ]]; then
+        _wanted charmaps expl charmap _files && ret=0
+      else
+        typeset -a charmaps
+        charmaps=( ${(f)"$(locale -m)"} )
+        _wanted charmaps expl charmap compadd -a charmaps && ret=0
+      fi
+    ;;
+  esac
+
+  return ret
+
+fi
diff --git a/Completion/Unix/Command/_locate b/Completion/Unix/Command/_locate
index 694f506c3..23305f798 100644
--- a/Completion/Unix/Command/_locate
+++ b/Completion/Unix/Command/_locate
@@ -1,132 +1,96 @@
-#compdef locate mlocate slocate
+#compdef locate mlocate slocate glocate
 
-# Decide if we are using mlocate or slocate.
-local ltype basename=${words[1]:t} input
-# If we can't, use this guess.
-local best_guess=mlocate
+local variant=$service
+local -a args
+[[ $service = locate ]] &&
+  _pick_variant -r variant glocate=findutils mlocate=mlocate slocate=secure $OSTYPE -V
+args=( '(-)'{-V,--version}'[display version information]' )
 
-case $basename in
-  ([ms]locate)
-  ltype=$basename
-  ;;
-
-  (locate)
-  input="$(_call_program locate $words[1] -V 2>&1)"
-  case $input in
-    (*mlocate*)
-    ltype=mlocate
-    ;;
-
-    (*(#i)secure locate*)
-    ltype=slocate
-    ;;
-
-    (*(#i)gnu locate*|*findutils*gnu*)
-    ltype=gnu
-    ;;
-
-    (*"illegal option"*)
-    if [[ $OSTYPE == (freebsd|openbsd|dragonfly|darwin)* ]]; then
-      ltype=bsd
-    else
-      ltype=$best_guess
-    fi
-    ;;
-
-    # guess
-    (*)
-    ltype=$best_guess
-    ;;
-  esac
-  ;;
-
-  (*)
-  # too dangerous to run: guess
-  ltype=$best_guess
-esac
+case $variant in
+  [mg]locate)
+    args+=(
+      '(-A --all)'{-A,--all}'[only print entries that match all patterns]'
+      '(-E --non-existing -e --existing)'{-e,--existing}'[restrict display to existing files]'
+      '(-c --count)'{-c,--count}'[output the number of matching entries]'
+      '(-i --ignore-case)'{-i,--ignore-case}'[ignore case distinctions in patterns]'
+      '(-w --wholename -b --basename)'{-w,--wholename}'[match entire file path (default)]'
+      '(-w --wholename -b --basename)'{-b,--basename}'[match only the basename of files in the database]'
+      '(-P -H --no-follow -L --follow)'{-P,-H,--nofollow}"[don't follow symbolic links]"
+      '(-P -H --no-follow -L --follow)'{-L,--follow}'[follow symbolic links to find existing files (default)]'
+      '(-0 --null)'{-0,--null}'[output separated by NUL characters]'
+      '(-S --statistics)'{-S,--statistics}'[show database statistics]'
+    )
+  ;|
 
-case $ltype in
   (mlocate)
-  # actually, -d can take a colon-separate list
   # -r/--regexp mean no normal arguments, so shouldn't complete
   # -m and --mmap are ignored, so don't bother
   # -s and --stdio likewise
-  _arguments -s -S : \
-    {-b,--basename}'[match only the basename of files in the database]' \
-    {-c,--count}'[output the number of matching entries]' \
-    {-d,--database=}'[use alternative database]:database:_files' \
-    {-e,--existing}'[restrict display to existing files]' \
-    {-L,--follow}'[follow symbolic links to find existing files (default)]' \
-    {-h,--help}'[show help]' \
-    {-i,--ignore-case}'[ignore case distinctions in patterns]' \
-    {-l,-n,--limit=}'[limit search results]:file limit: ' \
-    {-P,-H,--nofollow}'[don'\''t follow symbolic links]' \
-    {-0,--null}'[output separated by NUL characters]' \
-    {-S,--statistics}'[show database statistics]' \
-    {-q,--quiet}'[don'\''t report errors]' \
-    {-r,--regexp=}'[search for given basic regexp]:basic regexp: ' \
-    --regex'[patterns are extended regexps]' \
-    {-V,--version}'[show version]' \
-    {-w,--wholename}'[match entire file path (default)]' \
-    '*:pattern: '
+    args=( -s -S : $args
+      \*{-d,--database=}'[use alternative database]:database:_sequence -s \: _files'
+      '(-)'{-h,--help}'[display help information]'
+      '(-l -n --limit)'{-l,-n,--limit=}'[limit search results]:file limit'
+      '(-q --quiet)'{-q,--quiet}"[don't report errors]"
+      '(:)*'{-r,--regexp=}'[search for given basic regexp]:basic regexp'
+      '--regex[patterns are extended regexps]'
+    )
   ;;
 
   (slocate)
   # -d can take path
   # -e can take a comma-separated list of directories.
   # -f should complete list of file system types like mount
-  _arguments -s -S : \
-    -u'[create slocate database starting at path /]' \
-    -U'[create slocate database starting at given path]:directory:_files -/' \
-    -c'[parse GNU locate updatedb with -u, -U]' \
-    -e'[exclude directories with -u, -U]:directories:_files -/' \
-    -f'[exclude file system types from db with -u, -U]:file system:_file_systems' \
-    -l'[security level]:level:(0 1)' \
-    -q'[quiet mode]' \
-    -n'[limit search results]:file limit: ' \
-    -i'[case insensitive search]' \
-    {-r,--regexp=}'[use basic regular expression]:regexp: ' \
-    {-o,--output=}'[specify database to create]:database:_files' \
-    {-d,--database=}'[specify database to search]:database:_files' \
-    {-h,--help}'[display help]' \
-    {-v,--verbose}'[display files when creating database]' \
-    {-V,--version}'[display version]' \
-    '*:pattern: '
+    args=( -s -S : $args
+      -u'[create slocate database starting at path /]'
+      -U'[create slocate database starting at given path]:directory:_files -/'
+      -c'[parse GNU locate updatedb with -u, -U]'
+      -e'[exclude directories with -u, -U]:directories:_files -/'
+      -f'[exclude file system types from db with -u, -U]:file system:_file_systems'
+      -l'[security level]:level:(0 1)'
+      -q'[quiet mode]'
+      -n'[limit search results]:file limit '
+      -i'[case insensitive search]'
+      {-r,--regexp=}'[use basic regular expression]:regexp'
+      {-o,--output=}'[specify database to create]:database:_files'
+      {-d,--database=}'[specify database to search]:database:_files'
+      '(-)'{-h,--help}'[display help information]'
+      {-v,--verbose}'[display files when creating database]'
+    )
   ;;
 
-  (gnu)
-  _arguments -s : \
-    {-d,--database=}'[use alternative database]:database:_files' \
-    {-e,--existing}'[restrict display to existing files]' \
-    {-E,--non-existing}'[allow display of nonexistent files (default)]' \
-    {-i,--ignore-case}'[ignore case distinctions in patterns]' \
-    {-w,--wholename}'[match entire file path (default)]' \
-    {-b,--basename}'[match only the basename of files in the database]' \
-    {-l,-n,--limit=}'[limit search results]:file limit: ' \
-    {-S,--statistics}'[show database statistics]' \
-    {-0,--null}'[output separated by NUL characters]' \
-    {-c,--count}'[output the number of matching entries]' \
-    {-P,-H,--nofollow}'[don'\''t follow symbolic links]' \
-    {-L,--follow}'[follow symbolic links to find existing files (default)]' \
-    {-A,-all}'[match all arguments instead of at least one]' \
-    {-p,--print}'[include search results with statistics or count]' \
-    {-r,--regex=}'[patterns are regular expressions]:basic regexp: ' \
-    --regextype='[select type of regular expression]' \
-    {-V,--version}'[show version]' \
-    --help'[show help]' \
-    '*:pattern: '
+  (glocate)
+    args=( -s : $args
+      \*{-d,--database=}'[use alternative database]:database:_files'
+      '(-E --non-existing -e --existing)'{-E,--non-existing}'[restrict display to nonexistent files]'
+      '(-l --limit)'{-l,--limit=}'[limit search results]:file limit: '
+      '--max-database-age[specify database age at which warning should be issued]:age (days) [8]'
+      '(-p --print)'{-p,--print}'[include search results with statistics or count]'
+      \*{-r,--regex=}'[patterns are regular expressions]:regexp'
+      --regextype='[select type of regular expression]:regex type [basic]:(findutils-default awk egrep ed emacs gnu-awk grep posix-awk posix-basic posix-egrep posix-extended posix-minimal-basic sed)'
+      '(-)'--help'[display help information]'
+    )
   ;;
 
-  (bsd)
-  _arguments -s -S -A '-*' \
-    '(-S)-0[separate file names by NUL characters]' \
-    '(- *)-S[show database statistics and exit]' \
-    '(-S)-c[output the number of matching file names]' \
-    '(-S)*-d[specify database to search]:database:_files' \
-    '(-S)-i[ignore case distinctions in pattern and database]' \
-    '(-S)-l[limit output to specified number of file names]:file limit: ' \
-    '(-S)-m[use mmap(2) instead of stdio(3) (default)]' \
-    '(-S)-s[use stdio(3) instead of mmap(2)]' \
-    '*:pattern: '
-  ;;
+  (freebsd|openbsd|dragonfly|darwin)*)
+    args=( -s -S -A '-*'
+      '(-S)-c[output the number of matching file names]'
+      '(-S)-i[ignore case distinctions in pattern and database]'
+      '(-S)-l[limit output to specified number of file names]:file limit '
+      '(- *)-S[show database statistics and exit]'
+    )
+  ;|
+  openbsd*)
+    args+=( '(-S)-b[match only the basename of files in the database]' )
+  ;|
+  (freebsd|dragonfly|darwin)*)
+    args+=(
+      '(-S)-0[separate file names by NUL characters]'
+      '(-S)-m[use mmap(2) instead of stdio(3) (default)]'
+      '(-S)-s[use stdio(3) instead of mmap(2)]'
+    )
+  ;|
+  (*) args+=( '(-S)*-d[specify database to search]:database:_files' ) ;;
+
 esac
+
+_arguments $args '*: :_guard "^-*" pattern'
diff --git a/Completion/Unix/Command/_look b/Completion/Unix/Command/_look
index 24a016af4..be8cf8c14 100644
--- a/Completion/Unix/Command/_look
+++ b/Completion/Unix/Command/_look
@@ -1,16 +1,34 @@
 #compdef look
 
-local curcontext="$curcontext" state line expl ret=1
+local curcontext="$curcontext" state line expl args sep='+' ret=1
 typeset -A opt_args
 
-_arguments -C -s \
-  '-t+[termination character]:termination character:' \
-  '-f[case insensitive]' \
-  '-d[dictionary order]' \
-  ':string:->string' \
-  ':dictionary file:_files' && ret=0
+case $OSTYPE in
+  linux*)
+    args=(
+      '(2 -a --alternative)'{-a,--alternative}'[use alternative dictionary file]'
+      '(-d --alphanum)'{-d,--alphanum}'[dictionary order]'
+      '(-f --ignore-case)'{-f,--ignore-case}'[case insensitive]'
+      '(-t --terminate)'{-t+,--terminate=}'[specify termination character]:termination character'
+      '(-)'{-h,--help}'[display help information]'
+      '(-)'{-V,--version}'[display version information]'
+    )
+  ;;
+  solaris*) sep='-' ;&
+  *)
+    args=( -A "-*"
+      "-t${sep}[specify termination character]:termination character"
+      '-f[case insensitive]'
+      '-d[dictionary order]'
+    )
+  ;;
+esac
+
+_arguments -C -s -S $args \
+  '1:string:->string' \
+  '2:dictionary file:_files' && ret=0
 
 [[ -n "$state" && ! -prefix - ]] && _wanted values expl 'word prefix' \
-    compadd - $(_call_program words $words[1] '"$PREFIX"' 2>/dev/null) && return
+    compadd - $(_call_program words $words[1] '"$PREFIX"') && return
 
 return ret
diff --git a/Completion/Unix/Command/_lp b/Completion/Unix/Command/_lp
index 8da84a180..63fbab78a 100644
--- a/Completion/Unix/Command/_lp
+++ b/Completion/Unix/Command/_lp
@@ -44,7 +44,7 @@ _lp_job_options()
 
   # The program specified by the style list-printer-options should list jobs in
   # the same style as lpoptions -l.
-  if compset -P '*='; then
+  if compset -P 1 '*='; then
     # List values for the option
     case ${IPREFIX%=} in
       (media)
diff --git a/Completion/Unix/Command/_ls b/Completion/Unix/Command/_ls
index f2e149453..955b0894d 100644
--- a/Completion/Unix/Command/_ls
+++ b/Completion/Unix/Command/_ls
@@ -1,6 +1,6 @@
 #compdef ls gls
 
-local arguments is_gnu
+local arguments is_gnu datef
 
 if ! _pick_variant gnu=gnu unix --help; then
   arguments=(
@@ -11,10 +11,10 @@ if ! _pick_variant gnu=gnu unix --help; then
     '-R[list subdirectories recursively]'
 
     '(-k)-h[print sizes in human readable form]'
+    '(-h)-k[print sizes in kilobytes]'
 
     '-i[print file inode numbers]'
     '(-l -g -1 -C -m -x)-l[long listing]'
-    '(-l -1 -C -m -x)-g[long listing but without owner information]'
     '(-l -g -C -m -x)-1[single column output]'
     '(-l -g -1 -m -x)-C[list entries in columns sorted vertically]'
     '(-l -g -1 -C -x)-m[comma separated]'
@@ -27,6 +27,7 @@ if ! _pick_variant gnu=gnu unix --help; then
 
     '-r[reverse sort order]'
 
+    '(-t)-S[sort by size]'
     '(-S)-t[sort by modification time]'
 
     '(-p)-F[append file type indicators]'
@@ -34,31 +35,80 @@ if ! _pick_variant gnu=gnu unix --help; then
 
     '-n[numeric uid, gid]'
 
-    '(-B -b -w -q)-b[as -B, but use C escape codes whenever possible]'
     '(-B -b -w -q)-q[hide control chars]'
 
     '*:files:_files'
   )
   if [[ "$OSTYPE" = (netbsd*|dragonfly*|freebsd*|openbsd*|darwin*) ]]; then
     arguments+=(
-      '(-h)-k[print sizes of 1k]'
-      '(-t)-S[sort by size]'
       '-T[show complete time information]'
-      '-o[display file flags]'
-      '-f[output is not sorted]'
-      '(-B -b -w -q)-w[print raw characters]'
+      '(-a -A -r -S -t)-f[output is not sorted]'
+    )
+  fi
+  if [[ $OSTYPE = (netbsd*|dragonfly*|freebsd*|openbsd*) ]]; then
+    arguments+=( '-o[display file flags]' )
+  fi
+  if [[ $OSTYPE = (netbsd*|dragonfly*|freebsd*|darwin*) ]]; then
+    arguments+=(
       '(-B -b -w -q)-B[print octal escapes for control characters]'
+      '(-B -b -w -q)-b[as -B, but use C escape codes whenever possible]'
+      '(-B -b -w -q)-w[print raw characters]'
+      '-W[display whiteouts when scanning directories]'
     )
   fi
-  if [[ "$OSTYPE" = (freebsd*|darwin*) ]]; then
+  if [[ $OSTYPE = (netbsd*|openbsd*|darwin*|solaris*) ]]; then
+    arguments+=(
+      '(-l -1 -C -m -x)-g[long listing but without owner information]'
+    )
+  fi
+  if [[ $OSTYPE = netbsd* ]]; then
+    arguments+=(
+      '-M[output file sizes in comma-separated form]'
+      '-O[output only leaf (non-directory) files]'
+      '-P[print full pathname for each file]'
+      "-X[don't cross mount points when recursing]"
+    )
+  fi
+  if [[ $OSTYPE = (dragonfly*|freebsd*|openbsd*|darwin*) ]]; then
+    arguments+=( '-H[follow symlinks on the command line]' )
+  fi
+  if [[ $OSTYPE = (dragonfly*|freebsd*|darwin*) ]]; then
     arguments+=(
       '-G[enable colorized output]'
-      '-H[follow symlinks on the command line]'
       '-P[do not follow symlinks]'
     )
   fi
+  if [[ $OSTYPE = (dragonfly*|freebsd*) ]]; then
+    arguments+=(
+      '(-A)-I[prevent -A from being automatically set for the super-user]'
+    )
+  fi
+  if [[ $OSTYPE = dragonfly* ]]; then
+    arguments+=( '-y[display FSMID in long listing]' )
+  fi
+  if [[ $OSTYPE = (freebsd*|darwin*) ]]; then
+    arguments+=( '(-c -u)-U[file creation time]' )
+  fi
+  if [[ $OSTYPE = freebsd* ]]; then
+    arguments+=(
+      '-,[print file sizes grouped and separated by thousands]'
+      '-D+[specify format for date]:format: _date_formats'
+      '-y[with -t, sort filenames in the same order as the time]'
+      '-Z[display MAC label]'
+    )
+  fi
+  if [[ $OSTYPE = darwin* ]]; then
+    arguments+=(
+      '-@[display extended attribute keys and sizes in long listing]'
+      '-e[display ACL in long listing]'
+      '(-l -1 -C -m -x)-o[long listing but without group information]'
+      '-O[display file flags]'
+      '-v[print raw characters]'
+    )
+  fi
   if [[ $OSTYPE = solaris* ]]; then
     arguments+=(
+      '(-q)-b[print octal escapes for control characters]'
       '(-l -1 -C -m -x)-o[long listing but without group information]'
       '(-l -t -s -r -a)-f[interpret each argument as a directory]'
       '(-E -l)-e[long listing with full and consistent date/time]'
@@ -70,6 +120,7 @@ if ! _pick_variant gnu=gnu unix --help; then
     )
   fi
 else
+  [[ $PREFIX = *+* ]] && datef='formats:format: _date_formats'
   arguments=(
     '(--all -a -A --almost-all)'{--all,-a}'[list entries starting with .]'
     '(--almost-all -A -a --all)'{--almost-all,-A}'[list all except . and ..]'
@@ -77,61 +128,64 @@ else
     '(--ignore-backups -B)'{--ignore-backups,-B}"[don't list entries ending with ~]"
     '(--directory -d)'{--directory,-d}'[list directory entries instead of contents]'
     '(--dired -D)'{--dired,-D}"[generate output designed for Emacs' dired mode]"
-    '(--ignore,-I)'{--ignore,-I}"[don't list entire matching pattern]:pattern:"
-    '(--dereference -L --dereference-command-line --dereference-command-line-symlink-to-dir)'{--dereference,-L}'[list referenced file for sym link]'
-    '(--dereference -L --dereference-command-line --dereference-command-line-symlink-to-dir)'{--dereference-command-line,--dereference-command-line-symlink-to-dir}
+    '*'{--ignore=,-I+}"[don't list entries matching pattern]:pattern: "
+    '(--dereference -L --dereference-command-line -H --dereference-command-line-symlink-to-dir)'{--dereference,-L}'[list referenced file for sym link]'
+    '(--dereference -L --dereference-command-line -H --dereference-command-line-symlink-to-dir)'{--dereference-command-line,-H}'[follow symlink on the command line]'
+    '(--dereference -L --dereference-command-line -H)'--dereference-command-line-symlink-to-dir
     '(--recursive -R)'{--recursive,-R}'[list subdirectories recursively]'
 
     '(--no-group -G)'{--no-group,-G}'[inhibit display of group information]'
-    '(--human-readable -h -H --si)'{--human-readable,-h}'[print sizes in human readable form]'
-    '(--si -H -h --human-readable)'{--si,-H}'[sizes in human readable form; powers of 1000]'
+    '(--block-size --human-readable -h --si --kilobytes -k)'{--human-readable,-h}'[print sizes in human readable form]'
+    '(--block-size --human-readable -h --si --kilobytes -k)--si[sizes in human readable form; powers of 1000]'
     '(--inode -i)'{--inode,-i}'[print file inode numbers]'
 
     '(--format -l -g -o -1 -C -m -x)-l[long listing]'
     '(--format -l -1 -C -m -x)-g[long listing but without owner information]'
+    --group-directories-first
     '(--format -l --no-group -G -1 -C -m -x)-o[no group, long]'
     '(--format -l -g -o -C -m -x)-1[single column output]'
     '(--format -l -g -o -1 -m -x)-C[list entries in columns sorted vertically]'
     '(--format -l -g -o -1 -C -x)-m[comma separated]'
     '(--format -l -g -o -1 -C -m)-x[sort horizontally]'
-    '(-l -g -o -1 -C -m -x)--format=:format:(verbose long commas horizontal across vertical single-column)'
+    '(-l -g -o -1 -C -m -x)--format=[specify output format]:format:(verbose long commas horizontal across vertical single-column)'
 
     '(--size -s -f)'{--size,-s}'[display size of each file in blocks]'
 
     '(--time -u)-c[status change time]'
     '(--time -c)-u[access time]'
     '(-c -u)--time=[specify time to show]:time:(ctime status use atime access)'
-    '--time-style=[show times using specified style]:time style:(full-iso long-iso iso locale)'
+    '--time-style=[show times using specified style]:style: _alternative "time-styles\:time style\:(full-iso long-iso iso locale)" $datef'
 
     '(-a --all -U -l --format -s --size -t --sort --full-time)-f[unsorted, all, short list]'
-    '(--reverse)'{--reverse,-r}'[reverse sort order]'
+    '(--reverse -r -U -f)'{--reverse,-r}'[reverse sort order]'
 
     '(--sort -t -U -v -X)-S[sort by size]'
     '(--sort -S -U -v -X)-t[sort by modification time]'
     '(--sort -S -t -v -X)-U[unsorted]'
     '(--sort -S -t -U -X)-v[sort by version (filename treated numerically)]'
     '(--sort -S -t -U -v)-X[sort by extension]'
-    '(-S -t -U -v -X)--sort=:sort:(size time none version extension)'
+    '(-S -t -U -v -X)--sort=[specify sort key]:sort key:(size time none version extension)'
 
     '--color=-[control use of color]:color:(never always auto)'
-
+    "*--hide=[like -I, but overridden by -a or -A]:pattern: "
     '(--classify -F --indicator-style -p --file-type)'{--classify,-F}'[append file type indicators]'
-    '(--file-type -p --indicator-style -F --classify)'{--file-type,-p}'[append file type indicators except *]'
-    '(-F --classify -p --file-type)--indicator-style=:indicator style:(none file-type classify)'
+    '(--file-type -p --indicator-style -F --classify)--file-type[append file type indicators except *]'
+    '(--file-type -p --indicator-style -F --classify)-p[append / to directories]'
+    '(-F --classify -p --file-type)--indicator-style=[specify indicator style]:indicator style:(none file-type classify slash)'
 
     '(-f)--full-time[list both full date and full time]'
 
-    '(--kilobytes -k --block-size)'{--kilobytes,-k}'[use block size of 1k]'
-    '(-k --kilobytes)--block-size=[specify block size]:block size (bytes):(1024)'
+    '(--block-size --human-readable -h --si --kilobytes -k)'{--kilobytes,-k}'[use block size of 1k]'
+    '(--human-readable -h --si --kilobytes -k)--block-size=[specify block size]:block size (bytes):(1024)'
 
     '(--numeric-uid-gid -n)'{--numeric-uid-gid,-n}'[numeric uid, gid]'
     '(--tabsize -T)'{--tabsize=,-T+}'[specify tab size]:tab size'
     '(--width -w)'{--width=,-w+}'[specify screen width]:screen width'
 
     '(--quoting-style -b --escape -N --literal -Q --quote-name)'{--escape,-b}'[print octal escapes for control characters]'
-    '(--quoting-style -b --escape -N --literal -Q --quote-name)'{--literal,-N}'[print raw characters]'
+    '(--quoting-style -b --escape -N --literal -Q --quote-name)'{--literal,-N}'[print entry names without quoting]'
     '(--quoting-style -b --escape -N --literal -Q --quote-name)'{--quote-name,-Q}'[quote names]'
-    '(-b --escape -N --literal -Q --quote-name)--quoting-style=:quoting style:(literal shell shell-always c escape clocale locale)'
+    '(-b --escape -N --literal -Q --quote-name)--quoting-style=[specify quoting style]:quoting style:(literal shell shell-always c escape clocale locale)'
 
     '(--hide-control-chars -q --show-control-chars)'{--hide-control-chars,-q}'[hide control chars]'
     '(-q --hide-control-chars)--show-control-chars'
@@ -139,6 +193,11 @@ else
     '(- :)--version[display version information]'
     '*:files:_files'
   )
+  if [[ $OSTYPE = linux* ]]; then
+    arguments+=(
+      '(-Z --context)'{-Z,--context}'[print any security context of each file]'
+    )
+  fi
 fi
 
-_arguments -s $arguments
+_arguments -s -S : $arguments
diff --git a/Completion/Unix/Command/_lsof b/Completion/Unix/Command/_lsof
index c12b9910a..bbb6eab59 100644
--- a/Completion/Unix/Command/_lsof
+++ b/Completion/Unix/Command/_lsof
@@ -1,6 +1,6 @@
 #compdef lsof
 
-local curcontext="$curcontext" state line fields args
+local curcontext="$curcontext" state line expl fields args
 
 case $OSTYPE in
   linux*) args=( '-X[skip reporting of info on network connections]' ) ;;
diff --git a/Completion/Unix/Command/_lzop b/Completion/Unix/Command/_lzop
index 6f09cf47e..7661d117c 100644
--- a/Completion/Unix/Command/_lzop
+++ b/Completion/Unix/Command/_lzop
@@ -69,8 +69,9 @@ case "$state" in
       "F $sep Append a \`*' for executable files"
       "G $sep Inhibit display of group information"
       "Q $sep Enclose file names in double quotes" )
+    # The "Z" on the next line is sentinel to prevent the character class from being empty.
     disp=( ${disp[@]:#[Z$PREFIX]*} )
-    compset -P '[FGQ]*'
+    compset -P '[FGQ]#'
     compadd -d disp - ${disp[@]%% *} && ret=0
   ;;
 esac
diff --git a/Completion/Unix/Command/_make b/Completion/Unix/Command/_make
index c3f3961c9..a2ee9ecac 100644
--- a/Completion/Unix/Command/_make
+++ b/Completion/Unix/Command/_make
@@ -184,9 +184,10 @@ _make() {
       '(-j --jobs)'{-j,--jobs=}'[allow N jobs at once; infinite jobs with no arg]:number of jobs'
       '(-k --keep-going)'{-k,--keep-going}"[keep going when some targets can't be made]"
       '(-l --load-average --max-load)'{-l,--load-average=,--max-load}"[don't start multiple jobs unless load is below N]:load"
-      '(-L --check-symlik-times)'{-L,--check-symlink-times}'[use the latest mtime between symlinks and target]'
+      '(-L --check-symlink-times)'{-L,--check-symlink-times}'[use the latest mtime between symlinks and target]'
       '(-n --just-print --dry-run --recon)'{-n,--just-print,--dry-run,--recon}"[don't actually run any recipe; just print them]"
-      '*'{-o,--old-file=,--assume-old=}"[consider FILE to be very old and don't remake it]:file not to remake:->file"
+      '*'{-o,--old-file=,--assume-old=}"[consider specified file to be old and don't remake it]:file not to remake:->file"
+      '(-O --output-sync)'{-O-,--output-sync=-}'[synchronize output of parallel jobs]::granularity for grouping output:compadd -E 0 none line target recurse'
       '(-p --print-data-base)'{-p,--print-data-base}'[print makes internal database]'
       '(-q --question)'{-q,--question}'[run no recipe; exit status says if up to date]'
       '(-r --no-builtin-rules)'{-r,--no-builtin-rules}'[disable the built-in implicit rules]'
@@ -262,17 +263,20 @@ _make() {
 
     if [[ -n "$file" ]]
     then
-      if [[ $is_gnu == gnu ]] && zstyle -t ":completion:${curcontext}:targets" call-command
+      if [[ $is_gnu == gnu ]] 
       then
-        _make-parseMakefile $PWD < <(_call_program targets "$words[1]" -nsp --no-print-directory -f "$file" .PHONY 2> /dev/null)
+        if zstyle -t ":completion:${curcontext}:targets" call-command; then
+          _make-parseMakefile $PWD < <(_call_program targets "$words[1]" -nsp --no-print-directory -f "$file" .PHONY 2> /dev/null)
+        else
+          _make-parseMakefile $PWD < $file
+        fi
       else
-        case "$OSTYPE" in
-          freebsd*)
-          _make-parseMakefile $PWD < <(_call_program targets "$words[1]" -nsp -f "$file" .PHONY 2> /dev/null)
-    ;;
-    *)
+        if [[ $OSTYPE == (freebsd|dragonfly|netbsd)* || /$words[1] == */bmake* ]]; then
+          TARGETS+=(${=${(f)"$(_call_program targets "$words[1]" -s -f "$file" -V.ALLTARGETS 2> /dev/null)"}})
+          _make-parseMakefile $PWD < <(_call_program targets "$words[1]" -nsdg1Fstdout -f "$file" .PHONY 2> /dev/null)
+        else
           _make-parseMakefile $PWD < $file
-        esac
+        fi
       fi
     fi
 
diff --git a/Completion/Unix/Command/_man b/Completion/Unix/Command/_man
index 0534db753..67810e1dc 100644
--- a/Completion/Unix/Command/_man
+++ b/Completion/Unix/Command/_man
@@ -37,27 +37,43 @@ _man() {
 
   mrd=(${^_manpath/\%L/${LANG:-En_US.ASCII}}/mandb(N))
 
-  # $sect is from the command line, the "3p" in "man 3p memcpy"
+  # $sect is from the command line, the "3p" in "man 3p memcpy".
+  #   It may also be a |-joined (and later in the code "()"-enclosed) list of
+  #   section names.
+  #   TODO: disentangle this to always be an array.
   # $sect_dirname is from the filesystem, the "3" in "/usr/share/man/man3"
   # These are used by _man_pages
   local sect sect_dirname
   if [[ $OSTYPE = solaris* ]]; then
     sect=${${words[(R)-s*]#-s}:-$words[$words[(i)-s]+1]}
+    sect="${sect//,/|}"
   elif [[ -n ${sect:=$words[$words[(i)-S]+1]} || -n ${sect:=$MANSECT} ]]; then
-    if [[ $sect != ${sect::="${sect//:/|}"} ]]; then
-      sect="($sect)"
-    fi
+    sect="${sect//:/|}"
+    sect="${sect//,/|}"
   elif (( CURRENT > 2 )); then
-    sect=$words[2]
+    case $words[2] in
+      (-a) sect='*';;
+      (-*) ;;
+      (*)  sect=$words[2];;
+    esac
   fi
 
-  if [[ $sect = (<->*|1M|l|n) || $sect = \(*\|*\) ]]; then
-    dirs=( $^_manpath/(sman|man|cat)${~sect%%[^0-9]#}/ )
+  if [[ $sect = (<->*|1M|l|n) || $sect = *\|* ]]; then
+    () {
+      local -a sects=( ${(s.|.)sect} )
+      if [[ $sect != (l|n) ]]; then
+        sects=( ${sects%%[^0-9]#} )
+      fi
+      dirs=( $^_manpath/(sman|man|cat)${^sects}*/ )
+    }
+    if [[ $sect == *\|* ]]; then sect="($sect)"; fi
     awk="\$2 == \"$sect\" {print \$1}"
   else
     dirs=( $^_manpath/(sman|man|cat)*/ )
     awk='{print $1}'
   fi
+  # Solaris 11 and on have a man-index directory that doesn't contain manpages
+  dirs=( ${dirs:#*/man-index/} )
   if [[ $OSTYPE = solaris* && ( $words[CURRENT] = -s* || $words[CURRENT-1] == -s ) ]]; then
     [[ $words[CURRENT] = -s* ]] && compset -P '-s'
     sects=( ${(o)${dirs##*(man|cat)}%/} )
@@ -78,6 +94,13 @@ _man() {
       done
       (( ret )) || return 0
     done
+    ## To fall back to other sections' manpages when completing filenames, like
+    ## the 'else' codepath does:
+    #
+    # if (( ret )) && [[ $PREFIX$SUFFIX == */* ]]; then
+    #   sect_dirname=
+    #   _wanted manuals expl 'manual page' _man_pages && return
+    # fi
 
     return 1
   else
@@ -87,7 +110,7 @@ _man() {
 }
 
 _man_pages() {
-  local matcher pages dummy sopt
+  local pages sopt
 
   # What files corresponding to manual pages can end in.
   local suf='.((?|<->*)(|.gz|.bz2|.Z|.lzma))'
@@ -96,22 +119,17 @@ _man_pages() {
     # Easy way to test for versions of man that allow file names.
     # This can't be a normal man page reference.
     # Try to complete by glob first.
-    _path_files -g "*$suf" && return
-    _path_files
-    return
-  fi
-
-  zparseopts -E M+:=matcher
-
-  if (( $#matcher )); then
-    matcher=( ${matcher:#-M} )
-    matcher="$matcher"
-  else
-    matcher=
+    if [[ -n $sect_dirname ]]; then
+      _path_files -g "*.*$sect_dirname*(|.gz|.bz2|.Z|.lzma)" "$expl[@]"
+    else
+      _path_files -g "*$suf" "$expl[@]" && return
+      _path_files "$expl[@]"
+    fi
+    return $?
   fi
 
   pages=( ${(M)dirs:#*$sect_dirname/} )
-  compfiles -p pages '' '' "$matcher" '' dummy "*${(b)sect}*"
+  pages=( ${^pages}/"*$sect${sect:+"*"}" );
   pages=( ${^~pages}(N:t) )
 
   (($#mrd)) && pages[$#pages+1]=($(awk $awk $mrd))
diff --git a/Completion/Unix/Command/_md5sum b/Completion/Unix/Command/_md5sum
index c881a4e6d..073e6beba 100644
--- a/Completion/Unix/Command/_md5sum
+++ b/Completion/Unix/Command/_md5sum
@@ -5,10 +5,11 @@ _arguments -S \
   '(-c --check)'{-c,--check}'[read MD5 sums from the FILEs and check them]' \
   '--tag[create a BSD-style checksum]' \
   '(-t --text)'{-t,--text}'[read in text mode]' \
+  "--ignore-missing[don't fail or report status for missing files]" \
   '(-q --quiet)'{-q,--quiet}"[don't print OK for each successfully verified file]" \
   '--status[no output, status code shows success]' \
   '--strict[exit non-zero for improperly formatted checksum lines]' \
   '(-w --warn)'{-w,--warn}'[warn about improperly formatted checksum lines]' \
-  '--help[display help and exit]' \
-  '--version[output version information and exit]' \
+  '(-)--help[display help and exit]' \
+  '(-)--version[output version information and exit]' \
   '*:files:_files'
diff --git a/Completion/Unix/Command/_mencal b/Completion/Unix/Command/_mencal
index ca56c609d..4286b94ca 100644
--- a/Completion/Unix/Command/_mencal
+++ b/Completion/Unix/Command/_mencal
@@ -10,7 +10,7 @@ _arguments -C \
   '(--quiet -q)'{-q,--quiet}'[no top information]' \
   '(--nocolor -n)'{-n,--nocolor}'[noncolored output]' \
   '(--icolor -i)'{-i,--icolor}'[intersection color]:color:(red green blue yellow violet cyan shiny bold)' \
-  '(-)'{-h,--help}'[display help informaiton]' \
+  '(-)'{-h,--help}'[display help information]' \
   '(-)'{-V,--version}'[print version information]' \
   \*{-c,--config}'[config]:options:->option' && ret=0
 
diff --git a/Completion/Unix/Command/_module b/Completion/Unix/Command/_module
index 060f05b86..2a4dba740 100644
--- a/Completion/Unix/Command/_module
+++ b/Completion/Unix/Command/_module
@@ -41,7 +41,7 @@ _module()
     "purge:unload all loaded modules"
     "refresh:refresh all non-persistent components of loaded modules"
     "whatis:display module information"
-    "appropos:search for a given keyword in modules"
+    "apropos:search for a given keyword in modules"
     "keyword:search for a given keyword in modules"
     "initadd:add or append a module to the user's shell init file"
     "initprepend:add or prepend a module to the user's shell init files"
@@ -85,7 +85,7 @@ _module_available_modules()
 {
   if [[ -n $MODULEPATH ]] && [[ ${+_available_modules} -eq 0 ]]
   then
-    _available_modules=(${$(find ${(e)=MODULEPATH//:/ } -xtype f -print 2>/dev/null | grep -v \\.version | sed -e 's,\('${${(e)=MODULEPATH//:/\/\\\|}%\\\|}'\),,g' -e 's,^/*,,g'):#*\~})
+    _available_modules=(${$(find -L ${(e)=MODULEPATH//:/ } -type f -print 2>/dev/null | grep -v \\.version | sed -e 's,\('${${(e)=MODULEPATH//:/\/\\\|}%\\\|}'\),,g' -e 's,^/*,,g'):#*\~})
   fi
 }
 
@@ -93,14 +93,14 @@ _module_available_modules()
 (( $+functions[_module_help] )) || _module_help()
 {
   _module_available_modules
-  compadd "$@" -a -- _available_modules
+  _multi_parts / "($_available_modules)"
 }
 
 # Completion function for load|add
 (( $+functions[_module_load] )) || _module_load()
 {
   _module_available_modules
-  compadd "$@" -a -- _available_modules
+  _multi_parts / "($_available_modules)"
 }
 
 # Completion function for unload|rm
@@ -114,14 +114,14 @@ _module_available_modules()
 {
   # Actually first argument could be a loaded module
   _module_available_modules
-  compadd "$@" -a -- _available_modules
+  _multi_parts / "($_available_modules)"
 }
 
 # Completion function for display|show
 (( $+functions[_module_display] )) || _module_display()
 {
   _module_available_modules
-  compadd "$@" -a -- _available_modules
+  _multi_parts / "($_available_modules)"
 }
 
 # Completion function for avail
@@ -145,35 +145,35 @@ _module_available_modules()
 (( $+functions[_module_whatis] )) || _module_whatis()
 {
   _module_available_modules
-  compadd "$@" -a -- _available_modules
+  _multi_parts / "($_available_modules)"
 }
 
 # Completion function for initadd
 (( $+functions[_module_initadd] )) || _module_initadd()
 {
   _module_available_modules
-  compadd "$@" -a -- _available_modules
+  _multi_parts / "($_available_modules)"
 }
 
 # Completion function for initprepend
 (( $+functions[_module_initprepend] )) || _module_initprepend()
 {
   _module_available_modules
-  compadd "$@" -a -- _available_modules
+  _multi_parts / "($_available_modules)"
 }
 
 # Completion function for initrm
 (( $+functions[_module_initrm] )) || _module_initrm()
 {
   _module_available_modules
-  compadd "$@" -a -- _available_modules
+  _multi_parts / "($_available_modules)"
 }
 
 # Completion function for initswitch
 (( $+functions[_module_initswitch] )) || _module_initswitch()
 {
   _module_available_modules
-  compadd "$@" -a -- _available_modules
+  _multi_parts / "($_available_modules)"
 }
 
 _module "$@"
diff --git a/Completion/Unix/Command/_mosh b/Completion/Unix/Command/_mosh
index c19f6ebde..431fdbf9e 100644
--- a/Completion/Unix/Command/_mosh
+++ b/Completion/Unix/Command/_mosh
@@ -1,6 +1,7 @@
 #compdef mosh
 
 local curcontext="$curcontext" state line
+local -a _comp_priv_prefix
 
 _arguments -C \
   '(-)--help[display help information]' \
diff --git a/Completion/Unix/Command/_mount b/Completion/Unix/Command/_mount
index a3b58bb87..9a7041dee 100644
--- a/Completion/Unix/Command/_mount
+++ b/Completion/Unix/Command/_mount
@@ -619,7 +619,7 @@ if (( ! $+_fs_any )); then
       'longnames[force Windows 95 long filenames to be visible]'
       'nowin95[completely ignore Windows 95 extended file information]'
     )
-    if [[ "${OSTYPE}" =~ freebsd.* ]]; then
+    if [[ "${OSTYPE}" = freebsd* ]]; then
 	    _fs_msdosfs=( "$_fs_msdos[@]" )
     fi
     ;;
@@ -856,7 +856,7 @@ fsopt)
 devordir)
   local dev_tmp mp_tmp mline
 
-  if compset -P '*:'; then
+  if compset -P 1 '*:'; then
     _wanted exports expl 'exported path' compadd \
 	${${(f)"$(path+=( {/usr,}/sbin(N) ) _call_program exports \
 	showmount -e ${IPREFIX%:} 2>/dev/null)"}[2,-1]%% *} && ret=0
@@ -959,14 +959,20 @@ udevordir)
   esac
 
   local MATCH MBEGIN MEND
-  mp_tmp=("${(@qg::)mp_tmp}")
-  dpath_tmp=( "${(@Mqg::)dev_tmp:#/*}" )
-  dev_tmp=( "${(@qg::)dev_tmp:#/*}" )
+  # The complicated substitution for mount point names is required because
+  # characters in /etc/mtab that might confuse programs reading the names
+  # are encoded as exactly 3 octal digits, like for example \040 for space.
+  # The cleaner-looking ${(g::)mp_tmp} might consume too many digits.
+  # Both mp_tmp and dev_tmp are derived from /etc/mtab or "mount" output.
+  mp_tmp=("${(@)mp_tmp//(#m)\\[0-7](#c3)/${(#)$(( 8#${MATCH[2,-1]} ))}}")
+  dev_tmp=("${(@)dev_tmp//(#m)\\[0-7](#c3)/${(#)$(( 8#${MATCH[2,-1]} ))}}")
+  dpath_tmp=( "${(@M)dev_tmp:#/*}" )
+  dev_tmp=( "${(@)dev_tmp:#/*}" )
 
   _alternative \
     'device-labels:device label:compadd -a dev_tmp' \
-    'device-paths: device path:_canonical_paths -A dpath_tmp -N device-paths device\ path' \
-    'directories:mount point:_canonical_paths -A mp_tmp -N directories mount\ point' && ret=0
+    'device-paths: device path:_canonical_paths -A dpath_tmp -N -M "r:|/=* r:|=*" device-paths device\ path' \
+    'directories:mount point:_canonical_paths -A mp_tmp -N -M "r:|/=* r:|=*" directories mount\ point' && ret=0
   ;;
 labels)
   _wanted labels expl 'disk label' compadd /dev/disk/by-label/*(:t) && ret=0
diff --git a/Completion/Unix/Command/_mpc b/Completion/Unix/Command/_mpc
index 11eab8463..e0c6888b0 100644
--- a/Completion/Unix/Command/_mpc
+++ b/Completion/Unix/Command/_mpc
@@ -131,7 +131,7 @@ _mpc_helper_songnumbers() {
 (( $+functions[_mpc_helper_playlists] )) ||
 _mpc_helper_playlists() {
   local list expl
-  list=($(mpc lsplaylists))
+  list=(${(f)"$(mpc lsplaylists)"})
   _wanted list expl playlist compadd -M $MPC_PLAYLIST_MATCHER $expl -a list
 }
 
diff --git a/Completion/Unix/Command/_mutt b/Completion/Unix/Command/_mutt
index 3d433a0b2..67b974a8d 100644
--- a/Completion/Unix/Command/_mutt
+++ b/Completion/Unix/Command/_mutt
@@ -7,11 +7,12 @@ _arguments -s -S \
   '*-b[specify a BCC recipient]:BCC recipient:_email_addresses -n mutt' \
   '*-c[specify a CC recipient]:CC recipient:_email_addresses -n mutt' \
   '(- :)-D[print the value of all variables]' \
+  '(-x)-E[edit the draft (-H) or include (-i) file]' \
   '-d+[log debugging output to ~/.muttdebug0]:level:(1 2 3 4 5)' \
   '-e+[specify a post-init configuration command]:post-init configuration' \
   '-f+[specify mailbox to load]:mailbox: _mailboxes' \
   '-F+[specify an init file]:init file:_files' \
-  '-h[display help]' \
+  '(- :)-h[display help]' \
   '-H+[specify a draft file for message]:draft file:_files' \
   '-i+[specify file to include in message]:include file:_files' \
   '-m+[specify default mailbox type]:mailbox type:(mbox MMDF MH Maildir)' \
@@ -20,8 +21,8 @@ _arguments -s -S \
   '(- :)-Q+[query a configuration variable]:variable:(${${(f)"$(_call_program variables mutt -D 2>/dev/null)"}%%( is|=)*})' \
   '-R[open in read-only mode]' \
   '-s+[specify a subject]:subject:' \
-  '-v[display mutt version]' \
-  '-x[emulate mailx compose]' \
+  '(- :)-v[display mutt version]' \
+  '(-E)-x[emulate mailx compose]' \
   '-y[start listing mailboxes]' \
   '-z[start only if new messages]' \
   '-Z[open first mailbox with new mail]'
diff --git a/Completion/Unix/Command/_mysql_utils b/Completion/Unix/Command/_mysql_utils
index 6532d0a7b..f1ad97bcd 100644
--- a/Completion/Unix/Command/_mysql_utils
+++ b/Completion/Unix/Command/_mysql_utils
@@ -212,7 +212,7 @@ _mysql_utils() {
     '--defaults-extra-file=[read specified file after the global files]:defaults file:_files'
     '(-S --socket)'{-S+,--socket=}'[specify socket file to use for connection]:server socket file:_files'
     '(-h --host)'{-h+,--host=}'[specify server hostname]:hostname:_mysql_hosts'
-    '(-P,--port)'{-P+,--port=}'[specify port number for connection]:server port:_mysql_ports'
+    '(-P --port)'{-P+,--port=}'[specify port number for connection]:server port:_mysql_ports'
     '(-u --user)'{-u+,--user=}'[specify username for login]:server username:_mysql_users'
     '(-p --password)'{-p-,--password=}'[specify password to use for connection]:server password'
     '(-C --compress)'{-C,--compress}'[use compression in server/client protocol]'
diff --git a/Completion/Unix/Command/_nkf b/Completion/Unix/Command/_nkf
index 4d541274d..04977e467 100644
--- a/Completion/Unix/Command/_nkf
+++ b/Completion/Unix/Command/_nkf
@@ -35,7 +35,7 @@ _arguments -s \
   '(-m)-M-[MIME encode]:mime encode:((\:header B\:base64 Q\:quoted))' \
   '-h-[hirakana<->katakana]:hirakata:((1\:hirakana-\>katakana 2\:katakana-\>hirakana 3\:both))' \
   '-L-[line mode]:line mode:((u\:LF w\:CRLF m\:CR))' \
-  '-I[Convert non ISO-2022-JP charactor to GETA]' \
+  '-I[convert non ISO-2022-JP character to GETA]' \
   '(--cap-input --url-input)'{--cap-input,--url-input}'[Convert hex after \: or \%]' \
   '--overwrite[Overwrite original listed files by filtered result]' \
   '(-v --help)'{-v,--help}'[display help message]' \
diff --git a/Completion/Unix/Command/_nm b/Completion/Unix/Command/_nm
index 73d7508b4..963b43f27 100644
--- a/Completion/Unix/Command/_nm
+++ b/Completion/Unix/Command/_nm
@@ -58,6 +58,7 @@ if _pick_variant -r variant binutils=GNU elftoolchain=elftoolchain elfutils=elfu
 	'--special-syms[include special symbols in the output]'
 	'--synthetic[display synthetic symbols as well]'
 	"--target=[target object format]:targets:(${${(@M)${(f)$(_call_program targets nm --help)}:#*supported targets:*}##*: })"
+	'--with-symbol-versions[display version strings after symbol names]'
       )
     ;;
   esac
diff --git a/Completion/Unix/Command/_nmap b/Completion/Unix/Command/_nmap
index 437e68b7d..b3d3d488a 100644
--- a/Completion/Unix/Command/_nmap
+++ b/Completion/Unix/Command/_nmap
@@ -5,7 +5,7 @@ local curcontext="$curcontext" state line suf ret=1
 _arguments -C \
   '!-sI:zombie host:_hosts' \
   '!-P'{T,S,U}'+:port list' \
-  '*-s-[specify scan type]:scan type:((S\:TCP\ SYN\ scan T\:TCP\ connect\(\)\ scan F\:stealth\ FIN\ scan X\:stealth\ Xmas\ tree\ scan N\:stealth\ null\ scan P\:ping\ scanning U\:UDP\ scan O\:IP\ prototocol\ scan I\:idle\ scan A\:ACK\ scan W\:window\ scan R\:RPC\ scan L\:list\ scan V\:version\ detection))' \
+  '*-s-[specify scan type]:scan type:((S\:TCP\ SYN\ scan T\:TCP\ connect\(\)\ scan F\:stealth\ FIN\ scan X\:stealth\ Xmas\ tree\ scan N\:stealth\ null\ scan P\:ping\ scanning U\:UDP\ scan O\:IP\ protocol\ scan I\:idle\ scan A\:ACK\ scan W\:window\ scan R\:RPC\ scan L\:list\ scan V\:version\ detection))' \
   '-b[specify ftp relay host]:ftp relay host:_hosts' \
   '*-P-[specify probe types and options]:probe type/options:->probe-opts' \
   '-A[enable OS detection and version scanning]' \
@@ -29,7 +29,7 @@ _arguments -C \
   '--data_length[add random data to packets]:data length' \
   '(-R)-n[skip reverse DNS to speed things up]' \
   '(-n)-R[always do reverse DNS on targets]' \
-  '-r[do not ramdomize order in which ports are scanned]' \
+  "-r[don't randomize order in which ports are scanned]" \
   '-ttl[specify IPv4 time to live for sent packets]' \
   '--randomize_hosts[scan hosts in random order]' \
   '-M[specify maximum number of parallel TCP connects]:maximum TCP connects' \
diff --git a/Completion/Unix/Command/_openstack b/Completion/Unix/Command/_openstack
new file mode 100644
index 000000000..fcb704ac8
--- /dev/null
+++ b/Completion/Unix/Command/_openstack
@@ -0,0 +1,192 @@
+#compdef openstack aodh barbican ceilometer cinder cloudkitty designate freezer glance gnocchi heat ironic keystone magnum manila mistral monasca murano neutron nova sahara senlin tacker trove vitrage watcher zun
+
+# https://wiki.openstack.org/wiki/OpenStackClients
+# http://docs.openstack.org/user-guide/common/cli-install-openstack-command-line-clients.html
+
+local curcontext="$curcontext" state line expl ret=1
+
+local -a clnts_compl_new clnts_compl_old clnts_swift_like
+
+#
+# We support three different client categories:
+#  1) Clients with new style complete command where output is like:
+#
+#    cmds='alarm alarm-history capabilities complete help'
+#    cmds_alarm='create delete list show update'
+#    cmds_alarm_history='search show'
+#    cmds_alarm_history_search='-h --help -f --format -c --column --max-width --noindent --quote --query'
+#
+#  2) Clients with old style bash-completion command which does
+#     not separate options and commands:
+#
+#    --tenant_id floatingip-delete bgp-peer-delete --default-prefixlen net-create [...]
+#
+#  3) Swift, slightly different from 2)
+#
+clnts_compl_new=( aodh barbican designate freezer gnocchi openstack vitrage watcher )
+clnts_compl_old=( ceilometer cinder cloudkitty glance heat ironic keystone magnum manila mistral monasca murano neutron nova sahara senlin tacker trove zun )
+clnts_swift_like=( swift )
+
+# Python clients take quite some time to start up and some (openstack(1))
+# even go over the network for completions so we cache things pretty hard
+if (( ! $+_cache_openstack_clnt_opts )); then
+  typeset -gA _cache_openstack_clnt_outputs
+  typeset -gA _cache_openstack_clnt_opts
+  typeset -gA _cache_openstack_clnt_cmds
+  typeset -gA _cache_openstack_clnt_cmds_opts
+  typeset -gA _cache_openstack_clnt_cmds_subcmds
+  typeset -gA _cache_openstack_clnt_cmds_subcmd_opts
+fi
+
+local -a conn_opts
+local opt arg word
+# Only openstack(1) requires parameters to provide completion info
+if [[ $service == openstack && -n ${words[(r)--os-*]} ]]; then
+  if (( ! $+_cache_openstack_conn_opts )); then
+    _cache_openstack_conn_opts=( ${(M)${=${(f)"$($service help 2>/dev/null)"}}:#--os-*} )
+  fi
+  # --os-tenant-id --os-tenant-name are deprecated but still widely used
+  for opt in ${=_cache_openstack_conn_opts} --os-tenant-id --os-tenant-name; do
+    arg=
+    for word in ${words:1}; do
+      [[ $word == $opt ]] && arg=$word && break
+    done
+    [[ -n $arg && -n ${arg##-*} ]] && conn_opts=( $conn_opts $opt $arg )
+  done
+fi
+
+# New style clients
+if [[ -n ${clnts_compl_new[(r)$service]} ]]; then
+  if [[ -z $_cache_openstack_clnt_cmds[$service] ]]; then
+    # Populate caches - clnt_outputs is command raw output used later
+    _cache_openstack_clnt_outputs[$service]=${:-"$($service ${(Q)conn_opts} complete 2>/dev/null)"}
+    _cache_openstack_clnt_opts[$service]=${${${${(M)${${${${=${(f)"$($service help 2>/dev/null)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}/,}/\.}%--os-}
+    _cache_openstack_clnt_cmds[$service]=${${${${_cache_openstack_clnt_outputs[$service]}/* cmds=\'}/\'*}/complete}
+  fi
+  local cmd subcmd
+  # Determine the command
+  for word in ${words:1}; do
+    local s=${_cache_openstack_clnt_cmds[$service]}
+    [[ $s[(wI)$word] -gt 0 ]] && cmd=$word && break
+  done
+  # Populate the subcommand cache
+  if [[ -n $cmd && -z $_cache_openstack_clnt_cmds_subcmds[$service$cmd] ]]; then
+      local t=cmds_${cmd//-/_}
+      _cache_openstack_clnt_cmds_subcmds[$service$cmd]=${${${_cache_openstack_clnt_outputs[$service]}/* $t=\'}/\'*}
+  fi
+  # Determine the subcommand
+  if [[ -n $cmd ]]; then
+    for word in ${words:2}; do
+      local s=${_cache_openstack_clnt_cmds_subcmds[$service$cmd]}
+      [[ $s[(wI)$word] -gt 0 ]] && subcmd=$word && break
+    done
+    # Populate subcommand option cache
+    if [[ -n $subcmd && -z $_cache_openstack_clnt_cmds_subcmd_opts[$service${cmd}--$subcmd] ]]; then
+      local t=cmds_${cmd//-/_}_${subcmd//-/_}
+      _cache_openstack_clnt_cmds_subcmd_opts[$service${cmd}--$subcmd]=${${${_cache_openstack_clnt_outputs[$service]}/* $t=\'}/\'*}
+    fi
+  fi
+  # Special treatment for the help command
+  if [[ $cmd == help ]]; then
+      if [[ $words[CURRENT-1] == $cmd && $words[CURRENT] != -* ]]; then
+        # Offer commands
+        [[ -n $_cache_openstack_clnt_cmds[$service] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0
+      elif [[ $words[CURRENT-2] == $cmd && $words[CURRENT-1] != -* && $words[CURRENT] != -* ]]; then
+        # Offer subcommands
+        local cmd=$words[CURRENT-1]
+        local t=cmds_${cmd//-/_}
+        [[ -z $_cache_openstack_clnt_cmds_subcmds[$service$cmd] ]] && _cache_openstack_clnt_cmds_subcmds[$service$cmd]=${${${_cache_openstack_clnt_outputs[$service]}/* $t=\'}/\'*}
+        [[ -n $_cache_openstack_clnt_cmds_subcmds[$service$cmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_subcmds[$service$cmd]} && ret=0
+      else
+        # Handle help<TAB> properly
+        _values -w option help && ret=0
+      fi
+  # Client options
+  elif [[ -z $cmd && $words[CURRENT] == -* ]]; then
+    _values -w option ${(u)=_cache_openstack_clnt_opts[$service]} && ret=0
+  # Commands
+  elif [[ -z $cmd ]]; then
+    if [[ -z $_cache_openstack_clnt_cmds[$service] ]]; then
+      _message "missing authentication options"
+    else
+      _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0
+    fi
+  # Subcommands
+  elif [[ -z $subcmd ]]; then
+    [[ -n $_cache_openstack_clnt_cmds_subcmds[$service$cmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_subcmds[$service$cmd]} && ret=0
+  # Subcommand options
+  else
+    { ! zstyle -T ":completion:${curcontext}:options" prefix-needed || [[ -prefix - ]] } && \
+      [[ -n $_cache_openstack_clnt_cmds_subcmd_opts[$service${cmd}--$subcmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_subcmd_opts[$service${cmd}--$subcmd]//\:/\\\:} && ret=0
+  fi
+
+# Old style clients
+elif [[ -n ${clnts_compl_old[(r)$service]} ]]; then
+  if [[ -z $_cache_openstack_clnt_cmds[$service] ]]; then
+    # Populate caches
+    _cache_openstack_clnt_opts[$service]=${${${(M)${${${${=${(f)"$($service help 2>/dev/null)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}/,}/\.}
+    _cache_openstack_clnt_cmds[$service]=${${(M)${=${(f)"$($service bash-completion 2>/dev/null)"}}:#[A-Za-z]*}/bash-completion}
+  fi
+  local cmd
+  # Determine the command
+  for word in ${words:1}; do
+    local s=${_cache_openstack_clnt_cmds[$service]}
+    [[ $s[(wI)$word] -gt 0 ]] && cmd=$word && break
+  done
+  # Populate command option cache
+  # Mostly no options for help, prevent consecutive calls with help here
+  if [[ -n $cmd && $cmd != help && -z $_cache_openstack_clnt_cmds_opts[$service$cmd] ]]; then
+    _cache_openstack_clnt_cmds_opts[$service$cmd]=${${${(M)${${${${=${(f)"$($service help $cmd 2>/dev/null)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}/,}/\.}
+  fi
+  # Special treatment for the help command
+  if [[ $cmd == help ]]; then
+      if [[ $words[CURRENT-1] == help && $words[CURRENT] != -* ]]; then
+        _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0
+      else
+        _values -w option help && ret=0
+      fi
+  # Client options
+  elif [[ -z $cmd && $words[CURRENT] == -* ]]; then
+    _values -w option ${(u)=_cache_openstack_clnt_opts[$service]} && ret=0
+  # Commands
+  elif [[ -z $cmd ]]; then
+    _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0
+  # Command options
+  else
+    { ! zstyle -T ":completion:${curcontext}:options" prefix-needed || [[ -prefix - ]] } && \
+      [[ -n $_cache_openstack_clnt_cmds_opts[$service$cmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_opts[$service$cmd]//\:/\\\:} && ret=0
+  fi
+
+# Swift like clients
+elif [[ -n ${clnts_swift_like[(r)$service]} ]]; then
+  if [[ -z $_cache_openstack_clnt_cmds[$service] ]]; then
+    # Populate caches - clnt_outputs is command raw output used later
+    _cache_openstack_clnt_outputs[$service]=${(f)"$($service --help 2>/dev/null)"}
+    _cache_openstack_clnt_opts[$service]=${${${${(M)${${${${=_cache_openstack_clnt_outputs[$service]}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}/,}/\.}/=*}
+    _cache_openstack_clnt_cmds[$service]=${=${(M)${(M)${(f)_cache_openstack_clnt_outputs[$service]}:#    [a-z]*}/ [A-Z]*}}
+  fi
+  local cmd
+  # Determine the command
+  for word in ${words:1}; do
+    local s=${_cache_openstack_clnt_cmds[$service]}
+    [[ $s[(wI)$word] -gt 0 ]] && cmd=$word && break
+  done
+  # Populate command option cache
+  if [[ -n $cmd && -z $_cache_openstack_clnt_cmds_opts[$service$cmd] ]]; then
+    _cache_openstack_clnt_cmds_opts[$service$cmd]=${${${(M)${${${${=${(f)"$($service $cmd --help 2>/dev/null)"}}/\[}/\]}/\;}:#-[-0-9A-Za-z]*}/,}/\.}
+  fi
+  # Client options
+  if [[ -z $cmd && $words[CURRENT] == -* ]]; then
+    _values -w option ${(u)=_cache_openstack_clnt_opts[$service]} && ret=0
+  # Commands
+  elif [[ -z $cmd ]]; then
+    _values -w option ${(u)=_cache_openstack_clnt_cmds[$service]} && ret=0
+  # Command options
+  else
+    { ! zstyle -T ":completion:${curcontext}:options" prefix-needed || [[ -prefix - ]] } && \
+      [[ -n $_cache_openstack_clnt_cmds_opts[$service$cmd] ]] && _values -w option ${(u)=_cache_openstack_clnt_cmds_opts[$service$cmd]//\:/\\\:} && ret=0
+  fi
+
+fi
+
+return ret
diff --git a/Completion/Unix/Command/_paste b/Completion/Unix/Command/_paste
new file mode 100644
index 000000000..6efe8eacb
--- /dev/null
+++ b/Completion/Unix/Command/_paste
@@ -0,0 +1,19 @@
+#compdef paste
+
+local -a args
+local dopt='[specify delimiter list]:delimiter list [tab]'
+local sopt='[paste one file at a time instead of in parallel]'
+
+if _pick_variant gnu=GNU unix --version; then
+  args=(
+    '(-z --zero-terminated)'{-z,--zero-terminated}'[use NUL as line delimiter instead of newline]'
+    "(-d)--delimiters=$dopt"
+    "(-s)--serial$sopt"
+    '(- *)--help[display help information]'
+    '(- *)--version[display version information]'
+  )
+else
+  args=( -A "-?*" )
+fi
+
+_arguments -s $args "(--delimiters)-d+$dopt" "(--serial)-s$sopt" '*:file:_files'
diff --git a/Completion/Unix/Command/_patch b/Completion/Unix/Command/_patch
index c0df00c00..c2725d46b 100644
--- a/Completion/Unix/Command/_patch
+++ b/Completion/Unix/Command/_patch
@@ -9,8 +9,7 @@ if (( ! $+_patch_args )); then
   [[ "$help" = *--[^h]* || "$help" = *\[-c\|-e\|-n\]* ]] && arg=+
 
   optionmap=(
-    '*\[-p\[strip-count\]\]*' '(--strip)-p-[number of path prefix components to strip]:number of path prefix components to strip:'"{$comp_p}"
-    '*( -p NUM[, ]|\[-p num\])*' '(--strip)-p+[number of path prefix components to strip]:number of path prefix components to strip:'"{$comp_p}"
+    '*( -p NUM[, ]|\[-p (num|strip-count)\])*' '(--strip)-p+[number of path prefix components to strip]:number of path prefix components to strip:'"{$comp_p}"
     '*\[--strip\[=strip-count\]\]*' '(-p)--strip=-[number of path prefix components to strip]:number of path prefix components to strip:'"{$comp_p}"
     '* --strip=NUM[, ]*' '(-p)--strip=[number of path prefix components to strip]:number of path prefix components to strip:'"{$comp_p}"
 
@@ -35,8 +34,8 @@ if (( ! $+_patch_args )); then
     '*(\[-[a-zA-Z]#R[a-zA-Z]#\]| -R[, ])*' '(--reverse)-R[reverse mode]'
     '*(\[--reverse\]| --reverse[, ])*' '(-R)--reverse[reverse mode]'
 
-    '*(\[-i patchfile\]| -i PATCHFILE[, ])*' '(--input)-i+[patch file]:patch file:_files'
-    '* --input=PATCHFILE[, ]*' '(-i)--input=[patch file]:patch file:_files'
+    '*(\[-i patchfile\]| -i PATCHFILE[, ])*' '*-i+[specify input patch file]:patch file:_files'
+    '* --input=PATCHFILE[, ]*' '*--input=[specify input patch file]:patch file:_files'
 
     '*(\[-o out-file\]| -o FILE[, ]|\[-o outfile\])*' "(--output)-o${arg}[output file]:output file:_files"
     '*(\[--output=out-file\]| --output=FILE[, ])*' '(-o)--output=[output file]:output file:_files'
@@ -46,6 +45,8 @@ if (( ! $+_patch_args )); then
     '*(\[-D symbol\]| -D NAME[, ]|\[-D define\])*' '(--ifdef)-D+[cpp symbol]:symbol:'
     '*(\[--ifdef=symbol\]| --ifdef=NAME[, ])*' '(-D)--ifdef=[cpp symbol]:symbol:'
 
+    '* --merge*' '(--reject-file --reject-format -r)--merge[merge using conflict markers instead of creating reject files]'
+
     '*(\[-[a-zA-Z]#E[a-zA-Z]#\]| -E[, ])*' '(--remove-empty-files)-E[remove empty files]'
     '*(\[--remove-empty-files\]| --remove-empty-files[, ])*' '(-E)--remove-empty-files[remove empty files]'
 
@@ -62,6 +63,7 @@ if (( ! $+_patch_args )); then
     '* --no-backup-if-mismatch[, ]*' '(--backup-if-mismatch)--no-backup-if-mismatch[back up only if otherwise requested]'
 
     '*(\[-V {numbered,existing,simple}\]| -V STYLE[, ])*' "(--version-control)-V${arg}[backup method]:backup method:(numbered existing simple)"
+    '*\[-V t \| nil \| never\]*' '(-B)-V+[specify backup method]:backup method:((t\:numbered nil\:existing never\:simple))'
     '*(\[--version-control={numbered,existing,simple}\]| --version-control=STYLE[, ])*' '(-V)--version-control=[backup method]:backup method:(numbered existing simple)'
 
     '*(\[-B backup-prefix\]| -B PREFIX[, ])*' "(--prefix)-B${arg}[specify backup prefix]:backup prefix:"
@@ -69,6 +71,7 @@ if (( ! $+_patch_args )); then
     '* -Y PREFIX[, ]*' '(--basename-prefix)-Y+[specify backup basename prefix]:backup basename prefix:'
     '* --basename-prefix=PREFIX[, ]*' '(-Y)--basename-prefix=[specify backup basename prefix]:backup basename prefix:'
     '*\[-b backup-ext\]*'                              "(   -z --suffix)-b${arg}[specify backup suffix]:backup suffix:(.bak)"
+    '*\[-z backup-ext\]*' '(--suffix)-z+[specify backup suffix]:backup suffix [.orig]:(.bak)'
     '* -z SUFFIX[, ]*'                                 '(-b    --suffix)-z+[specify backup suffix]:backup suffix:(.bak)'
     '*(\[--suffix=backup-ext\]| --suffix=SUFFIX[, ])*' '(-b -z         )--suffix=[specify backup suffix]:backup suffix:(.bak)'
 
@@ -90,24 +93,28 @@ if (( ! $+_patch_args )); then
     '*(\[--silent\]|--silent[, ])*'        '(-s --quiet         )--silent[silent mode]'
 
     '* --verbose[, ]*' '--verbose[verbose mode]'
-    '* --dry-run[, ]*' '--dry-run[don'\''t actually change files]'
-    '* --posix[, ]*' '--posix[POSIX mode]'
+
+    '*(\[-[a-zA-Z]#C[a-zA-Z]#\])*' '(--check)-C[only check that the patch would apply cleanly]'
+    '*\[--check\]*' '(-C)--check[only check that the patch would apply cleanly]'
+    '* --dry-run[, ]*' '--dry-run[only check that the patch would apply cleanly]'
+
+    '*--posix*' '--posix[POSIX mode]'
 
     '*(\[-d directory\]| -d DIR[, ]|\[-d dir\])*' '(--directory)-d+[change the working directory]:chdir to:_files -/'
     '*(\[--directory=directory\]| --directory=DIR[, ])*' '(-d)--directory=[change the working directory]:chdir to:_files -/'
 
+    '* --reject-format=*' '--reject-format=[specify format of rejects]:format:(context unified)'
+    '* --read-only=*' '--read-only=[specify how to handle read-only input files]:behaviour [warn]: (ignore warn fail)'
+
     '* --binary[, ]*' '--binary[binary mode]'
 
-    '*(\[-[a-zA-Z]#v[a-zA-Z]#\]| -v[, ])*' '(--version)-v[print version]'
-    '*(\[--version\]| --version[, ])*' '(-v)--version[print version]'
-    '* --help[, ]*' '--help[print help message]'
+    '*(\[-[a-zA-Z]#v[a-zA-Z]#\]| -v[, ])*' '(-)-v[print version]'
+    '*(\[--version\]| --version[, ])*' '(-)--version[print version]'
+    '* --help[, ]*' '(-)--help[print help message]'
 
     '*\[-[a-zA-Z]#S[a-zA-Z]#\]*' '*-S[ignore this patch]'
     '*\[--skip\]*' '*--skip[ignore this patch]'
 
-    '*(\[-[a-zA-Z]#C[a-zA-Z]#\])*' '(--check)-C[check only]'
-    '*\[--check\]*' '(-C)--check[check only]'
-
     '*\[--index-first\]*' '--index-first[take Index: line precedence]'
 
     '* --use-index-line *' '--use-index-line[deal with Index: line]'
diff --git a/Completion/Unix/Command/_patchutils b/Completion/Unix/Command/_patchutils
index 323c0a730..50ea3e4c4 100644
--- a/Completion/Unix/Command/_patchutils
+++ b/Completion/Unix/Command/_patchutils
@@ -43,6 +43,7 @@ case $service in
       '(-F --files)'{-F+,--files=}'[only list files within specified range]:range'
       '--strip=[remove specified number of pathname components before displaying]:components'
       '--addprefix=[insert specified path prefix before displaying path names]:prefix:_directories'
+      '*:diff file:_files'
     )
   ;|
   grepdiff|lsdiff)
@@ -58,7 +59,6 @@ case $service in
       \*{-v,--verbose}'[verbose operation]' 
       '(-H --with-filename -h --no-filename)'{-H,--with-filename}'[print the name of the patch file containing each patch]'
       '!--filter' '!--grep'
-      '*:diff file:_files'
     )
   ;;
   grepdiff|filterdiff)
@@ -71,7 +71,7 @@ case $service in
       '--as-numbered-lines=[display lines of selected hunks]:line numbers:(before after)'
       "--remove-timestamps[don't include timestamps in output]"
     )
-  ;;
+  ;|
   filterdiff)
     args+=(
       '(-X --exclude-from-file)'{-X+,--exclude-from-file=}'[exclude files matching any pattern listed in specified file]:file:_files'
diff --git a/Completion/Unix/Command/_pbm b/Completion/Unix/Command/_pbm
index 747aed3ce..62004f79e 100644
--- a/Completion/Unix/Command/_pbm
+++ b/Completion/Unix/Command/_pbm
@@ -698,7 +698,7 @@ ppmtoacad)
 ppmtobmp)
   _arguments \
     '(-windows)-os2' '(-os2)-windows' \
-    '-bpp[secify bits per pixel for BMP file]:bits per pixel:(1 4 8 24)' \
+    '-bpp[specify bits per pixel for BMP file]:bits per pixel:(1 4 8 24)' \
     ':file:_pbm'
   ;;
 
diff --git a/Completion/Unix/Command/_perforce b/Completion/Unix/Command/_perforce
index db91e11af..3cd99d519 100644
--- a/Completion/Unix/Command/_perforce
+++ b/Completion/Unix/Command/_perforce
@@ -3,7 +3,7 @@
 # Maintainer: Peter Stephenson <pws@csr.com>.
 
 # Increasingly loosely based on _cvs version 1.17.
-# Completions currently based on Perforce release 2010.2.
+# Completions currently based on Perforce release 2016.1.
 
 # Styles, tags and contexts
 # =========================
@@ -460,7 +460,7 @@ _perforce_call_p4() {
   # This is for our own use for parsing, and we need English output,
   # so...
   local +x P4LANGUAGE
-  _call_program $cp_tag p4 "${_perforce_global_options[@]}" "$@"
+  _call_program $cp_tag command p4 "${_perforce_global_options[@]}" "$@"
 }
 
 
@@ -1656,11 +1656,12 @@ _perforce_variables() {
 (( $+functions[_perforce_cmd_add] )) ||
 _perforce_cmd_add() {
   _arguments -s : \
-    '-c+[select by change]:change:_perforce_changes -tc' \
-    '-d[reopen removed file for add (downgrade)]' \
+    '-c[add files to change]:change:_perforce_changes -tc' \
+    '-d[reopen files for add]' \
     '-f[allow filenames with wild cards]' \
-    '-n[display operation without doing it]' \
-    '-t+[set file type]:file type:_perforce_filetypes' \
+    '-I[do not perform ignore checking]' \
+    '-n[preview add]' \
+    '-t[set file type]:file type:_perforce_filetypes' \
     '*:file:_perforce_files -tu'
 }
 
@@ -1682,15 +1683,15 @@ _perforce_cmd_admin() {
       shift words
       (( CURRENT-- ))
       _arguments -s : \
-	'-z[gzip journal file]' \
-	'1::journal file prefix: '
+        '-z[gzip journal file]' \
+        '1::journal file prefix: '
       ;;
 
       (dbstat)
       shift words
       (( CURRENT -- ))
       _arguments -s : \
-	'-s[show sizes]'
+        '-s[show sizes]'
     esac
   fi
 }
@@ -1701,11 +1702,17 @@ _perforce_cmd_annotate() {
   # New in release 2002.2.
   # -c was new in about 2003.2.
   _arguments -s : \
-    '-a[all, show both added and deleted lines]' \
-    '-c[output change numbers instead of revisions]' \
-    '-i[follow branches (integration records)]' \
-    '-I[follow integrations to get change numbers]' \
-    '-q[quiet, suppress one-line file header]' \
+    '-a[include deleted files and lines]' \
+    '-c[output change numbers]' \
+    '-d-[select whitespace option]:whitespace option:((
+b\:ignore\ whitespace\ changes
+w\:ignore\ whitespace
+l\:ignore\ line\ endings))' \
+    '-i[follow branches]' \
+    '-I[follow all integrations]' \
+    '-q[suppress one-line header]' \
+    '-t[display binary files]' \
+    '-u[output user and date]' \
     '*::file:_perforce_files -tR'
 }
 
@@ -1718,11 +1725,12 @@ _perforce_cmd_attribute() {
   # If -f is present, search unopened files, else don't
   [[ ${words[(I)-f]} -eq 0 ]] && limit=" -to"
   _arguments -s : \
-    '-e[value is in hex]' \
-    '-f[set the attribute on a submitted file]' \
-    '-n[set name of attribute]:attribute: ' \
-    '-p[propagate attribute when opened for edit etc.]' \
-    '-v[set value of attribute]:value: ' \
+    '-n[attribute name]:name: ' \
+    '-v[attribute value]:value: ' \
+    '-e[use hex value]' \
+    '-f[set attribute on submitted file]' \
+    '-p[propagate attribute when opened]' \
+    '(-v)-i[read attribute from standard input]' \
     "*::file:_perforce_files$limit"
 }
 
@@ -1730,10 +1738,12 @@ _perforce_cmd_attribute() {
 (( $+functions[_perforce_cmd_branch] )) ||
 _perforce_cmd_branch() {
   _arguments -s : \
-    '(-o)-f[force operation by superuser]' \
-    '(-o -i)-d[delete branch]' \
-    '(-d -i -f)-o[write specification to standard output]' \
-    '(-d -o)-i[read specification from standard input]' \
+    '(-o -S -P)-f[force operation]' \
+    '(-o -i -S -P)-d[delete branch]' \
+    '(-d -i -f)-o[write branch spec to standard output]' \
+    '(-d -o -S -P)-i[read branch spec from standard input]' \
+    '(-f -d -i)-S[expose internally generated mapping]:stream: ' \
+    '(-f -d -i)-P[treat stream as a child of parent stream]:parent stream: ' \
     '(-i)*::branch name:_perforce_branches'
 }
 
@@ -1741,9 +1751,11 @@ _perforce_cmd_branch() {
 (( $+functions[_perforce_cmd_branches] )) ||
 _perforce_cmd_branches() {
   _arguments -s : \
-    '-e[limit by wildcard]:wildcard on branches: ' \
-    '-u+[select by user]:user:_perforce_users' \
-    '-m+[set maximum to show]:max branches: '
+    '(-E)-e[list branches that match pattern]:pattern: ' \
+    '(-e)-E[list branches that match case-insensitive pattern]:case-insensitive pattern: ' \
+    '-u[list branches owned by user]:user:_perforce_users' \
+    '-m[limit output to max branches]:max branches: ' \
+    '-t[display time and date]'
 }
 
 
@@ -1761,14 +1773,18 @@ _perforce_cmd_change() {
     fi
   fi
   _arguments -s : \
-    '(-o)-f[allow force by superuser]' \
-    '-s[joblist includes the fix status]' \
-    '(-o -i)-d[discard newly created pending change]' \
-    '(-d -i -f)-o[output specification to standard output]' \
-    '(-d -o)-i[read specification from standard input]' \
-    '(-d -o)-u[force change of jobs or description by owner]' \
-    "(-i)1::change:_perforce_changes$ctype" \
-    '-t[specify visibility type]:visibility type:(public restricted)'
+    '-f[force update of change]' \
+    '-s[include fix status in job list]' \
+    '(-u -I -o -i -t -U)-d[delete change]' \
+    '(-u -d -o -i -t -U --serverid)-o[write change spec to the standard output]' \
+    '(-O -I -d -o -i -t -U --serverid)-i[read change spec from the standard input]' \
+    '(-s -d -o -i --serverid)-t[set type of change]:type:(public restricted)' \
+    '-U[set user of empty change]:user:_perforce_users' \
+    '-O[change is original number before submit]' \
+    '-I[change is number of Identity field]' \
+    '-u[force update of submitted change]' \
+    '(-s -u -O -I -o -i -t -U)--serverid[specify server]:server ID: ' \
+    "(-i)1::change:_perforce_changes$ctype"
 }
 
 
@@ -1782,13 +1798,15 @@ _perforce_cmd_changelist() {
 _perforce_cmd_changes() {
   _arguments -s : \
     '-i[include integrated changes]' \
-    '-t[output time as well as date]' \
-    '-l[long output, full change text]' \
-    '-L[long output, truncated change text]' \
-    '-c+[select by client]:client:_perforce_clients' \
-    '-m+[most recent N changes]:max changes: ' \
-    '-s+[select by status]:status:(pending shelved submitted)' \
-    '-u+[select by user]:user:_perforce_users' \
+    '-t[display time and date]' \
+    '-l[display full change text]' \
+    '-L[display truncated change text]' \
+    '-f[view restricted changes]' \
+    '-c[display changes submitted by client]:client:_perforce_clients' \
+    '-e[display changes above this change]:change:_perforce_changes' \
+    '-m[limit to max changes]:max changes: ' \
+    '-s[limit output to changes with status]:status:(pending shelved submitted)' \
+    '-u[display changes owned by user]:user:_perforce_users' \
     '*::file:_perforce_files -tR'
 }
 
@@ -1799,14 +1817,33 @@ _perforce_cmd_changelists() {
 }
 
 
+(( $+functions[_perforce_cmd_clean] )) ||
+_perforce_cmd_clean() {
+  _arguments -s : \
+    '-e[clean modified files]' \
+    '-a[clean added files]' \
+    '-d[clean deleted files]' \
+    '-I[do not perform ignore checking]' \
+    '-l[output relative paths]' \
+    '-n[preview clean]' \
+    '*:file:_perforce_files -tu'
+}
+
+
+
 (( $+functions[_perforce_cmd_client] )) ||
 _perforce_cmd_client() {
   _arguments -s : \
-    '(-o)-f[force modification by superuser]' \
-    '-t[use template]:template client:_perforce_clients' \
-    '(-o -i -t)-d[delete client]' \
-    '(-d -i -f)-o[print to standard output]' \
-    '(-d -o -t)-i[read from standard input]' \
+    '-f[force update of client]' \
+    '-Fs[force delete with shelved changes]' \
+    '(-t -o -S -c -s -i)-d[delete client]' \
+    '(-f -d -Fs -s -i --serverid)-o[write client spec to standard output]' \
+    '(-t -d -Fs -i --serverid)-S[create new client dedicated to stream]:stream: ' \
+    '(-d -Fs -o -c -i --serverid)-s[switch client view without opening editor]' \
+    '(-t -d -Fs -o -S -c -s --serverid)-i[read client spec from standard input]' \
+    '-t[use client as template]:client:_perforce_clients' \
+    '(-f -t -d -Fs -s -i --serverid)-c[yield client spec for stream at moment change was recorded]:change:_perforce_changes -ts' \
+    '--serverid[specify server]:server ID: ' \
     '1::file:_perforce_clients'
 }
 
@@ -1814,34 +1851,59 @@ _perforce_cmd_client() {
 (( $+functions[_perforce_cmd_clients] )) ||
 _perforce_cmd_clients() {
   _arguments -s : \
-    '-e[limit by wildcard]:wildcard on clients: ' \
-    '-u+[select by user]:user:_perforce_users' \
-    '-m+[set maximum to show]:max clients: '
+    '-t[display time and date]' \
+    '-u[list clients owned by user]:user:_perforce_users' \
+    '(-E)-e[list clients that match pattern]:pattern: ' \
+    '(-e)-E[list clients that match case-insensitive pattern]:case-insensitive pattern: ' \
+    '-m[limit to max clients]:max clients: ' \
+    '-S[limit output to clients dedicated to stream]:stream: ' \
+    '-U[list unloaded clients]' \
+    '(-s)-a[display all clients]' \
+    '(-a)-s[display clients dedicated to server]:server ID: '
 }
 
 
 (( $+functions[_perforce_cmd_copy] )) ||
 _perforce_cmd_copy() {
-  local range
-  # If -s is present, the first normal argument can't have revRange.
-  [[ ${words[(I)-s]} -eq 0 ]] && range=" -tR"
+  local -a fileargs
+  if [[ ${words[(I)-b*]} -ne 0 ]]; then
+    if [[ ${words[(I)-*s*]} -eq 0 ]]; then
+      # with -b and no -s, all files are to-files (but -s may come later)
+      fileargs=('*::to file:_perforce_files -tR')
+    else
+      # with -b and -s we have one from-file and any number of to-files
+      fileargs=('*::to file:_perforce_files')
+    fi
+  elif [[ ${words[(I)-(S|P)]} -ne 0 ]]; then
+      fileargs=('*::file:_perforce_files -tR')
+  else
+    # with no -b we have one from-file and one to-file
+    fileargs=('1::from file:_perforce_files -tR'
+              '2::to file:_perforce_files')
+  fi
   _arguments -s : \
-    '-b[select branch]:branch:_perforce_branches' \
-    '-c[select change for copy]:change:_perforce_changes -tc' \
-    '-n[no action, dummy run]' \
-    '-r[reverse direction of copy with branch]' \
-    '-s[select source with -b]:source file:_perforce_files -tR' \
-    '-v[leave newly copied files uncopied till sync]' \
-    "1:file:_perforce_files$range" \
-    '*::file:_perforce_files'
+    '-b[use branch view'\''s source and target]:branch:_perforce_branches' \
+    '-s[select source file, use branch view as target]:source file:_perforce_files -tR' \
+    '-r[reverse direction of copy]' \
+    '-c[open files in change]:change:_perforce_changes -tc' \
+    '-f[force creation of extra revisions]' \
+    '-n[preview copy]' \
+    '-m[limit copy to max files]:max files: ' \
+    '-q[suppress normal output messages]' \
+    '-v[do not modify client files]' \
+    '(-b -s)-S[copy from stream to its parent]:stream: ' \
+    '(-b -s)-P[generate branch view using a parent stream]:parent stream: ' \
+    '(-b -s)-F[copy against stream'\''s expected flow]' \
+    $fileargs
 }
 
 (( $+functions[_perforce_cmd_counter] )) ||
 _perforce_cmd_counter() {
   _arguments -s : \
     '-d[delete counter]' \
-    '-f[force setting of internal counter]' \
-    '-i[increment by one atomically]' \
+    '-f[set or delete internal counter]' \
+    '-i[increment counter by 1]' \
+    '-m[allow multiple operations]' \
     '1:counter:_perforce_counters' \
     '(-d -i)2::numeric value:_perforce_counter_values'
 }
@@ -1849,8 +1911,9 @@ _perforce_cmd_counter() {
 
 (( $+functions[_perforce_cmd_counters] )) ||
 _perforce_cmd_counters() {
-  # No arguments
-  _arguments -s :
+  _arguments -s : \
+    '-e[list counters that match pattern]:pattern: ' \
+    '-m[limit to max counters]:max counters: '
 }
 
 
@@ -1884,8 +1947,10 @@ _perforce_cmd_dbstat() {
 (( $+functions[_perforce_cmd_delete] )) ||
 _perforce_cmd_delete() {
   _arguments -s : \
-    '-c[select change for deletion]:change:_perforce_changes -tc' \
-    '-n[show deletions without doing them]' \
+    '-c[delete files for change]:change:_perforce_changes -tc' \
+    '-n[preview delete]' \
+    '-k[perform delete on server]' \
+    '-v[delete unsynced files]' \
     '*::file:_perforce_files'
 }
 
@@ -1893,9 +1958,11 @@ _perforce_cmd_delete() {
 (( $+functions[_perforce_cmd_depot] )) ||
 _perforce_cmd_depot() {
   _arguments -s : \
-    '-d[delete depot]' \
-    '-o[print to stdout]' \
-    '-i[read name from stdin]' \
+    '(-t -o -i)-d[delete depot]' \
+    '(-t -o -i)-f[force delete]' \
+    '(-d -o -i)-t[insert value into type]:type: ' \
+    '(-t -d -i -f)-o[write depot spec to standard output]' \
+    '(-t -d -o -f)-i[read depot spec from standard input]' \
     '(-i)*::depot name:_perforce_depots'
 }
 
@@ -1910,9 +1977,20 @@ _perforce_cmd_depots() {
 (( $+functions[_perforce_cmd_describe] )) ||
 _perforce_cmd_describe() {
   _arguments -s : \
-    '-d-[select diff option]:diff option:((b\:ignore\ blanks c\:context n\:RCS s\:summary u\:unified w\:ignore\ all\ whitespace))' \
-    '-s[short form]' \
-    '-S[show shelved changes]' \
+    '-d-[diff options]:diff options:((
+n\:RCS
+c\:context
+s\:summary
+u\:unified
+b\:ignore\ whitespace\ changes
+w\:ignore\ whitespace
+l\:ignore\ line\ endings))' \
+    '-s[omit diffs]' \
+    '-S[list shelved files]' \
+    '-f[force display of restricted change]' \
+    '-O[change is original number before submit]' \
+    '-I[change is number of Identity field]' \
+    '-m[limit output to max files]:max files: ' \
     '*::change:_perforce_changes'
 }
 
@@ -1920,17 +1998,27 @@ _perforce_cmd_describe() {
 (( $+functions[_perforce_cmd_diff] )) ||
 _perforce_cmd_diff() {
   local limit
-  [[ ${words[(I)-(f|sd|se)]} -eq 0 ]] && limit=" -to"
-  _arguments -s : \
-    '-d-[select diff option]:diff option:((b\:ignore\ blanks c\:context l\:ignore\ line\ endings n\:RCS s\:summary u\:unified w\:ignore\ all\ whitespace))' \
+  [[ ${words[(I)-(f|sd|se|sl)]} -eq 0 ]] && limit=" -to"
+  _arguments -s : \
+    '-d-[diff options]:diff options:((
+n\:RCS
+c\:context
+s\:summary
+u\:unified
+b\:ignore\ whitespace\ changes
+w\:ignore\ whitespace
+l\:ignore\ line\ endings))' \
     '-f[diff every file]' \
-    '-m+[set maximum files to show]:max files: ' \
-    '(-sd -se -sl -sr)-sa[opened files, different or missing]' \
-    '(-sa -se -sl -sr)-sd[unopened files, missing]' \
-    '(-sa -sd -sl -sr)-se[unopened files, different]' \
-    '(-sa -sd -se -sr)-sl[all unopened files with status]' \
-    '(-sa -sd -se -sl)-sr[opened files, same as depot]' \
-    '-t[include non-text files]' \
+    '-m[limit output to max files]:max files: ' \
+    '-Od[limit output to files that differ]' \
+    '-s-[filter options]:filter options:((
+a\:list\ opened\ files\ that\ differ\ from\ depot
+b\:list\ modified\ integrated\ files
+d\:list\ unopened\ missing\ files
+e\:list\ unopened\ files\ that\ differ\ from\ depot
+l\:list\ all\ unopened\ files\ with\ status
+r\:list\ opened\ files\ that\ do\ not\ differ\ from\ depot))' \
+    '-t[diff binary files]' \
     "*::file:_perforce_files$limit"
 }
 
@@ -1938,22 +2026,33 @@ _perforce_cmd_diff() {
 (( $+functions[_perforce_cmd_diff2] )) ||
 _perforce_cmd_diff2() {
   _arguments -s : \
-    '-b[specify branch view]:branch name:_perforce_branches' \
-    '-d-[select diff option]:diff option:((b\:ignore\ blanks c\:context n\:RCS s\:summary u\:unified w\:ignore\ all\ whitespace))' \
-    '-q[only list different files]' \
-    '-t[include non-text files]' \
-    '-u[use patch-friendly output]' \
-    '1::first file:_perforce_files' \
-    '2::second file:_perforce_files'
+    '-b[use branch view'\''s source and target]:branch:_perforce_branches' \
+    '-d-[diff options]:diff options:((
+n\:RCS
+c\:context
+s\:summary
+u\:unified
+b\:ignore\ whitespace\ changes
+w\:ignore\ whitespace
+l\:ignore\ line\ endings))' \
+    '-Od[limit output to files that differ]' \
+    '-q[omit identical files]' \
+    '-t[diff binary files]' \
+    '-u[use GNU diff -u format]' \
+    '(-b)-S[use generated branch view from stream]:stream: ' \
+    '(-b)-P[use generated branch view from parent stream]:parent stream: ' \
+    '1::from file:_perforce_files' \
+    '2::to file:_perforce_files'
 }
 
 
 (( $+functions[_perforce_cmd_dirs] )) ||
 _perforce_cmd_dirs() {
   _arguments -s : \
-    '-C[only dirs on current client]' \
-    '-D[include dirs with deleted files]' \
-    '-H[only dirs on the `have'\'' list]' \
+    '-C[list only directories in current client]' \
+    '-D[include directories with only deleted files]' \
+    '-H[list directories with synced files]' \
+    '-S[limit output to depot directories mapped to stream'\''s client]:stream: ' \
     '*::directory:_perforce_files -td'
 }
 
@@ -1961,10 +2060,10 @@ _perforce_cmd_dirs() {
 (( $+functions[_perforce_cmd_edit] )) ||
 _perforce_cmd_edit() {
   _arguments -s : \
-    '-c[set change for edit]:change:_perforce_changes -tc' \
-    '-k[no resync from server]' \
-    '-n[show files to edit without opening them]' \
-    '-t[set filetype]:filetype:_perforce_filetypes' \
+    '-c[edit files for change]:change:_perforce_changes -tc' \
+    '-t[specify filetype]:filetype:_perforce_filetypes' \
+    '-n[preview edit]' \
+    '-k[edit files on server]' \
     '*::file:_perforce_files'
 }
 
@@ -1986,14 +2085,15 @@ _perforce_cmd_export() {
 (( $+functions[_perforce_cmd_filelog] )) ||
 _perforce_cmd_filelog() {
   _arguments -s : \
-    '-c[select by changelist]:change:_perforce_changes -ts' \
-    '-h[follow branc/copy from records]' \
-    '-i[follow branches]' \
-    '-l[long output, full change text]' \
-    '-L[long output, truncated change text]' \
-    '-m[set maximum number of revisions to show]:max revisions: ' \
-    '-s[short output]' \
-    '-t[include time with date]' \
+    '-c[display files at change]:change:_perforce_changes -ts' \
+    '-i[include inherited file history]' \
+    '-h[display file content history]' \
+    '-t[display time and date]' \
+    '-l[display full change text]' \
+    '-L[display truncated change text]' \
+    '-m[display max number of revisions]:max revisions: ' \
+    '-p[do not follow content of promoted task streams]' \
+    '-s[display shortened form]' \
     '*::file:_perforce_files'
 }
 
@@ -2001,7 +2101,11 @@ _perforce_cmd_filelog() {
 (( $+functions[_perforce_cmd_files] )) ||
 _perforce_cmd_files() {
   _arguments -s : \
-    '-a[display all revisions in given range]' \
+    '-a[display all revisions in range]' \
+    '-A[display files in archive depots]' \
+    '-e[do not display deleted, purged or archived files]' \
+    '-m[limit output to max files]:max files: ' \
+    '-U[display files in unload depot]' \
     '*::file:_perforce_files -tR'
 }
 
@@ -2024,10 +2128,9 @@ _perforce_cmd_fix() {
   fi
 
   _arguments -s : \
-    '-d[delete the fix]' \
-    '-s[set job status]:status:_perforce_statuses' \
-    '1::-c required:(-c)' \
-    '2::change:_perforce_changes' \
+    '-d[delete fix]' \
+    '-s[set status]:status:_perforce_statuses' \
+    '-c[display jobs fixed by change]:change:_perforce_changes -ts' \
     "*::job:_perforce_jobs$job"
 }
 
@@ -2035,10 +2138,10 @@ _perforce_cmd_fix() {
 (( $+functions[_perforce_cmd_fixes] )) ||
 _perforce_cmd_fixes() {
   _arguments -s : \
+    '-j[list fixes for job]:job:_perforce_jobs' \
+    '-c[list fixes for change]:change:_perforce_changes -tR' \
     '-i[include integrated changes]' \
-    '-j[select by job]:job:_perforce_jobs' \
-    '-c[select by change]:change:_perforce_changes' \
-    '-m[set max fixes to show]:max fixes: ' \
+    '-m[limit output to max fixes]:max fixes: ' \
     '*::fixed file:_perforce_files -tR'
 }
 
@@ -2047,8 +2150,12 @@ _perforce_cmd_fixes() {
 _perforce_cmd_flush() {
   _arguments -s : \
     '-f[force resynchronisation]' \
-    '-k[bypass client file update]' \
-    '-n[show operations but don'\''t perform them]' \
+    '-L[use full depot syntax, including revision number]' \
+    '-n[preview flush]' \
+    '-N[preview flush with summary]' \
+    '-q[suppress normal output messages]' \
+    '-r[reopen moved files in new location]' \
+    '-m[limit sync to max files]:max files: ' \
     '*::file:_perforce_files -tR'
 }
 
@@ -2058,26 +2165,46 @@ _perforce_cmd_fstat() {
   local Oattr Aattr
   if [[ ${_perforce_cmd_list[(r)attribute:*]} != '' ]]; then
     # Unsupported feature, try not to show if not present
-    Oattr=' a\:show\ attributes d\:attributes\ digest e\:attributes\ in\ hex'
+    Oattr=' a\:output\ attributes d\:output\ digest e\:output\ values\ in\ hex'
     Aattr='-A[restrict attributes by pattern]:attribute pattern: '
   fi
   _arguments -s : \
-    '-c+[affected since change]:change:_perforce_changes -ts' \
-    '-e+[affected by change]:change:_perforce_changes -ts' \
-    '-C[select mapped files (-Rc)]' \
-    '-F[pick filter for files]:filter:_perforce_fstat_fields -tv' \
-    '-H[select synced files (-Rh)]' \
-    '-W[select opened files (-Ro)]' \
-    '-l[include fileSize, possibly slow (-Ol)]' \
-    '-m[set max files to show]:max files: ' \
-    "-O-[select output type]:output type:((f\:all\ revisions l\:fileSize p\:client\ path\ format r\:pending\ integrations s\:exclude\ local\ path$Oattr))" \
-    '-P[output clientFile in full Perforce syntax (deprecated: use -Op)]' \
-    '-r[show in reverse order]' \
-    '-R-[restrict selected files]:restriction:((c\:mapped\ in\ client h\:synced\ to\ client n\:not\ synced\ to\ head o\:opened r\:resolved s\:shelved u\:unresolved))' \
-    '-s[shorten, no client-related data (deprecated: use -Os)]' \
-    '-S-[changes sort order]:sort criterion:((t\:filetype d\:date r\:head\ revision h\:have\ revision s\:filesize))' \
-    '-T[select output fields]:output field:_perforce_fstat_fields' \
     $Aattr \
+    '-F[list only files satisfying filter]:filter:_perforce_fstat_fields -tv' \
+    '-L[use full depot syntax, including revision number]' \
+    '-T[return specified fields]:output field:_perforce_fstat_fields' \
+    '-m[limits output to max files]:max files: ' \
+    '-r[sort output in reverse order]' \
+    '-c[display files modified by or after change]:change:_perforce_changes -ts' \
+    '-e[list files modified by change]:change:_perforce_changes -ts' \
+    "-O-[output options]:output options:((
+f\:all\ revisions
+l\:fileSize\ and\ digest
+p\:local\ file\ path
+r\:pending\ integration
+s\:exclude\ local\ path
+$Oattr))" \
+    '-R-[restrict files]:file restrictions:((
+c\:mapped\ in\ client
+h\:synced\ to\ client
+n\:opened\ not\ at\ head\ revision
+o\:opened
+r\:resolved
+s\:shelved
+u\:unresolved))' \
+    '-S-[sort order]:sort by:((
+t\:filetype
+d\:date
+r\:head\ revision
+h\:have\ revision
+s\:filesize))' \
+    '-U[display info about unload files in unload depot]' \
+    '-C[limit output to mapped files (-Rc)]' \
+    '-H[limit output to synced files (-Rh)]' \
+    '-W[limit output to opened files (-Ro)]' \
+    '-l[output fileSize and digest (-Ol)]' \
+    '-P[output local file paths (-Op)]' \
+    '-s[exclude local file paths (-Os)]' \
     '*::file:_perforce_files'
 }
 
@@ -2085,22 +2212,21 @@ _perforce_cmd_fstat() {
 (( $+functions[_perforce_cmd_grep] )) ||
 _perforce_cmd_grep() {
   _arguments -s : \
+    '-e[search pattern]:pattern: ' \
     '-a[search all revisions]' \
     '-i[case insensitive match]' \
-    '-n[display matching line]' \
-    '-v[display file name]' \
-    '-F[interpret as fixed string]' \
-    '-G[interpret as regexp (default)]' \
-    '-L[list non-matching file/revisions]' \
-    '-l[list matching file/revisions]' \
-    '-s[suppresses errors on long lines]' \
-    '-t[treat all files as text]' \
-    '-A[]:trailing context lines: ' \
-    '-V[]:leading context lines: ' \
-    '-C[]:context lines: ' \
-    '1:-e required before pattern:(-e)' \
-    '2:pattern: ' \
-    '*::file:_perforce_files'
+    '-n[display matching line number]' \
+    '-v[display files with non-matching lines]' \
+    '-F[interpret pattern as fixed string]' \
+    '-G[interpret pattern as regexp]' \
+    '-L[display non-matching files]' \
+    '-l[display matching files]' \
+    '-s[suppress errors on long lines]' \
+    '-t[search binary files]' \
+    '-A[display N lines of trailing context]:lines: ' \
+    '-B[display N lines of leading context]:lines: ' \
+    '-C[display N lines of output context]:lines: ' \
+    '*::file:_perforce_files -tR'
 }
 
 
@@ -2108,9 +2234,10 @@ _perforce_cmd_grep() {
 _perforce_cmd_group() {
   _arguments -s : \
     '-d[delete group]' \
-    '-o[output to stdout]' \
-    '-i[read from stdin]' \
-    '(-o)-a[allow non-super owner to modify group]' \
+    '-o[write group spec to standard output]' \
+    '-i[read group spec from standard input]' \
+    '(-o -A)-a[allow owner to modify group]' \
+    '(-a -d)-A[allow admin user to add new group]' \
     '1::perforce group:_perforce_groups'
 }
 
@@ -2118,10 +2245,13 @@ _perforce_cmd_group() {
 (( $+functions[_perforce_cmd_groups] )) ||
 _perforce_cmd_groups() {
   _arguments -s : \
-    '-i[show indirect membership by subgroups]' \
-    '-m[set max groups to show]:max groups: ' \
-    '-v[show summary data]' \
-    '1::user or group name:_perforce_users_or_groups'
+    '-i[display indirect membership by subgroups]' \
+    '-m[limit output to max groups]:max groups: ' \
+    '-v[display group data]' \
+    '(-u -o)-g[display group with name]:group:_perforce_groups' \
+    '(-g -o)-u[display all groups for user]:user:_perforce_users' \
+    '(-g -u)-o[display all groups for owner]:owner:_perforce_users' \
+    '(-g -u -o)1::user or group name:_perforce_users_or_groups'
 }
 
 
@@ -2141,7 +2271,7 @@ _perforce_cmd_help() {
     _perforce_help_list=($_perforce_cmd_list)
     _perforce_call_p4 help help | while read -A hline; do
       if [[ $hline[1] = p4 && $hline[2] = help ]]; then
-	_perforce_help_list+=("$hline[3]:${hline[4,-1]}")
+        _perforce_help_list+=("$hline[3]:${hline[4,-1]}")
       fi
     done
     if [[ -z ${_perforce_help_list[(r)undoc:*]} ]]; then
@@ -2155,7 +2285,7 @@ _perforce_cmd_help() {
 (( $+functions[_perforce_cmd_info] )) ||
 _perforce_cmd_info() {
   _arguments -s : \
-    '-s[don'\''t check for unknown users or clients]'
+    '-s[short output]'
 }
 
 
@@ -2165,24 +2295,26 @@ _perforce_cmd_integrate() {
   # If -s is present, the first normal argument can't have revRange.
   [[ ${words[(I)-s]} -eq 0 ]] && range=" -tR"
   _arguments -s : \
-    '-b[select branch]:branch:_perforce_branches' \
-    '-c[select change for integration]:change:_perforce_changes -tc' \
-    '-f[force reintegration]' \
-    '-d[reintegrate deleted files]' \
-    '-D-[specify allowed deletions]:deletion type:((
-t\:rebranch\ on\ deleted\ file
-s\:delete\ modified\ target\ file
-i\:ignore\ readded\ source\ file
-))' \
-    '-h[integrate to revision had on client]' \
-    '-i[integrate if no common file base]' \
-    '-I[same as -i from 2004.2]' \
-    '-n[no action, dummy run]' \
-    '-o[display base file name for subsequent resolve]' \
-    '-r[reverse direction of integration with branch]' \
-    '-s[select source with -b]:source file:_perforce_files -tR' \
-    '-t[propagate type changes]' \
-    '-v[leave newly branched files uncopied till sync]' \
+    '-b[use branch view'\''s source and target]:branch:_perforce_branches' \
+    '(-r)-s[select source file, use branch view as target]:source file:_perforce_files -tR' \
+    '-f[force integration]' \
+    '-O-[output more information]:output options:((
+b\:show\ base\ revision\ for\ merge
+r\:show\ scheduled\ resolves))' \
+    '-R-[specify resolve schedule]:schedule:((
+b\:branch\ resolves
+d\:delete\ resolves
+s\:skip\ cherry-picked\ revisions\ already\ integrated))' \
+    '-Di[retain revisions of deleted files]' \
+    '-h[leave files at revision currently synced]' \
+    '-m[limit integration to max files]:max files: ' \
+    '-n[preview integration]' \
+    '-q[suppress normal output messages]' \
+    '-c[open in change]:change:_perforce_changes -tc' \
+    '-v[do not modify client files]' \
+    '-r[reverse direction of mapping]' \
+    '-S[use generated branch view from stream]:stream: ' \
+    '-P[use generated branch view from parent stream]:parent stream: ' \
     "1:file:_perforce_files$range" \
     '*::file:_perforce_files'
 }
@@ -2196,8 +2328,8 @@ _perforce_cmd_integ() {
 (( $+functions[_perforce_cmd_integrated] )) ||
 _perforce_cmd_integrated() {
   _arguments -s : \
-    '-r[reverse mapping in branch view with -b]' \
-    '-b[select files integrated via branch]:branch:_perforce_branches' \
+    '-r[reverse mapping in branch view]' \
+    '-b[list files integrated from branch view]:branch:_perforce_branches' \
     '*::file:_perforce_files -ti'
 }
 
@@ -2211,34 +2343,51 @@ _perforce_cmd_interchanges() {
   if [[ ${words[(I)-b*]} -ne 0 ]]; then
     if [[ ${words[(I)-*s*]} -eq 0 ]]; then
       # with -b and no -s, all files are to-files (but -s may come later)
-      fileargs=('-s[specify source file]'
-	        '*::to file:_perforce_files -tR')
+      fileargs=('*::to file:_perforce_files -tR')
     else
       # with -b and -s we have one from-file and any number of to-files
-      fileargs=('1::from file:_perforce_files -tR'
-	        '*::to file:_perforce_files')
+      fileargs=('*::to file:_perforce_files')
     fi
+  elif [[ ${words[(I)-(S|P)]} -ne 0 ]]; then
+      fileargs=('*::file:_perforce_files -tR')
   else
     # with no -b we have one from-file and one to-file
     fileargs=('1::from file:_perforce_files -tR'
               '2::to file:_perforce_files')
   fi
   _arguments -s : \
-    '-f[show individual files]' \
-    '-l[long changelist description]' \
-    '-b[select files integrated via branch]:branch:_perforce_branches' \
-    '-r[reverse branch mapping]' \
+    '-f[list files that require integration]' \
+    '-l[display full change text]' \
+    '-t[display time and date]' \
+    '(-S -P)-b[use branch view'\''s source and target]:branch:_perforce_branches' \
+    '(-S -P)-s[select source file, use branch view as target]:source file:_perforce_files -tR' \
+    '-u[limit files submitted by user]:user:_perforce_users' \
+    '-r[reverse mapping direction]' \
+    '-S[use generated branch view from stream]:stream: ' \
+    '-P[use generated branch view from parent stream]:parent stream: ' \
+    '-F[ignore stream'\''s expected flow]' \
     $fileargs
 }
 
 
+(( $+functions[_perforce_cmd_istat] )) ||
+_perforce_cmd_istat() {
+  _arguments -s : \
+    '-a[show status of integration in both directions]' \
+    '-c[assume cache is stale]' \
+    '-r[show status of integration from parent]' \
+    '-s[show cached state without refreshing stale data]' \
+    '1::stream: '
+}
+
+
 (( $+functions[_perforce_cmd_job] )) ||
 _perforce_cmd_job() {
   _arguments -s : \
     '(-d -o -i)-f[force setting of readonly fields]' \
     '(-f -o -i)-d[delete job]' \
-    '(-f -d -i)-o[print to stdout]' \
-    '(-d -o)-i[read from stdin]' \
+    '(-f -d -i)-o[write job spec to standard output]' \
+    '(-d -o)-i[read job spec from standard input]' \
     '(-i)1::job:_perforce_jobs'
 }
 
@@ -2246,12 +2395,12 @@ _perforce_cmd_job() {
 (( $+functions[_perforce_cmd_jobs] )) ||
 _perforce_cmd_jobs() {
   _arguments -s : \
-    '-e[select by jobview]:jobview:_perforce_jobviews' \
-    '-i[included integrated changes]' \
-    '-l[long output, full job descriptions]' \
-    '-r[reverse order of job names]' \
-    '-m[limit to most recent N jobs]:number of most recent jobs: ' \
-    '(-e -i -l -m)-R[rebuild jobs table on upgrade]' \
+    '-e[list jobs matching parameter]::_perforce_jobviews' \
+    '-i[include integrated changes]' \
+    '-l[display full job text]' \
+    '-m[limit output to max jobs]:max jobs: ' \
+    '-r[sort in reverse order]' \
+    '(-e -i -l -m)-R[rebuild jobs table]' \
     '*::file:_perforce_files -tR'
 }
 
@@ -2264,14 +2413,41 @@ _perforce_cmd_jobspec() {
 }
 
 
+(( $+functions[_perforce_cmd_key] )) ||
+_perforce_cmd_key() {
+  local -a keyargs
+  if [[ ${words[(I)-(d|i)]} -ne 0 ]]; then
+    keyargs=('1::name: ')
+  elif [[ ${words[(I)-m]} -ne 0 ]]; then
+    keyargs=('*::name value pairs: ')
+  else
+    keyargs=('1::name: ' '2::value: ')
+  fi
+  _arguments -s : \
+    '(-i -m)-d[delete key]' \
+    '(-d -m)-i[increment key value by 1]' \
+    '(-d -i)-m[allow mulitple operations]' \
+    $keyargs
+}
+
+
+(( $+functions[_perforce_cmd_keys] )) ||
+_perforce_cmd_keys() {
+  _arguments -s : \
+    '-e[list keys that match pattern]:pattern: ' \
+    '-m[limit output to max keys]:max keys: '
+}
+
+
 (( $+functions[_perforce_cmd_label] )) ||
 _perforce_cmd_label() {
   _arguments -s : \
     '-f[force operation]' \
-    '-t+[copy template]:template: ' \
+    '-t[copy view and options from label]:label:_perforce_labels' \
     '(-o -i -t)-d[delete label]' \
-    '(-d -f -i)-o[write to standard output]' \
-    '(-o -d -t)-i[read from standard input]' \
+    '(-d -f -i -g)-o[write label spec to standard output]' \
+    '(-o -d -t)-i[read label spec from standard input]' \
+    '-g[update global label]' \
     '*::label:_perforce_labels'
 }
 
@@ -2279,10 +2455,14 @@ _perforce_cmd_label() {
 (( $+functions[_perforce_cmd_labels] )) ||
 _perforce_cmd_labels() {
   _arguments -s : \
-    '-e[limit by wildcard]:label wildcard: ' \
-    '-m+[set maximum to show]:max labels: ' \
-    '-t[output time as well as date]' \
-    '-u+[select by user]:user:_perforce_users' \
+    '-t[display time and date]' \
+    '-u[list labels owned by user]:user:_perforce_users' \
+    '(-E)-e[list labels that match pattern]:pattern: ' \
+    '(-e)-E[list labels that match case-insensitive pattern]:case-insensitive pattern: ' \
+    '-m[limit output to max labels]:max labels: ' \
+    '(-s)-a[display all labels]' \
+    '(-a)-s[display labels from server]:server ID: ' \
+    '-U[list unloaded labels]' \
     '1::file or revisions which must contain label:_perforce_files -tR'
 }
 
@@ -2290,11 +2470,23 @@ _perforce_cmd_labels() {
 (( $+functions[_perforce_cmd_labelsync] )) ||
 _perforce_cmd_labelsync() {
   _arguments -s : \
+    '-l[specify label]:label:_perforce_labels' \
     '-a[add files to label]' \
     '-d[delete files from label]' \
-    '-n[no effect, dummy run]' \
-    '-l[specify label]:label:_perforce_labels' \
-    '-q[suppress informational messages]' \
+    '-n[preview labelsync]' \
+    '-q[suppress normal output messages]' \
+    '-g[update global label]' \
+    '*::file:_perforce_files -tR'
+}
+
+
+(( $+functions[_perforce_cmd_list] )) ||
+_perforce_cmd_list() {
+  _arguments -s : \
+    '-l[use temporary list name]:list name: ' \
+    '(-C)-d[delete list]' \
+    '-C[limit files to client]' \
+    '-M[forward list to master server]' \
     '*::file:_perforce_files -tR'
 }
 
@@ -2310,7 +2502,8 @@ _perforce_cmd_license() {
 (( $+functions[_perforce_cmd_lock] )) ||
 _perforce_cmd_lock() {
   _arguments -s : \
-    '-c[select by change]:change:_perforce_changes -tc' \
+    '-c[lock files for change]:change:_perforce_changes -tc' \
+    '-g[lock files globally]' \
     '*::file:_perforce_files -to'
 }
 
@@ -2324,8 +2517,8 @@ _perforce_cmd_lockstat() {
 (( $+functions[_perforce_cmd_logger] )) ||
 _perforce_cmd_logger() {
   _arguments -s : \
-    '-c[limit by counter no]:number: ' \
-    '-t[use counter instead of logger]:counter:_perforce_counters'
+    '-c[list events after sequence]:sequence: ' \
+    '-t[list events after counter]:counter:_perforce_counters'
 }
 
 
@@ -2333,9 +2526,11 @@ _perforce_cmd_logger() {
 (( $+functions[_perforce_cmd_login] )) ||
 _perforce_cmd_login() {
   _arguments -s : \
-    '-a[ticket valid on all machines]' \
+    '-a[issue ticket on all host machines]' \
+    '-h[issue ticket on host]:host: ' \
     '-p[display ticket, do not store]' \
-    '-s[show status of ticket]' \
+    '-r[forward login to server]:remote spec: ' \
+    '(-a -p -h)-s[display status of current ticket]' \
     '(-s)1::user:_perforce_users'
 }
 
@@ -2362,6 +2557,27 @@ _perforce_cmd_logtail() {
 }
 
 
+(( $+functions[_perforce_cmd_merge] )) ||
+_perforce_cmd_merge() {
+  local -a fileargs
+  if [[ ${words[(I)--from]} -ne 0 ]]; then
+    fileargs=('1:to file:_perforce_files -tR')
+  else
+    fileargs=('1:from file:_perforce_files -tR'
+              '2:to file:_perforce_files')
+  fi
+  _arguments -s : \
+    '-F[merge against stream'\''s expected flow]' \
+    '-Ob[show base revision for merge]' \
+    '-q[suppress normal output messages]' \
+    '--from[merge from stream other than the parent stream]:stream: ' \
+    '-m[limit merge to max files]:max files: ' \
+    '-n[preview merge]' \
+    '-c[open in change]:change:_perforce_changes -tc' \
+    $fileargs
+}
+
+
 (( $+functions[_perforce_cmd_monitor] )) ||
 _perforce_cmd_monitor() {
   if (( CURRENT > 2 )); then
@@ -2397,25 +2613,25 @@ _perforce_cmd_monitor() {
 (( $+functions[_perforce_cmd_move] )) ||
 _perforce_cmd_move() {
   _arguments -s : \
-    '-c[specify new change list]:change:_perforce_changes -tc' \
-    '-f[force move when already synced]' \
-    '-k[no resync from server]' \
+    '-c[reopen in change]:change:_perforce_changes -tc' \
+    '-f[force move]' \
     '-t[specify new file type]:filetype:_perforce_filetypes' \
-    '-n[show files to move without moving them]' \
-    '1::source file, wildcards allowed:_perforce_files -to' \
-    '2::destination file, wildcards match source:_perforce_files'
+    '-n[preview move]' \
+    '-k[perform move on server]' \
+    '1::from file:_perforce_files -to' \
+    '2::to file:_perforce_files -tu'
 }
 
 
 (( $+functions[_perforce_cmd_obliterate] )) ||
 _perforce_cmd_obliterate() {
     if [[ ${words[(I)-y]} -gt 0 ]]; then
-	_message \
+      _message \
 ": don't complete after -y; run obliterate without, then add the -y"
     else
-	_arguments -s : \
-	    '-y[actually perform the operation]' \
-	    '*::file:_perforce_files -tR'
+      _arguments -s : \
+        '-y[actually perform the operation]' \
+        '*::file:_perforce_files -tR'
     fi
 }
 
@@ -2427,11 +2643,14 @@ _perforce_cmd_opened() {
   # -tp, but currently Perforce doesn't allow that, so -tc is correct.
   # This is true even if -a is also given.
   _arguments -s : \
-    '-a[list for all clients]' \
-    '-c+[select by change]:change:_perforce_changes -tc' \
-    '-C[select by client]:client:_perforce_clients' \
-    '-m[max files to show]:max files: ' \
-    '-u[select by user]:user name:_perforce_users' \
+    '-a[list files for all clients]' \
+    '-c[list files opened in change]:change:_perforce_changes -tc' \
+    '-C[list files open in client]:client:_perforce_clients' \
+    '-u[list files opened by user]:user name:_perforce_users' \
+    '-m[limit output to max files]:max files: ' \
+    '-s[short output]' \
+    '-x[list exclusive files]' \
+    '-g[list files opened on Commit Server]' \
     '*::file:_perforce_files -to'
 }
 
@@ -2458,12 +2677,49 @@ _perforce_cmd_ping() {
 }
 
 
+(( $+functions[_perforce_cmd_populate] )) ||
+_perforce_cmd_populate() {
+  local -a fileargs
+  if [[ ${words[(I)-b*]} -ne 0 ]]; then
+    if [[ ${words[(I)-*s*]} -eq 0 ]]; then
+      # with -b and no -s, all files are to-files (but -s may come later)
+      fileargs=('*::to file:_perforce_files -tR')
+    else
+      # with -b and -s we have one from-file and any number of to-files
+      fileargs=('*::to file:_perforce_files')
+    fi
+  elif [[ ${words[(I)-(S|P)]} -ne 0 ]]; then
+      fileargs=('*::file:_perforce_files -tR')
+  else
+    # with no -b we have one from-file and one to-file
+    fileargs=('1::from file:_perforce_files -tR'
+              '2::to file:_perforce_files')
+  fi
+  _arguments -s : \
+    '(-S -P)-b[use branch view'\''s source and target]:branch:_perforce_branches' \
+    '(-S -P)-s[select source file, use branch view as target]:source file:_perforce_files -tR' \
+    '-r[reverse mapping direction]' \
+    '-S[use generated branch view from stream]:stream: ' \
+    '-P[use generated branch view from parent stream]:parent stream: ' \
+    '-d[description for submitted change]:description: ' \
+    '-f[force deleted files to branch into target]' \
+    '-n[preview populate]' \
+    '-o[display files created by populate]' \
+    '-m[limit max actions]:max actions: ' \
+    $fileargs
+}
+
+
 (( $+functions[_perforce_cmd_print] )) ||
 _perforce_cmd_print() {
   _arguments -s : \
-    '-a[display all revisions in a range]' \
-    '-o[select output file]:output file:_files' \
+    '-a[print all revisions in range]' \
+    '-A[print files in archive depots]' \
+    '-k[suppress keyword expansion]' \
+    '-o[redirect output to file]:file:_files' \
     '-q[suppress header]' \
+    '-m[limit max files]:max files: ' \
+    '-U[print files in unload depot]:unload file:_perforce_files' \
     '*::file:_perforce_files -tR'
 }
 
@@ -2471,23 +2727,31 @@ _perforce_cmd_print() {
 (( $+functions[_perforce_cmd_protect] )) ||
 _perforce_cmd_protect() {
   _arguments -s : \
-    '-o[write spec to stdout]' \
-    '-i[read spec from stdin]'
+    '-o[write protection table to standard output]' \
+    '-i[read protection table from standard input]'
 }
 
 
 (( $+functions[_perforce_cmd_protects] )) ||
 _perforce_cmd_protects() {
   _arguments -s : \
-    '(-g -u)-a[show for all users]' \
-    '(-a -u)-g[select by group]:perforce group:_perforce_groups' \
-    '(-a -g)-u[select by user]:perforce user:_perforce_users' \
-    '-h[limit to host]:host:_perforce_hosts' \
-    '-m[single word summary]' \
+    '(-g -u)-a[display protection lines for all users]' \
+    '(-a -u)-g[display protection lines for group]:perforce group:_perforce_groups' \
+    '(-a -g)-u[display protection lines for user]:perforce user:_perforce_users' \
+    '-h[display protection lines for host]:host:_perforce_hosts' \
+    '-m[report single word summary]' \
     '*:file:_perforce_files'
 }
 
 
+(( $+functions[_perforce_cmd_prune] )) ||
+_perforce_cmd_prune() {
+  _arguments -s : \
+    '-y[execute prune]' \
+    '-S[stream to prune]:stream: '
+}
+
+
 (( $+functions[_perforce_cmd_pull] )) ||
 _perforce_cmd_pull() {
   _arguments -s : \
@@ -2497,6 +2761,37 @@ _perforce_cmd_pull() {
     '-J[specify prefix for journal file]:journal file prefix: '
 }
 
+
+(( $+functions[_perforce_cmd_reconcile] )) ||
+_perforce_cmd_reconcile() {
+  _arguments -s : \
+    '-n[preview reconcile]' \
+    '-c[open files for change]:change:_perforce_changes -tc' \
+    '-e[open modified files for edit]' \
+    '-a[open new files for add]' \
+    '-d[open removed files for delete]' \
+    '-f[reformat filenames with wildcard characters]' \
+    '-I[do not perform ignore checking]' \
+    '-l[output relative paths]' \
+    '-m[check file modification times]' \
+    '-w[force client files to be updated to match depot]' \
+    '-k[reconcile have list with client]' \
+    '*:file:_perforce_files -tu'
+}
+
+
+(( $+functions[_perforce_cmd_rec] )) ||
+_perforce_cmd_rec() {
+  _perforce_cmd_reconcile "$@"
+}
+
+
+(( $+functions[_perforce_cmd_rename] )) ||
+_perforce_cmd_rename() {
+  _perforce_cmd_move "$@"
+}
+
+
 (( $+functions[_perforce_cmd_reopen] )) ||
 _perforce_cmd_reopen() {
   # Assume user doesn't want to reopen to same changelist.
@@ -2508,8 +2803,8 @@ _perforce_cmd_reopen() {
   fi
 
   _arguments -s : \
-    '-c+[select change to reopen on]:change:_perforce_changes -tc' \
-    '-t+[set file type]:file type:_perforce_filetypes' \
+    '-c[reopen files for change]:change:_perforce_changes -tc' \
+    '-t[specify new file type]:filetype:_perforce_filetypes' \
     '*::file:_perforce_files -to'
 }
 
@@ -2533,13 +2828,32 @@ _perforce_cmd_replicate() {
 (( $+functions[_perforce_cmd_resolve] )) ||
 _perforce_cmd_resolve() {
   _arguments -s : \
-    '-a-[select automatic merge type]:automation type:((f\:force\ acceptance m\:skip\ conflicts s\:safe t\:use\ theirs y\:use\ yours))' \
-    '-d-[select diff option]:diff option:((b\:ignore\ blanks w\:ignore\ all\ whitespace))' \
-    '-f[force re-resolution]' \
-    '-n[no action, just list]' \
+    '-A-[limit resolve attempts]:resolve attempts:((
+a\:resolve\ attributes
+b\:resolve\ file\ branching
+c\:resolve\ file\ content\ changes
+d\:resolve\ file\ deletions
+m\:resolve\ moved\ and\ renamed\ files
+t\:resolve\ filetype\ changes
+Q\:resolve\ charset\ changes
+))' \
+    '-a-[set automatic resolve]:resolve:((
+s\:skip\ files\ that\ need\ merging
+m\:skip\ files\ with\ conflicts
+f\:accept\ merged\ files\ with\ conflicts
+t\:use\ theirs
+y\:use\ yours))' \
+    '-d-[control whitespace merging]:whitespace option:((
+b\:ignore\ whitespace\ changes
+w\:ignore\ whitespace\ altogether
+l\:ignores\ line\ endings))' \
+    '-f[re-resolve files]' \
+    '-n[preview resolve]' \
+    '-N[preview resolve with summary]' \
     '-o[display base file name and revision for merge]' \
-    '-t[force textual merge on binary files]' \
-    '-v[verbose, mark all changes]' \
+    '-t[force textual merge]' \
+    '-v[insert markers for all changes]' \
+    '-c[limit resolve to change]:change:_perforce_changes -tc' \
     '*::file:_perforce_files -to'
 }
 
@@ -2555,10 +2869,12 @@ _perforce_cmd_resolved() {
 (( $+functions[_perforce_cmd_revert] )) ||
 _perforce_cmd_revert() {
   _arguments -s : \
-    '-a[revert unaltered files]' \
-    '-c[limit reversions to change]:change:_perforce_changes -tc' \
-    '-k[bypass client refresh]' \
-    '-n[no action, show effect only]' \
+    '-a[revert open unchanged files]' \
+    '-n[preview revert]' \
+    '-k[mark files as reverted on server]' \
+    '-w[delete new files]' \
+    '-c[revert files opened in change]:change:_perforce_changes -tc' \
+    '-C[specify client]:client:_perforce_clients' \
     '*::file:_perforce_files -to'
 }
 
@@ -2566,15 +2882,16 @@ _perforce_cmd_revert() {
 (( $+functions[_perforce_cmd_review] )) ||
 _perforce_cmd_review() {
   _arguments -s : \
-    '-c[select change for counter]:change:_perforce_changes -ts' \
-    '-t[limit change number by counter]:counter:_perforce_counters'
+    '-c[specify change]:change:_perforce_changes -ts' \
+    '-t[specify counter]:counter:_perforce_counters'
 }
 
 
 (( $+functions[_perforce_cmd_reviews] )) ||
 _perforce_cmd_reviews() {
   _arguments -s : \
-    '-c[show users by change]:change:_perforce_changes -ts' \
+    '-c[limit files submitted in change]:change:_perforce_changes -ts' \
+    '-C[limit files opened in client]:client:_perforce_clients' \
     '*::file:_perforce_files'
 }
 
@@ -2583,8 +2900,9 @@ _perforce_cmd_reviews() {
 _perforce_cmd_set() {
   # Only works under Windoze but maybe we are on Cygwin.
   _arguments -s : \
+    '-q[remove origin]' \
     '-s[set for whole system]' \
-    '-S[set for specified service]:service: ' \
+    '-S[specify service]:service: ' \
     "*::environment variable:_perforce_variables"
 }
 
@@ -2592,26 +2910,80 @@ _perforce_cmd_set() {
 (( $+functions[_perforce_cmd_shelve] )) ||
 _perforce_cmd_shelve() {
   _arguments -s : \
-    '(-i)-c[specify changelist if not default]:change:_perforce_changes -tc' \
-    '(-i -r)-d[delete shelved files]' \
-    '(-r)-f[force by admin user or force to overwrite]' \
-    '(-c)-i[read from standard input]' \
-    '(-d)-r[replace shelved files in changelist]' \
+    '-i[read change spec from standard input]' \
+    '(-i)-c[shelve files in change]:change:_perforce_changes -tc' \
+    '-f[overwrite existing shelved files]' \
+    '-r[replace shelved files in change]' \
+    '-a[handle unchanged files]:option:(submitunchanged leaveunchanged)' \
+    '(-p -a -i -r)-d[delete shelved files]' \
+    '-p[promote shelved change to commit server]' \
     '(-i -r)*::file:_perforce_files -to'
 }
 
 
+(( $+functions[_perforce_cmd_status] )) ||
+_perforce_cmd_status() {
+  _arguments -s : \
+    '-c[list files in change]:change:_perforce_changes -tc' \
+    '-A[list all new, modified, and removed files]' \
+    '-e[list modified files]' \
+    '-a[list new files]' \
+    '-d[list removed files]' \
+    '-f[reformat filenames with wildcard characters]' \
+    '-s[summarize output for new files]' \
+    '-I[do not perform ignore checking]' \
+    '-m[check file modification times]' \
+    '-k[reconcile have list with client]' \
+    '*:file:_perforce_files -tuo'
+}
+
+
 (( $+functions[_perforce_cmd_sizes] )) ||
 _perforce_cmd_sizes() {
   _arguments -s : \
-    '-a[show for all revisions]' \
-    '-b[set blocksize]:blocksize in bytes: ' \
+    '-a[list all revisions in range]' \
+    '-b[specify blocksize]:blocksize in bytes: ' \
+    '(-H)-h[print sizes in human-readable form (GiB)]' \
+    '(-h)-H[print sizes in human-readable form (GB)]' \
+    '-m[limit max files]:max files: ' \
     '-s[sum the file sizes]' \
-    '-S[show sizes of shelved files]' \
+    '-S[display sizes for shelved files]' \
+    '-z[omit lazy copies]' \
+    '(-z -S)-A[display files in archive depots]' \
+    '-U[display sizes for unload files]' \
     '*:file:_perforce_files -tR'
 }
 
 
+# TODO Add more logic for subcommands
+#p4 stream edit
+#p4 stream resolve [-a<flag>] [-n] [-o]
+#p4 stream revert
+(( $+functions[_perforce_cmd_stream] )) ||
+_perforce_cmd_stream() {
+  _arguments -s : \
+    '(-o -v)-d[delete stream]' \
+    '(-f)-o[write stream spec to standard output]' \
+    '(-o -v)-P[insert value into parent field]:parent stream: ' \
+    '(-o -v)-t[insert value into type field]:type: ' \
+    '(-o -v)-i[read stream spec from standard input]' \
+    '(-o -v)-f[force modification]' \
+    '(-f)-v[expose client view]' \
+    '1:stream name: '
+}
+
+
+(( $+functions[_perforce_cmd_streams] )) ||
+_perforce_cmd_streams() {
+  _arguments -s : \
+    '-F[limit files to pattern]:file pattern: ' \
+    '-T[limit fields to list]:field list: ' \
+    '-m[limit max streams]:max streams: ' \
+    '-U[list unloaded task streams]' \
+    '*:stream path: '
+}
+
+
 (( $+functions[_perforce_cmd_spec] )) ||
 _perforce_cmd_spec() {
   _arguments -s : \
@@ -2623,26 +2995,40 @@ label spec trigger typemap user)"
 }
 
 
+# TODO Figure out how --parallel will work
+#p4 submit -i [-r -s -f option] --parallel=threads=N[,batch=N][,min=N]
 (( $+functions[_perforce_cmd_submit] )) ||
 _perforce_cmd_submit() {
   _arguments -s : \
-    '-r[files open for add or edit remain open]' \
-    '-s[include fix status in list]' \
+    '(-s -d -e -i)-c[submit change]:change:_perforce_changes -tc' \
+    '(-r -s -f -d -c -i --noretransfer)-e[submit shelved change]:change:_perforce_changes -tS' \
+    '(-s -c -e -i --noretransfer)-d[specify description]:description: ' \
+    '(-d -c -e --noretransfer)-i[read change spec from standard input]' \
     '-f[override submit option]:submit option:_perforce_submit_options' \
-    '(-s -i)-c[submit specific change]:change:_perforce_changes -tc' \
-    '(-s -c)-d[specify description on command line]:description: ' \
-    '(-c)-i[read change spec from stdin]' \
+    '-r[reopen submitted files]' \
+    '(-d -c)-s[include fix status in list]' \
+    '--parallel[parallel file transfer options]:parallel options: ' \
+    '--noretransfer[do not re-transfer submitted files]:no re-transfer?:(0 1)' \
     '*::file:_perforce_files -to -tr'
 }
 
 
+# TODO Figure out how --parallel will work
+#--parallel=threads=N[,batch=N][,batchsize=N][,min=N][,minsize=N]
 (( $+functions[_perforce_cmd_sync] )) ||
 _perforce_cmd_sync() {
   _arguments -s : \
     '-f[force resynchronisation]' \
-    '-n[show operations but don'\''t perform them]' \
-    '-k[bypass client file update]' \
-    '-q[suppress informational messages]' \
+    '-L[use full depot syntax]' \
+    '-n[preview sync]' \
+    '-N[preview sync with summary]' \
+    '(-s -p)-k[update server without syncing files]' \
+    '(-f -k -r -s)-p[sync client without updating server]' \
+    '-q[suppress normal output messages]' \
+    '(-s -p)-r[reopen moved files in new location]' \
+    '(-f -k -r -p)-s[do not clobber modified files]' \
+    '-m[limit max files to sync]:max files: ' \
+    '--parallel[parallel file transfer options]:parallel options: ' \
     '*::file:_perforce_files -tR'
 }
 
@@ -2651,7 +3037,9 @@ _perforce_cmd_sync() {
 _perforce_cmd_tag() {
   _arguments -s : \
     '-d[delete association between label and files]' \
-    '-n[show what files would be tagged]' \
+    '-n[preview tag]' \
+    '-g[update global label]' \
+    '-U[create label with autoreload option]' \
     '-l[specify label]:label:_perforce_labels' \
     '*::file:_perforce_files -tR'
 }
@@ -2683,8 +3071,10 @@ _perforce_cmd_typemap() {
 (( $+functions[_perforce_cmd_unlock] )) ||
 _perforce_cmd_unlock() {
   _arguments -s : \
-    '-c[non-default change to unlock]:change:_perforce_changes -tc' \
-    '-f[allow superuser to unlock any file]' \
+    '-s[unlock files from shelved change]:change:_perforce_changes -tS' \
+    '-c[unlock files from change]:change:_perforce_changes -tc' \
+    '-x[unlock exclusive files]' \
+    '-f[unlock files owned by other users]' \
     '*::file:_perforce_files'
 }
 
@@ -2692,21 +3082,37 @@ _perforce_cmd_unlock() {
 (( $+functions[_perforce_cmd_unshelve] )) ||
 _perforce_cmd_unshelve() {
   _arguments -s : \
-    '-s[specify shelving change]:change:_perforce_changes -tS' \
-    '-c[specify change for unshelve]:change:_perforce_changes -tc' \
+    '-s[unshelve files from change]:change:_perforce_changes -tS' \
+    '-c[unshelve files to change]:change:_perforce_changes -tc' \
     '-f[force clobbering of writeable files]' \
     '-n[preview unshelve]' \
+    '-b[use branch view for unshelve]:branch:_perforce_branches' \
+    '-S[use generated branch view from stream]:stream: ' \
+    '-P[use generated branch view from parent stream]:parent stream: ' \
     '*::file, pattern allowed:_perforce_files'
 }
 
 
+(( $+functions[_perforce_cmd_update] )) ||
+_perforce_cmd_update() {
+  _arguments -s : \
+    '-f[force resynchronisation]' \
+    '-L[use full depot syntax]' \
+    '-n[preview update]' \
+    '-N[preview update with summary]' \
+    '-q[suppress normal output messages]' \
+    '-m[limit max files to update]:max files: ' \
+    '*::file:_perforce_files -tR'
+}
+
+
 (( $+functions[_perforce_cmd_user] )) ||
 _perforce_cmd_user() {
   _arguments -s : \
-    '(-o)-f[force edit by superuser]' \
     '(-o -i)-d[delete user]' \
-    '(-o -d)-i[read form from stdin]' \
-    '(-f -i -d)-o[write form to stdout]' \
+    '(-f -i -d)-o[write user spec to standard output]' \
+    '(-o -d)-i[read user spec from standard input]' \
+    '(-o)-f[force edit of user]' \
     '(-i)1::username:_perforce_users'
 }
 
@@ -2714,7 +3120,11 @@ _perforce_cmd_user() {
 (( $+functions[_perforce_cmd_users] )) ||
 _perforce_cmd_users() {
   _arguments -s : \
-    '-m[set max users to show]:max users: ' \
+    '-m[limit output to max users]:max users: ' \
+    '-a[output service and operator users]' \
+    '-l[long output]' \
+    '-r[list only replica users]' \
+    '-c[list only central server users]' \
     '*::username:_perforce_users'
 }
 
diff --git a/Completion/Unix/Command/_perl b/Completion/Unix/Command/_perl
index 52559b800..0914264fe 100644
--- a/Completion/Unix/Command/_perl
+++ b/Completion/Unix/Command/_perl
@@ -56,7 +56,7 @@ _perl_normal() {
 _perl_m_opt () {
   compset -P '-'
 
-  if compset -P '*='; then
+  if compset -P 1 '*='; then
     _message -e module-arguments 'module arguments, comma separated'
   else
     _perl_modules -S= -q
diff --git a/Completion/Unix/Command/_pgrep b/Completion/Unix/Command/_pgrep
index 0b7d23d4d..714bf095b 100644
--- a/Completion/Unix/Command/_pgrep
+++ b/Completion/Unix/Command/_pgrep
@@ -28,6 +28,7 @@ arguments=('-P[parent process id]:parent process id:->ppid'
      '-q[do not write anything to standard output]'
      '-S[search also in system processes]'
      '-v[negate matching]'
+     '-w[show thread ids instead of pids]'
      '-x[match exactly]'
      '-z[match only in zones]:zone:_zones')
 
@@ -41,7 +42,7 @@ fi
 local optchars
 case "$OSTYPE" in
   linux*)
-    optchars="cflvxdnoPgsuUGt"
+    optchars="cflvxdnoPgsuUGtw"
     ;;
   freebsd*)
     optchars="LSafilnoqvxFGMNPUdgjstu"
diff --git a/Completion/Unix/Command/_php b/Completion/Unix/Command/_php
index 0e840032f..d03f3395e 100644
--- a/Completion/Unix/Command/_php
+++ b/Completion/Unix/Command/_php
@@ -50,7 +50,7 @@ case $state in
     local -a directives suf
     local code='foreach (ini_get_all() as $k => $v) { echo "$k\n"; }'
     directives=( $(_call_program directives $words[1] -r ${(q)code} 2>/dev/null) )
-    if compset -P '*='; then
+    if compset -P 1 '*='; then
       _default && return 0
     else
       compset -S '=*' || suf=( -qS '=' )
diff --git a/Completion/Unix/Command/_picocom b/Completion/Unix/Command/_picocom
index c9d3be0e0..4b8b3ea2e 100644
--- a/Completion/Unix/Command/_picocom
+++ b/Completion/Unix/Command/_picocom
@@ -32,7 +32,7 @@ function _picocom_escape () {
 function _picocom () {
     local -a args
 
-    args=( '(--baud -b)'{--baud,-b}'[define baud-rate to set the terminal to]:baud rate:_baudrate'
+    args=( '(--baud -b)'{--baud,-b}'[define baud-rate to set the terminal to]:baud rate:_baudrates'
            '(--flow -f)'{--flow,-f}'[define type of flow control to use]:flow control:_picocom_flowcontrol'
            '(--parity -p)'{--parity,-p}'[define type of parity to use]:parity mode:_picocom_paritymode'
            '(--databits -d)'{--databits,-d}'[define the number of databits per word]:data bits:_picocom_databits'
diff --git a/Completion/Unix/Command/_pkg-config b/Completion/Unix/Command/_pkg-config
index e44b36897..43773967e 100644
--- a/Completion/Unix/Command/_pkg-config
+++ b/Completion/Unix/Command/_pkg-config
@@ -1,14 +1,17 @@
 #compdef pkg-config
 
-local arguments packages curcontext="$curcontext" state line ret=1
+local arguments packages curcontext="$curcontext" state line expl ret=1
 declare -A opt_args
 
+# Up-to-date as of pkg-config 0.29-4 (debian package) man page synopsis
 arguments=(
       "--modversion[print the version information of a given package]"
+      "--version[print version information of pkg-config itself]"
       "--atleast-pkgconfig-version=[require given version of pkg-config]:minimum version"
       "(- *)"{--help,-\?}"[display a help message]"
       "(- *)--usage[display brief usage message]"
       "--print-errors[cause errors to be printed]"
+      "--short-errors[print short error messages]"
       "--silence-errors[prevent the printing of errors]"
       "--errors-to-stdout[print errors to stdout rather than stderr]"
       "--cflags[print the preprocessor and compiler flags]"
@@ -19,25 +22,45 @@ arguments=(
       "--libs-only-L[print the -L and -R parts of \"--libs\"]"
       "--libs-only-l[print the -l part of \"--libs\"]"
       "--libs-only-other[output other libs]"
+      "--validate[syntax check a .pc file]"
       "--list-all[list all known packages]"
-      "--variable=[return the value of the specified variable]:variable"
+      "--variable=[return the value of the specified variable]:variable:->variable"
       "--define-variable=[set the global value for a variable]:name value pair"
+      "--print-variables[list all variables defined in the package]"
       "--uninstalled[return success if any \"-uninstalled\" packages are being used]"
       "--exists[test whether the package exists or not]"
       "--atleast-version=[test whether the version is at least that of the specified value]:least value"
       "--exact-version=[test whether the version is exactly that of the specified value]:exact value"
       "--max-version=[test whether the version is no greater than some specific value]:max version"
+      "--print-provides[list all modules the package provides]"
+      "--print-requires[list all modules the package requires]"
+      "--print-requires-private[list all modules the package requires for static linking (see --static)]"
 #      "--msvc-syntax[output linker flags in a form compatible with MSVC++ (Windows only)]"
 #      "--dont-define-prefix[disables automatic overiding of the variable \"prefix\" (Windows only)]"
 #      "--prefix-variable=[set the name of the variable \"prefix\" (Windows only)]:prefix value"
       "*: :->packages"
 )
 
-_arguments -C $arguments && ret=0
+_arguments -C -S : $arguments && ret=0
 
-if [[ -n $state ]] ; then
-  packages=( ${${(f)"$(_call_program packages pkg-config --list-all)"}%% *} )
-  _wanted packages expl 'package' compadd -a - packages && ret=0
-fi
+case $state in
+  (packages)
+    packages=( ${${(f)"$(_call_program packages pkg-config --list-all)"}%% *} )
+    _alternative \
+      'packages:package:compadd -a - packages' \
+      'files:package file:_files -g "*.pc(-.)"' \
+      && ret=0
+  ;;
+
+  (variable)
+    if [[ $line[1] != '-' ]]; then
+      local -a variables=( ${${(f)"$(_call_program variables "pkg-config --print-variables -- ${(q)line[1]}")"}} )
+      _wanted variables expl 'variable' compadd -a - variables && ret=0
+    else
+      _message "variable"
+    fi
+    ;;
+
+esac
 
 return ret
diff --git a/Completion/Unix/Command/_postfix b/Completion/Unix/Command/_postfix
index 0ced6c24a..006c950e3 100644
--- a/Completion/Unix/Command/_postfix
+++ b/Completion/Unix/Command/_postfix
@@ -1,20 +1,163 @@
-#compdef postsuper
+#compdef postconf postqueue postsuper
+
+# Last updated from postfix 3.1.0.
+
+local context state state_descr line
+typeset -A opt_args
+integer NORMARG
 
 _postfix_queue_id() {
-compadd ${${(M)${(f)"$(postqueue -p)"}:#(#s)([0-9A-F]##)*}/(#s)(#b)([0-9A-F]##)*/$match[1]}
+  local -a lines=( ${(f)"$(_call_program mailq 'mailq')"} )
+
+  # $enable_long_queue_ids = no
+  compadd "$@" -- ${${(M)lines:#(#s)([0-9A-F]##([*!]|) )*}/(#s)(#b)([0-9A-F]##)*/$match[1]}
+
+  # $enable_long_queue_ids = yes
+  compadd "$@" -- ${${${(M)lines:#(#s)[0-9bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ](#c10)z*}%% *}%[*!]}
+}
+
+# The first argument must be either 'd' or 'n', to complete parameters in
+# `postconf -d -H` or `postconf -n -H` output, respectively.
+_postfix_main_cf_parameter() {
+  local -a expl
+  local kind
+
+  kind=$1; shift
+  case $kind in
+    (d)
+      # Note for the future: if $mail_version >= 3.1, we can pass -H instead of
+      # stripping the = signs by hand.
+      #
+      # don't bother to add -c in this case: the defaults don't depend on the config dir.
+      _wanted parameters expl 'main.cf parameter' compadd "$@" -- \
+        ${${(f)"$(_call_program postconf_-d "${words[1]} -$kind")"}%% =*}
+      ;;
+    (n)
+      # Show the values too.
+      local -a kv=( ${${(f)"$(_call_program postconf_-n "${words[1]} ${opt_args[-c]+"-c ${opt_args[-c]}"} -$kind")"}/ = /:} )
+      _describe -t parameters 'main.cf parameter' kv
+      ;;
+    (*)
+      return 1 # can't happen due to the guard above
+      ;;
+  esac
+}
+
+_postfix_main_cf_parameter_and_value() {
+  # Note: be careful not to accidentally run 'postconf foo=bar' here.
+  #
+  # The '-P 1' syntax chooses the shortest match.  If the '1' were removed,
+  # completing «postconf foo=bar=<TAB>» would change main.cf.  So, don't remove
+  # it.
+  if compset -P 1 '*='; then
+    local value="`_call_program postconf-get-value-at-dir "${words[1]} ${opt_args[-c]+"-c ${opt_args[-c]}"} -h -- ${(q)IPREFIX%=}"`"
+    [[ -n $value ]] && compadd "$@" -- $value
+  else
+    _postfix_main_cf_parameter d -S=
+  fi
 }
 
 case $service in
 	(postsuper)
-	_arguments -C \
+	_arguments -C -s : \
 	'-p[purge old tempfiles]' \
 	'-s[structure check and repair]' \
-	'-v[verbose]' \
+	'*-v[verbose]' \
 	'-c[configdir]:config dir:_files -/' \
 	'-d[delete]:queue id:_postfix_queue_id' \
 	'-h[hold]:queue id:_postfix_queue_id' \
 	'-H[release]:queue id:_postfix_queue_id' \
-	'-r[requeue]:queue id:_postfix_queue_id' \
+	'*-r[requeue]:queue id, or "ALL":_postfix_queue_id' \
 	'1:queue:(hold incoming active deferred)'
 	;;
+        (postqueue)
+        _arguments -C -s : \
+          '*-v[enable verbose logging]' \
+          '-c[configdir]:config dir:_files -/' \
+          '(-f -i -j -p -s)-f[flush the queue]' \
+          '(-f -i -j -p -s)-i[immediately deliver one message]:queue id:_postfix_queue_id' \
+          '(-f -i -j -p -s)-j[print the queue in json]' \
+          '(-f -i -j -p -s)-p[print the queue in traditional format]' \
+          '(-f -i -j -p -s)-s[immediately deliver messages to specified site]:site:_hosts' 
+        ;;
+        (postconf)
+          local -a file_ops=(-b -t -a -A -l -m -T)
+          local -a modes=(-F -M -P $file_ops -p)
+          local -a only_for_p=(-C -d -h -n)
+          local -a only_for_asterisk_dot_cf=(-e -f -o -x)
+          local -a args=(
+              # global
+              '-c[configdir]:config dir:_files -/' \
+              '*-v[enable verbose logging]'
+
+              # mode selection
+              "($modes $only_for_p                              -X -#)"'-F[operate on master.cf entry fields]'
+              "($modes $only_for_p                           -H      )"'-M[operate on master.cf services entries]'
+              "($modes $only_for_p                                 -#)"'-P[operate on master.cf parameter settings]'
+              "($modes $only_for_p $only_for_asterisk_dot_cf -H -X -#)"'-b[display DSN text, expanded]'
+              "($modes $only_for_p $only_for_asterisk_dot_cf -H -X -#)"'-t[display DSN text, unexpanded]'
+              "($modes $only_for_p $only_for_asterisk_dot_cf -H -X -#)"'-a[list available SASL server plug-ins]'
+              "($modes $only_for_p $only_for_asterisk_dot_cf -H -X -#)"'-A[list available SASL client plug-ins]'
+              "($modes $only_for_p $only_for_asterisk_dot_cf -H -X -#)"'-l[list supported mailbox locking methods]'
+              "($modes $only_for_p $only_for_asterisk_dot_cf -H -X -#)"'-m[list supported lookup table types]'
+              "($modes                                               )"'-p[(default) print main.cf settings]'
+              "($modes $only_for_p $only_for_asterisk_dot_cf -H -X -#)"'-T[print TLS information]:mode:((
+                           compile-version\:"OpenSSL compile-time version"
+                           run-version\:"OpenSSL run-time version"
+                           public-key-algorithms\:"supported public-key algorithms"
+                           ))'
+
+              # other options; comments indicate modes:
+              # -p only
+              '-C[display parameters from the specified class]:parameter class:(builtin service user all)'
+              '-d[print default values]'
+              '-h[print value only]'
+              '-n[show parameters changed in main.cf]'
+
+              # all except $file_ops:
+              '-e[change parameter values]'
+              '-f[fold long output lines]'
+              '-o[set parameter value for this operation]: :_postfix_main_cf_parameter_and_value'
+              '-x[expand '\''$name'\'' in parameter values]'
+
+              # -p -F -P only
+              '-H[show parameter name only]'
+              # -p -M -P only
+              '-X[remove parameters from main.cf/master.cf]'
+              # -p -M only
+              '-#[comment out parameters in main.cf/master.cf]'
+
+              # $state
+              '*: :->positional'
+          )
+          _arguments -C -s -S -n : "$args[@]"
+
+          case $state in
+            (positional)
+              # Simulate the default
+              if [[ -z $opt_args[(i)${(j.|.)modes}] ]]; then
+                opt_args[-p]=""
+              fi
+              if [[ -n $opt_args[(i)(-b|-t)] ]]; then
+                local expl
+                if (( CURRENT == NORMARG )); then
+                  _wanted files expl 'template file, or "" for default' _files
+                else
+                  _nothing
+                fi
+              elif [[ -n $opt_args[(i)${(j.|.)file_ops}] ]]; then
+                _nothing
+              elif (( $+opt_args[-p] )); then
+                if (( $+opt_args[-e] )); then
+                  _postfix_main_cf_parameter_and_value
+                elif [[ -n $opt_args[(i)(-X|-[#])] ]]; then
+                  _postfix_main_cf_parameter n
+                else
+                  _postfix_main_cf_parameter d
+                fi
+              else # one of the master.cf modes: -M -F -P
+                _message "arguments for $opt_args[(i)${(j.|.)modes}] mode"
+              fi
+          esac
+        ;;
 esac
diff --git a/Completion/Unix/Command/_prove b/Completion/Unix/Command/_prove
index 1f21423e6..17b48a54d 100644
--- a/Completion/Unix/Command/_prove
+++ b/Completion/Unix/Command/_prove
@@ -7,7 +7,7 @@
 _arguments \
   '(- *)'{-h,--help}'[display help and exit]' \
   '(- *)'{-H,--man}'[display longer help and exit]' \
-  '(- *)--version[display version and exit]' \
+  '(- *)'{-V,--version}'[display version and exit]' \
   '(--quiet -q -Q --QUIET)'{-v,--verbose}'[print all test lines]' \
   {-l,--lib}'[add lib to path]' \
   {-b,--blib}'[add blib to path]' \
diff --git a/Completion/Unix/Command/_rake b/Completion/Unix/Command/_rake
index 7fed949ab..8814edaa5 100644
--- a/Completion/Unix/Command/_rake
+++ b/Completion/Unix/Command/_rake
@@ -22,7 +22,7 @@ _arguments -C -s -S \
   \*{--require,-r}'[require specified module before executing rakefile]:library:->library' \
   '(- *)--rules[trace the rules resolution]' \
   '(--quiet -q --silent -s --verbose -v)'{--silent,-s}"[like --quiet, but also suppresses the 'in directory' announcement]" \
-  '(--system,-g)'{--system,-g}'[using system wide (global) rakefiles (usually '~/.rake/*.rake')]' \
+  '(--system -g)'{--system,-g}'[using system wide (global) rakefiles (usually '~/.rake/*.rake')]' \
   '(- *)'{--tasks,-T}'[display the tasks (matching the specified pattern) with descriptions, then exit]:pattern::' \
   '(--trace -t)'{--trace,-t}'[turn on invoke/execute tracing, enable full backtrace]' \
   '(--quiet -q --silent -s --verbose -v)'{--verbose,-v}'[log message to standard output (default)]' \
diff --git a/Completion/Unix/Command/_rar b/Completion/Unix/Command/_rar
index e2081f6e7..906e236fd 100644
--- a/Completion/Unix/Command/_rar
+++ b/Completion/Unix/Command/_rar
@@ -22,7 +22,7 @@ common=(
   '-ow[save or restore file owner and group]'
   '-p+:password'
   '-p\-[do not query password]'
-  '-r[recurse subdirectorie]'
+  '-r[recurse subdirectories]'
   '-ta+[process files modified after a date]:date (YYYYMMDDHHMMSS)'
   '-tb+[process files modified before a date]:date (YYYYMMDDHHMMSS)'
   '-tn+[process files newer than a specified time]:time'
diff --git a/Completion/Unix/Command/_readelf b/Completion/Unix/Command/_readelf
index a474a8dc4..46da00cc4 100644
--- a/Completion/Unix/Command/_readelf
+++ b/Completion/Unix/Command/_readelf
@@ -34,6 +34,7 @@ case $variant in
       '(-g --section-groups)'{-g,--section-groups}'[show section groups]'
       '(-t --section-details)'{-t,--section-details}'[show section details]'
       '(-e --headers)'{-e,--headers}'[show file, program and sections headers]'
+      '(-s --syms --symbols)'{-s,--syms,--symbols}'[show symbol table]'
       '(-u --unwind)'{-u,--unwind}'[show unwind info (if present)]'
       '(-D --use-dynamic)'{-D,--use-dynamic}'[use dynamic section info when showing symbols]'
     )
@@ -51,6 +52,9 @@ case $variant in
       '--elf-section[used specified section as ELF input data]::section'
       '(-e --exception)'{-e,--exception}'[show sections for exception handling]'
       '(-N --numeric-addresses)'{-N,--numeric-addresses}"[don't find symbol names for addresses in DWARF data]"
+      '(-z --decompress)'{-z,--decompress}'[show compression information; decompress before dumping data]'
+      '(--symbols)-s[show symbol table]'
+      '(-s)--symbols=-[show symbol table]::section:(.dynsym .symtab)'
     )
   ;;
 esac
diff --git a/Completion/Unix/Command/_rlogin b/Completion/Unix/Command/_rlogin
index a04c6d068..685e4e50b 100644
--- a/Completion/Unix/Command/_rlogin
+++ b/Completion/Unix/Command/_rlogin
@@ -12,6 +12,7 @@ _rlogin () {
   rsh|remsh)
     local context state line ret=1
     typeset -A opt_args
+    local -a _comp_priv_prefix
 
     _arguments -s \
       '-n[ignore stdin]' \
@@ -37,9 +38,9 @@ _rlogin () {
       '*:files:->files' && ret=0
 
     if [[ -n "$state" ]]; then
-      if compset -P '*:'; then
+      if compset -P 1 '*:'; then
 	_remote_files -- rsh && ret=0
-      elif compset -P '*@'; then
+      elif compset -P 1 '*@'; then
         _wanted hosts expl host _rlogin_hosts -S: && ret=0
       else
         _alternative \
diff --git a/Completion/Unix/Command/_rm b/Completion/Unix/Command/_rm
index 4d0dbdb3f..6d728b157 100644
--- a/Completion/Unix/Command/_rm
+++ b/Completion/Unix/Command/_rm
@@ -22,16 +22,24 @@ if _pick_variant gnu=gnu unix --help; then
     '(- *)--version[output version information and exit]'
   )
 else
-  args=(${args:#*--(force|recursive)\[*})
+  args=(${args:#*)--*\[*})
   case $OSTYPE in
-    freebsd*)
+    dragonfly*|freebsd*|netbsd*|openbsd*)
       args+=(
         '-d[remove directories as well]'
-	'(-i)-I[prompt when removing many files]'
-	'-P[overwrite files before deleting them]'
-	'-v[explain what is being done]'
-	'-W[attempt to undelete named files]'
-	"-x[don't cross file systems when removing a hierarchy]"
+        '-P[overwrite files before deleting them]'
+      )
+    ;|
+    dragonfly*|freebsd*|netbsd*)
+      args+=(
+        '-v[explain what is being done]'
+        '-W[attempt to undelete named files]'
+        "-x[don't cross file systems when removing a hierarchy]"
+      )
+    ;|
+    dragonfly*|freebsd*)
+      args+=(
+        '(-i)-I[prompt when removing many files]'
       )
     ;;
   esac
diff --git a/Completion/Unix/Command/_rrdtool b/Completion/Unix/Command/_rrdtool
index 9d097bd77..96bdaf0db 100644
--- a/Completion/Unix/Command/_rrdtool
+++ b/Completion/Unix/Command/_rrdtool
@@ -1,23 +1,25 @@
 #compdef rrdtool
 
-_arguments \
+local curcontext="$curcontext" state line expl ret=1
+
+_arguments -C \
   ':rrdtool command:(create update updatev graph dump restore
                    last lastupdate first help info fetch tune
 		   resize xport)' \
-  '*::subcmd:->subcmd' && return 0
+  '*:: :->subcmds' && ret=0
 
+curcontext="${curcontext%:*}-$words[1]:"
 case "$state" in
-    (subcmd)
-
-  case "$words[1]" in
+  (subcmds)
+    case "$words[1]" in
       (help)
-      	_wanted -V 'subcommands' expl 'subcommand' compadd \
-	create update updatev graph dump restore last lastupdate \
-	first help info fetch tune resize xport
-	;;
+	_wanted -V 'subcommands' expl 'subcommand' compadd \
+	  create update updatev graph dump restore last lastupdate \
+	  first help info fetch tune resize xport
+      ;;
       (*)
-    	_files
-    	;;
-  esac
+        _files
+      ;;
+    esac
   ;;
 esac
diff --git a/Completion/Unix/Command/_rsync b/Completion/Unix/Command/_rsync
index 79d8243e1..f79ec1dfe 100644
--- a/Completion/Unix/Command/_rsync
+++ b/Completion/Unix/Command/_rsync
@@ -66,6 +66,20 @@ fi
 
 }
 
+_rsync_info() {
+  local opts
+  opts=( ${${(M)${(f)"$(_call_program values $words[1] --info=help)"}:#*Mention*}/ ##Me/[me} )
+  (( $#opts )) && opts=( '(ALL NONE HELP)'${^opts}\] )
+  _values -s , 'info options' $opts ALL NONE HELP
+}
+
+_rsync_debug() {
+  local opts
+  opts=( ${${(M)${(f)"$(_call_program values $words[1] --debug=help)"}:#*Debug*}/ ##De/[de} )
+  (( $#opts )) && opts=( '(ALL NONE HELP)'${^opts}\] )
+  _values -s , 'debug options' $opts ALL NONE HELP
+}
+
 _rsync_files() {
   _alternative "files:file:_files" "remote-files:remote file:_rsync_remote_files"
 }
@@ -78,6 +92,8 @@ _rsync() {
     '--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' \
+    '--log-file-format=[log updates using specified format]:format' \
+    '--log-file=[log what rsync is doing to the specified file]:file:_files' \
     '(-T --temp-dir)'{-T,--temp-dir=}'[create temporary files in specified directory]:directory:_directories' \
     '--sockopts=[specify custom TCP options]' \
     '(-4 -6 --ipv4 --ipv6)'{-4,--ipv4}'[prefer IPv4]' \
@@ -87,6 +103,7 @@ _rsync() {
     '--config=[specify alternate rsyncd.conf file]:file:_files' \
     '--daemon[run as an rsync daemon]' \
     '--detach[detach from the parent]' \
+    '(-M --dparam)'{-M,--dparam=}'[override global daemon config parameter]:config parameter' \
     '--no-detach[do not detach from the parent]' \
    - client \
     '(-)--help[display help information]' \
@@ -205,15 +222,13 @@ _rsync() {
     '(-i --itemize-changes)'{-i,--itemize-changes}'[output a change-summary for all updates]' \
     '--log-format=[deprecated version of --out-format]' \
     '--out-format=[output updates using specified format]:format' \
-    '--log-file-format=[log updates using specified format]:format' \
-    '--log-file=[log what rsync is doing to the specified file]:file:_files' \
     '--password-file=[read daemon-access password from file]:file:_files' \
     '--list-only[list the files instead of copying them]' \
     '(--only-write-batch)--write-batch=[write a batched update to the specified file]:file:_files' \
     '(--write-batch)--only-write-batch=[like --write-batch but w/o updating destination]:file:_files' \
     '--protocol=[force an older protocol version to be used]:number' \
-    '--info=[fine-grained informational verbosity]:comma-separated list' \
-    '--debug=[fine-grained debug verbosity]:comma-separated list' \
+    '--info=[fine-grained informational verbosity]:info option:_rsync_info' \
+    '--debug=[fine-grained debug verbosity]:debug option:_rsync_debug' \
     '--msgs2stderr[special output handling for debugging]' \
     '--munge-links[munge symlinks to make them safer, but unusable]' \
     '--ignore-missing-args[ignore missing source args without error]' \
diff --git a/Completion/Unix/Command/_ruby b/Completion/Unix/Command/_ruby
index 49a1170ef..bb0558fde 100644
--- a/Completion/Unix/Command/_ruby
+++ b/Completion/Unix/Command/_ruby
@@ -35,11 +35,12 @@ opts=(
   '(-n)-p[assume loop like -n but print line also like sed]'
   '-s[enable some switch parsing for switches after script name]'
   '-S[look for the script using PATH environment variable]'
-  '-T-[turn on tainting checks]:taint level:((0\:strings\ from\ streams/environment/ARGV\ are\ tainted 1\:no\ dangerous\ operation\ by\ tainted\ value 2\:process/file\ operations\ prohibited 3\:all\ generated\ objects\ are\ tainted 4\:no\ global\ \(non-tainted\)\ variable\ modification/no\ direct\ output))'
+  '-T-[turn on tainting checks]::taint level [1]:((0\:strings\ from\ streams/environment/ARGV\ are\ tainted 1\:no\ dangerous\ operation\ by\ tainted\ value 2\:process/file\ operations\ prohibited 3\:all\ generated\ objects\ are\ tainted 4\:no\ global\ \(non-tainted\)\ variable\ modification/no\ direct\ output))'
   '(-v --verbose)'{-v,--verbose}'[print version number, then turn on verbose mode]'
   '-x-[strip off text before #!ruby line and perhaps cd to directory]:directory:_files -/'
   '(1 * -)--copyright[print the copyright]'
-  --{en,dis}able-{gems,rubyopt,all}
+  --{en,dis}'able=[enable or disable features]:feature:(gems did_you_mean rubyopt frozen_string_literal all)'
+  \!--{en,dis}able-{gems,rubyopt,all}
   --{external,internal}'-encoding=:charset:->charsets'
   '!'{-y,--yydebug}
   '!--dump=:target:(version copyright usage yydebug syntax parsetree parsetree_with_comment insns)'
@@ -113,7 +114,7 @@ case "$state" in
     charsets=( $(_call_program charsets $RUBY -e 'puts\ Encoding.list' 2>/dev/null) )
     # could also add Encoding.aliases.map(&:first) for aliases
     desc='charset'
-    if [[ $curcontext = *option-E-1 ]]; then
+    if [[ $curcontext = *option-(E|-encoding)-1 ]]; then
       if compset -P '*:'; then
 	desc='internal charset'
       else
diff --git a/Completion/Unix/Command/_screen b/Completion/Unix/Command/_screen
index f4d2ef943..ebc8ba155 100644
--- a/Completion/Unix/Command/_screen
+++ b/Completion/Unix/Command/_screen
@@ -99,7 +99,7 @@ if [[ -n $state ]]; then
       if (( CURRENT == 1 )) && [[ $PREFIX == /dev/* ]]; then
 	  _path_files -g '*(%)'
       elif (( CURRENT == 2 )) && [[ ${words[1]} == /dev/* ]]; then
-	  _baudrate
+	  _baudrates
       elif (( CURRENT > 2 )) && [[ ${words[1]} == /dev/* ]]; then
 	  _message "no more parameters"
       else
diff --git a/Completion/Unix/Command/_script b/Completion/Unix/Command/_script
new file mode 100644
index 000000000..7f682eaa2
--- /dev/null
+++ b/Completion/Unix/Command/_script
@@ -0,0 +1,71 @@
+#compdef script scriptreplay
+
+local args hlp="-h --help -V --version"
+
+if [[ $service = scriptreplay ]]; then
+  _arguments -S -s \
+    "(1 -t --timing $hlp)"{-t+,--timing=}'[specify file containing timing output]:timing file:_files' \
+    "(2 -s --typescript $hlp)"{-s+,--typescript=}'[specify file containing terminal output]:typescript file:_files' \
+    "(3 -d --divisor $hlp)"{-d+,--divisor=}'[speed up replay]:factor' \
+    "(-m --maxdelay $hlp)"{-m+,--maxdelay=}'[set maximum delay between updates]:delay (seconds)' \
+    '(- *)'{-h,--help}'[display help information]' \
+    '(- *)'{-V,--version}'[display version information]' \
+    "(-t --timing $hlp):timing file:_files" \
+    "(-s --typescript $hlp):typescript file:_files" \
+    "(-d --divisor $hlp): :_guard '[0-9.]#' 'timing divisor'"
+  return
+fi
+
+case $OSTYPE in
+  linux*)
+    args=( -S
+      "(-a --append $hlp)"{-a,--append}'[append output]'
+      "(-c --command $hlp)"{-c,--command=}'[run specified command instead of a shell]:command:_cmdstring'
+      "(-e --return $hlp)"{-e,--return}'[return exit status of the child process]'
+      "(-f --flush $hlp)"{-f,--flush}'[flush output after each write]'
+      "($hlp)--force[use output file even when it is a link]"
+      "(-q --quiet $hlp)"{-q,--quiet}'[be quiet]'
+      "(-t --timing $hlp)"{-t-,--timing=-}'[output timing data]::timing file:_files'
+      '(- 1)'{-h,--help}'[display help information]'
+      '(- 1)'{-V,--version}'[display version information]'
+    )
+  ;;
+  darwin*|dragonfly*|netbsd*|freebsd*)
+    args=(
+      '-q[be quiet: suppress display of starting and ending lines]'
+    )
+  ;|
+  netbsd*|freebsd*)
+    args+=(
+      '(-a -r -k)-d[suppress sleeps when playing back a session]'
+      '(-a -r -k -t)-p[play back a recorded session]'
+      '(-d -p)-r[record a session with input, output and timing data]'
+    )
+  ;|
+  netbsd*)
+    args+=(
+      '-f[flush output after each write]'
+      '-c[run specified command instead of a shell]:command:_cmdstring'
+    )
+  ;|
+  freebsd*)
+    args+=(
+      '-f[use filemon(4)]'
+    )
+  ;|
+  darwin*|dragonfly*|freebsd*)
+    args+=(
+      '-t+[specify interval of data flushing]:interval (seconds)'
+      '-k[log keys sent to the program as well as output]'
+      '*:::arguments: _normal'
+    )
+  ;|
+  *)
+    args+=(
+      '(-p -d)-a[append output]'
+    )
+  ;;
+esac
+
+_arguments -s $args \
+  "($hlp):typescript file:_files"
diff --git a/Completion/Unix/Command/_sed b/Completion/Unix/Command/_sed
index cc7a9631d..222798b8a 100644
--- a/Completion/Unix/Command/_sed
+++ b/Completion/Unix/Command/_sed
@@ -23,11 +23,13 @@ elif _pick_variant gnu=GNU unix --version; then
     '(-i --in-place)'{-i-,--in-place=-}$inplace
     '(-l --line-length)'{-l,--line-length=-}'[specify line-wrap length for the l command]'
     '(-r)--posix[disable GNU extensions]'
-    '(-r --regexp-extended)'{-r,--regexp-extended}$extended
+    '(-E -r --regexp-extended)'{-E,-r,--regexp-extended}$extended
     '(-s --separate)'{-s,--separate}'[consider files separately instead of as a combined stream]'
+    '--sandbox[block commands that can affect the system (r/w/W/e)]'
     '(-u --unbuffered)'{-u,--unbuffered}'[disable data buffering]'
+    '(-z --null-data)'{-z,--null-data}'[separate lines by NUL characters]'
     '(- 1 :)--help[print program usage]'
-    '(- 1 :)'{-V,--version}'[print program version]'
+    '(- 1 :)--version[print program version]'
   )
 else
   args=( "${(@)args:#(|\(*\))(|\*)--*}" )
diff --git a/Completion/Unix/Command/_service b/Completion/Unix/Command/_service
index e99ad556f..50e8607bf 100644
--- a/Completion/Unix/Command/_service
+++ b/Completion/Unix/Command/_service
@@ -5,27 +5,34 @@
 # _sub_command happy
 
 # we are interested in init service only
-local ctx="${curcontext}argument-1:"
+local args ctx="${curcontext}argument-1:"
 zstyle -T  ":completion:${ctx}" tag-order && \
   zstyle ":completion:${ctx}" tag-order init
 
-
-if [[ $OSTYPE = freebsd* ]]; then
-  _arguments -s \
-    '(-)-e[show services that are enabled]' \
-    '(-)-l[list all scripts in /etc/rc.d and the local startup directory]' \
-    '(-)-r[show the results of boot time rcorder]' \
-    '(-)-v[verbose]' \
-    ':service name:_services' \
-    '*::service argument:_init_d'
-else
-  _arguments -s \
-    '(-d --debug)'{-d,--debug}'[turn debugging on]' \
-    '(- :)'{-h,--help}'[print usage]' \
-    '(- :)'{-v,--version}'[print version]' \
-    '(-)'{-f,--full-restart}'[restart service]' \
-    '(- :)'{-R,--full-restart-all}'[restart all services]' \
-    '(- :)'{-s,--status-all}'[print status of all services]' \
-    ':service name:_services' \
-    '*::service argument: _init_d'
-fi
+case $OSTYPE in
+  freebsd*|dragonfly*)
+    args=(
+      '(-)-r[show the results of boot time rcorder]'
+      '(-)-R[restart all enabled local services]'
+    )
+  ;&
+  netbsd*)
+    _arguments -s $args \
+      '(-)-e[show services that are enabled]' \
+      '(-)-l[list all scripts in /etc/rc.d and the local startup directory]' \
+      '(-e -R)-v[verbose]' \
+      ':service name:_services' \
+      '*::service argument:_init_d'
+  ;;
+  *)
+    _arguments -s \
+      '(-d --debug)'{-d,--debug}'[turn debugging on]' \
+      '(- :)'{-h,--help}'[print help information]' \
+      '(- :)'{-v,--version}'[print version information]' \
+      '(-)'{-f,--full-restart}'[restart service]' \
+      '(- :)'{-R,--full-restart-all}'[restart all services]' \
+      '(- :)'{-s,--status-all}'[print status of all services]' \
+      ':service name:_services' \
+      '*::service argument: _init_d'
+  ;;
+esac
diff --git a/Completion/Unix/Command/_sisu b/Completion/Unix/Command/_sisu
index 6ca2abfaa..0250581cf 100644
--- a/Completion/Unix/Command/_sisu
+++ b/Completion/Unix/Command/_sisu
@@ -23,7 +23,7 @@ _arguments -s -C -M 'r:|[_-]=* r:|=*' \
   '(--convert --to -C)-I[texinfo output, not maintained]' \
   '(--convert --to -C)-L[print license info]' \
   '(--convert --to -C)-M[maintenance mode, retain intermediate processing files]' \
-  '(--convert --to -C)-m[create intermediate markup file, metaverse, assumed for most output instuctions]' \
+  '(--convert --to -C)-m[create intermediate markup file, metaverse, assumed for most output instructions]' \
   '(--convert --to -C)-N[document content certificate, output document digests]' \
   '(--convert --to -C)-n[skip intermediate markup, skip -m]' \
   '(--convert --to -C)-o[Open Document text format output]' \
diff --git a/Completion/Unix/Command/_sort b/Completion/Unix/Command/_sort
index 1ad57f442..91e9132ad 100644
--- a/Completion/Unix/Command/_sort
+++ b/Completion/Unix/Command/_sort
@@ -4,8 +4,8 @@ local args variant
 local ordering='(-d --dictionary-order -g --general-numeric-sort -M --month-sort -h --human-numeric-sort -n --numeric-sort --sort -V --version-sort --help --version)'
 
 args=(
-  '(-c --check -C)-c[check whether input is sorted; do not sort]'
-  '(-m --merge)'{-m,--merge}'[merge already sorted files; do not sort]'
+  "(-c --check -C)-c[check whether input is sorted; don't sort]"
+  '(-m --merge)'{-m,--merge}"[merge already sorted files; don't sort]"
   '(-o --output)'{-o+,--output=}'[write result to file instead of standard output]:output file:_files'
   \*{-T+,--temporary-directory=}'[specify directory for temporary files]:directory:_directories'
   '(-u --unique)'{-u,--unique}'[with -c, check for strict ordering; without -c, output only the first of an equal run]'
@@ -26,15 +26,19 @@ case $variant in
       '(-s --stable)'{-s,--stable}'[preserve original order of lines with the same key]'
     )
   ;|
+  openbsd*|freebsd*|gnu|solaris2.<11->)
+    args+=(
+      "(-c --check -C)-C[check whether input is sorted silently; don't sort]"
+    )
+  ;|
   openbsd*|freebsd*|gnu)
     args+=(
-      '(-c --check -C)-C[check whether input is sorted; do not sort]'
       '(-z --zero-terminated)'{-z,--zero-terminated}'[end lines with 0 byte, not newline]'
     )
   ;|
   freebsd*|gnu)
     args+=(
-      '(-c --check -C)--check=-[check whether input is sorted; do not sort]::bad line handling:(diagnose-first silent quiet)'
+      "(-c --check -C)--check=-[check whether input is sorted; don't sort]::bad line handling:(diagnose-first silent quiet)"
       "$ordering"{-g,--general-numeric-sort}'[compare according to general numeric value]'
       "$ordering"{-M,--month-sort}"[compare (unknown) < 'JAN' < ... < 'DEC']"
       "$ordering"{-h,--human-numeric-sort}'[compare human readable numbers (e.g., 2K 1G)]'
@@ -46,7 +50,7 @@ case $variant in
       '--batch-size=[maximum inputs to merge]:number'
       '--compress-program=[specify program to compress temporary files with]:program:(gzip bzip2 lzop xz)'
       '--debug[annotate the of the line used to sort]'
-      '--files0-from=[read input files from file]:file:_files'
+      '(*)--files0-from=[read input files from file]:file:_files'
       '(-S --buffer-size)'{-S+,--buffer-size=}'[specify size for main memory buffer]:size'
       '(- *)--help[display help and exit]'
       '(- *)--version[output version information and exit]'
diff --git a/Completion/Unix/Command/_sqsh b/Completion/Unix/Command/_sqsh
index f41ab5bf8..5caf7f981 100644
--- a/Completion/Unix/Command/_sqsh
+++ b/Completion/Unix/Command/_sqsh
@@ -17,7 +17,7 @@ _sybase_server() {
 _arguments : \
     '-A[adjust TDS packet size (512)]:packet size (bytes):(512 1024 1536 2048)' \
     '-B[turn off file buffering on startup]' \
-    '-C[send sql statment to server]:sql' \
+    '-C[send sql statement to server]:sql' \
     '-D[change database context on startup]:database' \
     '-E[replace default editor (vi)]:editor' \
     '-H[set the client hostname]:reported hostname' \
diff --git a/Completion/Unix/Command/_ssh b/Completion/Unix/Command/_ssh
index 1f8f62cac..28d2c38e7 100644
--- a/Completion/Unix/Command/_ssh
+++ b/Completion/Unix/Command/_ssh
@@ -1,12 +1,10 @@
 #compdef ssh slogin=ssh scp ssh-add ssh-agent ssh-keygen sftp ssh-copy-id
 
-# Completions currently based on OpenSSH 7.0 (released on 2015-08-11).
-#
-# TODO: update ssh-keygen (not based on 5.9)
 # TODO: sshd, ssh-keyscan, ssh-keysign
 
 _ssh () {
-  local curcontext="$curcontext" state line expl common tmp cmds suf ret=1
+  local curcontext="$curcontext" state line expl suf ret=1
+  local args common tmp p1 file cmn cmds sdesc
   typeset -A opt_args
 
   common=(
@@ -44,6 +42,7 @@ _ssh () {
       '-g[allow remote hosts to connect to local forwarded ports]' \
       '-G[output configuration and exit]' \
       '-I+[specify smartcard device]:device:_files' \
+      '-J+[connect via a jump host]: :->userhost' \
       '-K[enable GSSAPI-based authentication and forwarding]' \
       '-k[disable forwarding of GSSAPI credentials]' \
       '*-L[specify local port forwarding]:local port forwarding:->forward' \
@@ -84,11 +83,12 @@ _ssh () {
       '-c[identity is subject to confirmation via SSH_ASKPASS]' \
       '-D[delete all identities]' \
       '-d[remove identity]' \
-      '-e[remove keys provided by the PKCS#11 shared library]:library:' \
+      '-E[specify hash algorithm for fingerprints]:algorithm:(md5 sha256)' \
+      '-e[remove keys provided by the PKCS#11 shared library]:library:_files -g "*.(so|dylib)(|.<->)(-.)"' \
       '-k[load plain private keys only and skip certificates]' \
       '-L[lists public key parameters of all identities in the agent]'\
       '-l[list all identities]' \
-      '-s[add keys provided by the PKCS#11 shared library]:library:' \
+      '-s[add keys provided by the PKCS#11 shared library]:library:_files -g "*.(so|dylib)(|.<->)(-.)"' \
       '-t[set maximum lifetime for identity]:maximum lifetime (in seconds or time format):' \
       '-X[unlock the agent]' \
       '-x[lock the agent with a password]' \
@@ -107,32 +107,83 @@ _ssh () {
     return
     ;;
   ssh-keygen)
-    cmds=( -p -i -e -y -c -l -B -D -U )
-    _arguments -s \
-      '-q[silence ssh-keygen]' \
-      "($cmds -P)-b[specify number of bits in key]:bits in key" \
-      "($cmds -P)-t[specify the type of the key to create]:key type:(rsa1 rsa dsa ecdsa ed25519)" \
-      "(${cmds#-p })-N[provide new passphrase]:new passphrase" \
-      "($cmds -b -t)-C[provide new comment]:new comment" \
-      '(-D)-f[key file]:key file:_files' \
-      '('${(j. .)cmds:#-[pc]}' -t -b)-P[provide old passphrase]:old passphrase' \
-      "($cmds -q -b -t -C)-p[change passphrase of private key file]" \
-      "($cmds -q -b -t -N -C -P)-i[import key to OpenSSH format]" \
-      "($cmds -q -b -t -N -C -P)-e[export key to SECSH file format]" \
-      "($cmds -q -b -t -N -C -P)-y[get public key from private key]" \
-      "($cmds -q -b -t -N)-c[change comment in private and public key files]" \
-      "($cmds -q -b -t -N -C -P)-l[show fingerprint of key file]" \
-      "($cmds -q -b -t -N -C -P)-B[show the bubblebabble digest of key]" \
-      "($cmds -q -b -t -N -C -P -f)-D[download key stored in smartcard reader]:reader" \
-      "($cmds -q -b -t -N -C -P)-U[upload key to smartcard reader]:reader"
+    # options can be in any order but use ! to limit those shown for the first argument
+    (( CURRENT == 2 )) && p1='!'
+    args=( '!-z:number' )
+    sdesc='certify keys with CA key'
+    (( $+words[(r)-s] )) && args=( '-z[specify serial number]:serial number' )
+    (( $+words[(r)-[ku]] )) && args=( '-z[specify version number]:version number' ) &&
+        sdesc='specify CA public key file'
+    file=key
+    (( $+words[(r)-[HR]] )) && file=known_hosts
+    (( $+words[(r)-T] )) && file=input
+    if (( $+words[(r)-[kQ]] )); then
+      file=krl
+      args+=( '*:file:_files' )
+    fi
+    cmds=( -p -i -e -y -c -l -B -D -H -R -r -G -T -s -L -A -k -Q ) # basic commands
+    cmn=( -b -P -N -C -m -v ) # options common to many basic commands (except -f which is common to most)
+    cms=( -E -q -t -g -M -S -a -J -j -K -W -I -h -n -O -V -u ) # options specific to one basic command
+    _arguments -s $args \
+      "(${${(@)cmds:#-G}} -P -m ${${(@)cms:#-[MS]}})-b[specify number of bits in key]:bits in key" \
+      "$p1(${${(@)cmds:#-[pc]}} -b -C $cms)-P[provide old passphrase]:old passphrase" \
+      "(${${(@)cmds:#-p}} -m -v ${${(@)cms:#-[qt]}})-N[provide new passphrase]:new passphrase" \
+      "(${${(@)cmds:#-c}} -m -v $cms)-C[provide new comment]:new comment" \
+      "(-D -G -M -S -I -h -n -O -V -A)-f[$file file]:$file file:_files" \
+      "$p1(${${(@)cmds:#-[ie]}})-m[specify conversion format]:format:(PEM PKCS7 RFC4716)" \
+      "(${${(@)cmds:#-[lGT]}} ${${(@)cmn:#-[bv]}} -f)*-v[verbose mode]" \
+      - '(commands)' \
+      "(-b -P -C -v)-p[change passphrase of private key file]" \
+      '(-b -P -N -C -v)-i[import key to OpenSSH format]' \
+      '(-b -P -N -C -v)-e[export key to SECSH file format]' \
+      "($cmn)-y[get public key from private key]" \
+      '(-b -N -m -v)-c[change comment in private and public key files]' \
+      "($cmn)-B[show the bubblebabble digest of key]" \
+      "(-)-D[download key stored in smartcard reader]:reader" \
+      "($cmn)-H[hash names in known_hosts file]" \
+      "($cmn)-R[remove host from known_hosts file]:host:_ssh_hosts" \
+      "($cmn)-L[print the contents of a certificate]" \
+      "(-)-A[generate host keys for all key types]" \
+      "($cmn)-Q[test whether keys have been revoked in a KRL]" \
+      - finger \
+      "($cmn)-l[show fingerprint of key file]" \
+      "$p1($cmn)-E[specify hash algorithm for displayed fingerprints]:hash algorithim:(md5 sha256)" \
+      - create \
+      '(-P -m)-q[silence ssh-keygen]' \
+      "(-P -m)-t[specify the type of the key to create]:key type:(rsa1 rsa dsa ecdsa ed25519)" \
+      - dns \
+      "($cmn)-r[print DNS resource record]:hostname:_hosts" \
+      "$p1($cmn)-g[use generic DNS format]" \
+      - primes \
+      "(-P -N -C -m -f)-G[generate candidates for DH-GEX moduli]" \
+      "$p1(-P -N -C -m -f)-M[specify amount of memory to use for generating DH-GEX moduli]:memory (MB)" \
+      "$p1(-P -N -C -m -f)-S[specify start point]:start point (hex)" \
+      - screen \
+      "(${${(@)cmn:#-v}})-T[screen candidates for DH-GEX moduli]:output file:_files" \
+      "${p1}(${${(@)cmn:#-v}})-a[specify number of rounds]:rounds" \
+      "${p1}(${${(@)cmn:#-v}})-J[exit after screening specified number of lines]" \
+      "${p1}(${${(@)cmn:#-v}})-j[start screening at the specified line number]:line number" \
+      "${p1}(${${(@)cmn:#-v}})-K[write the last line processed to file]:file:_files" \
+      "${p1}(${${(@)cmn:#-v}})-W[specify desired generator]:generator" \
+      - certify \
+      "($cmn)-s[$sdesc]:CA key:_files" \
+      "$p1($cmn -f -u)-I[specify key identifier to include in certificate]:key id" \
+      "$p1($cmn -f -u)-h[generate host certificate instead of a user certificate]" \
+      "$p1($cmn -f -u)-n[specify user/host principal names to include in certificate]:principals" \
+      "$p1($cmn -f -u)-O[specify a certificate option]:option" \
+      "$p1($cmn -f -u)-V[specify certificate validity interval]:interval" \
+      "($cmn -I -h -n -O -V)-k[generate a KRL file]" \
+      "$p1($cmn -I -h -n -O -V)-u[update a KRL]"
     return
     ;;
   sftp)
     _arguments -C -s \
-      '-B+[specify buffer size]:buffer size in bytes (default\: 32768):' \
+      '-a[attempt to continue interrupted transfers]' \
+      '-B+[specify buffer size]:buffer size (bytes) [32768]' \
       '-b+[specify batch file to read]:batch file:_files' \
-      '-D[connect directly to a local sftp server]:sftp server path:' \
-      '-R[specify number of outstanding requests]:number of requests (default\: 64):' \
+      '-D[connect directly to a local sftp server]:sftp server path' \
+      '-f[request that files be flushed immediately after transfer]' \
+      '-R[specify number of outstanding requests]:number of requests [64]' \
       '-s[SSH2 subsystem or path to sftp server on the remote host]' \
       '1:file:->rfile' '*:file:->file' "$common[@]" "$common_transfer[@]" && ret=0
     ;;
@@ -149,7 +200,7 @@ _ssh () {
 
     case "$lstate" in
     option)
-      if compset -P '*='; then
+      if compset -P 1 '*='; then
         case "${IPREFIX#-o}" in
           (#i)(ciphers|macs|kexalgorithms|hostkeyalgorithms|pubkeyacceptedkeytypes|hostbasedkeytypes)=)
           if ! compset -P +; then
@@ -315,7 +366,7 @@ _ssh () {
           ;;
         (#i)pkcs11provider=*)
           _description files expl 'PKCS#11 shared library'
-          _files -g '*.so' "$expl[@]" && ret=0
+          _files -g '*.(so|dylib)(|.<->)(-.)' "$expl[@]" && ret=0
           ;;
         (#i)port=*)
           _message -e 'port number on remote host'
@@ -331,10 +382,7 @@ _ssh () {
               '2' && ret=0
           ;;
         (#i)proxycommand=*)
-          compset -q
-          shift 1 words
-          (( CURRENT-- ))
-          _normal && ret=0
+          _cmdstring && ret=0
           ;;
         (#i)rekeylimit=*)
           _message -e 'maximum number of bytes transmitted before renegotiating session key'
@@ -398,6 +446,7 @@ _ssh () {
         # old options are after the empty "\"-line
         _wanted values expl 'configure file option' \
             compadd -M 'm:{a-z}={A-Z}' -q -S '=' - \
+	        AddKeysToAgent \
                 AddressFamily \
                 BatchMode \
                 BindAddress \
@@ -406,6 +455,7 @@ _ssh () {
                 CanonicalizeHostname \
                 CanonicalizeMaxDots \
                 CanonicalizePermittedCNAMEs \
+		CertificateFile \
                 ChallengeResponseAuthentication \
                 CheckHostIP \
                 Cipher \
@@ -440,6 +490,7 @@ _ssh () {
                 HostKeyAlias \
                 HostName \
                 IdentitiesOnly \
+		IdentityAgent \
                 IdentityFile \
                 IgnoreUnknown \
                 IPQoS \
@@ -459,6 +510,7 @@ _ssh () {
                 PreferredAuthentications \
                 Protocol \
                 ProxyCommand \
+		ProxyJump \
                 ProxyUseFdpass \
                 PubkeyAcceptedKeyTypes \
                 PubkeyAuthentication \
@@ -553,6 +605,7 @@ _ssh () {
           hmac-sha2-256-96 hmac-sha2-512 hmac-sha2-512-96 && ret=0
       ;;
     command)
+      local -a _comp_priv_prefix
       shift 1 words
       (( CURRENT-- ))
       _normal
@@ -575,9 +628,9 @@ _ssh () {
       fi
       ;;
     file)
-      if compset -P '[^./][^/]#:'; then
+      if compset -P 1 '[^./][^/]#:'; then
         _remote_files -- ssh ${(kv)~opt_args[(I)-[FP1246]]/-P/-p} && ret=0
-      elif compset -P '*@'; then
+      elif compset -P 1 '*@'; then
         suf=( -S '' )
         compset -S ':*' || suf=( -r: -S: )
         _wanted hosts expl 'remote host name' _ssh_hosts $suf && ret=0
@@ -589,9 +642,9 @@ _ssh () {
       fi
       ;;
     rfile)
-      if compset -P '*:'; then
+      if compset -P 1 '*:'; then
         _remote_files -- ssh && ret=0
-      elif compset -P '*@'; then
+      elif compset -P 1 '*@'; then
         _wanted hosts expl host _ssh_hosts -r: -S: && ret=0
       else
         _alternative \
@@ -627,17 +680,23 @@ _ssh_hosts () {
     config="$HOME/.ssh/config"
   fi
   if [[ -r $config ]]; then
-    local key hosts host
-    while IFS=$'=\t ' read -r key hosts; do
-      if [[ "$key" == (#i)host ]]; then
-         for host in ${(z)hosts}; do
-            case $host in
-            (*[*?]*) ;;
-            (*) config_hosts+=("$host") ;;
-            esac
-         done
-      fi
-    done < "$config"
+    local key line host
+    local -a lines=("${(@f)$(<"$config")}") 2>/dev/null
+    while (($#lines)); do
+      IFS=$'=\t ' read -r key line <<<"${lines[1]}"
+      case "$key" in
+      ((#i)include)
+        lines[1]=("${(@f)$(cd $HOME/.ssh; cat ${(z)~line})}") 2>/dev/null;;
+      ((#i)host(|name))
+        for host in ${(z)line}; do
+          case $host in
+          (*[*?]*) ;;
+          (*) config_hosts+=("$host") ;;
+          esac
+        done ;&
+      (*) shift lines;;
+      esac
+    done
     if (( ${#config_hosts} )); then
       _wanted hosts expl 'remote host name' \
         compadd -M 'm:{a-zA-Z}={A-Za-z} r:|.=* r:|=*' "$@" $config_hosts
diff --git a/Completion/Unix/Command/_stgit b/Completion/Unix/Command/_stgit
index f313f651a..e31af460a 100644
--- a/Completion/Unix/Command/_stgit
+++ b/Completion/Unix/Command/_stgit
@@ -4,7 +4,7 @@ typeset -a subcmds
 
 subcmds=( ${${${(M)${(f)"$(stg help 2> /dev/null)"}## *}#  }/#(#b)([^[:space:]]##)[[:space:]]##(*)/$match[1]:$match[2]} )
 
-local curcontext="$curcontext"
+local curcontext="$curcontext" expl
 local subcmd
 local ret=1
 
@@ -18,17 +18,17 @@ else
 
   case $subcmd in
     (push)
-      _wanted -V "unapplied patches" expl "patch" \
+      _wanted -V unapplied-patches expl "patch" \
        	compadd ${${(M)${(f)"$(stg series 2> /dev/null)"}##- *}#- } \
 		&& ret=0
     ;;
     (pop)
-      _wanted -V "applied patches" expl "patch" \
+      _wanted -V applied-patches expl "patch" \
 	compadd ${${(M)${(f)"$(stg series 2> /dev/null)"}##[+>] *}#[+>] } \
 		&& ret=0
     ;;
-    (edit|files|goto|rename|log|float|delete|sink|mail|sync|show|pick|hide)
-      _wanted -V "patches" expl "patch" \
+    (edit|files|goto|rename|log|float|delete|sink|mail|sync|show|pick|hide|squash)
+      _wanted -V patches expl "patch" \
 	compadd $(stg series --noprefix 2> /dev/null) \
 		&& ret=0
     ;;
@@ -36,7 +36,7 @@ else
       last_word="$words[$CURRENT-1]"
       refresh_patch_options=( -p --patch )
       if [[ -n ${refresh_patch_options[(r)$last_word]} ]]; then
-	_wanted -V "applied patches" expl "patch" \
+	_wanted -V applied-patches expl "patch" \
 	  compadd ${${(M)${(f)"$(stg series 2> /dev/null)"}##[+>] *}#[+>] } \
 		  && ret=0
       else
diff --git a/Completion/Unix/Command/_strip b/Completion/Unix/Command/_strip
index 726d87c2b..f244b0ace 100644
--- a/Completion/Unix/Command/_strip
+++ b/Completion/Unix/Command/_strip
@@ -16,11 +16,12 @@ if _pick_variant gnu=GNU solaris --version; then
   fi
   args=(
     '(-F --target)'{-F+,--target=}'[object code format to use]:bfd name:->bfdnames'
-    '--help[display usage information and exit]'
-    '--info[display list of architectures and object formats]'
+    '(-)--help[display usage information]'
+    '(-)--info[display list of architectures and object formats]'
     '(-I --input-target)'{-I+,--input-target=}'[object code format of input]:bfd name:->bfdnames'
     '(-O --output-target)'{-I+,--output-target=}'[object code format of output]:bfd name:->bfdnames'
     '*'{-R+,--remove-section=}'[remove given sections]:section name'
+    '--remove-relocations=[remove relocations from specified section]:section'
     '(-s --strip-all)'{-s,--strip-all}'[remove all symbols]'
     '(-g -S -d --strip-debug)'{-g,-S,-d,--strip-debug}'[remove debugging symbols]'
     '--strip-unneeded[remove symbols not needed for relocation processing]'
@@ -33,7 +34,7 @@ if _pick_variant gnu=GNU solaris --version; then
     '(-X --discard-locals)'{-X,--discard-locals}'[remove compiler-generated local symbols]'
     '--keep-file-symbols[retain symbols specifying source file names]'
     '--only-keep-debug[remove everything except debugging information]'
-    '(-V --version)'{-V,--version}'[display version information and exit]'
+    '(-)'{-V,--version}'[display version information and exit]'
     '(-v --verbose)'{-v,--verbose}'[list all object files modified or members of archives]')
 else
   args=(
diff --git a/Completion/Unix/Command/_su b/Completion/Unix/Command/_su
index 057a41371..900905632 100644
--- a/Completion/Unix/Command/_su
+++ b/Completion/Unix/Command/_su
@@ -2,79 +2,86 @@
 
 local -A opt_args
 local -a args context state line expl
-local shell=${words[(i)(-s|--shell=*)]} first='1:user name:_users'
-local usr=root
+local first='(-)${norm}:user name:_users'
+integer norm=1 strip
+local shell usr
 
-if _pick_variant gnu="Free Software Foundation" unix --version; then
-  args=(
-    '(--command)-c[pass command to shell]:command string:->command'
-    '(-c)--command=-[pass command to shell]:command string:->command'
-    '-f[pass -f to shell (csh)]'
-    '(--login)-l[use a login shell]'
-    '(-l)--login[use a login shell]'
-    '(-p --preserve-environment)-m[do not reset environment]'
-    '(-m --preserve-environment)-p[do not reset environment]'
-    '(-m -p)--preserve-environment[do not reset environment]'
-    '(--shell)-s[run the specified shell]:shell:->shell'
-    '(-s)--shell=-[run the specified shell]:shell:->shell'
-  )
-else
-  args=(
-    '-l[use a login shell]'
-    '-s[run the specified shell]:shell:->shell'
-  )
-  case $OSTYPE in
-  freebsd*)
-    args=(
+(( $words[(i)-(l|-login)] < CURRENT )) || args=( '-[use a login shell]' )
+case $OSTYPE in
+  linux*)
+    args=( -S $args
+      '(-c --command --session-command *)'{-c,--command=}'[pass command to shell]:command string:_cmdstring'
+      "(-c --command *)--session-command=[pass command to shell and don't create a new session]:command string:_cmdstring"
+      '(--fast -f)'{-f,--fast}'[pass -f to shell]'
+      '(-l --login -m -p --preserve-environment)'{-l,--login}'[use a login shell]'
+      '(-l --login -m -p --preserve-environment)'{-m,-p,--preserve-environment}"[don't reset environment]"
+      '(-s --shell)'{-s,--shell=}'[run the specified shell]:shell:->shells'
+      '(-)--help[display help information]'
+      '(-)--version[display version information]'
+    )
+    (( EUID )) || args+=(
+      '(-g --group)'{-g,--group=}'[specify primary group]:group:_groups'
+      \*{-G,--supp-group=}'[specify supplemental group]:group:_groups'
+    )
+    first="(--help --version)${first#???}"
+  ;;
+  *bsd*|darwin*|dragonfly*)
+    args+=(
       '-c[use settings from specified login class]:class'
       '-f[if the invoked shell is csh, prevent it from reading .cshrc]'
-      '-l[use a login shell]'
-      '-m[do not reset environment]'
-      '-s[set the MAC label]'
+      '(-m)-l[use a login shell]'
+      "(-l)-m[don't reset environment]"
+    )
+  ;|
+  freebsd*) args+=( '-s[set the MAC label]' ) ;;
+  openbsd*)
+    args+=(
+      '(-K)-a[specify authentication type]:authentication type'
+      '(-a)-K[shorthand for -a passwd]'
+      '-s[run the specified shell]:shell:->shells'
+      '-L[loop until login succeeds]'
     )
   ;;
-  *) args+=( '-c[pass command to shell]:command string:->command' ) ;;
-  esac
-fi
+  netbsd*)
+    args+=(
+      '-d[use a login shell but retain current directory]'
+      "-K[don't use Kerberos]"
+    )
+  ;;
+esac
 
-if [[ $#words -ge 2 && $words[2] != -* && CURRENT -ne 2 ]]; then
-    usr=$words[2]
-    first=
+if (( $words[(i)-] < CURRENT )); then
+  args=( ${args:#*-(-login|l|)\[*} '1:-' )
+  norm=2
 fi
 
-[[ $words[shell] == -s ]] && ((shell++))
+_arguments $args ${(e)first} "*:shell arguments:= ->rest" && return
 
-if [[ CURRENT -ne shell && -n ${words[shell]} ]]; then
-    shell=${words[shell]#*=}
+usr=${line[norm]/--/root}
+if (( $#opt_args[(i)-(s|-shell)] )); then
+  shell=${(v)opt_args[(i)-(s|-shell)]}
+elif (( ${+commands[getent]} )); then
+  shell="${$(_call_program shells getent passwd $usr)##*:}"
 else
-    shell="${${(M@)${(@f)$(</etc/passwd)}:#$usr*}##*:}"
+  shell="${${(M@)${(@f)$(</etc/passwd)}:#$usr*}##*:}"
 fi
 
-[[ -z $first ]] && compset -n 2
-
-_arguments : $args[@] $first "*:${shell:t} arguments:->rest" && return
-
 case $state in
-    (command)
-        compset -q
-        _normal
-        return
-        ;;
-    (shell)
-        _wanted -C $context shells expl shell compadd ${(f)^"$(</etc/shells)"}(N)
-        return
-        ;;
-    (rest)
-        if [[ -z $shell || $shell = */(nologin|false) ]]; then
-            _arguments "-s[run the specified shell, $usr has no shell]" ||
-                _message "-s option required, $usr has no shell"
-            compstate[insert]=
-        else
-            # Something wrong here: doubles the file listing sometimes
-            _dispatch ${service}:${context} $shell $shell:t -default-
-            return
-        fi
-        ;;
+  shells)
+    _wanted -C $context shells expl shell compadd ${(f)^"$(</etc/shells)"}(N)
+    return
+  ;;
+  rest)
+    if [[ -z $shell || $shell = */(nologin|false) ]]; then
+      _message "-s option required, $usr has no shell"
+    else
+      (( strip = $#words - $#line + norm ))
+      (( CURRENT -= strip - 1 ))
+      words[2,strip]=()
+      _dispatch ${service}:${context} $shell $shell:t -default-
+      return
+    fi
+  ;;
 esac
 
 return 1
diff --git a/Completion/Unix/Command/_subversion b/Completion/Unix/Command/_subversion
index 3cc9e8a0b..640c88950 100644
--- a/Completion/Unix/Command/_subversion
+++ b/Completion/Unix/Command/_subversion
@@ -65,30 +65,33 @@ _svn () {
 	    ${=${${${(M)${(f)"$(_comp_locale; _call_program options svn help $cmd)"#(*Valid options:|(#e))}:#* :*}%% #:*}/ (arg|ARG)/:arg:}/(#b)(-##)([[:alpha:]]##) \[--([a-z-]##)\](:arg:)#/(--$match[3])$match[1]$match[2]$match[4] ($match[1]$match[2])--$match[3]$match[4]}
 	  )
           while (( idx=$args[(I)*--accept:arg:] )); do
-            args[(I)*--accept:arg:]=( --accept':automatic conflict resolution action:((working\:working base\:base '"`for i j in p postpone mc mine-conflict tc theirs-conflict mf mine-full tf theirs-full e edit l launch; do print -rn $i\\\\:$j $j\\\\:$j ""; done `"'))' )
+            args[(I)*--accept:arg:]=( --accept'=:automatic conflict resolution action:((working\:working base\:base '"`for i j in p postpone mc mine-conflict tc theirs-conflict mf mine-full tf theirs-full e edit l launch; do print -rn $i\\\\:$j $j\\\\:$j ""; done `"'))' )
           done
           while (( idx=$args[(I)*--c(l|hangelist):arg:] )); do
-            args[(I)*--c(l|hangelist):arg:]=( \*{--cl,--changelist}':change list:_svn_changelists' )
+            args[(I)*--c(l|hangelist):arg:]=( \*{--cl,--changelist}'=:change list:_svn_changelists' )
           done
           while (( idx=$args[(I)*--config-dir:arg:] )); do
-            args[(I)*--config-dir:arg:]=( --config-dir':config dir:_directories' )
+            args[(I)*--config-dir:arg:]=( --config-dir'=:config dir:_directories' )
           done
           while (( idx=$args[(I)*--depth:arg:] )); do
-            args[(I)*--depth:arg:]=( --depth':operation depth (how far to recurse):(empty files immediates infinity)' )
+            args[(I)*--depth:arg:]=( --depth'=:operation depth (how far to recurse):(empty files immediates infinity)' )
           done
           while (( idx=$args[(I)*(-F|--file):arg:] )); do
-            args[(I)*(-F|--file):arg:]=( '(-F --file)'{-F,--file}':log message file:_files' )
+            args[(I)*(-F|--file):arg:]=( '(-F --file)'{-F+,--file=}':log message file:_files' )
           done
           while (( idx=$args[(I)*--set-depth:arg:] )); do
-            args[(I)*--set-depth:arg:]=( --set-depth'[make working copy deeper or shallower]:new depth:(exclude empty files immediates infinity)' )
+            args[(I)*--set-depth:arg:]=( --set-depth'=[make working copy deeper or shallower]:new depth:(exclude empty files immediates infinity)' )
           done
           while (( idx=$args[(I)*--trust-server-cert-failures:arg:] )); do
-            args[(I)*--trust-server-cert-failures:arg:]=( --trust-server-cert-failures':failures:_values -s , "certificate failures to ignore" "unknown-ca[unknown authority]" "cn-mismatch[hostname mismatch]" "expired[certificate expired]" "not-yet-valid[certificate not yet valid]" "other[all other failures]"' )
+            args[(I)*--trust-server-cert-failures:arg:]=( --trust-server-cert-failures'=:failures:_values -s , "certificate failures to ignore" "unknown-ca[unknown authority]" "cn-mismatch[hostname mismatch]" "expired[certificate expired]" "not-yet-valid[certificate not yet valid]" "other[all other failures]"' )
           done
           while (( idx=$args[(I)*--show-item:arg:] )); do
             # (q) to quote the parentheses in the value
-            args[(I)*--show-item:arg:]=( --show-item':item key:(('"`for i j in ${(kv)show_item_keys}; do print -rn - $i\\\\:"${(q)j}" ""; done`"'))' )
+            args[(I)*--show-item:arg:]=( --show-item'=:item key:(('"`for i j in ${(kv)show_item_keys}; do print -rn - $i\\\\:"${(q)j}" ""; done`"'))' )
           done
+          # All other options get {-x+,--long-x=}
+          args=( ${args/(#b)(--[A-Za-z0-9-]##):arg:/$match[1]=:arg:} )
+          args=( ${args/(#b)([^=]):arg:/$match[1]+:arg:} )
 	  _store_cache svn-${cmd}-args args
 	fi
 
@@ -132,7 +135,7 @@ _svn () {
             )
           ;;
           (mergeinfo)
-            args[(r)--show-revs:arg:]=( '--show-revs=:revisions:(merged eligible)' )
+            args[(r)--show-revs=:arg:]=( '--show-revs=:revisions:(merged eligible)' )
           ;;
 	  (propget|propedit|propdel)
 	    args+=(
@@ -183,6 +186,9 @@ _svn () {
 
 _svnadmin () {
   local curcontext="$curcontext" state line ret=1
+  integer NORMARG
+  local context state_descr
+  typeset -A opt_args
 
   _arguments -C \
     '(-)--help[print help information]' \
@@ -212,13 +218,53 @@ _svnadmin () {
         args=(
           ${=${${${(M)${(f)"$(_comp_locale; _call_program options svnadmin help $cmd)"#(*Valid options:|(#e))}:#* :*}%% #:*}/ (arg|ARG)/:arg:}/(#b)-([[:alpha:]]) \[--([a-z-]##)\](:arg:)#/(--$match[2])-$match[1]$match[3] (-$match[1])--$match[2]$match[3]}
         )
+        # All options get {-x+,--long-x=}
+        args=( ${args/(#b)(--[A-Za-z0-9-]##):arg:/$match[1]=:arg:} )
+        args=( ${args/(#b)([^=]):arg:/$match[1]+:arg:} )
         if [[ $usage == *REPOS_PATH* ]]; then
-          args+=( ":path:_files -/" )
+          args+=( ":repository path:_files -/" )
+          case $cmd in
+            (freeze)
+              args+=( "*:arguments:->normal" )
+              ;;
+            (hotcopy)
+              args+=( ":new repository:_files -/" )
+              ;;
+            (setlog)
+              args+=( ": :_files" )
+              ;;
+            (setrevprop)
+              args+=( ":property name" ":property value file:_files" )
+              ;;
+            (delrevprop)
+              args+=( ":property name" )
+              ;;
+          esac
         elif [[ $cmd = help ]]; then
           args+=( "*:subcommand:_svnadmin_commands" )
         fi
 
-        _arguments "$args[@]" && ret=0
+        _arguments -n -s -S : "$args[@]" && ret=0
+
+        case $state in
+          # Test cases:
+          #   svnadmin freeze . rsync --<TAB> offers --file
+          #   svnadmin freeze -- . rsync -<TAB> offers rsync's options
+          #
+          # Note: the NORMARG calculations here include one positional argument
+          # (the '.') before the command.
+          (normal)
+            if (( ${words[(i)--]} < CURRENT )); then
+              words[1,NORMARG]=()
+              (( CURRENT -= NORMARG ))
+              _normal && ret=0
+            elif (( NORMARG+1 == CURRENT )); then
+              # ### don't allow --options in this case
+              # TODO: this should just use '_normal -F "(-*)"', but _normal ignores its arguments.
+              _command_names -e && ret=0
+            fi
+            ;;
+        esac
       else
         _message "unknown svnadmin command: $words[1]"
       fi
diff --git a/Completion/Unix/Command/_sudo b/Completion/Unix/Command/_sudo
index 63ac37f62..aa7a1a498 100644
--- a/Completion/Unix/Command/_sudo
+++ b/Completion/Unix/Command/_sudo
@@ -2,8 +2,9 @@
 
 setopt localoptions extended_glob
 
-local environ e
+local environ e cmd
 local -a args
+local -a _comp_priv_prefix
 
 zstyle -a ":completion:${curcontext}:" environ environ
 
@@ -39,6 +40,7 @@ args=(
 if [[ $service = sudoedit ]] || (( $words[(i)-e] < $words[(i)^(*sudo|-[^-]*)] ))  ; then
   args=( -A "-*" $args '!(-V --version -h --help)-e' '*:file:_files' )
 else
+  cmd="$words[1]"
   args+=(
     '(-e --edit 1 *)'{-e,--edit}'[edit files instead of running a command]' \
     '(-s --shell)'{-s,--shell}'[run shell as the target user; a command may also be specified]' \
@@ -48,7 +50,7 @@ else
     '(-H --set-home -i --login -s --shell -e --edit)'{-H,--set-home}"[set HOME variable to target user's home dir]" \
     '(-P --preserve-groups -i -login -s --shell -e --edit)'{-P,--preserve-groups}"[preserve group vector instead of setting to target's]" \
     '(-)1:command: _command_names -e'
-    '*::arguments: _normal'
+    '*::arguments:{ _comp_priv_prefix=( $cmd -n ${(kv)opt_args[(I)(-[ugHEP]|--(user|group|set-home|preserve-env|preserve-groups))]} ) ; _normal }'
   )
 fi
 
diff --git a/Completion/Unix/Command/_surfraw b/Completion/Unix/Command/_surfraw
index 0307555a9..d9df4170d 100644
--- a/Completion/Unix/Command/_surfraw
+++ b/Completion/Unix/Command/_surfraw
@@ -118,7 +118,7 @@ case $state in
       deblists)
         _arguments $args \
           '-shelp[go to search help page]' \
-          '-results=-[specifiy number of results to return]:number' \
+          '-results=-[specify number of results to return]:number' \
           '-dates=-[quarters to search]:date' \
           '-case[use case sensitive search]' \
           '-errors=-[specify errors allowed]:errors:(0 1 2 best)' \
diff --git a/Completion/Unix/Command/_swaks b/Completion/Unix/Command/_swaks
new file mode 100644
index 000000000..a0ffb53ad
--- /dev/null
+++ b/Completion/Unix/Command/_swaks
@@ -0,0 +1,40 @@
+#compdef swaks
+
+_arguments \
+  '(-s --server -p --port)'{-s+,--server=}'[target host[:port\]]:host[\:port]:_hosts' \
+  '(-p --port)'{-p+,--port=}'[target port number]:port number:(25 465 587)' \
+  '--copy-routing[derive target host:port from email address domain part]:email address domain part' \
+  '(-t --to)'{-t+,--to=}':envelope recipient(s):_sequence _email_addresses -c' \
+  '(-f --from)'{-f+,--from=}':envelope sender:_email_addresses -c' \
+  '(--ehlo --lhlo -h --helo)'{-h+,--ehlo,--helo,--lhlo}':HELO string:_hosts' \
+  '(-q --quit-after)'{-q+,--quit-after=}'[stop transaction early]:stop point:((
+      CONNECT\:banner BANNER\:banner
+      FIRST-HELO\:first\ HELO FIRST-EHLO\:first\ HELO
+      XCLIENT\:XCLIENT
+      TLS\:TLS\ negotiation
+      HELO\:second\ HELO EHLO\:second\ HELO
+      AUTH\:authentication
+      MAIL\:MAIL\ FROM FROM\:MAIL\ FROM
+      RCPT\:RCPT\ TO TO\:RCPT\ TO
+      ))' \
+  '--protocol=:protocol variant:((
+      SMTP\:HELO\ 25
+      SSMTP\:EHLO\ 465
+      SSMTPA\:EHLO\ 465\ authenticated
+      SMTPS\:HELO\ 465
+      ESMTP\:EHLO\ 25
+      ESMTPA\:EHLO\ 25\ authenticated
+      ESMTPS\:EHLO\ STARTTLS\ 25
+      ESMTPSA\:EHLO\ STARTTLS\ 25\ authenticated
+      ))' \
+  '-tls[TLS required]' \
+  '(-tlsos --tls-optional-strict)'{-tlsos,--tls-optional-strict}'[TLS iff offered by target]' \
+  '(-tlsc --tls-on-connect)'{-tlsc,--tls-on-connect}'[TLS on connect (port 465)]' \
+  '(-tlsp --tls-protocol)'{-tlsp,--tls-protocol=}':TLS protocol:(sslv2 sslv3 tlsv1 tlsv1_1 tlsv1_2)' \
+  '-tls-cipher:OpenSSL cipher string: ' \
+  '--tls-verify[verify TLS certificates]' \
+  '--tls-ca-path=:OpenSSL CAfile or CAdir:_files' \
+  '--tls-get-peer-cert=-:file to write (omit for STDOUT)' \
+  '(-d --data)'{-d+,--data=}'[specify DATA payload]:filename (or string with tokens):_files' \
+  '*'{-ah,--add-header=}'[add headers]:<Header>\: Value' \
+  '*'{-h,--header=}'[replace headers]:<Header>\: Value'
diff --git a/Completion/Unix/Command/_swift b/Completion/Unix/Command/_swift
new file mode 100644
index 000000000..6776f0dea
--- /dev/null
+++ b/Completion/Unix/Command/_swift
@@ -0,0 +1,123 @@
+#compdef swift swiftc
+
+# swift(1) and swiftc(1) are part of the Swift programming language.
+# https://swift.org/
+
+local -a common_options swiftc_mode_options swiftc_additional_options
+local -A swiftc_modes
+
+common_options=(
+  '-assert-config[specify the assert_configuration replacement]:config:(Debug Release Unchecked DisableReplacement)'
+  '*-D[marks a conditional compilation flag as true]:flag:'
+  '*-framework[specifies a framework which should be linked against]:framework:'
+  '*-F[add directory to framework search path]:path:_files -/'
+  '(-gnone)-gdwarf-types[emit full DWARF type info]'
+  '(-gnone)-gline-tables-only[emit minimal debug info for backtraces only]'
+  '(-gdwarf-types -gline-tables-only -g)-gnone[do not emit debug info]'
+  '(-gnone)-g[emit debug info]'
+  '(- : *)-help[display available options]'
+  '-index-store-path[store indexing data to the specified path]:directory:_files -/'
+  '*-I[add directory to the import search path]:path:_files -/'
+  '-j[number of commands to execute in parallel]:count:'
+  '*-L[add directory to library link search path]:path:_files -/'
+  '*-l-[specifies a library which should be linked against]:library:'
+  '-module-cache-path[specifies the Clang module cache path]:path:_files -/'
+  '-module-link-name[library to link against when using this module]:name:'
+  '-module-name[name of the module to build]:name:'
+  '-nostdimport[do not search the standard library import path for modules]'
+  '-num-threads[enable multi-threading and specify number of threads]:count:'
+  '(-Ounchecked -O)-Onone[compile without any optimization]'
+  '(-Onone)-Ounchecked[compile with optimizations and remove runtime safety checks]'
+  '(-Onone)-O[compile with optimizations]'
+  '-sdk[compile against SDK]:sdk:_files -/'
+  '-static-stdlib[statically link the Swift standard library]'
+  '-suppress-warnings[suppress all warnings]'
+  '-target-cpu[generate code for a particular CPU variant]:cpu'
+  '-target[generate code for the given target]:target'
+  '-use-ld=-[specifies the linker to be used]:linker:_files -/'
+  '(- : *)-version[print version information and exit]'
+  '-v[show commands to run and use verbose output]'
+  '-warnings-as-errors[treat warnings as errors]'
+  '*-Xcc[pass argument to the C/C++/Objective-C compiler]:arg:'
+  '*-Xlinker[specifies an option which should be passed to the linker]:option:'
+)
+
+swiftc_modes=(
+  -dump-ast        'parse and type-check input file(s) and dump AST(s)'
+  -dump-parse      'parse input file(s) and dump AST(s)'
+  -dump-type-refinement-contexts
+                   'type-check input file(s) and dump type refinement contexts(s)'
+  -emit-assembly   'emit assembly file(s) (-S)'
+  -emit-bc         'emit LLVM BC file(s)'
+  -emit-executable 'emit a linked executable'
+  -emit-ir         'emit LLVM IR file(s)'
+  -emit-library    'emit a linked library'
+  -emit-object     'emit object file(s) (-c)'
+  -emit-sibgen     'emit serialized AST + raw SIL file(s)'
+  -emit-sib        'emit serialized AST + canonical SIL file(s)'
+  -emit-silgen     'emit raw SIL file(s)'
+  -emit-sil        'emit canonical SIL file(s)'
+  -parse           'parse input file(s)'
+  -print-ast       'parse and type-check input file(s) and pretty print AST(s)'
+)
+local mode
+for mode in ${(k)swiftc_modes}; do
+  # Mode options are mutually exclusive
+  swiftc_mode_options+=("(${(k)swiftc_modes})${mode}[$swiftc_modes[$mode]]")
+done
+
+swiftc_additional_options=(
+  '-application-extension[restrict code to those available for App Extensions]'
+  '-embed-bitcode-marker[embed placeholder LLVM IR data as a marker]'
+  '-embed-bitcode[embed LLVM IR bitcode as data]'
+  '-emit-dependencies[emit basic Make-compatible dependencies files]'
+  '-emit-module-path[emit an importable module to the specified path]:path:_files -/'
+  '-emit-module[emit an importable module]'
+  '-emit-objc-header-path[emit an Objective-C header file to the specified path]:path:_files -/'
+  '-emit-objc-header[emit an Objective-C header file]'
+  '-fixit-all[apply all fixits from diagnostics without any filtering]'
+  '-fixit-code[get compiler fixits as code edits]'
+  '-import-underlying-module[implicitly imports the Objective-C half of a module]'
+  '-output-file-map[a file which specifies the location of outputs]:path:_files'
+  '-o[write output to specified file]:path:_files'
+  '-parse-as-library[parse the input file(s) as libraries, not scripts]'
+  '-parse-sil[parse the input file as SIL code, not Swift source]'
+  '-parseable-output[emit textual output in a parseable format]'
+  '-profile-coverage-mapping[generate coverage data for use with profiled execution counts]'
+  '-profile-generate[generate instrumented code to collect execution counts]'
+  '-sanitize-coverage=-[specify the type of coverage instrumentation for Sanitizers and additional options separated by commas]:type:'
+  '*-sanitize=-[turn on runtime checks for erroneous behavior]:check:'
+  '-save-temps[save intermediate compilation results]'
+  '-serialize-diagnostics[serialize diagnostics in a binary format]'
+  '-tools-directory[look for external executables (ld, clang, binutils) in the specified directory]:directory:_files -/'
+  '-whole-module-optimization[optimize input files together instead of individually]'
+)
+
+case "$words[1]" in
+  swift)
+    # The name swift conflicts with the command with the same name from the
+    # OpenStack project. We delegate completion to _openstack if swift(1) is
+    # detected to be from OpenStack.
+    local variant
+    _pick_variant -r variant openstack=OpenStack swiftlang='Swift compiler' unknown --help
+    case $variant in
+      openstack)
+        _openstack "$@"
+        ;;
+      swiftlang)
+        _arguments \
+            "$common_options[@]" \
+            '*:input:_files'
+        ;;
+      *)
+        _default "$@"
+    esac
+    ;;
+  swiftc)
+    _arguments \
+        "$swiftc_mode_options[@]" \
+        "$common_options[@]" \
+        "$swiftc_additional_options[@]" \
+        '*:input:_files'
+    ;;
+esac
diff --git a/Completion/Unix/Command/_sysctl b/Completion/Unix/Command/_sysctl
index 9d9e8b968..c5bb26eb0 100644
--- a/Completion/Unix/Command/_sysctl
+++ b/Completion/Unix/Command/_sysctl
@@ -1,21 +1,39 @@
 #compdef sysctl
 
+local -a args
+
 case $OSTYPE in
-  *freebsd<5->.*|freebsd4.[4-9]*)
+  freebsd<10->.*)
+    args+=(
+      '-f+[specify file of name/value pairs to process first]:file:_files'
+      '-T[display only variables that are setable via loader]'
+      '-W[display only writable variables that are not statistical]'
+    )
+  ;|
+  dragonfly*|freebsd*)
+    args+=( "-i[silently exit if variable doesn't exist]" )
+  ;|
+  freebsd*)
     local -a sysctlvars
     sysctlvars=( $(_call_program sysctl-variables sysctl -aN) )
-    _arguments -s -A "-*" \
+    _arguments -s -S -A "-*" $args \
+      '(-a -o *)-A[equivalent to -o -a (for compatibility)]' \
       '(*)-a[list all]' \
       '-b[binary output]' \
+      '-d[print the description of the variable instead of its value]' \
+      '(-N -n)-e[separate name and value with =]' \
+      '-h[format output for human readability]' \
       '(-n)-N[show only variable names]' \
       '(-N)-n[show only variable values]' \
       '(-x)-o[show opaques as well (values suppressed)]' \
+      '-q[suppress some warnings]' \
+      '(* -o)-X[equivalent to -x -a (for compatibility)]' \
       '(-o)-x[show opaques as well (entire values)]' \
       '(-a)*:sysctl variable:_multi_parts -i . sysctlvars'
   ;;
-  freebsd[0-4].*|darwin*|dragonfly*)
+  darwin*|dragonfly*)
     : ${(A)_cache_sysctlvars:=${${$(sysctl -A 2>/dev/null):#[^a-z]*}%%:*}}
-    _arguments -s -A "-*" \
+    _arguments -s -A "-*" $args \
       '(-w -X *)-a[list all]' \
       '(-w -X *)-A[show all opaques (values suppressed)]' \
       '(-w)-b[binary output]' \
diff --git a/Completion/Unix/Command/_tail b/Completion/Unix/Command/_tail
index fbe30f13a..4e6422621 100644
--- a/Completion/Unix/Command/_tail
+++ b/Completion/Unix/Command/_tail
@@ -56,7 +56,7 @@ case $state in
     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
+    if compset -P '(-|+|)[0-9]##'; then
       _alternative $mlt $digit && ret=0
     elif [[ -z $PREFIX ]]; then
       _alternative $sign $digit && ret=0
diff --git a/Completion/Unix/Command/_tcpdump b/Completion/Unix/Command/_tcpdump
index edc6c7306..2c1d82226 100644
--- a/Completion/Unix/Command/_tcpdump
+++ b/Completion/Unix/Command/_tcpdump
@@ -2,33 +2,70 @@
 
 typeset -A opt_args
 
-_interfaces () {
-	_wanted interfaces expl 'network interface' \
-		_net_interfaces
-	_values "Pseudo-device that captures on all interfaces" "any"
+_interfaces() {
+  local disp expl sep
+  _description interfaces expl 'network interface'
+  _net_interfaces "$expl[@]"
+  if zstyle -t ":completion:${curcontext}:interfaces" verbose; then
+    zstyle -s ":completion:${curcontext}:interfaces" list-separator sep || sep=--
+    disp=( "any $sep capture on all interfaces" )
+    compadd "$expl[@]" -ld disp any
+  else
+    compadd "$expl[@]" any
+  fi
 }
 
 _esp_secrets () {
-	_values -S : 'ESP algorithm' \
-		'des-cbc:secret' \
-		'3des-cbc:secret' \
-		'blowfish-cbc:secret' \
-		'rc3-cbc:secret' \
-		'cast128-cbc:secret' \
-		none
+  if [[ $OSTYPE = openbsd* ]]; then
+    _values -S : 'ESP algorithm' \
+      aes128:secret \
+      aes128-hmac96:secret \
+      blowfish:secret \
+      blowfish-hmac96:secret \
+      cast:secret \
+      cast-hmac96:secret \
+      des3:secret \
+      des3-hmac96:secret \
+      des:secret \
+      des-hmac96:secret
+  else
+    _values -S : 'ESP algorithm' \
+      'des-cbc:secret' \
+      '3des-cbc:secret' \
+      'blowfish-cbc:secret' \
+      'rc3-cbc:secret' \
+      'cast128-cbc:secret' \
+      none
+  fi
 }
 
 _packet_types () {
-	_values 'Packets type' \
-		'aodv[Ad-hoc On-demand Distance Vector protocol]' \
-		'cnfp[Cisco NetFlow protocol]' \
-		'rpc[Remote Procedure Call]' \
-		'rtp[Real-Time  Applications  protocol]' \
-		'rtcp[Real-Time Applications control protocol]' \
-		'snmp[Simple Network Management Protocol]' \
-		'tftp[Trivial File Transfer Protocol]' \
-		'vat[Visual Audio Tool]' \
-		'wb[distributed White Board]'
+  types=(
+    'cnfp[Cisco NetFlow protocol]'
+    'rpc[Remote Procedure Call]'
+    'rtp[Real-Time Applications protocol]'
+    'rtcp[Real-Time Applications control protocol]'
+    'vat[Visual Audio Tool]'
+    'wb[distributed White Board]'
+  )
+  if [[ $OSTYPE = openbsd* ]]; then
+    types+=(
+      'sack[RFC 2018 TCP Selective Acknowledgements Options]'
+      'vrrp[Virtual Router Redundancy Protocol]'
+      'tcp[Transmission Control Protocol]'
+    )
+  else
+    types+=(
+      'aodv[Ad-hoc On-demand Distance Vector protocol]'
+      'carp[Common Address Redundancy Protocol]'
+      'radius[RADIUS]'
+      'snmp[Simple Network Management Protocol]'
+      'tftp[Trivial File Transfer Protocol]'
+      'vxlan[Virtual eXtensible Local Area Network]'
+      'zmtpl[ZeroMQ Message Transport Protocol]'
+    )
+  fi
+  _values 'Packets type' $types
 }
 
 _data_link_types () {
@@ -45,48 +82,81 @@ _data_link_types () {
 _bpf_filter () {
 }
 
-_arguments \
-  '()-A[print each packet in ASCII]' \
-  -c'[exit after receiving specified number of packets]:number of packets' \
-  -C'[specify output file size in MB (10e6 bytes)]:output file size' \
-  '(-ddd)-d[dump the compiled packet-matching code in a human readable form]' \
-  '(-ddd)-dd[dump packet-matching code as a C program fragment]' \
-  '(-d -dd)-ddd[dump packet-matching code as decimal numbers (preceded with a count)]' \
-  '(-A -c -C -d -dd -ddd -e -E -f -F -i -l -L -m -M -n -N -O -p -q -R -r -S -s -T -t -tt -ttt -tttt -u -U -v -vv -vvv -w -W -x -xx -X -XX -y -Z *)'-D'[print the list of the network interfaces available on the system]' \
-  -e'[print the link-level header on each dump line]' \
-  -E"[decrypting IPsec ESP packets]:spi@ipaddr::algo\:secret:_esp_secrets" \
-  -f"[print 'foreign' IPv4 addresses numerically]" \
-  -F'[input file for the filter expression]:filter expression file:_files' \
-  -i'[interface]:interface:_interfaces' \
-  -l'[make stdout line buffered]' \
-  '(-A -c -C -d -dd -ddd -e -E -f -F -l -D -m -M -n -N -O -p -q -R -r -S -s -T -t -tt -ttt -tttt -u -U -v -vv -vvv -w -W -x -xx -X -XX -y -Z *)'-L'[list the known data link types for the interface]' \
-  '*-m[load SMI MIB module definitions]:SMI MIB module definitions:_files' \
-  -M'[shared secret for validating the digests in TCP segments with the TCP-MD5 option]:secret' \
-  -n"[don't convert addresses to names]" \
-  -N"[don't print domain name qualification of host names]" \
-  -O"[don't run the packet-matching code optimizer]" \
-  -p"[don't put the interface into promiscuous mode]" \
-  -q'[quick (quiet?) output]' \
-  -R'[assume ESP/AH packets to be based on old specification (RFC1825 to RFC1829)]' \
-  -r'[read packets from file]:input file:_files' \
-  -S'[print absolute TCP sequence numbers]' \
-  -s'[specify number of bytes of data to snarf from each packet]:number of bytes to snap' \
-  -T'[interpret captured packets as specified type]:packet type:_packet_types' \
-  "(-tttt)-t[don't print a timestamp on each dump line]" \
-  '(-ttt -tttt)-tt[print an unformatted timestamp on each dump line]' \
-  '(-tt -tttt)-ttt[print a delta (in micro-seconds) between current and previous line on each dump line]' \
-  '(-t -tt -ttt)-tttt[print a timestamp in default format proceeded by date on each dump line]' \
-  -u'[print undecoded NFS handles]' \
-  -U'[make output packet-buffered when saving to file (-w)]' \
-  '(-vvv)-v[slightly more verbose output]' \
-  '(-vvv)-vv[more verbose output]' \
-  '(-v -vv)-vvv[most verbose output]' \
-  -w'[write the raw packets to file]:output file:_files' \
-  -W'[limit the number of created files (-C)]:number of files' \
-  -x'[print each packet (minus its link level header) in hex]' \
-  '(-x)-xx[print each packet, including its link level header, in hex]' \
-  -X'[print each packet (minus its link level header) in hex and ASCII]' \
-  '(-X)-XX[print each packet, including its link level header, in hex and ASCII]' \
-  -y'[set the data link type to use while capturing packets]:data link type:_data_link_types' \
-  -Z'[drops privileges (if root) and changes user ID (along with primary group)]:user:_users' \
+args=(
+  '-A[print each packet in ASCII]'
+  '-c[exit after receiving specified number of packets]:number of packets'
+  '(-ddd)-d[dump the compiled packet-matching code in a human readable form]'
+  '(-ddd)-dd[dump packet-matching code as a C program fragment]'
+  '(-d -dd)-ddd[dump packet-matching code as decimal numbers (preceded with a count)]'
+  "-E[decrypting IPsec ESP packets]:spi@ipaddr::algo\:secret:_esp_secrets"
+  '-e[print the link-level header on each dump line]'
+  '-F[input file for the filter expression]:filter expression file:_files'
+  "-f[print 'foreign' IPv4 addresses numerically]"
+  '-l[make stdout line buffered]'
+  "-N[don't print domain name qualification of host names]"
+  "-n[don't convert addresses to names]"
+  "-O[don't run the packet-matching code optimizer]"
+  "-p[don't put the interface into promiscuous mode]"
+  '-q[quick (quiet?) output]'
+  '-r[read packets from file]:input file:_files'
+  '-S[print absolute TCP sequence numbers]'
+  '-s[specify number of bytes of data to snarf from each packet]:number of bytes to snap'
+  '-T[interpret captured packets as specified type]:packet type:_packet_types'
+  "(-tt -ttt -tttt -ttttt)-t[don't print a timestamp on each dump line]"
+  '(-t -ttt -tttt -ttttt)-tt[print an unformatted timestamp on each dump line]'
+  '(-vv -vvv)-v[slightly more verbose output]'
+  '(-v -vvv)-vv[more verbose output]'
+  '-w[write the raw packets to file]:output file:_files'
+  '-X[print each packet (minus its link level header) in hex and ASCII]'
+  '-x[print each packet (minus its link level header) in hex]'
+  '-y[set the data link type to use while capturing packets]:data link type:_data_link_types'
   '*:BPF filter:_bpf_filter'
+)
+
+if [[ $OSTYPE = openbsd* ]]; then
+  args+=(
+    '(-n)-a[attempt to convert network and broadcast addresses to names]'
+    '-D[select packet flowing in specified direction]:direction:(in out)'
+    '-I[print the interface on each dump line]'
+    '-o[print a guess of the possible operating system(s)]'
+    '(-t -tt -tttt -ttttt)-ttt[print day and month in timestamp]'
+    '(-t -tt -ttt -ttttt)-tttt[print timestamp difference between packets]'
+    '(-t -tt -ttt -tttt)-ttttt[print timestamp difference since the first packet]'
+  )
+else
+  args+=(
+    '-B[specify the capture buffer size in KiB]:capture buffer size'
+    '-b[print the AS number in BGP packets in ASDOT notation]'
+    '-C[specify output file size in MB (10e6 bytes)]:output file size'
+    '(-* *)'-D'[print the list of the network interfaces available on the system]'
+    '-G[specify the interval to rotate the dump file in seconds]:dump file rotate seconds'
+    '-H[attempt to detect 802.11s draft mesh headers]'
+    '(-* *)-h[print version strings and a usage message]'
+    '-I[put the interface in monitor mode]'
+    '(-* *)-J[list the supported timestamp types]'
+    '-j[set the timestamp type]:timestamp type'
+    "-K[don't attempt to verify checksums]"
+    '*-m[load SMI MIB module definitions]:SMI MIB module definitions:_files'
+    '-M[shared secret for validating the digests in TCP segments with the TCP-MD5 option]:secret'
+    '-R[assume ESP/AH packets to be based on old specification (RFC1825 to RFC1829)]'
+    '(-t -tt -tttt)-ttt[print a delta (in micro-seconds) between current and previous line on each dump line]'
+    '(-t -tt -ttt)-tttt[print a timestamp in default format proceeded by date on each dump line]'
+    '(-t -tt -ttt -tttt)-ttttt[print a delta (micro-second resolution) since the first line on each dump line]'
+    '-U[make output packet-buffered when saving to file (-w)]'
+    '-u[print undecoded NFS handles]'
+    '-V[Read a list of filenames from file]:file:_files'
+    '(-v -vv)-vvv[most verbose output]'
+    '-W[limit the number of created files (-C)]:number of files'
+    '(-X)-XX[print each packet, including its link level header, in hex and ASCII]'
+    '(-x)-xx[print each packet, including its link level header, in hex]'
+    '-Z[drops privileges (if root) and changes user ID (along with primary group)]:user:_users'
+    '-z[command to run after file rotation]:command:_command_names'
+  )
+fi
+
+_arguments : \
+  '-i[interface]:interface:_interfaces' \
+  - optL \
+  '-L[list the known data link types for the interface]' \
+  - default \
+  $args
diff --git a/Completion/Unix/Command/_texinfo b/Completion/Unix/Command/_texinfo
index 12d964e3f..57e13bdc2 100644
--- a/Completion/Unix/Command/_texinfo
+++ b/Completion/Unix/Command/_texinfo
@@ -1,29 +1,65 @@
-#compdef info tkinfo install-info makeinfo texi2dvi texindex
+#compdef info tkinfo install-info makeinfo texi2any texi2dvi pdftexi2dvi texi2pdf texindex
 
-local curcontext="$curcontext" state line ret=1
-local -A opt_args
-local -a info
+_info_nodes_caching_policy() {
+  local dir oldp
+  for dir in $^infopath/usr/share/info}/dir(.N); do
+    # rebuild if any dir file is newer than cache
+    [[ $dir -nt $1 ]] && return
+  done
+  # and if we didn't find a dir file, rebuild if cache older than a month
+  if [[ -z $dir ]]; then
+    oldp=( "$1"(mM+1) )
+    (( $#oldp )) && return
+  fi
+  return 1
+}
+
+_info_retrieve_nodes() {
+  # expects $cmd, $hash and $infopath to be set
+  local update_policy
+  zstyle -s ":completion:${curcontext}:" cache-policy update_policy
+  if [[ -z "$update_policy" ]]; then
+    zstyle ":completion:${curcontext}:" cache-policy \
+      _info_nodes_caching_policy
+  fi
+
+  if _cache_invalid info-nodes-$hash || ! _retrieve_cache info-nodes-$hash; then
+    set -A _cache_info_nodes_$hash \
+      ${${${${(f)"$(INFOPATH="${(j.:.)infopath}" _call_program info-nodes $cmd -k '')"}%%\" --*}#??}/\)/:}
+    _store_cache info-nodes-$hash _cache_info_nodes_$hash
+  fi
+}
+
+local curcontext="$curcontext" state line cmd ret=1
+local -A opt_args infodirs
 
 case $service in
   info)
-    info=( _call_program info ${words[1]} )
+    cmd=${words[1]}
     _arguments -C -s \
-      '(: -)--apropos=[look up string in indices]:search string: ' \
-      '(-f --file)'{\*-d,\*--directory=}'[add directory to infopath]:info dir:_files -/' \
+      '(-a --all)'{-a,--all}'[use all matching manuals]' \
+      '(: -)'{-k+,--apropos=}'[look up string in indices]:search string: ' \
+      \*{-d+,--directory=}'[add directory to infopath]:info dir:_files -/' \
       '--dribble=[record keystrokes]:file with keystrokes:_files' \
-      '(-f --file -d --directory)'{-f,--file=}'[info file to show]:info file:_files -g "*.info(|.gz|.bz2)(-.)"' \
+      '(-f --file 1)'{-f+,--file=}'[specify Info manual to visit]:info manual:->infofiles' \
       '(: - -h --help)'{-h,--help}'[display usage]' \
       '(-o --output -O)--index-search=[go directly to node if found]:search string: ' \
-      '(--index-search -o --output -O)'{-o,--output=}'[dump selected nodes to filename]:filename:_files -g "*(-.)"' \
-      '(-R --raw-escapes)'{-R,--raw-escapes}'[do not remove ANSI escapes from man pages]' \
+      '(--index-search -o --output -O)'{-o+,--output=}'[dump selected nodes to filename]:filename:_files -g "*(-.)"' \
+      '(-n --node)'{-n+,--node=}'[specify nodes in first visited Info file]:node:->nodes' \
+      '--no-raw-escapes[output escapes as literal text]' \
       '--restore=[read keystrokes from file]:filename:_files -g "*(-.)"' \
       '(--index-search -o --output -O --show-options --usage)'{-O,--show-options,--usage}'[go to command-line options node]' \
+      '--strict-node-location[(for debugging) use Info file pointers as-is]' \
       '--subnodes[recursively output menu items]' \
+      \*{-v,--variable}'[assign to Info variable]:variable' \
       '--vi-keys[use Vi-like key bindings]' \
       '(: -)--version[display version information]' \
-      '(--apropos -h --help -O --output --version)*::menu item:->item' && ret=0
+      '(-w --where --location)'{-w,--where,--location}'[print physical location of Info file]' \
+      '(-x --debug)'{-x+,--debug=}'[set debugging level]:number' \
+      '1:menu item:->infomanuals' \
+      '2:node:->nodes' && ret=0
 
-    info=( $info {(kv)opt_args[(I)(-d|--directory|-f|--file)]} )
+    infodirs=( "${(@Qkv)opt_args[(I)(-d|--directory)]}" )
   ;;
   install-info)
     if _pick_variant debian=Debian gnu --version; then
@@ -41,131 +77,216 @@ case $service in
         '--description=[specify description for menu entry]:description' \
         '--menuentry=[specify the menu entry for dir file]:menu entry' \
         '--keep-old=[inhibit replacement of existing entries/removal of empty sections]' \
-        '--test[do not update dir file]' \
+        "--test[don't update dir file]" \
         '--debug[enable debugging]' \
         '*:info file:_files -g "*.info(-.)"'
 	return
     else
       _arguments \
+	'--add-once[add only to first matching section]' \
+	'--align=[specify minimum indentation for description]:indentation' \
+	'--calign=[specify minimum indentation for continuation lines of description]:indentation' \
+        '--debug[report what is being done]' \
         '(--delete --remove)'{--delete,--remove}'[delete existing entries]' \
+        '--description=[specify description for menu entry]:description' \
         '(2)--dir-file=[specify dir file]:dir file:_files -g "dir(-.)"' \
+	'(--test --dry-run)'{--dry-run,--test}"[don't update dir file]" \
         \*{--entry,--info}'[specify directory entry to add]:text' \
+	'--defsection=[like --section, but only use text if no sections present in info file]:text' \
+	{--maxwidth,--max-width}'=[specify maximum width of dir file]:width' \
         '(- 1 2)--help[display help information]' \
         '(1)--info-file=[specify info file to install in the directory]:info file:_files -g "*.info(-.)"' \
         '(2)--info-dir=[specify directory containing dir file]:info directory:_files -/' \
+        '--keep-old=[inhibit replacement of existing entries/removal of empty sections]' \
+	'(--name --menuentry)'{--name,--menuentry}'=[specify the menu entry for dir file]:menu entry' \
+	"--no-indent[don't format new entries in the DIR file]"
         '--quiet[suppress warnings]' \
-        '(--delete --remove)--section=[put new entries in specified section]' \
+	'--regex=[specify sections for file entries using a regex]:regex' \
+        '(--delete --remove)--section=[put new entries in specified section]:section' \
+        '--silent[no output except errors]' \
         '(- 1 2)--version[display version information]' \
         '1:info file:_files -g "*.info(-.)"' \
         '2:dir file:_files -g "dir(-.)"'
 	return
     fi
   ;;
-  makeinfo)
+  makeinfo|texi2any)
     _arguments -s \
-      '--error-limit=[specify number of errors to quit after]:number' \
+      '--error-limit=[specify number of errors to quit after]:number [100]' \
+      '--document-language=[specify locale to use in translating Texinfo keywords]:locale [C]:_locales' \
       '--force[preserve output even if errors]' \
       '(* -)--help[display help]' \
       '--no-validate[suppress node cross-reference validation]' \
       '--no-warn[suppress warnings]' \
-      '--reference-limit=[warn about at most specified number of references]:number' \
+      '--conf-dir=[specify additional directory for configuration files]:directory:_directories' \
+      '--init-file=[specify initialisation file]:file:_files' \
+      \*{-c+,--set-customization-variable=}'[set customization variable]:variable' \
       '(-v --verbose)'{-v,--verbose}'[explain what is being done]' \
       '(* -)--version[display version info]' \
-      '(--html --xml)--docbook[output in DocBook format]' \
-      '(--docbook --xml)--html[output in HTML format]' \
+      '(--html --xml --plaintext)--docbook[output in DocBook XML format]' \
+      '(--docbook --xml --plaintext)--html[output in HTML format]' \
       '--no-headers[suppress node separators and menus]' \
-      '(--docbook --html)--xml[output in XML (TexinfoML) format]' \
+      '(--docbook --html --plaintext)--xml[output in XML (TexinfoML) format]' \
+      '(--docbook --html --xml)--plaintext[output in plain text format]' \
+      '--dvi[output in dvi format]' \
+      '--dvipdf[output in pdf format]' \
+      '--ps[output in ps format]' \
+      '--pdf[output in pdf format]' \
       '(-E --macro-expand)'{-E,--macro-expand}'[output macro-expanded source to specified file]:file:_files' \
       '(--docbook --xml)--no-split[suppress splitting of output]' \
-      '--number-sections[output chapter and section numbers]' \
+      '--no-number-sections[output chapter and section numbers]' \
       '(-o --output=)'{-o,--output}'[specify output file]:output file:_files' \
-      '(--docbook --html --xml)--enable-encoding[output special characters]' \
+      '(--docbook --html --xml)--disable-encoding[output special characters]' \
       '(--docbook --html --xml)--fill-column=[specify width to break lines at]:width' \
       '(--docbook --html --xml)--footnote-style=[specify style for output of footnotes]:((separate\:in\ own\ node end\:at\ end\ of\ node))' \
       '(--docbook --html --xml)--paragraph-indent=[specify spaces to indent paragraphs by]' \
-      '(--docbook --html --xml)--split-size=[split Info files at specified size]:size' \
-      '--commands-in-node-names[allow @ commands in node names]' \
+      '(--docbook --html --xml)--split-size=[split Info files at specified size]:size [300000]' \
+      '(--docbook --xml --plaintext)--css-include=[specify file in include in HTML <style> output]:file:_files' \
+      '(--docbook --xml --plaintext)--css-ref=[generate reference to a CSS file]:URL:_urls' \
+      '(--docbook --xml --plaintext)--internal-links=[produce list of internal links in a file]:file:_files' \
+      '(--docbook --xml --plaintext)--split=[split output at specified boundary]:split boundary:(chapter section node)' \
+      '(--docbook --xml --plaintext)--transliterate-file-names[produce file names in ASCII transliteration]' \
+      '(--docbook --xml --plaintext)--node-files[produce redirection files for nodes]' \
+      '-Xopt=[specify option to texi2dvi]:options' \
       '-D[define variable]:variable' \
       '-I[specify directory to append to @include search path]:directory:_files -/' \
       '-P[specify directory to prepend to @include search path]:directory:_files -/' \
       '-U[undefine variable]:variable' \
+      '(--docbook --no-ifdocbook)--ifdocbook[process @ifdocbook and @docbook even when not generating Docbook]' \
       '(--html --no-ifhtml)--ifhtml[process @ifhtml and @html when not generating HTML]' \
       '(--no-ifinfo)--ifinfo[process @ifinfo even when not generating Info]' \
+      '(--plaintext --no-ifplaintext)--ifplaintext[process @ifplaintext even when not generating plain text]' \
       '(--no-iftex)--iftex[process @iftex and @tex text]' \
-      '(--no-ifplaintext)--ifplaintext[process @ifplaintext even when not generating plain text]' \
-      '(--ifhtml)--no-ifhtml[do not process @ifhtml and @html text]' \
-      '(--ifinfo)--no-ifinfo[do not process @ifinfo text]' \
-      '(--ifplaintext)--no-ifplaintext[do not process @ifplaintext text]' \
-      '(--iftex)--no-iftex[do not process @iftex and @tex text]' \
+      '(--xml --no-ifxml)--ifxml[process @ifxml and @xml]' \
+      "(--ifdocbook --html --xml --plaintext)--no-ifdocbook[don't process @ifdocbook and @docbook text]" \
+      "(--ifhtml --docbook --xml --plaintext)--no-ifhtml[don't process @ifhtml and @html text]" \
+      "(--ifinfo --docbook --html --xml --plaintext)--no-ifinfo[don't process @ifinfo text]" \
+      "(--ifplaintext --docbook --html --xml)--no-ifplaintext[don't process @ifplaintext text]" \
+      "(--iftex --docbook --html --xml --plaintext)--no-iftex[don't process @iftex and @tex text]" \
+      "(--ifxml --docbook --html --xml --plaintext)--no-ifxml[don't process @ifxml and @xml text]" \
       '*:texinfo file:_texi'
       return
   ;;
-  texi2dvi)
+  (pdf|)texi2(dvi|pdf))
     _arguments -s \
       '(-b --batch)'{-b,--batch}'[no interaction]' \
-      '(-c --clean)'{-c,--clean}'[remove all auxiliary files]' \
       '(-D --debug)'{-D,--debug}'[turn on shell debugging (set -x)]' \
       '(- *)'{-h,--help}'[display help information]' \
       '(-o --output)'{-o,--output=}'[specify output file]:output file:_files' \
       '(-q -s --quiet --silent -V --verbose)'{-q,-s,--silent,--quiet}'[no output except errors]' \
       '(-q -s --quiet --silent -V --verbose)'{-V,--verbose}'[report on what is done]' \
       '(- *)'{-v,--version}'[display version information]' \
+      "--max-iterations=[don't process files more than specified times]:iterations [7]" \
+      '--mostly-clean[remove the auxiliary files and directories]' \
       '-@[use @input instead of \input; for preloaded Texinfo]' \
-      '(-e --expand)'{-e,--expand}'[force macro expansion using makeinfo]' \
+      '(-p --pdf --dvipdf --html --info --ps --text)--dvi[output a DVI file]' \
+      '(-p --pdf --dvi --html --info --ps --text)--dvipdf[output a PDF file via DVI]' \
+      '(-p --pdf --dvi --dvipdf --info --ps --text)--html[output an HTML file]' \
+      '(-p --pdf --dvi --dvipdf --html --ps --text)--info[output an Info file]' \
+      '(-p --pdf --dvi --dvipdf --html --info --ps --text)'{-p,--pdf}'[use pdftex or pdflatex for processing]' \
+      '(-p --pdf --dvi --dvipdf --html --info --text)--ps[output a PostScript file]' \
+      '(-p --pdf --dvi --dvipdf --html --info --ps)--text[output a plain text file]' \
+      '(-e -E --expand)'{-e,-E,--expand}'[force macro expansion using makeinfo]' \
       '-I[specify search dir for texinfo files]:directory:_files -/' \
       '(-l --language)'{-l,--language}'[specify language of input file]:language:(LaTeX Texinfo)' \
-      '(-p --pdf)'{-p,--pdf}'[use pdftex or pdflatex for processing]' \
-      \*{-t,--texinfo}'[specify command to insert after @setfilename]:command' \
+      "--no-line-error[don't pass --file-line-error to TeX]" \
+      '--shell-escape[pass --shell-escape to TeX]' \
+      '--src-specials[pass --src-specials to TeX]' \
+      \*{-t,--command,--texinfo}'[specify command to insert after @setfilename]:command' \
+      '--translate-file=[use given charset translation file for TeX]:file:_files' \
+      '(--tidy -c --clean)--build=[specify build mode]:build mode:(local tidy clean)' \
+      '(--tidy -c --clean --build)--tidy[same as --build=tidy]' \
+      '(--tidy -c --clean --build)'{--tidy,-c,--clean}'[same as --build=clean]' \
+      '--build-dir=[specify build directory]:directory:_directories' \
       '*:file:_texi'
       return
   ;;
   texindex)
-    _arguments \
+    _arguments -S \
       '(* -)--help[display help]' \
       '(* -)--version[display version info]' \
-      '(--no-keep)--keep[keep temporary files around after processing]' \
-      '(--keep)--no-keep[do not keep temporary files (default)]' \
-      '(-o --output)'{-o,--output=}'[specify output file]:file:_files' \
       '*:texinfo file:_texi'
       return
   ;;
   tkinfo)
+    cmd=$commands[info]
     _x_arguments -C \
+      '(-)--help' \
       '-+'{headers,buttons,balloons,scrollthrough,pagesep,showdir} \
       '-linklook:menu display:(color font underline)' \
       '-highlight:link display:(color inverse underline)' \
       '-searchlook:search match display:(color inverse underline)' \
       '-iconic' \
       '-dir:info path:_dir_list' \
-      '-file:info file:_files -g "*.info(|.gz|.bz2)(-.)"' \
-      '-node:info node:->item' \
-      '*::menu items:->item' && ret=0
-    info=( _call_program info info ${(kv)opt_args[(I)-file]/-file/-f} )
+      '-file:info file:->infofiles' \
+      '-node:info node:->nodes' \
+      '*::menu items:->infomanuals' && ret=0
+    info=( info ${(kv)opt_args[(I)-file]/-file/-f} )
   ;;
 esac
 
-case $state in
-  item )
-    local -a items tags expl infopath
-    (( $#INFOPATH )) && infopath=( -W ${s/:/INFOPATH} )
+if [[ -n $state ]]; then
+  local chr cache file q
+  local -a expl suf tags
+  local -i36 hash=5381
+  local -aU infopath=( /usr/share/info ${commands[info]:h:h}/share/info ${(s.:.)INFOPATH} $infodirs )
+  infopath=( $^infopath(N) )
+  # run simple hash function on infopath
+  for chr in "${(s..)infopath}"; do
+    (( hash += (hash << 5) + ##$chr ))
+  done
+  typeset +i -l hash=${hash#*\#}
+  cache=_cache_info_nodes_$hash
+  typeset -gHU $cache
+
+  if [[ $state = infomanuals ]]; then
+    [[ -z $compstate[quote] ]] && q='\\'
+    if compset -P "${q}\("; then
+      if [[ -prefix *\) ]]; then
+	file="${PREFIX%%\)*}"
+	compset -P 1 '*\)'
+	state=nodes
+      else
+	compset -S "${q}\)*" || suf=( -S "${q[1]})" )
+	state=infofiles
+      fi
+    fi
+  fi
 
-    tags=(items)
-    if [[ $CURRENT -eq 1 ]]; then
-	tags+=files
+  [[ -z $file ]] && file=${opt_args[-f]:-$line[1]}
+  if [[ $state = info* ]]; then
+    local -aU files=( $^infopath/*.info(|.gz|.bz2)(-.:t:s/.gz//:s/.bz2//:r) )
+    local items
+    if (( ! $#files )); then
+      _info_retrieve_nodes
+      files=( ${(P)cache%%:*} )
+    fi
+    items=( ${${${(M)${(f)"$(_call_program menu-items info -o-)"}:#(#s)\* *: \(*}#??}%%\)*} )
+    files+=( ${items##*\(} )
+    tags=( info-files )
+    if [[ $state != infofiles ]]; then
+      tags+=( menu-items )
+      items=( ${items%:*} )
+    fi
+  elif [[ $state = nodes ]]; then
+    tags=( menu-items )
+    if [[ -n $file ]]; then
+      _info_retrieve_nodes
+      nodes=( ${(@PM)cache:#${file}:*} )
+      tags+=( info-nodes )
     fi
+    items=( ${${${(M)${(f)"$(_call_program menu-items info -o- $file)"}:#(#s)\* *::*}%%::*}#??} )
+  fi
 
-    _tags $tags
-    while _tags; do
-	if _requested files expl 'info file'; then
-	    _files "$expl[@]" $infopath -g '*.info(-.)' && ret=0
-	fi
-	if _requested items; then
-	    items=(${${(M)${${(f)"$(${info} --output - ${words[1,CURRENT-1]} 2>/dev/null)"}[1,(r)[[:space:]]#--- The Detailed Node Listing ---[[:space:]]#]}:#\* *~\* Menu:*}:/(#b)\*[[:space:]]##([^:]##):(#B)( \(?##\)[^.]#.|(:|))[[:space:]]#(#b)(*)[[:space:]]#/$match[1]${match[2]:+:}$match[2]})
+  _tags $tags
 
-	    _describe -t items "menu item" items && ret=0
-	fi
-    done
-  ;;
-esac
+  while _tags; do
+    _requested info-files expl 'info file' compadd $suf -M 'm:{a-zA-Z}={A-Za-z}' -a files && ret=0
+    _requested menu-items expl 'menu item' compadd -M 'm:{a-zA-Z}={A-Za-z}' -a items && ret=0
+    _requested info-nodes expl 'node' compadd -M 'm:{a-zA-Z}={A-Za-z}' ${nodes#*:} && ret=0
+
+    (( ret )) || break
+  done
+fi
 
 return ret
diff --git a/Completion/Unix/Command/_tin b/Completion/Unix/Command/_tin
index c63ccd9e6..61d203d1f 100644
--- a/Completion/Unix/Command/_tin
+++ b/Completion/Unix/Command/_tin
@@ -3,6 +3,8 @@
 local newshosts expl state line curcontext="$curcontext" ret=1
 
 _arguments -C -s \
+  '(-6)-4[force connecting via IPv4]' \
+  '(-4)-6[force connecting via IPv6]' \
   '-a[toggle color flag]' \
   '-A[force authentication on connect]' \
   '-c[mark all news as read in subscribed groups]' \
diff --git a/Completion/Unix/Command/_tmux b/Completion/Unix/Command/_tmux
index 93b5c7faa..0917da604 100644
--- a/Completion/Unix/Command/_tmux
+++ b/Completion/Unix/Command/_tmux
@@ -51,7 +51,7 @@
 
 # Global variables; setup the first time _tmux is called.
 # For $_tmux_commands[] generation, see the very end of this file.
-typeset -ga _tmux_commands _tmux_aliases
+typeset -ga _tmux_commands=() _tmux_aliases=()
 typeset -gA _tmux_aliasmap
 
 _tmux_aliasmap=(
@@ -147,970 +147,775 @@ _tmux_aliasmap=(
 
 # --- Sub-command functions ---
 # These *must* be called _tmux-*(); The description generation relies on
-# them being names that way. *No* other functions may match that pattern.
+# them being named that way. *No* other functions may match that pattern.
 # Other utility functions should be named __tmux-*() (see below).
 #
 # Another thing, the description generation needs, is handling of
 # $tmux_describe: If that parameter is non-empty, the sub-command function
 # should only print a description of the sub-command it handles and return
-# immidiately after doing so.
+# immediately after doing so.
 #
 # To add support for a new sub-command, you only have to add a new
 # _tmux-<foo>() function below (preferably alphabetically sorted), that
 # behaves like described above; and add a alias->command pair in the
-# _tmux_aliasmap associative array above (if the comand in fact has an
+# _tmux_aliasmap associative array above (if the command in fact has an
 # alias). The rest should just work[tm].
 
-function _tmux-attach-session() {
-    [[ -n ${tmux_describe} ]] && print "Attach or switch to a session" && return
-    local -a args
+_tmux-attach-session() {
+  [[ -n ${tmux_describe} ]] && print "attach or switch to a session" && return
+
+  _arguments -s \
+    '-c+[specify working directory for the session]:directory:_directories' \
+    '-d[detach other clients attached to target session]' \
+    '-r[put the client into read-only mode]' \
+    '-t+[specify target session]:target session:__tmux-sessions' \
+    "-E[don't apply update-environment option]"
+}
+
+_tmux-bind-key() {
+  [[ -n ${tmux_describe} ]] && print "bind a key to a command" && return
+  _arguments -s -A "-*" \
+    '-c[bind to command mode instead of normal mode]' \
+    '-n[make the binding work without the need for the prefix key]' \
+    '-r[the key may repeat]' \
+    '-t+[specify mode table for the binding]:mode table:__tmux-mode-tables' \
+    '-R+[specify repeat count]:repeat count' \
+    '-T+[specify key table for the binding]:key table' \
+    '1:key' \
+    '*:::template:_tmux'
+}
+
+_tmux-break-pane() {
+  [[ -n ${tmux_describe} ]] && print "break a pane from an existing into a new window" && return
+  _arguments -s \
+    "-d[don't make the new window become the active one]" \
+    '-F+[specify output format]:format:__tmux-format__tmux-format' \
+    '-P[print information of new window after it has been created]' \
+    '-s+[specify source pane]:pane:__tmux-panes' \
+    '-t+[specify destination window]:pane:__tmux-panes'
+}
+
+_tmux-capture-pane() {
+  [[ -n ${tmux_describe} ]] && print "capture the contents of a pane to a buffer" && return
+  _arguments -s \
+    '-a[use alternate screen]' \
+    '(-p)-b+[choose target buffer]:target buffer:__tmux-buffers' \
+    '-C[escape non-printable characters as octal \\ooo]' \
+    '-e[include escape sequences for attributes etc]' \
+    '-E[specify last line to capture]:line number (- means last line)' \
+    '-J[join wrapped lines and preserve trailing space]' \
+    '-q[ignore errors when trying to access alternate screen]' \
+    '(-b)-p[print data to stdout]' \
+    '-P[only capture beginnings of as-yet incomplete escape sequences]' \
+    '-S[specify start line to capture]:first line (- means start of scrollback)' \
+    '-t+[choose source pane]:source pane:__tmux-panes'
+}
+
+_tmux-choose-buffer() {
+  [[ -n ${tmux_describe} ]] && print "put a window into buffer choice mode" && return
+  _arguments -s \
+    '-F+[specify output format]:format:__tmux-format' \
+    '-t+[specify target window]:session:__tmux-windows' \
+    '*:::template:_tmux'
+}
+
+_tmux-choose-client() {
+  [[ -n ${tmux_describe} ]] && print "put a window into client choice mode" && return
+  _arguments -s \
+    '-F+[specify output format]:format:__tmux-format' \
+    '-t+[specify target window]:session:__tmux-windows' \
+    '*:::template:_tmux'
+}
+
+_tmux-choose-session() {
+  [[ -n ${tmux_describe} ]] && print "put a window into session choice mode" && return
+  _arguments -s \
+    '-F+[specify output format]:format:__tmux-format' \
+    '-t+[specify target window]:session:__tmux-windows' \
+    '*:::template:_tmux'
+}
+
+_tmux-choose-tree() {
+  [[ -n ${tmux_describe} ]] && print "put a window into tree choice mode" && return
+  _arguments -s \
+    '-b+[override default session command]:session-command' \
+    '-c+[override default window command]:window-command' \
+    '-S+[specify session format]:session-format:__tmux-formats' \
+    '-s[choose among sessions]' \
+    '-t+[specify target window]:session:__tmux-windows' \
+    '-u[show generated tree uncollapsed at startup]' \
+    '-W+[specify window format]:window-format:__tmux-formats' \
+    '-w[choose among windows]' \
+    '*:::template:_tmux'
+}
+
+_tmux-choose-window() {
+  [[ -n ${tmux_describe} ]] && print "put a window into window choice mode" && return
+  _arguments -s \
+    '-F=[specify output format]:format:__tmux-format' \
+    '-t+[specify target window]:session:__tmux-windows' \
+    '*:::template:_tmux'
+}
+
+_tmux-clear-history() {
+  [[ -n ${tmux_describe} ]] && print "remove and clear history for a pane" && return
+  _arguments '-t+[specify target pane]:pane:__tmux-panes'
+}
+
+_tmux-clock-mode() {
+  [[ -n ${tmux_describe} ]] && print "enter clock mode" && return
+  _arguments '-t+[specify target pane]:pane:__tmux-panes'
+}
+
+_tmux-command-prompt() {
+  [[ -n ${tmux_describe} ]] && print "open the tmux command prompt in a client" && return
+  _arguments -s \
+    '-I+[specify list of initial inputs]:initial-text (comma-separated list)' \
+    '-p+[specify list of prompts]:prompts (comma-separated list)' \
+    '-t+[specify target client]:client:__tmux-clients' \
+    '*:::template:_tmux'
+}
+
+_tmux-confirm-before() {
+  [[ -n ${tmux_describe} ]] && print "run a command but ask for confirmation before" && return
+  _arguments -s \
+    '-p+[specify prompt]:prompt string' \
+    '-t+[specify target client]:client:__tmux-clients' \
+    '*:::command:_tmux'
+}
+
+_tmux-copy-mode() {
+  [[ -n ${tmux_describe} ]] && print "enter copy mode" && return
+  _arguments -s \
+    '-t+[specify target pane]:pane:__tmux-panes' \
+    '-u[scroll up one page]' \
+    '-M[begin a mouse drag]'
+}
+
+_tmux-delete-buffer() {
+  [[ -n ${tmux_describe} ]] && print "delete a paste buffer" && return
+  _arguments '-b+[specify target buffer name]:buffer:__tmux-buffers'
+}
+
+_tmux-detach-client() {
+  [[ -n ${tmux_describe} ]] && print "detach a client from the server" && return
+  _arguments -s \
+    '-a[kill all clients except for the named by -t]' \
+    '-P[send SIGHUP to parent process]' \
+    '-s+[specify target session and kill its clients]:session:__tmux-sessions' \
+    '-t+[specify target client]:client:__tmux-clients'
+}
 
-    args=(
-        '-c[specify working directory for the session]:directory:_path_files -g "*(-/)"'
-        '-d[detach other clients attached to target session]'
-        '-r[put the client into read-only mode]'
-        '-t[choose a target session]:target session:__tmux-sessions'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-bind-key() {
-    [[ -n ${tmux_describe} ]] && print "Bind a key to a command" && return
-    local curcontext="${curcontext}" state
-    local -a args
-
-    args=(
-        '-c[bind to command mode instead of normal mode]'
-        '-n[make the binding work without the need for the prefix key]'
-        '-r[the key may repeat]'
-        '-t[choose a key table for the binding]:key tables:__tmux-key-tables'
-        '1: :->key'
-        '*:: :->command_and_args'
-    )
-    _arguments -C : ${args} && return
-
-    if [[ ${state} == 'key' ]]; then
-        _message "key"
-    else
-        # quite cool, that this works. :-)
-        _tmux
-    fi
-}
-
-function _tmux-break-pane() {
-    [[ -n ${tmux_describe} ]] && print "Break a pane from an existing into a new window" && return
-    local -a args
-    args=(
-        '-d[do not make the new window become the active one]'
-        '-F[specify format of output]:format:__tmux-format__tmux-format'
-        '-P[print information of new window after it has been created]'
-        '-t[choose a target pane]:panes:__tmux-panes'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-capture-pane() {
-    [[ -n ${tmux_describe} ]] && print "Capture the contents of a pane to a buffer" && return
-    local -a args
-    args=(
-        '-a[use alternate screen]'
-        '-b[choose target buffer]:target buffer:__tmux-buffers'
-        '-C[escape non-printable characters as octal \\ooo]'
-        '-e[include escape sequences for attributes etc]'
-        '-E[specify last line to capture. - means last line of pane]'
-        '-J[join wrapped lines and preserver trailing space]'
-        '-q[ignore errors when trying to access alternate screen]'
-        '-p[print data to stdout]'
-        '-P[only capture that is the beginning of an as-yet incomplete esc seq]'
-        '-S[specify start line to capture. - means first line of scrollback]'
-        '-t[choose source pane]:source pane:__tmux-panes'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-choose-buffer() {
-    [[ -n ${tmux_describe} ]] && print "Put a window into buffer choice mode" && return
-    local -a args
-    args=(
-        '-F[specify format of output]:format:__tmux-format'
-        '-t[choose a target window]:sessions:__tmux-windows'
-        '*:: :->tmpl'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-choose-client() {
-    [[ -n ${tmux_describe} ]] && print "Put a window into client choice mode" && return
-    local -a args
-    args=(
-        '-F[specify format of output]:format:__tmux-format'
-        '-t[choose a target window]:sessions:__tmux-windows'
-        '*:: :->tmpl'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-choose-session() {
-    [[ -n ${tmux_describe} ]] && print "Put a window into session choice mode" && return
-    local -a args
-    args=(
-        '-F[specify format of output]:format:__tmux-format'
-        '-t[choose a target window]:sessions:__tmux-windows'
-        '*:: :->tmpl'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-choose-tree() {
-    [[ -n ${tmux_describe} ]] && print "Put a window into tree choice mode" && return
-    local -a args
-    args=(
-        '-b[override default session command]:session-command:'
-        '-c[override default window command]:window-command:'
-        '-S[specify session format]:session-format:__tmux-formats'
-        '-s[choose among sessions]'
-        '-t[choose a target window]:sessions:__tmux-windows'
-        '-u[show generated tree uncollapsed at startup]'
-        '-W[specify window format]:window-format:__tmux-formats'
-        '-w[choose among windows]'
-        '*:: :->tmpl'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-choose-window() {
-    [[ -n ${tmux_describe} ]] && print "Put a window into window choice mode" && return
-    local -a args
-    args=(
-        '-F[specify format of output]:format:__tmux-format'
-        '-t[choose a target window]:sessions:__tmux-windows'
-        '*:: :->tmpl'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-clear-history() {
-    [[ -n ${tmux_describe} ]] && print "Remove and clear history for a pane" && return
-    local -a args
-    args=('-t[choose a target pane]:panes:__tmux-panes')
-    _arguments : ${args}
-}
-
-function _tmux-clock-mode() {
-    [[ -n ${tmux_describe} ]] && print "Enter clock mode" && return
-    local -a args
-    args=('-t[choose a target pane]:panes:__tmux-panes')
-    _arguments : ${args}
-}
-
-function _tmux-command-prompt() {
-    [[ -n ${tmux_describe} ]] && print "Open the tmux command prompt in a client" && return
-    local state
-    local -a args
-    args=(
-        '-I[comma separated list of initial inputs]:initial-text:->ilist'
-        '-p[list of prompts]:prompts:->plist'
-        '-t[choose a target client]:clients:__tmux-clients'
-        '*:: :->tmpl'
-    )
-    _arguments -C : ${args} && return
-    if [[ ${state} == 'plist' ]]; then
-        _message "comma seperated list of prompts"
-        return
-    elif [[ ${state} == 'ilist' ]]; then
-        _message "comma seperated list of initial text"
-        return
-    fi
-    __tmux-lastarg ${state} 'tmpl' 1 "command template"
-}
-
-function _tmux-confirm-before() {
-    [[ -n ${tmux_describe} ]] && print "Run a command but ask for confirmation before" && return
-    local state
-    local -a args
-    args=(
-        '-p[specify prompt]:prompt:->prompt'
-        '-t[choose a target client]:clients:__tmux-clients'
-        '*:: :->command_and_args'
-    )
-    _arguments -C : ${args} && return
-    if [[ ${state} == 'prompt' ]]; then
-        _message 'prompt string'
-        return
-    fi
-    __tmux-lastarg ${state} 'command_and_args' 1 "command string"
-}
-
-function _tmux-copy-mode() {
-    [[ -n ${tmux_describe} ]] && print "Enter copy mode" && return
-    local -a args
-    args=(
-        '-t[choose a target pane]:panes:__tmux-panes'
-        '-u[scroll up one page]'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-delete-buffer() {
-    [[ -n ${tmux_describe} ]] && print "Delete a paste buffer" && return
-    local -a args
-    args=('-b[choose a target buffer index]:buffers:__tmux-buffers')
-    _arguments : ${args} && return
-}
-
-function _tmux-detach-client() {
-    [[ -n ${tmux_describe} ]] && print "Detach a client from the server" && return
-    local -a args
-    args=(
-        '-a[kill all clients except for the named by -t]'
-        '-P[send SIGHUP to parent process]'
-        '-s[choose a target session and kill its clients]:sessions:__tmux-sessions'
-        '-t[choose a target client]:clients:__tmux-clients'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-display-message() {
-    [[ -n ${tmux_describe} ]] && print "Display a message in the status line" && return
-    local -a args
-    args=(
-        '-c[choose a target client]:clients:__tmux-clients'
-        '-p[print message to stdout]'
-        '-t[choose a target client]:clients:__tmux-clients'
-        '*:: :->msg'
-    )
-    _arguments : ${args} && return
-    __tmux-lastarg ${state} 'msg' 1 "message"
-}
-
-function _tmux-display-panes() {
-    [[ -n ${tmux_describe} ]] && print "Display an indicator for each visible pane" && return
-    local -a args
-    args=('-t[choose a target client]:clients:__tmux-clients')
-    _arguments : ${args}
-}
-
-function _tmux-find-window() {
-    [[ -n ${tmux_describe} ]] && print "Search for a pattern in windows" && return
-    local curcontext="${curcontext}" state
-    local -a args
-    args=(
-        '-C[match visible contents]'
-        '-F[specify format of output]:format:__tmux-format'
-        '-N[match window name]'
-        '-T[match window title]'
-        '-t[choose a target window]:windows:__tmux-windows'
-        '*:: :->pattern'
-    )
-    _arguments : ${args} && return
-    __tmux-lastarg ${state} 'pattern' 1 "window search pattern"
-}
-
-function _tmux-has-session() {
-    [[ -n ${tmux_describe} ]] && print "Check and report if a session exists on the server" && return
-    local -a args
-    args=('-t[choose a target session]:sessions:__tmux-sessions')
-    _arguments : ${args}
-}
-
-function _tmux-if-shell() {
-    [[ -n ${tmux_describe} ]] && print "Execute a tmux command if a shell-command succeeded" && return
-    local -a args
-    args=(
-        '-b[run shell command in background]'
-        '-F[do not execute shell command but use it as a string-value]'
-        '1:shell command:'
-        '2:tmux command:'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-join-pane() {
-    [[ -n ${tmux_describe} ]] && print "Split a pane and move an existing one into the new space" && return
-    local -a args
-    args=(
-        '-b[join source pane left of or above target pane]'
-        '-d[do not make the new window become the active one]'
-        '-h[split horizontally]'
-        '-v[split vertically]'
-        '-l[define new pane'\''s size]: :_guard "[0-9]#" "numeric value"'
-        '-p[define new pane'\''s size in percent]: :_guard "[0-9]#" "numeric value"'
-        '-s[choose source pane]:window:__tmux-panes'
-        '-t[choose target pane]:window:__tmux-panes'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-kill-pane() {
-    [[ -n ${tmux_describe} ]] && print "Destroy a given pane" && return
-    local -a args
-    args=(
-        '-a[kill all panes except the one specified by -t]'
-        '-t[choose a target pane]:panes:__tmux-panes'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-kill-server() {
-    [[ -n ${tmux_describe} ]] && print "Kill clients, sessions and server" && return
-    __tmux-nothing-else
-}
-
-function _tmux-kill-session() {
-    [[ -n ${tmux_describe} ]] && print "Destroy a given session" && return
-    local -a args
-    args=(
-        '-a[kill all session except the one specified by -t]'
-        '-t[choose a target session]:sessions:__tmux-sessions'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-kill-window() {
-    [[ -n ${tmux_describe} ]] && print "Destroy a given window" && return
-    local -a args
-    args=(
-        '-a[kill all windows except the one specified by -t]'
-        '-t[choose a target window]:windows:__tmux-windows'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-last-pane() {
-    [[ -n ${tmux_describe} ]] && print "Select the previously selected pane" && return
-    local -a args
-    args=(
-        '-d[disable input to the pane]'
-        '-e[enable input to the pane]'
-        '-t[choose a session]:sessions:__tmux-sessions'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-last-window() {
-    [[ -n ${tmux_describe} ]] && print "Select the previously selected window" && return
-    local -a args
-    args=('-t[choose a session]:sessions:__tmux-sessions')
-    _arguments : ${args} && return
-}
-
-function _tmux-link-window() {
-    [[ -n ${tmux_describe} ]] && print "Link a window to another" && return
-    local -a args
-    args=(
-        '-d[do not make the new window become the active one]'
-        '-k[kill the target window if it exists]'
-        '-s[choose source window]:window:__tmux-windows'
-        '-t[choose destination window]:window:__tmux-windows'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-list-buffers() {
-    [[ -n ${tmux_describe} ]] && print "List paste buffers of a session" && return
-    local -a args
-    args=('-F[specify format of output]:format:__tmux-format')
-    _arguments : ${args} && return
-}
-
-function _tmux-list-clients() {
-    [[ -n ${tmux_describe} ]] && print "List clients attached to server" && return
-    local -a args
-    args=(
-        '-F[specify format of output]:format:__tmux-format'
-        '-t[choose a session]:sessions:__tmux-sessions'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-list-commands() {
-    [[ -n ${tmux_describe} ]] && print "List supported sub-commands" && return
-    __tmux-nothing-else
-}
-
-function _tmux-list-keys() {
-    [[ -n ${tmux_describe} ]] && print "List all key-bindings" && return
-    local -a args
-    args=('-t[choose a key table]:key table:__tmux-key-tables')
-    _arguments : ${args} && return
-}
-
-function _tmux-list-panes() {
-    [[ -n ${tmux_describe} ]] && print "List panes of a window" && return
-    local -a args
-    args=(
-        '-a[list all panes the server possesses]'
-        '-F[specify format of output]:format:__tmux-format'
-        '-s[if specified, -t chooses a session]'
-        # TODO: Use __tmux-windows or __tmux-sessions depending on -s.
-        '-t[choose a window]:windows:__tmux-windows'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-list-sessions() {
-    [[ -n ${tmux_describe} ]] && print "List sessions managed by server" && return
-    local -a args
-    args=('-F[specify format of output]:format:__tmux-format')
-    _arguments : ${args} && return
-}
-
-function _tmux-list-windows() {
-    [[ -n ${tmux_describe} ]] && print "List windows of a session" && return
-    local -a args
-    args=(
-        '-a[list all windows the tmux server possesses]'
-        '-F[specify format of output]:format:__tmux-format'
-        '-t[choose a session]:sessions:__tmux-sessions'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-load-buffer() {
-    [[ -n ${tmux_describe} ]] && print "Load a file into a paste buffer" && return
-    local -a args
-
-    args=(
-        '-b[choose a target buffer index]:panes:__tmux-buffers'
-        '1:file name:_files -g "*(-.)"'
-    )
-    _arguments : ${args} && return
+_tmux-display-message() {
+  [[ -n ${tmux_describe} ]] && print "display a message in the status line" && return
+  _arguments -s -S \
+    '-c+[specify target client]:client:__tmux-clients' \
+    '-p[print message to stdout]' \
+    '-t+[specify target client]:client:__tmux-clients' \
+    '-F+[specify output format]:format:__tmux-format' \
+    ':message'
 }
 
-function _tmux-lock-client() {
-    [[ -n ${tmux_describe} ]] && print "Lock a client" && return
-    local -a args
-    args=('-t[choose a client]:clients:__tmux-clients')
-    _arguments : ${args} && return
+_tmux-display-panes() {
+  [[ -n ${tmux_describe} ]] && print "display an indicator for each visible pane" && return
+  _arguments '-t+[specify target client]:client:__tmux-clients'
 }
 
-function _tmux-lock-server() {
-    [[ -n ${tmux_describe} ]] && print "Lock all clients attached to the server" && return
-    __tmux-nothing-else
+_tmux-find-window() {
+  [[ -n ${tmux_describe} ]] && print "search for a pattern in windows" && return
+  _arguments -s \
+    '-C[match visible contents]' \
+    '-F+[specify output format]:format:__tmux-format' \
+    '-N[match window name]' \
+    '-T[match window title]' \
+    '-t+[specify target window]:window:__tmux-windows' \
+    ':window search pattern'
 }
 
-function _tmux-lock-session() {
-    [[ -n ${tmux_describe} ]] && print "Lock all clients attached to a session" && return
-    local -a args
-    args=('-t[choose a session]:sessions:__tmux-sessions')
-    _arguments : ${args} && return
-}
-
-function _tmux-move-pane() {
-    [[ -n ${tmux_describe} ]] && print "Move a pane into a new space" && return
-    local -a args
-    args=(
-        '-b[join source pane left of or above target pane]'
-        '-d[do not make the new window become the active one]'
-        '-h[split horizontally]'
-        '-v[split vertically]'
-        '-l[define new pane'\''s size]: :_guard "[0-9]#" "numeric value"'
-        '-p[define new pane'\''s size in percent]: :_guard "[0-9]#" "numeric value"'
-        '-s[choose source pane]:window:__tmux-panes'
-        '-t[choose target pane]:window:__tmux-panes'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-move-window() {
-    [[ -n ${tmux_describe} ]] && print "Move a window to another" && return
-    local -a args
-    args=(
-        '-d[do not make the new window become the active one]'
-        '-k[kill the target window if it exists]'
-        '-s[choose source window]:window:__tmux-windows'
-        '-r[renumber windows in session in sequential order]'
-        '-t[choose destination window]:window:__tmux-windows'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-new-session() {
-    [[ -n ${tmux_describe} ]] && print "Create a new session" && return
-    local -a args
-    args=(
-        '-A[attach to existing session if it already exists]'
-        '-c[specify working directory for the session]:directory:_path_files -g "*(-/)"'
-        '-d[do not attach new session to current terminal]'
-        '-D[in case of -A behave like attach-session'\''s -d]'
-        '-F[specify format of output]:format:__tmux-format'
-        '-n[name the initial window]:window name'
-        '-P[print information about new session after it is created]'
-        '-s[name the session]:session name:__tmux-sessions'
-        '-t[specify target session]:sessions:__tmux-sessions'
-        '-x[specify width]:width:_guard "[0-9]#" "numeric value"'
-        '-y[specify height]:height:_guard "[0-9]#" "numeric value"'
-        '*:: :_cmdstring'
-    )
-    _arguments -s : ${args}
-}
-
-function _tmux-new-window() {
-    [[ -n ${tmux_describe} ]] && print "Create a new window" && return
-    local -a args
-    args=(
-        '-a[insert new window at next free index from -t]'
-        '-c[specify working directory for the session]:directory:_path_files -g "*(-/)"'
-        '-d[do not make the new window become the active one]'
-        '-F[specify format of output]:format:__tmux-format'
-        '-k[destroy it if the specified window exists]'
-        '-n[specify a window name]:window name:'
-        '-P[print information about new window after it is created]'
-        '-t[specify target window]:windows:__tmux-windows'
-        '*:: :_cmdstring'
-    )
-    _arguments : ${args}
+_tmux-has-session() {
+  [[ -n ${tmux_describe} ]] && print "check and report if a session exists on the server" && return
+  _arguments '-t+[specify target session]:session:__tmux-sessions'
 }
 
-function _tmux-next-layout() {
-    [[ -n ${tmux_describe} ]] && print "Move a window to the next layout" && return
-    local -a args
-    args=('-t[choose target window]:window:__tmux-windows')
-    _arguments : ${args}
+_tmux-if-shell() {
+  [[ -n ${tmux_describe} ]] && print "execute a tmux command if a shell-command succeeded" && return
+  local curcontext="$curcontext" state line ret=1
+  _arguments -C -s \
+    '-b[run shell command in background]' \
+    "-F[don't execute shell command but use it as a string-value]" \
+    '-t+[specify target pane]:pane:__tmux-panes' \
+    '1:shell command:_cmdstring' \
+    '2:tmux command (success):->tmuxcmd' \
+    '3:tmux command (failure):->tmuxcmd' && ret=0
+  if [[ -n $state ]]; then
+    compset -q
+    _tmux && ret=0
+  fi
 }
 
-function _tmux-next-window() {
-    [[ -n ${tmux_describe} ]] && print "Move to the next window in a session" && return
-    local -a args
-    args=(
-        '-a[move to the next window with an alert]'
-        '-t[choose target session]:session:__tmux-sessions'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-paste-buffer() {
-    [[ -n ${tmux_describe} ]] && print "Insert a paste buffer into the window" && return
-    local -a args
-    args=(
-        '-b[choose buffer]:source buffer:__tmux-buffers'
-        '-d[remove buffer from stack after pasting]'
-        '-p[use bracketed paste mode if the application requested it]'
-        '-r[do not replace LF with CR when pasting]'
-        '-s[specify separator]:separator:'
-        '-t[choose target window]:window:__tmux-windows'
-    )
-    _arguments : ${args}
+_tmux-join-pane() {
+  [[ -n ${tmux_describe} ]] && print "split a pane and move an existing one into the new space" && return
+  _arguments -s \
+    '-b[join source pane left of or above target pane]' \
+    "-d[don't make the new window become the active one]" \
+    '-h[split horizontally]' \
+    '-v[split vertically]' \
+    "-l+[define new pane's size]: :_guard '[0-9]#' 'numeric value'" \
+    "-p+[define new pane's size in percent]: :_guard '[0-9]#' 'numeric value'" \
+    '-s+[specify source pane]:pane:__tmux-panes' \
+    '-t+[specify target pane]:pane:__tmux-panes'
 }
 
-function _tmux-pipe-pane() {
-    [[ -n ${tmux_describe} ]] && print "Pipe output from a pane to a shell command" && return
-    local state
-    args=(
-        '-o[only open a pipe if none is currently opened]'
-        '-t[choose target pane]:pane:__tmux-panes'
-        '*:: :->cmd'
-    )
-    _arguments : ${args} && return
-    __tmux-lastarg ${state} 'cmd' 1 "command string"
+_tmux-kill-pane() {
+  [[ -n ${tmux_describe} ]] && print "destroy a given pane" && return
+  _arguments -s \
+    '-a[kill all panes except the one specified by -t]' \
+    '-t+[specify target pane]:pane:__tmux-panes'
 }
 
-function _tmux-previous-layout() {
-    [[ -n ${tmux_describe} ]] && print "Move a window to the previous layout" && return
-    local -a args
-    args=('-t[choose target window]:window:__tmux-windows')
-    _arguments : ${args}
+_tmux-kill-server() {
+  [[ -n ${tmux_describe} ]] && print "kill clients, sessions and server" && return
+  __tmux-nothing-else
 }
 
-function _tmux-previous-window() {
-    [[ -n ${tmux_describe} ]] && print "Move to the previous window in a session" && return
-    local -a args
-    args=(
-        '-a[move to the previous window with an alert]'
-        '-t[choose target session]:session:__tmux-sessions'
-    )
-    _arguments : ${args}
+_tmux-kill-session() {
+  [[ -n ${tmux_describe} ]] && print "destroy a given session" && return
+  _arguments -s \
+    '-a[kill all session except the one specified by -t]' \
+    '-t+[specify target session]:session:__tmux-sessions' \
+    '-C[clear alerts (bell, activity, silence) in all windows linked to the session]'
 }
 
-function _tmux-refresh-client() {
-    [[ -n ${tmux_describe} ]] && print "Refresh a client" && return
-    local -a args
-    args=(
-        '-S[Only update the client'\''s status bar]'
-        '-t[choose target client]:client:__tmux-clients'
-    )
-    _arguments : ${args}
+_tmux-kill-window() {
+  [[ -n ${tmux_describe} ]] && print "destroy a given window" && return
+  _arguments -s \
+    '-a[kill all windows except the one specified by -t]' \
+    '-t+[specify target window]:window:__tmux-windows'
 }
 
-function _tmux-rename-session() {
-    [[ -n ${tmux_describe} ]] && print "Rename a session" && return
-    local state
-    args=(
-        '-t[choose target session]:session:__tmux-sessions'
-        '*:: :->name'
-    )
-    _arguments : ${args} && return
-    __tmux-lastarg ${state} 'name' 1 "new session name"
+_tmux-last-pane() {
+  [[ -n ${tmux_describe} ]] && print "select the previously selected pane" && return
+  _arguments -s \
+    '-d[disable input to the pane]' \
+    '-e[enable input to the pane]' \
+    '-t+[specify session]:session:__tmux-sessions'
 }
 
-function _tmux-rename-window() {
-    [[ -n ${tmux_describe} ]] && print "Rename a window" && return
-    local state
-    args=(
-        '-t[choose target window]:window:__tmux-windows'
-        '*:: :->name'
-    )
-    _arguments : ${args} && return
-    __tmux-lastarg ${state} 'name' 1 "new window name"
-}
-
-function _tmux-resize-pane() {
-    [[ -n ${tmux_describe} ]] && print "Resize a pane" && return
-    args=(
-        '-D[resize downward]'
-        '-L[resize to the left]'
-        '-R[resize to the right]'
-        '-U[resize upward]'
-        '-t[choose target pane]:pane:__tmux-panes'
-        '-x[specify width]:width:_guard "[0-9]#" "numeric value"'
-        '-y[specify height]:height:_guard "[0-9]#" "numeric value"'
-        '-Z[toggle zoom of pane]'
-        '1::adjustment (defaults to one):_guard "[0-9]#" "numeric value"'
-    )
-    _arguments : ${args}
+_tmux-last-window() {
+  [[ -n ${tmux_describe} ]] && print "select the previously selected window" && return
+  _arguments '-t+[specify session]:session:__tmux-sessions'
 }
 
-function _tmux-respawn-pane() {
-    [[ -n ${tmux_describe} ]] && print "Reuse a pane in which a command has exited" && return
-    local -a args
-    args=(
-        '-k[kill window if it is in use]'
-        '-t[choose target pane]:window:__tmux-pane'
-        '*::command:_cmdstring'
-    )
-    _arguments : ${args}
+_tmux-link-window() {
+  [[ -n ${tmux_describe} ]] && print "link a window to another" && return
+  _arguments -s \
+    "-d[don't make the new window become the active one]" \
+    '-k[kill the target window if it exists]' \
+    '-s+[specify source window]:window:__tmux-windows' \
+    '-t+[specify destination window]:window:__tmux-windows'
 }
 
-function _tmux-respawn-window() {
-    [[ -n ${tmux_describe} ]] && print "Reuse a window in which a command has exited" && return
-    local -a args
-    args=(
-        '-k[kill window if it is in use]'
-        '-t[choose target window]:window:__tmux-windows'
-        '*::command:_cmdstring'
-    )
-    _arguments : ${args}
+_tmux-list-buffers() {
+  [[ -n ${tmux_describe} ]] && print "list paste buffers of a session" && return
+  _arguments '-F+[specify output format]:format:__tmux-format' 
 }
 
-function _tmux-rotate-window() {
-    [[ -n ${tmux_describe} ]] && print "Rotate positions of panes in a window" && return
-    local -a args
-    args=(
-        '-D[rotate downward]'
-        '-U[rotate upward]'
-        '-t[choose target window]:window:__tmux-windows'
-    )
-    _arguments : ${args}
+_tmux-list-clients() {
+  [[ -n ${tmux_describe} ]] && print "list clients attached to server" && return
+  _arguments -s \
+    '-F+[specify output format]:format:__tmux-format' \
+    '-t+[specify session]:session:__tmux-sessions'
+}
+
+_tmux-list-commands() {
+  [[ -n ${tmux_describe} ]] && print "list supported sub-commands" && return
+  _arguments '-F+[specify format]:format'
 }
-
-function _tmux-run-shell() {
-    [[ -n ${tmux_describe} ]] && print "Execute a command without creating a new window" && return
-    local -a args
-    args=(
-        '-b[run shell command in background]'
-        '-t[choose target pane]:pane:__tmux-panes'
-        '*::command:_cmdstring'
-    )
-    _arguments : ${args}
+
+_tmux-list-keys() {
+  [[ -n ${tmux_describe} ]] && print "list all key-bindings" && return
+  _arguments -s \
+    '-t+[specify mode table]:mode table:__tmux-mode-tables' \
+    '-T+[specify key table]:key table'
+}
+
+_tmux-list-panes() {
+  [[ -n ${tmux_describe} ]] && print "list panes of a window" && return
+  local -a args
+  if (( ${+words[(r)-*s*]} )); then
+    args=( '-t+[specify target session]:session:__tmux-sessions' )
+  else
+    args=( '-t+[specify target window]:window:__tmux-windows' )
+  fi
+  _arguments -s $args \
+    '-a[list all panes the server possesses]' \
+    '-F+[specify output format]:format:__tmux-format' \
+    '-s[if specified, -t chooses a session]'
 }
-
-function _tmux-save-buffer() {
-    [[ -n ${tmux_describe} ]] && print "Save a paste buffer to a file" && return
-    local -a args
-
-    args=(
-        '-a[append to rather than overwriting file]'
-        '-b[choose a target buffer index]:buffer:__tmux-buffers'
-    )
-    _arguments : ${args} && return
+
+_tmux-list-sessions() {
+  [[ -n ${tmux_describe} ]] && print "list sessions managed by server" && return
+  _arguments '-F+[specify output format]:format:__tmux-format'
 }
 
-function _tmux-select-layout() {
-    [[ -n ${tmux_describe} ]] && print "Choose a layout for a window" && return
-    args=(
-        '-n[behave like next-layout]'
-        '-p[behave like previous-layout]'
-        '-t[choose a target window]:target window:__tmux-windows'
-        '*::layout name:__tmux-layouts'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-select-pane() {
-    [[ -n ${tmux_describe} ]] && print "Make a pane the active one in the window" && return
-    local -a args
-    args=(
-        '-D[Move to the pane down of this]'
-        '-d[disable input to the pane]'
-        '-e[enable input to the pane]'
-        '-l[behave like last-pane]'
-        '-L[Move to the pane left of this]'
-        '-R[Move to the pane right of this]'
-        '-U[Move to the pane above this]'
-        '-t[choose a target pane]:panes:__tmux-panes'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-select-window() {
-    [[ -n ${tmux_describe} ]] && print "Select a window" && return
-    local -a args
-    args=(
-        '-l[behave like last-window]'
-        '-n[behave like next-window]'
-        '-p[behave like previous-window]'
-        '-T[if selected window is the current behave like last-window]'
-        '-t[choose a target window]:windows:__tmux-windows'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-send-keys() {
-    [[ -n ${tmux_describe} ]] && print "Send key(s) to a window" && return
-    local curcontext="${curcontext}" state
-    local -a args
-    args=(
-        '-l[disable key name lookup and send data literally]'
-        '-R[reset terminal state]'
-        '-t[choose a target pane]:panes:__tmux-panes'
-        '*:: :->key'
-    )
-    _arguments : ${args} && return
-    __tmux-lastarg ${state} 'key' 1 "key"
+_tmux-list-windows() {
+  [[ -n ${tmux_describe} ]] && print "list windows of a session" && return
+  _arguments -s \
+    '-a[list all windows the tmux server possesses]' \
+    '-F[specify output format]:format:__tmux-format' \
+    '-t+[specify session]:session:__tmux-sessions'
 }
 
-function _tmux-send-prefix() {
-    [[ -n ${tmux_describe} ]] && print "Send the prefix key to a window" && return
-    local -a args
-    args=(
-        '-2[send secondary prefix key]'
-        '-t[choose a target pane]:panes:__tmux-panes'
-    )
-    _arguments : ${args}
+_tmux-load-buffer() {
+  [[ -n ${tmux_describe} ]] && print "load a file into a paste buffer" && return
+  _arguments -A "-*" -S \
+    '-b+[specify target buffer name]:buffer:__tmux-buffers' \
+    '1:file:_files'
 }
 
-function _tmux-server-info() {
-    [[ -n ${tmux_describe} ]] && print "Show server information" && return
-    __tmux-nothing-else
+_tmux-lock-client() {
+  [[ -n ${tmux_describe} ]] && print "lock a client" && return
+  _arguments '-t+[specify client]:client:__tmux-clients'
 }
 
-function _tmux-set-buffer() {
-    [[ -n ${tmux_describe} ]] && print "Set contents of a paster buffer" && return
-    local state
-    local -a args
-    args=(
-        '-a[append to rather than overwriting target buffer]'
-        '-b[choose a target buffer index]:panes:__tmux-buffer'
-        '-n[specify new buffer name]:buffer-name:'
-        '*:: :->data'
-    )
-    _arguments : ${args} && return
-    __tmux-lastarg ${state} 'data' 1 "data"
-}
-
-function _tmux-set-environment() {
-    [[ -n ${tmux_describe} ]] && print "(Un)Set an environment variable" && return
-    local state
-    local -a args
-    args=(
-        '-g[modify global environment]'
-        '-r[remove variable before starting new processes]'
-        '-u[unset a variable]'
-        '-t[choose a target session]:target session:__tmux-sessions'
-        '*:: :->name_or_value'
-    )
-    _arguments -C : ${args}
-
-    case ${state} in
-        name_or_value)
-            if (( CURRENT == 1 )); then
-                _message 'name'
-            elif (( CURRENT == 2 )); then
-                _message 'value'
-            else
-                __tmux-nothing-else
-            fi
-            ;;
-    esac
+_tmux-lock-server() {
+  [[ -n ${tmux_describe} ]] && print "lock all clients attached to the server" && return
+  __tmux-nothing-else
 }
 
-function _tmux-set-option() {
-    [[ -n ${tmux_describe} ]] && print "Set a session option" && return
-    local mode
-    local -a args
-    args=(
-        '-a[append to string options]'
-        '-g[set a global session option]'
-        '-u[unset a non-global option]'
-        '-w[change window (not session) options]'
-        '-s[change server (not session) options]'
-        '-t[choose a target session]:target session:__tmux-sessions'
-        '*:: :->name_or_value'
-    )
-    if __tmux-got-option-already -w; then
-        mode=window
-    elif __tmux-got-option-already -s; then
-        mode=server
-    else
-        mode=session
-    fi
-    _arguments -C : ${args}
-    __tmux-options-complete ${mode} ${state}
-}
-
-function _tmux-set-window-option() {
-    [[ -n ${tmux_describe} ]] && print "Set a window option" && return
-    local -a args
-    args=(
-        '-a[append to string options]'
-        '-g[set a global window option]'
-        '-u[unset a non-global option]'
-        '-t[choose a target window]:target window:__tmux-windows'
-        '*:: :->name_or_value'
-    )
-    _arguments -C : ${args}
-    __tmux-options-complete window ${state}
+_tmux-lock-session() {
+  [[ -n ${tmux_describe} ]] && print "lock all clients attached to a session" && return
+  _arguments '-t+[specify session]:session:__tmux-sessions'
 }
 
-function _tmux-show-buffer() {
-    [[ -n ${tmux_describe} ]] && print "Display the contents of a paste buffer" && return
-    local -a args
-    args=('-b[choose a target buffer index]:panes:->buffer')
-    _arguments : ${args} && return
+_tmux-move-pane() {
+  [[ -n ${tmux_describe} ]] && print "move a pane into a new space" && return
+  _arguments -s \
+    '-b[join source pane left of or above target pane]' \
+    "-d[don't make the new window become the active one]" \
+    '-h[split horizontally]' \
+    '-v[split vertically]' \
+    "-l+[define new pane's size]:numeric value" \
+    "-p+[define new pane's size in percent]:numeric value" \
+    '-s+[specify source pane]:pane:__tmux-panes' \
+    '-t+[specify target pane]:pane:__tmux-panes'
 }
 
-function _tmux-show-environment() {
-    [[ -n ${tmux_describe} ]] && print "Display the environment" && return
-    local -a args
-    args=(
-        '-g[show global environment]'
-        '-t[choose a target session]:target session:__tmux-sessions'
-    )
-    _arguments : ${args}
+_tmux-move-window() {
+  [[ -n ${tmux_describe} ]] && print "move a window to another" && return
+  _arguments -s \
+    "-d[don't make the new window become the active one]" \
+    '-k[kill the target window if it exists]' \
+    '-s+[specify source window]:window:__tmux-windows' \
+    '-r[renumber windows in session in sequential order]' \
+    '-t+[specify destination window]:window:__tmux-windows'
 }
 
-function _tmux-show-messages() {
-    [[ -n ${tmux_describe} ]] && print "Show client"\'"s message log" && return
-    local -a args
-    args=(
-        '-I[show debugging information about the tmux server]'
-        '-J[show debugging information about running jobs]'
-        '-T[show debugging information about involved terminals]'
-        '-t[choose target client]:client:__tmux-clients'
-    )
-    _arguments : ${args}
-}
+_tmux-new-session() {
+  [[ -n ${tmux_describe} ]] && print "create a new session" && return
+  _arguments -s -A "-*" -S \
+    '-A[attach to existing session if it already exists]' \
+    '-c+[specify working directory for the session]:directory:_directories' \
+    "-d[don't attach new session to current terminal]" \
+    "-D[in case of -A behave like attach-session's -d]" \
+    "-E[don't apply update-environment option]" \
+    '-F+[specify output format]:format:__tmux-format' \
+    '-n+[specify initial window name]:window name' \
+    '-P[print information about new session after it is created]' \
+    '-s+[name the session]:session name:__tmux-sessions' \
+    '-t+[specify target session]:session:__tmux-sessions' \
+    '-x[specify width]:width' \
+    '-y[specify height]:height' \
+    '*:: :_cmdstring'
+}
+
+_tmux-new-window() {
+  [[ -n ${tmux_describe} ]] && print "create a new window" && return
+  _arguments -s -A "-*" -S \
+    '-a[insert new window at next free index from -t]' \
+    '-c+[specify working directory for the session]:directory:_directories' \
+    "-d[don't make the new window become the active one]" \
+    '-F+[specify output format]:format:__tmux-format' \
+    '-k[destroy it if the specified window exists]' \
+    '-n+[specify a window name]:window name' \
+    '-P[print information about new window after it is created]' \
+    '-t+[specify target window]:window:__tmux-windows' \
+    '*:: :_cmdstring'
+}
+
+_tmux-next-layout() {
+  [[ -n ${tmux_describe} ]] && print "move a window to the next layout" && return
+  _arguments '-t+[specify target window]:window:__tmux-windows'
+}
+
+_tmux-next-window() {
+  [[ -n ${tmux_describe} ]] && print "move to the next window in a session" && return
+  _arguments -s \
+    '-a[move to the next window with an alert]' \
+    '-t+[specify target session]:session:__tmux-sessions'
+}
+
+_tmux-paste-buffer() {
+  [[ -n ${tmux_describe} ]] && print "insert a paste buffer into the window" && return
+  _arguments -s \
+    '-b+[specify buffer]:source buffer:__tmux-buffers' \
+    '-d[remove buffer from stack after pasting]' \
+    '-p[use bracketed paste mode if the application requested it]' \
+    "-r[don't replace LF with CR when pasting]" \
+    '-s+[specify separator]:separator' \
+    '-t+[specify target window]:window:__tmux-windows'
+}
+
+_tmux-pipe-pane() {
+  [[ -n ${tmux_describe} ]] && print "pipe output from a pane to a shell command" && return
+  _arguments -s -A "-*" -S \
+    '-o[only open a pipe if none is currently opened]' \
+    '-t+[specify target pane]:pane:__tmux-panes' \
+    ':shell command:_cmdstring'
+}
+
+_tmux-previous-layout() {
+  [[ -n ${tmux_describe} ]] && print "move a window to the previous layout" && return
+  _arguments '-t+[specify target window]:window:__tmux-windows'
+}
+
+_tmux-previous-window() {
+  [[ -n ${tmux_describe} ]] && print "move to the previous window in a session" && return
+  _arguments -s \
+    '-a[move to the previous window with an alert]' \
+    '-t+[specify target session]:session:__tmux-sessions'
+}
+
+_tmux-refresh-client() {
+  [[ -n ${tmux_describe} ]] && print "refresh a client" && return
+  _arguments -s \
+    "-S[only update the client's status bar]" \
+    '-t+[specify target client]:client:__tmux-clients' \
+    '-C+:size'
+}
+
+_tmux-rename-session() {
+  [[ -n ${tmux_describe} ]] && print "rename a session" && return
+  _arguments -s -A "-*" -S \
+    '-t+[specify target session]:session:__tmux-sessions' \
+    ':new session name'
+}
+
+_tmux-rename-window() {
+  [[ -n ${tmux_describe} ]] && print "rename a window" && return
+  _arguments -s -A "-*" -S \
+    '-t+[specify target window]:window:__tmux-windows' \
+    ':new window name'
+}
+
+_tmux-resize-pane() {
+  [[ -n ${tmux_describe} ]] && print "resize a pane" && return
+  _arguments -s -A "-*" -S \
+    '-D[resize downward]' \
+    '-L[resize to the left]' \
+    '-M[begin mouse resizing]' \
+    '-R[resize to the right]' \
+    '-U[resize upward]' \
+    '-t+[specify target pane]:pane:__tmux-panes' \
+    '-x+[specify width]:width' \
+    '-y+[specify height]:height' \
+    '-Z[toggle zoom of pane]' \
+    ':adjustment'
+}
+
+_tmux-respawn-pane() {
+  [[ -n ${tmux_describe} ]] && print "reuse a pane in which a command has exited" && return
+  _arguments -s -A "-*" -S \
+    '-k[kill window if it is in use]' \
+    '-t+[specify target pane]:pane:__tmux-pane' \
+    ':command:_cmdstring'
+}
+
+_tmux-respawn-window() {
+  [[ -n ${tmux_describe} ]] && print "reuse a window in which a command has exited" && return
+  _arguments -s -A "-*" -S \
+    '-k[kill window if it is in use]' \
+    '-t+[specify target window]:window:__tmux-windows' \
+    ':command:_cmdstring'
+}
+
+_tmux-rotate-window() {
+  [[ -n ${tmux_describe} ]] && print "rotate positions of panes in a window" && return
+  _arguments -s \
+    '-D[rotate downward]' \
+    '-U[rotate upward]' \
+    '-t+[specify target window]:window:__tmux-windows'
+}
+
+_tmux-run-shell() {
+  [[ -n ${tmux_describe} ]] && print "execute a command without creating a new window" && return
+  _arguments -s -A "-*" -S \
+    '-b[run shell command in background]' \
+    '-t+[specify target pane]:pane:__tmux-panes' \
+    ':command:_cmdstring'
+}
+
+_tmux-save-buffer() {
+  [[ -n ${tmux_describe} ]] && print "save a paste buffer to a file" && return
+  _arguments -s \
+    '-a[append to rather than overwriting file]' \
+    '-b+[specify a target buffer index]:buffer:__tmux-buffers'
+}
+
+_tmux-select-layout() {
+  [[ -n ${tmux_describe} ]] && print "choose a layout for a window" && return
+  _arguments -s -A "-*" -S \
+    '-n[behave like next-layout]' \
+    '-o[revert to previous layout]' \
+    '-p[behave like previous-layout]' \
+    '-t+[specify a target window]:target window:__tmux-windows' \
+    ':layout:(even-horizontal even-vertical main-horizontal main-vertical tiled)'
+}
+
+_tmux-select-pane() {
+  [[ -n ${tmux_describe} ]] && print "make a pane the active one in the window" && return
+  _arguments -s \
+    '-D[move to the pane below target]' \
+    '-d[disable input to the pane]' \
+    '-e[enable input to the pane]' \
+    '-g[show current pane style]' \
+    '-l[behave like last-pane]' \
+    '-L[move to the pane left of target]' \
+    '-M[clear marked pane]' \
+    '-m[set marked pane]' \
+    '-R[move to the pane right of target]' \
+    '-U[move to the pane above target]' \
+    '-P+[set pane style]:style:__tmux-style' \
+    '-t+[specify target pane]:pane:__tmux-panes'
+}
+
+_tmux-select-window() {
+  [[ -n ${tmux_describe} ]] && print "select a window" && return
+  _arguments -s \
+    '-l[behave like last-window]' \
+    '-n[behave like next-window]' \
+    '-p[behave like previous-window]' \
+    '-T[if selected window is the current behave like last-window]' \
+    '-t+[specify target window]:window:__tmux-windows'
+}
+
+_tmux-send-keys() {
+  [[ -n ${tmux_describe} ]] && print "send key(s) to a window" && return
+  _arguments -s -A "-*" -S \
+    '-l[disable key name lookup and send data literally]' \
+    '-R[reset terminal state]' \
+    '-M[pass through a mouse event]' \
+    '-t+[specify target pane]:pane:__tmux-panes' \
+    '*:key'
+}
+
+_tmux-send-prefix() {
+  [[ -n ${tmux_describe} ]] && print "send the prefix key to a window" && return
+  _arguments -s \
+    '-2[send secondary prefix key]' \
+    '-t+[specify target pane]:pane:__tmux-panes'
+}
+
+_tmux-server-info() {
+  [[ -n ${tmux_describe} ]] && print "show server information" && return
+  __tmux-nothing-else
+}
+
+_tmux-set-buffer() {
+  [[ -n ${tmux_describe} ]] && print "set contents of a paster buffer" && return
+  _arguments -s -A "-*" -S \
+    '-a[append to rather than overwriting target buffer]' \
+    '-b+[specify target buffer index]:pane:__tmux-buffers' \
+    '-n+[specify new buffer name]:buffer-name' \
+    ':data'
+}
+
+_tmux-set-environment() {
+  [[ -n ${tmux_describe} ]] && print "(un)set an environment variable" && return
+  _arguments -s -A "-*" -S \
+    '-g[modify global environment]' \
+    '-r[remove variable before starting new processes]' \
+    '-u[unset a variable]' \
+    '-t[specify target session]:target session:__tmux-sessions' \
+    ':name' ':value'
+}
+
+_tmux-set-option() {
+  [[ -n ${tmux_describe} ]] && print "set a session option" && return
+  local mode=session
+  local curcontext="$curcontext" state line ret=1
+  typeset -A opt_args
+  _arguments -C -s \
+    '-a[append to string options]' \
+    '-g[set a global session option]' \
+    '-o[prevent setting of an option that is already set]' \
+    '-q[suppress errors about unknown or ambiguous options]' \
+    '-u[unset a non-global option]' \
+    '-w[change window (not session) options]' \
+    '-s[change server (not session) options]' \
+    '-t+[specify target session]:target session:__tmux-sessions' \
+    '*:: :->name_or_value' && ret=0
+
+  if (( ${+opt_args[-w]} )); then
+    mode=window
+  elif (( ${+opt_args[-s]} )); then
+    mode=server
+  fi
+  __tmux-options-complete ${mode} ${state} && ret=0
+  return ret
+}
+
+_tmux-set-window-option() {
+  [[ -n ${tmux_describe} ]] && print "set a window option" && return
+  local curcontext="$curcontext" state line ret=1
+  typeset -A opt_args
+  _arguments -C -s \
+    '-a[append to string options]' \
+    '-g[set a global window option]' \
+    '-o[prevent setting of an option that is already set]' \
+    '-q[suppress errors about unknown or ambiguous options]' \
+    '-u[unset a non-global option]' \
+    '-t+[specify target window]:target window:__tmux-windows' \
+    '*:: :->name_or_value' && ret=0
+  __tmux-options-complete window ${state} && ret=0
+  return ret
+}
+
+_tmux-set-hook() {
+  [[ -n ${tmux_describe} ]] && print "set a hook to a command" && return
+  _arguments -s \
+    '-g[add hook to global list]' \
+    '-t+[specify target session]:session:__tmux-sessions'
+    ':command'
+}
+
+_tmux-show-hooks() {
+  [[ -n ${tmux_describe} ]] && print "show the global list of hooks" && return
+  _arguments -s -S -A "-*" \
+    '-g[show global list of hooks]' \
+    '-t+[specify target session]:session:__tmux-sessions' \
+    ':hook name:(alert-activity alert-bell alert-silence client-attached client-detached client-resized pane-died pane-exited)' \
+}
+
+_tmux-show-buffer() {
+  [[ -n ${tmux_describe} ]] && print "display the contents of a paste buffer" && return
+  _arguments '-b[specify target buffer index]:pane:->buffer'
+}
+
+_tmux-show-environment() {
+  [[ -n ${tmux_describe} ]] && print "display the environment" && return
+  _arguments -s \
+    '-g[show global environment]' \
+    '-s[format output as Bourne shell commands]' \
+    '-t+[specify target session]:target session:__tmux-sessions'
+}
+
+_tmux-show-messages() {
+  [[ -n ${tmux_describe} ]] && print "show client's message log" && return
+  _arguments -s \
+    '-J[show debugging information about running jobs]' \
+    '-T[show debugging information about involved terminals]' \
+    '-t+[specify target client]:client:__tmux-clients'
+}
+
+_tmux-show-options() {
+  [[ -n ${tmux_describe} ]] && print "show session options" && return
+  _arguments -s \
+    '-g[show global options]' \
+    '-q[suppress errors about unknown or ambiguous options]' \
+    '-s[show server options]' \
+    '-v[show only the option value, not the name]' \
+    '-w[show window options]' \
+    '-t+[specify target session]:target session:__tmux-sessions'
+}
+
+_tmux-show-window-options() {
+  [[ -n ${tmux_describe} ]] && print "show window options" && return
+  _arguments -s \
+    '-g[show global options]' \
+    '-v[show only the option value, not the name]' \
+    '-t+[specify target window]:target window:__tmux-windows'
+}
+
+_tmux-source-file() {
+  [[ -n ${tmux_describe} ]] && print "execute tmux commands from a file" && return
+  _arguments \
+    "-q[don't report error if path doesn't exist]" \
+    '*:path:_directories'
+}
+
+_tmux-split-window() {
+  [[ -n ${tmux_describe} ]] && print "splits a pane into two" && return
+  _arguments -s \
+    '-b[create new pane left of or above target pane]' \
+    "-d[don't make the new window become the active one]" \
+    '-F+[specify output format]:format:__tmux-format' \
+    '-h[split horizontally]' \
+    '-v[split vertically]' \
+    "-l[define new pane's size]:size" \
+    "-p[define new pane's size in percent]:size (percentage)" \
+    '-t+[specify target pane]:pane:__tmux-panes' \
+    ':command:_cmdstring'
+  # Yes, __tmux-panes is correct here. The behaviour was changed
+  # in recent tmux versions and makes more sense. Except that
+  # changing the command's name might annoy users. So it stays like
+  # this.
+}
+
+_tmux-start-server() {
+  [[ -n ${tmux_describe} ]] && print "start a tmux server" && return
+  __tmux-nothing-else
+}
+
+_tmux-suspend-client() {
+  [[ -n ${tmux_describe} ]] && print "suspend a client" && return
+  _arguments '-t+[specify destination client]:client:__tmux-clients'
+}
+
+_tmux-swap-pane() {
+  [[ -n ${tmux_describe} ]] && print "swap two panes" && return
+  _arguments -s \
+    '-D[move pane down]' \
+    '-U[move pane up]' \
+    "-d[don't change the active pane]" \
+    '-s+[specify source pane]:pane:__tmux-panes' \
+    '-t+[specify destination pane]:pane:__tmux-panes'
+}
+
+_tmux-swap-window() {
+  [[ -n ${tmux_describe} ]] && print "swap two windows" && return
+  _arguments -s \
+    "-d[don't make the new window become the active one]" \
+    '-s+[specify source window]:window:__tmux-windows' \
+    '-t+[specify destination window]:window:__tmux-windows'
+}
+
+_tmux-switch-client() {
+  [[ -n ${tmux_describe} ]] && print "switch the client to another session" && return
+  _arguments -s \
+    '-c+[specify a target client]:client:__tmux-clients' \
+    "-E[don't apply update-environment option]" \
+    '-l[move client to last session]' \
+    '-n[move client to next session]' \
+    '-p[move client to previous session]' \
+    '-r[toggle read-only flag of client]' \
+    '-t+[specify target window]:window:__tmux-windows'
+}
+
+_tmux-unbind-key() {
+  [[ -n ${tmux_describe} ]] && print "unbind a key" && return
+  local curcontext="$curcontext" state line keytable
+  local -a ow
+
+  ow=( "${words[@]}" )
+  _arguments -C -s \
+    '-a[remove all key bindings]' \
+    '-c[kill the window if it is only in one session]' \
+    '-n[remove a non-prefix binding]' \
+    '-t+[specify mode table]:mode table:__tmux-mode-tables' \
+    '-T[specify key table]:key table' \
+    '*:: :->boundkeys'
 
-function _tmux-show-options() {
-    [[ -n ${tmux_describe} ]] && print "Show session options" && return
-    local -a args
-    args=(
-        '-g[show global options]'
-        '-t[choose a target session]:target session:__tmux-sessions'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-show-window-options() {
-    [[ -n ${tmux_describe} ]] && print "Show window options" && return
-    local -a args
-    args=(
-        '-g[show global options]'
-        '-t[choose a target window]:target window:__tmux-windows'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-source-file() {
-    [[ -n ${tmux_describe} ]] && print "Execute tmux commands from a file" && return
-    _files -g "*(-.)"
-}
-
-function _tmux-split-window() {
-    [[ -n ${tmux_describe} ]] && print "Splits a pane into two" && return
-    local -a args
-    args=(
-        '-b[create new pane left of or above target pane]'
-        '-d[do not make the new window become the active one]'
-        '-F[specify format of output]:format:__tmux-format'
-        '-h[split horizontally]'
-        '-v[split vertically]'
-        '-l[define new pane'\''s size]: :_guard "[0-9]#" "numeric value"'
-        '-p[define new pane'\''s size in percent]: :_guard "[0-9]#" "numeric value"'
-        # Yes, __tmux-panes is correct here. The behaviour was changed
-        # in recent tmux versions and makes more sense. Except that
-        # changing the command's name might annoy users. So it stays like
-        # this.
-        '-t[choose target pane]:window:__tmux-panes'
-        '*:: :_cmdstring'
-    )
-    _arguments : ${args} && return
-}
-
-function _tmux-start-server() {
-    [[ -n ${tmux_describe} ]] && print "Start a tmux server" && return
-    __tmux-nothing-else
-}
-
-function _tmux-suspend-client() {
-    [[ -n ${tmux_describe} ]] && print "Suspend a client" && return
-    local -a args
-    args=('-t[choose destination client]:client:__tmux-clients')
-    _arguments : ${args}
-}
-
-function _tmux-swap-pane() {
-    [[ -n ${tmux_describe} ]] && print "Swap two panes" && return
-    local -a args
-    args=(
-        '-D[move pane down]'
-        '-U[move pane up]'
-        '-d[do not change the active pane]'
-        '-s[choose source pane]:pane:__tmux-panes'
-        '-t[choose destination pane]:pane:__tmux-panes'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-swap-window() {
-    [[ -n ${tmux_describe} ]] && print "Swap two windows" && return
-    local -a args
-    args=(
-        '-d[do not make the new window become the active one]'
-        '-s[choose source window]:window:__tmux-windows'
-        '-t[choose destination window]:window:__tmux-windows'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-switch-client() {
-    [[ -n ${tmux_describe} ]] && print "Switch the client to another session" && return
-    local -a args
-    args=(
-        '-c[choose a target client]:client:__tmux-clients'
-        '-l[move client to last session]'
-        '-n[move client to next session]'
-        '-p[move client to previous session]'
-        '-r[toggle read-only flag of client]'
-        '-t[choose a target window]:window:__tmux-windows'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-unbind-key() {
-    [[ -n ${tmux_describe} ]] && print "Unbind a key" && return
-    local state keytable
-    local -a args ow
-
-    ow=( "${words[@]}" )
-    args=(
-        '-a[Remove all key bindings]'
-        '-c[kill the window if it is only in one session]'
-        '-n[remove a non-prefix binding]'
-        '-t[choose a key table]:key table:__tmux-key-tables'
-        '*:: :->boundkeys'
-    )
-    _arguments : ${args} && return
     [[ ${state} != 'boundkeys' ]] && return
     keytable="$(__tmux-get-optarg -t "${ow[@]}")"
     if [[ -n ${keytable} ]]; then
@@ -1120,39 +925,31 @@ function _tmux-unbind-key() {
     __tmux-bound-keys
 }
 
-function _tmux-unlink-window() {
-    [[ -n ${tmux_describe} ]] && print "Unlink a window" && return
-    local -a args
-    args=(
-        '-k[kill the window if it is only in one session]'
-        '-t[choose a target window]:target window:__tmux-windows'
-    )
-    _arguments : ${args}
-}
-
-function _tmux-wait-for() {
-    [[ -n ${tmux_describe} ]] && print "Wait for an event or trigger it" && return
-    local state
-    local -a args
-    args=(
-        '-L[lock the named channel]'
-        '-S[send signal to channel]'
-        '-U[unlock the named channel]'
-        '*:: :->channel'
-    )
-    _arguments : ${args} && return
-    __tmux-lastarg ${state} 'channel' 1 "event channel"
+_tmux-unlink-window() {
+  [[ -n ${tmux_describe} ]] && print "unlink a window" && return
+  _arguments -s \
+    '-k[kill the window if it is only in one session]' \
+    '-t+[specify target window]:target window:__tmux-windows'
+}
+
+_tmux-wait-for() {
+  [[ -n ${tmux_describe} ]] && print "wait for an event or trigger it" && return
+  _arguments -s \
+    '-L[lock the named channel]' \
+    '-S[send signal to channel]' \
+    '-U[unlock the named channel]' \
+    ':event channel'
 }
 
 # --- Utility functions ---
-# They should be called __tmux-*() and kept seperate from the
+# They should be called __tmux-*() and kept separate from the
 # sub-command functions.
 
 function __tmux-attributes() {
     local -a attr already
     attr=( default bright bold dim underscore blink reverse hidden italics )
     compset -P '*,'
-    already=(${(s<,>)IPREFIX})
+    already=( ${(s<,>)IPREFIX} )
     _describe -t tmux-attribute 'tmux attribute' attr -S, -F already -q
 }
 
@@ -1213,37 +1010,14 @@ function __tmux-get-optarg() {
     done
 }
 
-function __tmux-got-option-already() {
-    [[ -n ${(M)words:#$1} ]] && return 0
-    return 1
+__tmux-mode-tables() {
+  local expl
+  _description mode-tables expl 'mode table'
+  compadd "$@" "$expl[@]" vi-edit emacs-edit vi-choice emacs-choice vi-copy emacs-copy
 }
 
-function __tmux-key-tables() {
-    local expl
-    local -a tables
-    tables=( vi-edit emacs-edit vi-choice emacs-choice vi-copy emacs-copy )
-    _wanted keytable expl 'key tables' compadd ${expl} -- ${tables}
-}
-
-function __tmux-lastarg() {
-    local got_state="$1" want_state="$2" pos="$3" msg="$4"
-
-    if [[ ${want_state} == ${got_state} ]] && (( CURRENT == ${pos} )); then
-        _message ${msg}
-    else
-        __tmux-nothing-else
-    fi
-}
-
-function __tmux-layouts() {
-    local expl
-    local -a layouts
-    layouts=( even-horizontal even-vertical main-horizontal main-vertical )
-    _wanted layout expl 'layouts' compadd ${expl} -- ${layouts}
-}
-
-function __tmux-nothing-else() {
-    _message "no further arguments"
+__tmux-nothing-else() {
+  _message "no further arguments"
 }
 
 function __tmux-option-guard() {
@@ -1273,12 +1047,10 @@ function __tmux-option-guard() {
             'message-command-style:__tmux-style'
             'message-style:__tmux-style'
             'mouse:DESC:on off'
-            'mouse-utf8:DESC:on off'
             'prefix:MSG:primary prefix key'
             'prefix2:MSG:secondary prefix key'
             'renumber-windows:DESC:on off'
             'repeat-time:'${int_guard}
-            'set-remain-on-exit:DESC:on off'
             'set-titles:DESC:on off'
             'set-titles-string:MSG:title format string'
             'status:DESC:on off'
@@ -1293,7 +1065,6 @@ function __tmux-option-guard() {
             'status-right-length:'${int_guard}
             'status-right-style:__tmux-style'
             'status-style:__tmux-style'
-            'status-utf8:DESC:on off'
             'update-environment:MSG:string listing env. variables'
             'visual-activity:DESC:on off'
             'visual-bell:DESC:on off'
@@ -1334,10 +1105,11 @@ function __tmux-option-guard() {
             'other-pane-width:'${int_guard}
             'pane-active-border-style:__tmux-style'
             'pane-base-index:'${int_guard}
+            'pane-border-format:MSG:pane border status string'
+            'pane-border-status:DESC:off top bottom'
             'pane-border-style:__tmux-style'
             'remain-on-exit:DESC:on off'
             'synchronize-panes:DESC:on off'
-            'utf8:DESC:on off'
             'window-active-style:__tmux-style'
             'window-status-activity-style:__tmux-style'
             'window-status-bell-style:__tmux-style'
@@ -1348,7 +1120,7 @@ function __tmux-option-guard() {
             'window-status-separator:MSG:separator string'
             'window-status-style:__tmux-style'
             'window-style:__tmux-style'
-            'wrap-seach:DESC:on off'
+            'wrap-search:DESC:on off'
             'xterm-keys:DESC:on off'
         )
     fi
@@ -1391,17 +1163,16 @@ function __tmux-session-options() {
         'display-panes-time:time (in msecs) of display-panes output'
         'display-time:time (in msecs) messages are displayed'
         'history-limit:number of copy-mode lines per window'
+        'key-table:default key table'
         'lock-after-time:lock sessions after N seconds'
         'lock-command:command to run for locking a client'
         'message-command-style:status line message command style'
         'message-style:status line message style'
         'mouse:enable mouse support'
-        'mouse-utf8:request utf8 mouse support'
         'prefix:primary prefix key'
         'prefix2:secondary prefix key'
         'renumber-windows:renumber windows if a window is closed'
         'repeat-time:time for multiple commands without prefix-key presses'
-        'set-remain-on-exit:set remain-on-exit window option'
         'set-titles:try to set xterm window titles'
         'set-titles-string:format used by set-titles'
         'status:show or hide the status bar'
@@ -1416,8 +1187,7 @@ function __tmux-session-options() {
         'status-right-length:maximum length of the right part of the status bar'
         'status-right-style:style of right part of status line'
         'status-style:style status line'
-        'status-utf8:assume UTF-8 sequences to appear in status bar'
-        'update-environment:list of variables to be copied to a session'\''s environment'
+        "update-environment:list of variables to be copied to a session's environment"
         'visual-activity:display status line messages upon activity'
         'visual-bell:use visual bell instead of audible'
         'visual-silence:print a message if monitor-silence is on'
@@ -1485,7 +1255,6 @@ function __tmux-server-options() {
 }
 
 function __tmux-sessions() {
-    local expl
     local -a sessions
     sessions=( ${${(f)"$(command tmux 2> /dev/null list-sessions)"}/:[ $'\t']##/:} )
     _describe -t sessions 'sessions' sessions "$@"
@@ -1495,7 +1264,7 @@ function __tmux-socket-name() {
     local expl sdir
     local curcontext="${curcontext}"
     local -a socks
-    zstyle -s ":completion:${curcontext}:sockets" socketdir sdir || sdir="/tmp/tmux-${UID}"
+    zstyle -s ":completion:${curcontext}:sockets" socketdir sdir || sdir="${TMUX_TMPDIR:-/tmp}/tmux-${UID}"
     socks=(${sdir}/*(=:t))
     _wanted socket expl 'socket name' compadd ${expl} -- ${socks}
 }
@@ -1526,10 +1295,11 @@ function __tmux-window-options() {
         'other-pane-width:width of other panes'
         'pane-active-border-style:style of border of active pane'
         'pane-base-index:integer at which to start indexing panes'
+        'pane-border-format:set pane border format string'
+        'pane-border-status:turn border status off or set its position'
         'pane-border-style:style of border pane'
-        'remain-on-exit:do not destroy windows after the program exits'
+        "remain-on-exit:don't destroy windows after the program exits"
         'synchronize-panes:send input to all panes of a window'
-        'utf8:assume UTF-8 sequences to appear in a window'
         'window-active-style:style of active window'
         'window-status-activity-style:style of status bar activity tag'
         'window-status-bell-style:style of status bar bell tag'
@@ -1564,65 +1334,58 @@ function __tmux-windows() {
 }
 
 # And here is the actual _tmux(), that puts it all together:
-function _tmux() {
-    local curcontext="${curcontext}"
-    local mode state ret=1
-    local -a args
-    local tmuxcommand
-    local tmux_describe=
-
-    args=(
-        '-2[force using 256 colours]'
-        '-8[force using 88 colours]'
-        '-c[execute a shell command]:command name:_command_names'
-        '-C[start tmux in control mode. -CC disables echo]'
-        '-f[specify configuration file]:tmux config file:_files -g "*(-.)"'
-        '-l[behave like a login shell]'
-        '-L[specify socket name]:socket name:__tmux-socket-name'
-        '-S[specify socket path]:server socket:_path_files -g "*(=,/)"'
-        '-u[force using UTF-8]'
-        '-v[request verbose logging]'
-        '-V[report tmux version]'
-        '*:: :->subcommand_or_options'
-    )
-    _arguments -C -s -w : ${args} && ret=0
-
-    if [[ ${state} == "subcommand_or_options" ]]; then
-        if (( CURRENT == 1 )) ; then
-            zstyle -s ":completion:${curcontext}:subcommands" mode mode || mode='both'
-            if [[ ${mode} == 'commands' ]]; then
-                _describe -t subcommands 'tmux commands' _tmux_commands && ret=0
-            elif [[ ${mode} == 'aliases' ]]; then
-                _describe -t subcommands 'tmux aliases' _tmux_aliases && ret=0
-            else
-                _describe -t subcommands 'tmux commands and aliases' _tmux_commands -- _tmux_aliases && ret=0
-            fi
-        else
-            if (( ${+commands[tmux]} == 0 )); then
-                _message '`tmux'\'' not found in $path; sub-cmd completions disabled.'
-                return
-            fi
-            tmuxcommand="${words[1]}"
-            if [[ -n ${_tmux_aliasmap[$tmuxcommand]} ]] ; then
-                tmuxcommand="${_tmux_aliasmap[$tmuxcommand]}"
-            fi
-            if ! (( ${+functions[_tmux-$tmuxcommand]} )); then
-              local low high
-              low=$_tmux_commands[(i)$tmuxcommand*]
-              high=$_tmux_commands[(I)$tmuxcommand*]
-              if (( low == high )); then
-                tmuxcommand=${_tmux_commands[low]%%:*}
-              elif (( low < high )); then
-                _message -e "Ambiguous command $tmuxcommand"
-              else
-                _message -e "Subcommand $tmuxcommand not known"
-              fi
-            fi
-            curcontext="${curcontext%:*:*}:tmux-${tmuxcommand}:"
-            _call_function ret _tmux-${tmuxcommand}
-        fi
+_tmux() {
+  local curcontext="${curcontext}" state line ret=1
+  local mode
+  local tmuxcommand
+  local tmux_describe
+
+  _arguments -C -s -w \
+    '-2[force using 256 colours]' \
+    '-c[execute a shell command]:command name:_command_names' \
+    '-C[start tmux in control mode. -CC disables echo]' \
+    '-f[specify configuration file]:tmux config file:_files -g "*(-.)"' \
+    '-l[behave like a login shell]' \
+    '-L[specify socket name]:socket name:__tmux-socket-name' \
+    '-S[specify socket path]:server socket:_path_files -g "*(=,/)"' \
+    '-u[force using UTF-8]' \
+    '-v[request verbose logging]' \
+    '-V[report tmux version]' \
+    '*:: :->subcommand_or_options' && ret=0
+
+  [[ -z $state ]] && return ret
+
+  if (( CURRENT == 1 )); then
+    zstyle -s ":completion:${curcontext}:subcommands" mode mode || mode='both'
+    if [[ ${mode} == 'commands' ]]; then
+      _describe -t subcommands 'tmux commands' _tmux_commands && ret=0
+    elif [[ ${mode} == 'aliases' ]]; then
+      _describe -t subcommands 'tmux aliases' _tmux_aliases && ret=0
+    else
+      _describe -t subcommands 'tmux commands and aliases' _tmux_commands -- _tmux_aliases && ret=0
+    fi
+  else
+    tmuxcommand="${words[1]}"
+    if [[ -n ${_tmux_aliasmap[$tmuxcommand]} ]] ; then
+      tmuxcommand="${_tmux_aliasmap[$tmuxcommand]}"
+    fi
+    if ! (( ${+functions[_tmux-$tmuxcommand]} )); then
+      local low high
+      low=$_tmux_commands[(i)$tmuxcommand*]
+      high=$_tmux_commands[(I)$tmuxcommand*]
+      if (( low == high )); then
+	tmuxcommand=${_tmux_commands[low]%%:*}
+      elif (( low < high )); then
+	_message "ambiguous command $tmuxcommand"
+      else
+	_message "subcommand $tmuxcommand not known"
+	_normal && ret=0
+      fi
     fi
-    return ret
+    curcontext="${curcontext%:*}-${tmuxcommand}:"
+    _call_function ret _tmux-${tmuxcommand}
+  fi
+  return ret
 }
 
 # description generation follows; only done on 1st _tmux call.
@@ -1631,12 +1394,12 @@ local -A rev
 local tmux_describe
 tmux_describe='yes, please'
 for f in ${(k)_tmux_aliasmap} ; do
-    rev+=( ${_tmux_aliasmap[$f]} $f )
+  rev+=( ${_tmux_aliasmap[$f]} $f )
 done
 for f in ${(M)${(k)functions}:#_tmux-*} ; do
-    desc="$($f)"
-    _tmux_commands+=( "${f#_tmux-}${desc:+:$desc}" )
-    [[ -n ${rev[${f#_tmux-}]} ]] && _tmux_aliases+=( "${rev[${f#_tmux-}]}${desc:+:$desc}" )
+  desc="$($f)"
+  _tmux_commands+=( "${f#_tmux-}${desc:+:$desc}" )
+  [[ -n ${rev[${f#_tmux-}]} ]] && _tmux_aliases+=( "${rev[${f#_tmux-}]}${desc:+:$desc}" )
 done
 
 _tmux "$@"
diff --git a/Completion/Unix/Command/_top b/Completion/Unix/Command/_top
index 10c0e3481..8f81e9526 100644
--- a/Completion/Unix/Command/_top
+++ b/Completion/Unix/Command/_top
@@ -1,12 +1,16 @@
 #compdef top
 
-local specs fields
+local curcontext="$curcontext" state state_descr line ret=1
+local -A opt_args
+local -a specs fields order opts
+
+opts=( -s -w -C )
 
 case $OSTYPE in
   *linux*)
     fields=(
       '%CPU:CPU usage' '%MEM:memory usage (res)' 'CGROUPS:control groups'
-      'CODE:code size' 'COMMAND:Commane name/line' 'DATA:data + stack size'
+      'CODE:code size' 'COMMAND:Command name/line' 'DATA:data + stack size'
       'ENVIRON:environment variables' 'Flags:task flags' 'GID:group id'
       'GROUP:group name' 'NI:nice value' 'P:last used CPU (SMP)'
       'PGRP:process group id' 'PID:process id' 'PPID:parent pid' 'PR:priority'
@@ -24,78 +28,147 @@ case $OSTYPE in
       'nsUTS:UTS namespace' 'vMj:major page fault count delta'
       'vMn:minor page fault count delta'
     )
+    order=( '+:descending (default)' '-:ascending' )
     specs=(
-      '(-v -h)-'{h,v}'[show version and usage]'
+      '(-)-'{h,v}'[show version and usage]'
       '-b[batch mode]'
       '-c[command line/program name toggle]'
-      '-d[delay time interval]:interval'
+      '-d+[delay time interval]:interval'
       '-H[threads mode operation]'
       '-i[idle process toggle]'
-      '-n[number of iterations]:number of iterations'
-      '-o[override sort field]:fieldname:(( $fields ))'
+      '-n+[number of iterations]:number of iterations'
+      '-o+[override sort field]:fieldname:->sortkey'
       '-O[output field names]'
-      '*-p[monitor pids]: :_pids'
+      '*-p+[monitor pids]: :_sequence -s , _pids'
       '-s[secure mode operation]'
       '-S[cumulative time toggle]'
-      '(-U)-u[effective user filter mode]: :_users'
-      '(-u)-U[user filter mode]: :_users'
-      '-w[output width override]:number'
+      '(-U)-u+[effective user filter mode]: :_users'
+      '(-u)-U+[user filter mode]: :_users'
+      '-w+[output width override]::number'
     );;
-  freebsd*)
+  freebsd*|openbsd*)
+    fields=( cpu size res time pri pid )
     specs=(
-      '-C[CPU display mode]'
-      '-S[show system processes]'
-      '-a[display command names via argv]'
       '-b[batch mode]'
+      '-d+[show only specified number of displays then exit]:count:'
       '-H[display individual threads]'
-      '-i[interactive mode]'
       '-I[do not display idle processes]'
+      '-i[interactive mode]'
+      '-n[non-interactive mode (identical to batch mode)]'
+      '-o+[sort process display by the specified field]:field:( $fields )'
+      '-q[renice top to -20]'
+      '-S[show system processes]'
+      '-s+[specify delay interval]:seconds:'
+      '-u[do not translate uid to name]'
+      '1: :_guard "^-*" "number of processes to display"'
+    );|
+  freebsd*)
+    fields+=( threads total read write fault vcsw ivcsw jid )
+    specs+=(
+      '-C[CPU display mode]'
+      '-a[display command names via argv]'
       '-j[display the jail ID]'
       '-t[do not display the top process]'
-      '-m+[statistic type]:type:(( cpu\:default io ))'
-      '-n[non-interactive mode]'
+      '-m+[specify statistic type]:type:(( cpu\:default io ))'
       '-P[per-cpu CPU usage statistics]'
-      '-q[renice top to -20]'
-      '-u[do not translate uid to name]'
-      '-v[write version number]'
-      '-z[no not display system idle process]'
-      '-d+[number of iterations]:count:'
-      '-s+[set delay interval]:interval:'
-      '-o+[sort process display by field]:field:(
-        cpu size res time pri threads total read 
-        write fault vcsw ivcsw jid pid
-      )'
-      '-J+[show processes owned by jail]:jail:_jails -0'
-      '-U+[show processes owned by username]: :_users'
-      '1: : _message "top number of processes"'
+      '(1 -)-v[write version number and exit]'
+      '-z[do not display system idle process]'
+      '-J+[show processes owned by the specified jail]:jail:_jails -0'
+      '-U+[show processes owned by the specified username]: :_users'
     );;
   openbsd*)
-    specs=(
+    fields+=( command )
+    specs+=(
       '-1[combine CPU statistic into one line]'
-      '-b[batch mode]'
-      '-C[show command arguments as well and process name]'
-      '-d[number of iterations]:number of iterations'
-      '-g[filter processes by string]:string'
-      '-H[display process threads]'
-      '-I[do not display idle processes]'
-      '-i[interactive mode]'
-      '-n[non-interactive mode]'
-      '-o[sort display by field]:field:(
-        cpu size res time pri pid command
-      )'
-      '-p[filter by pid]: :_pids'
-      '-q[renice top to -20]'
-      '-S[show system processes]'
-      '-s[delay time interval]:interval'
-      '-U[filter processes by user]: :_users -M "L\:|-="'
-      '-u[do not map uid to usernames]'
-      '1: : _message "top number of processes"'
+      '-C[show command arguments as well as process name]'
+      '-g+[filter processes by the specified string]:string'
+      '-p+[filter by the specified pid]: :_pids'
+      '-U+[filter processes by the specified user]: :_users -M "L\:|-="'
+    );;
+  darwin*)
+    local -a modes
+    opts=( -C )   # no option stacking
+    modes=(
+      'a:count events cumulatively'
+      'd:count events relative to the previous sample'
+      'e:count events using absolute counters'
+      'n:non-event mode (default)'
+    )
+    fields=(
+      'pid:process id'
+      'command:command name'
+      'cpu:CPU usage'
+      'cpu_me:CPU time charged to me by other processes'
+      'cpu_others:CPU time charged to other processes by me'
+      'csw:number of context switches'
+      'time:execution time'
+      {threads,th}':number of threads'
+      {ports,prt}':number of Mach ports'
+      {mregion,mreg,reg}':number of memory regions'
+      'mem:internal memory size'
+      'rprvt:resident private address space size'
+      'purg:purgeable memory size'
+      'vsize:total memory size'
+      'vprvt:private address space size'
+      'kprvt:private kernel memory size'
+      'kshrd:shared kernel memory size'
+      'pgrp:process group id'
+      'ppid:parent process id'
+      {state,pstate}':process state'
+      'uid:user id'
+      {wq,'#wq',workqueue}':workqueue total/running'
+      {faults,fault}':number of page faults'
+      {cow,cow_faults}':copy-on-write faults'
+      {user,username}':username'
+      'msgsent:total number of Mach messages sent'
+      'msgrecv:total number of Mach messages received'
+      'sysbsd:total BSD syscalls'
+      'sysmach:total Mach syscalls'
+      'pageins:total pageins'
+      'boosts:number of boosts held by the process'
+    )
+    order=( '-:descending (default)' '+:ascending' )
+    specs=(
+      '-a[count events cumulatively]'
+      '-c[set event counting mode]:mode:(( $modes ))'
+      '-d[count events relative to the previous sample]'
+      '-e[count events using absolute counters]'
+      '-F[do not calculate statistics on shared libraries]'
+      '-f[calculate statistics on shared libraries]'
+      '(-)-h[print usage information and exit]'
+      '-i+[specify interval between samples for -f option]:interval'
+      '-l+[logging mode. output specified number of samples periodically]:number of samples'
+      '-ncols[output specified number of columns in logging mode]:number of columns'
+      '-n+[only display up to the specified number of processes]:number of processes:'
+      '-O+[specify the secondary sort key]:key:->sortkey'
+      '-o+[specify the primary sort key]:key:->sortkey'
+      '-R[do not traverse and report memory object map for each process]'
+      '-r[traverse and report memory object map for each process]'
+      '-S[display global statistics for swap and purgeable memory]'
+      '-s[set the delay between update]:number of seconds'
+      '-stats[only display the specified fields]:list of fields:->fieldlist'
+      '*-pid[only display the specified process]:pid:_pids'
+      {-user,-U+}'[only display processes owned by the specified user]:user:_users'
+      '-u[same as -o cpu -O time]'
     );;
 esac
 
-if (( $#specs )); then
-  _arguments -s -w : "$specs[@]"
+if (( $#specs == 0 )); then
+  _default
   return
 fi
 
-_normal
+_arguments $opts : "$specs[@]" && ret=0
+
+case $state in
+  (sortkey)
+    compset -P '(+|-)' && order=()
+    _alternative 'sort-orders:sort order:(( $order ))' \
+	'sort-keys:sort key:(( $fields ))' && ret=0
+    ;;
+  (fieldlist)
+    _sequence -s , _describe -t fields 'field' fields && ret=0
+    ;;
+esac
+
+return ret
diff --git a/Completion/Unix/Command/_touch b/Completion/Unix/Command/_touch
new file mode 100644
index 000000000..9b9144756
--- /dev/null
+++ b/Completion/Unix/Command/_touch
@@ -0,0 +1,45 @@
+#compdef touch gtouch
+
+local args variant
+_pick_variant -r variant gnu=GNU $OSTYPE --version
+
+args=(
+  '-a[change access time (atime)]'
+  '-m[change modification time (mtime)]'
+  '(-r -d --date --reference 1)-t+[use specified time]:time ([[CC]YY]MMDDhhmm[.SS])'
+)
+case $variant in
+  gnu)
+    args+=(
+      '(-c --no-create)'{-c,--no-create}"[don't create file if it doesn't exist]"
+      '(-h --no-dereference)'{-h,--no-dereference}'[act on symbolic links themselves]'
+      '(-t -d --date -r --reference)'{-r+,--reference=}'[use corresponding times from specified reference file]:file:_files'
+      '(-t -d --date -r --reference)'{-d+,--date=}'[use specified date/time]:date/time'
+      '*--time=[change specified time]:time:(atime access use mtime modify)'
+      '(-)--help[display help information]'
+      '(-)--version[display version information]'
+    )
+  ;;
+  darwin*|dragonfly*|freebsd*)
+    args+=(
+      '(-c 1)-A+[adjust time stamps by relative value]:time delta ([[-][hh]mm]SS)'
+    )
+  ;|
+  darwin*|dragonfly*|freebsd*|netbsd*)
+    args+=( '-h[act on symbolic links themselves]' )
+  ;|
+  dragonfly*|freebsd*|openbsd*|solaris*)
+    args+=( '(-r -t 1)-d+[use specified date/time]:date/time' )
+  ;|
+  darwin*|dragonfly*|freebsd*|netbsd*|solaris*)
+    args+=( '1:: :_guard "[0-9]#" "timespec (MMDDhhmm[YY])"' )
+  ;|
+  *) # not GNU
+    args=( -A "-*" $args
+      "(-A)-c[don't create file if it doesn't exist]"
+      '(-t -d 1)-r+[use corresponding times from specified reference file]:file:_files'
+    )
+  ;;
+esac
+
+_arguments -s -S $args '*:file:_files'
diff --git a/Completion/Unix/Command/_tree b/Completion/Unix/Command/_tree
index d759409b5..4fd9aed44 100644
--- a/Completion/Unix/Command/_tree
+++ b/Completion/Unix/Command/_tree
@@ -1,51 +1,54 @@
 #compdef tree
 
-# Completions for tree, version 1.5.3
-# Tree is available at
-# http://mama.indstate.edu/users/ice/tree/
-
-typeset -a opts
-
-opts=(
-'--help[verbose usage listing]'
-'--version[version of tree]'
-'-a[show all files, including hidden ones]'
-'-d[list directories only]'
-'-f[print full path prefix for each file]'
-'-i[do not print indentation lines]'
-'-l[follow symlinks that point to directories]'
-'-x[stay on current filesystem]'
-'-P[list only files matching a pattern]:pattern:'
-'-I[do not list files matching a pattern]:pattern:'
-'--noreport[do not print file and directory report at end]'
-'-p[print file type and permissions, like ls -l]'
-'-s[print size of each file in bytes]'
-'-h[print human readable file size]'
-'-u[print username]'
-'-g[print group name]'
-'-D[print date of last modification]'
-'--inodes[print inode numbers]'
-'--device[print device number to which file or directory belongs]'
-'-F[append descriptive character to end, like ls -F]'
-'-q[print non-printable characters as question mark, not caret]'
-'-N[print non-printable characters as is, not as caret]'
-'-v[sort the output as version]'
-'-r[sort output in reverse alphabetic order]'
-'-t[sort output by last modification time instead of alphabetically]'
-'--dirsfirst[list directories before files]'
-'-n[turn colorization off always, over-ridden by the -C option]'
-'-C[turn colorization on always]'
-'-A[turn on ANSI line graphics hack when printing indentation lines]'
-'-S[turn on ASCII line graphics]'
-'-L[max display depth of tree]:level:'
-'--filelimit[do not descend directories with more than number of entries]:number:'
-'-R[recursively cross down the tree and execute tree again]'
-'-H[turn on HTML output]'
-'-T[title for HTML output]'
-'--charset[character set for HTML and for line drawing]:charset:'
-'--nolinks[turn off hyperlinks in HTML output]'
-'-o[send output to file]:filename:_files'
-'*:directory:_files -/'
-)
-
-_arguments $opts
+_arguments -s -S \
+  '-a[show all files, including hidden ones]' \
+  '-d[list directories only]' \
+  '-l[follow symlinks that point to directories]' \
+  '-f[print full path prefix for each file]' \
+  '-x[stay on current filesystem]' \
+  '-L[specify max tree depth to descend]:level' \
+  '-R[recursively cross down the tree and execute tree again]' \
+  '-P[only list files matching a pattern]:pattern:_files' \
+  "-I[don't list files matching a pattern]:pattern:_files" \
+  '--ignore-case[ignore case when pattern matching]' \
+  '--matchdirs[include directory names in -P pattern matching]' \
+  '--noreport[omit file and directory report at end]' \
+  '--charset=[character set for HTML and for line drawing]:charset' \
+  "--filelimit=[don't descend directories with more than specified number of entries]:entries" \
+  '--timefmt=[use specified time format]:format:_date_formats' \
+  '-o[output to specified file]:file:_files' \
+  '--du[print directory sizes]' \
+  '--prune[exclude empty directories from the output]' \
+  '(-N)-q[print non-printable characters as question mark, not caret]' \
+  '(-q)-N[print non-printable characters as is, not as caret]' \
+  '-Q[quote filenames with double quotes]' \
+  '-p[print file type and permissions, like ls -l]' \
+  '-u[print file owner]' \
+  '-g[print file group]' \
+  '(-h --si)-s[print size of each file in bytes]' \
+  '(-s --si)-h[print human readable file size]' \
+  '(-s -h)--si[print human readable file size in SI units (powers of 1000)]' \
+  '-D[print last modification date or change time (with -c)]' \
+  '-F[append descriptive character to end, like ls -F]' \
+  '--inodes[print inode numbers]' \
+  '--device[print device number to which file or directory belongs]' \
+  '(--sort -t -c -U)-v[sort the output as version]' \
+  '(-v --sort -c -U)-t[sort output by modification time]' \
+  '(-v --sort -t -U)-c[sort output by change time]' \
+  '(-v --sort -t -c -r --dirsfirst)-U[leave files unsorted]' \
+  '(-U)-r[sort in reversed order]' \
+  '(-v -t -c -U)--sort[sort in specified order]:order:(name version size mtime ctime)' \
+  '(-U)--dirsfirst[list directories before files]' \
+  "-i[don't print indentation lines]" \
+  '(-S -X)-A[use ANSI line graphics hack when printing indentation lines]' \
+  '(-A -X)-S[use console (CP437) line graphics]' \
+  '(-C)-n[turn colorization off always, over-ridden by the -C option]' \
+  '(-n)-C[turn colorization on always]' \
+  '(-A -S -n -C -J -H -T --nolinks)-X[XML output]' \
+  '(-A -S -n -C -X -H -T --nolinks)-J[JSON output]' \
+  '(-n -C -X)-H[turn on HTML output]:base HREF' \
+  '(-n -C -X)-T[title for HTML output]:title' \
+  '(-n -C -X)--nolinks[turn off hyperlinks in HTML output]' \
+  '(-)--version[version of tree]' \
+  '(-)--help[verbose usage listing]' \
+  '*:directory:_files -/'
diff --git a/Completion/Unix/Command/_truss b/Completion/Unix/Command/_truss
new file mode 100644
index 000000000..656c94244
--- /dev/null
+++ b/Completion/Unix/Command/_truss
@@ -0,0 +1,76 @@
+#compdef truss
+
+local curcontext="$curcontext" state line expl ret=1
+typeset -A opt_args
+local args faults
+
+args=(
+  '(-c)-a[show argument strings with exec system call]'
+  '(-a -d -D -e -E -l -r -v -w -x)-c[count traced system calls, signals etc]'
+  '(-c)-d[include timestamps in output]'
+  '(-c)-D[include delta timestamps in output]'
+  '(-c)-e[show environment strings with exec system call]'
+  '-f[follow child processes created after a fork]'
+  '-o+[specify trace output file]:output file:_files'
+)
+
+case $OSTYPE in
+  aix*|solaris*)
+    args+=(
+      '(-c)-E[include delta timestamps of time spent within the system call]'
+      "-i[don't display interruptible sleeping system calls]"
+      '(-c)-l[include LWP id in each line of output]'
+      '-m+[specify machine faults to trace]: :->faults'
+      '(-c)-r+[show full contents of the I/O buffer for each read()]:file descriptor'
+      '-s+[specify signals to trace]:signal:_sequence _signals -M "B\:!="'
+      '-S+[specify signals at which process should be stopped and abandoned]:signal:_sequence _signals -M "B\:!="'
+      '-t+[specify system calls to trace or exclude]:system call:_sequence _sys_calls -a -M "B\:!="'
+      '-T+[specify system calls at which process should be stopped and abandoned]:system call:_sequence _sys_calls -a -M "B\:!="'
+      '*-u+[user-level function call tracing]: :->userfuncs'
+      '-U+[specify user-level functions at which process should be stopped and abandoned]: :->userfuncs'
+      '(-c)-v+[enable verbose output of structures for specified system calls]:system call:_sequence _sys_calls -a -M "B\:!="'
+      '(-c)-w+[show full contents of the I/O buffer for each write()]:file descriptor'
+      '(-c)-x+[enable raw output of structures for specified system calls]:system call:_sequence _sys_calls -a'
+      '-p[trace specified existing processes]'
+    )
+  ;;
+  dragonfly*|freebsd*)
+    args+=(
+      '-s+[specify the maximum string size to print]:maximum string size [32]'
+      "(-c)-S[don't report signals received by the process]"
+      '(*)-p[trace specified existing processes]:pid:_pids'
+    )
+  ;;
+esac
+
+_arguments -C -s : $args \
+  '*::arguments:->args' && ret=0
+
+case $state in
+  faults)
+    faults=( all ${${${(M)${(f)"$(</usr/include/sys/fault.h)"}:#?define[[:blank:]]##FLT*}#*[[:blank:]]FLT}%%[[:blank:]]*} ) 2>/dev/null
+    _sequence _wanted faults expl fault compadd - -M 'B:!=' -M 'B:[Ff][Ll][Tt]=' -M 'm:{a-z}={A-Z}' -a faults && ret=0
+  ;;
+  args)
+    if [[ $OSTYPE = solaris* ]] && (( $+opt_args[-p] )); then
+      _pids && ret=0
+    elif (( CURRENT == 1 )); then
+      _command_names -e && ret=0
+    else
+      _normal && ret=0
+    fi
+  ;;
+  userfuncs)
+    if [[ -prefix *: ]]; then
+      _message -e functions function
+    else
+      compset -P '*,'
+      compset -S '[,:]*'
+      _description -x libs expl lib
+      compadd "$expl[@]" -S '' lib && ret=0
+      compadd "$expl[@]" -qS, a.out && ret=0
+    fi
+  ;;
+esac
+
+return ret
diff --git a/Completion/Unix/Command/_uniq b/Completion/Unix/Command/_uniq
index 1cf12357b..6973cb312 100644
--- a/Completion/Unix/Command/_uniq
+++ b/Completion/Unix/Command/_uniq
@@ -5,8 +5,10 @@ local args
 args=(
   '(-c --count)'{-c,--count}'[prefix lines by the number of occurrences]'
   '(-d --repeated)'{-d,--repeated}'[only print duplicate lines]'
-  '(-D --all-repeated)'{-D,--all-repeated=}'[print all duplicate lines]:delimit method:(none prepend separate)'
+  '(--all-repeated)-D-[print all duplicate lines]'
+  '(-D)--all-repeated=-[print all duplicate lines]::delimit method [none]:(none prepend separate)'
   '(-f --skip-fields)'{-f,--skip-fields=}'[avoid comparing initial fields]:number of fields'
+  '--group=-[show all items]::group separation [separate]:(separate prepend append both)'
   '(-i --ignore-case)'{-i,--ignore-case}'[ignore differences in case when comparing]'
   '(-s --skip-chars)'{-s,--skip-chars=}'[avoid comparing initial characters]:number of characters'
   '(-t --separator)'{-t,--separator=}'[specify field delimiter]:separator'
diff --git a/Completion/Unix/Command/_units b/Completion/Unix/Command/_units
index 7fcb07894..bea77ae67 100644
--- a/Completion/Unix/Command/_units
+++ b/Completion/Unix/Command/_units
@@ -1,6 +1,6 @@
 #compdef units
 
-local curcontext="$curcontext" state line
+local curcontext="$curcontext" state line expl
 integer ret=1
 typeset -A opt_args
 
@@ -13,12 +13,12 @@ typeset -A opt_args
 _arguments -C -s -S \
   '(-c --check --check-verbose)'{-c,--check}'[check units are reducible]' \
   '(-c --check)--check-verbose[verbosely check units are reducible]' \
-  '(-o --output-format)'{-o,--output-format}'[specify output format]:printf formt: ' \
+  '(-o --output-format)'{-o,--output-format}'[specify output format]:printf format' \
   '(-f --file)'{-f,--file}'[specify file with units]:units file:_files' \
   '(-m --minus)'{-m,--minus}'[- is subtraction]' \
   '(-p --product)'{-p,--product}'[binary - is product]' \
   '(-q --quiet --silent)'{-q,--quiet,--silent}'[suppress prompts and statistics]' \
-  '(-s --strict)'{-s,--strict}'[suppress conversion to reciprocals units]' \
+  '(-s --strict)'{-s,--strict}'[suppress conversion to reciprocal units]' \
   '(-t --terse)'{-t,--terse}'[make conversion output briefer]' \
   '(-v --verbose)'{-v,--verbose}'[make output more verbose]' \
   '(- *)'{-h,--help}'[show help information and exit]' \
diff --git a/Completion/Unix/Command/_user_admin b/Completion/Unix/Command/_user_admin
index 3653308a1..7eeec7e9b 100644
--- a/Completion/Unix/Command/_user_admin
+++ b/Completion/Unix/Command/_user_admin
@@ -1,69 +1,171 @@
 #compdef useradd usermod groupadd groupmod
 
-local args shells home=${${words[(r)-D]:+b}:-d} sun redhat
+local -a args shells logindefs
+local defex
 
-[[ -e /etc/redhat-release || -e /etc/mandrake-release ]]
-redhat=$?
-
-if [[ $service = user* ]]; then
-  if [[ -r /etc/shells ]]; then
-    shells=( ${${(f)"$(</etc/shells)"}:#\#*} )
-  else
-    shells=( ${(M)commands:#*/(|[abckz]|tc|ba)sh} )
-  fi
-
-  args=(
-    '(-D)-c+[comment]:comment'
-    "(-D)-${home}+[home directory]:home directory:_directories -W /"
-    '(-D)-e+[specify expiration date]:expiration date (YYYY-MM-DD)'
-    '(-D)-f+[specify inactive days]:inactive days'
-    '(-D)-g+[initial group]:initial group:_groups'
-    '(-D)-G+[supplementary groups]:supplementary group:_groups -S,'
-    "(-D)-s+[shell]:shell:( $shells /bin/false )"
-    '(-D -M)-m[create home directory]'
-    '(-D)-u[uid]:uid'
-    '(-D)-o[allow non unique uid]'
-  )
-  [[ $OSTYPE = linux* ]] && args+=(
-      '(-D -U -L)-p+[specify encrypted password]:encrypted password' )
-
-  if [[ $service = useradd ]]; then
-    (( redhat )) || args+=(
-      "(-D -k -m)-M[don't create home]"
-      '(-D)-r[create system account]'
-      "(-D)-n[don't create mirrored user]"
+# this handles solaris, netbsd, openbsd and linux
+case ${service%???}:${(M)service%???}:$OSTYPE in
+  user:*)
+    if [[ -r /etc/shells ]]; then
+      shells=( ${${(f)"$(</etc/shells)"}:#\#*} )
+    else
+      shells=( ${(M)commands:#*/(|[abckz]|tc|ba)sh} )
+    fi
+    args+=(
+      '(-D -c --commend)'{-c+,--comment=}'[comment]:comment'
+      '(-D -d --home -b --base-dir)'{-d+,--home=}"[specify home directory]:home directory:_directories -W /"
+      '(-D -e --expiredate)'{-e+,--expiredate}'[specify expiration date]:expiration date (YYYY-MM-DD)'
+      '(-D -f --inactive)'{-f+,--inactive=}'[specify inactive days]:inactive days'
+      '(-D -g --gid)'{-g+,--gid=}'[specify primary group]:initial group:_groups'
+      '(-D -G --groups)'{-G+,--groups=}'[specify supplementary groups]:supplementary group:_sequence _groups'
+      '(-D -s --shell)'{-s+,--shell=}"[shell]:shell:( $shells /bin/false )"
+      '(-D -u --uid)'{-u+,--uid=}'[specify uid]:uid'
+      '(-D -o --non-unique)'{-o,--non-unique}'[allow non unique uid]'
     )
-    [[ $OSTYPE = linux* ]] || sun=" -s"
+  ;|
+  user:add:*)
+    [[ $OSTYPE = linux* ]] && defex=" -k -K -M -p -r -R -Z"
+    [[ $OSTYPE = (net|open)bsd* ]] && defex=" -p -v"
     args+=(
-      "(-c -G -m -k -M -p -u -o -n -r$sun)-D[modify or display defaults]"
-      '(-D -M)-k[skeleton home directory]:skeleton directory:_directories -W /'
+      "(-c -d -G -m -l -N -u -U -o -n -S$defex)"{-D,--defaults}'[show or modify defaults]'
+      '(-D -M -k --skel)'{-k,--skel}'[skeleton home directory]:skeleton directory:_directories -W /'
+      '(-D -M -m --create-home)'{-m,--create-home}'[create home directory]'
       ':username'
     )
-  else
-    (( redhat )) || args=( $args[@]
-	"(-U -p)-L[lock user's password]"
-	"(-L -p)-U[unlock user's password]"
+  ;|
+  user:add:linux*)
+    logindefs=( # see login.defs(5)
+      CREATE_HOME GID_MAX GID_MIN MAIL_DIR MAX_MEMBERS_PER_GROUP
+      PASS_MAX_DAYS PASS_MIN_DAYS PASS_WARN_AGE SUB_GID_COUNT SUB_GID_MAX
+      SUB_GID_MIN SUB_UID_COUNT SUB_UID_MAX SUB_UID_MIN SYS_GID_MAX
+      SYS_GID_MIN SYS_UID_MAX SYS_UID_MIN UID_MAX UID_MIN UMASK
+    )
+    args+=(
+      '(-l --no-log-init)'{-l,--no-log-init}"[don't add user to lastlog and faillog databases]"
+      '(-m --create-home -M --no-create-home)'{-M,--no-create-home}"[don't create user's home directory, regardless of /etc/login.defs]"
+      '(-N --no-user-group -U --user-group)'{-N,--no-user-group}"[don't create a group with the same name as the user]"
+      '(-N --no-user-group -U --user-group)'{-U,--user-group}"[create a group with the same name as the user]"
+    )
+  ;|
+  user:add:(^solaris2.<-10>))
+    args+=(
+      '(-b --base-dir -d --home-dir)'{-b,--base-dir=}'[specify base directory for new home directory]:directory:_directories'
+    )
+  ;|
+  user:*:(net|open)bsd*)
+    args+=(
+      '-L[specify login class]:login class'
+      '-p+[specify encrypted password]:encrypted password'
+    )
+  ;|
+  user:add:(net|open)bsd*)
+    args+=( '-r[specify UID range]:low.high' )
+  ;|
+  user:add:netbsd*)
+    args+=( '-M[specify home directory permissions]:permissions (octal)' )
+  ;|
+  user:mod:netbsd*)
+    args+=( '-C[lock or unlock account]:locked:(yes no)' )
+  ;|
+  user:*:netbsd*)
+    args+=(
+      '-F[force password change on first login]'
+      '-S[allow samba usernames with a trailing dollar]'
+    )
+  ;|
+  user:mod:openbsd*)
+    args+=(
+      '(-G)-S+[set supplementary groups]:supplementary group:_sequence _groups'
+      "(-U)-Z[lock the account by adding \'-\' to password and shell]"
+      '(-Z)-U[unlock the account]'
+    )
+  ;|
+  *:*:(net|open)bsd*)
+    args+=( '-v[verbose mode - explain commands as they are executed]' )
+  ;|
+  *:*:solaris2.<11->)
+    args+=( '-S[specify repository]:repository:(files ldap)' )
+  ;|
+  user:*:solaris2.<11->)
+    args+=(
+      '-A[specify authorizations]:authorization'
+      \*{-K,--key=}'[set user attributes]:user attribute'
+      '-P[specify execution profiles]:profile'
+      '-R[specify roles]:role'
+      '-p[specify projects]:project'
     )
-    [[ $OSTYPE = openbsd* ]] || args+=('-a[append groups]')
+  ;|
+  user:mod:solaris2.<11->)
     args+=(
-      '-l[specify new user name]:new username'
+      '-q[specify host or netgroup to qualify extended attributes]:host or netgroup:_hosts'
+    )
+  ;|
+  user:mod:linux*)
+    args+=(
+      '(-a --append)'{-a,--append}'[add user to supplementary groups without removing from other groups]'
+      \*{-v,--add-sub-uids}'[add a range of subordinate uids]:uids (first-last)'
+      \*{-V,--del-sub-uids}'[remove a range of subordinate uids]:uids (first-last)'
+      \*{-w,--add-sub-gids}'[add a range of subordinate gids]:gids (first-last)'
+      \*{-W,--del-sub-gids}'[remove a range of subordinate gids]:gids (first-last)'
+    )
+  ;|
+  user:mod:*)
+    args+=(
+      '(-l --no-log-init)'{-l,--no-log-init}'[specify new user name]:new username'
+      '(-m --move-home)'{-m,--move-home}'[move home directory contents to new location]'
       ':username:_users'
     )
-  fi
-else
-  args=( '-g+[gid]:gid' '-o[allow non unique gid]' )
-  if [[ $service = groupadd ]]; then
-    args+=( ':group name' )
-    (( redhat )) || args=( $args[@]
-      '-r[create system group]'
-      '-f[force]'
+  ;|
+  user:*:linux*)
+    args+=(
+      '(-U --unlock --lock -L -p)'{-L,--lock}"[lock user's password]"
+      '(-U --unlock --lock -L -p)'{-U,--unlock}"[unlock user's password]"
+      '(-Z --selinux-user)'{-Z,--selinux-user}"[specify SELinux user for the user's login]:user"
+    )
+  ;|
+  group:*)
+    args+=(
+      '(-g --gid)'{-g+,--gid=}'[specify gid]:gid'
+      '(-o --non-unique)'{-o,--non-unique}'[allow non unique gid]'
     )
-  else
+  ;|
+  group:add:linux*)
+    logindefs=( GID_MAX GID_MIN MAX_MEMBERS_PER_GROUP SYS_GID_MAX SYS_GID_MIN )
+    args+=(
+      '(-r --system)'{-r,--system}'[create a system account]'
+      '(-f --force)'{-f,--force}'[exit successfully if group already exists; cancel -g if GID in use]'
+    )
+  ;|
+  group:*:solaris2.<11->)
+    args+=( '-U[add users to the group]:user:_sequence _users' )
+  ;|
+  *:add:linux*)
+    args+=(
+      \*{-K,--key=}'[override /etc/login.defs defaults]:key: _values 'key' ${^logindefs}\:value'
+    )
+  ;|
+  group:add:netbsd*)
+    args+=( '-r[specify GID range]:low.high' )
+  ;|
+  *:*:linux*)
+    args+=(
+      '(-R --root)'{-R,--root=}'[specify directory to chroot into]:directory:_directories'
+      '(-D -U -L -p --password)'{-p+,--password=}'[specify encrypted password]:encrypted password'
+      '(-)'{-h,--help}'[display help information]'
+    )
+  ;|
+  group:add:*)
+    args+=( ':group name' )
+  ;|
+  group:mod:*)
     args+=(
-      '-n[specify new group name]:new group name'
+      '(-n --new-name)'{-n,--new-name}'[specify new group name]:new group name'
       ':group:_groups'
     )
-  fi
-fi
+  ;|
+  ^*:linux*)
+    args=( ${(R)args:#(|\*)(|\(*\))--*} )    # remove long options
+  ;|
+esac
 
 _arguments -A "-*" -s $args[@]
diff --git a/Completion/Unix/Command/_vim b/Completion/Unix/Command/_vim
index dbc946cb1..9fd2d63e8 100644
--- a/Completion/Unix/Command/_vim
+++ b/Completion/Unix/Command/_vim
@@ -13,7 +13,7 @@ _vim_files () {
   esac
 }
 
-local curcontext="$curcontext" state line expl
+local curcontext="$curcontext" state line expl ret=1
 typeset -A opt_args
 
 local arguments
@@ -30,16 +30,7 @@ arguments=(
   '-C[start in compatible mode]'
   '-N[start in incompatible mode]'
   '(--nofork -f)'{--nofork,-f}'[do not detach the GUI version from the shell]'
-  '-V-[verbosity level]::verbosity:((0\:"do not display any messages"
-                                     1\:"display when viminfo file is read or written"
-                                     2\:"display sourced files"
-                                     5\:"display every searched tag-file"
-                                     8\:"display files that trigger autocommands"
-                                     9\:"display every triggered autocommand (default)"
-                                    12\:"display every executed function"
-                                    13\:"report every thrown, caught, finished, or discarded exception"
-                                    14\:"display anything pending in a \:finally clause"
-                                    15\:"display every executed ex-command"))'
+  '-V-[verbosity level]::verbosity [10]:->verbosity'
   '-D[debugging mode]'
   '-n[no swap file (memory only)]'
   '-nb[start as NetBean server]'
@@ -48,8 +39,9 @@ arguments=(
   '(-A    -F)-H[start in Hebrew mode]'
   '(-A -H   )-F[start in Farsi mode]'
   '-T[set terminal type]:::_terminals'
+  '--not-a-term[skip warning for input/output not being a terminal]'
   '-u[use given vimrc file instead of default .vimrc]::rc file:_files'
-  '--noplugin[do not load plugin scripts]'
+  "--noplugin[don't load plugin scripts]"
   '-o-[number of windows to open (default: one for each file)]::window count: '
   '-O-[number of windows to vertically split open (default is one for each file)]::window count: '
   '-p-[number of tabs to open (default: one for each file)]::tab count: '
@@ -77,7 +69,7 @@ arguments=(
   '--servername[name of vim server to send to or name of server to become]:server name:->server'
   '--startuptime[write startup timing messages to given file]:log file:_files'
   '--socketid[run GVIM in another window]'
-  '-i[use given viminfo file instead of default .viminfo]:viminfo file:_files'
+  '-i[use specified viminfo file]:viminfo file [~/.viminfo]:_files'
   '(- *)'{-h,--help}'[print help and exit]'
   '(- *)--version[print version information and exit]'
   '(* -q)-t[edit file where tag is defined]:tag:_complete_tag'
@@ -98,12 +90,33 @@ arguments=(
   '-U[use given gvimrc file instead of default .gvimrc]::rc file:_files'
 )
 
-_arguments -C -S $arguments && return
+_arguments -C -S $arguments && ret=0
 
 if [[ "$state" = server ]]; then
   local -a servers
   servers=( ${(f)"$(_call_program servers $words[1] --serverlist 2>/dev/null)"} )
-  _wanted servers expl server compadd -M 'm:{a-z}={A-Z}' -a servers && return
+  _wanted servers expl server compadd -M 'm:{a-z}={A-Z}' -a servers && ret=0
+elif [[ $state = verbosity ]]; then
+  _tags numbers files
+  while _tags; do
+    _requested numbers && _describe 'verbosity [10]' "(
+      0:don\'t' display any messages'
+      1:'display when viminfo file is read or written'
+      2:'display sourced files'
+      5:'display every searched tag-file'
+      8:'display files that trigger autocommands'
+      9:'display every triggered autocommand'
+      12:'display every executed function'
+      13:'report every thrown, caught, finished, or discarded exception'
+      14:'display anything pending in a :finally clause'
+      15:'display every executed ex-command'
+    )" && ret=0
+    if _requested files expl 'file for verbose output'; then
+      compset -P '[0-9]#'
+      _files "$expl[@]" && ret=0
+    fi
+    (( ret )) || break
+  done
 fi
 
-return 1
+return ret
diff --git a/Completion/Unix/Command/_vmstat b/Completion/Unix/Command/_vmstat
index 02fa6be64..7082cbbd5 100644
--- a/Completion/Unix/Command/_vmstat
+++ b/Completion/Unix/Command/_vmstat
@@ -18,40 +18,38 @@ case $OSTYPE in
       '1:delay' '2:count'
     )
   ;;
-  freebsd*)
+  freebsd*|openbsd*)
     specs=(
-      '-a[include statistics about all interrupts]'
       '-c[number of times to refresh the display]:count'
       '-f[report on the number fork syscalls since boot and pages of virtual memory for each]'
-      '-h[human readable memory columns output]'
-      '-H[scriptable memory columns output]'
       '-i[report the number of interrupts taken by devices since boot]'
       '-M[source file to extract values associated with the name list from]:core:_files'
       '-N[source file to extract the name list from]:system:_files'
+      '-w[specify delay between each display]:delay (seconds)'
+      '*:disk:_files'
+    )
+  ;|
+  freebsd*)
+    specs+=(
+      '-a[include statistics about all interrupts]'
+      '-h[human readable memory columns output]'
+      '-H[scriptable memory columns output]'
       '-m[report on the usage of kernel dynamic memory allocated using malloc(9) by type]'
       '-n[change the maximum number of disks to display]:number of disks to display'
+      '-o[list virtual memory objects]'
       '-P[report per-cpu system/user/idle cpu statistics]'
       '-p[specify which types of devices to display]: :->devices'
       '-s[display the contents of the SUM structure]:sum'
-      '-w[delay N seconds between each display]:delay'
       '-z[report on memory used by the kernel zone allocator, uma(9), by zone]'
-      '*:disks:_files'
     )
   ;;
   openbsd*)
-    specs=(
-      '-c[number of times to refresh the display]:count'
-      '-f[report on the number fork syscalls since boot and pages of virtual memory for each]'
-      '-i[report the number of interrupts taken by devices since boot]'
-      '-M[source file to extract values associated with the name list from]:core:_files'
+    specs+=(
       '-m[report usage of kernel dynamic memory listed first by size of allocation then type of usage]'
-      '-N[source file to extract the name list from]:system:_files'
       '-s[display the contents of the UVMEXP structure]:uvmexp'
       '-t[report on the number of page in and page reclaims since boot]'
       '-v[print more verbose information]'
-      '-w[delay N seconds between each display]:delay'
       '-z[include statistics about all interrupts]'
-      '*:disks:_files'
     )
   ;;
 esac
diff --git a/Completion/Unix/Command/_w3m b/Completion/Unix/Command/_w3m
index 9569368a5..6e83a6781 100644
--- a/Completion/Unix/Command/_w3m
+++ b/Completion/Unix/Command/_w3m
@@ -90,7 +90,7 @@ case "$state" in
   option)
     local -a options
     options=( ${${(M)${(f)"$(_call_program options $words[1] -show-option 2>/dev/null)"}:#    -o *}/(#b)    -o (*)=[^ ]#[[:blank:]]##(*)/$match[1]:${match[2]:l}} )
-    if compset -P '*='; then
+    if compset -P 1 '*='; then
       _message -e values 'value'
     else
       compset -S '=*' || suf=( -S '=' )
@@ -98,7 +98,7 @@ case "$state" in
     fi
   ;;
   pauth)
-    if compset -P '*:'; then
+    if compset -P 1 '*:'; then
       _message -e passwords 'password'
     else
       compset -S ':*' || suf=( -S ':' )
diff --git a/Completion/Unix/Command/_wget b/Completion/Unix/Command/_wget
index b6feab581..8a9cc871a 100644
--- a/Completion/Unix/Command/_wget
+++ b/Completion/Unix/Command/_wget
@@ -32,7 +32,7 @@ _arguments -C -s \
   '(--timeout -T)--dns-timeout=[set the DNS lookup timeout]:DNS lookup timeout (seconds)' \
   '(--timeout -T)--connect-timeout=[set the connect timeout]:connect timeout (seconds)' \
   '(--timeout -T)--read-timeout=[set the read timeout]:read timeout (seconds)' \
-  '(--wait,-w)'{--wait=,-w+}'[specify wait between retrievals]:time (seconds)' \
+  '(--wait -w)'{--wait=,-w+}'[specify wait between retrievals]:time (seconds)' \
   '(--random-wait)--waitretry=:time (seconds)' \
   '(--waitretry)--random-wait[random wait time between retrievals]' \
   '(--proxy -Y --no-proxy)'{--proxy=,-Y+}'[explicitly turn on proxy]' \
@@ -127,7 +127,6 @@ _arguments -C -s \
   '--trust-server-names' \
   '(--exclude-directories -X)'{--exclude-directories=,-X+}'[exclude directories]:excluded directories' \
   '(-np --no-parent)'{-np,--no-parent}"[don't ascend to parent directory]" \
-  '--no-host-lookup' \
   '--no-verbose' \
   '--no-clobber' \
   '--no-directories' \
diff --git a/Completion/Unix/Command/_wiggle b/Completion/Unix/Command/_wiggle
index ec71fdcb9..0a2f0c0cb 100644
--- a/Completion/Unix/Command/_wiggle
+++ b/Completion/Unix/Command/_wiggle
@@ -9,7 +9,7 @@ _arguments \
   '(-w --words -l --lines)'{-w,--words}'[make operations and display word based]' \
   '(-l --lines -w --words)'{-l,--lines}'[make operations and display line based]' \
   '(-p --patch)'{-p,--patch}'[treat last named file as a patch]' \
-  '(-r --replace)'{-r,--replace}'[replace orginal file with merged output]' \
+  '(-r --replace)'{-r,--replace}'[replace original file with merged output]' \
   '(-R --reverse -x --extract)'{-R,--reverse}'[swap the files or revert changes]' \
   '(-2 -3 -m --merge)-1[select branch]' \
   '(-1 -3 -m --merge)-2[select branch]' \
diff --git a/Completion/Unix/Command/_xargs b/Completion/Unix/Command/_xargs
index d8c0a0ec2..8b543341d 100644
--- a/Completion/Unix/Command/_xargs
+++ b/Completion/Unix/Command/_xargs
@@ -47,6 +47,7 @@ case $variant in
       '(-0 --null -d --delimiter)'{-0,--null}'[expect NUL characters as input separators]'
       '(-d --delimiter -0 --null)'{-d+,--delimiter=}'[specify delimiter of input items]:delimiter'
       '(-l -L --max-lines -n --max-args -s --max-chars)--max-lines=-[call program for every number of lines]::number of input lines'
+      '--process-slot-var=[set environment variable to unique value in child processes]:variable:_parameters -g "*export*"'
       '(-r --no-run-if-empty)'{-r,--no-run-if-empty}"[don't run command in absence of input]"
       '(- *)--help[show help information]'
       '(- *)--version[show version information]'
diff --git a/Completion/Unix/Command/_xmlsoft b/Completion/Unix/Command/_xmlsoft
index 67ba6c001..7b4b0b03d 100644
--- a/Completion/Unix/Command/_xmlsoft
+++ b/Completion/Unix/Command/_xmlsoft
@@ -1,7 +1,7 @@
 #compdef xsltproc xmllint -value-,XML_CATALOG_FILES,-default-
 
-# xmllint: using libxml version 20707
-# xsltproc: using libxml 20707, libxslt 10126 and libexslt 815
+# xmllint: using libxml version 20904
+# xsltproc: using libxml 20904, libxslt 10129 and libexslt 817
 
 local -a encoding
 
@@ -38,7 +38,9 @@ case $service in
       '--nodtdattr[do not default attributes from the DTD]' \
       '--noout[do not dump the result]' \
       '--maxdepth[increase the maximum depth]:depth' \
+      '--maxvars[increase the maximum variables]:variables' \
       '--maxparserdepth[increase the maximum parser depth]:depth' \
+      '--seed-rand[initialise random number generator]:seed' \
       '--html[input document is an HTML file]' \
       '--encoding[the input document character encoding]:encoding:(${encoding[@]})' \
       '*--param[pass a parameter,value pair]:name::value (xpath expression)' \
@@ -87,6 +89,7 @@ case $service in
       '--xmlout[use the XML serializer when using --html]' \
       '--nodefdtd[do not default HTML doctype]' \
       '--push[use the push mode of the parser]' \
+      '--pushsmall[use the push mode of the parser using tiny increments]' \
       '--memory[parse from memory]' \
       '--maxmem[limit memory allocation]:bytes' \
       '--nowarning[do not emit warnings from parser/validator]' \
diff --git a/Completion/Unix/Command/_xxd b/Completion/Unix/Command/_xxd
index 1a822553c..1b1613a64 100644
--- a/Completion/Unix/Command/_xxd
+++ b/Completion/Unix/Command/_xxd
@@ -22,21 +22,23 @@ local arguments
 
 arguments=(
   # output options
-  '(-b -bits            -i -include -p -postscript -plain -ps -r -reverse -u -uppercase)'{-b,-bits}'[output in binary digits, rather than hex]'
-  '(         -E -EBCDIC -i -include -p -postscript -plain -ps -r -reverse              )'{-E,-EBCDIC}'[print human-readable part in EBCDIC rather than ASCII]'
-  '(-b -bits -E -EBCDIC -i -include -p -postscript -plain -ps -r -reverse              )'{-i,-include}'[output in C include file style]'
-  '(-b -bits -E -EBCDIC -i -include -p -postscript -plain -ps                          )'{-p,-postscript,-plain,-ps}'[read or write a plain hexdump (no line numbers or ASCII rendering)]'
+  '(-b -bits            -i -include -p -postscript -plain -ps -r -revert -u -uppercase)'{-b,-bits}'[output in binary digits, rather than hex]'
+  '(         -E -EBCDIC -i -include -p -postscript -plain -ps -r -revert              )'{-E,-EBCDIC}'[print human-readable part in EBCDIC rather than ASCII]'
+  '(-i -include -p -postscript -plain -ps -r -revert)'{-e,-endian}'[little-endian dump]'
+  '(-b -bits -E -EBCDIC -i -include -p -postscript -plain -ps -r -revert              )'{-i,-include}'[output in C include file style]'
+  '(-b -bits -E -EBCDIC -i -include -p -postscript -plain -ps                         )'{-p,-postscript,-plain,-ps}'[read or write a plain hexdump (no line numbers or ASCII rendering)]'
 
-  '(-b -bits -E -EBCDIC -i -include                           -r -reverse -u -uppercase)'{-r,-reverse}'[reverse mode\: read a hex dump and output binary data]'
-  '(-b -bits                                                  -r -reverse -u -uppercase)'{-u,-uppercase}'[output upper-case hex digits]'
+  '(-b -bits -E -EBCDIC -i -include                           -r -revert -u -uppercase)'{-r,-revert}'[reverse mode\: read a hex dump and output binary data]'
+  '(-b -bits                                                  -r -revert -u -uppercase)'{-u,-uppercase}'[output upper-case hex digits]'
 
-  {-h,-help}'[display usage message]'
-  {-v,-version}'[show program version]'
+  '(- :)'{-h,-help}'[display usage message]'
+  '(- :)'{-v,-version}'[show program version]'
   '*'{-a,-autoskip}"[a single '*' replaces runs of NUL (toggleable)]"
 
   {-c+,-cols}'[specify number of octets per line]: :_guard "[0-9a-fA-Fx]#" "number of octets per line"'
   {-g+,-groupsize}'[specify the number of octets per group]: :_guard "[0-9]#" "number of octets per group"'
   {-l+,-len}'[specify number of octets to output]: :_guard "[0-9]#" "number of octets to output"'
+  {-o+,-offset}'[add specified offset to displayed file position]:offset'
   {-s,-skip,-seek}'[specify file offset to dump from]: :_guard "[0-9]#" "file offset to dump from (absolute or relative)"'
 
   ':files:_files'
diff --git a/Completion/Unix/Command/_xz b/Completion/Unix/Command/_xz
index 028285a35..01e56022b 100644
--- a/Completion/Unix/Command/_xz
+++ b/Completion/Unix/Command/_xz
@@ -42,7 +42,7 @@ case "$service" in
     "${decomp}--no-sparse[do not create sparse files when decompressing]" \
     '(* --files --files0)--files=-[read list of files to process from file]::file:_files' \
     '(* --files --files0)--files0=-[read null terminated list of files to process from file]::file:_files' \
-    '(-F --format)'{-F,--format}'=[specify file format]:format;(auto xz lzma raw)' \
+    '(-F --format)'{-F,--format}'=[specify file format]:format:(auto xz lzma raw)' \
     '(-C --check)'{-C,--check}'=[integrity check type]:check type:(none crc32 crc64 sha256)' \
     '--memlimit-compress=[set memory usage limit for compression]:memory usage' \
     '--memlimit-decompress=[set memory usage limit for decompression]:memory usage' \
diff --git a/Completion/Unix/Command/_yafc b/Completion/Unix/Command/_yafc
index df9623beb..1e0a601a1 100644
--- a/Completion/Unix/Command/_yafc
+++ b/Completion/Unix/Command/_yafc
@@ -1,28 +1,28 @@
-# compdef yafc
+#compdef yafc
 
 _yafc() {
-    local arguments
-    arguments=(
+  local arguments
+  arguments=(
     '(--anon -a)'{--anon,-a}'[try an anonymous login]'
     '(--debug -d)'{--debug,-d}'[print all messages to/from server]'
     '(--dump-rc -D)'{--dump-rc,-D}'[print the default configuration file]'
-    '(--mechanism -m)'{--mechanism=-,-m}'[specify a security mechanism]:security mechanism:(krb4, krb5, none)'
-    '(--norc -n)'{--norc,-n}'[do not read the users configuration file]'
-    '(--noproxy -p)'{--noproxy,-p}'[do not connect via the proxy]'
-    '(--quiet -q)'{--quiet,-q}'[do not print the welcome message]'
+    '(--mechanism -m)'{--mechanism=-,-m}'[specify a security mechanism]:security mechanism:(krb4 krb5 none)'
+    '(--norc -n)'{--norc,-n}"[don't read the users configuration file]"
+    '(--noproxy -p)'{--noproxy,-p}"[don't connect via the proxy]"
+    '(--quiet -q)'{--quiet,-q}"[don't print the welcome message]"
     '(--rcfile -r)'{--rcfile=-,-r}'[specify a configuration file]:configuration file:_files'
     '(--trace -t)'{--trace=-,-t-}'[specify a trace file]:trace file:_files'
-    '(--noauto -u)'{--noauto,-u}'[do not login automagically]'
+    '(--noauto -u)'{--noauto,-u}"[don't login automatically]"
     '(--noalias -U)'{--noalias,-U}'[as --noauto, but bookmark aliases is disabled]'
     '(--verbose -v)'{--verbose,-v}'[print all responses received]'
-    '(--wait -w)'{--wait,-w=}'[specify a waiting time between connection attempts]:wait time: '
+    '(--wait -w)'{--wait,-w=}'[specify a waiting time between connection attempts]:wait time (seconds)'
     '(--workdir -W)'{--workdir=,-W+}'[use a different working directory]:working directory:_directories'
-    '(--version -V)'{--version,-V}'[print version information]'
-    '(--help -h)'{--help,-h}'[print a short help description]'
+    '(- *)'{--version,-V}'[print version information]'
+    '(- *)'{--help,-h}'[print a short help description]'
     '*:address:_yafc_address'
-    )
+  )
 
-    _arguments -S -s $arguments
+  _arguments -S -s $arguments
 }
 
  (( $+functions[_yafc_bookmarks] )) ||
@@ -30,7 +30,7 @@ _yafc_bookmarks() {
     local bkmfile=~/.yafc/bookmarks
     
     if [[ -f $bkmfile ]]; then
-        local -a bkms
+        local -a bkms expl
         bkms=(${${${(M)"${(f)$(<$bkmfile)}":#machine*alias ##\'*\' #}##machine*alias ##\'}%%\' #}) #" vim syntax goes crazy
         _wanted bookmarks expl 'bookmarks' compadd "$@" -a - bkms
     fi
diff --git a/Completion/Unix/Command/_zfs b/Completion/Unix/Command/_zfs
index f3869da43..553996da0 100644
--- a/Completion/Unix/Command/_zfs
+++ b/Completion/Unix/Command/_zfs
@@ -149,7 +149,7 @@ _zfs() {
 		"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)"
+		"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)"
@@ -247,7 +247,7 @@ _zfs() {
 			':filesystem/volume/snapshot:_zfs_dataset -t fs -t vol'
 		;;
 
-	("snapshot")
+	(snap(|shot))
 		_arguments -A "-*" \
 			'-r[Recursively snapshot all descendant datasets]' \
 			'*-o[Set property]:property:_values -s , "property" $create_properties' \
@@ -418,30 +418,16 @@ _zfs() {
 
 	("allow")
 		_arguments -A "-*" \
-			- set1 \
-			':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]' \
+			'(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' \
-			- 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' \
-			- set5 \
-			'-s[Define or modify permission sets]' \
-			':setname:' \
-			':permissions or sets:_values -s , "permission or set" $delegatable_perms' \
-			':filesystem/volume:_zfs_dataset -t fs -t vol'
 		;;
 
 	("unallow")
diff --git a/Completion/Unix/Command/_zip b/Completion/Unix/Command/_zip
index 1040fa977..1b1b6c315 100644
--- a/Completion/Unix/Command/_zip
+++ b/Completion/Unix/Command/_zip
@@ -6,38 +6,28 @@ typeset -A opt_args
 
 case $service in
   zip)
-    _arguments -C -s \
-      '-f[freshen: only changed files]' \
-      '-d[delete entries in zipfile]' \
+    _arguments -C -s -S \
+      '(-u --update)'{-u,--update}'[update: only changed or new files]' \
+      '(-f --freshen)'{-f,--freshen}'[freshen: update existing files only (no files added)]' \
+      '(-FS --filesync)'{-FS,--filesync}'[filesync: update if date or size changed]' \
+      '(-d --delete)'{-d,--delete}'[delete: delete files from archive]' \
+      '(-U --copy)'{-U,--copy}'[copy: select files in archive to copy]' \
       '-r[recurse into directories]' \
-      '-R[PKZIP recursion]' \
+      '(-m --move)'{-m,--move}'[after archive created, delete original files]' \
+      '(-j --junk-paths)'{-j,--junk-paths}'[junk directory names (store just file names)]' \
       '-q[quiet operation]' \
-      '-c[add one-line comments]' \
-      '-b[use "path" for temp file]:path for temporary archive:_files -/' \
-      '-@[read names from stdin]' \
-      '-F[fix zipfile]' \
-      '-FF[try harder to fix zipfile]' \
-      '-A[adjust self-extracting exe]' \
-      '-T[test zipfile integrity]' \
-      '-y[store symbolic links as the link instead of the referenced file]' \
-      '-e[encrypt]' \
-      '-h[show help]' \
-      '-u[update: only changed or new files]' \
-      '-m[move into zipfile (delete files)]' \
-      '-g[allow growing existing zipfile]' \
-      '-j[junk: do not record directory names]' \
-      '-l[convert LF to CR LF]' \
-      '-ll[convert CR LF to LF]' \
-      '-L[display license]' \
       '-v[verbose operation/print version info]' \
-      '-z[add zipfile comment]' \
-      '-t[only do files after or at "mmddyyyy"]:mmddyyyy' \
-      '-tt[only do files before "mmddyyyy"]:mmddyyyy' \
+      '(-c --entry-comments)'{-c,--entry-comments}'[prompt for one-line comment for each entry]' \
+      '(-z --archive-comment)'{-z,--archive-comment}'[prompt for comment for archive]' \
+      '(-@ --names-stdin)'{-@,--names-stdin}'[read names to zip from stdin]' \
       '-o[make zipfile as old as latest entry]' \
-      '-D[do not add directory entries]' \
-      '-J[junk zip file prefix (unzipsfx)]' \
-      '-X[eXclude eXtra file attributes]' \
-      '-n[specify suffixes of files not to be compressed]:suffixes:->suffixes' \
+      '(-i --include)'{-i,--include}'[include files that match a pattern]:*(-*|@):pattern:_files' \
+      '(-x --exclude)'{-x,--exclude}'[exclude files that match a pattern]:*(-*|@):pattern:_files' \
+      '-l[convert LF to CR LF]' \
+      '-ll[convert CR LF to LF]' \
+      '-R[recurse current dir and match patterns]' \
+      '-t+[exclude files modified before specified date]:date (mmddyyyy)' \
+      '-tt+[exclude files modified on or after specified date]:date (mmddyyyy)' \
       '(   -1 -2 -3 -4 -5 -6 -7 -8 -9)-0[store only]' \
       '(-0    -2 -3 -4 -5 -6 -7 -8 -9)-1[compress faster]' \
       '(-0 -1    -3 -4 -5 -6 -7 -8 -9)-2' \
@@ -48,18 +38,60 @@ case $service in
       '(-0 -1 -2 -3 -4 -5 -6    -8 -9)-7' \
       '(-0 -1 -2 -3 -4 -5 -6 -7    -9)-8' \
       '(-0 -1 -2 -3 -4 -5 -6 -7 -8   )-9[compress better]' \
-      '-x[exclude the following names]' \
-      '-i[include only the following names]' \
+      '(-Z --compression-method)'{-Z+,--compression-method=}'[specify compression method]:method:(store deflate bzip2)' \
+      '(-e --encrypt)'{-e,--encrypt}'[encrypt using (weak) PKZip 2.0 encryption, prompt for password]' \
+      '-P+[specify password for standard encryption]:password' \
+      '-s+[create split archive with splits of specified size]:size (MB)' \
+      '-sp[pause after each split to allow changing disks]' \
+      '-sb[ring bell when pausing between splits]' \
+      '-sv[be verbose about creating splits]' \
+      '(-O --out)'{-O,--out=}'[output to new archive]:archive' \
+      '(-FI --fifo)'{-FI,--fifo}'[support reading from a pipe]' \
+      '(-db --display-bytes)'{-db,--display-bytes}'[display running count of bytes processed and bytes to go]' \
+      '(-dc --display-counts)'{-dc,--display-counts}'[display running count of entries done and entries to go]' \
+      '(-dd --display-dots)'{-dd,--display-dots}'[display dots while each entry is zipped]' \
+      '(-dg --display-globaldots)'{-dg,--display-globaldots}'[display dots for the archive instead of each file]' \
+      '(-ds --dot-size)'{-ds,--dot-size=}'[specify size at which a dot is output while processing files]:size [10MB]' \
+      '(-du --display-usize)'{-du,--display-usize}'[display original uncompressed size for each entry as added]' \
+      '(-dv --display-volume)'{-dv,--display-volume}'[display volume (disk) number in format in_disk>out_disk]' \
+      '(-lf --logfile-path)'{-lf,--logfile-path=}'[specify log file]:file:_files' \
+      '(-la --log-append)'{-la,--log-append}'[append to existing log file]' \
+      '(-li --log-info)'{-li,--log-info}'[include info messages in log]' \
+      '(-T --test)'{-T,--test}'[test zipfile integrity]' \
+      '(-TT --unzip-command)'{-TT,--unzip-command=}'[specify command for testing archive]:command [unzip -tqq]:_cmdstring' \
+      '(-F --fix)'{-F,--fix}'[fix zipfile]' \
+      '(-F --fix -FF --fixfix)'{-FF,--fixfix}'[try harder to fix zipfile]' \
+      '(-DF --dif)'{-DF,--dif}'[only include files that have changed or are new as compared to the input archive]' \
+      '-sf[show files to operate on and exit]' \
+      '-su[as -sf but show escaped UTF-8 Unicode names also]' \
+      '-su[as -sf but show escaped UTF-8 Unicode names instead]' \
+      '-UN=[specify Unicode path mismatch handling]:mismatch handling:(Quit Warn Ignore No Escape UTF8)' \
+      '(-A --adjust-sfx)'{-A,--adjust-sfx}'[adjust self-extracting exe]' \
+      '(-J --junk-sfx)'{-J,--junk-sfx}'[junk zip file prefix (unzipsfx)]' \
+      '(-b --temp-path)'{-b,--temp-path}'[specify location for temporary archive]:path for temporary archive:_directories' \
+      '-nw[no wildcards]' \
+      '(-X --no-extra)'{-X,--no-extra}'[eXclude eXtra file attributes]' \
+      '(-y --symlinks)'{-y,--symlinks}'[store symbolic links as the link instead of the referenced file]' \
+      '(-)'{-h,--help}'[display help information]' \
+      '(-)'{-h2,--more-help}'[display extended help information]' \
+      '(-g --grow)'{-g,--grow}'[allow growing existing zipfile]' \
+      '(-L --license)'{-L,--license}'[display license]' \
+      '(-D --no-dir-entries)'{-D,--no-dir-entries}"[don't add directory entries]" \
+      '-n[specify suffixes of files not to be compressed]:suffixes:->suffixes' \
       "(-f -d -R -q -c -b -@ -F -FF -A -T -y -e -h -u -m -g -j -l -ll -L -v -z -t -tt -o -D -J -X -n -0 -1 -2 -3 -4 -5 -6 -7 -8 -9):zip file:_files -g '(#i)*.(zip|xpi|[ejw]ar)(-.)'" \
       '*:file:->files' && ret=0
   ;;
   unzip)
     _arguments -C -s \
-      '(-f -u -l -t -z -d)-p[extract files to pipe]' \
+      '(-Z)-M[page output]' \
+      - unzip \
+      '(-f -u -l -t -z -d -p)-c[extract files to stdout including file names]' \
       '(-p -u -l -t -z)-f[freshen existing files; create none]' \
+      '(-f -u -l -t -z -c -d)-p[extract files to stdout]' \
       '(-p -f -l -t -z)-u[update files; create if necessary]' \
       '(-p -f -u -t -z -d)-l[list files]' \
       '(-p -f -u -l -z -d)-t[test compressed archive data]' \
+      '-T[timestamp archive to latest]' \
       '(-p -f -u -l -t -d)-z[display archive comment]' \
       '(-p -l -t -z)-d+[specify directory to extract files to]:directory:_files -/' \
       '(-p -l -t -z -o)-n[never overwrite existing files]' \
@@ -70,13 +102,28 @@ case $service in
       '-q[quiet]' '-qq[quieter]' \
       '(-l -t -z)-a[auto-convert any text files]' \
       '(-l -t -z)-aa[treat all files as text]' \
+      '(-l -t -z)-b[treat all files as binary]' \
+      '(-l -t -z)-B[save backup copy of each overwritten file]' \
+      '(-DD)-D[skip restoration of timestamps for extracted directories]' \
+      '(-D)-DD[skip restoration of timestamps for all entries]' \
       '-v[verbose/display version info]' \
-      '(-p -z)-L[lowercase (some) filenames]' \
-      '-M[page output]' \
-      '-x[exclude the following names]' \
-      '-Z[zipinfo mode]:zipinfomode:->zipinfo' \
+      '(-c -p -z -LL)-L[lowercase filenames from case-insensitive file systems]' \
+      '(-c -p -z -L)-LL[lowercase all filenames]' \
+      '-K[keep setuid/setgid/sticky permissions]' \
+      '-P[specify password for decryption]:password' \
+      '(-UU)-U[use escapes for all non-ASCII Unicode]' \
+      '(-U)-UU[ignore any Unicode fields]' \
+      '-W[modify pattern matching so only ** matches /]' \
+      '-\:[allow extraction outside of extraction base directory]' \
+      '-\\\^[allow control characters in extracted entries]' \
+      '-i[include the following names]:*-*:pattern' \
+      '-x[exclude the following names]:*-*:pattern' \
       "(-p -f -u -l -t -z -n -o -j -C -X -q -qq -a -aa -v -L -M)1:zip file:_files -g '(#i)*.(zip|xpi|[ejw]ar)(-.)'" \
-      '*:file:->files' && ret=0
+      '*:file:->files' \
+      - help \
+      '-hh[display detailed help information]' \
+      - zipinfo \
+      '-Z[zipinfo mode]:zipinfomode:->zipinfo' && ret=0
   ;;
 esac
 
diff --git a/Completion/Unix/Command/_zpool b/Completion/Unix/Command/_zpool
index 950266896..4d4793eea 100644
--- a/Completion/Unix/Command/_zpool
+++ b/Completion/Unix/Command/_zpool
@@ -237,7 +237,7 @@ _zpool() {
 		_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 -/' \
+			'(-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]' \
diff --git a/Completion/Unix/Type/_absolute_command_paths b/Completion/Unix/Type/_absolute_command_paths
index e9ab17023..0d52ff851 100644
--- a/Completion/Unix/Type/_absolute_command_paths
+++ b/Completion/Unix/Type/_absolute_command_paths
@@ -7,7 +7,9 @@ _hashed_absolute_command_paths() {
   integer ret=1
   for i in $set_of_dirs_of_hashed_commands
   do
-    compadd -M "l:|=$i" "$expl[@]" -a 'commands[(R)${~i}[^/]#]'
+    local -a matches=( "${(@)commands[(R)${~i}[^/]#]}" )
+    local -a descs=( $matches:t )
+    compadd -M "l:|=$i" -d descs "$@" -a matches
     ret=0
   done
   return ret
@@ -16,7 +18,13 @@ _hashed_absolute_command_paths() {
 # This function completes absolute pathnames of executables, e.g., /etc/rc.local
 _typed-in_absolute_command_paths() {
   # TODO: the description "full path to an executable" and tag in the caller are ignored by _path_files
-  _path_files -/ -g '*(-*)' -P / -W /
+  if [[ -z $PREFIX ]]; then
+    _path_files -/ -g '*(-*)' -P / -W /
+  elif [[ $PREFIX[1] == / ]]; then
+    _path_files -/ -g '*(-*)' -W /
+  else
+    return 1
+  fi
 }
 
 _absolute_command_paths() {
diff --git a/Completion/Unix/Type/_baudrate b/Completion/Unix/Type/_baudrates
index c16928444..add359d13 100644
--- a/Completion/Unix/Type/_baudrate
+++ b/Completion/Unix/Type/_baudrates
@@ -2,7 +2,7 @@
 
 # Offer a list of baud-rates. Usage:
 #
-#   _baudrate [OPTION(s)...]
+#   _baudrates [OPTION(s)...]
 #
 # Available options:
 #
@@ -15,10 +15,6 @@
 #                  value in the complete list to generate an arbitrary
 #                  sub-set.
 #
-#     -t TAG       Use TAG as the tag value in _wanted call.
-#
-#     -d DESC      Use DESC as the description value in _wanted call.
-#
 # The default complete list of available baud rates is also configurable via
 # the 'baud-rates' style:
 #
@@ -27,15 +23,15 @@
 # It is also possible to override the arguments to -f, -u and -l via styles in
 # a similar fashion:
 #
-#   zstyle ':completion:*:*:screen:*' max-baud-rate 9600
-#   zstyle ':completion:*:*:screen:*' min-baud-rate 1200
-#   zstyle ':completion:*:*:screen:*' baud-rate-filter some_function_name
+#   zstyle ':completion:*:*:screen:*:baud-rates' max-value 9600
+#   zstyle ':completion:*:*:screen:*:baud-rates' min-value 1200
+#   zstyle ':completion:*:*:screen:*:baud-rates' filter some_function_name
 
+local tmp
+local -a expl rates
 local -A opts
-local expl tag desc tmp
-local -a rates
 
-zparseopts -E -A opts u: l: f: d: t:
+zparseopts -E -A opts u: l: f:
 
 # The following uses a generated list; first find out where the B* macros are
 # defined:
@@ -51,34 +47,32 @@ zparseopts -E -A opts u: l: f: d: t:
 # which is synonymous to "hang-up".
 
 zstyle -a ":completion:${curcontext}:" baud-rates rates ||
-    rates=( 50 75 110 134 150 200 300 600
-            1200 1800 2400 4800 9600
-            19200 38400 57600
-            115200 230400 460800 500000 576000 921600
-            1000000 1152000 1500000 2000000 2500000 3000000 3500000 4000000 )
+  rates=( 50 75 110 134 150 200 300 600
+          1200 1800 2400 4800 9600
+          19200 38400 57600
+          115200 230400 460800 500000 576000 921600
+          1000000 1152000 1500000 2000000 2500000 3000000 3500000 4000000 )
 
-zstyle -s ":completion:${curcontext}:" max-baud-rate tmp && opts[-u]=$tmp
-zstyle -s ":completion:${curcontext}:" min-baud-rate tmp && opts[-l]=$tmp
-zstyle -s ":completion:${curcontext}:" baud-rate-filter tmp && opts[-f]=$tmp
+zstyle -s ":completion:${curcontext}:baud-rates" max-value tmp && opts[-u]=$tmp
+zstyle -s ":completion:${curcontext}:baud-rates" min-value tmp && opts[-l]=$tmp
+zstyle -s ":completion:${curcontext}:baud-rates" filter tmp && opts[-f]=$tmp
 
 if (( ${+opts[-u]} )) || (( ${+opts[-l]} )); then
-    local -i min max
-    min=${opts[-l]:-0}
-    max=${opts[-u]:-${${(On)rates}[1]}}
-    rates=( ${(M)rates:#${~:-<$min-$max>}} )
+  local -i min max
+  min=${opts[-l]:-0}
+  max=${opts[-u]:-${${(On)rates}[1]}}
+  rates=( ${(M)rates:#${~:-<$min-$max>}} )
 fi
 
 if (( ${+opts[-f]} )); then
-    set -- $rates
-    rates=( )
-    for item; do
-        ${opts[-f]} $item && rates+=( $item )
-    done
+  set -- $rates
+  rates=( )
+  for item; do
+    ${opts[-f]} $item && rates+=( $item )
+  done
 fi
 
-tag=${opts[-t]:-baud-rate}
-desc=${opts[-d]:-baud rate}
-
 # -1V removes dupes (which there shouldn't be) and otherwise leaves the
 # order in the $rates array intact.
-_wanted -1V $tag expl $desc compadd -a $expl -- rates
+_description -1V baud-rates expl 'baud rate'
+compadd "${(@)argv/#-J/-V}" "$expl[@]" -- "${rates[@]}"
diff --git a/Completion/Unix/Type/_canonical_paths b/Completion/Unix/Type/_canonical_paths
index e4a725b2b..6eab7b677 100644
--- a/Completion/Unix/Type/_canonical_paths
+++ b/Completion/Unix/Type/_canonical_paths
@@ -3,83 +3,59 @@
 # This completion function completes all paths given to it, and also tries to
 # offer completions which point to the same file as one of the paths given
 # (relative path when an absolute path is given, and vice versa; when ..'s are
-# present in the word to be completed, and some paths got from symlinks.
+# present in the word to be completed, and some paths got from symlinks).
 
-# Usage: _canonical_paths [-A var] [-N] [-MJV12nfX] tag desc paths...
+# Usage: _canonical_paths [-A var] [-N] [-MJV12nfX] tag desc [paths...]
 
-# -A, if specified, takes the paths from the array variable specified. Paths can
-# also be specified on the command line as shown above. -N, if specified,
+# -A, if specified, takes the paths from the array variable specified. Paths
+# can also be specified on the command line as shown above. -N, if specified,
 # prevents canonicalizing the paths given before using them for completion, in
 # case they are already so. `tag' and `desc' arguments are well, obvious :) In
 # addition, the options -M, -J, -V, -1, -2, -n, -F, -X are passed to compadd.
 
-_canonical_paths_pwd() {
-  # Get the canonical directory name by changing to it.
-  integer chaselinks
-  [[ -o chaselinks ]] && (( chaselinks = 1 ))
-  setopt localoptions nopushdignoredups chaselinks
-  if builtin pushd -q -- $1 2>/dev/null; then
-    REPLY=$PWD
-    (( chaselinks )) || unsetopt chaselinks
-    builtin popd -q
-  else
-    REPLY=$1
-  fi
-}
-
-_canonical_paths_get_canonical_path() {
-  typeset newfile nondir
-  typeset -A seen
-
-  REPLY=$1
-  # Canonicalise the directory path.  We may not be able to
-  # do this if we can't read all components.
-  if [[ -d $REPLY ]]; then
-    _canonical_paths_pwd $REPLY
-  else
-    # Resolve any trailing symbolic links, guarding against loops.
-    while [[ -z ${seen[$REPLY]} ]]; do
-      seen[$REPLY]=1
-      newfile=()
-      zstat -A newfile +link $REPLY 2>/dev/null
-      if [[ -n $newfile[1] ]]; then
-	REPLY=$newfile[1]
-      else
-	break
-      fi
-    done
-    if [[ $REPLY = */*[^/] && $REPLY != /[^/]# ]]; then
-      # Don't try this if there's a trailing slash or we're in
-      # the root directory.
-      nondir=${REPLY##*/#}
-      _canonical_paths_pwd ${REPLY%/#*}
-      REPLY+="/$nondir"
-    fi
-  fi
-}
-
 _canonical_paths_add_paths () {
+  # origpref = original prefix
+  # expref = expanded prefix
+  # curpref = current prefix
+  # canpref = canonical prefix
+  # rltrim = suffix to trim and readd
   local origpref=$1 expref rltrim curpref canpref subdir
   [[ $2 != add ]] && matches=()
   expref=${~origpref} 2>/dev/null
   [[ $origpref == (|*/). ]] && rltrim=.
   curpref=${${expref%$rltrim}:-./}
-  if zstat $curpref >&/dev/null; then
-    _canonical_paths_get_canonical_path $curpref
-    canpref=$REPLY
-  else
-    canpref=$curpref
-  fi
+  canpref=$curpref:P
   [[ $curpref == */ && $canpref == *[^/] ]] && canpref+=/
   canpref+=$rltrim
   [[ $expref == *[^/] && $canpref == */ ]] && origpref+=/
-  matches+=(${${(M)files:#$canpref*}/$canpref/$origpref})
+
+  # Append to $matches the subset of $files that matches $canpref.
+  if [[ $canpref == $origpref ]]; then
+    # This codepath honours any -M matchspec parameters.
+    () {
+      local -a tmp_buffer
+      compadd -A tmp_buffer "$__gopts[@]" -a files
+      matches+=( "${(@)tmp_buffer/$canpref/$origpref}" )
+    }
+  else
+    # ### Ideally, this codepath would do what the 'if' above does,
+    # ### but telling compadd to pretend the "word on the command line"
+    # ### is ${"the word on the command line"/$origpref/$canpref}.
+    matches+=(${${(M)files:#$canpref*}/$canpref/$origpref})
+  fi
+
   for subdir in $expref?*(@); do
     _canonical_paths_add_paths ${subdir/$expref/$origpref} add
   done
 }
 
 _canonical_paths() {
+  # The following parameters are used by callee functions:
+  #    __gopts
+  #    matches
+  #    files
+  #    (possibly others)
+
   local __index
   typeset -a __gopts __opts
 
@@ -105,10 +81,7 @@ _canonical_paths() {
   if (( $__opts[(I)-N] )); then
     files=($@)
   else
-    for __index in $@; do
-      _canonical_paths_get_canonical_path $__index
-      files+=($REPLY)
-    done
+    files+=($@:P)
   fi
 
   local base=$PREFIX
diff --git a/Completion/Unix/Type/_date_formats b/Completion/Unix/Type/_date_formats
index 09f8cab52..6bd5c6d6b 100644
--- a/Completion/Unix/Type/_date_formats
+++ b/Completion/Unix/Type/_date_formats
@@ -19,27 +19,24 @@ compset -S '%*'
 specs=(
   'a:abbreviated day name'
   'A:full day name'
-  'b:abbreviated month name'
+  {b,h}':abbreviated month name'
   'B:full month name'
   'c:preferred locale date and time'
   'C:2-digit century'
   'd:day of month (01-31)'
   'D:american format month/day/year (%m/%d/%y)'
   'e:day of month ( 1-31)'
-  'E:alternate representation'
   'F:ISO 8601 year-month-date (%Y-%m-%d)'
   'G:4-digit ISO 8601 week-based year'
   'g:2-digit ISO 8601 week-based year'
-  'h:abbreviated month name'
   'H:hour (00-23)'
   'I:hour (01-12)'
   'j:day of year (001-366)'
-  'k:hour ( 1-23)'
+  'k:hour ( 0-23)'
   'l:hour ( 1-12)'
   'm:month (01-12)'
   'M:minute (00-59)'
   'n:newline'
-  'O:alternative format modifier'
   'p:locale dependent AM/PM'
   'r:locale dependent a.m. or p.m. time (%I:%M:%S %p)'
   'R:24-hour notation time (%H:%M)'
@@ -62,24 +59,26 @@ specs=(
 )
 
 case $OSTYPE in
-  freebsd*|linux-gnu|solaris2.<11->)
+  freebsd*|dragonfly*|darwin*|linux-gnu|solaris2.<11->)
     specs+=(
+      'E:alternate representation'
+      'O:alternative format modifier'
       "-:don't pad numeric values"
-      '#:swap case of alphabetic characters'
       '0:left pad numeric values with zeroes'
-      '^:convert lowercase characters to uppercase'
       '_:left pad numeric values with spaces'
     )
   ;|
   linux-gnu|solaris2.<11->)
     specs+=(
+      '#:swap case of alphabetic characters'
+      '^:convert lowercase characters to uppercase'
       'P:lower case locale dependent am/pm'
     )
   ;|
-  freebsd*)
+  freebsd*|dragonfly*|darwin*|openbsd*|netbsd*)
     specs+=( 'v:date in short form (%e-%b-%Y)' )
   ;|
-  solaris2.<11->|freebsd*)
+  solaris2.<11->|freebsd*|dragonfly*|darwin*|openbsd*)
     specs+=( '+:localized representation of date and time' )
   ;;
   solaris2.<-10>)
@@ -103,4 +102,4 @@ done
 
 _describe -t date-format-specifier 'date format specifier' specs \
     -p "${(Q)PREFIX:-%}" -S ''
-_message -e date-format-precision 'precision for %%. (1-6)'
+[[ $1 == zsh ]] && _message -e date-format-precision 'precision for %%. (1-6)'
diff --git a/Completion/Unix/Type/_dates b/Completion/Unix/Type/_dates
index 83397b45a..0af10eb24 100644
--- a/Completion/Unix/Type/_dates
+++ b/Completion/Unix/Type/_dates
@@ -9,7 +9,7 @@
 #   max-matches-length : maximum number or percentage of lines to use for
 #                        completion listing, if both are specified, the
 #                        lowest takes precedence.
-#   format             : override date format
+#   date-format        : override date format
 
 local -a disp cand expl
 local userformat format spacer=1 spacing month monstart skip match
@@ -22,10 +22,10 @@ local future mlabel mfmt mlabels
 
 zparseopts -D -K -E f:=format F=future
 (( future = $#future ? 1 : -1 ))
-zstyle -s ':completion:$curcontext:dates' date-format userformat
+zstyle -s ":completion:${curcontext}:dates" date-format userformat
 format=${userformat:-${format[2]:-%F}}
 
-zstyle -a ':completion:$curcontext:dates' max-matches-length r
+zstyle -a ":completion:${curcontext}:dates" max-matches-length r
 for ri in $r; do
   [[ $ri = [0-9]##% ]] && (( ri = LINES * .${ri%%%} ))
   (( ri < rows )) && (( rows=ri ))
@@ -37,6 +37,7 @@ _tags dates || return 0
 _comp_mesg=yes
 _description -2V -x dates expl date
 compadd "${@:/-X/-x}" "$expl[@]" -
+[[ -z $MENUSELECT && $WIDGET != menu-select ]] && return
 [[ -n $PREFIX$SUFFIX ]] && return 0
 (( rows )) || return 0
 compstate[list]='packed rows'
diff --git a/Completion/Unix/Type/_diff_options b/Completion/Unix/Type/_diff_options
index 6af392a13..55ddecd2a 100644
--- a/Completion/Unix/Type/_diff_options
+++ b/Completion/Unix/Type/_diff_options
@@ -6,6 +6,25 @@ local -a args
 cmd="$1"
 shift
 
+_diff_palette() {
+  local context state line ret=1
+  local -a suf
+  _values -s : attribute \
+    "ad[added text]:attribute [32]:->attrs" \
+    "de[deleted text]:attribute [31]:->attrs" \
+    "hd[header]:attribute [1]:->attrs" \
+    "ln[line numbers]:attribute [36]:->attrs" \
+    "rs[rest - other text]:attribute [0]:->attrs" && ret=0
+  if [[ -n $state ]]; then
+    compset -P '*;'
+    compset -S '[;=]*' || suf=( -S: -r ": ;\\\t\n\=" )
+    _alternative -C context -O suf \
+      'attributes:attributes:((0:reset 1:bold 3:italics 4:underline 5:blink))' \
+      'colors:color:((30:default 31:red 32:green 33:yellow 34:blue 35:magenta 36:cyan 37:white))' && ret=0
+  fi
+  return ret
+}
+
 if _pick_variant -c $cmd gnu=GNU unix -v; then
   # output formats
   of="-y --side-by-side -n --rcs -e -f --ed -q --brief -c -C --context -u -U \
@@ -46,6 +65,7 @@ if _pick_variant -c $cmd gnu=GNU unix -v; then
     '(--no-ignore-file-name-case)--ignore-file-name-case[ignore case when comparing file names]' \
     '(--ignore-file-name-case)--no-ignore-file-name-case[consider case when comparing file names]' \
     '(-E --ignore-tab-expansion)'{-E,--ignore-tab-expansion}'[ignore changes due to tab expansion]' \
+    '(-Z --ignore-trailing-space)'{-Z,--ignore-trailing-space}'[ignore white space at line end]' \
     '(-b --ignore-space-change)'{-b,--ignore-space-change}'[ignore changes in the amount of white space]' \
     '(--ignore-all-space -w)'{--ignore-all-space,-w}'[ignore all white space]' \
     '(-B --ignore-blank-lines)'{-B,--ignore-blank-lines}'[ignore lines that are all blank]' \
@@ -56,7 +76,7 @@ if _pick_variant -c $cmd gnu=GNU unix -v; then
     "($of $oss)-c[output a context diff]" \
     "($of $oss)"{-U+,--unified=-}'[output a unified diff]:number of lines of unified context' \
     "($of $oss)-u[output a unified diff]" \
-    "($ofwuc $oss)*"{-L+,--label=}'[set label to use instead of file name]:label' \
+    "($ofwuc $oss)*"{-L+,--label=}'[set label to use instead of file name and timestamp]:label' \
     "($ofwuc $oss -p --show-c-function)"{-p,--show-c-function}'[show C function of each change]' \
     "($ofwuc $oss -F --show-function-line)"{-F+,--show-function-line=}'[show the most recent line matching regex]:regex' \
     "($of $ouc $oss)--brief[output only whether files differ]" \
@@ -84,8 +104,9 @@ if _pick_variant -c $cmd gnu=GNU unix -v; then
     '(-l --paginate)'{-l,--paginate}'[output through pr]' \
     '(-t --expand-tabs)'{-t,--expand-tabs}'[expand tabs to spaces]' \
     '(-T --initial-tab)'{-T,--initial-tab}'[prepend a tab]' \
-    '--tabsize=[specify width of tab]:width' \
+    '--tabsize=[specify width of tab]:width [8]' \
     '(-r --recursive)'{-r,--recursive}'[recursively compare subdirectories]' \
+    "--no-dereference[don't follow symbolic links]" \
     '(-N --new-file)'{-N,--new-file}'[treat absent files as empty]' \
     '(-P --unidirectional-new-file)'{-P,--unidirectional-new-file}'[treat absent first files as empty]' \
     '(-s --report-identical-files)'{-s,--report-identical-files}'[report when two files are the same]' \
@@ -97,6 +118,8 @@ if _pick_variant -c $cmd gnu=GNU unix -v; then
     '--horizon-lines=[set number of lines to keep in prefix and suffix]:number of horizon lines' \
     '(-d --minimal)'{-d,--minimal}'[try to find a smaller set of changes]' \
     '(-H --speed-large-files)'{-H,--speed-large-files}'[assume large files and many small changes]' \
+    '--color=-[use colors in output]::when [auto]:(never always auto)' \
+    '--palette=[specify colors to use]:color:_diff_palette' \
     '(-v --version)'{-v,--version}'[display version info]' \
     '--help[display help info]' \
     "$@"
diff --git a/Completion/Unix/Type/_email_addresses b/Completion/Unix/Type/_email_addresses
index 7d5c942de..8a5877a9c 100644
--- a/Completion/Unix/Type/_email_addresses
+++ b/Completion/Unix/Type/_email_addresses
@@ -5,6 +5,8 @@
 # -s sep    - complete a list of addresses separated by specified character
 # -c        - e-mail address must be of form user@host (no comments or aliases)
 #
+# TODO: with -n, have the named plugin complete not only aliases but also addresses?
+# 
 # Plugins are written as separate functions with names starting `_email-'.
 # They should either do their own completion or return the addresses in the
 # reply array in the form 'alias:address' and return 300. The -c option is
diff --git a/Completion/Unix/Type/_files b/Completion/Unix/Type/_files
index fe0780a57..2b0c5580a 100644
--- a/Completion/Unix/Type/_files
+++ b/Completion/Unix/Type/_files
@@ -7,6 +7,7 @@ local ret=1
 # we don't want to complete them multiple times (for each file pattern).
 if _have_glob_qual $PREFIX; then
   compset -p ${#match[1]}
+  compset -S '[^\)\|\~]#(|\))'
   if [[ $_comp_caller_options[extendedglob] == on ]] && compset -P '\#'; then
     _globflags && ret=0
   else
@@ -34,7 +35,7 @@ if (( $tmp[(I)-g*] )); then
   [[ "$glob" = (#b)(*\()([^\|\~]##\)) && $match[2] != \#q* ]] &&
       glob="${match[1]}#q${match[2]}"
 elif [[ $type = */* ]]; then
-  glob="*(-/)"
+  glob="*(#q-/)"
 fi
 tmp=$opts[(I)-F]
 if (( tmp )); then
diff --git a/Completion/Unix/Type/_hosts b/Completion/Unix/Type/_hosts
index c3133dc68..d9e1090a4 100644
--- a/Completion/Unix/Type/_hosts
+++ b/Completion/Unix/Type/_hosts
@@ -45,13 +45,23 @@ if ! zstyle -a ":completion:${curcontext}:hosts" hosts _hosts; then
 
         # known_hosts syntax supports the host being in the form [hostname]:port
         # The filter below extracts the hostname from lines using this format.
-        khosts=($(for host ($khosts); do
-          if [[ $host =~ "\[(.*)\]:\d*" ]]; then
-            echo $match
-          else
-            echo $host
-          fi
-        done))
+        #
+        # known_hosts syntax supports wildcards.  The filter below removes wildcard
+        # entries.
+        () {
+          local host
+          local -a match mbegin mend
+          khosts=()
+          for host; do
+            if [[ $host == *[*?]* ]]; then
+              continue
+            elif [[ $host = (#b)*\[(*)\]:[[:digit:]]#* ]]; then
+              khosts+=${match[1]}
+            else
+              khosts+=$host
+            fi
+          done
+        } "$khosts[@]"
 
         if [[ -z $useip ]]; then
           khosts=(${${khosts:#(#s)[0-9]##.[0-9]##.[0-9]##.[0-9]##(#e)}:#(#s)[0-9a-f:]##(#e)})
diff --git a/Completion/Unix/Type/_path_files b/Completion/Unix/Type/_path_files
index c64ebf58c..9fa6ae9fc 100644
--- a/Completion/Unix/Type/_path_files
+++ b/Completion/Unix/Type/_path_files
@@ -17,6 +17,7 @@ local -a match mbegin mend
 if _have_glob_qual $PREFIX; then
   local ret=1
   compset -p ${#match[1]}
+  compset -S '[^\)\|\~]#(|\))'
   if [[ $_comp_caller_options[extendedglob] == on ]] && compset -P '\#'; then
     _globflags && ret=0
   else
@@ -68,7 +69,7 @@ fi
 pats=( "${(@)pats:# #}" )
 
 if (( $#pfx )); then
-  compset -P "$pfx[2]" || pfxsfx=( "$pfx[@]" "$pfxsfx[@]" )
+  compset -P "${(b)pfx[2]}" || pfxsfx=( "$pfx[@]" "$pfxsfx[@]" )
 fi
 
 if (( $#prepaths )); then
@@ -273,7 +274,7 @@ if [[ "$pre" = [^][*?#^\|\<\>\\]#(\`[^\`]#\`|\$)*/* && "$compstate[quote]" != \'
   orig="${orig[1,(in:i:)/][1,-2]}"
   donepath=
   prepaths=( '' )
-elif [[ "$pre[1]" = \~ && -z "$compstate[quote]" ]]; then
+elif [[ "$pre[1]" = \~ && "$compstate[quote]" = (|\`) ]]; then
 
   # It begins with `~', so remember anything before the first slash to be able
   # to report it to the completion code. Also get an expanded version of it
@@ -376,11 +377,10 @@ for prepath in "$prepaths[@]"; do
     # on, we need to remove quotes from everything that's not a pattern
     # character, because the code that does the file generation only
     # strips quotes from pattern characters (you know better than
-    # to ask why).  Because we need to test for a real directory,
-    # however, for tmp2 we unquote everything.
+    # to ask why).
     tmp1=${match[1]}
     tpre=${match[2]}
-    tmp2=${(Q)tmp1}
+    tmp2=$tmp1
     tmp1=${tmp1//(#b)\\(?)/$match[1]}
     tpre=${tpre//(#b)\\([^\\\]\[\^\~\(\)\#\*\?])/$match[1]}
     # Theory: donepath needs the quoting of special characters
@@ -593,7 +593,7 @@ for prepath in "$prepaths[@]"; do
     # There are more components, so skip over the next components and make a
     # slash be added.
 
-    tmp1=( ${tmp1//(#b)([][()|*?^#~<>\\=])/\\${match[1]}} )
+    #tmp1=( ${tmp1//(#b)([][()|*?^#~<>\\=])/\\${match[1]}} )
     tmp2="${(M)tpre##${~skips}}"
     if [[ -n "$tmp2" ]]; then
       skipped="/$tmp2"
diff --git a/Completion/Unix/Type/_pspdf b/Completion/Unix/Type/_pspdf
index d0ca31a7c..5bb8650b1 100644
--- a/Completion/Unix/Type/_pspdf
+++ b/Completion/Unix/Type/_pspdf
@@ -1,4 +1,4 @@
-#compdef evince gsbj gsdj gsdj500 gslj gslp gsnd ps2ascii ghostview mgv pstoedit pstotgif zathura
+#compdef gsbj gsdj gsdj500 gslj gslp gsnd ps2ascii ghostview mgv pstoedit pstotgif zathura
 
 local expl ext
 
diff --git a/Completion/Unix/Type/_remote_files b/Completion/Unix/Type/_remote_files
index db3316422..a5fce9afd 100644
--- a/Completion/Unix/Type/_remote_files
+++ b/Completion/Unix/Type/_remote_files
@@ -28,16 +28,19 @@
 
 
 # There should be coloring based on all the different ls -F classifiers.
-local expl rempat remfiles remdispf remdispd args cmd cmd_args suf ret=1
+local expl rempat remfiles remdispf remdispd args cmd suf ret=1
+local -a args cmd_args
 local glob host
 
 if zstyle -T ":completion:${curcontext}:files" remote-access; then
 
   # Parse options to _remote_files. Stops at the first "--".
   zparseopts -D -E -a args / g:=glob h:=host
-  shift
   (( $#host)) && shift host || host="${IPREFIX%:}"
 
+  args=( ${argv[1,(i)--]} )
+  shift ${#args}
+  [[ $args[-1] = -- ]] && args[-1]=()
   # Command to run on the remote system.
   cmd="$1"
   shift
@@ -45,9 +48,9 @@ if zstyle -T ":completion:${curcontext}:files" remote-access; then
   # Handle arguments to ssh.
   if [[ $cmd == ssh ]]; then
     zparseopts -D -E -a cmd_args p: 1 2 4 6 F:
-    cmd_args="-o BatchMode=yes $cmd_args -a -x"
+    cmd_args=( -o BatchMode=yes "$cmd_args[@]" -a -x )
   else
-    cmd_args="$@"
+    cmd_args=( "$@" )
   fi
 
   if [[ -z $QIPREFIX ]]
@@ -55,11 +58,13 @@ if zstyle -T ":completion:${curcontext}:files" remote-access; then
     else rempat="${(q)PREFIX%%[^./][^/]#}\*"
   fi
 
+  # remote filenames
   remfiles=(${(M)${(f)"$(_call_program files $cmd $cmd_args $host ls -d1FL -- "$rempat" 2>/dev/null)"}%%[^/]#(|/)})
 
   compset -P '*/'
   compset -S '/*' || (( ${args[(I)-/]} )) || suf='remote file'
 
+  # display strings for remote files and directories
   remdispf=(${remfiles:#*/})
   remdispd=(${(M)remfiles:#*/})
 
@@ -74,9 +79,9 @@ if zstyle -T ":completion:${curcontext}:files" remote-access; then
   while _tags; do
     while _next_label files expl ${suf:-remote directory}; do
       [[ -n $suf ]] &&
-          compadd "$@" "$expl[@]" -d remdispf ${(q)remdispf%[*=|]} && ret=0
-      compadd ${suf:+-S/} -r "/ \t\n\-" "$@" "$expl[@]" -d remdispd \
-	${(q)remdispd%/} && ret=0
+          compadd "$args[@]" "$expl[@]" -d remdispf -- ${(q)remdispf%[*=|]} && ret=0
+      compadd ${suf:+-S/} -r "/ \t\n\-" "$args[@]" "$expl[@]" -d remdispd \
+	-- ${(q)remdispd%/} && ret=0
     done
     (( ret )) || return 0
   done
diff --git a/Completion/Unix/Type/_signals b/Completion/Unix/Type/_signals
index 104efd4f4..df9a2f339 100644
--- a/Completion/Unix/Type/_signals
+++ b/Completion/Unix/Type/_signals
@@ -8,12 +8,15 @@
 #
 # A `-' or `--' as the first argument is ignored.
 
-local expl last minus pre sigs
+local expl minus pre sigs
+local first last # keep these as strings for -z/-n tests
 
 zparseopts -D -K -E 'p=minus' 'a=last' 's=pre'
 if [[ -z "$last" ]]; then
+  first=2
   last=-3
 else
+  first=1
   last=-1
 fi
 [[ -n "$minus" ]] && minus='-'
@@ -26,20 +29,20 @@ if [[ -z "$minus" ]] ||
   local disp tmp
 
   if zstyle -t ":completion:${curcontext}:signals" prefix-hidden; then
-    tmp=( "${(@)signals[1,last]}" )
+    tmp=( "${(@)signals[first,last]}" )
     disp=(-d tmp)
   else
     disp=()
   fi
 
   if [[ -n "$pre" && $PREFIX = ${minus}S* ]]; then
-    sigs=( "${minus}SIG${(@)^signals[1,last]}" )
-    (( $#disp )) && tmp=( "$tmp[@]" "${(@)signals[1,last]}" )
+    sigs=( "${minus}SIG${(@)^signals[first,last]}" )
+    (( $#disp )) && tmp=( "$tmp[@]" "${(@)signals[first,last]}" )
   else
     sigs=()
   fi
 
   _wanted signals expl signal \
       compadd "$@" "$disp[@]" -M 'm:{a-z}={A-Z}' - \
-              "${minus}${(@)^signals[1,last]}" "$sigs[@]"
+              "${minus}${(@)^signals[first,last]}" "$sigs[@]"
 fi
diff --git a/Completion/Unix/Type/_sys_calls b/Completion/Unix/Type/_sys_calls
new file mode 100644
index 000000000..bd9b34f70
--- /dev/null
+++ b/Completion/Unix/Type/_sys_calls
@@ -0,0 +1,20 @@
+#autoload
+
+# Options:
+#
+# -a    add "all" as an additional match
+# -n    add "none" as an additional match
+
+local expl all none
+local ifile=/usr/include/sys/syscall.h
+local -au syscalls
+
+zparseopts -D -K -E a=all n=none
+
+[[ $OSTYPE = linux* ]] && ifile=/usr/include/bits/syscall.h
+syscalls=( ${${${(M)${(f)"$(<$ifile)"}:#?define[[:blank:]]##SYS_*}#*[[:blank:]]SYS_}%%[[:blank:]]*} ) 2>/dev/null
+[[ -n $all ]] && syscalls+=( all )
+[[ -n $none ]] && syscalls+=( none )
+
+_description syscalls expl 'system call'
+compadd "$@" "$expl[@]" -a syscalls
diff --git a/Completion/Unix/Type/_tilde_files b/Completion/Unix/Type/_tilde_files
index ee6b18088..b1b3b37f0 100644
--- a/Completion/Unix/Type/_tilde_files
+++ b/Completion/Unix/Type/_tilde_files
@@ -30,7 +30,8 @@ case "$PREFIX" in
   ;;
 \~*)
   compset -P '?'
-  _users "$@"
+  local -a expl=( "$@" )
+  _alternative -O expl users:user:_users named-directories:'named directory':'compadd -k nameddirs'
   ;;
 *)
   _files "$@"
diff --git a/Completion/Unix/Type/_zfs_dataset b/Completion/Unix/Type/_zfs_dataset
index 5fa3e9e50..6bef04e45 100644
--- a/Completion/Unix/Type/_zfs_dataset
+++ b/Completion/Unix/Type/_zfs_dataset
@@ -4,7 +4,7 @@ local -a type expl_type_arr rsrc rdst paths_allowed
 local -a typearg datasetlist expl mlist
 local expl_type
 
-# -e takes an argument which is passed as as the "descr" argument to _wanted
+# -e takes an argument which is passed as the "descr" argument to _wanted
 # -p indicates that filesystem paths, not just dataset names, are allowed
 # -r1 indicates that we're completing the source of a rename
 # -r2 indicates that we're completing the destination of a rename
@@ -58,14 +58,14 @@ if [[ ${#rdst} -gt 0 ]]; then
 fi
 
 if [[ -n $type[(r)clone] ]]; then
-	datasetlist=( ${="$(zfs list -H -o name,origin -t filesystem | awk "\$2 != \"-\" {print \$1}")":#no cloned filesystems available} )
+	datasetlist=( ${="$(zfs list -H -o name,origin -t filesystem 2>/dev/null | awk "\$2 != \"-\" {print \$1}")":#no cloned filesystems available} )
 else
-	datasetlist=( ${="$(zfs list -H -o name $typearg)":#no datasets available} )
+	datasetlist=( ${="$(zfs list -H -o name $typearg 2>/dev/null)":#no datasets available} )
 fi
 
 expl_type=${typearg[2,-1]//,/\/}
 if [[ -n $type[(r)mtpt] ]]; then
-	mlist=( ${="$(zfs list -H -o mountpoint $typearg)":#no mountpoints available} )
+	mlist=( ${="$(zfs list -H -o mountpoint $typearg 2>/dev/null)":#no mountpoints available} )
 	datasetlist=( $datasetlist $mlist )
 	expl_type="$expl_type/mountpoint"
 fi
diff --git a/Completion/X/Command/_evince b/Completion/X/Command/_evince
new file mode 100644
index 000000000..48d5a9aeb
--- /dev/null
+++ b/Completion/X/Command/_evince
@@ -0,0 +1,30 @@
+#compdef evince
+
+_arguments -s -S \
+  - '(help)' \
+  '--version[display version information]' \
+  {-h,--help}'[display help information]' \
+  '--help-all[display help on all options]' \
+  '--help-sm-client[display help on session management options]' \
+  '--help-gtk[display help on GTK+ options]' \
+  - normal \
+  '--sm-client-disable[disable connection to session manager]' \
+  '--sm-client-state-file=[specify file containing saved configuration]:file:_files' \
+  '--sm-client-id=[specify session management ID]:id' \
+  '--class=[specify program class as used by the window manager]:class' \
+  '--name=[program name as used by the window manager]:name:_x_name' \
+  '--gdk-debug=[specify GDK debugging flags to set]:flag' \
+  '--gdk-no-debug=[specify GDK debugging flags to unset]:flag' \
+  '--gtk-module=[load additional GTK+ modules]:module' \
+  '--g-fatal-warnings[make all warnings fatal]' \
+  '--gtk-debug=[specify GTK+ debugging flags to set]:flag' \
+  '--gtk-no-debug=[specify GTK+ debugging flags to unset]:flag' \
+  '(-p --page-label -i --page-index -n --named-dest)'{-p,--page-label=}'[specify page label of the document to display]' \
+  '(-p --page-label -i --page-index -n --named-dest)'{-i,--page-index=}'[specify page number of the document to display]' \
+  '(-p --page-label -i --page-index -n --named-dest)'{-n,--named-dest=}'[specify named destination to display]' \
+  '(-f --fullscreen -s --presentation -w --preview)'{-f,--fullscreen}'[run evince in fullscreen mode]' \
+  '(-f --fullscreen -s --presentation -w --preview)'{-s,--presentation}'[run evince in presentation mode]' \
+  '(-f --fullscreen -s --presentation -w --preview)'{-w,--preview}'[run evince as a previewer]' \
+  '(-l --find)'{-l,--find=}'[specify word or phrase to find in the document]:word or phrase' \
+  '--display=[specify X display]:display:_x_display' \
+  '*:document file:_files -g "*.(#i)(dvi|djvu|pdf|ps|eps|tif|tiff)(-.)"' \
diff --git a/Completion/X/Command/_okular b/Completion/X/Command/_okular
index bfdb4de4b..f08f81c6e 100644
--- a/Completion/X/Command/_okular
+++ b/Completion/X/Command/_okular
@@ -1,10 +1,10 @@
 #compdef okular
 local extns
 
-extns="{pdf,ps,eps,dvi}(.gz|.bz2)(#c,1)|djvu|tiff|chm|cbr|cbz"
+extns="(pdf|ps|eps|dvi)(|.gz|.bz2)|djvu|tif|tiff|chm|cbr|cbz"
 
 _arguments \
   '(-p --page)'{-p,--page}'[page of the document to be shown]:page: ' \
   '--presentation[start the document in presentation mode]' \
   '--unique[unique instance control]' \
-  "*:Okular documents:_files -g '*.(#i)($extns)(-.)'"
+  "*:okular document:_files -g '*.(#i)($extns)(-.)'"
diff --git a/Completion/X/Command/_rdesktop b/Completion/X/Command/_rdesktop
index 0bd08d932..55a6ea7b5 100644
--- a/Completion/X/Command/_rdesktop
+++ b/Completion/X/Command/_rdesktop
@@ -1,37 +1,81 @@
-#compdef rdesktop
+#compdef rdesktop xfreerdp
 
-local curcontext="$curcontext" state line expl redir ret=1
+local curcontext="$curcontext" state line expl args redir ret=1
 typeset -A opt_args
 
-_arguments -C -s \
+case $service in
+  rdesktop)
+    args=(
+      '(-5)-4[use RDP version 4]'
+      '(-4)-5[use RDP version 5]'
+      '-b[force server to use bitmaps for screen updates]'
+      '-B[use X server backing store]'
+      '-E[disable encryption]'
+      "-m[don't send mouse motion events]"
+      '-C[use private colormap]'
+      '-S[single application mode]'
+      '-N[enable numlock synchronisation]'
+      '-P[enable bitmap caching]'
+      '*-r[device redirection]: :->redirection'
+    )
+  ;;
+  xfreerdp)
+    args=(
+      '(-)'{-h,--help}'[display help information]'
+      "-o[don't redirect audio to client]"
+      '-s[set startup-shell]:shell'
+      '-t[connect to specified port]:port:_ports'
+      '--app[RemoteApp connection]'
+      '--ext[load an extension]:extension'
+      '--no-auth[disable authentication]'
+      '--authonly[authentication only, no UI]'
+      '--from-stdin[prompt for username, password, domain and hostname]'
+      '--no-fastpath[disable fast-path]'
+      '--gdi[graphics rendering]:backend:(hw sw)'
+      "--no-motion[don't send mouse motion events]"
+      '--no-osb[disable offscreen bitmaps]'
+      '--no-bmp-cache[disable bitmap cache]'
+      '--plugin[load a virtual channel plugin]:(cliprdr drdynvc rdpsnd rail rdpdbg rdpdr)'
+      '--rfx[enable RemoteFX]'
+      '--rfx-mode[RemoteFX operational flags]:mode:((v\:video i\:image))'
+      '--nsc[enable NSCodec]'
+      '--disable-wallpaper'
+      '--composition[enable desktop composition]'
+      '--disable-full-window-drag'
+      '--disable-menu-animations'
+      '--disable-theming'
+      '--kbd-list[list all keyboard layout ids used by -k]'
+      '--no-rdp[disable Standard RDP encryption]'
+      '--no-tls[disable TLS encryption]'
+      '--no-nla[disable network level authentication]'
+      '--ntlm[force NTLM authentication protocol version]:version:(1 2)'
+      '--certificate-name[specify logon certificate]:certificate'
+      '--ignore-certificate[ignore verification of logon certificate]'
+      '--sec[force protocol security]:security:(rdp tls nla)'
+      '--secure-checksum[use salted checksums with Standard RDP encryption]'
+      '--version[print version information]'
+    )
+  ;;
+esac
+
+_arguments -C -s $args \
   '-u[username]:username:_users' \
   '-d[domain]:domain' \
   '-s[shell]:startup shell' \
   '-c[working directory]:directory:_directories' \
   '-p[password]:password' \
-  '-n[hostname]:host:_hosts' \
+  '-n[set reported client hostname]:client hostname:_hosts' \
   '-k[keyboard map]:keyboard map' \
   '(-f)-g[geometry]:geometry (WxH)' \
   '(-g)-f[fullscreen mode]' \
-  '-b[force server to use bitmaps for screen updates]' \
-  '-B[use X server backing store]' \
-  '-E[disable encryption]' \
-  "-m[don't send mouse motion events]" \
-  '-C[use private colormap]' \
   '-D[hide window decorations]' \
   '-K[do not override window manager key bindings]' \
-  '-S[single application mode]' \
   '-T[set window title]:title' \
-  '-N[enable numlock synchronisation]' \
   '-X[embed in another window]:window:_x_window' \
-  '-a[colour depth]:depth:(8 15 16 24)' \
+  '-a[colour depth]:depth:(8 15 16 24 32)' \
   '-z[enable compression]' \
   '-x[bandwidth performance behaviour]:experience:((b\:broadband l\:lan m\:modem))' \
-  '-P[enable bitmap caching]' \
-  '*-r[device redirection]: :->redirection' \
   '-0[attach to server console]' \
-  '(-5)-4[use RDP version 4]' \
-  '(-4)-5[use RDP version 5]' \
   ':server:->hostsport' && ret=0
 
 case $state in
@@ -45,9 +89,8 @@ case $state in
   ;;
   redirection)
     redir="${PREFIX%%:*}"
-    if compset -P '*='; then
+    if compset -P 1 '*='; then
       curcontext="${curcontext%:*}:$redir"
-      compset -P '*='
       case $redir in
 	comport|lptport) _wanted devices expl device _files -g '*(-%)' && ret=0 ;;
 	disk) _directories && ret=0 ;;
diff --git a/Completion/X/Command/_setxkbmap b/Completion/X/Command/_setxkbmap
index c490a9d8c..f7310ecdd 100644
--- a/Completion/X/Command/_setxkbmap
+++ b/Completion/X/Command/_setxkbmap
@@ -1,4 +1,4 @@
-# compdef setxkbmap
+#compdef setxkbmap
 
 # TODO:
 # model, option, symbols and types suggestions
@@ -55,7 +55,7 @@ _setxkbmap_files () {
     local dir="$1"
     local label="$2"
 
-    local -a fullpath shortpath
+    local -a fullpath shortpath expl
 
     fullpath=($sourcedir/$dir/**/*~*README(.))
     shortpath=(${fullpath#$sourcedir\/$dir\/})
@@ -82,7 +82,7 @@ _setxkbmap_geometry () {
 (( $+functions[_setxkbmap_variant] )) ||
 _setxkbmap_variant () {
     local file=$sourcedir/symbols/${1}
-    local -a variants lines
+    local -a variants lines expl
 
     if [ ! -f $file ]; then
         _message "no such layout: ${1}"
@@ -90,7 +90,7 @@ _setxkbmap_variant () {
     fi
 
     lines=("${(f)$(< ${file})}")
-    variants=(${${${(M)lines:#*xkb_symbols*\"([[:alnum:]])##\"*}##*xkb_symbols([^\"])##\"}%%\"*})
+    variants=(${${${(M)lines:#*xkb_symbols*\"([^\"])##\"*}##*xkb_symbols([^\"])##\"}%%\"*})
     
     _wanted variant expl 'variant' compadd -a variants
 
diff --git a/Completion/X/Command/_x_utils b/Completion/X/Command/_x_utils
index 05a899b68..13c5572af 100644
--- a/Completion/X/Command/_x_utils
+++ b/Completion/X/Command/_x_utils
@@ -1,6 +1,6 @@
-#compdef xdpyinfo xwininfo xkill xfontsel xfd xev xhost xon xsetroot xwd xwud xrdb xprop xlsatoms
+#compdef xdpyinfo xwininfo xkill xfontsel xfd xev xhost xon xsetroot xwd xwud xrdb xprop xlsatoms xlsclients
 
-local curcontext="$curcontext" state line expl ret=1
+local curcontext="$curcontext" state state_descr line expl ret=1
 
 case "$service" in
 xdpyinfo)
@@ -26,10 +26,10 @@ xprop)
     '-font[display the properties of a font]:font: _x_font' \
     '-len[show at most n bytes of any property]:n' \
     '*-fs[read more property formats from file]:file: _files' \
-    '*-remove[specify an atom to remove from the target window]:atoms:->atoms' \
-    '*-set[specify an atom and a value to set on the target window]:atoms:->atoms:value' \
-    '*-f:atoms:->atoms:format: ::dformat' \
-    '*:atoms:->atoms'
+    '*-remove[specify an atom to remove from the target window]:property atom:->atoms' \
+    '*-set[specify an atom and a value to set on the target window]:property atom:->atoms:value' \
+    '*-f:property atom:->atoms:format: ::dformat' \
+    '*:property atoms:->atoms'
   ;;
 xlsatoms)
   _x_arguments -C \
@@ -37,6 +37,12 @@ xlsatoms)
     '*-range[atom values to list]:[num]-[num]' \
     '*-name[name of single atom to print]:atom:->atoms'
   ;;
+xlsclients)
+  _x_arguments -C \
+    '-a[list all clients on all screens]' \
+    '-l[long format adding window/icon names and class hints]' \
+    '-m[maximum characters of command to include]:characters'
+ ;;
 xkill)
   _x_arguments \
     -version \
@@ -76,12 +82,16 @@ xev)
 xhost)
   local type tmp match
 
+  if [[ -z $PREFIX ]]; then
+    _describe prefixes '(-:disallow +:allow)' -S '' -r ''
+  fi
+
   if compset -P '-'; then
     tmp=(${(f)"$(xhost)"})
     shift tmp
     tmp=(${tmp:#LOCAL:|<*>})
     if [[ "$tmp" = *:* ]]; then
-      if compset -P '(#b)(*):'; then
+      if compset -P 1 '(#b)(*):'; then
 	type="$match[1]"
 	_tags displays
 	while _tags; do
@@ -112,7 +122,7 @@ xhost)
 
     if [[ "$PREFIX" = *:* ]]; then
       type="${(L)PREFIX%%:*}"
-      compset -P '*:'
+      compset -P 1 '*:'
 
       case "$type" in
       inet) _hosts && ret=0;;
@@ -193,7 +203,7 @@ xrdb)
 esac && ret=0
 
 if [[ $state == atoms ]]; then
-  _wanted atoms expl atoms compadd ${${(f)"$(_call_program atoms xlsatoms 2> /dev/null)"}#*$'\t'} && ret=0
+  _wanted atoms expl "$state_descr" compadd ${${(f)"$(_call_program atoms xlsatoms 2> /dev/null)"}#*$'\t'} && ret=0
 fi
 
 return ret
diff --git a/Completion/X/Type/_xft_fonts b/Completion/X/Type/_xft_fonts
index 535b9b5ae..991838d67 100644
--- a/Completion/X/Type/_xft_fonts
+++ b/Completion/X/Type/_xft_fonts
@@ -1,6 +1,6 @@
 #compdef fc-list fc-match
 
-local -a suf
+local -a expl suf
 local font=${${PREFIX//-[0-9]##:/:}%:*}: ret=1
 local attr
 
diff --git a/Completion/Zsh/Command/_bindkey b/Completion/Zsh/Command/_bindkey
index 4c4aeda9d..81ae69974 100644
--- a/Completion/Zsh/Command/_bindkey
+++ b/Completion/Zsh/Command/_bindkey
@@ -27,14 +27,11 @@ _arguments -C -s -S \
   '(-l -L -d -D -A -N -m -p -r *)-s[bind each in-string to each out-string]:*:key string' \
   '(-e -v -a -M -l -L -d -D -A -N -m -p)-R[interpret in-strings as ranges]' \
   '(-l -L -d -A -N -m -p -r -s):in-string' \
-  '(-l -L -d -A -N -m -p -r -s)*::widgets:->widget' && ret=0
+  '(-l -L -d -A -N -m -p -r -s)*::widgets:_widgets' && ret=0
 
 case $state in
   keymap)
-    _wanted -C -M keymaps expl keymap compadd -a keymaps && ret=0
-  ;;
-  widget)
-    _wanted widgets expl widget compadd -M 'r:|-=* r:|=*' -k widgets && ret=0
+    _wanted keymaps expl keymap compadd -a keymaps && ret=0
   ;;
 esac
 
diff --git a/Completion/Zsh/Command/_fc b/Completion/Zsh/Command/_fc
index 68456cc3d..dd014e7d7 100644
--- a/Completion/Zsh/Command/_fc
+++ b/Completion/Zsh/Command/_fc
@@ -1,7 +1,8 @@
 #compdef fc history r
 
-local curcontext="$curcontext" state state_descr line ret=1
-local events num cmd sep
+local curcontext="$curcontext" state state_descr line expl ret=1
+local num cmd sep
+local -a events
 typeset -A opt_args
 local fc_common fc_hist fc_r
 
@@ -68,10 +69,10 @@ esac
 
 if [[ -n $state ]]; then
   zstyle -s ":completion:${curcontext}:" list-separator sep || sep=--
-  if [[ -z ${line:#*=*} ]] && compset -P '*='; then
+  if [[ -z ${line:#*=*} ]] && compset -P 1 '*='; then
     _message -e replacements 'replacement'
   elif [[ -prefix [0-9] ]]; then
-    events=( ${(0)"$(printf "%-${#HISTNO}.${#HISTNO}s $sep %s\0" "${(kv)history[@]}")"} )
+    print -v events -f "%-${#HISTNO}.${#HISTNO}s $sep %s" "${(kv)history[@]}"
     _wanted -2V events expl "$state_descr" compadd -M "B:0=" -ld events - \
         "${events[@]%% *}"
   elif [[ -prefix - ]]; then
diff --git a/Completion/Zsh/Command/_precommand b/Completion/Zsh/Command/_precommand
index f848721d9..f57e668c6 100644
--- a/Completion/Zsh/Command/_precommand
+++ b/Completion/Zsh/Command/_precommand
@@ -1,4 +1,4 @@
-#compdef - nohup eval time rusage noglob nocorrect exec catchsegv aoss hilite eatmydata
+#compdef - nohup eval time rusage noglob nocorrect exec catchsegv aoss hilite eatmydata setsid
 
 # precommands is made local in _main_complete
 precommands+=($words[1])
diff --git a/Completion/Zsh/Command/_print b/Completion/Zsh/Command/_print
index 1eba13e88..0610cd4cf 100644
--- a/Completion/Zsh/Command/_print
+++ b/Completion/Zsh/Command/_print
@@ -1,15 +1,18 @@
 #compdef print printf
 
-local state expl line eflag pflag rest ret=1
+local state expl line eflag pflag rflag rest ret=1
 
 if [[ $service = print ]]; then
   # -e flag available only after -R 
   eflag="${words[1,CURRENT-1][(r)-*R*]:+-e[enable escapes]}"
+  rflag='-r[ignore escape conventions of echo]'
 
   # -p flag only relevant if we have a coprocess
   (:>&p) 2>/dev/null &&
     pflag='(-s -u -z)-p[print arguments to input of coprocess]'
 
+  [[ -n ${words[(r)-*f]} ]] && rflag='-r[disable reuse of format string]'
+
   if [[ -n ${words[1,CURRENT][(r)-*P*]} ]]; then
     rest='*: :->prompt'
   else
@@ -17,32 +20,37 @@ if [[ $service = print ]]; then
   fi
 
   _arguments -C -s -A "-*" -S \
-    '(-f)-r[ignore escape conventions of echo]' \
-    '(-r -b -f -m -s -l -N -o -O -i -c -u -p -z -D -P)-R[emulate BSD echo (no escapes, -n & -e flags only)]' \
+    '-r[ignore escape conventions of echo]' \
+    '(-r -b -f -m -s -S -l -N -o -O -i -c -u -p -z -D -P)-R[emulate BSD echo (no escapes, -n & -e flags only)]' \
     '-b[recognise bindkey escape sequences]' \
-    '-m[remove arguments matching specified pattern]' \
-    '(-r -n -R -l -N -c)-f+[print arguments as for the printf builtin]:format:->printfformat' \
-    '(-u -p -z)-s[place results in the history list]' \
+    '-m[remove arguments not matching specified pattern]:pattern' \
+    '(-n -R -l -N -c -S)-f+[print arguments as for the printf builtin]:format:->printfformat' \
+    '(-u -p -z -S)-s[place results in the history list]' \
+    '(-a -f -c -C -i -l -o -O -N -u -p -v -z -s -x -X)-S[place results in the history list, after splitting argument into words]' \
     '(-c -f)-n[do not add a newline to the result]' \
-    '(-N -c -f)-l[print arguments separated by newlines]' \
-    '(-n -l -c -f)-N[print arguments separated and terminated by nulls]' \
-    '(-O)-o[sort arguments in ascending order]' \
-    '(-o)-O[sort arguments in descending order]' \
-    '-i[case-insensitive sorting]' \
-    '(-n -l -N -f -s -z)-a[with -c/-C, print arguments across before down]' \
-    '(-n -l -N -f -C -s -z)-c[print arguments in columns]' \
-    '(-n -l -N -f -c -s -z)-C+[print arguments in specified number of columns]:columns' \
-    '(-s -p -z)-u+[specify file descriptor to print arguments to]:file descriptor:_file_descriptors' \
-    '(-s -p -u)-z[push arguments onto editing buffer stack]' \
+    '(-N -c -f -S)-l[print arguments separated by newlines]' \
+    '(-n -l -c -f -S)-N[print arguments separated and terminated by nulls]' \
+    '(-O -S)-o[sort arguments in ascending order]' \
+    '(-o -S)-O[sort arguments in descending order]' \
+    '(-S)-i[case-insensitive sorting]' \
+    '(-n -l -N -f -s -S -z)-a[with -c/-C, print arguments across before down]' \
+    '(-n -l -N -f -C -s -S -z)-c[print arguments in columns]' \
+    '(-n -l -N -f -c -s -S -z)-C+[print arguments in specified number of columns]:columns' \
+    '(-s -S -p -z)-u+[specify file descriptor to print arguments to]:file descriptor:_file_descriptors' \
+    '(-s -S -z -p -u)-v[store output in named parameter]:parameter:_parameters' \
+    '(-s -S -p -u)-z[push arguments onto editing buffer stack]' \
     '-D[substitute any arguments which are named directories using ~ notation]' \
     '-P[perform prompt expansion]' \
-    $pflag $eflag $rest && ret=0
+    '(-X -f -a -C -c -s -S -z)-x+[expand leading tabs]:tab width' \
+    '(-x -f -a -C -c -s -S -z)-X+[expand all tabs]:tab width' \
+    $pflag $eflag $rflag $rest && ret=0
 elif [[ $service = printf ]]; then
   state=printf
 fi
 
 if [[ $state = printf ]]; then
   _arguments -C -s -S \
+    '-v[store output in named parameter]:parameter:_parameters' \
     '1:format:->printfformat' \
     '*: :_default' && ret=0
 fi
diff --git a/Completion/Zsh/Command/_strftime b/Completion/Zsh/Command/_strftime
index 0be7b078d..24ff7dc3e 100644
--- a/Completion/Zsh/Command/_strftime
+++ b/Completion/Zsh/Command/_strftime
@@ -6,7 +6,7 @@ _arguments -S -A '-*' -s \
   '-q[run quietly]' \
   '-r[reverse lookup using strptime]' \
   '-s+[assign result to parameter]:param:_parameters' \
-  '1:format: _date_formats' \
+  '1:format: _date_formats zsh' \
   '2:epoch time (or date string with -r)' && ret=0
 
 return ret
diff --git a/Completion/Zsh/Command/_typeset b/Completion/Zsh/Command/_typeset
index 4e29c23e7..160150234 100644
--- a/Completion/Zsh/Command/_typeset
+++ b/Completion/Zsh/Command/_typeset
@@ -21,6 +21,7 @@ allargs=(
   X '+X[immediately autoload function]'
   Z "($fopts -A -E -F -i)-Z+[right justify and fill with leading zeros]:width"
   a "($fopts -A -E -F -T -i)-a[specify that arguments refer to arrays]"
+  df "-d[default absolute path autoload to fpath]"
   f "($popts)-f[specify that arguments refer to functions]"
   g "($fopts -T)-+g[do not restrict parameter to local scope]"
   h "($fopts -T)-+h[hide specialness of parameter]"
@@ -31,6 +32,8 @@ allargs=(
   m '(-A -E -F -T -i)-m[treat arguments as patterns]'
   p '-p[output parameters in form of calls to typeset]'
   r '(-f)-+r[mark parameters as readonly]'
+  rf '-r[remember autoload path]'
+  Rf '-R[remember autoload path, error if not found]'
   t '(-T)-+t[tag parameters and turn on execution tracing for functions]'
   tf '(-T)-+t[turn on execution tracing for functions]'
   tp '(-T)-+t[tag parameters]'
@@ -46,7 +49,7 @@ use="AEFHLRTUZafghiklmprtuxz"
 
 case ${service} in
   autoload)
-    use="UTXktwz"
+    use="URTXdkrtwz"
     func=f
   ;;
   float) use="EFHghlprtux";;
@@ -57,7 +60,7 @@ case ${service} in
   ;;
   integer)
     use="Hghilprtux"
-    allargs[i]='-i[specify arithmetic base for output]' \
+    allargs[i]='-i+[specify arithmetic base for output]:: :_guard "[0-9]#" base' \
   ;;
   readonly) use="${use/r/}" ;;
   local) use="${use/[fkz]/}" ;&
@@ -74,7 +77,7 @@ onopts=${(j..)${${words[1,CURRENT-1]:#^-*}##-}}
 offopts=${(j..)${${words[1,CURRENT-1]:#^+*}##+}}
 
 for ((i=1;i<=$#use;++i)); do
-  args+=( ${allargs[${use[$i]}${${(s::)use[$i]}[(r)[UutT]]:+$func}]} )
+  args+=( ${allargs[${use[$i]}${${(s::)use[$i]}[(r)[dUurRtT]]:+$func}]} )
 done
 
 _arguments -C -s -A "-*" -S "${args[@]}" '*::vars:= ->vars_eq'
@@ -90,12 +93,17 @@ if [[ "$state" = vars_eq ]]; then
     elif (( $+opt_args[-w] )); then
       _wanted files expl 'zwc file' _files -g '*.zwc(-.)'
     elif [[ $service = autoload || -n $opt_args[(i)-[uU]] ]]; then
-      args=(${^fpath}/*(-.:t))
-      # Filter out functions already loaded or marked for autoload.
-      local -a funckeys
-      funckeys=(${(k)functions})
-      args=(${args:|funckeys})
-      _wanted functions expl 'shell function' compadd -a args
+      if [[ $PREFIX[1] = [/~] ]]; then
+	# Autoload by absolute path
+	_files
+      else
+	  args=(${^fpath}/*(-.:t))
+	  # Filter out functions already loaded or marked for autoload.
+	  local -a funckeys
+	  funckeys=(${(k)functions})
+	  args=(${args:|funckeys})
+	  _wanted functions expl 'shell function' compadd -a args
+      fi
     elif [[ -n $onopts$offopts ]]; then
       if [[ -n $offopts ]]; then
 	args=(${(f)"$(functions +${offopts//[^UXkmtTuz]/})"})
diff --git a/Completion/Zsh/Command/_vared b/Completion/Zsh/Command/_vared
index bb31cc129..aba64880a 100644
--- a/Completion/Zsh/Command/_vared
+++ b/Completion/Zsh/Command/_vared
@@ -6,6 +6,8 @@ _arguments -s -A "-*" \
   "-c[create parameter or change type]" \
   '-p+[specify left prompt]:prompt' \
   '-r+[specify right prompt]:right prompt' \
+  '-i+[specify initialisation widget]:widget:_widgets' \
+  '-f+[specify finish widget]:widget:_widgets' \
   '-h[allow access to history]' \
   '-e[exit on EOF (^D)]' \
   '1:parameter spec:_vars'
diff --git a/Completion/Zsh/Command/_zed b/Completion/Zsh/Command/_zed
index 184234b02..211dae7df 100644
--- a/Completion/Zsh/Command/_zed
+++ b/Completion/Zsh/Command/_zed
@@ -1,7 +1,9 @@
 #compdef zed fned
 
 case $service in
-(fned) _arguments ':shell function:_functions';;
-(zed) _arguments '(-):file:_files' \
-	'(:)-f[edit function]:shell function:_functions';;
+(fned) _arguments -S : ':shell function:_functions';;
+(zed) _arguments -S : \
+	'(- 2):file:_files' \
+	'(1):shell function:_functions' \
+	'(1)-f[edit function]';;
 esac
diff --git a/Completion/Zsh/Command/_zle b/Completion/Zsh/Command/_zle
index 3ed373348..e01d0a943 100644
--- a/Completion/Zsh/Command/_zle
+++ b/Completion/Zsh/Command/_zle
@@ -45,23 +45,25 @@ case "$state[1]" in
     _arguments \
       '(-N)-n[numeric prefix]:number:' \
       '(-n)-N[reset numeric prefix]' \
+      '-K[specify temporary keymap]:keymap:compadd -a keymaps' \
+      '-w[set $WIDGET to reflect widget being called]' \
       '(-)*:widget arguments: ' && ret=0
     ;;
   (widget*)
-    _wanted -C "$context[1]" widgets expl "${state_descr[1]:-widget}" compadd -k widgets && ret=0
+    _wanted -C "$context[1]" widgets expl "${state_descr[1]:-widget}" _widgets && ret=0
     ;&
   (function)
     [[ $state[1] != *function ]] ||	# Handle fall-through
     _wanted -C "$context[1]" functions expl 'widget shell function' \
-      compadd -k functions && ret=0
+      compadd -M 'r:|-=* r:|=*' -k functions && ret=0
     ;;
   (comp-widget)
     _wanted -C "$context[1]" widgets expl 'completion widget' \
-      compadd -k "widgets[(R)(*:|)(.|)(${(j(|))compwids})(|:*)]" && ret=0
+      _widgets -g "(*:|)(.|)(${(j(|))compwids})(|:*)" && ret=0
     ;&
   (builtin-comp-widget)
     _wanted -C "$context[1]" widgets expl 'builtin completion widget' \
-      compadd -k "widgets[(I)(.|)(${(j(|))compwids})]" && ret=0
+      compadd -M 'r:|-=* r:|=*' -k "widgets[(I)(.|)(${(j(|))compwids})]" && ret=0
     ;;
   (redisplay)
     _arguments -s -S '!-R' \
diff --git a/Completion/Zsh/Command/_zstyle b/Completion/Zsh/Command/_zstyle
index 9a6d61891..0e828225e 100644
--- a/Completion/Zsh/Command/_zstyle
+++ b/Completion/Zsh/Command/_zstyle
@@ -1,25 +1,9 @@
 #compdef zstyle
 
 local state context ostate line expl ctop suf
-local nm=$compstate[nmatches] taglist patterns pstyles contexts
+local nm=$compstate[nmatches] taglist patterns contexts
 typeset -A opt_args styles
 
-(( $+functions[_completers] )) ||
-_completers() {
-  # option: -p - needs a `_' prefix
-  local us
-  local -a disp list expl
-
-  list=( complete approximate correct match expand list menu oldlist
-         ignored prefix history )
-  zparseopts -D -K -E 'p=us'
-  [[ -n "$us" ]] && us='_'
-  zstyle -t ":completion:${curcontext}:completers" prefix-hidden &&
-      disp=(-d list)
-  _wanted completers expl 'completer' \
-      compadd "$@" "$disp[@]" - "$us${^list[@]}"
-}
-
 _vcs_info_hooks() {
   compadd - ${functions[(I)+vi-*]#+vi-}
 }
@@ -173,6 +157,7 @@ styles=(
   url-seps               e:
   whence                 e:
   word-chars             e:
+  word-class             e:
   word-style             e:word-style
   word-context           e:
 
@@ -231,21 +216,33 @@ taglist=(
   email-address ${(k)functions[(I)_email-*]#_}
 )
 
+# Be careful with the context arguments here.  They like to masquerade.
 _arguments -C \
-  '(-)-L[output in form of zstyle commands]' \
-  '(: -)-d[delete style definitions]:context pattern:->patterns:*:styles:->pstyles' \
-  '(-)-e[value is evaluated when style is looked up]' \
-  ':context:->contexts' ':style:->styles' '*:argument:->style-arg'
+  '(: -)-L[output in form of zstyle commands]:pattern for context patterns:->metapatterns:style:->metastyles' \
+  '(: -)-d[delete style definitions]:verbatim context pattern:->patterns:*:styles:->pstyles' \
+  '(: -)-e[value is evaluated when style is looked up]:context pattern:->contexts:style:->styles:*:command:_cmdstring' \
+  '(: -)-g[retrieve style definition]:array parameter:_parameters -g "*array*":verbatim context pattern:->patterns:styles:->pstyles' \
+  '(: -)-s[retrieve style value as string]:context name:->contexts:style:->styles:scalar parameter:_parameters -g "*scalar*":separator: ' \
+  '(: -)-b[retrieve style value as boolean]:context name:->contexts:style:->styles:scalar parameter:_parameters -g "*scalar*"' \
+  '(: -)-a[retrieve style value as array]:context name:->contexts:style:->styles:array parameter:_parameters -g "*array*"' \
+  '(: -)-t[test a style, returning false if it'\''s undefined]:context name:->contexts:style:->styles:*:strings to test presence of: ' \
+  '(: -)-T[test a style, returning true if it'\''s undefined]:context name:->contexts:style:->styles:*:strings to test presence of: ' \
+  '(: -)-m[pattern-match values of a style]:context name:->contexts:style:->styles:pattern: ' \
+  '(-):context pattern:->contexts' '(-):style:->styles' '(-)*:argument:->style-arg'
 
 while (( $#state )); do
   case "$state[1]" in
+    # 'contexts' completes either full context names (for -t/-s/-a), or context
+    # patterns (for 'zstyle :foo bar' and -e).
     (contexts)
       if [[ ! -prefix :*: ]]; then
-	_wanted contexts expl context compadd -P : -qS : completion vcs_info zftp
+	_wanted contexts expl "$state_descr"  compadd -P : -qS : chpwd completion vcs_info zftp zle
       elif compset -P :completion:; then
         contexts=( functions _completers cmdorcont argument tag )
       elif compset -P :vcs_info:; then
         contexts=( vcs-string user-context repo-root-name )
+      elif compset -P :zle:; then
+	_wanted widgets expl widget _widgets -qS :
       fi
       if (( $#contexts )); then
         for ostate in $contexts; do
@@ -263,19 +260,80 @@ while (( $#state )); do
       fi
       ;;
 
+    # 'patterns' completes context patterns that are set, for -d/-g.
     (patterns)
       zstyle -g patterns
-      _wanted contexts expl 'context pattern' compadd -a patterns
+      _wanted contexts expl "$state_descr" compadd -a patterns
       ;;
 
+    # 'metapatterns': patterns that are matched not against contexts, but
+    # against patterns.
+    (metapatterns)
+      zstyle -g patterns
+      patterns=( "${(@b)patterns}" )
+      _wanted contexts expl "$state_descr" compadd -a patterns
+      ;;
+
+    # 'metastyles': styles that are set on context patterns matching the given
+    # metapattern.
+    (metastyles)
+      # Anonymous function to shadow the global $styles assoc
+      () { 
+        local metapattern=${(Q)${${opt_args[-L]%:*}//(#m)\\([\\:])/${MATCH[2]}}}
+        local -a metastyles styles
+        local pattern
+        zstyle -g patterns
+        for pattern in "${(@M)patterns:#${~metapattern}}"; do
+          zstyle -g styles $pattern
+          metastyles+=( "${styles[@]}" )
+        done
+        _wanted styles expl "$state_descr" compadd -a metastyles
+        unset pattern
+        unset metastyles
+      }
+      ;;
+
+    # 'pstyles': complete styles that are set for the verbatim context pattern
+    # specified on the command line.  (If the user has set no zstyles, this
+    # will complete nothing.)
     (pstyles)
-      zstyle -g pstyles ${(Q)${(M)opt_args[-d]#*[^\\]:}%:}
+      local -a pstyles
+      local pattern
+      if (( $+opt_args[-d] )); then
+        pattern=${opt_args[-d]}
+        pattern=${pattern%":${(b)PREFIX}"} # remove style
+        pattern=${pattern//(#m)\\([\\:])/${MATCH[2]}} # undo _arguments escaping
+        pattern=${(Q)pattern} # undo command-line escaping (assumes no noglob)
+        zstyle -g pstyles $pattern
+      elif (( $+opt_args[-g] )); then
+        pattern=${opt_args[-g]}
+        pattern=${pattern%":${(b)PREFIX}"} # remove style
+        pattern=${pattern#*:} # remove array name
+        pattern=${pattern//(#m)\\([\\:])/${MATCH[2]}} # undo _arguments escaping
+        pattern=${(Q)pattern} # undo command-line escaping (assumes no noglob)
+        zstyle -g pstyles $pattern
+      fi
       _wanted styles expl style compadd -a pstyles
+      unset pattern
+      unset pstyles
     ;;
 
+    # 'styles': complete all styles that may be set in the context given on the
+    # command line.  This is independent of what styles are set.
     (styles)
       # Get the top-level context we're completing for, if any.
-      case ${(Q)line[1]} in
+      if [[ -n $line[1] ]]; then
+        # zstyle :something <TAB>
+        local the_context=$line[1]
+      else
+        # zstyle -x :something <TAB>
+        local joined_value=${(v)opt_args[(i)(-e|-s|-b|-a|-t|-T|-m)]}
+        local the_context=${(Q)joined_value[0, ${joined_value[(i)[^\\]:]}-2 ]}
+      fi
+      # Note: for 'zstyle :something <TAB>' and for 'zstyle -e :something <TAB>',
+      # $the_context is a context pattern; for -s,-b,-a,-t,-T,-m, it is a context
+      # name.  We currently draw no distinction between these two cases.
+      case $the_context in
 	(:completion:*)
 	ctop=c
 	;;
@@ -297,7 +355,7 @@ while (( $#state )); do
 	;;
 
 	(*)
-        ctop=cdez
+        ctop=a-z
 	;;
       esac
       _wanted styles expl style \
@@ -521,7 +579,7 @@ while (( $#state )); do
       ;;
 
     (word-style)
-      _wanted word-styles expl 'word style' compadd normal shell space
+      _wanted word-styles expl 'word style' compadd {normal,specified,unspecified,shell,whitespace}-subword
       ;;
 
     (vcs-string)
diff --git a/Completion/Zsh/Context/_brace_parameter b/Completion/Zsh/Context/_brace_parameter
index e77d4c58b..f22db8895 100644
--- a/Completion/Zsh/Context/_brace_parameter
+++ b/Completion/Zsh/Context/_brace_parameter
@@ -141,7 +141,7 @@ if [[ $PREFIX = *'${('[^\)]# ]]; then
   flags+=(
     "#:evaluate as numeric expression"
     "@:double-quoted splitting of scalars"
-    "A:create array parameter"
+    "A:assign as an array parameter"
     "a:sort in array index order (with O to reverse)"
     "b:backslash quote pattern characters only"
     "c:count characters in an array (with \${(c)#...})"
@@ -179,7 +179,7 @@ if [[ $PREFIX = *'${('[^\)]# ]]; then
     "S:search substrings in #, %, / expressions"
     "I:search <argument>th match in #, %, / expressions"
     "B:include index of beginning of match in #, %, / expressions"
-    "E:include index of end of match in #, %, / expressions"
+    "E:include index of one past end of match in #, %, / expressions"
     "M:include matched portion in #, %, / expressions"
     "N:include length of match in #, % expressions"
     "R:include rest (unmatched portion) in #, %, / expressions"
@@ -197,6 +197,7 @@ elif compset -P '*:'; then
       '\:=:unconditionally assign value to parameter'
       '?:print error if parameter is set and non-null'
       '#:filter value matching pattern'
+      '/:replace whole word matching pattern'
       '|:set difference'
       '*:set intersection'
       '^:zip arrays'
diff --git a/Completion/Zsh/Context/_condition b/Completion/Zsh/Context/_condition
index 6f5e601f0..0285911ac 100644
--- a/Completion/Zsh/Context/_condition
+++ b/Completion/Zsh/Context/_condition
@@ -8,6 +8,8 @@ elif [[ "$prev" = -([a-hkprsuwxLOGSN]|[no]t|ef) ]]; then
   _tags -C "$prev" files && _files
 elif [[ "$prev" = -t ]]; then
   _file_descriptors
+elif [[ "$prev" = -v ]]; then
+  _parameters -r "\= \t\n\[\-"
 else
   if [[ "$PREFIX" = -* ]] ||
      ! zstyle -T ":completion:${curcontext}:options" prefix-needed; then
@@ -30,6 +32,7 @@ else
 	           -s:non-empty\ file
 	           -t:terminal\ file\ descriptor
 	           -u:setuid\ bit
+		   -v:set\ variable
 	           -w:writable\ file
 	           -x:executable\ file
 	           -z:empty\ string
diff --git a/Completion/Zsh/Context/_subscript b/Completion/Zsh/Context/_subscript
index 80642d8db..0c9a89ad5 100644
--- a/Completion/Zsh/Context/_subscript
+++ b/Completion/Zsh/Context/_subscript
@@ -62,6 +62,7 @@ elif compset -P '\('; then
       'e[interpret * or @ as a single key]'
     );;
     (|scalar*)) flags=(
+      'f[make subscripting work on lines of scalar]'
       'w[make subscripting work on words of scalar]'
       's[specify word separator]'
       'p[recognise escape sequences in subsequent s flag]'
@@ -99,10 +100,9 @@ elif [[ ${(Pt)${compstate[parameter]}} = array* ]]; then
         list=()
         for i in "$ind[@]"; do
           if [[ "$i" = ${PREFIX}*${SUFFIX} ]]; then
-              list=( "$list[@]" 
-	             "${i}:$(print -D ${(P)${compstate[parameter]}[$i]})" )
+              list+=( "${i}:$(print -D -- ${(P)${compstate[parameter]}[$i]})" )
 	  else
-	      list=( "$list[@]" '' )
+	      list+=( '' )
 	  fi
         done
         zstyle -s ":completion:${curcontext}:indexes" list-separator sep || sep=--
diff --git a/Completion/Zsh/Context/_value b/Completion/Zsh/Context/_value
index 47fbc1749..22372ab36 100644
--- a/Completion/Zsh/Context/_value
+++ b/Completion/Zsh/Context/_value
@@ -24,6 +24,7 @@ else
   if [[ "$compstate[parameter]" != *-* &&
         "$compstate[context]" = *value &&
         "${(Pt)${compstate[parameter]}}" = assoc* ]]; then
+    local expl
     if (( CURRENT & 1 )); then
       _wanted association-keys expl 'association key' \
           compadd -k "$compstate[parameter]"
diff --git a/Completion/Zsh/Function/_add-zle-hook-widget b/Completion/Zsh/Function/_add-zle-hook-widget
new file mode 100644
index 000000000..f108d1868
--- /dev/null
+++ b/Completion/Zsh/Function/_add-zle-hook-widget
@@ -0,0 +1,36 @@
+#compdef add-zle-hook-widget
+
+_add-zle-hook-widget_types() {
+  local -a tmp
+
+  autoload -U add-zle-hook-widget
+  add-zle-hook-widget -h >&/dev/null # sets the zstyle
+  zstyle -g tmp zle-hook types
+
+  compadd "$@" -M 'L:|=zle-' -M 'r:|-=* r:|=*' -- zle-${^tmp}
+}
+
+_add-zle-hook-widget_widgets() {
+  local expl
+  if (( $+opt_args[-d] )); then
+    local -a tmp
+    zstyle -g tmp $line[1] widgets
+    _wanted widgets expl "installed hook" compadd -- ${tmp#<->:} && return 0
+  else
+    _wanted widgets expl widget _widgets -g 'user:*' && return 0
+  fi
+  return 1
+}
+
+_add-zle-hook-widget() {
+  local context state state_descr line
+  typeset -A opt_args
+  _arguments -s -w -S : \
+    '(-D)-d[remove HOOK from the array]' \
+    '(-d)-D[interpret HOOK as pattern to remove from the array]' \
+    {-U,-z,-k}"[passed to 'autoload']" \
+    ':hook type:_add-zle-hook-widget_types' \
+    ':widget:_add-zle-hook-widget_widgets'
+}
+
+_add-zle-hook-widget "$@"
diff --git a/Completion/Zsh/Function/_add-zsh-hook b/Completion/Zsh/Function/_add-zsh-hook
index c70a4976f..e8ae97052 100644
--- a/Completion/Zsh/Function/_add-zsh-hook
+++ b/Completion/Zsh/Function/_add-zsh-hook
@@ -1,9 +1,7 @@
 #compdef add-zsh-hook
 
-local context state state_descr line
-typeset -A opt_args
-
 _add-zsh-hook_hooks() {
+  local expl
   if (( $+opt_args[-d] )); then
     _wanted functions expl "installed hooks" compadd -a - "$line[1]_functions" && return 0
   else
@@ -13,6 +11,8 @@ _add-zsh-hook_hooks() {
 }
 
 _add-zsh-hook() {
+  local context state state_descr line
+  typeset -A opt_args
   _arguments -s -w -S : \
     '(-D)-d[remove HOOK from the array]' \
     '(-d)-D[interpret HOOK as pattern to remove from the array]' \
diff --git a/Completion/Zsh/Function/_zargs b/Completion/Zsh/Function/_zargs
index c24b276f2..f974ab646 100644
--- a/Completion/Zsh/Function/_zargs
+++ b/Completion/Zsh/Function/_zargs
@@ -4,7 +4,7 @@ local arguments eofstr pos=$((CURRENT)) numeofs=0 ret=1 cmdpos=1
 
 #this doesn't handle '--' on the command line, only --
 #it also by extension doesn't handle eofstr being the empty string
-#it also also doesn't handle eofstr being -e or --eof, and everything will
+#it also doesn't handle eofstr being -e or --eof, and everything will
 # probably also be confused if the command at the end takes a -e, --eof= or --
 eofstr=${${${${words[(r)(--eof=*|-e*)]}#--eof=}#-e}:---}
 while {
diff --git a/Completion/Zsh/Type/_arrays b/Completion/Zsh/Type/_arrays
index 24c8957e8..c28fb179e 100644
--- a/Completion/Zsh/Type/_arrays
+++ b/Completion/Zsh/Type/_arrays
@@ -2,4 +2,4 @@
 
 local expl
 
-_wanted arrays expl array _parameters "$@" - -g '*array*'
+_wanted arrays expl array _parameters "$@" -g '*array*'
diff --git a/Completion/Zsh/Type/_completers b/Completion/Zsh/Type/_completers
new file mode 100644
index 000000000..87c986a17
--- /dev/null
+++ b/Completion/Zsh/Type/_completers
@@ -0,0 +1,14 @@
+#autoload
+
+# option: -p - needs a `_' prefix
+local us
+local -a disp list expl
+
+list=( complete approximate correct match expand list menu oldlist
+       ignored prefix history )
+zparseopts -D -K -E 'p=us'
+[[ -n "$us" ]] && us='_'
+zstyle -t ":completion:${curcontext}:completers" prefix-hidden &&
+    disp=(-d list)
+_wanted completers expl 'completer' \
+    compadd "$@" "$disp[@]" - "$us${^list[@]}"
diff --git a/Completion/Zsh/Type/_globflags b/Completion/Zsh/Type/_globflags
index 5833dc8f1..13ef14c17 100644
--- a/Completion/Zsh/Type/_globflags
+++ b/Completion/Zsh/Type/_globflags
@@ -1,5 +1,8 @@
 #autoload
 
+# Complete 'globbing flags', i.e., '(#x)'; everything up to the '#' will
+# have been "compset -P"'d by the caller.
+
 local ret=1
 local -a flags
 
diff --git a/Completion/Zsh/Type/_globquals b/Completion/Zsh/Type/_globquals
index 2523eb333..a904bdf0d 100644
--- a/Completion/Zsh/Type/_globquals
+++ b/Completion/Zsh/Type/_globquals
@@ -1,7 +1,7 @@
 #autoload
 
-local state=qual expl char delim timespec
-local -a alts tdisp sdisp
+local state=qual expl char delim timespec default
+local -a alts tdisp sdisp tmatch smatch
 local -A specmap
 
 while [[ -n $PREFIX ]]; do
@@ -118,14 +118,29 @@ while [[ -n $PREFIX ]]; do
       timespec=$PREFIX[1]
       if ! compset -P '[Mwhmsd]' && [[ -z $PREFIX ]]; then
 	tdisp=( seconds minutes hours days weeks Months )
-        alts+=( "time-specifiers:time specifier:compadd -E 0 -d tdisp -S '' - s m h d w M" )
+	tmatch=( s m h d w M )
+	if zstyle -t ":completion:${curcontext}:time-specifiers" verbose; then
+	  zstyle -s ":completion:${curcontext}:time-specifiers" list-separator sep || sep=--
+          print -v tdisp -f "%s ${sep//(#m)[%\\]/$MATCH$MATCH} %s" ${tmatch:^^tdisp}
+	fi
+	alts+=( "time-specifiers:time specifier:compadd -E 0 -d tdisp -S '' -a tmatch" )
       fi
       if ! compset -P '[-+]' && [[ -z $PREFIX ]]; then
 	sdisp=( before exactly since )
-        alts+=("senses:sense:compadd -E 0 -d sdisp -S '' - + '' -")
+	smatch=( - '' + )
+	if zstyle -t ":completion:${curcontext}:senses" verbose; then
+	  zstyle -s ":completion:${curcontext}:senses" list-separator sep || sep=--
+	  default=" [default exactly]"
+	  sdisp=( "- $sep before" "+ $sep since" )
+	  smatch=( - + )
+	else
+	  sdisp=( before exactly since )
+	  smatch=( - '' + )
+	fi
+        alts+=( "senses:sense${default}:compadd -E 0 -d sdisp -S '' -a smatch" )
       fi
       specmap=( M months w weeks h hours m minutes s seconds '(|+|-|d)' days)
-      alts+=('digits:digit ('${${specmap[(K)$timespec]}:-invalid time specifier}'):_dates -f ${${timespec/[-+]/d}:-d} -S ""' )
+      alts+=('digits:digit ('${${specmap[(K)${timespec:-d}]}:-invalid time specifier}'):_dates -f ${${timespec/[-+]/d}:-d} -S ""' )
       _alternative $alts
       return
     fi
diff --git a/Completion/Zsh/Type/_history_modifiers b/Completion/Zsh/Type/_history_modifiers
index 658f9f346..1a049d6cb 100644
--- a/Completion/Zsh/Type/_history_modifiers
+++ b/Completion/Zsh/Type/_history_modifiers
@@ -64,8 +64,8 @@ while true; do
       )
     if (( ! global )); then
       list+=(
-	"a:absolute path"
-	"A:absolute path resolving symbolic links"
+	"a:absolute path, resolve '..' lexically"
+	"A:as ':a', then resolve symlinks"
 	"c:PATH search for command"
 	"g:globally apply s or &"
 	"h:head - strip trailing path element"
@@ -73,6 +73,7 @@ while true; do
 	"r:root - strip suffix"
 	"e:leave only extension"
 	"Q:strip quotes"
+	"P:realpath, resolve '..' physically"
 	"l:lower case all words"
 	"u:upper case all words"
 	)
diff --git a/Completion/Zsh/Type/_module_math_func b/Completion/Zsh/Type/_module_math_func
index 4df8d9714..6be9c006a 100644
--- a/Completion/Zsh/Type/_module_math_func
+++ b/Completion/Zsh/Type/_module_math_func
@@ -6,4 +6,4 @@ local -a funcs
 funcs=(${${${(f)"$(zmodload -Fl zsh/mathfunc 2>/dev/null)"}:#^+f:*}##+f:})
 
 _wanted module-math-functions expl 'math function from zsh/mathfunc' \
-    compadd -S '(' "$@" -a funcs
+    compadd -S '(' -q "$@" -a funcs
diff --git a/Completion/Zsh/Type/_ps1234 b/Completion/Zsh/Type/_ps1234
index 8edf0d0cb..cf1982219 100644
--- a/Completion/Zsh/Type/_ps1234
+++ b/Completion/Zsh/Type/_ps1234
@@ -1,6 +1,6 @@
 #compdef -value-,PROMPT,-default- -value-,PROMPT2,-default- -value-,PROMPT3,-default- -value-,PROMPT4,-default- -value-,RPROMPT,-default- -value-,RPROMPT2,-default- -value-,PS1,-default- -value-,PS2,-default- -value-,PS3,-default- -value-,PS4,-default- -value-,RPS1,-default- -value-,RPS2,-default- -value-,SPROMPT,-default-
 
-local -a specs
+local -a specs ccol
 local expl grp cols bs suf pre changed=1 ret=1
 local -A ansi
 
@@ -39,7 +39,8 @@ if compset -P '%[FK]'; then
 
   _description -V ansi-colors expl 'ansi color'
   grp="$expl[expl[(i)-V]+1]"
-  _comp_colors+=( ${(ps.\0.)"$(printf "($grp)=%s=%s\0" ${(kv)ansi})"} )
+  print -v ccol -f "($grp)=%s=%s" ${(kv)ansi}
+  _comp_colors+=( $ccol )
   compadd "$expl[@]" $suf $pre -k ansi && ret=0
   if (( $#suf )) && compset -P "(<->|%v)"; then
     _wanted ansi-colors expl 'closing brace' compadd -S '' \} && ret=0
@@ -58,11 +59,14 @@ if compset -P '%[FK]'; then
 fi
 
 if compset -P '%[0-9-\\]#(\\|)\([0-9-]#[^0-9]'; then
+  # ternary conditional: first delimiter
   compset -S '*'
   _delimiters && ret=0
 elif compset -P '%[0-9-\\]#[<>\]]'; then
+  # truncation
   _message -e replacements 'replacement string'
 elif compset -P '%[0-9-\\]#(\\|)\([0-9-]#'; then
+  # ternary conditional: condition character
   compset -S '[.:+/-%]*' || suf=( -S . )
   compset -S '*'
   specs=(
diff --git a/Completion/Zsh/Type/_user_math_func b/Completion/Zsh/Type/_user_math_func
index 16774f70b..35a49d50e 100644
--- a/Completion/Zsh/Type/_user_math_func
+++ b/Completion/Zsh/Type/_user_math_func
@@ -6,4 +6,4 @@ local -a funcs
 funcs=(${${${(f)"$(functions -M)"}##functions -M }%% *})
 
 _wanted user-math-functions expl 'user math function' \
-    compadd -S '(' "$@" -a funcs
+    compadd -S '(' -q "$@" -a funcs
diff --git a/Completion/Zsh/Type/_vars b/Completion/Zsh/Type/_vars
index 0f97d6cda..ec59c03be 100644
--- a/Completion/Zsh/Type/_vars
+++ b/Completion/Zsh/Type/_vars
@@ -1,7 +1,7 @@
 #compdef getopts unset
 
 # This will handle completion of keys of associative arrays, e.g. at
-# `vared foo[<TAB>'.
+# `vared foo[<TAB>' could complete to `vared foo[key]'.
 
 local ret=1
 
diff --git a/Completion/Zsh/Type/_widgets b/Completion/Zsh/Type/_widgets
new file mode 100644
index 000000000..fdab2814e
--- /dev/null
+++ b/Completion/Zsh/Type/_widgets
@@ -0,0 +1,9 @@
+#autoload
+
+local expl pattern
+
+pattern=( -g \* )
+zparseopts -D -K -E g:=pattern
+
+_description widgets expl widget
+compadd "$@" "$expl[@]" -M 'r:|-=* r:|=*' - "${(@k)widgets[(R)${pattern[2]}]}"
diff --git a/Completion/compaudit b/Completion/compaudit
index fb0463ca5..93a043124 100644
--- a/Completion/compaudit
+++ b/Completion/compaudit
@@ -15,8 +15,10 @@ compaudit() {                           # Define and then call
 emulate -L zsh
 setopt extendedglob
 
-[[ -x /usr/bin/getent ]] || getent() {
-  if [[ $2 = <-> ]]; then
+[[ -n $commands[getent] ]] || getent() {
+  if [[ $1 = hosts ]]; then
+    sed 's/#.*//' /etc/$1 | grep -w $2
+  elif [[ $2 = <-> ]]; then
     grep ":$2:[^:]*$" /etc/$1
   else
     grep "^$2:" /etc/$1
diff --git a/Completion/compdump b/Completion/compdump
index 068383c22..e0dc8b805 100644
--- a/Completion/compdump
+++ b/Completion/compdump
@@ -101,9 +101,10 @@ bindkey |
 print >> $_d_file
 
 
-# Autoloads: look for all functions beginning with `_'.
+# Autoloads: look for all defined functions beginning with `_' (that also
+# exists in fpath: see workers/38547).
 
-_d_als=(${(o)$(typeset +fm '_*')})
+_d_als=($^fpath/(${(o~j.|.)$(typeset +fm '_*')})(N:t))
 
 # print them out:  about five to a line looks neat
 
diff --git a/Completion/compinit b/Completion/compinit
index 6612baca0..c345ceb43 100644
--- a/Completion/compinit
+++ b/Completion/compinit
@@ -142,6 +142,7 @@ _comp_options=(
     NO_cshnullglob
     NO_cshjunkiequotes
     NO_errexit
+    NO_errreturn
     NO_globassign
     NO_globsubst
     NO_histsubstpattern
@@ -153,8 +154,10 @@ _comp_options=(
     NO_markdirs
     NO_octalzeroes
     NO_posixbuiltins
+    NO_posixidentifiers
     NO_shwordsplit
     NO_shglob
+    NO_warnnestedvar
     NO_warncreateglobal
 )
 
@@ -168,7 +171,7 @@ typeset -gH _comp_setup='local -A _comp_caller_options;
              _comp_caller_options=(${(kv)options[@]});
              setopt localoptions localtraps localpatterns ${_comp_options[@]};
              local IFS=$'\'\ \\t\\r\\n\\0\'';
-             builtin enable -p \| \~ \( \? \* \[ \< \^ \# 2>/dev/null;
+             builtin enable -p \| \~ \( \? \* \[ \< \^ \# 2>&-;
              exec </dev/null;
              trap - ZERR;
              local -a reply;
diff --git a/Config/version.mk b/Config/version.mk
index b182ffa79..a15ccb964 100644
--- a/Config/version.mk
+++ b/Config/version.mk
@@ -27,5 +27,5 @@
 # This must also serve as a shell script, so do not add spaces around the
 # `=' signs.
 
-VERSION=5.2-dev-1-badarrays
-VERSION_DATE='January 20, 2015'
+VERSION=5.3.1-dev-0-badarrays
+VERSION_DATE='May 28, 2017'
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 1ca1f24a8..81687c7a7 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -147,20 +147,42 @@ ifnzman(noderef(Aliasing)).
 findex(autoload)
 cindex(functions, autoloading)
 cindex(autoloading functions)
-item(tt(autoload) [ {tt(PLUS())|tt(-)}tt(TUXkmtz) ] [ tt(-w) ] [ var(name) ... ])(
+item(tt(autoload) [ {tt(PLUS())|tt(-)}tt(RTUXdkmrtWz) ] [ tt(-w) ] [ var(name) ... ])(
 vindex(fpath, searching)
-Equivalent to tt(functions -u), with the exception of tt(-X)/tt(+X) and
-tt(-w).  See the section `Autoloading Functions' in ifzman(zmanref(zshmisc))\
+See the section `Autoloading Functions' in ifzman(zmanref(zshmisc))\
 ifnzman(noderef(Functions)) for full details.  The tt(fpath) parameter
 will be searched to find the function definition when the function is
 first referenced.
 
-The flag tt(-X) may be used only inside a shell function, and may not be
-followed by a var(name).  It causes the calling function to be marked for
-autoloading and then immediately loaded and executed, with the current
-array of positional parameters as arguments.  This replaces the previous
-definition of the function.  If no function definition is found, an error
-is printed and the function remains undefined and marked for autoloading.
+If var(name) consists of an absolute path, the function is defined to
+load from the file given (searching as usual for dump files in the given
+location).  The name of the function is the basename (non-directory
+part) of the file.  It is normally an error if the function is not found
+in the given location; however, if the option tt(-d) is given, searching
+for the function defaults to tt($fpath).  If a function is loaded by
+absolute path, any functions loaded from it that are marked for
+tt(autoload) without an absolute path have the load path of the parent
+function temporarily prepended to tt($fpath).
+
+If the option tt(-r) or tt(-R) is given, the function is searched for
+immediately and the location is recorded internally for use when the
+function is executed; a relative path is expanded using the value of
+tt($PWD).  This protects against a change to tt($fpath) after the call
+to tt(autoload).  With tt(-r), if the function is not found, it is
+silently left unresolved until execution; with tt(-R), an error message
+is printed and command processing aborted immediately the search fails,
+i.e. at the tt(autoload) command rather than at function execution..
+
+The flag tt(-X) may be used only inside a shell function.  It causes the
+calling function to be marked for autoloading and then immediately
+loaded and executed, with the current array of positional parameters as
+arguments.  This replaces the previous definition of the function.  If
+no function definition is found, an error is printed and the function
+remains undefined and marked for autoloading.  If an argument is given,
+it is used as a directory (i.e. it does not include the name of the
+function) in which the function is to be found; this may be combined
+with the tt(-d) option to allow the function search to default to tt($fpath)
+if it is not in the given location.
 
 The flag tt(+X) attempts to load each var(name) as an autoloaded function,
 but does em(not) execute it.  The exit status is zero (success) if the
@@ -176,6 +198,13 @@ If the tt(-m) flag is also given each var(name) is treated as a
 pattern and all functions already marked for autoload that match the
 pattern are loaded.
 
+With the tt(-t) flag, turn on execution tracing; with tt(-T), turn on
+execution tracing only for the current function, turning it off on entry
+to any called functions that do not also have tracing enabled.
+
+With the tt(-U) flag, alias expansion is suppressed when the function is
+loaded.
+
 With the tt(-w) flag, the var(name)s are taken as names of files compiled
 with the tt(zcompile) builtin, and all functions defined in them are
 marked for autoloading.
@@ -193,6 +222,10 @@ example(emulate zsh -c 'autoload -Uz var(func)')
 
 arranges that when var(func) is loaded the shell is in native tt(zsh)
 emulation, and this emulation is also applied when var(func) is run.
+
+Some of the functions of tt(autoload) are also provided by tt(functions
+-u) or tt(functions -U), but tt(autoload) is a more comprehensive
+interface.
 )
 findex(bg)
 cindex(jobs, backgrounding)
@@ -453,7 +486,7 @@ tt(echo) recognizes the following escape sequences:
 startsitem()
 sitem(tt(\a))(bell character)
 sitem(tt(\b))(backspace)
-sitem(tt(\c))(suppress final newline)
+sitem(tt(\c))(suppress subsequent characters and final newline)
 sitem(tt(\e))(escape)
 sitem(tt(\f))(form feed)
 sitem(tt(\n))(linefeed (newline))
@@ -570,7 +603,7 @@ with emulations to be set to their values in tt(sh).  tt(fno) then
 calls tt(fni); because tt(fni) is also marked for sticky tt(sh)
 emulation, no option changes take place on entry to or exit from it.
 Hence the option tt(cshnullglob), turned off by tt(sh) emulation, will
-be turned on within tt(fni) and remain on on return to tt(fno).  On exit
+be turned on within tt(fni) and remain on return to tt(fno).  On exit
 from tt(fno), the emulation mode and all options will be restored to the
 state they were in before entry to the temporary emulation.
 
@@ -656,7 +689,7 @@ An EOF condition will also cause the shell to exit, unless
 the tt(IGNORE_EOF) option is set.
 
 See notes at the end of
-ifzman(the section JOBS in in zmanref(zshmisc))\
+ifzman(the section JOBS in zmanref(zshmisc))\
 ifnzman(noderef(Jobs & Signals)) for some possibly unexpected interactions
 of the tt(exit) command with jobs.
 )
@@ -697,7 +730,7 @@ specifies the most recent event beginning with the given string.  All
 substitutions var(old)tt(=)var(new), if any, are then performed on the
 text of the events.
 
-In addition to the the number range,
+In addition to the number range,
 startsitem()
 sitem(tt(-I))(restricts to only internal events (not from tt($HISTFILE)))
 sitem(tt(-L))(restricts to only local events (not from other shells, see
@@ -806,18 +839,24 @@ Equivalent to tt(typeset -E), except that options irrelevant to floating
 point numbers are not permitted.
 )
 findex(functions)
-xitem(tt(functions) [ {tt(PLUS())|tt(-)}tt(UkmtTuz) ] [ tt(-x) var(num) ] [ var(name) ... ])
-xitem(tt(functions -M) var(mathfn) [ var(min) [ var(max) [ var(shellfn) ] ] ])
+xitem(tt(functions) [ {tt(PLUS())|tt(-)}tt(UkmtTuWz) ] [ tt(-x) var(num) ] [ var(name) ... ])
+xitem(tt(functions -M) [tt(-s)] var(mathfn) [ var(min) [ var(max) [ var(shellfn) ] ] ])
 xitem(tt(functions -M) [ tt(-m) var(pattern) ... ])
 item(tt(functions +M) [ tt(-m) ] var(mathfn) ... )(
-Equivalent to tt(typeset -f), with the exception of the tt(-x) and
-tt(-M) options.
+Equivalent to tt(typeset -f), with the exception of the tt(-x),
+tt(-M) and tt(-W) options.  For tt(functions -u) and tt(functions -U),
+see tt(autoload), which provides additional options.
 
 The tt(-x) option indicates that any functions output will have
 each leading tab for indentation, added by the shell to show syntactic
 structure, expanded to the given number var(num) of spaces.  var(num)
 can also be 0 to suppress all indentation.
 
+The tt(-W) option turns on the option tt(WARN_NESTED_VAR) for the named
+function or functions only.  The option is turned off at the start of
+nested functions (apart from anonoymous functions) unless the called
+function also has the tt(-W) attribute.
+
 Use of the tt(-M) option may not be combined with any of the options
 handled by tt(typeset -f).
 
@@ -843,6 +882,13 @@ The result of the last arithmetical expression evaluated
 inside the shell function (even if it is a form that normally only returns
 a status) gives the result of the mathematical function.
 
+If the additional option tt(-s) is given to tt(functions -M), the
+argument to the function is a single string: anything between the
+opening and matching closing parenthesis is passed to the function as a
+single argument, even if it includes commas or white space.  The minimum
+and maximum argument specifiers must therefore be 1 if given.  An empty
+argument list is passed as a zero-length string.
+
 tt(functions -M) with no arguments lists all such user-defined functions in
 the same form as a definition.  With the additional option tt(-m) and
 a list of arguments, all functions whose var(mathfn) matches one of
@@ -850,15 +896,22 @@ the pattern arguments are listed.
 
 tt(function +M) removes the list of mathematical functions; with the
 additional option tt(-m) the arguments are treated as patterns and
-all functions whose tt(mathfn) matches the pattern are removed.  Note
+all functions whose var(mathfn) matches the pattern are removed.  Note
 that the shell function implementing the behaviour is not removed
-(regardless of whether its name coincides with tt(mathfn)).
+(regardless of whether its name coincides with var(mathfn)).
 
 For example, the following prints the cube of 3:
 
 example(zmath_cube+LPAR()RPAR() { (( $1 * $1 * $1 )) }
 functions -M cube 1 1 zmath_cube
 print $(( cube+LPAR()3+RPAR() )))
+
+The following string function takes a single argument, including
+the commas, so prints 11:
+
+example(stringfn+LPAR()RPAR() { (( $#1 )) }
+functions -Ms stringfn
+print $(( stringfn+LPAR()foo,bar,rod+RPAR() )))
 )
 module(getcap)(zsh/cap)
 findex(getln)
@@ -1199,6 +1252,9 @@ Perform prompt expansion (see
 ifzman(EXPANSION OF PROMPT SEQUENCES in zmanref(zshmisc))\
 ifnzman(noderef(Prompt Expansion))\
 ).
+In combination with `tt(-f)',
+prompt escape sequences are parsed only within interpolated arguments,
+not within the format string.
 )
 item(tt(-r))(
 Ignore the escape conventions of tt(echo).
@@ -1289,7 +1345,9 @@ required by the format than have been specified, the behaviour is as if
 zero or an empty string had been specified as the argument.
 
 The tt(-v) option causes the output to be stored as the value of the
-parameter var(name), instead of printed.
+parameter var(name), instead of printed. If var(name) is an array and
+the format string is reused when consuming arguments then one
+array element will be used for each use of the format string.
 )
 findex(pushd)
 pindex(PUSHD_TO_HOME, use of)
@@ -1918,11 +1976,6 @@ If the tt(-p) option is given, parameters and values are printed in the
 form of a typeset command with an assignment, regardless of other flags
 and options.  Note that the tt(-H) flag on parameters is respected; no
 value will be shown for these parameters.
-
-As the intention of this option is to produce output that can restore
-the current state, readonly specials (whose values cannot be
-changed) are not shown and assignments to arrays are shown before
-the tt(typeset) rendering the array readonly.
 )
 item(tt(-T) [ var(scalar)[tt(=)var(value)] var(array)[tt(=LPAR())var(value) ...tt(RPAR())] [ var(sep) ] ])(
 This flag has a different meaning when used with tt(-f); see below.
@@ -2019,23 +2072,27 @@ place in the output.
 )
 item(tt(-a))(
 The names refer to array parameters.  An array parameter may be
-created this way, but it may not be assigned to in the tt(typeset)
-statement.  When displaying, both normal and associative arrays are
-shown.
+created this way, but it may be assigned to in the tt(typeset)
+statement only if the reserved word form of tt(typeset) is enabled
+(as it is by default).  When displaying, both normal and associative
+arrays are shown.
 )
 item(tt(-f))(
 The names refer to functions rather than parameters.  No assignments
 can be made, and the only other valid flags are tt(-t), tt(-T), tt(-k),
 tt(-u), tt(-U) and tt(-z).  The flag tt(-t) turns on execution tracing
 for this function; the flag tt(-T) does the same, but turns off tracing
-on any function called from the present one, unless that function also
+for any named (not anonymous) function called from the present one,
+unless that function also
 has the tt(-t) or tt(-T) flag.  The tt(-u) and tt(-U) flags cause the
 function to be marked for autoloading; tt(-U) also causes alias
 expansion to be suppressed when the function is loaded.  See the
 description of the `tt(autoload)' builtin for details.
 
 Note that the builtin tt(functions) provides the same basic capabilities
-as tt(typeset -f) but gives access to a few extra options.
+as tt(typeset -f) but gives access to a few extra options; tt(autoload)
+gives further additional options for the case tt(typeset -fu) and
+tt(typeset -fU).
 )
 item(tt(-h))(
 Hide: only useful for special parameters (those marked `<S>' in the table in
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index d478e818e..47b07e85e 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -91,6 +91,7 @@ menu(Control Functions)
 menu(Bindable Commands)
 menu(Completion Functions)
 menu(Completion Directories)
+menu(Completion System Variables)
 endmenu()
 
 texinode(Initialization)(Completion System Configuration)()(Completion System)
@@ -825,6 +826,10 @@ kindex(domains, completion tag)
 item(tt(domains))(
 for network domains
 )
+kindex(email-*, completion tag)
+item(tt(email-)var(plugin))(
+for email addresses from the `tt(_email-)var(plugin)' backend of tt(_email_addresses)
+)
 kindex(expansions, completion tag)
 item(tt(expansions))(
 used by the tt(_expand) completer for individual words (as opposed to
@@ -1110,14 +1115,14 @@ tt(default) tag.  The most notable styles of this type are tt(menu),
 tt(list-colors) and styles controlling completion listing such as 
 tt(list-packed) and tt(last-prompt).  When tested for the tt(default)
 tag, only the var(function) field of the context will be set so that
-a style using the default tag will normally be defined along the lines of:
+a style using the tt(default) tag will normally be defined along the lines of:
 
 example(zstyle ':completion:*:default' menu ...)
 
 startitem()
 kindex(accept-exact, completion style)
 item(tt(accept-exact))(
-This is tested for the default tag in addition to the tags valid for
+This is tested for the tt(default) tag in addition to the tags valid for
 the current context.  If it is set to `true' and any of the trial
 matches is the same as the string on the command line, this match will
 immediately be accepted (even if it would otherwise be considered
@@ -1153,6 +1158,12 @@ directory is accepted without any attempt to complete it further.
 Hence, in the given example, the path tt(/usr/bin/) is accepted
 immediately and completion tried in that directory.
 
+This style is also useful when completing after directories that
+magically appear when referenced, such as ZFS tt(.zfs) directories
+or NetApp tt(.snapshot) directories.  When the style is set the
+shell does not check for the existence of the directory within the
+parent directory.
+
 If you wish to inhibit this behaviour entirely, set the tt(path-completion)
 style (see below) to `false'.
 )
@@ -1423,9 +1434,10 @@ or colons in var(dir) should be quoted with a backslash to be treated
 literally.
 
 This can be useful on systems that support special file systems whose
-top-level pathnames can not be listed or generated with glob patterns.
-It can also be used for directories for which one does not have read
-permission.
+top-level pathnames can not be listed or generated with glob patterns
+(but see tt(accept-exact-dirs) for a more general way of dealing
+with this problem).  It can also be used for directories for which one
+does not have read permission.
 
 The pattern form can be used to add a certain `magic' entry
 to all directories on a particular file system.
@@ -1842,6 +1854,15 @@ In the case of the tt(_match) completer, the style may also be set to
 the string `tt(pattern)'.  Then the pattern on the line is left
 unchanged if it does not match unambiguously.
 )
+kindex(gain-privileges, completion style)
+item(tt(gain-privileges))(
+If set to tt(true), this style enables the use of commands like tt(sudo)
+or tt(doas) to gain extra privileges when retrieving information for
+completion. This is only done when a command such as tt(sudo) appears on
+the command-line. To force the use of, e.g. tt(sudo) or to override any
+prefix that might be added due to tt(gain-privileges), the tt(command)
+style can be used with a value that begins with a hyphen.
+)
 kindex(keep-prefix, completion style)
 item(tt(keep-prefix))(
 This style is used by the tt(_expand) completer.  If it is `true', the
@@ -2023,8 +2044,9 @@ only be performed with the `tt(*)' inserted.
 kindex(matcher, completion style)
 item(tt(matcher))(
 This style is tested separately for each tag valid in the current
-context.  Its value is tried before any match specifications given by the 
-tt(matcher-list) style.  It should be in the form described in
+context.  Its value is placed before any match specifications given by the
+tt(matcher-list) style so can override them via the use of an tt(x:)
+specification.  The value should be in the form described in
 ifzman(the section `Completion Matching Control' in zmanref(zshcompwid))\
 ifnzman(noderef(Completion Matching Control))\
 .  For examples of this, see the description of the tt(tag-order) style.
@@ -2181,7 +2203,7 @@ matches does not fit on the screen by using the value
 `tt(select=long)'.  To start menu selection even if the current widget
 only performs listing, use the value `tt(select=long-list)'.
 
-To turn on menu completion or menu selection when a there are a certain
+To turn on menu completion or menu selection when there are a certain
 number of matches em(or) the list of matches does not fit on the
 screen, both of `tt(yes=)' and `tt(select=)' may be given twice, once
 with a number and once with `tt(long)' or `tt(long-list)'.
@@ -2977,6 +2999,21 @@ widgets to find out if the command line hasn't changed since the last
 time completion was tried.  Only then are the tt(_ignored),
 tt(_correct) and tt(_approximate) completers called.
 )
+findex(_canonical_paths)
+item(tt(_canonical_paths) [ tt(-A) var(var) ] [ tt(-N) ] [ tt(-MJV12nfX) ] var(tag) var(descr) [ var(paths) ... ])(
+This completion function completes all paths given to it, and also tries to
+offer completions which point to the same file as one of the paths given
+(relative path when an absolute path is given, and vice versa; when tt(..)'s
+are present in the word to be completed; and some paths got from symlinks).
+
+tt(-A), if specified, takes the paths from the array variable specified. Paths can
+also be specified on the command line as shown above.  tt(-N), if specified,
+prevents canonicalizing the paths given before using them for completion, in
+case they are already so. The options tt(-M), tt(-J), tt(-V), tt(-1), tt(-2),
+tt(-n), tt(-F), tt(-X) are passed to tt(compadd).
+
+See tt(_description) for a description of var(tag) and var(descr).
+)
 findex(_complete)
 item(tt(_complete))(
 This completer generates all possible completions in a context-sensitive
@@ -3093,6 +3130,15 @@ completion continues to allow other completers such as tt(_expand) to
 expand the pattern. The standard tt(add-space) and tt(prefix-hidden)
 styles are observed.
 )
+findex(_external_pwds)
+item(tt(_external_pwds))(
+Completes current directories of other zsh processes belonging to the
+current user.
+
+This is intended to be used via tt(_generic), bound to a custom key
+combination. Note that pattern matching is enabled so matching is
+performed similar to how it works with the tt(_match) completer.
+)
 findex(_history)
 item(tt(_history))(
 Complete words from the shell's command  history.  This completer 
@@ -3222,6 +3268,7 @@ for the current context; remember that the context for completers is less
 specific than that for contextual completion as the full context has not
 yet been determined.  Elements of the array may have one of the following
 forms:
+
 startsitem()
 sitem(tt($)var(hash))(
 var(hash) is the name of an associative array.  Note this is not a full
@@ -3442,7 +3489,7 @@ emacs and vi respectively.
 )
 enditem()
 
-texinode(Completion Functions)(Completion Directories)(Bindable Commands)(Completion System)
+texinode(Completion Functions)(Completion System Variables)(Bindable Commands)(Completion System)
 sect(Utility Functions)
 cindex(completion system, utility functions)
 
@@ -3455,12 +3502,6 @@ generating matches all follow the convention of returning status zero if they
 generated completions and non-zero if no matching completions could be 
 added.
 
-Two more features are offered by the tt(_main_complete) function.  The
-arrays tt(compprefuncs) and tt(comppostfuncs) may contain
-names of functions that are to be called immediately before or after
-completion has been tried.  A function will only be called once unless
-it explicitly reinserts itself into the array.
-
 startitem()
 findex(_absolute_command_paths)
 item(tt(_absolute_command_paths))(
@@ -3539,13 +3580,11 @@ This function can be used to give a complete specification for completion
 for a command whose arguments follow standard UNIX option and argument
 conventions.
 
-em(Options overview)
+em(Options Overview)
 
 Options to tt(_arguments) itself must be in separate words, i.e. tt(-s -w),
 not tt(-sw).  The options are followed by var(spec)s that describe options and
-arguments of the analyzed command.  var(spec)s that describe option flags must
-precede var(spec)s that describe non-option ("positional" or "normal")
-arguments of the analyzed line.  To avoid ambiguity, all
+arguments of the analyzed command.  To avoid ambiguity, all
 options to tt(_arguments) itself may be separated from the var(spec) forms
 by a single colon.
 
@@ -3590,8 +3629,9 @@ even if one or more of the options take
 arguments.  For example, if tt(-x) takes an argument, with no
 tt(-s), `tt(-xy)' is considered as a single (unhandled) option; with
 tt(-s), tt(-xy) is an option with the argument `tt(y)'; with both tt(-s)
-and tt(-w), tt(-xy) may be the option tt(-x) and the option tt(-y) with
-arguments still to come.
+and tt(-w), tt(-xy) is the option tt(-x) and the option tt(-y) with
+arguments to tt(-x) (and to tt(-y), if it takes arguments) still to come
+in subsequent words.
 )
 item(tt(-W))(
 This option takes tt(-w) a stage further:  it is possible to
@@ -3816,7 +3856,7 @@ form below, the var(action) will be executed by calling the
 tt(_all_labels) function to process all tag labels.  No special handling
 of tags is needed unless a function call introduces a new one.
 
-The functions called to execute var(action)s will be called with the the
+The functions called to execute var(action)s will be called with the
 elements of the array named by the `tt(-O) var(name)' option as arguments.
 This can be used, for example, to pass the same set of options for the
 tt(compadd) builtin to all var(action)s.
@@ -3931,8 +3971,8 @@ command line after the command name excluding all options and their
 arguments.  Options are stored in the associative array
 `tt(opt_args)' with option names as keys and their arguments as
 the values.  For options that have more than one argument these are
-given as one string, separated by colons.  All colons in the original
-arguments are preceded with backslashes.
+given as one string, separated by colons.  All colons and backslashes
+in the original arguments are preceded with backslashes.
 
 The parameter `tt(context)' is set when returning to the calling function
 to perform an action of the form `tt(->)var(string)'.  It is set to an
@@ -3962,18 +4002,48 @@ example(local curcontext="$curcontext")
 This is useful where it is not possible for multiple states to be valid
 together.
 
-em(Specifying multiple sets of options)
+em(Grouping Options)
 
-It is possible to specify multiple sets of options and
-arguments with the sets separated by single hyphens.  The specifications
-before the first hyphen (if any) are shared by all the remaining sets.
-The first word in every other set provides a name for the
-set which may appear in exclusion lists in specifications,
-either alone or before one of the possible values described above.
-In the second case a `tt(-)' should appear between this name and the
-remainder.
+Options can be grouped to simplify exclusion lists. A group is
+introduced with `tt(PLUS())' followed by a name for the group in the
+subsequent word. Whole groups can then be referenced in an exclusion
+list or a group name can be used to disambiguate between two forms of
+the same option. For example:
 
-For example:
+example(_arguments \ 
+    '(group2--x)-a' \ 
+  PLUS() group1 \ 
+    -m \ 
+    '(group2)-n' \ 
+  PLUS() group2 \ 
+    -x -y)
+
+If the name of a group is specified in the form
+`tt(LPAR())var(name)tt(RPAR())' then only one value from that group
+will ever be completed; more formally, all specifications are mutually
+exclusive to all other specifications in that group. This is useful for
+defining options that are aliases for each other. For example:
+
+example(_arguments \ 
+    -a -b \ 
+  PLUS() '(operation)' \ 
+    {-c,--compress}'[compress]' \ 
+    {-d,--decompress}'[decompress]' \ 
+    {-l,--list}'[list]')
+
+If an option in a group appears on the command line, it is stored in the
+associative array `tt(opt_args)' with 'var(group)tt(-)var(option)'
+as a key.  In the example above, a key `tt(operation--c)' is used if the option
+`tt(-c)' is present on the command line.
+
+em(Specifying Multiple Sets of Arguments)
+
+It is possible to specify multiple sets of options and arguments with
+the sets separated by single hyphens. This differs from groups in that
+sets are considered to be mutually exclusive of each other.
+
+Specifications before the first set and from any group are common to
+all sets. For example:
 
 example(_arguments \ 
     -a \ 
@@ -3989,28 +4059,11 @@ possible completions.  When it contains `tt(-d)' or an argument, the
 option `tt(-c)' will not be considered.  However, after `tt(-a)'
 both sets will still be considered valid.
 
-If an option in a set appears on the command line, it is stored in the
-associative array `tt(opt_args)' with 'var(set)tt(-)var(option)'
-as a key.  In the example above, a key `tt(set1--c)' is used if the option
-`tt(-c)' is on the command line.
-
-If the name given for one of the mutually exclusive sets is of the form
-`tt(LPAR())var(name)tt(RPAR())' then only one value from each set will ever
-be completed; more formally, all specifications are mutually
-exclusive to all other specifications in the same set.  This is
-useful for defining multiple sets of options which are mutually
-exclusive and in which the options are aliases for each other.  For
-example:
-
-example(_arguments \ 
-    -a -b \ 
-  - '(compress)' \ 
-    {-c,--compress}'[compress]' \ 
-  - '(uncompress)' \ 
-    {-d,--decompress}'[decompress]')
+As for groups, the name of a set may appear in exclusion lists, either
+alone or preceding a normal option or argument specification.
 
-As the completion code has to parse the command line separately for each
-set this form of argument is slow and should only be used when necessary.
+The completion code has to parse the command line separately for each
+set. This can be slow so sets should only be used when necessary.
 A useful alternative is often an option specification with rest-arguments
 (as in `tt(-foo:*:...)'); here the option tt(-foo) swallows up all
 remaining arguments as described by the var(optarg) definitions.
@@ -4156,7 +4209,7 @@ The return status of tt(_call_function) itself is zero if the function
 var(name) exists and was called and non-zero otherwise.
 )
 findex(_call_program)
-item(tt(_call_program) var(tag) var(string) ...)(
+item(tt(_call_program) [ tt(-p) ] var(tag) var(string) ...)(
 This function provides a mechanism for the user to override the use of an
 external command.  It looks up the tt(command) style with the supplied
 var(tag).  If the style is set, its value is used as the command to
@@ -4164,6 +4217,15 @@ execute.  The var(string)s from the call to tt(_call_program), or from the
 style if set, are concatenated with spaces between them and the resulting
 string is evaluated.  The return status is the return status of the command
 called.
+
+If the option `tt(-p)' is supplied it indicates that the command
+output is influenced by the permissions it is run with. If the
+tt(gain-privileges) style is set to true, tt(_call_program) will make
+use of commands such as tt(sudo), if present on the command-line, to
+match the permissions to whatever the final command is likely to run
+under. When looking up the tt(gain-privileges) and tt(command) styles,
+the command component of the zstyle context will end with a slash
+(`tt(/)') followed by the command that would be used to gain privileges.
 )
 findex(_combination)
 item(tt(_combination) [ tt(-s) var(pattern) ] var(tag) var(style) var(spec) ... var(field) var(opts) ...)(
@@ -4216,6 +4278,16 @@ This function completes words that are valid at command position: names of
 aliases, builtins, hashed commands, functions, and so on.  With the tt(-e)
 flag, only hashed commands are completed.  The tt(-) flag is ignored.
 )
+findex(_completers)
+item(tt(_completers) [ tt(-p) ])(
+This function completes names of completers.
+
+startitem()
+item(tt(-p))(
+Include the leading underscore (`tt(_)') in the matches.
+)
+enditem()
+)
 findex(_describe)
 redef(SPACES)(0)(tt(ifztexi(NOTRANS(@ @ @ @ @ @ @ @ @ @ ))ifnztexi(          )))
 xitem(tt(_describe )[tt(-12JVx)] [ tt(-oO) | tt(-t) var(tag) ] var(descr) var(name1) [ var(name2) ] [ var(opt) ... ])
@@ -4227,13 +4299,13 @@ different completion options var(opt)s.
 The var(descr) is taken as a string to display above the matches if the
 tt(format) style for the tt(descriptions) tag is set.  This is followed by
 one or two names of arrays followed by options to pass to tt(compadd).  The
-first array contains the possible completions with their descriptions in
+array var(name1) contains the possible completions with their descriptions in
 the form `var(completion)tt(:)var(description)'.  Any literal colons in
-var(completion) must be quoted with a backslash.  If a second array is
-given, it should have the same number of elements as the first; in this
+var(completion) must be quoted with a backslash.  If a var(name2) is
+given, it should have the same number of elements as var(name1); in this
 case the corresponding elements are added as possible completions instead
-of the var(completion) strings from the first array.  The completion list
-will retain the descriptions from the first array.  Finally, a set of
+of the var(completion) strings from var(name1).  The completion list
+will retain the descriptions from var(name1).  Finally, a set of
 completion options can appear.
 
 If the option `tt(-o)' appears before the first argument, the matches added
@@ -4317,6 +4389,21 @@ matches.  Almost all calls to tt(compadd) within the completion system use
 a similar format; this ensures that user-specified styles are correctly
 passed down to the builtins which implement the internals of completion.
 )
+findex(_dir_list)
+item(tt(_dir_list) [ tt(-s) var(sep) ] [ tt(-S) ])(
+Complete a list of directory names separated by colons
+(the same format as tt($PATH)).
+
+startitem()
+item(tt(-s) var(sep))(
+Use var(sep) as separator between items.
+var(sep) defaults to a colon (`tt(:)').
+)
+item(tt(-S))(
+Add var(sep) instead of slash (`tt(/)') as an autoremoveable suffix.
+)
+enditem()
+)
 findex(_dispatch)
 item(tt(_dispatch) var(context string) ...)(
 This sets the current context to var(context) and looks for completion
@@ -4332,6 +4419,44 @@ tt($service) to the var(string) being tried, and sets
 the var(context/command) field (the fourth) of the tt($curcontext)
 parameter to the var(context) given as the first argument.
 )
+findex(_email_addresses)
+item(tt(_email_addresses) [ tt(-c) ] [ tt(-n) var(plugin) ])(
+Complete email addresses.  Addresses are provided by plugins.
+
+startitem()
+item(tt(-c))(
+Complete bare tt(localhost@domain.tld) addresses, without a name part or
+a comment.
+Without this option, RFC822 `var(Firstname Lastname) tt(<)var(address)tt(>)'
+strings are completed.
+)
+item(tt(-n) var(plugin))(
+Complete aliases from var(plugin).  
+)
+COMMENT(Intentionally leaving tt(-s) undocumented: new code should use
+tt(_sequence) instead.)\
+enditem()
+
+The following plugins are available by default:
+tt(_email-ldap) (see the tt(filter) style),
+tt(_email-local) (completes var(user)tt(@)var(hostname) Unix addresses),
+tt(_email-mail) (completes aliases from tt(~/.mailrc)),
+tt(_email-mush),
+tt(_email-mutt),
+and
+tt(_email-pine).
+
+Addresses from the tt(_email-)var(foo) plugin are added under the
+tag `tt(email-)var(foo)'.
+
+em(Writing plugins)
+
+Plugins are written as separate functions with names starting with `tt(_email-)'.
+They are invoked with the tt(-c) option and tt(compadd) options.
+They should either do their own completion or
+set the tt($reply) array to a list of `var(alias)tt(:)var(address)' elements and return tt(300).
+New plugins will be picked up and run automatically.
+)
 findex(_files)
 item(tt(_files))(
 The function tt(_files) calls tt(_path_files) with all the arguments it
@@ -4416,7 +4541,7 @@ the time this function is called, tt(compstate[insert]) is cleared, so
 additional matches generated later are not inserted on the command line.
 )
 findex(_multi_parts)
-item(tt(_multi_parts) var(sep) var(array))(
+item(tt(_multi_parts) [ tt(-i) ] var(sep) var(array))(
 The argument var(sep) is a separator character.
 The var(array) may be either the
 name of an array parameter or a literal array in the form
@@ -4916,12 +5041,18 @@ particular tag is to be tried, the tt(_requested) function should be
 called (see above).
 
 If `tt(-C) var(name)' is given, var(name) is temporarily stored in the
-argument field (the fifth) of the context in the tt(curcontext) parameter
+var(argument) field (the fifth) of the context in the tt(curcontext) parameter
 during the call to tt(_tags); the field is restored on exit.  This
 allows tt(_tags) to use a more 
 specific context without having to change and reset the
 tt(curcontext) parameter (which has the same effect).
 )
+findex(_tilde_files)
+item(tt(_tilde_files))(
+Like tt(_files), but resolve leading tildes according to the rules of
+filename expansion, so the suggested completions don't start with
+a `tt(~)' even if the filename on the command-line does.
+)
 findex(_values)
 item(tt(_values) [ tt(-O) var(name) ] [ tt(-s) var(sep) ] [ tt(-S) var(sep) ] [ tt(-wC) ] var(desc) var(spec) ...)(
 This is used to complete arbitrary keywords (values) and their arguments,
@@ -5044,9 +5175,52 @@ Like tt(_tags) this function supports the tt(-C) option to give a
 different name for the argument context field.  The tt(-x) option has
 the same meaning as for tt(_description).
 )
+findex(_widgets)
+item(tt(_widgets) [ tt(-g) var(pattern) ])(
+This function completes names of zle widgets (see
+ifzman(the section `Widgets' in zmanref(zshzle))\
+ifnzman(noderef(Zle Widgets))\
+).  The var(pattern), if present, is matched against values of the tt($widgets)
+special parameter, documented in 
+ifzman(the section `The zsh/zleparameter Module' in zmanref(zshmodules))\
+ifnzman(noderef(The zsh/zleparameter Module)).
+)
 enditem()
 
-texinode(Completion Directories)()(Completion Functions)(Completion System)
+texinode(Completion System Variables)(Completion Directories)(Completion Functions)(Completion System)
+sect(Completion System Variables)
+cindex(completion system, variables)
+
+There are some standard variables, initialised by the tt(_main_complete)
+function and then used from other functions.
+
+The standard variables are:
+
+startitem()
+item(tt(_comp_caller_options))(
+The completion system uses tt(setopt) to set a number of options. This
+allows functions to be written without concern for compatibility with
+every possible combination of user options. However, sometimes completion
+needs to know what the user's option preferences are. These are saved
+in the tt(_comp_caller_options) associative array. Option names, spelled
+in lowercase without underscores, are mapped to one or other of the
+strings `tt(on)' and `tt(off)'.
+)
+
+item(tt(_comp_priv_prefix))(
+Completion functions such as tt(_sudo) can set the tt(_comp_priv_prefix)
+array to a command prefix that may then be used by tt(_call_program) to
+match the privileges when calling programs to generate matches.
+)
+enditem()
+
+Two more features are offered by the tt(_main_complete) function.  The
+arrays tt(compprefuncs) and tt(comppostfuncs) may contain
+names of functions that are to be called immediately before or after
+completion has been tried.  A function will only be called once unless
+it explicitly reinserts itself into the array.
+
+texinode(Completion Directories)()(Completion System Variables)(Completion System)
 sect(Completion Directories)
 cindex(completion system, directory structure)
 
diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo
index c01763316..1cc94bf95 100644
--- a/Doc/Zsh/compwid.yo
+++ b/Doc/Zsh/compwid.yo
@@ -913,6 +913,13 @@ line and trial completion patterns are anchored on the right side.
 Here an empty var(ranchor) and the tt(e) and tt(E) forms force the
 match to the end of the command line or trial completion string.
 )
+item(tt(x:))(
+This form is used to mark the end of matching specifications:
+subsequent specifications are ignored. In a single standalone list
+of specifications this has no use but where matching specifications
+are accumulated, such as from nested function calls, it can allow one
+function to override another.
+)
 enditem()
 
 Each var(lpat), var(tpat) or var(anchor) is either an empty string or
diff --git a/Doc/Zsh/cond.yo b/Doc/Zsh/cond.yo
index 3d369fb83..e08fc0d36 100644
--- a/Doc/Zsh/cond.yo
+++ b/Doc/Zsh/cond.yo
@@ -63,6 +63,9 @@ is open and associated with a terminal device.
 item(tt(-u) var(file))(
 true if var(file) exists and has its setuid bit set.
 )
+item(tt(-v) var(varname))(
+true if shell variable var(varname) is set.
+)
 item(tt(-w) var(file))(
 true if var(file) exists and is writable by current process.
 )
@@ -103,8 +106,10 @@ true if var(file1) and var(file2) exist and refer to the same file.
 xitem(var(string) tt(=) var(pattern))
 item(var(string) tt(==) var(pattern))(
 true if var(string) matches var(pattern).
-The `tt(==)' form is the preferred one.  The `tt(=)' form is for
-backward compatibility and should be considered obsolete.
+The two forms are exactly equivalent.  The `tt(=)' form is
+the traditional shell syntax (and hence the only one generally used
+with the tt(test) and tt([) builtins); the `tt(==)' form provides
+compatibility with other sorts of computer language.
 )
 item(var(string) tt(!=) var(pattern))(
 true if var(string) does not match var(pattern).
diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 923bb29a9..35ab100b5 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -292,11 +292,11 @@ cindex(hook function utility)
 
 startitem()
 findex(add-zsh-hook)
-item(tt(add-zsh-hook) [ tt(-dD) ] [ tt(-Uzk) ] var(hook) var(function))(
+item(tt(add-zsh-hook) [ tt(-L) | tt(-dD) ] [ tt(-Uzk) ] var(hook) var(function))(
 Several functions are special to the shell, as described in the section
 ifnzman(Special Functions, noderef(Functions))\
 ifzman(SPECIAL FUNCTIONS, see zmanref(zshmisc)),
-in that they are automatic called at a specific point during shell execution.
+in that they are automatically called at specific points during shell execution.
 Each has an associated array consisting of names of functions to be
 called at the same point; these are so-called `hook functions'.
 The shell function tt(add-zsh-hook) provides a simple way of adding or
@@ -311,6 +311,10 @@ still be manipulated as a hook.
 var(function) is name of an ordinary shell function.  If no options
 are given this will be added to the array of functions to be executed
 in the given context.
+Functions are invoked in the order they were added.
+
+If the option tt(-L) is given, the current values for the hook arrays
+are listed with tt(typeset).
 
 If the option tt(-d) is given, the var(function) is removed from
 the array of functions to be executed.
@@ -323,6 +327,55 @@ The options tt(-U), tt(-z) and tt(-k) are passed as arguments to
 tt(autoload) for var(function).  For functions contributed with zsh, the
 options tt(-Uz) are appropriate.
 )
+findex(add-zle-hook-widget)
+item(tt(add-zle-hook-widget) [ tt(-L) | tt(-dD) ] [ tt(-Uzk) ] var(hook) var(widgetname))(
+Several widget names are special to the line editor, as described in the section
+ifnzman(Special Widgets, noderef(Zle Widgets))\
+ifzman(Special Widgets, see zmanref(zshzle)),
+in that they are automatically called at specific points during editing.
+Unlike function hooks, these do not use a predefined array of other names
+to call at the same point; the shell function tt(add-zle-hook-widget)
+maintains a similar array and arranges for the special widget to invoke
+those additional widgets.
+
+var(hook) is one of tt(isearch-exit), tt(isearch-update),
+tt(line-pre-redraw), tt(line-init), tt(line-finish), tt(history-line-set),
+or tt(keymap-select), corresponding to each of the special widgets
+tt(zle-isearch-exit), etc.  The special widget names are also accepted
+as the var(hook) argument.
+
+var(widgetname) is the name of a ZLE widget.  If no options are given this
+is added to the array of widgets to be invoked in the given hook context.
+Widgets are invoked in the order they were added, with
+example(tt(zle )var(widgetname)tt( -Nw -- "$@"))
+
+vindex(WIDGET, in hooks)
+Note that this means that the `tt(WIDGET)' special parameter tracks the
+var(widgetname) when the widget function is called, rather than tracking
+the name of the corresponding special hook widget.
+
+If the option tt(-d) is given, the var(widgetname) is removed from
+the array of widgets to be executed.
+
+If the option tt(-D) is given, the var(widgetname) is treated as a pattern
+and any matching names of widgets are removed from the array.
+
+If var(widgetname) does not name an existing widget when added to the
+array, it is assumed that a shell function also named var(widgetname) is
+meant to provide the implementation of the widget.  This name is therefore
+marked for autoloading, and the options tt(-U), tt(-z) and tt(-k) are
+passed as arguments to tt(autoload) as with tt(add-zsh-hook).  The
+widget is also created with `tt(zle -N )var(widgetname)' to cause the
+corresponding function to be loaded the first time the hook is called.
+
+The arrays of var(widgetname) are currently maintained in tt(zstyle)
+contexts, one for each var(hook) context, with a style of `tt(widgets)'.
+If the tt(-L) option is given, this set of styles is listed with
+`tt(zstyle -L)'.  This implementation may change, and the special widgets
+that refer to the styles are created only if tt(add-zle-hook-widget) is
+called to add at least one widget, so if this function is used for any
+hooks, then all hooks should be managed only via this function.
+)
 enditem()
 
 texinode(Recent Directories)(Other Directory Functions)(Utilities)(User Contributions)
@@ -809,7 +862,8 @@ sitem(SVK (tt(svk)))(uref(http://svk.bestpractical.com/))
 endsitem()
 
 There is also support for the patch management system tt(quilt)
-(uref(http://savannah.nongnu.org/projects/quilt)). See tt(Quilt Support)
+(uref(http://savannah.nongnu.org/projects/quilt)). See
+ifzman(bf(Quilt Support))ifnzman(noderef(vcs_info Quilt Support))
 below for details.
 
 To load tt(vcs_info):
@@ -819,6 +873,18 @@ example(autoload -Uz vcs_info)
 It can be used in any existing prompt, because it does not require any
 specific tt($psvar) entries to be available.
 
+startmenu()
+menu(vcs_info Quickstart)
+menu(vcs_info Configuration)
+menu(vcs_info Oddities)
+menu(vcs_info Quilt Support)
+menu(vcs_info API)
+menu(vcs_info Variables)
+menu(vcs_info Hooks)
+menu(vcs_info Examples)
+endmenu()
+
+texinode(vcs_info Quickstart)(vcs_info Configuration)()(Version Control Information)
 subsect(Quickstart)
 
 To get this feature working quickly (including colors), you can do the
@@ -882,6 +948,7 @@ That means the detection of these systems is skipped em(completely). No
 wasted time there.
 
 
+texinode(vcs_info Configuration)(vcs_info Oddities)(vcs_info Quickstart)(Version Control Information)
 subsect(Configuration)
 
 The tt(vcs_info) feature can be configured via tt(zstyle).
@@ -891,11 +958,17 @@ example(:vcs_info:var(vcs-string):var(user-context):var(repo-root-name))
 
 startitem()
 item(var(vcs-string))(
-is one of: bf(git), bf(git-svn), bf(git-p4), bf(hg), bf(hg-git),
-bf(hg-hgsubversion), bf(hg-hgsvn), bf(darcs), bf(bzr), bf(cdv), bf(mtn),
-bf(svn), bf(cvs), bf(svk), bf(tla), bf(p4) or bf(fossil). When hooks are
-active the hooks name is added after a `+'. (See bf(Hooks in vcs_info)
-below.)
+is one of: tt(git), tt(git-svn), tt(git-p4), tt(hg), tt(hg-git),
+tt(hg-hgsubversion), tt(hg-hgsvn), tt(darcs), tt(bzr), tt(cdv), tt(mtn),
+tt(svn), tt(cvs), tt(svk), tt(tla), tt(p4) or tt(fossil).
+This is followed by `tt(.quilt-)var(quilt-mode)' in Quilt mode
+(see ifzman(bf(Quilt Support))ifnzman(noderef(vcs_info Quilt Support)) for details)
+and by `tt(+)var(hook-name)' while hooks are active
+(see ifzman(bf(Hooks in vcs_info))ifnzman(noderef(vcs_info Hooks)) for details).
+
+COMMENT(users/20807)
+Currently, hooks in quilt mode don't add the `tt(.quilt-)var(quilt-mode)' information.
+This may change in the future.
 )
 item(var(user-context))(
 is a freely configurable string, assignable by
@@ -998,26 +1071,26 @@ tt(:vcs_info:-init-:*:-all-) context.
 
 Say, tt(~/.zsh) is a directory under version control, in which you do
 not want tt(vcs_info) to be active, do:
-example(zstyle ':vcs_info:*' disable-patterns "$HOME/.zsh+LPAR()|/*+RPAR()")
+example(zstyle ':vcs_info:*' disable-patterns "${+LPAR()b+RPAR()HOME}/.zsh+LPAR()|/*+RPAR()")
 )
 kindex(use-quilt)
 item(tt(use-quilt))(
 If enabled, the tt(quilt) support code is active in `addon' mode.
-See tt(Quilt Support) for details.
+See ifzman(bf(Quilt Support))ifnzman(noderef(vcs_info Quilt Support)) for details.
 )
 kindex(quilt-standalone)
 item(tt(quilt-standalone))(
 If enabled, `standalone' mode detection is attempted if no VCS is active
-in a given directory. See tt(Quilt Support) for details.
+in a given directory. See ifzman(bf(Quilt Support))ifnzman(noderef(vcs_info Quilt Support)) for details.
 )
 kindex(quilt-patch-dir)
 item(tt(quilt-patch-dir))(
 Overwrite the value of the tt($QUILT_PATCHES) environment variable. See
-tt(Quilt Support) for details.
+ifzman(bf(Quilt Support))ifnzman(noderef(vcs_info Quilt Support)) for details.
 )
 kindex(quiltcommand)
 item(tt(quiltcommand))(
-When tt(quilt) itself is called in quilt support the value of this style
+When tt(quilt) itself is called in quilt support, the value of this style
 is used as the command name.
 )
 kindex(check-for-changes)
@@ -1144,7 +1217,8 @@ is only used by tt(vcs_info)'s hooks system.
 )
 kindex(hooks)
 item(tt(hooks))(
-A list style that defines hook-function names. See bf(Hooks in vcs_info)
+A list style that defines hook-function names. See ifzman(bf(Hooks in vcs_info))\
+ifnzman(noderef(vcs_info Hooks))
 below for details.
 )
 kindex(patch-format)
@@ -1154,6 +1228,8 @@ item(tt(nopatch-format))(
 This pair of styles format the patch information used by the tt(%m) expando in
 formats and actionformats for the tt(git) and tt(hg) backends.  The value is
 subject to certain tt(%)-expansions described below.
+The expanded value is made available in the global tt(backend_misc) array as
+tt(${backend_misc[patches]}) (also if a tt(set-patch-format) hook is used).
 )
 kindex(get-unapplied)
 item(tt(get-unapplied))(
@@ -1224,8 +1300,8 @@ A "misc" replacement. It is at the discretion of the backend to
 decide what this replacement expands to.
 
 The tt(hg) and tt(git) backends use this expando to display patch information.
-tt(hg) sources patch information from the tt(mq) extensions; tt(git) from the
-tt(rebase) command and from the and tt(stgit) extension.  The tt(patch-format)
+tt(hg) sources patch information from the tt(mq) extensions; tt(git) from in-progress
+tt(rebase) and tt(cherry-pick) operations and from the tt(stgit) extension.  The tt(patch-format)
 and tt(nopatch-format) styles control the generated string.  The former is used
 when at least one patch from the patch queue has been applied, and the latter
 otherwise.
@@ -1235,6 +1311,17 @@ to tt(mq) information).  See the tt(get-mq) and tt(get-bookmarks) styles.  Both
 of these styles may be enabled at the same time.  If both are enabled, both
 resulting strings will be shown separated by a semicolon (that cannot currently
 be customized).
+
+The tt(quilt) `standalone' backend sets this expando to the same value as the
+tt(%Q) expando.
+)
+item(tt(%Q))(
+Quilt series information.
+When quilt is used (either in `addon' mode or as a `standalone' backend),
+this expando is set to quilt series' tt(patch-format) string.
+The tt(set-patch-format) hook and tt(nopatch-format) style are honoured.
+
+See ifzman(tt(Quilt Support))ifnzman(noderef(vcs_info Quilt Support)) below for details.
 )
 enditem()
 
@@ -1268,6 +1355,7 @@ endsitem()
 Not all VCS backends have to support all replacements. For tt(nvcsformats)
 no replacements are performed at all, it is just a string.
 
+texinode(vcs_info Oddities)(vcs_info Quilt Support)(vcs_info Configuration)(Version Control Information)
 subsect(Oddities)
 
 If you want to use the tt(%b) (bold off) prompt expansion in tt(formats),
@@ -1278,7 +1366,14 @@ tt(branchformat), use tt(%%%%b). Sorry for this inconvenience, but it
 cannot be easily avoided. Luckily we do not clash with a lot of prompt
 expansions and this only needs to be done for those.
 
+When one of the tt(gen-applied-string), tt(gen-unapplied-string), and
+tt(set-patch-format) hooks is defined,
+applying tt(%)-escaping (`tt(foo=${foo//'%'/%%})') to the interpolated values
+for use in the prompt is the responsibility of those hooks (jointly);
+when neither of those hooks is defined, tt(vcs_info) handles escaping by itself.
+We regret this coupling, but it was required for backwards compatibility.
 
+texinode(vcs_info Quilt Support)(vcs_info API)(vcs_info Oddities)(Version Control Information)
 subsect(Quilt Support)
 
 bf(Quilt) is not a version control system, therefore this is not implemented
@@ -1293,13 +1388,22 @@ The tt(vcs_info) integration tries to support both ways of using quilt by
 having two slightly different modes of operation: `addon' mode and
 `standalone' mode).
 
+Quilt integration is off by default; to enable it, set the tt(use-quilt) style,
+and add tt(%Q) to your tt(formats) or tt(actionformats) style:
+example(zstyle ':vcs_info:*' use-quilt true)
+
+Styles looked up from the Quilt support code include `tt(.quilt-)var(quilt-mode)'
+in the var(vcs-string) part of the context, where var(quilt-mode) is either
+tt(addon) or tt(standalone).
+Example: tt(:vcs_info:git.quilt-addon:default:)var(repo-root-name).
+
 For `addon' mode to become active tt(vcs_info) must have already detected a
 real version control system controlling the directory. If that is the case,
 a directory that holds quilt's patches needs to be found. That directory is
 configurable via the `tt(QUILT_PATCHES)' environment variable. If that
 variable exists its value is used, otherwise the value `tt(patches)' is
 assumed. The value from tt($QUILT_PATCHES) can be overwritten using the
-tt(`quilt-patches') style. (Note: you can use tt(vcs_info) to keep the value
+`tt(quilt-patches)' style. (Note: you can use tt(vcs_info) to keep the value
 of tt($QUILT_PATCHES) correct all the time via the tt(post-quilt) hook).
 
 When the directory in question is found, quilt is assumed to be active. To
@@ -1313,8 +1417,10 @@ to know which patches of a series are not yet applied, you need to activate
 the tt(get-unapplied) style in the appropriate context.
 
 tt(vcs_info) allows for very detailed control over how the gathered
-information is presented (see the below sections, bf(Styles) and bf(Hooks in
-vcs_info)), all of which are documented below. Note there are a number of
+information is presented (see 
+ifzman(the bf(Configuration) and bf(Hooks in vcs_info) sections)\
+ifnzman(noderef(vcs_info Configuration) and noderef(vcs_info Hooks))),
+all of which are documented below. Note there are a number of
 other patch tracking systems that work on top of a certain version control
 system (like tt(stgit) for bf(git), or tt(mq) for bf(hg)); the configuration
 for systems like that are generally configured the same way as the bf(quilt)
@@ -1368,6 +1474,7 @@ Note, if there is both a function and a variable by the name of
 tt(quilt-standalone), the function will take precedence.
 
 
+texinode(vcs_info API)(vcs_info Variables)(vcs_info Quilt Support)(Version Control Information)
 subsect(Function Descriptions (Public API))
 
 startitem()
@@ -1376,7 +1483,8 @@ item(tt(vcs_info) [var(user-context)])(
 The main function, that runs all backends and assembles all data into
 tt(${vcs_info_msg_*_}). This is the function you want to call from
 tt(precmd) if you want to include up-to-date information in your prompt (see
-tt(Variable description) below). If an argument is given, that string will be
+ifzman(bf(Variable Description))ifnzman(noderef(vcs_info Variables))
+below).  If an argument is given, that string will be
 used instead of tt(default) in the var(user-context) field of the style
 context.
 )
@@ -1385,7 +1493,8 @@ item(tt(vcs_info_hookadd))(
 Statically registers a number of functions to a given hook. The hook needs
 to be given as the first argument; what follows is a list of hook-function
 names to register to the hook. The `tt(+vi-)' prefix needs to be left out
-here. See bf(Hooks in vcs_info) below for details.
+here. See ifzman(bf(Hooks in vcs_info))ifnzman(noderef(vcs_info Hooks))
+below for details.
 )
 findex(vcs_info_hookdel)
 item(tt(vcs_info_hookdel))(
@@ -1394,8 +1503,9 @@ first non-option argument; what follows is a list of hook-function
 names to un-register from the hook. If `tt(-a)' is used as the first
 argument, tt(all) occurrences of the functions are unregistered. Otherwise
 only the last occurrence is removed (if a function was registered to a hook
-more than once) . The `tt(+vi-)' prefix needs to be left out here. See
-bf(Hooks in vcs_info) below for details.
+more than once). The `tt(+vi-)' prefix needs to be left out here.
+See ifzman(bf(Hooks in vcs_info))ifnzman(noderef(vcs_info Hooks))
+below for details.
 )
 findex(vcs_info_lastmsg)
 item(tt(vcs_info_lastmsg))(
@@ -1420,6 +1530,7 @@ enditem()
 
 All functions named tt(VCS_INFO_*) are for internal use only.
 
+texinode(vcs_info Variables)(vcs_info Hooks)(vcs_info API)(Version Control Information)
 subsect(Variable Description)
 
 startitem()
@@ -1437,6 +1548,7 @@ enditem()
 
 All variables named tt(VCS_INFO_*) are for internal use only.
 
+texinode(vcs_info Hooks)(vcs_info Examples)(vcs_info Variables)(Version Control Information)
 subsect(Hooks in vcs_info)
 
 Hooks are places in tt(vcs_info) where you can run your own code. That
@@ -1544,10 +1656,11 @@ so far in the opposite order, which means that the first argument is the
 top-most patch and so forth.
 
 When setting tt(ret) to non-zero, the string in
-tt(${hook_com[applied-string]}) will be used in the tt(%m) escape in
-tt(formats) and tt(actionformats); it will be available in the global
-tt(backend_misc) array as tt($backend_misc[patches]}); and it will be
+tt(${hook_com[applied-string]}) will be
 available as tt(%p) in the tt(patch-format) and tt(nopatch-format) styles.
+This hook is, in concert with tt(set-patch-format), responsible for
+tt(%)-escaping that value for use in the prompt.
+(See ifzman(the bf(Oddities) section)ifnzman(noderef(vcs_info Oddities)).)
 )
 item(tt(gen-unapplied-string))(
 Called in the tt(git) (with tt(stgit) or during rebase), and tt(hg) (with
@@ -1561,6 +1674,9 @@ the patch next-in-line to be applied and so forth.
 When setting tt(ret) to non-zero, the string in
 tt(${hook_com[unapplied-string]}) will be available as tt(%u) in the
 tt(patch-format) and tt(nopatch-format) styles.
+This hook is, in concert with tt(set-patch-format), responsible for
+tt(%)-escaping that value for use in the prompt.
+(See ifzman(the bf(Oddities) section)ifnzman(noderef(vcs_info Oddities)).)
 )
 item(tt(gen-mqguards-string))(
 Called in the tt(hg) backend when tt(guards-string) is generated; the
@@ -1638,6 +1754,11 @@ controllable in addition to that.
 If tt(ret) is set to non-zero, the string in tt(${hook_com[patch-replace]})
 will be used unchanged instead of an expanded format from tt(patch-format) or
 tt(nopatch-format).
+
+This hook is, in concert with the tt(gen-applied-string) or
+tt(gen-unapplied-string) hooks if they are defined, responsible for
+tt(%)-escaping the final tt(patch-format) value for use in the prompt.
+(See ifzman(the bf(Oddities) section)ifnzman(noderef(vcs_info Oddities)).)
 )
 item(tt(set-message))(
 Called each time before a `tt(vcs_info_msg_)var(N)tt(_)' message is set.
@@ -1666,10 +1787,12 @@ tt(vcs_info).
 )
 enditem()
 
-If all of this sounds rather confusing, take a look at the bf(Examples)
-section below and also in the tt(Misc/vcs_info-examples) file in the Zsh source.
+If all of this sounds rather confusing, take a look at
+ifzman(the bf(Examples) section below)ifnzman(noderef(vcs_info Examples))
+and also in the tt(Misc/vcs_info-examples) file in the Zsh source.
 They contain some explanatory code.
 
+texinode(vcs_info Examples)()(vcs_info Hooks)(Version Control Information)
 subsect(Examples)
 
 Don't use tt(vcs_info) at all (even though it's in your prompt):
@@ -1888,6 +2011,8 @@ tindex(transpose-words-match)
 tindex(capitalize-word-match)
 tindex(up-case-word-match)
 tindex(down-case-word-match)
+tindex(delete-whole-word-match)
+tindex(select-word-match)
 tindex(select-word-style)
 tindex(match-word-context)
 tindex(match-words-by-style)
@@ -1895,12 +2020,14 @@ xitem(tt(forward-word-match), tt(backward-word-match))
 xitem(tt(kill-word-match), tt(backward-kill-word-match))
 xitem(tt(transpose-words-match), tt(capitalize-word-match))
 xitem(tt(up-case-word-match), tt(down-case-word-match))
+xitem(tt(delete-whole-word-match), tt(select-word-match))
 item(tt(select-word-style), tt(match-word-context), tt(match-words-by-style))(
-The eight `tt(-match)' functions are drop-in replacements for the
+The first eight `tt(-match)' functions are drop-in replacements for the
 builtin widgets without the suffix.  By default they behave in a similar
 way.  However, by the use of styles and the function tt(select-word-style),
-the way words are matched can be altered.  For comparison, the widgets
-described in ifzman(zmanref(zshzle) under Text Objects)\
+the way words are matched can be altered. tt(select-word-match) is intended
+to be used as a text object in vi mode but with custom word styles. For
+comparison, the widgets described in ifzman(zmanref(zshzle) under Text Objects)\
 ifnzman(noderef(Text Objects)) use fixed definitions of words, compatible
 with the tt(vim) editor.
 
@@ -1908,7 +2035,7 @@ The simplest way of configuring the functions is to use
 tt(select-word-style), which can either be called as a normal function with
 the appropriate argument, or invoked as a user-defined widget that will
 prompt for the first character of the word style to be used.  The first
-time it is invoked, the eight tt(-match) functions will automatically
+time it is invoked, the first eight tt(-match) functions will automatically
 replace the builtin versions, so they do not need to be loaded explicitly.
 
 The word styles available are as follows.  Only the first character
@@ -2049,7 +2176,7 @@ Here are some examples of use of the tt(word-context) style to extend
 the context.
 
 example(zstyle ':zle:*' word-context \ 
-       "*/*" file "[[:space:]]" whitespace
+       "*/*" filename "[[:space:]]" whitespace
 zstyle ':zle:transpose-words:whitespace' word-style shell
 zstyle ':zle:transpose-words:filename' word-style normal
 zstyle ':zle:transpose-words:filename' word-chars '')
@@ -2076,6 +2203,17 @@ non-word characters following that word (7) the remainder of the line.  Any
 of the elements may be an empty string; the calling function should test
 for this to decide whether it can perform its function.
 
+If the variable tt(matched_words) is defined by the caller to
+tt(match-words-by-style) as an associative array (tt(local -A
+matched_words)), then the seven values given above should be retrieved
+from it as elements named tt(start), tt(word-before-cursor),
+tt(ws-before-cursor), tt(ws-after-cursor), tt(word-after-cursor),
+tt(ws-after-word), and tt(end).  In addition the element
+tt(is-word-start) is 1 if the cursor is on the start of a word or
+subword, or on white space before it (the cases can be distinguished by
+testing the tt(ws-after-cursor) element) and 0 otherwise.  This form is
+recommended for future compatibility.
+
 It is possible to pass options with arguments to tt(match-words-by-style)
 to override the use of styles.  The options are:
 startsitem()
@@ -2094,7 +2232,7 @@ tt(match-word-context).  This should not usually need to be called
 directly.
 )
 tindex(bracketed-paste-magic)
-item(bracketed-paste-magic)(
+item(tt(bracketed-paste-magic))(
 The tt(bracketed-paste) widget (see ifzman(subsection Miscellaneous in
 zmanref(zshzle))ifnzman(noderef(Miscellaneous) in noderef(Zle Widgets)))
 inserts pasted text literally into the editor buffer rather than interpret
@@ -2912,6 +3050,17 @@ and aliases `tt(globurl)' to `tt(noglob urlglobber)'.  This function takes
 a local URL apart, attempts to pattern-match the local file portion of the
 URL path, and then puts the results back into URL format again.
 )
+tindex(vi-pipe)
+item(tt(vi-pipe))(
+This function reads a movement command from the keyboard and then
+prompts for an external command. The part of the buffer covered by
+the movement is piped to the external command and then replaced by
+the command's output. If the movement command is bound to vi-pipe,
+the current line is used.
+
+The function serves as an example for reading a vi movement command
+from within a user-defined widget.
+)
 tindex(which-command)
 item(tt(which-command))(
 This function is a drop-in replacement for the builtin widget
@@ -2941,6 +3090,9 @@ the variable tt(ZCALC_AUTO_INSERT_PREFIX).
 Hence, for example, typing `tt(PLUS()12)' followed by return adds 12
 to the previous result.
 
+If zcalc is in RPN mode (tt(-r) option) the effect of this binding is
+automatically suppressed as operators alone on a line are meaningful.
+
 When not in zcalc, the key simply inserts the symbol itself.
 )
 enditem()
@@ -3396,7 +3548,7 @@ will ensure that any files found in that area will be executed as MIME
 types even if they are executable.  As this example shows, the complete
 file name is matched against the pattern, regardless of how the file
 was passed to the handler.  The file is resolved to a full path using
-the tt(:A) modifier described in
+the tt(:P) modifier described in
 ifzman(the subsection Modifiers in zmanref(zshexpn))\
 ifnzman(noderef(Modifiers));
 this means that symbolic links are resolved where possible, so that
@@ -3654,7 +3806,7 @@ sect(Mathematical Functions)
 
 startitem()
 findex(zcalc)
-item(tt(zcalc) [ tt(-ef) ] [ var(expression) ... ])(
+item(tt(zcalc) [ tt(-erf) ] [ var(expression) ... ])(
 A reasonably powerful calculator based on zsh's arithmetic evaluation
 facility.  The syntax is similar to that of formulae in most programming
 languages; see
@@ -3705,8 +3857,14 @@ first few positional parameters.  A visual indication of this is given when
 the calculator starts.
 
 The constants tt(PI) (3.14159...) and tt(E) (2.71828...) are provided.
-Parameter assignment is possible, but note that all parameters will be put
-into the global namespace.
+Parameter assignment is possible, but note that all parameters will be
+put into the global namespace unless the tt(:local) special command is
+used.  The function creates local variables whose names start with
+tt(_), so users should avoid doing so.  The variables tt(ans) (the last
+answer) and tt(stack) (the stack in RPN mode) may be referred to
+directly; tt(stack) is an array but elements of it are numeric.  Various
+other special variables are used locally with their standard meaning,
+for example tt(compcontext), tt(match), tt(mbegin), tt(mend), tt(psvar).
 
 The output base can be initialised by passing the option `tt(-#)var(base)',
 for example `tt(zcalc -#16)' (the `tt(#)' may have to be quoted, depending
@@ -3720,12 +3878,70 @@ If the option `tt(-f)' is set, all numbers are treated as floating
 point, hence for example the expression `tt(3/4)' evaluates to 0.75
 rather than 0.  Options must appear in separate words.
 
+If the option `tt(-r)' is set, RPN (Reverse Polish Notation) mode is
+entered.  This has various additional properties:
+startitem()
+item(Stack)(
+Evaluated values are maintained in a stack; this is contained in
+an array named tt(stack) with the most recent value in tt(${stack[1]}).
+)
+item(Operators and functions)(
+If the line entered matches an operator (tt(+), tt(-), tt(*),
+tt(/), tt(**), tt(^), tt(|) or tt(&)) or a function supplied by the
+tt(zsh/mathfunc) library, the bottom element or elements of the stack
+are popped to use as the argument or arguments.  The higher elements
+of stack (least recent) are used as earlier arguments.  The result is
+then pushed into tt(${stack[1]}).
+)
+item(Expressions)(
+Other expressions are evaluated normally, printed, and added to the
+stack as numeric values.  The syntax within expressions on a single line
+is normal shell arithmetic (not RPN).
+)
+item(Stack listing)(
+If an integer follows the option tt(-r) with no space, then
+on every evaluation that many elements of the stack, where available,
+are printed instead of just the most recent result.  Hence, for example,
+tt(zcalc -r4) shows tt($stack[4]) to tt($stack[1]) each time results
+are printed.
+)
+item(Duplication: tt(=))(
+The pseudo-operator tt(=) causes the most recent element of
+the stack to be duplicated onto the stack.
+)
+item(tt(pop))(
+The pseudo-function tt(pop) causes the most recent element of
+the stack to be popped.  A `tt(>)' on its own has the same effect.
+)
+item(tt(>)var(ident))(
+The expression tt(>) followed (with no space) by a shell identifier
+causes the most recent element of the stack to be popped and
+assigned to the variable with that name.  The variable is
+local to the tt(zcalc) function.
+)
+item(tt(<)var(ident))(
+The expression tt(<) followed (with no space) by a shell identifier
+causes the value of the variable with that name to be pushed
+onto the stack.  var(ident) may be an integer, in which
+case the previous result with that number (as shown before
+the tt(>) in the standard tt(zcalc) prompt) is put on the stack.
+)
+item(Exchange: tt(xy))(
+The pseudo-function tt(xy) causes the most recent two elements of
+the stack to be exchanged.  `tt(<>)' has the same effect.
+)
+enditem()
+
 The prompt is configurable via the parameter tt(ZCALCPROMPT), which
 undergoes standard prompt expansion.  The index of the current entry is
 stored locally in the first element of the array tt(psvar), which can be
 referred to in tt(ZCALCPROMPT) as `tt(%1v)'.  The default prompt is
 `tt(%1v> )'.
 
+The variable tt(ZCALC_ACTIVE) is set within the function and can
+be tested by nested functions; it has the value tt(rpn) if RPN mode is
+active, else 1.
+
 A few special commands are available; these are introduced by a colon.
 For backward compatibility, the colon may be omitted for certain
 commands.  Completion is available if tt(compinit) has been run.
@@ -3765,8 +3981,7 @@ is executed in the context of the function, i.e. with local variables.
 Space is optional after tt(:!).
 )
 item(tt(:local) var(arg) ...)(
-Declare variables local to the function.  Note that certain variables
-are used by the function for its own purposes.  Other variables
+Declare variables local to the function.  Other variables
 may be used, too, but they will be taken from or put into the global
 scope.
 )
@@ -3780,7 +3995,13 @@ Note that tt(zcalc) takes care of all quoting.  Hence for example:
 
 example(:f cube $1 * $1 * $1)
 
-defines a function to cube the sole argument.
+defines a function to cube the sole argument.  Functions so defined, or
+indeed any functions defined directly or indirectly using tt(functions
+-M), are available to execute by typing only the name on the line in RPN
+mode; this pops the appropriate number of arguments off the stack
+to pass to the function, i.e. 1 in the case of the example tt(cube)
+function.  If there are optional arguments only the mandatory
+arguments are supplied by this means.
 )
 item(tt([#)var(base)tt(]))(
 This is not a special command, rather part of normal arithmetic
@@ -3792,6 +4013,10 @@ always specified in decimal. `tt([#])' restores the normal output format.
 Note that setting an output base suppresses floating point output; use
 `tt([#])' to return to normal operation.
 )
+item(tt($)var(var))(
+Print out the value of var literally; does not affect the calculation.
+To use the value of var, omit the leading `tt($)'.
+)
 enditem()
 
 See the comments in the function for a few extra tips.
diff --git a/Doc/Zsh/exec.yo b/Doc/Zsh/exec.yo
index 30e4a61a2..5f79967de 100644
--- a/Doc/Zsh/exec.yo
+++ b/Doc/Zsh/exec.yo
@@ -28,10 +28,11 @@ not handle this executable format in the kernel.
 
 If no external command is found but a function tt(command_not_found_handler)
 exists the shell executes this function with all
-command line arguments.  The function should return status zero if it
-successfully handled the command, or non-zero status if it failed.
-In the latter case the standard handling is applied: `command not
-found' is printed to standard error and the shell exits with status 127.
-Note that the handler is executed in a subshell forked to execute
-an external command, hence changes to directories, shell parameters,
-etc. have no effect on the main shell.
+command line arguments.  The return status of the function becomes the
+status of the command.  If the function wishes to mimic the
+behaviour of the shell when the command is not found, it should
+print the message `tt(command not found:) var(cmd)' to standard error
+and return status 127.  Note that the handler is executed in a
+subshell forked to execute an external command, hence changes to
+directories, shell parameters, etc. have no effect on the main shell.
+
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index c6e7b6f16..d161b0ba5 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -219,15 +219,28 @@ noted.
 startitem()
 item(tt(a))(
 Turn a file name into an absolute path:  prepends the current directory,
-if necessary, and resolves any use of `tt(..)' and `tt(.)' in the path.
-Note that the transformation takes place even if the file or any
-intervening directories do not exist.
+if necessary; remove `tt(.)' path segments; and remove `tt(..)' path segments
+and the segments that immediately precede them.
+
+This transformation is agnostic about what is in the filesystem, i.e. is
+on the logical, not the physical directory.  It takes place in the same
+manner as when changing directories when neither of the options
+tt(CHASE_DOTS) or tt(CHASE_LINKS) is set.  For example,
+`tt(/before/here/../after)' is always transformed to
+`tt(/before/after)', regardless of whether `tt(/before/here)' exists or what
+kind of object (dir, file, symlink, etc.) it is.
 )
 item(tt(A))(
-As `tt(a)', but also resolve use of symbolic links where possible.
-Note that resolution of `tt(..)' occurs em(before) resolution of symbolic
-links.  This call is equivalent to tt(a) unless your system has the
-tt(realpath) system call (modern systems do).
+Turn a file name into an absolute path as the `tt(a)' modifier does, and
+em(then) pass the result through the tt(realpath+LPAR()3+RPAR()) library
+function to resolve symbolic links.
+
+Note: on systems that do not have a tt(realpath+LPAR()3+RPAR()) library
+function, symbolic links are not resolved, so on those systems `tt(a)' and
+`tt(A)' are equivalent.
+
+Note: tt(foo:A) and tt(realpath+LPAR()foo+RPAR()) are different on some inputs.
+For tt(realpath+LPAR()foo+RPAR()) semantics, see the `tt(P)` modifier.
 )
 item(tt(c))(
 Resolve a command name into an absolute path by searching the command
@@ -253,6 +266,14 @@ item(tt(p))(
 Print the new command but do not execute it.  Only works with history
 expansion.
 )
+item(tt(P))(
+Turn a file name into an absolute path, like tt(realpath+LPAR()3+RPAR()).
+The resulting path will be absolute, have neither `tt(.)' nor `tt(..)' components,
+and refer to the same directory entry as the input filename.
+
+Unlike tt(realpath+LPAR()3+RPAR()), non-existent trailing components are
+permitted and preserved.
+)
 item(tt(q))(
 Quote the substituted words, escaping further substitutions.  Works
 with history expansion and parameter expansion, though for parameters
@@ -551,6 +572,18 @@ noderef(Modifiers) in noderef(History Expansion) can be
 applied:  for example, tt(${i:s/foo/bar/}) performs string
 substitution on the expansion of parameter tt($i).
 
+In the following descriptions, `word' refers to a single word
+substituted on the command line, not necessarily a space delimited word.
+With default options, after the assignments:
+
+example(array=("first word" "second word")
+scalar="only word")
+
+then tt($array) substitutes two words, `tt(first word)' and `tt(second
+word)', and tt($scalar) substitutes a single word `tt(only word)'.  This
+may be modified by explicit or implicit word-splitting, however.  The
+full rules are complicated and are noted at the end.
+
 startitem()
 item(tt(${)var(name)tt(}))(
 The value, if any, of the parameter var(name) is substituted.
@@ -724,7 +757,7 @@ character of tt($foo) if the substitution would otherwise return a scalar,
 or the array starting at the fourth element if tt($foo) would return an
 array.  Note that with the option tt(KSH_ARRAYS) tt($foo) always returns
 a scalar (regardless of the use of the offset syntax) and a form
-such as tt($foo[*]:3) is required to extract elements of an array named
+such as tt(${foo[*]:3}) is required to extract elements of an array named
 tt(foo).
 
 If var(offset) is negative, the tt(-) may not appear immediately
@@ -740,8 +773,8 @@ expression tt(${var: offs}) does work, retrieving the offset from
 tt($offs).
 
 For further compatibility with other shells there is a special case
-for array offset 0.  This usually accesses to the
-first element of the array.  However, if the substitution refers the
+for array offset 0.  This usually accesses the
+first element of the array.  However, if the substitution refers to the
 positional parameter array, e.g. tt($@) or tt($*), then offset 0
 instead refers to tt($0), offset 1 refers to tt($1), and so on.  In
 other words, the positional parameter array is effectively extended by
@@ -749,12 +782,14 @@ prepending tt($0).  Hence tt(${*:0:1}) substitutes tt($0) and
 tt(${*:1:1}) substitutes tt($1).
 )
 xitem(tt(${)var(name)tt(/)var(pattern)tt(/)var(repl)tt(}))
-item(tt(${)var(name)tt(//)var(pattern)tt(/)var(repl)tt(}))(
+xitem(tt(${)var(name)tt(//)var(pattern)tt(/)var(repl)tt(}))
+item(tt(${)var(name)tt(:/)var(pattern)tt(/)var(repl)tt(}))(
 Replace the longest possible match of var(pattern) in the expansion of
 parameter var(name) by string var(repl).  The first form
-replaces just the first occurrence, the second form all occurrences.
+replaces just the first occurrence, the second form all occurrences,
+and the third form replaces only if var(pattern) matches the entire string.
 Both var(pattern) and var(repl) are subject to double-quoted substitution,
-so that expressions like tt(${name/$opat/$npat}) will work, but note the
+so that expressions like tt(${name/$opat/$npat}) will work, but obey the
 usual rule that pattern characters in tt($opat) are not treated specially
 unless either the option tt(GLOB_SUBST) is set, or tt($opat) is instead
 substituted as tt(${~opat}).
@@ -770,8 +805,8 @@ single backslash; this is not necessary if the
 `tt(%)' and `tt(#%) are not active if they occur inside a substituted
 parameter, even at the start.
 
-The first `tt(/)' may be preceded by a `tt(:)', in which case the match
-will only succeed if it matches the entire word.  Note also the
+If, after quoting rules apply, tt(${)var(name)tt(}) expands to an array,
+the replacements act on each element individually.  Note also the
 effect of the tt(I) and tt(S) parameter expansion flags below; however,
 the flags tt(M), tt(R), tt(B), tt(E) and tt(N) are not useful.
 
@@ -920,15 +955,25 @@ This is distinct from em(field splitting) by the tt(f), tt(s)
 or tt(z) flags, which still applies within each array element.
 )
 item(tt(A))(
-Create an array parameter with `tt(${)...tt(=)...tt(})',
+Convert the substitution into an array expression, even if it otherwise
+would be scalar.  This has lower precedence than subscripting, so one
+level of nested expansion is required in order that subscripts apply
+to array elements.  Thus tt(${${LPAR()A)tt(RPAR())var(name)tt(}[1]})
+yields the full value of var(name) when var(name) is scalar.
+
+This assigns an array parameter with `tt(${)...tt(=)...tt(})',
 `tt(${)...tt(:=)...tt(})' or `tt(${)...tt(::=)...tt(})'.
-If this flag is repeated (as in `tt(AA)'), create an associative
+If this flag is repeated (as in `tt(AA)'), assigns an associative
 array parameter.  Assignment is made before sorting or padding;
 if field splitting is active, the var(word) part is split before
 assignment.  The var(name) part may be a subscripted range for
-ordinary arrays; the var(word) part em(must) be converted to
-an array, for example by using `tt(${(AA)=)var(name)tt(=)...tt(})'
-to activate field splitting, when creating an associative array.
+ordinary arrays; when assigning an associative array, the var(word)
+part em(must) be converted to an array, for example by using
+`tt(${(AA)=)var(name)tt(=)...tt(})' to activate field splitting.
+
+Surrounding context such as additional nesting or use of the value
+in a scalar assignment may cause the array to be joined back into
+a single string again.
 )
 item(tt(a))(
 Sort in array index order; when combined with `tt(O)' sort in reverse
@@ -1004,7 +1049,9 @@ If var(name) refers to an associative array, substitute the em(keys)
 (element names) rather than the values of the elements.  Used with
 subscripts (including ordinary arrays), force indices or keys to be
 substituted even if the subscript form refers to values.  However,
-this flag may not be combined with subscript ranges.
+this flag may not be combined with subscript ranges.  With the
+tt(KSH_ARRAYS) option a subscript `tt([*])' or `tt([@])' is needed
+to operate on the whole array, as usual.
 )
 item(tt(L))(
 Convert all letters in the result to lower case.
@@ -1359,7 +1406,8 @@ item(tt(B))(
 Include the index of the beginning of the match in the result.
 )
 item(tt(E))(
-Include the index of the end of the match in the result.
+Include the index one character past the end of the match in the result
+(note this is inconsistent with other uses of parameter index).
 )
 item(tt(M))(
 Include the matched portion in the result.
@@ -2064,6 +2112,7 @@ Matches either var(x) or var(y).
 This operator has lower precedence than any other.
 The `tt(|)' character
 must be within parentheses, to avoid interpretation as a pipeline.
+The alternatives are tried in order from left to right.
 )
 item(tt(^)var(x))(
 (Requires tt(EXTENDED_GLOB) to be set.)
diff --git a/Doc/Zsh/grammar.yo b/Doc/Zsh/grammar.yo
index 2a76964f3..463ac8831 100644
--- a/Doc/Zsh/grammar.yo
+++ b/Doc/Zsh/grammar.yo
@@ -461,6 +461,9 @@ item(tt(select) var(name) [ tt(in) var(word) ... var(term) ] var(sublist))(
 where var(term) is at least one newline or tt(;).
 A short form of tt(select).
 )
+item(tt(function) var(word) ... [ tt(+LPAR()+RPAR()) ] [ var(term) ] var(sublist))(
+This is a short form of tt(function).
+)
 enditem()
 texinode(Reserved Words)(Errors)(Alternate Forms For Complex Commands)(Shell Grammar)
 sect(Reserved Words)
diff --git a/Doc/Zsh/jobs.yo b/Doc/Zsh/jobs.yo
index d9395010a..6262dd244 100644
--- a/Doc/Zsh/jobs.yo
+++ b/Doc/Zsh/jobs.yo
@@ -74,8 +74,8 @@ or by one of the following:
 
 startsitem()
 sitem(tt(%)var(number))(The job with the given number.)
-sitem(tt(%)var(string))(Any job whose command line begins with var(string).)
-sitem(tt(%?)var(string))(Any job whose command line contains var(string).)
+sitem(tt(%)var(string))(The last job whose command line begins with var(string).)
+sitem(tt(%?)var(string))(The last job whose command line contains var(string).)
 sitem(tt(%%))(Current job.)
 sitem(tt(%PLUS()))(Equivalent to `tt(%%)'.)
 sitem(tt(%-))(Previous job.)
diff --git a/Doc/Zsh/mod_complist.yo b/Doc/Zsh/mod_complist.yo
index 31aa656f1..463486ff9 100644
--- a/Doc/Zsh/mod_complist.yo
+++ b/Doc/Zsh/mod_complist.yo
@@ -134,7 +134,10 @@ matches beginning with `tt(m)' in groups whose names  begin with
 
 Note also that all patterns are tried in the order in which they
 appear in the parameter value until the first one matches which is
-then used.
+then used.  Patterns may be matched against completions, descriptions
+(possibly with spaces appended for padding), or lines consisting of a
+completion followed by a description.  For consistent coloring it may be
+necessary to use more than one pattern or a pattern with backreferences.
 
 When printing a match, the code prints the value of tt(lc), the value
 for the file-type or the last matching specification with a `tt(*)',
diff --git a/Doc/Zsh/mod_curses.yo b/Doc/Zsh/mod_curses.yo
index 72dc4094a..6e4831abe 100644
--- a/Doc/Zsh/mod_curses.yo
+++ b/Doc/Zsh/mod_curses.yo
@@ -27,7 +27,8 @@ xitem(tt(zcurses) tt(scroll) var(targetwin) [ tt(on) | tt(off) | [tt(+)|tt(-)]va
 xitem(tt(zcurses) tt(input) var(targetwin) [ var(param) [ var(kparam) [ var(mparam) ] ] ])
 xitem(tt(zcurses) tt(mouse) [ tt(delay) var(num) | [tt(+)|tt(-)]tt(motion) ])
 xitem(tt(zcurses) tt(timeout) var(targetwin) var(intval))
-item(tt(zcurses) tt(querychar) var(targetwin) [ var(param) ])(
+xitem(tt(zcurses) tt(querychar) var(targetwin) [ var(param) ])
+item(tt(zcurses) tt(resize) var(height) var(width) [ tt(endwin) | tt(nosave) | tt(endwin_nosave) ])(
 Manipulate curses windows.  All uses of this command should be
 bracketed by `tt(zcurses init)' to initialise use of curses, and
 `tt(zcurses end)' to end it; omitting `tt(zcurses end)' can cause
@@ -211,6 +212,21 @@ second is the color pair in the usual var(fg_col)tt(/)var(bg_col)
 notation, or tt(0) if color is not supported.  Any attributes other than
 color that apply to the character, as set with the subcommand tt(attr),
 appear as additional elements.
+
+The subcommand tt(resize) resizes tt(stdscr) and all windows to given
+dimensions (windows that stick out from the new dimensions are resized
+down). The underlying curses extension (tt(resize_term call)) can be
+unavailable. To verify, zeroes can be used for var(height) and
+var(width). If the result of the subcommand is tt(0), resize_term is
+available (tt(2) otherwise). Tests show that resizing can be normally
+accomplished by calling tt(zcurses end) and tt(zcurses refresh). The
+tt(resize) subcommand is provided for versatility. Multiple system
+configurations have been checked and tt(zcurses end) and tt(zcurses
+refresh) are still needed for correct terminal state after resize. To
+invoke them with tt(resize), use var(endwin) argument.  Using
+var(nosave) argument will cause new terminal state to not be saved
+internally by tt(zcurses). This is also provided for versatility and
+should normally be not needed.
 )
 enditem()
 
diff --git a/Doc/Zsh/mod_db_gdbm.yo b/Doc/Zsh/mod_db_gdbm.yo
index 90974297c..699e9ab93 100644
--- a/Doc/Zsh/mod_db_gdbm.yo
+++ b/Doc/Zsh/mod_db_gdbm.yo
@@ -43,6 +43,17 @@ local scope (function) ends.  Note that a readonly parameter may not be
 explicitly unset, so the only way to unset a global parameter created with
 `tt(ztie -r)' is to use `tt(zuntie -u)'.
 )
+findex(zgdbmpath)
+cindex(database file path, reading)
+item(tt(zgdbmpath) var(parametername))(
+Put path to database file assigned to var(parametername) into tt(REPLY)
+scalar.
+)
+findex(zgdbm_tied)
+cindex(database tied arrays, enumerating)
+item(tt(zgdbm_tied))(
+Array holding names of all tied parameters.
+)
 enditem()
 
 The fields of an associative array tied to GDBM are neither cached nor
diff --git a/Doc/Zsh/mod_parameter.yo b/Doc/Zsh/mod_parameter.yo
index 3d260f8e9..942e4c5b6 100644
--- a/Doc/Zsh/mod_parameter.yo
+++ b/Doc/Zsh/mod_parameter.yo
@@ -37,6 +37,30 @@ vindex(dis_functions)
 item(tt(dis_functions))(
 Like tt(functions) but for disabled functions.
 )
+vindex(functions_source)
+item(tt(functions_source))(
+This readonly associative array maps names of enabled functions to the
+name of the file containing the source of the function.
+
+For an autoloaded function that has already been loaded, or marked for
+autoload with an absolute path, or that has had its path resolved with
+`tt(functions -r)', this is the file found for autoloading, resolved
+to an absolute path.
+
+For a function defined within the body of a script or sourced file,
+this is the name of that file.  In this case, this is the exact path
+originally used to that file, which may be a relative path.
+
+For any other function, including any defined at an interactive prompt or
+an autoload function whose path has not yet been resolved, this is
+the empty string.  However, the hash element is reported as defined
+just so long as the function is present:  the keys to this hash are
+the same as those to tt($funcions).
+)
+vindex(dis_functions_source)
+item(tt(dis_functions_source))(
+Like tt(functions_source) but for disabled functions.
+)
 vindex(builtins)
 item(tt(builtins))(
 This associative array gives information about the builtin commands
@@ -202,10 +226,13 @@ defined.  The line number is the line where the `tt(function) var(name)'
 or `var(name) tt(LPAR()RPAR())' started.  In the case of an autoloaded
 function  the line number is reported as zero.
 The format of each element is var(filename)tt(:)var(lineno).
+
 For functions autoloaded from a file in native zsh format, where only the
 body of the function occurs in the file, or for files that have been
 executed by the tt(source) or `tt(.)' builtins, the trace information is
-shown as var(filename)tt(:)var(0), since the entire file is the definition.
+shown as var(filename)tt(:)var(0), since the entire file is the
+definition.  The source file name is resolved to an absolute path when
+the function is loaded or the path to it otherwise resolved.
 
 Most users will be interested in the information in the
 tt(funcfiletrace) array instead.
diff --git a/Doc/Zsh/mod_sched.yo b/Doc/Zsh/mod_sched.yo
index 1350b9e4c..30b08a3ff 100644
--- a/Doc/Zsh/mod_sched.yo
+++ b/Doc/Zsh/mod_sched.yo
@@ -40,6 +40,11 @@ afterwards.  This should be used with any scheduled event that produces
 visible output to the terminal; it is not needed, for example, with
 output that updates a terminal emulator's title bar.
 
+To effect changes to the editor buffer when an event executes, use the
+`tt(zle)' command with no arguments to test whether the editor is active,
+and if it is, then use `tt(zle )var(widget)' to access the editor via
+the named var(widget).
+
 The tt(sched) builtin is not made available by default when the shell
 starts in a mode emulating another shell.  It can be made available
 with the command `tt(zmodload -F zsh/sched b:sched)'.
diff --git a/Doc/Zsh/mod_zleparameter.yo b/Doc/Zsh/mod_zleparameter.yo
index 03d504724..76d23ba2a 100644
--- a/Doc/Zsh/mod_zleparameter.yo
+++ b/Doc/Zsh/mod_zleparameter.yo
@@ -15,14 +15,16 @@ This array contains the names of the keymaps currently defined.
 )
 vindex(widgets)
 item(tt(widgets))(
-This associative array contains one entry per widget defined. The name 
+This associative array contains one entry per widget. The name 
 of the widget is the key and the value gives information about the
-widget. It is either the string `tt(builtin)' for builtin widgets, a
-string of the form `tt(user:)var(name)' for user-defined widgets,
-where var(name) is the name of the shell function implementing the
-widget, or it is a string of the form
-`tt(completion:)var(type)tt(:)var(name)', for completion widgets. In
-the last case var(type) is the name of the builtin widgets the
+widget. It is either
+  the string `tt(builtin)' for builtin widgets,
+  a string of the form `tt(user:)var(name)' for user-defined widgets,
+    where var(name) is the name of the shell function implementing the widget,
+  a string of the form `tt(completion:)var(type)tt(:)var(name)'
+    for completion widgets,
+  or a null value if the widget is not yet fully defined.
+In the penultimate case, var(type) is the name of the builtin widget the
 completion widget imitates in its behavior and var(name) is the name
 of the shell function implementing the completion widget.
 )
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 2fce10780..cc6ae2a47 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -395,8 +395,9 @@ pindex(RECEXACT)
 pindex(NORECEXACT)
 cindex(completion, exact matches)
 item(tt(REC_EXACT) (tt(-S)))(
-In completion, recognize exact matches even
-if they are ambiguous.
+If the string on the command line exactly matches one of the possible
+completions, it is accepted, even if there is another completion (i.e. that
+string with something else added) that also matches.
 )
 enditem()
 
@@ -722,9 +723,10 @@ pindex(REMATCHPCRE)
 pindex(NOREMATCHPCRE)
 cindex(regexp, PCRE)
 cindex(PCRE, regexp)
-item(tt(REMATCH_PCRE) <Z>)(
+item(tt(REMATCH_PCRE))(
 If set, regular expression matching with the tt(=~) operator will use
-Perl-Compatible Regular Expressions from the PCRE library, if available.
+Perl-Compatible Regular Expressions from the PCRE library.
+(The tt(zsh/pcre) module must be available.)
 If not set, regular expressions will use the extended regexp syntax
 provided by the system libraries.
 )
@@ -768,6 +770,37 @@ global from within a function using tt(typeset -g) do not cause a warning.
 Note that there is no warning when a local parameter is assigned to in
 a nested function, which may also indicate an error.
 )
+pindex(WARN_NESTED_VAR)
+pindex(NO_WARN_NESTED_VAR)
+pindex(WARNNESTEDVAR)
+pindex(NO_WARNNESTEDVAR)
+cindex(parameters, warning when setting in enclosing scope)
+item(tt(WARN_NESTED_VAR))(
+Print a warning message when an existing parameter from an
+enclosing function scope, or global, is set in a function
+by an assignment or in math context.  Assignment to shell
+special parameters does not cause a warning.  This is the companion
+to tt(WARN_CREATE_GLOBAL) as in this case the warning is only
+printed when a parameter is em(not) created.  Where possible,
+use of tt(typeset -g) to set the parameter suppresses the error,
+but note that this needs to be used every time the parameter is set.
+To restrict the effect of this option to a single function scope,
+use `tt(functions -W)'.
+
+For example, the following code produces a warning for the assignment
+inside the function tt(nested) as that overrides the value within
+tt(toplevel)
+
+example(toplevel+LPAR()RPAR() {
+  local foo="in fn"
+  nested
+}
+nested+LPAR()RPAR() {
+     foo="in nested"
+}
+setopt warn_nested_var
+toplevel)
+)
 enditem()
 
 subsect(History)
@@ -1539,6 +1572,36 @@ enditem()
 
 subsect(Scripts and Functions)
 startitem()
+pindex(ALIAS_FUNC_DEF)
+pindex(NO_ALIAS_FUNC_DEF)
+pindex(ALIASFUNCDEF)
+pindex(NOALIASFUNCDEF)
+cindex(functions, defining with expanded aliases)
+cindex(aliases, expanding in function definition)
+item(tt(ALIAS_FUNC_DEF) <S>)(
+By default, zsh does not allow the definition of functions using
+the `var(name) tt(LPAR()RPAR())' syntax if var(name) was expanded as an
+alias: this causes an error.  This is usually the desired behaviour, as
+otherwise the combination of an alias and a function based on the same
+definition can easily cause problems.
+
+When this option is set, aliases can be used for defining functions.
+
+For example, consider the following definitions as they might
+occur in a startup file.
+
+example(alias foo=bar
+foo+LPAR()RPAR() {
+  print This probably does not do what you expect.
+})
+
+Here, tt(foo) is expanded as an alias to tt(bar) before the
+tt(LPAR()RPAR()) is encountered, so the function defined would be named
+tt(bar).  By default this is instead an error in native mode.  Note that
+quoting any part of the function name, or using the keyword
+tt(function), avoids the problem, so is recommended when the function
+name can also be an alias.
+)
 pindex(C_BASES)
 pindex(NO_C_BASES)
 pindex(CBASES)
@@ -1598,7 +1661,7 @@ skipped.  The option is restored after the trap exits.
 
 Exiting due to tt(ERR_EXIT) has certain interactions with asynchronous
 jobs noted in
-ifzman(the section JOBS in in zmanref(zshmisc))\
+ifzman(the section JOBS in zmanref(zshmisc))\
 ifnzman(noderef(Jobs & Signals)).
 )
 pindex(ERR_RETURN)
@@ -1929,7 +1992,8 @@ Emulate bf(ksh) array handling as closely as possible.  If this option
 is set, array elements are numbered from zero, an array parameter
 without subscript refers to the first element instead of the whole array,
 and braces are required to delimit a subscript (`tt(${path[2]})' rather
-than just `tt($path[2])').
+than just `tt($path[2])') or to apply modifiers to any parameter
+(`tt(${PWD:h})' rather than `tt($PWD:h)').
 )
 pindex(KSH_AUTOLOAD)
 pindex(NO_KSH_AUTOLOAD)
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index d23c459a0..905e92d22 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -1059,7 +1059,8 @@ If the tt(ENV) environment variable is set when zsh is invoked as tt(sh)
 or tt(ksh), tt($ENV) is sourced after the profile scripts.  The value of
 tt(ENV) is subjected to parameter expansion, command substitution, and
 arithmetic expansion before being interpreted as a pathname.  Note that
-tt(ENV) is em(not) used unless zsh is emulating bf(sh) or bf(ksh).
+tt(ENV) is em(not) used unless the shell is interactive and zsh is
+emulating bf(sh) or bf(ksh).
 )
 vindex(FCEDIT)
 item(tt(FCEDIT))(
@@ -1390,6 +1391,20 @@ item(tt(READNULLCMD) <S>)(
 The command name to assume if a single input redirection
 is specified with no command.  Defaults to tt(more).
 )
+vindex(REPORTMEMORY)
+item(tt(REPORTMEMORY))(
+If nonnegative, commands whose maximum resident set size (roughly
+speaking, main memory usage) in megabytes is greater than this
+value have timing statistics reported.  The format used to output
+statistics is the value of the tt(TIMEFMT) parameter, which is the same
+as for the tt(REPORTTIME) variable and the tt(time) builtin; note that
+by default this does not output memory usage.  Appending
+tt(" max RSS %M") to the value of tt(TIMEFMT) causes it to output the
+value that triggered the report.  If tt(REPORTTIME) is also in use,
+at most a single report is printed for both triggers.  This feature
+requires the tt(getrusage+LPAR()RPAR()) system call, commonly supported by
+modern Unix-like systems.
+)
 vindex(REPORTTIME)
 item(tt(REPORTTIME))(
 If nonnegative, commands whose combined user and system execution times
@@ -1443,6 +1458,14 @@ The prompt used for spelling correction.  The sequence
 `tt(%R)' expands to the string which presumably needs spelling
 correction, and `tt(%r)' expands to the proposed correction.
 All other prompt escapes are also allowed.
+
+The actions available at the prompt are tt([nyae]):
+startsitem()
+sitem(tt(n) +LPAR()`no'+RPAR() +LPAR()default+RPAR())(Discard the correction and run the command.)
+sitem(tt(y) +LPAR()`yes'+RPAR())(Make the correction and run the command.)
+sitem(tt(a) +LPAR()`abort'+RPAR())(Discard the entire command line without running it.)
+sitem(tt(e) +LPAR()`edit'+RPAR())(Resume editing the command line.)
+endsitem()
 )
 vindex(STTY)
 item(tt(STTY))(
@@ -1470,10 +1493,20 @@ take effect.
 )
 vindex(TERMINFO)
 item(tt(TERMINFO) <S>)(
-A reference to a compiled description of the terminal, used by the
-`terminfo' library when the system has it; see manref(terminfo)(5).
-If set, this causes the shell to reinitialise the terminal, making
-the workaround `tt(TERM=$TERM)' unnecessary.
+A reference to your terminfo database, used by the `terminfo' library when the
+system has it; see manref(terminfo)(5).
+If set, this causes the shell to reinitialise the terminal, making the
+workaround `tt(TERM=$TERM)' unnecessary.
+)
+vindex(TERMINFO_DIRS)
+item(tt(TERMINFO_DIRS) <S>)(
+A colon-seprarated list of terminfo databases, used by the `terminfo' library
+when the system has it; see manref(terminfo)(5). This variable is only
+used by certain terminal libraries, in particular ncurses; see
+manref(terminfo)(5) to check support on your system.  If set, this
+causes the shell to reinitialise the terminal, making the workaround
+`tt(TERM=$TERM)' unnecessary.  Note that unlike other colon-separated
+arrays this is not tied to a zsh array.
 )
 vindex(TIMEFMT)
 item(tt(TIMEFMT))(
@@ -1532,6 +1565,15 @@ A pathname prefix which the shell will use for all temporary files.
 Note that this should include an initial part for the file name as
 well as any directory names.  The default is `tt(/tmp/zsh)'.
 )
+vindex(TMPSUFFIX)
+item(tt(TMPSUFFIX))(
+A filename suffix which the shell will use for temporary files created
+by process substitutions (e.g., `tt(=LPAR()var(list)RPAR())').
+Note that the value should include a leading dot `tt(.)' if intended
+to be interpreted as a file extension.  The default is not to append
+any suffix, thus this parameter should be assigned only when needed
+and then unset again.
+)
 vindex(watch)
 vindex(WATCH)
 item(tt(watch) <S> <Z> (tt(WATCH) <S>))(
diff --git a/Doc/Zsh/restricted.yo b/Doc/Zsh/restricted.yo
index b56b50c6f..6cf9b36b5 100644
--- a/Doc/Zsh/restricted.yo
+++ b/Doc/Zsh/restricted.yo
@@ -10,11 +10,11 @@ restricted mode:
 
 startitemize()
 itemiz(changing directories with the tt(cd) builtin)
-itemiz(changing or unsetting the tt(PATH), tt(path), tt(MODULE_PATH),
-tt(module_path), tt(SHELL), tt(HISTFILE), tt(HISTSIZE), tt(GID), tt(EGID),
-tt(UID), tt(EUID), tt(USERNAME), tt(LD_LIBRARY_PATH),
-tt(LD_AOUT_LIBRARY_PATH), tt(LD_PRELOAD) and  tt(LD_AOUT_PRELOAD)
-parameters)
+itemiz(changing or unsetting the tt(EGID), tt(EUID), tt(GID),
+tt(HISTFILE), tt(HISTSIZE), tt(IFS), tt(LD_AOUT_LIBRARY_PATH),
+tt(LD_AOUT_PRELOAD), tt(LD_LIBRARY_PATH), tt(LD_PRELOAD),
+tt(MODULE_PATH), tt(module_path), tt(PATH), tt(path), tt(SHELL),
+tt(UID) and tt(USERNAME) parameters)
 itemiz(specifying command names containing tt(/))
 itemiz(specifying command pathnames using tt(hash))
 itemiz(redirecting output to files)
diff --git a/Doc/Zsh/roadmap.yo b/Doc/Zsh/roadmap.yo
index bd064e2b2..94ef74d1f 100644
--- a/Doc/Zsh/roadmap.yo
+++ b/Doc/Zsh/roadmap.yo
@@ -139,6 +139,9 @@ startitem()
 item(tt(**))(
 for matching over multiple directories
 )
+item(tt(|))(
+for matching either of two alternatives
+)
 item(tt(~), tt(^))(
 the ability to exclude patterns from matching when the tt(EXTENDED_GLOB)
 option is set
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index 80d3f39d8..b65e3be64 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -479,6 +479,13 @@ tt(kill) for indicating that text has been killed into the cutbuffer.
 When repeatedly invoking a kill widget, text is appended to the cutbuffer
 instead of replacing it, but when wrapping such widgets, it is necessary
 to call `tt(zle -f kill)' to retain this effect.
+
+tt(vichange) for indicating that the widget represents a vi change that
+can be repeated as a whole with `tt(vi-repeat-change)'. The flag should be set
+early in the function before inspecting the value of tt(NUMERIC) or invoking
+other widgets. This has no effect for a widget invoked from insert mode. If
+insert mode is active when the widget finishes, the change extends until next
+returning to command mode.
 )
 cindex(completion widgets, creating)
 item(tt(-C) var(widget) var(completion-widget) var(function))(
@@ -857,6 +864,12 @@ vindex(KEYS)
 item(tt(KEYS) (scalar))(
 The keys typed to invoke this widget, as a literal string; read-only.
 )
+vindex(KEYS_QUEUED_COUNT)
+item(tt(KEYS_QUEUED_COUNT) (integer))(
+The number of bytes pushed back to the input queue and therefore
+available for reading immediately before any I/O is done; read-only.
+See also tt(PENDING); the two values are distinct.
+)
 vindex(killring)
 item(tt(killring) (array))(
 The array of previously killed items, with the most recently killed first.
@@ -896,7 +909,7 @@ vindex(MARK)
 item(tt(MARK) (integer))(
 Like tt(CURSOR), but for the mark. With vi-mode operators that wait for
 a movement command to select a region of text, setting tt(MARK) allows
-the selection to extend in both directions from the the initial cursor
+the selection to extend in both directions from the initial cursor
 position.
 )
 vindex(NUMERIC)
@@ -912,7 +925,8 @@ item(tt(PENDING) (integer))(
 The number of bytes pending for input, i.e. the number of bytes which have
 already been typed and can immediately be read. On systems where the shell
 is not able to get this information, this parameter will always have a
-value of zero.  Read-only.
+value of zero.  Read-only.  See also tt(KEYS_QUEUED_COUNT); the two
+values are distinct.
 )
 vindex(PREBUFFER)
 item(tt(PREBUFFER) (scalar))(
@@ -991,6 +1005,12 @@ and tt(zle_highlight); see
 ifzman(the section CHARACTER HIGHLIGHTING below)\
 ifnzman(noderef(Character Highlighting)) for details.
 )
+vindex(registers)
+item(tt(registers) (associative array))(
+The contents of each of the vi register buffers. These are
+typically set using tt(vi-set-buffer) followed by a delete, change or
+yank command.
+)
 vindex(SUFFIX_ACTIVE)
 vindex(SUFFIX_START)
 vindex(SUFFIX_END)
@@ -1781,6 +1801,13 @@ tindex(down-case-word)
 item(tt(down-case-word) (tt(ESC-L ESC-l)) (unbound) (unbound))(
 Convert the current word to all lowercase and move past it.
 )
+tindex(vi-down-case)
+item(tt(vi-down-case) (unbound) (tt(gu)) (unbound))(
+Read a movement command from the keyboard, and convert all characters
+from the cursor position to the endpoint of the movement to lowercase.
+If the movement command is tt(vi-down-case), swap the case of all
+characters on the current line.
+)
 tindex(kill-word)
 item(tt(kill-word) (tt(ESC-D ESC-d)) (unbound) (unbound))(
 Kill the current word.
@@ -1941,11 +1968,27 @@ to the left.
 tindex(transpose-words)
 item(tt(transpose-words) (tt(ESC-T ESC-t)) (unbound) (unbound))(
 Exchange the current word with the one before it.
+
+With a positive numeric argument em(N), the word around the cursor, or
+following it if the cursor is between words, is transposed with the
+preceding em(N) words.  The cursor is put at the end of the resulting
+group of words.
+
+With a negative numeric argument em(-N), the effect is the same as using
+a positive argument em(N) except that the original cursor position is
+retained, regardless of how the words are rearranged.
 )
 tindex(vi-unindent)
 item(tt(vi-unindent) (unbound) (tt(<)) (unbound))(
 Unindent a number of lines.
 )
+tindex(vi-up-case)
+item(tt(vi-up-case) (unbound) (tt(gU)) (unbound))(
+Read a movement command from the keyboard, and convert all characters
+from the cursor position to the endpoint of the movement to lowercase.
+If the movement command is tt(vi-up-case), swap the case of all
+characters on the current line.
+)
 tindex(up-case-word)
 item(tt(up-case-word) (tt(ESC-U ESC-u)) (unbound) (unbound))(
 Convert the current word to all caps and move past it.
@@ -2152,10 +2195,18 @@ item(tt(bracketed-paste))(
 This widget is invoked when text is pasted to the terminal emulator. It
 is not intended to be bound to actual keys but instead to the special
 sequence generated by the terminal emulator when text is pasted.
+
+When invoked interactively, the pasted text is inserted to the buffer
+and placed in the cutbuffer.
 If a numeric argument is given, shell quoting will be applied to the
-pasted text before it is inserted. When called from a widget function,
-an argument can be given to specify a variable to which pasted text is
-assigned.
+pasted text before it is inserted.
+
+When a named buffer is specified with tt(vi-set-buffer) (tt("x)),
+the pasted text is stored in that named buffer but not inserted.
+
+When called from a widget function as `tt(bracketed-paste) var(name)`, the
+pasted text is assigned to the variable var(name) and no other processing is
+done.
 
 See also the tt(zle_bracketed_paste) parameter.
 )
@@ -2193,8 +2244,12 @@ point and mark are not swapped.
 )
 tindex(execute-named-cmd)
 item(tt(execute-named-cmd) (tt(ESC-x)) (tt(:)) (unbound))(
-Read the name of an editor command and
-execute it.  A restricted set of editing functions is available in the
+Read the name of an editor command and execute it.  Aliasing this
+widget with `tt(zle -A)' or replacing it with `tt(zle -N)' has no
+effect when interpreting key bindings, but `tt(zle execute-named-cmd)'
+will invoke such an alias or replacement.
+
+A restricted set of editing functions is available in the
 mini-buffer.  Keys are looked up in the special
 tt(command) keymap, and if not found there in the main keymap.
 An interrupt signal, as defined by the stty setting, will
@@ -2235,7 +2290,8 @@ tindex(execute-last-named-cmd)
 item(tt(execute-last-named-cmd) (tt(ESC-z)) (unbound) (unbound))(
 Redo the last function executed with tt(execute-named-cmd).
 
-Currently this command may not be redefined or called by name.
+Like tt(execute-named-cmd), this command may not be redefined,
+but it may be called by name.
 )
 tindex(get-line)
 item(tt(get-line) (tt(ESC-G ESC-g)) (unbound) (unbound))(
@@ -2381,7 +2437,7 @@ concerned replaces the previous contents of the specified buffer. If
 a named buffer is specified using a capital, the newly cut text is
 appended to the buffer instead of overwriting it. When using the tt("_)
 buffer, nothing happens. This can be useful for deleting text without
-affecting the normal registers.
+affecting any buffers.
 
 If no buffer is specified for a cut or change command, tt("1) is used, and
 the contents of tt("1) to tt("8) are each shifted along one buffer;
@@ -2557,15 +2613,14 @@ When one of the incremental history search widgets is active, the
 area of the command line matched by the search string or pattern.
 )
 item(tt(region))(
-The region between the cursor (point) and the mark as set with
-tt(set-mark-command).  The region is only highlighted if it is active,
-which is the case if tt(set-mark-command) or tt(exchange-point-and-mark)
-has been called and the line has not been subsequently modified.  The
-region can be deactivated by calling tt(set-mark-command) with a
-negative numeric argument, or reactivated by calling
-tt(exchange-point-and-mark) with a zero numeric argument.  Note
-that whether or not the region is active has no effect on its
-use within widgets, it simply determines whether it is highlighted.
+The currently selected text. In emacs terminology, this is referred to as
+the region and is bounded by the cursor (point) and the mark. The region
+is only highlighted if it is active, which is the case after the mark
+is modified with tt(set-mark-command) or tt(exchange-point-and-mark).
+Note that whether or not the region is active has no effect on its
+use within emacs style widgets, it simply determines whether it is
+highlighted. In vi mode, the region corresponds to selected text in
+visual mode.
 )
 cindex(special characters, highlighting)
 cindex(highlighting, special characters)
diff --git a/Etc/BUGS b/Etc/BUGS
index 054727167..008e337ca 100644
--- a/Etc/BUGS
+++ b/Etc/BUGS
@@ -2,40 +2,6 @@
 KNOWN BUGS IN ZSH
 -----------------
 
-On some terminals, display of lines with exactly 80 characters is
-problematic.  zsh assumes that the terminal does not print an extra
-newline in this case, but some terminals (e.g. aixterm) do.
-------------------------------------------------------------------------
-When interrupting code like the following with ^C:
-  while true; do
-    sh -c '...'
-  done
-if the `sh' is executing, zsh does not know that the sh received a ^C and
-continues with the next iteration.  This happens for any program which
-handles the interrupt, then exits after tidying up; it does not happen for
-zsh, which exits directly from the signal handler.  The workaround is to
-use ^Z which forks the shell and makes the loop a separate job, then kill
-the suspended loop.
-------------------------------------------------------------------------
-If you suspend "man", zle seems to get into cooked mode.  It works ok
-for plain "less".
-It is not specific neither to man nor to zsh.
-E.g. call the following program foo:
-#include <sys/wait.h>
-#include <unistd.h>
-
-int main(int argc, char *argv[])
-{
-    int status;
-
-    if (!fork())	/* child */
-	execvp(argv[1], argv + 1);
-    else		/* parent */
-	wait(&status);
-}
-Then if you suspend
-% foo less something
-from zsh/bash, zle/readline gets into cooked mode.
 ------------------------------------------------------------------------
 The pattern %?* matches names beginning with %? instead of names with at
 least two characters beginning with %. This is a hack to allow %?foo job
@@ -46,3 +12,11 @@ the nonomatch and nullglob options.
 ------------------------------------------------------------------------
 It is currently impossible to time builtins.
 ------------------------------------------------------------------------
+40106: The comp* completion-related builtins (compadd, compset, etc) are
+run with $_comp_options in effect, rather than the user's options.
+------------------------------------------------------------------------
+users/20807: vcs_info quilt 'addon' mode: hook lookup context specifies
+the underlying VCS but not whether quilt is used.
+
+Workaround: test (( ${+funcstack[(r)VCS_INFO_quilt]} )).
+------------------------------------------------------------------------
diff --git a/Etc/ChangeLog-4.3 b/Etc/ChangeLog-4.3
index 1be618b48..6d85e40af 100644
--- a/Etc/ChangeLog-4.3
+++ b/Etc/ChangeLog-4.3
@@ -1182,7 +1182,7 @@
 
 2011-08-16  Wayne Davison  <wayned@users.sourceforge.net>
 
-	* 29650: Src/jobs.c: don't lose the the time info after a
+	* 29650: Src/jobs.c: don't lose the time info after a
 	suspend+restore.
 
 2011-08-15  Peter Stephenson  <p.w.stephenson@ntlworld.com>
diff --git a/Etc/FAQ.yo b/Etc/FAQ.yo
index e71dd5310..6b635ea3e 100644
--- a/Etc/FAQ.yo
+++ b/Etc/FAQ.yo
@@ -58,7 +58,7 @@ mydit(Archive-Name:) unix-faq/shell/zsh
 mydit(Last-Modified:) 2015/05/31
 mydit(Submitted-By:) email(coordinator@zsh.org (Peter Stephenson))
 mydit(Posting-Frequency:) Monthly
-mydit(Copyright:) (C) P.W. Stephenson, 1995--2015 (see end of document)
+mydit(Copyright:) (C) P.W. Stephenson, 1995--2016 (see end of document)
 )
 
 This document contains a list of frequently-asked (or otherwise
@@ -306,7 +306,7 @@ sect(On what machines will it run?)
 
 sect(What's the latest version?)
 
-  Zsh 5.2 is the latest production version.  For details of all the
+  Zsh 5.3.1 is the latest production version.  For details of all the
   changes, see the NEWS file in the source distribution.
 
   A beta of the next version is sometimes available.  Development of zsh is
diff --git a/Etc/zsh-development-guide b/Etc/zsh-development-guide
index c4aa1b243..ecbd3c081 100644
--- a/Etc/zsh-development-guide
+++ b/Etc/zsh-development-guide
@@ -327,22 +327,23 @@ in.
 
 The next pair are `features_' and `enables_' and deal with enabling module
 features.  Ensure you are familiar with the description of features under
-`zmodload -F'.  The function features_ takes an argument `char
-***featuresp'; *featuresp is to be set to a NULL-terminated array
-containing a list of all the features.  It should then return zero.
-It may return one to indicate features are not supported, but this is
-not recommended.  The function featuresarray conveniently interrogates
-the module's feature structures for all standard features; space
-is left for abstract features at the end of the array and the names
-must be added by the module.  Note that heap memory should
-be used for this (zhalloc, etc.) as memory for the features array is not
-freed; note also the pointers for the abstract features are not
-initialised so setting them is mandatory any time there are any present.
-
-A structure "struct features" should
-be used to contain all standard features as well as the number of
-abstract features (those only understood by the module itself).
-See below.
+`zmodload -F'.
+
+The function features_ takes an argument `char ***featuresp'; *featuresp
+is to be set to a NULL-terminated array containing a list of all the
+features.  It should then return zero.  It may return one to indicate
+features are not supported, but this is not recommended.  The function
+featuresarray conveniently interrogates the module's feature structures
+for all standard features; space is left for abstract features at the end
+of the array and the names must be added by the module.  Note that heap
+memory should be used for this (zhalloc, etc.) as memory for the features
+array is not freed; note also the pointers for the abstract features are
+not initialised so setting them is mandatory any time there are any
+present.
+
+A structure "struct features" should be used to contain all standard
+features as well as the number of abstract features (those only understood
+by the module itself).  See below.
 
 enables_ takes an argument `int **enablesp'.  If *enablesp is NULL, it
 should be set to an array of the same length as *featuresp without the
@@ -713,6 +714,24 @@ union looking like:
 The `type' field should be set to `MN_INTEGER' or `MN_FLOAT' and
 depending on its value either `u.l' or `u.d' contains the value.
 
+Widgets
+-------
+
+As of this writing, widgets are not managed by the features mechanism.
+Modules can add builtin widgets by calling `addzlefunction' as defined
+in Src/Zle/zle_thingy.c.  Typically this is called from the `boot_'
+routine.  Any widgets so added should be removed by `deletezlefunction'
+called from the `cleanup_' routine.
+
+Keymaps
+-------
+Keymaps are created with `newkeymap' and exposed for use with bindkey
+by `linkkeymap', both defined in Src/Zle/zle_keymap.c.  Typically the
+same name is used both to create and link the keymap.  As with widgets,
+there is currently no features mechanism for keymaps, and they should
+be initialized in the `boot_' function.  In `cleanup_', first remove
+linkage with `unlinkkeymap' and then discard with `deletehashtable'.
+
 Hooks
 -----
 
@@ -791,6 +810,34 @@ that are changed or called very often. These functions,
 structure defining the hook instead of the name and otherwise behave
 like their counterparts.
 
+The following hooks are defined by the standard set of modules and may be
+referenced by other modules.  Each has a corresponding macro name that
+points into the definition structure, to avoid repeating the hook names
+as strings.
+
+   zsh/main
+     after_trap           AFTERTRAPHOOK
+     before_trap          BEFORETRAPHOOK
+     exit                 EXITHOOK
+
+   zsh/complete
+     compctl_make       *  COMPCTLMAKEHOOK
+     compctl_cleanup    *  COMPCTLCLEANUPHOOK
+     insert_match          INSERTMATCHHOOK
+     comp_list_matches  *  COMPLISTMATCHESHOOK
+     menu_start            MENUSTARTHOOK
+
+   zsh/zle
+     before_complete    *  BEFORECOMPHOOK
+     complete           *  COMPLETEHOOK
+     after_complete     *  AFTERCOMPHOOK
+     accept_completion  *  ACCEPTCOMPHOOK
+     list_matches       *  LISTMATCHESHOOK
+     invalidate_list    *  INVALIDATELISTHOOK
+
+Hooks marked with "*" do not use the HOOKF_ALL flag and so are replaced if
+another module adds a function to the hook.  Use with caution.
+
 Wrappers
 --------
 
diff --git a/Functions/MIME/zsh-mime-handler b/Functions/MIME/zsh-mime-handler
index 24e5184fc..288a0796d 100644
--- a/Functions/MIME/zsh-mime-handler
+++ b/Functions/MIME/zsh-mime-handler
@@ -127,7 +127,7 @@ for pattern in $exec_asis; do
   files=(${dirpref}${~pattern})
   if [[ -n ${files[(r)$1]} ]]; then
     for pattern in $exec_never; do
-      [[ ${1:A} = ${~pattern} ]] && break 2
+      [[ ${1:P} = ${~pattern} ]] && break 2
     done
     if (( list )); then
       for (( i = 1; i <= $#; i++ )); do
diff --git a/Functions/Math/.distfiles b/Functions/Math/.distfiles
new file mode 100644
index 000000000..f03668b3a
--- /dev/null
+++ b/Functions/Math/.distfiles
@@ -0,0 +1,2 @@
+DISTFILES_SRC='
+'
diff --git a/Functions/Math/zmathfunc b/Functions/Math/zmathfunc
new file mode 100644
index 000000000..4ff40700d
--- /dev/null
+++ b/Functions/Math/zmathfunc
@@ -0,0 +1,34 @@
+#autoload
+
+zsh_math_func_min() {
+  local result=$1
+  shift
+  local arg
+  for arg ; do
+    (( $arg < result )) && result=$arg
+  done
+  (( result )) # return
+}
+functions -M min 1 -1 zsh_math_func_min # at least one argument
+
+zsh_math_func_max() {
+  local result=$1
+  shift
+  local arg
+  for arg ; do
+    (( $arg > result )) && result=$arg
+  done
+  (( result )) # return
+}
+functions -M max 1 -1 zsh_math_func_max # at least one argument
+
+zsh_math_func_sum() {
+  local sum
+  local arg
+  for arg ; do
+    (( sum += $arg ))
+  done
+  (( sum ))
+}
+functions -M sum 0 -1 zsh_math_func_sum
+
diff --git a/Functions/Misc/add-zle-hook-widget b/Functions/Misc/add-zle-hook-widget
new file mode 100644
index 000000000..d8a3950fb
--- /dev/null
+++ b/Functions/Misc/add-zle-hook-widget
@@ -0,0 +1,186 @@
+# Add to HOOK the given WIDGET
+# 
+# HOOK is one of isearch-exit, isearch-update, line-pre-redraw, line-init,
+# line-finish, history-line-set, keymap-select (the zle- prefix is allowed
+# but not required).  If a widget corresponding to HOOK already exists, it
+# is preserved and called first in the new set of HOOK widgets.
+#
+# With -d, remove the WIDGET from the hook instead; deletes the hook
+# linkage if it is empty.
+#
+# -D behaves like -d, but pattern characters are active in WIDGET, so
+# any matching widget will be deleted from the hook.
+#
+# Without -d, if the WIDGET is not already defined, a function having the
+# same name is marked for autoload; -U is passed down to autoload if that
+# is given, as are -z and -k.  (This is harmless if the function is
+# already defined.)  The WIDGET is then created with zle -N.
+#
+# The -L option lists the hooks and their associated widgets.
+
+# This is probably more safeguarding than necessary
+zmodload -e zsh/zle || return 1
+{ zmodload zsh/parameter && zmodload zsh/zleparameter } || {
+    print -u2 "add-zle-hook-widget: Need parameter modules for zle hooks"
+    return 1
+}
+
+() { # Preserve caller global option settings
+
+emulate -L zsh
+
+# Setup - create the base functions for hook widgets that call the others
+
+local -a hooktypes=( zle-isearch-exit zle-isearch-update
+                     zle-line-pre-redraw zle-line-init zle-line-finish
+                     zle-history-line-set zle-keymap-select )
+# Stash in zstyle to make it global
+zstyle zle-hook types ${hooktypes#zle-}
+
+# Relying on multifuncdef option here
+function azhw:${^hooktypes} {
+    local -a hook_widgets
+    local hook
+    # Values of these styles look like number:name
+    # and we run them in number order
+    zstyle -a $WIDGET widgets hook_widgets
+    for hook in "${(@)${(@on)hook_widgets[@]}#<->:}"; do
+	if [[ "$hook" = user:* ]]; then
+	    # Preserve $WIDGET within the renamed widget
+	    zle "$hook" -N -- "$@"
+	else
+	    zle "$hook" -Nw -- "$@"
+	fi || return
+    done
+    return 0
+}
+
+# Redefine ourself with the setup left out
+
+function add-zle-hook-widget {
+    local -a hooktypes
+    zstyle -a zle-hook types hooktypes
+
+    # This part copied from add-zsh-hook
+    local usage="Usage: $funcstack[1] hook widgetname\nValid hooks are:\n  $hooktypes"
+
+    local opt
+    local -a autoopts
+    integer del list help
+
+    while getopts "dDhLUzk" opt; do
+	case $opt in
+	    (d)
+	    del=1
+	    ;;
+
+	    (D)
+	    del=2
+	    ;;
+
+	    (h)
+	    help=1
+	    ;;
+
+	    (L)
+	    list=1
+	    ;;
+
+	    ([Uzk])
+	    autoopts+=(-$opt)
+	    ;;
+
+	    (*)
+	    return 1
+	    ;;
+	esac
+    done
+    shift $(( OPTIND - 1 ))
+
+    1=${1#zle-}	# Strip prefix not stored in zle-hook types style
+
+    if (( list )); then
+	zstyle -L "zle-(${1:-${(@j:|:)hooktypes[@]}})" widgets
+	return $?
+    elif (( help || $# != 2 || ${hooktypes[(I)$1]} == 0 )); then
+	print -u$(( 2 - help )) $usage
+	return $(( 1 - help ))
+    fi
+
+    local -aU extant_hooks
+    local hook="zle-$1"
+    local fn="$2"
+
+    if (( del )); then
+        # delete, if hook is set
+	if zstyle -g extant_hooks "$hook" widgets; then
+	    if (( del == 2 )); then
+		set -A extant_hooks ${extant_hooks[@]:#(<->:|)${~fn}}
+	    else
+		set -A extant_hooks ${extant_hooks[@]:#(<->:|)$fn}
+	    fi
+            # unset if no remaining entries
+	    if (( ${#extant_hooks} )); then
+		zstyle "$hook" widgets "${extant_hooks[@]}"
+	    else
+		zstyle -d "$hook" widgets
+	    fi
+	fi
+    else
+	# Check whether attempting to add a widget named for the hook
+	if [[ "$fn" = "$hook" ]]; then
+	    if [[ -n "${widgets[$fn]}" ]]; then
+		print -u2 "$funcstack[1]: Cannot hook $fn to itself"
+		return 1
+	    fi
+	    # No point in building the array until another is added
+	    autoload "${autoopts[@]}" -- "$fn"
+	    zle -N "$fn"
+	    return 0
+	fi
+	integer i=${#options[ksharrays]}-2
+	zstyle -g extant_hooks "$hook" widgets
+        # Check for an existing widget, add it as the first hook
+	if [[ ${widgets[$hook]} != "user:azhw:$hook" ]]; then
+	    if [[ -n ${widgets[$hook]} ]]; then
+		zle -A "$hook" "${widgets[$hook]}"
+		extant_hooks=(0:"${widgets[$hook]}" "${extant_hooks[@]}")
+	    fi
+	    zle -N "$hook" azhw:"$hook"
+	fi
+	# Add new widget only if not already in the hook list
+	if [[ -z ${(M)extant_hooks[@]:#(<->:|)$fn} ]]; then
+       	    # no index and not already hooked
+            # assign largest existing index plus 1
+	    i=${${(On@)${(@M)extant_hooks[@]#<->:}%:}[i]}+1
+	else
+	    return 0
+	fi
+	extant_hooks+=("${i}:${fn}")
+	zstyle -- "$hook" widgets "${extant_hooks[@]}"
+	if [[ -z "${widgets[$fn]}" ]]; then
+	    autoload "${autoopts[@]}" -- "$fn"
+	    zle -N -- "$fn"
+	fi
+	if [[ -z "${widgets[$hook]}" ]]; then
+	    zle -N "$hook" azhw:"$hook"
+	fi
+    fi
+}
+
+} "$@" # Resume caller global options
+
+# Handle zsh autoloading conventions:
+# - "file" appears last in zsh_eval_context when "source"-ing
+# - "evalautofunc" appears with kshautoload set or autoload -k
+# - "loadautofunc" appears with kshautoload unset or autoload -z
+# - use of autoload +X cannot reliably be detected, use best guess
+case "$zsh_eval_context" in
+*file) ;;
+*evalautofunc) ;;
+*loadautofunc) add-zle-hook-widget "$@";;
+*) [[ -o kshautoload ]] || add-zle-hook-widget "$@";;
+esac
+# Note fallback here is equivalent to the usual best-guess used by
+# functions written for zsh before $zsh_eval_context was available
+# so this case-statement is backward-compatible.
diff --git a/Functions/Misc/add-zsh-hook b/Functions/Misc/add-zsh-hook
index fc39659ae..3bc952e2f 100644
--- a/Functions/Misc/add-zsh-hook
+++ b/Functions/Misc/add-zsh-hook
@@ -19,7 +19,7 @@ hooktypes=(
   chpwd precmd preexec periodic zshaddhistory zshexit
   zsh_directory_name
 )
-local usage="Usage: $0 hook function\nValid hooks are:\n  $hooktypes"
+local usage="Usage: add-zsh-hook hook function\nValid hooks are:\n  $hooktypes"
 
 local opt
 local -a autoopts
diff --git a/Functions/Misc/run-help-ip b/Functions/Misc/run-help-ip
index 740af52b5..8807f9ef1 100644
--- a/Functions/Misc/run-help-ip
+++ b/Functions/Misc/run-help-ip
@@ -19,8 +19,8 @@ while [[ $# != 0 && $1 == -* ]]; do
 done
 
 case $1 in
-    (addr*) man ip-address ;;
-    (addrlabel) man ip-addrlabel ;;
+    (addrl*) man ip-addrlabel ;;
+    (a*) man ip-address ;;
     (l2*) man ip-l2tp ;;
     (li*) man ip-link ;;
     (ma*) man ip-maddress ;;
diff --git a/Functions/Misc/zcalc b/Functions/Misc/zcalc
index 857007a94..480373345 100644
--- a/Functions/Misc/zcalc
+++ b/Functions/Misc/zcalc
@@ -94,24 +94,53 @@
 # sequentially just as if read automatically.
 
 emulate -L zsh
-setopt extendedglob
+setopt extendedglob typesetsilent
+
+zcalc_show_value() {
+  if [[ -n $_base ]]; then
+    print -- $(( $_base $1 ))
+  elif [[ $1 = *.* ]] || (( _outdigits )); then
+    # With normal output, ensure trailing "." doesn't get lost.
+    if [[ -z $_forms[_outform] || ($_outform -eq 1 && $1 = *.) ]]; then
+      print -- $(( $1 ))
+    else
+      printf "$_forms[_outform]\n" $_outdigits $1
+    fi
+  else
+    printf "%d\n" $1
+  fi
+}
 
 # For testing in ZLE functions.
 local ZCALC_ACTIVE=1
 
 # TODO: make local variables that shouldn't be visible in expressions
 # begin with _.
-local line ans base defbase forms match mbegin mend psvar optlist opt arg
+local _line ans _base _defbase _forms match mbegin mend
+local psvar _optlist _opt _arg _tmp
 local compcontext="-zcalc-line-"
-integer num outdigits outform=1 expression_mode
-local -a expressions
+integer _num _outdigits _outform=1 _expression_mode
+integer _rpn_mode _matched _show_stack _i _n
+integer _max_stack _push
+local -a _expressions stack
 
 # We use our own history file with an automatic pop on exit.
 history -ap "${ZDOTDIR:-$HOME}/.zcalc_history"
 
-forms=( '%2$g' '%.*g' '%.*f' '%.*E' '')
+_forms=( '%2$g' '%.*g' '%.*f' '%.*E' '')
 
-zmodload -i zsh/mathfunc 2>/dev/null
+local _mathfuncs
+if zmodload -i zsh/mathfunc 2>/dev/null; then
+  zmodload -P _mathfuncs -FL zsh/mathfunc
+  _mathfuncs="("${(j.|.)${_mathfuncs##f:}}")"
+fi
+local -A _userfuncs
+for _line in ${(f)"$(functions -M)"}; do
+  match=(${=_line})
+  # get minimum number of arguments
+  _userfuncs[${match[3]}]=${match[4]}
+done
+_line=
 autoload -Uz zmathfuncdef
 
 if (( ! ${+ZCALCPROMPT} )); then
@@ -127,111 +156,119 @@ if [[ -f "${ZDOTDIR:-$HOME}/.zcalcrc" ]]; then
 fi
 
 # Process command line
-while [[ -n $1 && $1 = -(|[#-]*|f|e) ]]; do
-  optlist=${1[2,-1]}
+while [[ -n $1 && $1 = -(|[#-]*|f|e|r(<->|)) ]]; do
+  _optlist=${1[2,-1]}
   shift
-  [[ $optlist = (|-) ]] && break
-  while [[ -n $optlist ]]; do
-    opt=${optlist[1]}
-    optlist=${optlist[2,-1]}
-    case $opt in
+  [[ $_optlist = (|-) ]] && break
+  while [[ -n $_optlist ]]; do
+    _opt=${_optlist[1]}
+    _optlist=${_optlist[2,-1]}
+    case $_opt in
       ('#') # Default base
-            if [[ -n $optlist ]]; then
-	       arg=$optlist
-	       optlist=
+            if [[ -n $_optlist ]]; then
+	       _arg=$_optlist
+	       _optlist=
 	    elif [[ -n $1 ]]; then
-	       arg=$1
+	       _arg=$1
 	       shift
 	    else
 	       print -- "-# requires an argument" >&2
 	       return 1
 	    fi
-	    if [[ $arg != (|\#)[[:digit:]]## ]]; then
+	    if [[ $_arg != (|\#)[[:digit:]]## ]]; then
 	      print -- "-# requires a decimal number as an argument" >&2
 	      return 1
 	    fi
-            defbase="[#${arg}]"
+            _defbase="[#${_arg}]"
 	    ;;
 	(f) # Force floating point operation
 	    setopt forcefloat
 	    ;;
         (e) # Arguments are expressions
-	    (( expression_mode = 1 ));
+	    (( _expression_mode = 1 ));
+	    ;;
+        (r) # RPN mode.
+	    (( _rpn_mode = 1 ))
+	    ZCALC_ACTIVE=rpn
+	    if [[ $_optlist = (#b)(<->)* ]]; then
+	       (( _show_stack = ${match[1]} ))
+               _optlist=${_optlist[${#match[1]}+1,-2]}
+	    fi
 	    ;;
     esac
   done
 done
 
-if (( expression_mode )); then
-  expressions=("$@")
+if (( _expression_mode )); then
+  _expressions=("$@")
   argv=()
 fi
 
-for (( num = 1; num <= $#; num++ )); do
+for (( _num = 1; _num <= $#; _num++ )); do
   # Make sure all arguments have been evaluated.
   # The `$' before the second argv forces string rather than numeric
   # substitution.
-  (( argv[$num] = $argv[$num] ))
-  print "$num> $argv[$num]"
+  (( argv[$_num] = $argv[$_num] ))
+  print "$_num> $argv[$_num]"
 done
 
-psvar[1]=$num
-local prev_line cont_prompt
-while (( expression_mode )) ||
-  vared -cehp "${cont_prompt}${ZCALCPROMPT}" line; do
-  if (( expression_mode )); then
-    (( ${#expressions} )) || break
-    line=$expressions[1]
-    shift expressions
+psvar[1]=$_num
+local _prev_line _cont_prompt
+while (( _expression_mode )) ||
+  vared -cehp "${_cont_prompt}${ZCALCPROMPT}" _line; do
+  if (( _expression_mode )); then
+    (( ${#_expressions} )) || break
+    _line=$_expressions[1]
+    shift _expressions
   fi
-  if [[ $line = (|*[^\\])('\\')#'\' ]]; then
-    prev_line+=$line[1,-2]
-    cont_prompt="..."
-    line=
+  if [[ $_line = (|*[^\\])('\\')#'\' ]]; then
+    _prev_line+=$_line[1,-2]
+    _cont_prompt="..."
+    _line=
     continue
   fi
-  line="$prev_line$line"
-  prev_line=
-  cont_prompt=
+  _line="$_prev_line$_line"
+  _prev_line=
+  _cont_prompt=
   # Test whether there are as many open as close
-  # parentheses in the line so far.
-  if [[ ${#line//[^\(]} -gt ${#line//[^\)]} ]]; then
-      prev_line+=$line
-      cont_prompt="..."
-      line=
+  # parentheses in the _line so far.
+  if [[ ${#_line//[^\(]} -gt ${#_line//[^\)]} ]]; then
+      _prev_line+=$_line
+      _cont_prompt="..."
+      _line=
       continue
   fi
-  [[ -z $line ]] && break
+  [[ -z $_line ]] && break
   # special cases
   # Set default base if `[#16]' or `[##16]' etc. on its own.
   # Unset it if `[#]' or `[##]'.
-  if [[ $line = (#b)[[:blank:]]#('[#'(\#|)((<->|)(|_|_<->))']')[[:blank:]]#(*) ]]; then
+  if [[ $_line = (#b)[[:blank:]]#('[#'(\#|)((<->|)(|_|_<->))']')[[:blank:]]#(*) ]]; then
     if [[ -z $match[6] ]]; then
       if [[ -z $match[3] ]]; then
-	defbase=
+	_defbase=
       else
-	defbase=$match[1]
+	_defbase=$match[1]
       fi
-      print -s -- $line
-      print -- $(( ${defbase} ans ))
-      line=
+      print -s -- $_line
+      print -- $(( ${_defbase} ans ))
+      _line=
       continue
     else
-      base=$match[1]
+      _base=$match[1]
     fi
   else
-    base=$defbase
+    _base=$_defbase
   fi
 
-  print -s -- $line
+  print -s -- $_line
 
-  line="${${line##[[:blank:]]#}%%[[:blank:]]#}"
-  case "$line" in
+  _line="${${_line##[[:blank:]]#}%%[[:blank:]]#}"
+  case "$_line" in
     # Escapes begin with a colon
     (:(\\|)\!*)
     # shell escape: handle completion's habit of quoting the !
-    eval ${line##:(\\|)\![[:blank:]]#}
-    line=
+    eval ${_line##:(\\|)\![[:blank:]]#}
+    _line=
     continue
     ;;
 
@@ -241,72 +278,196 @@ while (( expression_mode )) ||
     ;;
 
     ((:|)norm) # restore output format to default
-      outform=1
+      _outform=1
     ;;
 
     ((:|)sci[[:blank:]]#(#b)(<->)(#B))
-      outdigits=$match[1]
-      outform=2
+      _outdigits=$match[1]
+      _outform=2
     ;;
 
     ((:|)fix[[:blank:]]#(#b)(<->)(#B))
-      outdigits=$match[1]
-      outform=3
+      _outdigits=$match[1]
+      _outform=3
     ;;
 
     ((:|)eng[[:blank:]]#(#b)(<->)(#B))
-      outdigits=$match[1]
-      outform=4
+      _outdigits=$match[1]
+      _outform=4
     ;;
 
     (:raw)
-    outform=5
+    _outform=5
     ;;
 
     ((:|)local([[:blank:]]##*|))
-      eval $line
-      line=
+      eval ${_line##:}
+      _line=
       continue
     ;;
 
     ((function|:f(unc(tion|)|))[[:blank:]]##(#b)([^[:blank:]]##)(|[[:blank:]]##([^[:blank:]]*)))
       zmathfuncdef $match[1] $match[3]
-      line=
+      _userfuncs[$match[1]]=${$(functions -Mm $match[1])[4]}
+      _line=
       continue
     ;;
 
     (:*)
     print "Unrecognised escape"
-    line=
+    _line=
+    continue
+    ;;
+
+    (\$[[:IDENT:]]##)
+    # Display only, no calculation
+    _line=${_line##\$}
+    print -r -- ${(P)_line}
+    _line=
     continue
     ;;
 
     (*)
-      # Latest value is stored as a string, because it might be floating
-      # point or integer --- we don't know till after the evaluation, and
-      # arrays always store scalars anyway.
-      #
-      # Since it's a string, we'd better make sure we know which
-      # base it's in, so don't change that until we actually print it.
-      eval "ans=\$(( $line ))"
-      # on error $ans is not set; let user re-edit line
-      [[ -n $ans ]] || continue
-      argv[num++]=$ans
-      psvar[1]=$num
+      _line=${${_line##[[:blank:]]##}%%[[:blank:]]##}
+      if [[ _rpn_mode -ne 0 && $_line != '' ]]; then
+	_push=1
+	_matched=1
+	case $_line in
+	  (\<[[:IDENT:]]##)
+	  ans=${(P)${_line##\<}}
+	  ;;
+
+	  (\=|pop|\>[[:IDENT:]]#)
+	  if (( ${#stack} < 1 )); then
+	    print -r -- "${_line}: not enough values on stack" >&2
+	    _line=
+	    continue
+	  fi
+	  case $_line in
+	    (=)
+	    ans=${stack[1]}
+	    ;;
+	    (pop|\>)
+	    _push=0
+	    shift stack
+	    ;;
+	    (\>[[:IDENT:]]##)
+	    if [[ ${_line##\>} = (_*|stack|ans|PI|E) ]]; then
+	      print "${_line##\>}: reserved variable" >&2
+	      _line=
+	      continue
+	    fi
+	    local ${_line##\>}
+	    (( ${_line##\>} = ${stack[1]} ))
+	    _push=0
+	    shift stack
+	    ;;
+	    (*)
+	    print "BUG in special RPN functions" >&2
+	    _line=
+	    continue
+	    ;;
+	  esac
+	  ;;
+
+	  (+|-|\^|\||\&|\*|/|\*\*|\>\>|\<\</)
+	  # Operators with two arguments
+	  if (( ${#stack} < 2 )); then
+	    print -r -- "${_line}: not enough values on stack" >&2
+	    _line=
+	    continue
+	  fi
+	  eval "(( ans = \${stack[2]} $_line \${stack[1]} ))"
+	  shift 2 stack
+	  ;;
+
+	  (ldexp|jn|yn|scalb|xy|\<\>)
+	  # Functions with two arguments
+	  if (( ${#stack} < 2 )); then
+	    print -r -- "${_line}: not enough values on stack" >&2
+	    _line=
+	    continue
+	  fi
+	  if [[ $_line = (xy|\<\>) ]]; then
+	    _tmp=${stack[1]}
+	    stack[1]=${stack[2]}
+	    stack[2]=$_tmp
+	    _push=0
+	  else
+	    eval "(( ans = ${_line}(\${stack[2]},\${stack[1]}) ))"
+	    shift 2 stack
+	  fi
+	  ;;
+
+	  (${~_mathfuncs})
+	  # Functions with a single argument.
+	  # This is actually a superset, but we should have matched
+	  # any that shouldn't be in it in previous cases.
+	  if (( ${#stack} < 1 )); then
+	    print -r -- "${_line}: not enough values on stack" >&2
+	    _line=
+	    continue
+	  fi
+	  eval "(( ans = ${_line}(\${stack[1]}) ))"
+	  shift stack
+	  ;;
+
+	  (${(kj.|.)~_userfuncs})
+	  # Get minimum number of arguments to user function
+	  _n=${_userfuncs[$_line]}
+	  if (( ${#stack} < n_ )); then
+	    print -r -- "${_line}: not enough values ($_n) on stack" >&2
+	    _line=
+	    continue
+	  fi
+	  _line+="("
+	  # least recent elements on stack are earlier arguments
+	  for (( _i = _n; _i > 0; _i-- )); do
+	    _line+=${stack[_i]}
+	    (( _i > 1 )) && _line+=","
+	  done
+	  _line+=")"
+	  shift $_n stack
+	  eval "(( ans = $_line ))"
+	  ;;
+
+	  (*)
+	  # Treat as expression evaluating to new value to go on stack.
+	  _matched=0
+	  ;;
+	esac
+      else
+	_matched=0
+      fi
+      if (( ! _matched )); then
+	# Latest value is stored` as a string, because it might be floating
+	# point or integer --- we don't know till after the evaluation, and
+	# arrays always store scalars anyway.
+	#
+	# Since it's a string, we'd better make sure we know which
+	# base it's in, so don't change that until we actually print it.
+	if ! eval "ans=\$(( $_line ))"; then
+	  _line=
+	  continue
+	fi
+	# on error $ans is not set; let user re-edit _line
+	[[ -n $ans ]] || continue
+      fi
+      argv[_num++]=$ans
+      psvar[1]=$_num
+      (( _push )) && stack=($ans $stack)
     ;;
   esac
-  if [[ -n $base ]]; then
-    print -- $(( $base $ans ))
-  elif [[ $ans = *.* ]] || (( outdigits )); then
-    if [[ -z $forms[outform] ]]; then
-      print -- $(( $ans ))
-    else
-      printf "$forms[outform]\n" $outdigits $ans
-    fi
+  if (( _show_stack )); then
+    (( _max_stack = (_show_stack > ${#stack}) ? ${#stack} : _show_stack ))
+    for (( _i = _max_stack; _i > 0; _i-- )); do
+      printf "%3d: " $_i
+      zcalc_show_value ${stack[_i]}
+    done
   else
-    printf "%d\n" $ans
+    zcalc_show_value $ans
   fi
-  line=
+  _line=
 done
 
 return 0
diff --git a/Functions/Misc/zed b/Functions/Misc/zed
index eb8f557ea..77d392bc3 100644
--- a/Functions/Misc/zed
+++ b/Functions/Misc/zed
@@ -6,6 +6,8 @@
 # Use ^X^W to save, ^C to abort.
 # Option -f: edit shell functions.  (Also if called as fned.)
 
+setopt localoptions noksharrays
+
 local var opts zed_file_name
 # We do not want timeout while we are editing a file
 integer TMOUT=0 okargs=1 fun bind
@@ -74,7 +76,7 @@ if ((fun)) then
   if [[ $var = *\#\ undefined* ]] then
     var="$(autoload +X $1; functions -- $1)"
   elif [[ -z $var ]] then
-    var="$1() {
+    var="${(q-)1} () {
 }"
   fi
   vared -M zed -m zed-vicmd var && eval function "$var"
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git
index 472c10d5d..f3dd95dcb 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git
@@ -120,41 +120,15 @@ VCS_INFO_git_getbranch () {
 }
 
 VCS_INFO_git_handle_patches () {
-    local git_applied_s git_unapplied_s gitmsg git_all
+    local git_applied_s git_unapplied_s gitmsg
     git_patches_applied=(${(Oa)git_patches_applied})
     git_patches_unapplied=(${(Oa)git_patches_unapplied})
-    (( git_all = ${#git_patches_applied} + ${#git_patches_unapplied} ))
 
-    if VCS_INFO_hook 'gen-applied-string' "${git_patches_applied[@]}"; then
-        if (( ${#git_patches_applied} )); then
-            git_applied_s=${git_patches_applied[1]}
-        else
-            git_applied_s=""
-        fi
-    else
-        git_applied_s=${hook_com[applied-string]}
-    fi
-    hook_com=()
-    if VCS_INFO_hook 'gen-unapplied-string' "${git_patches_unapplied[@]}"; then
-        git_patches_unapplied=${#git_patches_unapplied}
-    else
-        git_patches_unapplied=${hook_com[unapplied-string]}
-    fi
-
-    if (( ${#git_patches_applied} )); then
-        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" patch-format gitmsg || gitmsg="%p (%n applied)"
-    else
-        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" nopatch-format gitmsg || gitmsg="no patch applied"
-    fi
-    hook_com=( applied "${git_applied_s}"     unapplied "${git_patches_unapplied}"
-               applied-n ${#git_patches_applied} unapplied-n ${#git_patches_unapplied} all-n ${git_all} )
-    if VCS_INFO_hook 'set-patch-format' "${gitmsg}"; then
-        zformat -f gitmisc "${gitmsg}" "p:${hook_com[applied]}" "u:${hook_com[unapplied]}" \
-                                          "n:${#git_patches_applied}" "c:${#git_patches_unapplied}" "a:${git_all}"
-    else
-        gitmisc=${hook_com[patch-replace]}
-    fi
-    hook_com=()
+    VCS_INFO_set-patch-format 'git_patches_applied' 'git_applied_s' \
+                              'git_patches_unapplied' 'git_unapplied_s' \
+                              ":vcs_info:${vcs}:${usercontext}:${rrn}" gitmsg \
+                              '' ''
+    gitmisc=$REPLY
 }
 
 gitdir=${vcs_comm[gitdir]}
@@ -182,7 +156,7 @@ if (( querystaged || queryunstaged )) && \
    [[ "$(${vcs_comm[cmd]} rev-parse --is-inside-work-tree 2> /dev/null)" == 'true' ]] ; then
     # Default: off - these are potentially expensive on big repositories
     if (( queryunstaged )) ; then
-        ${vcs_comm[cmd]} diff --no-ext-diff --ignore-submodules=dirty --quiet --exit-code ||
+        ${vcs_comm[cmd]} diff --no-ext-diff --ignore-submodules=dirty --quiet --exit-code 2> /dev/null ||
             gitunstaged=1
     fi
     if (( querystaged )) ; then
@@ -205,6 +179,7 @@ local patchdir=${gitdir}/patches/${gitbranch}
 if [[ -d $patchdir ]] && [[ -f $patchdir/applied ]] \
    && [[ -f $patchdir/unapplied ]]
 then
+    # stgit
     git_patches_applied=(${(f)"$(< "${patchdir}/applied")"})
     git_patches_unapplied=(${(f)"$(< "${patchdir}/unapplied")"})
     VCS_INFO_git_handle_patches
@@ -213,11 +188,15 @@ elif [[ -d "${gitdir}/rebase-merge" ]]; then
     local p
     [[ -f "${patchdir}/done" ]] &&
     for p in ${(f)"$(< "${patchdir}/done")"}; do
-        # remove action
-        git_patches_applied+=("${${(s: :)p}[2,-1]}")
+        # pick/edit/fixup/squash/reword: Add "$hash $subject" to $git_patches_applied.
+        # exec: Add "exec ${command}" to $git_patches_applied.
+        # (anything else): As 'exec'.
+        p=${p/(#s)(p|pick|e|edit|r|reword|f|fixup|s|squash) /}
+        p=${p/(#s)x /exec }
+        git_patches_applied+=("$p")
     done
     if [[ -f "${patchdir}/git-rebase-todo" ]] ; then
-        git_patches_unapplied=(${(f)"$(grep -v '^$' "${patchdir}/git-rebase-todo" | grep -v '^#')"})
+        git_patches_unapplied=( ${${(f)${"$(<"${patchdir}/git-rebase-todo")"}}:#[#]*} )
     fi
     VCS_INFO_git_handle_patches
 elif [[ -d "${gitdir}/rebase-apply" ]]; then
@@ -228,10 +207,19 @@ elif [[ -d "${gitdir}/rebase-apply" ]]; then
         local cur=$(< $next)
         local p subject
         for ((p = 1; p < cur; p++)); do
-            git_patches_applied+=("$(printf "%04d" $p) ?")
+            printf -v "git_patches_applied[$p]"  "%04d ?" "$p"
         done
         if [[ -f "${patchdir}/msg-clean" ]]; then
             subject="${$(< "${patchdir}/msg-clean")[(f)1]}"
+        elif local this_patch_file
+             printf -v this_patch_file "%s/%04d" "${patchdir}" "${cur}"
+             [[ -f $this_patch_file ]]
+        then
+            () {
+              local REPLY
+              VCS_INFO_patch2subject "${this_patch_file}"
+              subject=$REPLY
+            }
         fi
         if [[ -f "${patchdir}/original-commit" ]]; then
             if [[ -n $subject ]]; then
@@ -257,6 +245,7 @@ elif [[ -f "${gitdir}/MERGE_HEAD" ]]; then
     # This is 'git merge --no-commit'
     local -a heads=( ${(@f)"$(<"${gitdir}/MERGE_HEAD")"} )
     local subject;
+    # TODO: maybe read up to the first blank line
     IFS='' read -r subject < "${gitdir}/MERGE_MSG"
     # $subject is the subject line of the would-be commit
     # Maybe we can get the subject lines of MERGE_HEAD's commits cheaply?
@@ -282,6 +271,7 @@ elif [[ -f "${gitdir}/CHERRY_PICK_HEAD" ]]; then
     # ### be "1".  The %u/%c tuple will assume the values [(1,2), (1,1), (1,0)],
     # ### whereas the correct sequence would be [(1,2), (2,1), (3,0)].
     local subject
+    # TODO: maybe read up to the first blank line
     IFS='' read -r subject < "${gitdir}/MERGE_MSG"
     git_patches_applied=( "$(<${gitdir}/CHERRY_PICK_HEAD) ${subject}" )
     if [[ -f "${gitdir}/sequencer/todo" ]]; then
diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
index f35ad5965..d4030125c 100644
--- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
+++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg
@@ -13,7 +13,7 @@ local hgbase bmfile branchfile rebasefile dirstatefile mqseriesfile \
     hgbmstring hgmqstring applied_string unapplied_string guards_string
 
 local -a hgid_args defrevformat defbranchformat \
-    hgbmarks mqpatches mqseries mqguards mqunapplied hgmisc \
+    hgbmarks mqpatches mqguards mqunapplied hgmisc \
     i_patchguards i_negguards i_posguards
 
 local -A hook_com
@@ -40,9 +40,10 @@ VCS_INFO_adjust
 # Disabled by default anyway, so no harm done.
 if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" get-revision ; then
     if zstyle -t ":vcs_info:${vcs}:${usercontext}:${rrn}" use-simple \
-            && ( VCS_INFO_check_com hexdump ) && [[ -r ${dirstatefile} ]] ; then
-        # Calling hexdump is (much) faster than hg but doesn't get the local rev
-        r_csetid=$(hexdump -n 20 -e '1/1 "%02x"' ${dirstatefile})
+            && VCS_INFO_hexdump ${dirstatefile} 20 ; then
+        # Calling VCS_INFO_hexdump is (much) faster than hg but doesn't get
+        # the local rev
+        r_csetid=$REPLY
     else
         # Settling for a short (but unique!) hash because getting the full
         # 40-char hash in addition to all the other info we want isn't
@@ -174,9 +175,6 @@ if zstyle -T ":vcs_info:${vcs}:${usercontext}:${rrn}" get-mq \
             # Skip commented lines
             [[ ${i_patch} == [[:space:]]#"#"* ]] && continue
 
-            # Keep list of all patches
-            mqseries+=( $i_patch )
-
             # Separate negative and positive guards to more easily find the
             # intersection of active guards with patch guards
             i_patchguards=( ${(s: :)i_patchguards} )
@@ -202,50 +200,21 @@ if zstyle -T ":vcs_info:${vcs}:${usercontext}:${rrn}" get-mq \
         done < ${mqseriesfile}
     fi
 
-    if VCS_INFO_hook 'gen-applied-string' "${mqpatches[@]}"; then
-        (( ${#mqpatches} )) && applied_string=${mqpatches[1]}
-    else
-        applied_string=${hook_com[applied-string]}
-    fi
-
-    hook_com=()
-
-    if VCS_INFO_hook 'gen-unapplied-string' "${mqunapplied[@]}"; then
-        unapplied_string=${#mqunapplied}
-    else
-        unapplied_string=${hook_com[unapplied-string]}
-    fi
-
-    hook_com=()
-
     if VCS_INFO_hook 'gen-mqguards-string' "${mqguards[@]}"; then
         guards_string=${(j:,:)mqguards}
+        # TODO: %-escape extra_zformats[g:...] value
     else
         guards_string=${hook_com[guards-string]}
     fi
 
-    if (( ${#mqpatches} )); then
-        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" patch-format \
-            hgmqstring || hgmqstring="%p (%n applied)"
-    else
-        zstyle -s ":vcs_info:${vcs}:${usercontext}:${rrn}" nopatch-format \
-            hgmqstring || hgmqstring="no patch applied"
-    fi
-
-    hook_com=( applied "${applied_string}" unapplied "${unapplied_string}"
-               applied-n ${#mqpatches}     unapplied-n ${#mqunapplied}     all-n ${#mqseries}
-               guards "${guards_string}"   guards-n ${#mqguards} )
+    local -A extra_hook_com=( guards "${guards_string}"   guards-n ${#mqguards} )
+    local -a extra_zformats=( "g:${extra_hook_com[guards]}" "G:${#mqguards}" )
 
-    if VCS_INFO_hook 'set-patch-format' ${qstring}; then
-        zformat -f hgmqstring "${hgmqstring}" \
-            "p:${hook_com[applied]}" "u:${hook_com[unapplied]}" \
-            "n:${#mqpatches}" "c:${#mqunapplied}" "a:${#mqseries}" \
-            "g:${hook_com[guards]}" "G:${#mqguards}"
-    else
-        hgmqstring=${hook_com[patch-replace]}
-    fi
-
-    hook_com=()
+    VCS_INFO_set-patch-format 'mqpatches' 'applied_string' \
+                              'mqunapplied' 'unapplied_string' \
+                              ":vcs_info:${vcs}:${usercontext}:${rrn}" hgmqstring \
+                              extra_hook_com extra_zformats
+    hgmqstring=$REPLY
 fi
 
 
diff --git a/Functions/VCS_Info/VCS_INFO_hexdump b/Functions/VCS_Info/VCS_INFO_hexdump
new file mode 100644
index 000000000..11f1c1a50
--- /dev/null
+++ b/Functions/VCS_Info/VCS_INFO_hexdump
@@ -0,0 +1,16 @@
+## vim:ft=zsh
+
+# VCS_INFO_hexdump FILENAME BYTECOUNT
+#
+# Return in $REPLY a hexadecimal representation (lowercase, no whitespace)
+# of the first BYTECOUNT bytes of FILENAME.
+
+if [[ -r $1 ]]; then
+  setopt localoptions nomultibyte extendedglob
+  local val
+  read -k $2 -u 0 val <$1
+  REPLY=${(Lj::)${(l:2::0:)${(@s//)val}//(#m)*/$(( [##16] ##$MATCH ))}}
+else
+  return 1
+fi
+
diff --git a/Functions/VCS_Info/VCS_INFO_patch2subject b/Functions/VCS_Info/VCS_INFO_patch2subject
new file mode 100644
index 000000000..e222e8382
--- /dev/null
+++ b/Functions/VCS_Info/VCS_INFO_patch2subject
@@ -0,0 +1,67 @@
+# This function takes as an argument a filename of a patch and sets $REPLY to
+# a single-line "subject", or unsets it if no subject could be extracted.
+{
+    setopt localoptions extendedglob
+    integer i
+    integer -r LIMIT=10
+    local -a lines
+    local needle
+    if [[ -f "$1" ]]; then
+        # Extract the first LIMIT lines, or up to the first empty line or the start of the unidiffs,
+        # whichever comes first.
+        while (( i++ < LIMIT )); do
+            IFS= read -r "lines[$i]"
+            if [[ -z ${lines[$i]} ]] || [[ ${lines[$i]} == (#b)(---|Index:)* ]]; then
+                lines[$i]=()
+                break
+            fi
+        done < "$1"
+        
+        if needle=${lines[(i)Subject:*]}; (( needle <= $#lines )); then
+            # "Subject: foo" line, plus rfc822 whitespace unfolding.
+            #
+            # Example: 'git format-patch' patches.
+            REPLY=${lines[needle]}
+            REPLY=${REPLY#*: }
+            REPLY=${REPLY#\[PATCH\] }
+            while [[ ${${lines[++needle]}[1]} == ' ' ]]; do
+                REPLY+=${lines[needle]}
+            done
+        elif needle=${lines[(r)Description:*]}; [[ -n $needle ]]; then
+            # "Description: foo" line.
+            #
+            # Example: DEP-3 patches.
+            REPLY=${needle#*: }
+        elif [[ ${lines[1]} == '# HG changeset patch' ]] && { needle=${${lines:#([#]*)}[1]}; [[ -n $needle ]] }; then
+            # Mercurial patch
+            REPLY=$needle
+        elif [[ ${lines[1]} == "commit "[0-9a-f](#c40) ]] &&
+             [[ ${lines[2]} == "Author:"* && ${lines[3]} == "Date:"* ]] &&
+             (( ! ${+lines[4]} )); then
+            # `git show` output.
+            #
+            # The log message is after the first blank line, so open() the file
+            # again.  Also check whether the following line (second line of the
+            # log message itself) is empty.
+            {
+              repeat 4 { IFS= read -r }
+              IFS= read -r needle; needle=${needle#'    '}
+              if IFS= read -r; REPLY=${REPLY#'    '}; [[ -n $REPLY ]]; then
+                needle+='...'
+              fi
+            } < "$1"
+            REPLY=$needle
+        elif (( ${+lines[1]} )); then
+            # The first line of the file is not part of the diff.
+            REPLY=${lines[1]}
+        else
+            # The patch has no subject.
+            unset REPLY
+            return 0
+        fi
+    else
+        # The patch cannot be examined, or invalid arguments.
+        unset REPLY
+        return 1
+    fi
+}
diff --git a/Functions/VCS_Info/VCS_INFO_quilt b/Functions/VCS_Info/VCS_INFO_quilt
index c3c3d864d..381b58489 100644
--- a/Functions/VCS_Info/VCS_INFO_quilt
+++ b/Functions/VCS_Info/VCS_INFO_quilt
@@ -80,6 +80,10 @@ function VCS_INFO_quilt-dirfind() {
     return ${ret}
 }
 
+function VCS_INFO_quilt-patch2subject() {
+    VCS_INFO_patch2subject "$@"
+}
+
 function VCS_INFO_quilt() {
     emulate -L zsh
     setopt extendedglob
@@ -87,7 +91,7 @@ function VCS_INFO_quilt() {
     local patches pc tmp qstring root
     local -i ret
     local context
-    local -a applied unapplied all applied_string unapplied_string quiltcommand quilt_env
+    local -a applied unapplied applied_string unapplied_string quiltcommand quilt_env
     local -A hook_com
 
     context=":vcs_info:${vcs}.quilt-${mode}:${usercontext}:${rrn}"
@@ -119,7 +123,7 @@ function VCS_INFO_quilt() {
             applied=()
         fi
         patches=$(<$pc/.quilt_patches)
-        patches=`builtin cd -q "${pc:h}" && print -r - ${patches:A}`
+        patches=`builtin cd -q "${pc:h}" && print -r - ${patches:P}`
     fi
     if zstyle -t "${context}" get-unapplied; then
         # This zstyle call needs to be moved further up if `quilt' needs
@@ -147,27 +151,19 @@ function VCS_INFO_quilt() {
 
     if [[ -n $patches ]]; then
       () {
-        local i line
+        local i
         for ((i=1; i<=$#applied; i++)); do
-          if [[ -f "$patches/$applied[$i]" ]] &&
-             read -r line < "$patches/$applied[$i]" &&
-             [[ $line != (#b)(---|Index:)* ]] &&
-             true
-            ;
+          if VCS_INFO_quilt-patch2subject "$patches/$applied[$i]" && (( $+REPLY ))
           then
-            applied[$i]+=" $line"
+            applied[$i]+=" $REPLY"
           else
             applied[$i]+=" ?"
           fi
         done
         for ((i=1; i<=$#unapplied; i++)); do
-          if [[ -f "$patches/$unapplied[$i]" ]] &&
-             read -r line < "$patches/$unapplied[$i]" &&
-             [[ $line != (#b)(---|Index:)* ]] &&
-             true
-            ;
+          if VCS_INFO_quilt-patch2subject "$patches/$unapplied[$i]" && (( $+REPLY ))
           then
-            unapplied[$i]+=" $line"
+            unapplied[$i]+=" $REPLY"
           else
             unapplied[$i]+=" ?"
           fi
@@ -175,41 +171,15 @@ function VCS_INFO_quilt() {
       }
     fi
 
-    all=( ${(Oa)applied} ${unapplied} )
-
-    if VCS_INFO_hook 'gen-applied-string' "${applied[@]}"; then
-        if (( ${#applied} )); then
-            applied_string=${applied[1]}
-        else
-            applied_string=""
-        fi
-    else
-        applied_string=${hook_com[applied-string]}
-    fi
-    hook_com=()
-    if VCS_INFO_hook 'gen-unapplied-string' "${unapplied[@]}"; then
-        unapplied_string="${#unapplied}"
-    else
-        unapplied_string=${hook_com[unapplied-string]}
-    fi
-
-    if (( ${#applied} )); then
-        zstyle -s "${context}" patch-format qstring || qstring="%p (%n applied)"
-    else
-        zstyle -s "${context}" nopatch-format qstring || qstring="no patch applied"
-    fi
-    hook_com=( applied "${applied_string}" unapplied "${unapplied_string}"
-               applied-n ${#applied}       unapplied-n ${#unapplied}       all-n ${#all} )
-    if VCS_INFO_hook 'set-patch-format' ${qstring}; then
-        zformat -f qstring "${qstring}" "p:${hook_com[applied]}" "u:${hook_com[unapplied]}" \
-                                        "n:${#applied}" "c:${#unapplied}" "a:${#all}"
-    else
-        qstring=${hook_com[patch-replace]}
-    fi
-    hook_com=()
+    VCS_INFO_set-patch-format 'applied' 'applied_string' \
+                              'unapplied' 'unapplied_string' \
+                              ${context} qstring \
+                              '' ''
+    qstring=$REPLY
 
     case ${mode} in
     (standalone)
+        backend_misc[patches]=${qstring}
         VCS_INFO_formats '' '' "${root}" '' '' '' "${qstring}"
         VCS_INFO_set
         ;;
diff --git a/Functions/VCS_Info/VCS_INFO_set-patch-format b/Functions/VCS_Info/VCS_INFO_set-patch-format
new file mode 100644
index 000000000..cdf2d303e
--- /dev/null
+++ b/Functions/VCS_Info/VCS_INFO_set-patch-format
@@ -0,0 +1,79 @@
+# This function is the common guts of the gen-applied-string /
+# gen-unapplied-string / set-patch-format dance of several backends.
+#
+# Parameters:
+# $1 - name of an array parameter to be the argument to gen-applied-string
+# $2 - name of a parameter to store the applied-string in
+# $3 - name of an array parameter to be the argument to gen-unapplied-string
+# $4 - name of a parameter to store the unapplied-string in
+# $5 - context argument for use in zstyle getters
+# $6 - name of a parameter to store a patch-format format string in
+# $7 - name of an assoc parameter with extra $hook_com key-value pairs for the
+#      set-patch-format hook invocation, or '' for none
+# $8 - name of an array parameter with extra arguments for the patch-format zformat call, or '' for empty
+#
+# The expanded patch-format string is returned in $REPLY.
+#
+# Output:
+# - $hook_com is overwritten and the keys 'applied', 'applied-n',
+#   'unapplied', 'unapplied-n', 'all-n' are set.
+{
+    local applied_needs_escaping='unknown'
+    local unapplied_needs_escaping='unknown'
+    if VCS_INFO_hook 'gen-applied-string' "${(@P)1}"; then
+        if (( ${(P)#1} )); then
+            REPLY=${(P)1[1]}
+        else
+            REPLY=""
+        fi
+        applied_needs_escaping='yes'
+    else
+        REPLY=${hook_com[applied-string]}
+    fi
+    : ${(P)2::=$REPLY}
+    hook_com=()
+
+    if VCS_INFO_hook 'gen-unapplied-string' "${(@P)3}"; then
+        REPLY=${(P)#3}
+        unapplied_needs_escaping='yes'
+    else
+        REPLY=${hook_com[unapplied-string]}
+    fi
+    : ${(P)4::=$REPLY}
+    hook_com=()
+
+    if (( ${(P)#1} )); then
+        zstyle -s "${5}" patch-format REPLY || REPLY="%p (%n applied)"
+    else
+        zstyle -s "${5}" nopatch-format REPLY || REPLY="no patch applied"
+    fi
+    : ${(P)6::=$REPLY}
+
+    hook_com=(
+      applied-n ${(P)#1}
+      applied "${(P)2}"
+      unapplied-n ${(P)#3}
+      unapplied "${(P)4}"
+    )
+    hook_com[all-n]=$(( ${hook_com[applied-n]} + ${hook_com[unapplied-n]} ))
+    hook_com+=( ${7:+"${(@kvP)7}"} )
+    if VCS_INFO_hook 'set-patch-format' "${(P)6}"; then
+        # Escape the value for use in $PS1
+        if [[ $applied_needs_escaping == 'yes' ]]; then
+          hook_com[applied]=${hook_com[applied]//'%'/%%}
+        fi
+        if [[ $unapplied_needs_escaping == 'yes' ]]; then
+          hook_com[unapplied]=${hook_com[unapplied]//'%'/%%}
+        fi
+
+        zformat -f REPLY "${(P)6}" "p:${hook_com[applied]}" "u:${hook_com[unapplied]}" \
+                                        "n:${hook_com[applied-n]}" "c:${hook_com[unapplied-n]}" \
+                                        "a:${hook_com[all-n]}" \
+                                        ${8:+"${(@P)8}"}
+    else
+        unset applied_needs_escaping unapplied_needs_escaping # the hook deals with escaping
+        REPLY=${hook_com[patch-replace]}
+    fi
+    hook_com=()
+
+}
diff --git a/Functions/VCS_Info/vcs_info b/Functions/VCS_Info/vcs_info
index f13f6b501..4e9ac6c6a 100644
--- a/Functions/VCS_Info/vcs_info
+++ b/Functions/VCS_Info/vcs_info
@@ -19,9 +19,12 @@ static_functions=(
     VCS_INFO_check_com
     VCS_INFO_formats
     VCS_INFO_get_cmd
+    VCS_INFO_hexdump
     VCS_INFO_hook
+    VCS_INFO_set-patch-format
     VCS_INFO_maxexports
     VCS_INFO_nvcsformats
+    VCS_INFO_patch2subject
     VCS_INFO_quilt
     VCS_INFO_realpath
     VCS_INFO_reposub
diff --git a/Functions/Zle/bracketed-paste-magic b/Functions/Zle/bracketed-paste-magic
index 498cf554d..4baae823e 100644
--- a/Functions/Zle/bracketed-paste-magic
+++ b/Functions/Zle/bracketed-paste-magic
@@ -20,7 +20,7 @@
 # active-widgets
 #  Looked up in the context :bracketed-paste-magic to obtain a list of
 #  patterns that match widget names that should be activated during the
-#  paste.  All other key sequences are processed as self-insert-unmeta.
+#  paste.  All other key sequences are processed as "zle .self-insert".
 #  The default is 'self-*' so any user-defined widgets named with that
 #  prefix are active along with the builtin self-insert.  If this style is
 #  not set (note: it must be explicitly deleted after loading this
@@ -31,7 +31,7 @@
 #
 # inactive-keys
 #  This is the inverse of active-widgets, it lists key sequences that
-#  always use self-insert-unmeta even when bound to an active-widget.
+#  always use "zle .self-insert" even when bound to an active-widget.
 #  Note that this is a list of literal key sequences, not patterns.
 #  This style is in context :bracketed-paste-magic and has no default.
 #
@@ -145,27 +145,26 @@ bracketed-paste-magic() {
 	done
     fi
 
-    # Save context, create a clean slate for the paste
-    integer bpm_mark=$MARK bpm_cursor=$CURSOR bpm_region=$REGION_ACTIVE
-    integer bpm_numeric=${NUMERIC:-1}
-    local bpm_buffer=$BUFFER
-    fc -p -a /dev/null 0 0
-    BUFFER=
-
     zstyle -a :bracketed-paste-magic inactive-keys bpm_inactive
     if zstyle -s :bracketed-paste-magic active-widgets bpm_active '|'; then
-        # There are active widgets.  Reprocess $PASTED as keystrokes.
-	NUMERIC=1
-	zle -U - $PASTED
-
+	# Save context, create a clean slate for the paste
+	integer bpm_mark=$MARK bpm_region=$REGION_ACTIVE
+	integer bpm_numeric=${NUMERIC:-1}
+	integer bpm_limit=$UNDO_LIMIT_NO bpm_undo=$UNDO_CHANGE_NO
+	zle .split-undo
+	UNDO_LIMIT_NO=$UNDO_CHANGE_NO
+	BUFFER=
+	CURSOR=1
+	fc -p -a /dev/null 0 0
 	if [[ $bmp_keymap = vicmd ]]; then
 	    zle -K viins
 	fi
 
+	# There are active widgets.  Reprocess $PASTED as keystrokes.
+	NUMERIC=1
+	zle -U - $PASTED
+
 	# Just in case there are active undo widgets
-	zle .split-undo
-	integer bpm_limit=$UNDO_LIMIT_NO bpm_undo=$UNDO_CHANGE_NO
-	UNDO_LIMIT_NO=$UNDO_CHANGE_NO
 
 	while [[ -n $PASTED ]] && zle .read-command; do
 	    PASTED=${PASTED#$KEYS}
@@ -183,21 +182,16 @@ bracketed-paste-magic() {
 	done
 	PASTED=$BUFFER
 
-	# Reset the undo state
+	# Restore state
+	zle -K $bpm_keymap
+	fc -P
+	MARK=$bpm_mark
+	REGION_ACTIVE=$bpm_region
+	NUMERIC=$bpm_numeric
 	zle .undo $bpm_undo
 	UNDO_LIMIT_NO=$bpm_limit
-
-	zle -K $bpm_keymap
     fi
 
-    # Restore state
-    BUFFER=$bpm_buffer
-    MARK=$bpm_mark
-    CURSOR=$bpm_cursor
-    REGION_ACTIVE=$bpm_region
-    NUMERIC=$bpm_numeric
-    fc -P
-
     # PASTED has been updated, run the paste-finish functions
     if zstyle -a :bracketed-paste-magic paste-finish bpm_hooks; then
 	for bpm_func in $bpm_hooks; do
@@ -212,7 +206,7 @@ bracketed-paste-magic() {
 
     # Reprocess $PASTED as an actual paste this time
     zle -U - $PASTED$'\e[201~'	# append paste-end marker
-    zle .bracketed-paste
+    zle .bracketed-paste -- "$@"
     zle .split-undo
 
     # Arrange to display highlighting if necessary
diff --git a/Functions/Zle/bracketed-paste-url-magic b/Functions/Zle/bracketed-paste-url-magic
index 06dee2657..b894696bb 100644
--- a/Functions/Zle/bracketed-paste-url-magic
+++ b/Functions/Zle/bracketed-paste-url-magic
@@ -19,7 +19,7 @@
 # The default can be seen just below.
 
 local -a schema
-zstyle -a :bracketed-paste-url-magic schema schema || schema=(http https ftp ftps file ssh sftp)
+zstyle -a :bracketed-paste-url-magic schema schema || schema=(http:// https:// ftp:// ftps:// file:// ssh:// sftp:// magnet:)
 
 local wantquote=${NUMERIC:-0}
 local content
@@ -28,7 +28,7 @@ local start=$#LBUFFER
 zle .$WIDGET -N content
 
 if (( $wantquote == 0 )); then
-  if [[ $content = (${(~j:|:)schema})://* ]]; then
+  if [[ $content = (${(~j:|:)schema})* ]]; then
     wantquote=1
   fi
 fi
diff --git a/Functions/Zle/delete-whole-word-match b/Functions/Zle/delete-whole-word-match
index aece86065..3d52dd3d7 100644
--- a/Functions/Zle/delete-whole-word-match
+++ b/Functions/Zle/delete-whole-word-match
@@ -12,30 +12,29 @@ emulate -L zsh
 setopt extendedglob
 
 local curcontext=:zle:$WIDGET
-local -a matched_words
+local -A matched_words
 # Start and end of range of characters to remove.
 integer pos1 pos2
 
 autoload -Uz match-words-by-style
 match-words-by-style
 
-if [[ -n "${matched_words[3]}" ]]; then
-    # There's whitespace before the cursor, so the word we are deleting
-    # starts at the cursor position.
+if (( ${matched_words[is-word-start]} )); then
+    # The word we are deleting starts at the cursor position.
     pos1=$CURSOR
 else
-    # No whitespace before us, so delete any wordcharacters there.
-    pos1="${#matched_words[1]}"
+    # Not, so delete any wordcharacters before, too
+    pos1="${#matched_words[start]}"
 fi
 
-if [[ -n "${matched_words[4]}" ]]; then
+if [[ -n "${matched_words[ws-after-cursor]}" ]]; then
     # There's whitespace at the cursor position, so only delete
     # up to the cursor position.
     (( pos2 = CURSOR + 1 ))
 else
     # No whitespace at the cursor position, so delete the
     # current character and any following wordcharacters.
-    (( pos2 = CURSOR + ${#matched_words[5]} + 1 ))
+    (( pos2 = CURSOR + ${#matched_words[word-after-cursor]} + 1 ))
 fi
 
 # Move the cursor then delete the block in one go for the
diff --git a/Functions/Zle/expand-absolute-path b/Functions/Zle/expand-absolute-path
index b85757600..4887f3c60 100644
--- a/Functions/Zle/expand-absolute-path
+++ b/Functions/Zle/expand-absolute-path
@@ -10,7 +10,7 @@ autoload -Uz modify-current-argument
 if (( ! ${+functions[glob-expand-absolute-path]} )); then
   glob-expand-absolute-path() {
     local -a files
-    files=(${~1}(N:A))
+    files=(${~1}(N:P))
     (( ${#files} )) || return
     REPLY=${(D)files[1]}
   }
diff --git a/Functions/Zle/history-beginning-search-menu b/Functions/Zle/history-beginning-search-menu
index 105518102..0e1bbc734 100644
--- a/Functions/Zle/history-beginning-search-menu
+++ b/Functions/Zle/history-beginning-search-menu
@@ -112,7 +112,7 @@ fi
 # go to the last one.  This allows accept-line-and-down-history etc.
 # to work.
 local -a lines
-local matchq=${matches[$chars]//(#m)[\][()\\*?#<>~^]/\\$MATCH}
+local matchq=${matches[$chars]//(#m)[\][|()\\*?#<>~^]/\\$MATCH}
 lines=(${(kon)history[(R)$matchq]})
 HISTNO=$lines[-1]
 
diff --git a/Functions/Zle/insert-unicode-char b/Functions/Zle/insert-unicode-char
index af9aad914..b943fb7f7 100644
--- a/Functions/Zle/insert-unicode-char
+++ b/Functions/Zle/insert-unicode-char
@@ -12,11 +12,11 @@ then
   local -i 16 -Z 10 arg=$NUMERIC
   # ...and use print to turn this into a Unicode character.
   LBUFFER+="$(print -n "\U${arg##0x}")"
-  _insert_unicode_ready=0
+  integer -g _insert_unicode_ready=0
 else
   # Set the base to 16...
   zle argument-base 16
   # ...wait for user to type hex keys then call this widget again.
   zle universal-argument
-  _insert_unicode_ready=1
+  integer -g _insert_unicode_ready=1
 fi
diff --git a/Functions/Zle/match-words-by-style b/Functions/Zle/match-words-by-style
index 54e019d23..fc59c2764 100644
--- a/Functions/Zle/match-words-by-style
+++ b/Functions/Zle/match-words-by-style
@@ -5,8 +5,16 @@
 #    <whitespace-after-cursor> <word-after-cursor> <whitespace-after-word>
 #    <stuff-at-end>
 # where the cursor position is always after the third item and `after'
-# is to be interpreted as `after or on'.  Some
-# of the array elements will be empty; this depends on the style.
+# is to be interpreted as `after or on'.
+#
+# matched_words may be an associative array, in which case the
+# values above are now given by the elements named start, word-before-cursor,
+# ws-before-cursor, ws-after-cursor, word-after-cursor, ws-after-word,
+# end.  In addition, the element is-word-start is 1 if the cursor
+# is on the start of a word; this is non-trivial in the case of subword
+# (camel case) matching as there may be no white space to test.
+#
+# Some of the array elements will be empty; this depends on the style.
 # For example
 #    foo bar  rod stick
 #            ^
@@ -202,7 +210,7 @@ if [[ $wordstyle = *subword* ]]; then
   # followed by a lower case letter, or an upper case letter at
   # the start of a group of upper case letters.  To make
   # it easier to be consistent, we just use anything that
-  # isn't an upper case characer instead of a lower case
+  # isn't an upper case character instead of a lower case
   # character.
   # Here the initial "*" will match greedily, so we get the
   # last such match, as we want.
@@ -224,11 +232,18 @@ charskip=${(l:skip::?:)}
 
 eval pat2='${RBUFFER##(#b)('${charskip}${spacepat}')('\
 ${wordpat2}')('${spacepat}')}'
+if [[ -n $match[2] ]]; then
+  ws2=$match[1]
+  word2=$match[2]
+  ws3=$match[3]
+else
+  # No more words, so anything left is white space after cursor.
+  ws2=$RBUFFER
+  pat2=
+fi
 
-ws2=$match[1]
-word2=$match[2]
-ws3=$match[3]
-
+integer wordstart
+[[ ( -n $ws1 || -n $ws2 ) && -n $word2 ]] && wordstart=1
 if [[ $wordstyle = *subword* ]]; then
   # Do we have a group of upper case characters at the start
   # of word2 (that don't form the entire word)?
@@ -237,12 +252,19 @@ if [[ $wordstyle = *subword* ]]; then
 	  -n $match[2] ]]; then
     # Yes, so the last one is new word boundary.
     (( epos = ${#match[1]} - 1 ))
+    # Otherwise, are we in the middle of a word?
+    # In other, er, words, we've got something on the left with no
+    # white space following and something that doesn't start a word here.
+  elif [[ -n $word1 && -z $ws1 && -z $ws2 && \
+    $word2 = (#b)([^${~subwordrange}]##)* ]]; then
+    (( epos = ${#match[1]} ))
     # Otherwise, do we have upper followed by non-upper not
     # at the start?  Ignore the initial character, we already
     # know it's a word boundary so it can be an upper case character
     # if it wants.
   elif [[ $word2 = (#b)(?[^${~subwordrange}]##)[${~subwordrange}]* ]]; then
     (( epos = ${#match[1]} ))
+    (( wordstart = 1 ))
   else
     (( epos = 0 ))
   fi
@@ -256,4 +278,19 @@ if [[ $wordstyle = *subword* ]]; then
   fi
 fi
 
-matched_words=("$pat1" "$word1" "$ws1" "$ws2" "$word2" "$ws3" "$pat2")
+# matched_words should be local to caller.
+# Just fix type here.
+if [[ ${(t)matched_words} = *association* ]]; then
+  matched_words=(
+    start              "$pat1"
+    word-before-cursor "$word1"
+    ws-before-cursor   "$ws1"
+    ws-after-cursor    "$ws2"
+    word-after-cursor  "$word2"
+    ws-after-word      "$ws3"
+    end                "$pat2"
+    is-word-start      $wordstart
+  )
+else
+  matched_words=("$pat1" "$word1" "$ws1" "$ws2" "$word2" "$ws3" "$pat2")
+fi
diff --git a/Functions/Zle/select-bracketed b/Functions/Zle/select-bracketed
index 00f51be2c..d467bb804 100644
--- a/Functions/Zle/select-bracketed
+++ b/Functions/Zle/select-bracketed
@@ -12,6 +12,8 @@
 #	done
 #     done
 
+setopt localoptions noksharrays
+
 local style=${${1:-$KEYS}[1]} matching="(){}[]<>bbBB"
 local -i find=${NUMERIC:-1} idx=${matching[(I)[${${1:-$KEYS}[2]}]]}%9
 (( idx )) || return 1 # no corresponding closing bracket
diff --git a/Functions/Zle/select-word-match b/Functions/Zle/select-word-match
new file mode 100644
index 000000000..8440852ab
--- /dev/null
+++ b/Functions/Zle/select-word-match
@@ -0,0 +1,120 @@
+# Select the entire word around the cursor. Intended for use as
+# a vim-style text object in vi mode but with customisable
+# word boundaries.
+#
+# For example:
+#   autoload -U select-word-match
+#   zle -N select-in-camel select-word-match
+#   bindkey -M viopp ic select-in-camel
+#   zstyle ':zle:*-camel' word-style normal-subword
+
+emulate -L zsh
+setopt extendedglob
+
+local curcontext=:zle:$WIDGET
+local -A matched_words
+# Start and end of range of characters
+integer pos1 pos2 num=${NUMERIC:-1}
+local style word
+
+# choose between inner word or a word style of widget
+for style in $1 ${${WIDGET#*-}[1]} $KEYS[1] "i"; do
+  [[ $style = [ai] ]] && break
+done
+
+autoload -Uz match-words-by-style
+
+while (( num-- )); do
+  if (( MARK > CURSOR )); then
+    # if cursor is at the start of the selection, just move back a word
+    match-words-by-style
+    if [[ $style = i && -n $matched_words[ws-before-cursor] ]]; then
+      word=$matched_words[ws-before-cursor]
+    else
+      word=$matched_words[word-before-cursor]$matched_words[ws-before-cursor]
+    fi
+    if [[ -n $word ]]; then
+      (( CURSOR -= ${#word} ))
+    else
+      return 1
+    fi
+  elif (( MARK >= 0 && MARK < CURSOR )); then
+    # cursor at the end, move forward a word
+    (( CURSOR+1 == $#BUFFER )) && return 1
+    (( CURSOR++ ))
+    match-words-by-style
+    if [[ -n $matched_words[ws-after-cursor] ]]; then
+      if [[ $style = i ]]; then
+	# just skip the whitespace
+	word=$matched_words[ws-after-cursor]
+      else
+	# skip the whitespace plus word
+	word=$matched_words[ws-after-cursor]$matched_words[word-after-cursor]
+      fi
+    else
+      if [[ $style = i ]]; then
+	# skip the word
+	word=$matched_words[word-after-cursor]
+      else
+	# skip word and following whitespace
+	word=$matched_words[word-after-cursor]$matched_words[ws-after-word]
+      fi
+    fi
+    (( CURSOR += ${#word} - 1 ))
+  else
+    match-words-by-style
+
+    if (( ${matched_words[is-word-start]} )); then
+      # The word we are selecting starts at the cursor position.
+      pos1=$CURSOR
+    else
+      # No whitespace before us, so select any wordcharacters there.
+      pos1="${#matched_words[start]}"
+    fi
+
+    if [[ -n "${matched_words[ws-after-cursor]}" ]]; then
+      if [[ -n "${matched_words[ws-before-cursor]}" ]] || (( CURSOR == 0 )); then
+        # whitespace either side, select it
+	(( pos1 = CURSOR - ${#matched_words[ws-before-cursor]} ))
+	(( pos2 = CURSOR + ${#matched_words[ws-after-cursor]} ))
+      else
+	# There's whitespace at the cursor position, so only select
+	# up to the cursor position.
+	(( pos2 = CURSOR + 1 ))
+      fi
+    else
+      # No whitespace at the cursor position, so select the
+      # current character and any following wordcharacters.
+      (( pos2 = CURSOR + ${#matched_words[word-after-cursor]} ))
+    fi
+
+    if [[ $style = a ]]; then
+      if [[ -n "${matched_words[ws-after-cursor]}"  && ( -n "${matched_words[ws-before-cursor]}" || CURSOR -eq 0 ) ]]; then
+	# in the middle of whitespace so grab a word
+	if [[ -n "${matched_words[word-after-cursor]}" ]]; then
+	  (( pos2 += ${#matched_words[word-after-cursor]} )) # preferably the one after
+	else
+	  (( pos1 -= ${#matched_words[word-before-cursor]} )) # otherwise the one before
+	fi
+      elif [[ -n "${matched_words[ws-after-word]}" ]]; then
+	(( pos2 += ${#matched_words[ws-after-word]} ))
+      elif [[ -n "${matched_words[ws-before-cursor]}" ]]; then
+	# couldn't grab whitespace forwards so try backwards
+	(( pos1 -= ${#matched_words[ws-before-cursor]} ))
+      elif (( pos1 > 0 )); then
+	# There might have been whitespace before the word
+	(( CURSOR = pos1 ))
+	match-words-by-style
+	if [[ -n "${matched_words[ws-before-cursor]}" ]]; then
+	  (( pos1 -= ${#matched_words[ws-before-cursor]} ))
+	fi
+      fi
+    fi
+
+    (( MARK = pos1, CURSOR = pos2-1 ))
+  fi
+done
+
+if [[ $KEYMAP == vicmd ]] && (( !REGION_ACTIVE )); then
+  (( CURSOR++ )) # Need to include cursor position for operators
+fi
diff --git a/Functions/Zle/surround b/Functions/Zle/surround
index b7be30b75..b51b77c04 100644
--- a/Functions/Zle/surround
+++ b/Functions/Zle/surround
@@ -19,6 +19,7 @@ local before after
 local -A matching
 matching=( \( \) \{ \} \< \> \[ \] )
 
+zle -f vichange
 case $WIDGET in
   change-*)
     local MARK="$MARK" CURSOR="$CURSOR" call
@@ -69,7 +70,11 @@ case $WIDGET in
       before="${(k)matching[(r)[$before:q]]}"
     fi
     CUTBUFFER="$before$CUTBUFFER$after"
-    zle .vi-put-after -n 1
+    if [[ CURSOR -eq 0 || $BUFFER[CURSOR] = $'\n' ]]; then
+      zle .vi-put-before -n 1
+    else
+      zle .vi-put-after -n 1
+    fi
     CUTBUFFER="$save_cut" CURSOR="$save_cur"
   ;;
 esac
diff --git a/Functions/Zle/vi-pipe b/Functions/Zle/vi-pipe
new file mode 100644
index 000000000..1729cb6e1
--- /dev/null
+++ b/Functions/Zle/vi-pipe
@@ -0,0 +1,39 @@
+# Example of a widget that takes a vi motion
+
+# Filter part of buffer corresponding to a vi motion through an external
+# program.
+
+# To enable with vi compatible bindings use:
+#   autoload -Uz vi-pipe
+#   bindkey -a '!' vi-pipe
+
+setopt localoptions noksharrays
+
+autoload -Uz read-from-minibuffer
+local _save_cut="$CUTBUFFER" REPLY
+
+# mark this widget as a vi change so it can be repeated as a whole
+zle -f vichange
+
+# force movement to default to line mode
+(( REGION_ACTIVE )) || zle -U V
+# Use the standard vi-change to accept a vi motion.
+zle .vi-change || return
+read-from-minibuffer "!"
+zle .vi-cmd-mode
+local _save_cur=$CURSOR
+
+# cut buffer contains the deleted text and can be modified
+CUTBUFFER=$(eval "$REPLY" <<<"$CUTBUFFER")
+
+# put the modified text back in position.
+if [[ CURSOR -eq 0 || $BUFFER[CURSOR] = $'\n' ]]; then
+  # at the beginning of a line, vi-delete won't have moved the cursor
+  # back to a previous line
+  zle .vi-put-before -n 1
+else
+  zle .vi-put-after -n 1
+fi
+
+# restore cut buffer and cursor to the start of the range
+CUTBUFFER="$_save_cut" CURSOR="$_save_cur"
diff --git a/Functions/Zle/zcalc-auto-insert b/Functions/Zle/zcalc-auto-insert
index c9a5c8867..e1affd1c3 100644
--- a/Functions/Zle/zcalc-auto-insert
+++ b/Functions/Zle/zcalc-auto-insert
@@ -1,6 +1,7 @@
 # Bind to a binary operator keystroke for use with zcalc
+# Not useful in RPN mode.
 
-if [[ -n $ZCALC_ACTIVE ]]; then
+if [[ -n $ZCALC_ACTIVE && $ZCALC_ACTIVE != rpn ]]; then
   if [[ $CURSOR -eq 0 || $LBUFFER[-1] = "(" ]]; then
     LBUFFER+=${ZCALC_AUTO_INSERT_PREFIX:-"ans "}
   fi
diff --git a/LICENCE b/LICENCE
index 1fb1fc4e2..08fcf88ff 100644
--- a/LICENCE
+++ b/LICENCE
@@ -10,7 +10,7 @@ simply be omitted.
 
 --
 
-The Z Shell is copyright (c) 1992-2009 Paul Falstad, Richard Coleman,
+The Z Shell is copyright (c) 1992-2017 Paul Falstad, Richard Coleman,
 Zoltán Hidvégi, Andrew Main, Peter Stephenson, Sven Wischnowsky, and
 others.  All rights reserved.  Individual authors, whether or not
 specifically named, retain copyright in all changes; in what follows, they
diff --git a/Makefile.in b/Makefile.in
index cb74e94ef..ae18855ff 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -151,8 +151,7 @@ config.modules: $(sdir)/config.h.in config.status config.modules.sh
 	$(SHELL) ./config.modules.sh
 
 $(sdir)/config.h.in: $(sdir)/stamp-h.in
-$(sdir)/stamp-h.in: $(sdir)/configure.ac \
-		$(sdir)/aclocal.m4 $(sdir)/aczsh.m4
+$(sdir)/stamp-h.in: $(sdir)/configure
 	cd $(sdir) && autoheader
 	echo > $(sdir)/stamp-h.in
 
diff --git a/Misc/vcs_info-examples b/Misc/vcs_info-examples
index 766eb82a4..58dd8cf98 100644
--- a/Misc/vcs_info-examples
+++ b/Misc/vcs_info-examples
@@ -31,7 +31,7 @@ precmd() {
     psvar=()
 
     vcs_info
-    [[ -n $vcs_info_msg_0_ ]] && psvar[1]="$vcs_info_msg_0_"
+    [[ -n $vcs_info_msg_0_ ]] && print -v 'psvar[1]' -Pr -- "$vcs_info_msg_0_"
 }
 
 # You can now use `%1v' to drop the $vcs_info_msg_0_ contents in your prompt;
diff --git a/NEWS b/NEWS
index 15822ad34..568b1609a 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,65 @@ CHANGES FROM PREVIOUS VERSIONS OF ZSH
 
 Note also the list of incompatibilities in the README file.
 
+Changes from 5.3.1 to 5.4
+-------------------------
+
+The 'exec' and 'command' precommand modifiers, and options to them, are
+now parsed after parameter expansion.  Previously, both the modifier and
+any options to it were parsed between alias expansion and parameter
+expansion (see zshexpn(1)), so they could neither be quoted nor be the
+result of parameter expansion.  Examples: 's=command; $s -V ls' and
+'\command -V ls' now work as expected.
+
+
+Changes from 5.2 to 5.3.1
+-------------------------
+
+There are only minor compatibility fixes between 5.3 and 5.3.1.
+
+It is possible to enable character width support for Unicode 9 by
+configuring with `--enable-unicode9'; this compiles in some additional
+tables.  At some point this support may move into a module, in which
+case the configure option will be changed to cause the module to be
+permanently loaded.  This option is not useful unless your terminal also
+supports Unicode 9.
+
+The new word modifier ':P' computes the physical path of the argument.
+It is different from the existing ':a' modifier which always resolves
+'/before/here/../after' to '/before/after', and differs from the
+existing ':A' modifier which resolves symlinks only after 'here/..' is
+removed, even when /before/here is itself a symbolic link.  It is
+recommended to review uses of ':A' and, if appropriate, convert them
+to ':P' as soon as compatibility with 5.2 is no longer a requirement.
+
+The output of "typeset -p" uses "export" commands or the "-g" option
+for parameters that are not local to the current scope.  Previously,
+all output was in the form of "typeset" commands, never using "-g".
+
+vi-repeat-change can repeat user-defined widgets if the widget calls
+zle -f vichange.
+
+The parameter $registers now makes the contents of vi register buffers
+available to user-defined widgets.
+
+New vi-up-case and vi-down-case builtin widgets bound to gU/gu (or U/u
+in visual mode) for doing case conversion.
+
+A new select-word-match function provides vim-style text objects with
+configurable word boundaries using the existing match-words-by-style
+mechanism.
+
+Support for the conditional expression [[ -v var ]] to test if a
+variable is set for compatibility with other shells.
+
+The print and printf builtins have a new option -v to assign the output
+to a variable. This is for bash compatibility but with the additional
+feature that, for an array, a separate element is used each time the
+format is reused.
+
+New x: syntax in completion match specifications make it possible to
+disable match specifications hardcoded in completion functions.
+
 Changes from 5.1.1 to 5.2
 -------------------------
 
diff --git a/README b/README
index d5343db19..432a35e76 100644
--- a/README
+++ b/README
@@ -5,10 +5,10 @@ THE Z SHELL (ZSH)
 Version
 -------
 
-This is version 5.2 of the shell.  This is a stable release.  There are
-a few visible improvements since 5.1.1 as well as many bugfixes.  Note
+This is version 5.3.1 of the shell.  This is a stable release.  There are
+a few visible improvements since 5.2 as well as many bugfixes.  Note
 in particular the changs highlighted under "Incompatibilites
-between 5.1 and 5.2" below.  See NEWS for more information.
+between 5.2 and 5.3.1" below.  See NEWS for more information.
 
 Installing Zsh
 --------------
@@ -29,8 +29,53 @@ Zsh is a shell with lots of features.  For a list of some of these, see the
 file FEATURES, and for the latest changes see NEWS.  For more
 details, see the documentation.
 
-Incompatibilities between 5.2 and 5.3
--------------------------------------
+Incompatibilities since 5.3.1
+-----------------------------
+
+1) The default behaviour of code like the following has changed:
+
+  alias foo='noglob foo'
+  foo() { print function body; }
+
+When this is encountered in a start-up file, or other place where input
+was read line by line, "foo" is in command position and is expanded as
+an alias before the function definition takes place.  In previous
+versions of the shell, this caused two functions "noglob" and "foo" to
+be defined.  Any expansion of an alias in a function definition is
+nearly always an unintended effect, as well as hard to detect, so has
+been made an error.  (The option setting NO_MULTI_FUNC_DEF turned this
+case into an error, but did not help with other cases and is off by
+default.)  The alternative, of not expanding the alias, was rejected as
+it was more difficult to achieve in the parser and also would silently
+change the shell's behaviur between versions.  A new option,
+ALIAS_FUNC_DEF, has been added, which can be set to make the shell
+behave as in previous versions.  It is in any case recommended to use
+the "function" keyword, as aliases are not expanded afterwards.
+
+2) It was an undocumented, and largely useless, feature that a function
+autoloaded with an absolute path was searched for along the normal fpath
+(as if the leading / was missing) and, if found, loaded under the full
+name including the leading slash.  This has been replaced with the more
+useful feature that the function is searched for only at the given
+absolute path; the name of the function is the base name of the file.
+Note that functions including a non-leading / behave as before,
+e.g. if `dir/name' is found anywhere under a directory in $fpath it is
+loaded as a function named `dir/name'.
+
+3) vcs_info: When neither a set-patch-format nor a gen-applied-string
+(resp. gen-unapplied-string) hook is set, vcs_info now '%'-escapes the
+applied-string (resp. unapplied-string) before interpolating it into the
+patch-format string, to prevent literal `%' signs in the interpolated
+value from being interpreted as prompt escape sequences.  If you use
+${vcs_info_msg_0_} in a context other than the shell prompt, you may need
+to undo the escaping with:
+
+    print -v vcs_info_msg_0_ -Pr -- "${vcs_info_msg_0_}"
+
+This is also needed if $vcs_info_msg_0_ is used to set $psvar.
+
+Incompatibilities between 5.0.8 and 5.3
+----------------------------------------
 
 1) In character classes delimited by "[" and "]" within patterns, whether
 used for filename generation (globbing) or other forms of pattern
@@ -79,8 +124,85 @@ Other aspects of EXIT trap handling have not changed --- there is still
 only one EXIT trap at any point in a programme, so it is not generally
 useful to combine POSIX and non-POSIX behaviour in the same script.
 
-Incompatibilities between 5.0.8 and 5.2
----------------------------------------
+4) There was an undocumented feature dating from the early days of zsh
+that glob qualifiers consisting only of the digits 0 to 7 were treated
+as an octal file mode to "and" with the modes of files being tested.
+This has been removed in order to be more sensitive to syntax errors.
+The "f" qualifier has for many years been the documented way of testing
+file modes; it allows the "and" test ("*(f+1)" is the documented
+equivalent of "*(1)") as well as many other forms.
+
+5) The completion helper function _arguments now escapes both backslashes
+and colons in the values of option arguments when populating the $opt_args
+associative array.  Previously, colons were escaped with a backslash but
+backslashes were not themselves escaped with a backslash, which lead to
+ambiguity: '-x foo\:bar' (one argument with a backslashed colon) and
+'-x foo\\ bar' (two arguments, and the first one ends in a backslash) would
+both set $opt_args[-x] to the same value.  This example assumes the -x
+option's spec declared two arguments, as in:
+    _arguments : -x:foo:${action}:bar:$action
+
+For the more common case of non-repeatable options that take a single
+argument, completion functions now have to unescape not only colons but
+also backslashes when obtaining the option's argument from $opt_args.
+
+6) Previously, if the function command_not_found_handler was run
+in place of a command-not-found error, and the function returned
+non-zero status, zsh set the status to 127 and printed an error message
+anyway.  Now, the status from the handler is retained and no additional
+message is printed.  The main reasons for this change are that it was not
+possible to return a non-zero status to the parent shell from a command
+executed as a replacement, and the new implementation is more consistent
+with other shells.
+
+7) The output of "typeset -p" (and synonyms) now takes into account the
+function scope and export state of each parameter.  Exported parameters
+are output as "export" commands unless the parameter is also local, and
+other parameters not local to the scope are output with the "-g" option.
+Previously, only "typeset" commands were output, never using "-g".
+
+8) At spelling-correction prompt ($SPROMPT), where the choices offered are
+[nyae], previously <Enter> would be accepted to mean [N] and <Space> and
+<Tab> would be accepted to mean [Y].  Now <Space> and <Tab> are invalid
+choices: typing either of them remains at the prompt.
+
+9) The $ary[i,j] subscript syntax to take a slice of an array behaves
+differently when both i and j are larger than the number of elements in
+the array.  When i == j, such a slice always yields an empty array, and
+when i < j it always yields an array of one empty string element.  The
+following example illustrates how this differs from past versions.
+
+     nargs() { print $# }
+     a=(one two)
+     for i in 1 2 3 4; do
+      for j in 1 2 3 4 5; do
+       print -n "$i $j => "
+       nargs "${(@)a[i,j]}"
+      done
+     done
+     
+     5.2       |  5.3 **
+     ----------+----------
+     1 1 => 1  |  1 1 => 1
+     1 2 => 2  |  1 2 => 2
+     1 3 => 2  |  1 3 => 2
+     1 4 => 2  |  1 4 => 2
+     1 5 => 2  |  1 5 => 2
+     2 1 => 0  |  2 1 => 0
+     2 2 => 1  |  2 2 => 1
+     2 3 => 1  |  2 3 => 1
+     2 4 => 1  |  2 4 => 1
+     2 5 => 1  |  2 5 => 1
+     3 1 => 0  |  3 1 => 0
+     3 2 => 0  |  3 2 => 0
+     3 3 => 0  |  3 3 => 0
+     3 4 => 0  |  3 4 => 1   **
+     3 5 => 0  |  3 5 => 1   **
+     4 1 => 0  |  4 1 => 0
+     4 2 => 0  |  4 2 => 0
+     4 3 => 0  |  4 3 => 0
+     4 4 => 1  |  4 4 => 0   **
+     4 5 => 1  |  4 5 => 1
 
 The behaviour of the parameter flag (P) has changed when it appears
 in a nested parameter group, in order to make it more useful in
diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c
index 70625edca..835e72cb7 100644
--- a/Src/Builtins/sched.c
+++ b/Src/Builtins/sched.c
@@ -353,7 +353,11 @@ schedgetfn(UNUSED(Param pm))
 	time_t t;
 
 	t = sch->time;
-	sprintf(tbuf, "%ld", t);
+#if defined(PRINTF_HAS_LLD)
+	sprintf(tbuf, "%lld", (long long)t);
+#else
+	sprintf(tbuf, "%ld", (long)t);
+#endif
 	if (sch->flags & SCHEDFLAG_TRASH_ZLE)
 	    flagstr = "-o";
 	else
diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c
index 63c6748f5..a60dfcbf8 100644
--- a/Src/Modules/curses.c
+++ b/Src/Modules/curses.c
@@ -1082,15 +1082,7 @@ zccmd_input(const char *nam, char **args)
 #endif
 
     /*
-     * Some documentation for wgetch() says:
-
-       The behavior of getch and friends in the presence of  handled  signals
-       is  unspecified  in the SVr4 and XSI Curses documentation.  Under his-
-       torical curses implementations, it varied  depending  on  whether  the
-       operating system's implementation of handled signal receipt interrupts
-       a read(2) call in progress or not, and also (in some  implementations)
-       depending  on  whether  an input timeout or non-blocking mode has been
-       set.
+     * Linux, OS X, FreeBSD documentation for wgetch() mentions:
 
        Programmers concerned about portability should be prepared for  either
        of  two cases: (a) signal receipt does not interrupt getch; (b) signal
@@ -1098,21 +1090,16 @@ zccmd_input(const char *nam, char **args)
        EINTR.  Under the ncurses implementation, handled signals never inter-
        rupt getch.
 
-     * The observed behavior, however, is different:  wgetch() consistently
-     * returns ERR with EINTR when a signal is handled by the shell "trap"
-     * command mechanism.  Further, it consistently returns ERR twice, the
-     * second time without even attempting to repeat the interrupted read,
-     * which has the side-effect of NOT updating errno.  A third call will
-     * then begin reading again.
-     *
-     * Therefore, to properly implement signal trapping, we must (1) call
-     * wgetch() in a loop as long as errno remains EINTR, and (2) clear
-     * errno only before beginning the loop, not on every pass.
+     * Some observed behavior: wgetch() returns ERR with EINTR when a signal is
+     * handled by the shell "trap" command mechanism. Observed that it returns
+     * ERR twice, the second time without even attempting to repeat the
+     * interrupted read. Third call will then begin reading again.
      *
-     * There remains a potential bug here in that, if the caller has set
-     * a timeout for the read [see zccmd_timeout()] the countdown is very
-     * likely restarted on every call to wgetch(), so an interrupted call
-     * might wait much longer than desired.
+     * Because of widespread of previous implementation that called wget*ch
+     * possibly indefinitely many times after ERR/EINTR, and because of the
+     * above observation, wget_wch call is repeated after each ERR/EINTR, but
+     * errno is being reset (it wasn't) and the loop to all means should break.
+     * Problem: the timeout may be waited twice.
      */
     errno = 0;
 
@@ -1120,6 +1107,7 @@ zccmd_input(const char *nam, char **args)
     while ((ret = wget_wch(w->win, &wi)) == ERR) {
 	if (errno != EINTR || errflag || retflag || breaks || exit_pending)
 	    break;
+        errno = 0;
     }
     switch (ret) {
     case OK:
@@ -1146,6 +1134,7 @@ zccmd_input(const char *nam, char **args)
     while ((ci = wgetch(w->win)) == ERR) {
 	if (errno != EINTR || errflag || retflag || breaks || exit_pending)
 	    return 1;
+        errno = 0;
     }
     if (ci >= 256) {
 	keypadnum = ci;
@@ -1501,6 +1490,74 @@ zccmd_touch(const char *nam, char **args)
     return ret;
 }
 
+static int
+zccmd_resize(const char *nam, char **args)
+{
+#ifdef HAVE_RESIZE_TERM
+    int y, x, do_endwin=0, do_save=1;
+    LinkNode stdscr_win = zcurses_getwindowbyname("stdscr");
+
+    if (stdscr_win) {
+        y = atoi(args[0]);
+        x = atoi(args[1]);
+        if (args[2]) {
+            if (0 == strcmp(args[2], "endwin")) {
+                do_endwin=1;
+            } else if (0 == strcmp(args[2], "endwin_nosave")) {
+                do_endwin=1;
+                do_save=0;
+            } else if (0 == strcmp(args[2], "nosave")) {
+                do_save=0;
+            } else {
+                zwarnnam(nam, "`resize' expects `endwin', `nosave' or `endwin_nosave' for third argument, if given");
+            }
+        }
+
+        if (y == 0 && x == 0 && args[2] == NULL) {
+            // Special case to just test that curses has resize_term. #ifdef
+            // HAVE_RESIZE_TERM will result in return value 2 if resize_term
+            // is not available.
+            return 0;
+        } else {
+            // Without this call some window moves are innacurate. Tested on
+            // OS X ncurses 5.4, Homebrew ncursesw 6.0-2, Arch Linux ncursesw
+            // 6.0, Ubuntu 14.04 ncurses 5.9, FreeBSD ncursesw.so.8
+            //
+            // On the other hand, the whole resize goal can be (from tests)
+            // accomplished by calling endwin and refresh. But to secure any
+            // future problems, resize_term is provided, and it is featured
+            // with endwin, so that users have multiple options.
+            if (do_endwin) {
+                endwin();
+            }
+
+            if( resize_term( y, x ) == OK ) {
+                // Things work without this, but we need to get out from
+                // endwin (i.e. call refresh), and in theory store new
+                // curses state (the resize might have changed it), which
+                // should be presented to terminal only after refresh.
+                if (do_endwin || do_save) {
+                    ZCWin w;
+                    w = (ZCWin)getdata(stdscr_win);
+                    wnoutrefresh(w->win);
+                    doupdate();
+                }
+
+                if (do_save) {
+                    gettyinfo(&curses_tty_state);
+                }
+                return 0;
+            } else {
+                return 1;
+            }
+        }
+    } else {
+        return 1;
+    }
+#else
+    return 2;
+#endif
+}
 
 /*********************
   Main builtin handler
@@ -1534,6 +1591,7 @@ bin_zcurses(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	{"mouse", zccmd_mouse, 0, -1},
 	{"querychar", zccmd_querychar, 1, 2},
 	{"touch", zccmd_touch, 1, -1},
+	{"resize", zccmd_resize, 2, 3},
 	{NULL, (zccmd_t)0, 0, 0}
     };
 
diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index bb82c542f..6e9047bc5 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -133,11 +133,15 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
 
     len = 0;
     for (x=0; x < 4; x++) {
-        if ((len = ztrftime(buffer, bufsize, argv[0], t, 0L)) >= 0)
+        if ((len = ztrftime(buffer, bufsize, argv[0], t, 0L)) >= 0 || x==3)
 	    break;
 	buffer = zrealloc(buffer, bufsize *= 2);
     }
-    DPUTS(len < 0, "bad output from ztrftime");
+    if (len < 0) {
+	zwarnnam(nam, "bad/unsupported format: '%s'", argv[0]);
+	zfree(buffer, bufsize);
+	return 1;
+    }
 
     if (scalar) {
 	setsparam(scalar, metafy(buffer, len, META_DUP));
diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c
index 8dd60fc0d..35254b68c 100644
--- a/Src/Modules/db_gdbm.c
+++ b/Src/Modules/db_gdbm.c
@@ -6,6 +6,9 @@
  * Copyright (c) 2008 Clint Adams
  * All rights reserved.
  *
+ * Modifications copyright (c) 2017 Sebastian Gniazdowski
+ * All rights reserved.
+ *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and to distribute modified versions of this software for any
@@ -31,6 +34,16 @@
 #include "db_gdbm.mdh"
 #include "db_gdbm.pro"
 
+#ifndef PM_UPTODATE
+#define PM_UPTODATE     (1<<19) /* Parameter has up-to-date data (e.g. loaded from DB) */
+#endif
+
+static Param createhash( char *name, int flags );
+static int append_tied_name( const char *name );
+static int remove_tied_name( const char *name );
+static char *unmetafy_zalloc(const char *to_copy, int *new_len);
+static void set_length(char *buf, int size);
+
 /*
  * Make sure we have all the bits I'm using for memory mapping, otherwise
  * I don't know what I'm doing.
@@ -41,8 +54,34 @@
 
 static char *backtype = "db/gdbm";
 
-static const struct gsu_scalar gdbm_gsu =
-{ gdbmgetfn, gdbmsetfn, gdbmunsetfn };
+/*
+ * Longer GSU structure, to carry GDBM_FILE of owning
+ * database. Every parameter (hash value) receives GSU
+ * pointer and thus also receives GDBM_FILE - this way
+ * parameters can access proper database.
+ *
+ * Main HashTable parameter has the same instance of
+ * the custom GSU struct in u.hash->tmpdata field.
+ * When database is closed, `dbf` field is set to NULL
+ * and hash values know to not access database when
+ * being unset (total purge at zuntie).
+ *
+ * When database closing is ended, custom GSU struct
+ * is freed. Only new ztie creates new custom GSU
+ * struct instance.
+ */
+
+struct gsu_scalar_ext {
+    struct gsu_scalar std;
+    GDBM_FILE dbf;
+    char *dbfile_path;
+};
+
+/* Source structure - will be copied to allocated one,
+ * with `dbf` filled. `dbf` allocation <-> gsu allocation. */
+static const struct gsu_scalar_ext gdbm_gsu_ext =
+{ { gdbmgetfn, gdbmsetfn, gdbmunsetfn }, 0, 0 };
+
 /**/
 static const struct gsu_hash gdbm_hash_gsu =
 { hashgetfn, gdbmhashsetfn, gdbmhashunsetfn };
@@ -50,6 +89,17 @@ static const struct gsu_hash gdbm_hash_gsu =
 static struct builtin bintab[] = {
     BUILTIN("ztie", 0, bin_ztie, 1, -1, 0, "d:f:r", NULL),
     BUILTIN("zuntie", 0, bin_zuntie, 1, -1, 0, "u", NULL),
+    BUILTIN("zgdbmpath", 0, bin_zgdbmpath, 1, -1, 0, "", NULL),
+};
+
+#define ROARRPARAMDEF(name, var) \
+    { name, PM_ARRAY | PM_READONLY, (void *) var, NULL,  NULL, NULL, NULL }
+
+/* Holds names of all tied parameters */
+char **zgdbm_tied;
+
+static struct paramdef patab[] = {
+    ROARRPARAMDEF( "zgdbm_tied", &zgdbm_tied ),
 };
 
 /**/
@@ -77,8 +127,7 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func))
     }
 
     /* Here should be a lookup of the backend type against
-     * a registry.
-     */
+     * a registry, if generam DB mechanism is to be added */
     if (strcmp(OPT_ARG(ops, 'd'), backtype) != 0) {
         zwarnnam(nam, "unsupported backend type `%s'", OPT_ARG(ops, 'd'));
 	return 1;
@@ -92,7 +141,8 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func))
 	/*
 	 * Unset any existing parameter.  Note there's no implicit
 	 * "local" here, but if the existing parameter is local
-	 * that will be reflected in the new one.
+	 * then new parameter will be also local without following
+         * unset.
 	 *
 	 * We need to do this before attempting to open the DB
 	 * in case this variable is already tied to a DB.
@@ -105,16 +155,17 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func))
 	    return 1;
     }
 
+    gdbm_errno=0;
     dbf = gdbm_open(resource_name, 0, read_write, 0666, 0);
-    if(dbf)
-	addmodulefd(gdbm_fdesc(dbf), FDT_INTERNAL);
-    else {
-	zwarnnam(nam, "error opening database file %s", resource_name);
+    if(dbf) {
+	addmodulefd(gdbm_fdesc(dbf), FDT_MODULE);
+        append_tied_name(pmname);
+    } else {
+	zwarnnam(nam, "error opening database file %s (%s)", resource_name, gdbm_strerror(gdbm_errno));
 	return 1;
     }
 
-    if (!(tied_param = createspecialhash(pmname, &getgdbmnode, &scangdbmkeys,
-					 pmflags))) {
+    if (!(tied_param = createhash(pmname, pmflags))) {
         zwarnnam(nam, "cannot create the requested parameter %s", pmname);
 	fdtable[gdbm_fdesc(dbf)] = FDT_UNUSED;
 	gdbm_close(dbf);
@@ -122,8 +173,23 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func))
     }
 
     tied_param->gsu.h = &gdbm_hash_gsu;
-    tied_param->u.hash->tmpdata = (void *)dbf;
 
+    /* Allocate parameter sub-gsu, fill dbf field. 
+     * dbf allocation is 1 to 1 accompanied by
+     * gsu_scalar_ext allocation. */
+
+    struct gsu_scalar_ext *dbf_carrier = (struct gsu_scalar_ext *) zalloc(sizeof(struct gsu_scalar_ext));
+    dbf_carrier->std = gdbm_gsu_ext.std;
+    dbf_carrier->dbf = dbf;
+    tied_param->u.hash->tmpdata = (void *)dbf_carrier;
+
+    /* Fill also file path field */
+    if (*resource_name != '/') {
+        /* Code copied from check_autoload() */
+        resource_name = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", resource_name);
+        resource_name = xsymlink(resource_name, 1);
+    }
+    dbf_carrier->dbfile_path = ztrdup(resource_name);
     return 0;
 }
 
@@ -162,6 +228,53 @@ bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func))
 }
 
 /**/
+static int
+bin_zgdbmpath(char *nam, char **args, Options ops, UNUSED(int func))
+{
+    Param pm;
+    char *pmname;
+
+    pmname = *args;
+
+    if (!pmname) {
+        zwarnnam(nam, "parameter name (whose path is to be written to $REPLY) is required");
+        return 1;
+    }
+
+    pm = (Param) paramtab->getnode(paramtab, pmname);
+    if(!pm) {
+        zwarnnam(nam, "no such parameter: %s", pmname);
+        return 1;
+    }
+
+    if (pm->gsu.h != &gdbm_hash_gsu) {
+        zwarnnam(nam, "not a tied gdbm parameter: %s", pmname);
+        return 1;
+    }
+
+    /* Paranoia, it *will* be always set */
+    if (((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbfile_path) {
+        setsparam("REPLY", ztrdup(((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbfile_path));
+    } else {
+        setsparam("REPLY", ztrdup(""));
+    }
+
+    return 0;
+}
+
+/*
+ * The param is actual param in hash – always, because
+ * getgdbmnode creates every new key seen. However, it
+ * might be not PM_UPTODATE - which means that database
+ * wasn't yet queried.
+ *
+ * It will be left in this state if database doesn't
+ * contain such key. That might be a drawback, maybe
+ * setting to empty value has sense, as no other writer
+ * can exist. This would remove subtle hcalloc(1) leak.
+ */
+
+/**/
 static char *
 gdbmgetfn(Param pm)
 {
@@ -169,18 +282,58 @@ gdbmgetfn(Param pm)
     int ret;
     GDBM_FILE dbf;
 
-    key.dptr = pm->node.nam;
-    key.dsize = strlen(key.dptr) + 1;
+    /* Key already retrieved? There is no sense of asking the
+     * database again, because:
+     * - there can be only multiple readers
+     * - so, no writer + reader use is allowed
+     *
+     * Thus:
+     * - if we are writers, we for sure have newest copy of data
+     * - if we are readers, we for sure have newest copy of data
+     */
+    if ( pm->node.flags & PM_UPTODATE ) {
+        return pm->u.str ? pm->u.str : (char *) hcalloc(1);
+    }
+
+    /* Unmetafy key. GDBM fits nice into this
+     * process, as it uses length of data */
+    int umlen = 0;
+    char *umkey = unmetafy_zalloc(pm->node.nam,&umlen);
+
+    key.dptr = umkey;
+    key.dsize = umlen;
+
+    dbf = ((struct gsu_scalar_ext *)pm->gsu.s)->dbf;
+
+    if((ret = gdbm_exists(dbf, key))) {
+        /* We have data – store it, return it */
+        pm->node.flags |= PM_UPTODATE;
 
-    dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
-    ret = gdbm_exists(dbf, key);
-    if(ret) {
         content = gdbm_fetch(dbf, key);
-    } else {
-        content.dptr = dupstring("");
+
+        /* Ensure there's no leak */
+        if (pm->u.str) {
+            zsfree(pm->u.str);
+        }
+
+        /* Metafy returned data. All fits - metafy
+         * can obtain data length to avoid using \0 */
+        pm->u.str = metafy(content.dptr, content.dsize, META_DUP);
+
+        /* Free key, restoring its original length */
+        set_length(umkey, umlen);
+        zsfree(umkey);
+
+        /* Can return pointer, correctly saved inside hash */
+        return pm->u.str;
     }
 
-    return content.dptr;
+    /* Free key, restoring its original length */
+    set_length(umkey, umlen);
+    zsfree(umkey);
+
+    /* Can this be "" ? */
+    return (char *) hcalloc(1);
 }
 
 /**/
@@ -190,78 +343,128 @@ gdbmsetfn(Param pm, char *val)
     datum key, content;
     GDBM_FILE dbf;
 
-    key.dptr = pm->node.nam;
-    key.dsize = strlen(key.dptr) + 1;
-    content.dptr = val;
-    content.dsize = strlen(content.dptr) + 1;
+    /* Set is done on parameter and on database.
+     * See the allowed workers / readers comment
+     * at gdbmgetfn() */
 
-    dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
-    (void)gdbm_store(dbf, key, content, GDBM_REPLACE);
+    /* Parameter */
+    if (pm->u.str) {
+        zsfree(pm->u.str);
+        pm->u.str = NULL;
+        pm->node.flags &= ~(PM_UPTODATE);
+    }
+
+    if (val) {
+        pm->u.str = ztrdup(val);
+        pm->node.flags |= PM_UPTODATE;
+    }
+
+    /* Database */
+    dbf = ((struct gsu_scalar_ext *)pm->gsu.s)->dbf;
+    if (dbf) {
+        int umlen = 0;
+        char *umkey = unmetafy_zalloc(pm->node.nam,&umlen);
+
+        key.dptr = umkey;
+        key.dsize = umlen;
+
+        if (val) {
+            /* Unmetafy with exact zalloc size */
+            char *umval = unmetafy_zalloc(val,&umlen);
+
+            /* Store */
+            content.dptr = umval;
+            content.dsize = umlen;
+            (void)gdbm_store(dbf, key, content, GDBM_REPLACE);
+
+            /* Free */
+            set_length(umval, umlen);
+            zsfree(umval);
+        } else {
+            (void)gdbm_delete(dbf, key);
+        }
+
+        /* Free key */
+        set_length(umkey, key.dsize);
+        zsfree(umkey);
+    }
 }
 
 /**/
 static void
 gdbmunsetfn(Param pm, UNUSED(int um))
 {
-    datum key;
-    GDBM_FILE dbf;
-
-    key.dptr = pm->node.nam;
-    key.dsize = strlen(key.dptr) + 1;
-
-    dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
-    (void)gdbm_delete(dbf, key);
+    /* Set with NULL */
+    gdbmsetfn(pm, NULL);
 }
 
 /**/
 static HashNode
 getgdbmnode(HashTable ht, const char *name)
 {
-    int len;
-    char *nameu;
-    Param pm = NULL;
-
-    nameu = dupstring(name);
-    unmetafy(nameu, &len);
-
-    pm = (Param) hcalloc(sizeof(struct param));
-    pm->node.nam = nameu;
-    pm->node.flags = PM_SCALAR;
-    pm->gsu.s = &gdbm_gsu;
-    pm->u.hash = ht;
+    HashNode hn = gethashnode2( ht, name );
+    Param val_pm = (Param) hn;
+
+    /* Entry for key doesn't exist? Create it now,
+     * it will be interfacing between the database
+     * and Zsh - through special gdbm_gsu. So, any
+     * seen key results in new interfacing parameter.
+     *
+     * Previous code was returning heap arena Param
+     * that wasn't actually added to the hash. It was
+     * plainly name / database-key holder. Here we
+     * add the Param to its hash, it is not PM_UPTODATE.
+     * It will be loaded from database *and filled*
+     * or left in that state if the database doesn't
+     * contain it.
+     *
+     * No heap arena memory is used, memory usage is
+     * now limited - by number of distinct keys seen,
+     * not by number of key *uses*.
+     * */
+
+    if ( ! val_pm ) {
+        val_pm = (Param) zshcalloc( sizeof (*val_pm) );
+        val_pm->node.flags = PM_SCALAR | PM_HASHELEM; /* no PM_UPTODATE */
+        val_pm->gsu.s = (GsuScalar) ht->tmpdata;
+        ht->addnode( ht, ztrdup( name ), val_pm ); // sets pm->node.nam
+    }
 
-    return &pm->node;
+    return (HashNode) val_pm;
 }
 
 /**/
 static void
 scangdbmkeys(HashTable ht, ScanFunc func, int flags)
 {
-    Param pm = NULL;
-    datum key, content;
-    GDBM_FILE dbf = (GDBM_FILE)(ht->tmpdata);
-
-    pm = (Param) hcalloc(sizeof(struct param));
-
-    pm->node.flags = PM_SCALAR;
-    pm->gsu.s = &nullsetscalar_gsu;
+    datum key;
+    GDBM_FILE dbf = ((struct gsu_scalar_ext *)ht->tmpdata)->dbf;
 
+    /* Iterate keys adding them to hash, so
+     * we have Param to use in `func` */
     key = gdbm_firstkey(dbf);
 
     while(key.dptr) {
-	content = gdbm_fetch(dbf, key);
+        /* This returns database-interfacing Param,
+         * it will return u.str or first fetch data
+         * if not PM_UPTODATE (newly created) */
+        char *zkey = metafy(key.dptr, key.dsize, META_DUP);
+        HashNode hn = getgdbmnode(ht, zkey);
+        zsfree( zkey );
 
-	pm->node.nam = key.dptr;
-	pm->u.str = content.dptr;
-	pm->gsu.s = &nullsetscalar_gsu;
-
-	func(&pm->node, flags);
+	func(hn, flags);
 
+        /* Iterate - no problem as interfacing Param
+         * will do at most only fetches, not stores */
         key = gdbm_nextkey(dbf, key);
     }
 
 }
 
+/*
+ * Replace database with new hash
+ */
+
 /**/
 static void
 gdbmhashsetfn(Param pm, HashTable ht)
@@ -274,7 +477,7 @@ gdbmhashsetfn(Param pm, HashTable ht)
     if (!pm->u.hash || pm->u.hash == ht)
 	return;
 
-    if (!(dbf = (GDBM_FILE)(pm->u.hash->tmpdata)))
+    if (!(dbf = ((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbf))
 	return;
 
     key = gdbm_firstkey(dbf);
@@ -292,6 +495,9 @@ gdbmhashsetfn(Param pm, HashTable ht)
     if (!ht)
 	return;
 
+     /* Put new strings into database, waiting
+      * for their interfacing-Params to be created */
+
     for (i = 0; i < ht->hsize; i++)
 	for (hn = ht->nodes[i]; hn; hn = hn->next) {
 	    struct value v;
@@ -301,16 +507,31 @@ gdbmhashsetfn(Param pm, HashTable ht)
 	    v.arr = NULL;
 	    v.pm = (Param) hn;
 
-	    key.dptr = v.pm->node.nam;
-	    key.dsize = strlen(key.dptr) + 1;
+            /* Unmetafy key */
+            int umlen = 0;
+            char *umkey = unmetafy_zalloc(v.pm->node.nam,&umlen);
+
+	    key.dptr = umkey;
+	    key.dsize = umlen;
 
 	    queue_signals();
 
-	    content.dptr = getstrvalue(&v);
-	    content.dsize = strlen(content.dptr) + 1;
+            /* Unmetafy */
+            char *umval = unmetafy_zalloc(getstrvalue(&v),&umlen);
 
+            /* Store */
+	    content.dptr = umval;
+	    content.dsize = umlen;
 	    (void)gdbm_store(dbf, key, content, GDBM_REPLACE);	
 
+            /* Free - unmetafy_zalloc allocates exact required
+             * space, however unmetafied string can have zeros
+             * in content, so we must first fill with non-0 bytes */
+            set_length(umval, content.dsize);
+            zsfree(umval);
+            set_length(umkey, key.dsize);
+            zsfree(umkey);
+
 	    unqueue_signals();
 	}
 }
@@ -319,15 +540,19 @@ gdbmhashsetfn(Param pm, HashTable ht)
 static void
 gdbmuntie(Param pm)
 {
-    GDBM_FILE dbf = (GDBM_FILE)(pm->u.hash->tmpdata);
+    GDBM_FILE dbf = ((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbf;
     HashTable ht = pm->u.hash;
 
     if (dbf) { /* paranoia */
 	fdtable[gdbm_fdesc(dbf)] = FDT_UNUSED;
-	gdbm_close(dbf);
-    }
+        gdbm_close(dbf);
 
-    ht->tmpdata = NULL;
+        /* Let hash fields know there's no backend */
+        ((struct gsu_scalar_ext *)ht->tmpdata)->dbf = NULL;
+
+        /* Remove from list of tied parameters */
+        remove_tied_name(pm->node.nam);
+    }
 
     /* for completeness ... createspecialhash() should have an inverse */
     ht->getnode = ht->getnode2 = gethashnode2;
@@ -342,20 +567,28 @@ static void
 gdbmhashunsetfn(Param pm, UNUSED(int exp))
 {
     gdbmuntie(pm);
-    /* hash table is now normal, so proceed normally... */
+
+    /* Remember custom GSU structure assigned to
+     * u.hash->tmpdata before hash gets deleted */
+    struct gsu_scalar_ext * gsu_ext = pm->u.hash->tmpdata;
+
+    /* Uses normal unsetter. Will delete all owned
+     * parameters and also hashtable. */
     pm->gsu.h->setfn(pm, NULL);
+
+    /* Don't need custom GSU structure with its
+     * GDBM_FILE pointer anymore */
+    zsfree( gsu_ext->dbfile_path );
+    zfree( gsu_ext, sizeof(struct gsu_scalar_ext));
+
     pm->node.flags |= PM_UNSET;
 }
 
-#else
-# error no gdbm
-#endif /* have gdbm */
-
 static struct features module_features = {
     bintab, sizeof(bintab)/sizeof(*bintab),
     NULL, 0,
     NULL, 0,
-    NULL, 0,
+    patab, sizeof(patab)/sizeof(*patab),
     0
 };
 
@@ -385,6 +618,7 @@ enables_(Module m, int **enables)
 int
 boot_(UNUSED(Module m))
 {
+    zgdbm_tied = zshcalloc((1) * sizeof(char *));
     return 0;
 }
 
@@ -392,6 +626,7 @@ boot_(UNUSED(Module m))
 int
 cleanup_(Module m)
 {
+    /* This frees `zgdbm_tied` */
     return setfeatureenables(m, &module_features, NULL);
 }
 
@@ -401,3 +636,153 @@ finish_(UNUSED(Module m))
 {
     return 0;
 }
+
+/*********************
+ * Utility functions *
+ *********************/
+
+static Param createhash( char *name, int flags ) {
+    Param pm;
+    HashTable ht;
+
+    pm = createparam(name, PM_SPECIAL | PM_HASHED);
+    if (!pm) {
+        return NULL;
+    }
+
+    if (pm->old)
+	pm->level = locallevel;
+
+    /* This creates standard hash. */
+    ht = pm->u.hash = newparamtable(32, name);
+    if (!pm->u.hash) {
+        paramtab->removenode(paramtab, name);
+        paramtab->freenode(&pm->node);
+        zwarnnam(name, "Out of memory when allocating hash");
+    }
+
+    /* These provide special features */
+    ht->getnode = ht->getnode2 = getgdbmnode;
+    ht->scantab = scangdbmkeys;
+
+    return pm;
+}
+
+/*
+ * Adds parameter name to `zgdbm_tied`
+ */
+
+static int append_tied_name( const char *name ) {
+    int old_len = arrlen(zgdbm_tied);
+    char **new_zgdbm_tied = zshcalloc( (old_len+2) * sizeof(char *));
+
+    /* Copy */
+    char **p = zgdbm_tied;
+    char **dst = new_zgdbm_tied;
+    while (*p) {
+        *dst++ = *p++;
+    }
+
+    /* Append new one */
+    *dst = ztrdup(name);
+
+    /* Substitute, free old one */
+    zfree(zgdbm_tied, sizeof(char *) * (old_len + 1));
+    zgdbm_tied = new_zgdbm_tied;
+
+    return 0;
+}
+
+/*
+ * Removes parameter name from `zgdbm_tied`
+ */
+
+static int remove_tied_name( const char *name ) {
+    int old_len = arrlen(zgdbm_tied);
+
+    /* Two stage, to always have arrlen() == zfree-size - 1.
+     * Could do allocation and revert when `not found`, but
+     * what would be better about that. */
+
+    /* Find one to remove */
+    char **p = zgdbm_tied;
+    while (*p) {
+        if (0==strcmp(name,*p)) {
+            break;
+        }
+        p++;
+    }
+
+    /* Copy x+1 to x */
+    while (*p) {
+        *p=*(p+1);
+        p++;
+    }
+
+    /* Second stage. Size changed? Only old_size-1
+     * change is possible, but.. paranoia way */
+    int new_len = arrlen(zgdbm_tied);
+    if (new_len != old_len) {
+        char **new_zgdbm_tied = zshcalloc((new_len+1) * sizeof(char *));
+
+        /* Copy */
+        p = zgdbm_tied;
+        char **dst = new_zgdbm_tied;
+        while (*p) {
+            *dst++ = *p++;
+        }
+        *dst = NULL;
+
+        /* Substitute, free old one */
+        zfree(zgdbm_tied, sizeof(char *) * (old_len + 1));
+        zgdbm_tied = new_zgdbm_tied;
+    }
+
+    return 0;
+}
+
+/*
+ * Unmetafy that:
+ * - duplicates bufer to work on it,
+ * - does zalloc of exact size for the new string,
+ * - restores work buffer to original content, to restore strlen
+ *
+ * No zsfree()-confusing string will be produced.
+ */
+static char *unmetafy_zalloc(const char *to_copy, int *new_len) {
+    char *work, *to_return;
+    int my_new_len = 0;
+
+    work = ztrdup(to_copy);
+    work = unmetafy(work,&my_new_len);
+
+    if (new_len)
+        *new_len = my_new_len;
+
+    /* This string can be correctly zsfree()-d */
+    to_return = (char *) zalloc((my_new_len+1)*sizeof(char));
+    memcpy(to_return, work, sizeof(char)*my_new_len); // memcpy handles $'\0'
+    to_return[my_new_len]='\0';
+
+    /* Restore original strlen and correctly free */
+    strcpy(work, to_copy);
+    zsfree(work);
+
+    return to_return;
+}
+
+/*
+ * For zsh-allocator, rest of Zsh seems to use
+ * free() instead of zsfree(), and such length
+ * restoration causes slowdown, but all is this
+ * way strict - correct */
+static void set_length(char *buf, int size) {
+    buf[size]='\0';
+    while ( -- size >= 0 ) {
+        buf[size]=' ';
+    }
+}
+
+#else
+# error no gdbm
+#endif /* have gdbm */
diff --git a/Src/Modules/db_gdbm.mdd b/Src/Modules/db_gdbm.mdd
index ce7926bd9..210c22177 100644
--- a/Src/Modules/db_gdbm.mdd
+++ b/Src/Modules/db_gdbm.mdd
@@ -7,6 +7,6 @@ fi
 '
 load=no
 
-autofeatures="b:ztie b:zuntie"
+autofeatures="b:ztie b:zuntie b:zgdbmpath p:zgdbm_tied"
 
 objects="db_gdbm.o"
diff --git a/Src/Modules/example.c b/Src/Modules/example.c
index 45ca2cffa..c80c9e7b2 100644
--- a/Src/Modules/example.c
+++ b/Src/Modules/example.c
@@ -69,7 +69,8 @@ bin_example(char *nam, char **args, Options ops, UNUSED(int func))
     intparam = i;
     zsfree(strparam);
     strparam = ztrdup(*oargs ? *oargs : "");
-    freearray(arrparam);
+    if (arrparam)
+	freearray(arrparam);
     arrparam = zarrdup(oargs);
     return 0;
 }
diff --git a/Src/Modules/mathfunc.c b/Src/Modules/mathfunc.c
index 451b3cfeb..a7e8b294c 100644
--- a/Src/Modules/mathfunc.c
+++ b/Src/Modules/mathfunc.c
@@ -411,7 +411,11 @@ math_func(char *name, int argc, mnumber *argv, int id)
       break;
 
   case MF_SCALB:
+#ifdef HAVE_SCALBN
+      retd = scalbn(argd, argi);
+#else
       retd = scalb(argd, argi);
+#endif
       break;
 
 #ifdef HAVE_SIGNGAM
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index edb051785..10c47d214 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -167,7 +167,7 @@ unsetpmcommand(Param pm, UNUSED(int exp))
 
 /**/
 static void
-setpmcommands(UNUSED(Param pm), HashTable ht)
+setpmcommands(Param pm, HashTable ht)
 {
     int i;
     HashNode hn;
@@ -190,7 +190,15 @@ setpmcommands(UNUSED(Param pm), HashTable ht)
 
 	    cmdnamtab->addnode(cmdnamtab, ztrdup(hn->nam), &cn->node);
 	}
-    deleteparamtable(ht);
+    /*
+     * On full-array assignment ht is a temporary hash with the default
+     * get/set functions, whereas pm->u.hash has the special $commands
+     * get/set functions.  Do not assign ht to pm, just delete it.
+     *
+     * On append, ht and pm->u.hash are the same table, don't delete.
+     */
+    if (ht != pm->u.hash)
+	deleteparamtable(ht);
 }
 
 static const struct gsu_scalar pmcommand_gsu =
@@ -330,7 +338,7 @@ unsetpmfunction(Param pm, UNUSED(int exp))
 
 /**/
 static void
-setfunctions(UNUSED(Param pm), HashTable ht, int dis)
+setfunctions(Param pm, HashTable ht, int dis)
 {
     int i;
     HashNode hn;
@@ -349,7 +357,9 @@ setfunctions(UNUSED(Param pm), HashTable ht, int dis)
 
 	    setfunction(hn->nam, ztrdup(getstrvalue(&v)), dis);
 	}
-    deleteparamtable(ht);
+    /* See setpmcommands() above */
+    if (ht != pm->u.hash)
+	deleteparamtable(ht);
 }
 
 /**/
@@ -487,11 +497,6 @@ scanfunctions(UNUSED(HashTable ht), ScanFunc func, int flags, int dis)
 			    strcat(pm.u.str, " \"$@\"");
 			} else
 			    pm.u.str = dyncat(start, t);
-			/*
-			 * TBD: Is this unmetafy correct?  Surely as this
-			 * is a parameter value it stays metafied?
-			 */
-			unmetafy(pm.u.str, NULL);
 			zsfree(t);
 
 			if (shf->redir) {
@@ -520,6 +525,98 @@ scanpmdisfunctions(HashTable ht, ScanFunc func, int flags)
     scanfunctions(ht, func, flags, DISABLED);
 }
 
+/* Functions for the functions_source special parameter. */
+
+/* Retrieve the source file for a function by explicit name */
+
+/**/
+static HashNode
+getfunction_source(UNUSED(HashTable ht), const char *name, int dis)
+{
+    Shfunc shf;
+    Param pm = NULL;
+
+    pm = (Param) hcalloc(sizeof(struct param));
+    pm->node.nam = dupstring(name);
+    pm->node.flags = PM_SCALAR|PM_READONLY;
+    pm->gsu.s = dis ? &pmdisfunction_gsu :  &pmfunction_gsu;
+
+    if ((shf = (Shfunc) shfunctab->getnode2(shfunctab, name)) &&
+	(dis ? (shf->node.flags & DISABLED) : !(shf->node.flags & DISABLED))) {
+	pm->u.str = getshfuncfile(shf);
+	if (!pm->u.str)
+	    pm->u.str = dupstring("");
+    }
+    return &pm->node;
+}
+
+/* Retrieve the source file for functions by scanning the table */
+
+/**/
+static void
+scanfunctions_source(UNUSED(HashTable ht), ScanFunc func, int flags, int dis)
+{
+    struct param pm;
+    int i;
+    HashNode hn;
+
+    memset((void *)&pm, 0, sizeof(struct param));
+    pm.node.flags = PM_SCALAR|PM_READONLY;
+    pm.gsu.s = dis ? &pmdisfunction_gsu : &pmfunction_gsu;
+
+    for (i = 0; i < shfunctab->hsize; i++) {
+	for (hn = shfunctab->nodes[i]; hn; hn = hn->next) {
+	    if (dis ? (hn->flags & DISABLED) : !(hn->flags & DISABLED)) {
+		pm.node.nam = hn->nam;
+		if (func != scancountparams &&
+		    ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
+		     !(flags & SCANPM_WANTKEYS))) {
+		    pm.u.str = getshfuncfile((Shfunc)hn);
+		    if (!pm.u.str)
+			pm.u.str = dupstring("");
+		}
+		func(&pm.node, flags);
+	    }
+	}
+    }
+}
+
+/* Param table entry for retrieving functions_source element */
+
+/**/
+static HashNode
+getpmfunction_source(HashTable ht, const char *name)
+{
+    return getfunction_source(ht, name, 0);
+}
+
+/* Param table entry for retrieving ds_functions_source element */
+
+/**/
+static HashNode
+getpmdisfunction_source(HashTable ht, const char *name)
+{
+    return getfunction_source(ht, name, 1);
+}
+
+/* Param table entry for scanning functions_source table */
+
+/**/
+static void
+scanpmfunction_source(HashTable ht, ScanFunc func, int flags)
+{
+    scanfunctions_source(ht, func, flags, 0);
+}
+
+/* Param table entry for scanning dis_functions_source table */
+
+/**/
+static void
+scanpmdisfunction_source(HashTable ht, ScanFunc func, int flags)
+{
+    scanfunctions_source(ht, func, flags, 1);
+}
+
 /* Functions for the funcstack special parameter. */
 
 /**/
@@ -850,7 +947,7 @@ unsetpmoption(Param pm, UNUSED(int exp))
 
 /**/
 static void
-setpmoptions(UNUSED(Param pm), HashTable ht)
+setpmoptions(Param pm, HashTable ht)
 {
     int i;
     HashNode hn;
@@ -875,7 +972,9 @@ setpmoptions(UNUSED(Param pm), HashTable ht)
 			      (val && strcmp(val, "off")), 0, opts))
 		zwarn("can't change option: %s", hn->nam);
 	}
-    deleteparamtable(ht);
+    /* See setpmcommands() above */
+    if (ht != pm->u.hash)
+	deleteparamtable(ht);
 }
 
 static const struct gsu_scalar pmoption_gsu =
@@ -1414,7 +1513,7 @@ unsetpmnameddir(Param pm, UNUSED(int exp))
 
 /**/
 static void
-setpmnameddirs(UNUSED(Param pm), HashTable ht)
+setpmnameddirs(Param pm, HashTable ht)
 {
     int i;
     HashNode hn, next, hd;
@@ -1456,7 +1555,9 @@ setpmnameddirs(UNUSED(Param pm), HashTable ht)
 
     i = opts[INTERACTIVE];
     opts[INTERACTIVE] = 0;
-    deleteparamtable(ht);
+    /* See setpmcommands() above */
+    if (ht != pm->u.hash)
+	deleteparamtable(ht);
     opts[INTERACTIVE] = i;
 }
 
@@ -1637,7 +1738,7 @@ unsetpmsalias(Param pm, UNUSED(int exp))
 
 /**/
 static void
-setaliases(HashTable alht, UNUSED(Param pm), HashTable ht, int flags)
+setaliases(HashTable alht, Param pm, HashTable ht, int flags)
 {
     int i;
     HashNode hn, next, hd;
@@ -1673,7 +1774,9 @@ setaliases(HashTable alht, UNUSED(Param pm), HashTable ht, int flags)
 		alht->addnode(alht, ztrdup(hn->nam),
 			      createaliasnode(ztrdup(val), flags));
 	}
-    deleteparamtable(ht);
+    /* See setpmcommands() above */
+    if (ht != pm->u.hash)
+	deleteparamtable(ht);
 }
 
 /**/
@@ -2100,6 +2203,8 @@ static struct paramdef partab[] = {
 	    NULL, getpmdisbuiltin, scanpmdisbuiltins),
     SPECIALPMDEF("dis_functions", 0, 
 	    &pmdisfunctions_gsu, getpmdisfunction, scanpmdisfunctions),
+    SPECIALPMDEF("dis_functions_source", PM_READONLY, NULL,
+		 getpmdisfunction_source, scanpmdisfunction_source),
     SPECIALPMDEF("dis_galiases", 0,
 	    &pmdisgaliases_gsu, getpmdisgalias, scanpmdisgaliases),
     SPECIALPMDEF("dis_patchars", PM_ARRAY|PM_READONLY,
@@ -2116,6 +2221,8 @@ static struct paramdef partab[] = {
 	    &funcstack_gsu, NULL, NULL),
     SPECIALPMDEF("functions", 0, &pmfunctions_gsu, getpmfunction,
 		 scanpmfunctions),
+    SPECIALPMDEF("functions_source", PM_READONLY, NULL,
+		 getpmfunction_source, scanpmfunction_source),
     SPECIALPMDEF("functrace", PM_ARRAY|PM_READONLY,
 	    &functrace_gsu, NULL, NULL),
     SPECIALPMDEF("galiases", 0,
diff --git a/Src/Modules/regex.c b/Src/Modules/regex.c
index edb7234d4..d02769ef0 100644
--- a/Src/Modules/regex.c
+++ b/Src/Modules/regex.c
@@ -111,7 +111,7 @@ zcond_regex_match(char **a, int id)
 		*x = NULL;
 	    }
 	    if (isset(BASHREMATCH)) {
-		setaparam("BASH_REMATCH", arr);
+		assignaparam("BASH_REMATCH", arr, 0);
 	    } else {
 		zlong offs;
 		char *ptr;
@@ -119,7 +119,7 @@ zcond_regex_match(char **a, int id)
 
 		m = matches;
 		s = metafy(lhstr + m->rm_so, m->rm_eo - m->rm_so, META_DUP);
-		setsparam("MATCH", s);
+		assignsparam("MATCH", s, 0);
 		/*
 		 * Count the characters before the match.
 		 */
@@ -133,7 +133,7 @@ zcond_regex_match(char **a, int id)
 		    ptr += clen;
 		    leftlen -= clen;
 		}
-		setiparam("MBEGIN", offs + !isset(KSHARRAYS));
+		assigniparam("MBEGIN", offs + !isset(KSHARRAYS), 0);
 		/*
 		 * Add on the characters in the match.
 		 */
@@ -144,7 +144,7 @@ zcond_regex_match(char **a, int id)
 		    ptr += clen;
 		    leftlen -= clen;
 		}
-		setiparam("MEND", offs + !isset(KSHARRAYS) - 1);
+		assigniparam("MEND", offs + !isset(KSHARRAYS) - 1, 0);
 		if (nelem) {
 		    char **mbegin, **mend, **bptr, **eptr;
 		    bptr = mbegin = (char **)zalloc(sizeof(char *)*(nelem+1));
diff --git a/Src/Modules/system.c b/Src/Modules/system.c
index 1ee61c00b..3eecd7e95 100644
--- a/Src/Modules/system.c
+++ b/Src/Modules/system.c
@@ -313,7 +313,7 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func))
     int flags = O_NOCTTY | append | ((append || write) ?
 	(read ? O_RDWR : O_WRONLY) : O_RDONLY);
     char *opt, *ptr, *nextopt, *fdvar;
-    int o, fd, explicit = -1;
+    int o, fd, moved_fd, explicit = -1;
     mode_t perms = 0666;
 #if defined(FD_CLOEXEC) && !defined(O_CLOEXEC)
     int fdflags;
@@ -376,22 +376,32 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func))
 	zwarnnam(nam, "can't open file %s: %e", *args, errno);
 	return 1;
     }
-    fd = (explicit > -1) ? redup(fd, explicit) : movefd(fd);
-    if (fd == -1) {
+    moved_fd = (explicit > -1) ? redup(fd, explicit) : movefd(fd);
+    if (moved_fd == -1) {
 	zwarnnam(nam, "can't open file %s", *args);
 	return 1;
     }
 
-#if defined(FD_CLOEXEC) && !defined(O_CLOEXEC)
+#ifdef FD_CLOEXEC
+#ifdef O_CLOEXEC
+    /*
+     * the O_CLOEXEC is a flag attached to the *file descriptor*, not the
+     * *open file description* so it doesn't survive a dup(). If that flag was
+     * requested and the fd was moved, we need to reapply it to the moved fd
+     * even if the original one was open with O_CLOEXEC
+     */
+    if ((flags & O_CLOEXEC) && fd != moved_fd)
+#else
     if (fdflags)
-	fcntl(fd, F_SETFD, FD_CLOEXEC);
-#endif
+#endif /* O_CLOEXEC */
+	fcntl(moved_fd, F_SETFD, FD_CLOEXEC);
+#endif /* FD_CLOEXEC */
     if (explicit == -1) {
-	fdtable[fd] = FDT_EXTERNAL;
-	setiparam(fdvar, fd);
-	/* if setting the variable failed, close fd to avoid leak */
+	fdtable[moved_fd] = FDT_EXTERNAL;
+	setiparam(fdvar, moved_fd);
+	/* if setting the variable failed, close moved_fd to avoid leak */
 	if (errflag)
-	    zclose(fd);
+	    zclose(moved_fd);
     }
 
     return 0;
@@ -521,7 +531,7 @@ static int
 bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 {
     int cloexec = 1, unlock = 0, readlock = 0;
-    time_t timeout = 0;
+    zlong timeout = -1;
     char *fdvar = NULL;
 #ifdef HAVE_FCNTL_H
     struct flock lck;
@@ -573,7 +583,7 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 		} else {
 		    optarg = *args++;
 		}
-		timeout = (time_t)mathevali(optarg);
+		timeout = mathevali(optarg);
 		break;
 
 	    case 'u':
@@ -650,7 +660,7 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	    sleep(1);
 	}
     } else {
-	while (fcntl(flock_fd, F_SETLKW, &lck) < 0) {
+	while (fcntl(flock_fd, timeout == 0 ? F_SETLK : F_SETLKW, &lck) < 0) {
 	    if (errflag)
 		return 1;
 	    if (errno == EINTR)
diff --git a/Src/Modules/system.mdd b/Src/Modules/system.mdd
index eed0c1b9d..00a3e7896 100644
--- a/Src/Modules/system.mdd
+++ b/Src/Modules/system.mdd
@@ -15,7 +15,12 @@ errnames.c: errnames1.awk errnames2.awk $(dir_top)/config.h @ERRNO_H@
 		touch errtmp.out; \
 	   else \
 		$(AWK) -f $(sdir)/errnames1.awk @ERRNO_H@ >errtmp.c; \
-		$(CPP) errtmp.c >errtmp.out; \
+		case "`$(CPP) --version </dev/null 2>&1`" in \
+		*"Free Software Foundation"*) \
+		$(CPP) -P errtmp.c >errtmp.out;; \
+		*) \
+		$(CPP) errtmp.c >errtmp.out;; \
+		esac; \
 	   fi
 	   $(AWK) -f $(sdir)/errnames2.awk errtmp.out > $@
 	   rm -f errtmp.c errtmp.out
diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c
index dec12142b..0bbce5d49 100644
--- a/Src/Modules/tcp.c
+++ b/Src/Modules/tcp.c
@@ -343,7 +343,8 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func))
 {
     int herrno, err=1, destport, force=0, verbose=0, test=0, targetfd=0;
     ZSOCKLEN_T  len;
-    char **addrp, *desthost, *localname, *remotename;
+    char **addrp, *desthost;
+    const char *localname, *remotename;
     struct hostent *zthost = NULL, *ztpeer = NULL;
     struct servent *srv;
     Tcp_session sess = NULL;
diff --git a/Src/Modules/terminfo.c b/Src/Modules/terminfo.c
index e0439afca..bbd325899 100644
--- a/Src/Modules/terminfo.c
+++ b/Src/Modules/terminfo.c
@@ -99,7 +99,7 @@ bin_echoti(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 	return 1;
     }
     /* check that the number of arguments provided is not too high */
-    if (arrlen(argv) > 9) {
+    if (arrlen_gt(argv, 9)) {
         zwarnnam(name, "too many arguments");
         return 1;
     }
diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c
index 0ef753915..3c1bef58f 100644
--- a/Src/Modules/zpty.c
+++ b/Src/Modules/zpty.c
@@ -331,6 +331,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock)
 	/* This code copied from the clone module, except for getting *
 	 * the descriptor from get_pty() and duplicating it to 0/1/2. */
 
+	deletehookfunc("exit", ptyhook);
 	clearjobtab(0);
 	ppid = getppid();
 	mypid = getpid();
@@ -544,7 +545,8 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch)
 	p = dupstring(args[1]);
 	tokenize(p);
 	remnulargs(p);
-	if (!(prog = patcompile(p, PAT_STATIC, NULL))) {
+	/* Signals handlers might stomp PAT_STATIC */
+	if (!(prog = patcompile(p, PAT_ZDUP, NULL))) {
 	    zwarnnam(nam, "bad pattern: %s", args[1]);
 	    return 1;
 	}
@@ -682,9 +684,14 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch)
 	write_loop(1, buf, used);
     }
 
-    if (seen && (!prog || matchok || !mustmatch))
-	return 0;
-    return cmd->fin + 1;
+    {
+	int ret = cmd->fin + 1;
+	if (seen && (!prog || matchok || !mustmatch))
+	    ret = 0;
+	if (prog)
+	    freepatprog(prog);
+	return ret;
+    }
 }
 
 static int
@@ -846,6 +853,7 @@ bin_zpty(char *nam, char **args, Options ops, UNUSED(int func))
     }
 }
 
+/**/
 static int
 ptyhook(UNUSED(Hookdef d), UNUSED(void *dummy))
 {
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 90d8faf2e..82542cf4f 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -472,7 +472,7 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	Patprog prog;
 	char *pat;
 
-	if (arrlen(args) < 2) {
+	if (arrlen_lt(args, 2)) {
 	    zwarnnam(nam, "not enough arguments");
 	    return 1;
 	}
@@ -491,7 +491,7 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	Style s;
 	char *context, *stylename;
 
-	switch (arrlen(args)) {
+	switch (arrlen_ge(args, 3) ? 3 : arrlen(args)) {
 	case 2:
 	    context = args[0];
 	    stylename = args[1];
@@ -510,25 +510,33 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	    zwarnnam(nam, "too many arguments");
 	    return 1;
 	}
+
+	queue_signals();	/* Protect PAT_STATIC */
+
 	if (context) {
 	    tokenize(context);
 	    zstyle_contprog = patcompile(context, PAT_STATIC, NULL);
 
-	    if (!zstyle_contprog)
+	    if (!zstyle_contprog) {
+		unqueue_signals();
 		return 1;
+	    }
 	} else
 	    zstyle_contprog = NULL;
 
 	if (stylename) {
 	    s = (Style)zstyletab->getnode2(zstyletab, stylename);
-	    if (!s)
+	    if (!s) {
+		unqueue_signals();
 		return 1;
+	    }
 	    zstyletab->printnode(&s->node, list);
 	} else {
 	    scanhashtable(zstyletab, 1, 0, 0,
 			  zstyletab->printnode, list);
 	}
 
+	unqueue_signals();
 	return 0;
     }
     switch (args[0][1]) {
@@ -675,14 +683,20 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	    char **vals;
 	    Patprog prog;
 
+	    queue_signals();	/* Protect PAT_STATIC */
+
 	    tokenize(args[3]);
 
 	    if ((vals = lookupstyle(args[1], args[2])) &&
 		(prog = patcompile(args[3], PAT_STATIC, NULL))) {
 		while (*vals)
-		    if (pattry(prog, *vals++))
+		    if (pattry(prog, *vals++)) {
+			unqueue_signals();
 			return 0;
+		    }
 	    }
+
+	    unqueue_signals();
 	    return 1;
 	}
 	break;
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 023c41814..3e9834560 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -125,7 +125,7 @@ struct cmatch {
 #define CMF_REMOVE   (1<< 1)	/* remove the suffix */
 #define CMF_ISPAR    (1<< 2)	/* is paramter expansion */
 #define CMF_PARBR    (1<< 3)	/* paramter expansion with a brace */
-#define CMF_PARNEST  (1<< 4)	/* nested paramter expansion */
+#define CMF_PARNEST  (1<< 4)	/* nested parameter expansion */
 #define CMF_NOLIST   (1<< 5)	/* should not be listed */
 #define CMF_DISPLINE (1<< 6)	/* display strings one per line */
 #define CMF_HIDE     (1<< 7)	/* temporarily hide this one */
@@ -153,16 +153,24 @@ struct cmatcher {
     Cpattern line;		/* what matches on the line */
     int llen;			/* length of line pattern */
     Cpattern word;		/* what matches in the word */
-    int wlen;			/* length of word pattern */
+    int wlen;			/* length of word pattern, or:
+				    -1: word pattern is one asterisk
+				    -2: word pattern is two asterisks */
     Cpattern left;		/* left anchor */
     int lalen;			/* length of left anchor */
     Cpattern right;		/* right anchor */
     int ralen;			/* length of right anchor */
 };
 
+/* Flags for cmatcher::flags */
+/* Upon match, insert the string from the line rather than the string
+ * from the trial completion ("word"). */
 #define CMF_LINE  1
+/* Match with an anchor on the left. */
 #define CMF_LEFT  2
+/* Match with an anchor on the right. */
 #define CMF_RIGHT 4
+/* ... */
 #define CMF_INTER 8
 
 /*
@@ -229,7 +237,6 @@ struct cpattern {
  * the anchor. */
 
 typedef struct cline *Cline;
-typedef struct clsub Clsub;
 
 struct cline {
     Cline next;
@@ -285,14 +292,14 @@ struct menuinfo {
 
 /* Flags for compadd and addmatches(). */
 
-#define CAF_QUOTE    1
-#define CAF_NOSORT   2
-#define CAF_MATCH    4
-#define CAF_UNIQCON  8
-#define CAF_UNIQALL 16
-#define CAF_ARRAYS  32
-#define CAF_KEYS    64
-#define CAF_ALL    128
+#define CAF_QUOTE    1    /* compadd -Q: positional arguments already quoted */
+#define CAF_NOSORT   2    /* compadd -V: don't sort */
+#define CAF_MATCH    4    /* compadd without -U: do matching */
+#define CAF_UNIQCON  8    /* compadd -2: don't deduplicate */
+#define CAF_UNIQALL 16    /* compadd -1: deduplicate */
+#define CAF_ARRAYS  32    /* compadd -a or -k: array/assoc parameter names */
+#define CAF_KEYS    64    /* compadd -k: assoc parameter names */
+#define CAF_ALL    128    /* compadd -C: _all_matches */
 
 /* Data for compadd and addmatches() */
 
@@ -367,7 +374,7 @@ typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int);
 #define CP_QISUFFIX    (1 <<  CPN_QISUFFIX)
 #define CPN_COMPSTATE  9
 #define CP_COMPSTATE   (1 <<  CPN_COMPSTATE)
-
+/* See comprpms */
 #define CP_REALPARAMS  10
 #define CP_ALLREALS    ((unsigned int) 0x3ff)
 
@@ -424,7 +431,7 @@ typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int);
 #define CP_QUOTES      (1 << CPN_QUOTES)
 #define CPN_IGNORED    25
 #define CP_IGNORED     (1 << CPN_IGNORED)
-
+/* See compkpms */
 #define CP_KEYPARAMS   26
 #define CP_ALLKEYS     ((unsigned int) 0x3ffffff)
 
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 0c32be14b..5fc15b0d3 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -425,6 +425,7 @@ do_completion(UNUSED(Hookdef dummy), Compldat dat)
 	}
     } else {
 	invalidatelist();
+	lastambig = isset(BASHAUTOLIST);
 	if (forcelist)
 	    clearlist = 1;
 	zlemetacs = 0;
@@ -835,6 +836,7 @@ callcompfunc(char *s, char *fn)
 	endparamscope();
 	lastcmd = 0;
 	incompfunc = icf;
+	startauto = 0;
 
 	if (!complist)
 	    uselist = 0;
@@ -882,8 +884,13 @@ callcompfunc(char *s, char *fn)
 		useline = 1, usemenu = 1;
 	    else if (strpfx("auto", compinsert))
 		useline = 1, usemenu = 2;
-	    else
+	    else {
 		useline = usemenu = 0;
+		/* if compstate[insert] was emptied, no unambiguous prefix
+		 * ever gets inserted so allow the next tab to already start
+		 * menu completion */
+		startauto = lastambig = isset(AUTOMENU);
+	    }
 
 	    if (useline && (p = strchr(compinsert, ':'))) {
 		insmnum = atoi(++p);
@@ -896,7 +903,7 @@ callcompfunc(char *s, char *fn)
 #endif
 	    }
 	}
-	startauto = ((compinsert &&
+	startauto = startauto || ((compinsert &&
 		      !strcmp(compinsert, "automenu-unambiguous")) ||
 		     (bashlistfirst && isset(AUTOMENU) &&
                       (!compinsert || !*compinsert)));
@@ -1043,6 +1050,13 @@ makecomplist(char *s, int incmd, int lst)
     }
 }
 
+/*
+ * Quote 's' according to compqstack, aka $compstate[all_quotes].
+ *
+ * If 'ign' is 1, skip the innermost quoting level.  Otherwise 'ign'
+ * must be 0.
+ */
+
 /**/
 mod_export char *
 multiquote(char *s, int ign)
@@ -1050,12 +1064,11 @@ multiquote(char *s, int ign)
     if (s) {
 	char *os = s, *p = compqstack;
 
-	if (p && *p && (ign < 1 || p[ign])) {
-	    if (ign > 0)
-		p += ign;
+	if (p && *p && (ign == 0 || p[1])) {
+	    if (ign)
+		p++;
 	    while (*p) {
-		if (ign >= 0 || p[1])
-		    s = quotestring(s, NULL, *p);
+		s = quotestring(s, *p);
 		p++;
 	    }
 	}
@@ -1065,6 +1078,12 @@ multiquote(char *s, int ign)
     return NULL;
 }
 
+/*
+ * tildequote(s, ign): Equivalent to multiquote(s, ign), except that if
+ * compqstack[0] == QT_BACKSLASH and s[0] == '~', then that tilde is not
+ * quoted.
+ */
+
 /**/
 mod_export char *
 tildequote(char *s, int ign)
@@ -1964,6 +1983,11 @@ get_user_var(char *nam)
     }
 }
 
+/*
+ * If KEYS, then NAME is an associative array; return its keys.
+ * Else, NAME is a plain array; return its elements.
+ */
+
 static char **
 get_data_arr(char *name, int keys)
 {
@@ -2025,16 +2049,17 @@ addmatch(char *str, int flags, char ***dispp, int line)
 int
 addmatches(Cadata dat, char **argv)
 {
+    /* ms: "match string" - string to use as completion.
+     * Overloaded at one place as a temporary. */
     char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
     char **aign = NULL, **dparr = NULL, *oaq = autoq, *oppre = dat->ppre;
     char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL, *ibuf = NULL;
     char **arrays = NULL;
-    int lpl, lsl, pl, sl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
+    int lpl, lsl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
     int ppl = 0, psl = 0, ilen = 0;
     int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern;
     int isexact, doadd, ois = instring, oib = inbackt;
     Cline lc = NULL, pline = NULL, sline = NULL;
-    Cmatch cm;
     struct cmlist mst;
     Cmlist oms = mstack;
     Patprog cp = NULL, *pign = NULL;
@@ -2193,9 +2218,14 @@ addmatches(Cadata dat, char **argv)
 
 	    /* Test if there is an existing -P prefix. */
 	    if (dat->pre && *dat->pre) {
-		pl = pfxlen(dat->pre, lpre);
-		llpl -= pl;
-		lpre += pl;
+		int prefix_length = pfxlen(dat->pre, lpre);
+		if (dat->pre[prefix_length] == '\0' ||
+		    lpre[prefix_length] == '\0') {
+		    /* $compadd_args[-P] is a prefix of ${PREFIX}, or
+		     * vice-versa. */
+		    llpl -= prefix_length;
+		    lpre += prefix_length;
+		}
 	    }
 	}
 	/* Now duplicate the strings we have from the command line. */
@@ -2407,6 +2437,7 @@ addmatches(Cadata dat, char **argv)
 	if (dat->psuf)
 	    psl = strlen(dat->psuf);
 	for (; (s = *argv); argv++) {
+	    int sl;
 	    bpl = obpl;
 	    bsl = obsl;
 	    if (disp) {
@@ -2467,6 +2498,7 @@ addmatches(Cadata dat, char **argv)
 		goto next_array;
 	    }
 	    if (doadd) {
+		Cmatch cm;
 		Brinfo bp;
 
 		for (bp = obpl; bp; bp = bp->next)
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index ce4576297..9e6ccb404 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -99,7 +99,7 @@ freecompctlp(HashNode hn)
 }
 
 /**/
-void
+static void
 freecompctl(Compctl cc)
 {
     if (cc == &cc_default ||
@@ -142,7 +142,7 @@ freecompctl(Compctl cc)
 }
 
 /**/
-void
+static void
 freecompcond(void *a)
 {
     Compcond cc = (Compcond) a;
@@ -186,7 +186,7 @@ freecompcond(void *a)
 }
 
 /**/
-int
+static int
 compctlread(char *name, char **args, Options ops, char *reply)
 {
     char *buf, *bptr;
@@ -1400,7 +1400,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
 	    untokenize(p);
 	    quotedzputs(p, stdout);
 	} else
-	    quotedzputs(quotestring(s, NULL, QT_BACKSLASH), stdout);
+	    quotedzputs(quotestring(s, QT_BACKSLASH), stdout);
     }
 
     /* loop through flags w/o args that are set, printing them if so */
@@ -1536,7 +1536,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
 		char *p = dupstring(s);
 
 		untokenize(p);
-		quotedzputs(quotestring(p, NULL, QT_BACKSLASH), stdout);
+		quotedzputs(quotestring(p, QT_BACKSLASH), stdout);
 	    }
 	}
 	putchar('\n');
@@ -1564,6 +1564,8 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
     Compctl cc = NULL;
     int ret = 0;
 
+    queue_signals();
+
     /* clear static flags */
     cclist = 0;
     showmask = 0;
@@ -1571,12 +1573,15 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
     /* Parse all the arguments */
     if (*argv) {
 	/* Let's see if this is a global matcher definition. */
-	if ((ret = get_gmatcher(name, argv)))
+	if ((ret = get_gmatcher(name, argv))) {
+	    unqueue_signals();
 	    return ret - 1;
+	}
 
 	cc = (Compctl) zshcalloc(sizeof(*cc));
 	if (get_compctl(name, &argv, cc, 1, 0, 0)) {
 	    freecompctl(cc);
+	    unqueue_signals();
 	    return 1;
 	}
 
@@ -1604,6 +1609,7 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 	printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0, 0);
  	printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0, 0);
 	print_gmatcher((cclist & COMP_LIST));
+	unqueue_signals();
 	return ret;
     }
 
@@ -1642,6 +1648,7 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 	    printcompctl("", &cc_first, 0, 0);
 	if (cclist & COMP_LISTMATCH)
 	    print_gmatcher(COMP_LIST);
+	unqueue_signals();
 	return ret;
     }
 
@@ -1656,6 +1663,7 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 	    compctl_process_cc(argv, cc);
     }
 
+    unqueue_signals();
     return ret;
 }
 
@@ -1667,12 +1675,18 @@ bin_compctl(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 static int
 bin_compcall(char *name, UNUSED(char **argv), Options ops, UNUSED(int func))
 {
+    int ret;
+
     if (incompfunc != 1) {
 	zwarnnam(name, "can only be called from completion function");
 	return 1;
     }
-    return makecomplistctl((OPT_ISSET(ops,'T') ? 0 : CFN_FIRST) |
-			   (OPT_ISSET(ops,'D') ? 0 : CFN_DEFAULT));
+
+    queue_signals();
+    ret = makecomplistctl((OPT_ISSET(ops,'T') ? 0 : CFN_FIRST) |
+			  (OPT_ISSET(ops,'D') ? 0 : CFN_DEFAULT));
+    unqueue_signals();
+    return ret;
 }
 
 /*
@@ -1740,8 +1754,8 @@ static int addwhat;
  * This uses the instring variable exported from zle_tricky.c.
  */
 
-#define quotename(s, e) \
-quotestring(s, e, instring == QT_NONE ? QT_BACKSLASH : instring)
+#define quotename(s) \
+quotestring(s, instring == QT_NONE ? QT_BACKSLASH : instring)
 
 /* Hook functions */
 
@@ -1756,6 +1770,8 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat)
     int onm = nmatches, odm = diffmatches, osi = movefd(0);
     LinkNode n;
 
+    queue_signals();
+
     /* We build a copy of the list of matchers to use to make sure that this
      * works even if a shell function called from the completion code changes
      * the global matchers. */
@@ -1883,6 +1899,8 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat)
     }
     redup(osi, 0);
     dat->lst = 1;
+
+    unqueue_signals();
     return 0;
 }
 
@@ -1965,7 +1983,7 @@ addmatch(char *s, char *t)
 	if (!ms)
 	    return;
 
-	if (addwhat == -7 && !findcmd(s, 0))
+	if (addwhat == -7 && !findcmd(s, 0, 0))
 	    return;
 	isfile = CMF_FILE;
     } else if (addwhat == CC_QUOTEFLAG || addwhat == -2  ||
@@ -2044,7 +2062,7 @@ maketildelist(void)
 /* This does the check for compctl -x `n' and `N' patterns. */
 
 /**/
-int
+static int
 getcpat(char *str, int cpatindex, char *cpat, int class)
 {
     char *s, *t, *p;
@@ -2135,7 +2153,7 @@ gen_matches_files(int dirs, int execs, int all)
 {
     DIR *d;
     struct stat buf;
-    char *n, p[PATH_MAX], *q = NULL, *e, *pathpref;
+    char *n, p[PATH_MAX+1], *q = NULL, *e, *pathpref;
     LinkList l = NULL;
     int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat, pathpreflen;
 
@@ -2469,7 +2487,7 @@ makecomplistcmd(char *os, int incmd, int flags)
     /* If the command string starts with `=', try the path name of the *
      * command. */
     if (cmdstr && cmdstr[0] == Equals) {
-	char *c = findcmd(cmdstr + 1, 1);
+	char *c = findcmd(cmdstr + 1, 1, 0);
 
 	if (c) {
 	    zsfree(cmdstr);
@@ -2509,7 +2527,8 @@ makecomplistpc(char *os, int incmd)
     int ret = 0;
 
     s = ((shfunctab->getnode(shfunctab, cmdstr) ||
-	  builtintab->getnode(builtintab, cmdstr)) ? NULL : findcmd(cmdstr, 1));
+	  builtintab->getnode(builtintab, cmdstr)) ? NULL :
+	 findcmd(cmdstr, 1, 0));
 
     for (pc = patcomps; pc; pc = pc->next) {
 	if ((pat = patcompile(pc->pat, PAT_STATIC, NULL)) &&
@@ -3153,10 +3172,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     lpre = zhalloc(lpl + 1);
     memcpy(lpre, s, lpl);
     lpre[lpl] = '\0';
-    qlpre = quotename(lpre, NULL);
+    qlpre = quotename(lpre);
     lsuf = dupstring(s + offs);
     lsl = strlen(lsuf);
-    qlsuf = quotename(lsuf, NULL);
+    qlsuf = quotename(lsuf);
 
     /* First check for ~.../... */
     if (ic == Tilde) {
@@ -3175,11 +3194,11 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     rpre = (*p || *lpre == Tilde || *lpre == Equals) ?
 	(noreal = 0, getreal(tt)) :
 	dupstring(tt);
-    qrpre = quotename(rpre, NULL);
+    qrpre = quotename(rpre);
 
     for (p = lsuf; *p && *p != String && *p != Tick; p++);
     rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf);
-    qrsuf = quotename(rsuf, NULL);
+    qrsuf = quotename(rsuf);
 
     /* Check if word is a pattern. */
 
@@ -3315,10 +3334,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	/* And get the file prefix. */
 	fpre = dupstring(((s1 == s || s1 == rpre || ic) &&
 			  (*s != '/' || zlemetacs == wb)) ? s1 : s1 + 1);
-	qfpre = quotename(fpre, NULL);
+	qfpre = quotename(fpre);
 	/* And the suffix. */
 	fsuf = dupstrpfx(rsuf, s2 - rsuf);
-	qfsuf = quotename(fsuf, NULL);
+	qfsuf = quotename(fsuf);
 
 	if (comppatmatch && *comppatmatch && (ispattern & 2)) {
 	    int t2;
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index 30fab541a..68bdf2332 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -52,7 +52,7 @@ char **compwords,
      *compqiprefix,
      *compqisuffix,
      *compquote,
-     *compqstack,
+     *compqstack,      /* compstate[all_quotes] */
      *comppatmatch,
      *complastprompt;
 /**/
@@ -67,13 +67,31 @@ char *compiprefix,
      *compexact,
      *compexactstr,
      *comppatinsert,
-     *comptoend,
+     *comptoend,      /* compstate[to_end]; populates 'movetoend' */
      *compoldlist,
      *compoldins,
      *compvared;
 
+/*
+ * An array of Param structures for compsys special parameters;
+ * see 'comprparams' below.  An entry for $compstate is added
+ * by makecompparams().
+ *
+ * See CP_REALPARAMS.
+ */
+
+/**/
+Param *comprpms;
+
+/* 
+ * An array of Param structures for elemens of $compstate; see
+ * 'compkparams' below.
+ *
+ * See CP_KEYPARAMS.
+ */
+
 /**/
-Param *comprpms, *compkpms;
+Param *compkpms;
 
 /**/
 mod_export void
@@ -209,7 +227,15 @@ cpcpattern(Cpattern o)
     return r;
 }
 
-/* Parse a string for matcher control, containing multiple matchers. */
+/* 
+ * Parse a string for matcher control, containing multiple matchers.
+ *
+ * 's' is the string to be parsed.
+ *
+ * 'name' is the name of the builtin from which this is called, for errors.
+ *
+ * Return 'pcm_err' on error; a NULL return value means ...
+ */
 
 /**/
 mod_export Cmatcher
@@ -231,16 +257,17 @@ parse_cmatcher(char *name, char *s)
 	if (!*s) break;
 
 	switch (*s) {
-	case 'b': fl2 = CMF_INTER;
+	case 'b': fl2 = CMF_INTER; /* FALLTHROUGH */
 	case 'l': fl = CMF_LEFT; break;
-	case 'e': fl2 = CMF_INTER;
+	case 'e': fl2 = CMF_INTER; /* FALLTHROUGH */
 	case 'r': fl = CMF_RIGHT; break;
 	case 'm': fl = 0; break;
-	case 'B': fl2 = CMF_INTER;
+	case 'B': fl2 = CMF_INTER; /* FALLTHROUGH */
 	case 'L': fl = CMF_LEFT | CMF_LINE; break;
-	case 'E': fl2 = CMF_INTER;
+	case 'E': fl2 = CMF_INTER; /* FALLTHROUGH */
 	case 'R': fl = CMF_RIGHT | CMF_LINE; break;
 	case 'M': fl = CMF_LINE; break;
+	case 'x': break;
 	default:
 	    if (name)
 		zwarnnam(name, "unknown match specification character `%c'",
@@ -252,6 +279,15 @@ parse_cmatcher(char *name, char *s)
 		zwarnnam(name, "missing `:'");
 	    return pcm_err;
 	}
+	if (*s == 'x') {
+	    if (s[2] && !inblank(s[2])) {
+		if (name)
+		    zwarnnam(name,
+			"unexpected pattern following x: specification");
+		return pcm_err;
+	    }
+	    return ret;
+	}
 	s += 2;
 	if (!*s) {
 	    if (name)
@@ -527,8 +563,8 @@ static int
 bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 {
     struct cadata dat;
-    char *p, **sp, *e, *m = NULL, *mstr = NULL;
-    int dm;
+    char *mstr = NULL; /* argument of -M options, accumulated */
+    int added; /* return value */
     Cmatcher match = NULL;
 
     if (incompfunc != 1) {
@@ -544,14 +580,16 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
     dat.dummies = -1;
 
     for (; *argv && **argv ==  '-'; argv++) {
+	char *p; /* loop variable, points into argv */
 	if (!(*argv)[1]) {
 	    argv++;
 	    break;
 	}
 	for (p = *argv + 1; *p; p++) {
-	    sp = NULL;
-	    e = NULL;
-	    dm = 0;
+	    char *m = NULL; /* argument of -M option (this one only) */
+	    char **sp = NULL; /* the argument to an option should be copied
+				 to *sp. */
+	    const char *e; /* error message */
 	    switch (*p) {
 	    case 'q':
 		dat.flags |= CMF_REMOVE;
@@ -633,7 +671,6 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 	    case 'M':
 		sp = &m;
 		e = "matching specification expected after -%c";
-		dm = 1;
 		break;
 	    case 'X':
 		sp = &(dat.exp);
@@ -720,14 +757,13 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 		    zsfree(mstr);
 		    return 1;
 		}
-		if (dm) {
+		if (m) {
 		    if (mstr) {
 			char *tmp = tricat(mstr, " ", m);
 			zsfree(mstr);
 			mstr = tmp;
 		    } else
 			mstr = ztrdup(m);
-		    m = NULL;
 		}
 	    }
 	}
@@ -746,10 +782,10 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 	return 1;
 
     dat.match = match = cpcmatcher(match);
-    dm = addmatches(&dat, argv);
+    added = addmatches(&dat, argv);
     freecmatcher(match);
 
-    return dm;
+    return added;
 }
 
 #define CVT_RANGENUM 0
@@ -865,7 +901,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
 		return 0;
 
 	    singsub(&sa);
-	    pp = patcompile(sa, PAT_STATIC, NULL);
+	    pp = patcompile(sa, PAT_HEAPDUP, NULL);
 
 	    for (i--, p = compwords + i; i >= 0; p--, i--) {
 		if (pattry(pp, *p)) {
@@ -919,7 +955,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
 	    if (!na)
 		return 0;
 
-	    if (!(pp = patcompile(sa, PAT_STATIC, 0)))
+	    if (!(pp = patcompile(sa, PAT_HEAPDUP, 0)))
 		return 0;
 
 	    if (test == CVT_PREPAT) {
@@ -1210,8 +1246,9 @@ makecompparams(void)
 
     addcompparams(comprparams, comprpms);
 
-    if (!(cpm = createparam(COMPSTATENAME,
-			    PM_SPECIAL|PM_REMOVABLE|PM_LOCAL|PM_HASHED)))
+    if (!(cpm = createparam(
+	      COMPSTATENAME,
+	      PM_SPECIAL|PM_REMOVABLE|PM_SINGLE|PM_LOCAL|PM_HASHED)))
 	cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME);
     DPUTS(!cpm, "param not set in makecompparams");
 
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 8aeb6c3b8..035038815 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -347,9 +347,10 @@ getcoldef(char *s)
 	    char sav = p[1];
 
 	    p[1] = '\0';
+	    s = metafy(s, -1, META_USEHEAP);
 	    tokenize(s);
 	    gprog = patcompile(s, 0, NULL);
-	    p[1]  =sav;
+	    p[1] = sav;
 
 	    s = p + 1;
 	}
@@ -415,6 +416,7 @@ getcoldef(char *s)
 		break;
 	    *s++ = '\0';
 	}
+	p = metafy(p, -1, META_USEHEAP);
 	tokenize(p);
 	if ((prog = patcompile(p, 0, NULL))) {
 	    Patcol pc, po;
@@ -662,7 +664,9 @@ clprintfmt(char *p, int ml)
 
     initiscol();
 
-    for (; *p; p++) {
+    while (*p) {
+	convchar_t chr;
+	int chrlen = MB_METACHARLENCONV(p, &chr);
 	doiscol(i++);
 	cc++;
 	if (*p == '\n') {
@@ -673,11 +677,16 @@ clprintfmt(char *p, int ml)
 	if (ml == mlend - 1 && (cc % zterm_columns) == zterm_columns - 1)
 	    return 0;
 
-	if (*p == Meta) {
+	while (chrlen) {
+	    if (*p == Meta) {
+		p++;
+		chrlen--;
+		putc(*p ^ 32, shout);
+	    } else
+		putc(*p, shout);
+	    chrlen--;
 	    p++;
-	    putc(*p ^ 32, shout);
-	} else
-	    putc(*p, shout);
+	}
 	if ((beg = !(cc % zterm_columns)))
 	    ml++;
 	if (mscroll && !(cc % zterm_columns) &&
@@ -1984,7 +1993,8 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat)
     if (noselect > 0)
 	noselect = 0;
 
-    if ((minfo.asked == 2 && mselect < 0) || nlnct >= zterm_lines) {
+    if ((minfo.asked == 2 && mselect < 0) || nlnct >= zterm_lines ||
+	errflag) {
 	showinglist = 0;
 	amatches = oamatches;
 	return (noselect = 1);
diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
index 0e41ac3a5..1cdbb8a48 100644
--- a/Src/Zle/compmatch.c
+++ b/Src/Zle/compmatch.c
@@ -498,14 +498,27 @@ add_match_sub(Cmatcher m, char *l, int ll, char *w, int wl)
 /**/
 int
 match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
-	  int sfx, int test, int part)
+	  const int sfx, int test, int part)
 {
-    int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw, exact = 0, wexact = 0;
-    int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc, bslash;
+    /* How many characters from the line string and from the word string are
+     * yet to be matched. */
+    int ll = strlen(l), lw = strlen(w);
+    /* Number of characters from the line string and word string matched. */
+    int il = 0, iw = 0;
+    /* How many characters were matched exactly in the line and in the word. */
+    int exact = 0, wexact = 0;
+    int he = 0;
+    int bslash;
     char *ow;
-    Cmlist ms;
+    Cmlist ms; /* loop variable */
     Cmatcher mp, lm = NULL;
     Brinfo bp = NULL;
+    const int obc = bc;
+    const int ind = (sfx ? -1 : 0);
+    const int add = (sfx ? -1 : 1);
+    const int original_ll = ll, original_lw = lw;
+
+    /* INVARIANT: il+ll == original_ll; iw+lw == original_lw */
 
     if (!test) {
 	start_match();
@@ -516,9 +529,6 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
 
     if (sfx) {
 	l += ll; w += lw;
-	ind = -1; add = -1;
-    } else {
-	ind = 0; add = 1;
     }
     /* ow will always point to the beginning (or end) of that sub-string
      * in w that wasn't put in the match-variables yet. */
@@ -559,8 +569,7 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
 	bslash = 0;
 	if (!sfx && lw && (!part || test) &&
 	    (l[ind] == w[ind] ||
-	     (bslash = (lw > 1 && w[ind] == '\\' &&
-			(ind ? (w[0] == l[0]) : (w[1] == l[0])))))) {
+	     (bslash = (lw > 1 && w[ind] == '\\' && w[ind+1] == l[0])))) {
 	    /* No matcher could be used, but the strings have the same
 	     * character here, skip over it. */
 	    l += add; w += (bslash ? (add + add) : add);
@@ -583,9 +592,8 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
 	/* First try the matchers. Err... see above. */
 	for (mp = NULL, ms = mstack; !mp && ms; ms = ms->next) {
 	    for (mp = ms->matcher; mp; mp = mp->next) {
-		t = 1;
 		if ((lm && lm == mp) ||
-		    ((oll == ll || olw == lw) &&
+		    ((original_ll == ll || original_lw == lw) &&
 		     (test == 1 || (test && !mp->left && !mp->right)) &&
 		     mp->wlen < 0))
 		    /* If we were called recursively, don't use `*' patterns
@@ -593,9 +601,88 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
 		    continue;
 
 		if (mp->wlen < 0) {
-		    int both, loff, aoff, llen, alen, zoff, moff, ct, ict, aol;
-		    char *tp, savl = '\0', savw;
-		    Cpattern ap, aop;
+		    /* `*'-pattern. */
+		    /*
+		     * Similar to the identically-named variable in the 'else'
+		     * block.
+		     */
+		    int t;
+		    /* 
+		     * 1 iff the anchor and the word are on the same side of
+		     * the line pattern; that is: if either
+		     * - the anchor is on the left and we are matching
+		     *   a prefix; or
+		     * - the anchor is on the right and we are matching
+		     *   a suffix.
+		     */
+		    int both;
+		    /*
+		     * Offset from the line pattern pointer ('l') to the start
+		     * of the line pattern.
+		     */
+		    int loff;
+		    /*
+		     * Offset from the line pattern pointer ('l') to the start
+		     * of the anchor.
+		     */
+		    int aoff;
+		    /*
+		     * The length of the line pattern.
+		     */
+		    int llen;
+		    /*
+		     * The length of the anchor.
+		     *
+		     * SEE: ap; aol, aop
+		     */
+		    int alen;
+		    /*
+		     * ### Related to 'zoff', which was removed in 2016.
+		     */
+		    int moff;
+		    /*
+		     * ### These two are related.
+		     *
+		     * ### They may have a relation similar to that of lw/iw
+		     * ### (q.v.), at least during the 'for' loop.  They may be
+		     * ### overloaded/repurposed after it.
+		     */
+		    int ct, ict;
+		    /*
+		     * The length of the OTHER anchor: the left anchor when
+		     * we're anchored on the right, and of the right anchor
+		     * when we're anchored on the left.
+		     */
+		    int aol;
+		    /*
+		     * LOST: Documentation comment.  Last seen 10 years ago in
+		     * the temporal lobe.  Reward promised for its safe return.
+		     * Contact zsh-workers@zsh.org.
+		     */
+		    char *tp;
+		    /* 
+		     * Temporary variable.  Used as temporary storage for a
+		     *
+		     *     {
+		     *         () {
+		     *           local foo="$foo"
+		     *           foo[1]=bar
+		     *           ...
+		     *         }
+		     *         (use original $foo here)
+		     *     }
+		     *
+		     * operation.  Similar to savw.
+		     */
+		    char savl = 0;
+		    /*
+		     * The anchor on this end.
+		     */
+		    Cpattern ap;
+		    /*
+		     * The anchor on the other end.
+		     */
+		    Cpattern aop;
 
 		    /* This is for `*' patterns, first initialise some
 		     * local variables. */
@@ -611,14 +698,14 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
 			continue;
 
 		    if (mp->flags & CMF_LEFT) {
-			ap = mp->left; zoff = 0; moff = alen; aop = mp->right;
+			ap = mp->left; moff = alen; aop = mp->right;
 			if (sfx) {
 			    both = 0; loff = -llen; aoff = -(llen + alen);
 			} else {
 			    both = 1; loff = alen; aoff = 0;
 			}
 		    } else {
-			ap = mp->right; zoff = alen; moff = 0; aop = mp->left;
+			ap = mp->right; moff = 0; aop = mp->left;
 			if (sfx) {
 			    both = 1; loff = -(llen + alen); aoff = -alen;
 			} else {
@@ -644,8 +731,8 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
 
 		    /* Fine, now we call ourselves recursively to find the
 		     * string matched by the `*'. */
-		    if (sfx && (savl = l[-(llen + zoff)]))
-			l[-(llen + zoff)] = '\0';
+		    if (sfx && (savl = l[-(llen + alen)]))
+			l[-(llen + alen)] = '\0';
 		    for (t = 0, tp = w, ct = 0, ict = lw - alen + 1;
 			 ict;
 			 tp += add, ct++, ict--) {
@@ -667,22 +754,25 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
 				!match_parts(l + aoff , tp - moff, alen, part))
 				break;
 			    if (sfx) {
-				if ((savw = tp[-zoff]))
-				    tp[-zoff] = '\0';
+				/* Call ourselves recursively with the
+				 * anchor removed. */
+				char savw;
+				if ((savw = tp[-alen]))
+				    tp[-alen] = '\0';
 				t = match_str(l - ll, w - lw,
-					      NULL, 0, NULL, 1, 2, part);
+					      NULL, 0, NULL, sfx, 2, part);
 				if (savw)
-				    tp[-zoff] = savw;
+				    tp[-alen] = savw;
 			    } else
 				t = match_str(l + llen + moff, tp + moff,
-					      NULL, 0, NULL, 0, 1, part);
+					      NULL, 0, NULL, sfx, 1, part);
 			    if (t || (mp->wlen == -1 && !both))
 				break;
 			}
 		    }
 		    ict = ct;
 		    if (sfx && savl)
-			l[-(llen + zoff)] = savl;
+			l[-(llen + alen)] = savl;
 
 		    /* Have we found a position in w where the rest of l
 		     * matches? */
@@ -752,18 +842,22 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
 		    bc += llen;
 		    exact = 0;
 
-		    if (!test)
+		    if (!test) {
+			int bpc;
 			while (bp &&
 			       bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
 			    bp->curpos = matchbufadded + bpc - bc + obc;
 			    bp = bp->next;
 			}
+		    }
 		    ow = w;
 
 		    if (!llen && !alen) {
 			lm = mp;
-			if (he)
+			if (he) {
+			    /* Signal the outer for loop to continue. */
 			    mp = NULL;
+			}
 			else
 			    he = 1;
 		    } else {
@@ -772,6 +866,11 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
 		    break;
 		} else if (ll >= mp->llen && lw >= mp->wlen) {
 		    /* Non-`*'-pattern. */
+		    /*
+		     * Similar to the identically-named variable in the 'if'
+		     * block.
+		     */
+		    int t = 1;
 		    char *tl, *tw;
 		    int tll, tlw, til, tiw;
 
@@ -875,12 +974,14 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
 		    bc += mp->llen;
 		    exact = 0;
 
-		    if (!test)
+		    if (!test) {
+			int bpc;
 			while (bp &&
 			       bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
 			    bp->curpos = matchbufadded + bpc - bc + obc;
 			    bp = bp->next;
 			}
+		    }
 		    ow = w;
 		    lm = NULL;
 		    he = 0;
@@ -896,8 +997,7 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
 	bslash = 0;
 	if ((!test || sfx) && lw &&
 	    (l[ind] == w[ind] ||
-	     (bslash = (lw > 1 && w[ind] == '\\' &&
-			(ind ? (w[0] == l[0]) : (w[1] == l[0])))))) {
+	     (bslash = (lw > 1 && w[ind] == '\\' && w[ind+1] == l[0])))) {
 	    /* No matcher could be used, but the strings have the same
 	     * character here, skip over it. */
 	    l += add; w += (bslash ? (add + add ) : add);
@@ -1448,27 +1548,11 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws)
 {
     convchar_t c, wc;
     convchar_t ind, wind;
-    int len = 0, wlen, mt, wmt;
-#ifdef MULTIBYTE_SUPPORT
-    mbstate_t lstate, wstate;
-
-    memset(&lstate, 0, sizeof(lstate));
-    memset(&wstate, 0, sizeof(wstate));
-#endif
+    int len = 0, wlen = 0, mt, wmt;
 
     while (p && wp && *s && *ws) {
 	/* First test the word character */
-#ifdef MULTIBYTE_SUPPORT
-	wlen = mb_metacharlenconv_r(ws, &wc, &wstate);
-#else
-	if (*ws == Meta) {
-	    wc = STOUC(ws[1]) ^ 32;
-	    wlen = 2;
-	} else {
-	    wc = STOUC(*ws);
-	    wlen = 1;
-	}
-#endif
+	wc = unmeta_one(ws, &wlen);
 	wind = pattern_match1(wp, wc, &wmt);
 	if (!wind)
 	    return 0;
@@ -1476,18 +1560,7 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws)
 	/*
 	 * Now the line character.
 	 */
-#ifdef MULTIBYTE_SUPPORT
-	len = mb_metacharlenconv_r(s, &c, &lstate);
-#else
-	/* We have the character itself. */
-	if (*s == Meta) {
-	    c = STOUC(s[1]) ^ 32;
-	    len = 2;
-	} else {
-	    c = STOUC(*s);
-	    len = 1;
-	}
-#endif
+	c = unmeta_one(s, &len);
 	/*
 	 * If either is "?", they match each other; no further tests.
 	 * Apply this even if the character wasn't convertable;
@@ -1527,17 +1600,7 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws)
     }
 
     while (p && *s) {
-#ifdef MULTIBYTE_SUPPORT
-	len = mb_metacharlenconv_r(s, &c, &lstate);
-#else
-	if (*s == Meta) {
-	    c = STOUC(s[1]) ^ 32;
-	    len = 2;
-	} else {
-	    c = STOUC(*s);
-	    len = 1;
-	}
-#endif
+	c = unmeta_one(s, &len);
 	if (!pattern_match1(p, c, &mt))
 	    return 0;
 	p = p->next;
@@ -1545,17 +1608,7 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws)
     }
 
     while (wp && *ws) {
-#ifdef MULTIBYTE_SUPPORT
-	wlen = mb_metacharlenconv_r(ws, &wc, &wstate);
-#else
-	if (*ws == Meta) {
-	    wc = STOUC(ws[1]) ^ 32;
-	    wlen = 2;
-	} else {
-	    wc = STOUC(*ws);
-	    wlen = 1;
-	}
-#endif
+	wc = unmeta_one(ws, &wlen);
 	if (!pattern_match1(wp, wc, &wmt))
 	    return 0;
 	wp = wp->next;
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index 7fec7c804..05799399d 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -1174,6 +1174,10 @@ do_single(Cmatch m)
 	zlemetacs = minfo.end;
 	if (zlemetacs + m->qisl == lastend)
 	    zlemetacs += minfo.insc;
+
+	/* Advance CURSOR past compadd -s/-S suffixes. */
+	zlemetacs += strlen(psuf);
+	zlemetacs += m->suf ? strlen(m->suf) : 0;
     }
     {
 	Cmatch *om = minfo.cur;
@@ -1191,6 +1195,7 @@ do_single(Cmatch m)
 	if (menucmp)
 	    minfo.cur = &m;
 	runhookdef(INSERTMATCHHOOK, (void *) &dat);
+	redrawhook();
 	minfo.cur = om;
     }
 }
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 184b263ee..7ffe00df4 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -199,11 +199,11 @@ cd_calc(void)
             set->count++;
             if ((l = strlen(str->str)) > cd_state.pre)
                 cd_state.pre = l;
-            if ((l = MB_METASTRWIDTH(str->str)) > cd_state.premaxw)
+            if ((l = ZMB_nicewidth(str->str)) > cd_state.premaxw)
                 cd_state.premaxw = l;
             if (str->desc) {
                 set->desc++;
-                if ((l = strlen(str->desc)) > cd_state.suf)
+                if ((l = strlen(str->desc)) > cd_state.suf) /* ### strlen() assumes no \n */
                     cd_state.suf = l;
             }
         }
@@ -490,7 +490,7 @@ cd_init(char *nam, char *hide, char *mlen, char *sep,
     setp = &(cd_state.sets);
     cd_state.sep = ztrdup(sep);
     cd_state.slen = strlen(sep);
-    cd_state.swidth = MB_METASTRWIDTH(sep);
+    cd_state.swidth = ZMB_nicewidth(sep);
     cd_state.sets = NULL;
     cd_state.showd = disp;
     cd_state.maxg = cd_state.groups = cd_state.descs = 0;
@@ -526,7 +526,8 @@ cd_init(char *nam, char *hide, char *mlen, char *sep,
             str->other = NULL;
             str->set = set;
 
-            for (tmp = *ap; *tmp && *tmp != ':'; tmp++)
+	    /* Advance tmp to the first unescaped colon. */
+	    for (tmp = *ap; *tmp && *tmp != ':'; tmp++)
                 if (*tmp == '\\' && tmp[1])
                     tmp++;
 
@@ -537,7 +538,7 @@ cd_init(char *nam, char *hide, char *mlen, char *sep,
             *tmp = '\0';
             str->str = str->match = ztrdup(rembslash(*ap));
             str->len = strlen(str->str);
-            str->width = MB_METASTRWIDTH(str->str);
+            str->width = ZMB_nicewidth(str->str);
 	    str->sortstr = NULL;
         }
         if (str)
@@ -692,7 +693,7 @@ cd_get(char **params)
 			 * end of screen as safety margin
 			 */
 			d = str->desc;
-			w = MB_METASTRWIDTH(d);
+			w = ZMB_nicewidth(d);
 			if (w <= remw)
 			    strcpy(p, d);
 			else {
@@ -701,7 +702,7 @@ cd_get(char **params)
 				l = MB_METACHARLEN(d);
 				memcpy(pp, d, l);
 				pp[l] = '\0';
-				w = MB_METASTRWIDTH(pp);
+				w = ZMB_nicewidth(pp);
 				if (w > remw) {
 				    *pp = '\0';
 				    break;
@@ -792,7 +793,7 @@ cd_get(char **params)
 			cd_state.swidth - CM_SPACE;
 		    p = pp = dbuf + cd_state.slen;
 		    d = str->desc;
-		    w = MB_METASTRWIDTH(d);
+		    w = ZMB_nicewidth(d);
 		    if (w <= remw) {
 			strcpy(p, d);
 			remw -= w;
@@ -802,7 +803,7 @@ cd_get(char **params)
 			    l = MB_METACHARLEN(d);
 			    memcpy(pp, d, l);
 			    pp[l] = '\0';
-			    w = MB_METASTRWIDTH(pp);
+			    w = ZMB_nicewidth(pp);
 			    if (w > remw) {
 				*pp = '\0';
 				break;
@@ -913,15 +914,14 @@ struct cadef {
     int lastt;			/* last time this was used */
     Caopt *single;		/* array of single-letter options */
     char *match;		/* -M spec to use */
-    int argsactive;		/* if arguments are still allowed */
+    int argsactive;		/* if normal arguments are still allowed */
 				/* used while parsing a command line */
     char *set;			/* set name prefix (<name>-), shared */
-    char *sname;		/* set name */
     int flags;			/* see CDF_* below */
     char *nonarg;		/* pattern for non-args (-A argument) */
 };
 
-#define CDF_SEP 1
+#define CDF_SEP 1		/* -S was specified: -- terminates options */
 
 /* Description for an option. */
 
@@ -934,15 +934,15 @@ struct caopt {
     Caarg args;			/* option arguments */
     int active;			/* still allowed on command line */
     int num;			/* it's the num'th option */
-    char *set;			/* set name, shared */
+    char *gsname;		/* group or set name, shared */
     int not;			/* don't complete this option (`!...') */
 };
 
-#define CAO_NEXT    1
-#define CAO_DIRECT  2
-#define CAO_ODIRECT 3
-#define CAO_EQUAL   4
-#define CAO_OEQUAL  5
+#define CAO_NEXT    1		/* argument follows in next argument (`-opt:...') */
+#define CAO_DIRECT  2		/* argument follows option directly (`-opt-:...') */
+#define CAO_ODIRECT 3		/* argument may follow option directly (`-opt+:...') */
+#define CAO_EQUAL   4		/* argument follows mandatory equals (`-opt=-:...') */
+#define CAO_OEQUAL  5		/* argument follows optional equals (`-opt=:...') */
 
 /* Description for an argument */
 
@@ -955,10 +955,10 @@ struct caarg {
     char *end;			/* end-pattern for ::<pat>:... */
     char *opt;			/* option name if for an option */
     int num;			/* it's the num'th argument */
-    int min;			/* it's also this argument, using opt. args */
-    int direct;			/* number was given directly */
+    int min;			/* earliest possible arg pos, given optional args */
+    int direct;			/* true if argument number was given explicitly */
     int active;			/* still allowed on command line */
-    char *set;			/* set name, shared */
+    char *gsname;		/* group or set name, shared */
 };
 
 #define CAA_NORMAL 1
@@ -1019,7 +1019,6 @@ freecadef(Cadef d)
 	s = d->snext;
 	zsfree(d->match);
 	zsfree(d->set);
-	zsfree(d->sname);
 	if (d->defs)
 	    freearray(d->defs);
 
@@ -1097,7 +1096,7 @@ parse_caarg(int mult, int type, int num, int opt, char *oname, char **def,
     ret->type = type;
     ret->opt = ztrdup(oname);
     ret->direct = 0;
-    ret->set = set;
+    ret->gsname = set;
 
     /* Get the description. */
 
@@ -1146,8 +1145,11 @@ alloc_cadef(char **args, int single, char *match, char *nonarg, int flags)
 	ret->defs = NULL;
 	ret->ndefs = 0;
     }
+    ret->nopts = 0;
+    ret->ndopts = 0;
+    ret->nodopts = 0;
     ret->lastt = time(0);
-    ret->set = ret->sname = NULL;
+    ret->set = NULL;
     if (single) {
 	ret->single = (Caopt *) zalloc(256 * sizeof(Caopt));
 	memset(ret->single, 0, 256 * sizeof(Caopt));
@@ -1181,12 +1183,10 @@ parse_cadef(char *nam, char **args)
     Cadef all, ret;
     Caopt *optp;
     char **orig_args = args, *p, *q, *match = "r:|[_-]=* r:|=*", **xor, **sargs;
-    char *adpre, *adsuf, *axor = NULL, *doset = NULL, **setp = NULL;
+    char *adpre, *adsuf, *axor = NULL, *doset = NULL, **pendset = NULL, **curset = NULL;
     char *nonarg = NULL;
-    int single = 0, anum = 1, xnum, nopts, ndopts, nodopts, flags = 0;
-    int state = 0, not = 0;
-
-    nopts = ndopts = nodopts = 0;
+    int single = 0, anum = 1, xnum, flags = 0;
+    int foreignset = 0, not = 0;
 
     /* First string is the auto-description definition. */
 
@@ -1249,51 +1249,72 @@ parse_cadef(char *nam, char **args)
 
     if (nonarg)
 	tokenize(nonarg = dupstring(nonarg));
-
     /* Looks good. Optimistically allocate the cadef structure. */
 
     all = ret = alloc_cadef(orig_args, single, match, nonarg, flags);
     optp = &(ret->opts);
-    anum = 1;
-
     sargs = args;
 
     /* Get the definitions. */
 
-    for (; *args; args++) {
+    for (; *args || pendset; args++) {
+	if (!*args) {
+	    /* start new set */
+	    args = sargs; /* go back and repeat parse of common options */
+	    doset = NULL;
+	    set_cadef_opts(ret);
+	    ret = ret->snext = alloc_cadef(NULL, single, match, nonarg, flags);
+	    optp = &(ret->opts);
+	    anum = 1;
+	    foreignset = 0;
+	    curset = pendset;
+	    pendset = 0;
+        }
         if (args[0][0] == '-' && !args[0][1] && args[1]) {
-	    if (!state) {
-		char *p;
-		int l;
-
-		if (setp)
-		    args = setp;
-		p = *++args;
-		l = strlen(p) - 1;
+	    if ((foreignset = curset && args != curset)) {
+		if (!pendset && args > curset)
+		    pendset = args; /* mark pointer to next pending set */
+		++args;
+	    } else {
+		/* Carrying on: this is the current set */
+		char *p = *++args;
+		int l = strlen(p) - 1;
+
 		if (*p == '(' && p[l] == ')') {
 		    axor = p = dupstring(p + 1);
 		    p[l - 1] = '\0';
 		} else
 		    axor = NULL;
+		if (!*p) {
+		    freecadef(all);
+		    zwarnnam(nam, "empty set name");
+		    return NULL;
+		}
 		ret->set = doset = tricat(p, "-", "");
-		ret->sname = ztrdup(p);
-		state = 1;
-	    } else {
-		setp = args;
-		state = 0;
-		args = sargs - 1;
-		doset = NULL;
-		ret->nopts = nopts;
-		ret->ndopts = ndopts;
-		ret->nodopts = nodopts;
-		set_cadef_opts(ret);
-		ret = ret->snext = alloc_cadef(NULL, single, NULL, nonarg, flags);
-		optp = &(ret->opts);
-		nopts = ndopts = nodopts = 0;
-		anum = 1;
+		curset = args; /* needed for the first set */
 	    }
 	    continue;
-	}
+	} else if (args[0][0] == '+' && !args[0][1] && args[1]) {
+	    char *p;
+	    int l;
+
+	    foreignset = 0; /* group not in any set, don't want to skip it */
+	    p = *++args;
+	    l = strlen(p) - 1;
+	    if (*p == '(' && p[l] == ')') {
+		axor = p = dupstring(p + 1);
+		p[l - 1] = '\0';
+	    } else
+		axor = NULL;
+	    if (!*p) {
+		freecadef(all);
+		zwarnnam(nam, "empty group name");
+		return NULL;
+	    }
+	    doset = tricat(p, "-", "");
+	    continue;
+	} else if (foreignset) /* skipping over a different set */
+	    continue;
 	p = dupstring(*args);
 	xnum = 0;
 	if ((not = (*p == '!')))
@@ -1505,7 +1526,7 @@ parse_cadef(char *nam, char **args)
 	    optp = &((*optp)->next);
 
 	    opt->next = NULL;
-	    opt->set = doset;
+	    opt->gsname = doset;
 	    opt->name = ztrdup(rembslashcolon(name));
 	    if (descr)
 		opt->descr = ztrdup(descr);
@@ -1525,13 +1546,13 @@ parse_cadef(char *nam, char **args)
 	    opt->xor = (again == 1 && xor ? zarrdup(xor) : xor);
 	    opt->type = otype;
 	    opt->args = oargs;
-	    opt->num = nopts++;
+	    opt->num = ret->nopts++;
 	    opt->not = not;
 
 	    if (otype == CAO_DIRECT || otype == CAO_EQUAL)
-		ndopts++;
+		ret->ndopts++;
 	    else if (otype == CAO_ODIRECT || otype == CAO_OEQUAL)
-		nodopts++;
+		ret->nodopts++;
 
 	    /* If this is for single-letter option we also store a
 	     * pointer for the definition in the array for fast lookup.
@@ -1583,7 +1604,7 @@ parse_cadef(char *nam, char **args)
 		continue;
 
 	    if ((direct = idigit(*p))) {
-		/* Argment number is given. */
+		/* Argument number is given. */
 		int num = 0;
 
 		while (*p && idigit(*p))
@@ -1629,9 +1650,6 @@ parse_cadef(char *nam, char **args)
 		ret->args = arg;
 	}
     }
-    ret->nopts = nopts;
-    ret->ndopts = ndopts;
-    ret->nodopts = nodopts;
     set_cadef_opts(ret);
 
     return all;
@@ -1693,10 +1711,10 @@ ca_get_opt(Cadef d, char *line, int full, char **end)
 	for (p = d->opts; p; p = p->next)
 	    if (p->active && ((!p->args || p->type == CAO_NEXT) ?
 			      !strcmp(p->name, line) : strpfx(p->name, line))) {
-	    int l = strlen(p->name);
-	    if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) &&
-		line[l] && line[l] != '=')
-		continue;
+		int l = strlen(p->name);
+		if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) &&
+		    line[l] && line[l] != '=')
+		    continue;
 
 		if (end) {
 		    /* Return a pointer to the end of the option. */
@@ -1750,6 +1768,27 @@ ca_get_sopt(Cadef d, char *line, char **end, LinkList *lp)
     return pp;
 }
 
+/* Search for an option in all sets except the current one.
+ * Return true if found */
+
+static int
+ca_foreign_opt(Cadef curset, Cadef all, char *option)
+{
+    Cadef d;
+    Caopt p;
+
+    for (d = all; d; d = d->snext) {
+	if (d == curset)
+	    continue;
+
+	for (p = d->opts; p; p = p->next) {
+	    if (!strcmp(p->name, option))
+		return 1;
+	}
+    }
+    return 0;
+}
+
 /* Return the n'th argument definition. */
 
 static Caarg
@@ -1771,98 +1810,135 @@ ca_get_arg(Cadef d, int n)
     return NULL;
 }
 
-/* Use a xor list, marking options as inactive. */
+/* Mark options as inactive.
+ *   d: option definitions for a set
+ *   pass either:
+ *     xor: a list if exclusions
+ *     opts: if set, all options excluded leaving only nornal/rest arguments */
 
-static LinkList ca_xor;
-
-static int
-ca_inactive(Cadef d, char **xor, int cur, int opts, char *optname)
+static void
+ca_inactive(Cadef d, char **xor, int cur, int opts)
 {
     if ((xor || opts) && cur <= compcurrent) {
 	Caopt opt;
 	char *x;
-	int sl = (d->set ? (int)strlen(d->set) : -1), set = 0;
+        /* current word could be a prefix of a longer one so only do
+	 * exclusions for single-letter options (for option clumping) */
+	int single = (cur == compcurrent);
 
 	for (; (x = (opts ? "-" : *xor)); xor++) {
-            if (optname && optname[0] == x[0] && strcmp(optname, x))
-                continue;
-	    if (ca_xor)
-		addlinknode(ca_xor, x);
-	    set = 0;
-	    if (sl > 0) {
-		if (strpfx(d->set, x)) {
-		    x += sl;
-		    set = 1;
-		} else if (!strncmp(d->set, x, sl - 1)) {
-		    Caopt p;
-
-		    for (p = d->opts; p; p = p->next)
-			if (p->set)
-			    p->active = 0;
-			
-		    x = ":";
-		    set = 1;
+	    int excludeall = 0;
+	    char *grp = NULL;
+	    size_t grplen;
+	    char *next, *sep = x;
+
+	    while (*sep != '+' && *sep != '-' && *sep != ':' && *sep != '*' && !idigit(*sep)) {
+		if (!(next = strchr(sep, '-')) || !*++next) {
+		    /* exclusion is just the name of a set or group */
+		    excludeall = 1; /* excluding options and args */
+		    sep += strlen(sep);
+		    /* A trailing '-' is included in the various gsname fields but is not
+		     * there for this branch. This is why we add excludeall to grplen
+		     * when checking for the null in a few places below */
+		    break;
 		}
+		sep = next;
+	    }
+	    if (sep > x) { /* exclusion included a set or group name */
+		grp = x;
+		grplen = sep - grp;
+		x = sep;
 	    }
-	    if (x[0] == ':' && !x[1]) {
-		if (set) {
+
+	    if (excludeall || (x[0] == ':' && !x[1])) {
+		if (grp) {
 		    Caarg a;
 
 		    for (a = d->args; a; a = a->next)
-			if (a->set)
+			if (a->gsname && !strncmp(a->gsname, grp, grplen) &&
+				!a->gsname[grplen + excludeall])
 			    a->active = 0;
-		    if (d->rest && (!set || d->rest->set))
+		    if (d->rest && d->rest->gsname &&
+			    !strncmp(d->rest->gsname, grp, grplen) &&
+			    !d->rest->gsname[grplen + excludeall])
 			d->rest->active = 0;
 		} else
 		    d->argsactive = 0;
-	    } else if (x[0] == '-' && !x[1]) {
+	    }
+
+	    if (excludeall || (x[0] == '-' && !x[1])) {
 		Caopt p;
 
 		for (p = d->opts; p; p = p->next)
-		    if (!set || p->set)
+		    if ((!grp || (p->gsname && !strncmp(p->gsname, grp, grplen) &&
+			    !p->gsname[grplen + excludeall])) &&
+			    !(single && *p->name && p->name[1] && p->name[2]))
 			p->active = 0;
-	    } else if (x[0] == '*' && !x[1]) {
-		if (d->rest && (!set || d->rest->set))
-		    d->rest->active = 0;
-	    } else if (idigit(x[0])) {
-		int n = atoi(x);
-		Caarg a = d->args;
-
-		while (a && a->num < n)
-		    a = a->next;
+	    }
 
-		if (a && a->num == n && (!set || a->set))
-		    a->active = 0;
-	    } else if ((opt = ca_get_opt(d, x, 1, NULL)) && (!set || opt->set))
-		opt->active = 0;
+	    if (excludeall || (x[0] == '*' && !x[1])) {
+		if (d->rest && (!grp || (d->rest->gsname &&
+			!strncmp(d->rest->gsname, grp, grplen) &&
+			!d->rest->gsname[grplen + excludeall])))
+		    d->rest->active = 0;
+            }
 
-	    if (opts)
-		break;
+	    if (!excludeall) {
+		if (idigit(x[0])) {
+		    int n = atoi(x);
+		    Caarg a = d->args;
+
+		    while (a && a->num < n)
+			a = a->next;
+
+		    if (a && a->num == n && (!grp || (a->gsname &&
+			    !strncmp(a->gsname, grp, grplen))))
+			a->active = 0;
+		} else if ((opt = ca_get_opt(d, x, 1, NULL)) &&
+			(!grp || (opt->gsname && !strncmp(opt->gsname, grp, grplen))) &&
+			!(single && *opt->name && opt->name[1] && opt->name[2]))
+		    opt->active = 0;
+		if (opts)
+		    break;
+	    }
 	}
     }
-    return 0;
 }
 
 /* State when parsing a command line. */
 
 typedef struct castate *Castate;
 
-/*
- *           **** DOCUMENT ME ****
- *
- * This structure and its use are a nightmare.
- */
+/* Encapsulates details from parsing the current line against a particular set,
+ * Covers positions of options and normal arguments. Used as a linked list
+ * with one state for each set. */
 
 struct castate {
-    Castate snext;
-    Cadef d;
-    int nopts;
-    Caarg def, ddef;
-    Caopt curopt, dopt;
-    int opt, arg, argbeg, optbeg, nargbeg, restbeg, curpos, argend;
-    int inopt, inrest, inarg, nth, doff, singles, oopt, actopts;
-    LinkList args;
-    LinkList *oargs;
+    Castate snext;	/* state for next set */
+    Cadef d;		/* parsed _arguments specs for the set */
+    int nopts;		/* number of specified options (size of oargs) */
+    Caarg def;		/* definition for the current set */
+    Caarg ddef;
+    Caopt curopt;	/* option description corresponding to option found on the command-line */
+    Caopt dopt;
+    int opt;		/* the length of the option up to a maximum of 2 */
+    int arg;		/* completing arguments to an option or rest args */
+    int argbeg;         /* position of first rest argument (+1) */
+    int optbeg;		/* first word after the last option to the left of the cursor:
+			 * in effect the start of any arguments to the current option */
+    int nargbeg;	/* same as optbeg but used during parse */
+    int restbeg;	/* same as argbeg but used during parse */
+    int curpos;		/* current word position */
+    int argend;         /* total number of words */
+    int inopt;		/* set to current word pos if word is a recognised option */
+    int inarg;          /* in a normal argument */
+    int nth;		/* number of current normal arg */
+    int doff;		/* length of current option */
+    int singles;	/* argument consists of clumped options */
+    int oopt;
+    int actopts;	/* count of active options */
+    LinkList args;	/* list of non-option args used for populating $line */
+    LinkList *oargs;	/* list of lists used for populating $opt_args */
 };
 
 static struct castate ca_laststate;
@@ -1908,10 +1984,12 @@ ca_opt_arg(Caopt opt, char *line)
     return ztrdup(line);
 }
 
-/* Parse a command line. */
+/* Parse the command line for a particular argument set (d).
+ * Returns 1 if the set should be skipped because it doesn't match
+ * existing options on the line. */
 
 static int
-ca_parse_line(Cadef d, int multi, int first)
+ca_parse_line(Cadef d, Cadef all, int multi, int first)
 {
     Caarg adef, ddef;
     Caopt ptr, wasopt = NULL, dopt;
@@ -1955,7 +2033,7 @@ ca_parse_line(Cadef d, int multi, int first)
     state.argbeg = state.optbeg = state.nargbeg = state.restbeg = state.actopts =
 	state.nth = state.inopt = state.inarg = state.opt = state.arg = 1;
     state.argend = argend = arrlen(compwords) - 1;
-    state.inrest = state.doff = state.singles = state.oopt = 0;
+    state.doff = state.singles = state.oopt = 0;
     state.curpos = compcurrent;
     state.args = znewlinklist();
     state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList));
@@ -1970,7 +2048,7 @@ ca_parse_line(Cadef d, int multi, int first)
 
 	goto end;
     }
-    if (d->nonarg)
+    if (d->nonarg) /* argument to -A */
 	napat = patcompile(d->nonarg, 0, NULL);
 
     /* Loop over the words from the line. */
@@ -2002,14 +2080,14 @@ ca_parse_line(Cadef d, int multi, int first)
         remnulargs(line);
         untokenize(line);
 
-	if (ca_inactive(d, argxor, cur, 0, NULL) ||
-	    ((d->flags & CDF_SEP) && cur != compcurrent && !strcmp(line, "--"))) {
-	    if (ca_inactive(d, NULL, cur, 1, NULL))
-		return 1;
+	ca_inactive(d, argxor, cur, 0);
+	if ((d->flags & CDF_SEP) && cur != compcurrent && !strcmp(line, "--")) {
+	    ca_inactive(d, NULL, cur, 1);
 	    continue;
 	}
-	/* We've got a definition for an argument, skip to the next. */
 
+	/* We've got a definition for an option/rest argument. For an option,
+	 * this means that we're completing arguments to that option. */
 	if (state.def) {
 	    state.arg = 0;
 	    if (state.curopt)
@@ -2080,9 +2158,7 @@ ca_parse_line(Cadef d, int multi, int first)
 	    if (!state.oargs[state.curopt->num])
 		state.oargs[state.curopt->num] = znewlinklist();
 
-	    if (ca_inactive(d, state.curopt->xor, cur, 0,
-                            (cur == compcurrent ? state.curopt->name : NULL)))
-		return 1;
+	    ca_inactive(d, state.curopt->xor, cur, 0);
 
 	    /* Collect the argument strings. Maybe. */
 
@@ -2135,9 +2211,7 @@ ca_parse_line(Cadef d, int multi, int first)
 		    if (!state.oargs[tmpopt->num])
 			state.oargs[tmpopt->num] = znewlinklist();
 
-		    if (ca_inactive(d, tmpopt->xor, cur, 0,
-                                    (cur == compcurrent ? tmpopt->name : NULL)))
-			return 1;
+		    ca_inactive(d, tmpopt->xor, cur, 0);
 		}
 	    }
 	    if (state.def &&
@@ -2159,20 +2233,17 @@ ca_parse_line(Cadef d, int multi, int first)
 	    else
 		state.curopt = NULL;
 	} else if (multi && (*line == '-' || *line == '+') && cur != compcurrent
-#if 0
-		   /**** Ouch. Using this will disable the mutual exclusion
-			 of different sets. Not using it will make the -A
-			 pattern be effectively ignored with multiple sets. */
-		   && (!napat || !pattry(napat, line))
-#endif
-		   )
+		&& (ca_foreign_opt(d, all, line)))
 	    return 1;
-	else if (state.arg && (!napat || !pattry(napat, line))) {
+	else if (state.arg &&
+		 (!napat || cur <= compcurrent || !pattry(napat, line))) {
 	    /* Otherwise it's a normal argument. */
-	    if (napat && ca_inactive(d, NULL, cur + 1, 1, NULL))
-		return 1;
+	    if (napat && cur <= compcurrent)
+		ca_inactive(d, NULL, cur + 1, 1);
 
 	    arglast = 1;
+	    /* if this is the first normal arg after an option, may have been
+	     * earlier normal arguments if they're intermixed with options */
 	    if (state.inopt) {
 		state.inopt = 0;
 		state.nargbeg = cur - 1;
@@ -2203,7 +2274,6 @@ ca_parse_line(Cadef d, int multi, int first)
 		if (ca_laststate.def)
 		    break;
 
-		state.inrest = 0;
 		state.opt = (cur == state.nargbeg + 1 &&
 			     (!multi || !*line || 
 			      *line == '-' || *line == '+'));
@@ -2308,7 +2378,10 @@ ca_parse_line(Cadef d, int multi, int first)
     return 0;
 }
 
-/* Build a colon-list from a list. */
+/* Build a colon-list from a list.
+ *
+ * This is only used to populate values of $opt_args.
+ */
 
 static char *
 ca_colonlist(LinkList l)
@@ -2318,16 +2391,19 @@ ca_colonlist(LinkList l)
 	int len = 0;
 	char *p, *ret, *q;
 
+	/* Compute the length to be allocated. */
 	for (n = firstnode(l); n; incnode(n)) {
 	    len++;
 	    for (p = (char *) getdata(n); *p; p++)
-		len += (*p == ':' ? 2 : 1);
+		len += (*p == ':' || *p == '\\') ? 2 : 1;
 	}
 	ret = q = (char *) zalloc(len);
 
+	/* Join L into RET, joining with colons and escaping colons and
+	 * backslashes. */
 	for (n = firstnode(l); n;) {
 	    for (p = (char *) getdata(n); *p; p++) {
-		if (*p == ':')
+		if (*p == ':' || *p == '\\')
 		    *q++ = '\\';
 		*q++ = *p;
 	    }
@@ -2387,19 +2463,19 @@ ca_set_data(LinkList descr, LinkList act, LinkList subc,
 		    restrict_range(ca_laststate.argbeg, ca_laststate.argend);
 	    }
 	    if (arg->opt) {
-		buf = (char *) zhalloc((arg->set ? strlen(arg->set) : 0) +
+		buf = (char *) zhalloc((arg->gsname ? strlen(arg->gsname) : 0) +
 				       strlen(arg->opt) + 40);
 		if (arg->num > 0 && arg->type < CAA_REST)
 		    sprintf(buf, "%soption%s-%d",
-			    (arg->set ? arg->set : ""), arg->opt, arg->num);
+			    (arg->gsname ? arg->gsname : ""), arg->opt, arg->num);
 		else
 		    sprintf(buf, "%soption%s-rest",
-			    (arg->set ? arg->set : ""), arg->opt);
+			    (arg->gsname ? arg->gsname : ""), arg->opt);
 	    } else if (arg->num > 0) {
 		sprintf(nbuf, "argument-%d", arg->num);
-		buf = (arg->set ? dyncat(arg->set, nbuf) : dupstring(nbuf));
+		buf = (arg->gsname ? dyncat(arg->gsname, nbuf) : dupstring(nbuf));
 	    } else
-		buf = (arg->set ? dyncat(arg->set, "argument-rest") :
+		buf = (arg->gsname ? dyncat(arg->gsname, "argument-rest") :
 		       dupstring("argument-rest"));
 
 	    addlinknode(subc, buf);
@@ -2503,46 +2579,29 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
          * auto-description string, the optional -s, -S, -A and -M options
          * given to _arguments and the specs. */
 	if (compcurrent > 1 && compwords[0]) {
-	    Cadef def;
+	    Cadef def, all;
 	    int cap = ca_parsed, multi, first = 1, use, ret = 0;
-	    LinkList cax = ca_xor, nx;
-	    LinkNode node;
 	    Castate states = NULL, sp;
-	    char *xor[2];
 
 	    ca_parsed = 0;
-	    xor[1] = NULL;
 
-	    if (!(def = get_cadef(nam, args + 1)))
+	    if (!(def = all = get_cadef(nam, args + 1)))
 		return 1;
 
-	    multi = !!def->snext;
+	    multi = !!def->snext; /* if we have sets */
 	    ca_parsed = cap;
-	    ca_xor = (multi ? newlinklist() : NULL);
-
-	    while (def) {
-		use = !ca_parse_line(def, multi, first);
-		nx = ca_xor;
-		ca_xor = NULL;
-		while ((def = def->snext)) {
-		    if (nx) {
-			for (node = firstnode(nx); node; incnode(node)) {
-			    xor[0] = (char *) getdata(node);
-			    if (!strcmp(xor[0], def->sname) ||
-				ca_inactive(def, xor, compcurrent, 0, NULL))
-				break;
-			}
-			if (!node)
-			    break;
-		    }
-		}
-		ca_xor = nx;
+
+	    while (def) { /* for each set */
+		use = !ca_parse_line(def, all, multi, first);
+		def = def->snext;
 		if (use && def) {
+		    /* entry needed so save it into list */
 		    sp = (Castate) zalloc(sizeof(*sp));
 		    memcpy(sp, &ca_laststate, sizeof(*sp));
 		    sp->snext = states;
 		    states = sp;
 		} else if (!use && !def) {
+		    /* final entry not needed */
 		    if (states) {
 			freecastate(&ca_laststate);
 			memcpy(&ca_laststate, states, sizeof(*sp));
@@ -2554,7 +2613,6 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 		}
 		first = 0;
 	    }
-	    ca_xor = cax;
 	    ca_parsed = 1;
 	    ca_laststate.snext = states;
 
@@ -2567,7 +2625,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
          * things _arguments has to execute at this place on the line (the
          * sub-contexts are used as tags).
          * The return value is particularly important here, it says if 
-         * there are arguments to completely at all. */
+         * there are arguments to complete at all. */
 	{
 	    LinkList descr, act, subc;
 	    Caarg arg;
@@ -2770,7 +2828,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	    for (s = lstate; s; s = s->snext)
 		for (o = s->d->opts, a = s->oargs; o; o = o->next, a++)
 		    if (*a) {
-			*p++ = (o->set ? tricat(o->set, o->name, "") :
+			*p++ = (o->gsname ? tricat(o->gsname, o->name, "") :
 				ztrdup(o->name));
 			*p++ = ca_colonlist(*a);
 		    }
@@ -3511,8 +3569,8 @@ bin_compvalues(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	    Cvval val = cv_get_val(cv_laststate.d, args[1]);
 
 	    if (val && val->arg) {
-		setsparam(args[2], val->arg->descr);
-		setsparam(args[3], val->arg->action);
+		setsparam(args[2], ztrdup(val->arg->descr));
+		setsparam(args[3], ztrdup(val->arg->action));
 
 		if (args[4])
 		    setsparam(args[4], ztrdup(val->name));
@@ -3546,7 +3604,7 @@ comp_quote(char *str, int prefix)
     if ((x = (prefix && *str == '=')))
 	*str = 'x';
 
-    ret = quotestring(str, NULL, *compqstack);
+    ret = quotestring(str, *compqstack);
 
     if (x)
 	*str = *ret = '=';
@@ -3870,6 +3928,8 @@ bin_comptry(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 		    if (*q) {
 			char *qq, *qqq;
 
+			queue_signals();
+
 			if (c)
 			    *c = '\0';
 
@@ -3941,6 +4001,8 @@ bin_comptry(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 			}
 			if (c)
 			    *c = ':';
+
+			unqueue_signals();
 		    }
 		}
 		if (num) {
@@ -4403,17 +4465,24 @@ cfp_matcher_pats(char *matcher, char *add)
     if (m && m != pcm_err) {
 	char *tmp;
 	int al = strlen(add), zl = ztrlen(add), tl, cl;
-	VARARR(Cmatcher, ms, zl);
+	VARARR(Cmatcher, ms, zl);	/* One Cmatcher per character */
 	Cmatcher *mp;
 	Cpattern stopp;
 	int stopl = 0;
 
+	/* zl >= (number of wide characters) is guaranteed */
 	memset(ms, 0, zl * sizeof(Cmatcher));
 
 	for (; m && *add; m = m->next) {
 	    stopp = NULL;
 	    if (!(m->flags & (CMF_LEFT|CMF_RIGHT))) {
 		if (m->llen == 1 && m->wlen == 1) {
+		    /*
+		     * In this loop and similar loops below we step
+		     * through tmp one (possibly wide) character at a
+		     * time.  pattern_match() compares only the first
+		     * character using unmeta_one() so keep in step.
+		     */
 		    for (tmp = add, tl = al, mp = ms; tl; ) {
 			if (pattern_match(m->line, tmp, NULL, NULL)) {
 			    if (*mp) {
@@ -4423,10 +4492,10 @@ cfp_matcher_pats(char *matcher, char *add)
 			    } else
 				*mp = m;
 			}
-			cl = (*tmp == Meta) ? 2 : 1;
+			(void) unmeta_one(tmp, &cl);
 			tl -= cl;
 			tmp += cl;
-			mp += cl;
+			mp++;
 		    }
 		} else {
 		    stopp = m->line;
@@ -4443,10 +4512,10 @@ cfp_matcher_pats(char *matcher, char *add)
 			    } else
 				*mp = m;
 			}
-			cl = (*tmp == Meta) ? 2 : 1;
+			(void) unmeta_one(tmp, &cl);
 			tl -= cl;
 			tmp += cl;
-			mp += cl;
+			mp++;
 		    }
 		} else if (m->llen) {
 		    stopp = m->line;
@@ -4469,7 +4538,7 @@ cfp_matcher_pats(char *matcher, char *add)
 			al = tmp - add;
 			break;
 		    }
-		    cl = (*tmp == Meta) ? 2 : 1;
+		    (void) unmeta_one(tmp, &cl);
 		    tl -= cl;
 		    tmp += cl;
 		}
@@ -4480,6 +4549,10 @@ cfp_matcher_pats(char *matcher, char *add)
     return add;
 }
 
+/*
+ * ### This function call is skipped by _approximate, so "opt" probably means "optimize".
+ */
+
 static void
 cfp_opt_pats(char **pats, char *matcher)
 {
@@ -4646,6 +4719,8 @@ cfp_add_sdirs(LinkList final, LinkList orig, char *skipped,
 		if (!*p)
 		    continue;
 
+		queue_signals();	/* Protect PAT_STATIC */
+
 		tokenize(f);
 		pprog = patcompile(f, PAT_STATIC, NULL);
 		untokenize(f);
@@ -4678,6 +4753,8 @@ cfp_add_sdirs(LinkList final, LinkList orig, char *skipped,
 			}
 		    }
 		}
+
+		unqueue_signals();
 	    }
 	}
     }
@@ -4732,7 +4809,7 @@ cf_ignore(char **names, LinkList ign, char *style, char *path)
     for (; (n = *names); names++) {
 	if (!ztat(n, &nst, 1) && S_ISDIR(nst.st_mode)) {
 	    if (tpwd && nst.st_dev == est.st_dev && nst.st_ino == est.st_ino) {
-		addlinknode(ign, quotestring(n, NULL, QT_BACKSLASH));
+		addlinknode(ign, quotestring(n, QT_BACKSLASH));
 		continue;
 	    }
 	    if (tpar && !strncmp((c = dupstring(n)), path, pl)) {
@@ -4748,7 +4825,7 @@ cf_ignore(char **names, LinkList ign, char *style, char *path)
 		if (found || ((e = strrchr(c, '/')) && e > c + pl &&
 			      !ztat(c, &st, 1) && st.st_dev == nst.st_dev &&
 			      st.st_ino == nst.st_ino))
-		    addlinknode(ign, quotestring(n, NULL, QT_BACKSLASH));
+		    addlinknode(ign, quotestring(n, QT_BACKSLASH));
 	    }
 	}
     }
@@ -4811,6 +4888,20 @@ cf_remove_other(char **names, char *pre, int *amb)
     return NULL;
 }
 
+/*
+ * SYNOPSIS:
+ *     1. compfiles -p  parnam1 parnam2 skipped matcher sdirs parnam3 varargs [..varargs]
+ *     2. compfiles -p- parnam1 parnam2 skipped matcher sdirs parnam3 varargs [..varargs]
+ *     3. compfiles -P  parnam1 parnam2 skipped matcher sdirs parnam3 
+ *
+ *     1. Set parnam1 to an array of patterns....
+ *        ${(P)parnam1} is an in/out parameter.
+ *     2. Like #1 but without calling cfp_opt_pats().  (This is only used by _approximate.)
+ *     3. Like #1 but varargs is implicitly set to  char *varargs[2] = { "*(-/)", NULL };.
+ *
+ *     parnam2 has to do with the accept-exact style (see cfp_test_exact()).
+ */
+
 static int
 bin_compfiles(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 {
@@ -4843,7 +4934,7 @@ bin_compfiles(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 		return 0;
 	    }
 	    for (l = newlinklist(); *tmp; tmp++)
-		addlinknode(l, *tmp);
+		addlinknode(l, quotestring(*tmp, QT_BACKSLASH_PATTERN));
 	    set_list_array(args[1], cf_pats((args[0][1] == 'P'), !!args[0][2],
 					    l, getaparam(args[2], NULL), args[3],
 					    args[4], args[5],
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index 2b2654c5d..58310cd74 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -143,6 +143,7 @@
 "vi-delete", videlete, ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_VIOPER
 "vi-delete-char", videletechar, ZLE_KEEPSUFFIX
 "vi-digit-or-beginning-of-line", vidigitorbeginningofline, 0
+"vi-down-case", vidowncase, ZLE_LASTCOL | ZLE_VIOPER
 "vi-down-line-or-history", vidownlineorhistory, ZLE_LINEMOVE
 "vi-end-of-line", viendofline, ZLE_LASTCOL
 "vi-fetch-history", vifetchhistory, ZLE_LINEMOVE
@@ -188,6 +189,7 @@
 "vi-swap-case", viswapcase, ZLE_LASTCOL
 "vi-undo-change", viundochange, ZLE_KEEPSUFFIX
 "vi-unindent", viunindent, ZLE_LASTCOL | ZLE_VIOPER
+"vi-up-case", viupcase, ZLE_LASTCOL | ZLE_VIOPER
 "vi-up-line-or-history", viuplineorhistory, ZLE_LINEMOVE
 "vi-yank", viyank, ZLE_LASTCOL | ZLE_VIOPER
 "vi-yank-eol", viyankeol, 0
diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c
index 9b3277a97..3db0781ff 100644
--- a/Src/Zle/textobjects.c
+++ b/Src/Zle/textobjects.c
@@ -1,5 +1,5 @@
 /*
- * textobjects.c - ZLE module implementing Vim style text objects
+ * textobjects.c - ZLE widgets implementing Vim style text objects
  *
  * This file is part of zsh, the Z shell.
  *
@@ -54,11 +54,7 @@ selectword(UNUSED(char **args))
     int sclass = viclass(zleline[zlecs]);
     int doblanks = all && sclass;
 
-    if (!invicmdmode()) {
-	region_active = 1;
-	mark = zlecs;
-    }
-    if (!region_active || zlecs == mark) {
+    if (!region_active || zlecs == mark || mark == -1) {
 	/* search back to first character of same class as the start position
 	 * also stop at the beginning of the line */
 	mark = zlecs;
@@ -207,8 +203,12 @@ selectword(UNUSED(char **args))
     /* Adjustment: vi operators don't include the cursor position, in insert
      * or emacs mode the region also doesn't but for vi visual mode it is
      * included. */
-    if (zlecs && zlecs > mark && !virangeflag)
-	DECCS();
+    if (!virangeflag) {
+	if (!invicmdmode())
+	    region_active = 1;
+	else if (zlecs && zlecs > mark)
+	    DECCS();
+    }
 
     return 0;
 }
@@ -315,7 +315,7 @@ selectargument(UNUSED(char **args))
     }
 
     /* Adjustment: vi operators don't include the cursor position */
-    if (!virangeflag)
+    if (!virangeflag && invicmdmode())
        DECCS();
 
     return 0;
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index e9b14281d..8f92e5611 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -284,6 +284,20 @@ struct change {
 #define CH_NEXT (1<<0)   /* next structure is also part of this change */
 #define CH_PREV (1<<1)   /* previous structure is also part of this change */
 
+/* vi change handling for vi-repeat-change */
+
+/*
+ * Examination of the code suggests vichgbuf is consistently tied
+ * to raw byte input, so it is left as a character array rather
+ * than turned into wide characters.  In particular, when we replay
+ * it we use ungetbytes().
+ */
+struct vichange {
+    struct modifier mod; /* value of zmod associated with vi change */
+    char *buf;           /* bytes for keys that make up the vi command */
+    int bufsz, bufptr;   /* allocated and in use sizes of buf */
+};
+
 /* known thingies */
 
 #define Th(X) (&thingies[X])
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index abd6e1749..581ca4979 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -1220,13 +1220,14 @@ doisearch(char **args, int dir, int pattern)
 		char *patbuf = ztrdup(sbuf);
 		char *patstring;
 		/*
-		 * Use static pattern buffer since we don't need
-		 * to maintain it and won't call other pattern functions
-		 * meanwhile.
-		 * Use PAT_NOANCH because we don't need the match
-		 * anchored to the end, even if it is at the start.
+		 * Do not use static pattern buffer (PAT_STATIC) since we
+		 * call zle hooks, which might call other pattern
+		 * functions.  Use PAT_ZDUP because we re-use the pattern
+		 * in subsequent loops, so we can't pushheap/popheap.
+		 * Use PAT_NOANCH because we don't need the match anchored
+		 * to the end, even if it is at the start.
 		 */
-		int patflags = PAT_STATIC|PAT_NOANCH;
+		int patflags = PAT_ZDUP|PAT_NOANCH;
 		if (sbuf[0] == '^') {
 		    /*
 		     * We'll handle the anchor later when
@@ -1521,6 +1522,7 @@ doisearch(char **args, int dir, int pattern)
 		    if (only_one || !top_spot || old_sbptr != sbptr)
 			break;
 		}
+		freepatprog(patprog);
 		patprog = NULL;
 		nosearch = 1;
 		skip_pos = 0;
@@ -1632,6 +1634,7 @@ doisearch(char **args, int dir, int pattern)
 	    }
 	    strcpy(sbuf + sbptr, paste);
 	    sbptr += pastelen;
+	    freepatprog(patprog);
 	    patprog = NULL;
 	    free(paste);
 	} else if (cmd == Th(z_acceptsearch)) {
@@ -1682,6 +1685,7 @@ doisearch(char **args, int dir, int pattern)
 	     * always valid at this point.
 	     */
 	    sbptr += zlecharasstring(LASTFULLCHAR, sbuf + sbptr);
+	    freepatprog(patprog);
 	    patprog = NULL;
 	}
 	if (feep)
@@ -1702,6 +1706,7 @@ doisearch(char **args, int dir, int pattern)
     zsfree(okeymap);
     if (matchlist)
 	freematchlist(matchlist);
+    freepatprog(patprog);
     isearch_active = 0;
     /*
      * Don't allow unused characters provided as a string to the
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 13fd13844..04eb70675 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -135,7 +135,10 @@ mod_export HashTable keymapnamtab;
 /**/
 char *keybuf;
 
-static int keybuflen, keybufsz = 20;
+/**/
+int keybuflen;
+
+static int keybufsz = 20;
 
 /* last command executed with execute-named-command */
 
@@ -1322,15 +1325,15 @@ default_bindings(void)
 	amap->first[i] = refthingy(t_undefinedkey);
 
     /* safe fallback keymap:
-     *   0-255  self-insert, except: *
-     *    '\n'  accept-line          *
-     *    '\r'  accept-line          */
+     *   0-255  .self-insert, except: *
+     *    '\n'  .accept-line          *
+     *    '\r'  .accept-line          */
     for (i = 0; i < 256; i++)
-	smap->first[i] = refthingy(t_selfinsert);
-    unrefthingy(t_selfinsert);
-    unrefthingy(t_selfinsert);
-    smap->first['\n'] = refthingy(t_acceptline);
-    smap->first['\r'] = refthingy(t_acceptline);
+	smap->first[i] = refthingy(t_Dselfinsert);
+    unrefthingy(t_Dselfinsert);
+    unrefthingy(t_Dselfinsert);
+    smap->first['\n'] = refthingy(t_Dacceptline);
+    smap->first['\r'] = refthingy(t_Dacceptline);
 
     /* vt100 arrow keys are bound by default, for historical reasons. *
      * Both standard and keypad modes are supported.                  */
@@ -1366,6 +1369,8 @@ default_bindings(void)
     bindkey(vismap, "\33", refthingy(t_deactivateregion), NULL);
     bindkey(vismap, "o", refthingy(t_exchangepointandmark), NULL);
     bindkey(vismap, "p", refthingy(t_putreplaceselection), NULL);
+    bindkey(vismap, "u", refthingy(t_vidowncase), NULL);
+    bindkey(vismap, "U", refthingy(t_viupcase), NULL);
     bindkey(vismap, "x", refthingy(t_videlete), NULL);
     bindkey(vismap, "~", refthingy(t_vioperswapcase), NULL);
 
@@ -1374,8 +1379,12 @@ default_bindings(void)
     bindkey(amap, "ge", refthingy(t_vibackwardwordend), NULL);
     bindkey(amap, "gE", refthingy(t_vibackwardblankwordend), NULL);
     bindkey(amap, "gg", refthingy(t_beginningofbufferorhistory), NULL);
+    bindkey(amap, "gu", refthingy(t_vidowncase), NULL);
+    bindkey(amap, "gU", refthingy(t_viupcase), NULL);
     bindkey(amap, "g~", refthingy(t_vioperswapcase), NULL);
     bindkey(amap, "g~~", NULL, "g~g~");
+    bindkey(amap, "guu", NULL, "gugu");
+    bindkey(amap, "gUU", NULL, "gUgU");
 
     /* emacs mode: arrow keys */ 
     add_cursor_key(emap, TCUPCURSOR, t_uplineorhistory, 'A');
@@ -1615,11 +1624,18 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
     else
 	lastchar = lastc;
     if(lastlen != keybuflen) {
+	/*
+	 * We want to keep only the first lastlen bytes of the key
+	 * buffer in the key buffer that were marked as used by the key
+	 * binding above, and make the rest available for input again.
+	 * That rest (but not what we are keeping) needs to be
+	 * unmetafied.
+	 */
 	unmetafy(keybuf + lastlen, &keybuflen);
 	ungetbytes(keybuf+lastlen, keybuflen);
 	if(vichgflag)
-	    vichgbufptr -= keybuflen;
-	keybuf[lastlen] = 0;
+	    curvichg.bufptr -= keybuflen;
+	keybuf[keybuflen = lastlen] = 0;
     }
     *funcp = func;
     *strp = str;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 9bea76e9b..dda2f56e6 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -471,7 +471,7 @@ calc_timeout(struct ztmout *tmoutp, long do_keytmout)
 
 	    tfdat = (Timedfn)getdata(tfnode);
 	    diff = tfdat->when - time(NULL);
-	    if (diff < 0) {
+	    if (diff <= 0) {
 		/* Already due; call it and rescan. */
 		tfdat->func();
 		continue;
@@ -924,13 +924,13 @@ getbyte(long do_keytmout, int *timeout)
 	ret = STOUC(cc);
     }
     /*
-     * vichgbuf is raw bytes, not wide characters, so is dealt
+     * curvichg.buf is raw bytes, not wide characters, so is dealt
      * with here.
      */
     if (vichgflag) {
-	if (vichgbufptr == vichgbufsz)
-	    vichgbuf = realloc(vichgbuf, vichgbufsz *= 2);
-	vichgbuf[vichgbufptr++] = ret;
+	if (curvichg.bufptr == curvichg.bufsz)
+	    curvichg.buf = realloc(curvichg.buf, curvichg.bufsz *= 2);
+	curvichg.buf[curvichg.bufptr++] = ret;
     }
     errno = old_errno;
     return lastchar = ret;
@@ -1041,28 +1041,43 @@ getrestchar(int inchar, char *outstr, int *outcount)
 #endif
 
 /**/
-void redrawhook(void)
+void
+redrawhook(void)
 {
     Thingy initthingy;
     if ((initthingy = rthingy_nocreate("zle-line-pre-redraw"))) {
+	/* Duplicating most of zlecallhook() to save additional state */
+	int saverrflag = errflag, savretflag = retflag;
 	int lastcmd_prev = lastcmd;
 	int old_incompfunc = incompfunc;
 	char *args[2];
 	Thingy lbindk_save = lbindk, bindk_save = bindk;
+
 	refthingy(lbindk_save);
 	refthingy(bindk_save);
 	args[0] = initthingy->nam;
 	args[1] = NULL;
+
+	/* The generic redraw hook cannot be a completion function, so
+	 * temporarily reset state for special variable handling etc.
+	 */
 	incompfunc = 0;
-	execzlefunc(initthingy, args, 0);
+	execzlefunc(initthingy, args, 1);
 	incompfunc = old_incompfunc;
+
+	/* Restore errflag and retflag as zlecallhook() does */
+	errflag = saverrflag | (errflag & ERRFLAG_INT);
+	retflag = savretflag;
+
 	unrefthingy(initthingy);
 	unrefthingy(lbindk);
 	unrefthingy(bindk);
 	lbindk = lbindk_save;
 	bindk = bindk_save;
+
 	/* we can't set ZLE_NOTCOMMAND since it's not a legit widget, so
-	 * restore lastcmd manually so that we don't mess up the global state */
+	 * restore lastcmd manually so that we don't mess up the global state
+	 */
 	lastcmd = lastcmd_prev;
     }
 }
@@ -1159,11 +1174,19 @@ zlecore(void)
 
     }
 
-    region_active = 0;
     popheap();
 }
 
-/* Read a line.  It is returned metafied. */
+/* Read a line.  It is returned metafied.
+ *
+ * Parameters:
+ * - lp: left prompt, e.g., $PS1
+ * - rp: right prompt, e.g., $RPS1
+ * - flags: ZLRF_* flags (I think), see zlereadflags
+ * - context: ZLCON_* flags (I think), see zlecontext
+ * - init: "zle-line-init"
+ * - finish: "zle-line-finish"
+ */
 
 /**/
 char *
@@ -1223,6 +1246,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
     resetneeded = 0;
     fetchttyinfo = 0;
     trashedzle = 0;
+    clearflag = 0;
     raw_lp = lp;
     lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL, &pmpt_attr);
     raw_rp = rp;
@@ -1238,6 +1262,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
     *zleline = ZWC('\0');
     virangeflag = lastcmd = done = zlecs = zlell = mark = yankb = yanke = 0;
     vichgflag = 0;
+    viinrepeat = 0;
     viinsbegin = 0;
     statusline = NULL;
     selectkeymap("main", 1);
@@ -1293,6 +1318,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
     lastcol = -1;
     initmodifier(&zmod);
     prefixflag = 0;
+    region_active = 0;
 
     zrefresh();
 
@@ -1300,7 +1326,11 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 
     zlecallhook(init, NULL);
 
-    if ((bracket = getaparam("zle_bracketed_paste", &bracket_len)) && bracket_len == 2)
+    if (zleline && *zleline)
+	redrawhook();
+
+    if ((bracket = getaparam("zle_bracketed_paste", &bracket_len)) &&
+	bracket_len == 2)
 	fputs(*bracket, shout);
 
     zrefresh();
@@ -1342,6 +1372,16 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
     return s;
 }
 
+/**/
+static int
+execimmortal(Thingy func, char **args)
+{
+    Thingy immortal = rthingy_nocreate(dyncat(".", func->nam));
+    if (immortal)
+	return execzlefunc(immortal, args, 0);
+    return 1;
+}
+
 /*
  * Execute a widget.  The third argument indicates that the global
  * variable bindk should be set temporarily so that WIDGET etc.
@@ -1353,6 +1393,8 @@ int
 execzlefunc(Thingy func, char **args, int set_bindk)
 {
     int r = 0, ret = 0, remetafy = 0;
+    int nestedvichg = vichgflag;
+    int isrepeat = (viinrepeat == 3);
     Widget w;
     Thingy save_bindk = bindk;
 
@@ -1362,8 +1404,10 @@ execzlefunc(Thingy func, char **args, int set_bindk)
 	unmetafy_line();
 	remetafy = 1;
     }
+    if (isrepeat)
+	viinrepeat = 2;
 
-    if(func->flags & DISABLED) {
+    if (func->flags & DISABLED) {
 	/* this thingy is not the name of a widget */
 	char *nm = nicedup(func->nam, 0);
 	char *msg = tricat("No such widget `", nm, "'");
@@ -1371,7 +1415,7 @@ execzlefunc(Thingy func, char **args, int set_bindk)
 	zsfree(nm);
 	showmsg(msg);
 	zsfree(msg);
-	ret = 1;
+	ret = execimmortal(func, args);
     } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_NCOMP)) {
 	int wflags = w->flags;
 
@@ -1435,7 +1479,7 @@ execzlefunc(Thingy func, char **args, int set_bindk)
 	    zsfree(nm);
 	    showmsg(msg);
 	    zsfree(msg);
-	    ret = 1;
+	    ret = execimmortal(func, args);
 	} else {
 	    int osc = sfcontext, osi = movefd(0);
 	    int oxt = isset(XTRACE);
@@ -1457,6 +1501,12 @@ execzlefunc(Thingy func, char **args, int set_bindk)
 	    opts[XTRACE] = oxt;
 	    sfcontext = osc;
 	    endparamscope();
+	    if (errflag == ERRFLAG_ERROR) {
+		int saverr = errflag;
+		errflag &= ~ERRFLAG_ERROR;
+		if ((ret = execimmortal(func, args)) != 0)
+		    errflag |= saverr;
+	    }
 	    lastcmd = w->flags & ~(WIDGET_INUSE|WIDGET_FREE);
 	    if (inuse) {
 		w->flags &= WIDGET_INUSE|WIDGET_FREE;
@@ -1485,6 +1535,25 @@ execzlefunc(Thingy func, char **args, int set_bindk)
     CCRIGHT();
     if (remetafy)
 	metafy_line();
+
+    /* if this widget constituted the vi change, end it */
+    if (vichgflag == 2 && !nestedvichg) {
+	if (invicmdmode()) {
+	    if (ret) {
+		free(curvichg.buf);
+	    } else {
+		if (lastvichg.buf)
+		    free(lastvichg.buf);
+		lastvichg = curvichg;
+	    }
+	    vichgflag = 0;
+	    curvichg.buf = NULL;
+	} else
+	    vichgflag = 1; /* vi change continues while in insert mode */
+    }
+    if (isrepeat)
+        viinrepeat = !invicmdmode();
+
     return ret;
 }
 
@@ -1620,6 +1689,7 @@ bin_vared(char *name, char **args, Options ops, UNUSED(int func))
 	return 1;
     } else if (v) {
 	if (*s) {
+	    unqueue_signals();
 	    zwarnnam(name, "not an identifier: `%s'", args[0]);
 	    return 1;
 	}
@@ -1856,11 +1926,17 @@ int
 recursiveedit(UNUSED(char **args))
 {
     int locerror;
+    int q = queue_signal_level();
+
+    /* zlecore() expects to be entered with signal queue disabled */
+    dont_queue_signals();
 
     redrawhook();
     zrefresh();
     zlecore();
 
+    restore_queue_signals(q);
+
     locerror = errflag ? 1 : 0;
     errflag = done = eofsent = 0;
 
@@ -2185,7 +2261,7 @@ finish_(UNUSED(Module m))
     cleanup_keymaps();
     deletehashtable(thingytab);
 
-    zfree(vichgbuf, vichgbufsz);
+    zfree(lastvichg.buf, lastvichg.bufsz);
     zfree(kungetbuf, kungetsz);
     free_isrch_spots();
     if (rdstrs)
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index a040ca0df..898b552de 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -609,8 +609,10 @@ viputbefore(UNUSED(char **args))
     int n = zmult;
 
     startvichange(-1);
-    if (n < 0 || zmod.flags & MOD_NULL)
+    if (n < 0)
 	return 1;
+    if (zmod.flags & MOD_NULL)
+	return 0;
     if (zmod.flags & MOD_VIBUF)
 	kctbuf = &vibuf[zmod.vibuf];
     else
@@ -630,8 +632,10 @@ viputafter(UNUSED(char **args))
     int n = zmult;
 
     startvichange(-1);
-    if (n < 0 || zmod.flags & MOD_NULL)
+    if (n < 0)
 	return 1;
+    if (zmod.flags & MOD_NULL)
+	return 0;
     if (zmod.flags & MOD_VIBUF)
 	kctbuf = &vibuf[zmod.vibuf];
     else
@@ -780,7 +784,7 @@ bracketedpaste(char **args)
 	int n;
 	ZLE_STRING_T wpaste;
 	wpaste = stringaszleline((zmult == 1) ? pbuf :
-	    quotestring(pbuf, NULL, QT_SINGLE_OPTIONAL), 0, &n, NULL, NULL);
+	    quotestring(pbuf, QT_SINGLE_OPTIONAL), 0, &n, NULL, NULL);
 	cuttext(wpaste, n, CUT_REPLACE);
 	if (!(zmod.flags & MOD_VIBUF)) {
 	    kct = -1;
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index c6387bfc7..0a922d2d6 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -85,6 +85,8 @@ static const struct gsu_integer cursor_gsu =
 { get_cursor, set_cursor, zleunsetfn };
 static const struct gsu_integer histno_gsu =
 { get_histno, set_histno, zleunsetfn };
+static const struct gsu_integer keys_queued_count_gsu =
+{ get_keys_queued_count, NULL, zleunsetfn };
 static const struct gsu_integer mark_gsu =
 { get_mark, set_mark, zleunsetfn };
 static const struct gsu_integer numeric_gsu =
@@ -118,6 +120,12 @@ static const struct gsu_integer suffixactive_gsu =
 
 static const struct gsu_array killring_gsu =
 { get_killring, set_killring, unset_killring };
+
+static const struct gsu_scalar register_gsu =
+{ strgetfn, set_register, unset_register };
+static const struct gsu_hash registers_gsu =
+{ hashgetfn, set_registers, zleunsetfn };
+
 /* implementation is in zle_refresh.c */
 static const struct gsu_array region_highlight_gsu =
 { get_region_highlight, set_region_highlight, unset_region_highlight };
@@ -140,6 +148,8 @@ static struct zleparam {
     { "HISTNO", PM_INTEGER, GSU(histno_gsu), NULL },
     { "KEYMAP", PM_SCALAR | PM_READONLY, GSU(keymap_gsu), NULL },
     { "KEYS", PM_SCALAR | PM_READONLY, GSU(keys_gsu), NULL },
+    { "KEYS_QUEUED_COUNT", PM_INTEGER | PM_READONLY, GSU(keys_queued_count_gsu),
+      NULL},
     { "killring", PM_ARRAY, GSU(killring_gsu), NULL },
     { "LASTABORTEDSEARCH", PM_SCALAR | PM_READONLY, GSU(lastabortedsearch_gsu),
       NULL },
@@ -181,6 +191,7 @@ mod_export void
 makezleparams(int ro)
 {
     struct zleparam *zp;
+    Param reg_param;
 
     for(zp = zleparams; zp->name; zp++) {
 	Param pm = createparam(zp->name, (zp->type |PM_SPECIAL|PM_REMOVABLE|
@@ -206,6 +217,11 @@ makezleparams(int ro)
 	if ((zp->type & PM_UNSET) && (zmod.flags & (MOD_MULT|MOD_TMULT)))
 	    pm->node.flags &= ~PM_UNSET;
     }
+
+    reg_param = createspecialhash("registers", get_registers, &scan_registers,
+	    PM_LOCAL|PM_REMOVABLE);
+    reg_param->gsu.h = &registers_gsu;
+    reg_param->level = locallevel + 1;
 }
 
 /* Special unset function for ZLE special parameters: act like the standard *
@@ -446,6 +462,13 @@ get_keys(UNUSED(Param pm))
 }
 
 /**/
+static zlong
+get_keys_queued_count(UNUSED(Param pm))
+{
+    return kungetct;
+}
+
+/**/
 static void
 set_numeric(UNUSED(Param pm), zlong x)
 {
@@ -712,6 +735,111 @@ unset_killring(Param pm, int exp)
     }
 }
 
+/**/
+static void
+set_register(Param pm, char *value)
+{
+    int n = 0;
+    int offset = -1;
+    Cutbuffer vbuf;
+
+    if (!pm->node.nam || pm->node.nam[1])
+	;
+    else if (*pm->node.nam >= '0' && *pm->node.nam <= '9')
+	offset = '0' - 26;
+    else if (*pm->node.nam >= 'a' && *pm->node.nam <= 'z')
+	offset = 'a';
+
+    if (offset == -1) {
+	zerr("invalid zle register: %s", pm->node.nam);
+	return;
+    }
+
+    vbuf = &vibuf[*pm->node.nam - offset];
+    if (*value)
+	vbuf->buf = stringaszleline(value, 0, &n, NULL, NULL);
+    vbuf->len = n;
+}
+
+/**/
+static void
+unset_register(Param pm, UNUSED(int exp))
+{
+    set_register(pm, "");
+}
+
+/**/
+static void
+scan_registers(UNUSED(HashTable ht), ScanFunc func, int flags)
+{
+    int i;
+    char ch;
+    struct param pm;
+
+    memset((void *)&pm, 0, sizeof(struct param));
+    pm.node.flags = PM_SCALAR | PM_READONLY;
+    pm.gsu.s = &nullsetscalar_gsu;
+
+    for (i = 0, ch = 'a'; i < 36; i++) {
+	pm.node.nam = zhalloc(2);
+	*pm.node.nam = ch;
+	pm.node.nam[1] = '\0';
+	pm.u.str = zlelineasstring(vibuf[i].buf, vibuf[i].len, 0, NULL, NULL, 1);
+	func(&pm.node, flags);
+	if (ch++ == 'z')
+	    ch = '0';
+    }
+}
+
+/**/
+static HashNode
+get_registers(UNUSED(HashTable ht), const char *name)
+{
+    Param pm = (Param) hcalloc(sizeof(struct param));
+    int vbuf = -1;
+    pm->node.nam = dupstring(name);
+    pm->node.flags = PM_SCALAR;
+    pm->gsu.s = &register_gsu;
+
+    if (name[1])
+       ;
+    else if (*name >= '0' && *name <= '9')
+	vbuf = *name - '0' + 26;
+    else if (*name >= 'a' && *name <= 'z')
+	vbuf = *name - 'a';
+
+    if (vbuf == -1) {
+	pm->u.str = dupstring("");
+	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
+    } else
+	pm->u.str = zlelineasstring(vibuf[vbuf].buf, vibuf[vbuf].len, 0, NULL, NULL, 1);
+
+    return &pm->node;
+}
+
+/**/
+static void
+set_registers(UNUSED(Param pm), HashTable ht)
+{
+    int i;
+    HashNode hn;
+
+    if (!ht)
+        return;
+
+    for (i = 0; i < ht->hsize; i++)
+        for (hn = ht->nodes[i]; hn; hn = hn->next) {
+            struct value v;
+            v.isarr = v.flags = v.start = 0;
+            v.end = -1;
+            v.arr = NULL;
+            v.pm = (Param) hn;
+
+	    set_register(v.pm, getstrvalue(&v));
+        }
+    deleteparamtable(ht);
+}
+
 static void
 set_prepost(ZLE_STRING_T *textvar, int *lenvar, char *x)
 {
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 13ab144ed..a64e2f29e 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -946,7 +946,7 @@ addmultiword(REFRESH_ELEMENT *base, ZLE_STRING_T tptr, int ichars)
 
 /*
  * Swap the old and new video buffers, plus any associated multiword
- * buffers.  The new buffer becomes the old one; the new new buffer
+ * buffers.  The new buffer becomes the old one; the new buffer
  * will be filled with the command line next time.
  */
 static void
@@ -1143,8 +1143,7 @@ zrefresh(void)
 	tsetcap(TCALLATTRSOFF, 0);
 	tsetcap(TCSTANDOUTEND, 0);
 	tsetcap(TCUNDERLINEEND, 0);
-	/* cheat on attribute unset */
-	txtunset(TXTBOLDFACE|TXTSTANDOUT|TXTUNDERLINE);
+	txtattrmask = 0;
 
 	if (trashedzle && !clearflag)
 	    reexpandprompt(); 
@@ -1279,7 +1278,7 @@ zrefresh(void)
 #ifdef __STDC_ISO_10646__
 		 !ZSH_INVALID_WCHAR_TEST(*t) &&
 #endif
-		 iswprint(*t) && (width = WCWIDTH(*t)) > 0) {
+		 WC_ISPRINT(*t) && (width = WCWIDTH(*t)) > 0) {
 	    int ichars;
 	    if (width > rpms.sen - rpms.s) {
 		int started = 0;
@@ -1461,7 +1460,7 @@ zrefresh(void)
 	u = outputline;
 	for (; u < outputline + outll; u++) {
 #ifdef MULTIBYTE_SUPPORT
-	    if (iswprint(*u)) {
+	    if (WC_ISPRINT(*u)) {
 		int width = WCWIDTH(*u);
 		/* Handle wide characters as above */
 		if (width > rpms.sen - rpms.s) {
@@ -2435,8 +2434,8 @@ redisplay(UNUSED(char **args))
     moveto(0, 0);
     zputc(&zr_cr);		/* extra care */
     tc_upcurs(lprompth - 1);
-    resetneeded = !showinglist;
-    clearflag = showinglist;
+    resetneeded = 1;
+    clearflag = 0;
     return 0;
 }
 
@@ -2469,7 +2468,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 	if (tmpline[t0] == ZWC('\t'))
 	    vsiz = (vsiz | 7) + 2;
 #ifdef MULTIBYTE_SUPPORT
-	else if (iswprint(tmpline[t0]) && ((width = WCWIDTH(tmpline[t0])) > 0)) {
+	else if (WC_ISPRINT(tmpline[t0]) && ((width = WCWIDTH(tmpline[t0])) > 0)) {
 	    vsiz += width;
 	    if (isset(COMBININGCHARS) && IS_BASECHAR(tmpline[t0])) {
 		while (t0 < tmpll-1 && IS_COMBINING(tmpline[t0+1]))
@@ -2557,7 +2556,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 	    vp->atr = all_atr_on | all_atr_off;
 	    vp++;
 #ifdef MULTIBYTE_SUPPORT
-	} else if (iswprint(tmpline[t0]) &&
+	} else if (WC_ISPRINT(tmpline[t0]) &&
 		   (width = WCWIDTH(tmpline[t0])) > 0) {
 	    int ichars;
 	    if (isset(COMBININGCHARS) && IS_BASECHAR(tmpline[t0])) {
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 21495b6f2..c003148f8 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -269,7 +269,7 @@ freewidget(Widget w)
     zfree(w, sizeof(*w));
 }
 
-/* Add am internal widget provided by a module.  The name given is the  *
+/* Add an internal widget provided by a module.  The name given is the  *
  * canonical one, which must not begin with a dot.  The widget is first *
  * bound to the dotted canonical name; if that name is already taken by *
  * an internal widget, failure is indicated.  The same widget is then   *
@@ -678,7 +678,16 @@ bin_zle_flags(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 		else if (!strcmp(*flag, "keepsuffix"))
 		    w->flags |= ZLE_KEEPSUFFIX;
 		*/
-		else {
+	        else if (!strcmp(*flag, "vichange")) {
+		    if (invicmdmode()) {
+			startvichange(-1);
+			if (zmod.flags & (MOD_MULT|MOD_TMULT)) {
+			    Param pm = (Param) paramtab->getnode(paramtab, "NUMERIC");
+			    if (pm && pm->node.flags & PM_SPECIAL)
+				pm->node.flags &= ~PM_UNSET;
+			}
+		    }
+		} else {
 		    zwarnnam(name, "invalid flag `%s' given to zle -f", *flag);
 		    ret = 1;
 		}
@@ -694,7 +703,7 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 {
     Thingy t;
     struct modifier modsave = zmod;
-    int ret, saveflag = 0, setbindk = 0;
+    int ret, saveflag = 0, setbindk = 0, remetafy;
     char *wname = *args++, *keymap_restore = NULL, *keymap_tmp;
 
     if (!wname)
@@ -705,7 +714,15 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 	return 1;
     }
 
-    UNMETACHECK();
+    /*
+     * zle is callable in traps, so we can't be sure the line is
+     * in its normal state.
+     */
+    if (zlemetaline) {
+	unmetafy_line();
+	remetafy = 1;
+    } else
+	remetafy = 0;
 
     while (*args && **args == '-') {
 	char *num;
@@ -719,6 +736,8 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 		num = args[0][1] ? args[0]+1 : args[1];
 		if (!num) {
 		    zwarnnam(name, "number expected after -%c", **args);
+		    if (remetafy)
+			metafy_line();
 		    return 1;
 		}
 		if (!args[0][1])
@@ -736,19 +755,26 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
 		keymap_tmp = args[0][1] ? args[0]+1 : args[1];
 		if (!keymap_tmp) {
 		    zwarnnam(name, "keymap expected after -%c", **args);
+		    if (remetafy)
+			metafy_line();
 		    return 1;
 		}
 		if (!args[0][1])
 		    *++args = "" - 1;
 		keymap_restore = dupstring(curkeymapname);
-		if (selectkeymap(keymap_tmp, 0))
+		if (selectkeymap(keymap_tmp, 0)) {
+		    if (remetafy)
+			metafy_line();
 		    return 1;
+		}
 		break;
 	    case 'w':
 		setbindk = 1;
 		break;
 	    default:
 		zwarnnam(name, "unknown option: %s", *args);
+		if (remetafy)
+		    metafy_line();
 		return 1;
 	    }
 	}
@@ -756,12 +782,18 @@ bin_zle_call(char *name, char **args, UNUSED(Options ops), UNUSED(char func))
     }
 
     t = rthingy(wname);
+    /* for internal widgets we set bindk except for when getting
+     * a vi range to detect a repeated key */
+    setbindk = setbindk ||
+	(t->widget && (t->widget->flags & (WIDGET_INT | ZLE_VIOPER)) == WIDGET_INT);
     ret = execzlefunc(t, args, setbindk);
     unrefthingy(t);
     if (saveflag)
 	zmod = modsave;
     if (keymap_restore)
 	selectkeymap(keymap_restore, 0);
+    if (remetafy)
+	metafy_line();
     return ret;
 }
 
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 1d4e1d284..3d8679119 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -424,8 +424,8 @@ mod_export int instring, inbackt;
  * This uses the instring variable above.
  */
 
-#define quotename(s, e) \
-quotestring(s, e, instring == QT_NONE ? QT_BACKSLASH : instring)
+#define quotename(s) \
+quotestring(s, instring == QT_NONE ? QT_BACKSLASH : instring)
 
 /* Check if the given string is the name of a parameter and if this *
  * parameter is one worth expanding.                                */
@@ -709,7 +709,8 @@ docomplete(int lst)
 			for (t0 = cmdnamtab->hsize - 1; t0 >= 0; t0--)
 			    for (hn = cmdnamtab->nodes[t0]; hn;
 				 hn = hn->next) {
-				if (strpfx(q, hn->nam) && findcmd(hn->nam, 0))
+				if (strpfx(q, hn->nam) &&
+				    findcmd(hn->nam, 0, 0))
 				    n++;
 				if (n == 2)
 				    break;
@@ -1304,8 +1305,12 @@ get_comp_string(void)
 	    zsfree(cmdstr);
 	    cmdstr = ztrdup(tokstr);
 	    cmdtok = tok;
-	    /* If everything before is a redirection, don't reset the index */
-	    if (wordpos != redirpos)
+	    /*
+	     * If everything before is a redirection, or anything
+	     * complicated enough that we've seen the word the
+	     * cursor is on, don't reset the index.
+	     */
+	    if (wordpos != redirpos && clwpos == -1)
 		wordpos = redirpos = 0;
 	} else if (tok == SEPER) {
 	    /*
@@ -1413,9 +1418,17 @@ get_comp_string(void)
 	/* If this is the word the cursor is in and we added a `x', *
 	 * remove it.                                               */
 	if (clwpos == wordpos++ && addedx) {
+	    int chuck_at, word_diff;
 	    zlemetacs_qsub = zlemetacs - qsub;
-	    chuck(&clwords[wordpos - 1][((zlemetacs_qsub - wb) >= sl) ?
-				 (sl - 1) : (zlemetacs_qsub - wb)]);
+	    word_diff = zlemetacs_qsub - wb;
+	    /* Ensure we chuck within the word... */
+	    if (word_diff >= sl)
+		chuck_at = sl -1;
+	    else if (word_diff < 0)
+		chuck_at = 0;
+	    else
+		chuck_at = word_diff;
+	    chuck(&clwords[wordpos - 1][chuck_at]);
 	}
     } while (tok != LEXERR && tok != ENDINPUT &&
 	     (tok != SEPER || (lexflags && tt0 == NULLTOK)));
@@ -1463,7 +1476,9 @@ get_comp_string(void)
 	t0 = STRING;
     } else if (t0 == STRING || t0 == TYPESET) {
 	/* We found a simple string. */
-	s = ztrdup(clwords[clwpos]);
+	s = clwords[clwpos];
+	DPUTS(!s, "Completion word has disappeared!");
+	s = ztrdup(s ? s : "");
     } else if (t0 == ENVSTRING) {
 	char sav;
 	/* The cursor was inside a parameter assignment. */
@@ -2004,11 +2019,11 @@ get_comp_string(void)
 
 			new->next = NULL;
 			new->str = dupstrpfx(bbeg, len);
-			new->str = ztrdup(quotename(new->str, NULL));
+			new->str = ztrdup(quotename(new->str));
 			untokenize(new->str);
 			new->pos = begi;
 			*dbeg = '\0';
-			new->qpos = strlen(quotename(predup, NULL));
+			new->qpos = strlen(quotename(predup));
 			*dbeg = '{';
 			i -= len;
 			boffs -= len;
@@ -2067,11 +2082,11 @@ get_comp_string(void)
 			lastbrbeg = new;
 
 			new->str = dupstrpfx(bbeg, len);
-			new->str = ztrdup(quotename(new->str, NULL));
+			new->str = ztrdup(quotename(new->str));
 			untokenize(new->str);
 			new->pos = begi;
 			*dbeg = '\0';
-			new->qpos = strlen(quotename(predup, NULL));
+			new->qpos = strlen(quotename(predup));
 			*dbeg = '{';
 			i -= len;
 			boffs -= len;
@@ -2117,7 +2132,7 @@ get_comp_string(void)
 		    brend = new;
 
 		    new->str = dupstrpfx(bbeg, len);
-		    new->str = ztrdup(quotename(new->str, NULL));
+		    new->str = ztrdup(quotename(new->str));
 		    untokenize(new->str);
 		    new->pos = dp - predup - len + 1;
 		    new->qpos = len;
@@ -2146,11 +2161,11 @@ get_comp_string(void)
 		lastbrbeg = new;
 
 		new->str = dupstrpfx(bbeg, len);
-		new->str = ztrdup(quotename(new->str, NULL));
+		new->str = ztrdup(quotename(new->str));
 		untokenize(new->str);
 		new->pos = begi;
 		*dbeg = '\0';
-		new->qpos = strlen(quotename(predup, NULL));
+		new->qpos = strlen(quotename(predup));
 		*dbeg = '{';
 		boffs -= len;
 		memmove(dbeg, dbeg + len, 1+strlen(dbeg+len));
@@ -2165,7 +2180,7 @@ get_comp_string(void)
 		    p = bp->pos;
 		    l = bp->qpos;
 		    bp->pos = strlen(predup + p + l);
-		    bp->qpos = strlen(quotename(predup + p + l, NULL));
+		    bp->qpos = strlen(quotename(predup + p + l));
 		    memmove(predup + p, predup + p + l, 1+bp->pos);
 		}
 	    }
@@ -2288,7 +2303,7 @@ doexpansion(char *s, int lst, int olst, int explincmd)
     foredel(we - wb, CUT_RAW);
     while ((ss = (char *)ugetnode(vl))) {
 	ret = 0;
-	ss = quotename(ss, NULL);
+	ss = quotename(ss);
 	untokenize(ss);
 	inststr(ss);
 	if (nonempty(vl) || !first) {
@@ -2997,7 +3012,7 @@ processcmd(UNUSED(char **args))
     inststr(" ");
     untokenize(s);
 
-    inststr(quotename(s, NULL));
+    inststr(quotename(s));
 
     zsfree(s);
     done = 1;
@@ -3027,7 +3042,7 @@ expandcmdpath(UNUSED(char **args))
 	return 1;
     }
 
-    str = findcmd(s, 1);
+    str = findcmd(s, 1, 0);
     zsfree(s);
     if (!str)
 	return 1;
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 68794c66a..c6df3d89c 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -1589,9 +1589,14 @@ undo(char **args)
 	    break;
 	if (prev->changeno <= undo_limitno && !*args)
 	    return 1;
-	if (!unapplychange(prev) && last_change >= 0)
-	    unapplychange(prev);
-	curchange = prev;
+	if (!unapplychange(prev)) {
+	    if (last_change >= 0) {
+		unapplychange(prev);
+		curchange = prev;
+	    }
+	} else {
+	    curchange = prev;
+	}
     } while (last_change >= (zlong)0 || (curchange->flags & CH_PREV));
     setlastline();
     return 0;
@@ -1699,6 +1704,7 @@ mergeundo(void)
 	current->flags |= CH_PREV;
 	current->prev->flags |= CH_NEXT;
     }
+    vistartchange = -1;
 }
 
 /*
@@ -1720,6 +1726,8 @@ zlecallhook(char *name, char *arg)
     if (!thingy)
 	return;
 
+    /* If anything here needs changing, see also redrawhook() */
+
     saverrflag = errflag;
     savretflag = retflag;
 
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index 953af2401..e0923db3e 100644
--- a/Src/Zle/zle_vi.c
+++ b/Src/Zle/zle_vi.c
@@ -45,61 +45,71 @@ int wordflag;
 /**/
 int vilinerange;
 
-/* last vi change buffer, for vi change repetition */
+/*
+ * lastvichg: last vi change buffer, for vi change repetition
+ * curvichg: current incomplete vi change
+ */
+
+/**/
+struct vichange lastvichg, curvichg;
+
+/*
+ * true whilst a vi change is active causing keys to be
+ * accumulated in curvichg.buf
+ * first set to 2 and when the initial widget finishes, reduced to 1 if
+ * in insert mode implying that the change continues until returning to
+ * normal mode
+ */
 
 /**/
-int vichgbufsz, vichgbufptr, vichgflag;
+int vichgflag;
 
 /*
- * Examination of the code suggests vichgbuf is consistently tied
- * to raw byte input, so it is left as a character array rather
- * than turned into wide characters.  In particular, when we replay
- * it we use ungetbytes().
+ * analogous to vichgflag for a repeated change with the value following
+ * a similar pattern (is 3 until first repeated widget starts)
  */
+
 /**/
-char *vichgbuf;
+int viinrepeat;
 
 /* point where vi insert mode was last entered */
 
 /**/
 int viinsbegin;
 
-static struct modifier lastmod;
-static int inrepeat, vichgrepeat;
-
 /**
  * im: >= 0: is an insertmode
- *    -1: skip setting insert mode
+ *    -1: skip setting insert/overwrite mode
  *    -2: entering viins at start of editing from clean --- don't use
- *        inrepeat or lastchar, synthesise an i to enter insert mode.
+ *        inrepeat or keybuf, synthesise an entry to insert mode.
+ * Note that zmult is updated so this should be called before zmult is used.
  */
 
 /**/
 void
 startvichange(int im)
 {
-    if (im != -1) {
-	vichgflag = 1;
-	if (im > -1)
-	    insmode = im;
-    }
-    if (inrepeat && im != -2) {
-	zmod = lastmod;
-	inrepeat = vichgflag = 0;
-	vichgrepeat = 1;
-    } else {
-	lastmod = zmod;
-	if (vichgbuf)
-	    free(vichgbuf);
-	vichgbuf = (char *)zalloc(vichgbufsz = 16);
+    if (im > -1)
+	insmode = im;
+    if (viinrepeat && im != -2) {
+	zmod = lastvichg.mod;
+	vichgflag = 0;
+    } else if (!vichgflag) {
+	curvichg.mod = zmod;
+	if (curvichg.buf)
+	    free(curvichg.buf);
+	curvichg.buf = (char *)zalloc(curvichg.bufsz = 16 + keybuflen);
 	if (im == -2) {
-	    vichgbuf[0] =
+	    vichgflag = 1;
+	    curvichg.buf[0] =
 		zlell ? (insmode ? (zlecs < zlell ? 'i' : 'a') : 'R') : 'o';
+	    curvichg.buf[1] = '\0';
+	    curvichg.bufptr = 1;
 	} else {
-	    vichgbuf[0] = lastchar;
+	    vichgflag = 2;
+	    strcpy(curvichg.buf, keybuf);
+	    unmetafy(curvichg.buf, &curvichg.bufptr);
 	}
-	vichgbufptr = 1;
-	vichgrepeat = 0;
     }
 }
 
@@ -208,10 +218,13 @@ getvirange(int wf)
 	     */
 	    if ((k2 == bindk) ? dovilinerange() : execzlefunc(k2, zlenoargs, 1))
 		ret = -1;
-	    if(vichgrepeat)
+	    if (viinrepeat)
 		zmult = mult1;
-	    else
+	    else {
 		zmult = mult1 * zmod.tmult;
+		if (vichgflag == 2)
+		    curvichg.mod.mult = zmult;
+            }
 	} while(prefixflag && !ret);
 	wordflag = 0;
 	selectlocalmap(NULL);
@@ -383,7 +396,6 @@ videlete(UNUSED(char **args))
 	    vifirstnonblank(zlenoargs);
 	}
     }
-    vichgflag = 0;
     return ret;
 }
 
@@ -391,9 +403,10 @@ videlete(UNUSED(char **args))
 int
 videletechar(char **args)
 {
-    int n = zmult;
+    int n;
 
     startvichange(-1);
+    n = zmult;
 
     /* handle negative argument */
     if (n < 0) {
@@ -440,9 +453,10 @@ vichange(UNUSED(char **args))
 int
 visubstitute(UNUSED(char **args))
 {
-    int n = zmult;
+    int n;
 
     startvichange(1);
+    n = zmult;
     if (n < 0)
 	return 1;
     /* it is an error to be on the end of line */
@@ -498,7 +512,6 @@ viyank(UNUSED(char **args))
 	cut(zlecs, c2 - zlecs, CUT_YANK);
 	ret = 0;
     }
-    vichgflag = 0;
     /* cursor now at the start of the range yanked. For line mode
      * restore the column position */
     if (vilinerange && lastcol != -1) {
@@ -536,9 +549,10 @@ int
 viyankwholeline(UNUSED(char **args))
 {
     int bol = findbol(), oldcs = zlecs;
-    int n = zmult;
+    int n;
 
     startvichange(-1);
+    n = zmult;
     if (n < 1)
 	return 1;
     while(n--) {
@@ -572,16 +586,17 @@ vireplace(UNUSED(char **args))
  * a change, we always read the argument normally, even if the count    *
  * was bad.  When recording a change for repeating, and a bad count is  *
  * given, we squash the repeat buffer to avoid repeating the partial    *
- * command; we've lost the previous change, but that can't be avoided   *
- * without a rewrite of the repeat code.                                */
+ * command.                                                             */
 
 /**/
 int
 vireplacechars(UNUSED(char **args))
 {
     ZLE_INT_T ch;
-    int n = zmult, fail = 0, newchars = 0;
+    int n, fail = 0, newchars = 0;
 
+    startvichange(1);
+    n = zmult;
     if (n > 0) {
 	if (region_active) {
 	    int a, b;
@@ -618,21 +633,15 @@ vireplacechars(UNUSED(char **args))
 	    n = pos - zlecs;
 	}
     }
-    startvichange(1);
+
     /* check argument range */
     if (n < 1 || fail) {
-	if(vichgrepeat)
+	if (viinrepeat)
 	    vigetkey();
-	if(vichgflag) {
-	    free(vichgbuf);
-	    vichgbuf = NULL;
-	    vichgflag = 0;
-	}
 	return 1;
     }
     /* get key */
     if((ch = vigetkey()) == ZLEEOF) {
-	vichgflag = 0;
 	return 1;
     }
     /* do change */
@@ -659,7 +668,6 @@ vireplacechars(UNUSED(char **args))
 	    zleline[zlecs++] = ch;
 	zlecs--;
     }
-    vichgflag = 0;
     return 0;
 }
 
@@ -670,7 +678,16 @@ vicmdmode(UNUSED(char **args))
     if (invicmdmode() || selectkeymap("vicmd", 0))
 	return 1;
     mergeundo();
-    vichgflag = 0;
+    insmode = unset(OVERSTRIKE);
+    if (vichgflag == 1) {
+	vichgflag = 0;
+	if (lastvichg.buf)
+	    free(lastvichg.buf);
+	lastvichg = curvichg;
+	curvichg.buf = NULL;
+    }
+    if (viinrepeat == 1)
+        viinrepeat = 0;
     if (zlecs != findbol())
 	DECCS();
     return 0;
@@ -725,7 +742,50 @@ vioperswapcase(UNUSED(char **args))
 	vifirstnonblank();
 #endif
     }
-    vichgflag = 0;
+    return ret;
+}
+
+/**/
+int
+viupcase(UNUSED(char **args))
+{
+    int oldcs, c2, ret = 1;
+
+    /* get the range */
+    startvichange(1);
+    if ((c2 = getvirange(0)) != -1) {
+	oldcs = zlecs;
+	/* covert the case of all letters within range */
+	while (zlecs < c2) {
+	    zleline[zlecs] = ZC_toupper(zleline[zlecs]);
+	    INCCS();
+	}
+	/* go back to the first line of the range */
+	zlecs = oldcs;
+	ret = 0;
+    }
+    return ret;
+}
+
+/**/
+int
+vidowncase(UNUSED(char **args))
+{
+    int oldcs, c2, ret = 1;
+
+    /* get the range */
+    startvichange(1);
+    if ((c2 = getvirange(0)) != -1) {
+	oldcs = zlecs;
+	/* convert the case of all letters within range */
+	while (zlecs < c2) {
+	    zleline[zlecs] = ZC_tolower(zleline[zlecs]);
+	    INCCS();
+	}
+	/* go back to the first line of the range */
+	zlecs = oldcs;
+	ret = 0;
+    }
     return ret;
 }
 
@@ -734,21 +794,23 @@ int
 virepeatchange(UNUSED(char **args))
 {
     /* make sure we have a change to repeat */
-    if (!vichgbuf || vichgflag)
+    if (!lastvichg.buf || vichgflag || virangeflag)
 	return 1;
     /* restore or update the saved count and buffer */
     if (zmod.flags & MOD_MULT) {
-	lastmod.mult = zmod.mult;
-	lastmod.flags |= MOD_MULT;
+	lastvichg.mod.mult = zmod.mult;
+	lastvichg.mod.flags |= MOD_MULT;
     }
     if (zmod.flags & MOD_VIBUF) {
-	lastmod.vibuf = zmod.vibuf;
-	lastmod.flags = (lastmod.flags & ~MOD_VIAPP) |
+	lastvichg.mod.vibuf = zmod.vibuf;
+	lastvichg.mod.flags = (lastvichg.mod.flags & ~MOD_VIAPP) |
 	    MOD_VIBUF | (zmod.flags & MOD_VIAPP);
-    }
+    } else if (lastvichg.mod.flags & MOD_VIBUF &&
+	    lastvichg.mod.vibuf >= 27 && lastvichg.mod.vibuf <= 34)
+	lastvichg.mod.vibuf++; /* for "1 to "8 advance to next buffer */
     /* repeat the command */
-    inrepeat = 1;
-    ungetbytes(vichgbuf, vichgbufptr);
+    viinrepeat = 3;
+    ungetbytes(lastvichg.buf, lastvichg.bufptr);
     return 0;
 }
 
@@ -764,10 +826,8 @@ viindent(UNUSED(char **args))
 	region_active = 2;
     /* get the range */
     if ((c2 = getvirange(0)) == -1) {
-	vichgflag = 0;
 	return 1;
     }
-    vichgflag = 0;
     /* must be a line range */
     if (!vilinerange) {
 	zlecs = oldcs;
@@ -802,10 +862,8 @@ viunindent(UNUSED(char **args))
 	region_active = 2;
     /* get the range */
     if ((c2 = getvirange(0)) == -1) {
-	vichgflag = 0;
 	return 1;
     }
-    vichgflag = 0;
     /* must be a line range */
     if (!vilinerange) {
 	zlecs = oldcs;
@@ -828,12 +886,13 @@ viunindent(UNUSED(char **args))
 int
 vibackwarddeletechar(char **args)
 {
-    int n = zmult;
+    int n;
 
     if (invicmdmode())
 	startvichange(-1);
 
     /* handle negative argument */
+    n = zmult;
     if (n < 0) {
 	int ret;
 	zmult = -n;
@@ -873,10 +932,11 @@ int
 vijoin(UNUSED(char **args))
 {
     int x, pos;
-    int n = zmult;
+    int n;
     int visual = region_active;
 
     startvichange(-1);
+    n = zmult;
     if (n < 1)
 	return 1;
     if (visual && zlecs > mark) {
@@ -915,9 +975,10 @@ vijoin(UNUSED(char **args))
 int
 viswapcase(UNUSED(char **args))
 {
-    int eol, n = zmult;
+    int eol, n;
 
     startvichange(-1);
+    n = zmult;
     if (n < 1)
 	return 1;
     eol = findeol();
diff --git a/Src/Zle/zle_word.c b/Src/Zle/zle_word.c
index 3c1f26c40..e4a878eab 100644
--- a/Src/Zle/zle_word.c
+++ b/Src/Zle/zle_word.c
@@ -678,46 +678,54 @@ killword(char **args)
 int
 transposewords(UNUSED(char **args))
 {
-    int p1, p2, p3, p4, len, x = zlecs, pos;
+    int p1, p2, p3, p4, pt, len, x = zlecs, pos;
     ZLE_STRING_T temp, pp;
     int n = zmult;
     int neg = n < 0, ocs = zlecs;
 
     if (neg)
 	n = -n;
-    while (n--) {
-	while (x != zlell && zleline[x] != ZWC('\n') && !ZC_iword(zleline[x]))
-	    INCPOS(x);
-	if (x == zlell || zleline[x] == ZWC('\n')) {
-	    x = zlecs;
-	    while (x) {
-		if (ZC_iword(zleline[x]))
-		    break;
-		pos = x;
-		DECPOS(pos);
-		if (zleline[pos] == ZWC('\n'))
-		    break;
-		x = pos;
-	    }
-	    if (!x)
-		return 1;
+
+    while (x != zlell && zleline[x] != ZWC('\n') && !ZC_iword(zleline[x]))
+	INCPOS(x);
+
+    if (x == zlell || zleline[x] == ZWC('\n')) {
+	x = zlecs;
+	while (x) {
+	    if (ZC_iword(zleline[x]))
+		break;
 	    pos = x;
 	    DECPOS(pos);
 	    if (zleline[pos] == ZWC('\n'))
-		return 1;
-	}
-	for (p4 = x; p4 != zlell && ZC_iword(zleline[p4]); INCPOS(p4))
-	    ;
-	for (p3 = p4; p3; ) {
-	    pos = p3;
-	    DECPOS(pos);
-	    if (!ZC_iword(zleline[pos]))
 		break;
-	    p3 = pos;
+	    x = pos;
 	}
-	if (!p3)
+	if (!x)
 	    return 1;
-	for (p2 = p3; p2; ) {
+	pos = x;
+	DECPOS(pos);
+	if (zleline[pos] == ZWC('\n'))
+	    return 1;
+    }
+
+    for (p4 = x; p4 != zlell && ZC_iword(zleline[p4]); INCPOS(p4))
+	;
+
+    for (p3 = p4; p3; ) {
+	pos = p3;
+	DECPOS(pos);
+	if (!ZC_iword(zleline[pos]))
+	    break;
+	p3 = pos;
+    }
+
+    if (!p3)
+	return 1;
+
+    p1 = p2 = pt = p3;
+
+    while (n--) {
+	for (p2 = pt; p2; ) {
 	    pos = p2;
 	    DECPOS(pos);
 	    if (ZC_iword(zleline[pos]))
@@ -733,20 +741,24 @@ transposewords(UNUSED(char **args))
 		break;
 	    p1 = pos;
 	}
-	pp = temp = (ZLE_STRING_T)zhalloc((p4 - p1)*ZLE_CHAR_SIZE);
-	len = p4 - p3;
-	ZS_memcpy(pp, zleline + p3, len);
-	pp += len;
-	len = p3 - p2;
-	ZS_memcpy(pp, zleline + p2, len);
-	pp += len;
-	ZS_memcpy(pp, zleline + p1, p2 - p1);
+	pt = p1;
+    }
 
-	ZS_memcpy(zleline + p1, temp, p4 - p1);
+    pp = temp = (ZLE_STRING_T)zhalloc((p4 - p1)*ZLE_CHAR_SIZE);
+    len = p4 - p3;
+    ZS_memcpy(pp, zleline + p3, len);
+    pp += len;
+    len = p3 - p2;
+    ZS_memcpy(pp, zleline + p2, len);
+    pp += len;
+    ZS_memcpy(pp, zleline + p1, p2 - p1);
+
+    ZS_memcpy(zleline + p1, temp, p4 - p1);
 
-	zlecs = p4;
-    }
     if (neg)
 	zlecs = ocs;
+    else
+	zlecs = p4;
+
     return 0;
 }
diff --git a/Src/builtin.c b/Src/builtin.c
index ba7136eb4..d54b0f41c 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -48,7 +48,7 @@ static struct builtin builtins[] =
     BUILTIN(".", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL),
     BUILTIN(":", BINF_PSPECIAL, bin_true, 0, -1, 0, NULL, NULL),
     BUILTIN("alias", BINF_MAGICEQUALS | BINF_PLUSOPTS, bin_alias, 0, -1, 0, "Lgmrs", NULL),
-    BUILTIN("autoload", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "mktTUwXz", "u"),
+    BUILTIN("autoload", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "dmktrRTUwWXz", "u"),
     BUILTIN("bg", 0, bin_fg, 0, -1, BIN_BG, NULL, NULL),
     BUILTIN("break", BINF_PSPECIAL, bin_break, 0, 1, BIN_BREAK, NULL, NULL),
     BUILTIN("bye", 0, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
@@ -74,7 +74,7 @@ static struct builtin builtins[] =
     BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt:W", NULL),
     BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
     BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"),
-    BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUx:z", NULL),
+    BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMstTuUWx:z", NULL),
     BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
     BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
     BUILTIN("hash", BINF_MAGICEQUALS, bin_hash, 0, -1, 0, "Ldfmrv", NULL),
@@ -102,7 +102,7 @@ static struct builtin builtins[] =
 
     BUILTIN("popd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 1, BIN_POPD, "q", NULL),
     BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "abcC:Df:ilmnNoOpPrRsSu:v:x:X:z-", NULL),
-    BUILTIN("printf", 0, bin_print, 1, -1, BIN_PRINTF, "v:", NULL),
+    BUILTIN("printf", BINF_SKIPINVALID | BINF_SKIPDASH, bin_print, 1, -1, BIN_PRINTF, "v:", NULL),
     BUILTIN("pushd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_PUSHD, "qsPL", NULL),
     BUILTIN("pushln", 0, bin_print, 0, -1, BIN_PRINT, NULL, "-nz"),
     BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
@@ -251,7 +251,7 @@ int
 execbuiltin(LinkList args, LinkList assigns, Builtin bn)
 {
     char *pp, *name, *optstr;
-    int flags, sense, argc, execop, xtr = isset(XTRACE);
+    int flags, argc, execop, xtr = isset(XTRACE);
     struct options ops;
 
     /* initialise options structure */
@@ -296,6 +296,7 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn)
 	/* Sort out the options. */
 	if (optstr) {
 	    char *arg = *argv;
+	    int sense; /* 1 for -x, 0 for +x */
 	    /* while arguments look like options ... */
 	    while (arg &&
 		   /* Must begin with - or maybe + */
@@ -389,7 +390,7 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn)
 		if (*arg) {
 		    if(*arg == Meta)
 			*++arg ^= 32;
-		    zwarnnam(name, "bad option: -%c", *arg);
+		    zwarnnam(name, "bad option: %c%c", "+-"[sense], *arg);
 		    return 1;
 		}
 		arg = *++argv;
@@ -540,18 +541,18 @@ bin_enable(char *name, char **argv, Options ops, int func)
     /* With -m option, treat arguments as glob patterns. */
     if (OPT_ISSET(ops,'m')) {
 	for (; *argv; argv++) {
+	    queue_signals();
+
 	    /* parse pattern */
 	    tokenize(*argv);
-	    if ((pprog = patcompile(*argv, PAT_STATIC, 0))) {
-		queue_signals();
+	    if ((pprog = patcompile(*argv, PAT_STATIC, 0)))
 		match += scanmatchtable(ht, pprog, 0, 0, 0, scanfunc, 0);
-		unqueue_signals();
-	    }
 	    else {
 		untokenize(*argv);
 		zwarnnam(name, "bad pattern : %s", *argv);
 		returnval = 1;
 	    }
+	    unqueue_signals();
 	}
 	/* If we didn't match anything, we return 1. */
 	if (!match)
@@ -795,8 +796,8 @@ set_pwd_env(void)
 	unsetparam_pm(pm, 0, 1);
     }
 
-    setsparam("PWD", ztrdup(pwd));
-    setsparam("OLDPWD", ztrdup(oldpwd));
+    assignsparam("PWD", ztrdup(pwd), 0);
+    assignsparam("OLDPWD", ztrdup(oldpwd), 0);
 
     pm = (Param) paramtab->getnode(paramtab, "PWD");
     if (!(pm->node.flags & PM_EXPORTED))
@@ -973,7 +974,7 @@ cd_do_chdir(char *cnam, char *dest, int hard)
      * Normalize path under Cygwin to avoid messing with
      * DOS style names with drives in them
      */
-    static char buf[PATH_MAX];
+    static char buf[PATH_MAX+1];
 #ifdef HAVE_CYGWIN_CONV_PATH
     cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE, dest, buf,
 		     PATH_MAX);
@@ -1273,7 +1274,23 @@ fixdir(char *src)
 #ifdef __CYGWIN__
     char *s0 = src;
 #endif
-    int ret = 0;
+    /* This function is always called with n path containing at
+     * least one slash, either because one was input by the user or
+     * because the caller has prepended either pwd or a cdpath dir.
+     * If asked to make a relative change and pwd is set to ".",
+     * the current directory has been removed out from under us,
+     * so force links to be chased.
+     *
+     * Ordinarily we can't get here with "../" as the first component
+     * but handle the silly special case of ".." in cdpath.
+     *
+     * Order of comparisons here looks funny, but it short-circuits
+     * most rapidly in the event of a false condition.  Set to 2
+     * here so we still obey the (lack of) CHASEDOTS option after
+     * the first "../" is preserved (test chasedots > 1 below).
+     */
+    int chasedots = (src[0] == '.' && pwd[0] == '.' && pwd[1] == '\0' &&
+		     (src[1] == '/' || (src[1] == '.' && src[2] == '/'))) * 2;
 
 /*** if have RFS superroot directory ***/
 #ifdef HAVE_SUPERROOT
@@ -1305,12 +1322,12 @@ fixdir(char *src)
 	    while (dest > d0 + 1 && dest[-1] == '/')
 		dest--;
 	    *dest = '\0';
-	    return ret;
+	    return chasedots;
 	}
 	if (src[0] == '.' && src[1] == '.' &&
 	    (src[2] == '\0' || src[2] == '/')) {
-	    if (isset(CHASEDOTS)) {
-		ret = 1;
+	    if (isset(CHASEDOTS) || chasedots > 1) {
+		chasedots = 1;
 		/* and treat as normal path segment */
 	    } else {
 		if (dest > d0 + 1) {
@@ -1348,6 +1365,7 @@ fixdir(char *src)
 		    dest[-1] = *src++ ^ 32;
 	}
     }
+    /* unreached */
 }
 
 /**/
@@ -1472,6 +1490,7 @@ bin_fc(char *nam, char **argv, Options ops, int func)
     }
 
     if (zleactive) {
+	unqueue_signals();
 	zwarnnam(nam, "no interactive history within ZLE");
 	return 1;
     }
@@ -1610,7 +1629,7 @@ bin_fc(char *nam, char **argv, Options ops, int func)
 		unqueue_signals();
 		if (fcedit(editor, fil)) {
 		    if (stuff(fil))
-			zwarnnam("fc", "%e: %s", errno, s);
+			zwarnnam("fc", "%e: %s", errno, fil);
 		    else {
 			loop(0,1);
 			retval = lastval;
@@ -1990,11 +2009,12 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
      * handled in createparam().  Here we just avoid using it for the
      * present tests if it's unset.
      *
-     * POSIXBUILTINS horror: we need to retain the 'readonly' flag
-     * of an unset parameter.
+     * POSIXBUILTINS horror: we need to retain the 'readonly' or 'export'
+     * flags of an unset parameter.
      */
     usepm = pm && (!(pm->node.flags & PM_UNSET) ||
-		   (isset(POSIXBUILTINS) && (pm->node.flags & PM_READONLY)));
+		   (isset(POSIXBUILTINS) &&
+		    (pm->node.flags & (PM_READONLY|PM_EXPORTED))));
 
     /*
      * We need to compare types with an existing pm if special,
@@ -2117,7 +2137,8 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
 	/*
 	 * Stricter rules about retaining readonly attribute in this case.
 	 */
-	if ((on & PM_READONLY) && (!usepm || (pm->node.flags & PM_UNSET)) &&
+	if ((on & (PM_READONLY|PM_EXPORTED)) &&
+	    (!usepm || (pm->node.flags & PM_UNSET)) &&
 	    !ASG_VALUEP(asg))
 	    on |= PM_UNSET;
 	else if (usepm && (pm->node.flags & PM_READONLY) &&
@@ -2125,6 +2146,10 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
 	    zerr("read-only variable: %s", pm->node.nam);
 	    return NULL;
 	}
+	/* This is handled by createparam():
+	if (usepm && (pm->node.flags & PM_EXPORTED) && !(off & PM_EXPORTED))
+	    on |= PM_EXPORTED;
+	*/
     }
 
     /*
@@ -2266,6 +2291,10 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
 	    zerrnam(cname, "%s: restricted", pname);
 	    return pm;
 	}
+	if (pm->node.flags & PM_SINGLE) {
+	    zerrnam(cname, "%s: can only have a single instance", pname);
+	    return pm;
+	}
 	/*
 	 * For specials, we keep the same struct but zero everything.
 	 * Maybe it would be easier to create a new struct but copy
@@ -2787,6 +2816,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
 	return 0;
     }
     if (off & PM_TIED) {
+	unqueue_signals();
 	zerrnam(name, "use unset to remove tied variables");
 	return 1;
     }
@@ -2892,9 +2922,61 @@ eval_autoload(Shfunc shf, char *name, Options ops, int func)
     }
 
     return !loadautofn(shf, (OPT_ISSET(ops,'k') ? 2 :
-			     (OPT_ISSET(ops,'z') ? 0 : 1)), 1);
+			     (OPT_ISSET(ops,'z') ? 0 : 1)), 1,
+		       OPT_ISSET(ops,'d'));
 }
 
+/* Helper for bin_functions() for -X and -r options */
+
+/**/
+static int
+check_autoload(Shfunc shf, char *name, Options ops, int func)
+{
+    if (OPT_ISSET(ops,'X'))
+    {
+	return eval_autoload(shf, name, ops, func);
+    }
+    if ((OPT_ISSET(ops,'r') || OPT_ISSET(ops,'R')) &&
+	(shf->node.flags & PM_UNDEFINED))
+    {
+	char *dir_path;
+	if (shf->filename && (shf->node.flags & PM_LOADDIR)) {
+	    char *spec_path[2];
+	    spec_path[0] = shf->filename;
+	    spec_path[1] = NULL;
+	    if (getfpfunc(shf->node.nam, NULL, &dir_path, spec_path, 1)) {
+		/* shf->filename is already correct. */
+		return 0;
+	    }
+	    if (!OPT_ISSET(ops,'d')) {
+		if (OPT_ISSET(ops,'R')) {
+		    zerr("%s: function definition file not found",
+			 shf->node.nam);
+		    return 1;
+		}
+		return 0;
+	    }
+	}
+	if (getfpfunc(shf->node.nam, NULL, &dir_path, NULL, 1)) {
+	    dircache_set(&shf->filename, NULL);
+	    if (*dir_path != '/') {
+		dir_path = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP),
+				    "/", dir_path);
+		dir_path = xsymlink(dir_path, 1);
+	    }
+	    dircache_set(&shf->filename, dir_path);
+	    shf->node.flags |= PM_LOADDIR;
+	    return 0;
+	}
+	if (OPT_ISSET(ops,'R')) {
+	    zerr("%s: function definition file not found",
+		 shf->node.nam);
+	    return 1;
+	}
+	/* with -r, we don't flag an error, just let it be found later. */
+    }
+    return 0;
+}
 
 /* List a user-defined math function. */
 static void
@@ -2911,7 +2993,7 @@ listusermathfunc(MathFunc p)
     else
 	showargs = 0;
 
-    printf("functions -M %s", p->name);
+    printf("functions -M%s %s", (p->flags & MFF_STR) ? "s" : "", p->name);
     if (showargs) {
 	printf(" %d", p->minargs);
 	showargs--;
@@ -2932,6 +3014,66 @@ listusermathfunc(MathFunc p)
 }
 
 
+static void
+add_autoload_function(Shfunc shf, char *funcname)
+{
+    char *nam;
+    if (*funcname == '/' && funcname[1] &&
+	(nam = strrchr(funcname, '/')) && nam[1] &&
+	(shf->node.flags & PM_UNDEFINED)) {
+	char *dir;
+	nam = strrchr(funcname, '/');
+	if (nam == funcname) {
+	    dir = "/";
+	} else {
+	    *nam++ = '\0';
+	    dir = funcname;
+	}
+	dircache_set(&shf->filename, NULL);
+	dircache_set(&shf->filename, dir);
+	shf->node.flags |= PM_LOADDIR;
+	shf->node.flags |= PM_ABSPATH_USED;
+	shfunctab->addnode(shfunctab, ztrdup(nam), shf);
+    } else {
+        Shfunc shf2;
+        Funcstack fs;
+        const char *calling_f = NULL;
+        char buf[PATH_MAX+1];
+
+        /* Find calling function */
+        for (fs = funcstack; fs; fs = fs->prev) {
+            if (fs->tp == FS_FUNC && fs->name && (!shf->node.nam || 0 != strcmp(fs->name,shf->node.nam))) {
+                calling_f = fs->name;
+                break;
+            }
+        }
+
+        /* Get its directory */
+        if (calling_f) {
+            /* Should contain load directory, and be loaded via absolute path */
+            if ((shf2 = (Shfunc) shfunctab->getnode2(shfunctab, calling_f))
+                    && (shf2->node.flags & PM_LOADDIR) && (shf2->node.flags & PM_ABSPATH_USED)
+                    && shf2->filename)
+            {
+                if (strlen(shf2->filename) + strlen(funcname) + 1 < PATH_MAX)
+                {
+                    sprintf(buf, "%s/%s", shf2->filename, funcname);
+                    /* Set containing directory if the function file
+                     * exists (do normal FPATH processing otherwise) */
+                    if (!access(buf, R_OK)) {
+                        dircache_set(&shf->filename, NULL);
+                        dircache_set(&shf->filename, shf2->filename);
+                        shf->node.flags |= PM_LOADDIR;
+                        shf->node.flags |= PM_ABSPATH_USED;
+                    }
+                }
+            }
+        }
+
+	shfunctab->addnode(shfunctab, ztrdup(funcname), shf);
+    }
+}
+
 /* Display or change the attributes of shell functions.   *
  * If called as autoload, it will define a new autoloaded *
  * (undefined) shell function.                            */
@@ -2962,6 +3104,10 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	on |= PM_TAGGED_LOCAL;
     else if (OPT_PLUS(ops,'T'))
 	off |= PM_TAGGED_LOCAL;
+    if (OPT_MINUS(ops,'W'))
+	on |= PM_WARNNESTED;
+    else if (OPT_PLUS(ops,'W'))
+	off |= PM_WARNNESTED;
     roff = off;
     if (OPT_MINUS(ops,'z')) {
 	on |= PM_ZSHSTORED;
@@ -2977,10 +3123,17 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	off |= PM_KSHSTORED;
 	roff |= PM_KSHSTORED;
     }
+    if (OPT_MINUS(ops,'d')) {
+	on |= PM_CUR_FPATH;
+	off |= PM_CUR_FPATH;
+    } else if (OPT_PLUS(ops,'d')) {
+	off |= PM_CUR_FPATH;
+	roff |= PM_CUR_FPATH;
+    }
 
     if ((off & PM_UNDEFINED) || (OPT_ISSET(ops,'k') && OPT_ISSET(ops,'z')) ||
 	(OPT_ISSET(ops,'x') && !OPT_HASARG(ops,'x')) ||
-	(OPT_MINUS(ops,'X') && (OPT_ISSET(ops,'m') || *argv || !scriptname))) {
+	(OPT_MINUS(ops,'X') && (OPT_ISSET(ops,'m') || !scriptname))) {
 	zwarnnam(name, "invalid option(s)");
 	return 1;
     }
@@ -3019,9 +3172,9 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	} else if (OPT_ISSET(ops,'m')) {
 	    /* List matching functions. */
 	    for (; *argv; argv++) {
+		queue_signals();
 		tokenize(*argv);
 		if ((pprog = patcompile(*argv, PAT_STATIC, 0))) {
-		    queue_signals();
 		    for (p = mathfuncs, q = NULL; p; q = p) {
 			MathFunc next;
 			do {
@@ -3040,12 +3193,12 @@ bin_functions(char *name, char **argv, Options ops, int func)
 			if (p)
 			    p = p->next;
 		    }
-		    unqueue_signals();
 		} else {
 		    untokenize(*argv);
 		    zwarnnam(name, "bad pattern : %s", *argv);
 		    returnval = 1;
 		}
+		unqueue_signals();
 	    }
 	} else if (OPT_PLUS(ops,'M')) {
 	    /* Delete functions. -m is allowed but is handled above. */
@@ -3067,11 +3220,18 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	    }
 	} else {
 	    /* Add a function */
-	    int minargs = 0, maxargs = -1;
+	    int minargs, maxargs;
 	    char *funcname = *argv++;
 	    char *modname = NULL;
 	    char *ptr;
 
+	    if (OPT_ISSET(ops,'s')) {
+		minargs = maxargs = 1;
+	    } else {
+		minargs = 0;
+		maxargs = -1;
+	    }
+
 	    ptr = itype_end(funcname, IIDENT, 0);
 	    if (idigit(*funcname) || funcname == ptr || *ptr) {
 		zwarnnam(name, "-M %s: bad math function name", funcname);
@@ -3085,6 +3245,10 @@ bin_functions(char *name, char **argv, Options ops, int func)
 			     *argv);
 		    return 1;
 		}
+		if (OPT_ISSET(ops,'s') && minargs != 1) {
+		    zwarnnam(name, "-Ms: must take a single string argument");
+		    return 1;
+		}
 		maxargs = minargs;
 		argv++;
 	    }
@@ -3098,6 +3262,10 @@ bin_functions(char *name, char **argv, Options ops, int func)
 			     *argv);
 		    return 1;
 		}
+		if (OPT_ISSET(ops,'s') && maxargs != 1) {
+		    zwarnnam(name, "-Ms: must take a single string argument");
+		    return 1;
+		}
 		argv++;
 	    }
 	    if (*argv)
@@ -3110,6 +3278,8 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	    p = (MathFunc)zshcalloc(sizeof(struct mathfunc));
 	    p->name = ztrdup(funcname);
 	    p->flags = MFF_USERFUNC;
+	    if (OPT_ISSET(ops,'s'))
+		p->flags |= MFF_STR;
 	    p->module = modname ? ztrdup(modname) : NULL;
 	    p->minargs = minargs;
 	    p->maxargs = maxargs;
@@ -3117,6 +3287,7 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	    queue_signals();
 	    for (q = mathfuncs; q; q = q->next) {
 		if (!strcmp(q->name, funcname)) {
+		    unqueue_signals();
 		    zwarnnam(name, "-M %s: function already exists",
 			     funcname);
 		    zsfree(p->name);
@@ -3134,47 +3305,58 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	return returnval;
     }
 
-    /* If no arguments given, we will print functions.  If flags *
-     * are given, we will print only functions containing these  *
-     * flags, else we'll print them all.                         */
-    if (!*argv) {
-	int ret = 0;
-
+    if (OPT_MINUS(ops,'X')) {
+	Funcstack fs;
+	char *funcname = NULL;
+	int ret;
+	if (*argv && argv[1]) {
+	    zwarnnam(name, "-X: too many arguments");
+	    return 1;
+	}
 	queue_signals();
-	if (OPT_MINUS(ops,'X')) {
-	    Funcstack fs;
-	    char *funcname = NULL;
-	    for (fs = funcstack; fs; fs = fs->prev) {
-		if (fs->tp == FS_FUNC) {
-		    /*
-		     * dupstring here is paranoia but unlikely to be
-		     * problematic
-		     */
-		    funcname = dupstring(fs->name);
-		    break;
-		}
+	for (fs = funcstack; fs; fs = fs->prev) {
+	    if (fs->tp == FS_FUNC) {
+		/*
+		 * dupstring here is paranoia but unlikely to be
+		 * problematic
+		 */
+		funcname = dupstring(fs->name);
+		break;
 	    }
-	    if (!funcname)
-	    {
-		zerrnam(name, "bad autoload");
-		ret = 1;
+	}
+	if (!funcname)
+	{
+	    zerrnam(name, "bad autoload");
+	    ret = 1;
+	} else {
+	    if ((shf = (Shfunc) shfunctab->getnode(shfunctab, funcname))) {
+		DPUTS(!shf->funcdef,
+		      "BUG: Calling autoload from empty function");
 	    } else {
-		if ((shf = (Shfunc) shfunctab->getnode(shfunctab, funcname))) {
-		    DPUTS(!shf->funcdef,
-			  "BUG: Calling autoload from empty function");
-		} else {
-		    shf = (Shfunc) zshcalloc(sizeof *shf);
-		    shfunctab->addnode(shfunctab, ztrdup(funcname), shf);
-		}
-		shf->node.flags = on;
-		ret = eval_autoload(shf, funcname, ops, func);
+		shf = (Shfunc) zshcalloc(sizeof *shf);
+		shfunctab->addnode(shfunctab, ztrdup(funcname), shf);
 	    }
-	} else {
-	    if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u'))
+	    if (*argv) {
+		dircache_set(&shf->filename, NULL);
+		dircache_set(&shf->filename, *argv);
+		on |= PM_LOADDIR;
+	    }
+	    shf->node.flags = on;
+	    ret = eval_autoload(shf, funcname, ops, func);
+	}
+	unqueue_signals();
+	return ret;
+    } else if (!*argv) {
+	/* If no arguments given, we will print functions.  If flags *
+	 * are given, we will print only functions containing these  *
+	 * flags, else we'll print them all.                         */
+	int ret = 0;
+
+	queue_signals();
+	if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u'))
 		on &= ~PM_UNDEFINED;
 	    scanshfunc(1, on|off, DISABLED, shfunctab->printnode,
 		       pflags, expand);
-	}
 	unqueue_signals();
 	return ret;
     }
@@ -3183,11 +3365,11 @@ bin_functions(char *name, char **argv, Options ops, int func)
     if (OPT_ISSET(ops,'m')) {
 	on &= ~PM_UNDEFINED;
 	for (; *argv; argv++) {
+	    queue_signals();
 	    /* expand argument */
 	    tokenize(*argv);
 	    if ((pprog = patcompile(*argv, PAT_STATIC, 0))) {
 		/* with no options, just print all functions matching the glob pattern */
-		queue_signals();
 		if (!(on|off) && !OPT_ISSET(ops,'X')) {
 		    scanmatchshfunc(pprog, 1, 0, DISABLED,
 				   shfunctab->printnode, pflags, expand);
@@ -3200,19 +3382,19 @@ bin_functions(char *name, char **argv, Options ops, int func)
 				!(shf->node.flags & DISABLED)) {
 				shf->node.flags = (shf->node.flags |
 					      (on & ~PM_UNDEFINED)) & ~off;
-				if (OPT_ISSET(ops,'X') &&
-				    eval_autoload(shf, shf->node.nam, ops, func)) {
+				if (check_autoload(shf, shf->node.nam,
+						   ops, func)) {
 				    returnval = 1;
 				}
 			    }
 		    }
 		}
-		unqueue_signals();
 	    } else {
 		untokenize(*argv);
 		zwarnnam(name, "bad pattern : %s", *argv);
 		returnval = 1;
 	    }
+	    unqueue_signals();
 	}
 	return returnval;
     }
@@ -3227,8 +3409,7 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	    if (on|off) {
 		/* turn on/off the given flags */
 		shf->node.flags = (shf->node.flags | (on & ~PM_UNDEFINED)) & ~off;
-		if (OPT_ISSET(ops,'X') &&
-		    eval_autoload(shf, shf->node.nam, ops, func))
+		if (check_autoload(shf, shf->node.nam, ops, func))
 		    returnval = 1;
 	    } else
 		/* no flags, so just print */
@@ -3245,13 +3426,38 @@ bin_functions(char *name, char **argv, Options ops, int func)
 		removetrapnode(signum);
 	    }
 
+	    if (**argv == '/') {
+		char *base = strrchr(*argv, '/') + 1;
+		if (*base &&
+		    (shf = (Shfunc) shfunctab->getnode(shfunctab, base))) {
+		    char *dir;
+		    /* turn on/off the given flags */
+		    shf->node.flags =
+			(shf->node.flags | (on & ~PM_UNDEFINED)) & ~off;
+		    if (shf->node.flags & PM_UNDEFINED) {
+			/* update path if not yet loaded */
+			if (base == *argv + 1)
+			    dir = "/";
+			else {
+			    dir = *argv;
+			    base[-1] = '\0';
+			}
+			dircache_set(&shf->filename, NULL);
+			dircache_set(&shf->filename, dir);
+		    }
+		    if (check_autoload(shf, shf->node.nam, ops, func))
+			returnval = 1;
+		    continue;
+		}
+	    }
+
 	    /* Add a new undefined (autoloaded) function to the *
 	     * hash table with the corresponding flags set.     */
 	    shf = (Shfunc) zshcalloc(sizeof *shf);
 	    shf->node.flags = on;
 	    shf->funcdef = mkautofn(shf);
 	    shfunc_set_sticky(shf);
-	    shfunctab->addnode(shfunctab, ztrdup(*argv), shf);
+	    add_autoload_function(shf, *argv);
 
 	    if (signum != -1) {
 		if (settrap(signum, NULL, ZSIG_FUNC)) {
@@ -3262,8 +3468,7 @@ bin_functions(char *name, char **argv, Options ops, int func)
 		}
 	    }
 
-	    if (ok && OPT_ISSET(ops,'X') &&
-		eval_autoload(shf, shf->node.nam, ops, func))
+	    if (ok && check_autoload(shf, shf->node.nam, ops, func))
 		returnval = 1;
 	} else
 	    returnval = 1;
@@ -3317,11 +3522,11 @@ bin_unset(char *name, char **argv, Options ops, int func)
     /* with -m option, treat arguments as glob patterns */
     if (OPT_ISSET(ops,'m')) {
 	while ((s = *argv++)) {
+	    queue_signals();
 	    /* expand */
 	    tokenize(s);
 	    if ((pprog = patcompile(s, PAT_STATIC, NULL))) {
 		/* Go through the parameter table, and unset any matches */
-		queue_signals();
 		for (i = 0; i < paramtab->hsize; i++) {
 		    for (pm = (Param) paramtab->nodes[i]; pm; pm = next) {
 			/* record pointer to next, since we may free this one */
@@ -3334,12 +3539,12 @@ bin_unset(char *name, char **argv, Options ops, int func)
 			}
 		    }
 		}
-		unqueue_signals();
 	    } else {
 		untokenize(s);
 		zwarnnam(name, "bad pattern : %s", s);
 		returnval = 1;
 	    }
+	    unqueue_signals();
 	}
 	/* If we didn't match anything, we return 1. */
 	if (!match)
@@ -3506,6 +3711,7 @@ bin_whence(char *nam, char **argv, Options ops, int func)
 	    pushheap();
 	    matchednodes = newlinklist();
 	}
+	queue_signals();
 	for (; *argv; argv++) {
 	    /* parse the pattern */
 	    tokenize(*argv);
@@ -3515,7 +3721,6 @@ bin_whence(char *nam, char **argv, Options ops, int func)
 		returnval = 1;
 		continue;
 	    }
-	    queue_signals();
 	    if (!OPT_ISSET(ops,'p')) {
 		/* -p option is for path search only.    *
 		 * We're not using it, so search for ... */
@@ -3546,9 +3751,9 @@ bin_whence(char *nam, char **argv, Options ops, int func)
 	    scanmatchtable(cmdnamtab, pprog, 1, 0, 0,
 			   (all ? fetchcmdnamnode : cmdnamtab->printnode),
 			   printflags);
-
-	    unqueue_signals();
+	    run_queued_signals();
 	}
+	unqueue_signals();
 	if (all) {
 	    allmatched = argv = zlinklist2array(matchednodes);
 	    matchednodes = NULL;
@@ -3625,9 +3830,11 @@ bin_whence(char *nam, char **argv, Options ops, int func)
 		    if (wd) {
 			printf("%s: command\n", *argv);
 		    } else {
-			if (v && !csh)
+			if (v && !csh) {
 			    zputs(*argv, stdout), fputs(" is ", stdout);
-			zputs(buf, stdout);
+			    quotedzputs(buf, stdout);
+			} else
+			    zputs(buf, stdout);
 			if (OPT_ISSET(ops,'s') || OPT_ISSET(ops, 'S'))
 			    print_if_link(buf, OPT_ISSET(ops, 'S'));
 			fputc('\n', stdout);
@@ -3636,26 +3843,39 @@ bin_whence(char *nam, char **argv, Options ops, int func)
 		}
 	    }
 	    if (!informed && (wd || v || csh)) {
+		/* this is information and not an error so, as in csh, use stdout */
 		zputs(*argv, stdout);
 		puts(wd ? ": none" : " not found");
 		returnval = 1;
 	    }
 	    popheap();
-	} else if ((cnam = findcmd(*argv, 1))) {
+	} else if (func == BIN_COMMAND && OPT_ISSET(ops,'p') &&
+		   (hn = builtintab->getnode(builtintab, *argv))) {
+	    /*
+	     * Special case for "command -p[vV]" which needs to
+	     * show a builtin in preference to an external command.
+	     */
+	    builtintab->printnode(hn, printflags);
+	    informed = 1;
+	} else if ((cnam = findcmd(*argv, 1,
+				   func == BIN_COMMAND &&
+				   OPT_ISSET(ops,'p')))) {
 	    /* Found external command. */
 	    if (wd) {
 		printf("%s: command\n", *argv);
 	    } else {
-		if (v && !csh)
+		if (v && !csh) {
 		    zputs(*argv, stdout), fputs(" is ", stdout);
-		zputs(cnam, stdout);
+		    quotedzputs(cnam, stdout);
+		} else
+		    zputs(cnam, stdout);
 		if (OPT_ISSET(ops,'s') || OPT_ISSET(ops,'S'))
 		    print_if_link(cnam, OPT_ISSET(ops,'S'));
 		fputc('\n', stdout);
 	    }
 	    informed = 1;
 	} else {
-	    /* Not found at all. */
+	    /* Not found at all. That's not an error as such so this goes to stdout */
 	    if (v || csh || wd)
 		zputs(*argv, stdout), puts(wd ? ": none" : " not found");
 	    returnval = 1;
@@ -3751,6 +3971,7 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func))
         if (!(asg = getasg(&argv, NULL))) {
 	    zwarnnam(name, "bad assignment");
 	    returnval = 1;
+	    break;
         } else if (ASG_VALUEP(asg)) {
 	    if(isset(RESTRICTED)) {
 		zwarnnam(name, "restricted: %s", asg->value.scalar);
@@ -3859,11 +4080,11 @@ bin_unhash(char *name, char **argv, Options ops, int func)
      * "unhash -m '*'" is legal, but not recommended.    */
     if (OPT_ISSET(ops,'m')) {
 	for (; *argv; argv++) {
+	    queue_signals();
 	    /* expand argument */
 	    tokenize(*argv);
 	    if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) {
 		/* remove all nodes matching glob pattern */
-		queue_signals();
 		for (i = 0; i < ht->hsize; i++) {
 		    for (hn = ht->nodes[i]; hn; hn = nhn) {
 			/* record pointer to next, since we may free this one */
@@ -3874,12 +4095,12 @@ bin_unhash(char *name, char **argv, Options ops, int func)
 			}
 		    }
 		}
-		unqueue_signals();
 	    } else {
 		untokenize(*argv);
 		zwarnnam(name, "bad pattern : %s", *argv);
 		returnval = 1;
 	    }
+	    unqueue_signals();
 	}
 	/* If we didn't match anything, we return 1. */
 	if (!match)
@@ -3962,18 +4183,18 @@ bin_alias(char *name, char **argv, Options ops, UNUSED(int func))
      * glob patterns of aliases to display.       */
     if (OPT_ISSET(ops,'m')) {
 	for (; *argv; argv++) {
+	    queue_signals();
 	    tokenize(*argv);  /* expand argument */
 	    if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) {
 		/* display the matching aliases */
-		queue_signals();
 		scanmatchtable(ht, pprog, 1, flags1, flags2,
 			       ht->printnode, printflags);
-		unqueue_signals();
 	    } else {
 		untokenize(*argv);
 		zwarnnam(name, "bad pattern : %s", *argv);
 		returnval = 1;
 	    }
+	    unqueue_signals();
 	}
 	return returnval;
     }
@@ -4048,10 +4269,11 @@ bin_print(char *name, char **args, Options ops, int func)
 {
     int flen, width, prec, type, argc, n, narg, curlen = 0;
     int nnl = 0, fmttrunc = 0, ret = 0, maxarg = 0, nc = 0;
-    int flags[6], *len;
+    int flags[6], *len, visarr = 0;
     char *start, *endptr, *c, *d, *flag, *buf = NULL, spec[14], *fmt = NULL;
     char **first, **argp, *curarg, *flagch = "'0+- #", save = '\0', nullstr = '\0';
     size_t rcount = 0, count = 0;
+    size_t *cursplit = 0, *splits = 0;
     FILE *fout = stdout;
 #ifdef HAVE_OPEN_MEMSTREAM
     size_t mcount;
@@ -4083,7 +4305,7 @@ bin_print(char *name, char **args, Options ops, int func)
             return 1; \
         } \
         unlink(tmpf); \
-        if ((fout = fdopen(tempfd, "w+")) == NULL) { \
+        if ((FOUT = fdopen(tempfd, "w+")) == NULL) { \
             close(tempfd); \
             zwarnnam(name, "can't open temp file: %e", errno); \
             return 1; \
@@ -4182,10 +4404,12 @@ bin_print(char *name, char **args, Options ops, int func)
 	    zwarnnam(name, "no pattern specified");
 	    return 1;
 	}
+	queue_signals();
 	tokenize(*args);
 	if (!(pprog = patcompile(*args, PAT_STATIC, NULL))) {
 	    untokenize(*args);
 	    zwarnnam(name, "bad pattern: %s", *args);
+	    unqueue_signals();
 	    return 1;
 	}
 	for (t = p = ++args; *p; p++)
@@ -4193,6 +4417,7 @@ bin_print(char *name, char **args, Options ops, int func)
 		*t++ = *p;
 	*t = NULL;
 	first = args;
+	unqueue_signals();
 	if (fmt && !*args) return 0;
     }
     /* compute lengths, and interpret according to -P, -D, -e, etc. */
@@ -4512,6 +4737,7 @@ bin_print(char *name, char **args, Options ops, int func)
 		    short *words;
 		    if (nwords > 1) {
 			zwarnnam(name, "option -S takes a single argument");
+			unqueue_signals();
 			return 1;
 		    }
 		    words = NULL;
@@ -4581,7 +4807,8 @@ bin_print(char *name, char **args, Options ops, int func)
 			  OPT_ISSET(ops,'N') ? '\0' : ' ', fout);
 	    }
 	}
-	if (!(OPT_ISSET(ops,'n') || OPT_ISSET(ops, 'v') || nnl))
+	if (!(OPT_ISSET(ops,'n') || nnl ||
+	    (OPT_ISSET(ops, 'v') && !OPT_ISSET(ops, 'l'))))
 	    fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout);
 	if (IS_MSTREAM(fout) && (rcount = READ_MSTREAM(buf,fout)) == -1)
 	    ret = 1;
@@ -4608,11 +4835,23 @@ bin_print(char *name, char **args, Options ops, int func)
      * special cases of printing to a ZLE buffer or the history, however.
      */
 
+    if (OPT_ISSET(ops,'v')) {
+	struct value vbuf;
+	char* s = OPT_ARG(ops,'v');
+	Value v = getvalue(&vbuf, &s, 0);
+	visarr = v && PM_TYPE(v->pm->node.flags) == PM_ARRAY;
+    }
     /* printf style output */
     *spec = '%';
     argp = args;
     do {
     	rcount = count;
+	if (argp > args && visarr) { /* reusing format string */
+	    if (!splits)
+		cursplit = splits = (size_t *)zhalloc(sizeof(size_t) *
+			(arrlen(args) / (argp - args) + 1));
+	    *cursplit++ = count;
+	}
     	if (maxarg) {
 	    first += maxarg;
 	    argc -= maxarg;
@@ -4841,9 +5080,10 @@ bin_print(char *name, char **args, Options ops, int func)
 		break;
 	    case 'q':
 		stringval = curarg ?
-		    quotestring(curarg, NULL, QT_BACKSLASH_SHOWNULL) : &nullstr;
+		    quotestring(metafy(curarg, curlen, META_USEHEAP),
+				QT_BACKSLASH_SHOWNULL) : &nullstr;
 		*d = 's';
-		print_val(stringval);
+		print_val(unmetafy(stringval, &curlen));
 		break;
 	    case 'd':
 	    case 'i':
@@ -4979,18 +5219,30 @@ bin_print(char *name, char **args, Options ops, int func)
 	    if (buf)
 		free(buf);
 	} else {
-	    stringval = metafy(buf, rcount, META_REALLOC);
-	    if (OPT_ISSET(ops,'z')) {
-		zpushnode(bufstack, stringval);
-	    } else if (OPT_ISSET(ops,'v')) {
-		setsparam(OPT_ARG(ops, 'v'), stringval);
+	    if (visarr && splits) {
+		char **arrayval = zshcalloc((cursplit - splits + 2) * sizeof(char *));
+		for (;cursplit >= splits; cursplit--) {
+		    int start = cursplit == splits ? 0 : cursplit[-1];
+		    arrayval[cursplit - splits] =
+			    metafy(buf + start, count - start, META_DUP);
+		    count = start;
+		}
+		setaparam(OPT_ARG(ops, 'v'), arrayval);
+		free(buf);
 	    } else {
-		ent = prepnexthistent();
-		ent->node.nam = stringval;
-		ent->stim = ent->ftim = time(NULL);
-		ent->node.flags = 0;
-		ent->words = (short *)NULL;
-		addhistnode(histtab, ent->node.nam, ent);
+		stringval = metafy(buf, rcount, META_REALLOC);
+		if (OPT_ISSET(ops,'z')) {
+		    zpushnode(bufstack, stringval);
+		} else if (OPT_ISSET(ops,'v')) {
+		    setsparam(OPT_ARG(ops, 'v'), stringval);
+		} else {
+		    ent = prepnexthistent();
+		    ent->node.nam = stringval;
+		    ent->stim = ent->ftim = time(NULL);
+		    ent->node.flags = 0;
+		    ent->words = (short *)NULL;
+		    addhistnode(histtab, ent->node.nam, ent);
+		}
 	    }
 	}
 	unqueue_signals();
@@ -5097,7 +5349,7 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun
 	zoptind = 1;
 	optcind = 0;
     }
-    if(zoptind > arrlen(args))
+    if (arrlen_lt(args, zoptind))
 	/* no more options */
 	return 1;
 
@@ -5252,7 +5504,7 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func)
 	}
 	/*FALLTHROUGH*/
     case BIN_EXIT:
-	if (locallevel > forklevel) {
+	if (locallevel > forklevel && shell_exiting != -1) {
 	    /*
 	     * We don't exit directly from functions to allow tidying
 	     * up, in particular EXIT traps.  We still need to perform
@@ -5261,6 +5513,9 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func)
 	     *
 	     * If we are forked, we exit the shell at the function depth
 	     * at which we became a subshell, hence the comparison.
+	     *
+	     * If we are already exiting... give this all up as
+	     * a bad job.
 	     */
 	    if (stopmsg || (zexit(0,2), !stopmsg)) {
 		retflag = 1;
@@ -5307,6 +5562,14 @@ checkjobs(void)
     }
 }
 
+/*
+ * -1 if the shell is already committed to exit.
+ * positive if zexit() was already called.
+ */
+
+/**/
+int shell_exiting;
+
 /* exit the shell.  val is the return value of the shell.  *
  * from_where is
  *   1   if zexit is called because of a signal
@@ -5318,10 +5581,8 @@ checkjobs(void)
 mod_export void
 zexit(int val, int from_where)
 {
-    static int in_exit;
-
     /* Don't do anything recursively:  see below */
-    if (in_exit == -1)
+    if (shell_exiting == -1)
 	return;
 
     if (isset(MONITOR) && !stopmsg && from_where != 1) {
@@ -5334,14 +5595,14 @@ zexit(int val, int from_where)
 	}
     }
     /* Positive in_exit means we have been here before */
-    if (from_where == 2 || (in_exit++ && from_where))
+    if (from_where == 2 || (shell_exiting++ && from_where))
 	return;
 
     /*
-     * We're now committed to exiting.  Set in_exit to -1 to
+     * We're now committed to exiting.  Set shell_exiting to -1 to
      * indicate we shouldn't do any recursive processing.
      */
-    in_exit = -1;
+    shell_exiting = -1;
     /*
      * We want to do all remaining processing regardless of preceding
      * errors, even user interrupts.
@@ -5369,6 +5630,11 @@ zexit(int val, int from_where)
 	}
     }
     lastval = val;
+    /*
+     * Now we are committed to exiting any previous state
+     * is irrelevant.  Ensure trap can run.
+     */
+    errflag = intrap = 0;
     if (sigtrapped[SIGEXIT])
 	dotrap(SIGEXIT);
     callhookfunc("zshexit", NULL, 1, NULL);
diff --git a/Src/compat.c b/Src/compat.c
index 9041c0bed..a130d9264 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -33,7 +33,10 @@
 /* Return pointer to first occurence of string t *
  * in string s.  Return NULL if not present.     */
 
+/**/
 #ifndef HAVE_STRSTR
+
+/**/
 char *
 strstr(const char *s, const char *t)
 {
@@ -48,10 +51,15 @@ strstr(const char *s, const char *t)
     }
     return NULL;
 }
+
+/**/
 #endif
 
 
+/**/
 #ifndef HAVE_GETHOSTNAME
+
+/**/
 int
 gethostname(char *name, size_t namelen)
 {
@@ -65,10 +73,15 @@ gethostname(char *name, size_t namelen)
     strcpy(name, uts.nodename);
     return 0;
 }
+
+/**/
 #endif
 
 
+/**/
 #ifndef HAVE_GETTIMEOFDAY
+
+/**/
 int
 gettimeofday(struct timeval *tv, struct timezone *tz)
 {
@@ -76,20 +89,28 @@ gettimeofday(struct timeval *tv, struct timezone *tz)
     tv->tv_sec = (long)time((time_t) 0);
     return 0;
 }
+
+/**/
 #endif
 
 
 /* compute the difference between two calendar times */
 
+/**/
 #ifndef HAVE_DIFFTIME
+
+/**/
 double
 difftime(time_t t2, time_t t1)
 {
     return ((double)t2 - (double)t1);
 }
+
+/**/
 #endif
 
 
+/**/
 #ifndef HAVE_STRERROR
 extern char *sys_errlist[];
 
@@ -97,11 +118,14 @@ extern char *sys_errlist[];
  * error number, and returns a pointer to that string.    *
  * This is not a particularly robust version of strerror. */
 
+/**/
 char *
 strerror(int errnum)
 {
     return (sys_errlist[errnum]);
 }
+
+/**/
 #endif
 
 
@@ -186,6 +210,7 @@ zpathmax(char *dir)
 }
 #endif /* 0 */
 
+/**/
 #ifdef HAVE_SYSCONF
 /*
  * This is replaced by a macro from system.h if not HAVE_SYSCONF.
@@ -230,6 +255,8 @@ zopenmax(void)
 
     return (max_zsh_fd > openmax) ? max_zsh_fd : openmax;
 }
+
+/**/
 #endif
 
 /*
@@ -270,7 +297,7 @@ zgetdir(struct dirsav *d)
     int len;
 #endif
 
-    buf = zhalloc(bufsiz = PATH_MAX);
+    buf = zhalloc(bufsiz = PATH_MAX+1);
     pos = bufsiz - 1;
     buf[pos] = '\0';
     strcpy(nbuf, "../");
@@ -439,11 +466,11 @@ zgetcwd(void)
 	    free(cwd);
 	}
 #else
-	char *cwdbuf = zalloc(PATH_MAX);
+	char *cwdbuf = zalloc(PATH_MAX+1);
 	ret = getcwd(cwdbuf, PATH_MAX);
 	if (ret)
 	    ret = dupstring(ret);
-	zfree(cwdbuf, PATH_MAX);
+	zfree(cwdbuf, PATH_MAX+1);
 #endif /* GETCWD_CALLS_MALLOC */
     }
 #endif /* HAVE_GETCWD */
@@ -532,6 +559,7 @@ output64(zlong val)
 /**/
 #endif /* ZSH_64_BIT_TYPE */
 
+/**/
 #ifndef HAVE_STRTOUL
 
 /*
@@ -569,6 +597,8 @@ output64(zlong val)
  * Ignores `locale' stuff.  Assumes that the upper and lower case
  * alphabets and digits are each contiguous.
  */
+
+/**/
 unsigned long
 strtoul(nptr, endptr, base)
 	const char *nptr;
@@ -632,329 +662,35 @@ strtoul(nptr, endptr, base)
 		*endptr = any ? s - 1 : nptr;
 	return (acc);
 }
-#endif /* HAVE_STRTOUL */
 
 /**/
-#if defined(BROKEN_WCWIDTH) && (defined(__STDC_ISO_10646__) || defined(__APPLE__))
-
-/*
- * This is an implementation of wcwidth() and wcswidth() (defined in
- * IEEE Std 1002.1-2001) for Unicode.
- *
- * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
- * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
- *
- * In fixed-width output devices, Latin characters all occupy a single
- * "cell" position of equal width, whereas ideographic CJK characters
- * occupy two such cells. Interoperability between terminal-line
- * applications and (teletype-style) character terminals using the
- * UTF-8 encoding requires agreement on which character should advance
- * the cursor by how many cell positions. No established formal
- * standards exist at present on which Unicode character shall occupy
- * how many cell positions on character terminals. These routines are
- * a first attempt of defining such behavior based on simple rules
- * applied to data provided by the Unicode Consortium.
- *
- * For some graphical characters, the Unicode standard explicitly
- * defines a character-cell width via the definition of the East Asian
- * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
- * In all these cases, there is no ambiguity about which width a
- * terminal shall use. For characters in the East Asian Ambiguous (A)
- * class, the width choice depends purely on a preference of backward
- * compatibility with either historic CJK or Western practice.
- * Choosing single-width for these characters is easy to justify as
- * the appropriate long-term solution, as the CJK practice of
- * displaying these characters as double-width comes from historic
- * implementation simplicity (8-bit encoded characters were displayed
- * single-width and 16-bit ones double-width, even for Greek,
- * Cyrillic, etc.) and not any typographic considerations.
- *
- * Much less clear is the choice of width for the Not East Asian
- * (Neutral) class. Existing practice does not dictate a width for any
- * of these characters. It would nevertheless make sense
- * typographically to allocate two character cells to characters such
- * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
- * represented adequately with a single-width glyph. The following
- * routines at present merely assign a single-cell width to all
- * neutral characters, in the interest of simplicity. This is not
- * entirely satisfactory and should be reconsidered before
- * establishing a formal standard in this area. At the moment, the
- * decision which Not East Asian (Neutral) characters should be
- * represented by double-width glyphs cannot yet be answered by
- * applying a simple rule from the Unicode database content. Setting
- * up a proper standard for the behavior of UTF-8 character terminals
- * will require a careful analysis not only of each Unicode character,
- * but also of each presentation form, something the author of these
- * routines has avoided to do so far.
- *
- * http://www.unicode.org/unicode/reports/tr11/
- *
- * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
- *
- * Permission to use, copy, modify, and distribute this software
- * for any purpose and without fee is hereby granted. The author
- * disclaims all warranties with regard to this software.
- *
- * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
- */
-
-struct interval {
-  int first;
-  int last;
-};
-
-/* auxiliary function for binary search in interval table */
-static int bisearch(wchar_t ucs, const struct interval *table, int max) {
-  int min = 0;
-  int mid;
-
-  if (ucs < table[0].first || ucs > table[max].last)
-    return 0;
-  while (max >= min) {
-    mid = (min + max) / 2;
-    if (ucs > table[mid].last)
-      min = mid + 1;
-    else if (ucs < table[mid].first)
-      max = mid - 1;
-    else
-      return 1;
-  }
-
-  return 0;
-}
-
+#endif /* HAVE_STRTOUL */
 
-/* The following two functions define the column width of an ISO 10646
- * character as follows:
- *
- *    - The null character (U+0000) has a column width of 0.
- *
- *    - Other C0/C1 control characters and DEL will lead to a return
- *      value of -1.
- *
- *    - Non-spacing and enclosing combining characters (general
- *      category code Mn or Me in the Unicode database) have a
- *      column width of 0.
- *
- *    - SOFT HYPHEN (U+00AD) has a column width of 1.
- *
- *    - Other format characters (general category code Cf in the Unicode
- *      database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
- *
- *    - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
- *      have a column width of 0.
- *
- *    - Spacing characters in the East Asian Wide (W) or East Asian
- *      Full-width (F) category as defined in Unicode Technical
- *      Report #11 have a column width of 2.
- *
- *    - All remaining characters (including all printable
- *      ISO 8859-1 and WGL4 characters, Unicode control characters,
- *      etc.) have a column width of 1.
- *
- * This implementation assumes that wchar_t characters are encoded
- * in ISO 10646.
- */
+/**/
+#ifdef ENABLE_UNICODE9
+#include "./wcwidth9.h"
 
 /**/
 int
-mk_wcwidth(wchar_t ucs)
-{
-  /* sorted list of non-overlapping intervals of non-spacing characters */
-  /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
-  static const struct interval combining[] = {
-    { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
-    { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
-    { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
-    { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
-    { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
-    { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
-    { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
-    { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
-    { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
-    { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
-    { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
-    { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
-    { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
-    { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
-    { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
-    { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
-    { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
-    { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
-    { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
-    { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
-    { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
-    { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
-    { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
-    { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
-    { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
-    { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
-    { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
-    { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
-    { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
-    { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
-    { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
-    { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
-    { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
-    { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
-    { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
-    { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
-    { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
-    { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
-    { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
-    { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
-    { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
-    { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
-    { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
-    { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
-    { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
-    { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
-    { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
-    { 0xE0100, 0xE01EF }
-  };
-
-  /* test for 8-bit control characters */
-  if (ucs == 0)
-    return 0;
-  if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
-    return -1;
-
-  /* binary search in table of non-spacing characters */
-  if (bisearch(ucs, combining,
-	       sizeof(combining) / sizeof(struct interval) - 1))
-    return 0;
-
-  /* if we arrive here, ucs is not a combining or C0/C1 control character */
-
-  return 1 +
-    (ucs >= 0x1100 &&
-     (ucs <= 0x115f ||                    /* Hangul Jamo init. consonants */
-      ucs == 0x2329 || ucs == 0x232a ||
-      (ucs >= 0x2e80 && ucs <= 0xa4cf &&
-       ucs != 0x303f) ||                  /* CJK ... Yi */
-      (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
-      (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
-      (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
-      (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
-      (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
-      (ucs >= 0xffe0 && ucs <= 0xffe6) ||
-      (ucs >= 0x20000 && ucs <= 0x2fffd) ||
-      (ucs >= 0x30000 && ucs <= 0x3fffd)));
-}
-
-
-/*
- * The following functions are part of the original wcwidth.c:
- * we don't use them but I've kept them in case - pws.
- */
-#if 0
-int mk_wcswidth(const wchar_t *pwcs, size_t n)
-{
-  int w, width = 0;
-
-  for (;*pwcs && n-- > 0; pwcs++)
-    if ((w = mk_wcwidth(*pwcs)) < 0)
-      return -1;
-    else
-      width += w;
-
-  return width;
-}
-
-
-/*
- * The following functions are the same as mk_wcwidth() and
- * mk_wcswidth(), except that spacing characters in the East Asian
- * Ambiguous (A) category as defined in Unicode Technical Report #11
- * have a column width of 2. This variant might be useful for users of
- * CJK legacy encodings who want to migrate to UCS without changing
- * the traditional terminal character-width behaviour. It is not
- * otherwise recommended for general use.
- */
-int mk_wcwidth_cjk(wchar_t ucs)
+u9_wcwidth(wchar_t ucs)
 {
-  /* sorted list of non-overlapping intervals of East Asian Ambiguous
-   * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
-  static const struct interval ambiguous[] = {
-    { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
-    { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
-    { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
-    { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
-    { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
-    { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
-    { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
-    { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
-    { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
-    { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
-    { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
-    { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
-    { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
-    { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
-    { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
-    { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
-    { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
-    { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
-    { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
-    { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
-    { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
-    { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
-    { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
-    { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
-    { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
-    { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
-    { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
-    { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
-    { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
-    { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
-    { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
-    { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
-    { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
-    { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
-    { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
-    { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
-    { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
-    { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
-    { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
-    { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
-    { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
-    { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
-    { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
-    { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
-    { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
-    { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
-    { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
-    { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
-    { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
-    { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
-    { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
-    { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
-  };
-
-  /* binary search in table of non-spacing characters */
-  if (bisearch(ucs, ambiguous,
-	       sizeof(ambiguous) / sizeof(struct interval) - 1))
-    return 2;
-
-  return mk_wcwidth(ucs);
+  int w = wcwidth9(ucs);
+  if (w < -1)
+    return 1;
+  return w;
 }
 
-
-int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n)
+/**/
+int
+u9_iswprint(wint_t ucs)
 {
-  int w, width = 0;
-
-  for (;*pwcs && n-- > 0; pwcs++)
-    if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
-      return -1;
-    else
-      width += w;
-
-  return width;
+    if (ucs == 0)
+	return 0;
+    return wcwidth9(ucs) != -1;
 }
-#endif /* 0 */
 
 /**/
-#endif /* BROKEN_WCWIDTH && (__STDC_ISO_10646__ || __APPLE__) */
+#endif	/* ENABLE_UNICODE9 */
 
 /**/
 #if defined(__APPLE__) && defined(BROKEN_ISPRINT)
diff --git a/Src/cond.c b/Src/cond.c
index 0381fe94b..a63841234 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -34,7 +34,7 @@
 int tracingcond;    /* updated by execcond() in exec.c */
 
 static char *condstr[COND_MOD] = {
-    "!", "&&", "||", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
+    "!", "&&", "||", "=", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
     "-ne", "-lt", "-gt", "-le", "-ge", "=~"
 };
 
@@ -138,13 +138,13 @@ evalcond(Estate state, char *fromtest)
 		strs = arrdup(sbuf);
 		l = 2;
 	    }
-	    if (name && name[0] == '-')
-		errname = name;
-	    else if (strs[0] && *strs[0] == '-')
-		errname = strs[0];
+	    if (name && IS_DASH(name[0]))
+		untokenize(errname = name);
+	    else if (strs[0] && IS_DASH(*strs[0]))
+		untokenize(errname = strs[0]);
 	    else
 		errname = "<null>";
-	    if (name && name[0] == '-' &&
+	    if (name && IS_DASH(name[0]) &&
 		(cd = getconddef((ctype == COND_MODI), name + 1, 1))) {
 		if (ctype == COND_MOD &&
 		    (l < cd->min || (cd->max >= 0 && l > cd->max))) {
@@ -171,7 +171,7 @@ evalcond(Estate state, char *fromtest)
 		strs[0] = dupstring(name);
 		name = s;
 
-		if (name && name[0] == '-' &&
+		if (name && IS_DASH(name[0]) &&
 		    (cd = getconddef(0, name + 1, 1))) {
 		    if (l < cd->min || (cd->max >= 0 && l > cd->max)) {
 			zwarnnam(fromtest, "unknown condition: %s",
@@ -196,7 +196,8 @@ evalcond(Estate state, char *fromtest)
 	cond_subst(&left, !fromtest);
 	untokenize(left);
     }
-    if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRNEQ) {
+    if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRDEQ &&
+	ctype != COND_STRNEQ) {
 	right = ecgetstr(state, EC_DUPTOK, &htok);
 	if (htok) {
 	    cond_subst(&right, !fromtest);
@@ -208,7 +209,8 @@ evalcond(Estate state, char *fromtest)
 	    fputc(' ',xtrerr);
 	    quotedzputs(left, xtrerr);
 	    fprintf(xtrerr, " %s ", condstr[ctype]);
-	    if (ctype == COND_STREQ || ctype == COND_STRNEQ) {
+	    if (ctype == COND_STREQ || ctype == COND_STRDEQ ||
+		ctype == COND_STRNEQ) {
 		char *rt = dupstring(ecrawstr(state->prog, state->pc, NULL));
 		cond_subst(&rt, !fromtest);
 		quote_tokenized_output(rt, xtrerr);
@@ -287,11 +289,14 @@ evalcond(Estate state, char *fromtest)
 
     switch (ctype) {
     case COND_STREQ:
+    case COND_STRDEQ:
     case COND_STRNEQ:
 	{
 	    int test, npat = state->pc[1];
 	    Patprog pprog = state->prog->pats[npat];
 
+	    queue_signals();
+
 	    if (pprog == dummy_patprog1 || pprog == dummy_patprog2) {
 		char *opat;
 		int save;
@@ -305,6 +310,7 @@ evalcond(Estate state, char *fromtest)
 		if (!(pprog = patcompile(right, (save ? PAT_ZDUP : PAT_STATIC),
 					 NULL))) {
 		    zwarnnam(fromtest, "bad pattern: %s", right);
+		    unqueue_signals();
 		    return 2;
 		}
 		else if (save)
@@ -313,7 +319,9 @@ evalcond(Estate state, char *fromtest)
 	    state->pc += 2;
 	    test = (pprog && pattry(pprog, left));
 
-	    return !(ctype == COND_STREQ ? test : !test);
+	    unqueue_signals();
+
+	    return !(ctype == COND_STRNEQ ? !test : test);
 	}
     case COND_STRLT:
 	return !(strcmp(left, right) < 0);
@@ -348,6 +356,8 @@ evalcond(Estate state, char *fromtest)
 	return (!S_ISSOCK(dostat(left)));
     case 'u':
 	return (!(dostat(left) & S_ISUID));
+    case 'v':
+	return (!issetvar(left));
     case 'w':
 	return (!doaccess(left, W_OK));
     case 'x':
diff --git a/Src/exec.c b/Src/exec.c
index 2dcd5bcf5..debb0aeca 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -46,6 +46,11 @@ enum {
 /**/
 int noerrexit;
 
+/* used to suppress ERREXIT for one occurrence */
+
+/**/
+int this_noerrexit;
+
 /*
  * noerrs = 1: suppress error messages
  * noerrs = 2: don't set errflag on parse error, either
@@ -207,7 +212,7 @@ static int (*execfuncs[WC_COUNT-WC_CURSH]) _((Estate, int)) = {
 
 /* structure for command builtin for when it is used with -v or -V */
 static struct builtin commandbn =
-    BUILTIN(0, 0, bin_whence, 0, -1, BIN_COMMAND, "vV", NULL);
+    BUILTIN("command", 0, bin_whence, 0, -1, BIN_COMMAND, "pvV", NULL);
 
 /* parse string into a list */
 
@@ -424,6 +429,7 @@ execcursh(Estate state, int do_exec)
     cmdpop();
 
     state->pc = end;
+    this_noerrexit = 1;
 
     return lastval;
 }
@@ -437,7 +443,7 @@ static int
 zexecve(char *pth, char **argv, char **newenvp)
 {
     int eno;
-    static char buf[PATH_MAX * 2];
+    static char buf[PATH_MAX * 2+1];
     char **eep;
 
     unmetafy(pth, NULL);
@@ -568,11 +574,49 @@ commandnotfound(char *arg0, LinkList args)
     Shfunc shf = (Shfunc)
 	shfunctab->getnode(shfunctab, "command_not_found_handler");
 
-    if (!shf)
-	return 127;
+    if (!shf) {
+	lastval = 127;
+	return 1;
+    }
 
     pushnode(args, arg0);
-    return doshfunc(shf, args, 1);
+    lastval = doshfunc(shf, args, 1);
+    return 0;
+}
+
+/*
+ * Search the default path for cmd.
+ * pbuf of length plen is the buffer to use.
+ * Return NULL if not found.
+ */
+
+static char *
+search_defpath(char *cmd, char *pbuf, int plen)
+{
+    char *ps = DEFAULT_PATH, *pe = NULL, *s;
+
+    for (ps = DEFAULT_PATH; ps; ps = pe ? pe+1 : NULL) {
+	pe = strchr(ps, ':');
+	if (*ps == '/') {
+	    s = pbuf;
+	    if (pe) {
+		if (pe - ps >= plen)
+		    continue;
+		struncpy(&s, ps, pe-ps);
+	    } else {
+		if (strlen(ps) >= plen)
+		    continue;
+		strucpy(&s, ps);
+	    }
+	    *s++ = '/';
+	    if ((s - pbuf) + strlen(cmd) >= plen)
+		continue;
+	    strucpy(&s, cmd);
+	    if (iscom(pbuf))
+		return pbuf;
+	}
+    }
+    return NULL;
 }
 
 /* execute an external command */
@@ -582,7 +626,7 @@ static void
 execute(LinkList args, int flags, int defpath)
 {
     Cmdnam cn;
-    char buf[MAXCMDLEN], buf2[MAXCMDLEN];
+    char buf[MAXCMDLEN+1], buf2[MAXCMDLEN+1];
     char *s, *z, *arg0;
     char **argv, **pp, **newenvp = NULL;
     int eno = 0, ee;
@@ -663,29 +707,12 @@ execute(LinkList args, int flags, int defpath)
 
     /* for command -p, search the default path */
     if (defpath) {
-	char *s, pbuf[PATH_MAX];
-	char *dptr, *pe, *ps = DEFAULT_PATH;
-
-	for(;ps;ps = pe ? pe+1 : NULL) {
-	    pe = strchr(ps, ':');
-	    if (*ps == '/') {
-		s = pbuf;
-		if (pe)
-		    struncpy(&s, ps, pe-ps);
-		else
-		    strucpy(&s, ps);
-		*s++ = '/';
-		if ((s - pbuf) + strlen(arg0) >= PATH_MAX)
-		    continue;
-		strucpy(&s, arg0);
-		if (iscom(pbuf))
-		    break;
-	    }
-	}
+	char pbuf[PATH_MAX+1];
+	char *dptr;
 
-	if (!ps) {
+	if (!search_defpath(arg0, pbuf, PATH_MAX)) {
 	    if (commandnotfound(arg0, args) == 0)
-		_exit(0);
+		_exit(lastval);
 	    zerr("command not found: %s", arg0);
 	    _exit(127);
 	}
@@ -700,7 +727,7 @@ execute(LinkList args, int flags, int defpath)
     } else {
 
 	if ((cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0))) {
-	    char nn[PATH_MAX], *dptr;
+	    char nn[PATH_MAX+1], *dptr;
 
 	    if (cn->node.flags & HASHED)
 		strcpy(nn, cn->u.cmd);
@@ -749,7 +776,7 @@ execute(LinkList args, int flags, int defpath)
     if (eno)
 	zerr("%e: %s", eno, arg0);
     else if (commandnotfound(arg0, args) == 0)
-	_exit(0);
+	_exit(lastval);
     else
 	zerr("command not found: %s", arg0);
     _exit((eno == EACCES || eno == ENOEXEC) ? 126 : 127);
@@ -760,32 +787,40 @@ execute(LinkList args, int flags, int defpath)
 /*
  * Get the full pathname of an external command.
  * If the second argument is zero, return the first argument if found;
- * if non-zero, return the path using heap memory.  (RET_IF_COM(X), above).
+ * if non-zero, return the path using heap memory.  (RET_IF_COM(X),
+ * above).
+ * If the third argument is non-zero, use the system default path
+ * instead of the current path.
  */
 
 /**/
 mod_export char *
-findcmd(char *arg0, int docopy)
+findcmd(char *arg0, int docopy, int default_path)
 {
     char **pp;
     char *z, *s, buf[MAXCMDLEN];
     Cmdnam cn;
 
+    if (default_path)
+    {
+	if (search_defpath(arg0, buf, MAXCMDLEN))
+	    return docopy ? dupstring(buf) : arg0;
+	return NULL;
+    }
     cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0);
-    if (!cn && isset(HASHCMDS))
+    if (!cn && isset(HASHCMDS) && !isrelative(arg0))
 	cn = hashcmd(arg0, path);
     if ((int) strlen(arg0) > PATH_MAX)
 	return NULL;
-    for (s = arg0; *s; s++)
-	if (*s == '/') {
-	    RET_IF_COM(arg0);
-	    if (arg0 == s || unset(PATHDIRS)) {
-		return NULL;
-	    }
-	    break;
+    if ((s = strchr(arg0, '/'))) {
+	RET_IF_COM(arg0);
+	if (arg0 == s || unset(PATHDIRS) || !strncmp(arg0, "./", 2) ||
+	    !strncmp(arg0, "../", 3)) {
+	    return NULL;
 	}
+    }
     if (cn) {
-	char nn[PATH_MAX];
+	char nn[PATH_MAX+1];
 
 	if (cn->node.flags & HASHED)
 	    strcpy(nn, cn->u.cmd);
@@ -818,6 +853,11 @@ findcmd(char *arg0, int docopy)
     return NULL;
 }
 
+/*
+ * Return TRUE if the given path denotes an executable regular file, or a
+ * symlink to one.
+ */
+
 /**/
 int
 iscom(char *s)
@@ -847,6 +887,11 @@ isreallycom(Cmdnam cn)
     return iscom(fullnam);
 }
 
+/*
+ * Return TRUE if the given path contains a dot or dot-dot component
+ * and does not start with a slash.
+ */
+
 /**/
 int
 isrelative(char *s)
@@ -866,7 +911,7 @@ mod_export Cmdnam
 hashcmd(char *arg0, char **pp)
 {
     Cmdnam cn;
-    char *s, buf[PATH_MAX];
+    char *s, buf[PATH_MAX+1];
     char **pq;
 
     for (; *pp; pp++)
@@ -930,9 +975,8 @@ entersubsh(int flags)
     int sig, monitor, job_control_ok;
 
     if (!(flags & ESUB_KEEPTRAP))
-	for (sig = 0; sig < VSIGCOUNT; sig++)
-	    if (!(sigtrapped[sig] & ZSIG_FUNC) &&
-		sig != SIGDEBUG && sig != SIGZERR)
+	for (sig = 0; sig < SIGCOUNT; sig++)
+	    if (!(sigtrapped[sig] & ZSIG_FUNC))
 		unsettrap(sig);
     monitor = isset(MONITOR);
     job_control_ok = monitor && (flags & ESUB_JOB_CONTROL) && isset(POSIXJOBS);
@@ -1023,6 +1067,18 @@ entersubsh(int flags)
     }
     if (!(sigtrapped[SIGQUIT] & ZSIG_IGNORED))
 	signal_default(SIGQUIT);
+    /*
+     * sigtrapped[sig] == ZSIG_IGNORED for signals that remain ignored,
+     * but other trapped signals are temporarily blocked when intrap,
+     * and must be unblocked before continuing into the subshell.  This
+     * is orthogonal to what the default handler for the signal may be.
+     *
+     * Start loop at 1 because 0 is SIGEXIT
+     */
+    if (intrap)
+	for (sig = 1; sig < SIGCOUNT; sig++)
+	    if (sigtrapped[sig] && sigtrapped[sig] != ZSIG_IGNORED)
+		signal_unblock(signal_mask(sig));
     if (!job_control_ok)
 	opts[MONITOR] = 0;
     opts[USEZLE] = 0;
@@ -1199,6 +1255,8 @@ execlist(Estate state, int dont_change_job, int exiting)
     }
     while (wc_code(code) == WC_LIST && !breaks && !retflag && !errflag) {
 	int donedebug;
+	int this_donetrap = 0;
+	this_noerrexit = 0;
 
 	ltype = WC_LIST_TYPE(code);
 	csp = cmdsp;
@@ -1282,9 +1340,17 @@ execlist(Estate state, int dont_change_job, int exiting)
 	    goto sublist_done;
 	}
 	while (wc_code(code) == WC_SUBLIST) {
+	    int isend = (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END);
 	    next = state->pc + WC_SUBLIST_SKIP(code);
 	    if (!oldnoerrexit)
-		noerrexit = (WC_SUBLIST_TYPE(code) != WC_SUBLIST_END);
+		noerrexit = !isend;
+	    if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT) {
+		/* suppress errexit for "! this_command" */
+		if (isend)
+		    this_noerrexit = 1;
+		/* suppress errexit for ! <list-of-shell-commands> */
+		noerrexit = 1;
+	    }
 	    switch (WC_SUBLIST_TYPE(code)) {
 	    case WC_SUBLIST_END:
 		/* End of sublist; just execute, ignoring status. */
@@ -1314,10 +1380,10 @@ execlist(Estate state, int dont_change_job, int exiting)
 			/* We've skipped to the end of the list, not executing *
 			 * the final pipeline, so don't perform error handling *
 			 * for this sublist.                                   */
-			donetrap = 1;
+			this_donetrap = 1;
 			goto sublist_done;
 		    } else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) {
-			donetrap = 1;
+			this_donetrap = 1;
 			/*
 			 * Treat this in the same way as if we reached
 			 * the end of the sublist normally.
@@ -1347,10 +1413,10 @@ execlist(Estate state, int dont_change_job, int exiting)
 			/* We've skipped to the end of the list, not executing *
 			 * the final pipeline, so don't perform error handling *
 			 * for this sublist.                                   */
-			donetrap = 1;
+			this_donetrap = 1;
 			goto sublist_done;
 		    } else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) {
-			donetrap = 1;
+			this_donetrap = 1;
 			/*
 			 * Treat this in the same way as if we reached
 			 * the end of the sublist normally.
@@ -1400,7 +1466,7 @@ sublist_done:
 	/* Check whether we are suppressing traps/errexit *
 	 * (typically in init scripts) and if we haven't  *
 	 * already performed them for this sublist.       */
-	if (!noerrexit && !donetrap) {
+	if (!noerrexit && !this_noerrexit && !donetrap && !this_donetrap) {
 	    if (sigtrapped[SIGZERR] && lastval) {
 		dotrap(SIGZERR);
 		donetrap = 1;
@@ -1546,6 +1612,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
 	    zclose(opipe[0]);
 	}
 	if (how & Z_DISOWN) {
+	    pipecleanfilelist(jobtab[thisjob].filelist, 0);
 	    deletejob(jobtab + thisjob, 1);
 	    thisjob = -1;
 	}
@@ -1570,6 +1637,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
 
 	    if (nowait) {
 		if(!pline_level) {
+		    int jobsub;
 		    struct process *pn, *qn;
 
 		    curjob = newjob;
@@ -1582,6 +1650,20 @@ execpline(Estate state, wordcode slcode, int how, int last1)
 		    if (!jn->procs->next || lpforked == 2) {
 			jn->gleader = list_pipe_pid;
 			jn->stat |= STAT_SUBLEADER;
+			/*
+			 * Pick up any subjob that's still lying around
+			 * as it's now our responsibility.
+			 * If we find it we're a SUPERJOB.
+			 */
+			for (jobsub = 1; jobsub <= maxjob; jobsub++) {
+			    Job jnsub = jobtab + jobsub;
+			    if (jnsub->stat & STAT_SUBJOB_ORPHANED) {
+				jn->other = jobsub;
+				jn->stat |= STAT_SUPERJOB;
+				jnsub->stat &= ~STAT_SUBJOB_ORPHANED;
+				jnsub->other = list_pipe_pid;
+			    }
+			}
 		    }
 		    for (pn = jobtab[jn->other].procs; pn; pn = pn->next)
 			if (WIFSTOPPED(pn->status))
@@ -1593,7 +1675,8 @@ execpline(Estate state, wordcode slcode, int how, int last1)
 		    }
 
 		    jn->stat &= ~(STAT_DONE | STAT_NOPRINT);
-		    jn->stat |= STAT_STOPPED | STAT_CHANGED | STAT_LOCKED;
+		    jn->stat |= STAT_STOPPED | STAT_CHANGED | STAT_LOCKED |
+			STAT_INUSE;
 		    printjob(jn, !!isset(LONGLISTJOBS), 1);
 		}
 		else if (newjob != list_pipe_job)
@@ -1636,7 +1719,13 @@ execpline(Estate state, wordcode slcode, int how, int last1)
 		    int synch[2];
 		    struct timeval bgtime;
 
+		    /*
+		     * A pipeline with the shell handling the right
+		     * hand side was stopped.  We'll fork to allow
+		     * it to continue.
+		     */
 		    if (pipe(synch) < 0 || (pid = zfork(&bgtime)) == -1) {
+			/* Failure */
 			if (pid < 0) {
 			    close(synch[0]);
 			    close(synch[1]);
@@ -1650,6 +1739,18 @@ execpline(Estate state, wordcode slcode, int how, int last1)
 			thisjob = newjob;
 		    }
 		    else if (pid) {
+			/*
+			 * Parent: job control is here.  If the job
+			 * started for the RHS of the pipeline is still
+			 * around, then its a SUBJOB and the job for
+			 * earlier parts of the pipeeline is its SUPERJOB.
+			 * The newly forked shell isn't recorded as a
+			 * separate job here, just as list_pipe_pid.
+			 * If the superjob exits (it may already have
+			 * done so, see child branch below), we'll use
+			 * list_pipe_pid to form the basis of a
+			 * replacement job --- see SUBLEADER code above.
+			 */
 			char dummy;
 
 			lpforked =
@@ -1668,7 +1769,9 @@ execpline(Estate state, wordcode slcode, int how, int last1)
 			    jobtab[list_pipe_job].other = newjob;
 			    jobtab[list_pipe_job].stat |= STAT_SUPERJOB;
 			    jn->stat |= STAT_SUBJOB | STAT_NOPRINT;
-			    jn->other = pid;
+			    jn->other = list_pipe_pid;	/* see zsh.h */
+			    if (hasprocs(list_pipe_job))
+				jn->gleader = jobtab[list_pipe_job].gleader;
 			}
 			if ((list_pipe || last1) && hasprocs(list_pipe_job))
 			    killpg(jobtab[list_pipe_job].gleader, SIGSTOP);
@@ -1677,13 +1780,21 @@ execpline(Estate state, wordcode slcode, int how, int last1)
 		    else {
 			close(synch[0]);
 			entersubsh(ESUB_ASYNC);
-			if (jobtab[list_pipe_job].procs) {
-			    if (setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader)
-				== -1) {
-				setpgrp(0L, mypgrp = getpid());
-			    }
-			} else
-			    setpgrp(0L, mypgrp = getpid());
+			/*
+			 * At this point, we used to attach this process
+			 * to the process group of list_pipe_job (the
+			 * new superjob) any time that was still available.
+			 * That caused problems in at least two
+			 * cases because this forked shell was then
+			 * suspended with the right hand side of the
+			 * pipeline, and the SIGSTOP below suspended
+			 * it a second time when it was continued.
+			 *
+			 * It's therefore not clear entirely why you'd ever
+			 * do anything other than the following, but no
+			 * doubt we'll find out...
+			 */
+			setpgrp(0L, mypgrp = getpid());
 			close(synch[1]);
 			kill(getpid(), SIGSTOP);
 			list_pipe = 0;
@@ -1720,6 +1831,8 @@ execpline(Estate state, wordcode slcode, int how, int last1)
 		deletejob(jn, 0);
 	    thisjob = pj;
 	}
+	else
+	    unqueue_signals();
 	if ((slflags & WC_SUBLIST_NOT) && !errflag)
 	    lastval = !lastval;
     }
@@ -1737,6 +1850,7 @@ execpline2(Estate state, wordcode pcode,
 {
     pid_t pid;
     int pipes[2];
+    struct execcmd_params eparams;
 
     if (breaks || retflag)
 	return;
@@ -1746,7 +1860,7 @@ execpline2(Estate state, wordcode pcode,
 	lineno = WC_PIPE_LINENO(pcode) - 1;
 
     if (pline_level == 1) {
-	if ((how & Z_ASYNC) || (!sfcontext && !sourcelevel))
+	if ((how & Z_ASYNC) || !sfcontext)
 	    strcpy(list_pipe_text,
 		   getjobtext(state->prog,
 			      state->pc + (WC_PIPE_TYPE(pcode) == WC_PIPE_END ?
@@ -1754,17 +1868,16 @@ execpline2(Estate state, wordcode pcode,
 	else
 	    list_pipe_text[0] = '\0';
     }
-    if (WC_PIPE_TYPE(pcode) == WC_PIPE_END)
-	execcmd(state, input, output, how, last1 ? 1 : 2);
-    else {
+    if (WC_PIPE_TYPE(pcode) == WC_PIPE_END) {
+	execcmd_analyse(state, &eparams);
+	execcmd_exec(state, &eparams, input, output, how, last1 ? 1 : 2);
+    } else {
 	int old_list_pipe = list_pipe;
 	int subsh_close = -1;
-	Wordcode next = state->pc + (*state->pc), pc;
-	wordcode code;
+	Wordcode next = state->pc + (*state->pc), start_pc;
 
-	state->pc++;
-	for (pc = state->pc; wc_code(code = *pc) == WC_REDIR;
-	     pc += WC_REDIR_WORDS(code));
+	start_pc = ++state->pc;
+	execcmd_analyse(state, &eparams);
 
 	if (mpipe(pipes) < 0) {
 	    /* FIXME */
@@ -1773,7 +1886,8 @@ execpline2(Estate state, wordcode pcode,
 	/* if we are doing "foo | bar" where foo is a current *
 	 * shell command, do foo in a subshell and do the     *
 	 * rest of the pipeline in the current shell.         */
-	if (wc_code(code) >= WC_CURSH && (how & Z_SYNC)) {
+	if ((eparams.type >= WC_CURSH || !eparams.args)
+	    && (how & Z_SYNC)) {
 	    int synch[2];
 	    struct timeval bgtime;
 
@@ -1791,7 +1905,7 @@ execpline2(Estate state, wordcode pcode,
 	    } else if (pid) {
 		char dummy, *text;
 
-		text = getjobtext(state->prog, state->pc);
+		text = getjobtext(state->prog, start_pc);
 		addproc(pid, text, 0, &bgtime);
 		close(synch[1]);
 		read_loop(synch[0], &dummy, 1);
@@ -1802,14 +1916,18 @@ execpline2(Estate state, wordcode pcode,
 		entersubsh(((how & Z_ASYNC) ? ESUB_ASYNC : 0)
 			   | ESUB_PGRP | ESUB_KEEPTRAP);
 		close(synch[1]);
-		execcmd(state, input, pipes[1], how, 1);
+		if (sigtrapped[SIGEXIT])
+		{
+		    unsettrap(SIGEXIT);
+		}
+		execcmd_exec(state, &eparams, input, pipes[1], how, 1);
 		_exit(lastval);
 	    }
 	} else {
 	    /* otherwise just do the pipeline normally. */
 	    addfilelist(NULL, pipes[0]);
 	    subsh_close = pipes[0];
-	    execcmd(state, input, pipes[1], how, 0);
+	    execcmd_exec(state, &eparams, input, pipes[1], how, 0);
 	}
 	zclose(pipes[1]);
 	state->pc = next;
@@ -2273,9 +2391,7 @@ addvars(Estate state, Wordcode pc, int addflags)
      * to be restored after the command, since then the assignment
      * is implicitly scoped.
      */
-    flags = (!(addflags & ADDVAR_RESTORE) &&
-	     locallevel > forklevel && isset(WARNCREATEGLOBAL)) ?
-	ASSPM_WARN_CREATE : 0;
+    flags = !(addflags & ADDVAR_RESTORE) ? ASSPM_WARN : 0;
     xtr = isset(XTRACE);
     if (xtr) {
 	printprompt4();
@@ -2465,55 +2581,51 @@ resolvebuiltin(const char *cmdarg, HashNode hn)
     return hn;
 }
 
+/*
+ * We are about to execute a command at the lowest level of the
+ * hierarchy.  Analyse the parameters from the wordcode.
+ */
+
 /**/
 static void
-execcmd(Estate state, int input, int output, int how, int last1)
+execcmd_analyse(Estate state, Execcmd_params eparams)
 {
-    HashNode hn = NULL;
-    LinkList args, filelist = NULL;
-    LinkNode node;
-    Redir fn;
-    struct multio *mfds[10];
-    char *text;
-    int save[10];
-    int fil, dfil, is_cursh, type, do_exec = 0, redir_err = 0, i, htok = 0;
-    int nullexec = 0, assign = 0, forked = 0, postassigns = 0;
-    int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0;
-    /* Various flags to the command. */
-    int cflags = 0, orig_cflags = 0, checked = 0, oautocont = -1;
-    LinkList redir;
     wordcode code;
-    Wordcode beg = state->pc, varspc, assignspc = (Wordcode)0;
-    FILE *oxtrerr = xtrerr, *newxtrerr = NULL;
+    int i;
 
-    doneps4 = 0;
-    redir = (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL);
+    eparams->beg = state->pc;
+    eparams->redir =
+	(wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL);
     if (wc_code(*state->pc) == WC_ASSIGN) {
 	cmdoutval = 0;
-	varspc = state->pc;
+	eparams->varspc = state->pc;
 	while (wc_code((code = *state->pc)) == WC_ASSIGN)
 	    state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ?
 			  3 : WC_ASSIGN_NUM(code) + 2);
     } else
-	varspc = NULL;
+	eparams->varspc = NULL;
 
     code = *state->pc++;
 
-    type = wc_code(code);
+    eparams->type = wc_code(code);
+    eparams->postassigns = 0;
 
     /* It would be nice if we could use EC_DUPTOK instead of EC_DUP here.
      * But for that we would need to check/change all builtins so that
      * they don't modify their argument strings. */
-    switch (type) {
+    switch (eparams->type) {
     case WC_SIMPLE:
-	args = ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok);
+	eparams->args = ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP,
+				  &eparams->htok);
+	eparams->assignspc = NULL;
 	break;
 
     case WC_TYPESET:
-	args = ecgetlist(state, WC_TYPESET_ARGC(code), EC_DUP, &htok);
-	postassigns = *state->pc++;
-	assignspc = state->pc;
-	for (i = 0; i < postassigns; i++) {
+	eparams->args = ecgetlist(state, WC_TYPESET_ARGC(code), EC_DUP,
+				  &eparams->htok);
+	eparams->postassigns = *state->pc++;
+	eparams->assignspc = state->pc;
+	for (i = 0; i < eparams->postassigns; i++) {
 	    code = *state->pc;
 	    DPUTS(wc_code(code) != WC_ASSIGN,
 		  "BUG: miscounted typeset assignments");
@@ -2523,8 +2635,71 @@ execcmd(Estate state, int input, int output, int how, int last1)
 	break;
 
     default:
-	args = NULL;
+	eparams->args = NULL;
+	eparams->assignspc = NULL;
+	eparams->htok = 0;
+	break;
     }
+}
+
+/*
+ * Transfer the first node of args to preargs, performing
+ * prefork expansion on the way if necessary.
+ */
+static void execcmd_getargs(LinkList preargs, LinkList args, int expand)
+{
+    if (!firstnode(args)) {
+	return;
+    } else if (expand) {
+	local_list0(svl);
+	init_list0(svl);
+	/* not init_list1, as we need real nodes */
+	addlinknode(&svl, uremnode(args, firstnode(args)));
+	/* Analysing commands, so vanilla options to prefork */
+	prefork(&svl, 0, NULL);
+	joinlists(preargs, &svl);
+    } else {
+        addlinknode(preargs, uremnode(args, firstnode(args)));
+    }
+}
+
+/*
+ * Execute a command at the lowest level of the hierarchy.
+ */
+
+/**/
+static void
+execcmd_exec(Estate state, Execcmd_params eparams,
+	     int input, int output, int how, int last1)
+{
+    HashNode hn = NULL;
+    LinkList filelist = NULL;
+    LinkNode node;
+    Redir fn;
+    struct multio *mfds[10];
+    char *text;
+    int save[10];
+    int fil, dfil, is_cursh, do_exec = 0, redir_err = 0, i;
+    int nullexec = 0, magic_assign = 0, forked = 0;
+    int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0;
+    /* Various flags to the command. */
+    int cflags = 0, orig_cflags = 0, checked = 0, oautocont = -1;
+    FILE *oxtrerr = xtrerr, *newxtrerr = NULL;
+    /*
+     * Retrieve parameters for quick reference (they are unique
+     * to us so we can modify the structure if we want).
+     */
+    LinkList args = eparams->args;
+    LinkList redir = eparams->redir;
+    Wordcode varspc = eparams->varspc;
+    int type = eparams->type;
+    /*
+     * preargs comes from expanding the head of the args list
+     * in order to check for prefix commands.
+     */
+    LinkList preargs;
+
+    doneps4 = 0;
 
     /*
      * If assignment but no command get the status from variable
@@ -2577,9 +2752,19 @@ execcmd(Estate state, int input, int output, int how, int last1)
      * command if it contains some tokens (e.g. x=ex; ${x}port), so this *
      * only works in simple cases.  has_token() is called to make sure   *
      * this really is a simple case.                                     */
-    if (type == WC_SIMPLE || type == WC_TYPESET) {
-	while (args && nonempty(args)) {
-	    char *cmdarg = (char *) peekfirst(args);
+    if ((type == WC_SIMPLE || type == WC_TYPESET) && args) {
+	/*
+	 * preargs contains args that have been expanded by prefork.
+	 * Running execcmd_getargs() causes the any argument available
+	 * in args to be exanded where necessary and transferred to
+	 * preargs.  We call execcmd_getargs() every time we need to
+	 * analyse an argument not available in preargs, though there is
+	 * no guarantee a further argument will be available.
+	 */
+	preargs = newlinklist();
+	execcmd_getargs(preargs, args, eparams->htok);
+	while (nonempty(preargs)) {
+	    char *cmdarg = (char *) peekfirst(preargs);
 	    checked = !has_token(cmdarg);
 	    if (!checked)
 		break;
@@ -2613,39 +2798,118 @@ execcmd(Estate state, int input, int output, int how, int last1)
 		/* autoload the builtin if necessary */
 		if (!(hn = resolvebuiltin(cmdarg, hn)))
 		    return;
-		assign = (hn->flags & BINF_MAGICEQUALS);
+		if (type != WC_TYPESET)
+		    magic_assign = (hn->flags & BINF_MAGICEQUALS);
 		break;
 	    }
 	    checked = 0;
-	    if ((cflags & BINF_COMMAND) && nextnode(firstnode(args))) {
-		/* check for options to command builtin */
-		char *next = (char *) getdata(nextnode(firstnode(args)));
-		char *cmdopt;
-		if (next && *next == '-' && strlen(next) == 2 &&
-		        (cmdopt = strchr("pvV", next[1])))
-		{
-		    if (*cmdopt == 'p') {
-			uremnode(args, firstnode(args));
-			use_defpath = 1;
-			if (nextnode(firstnode(args)))
-			    next = (char *) getdata(nextnode(firstnode(args)));
-		    } else {
-			hn = &commandbn.node;
-			is_builtin = 1;
+	    /*
+	     * We usually don't need the argument containing the
+	     * precommand modifier itself.  Exception: when "command"
+	     * will implemented by a call to "whence", in which case
+	     * we'll simply re-insert the argument.
+	     */
+	    uremnode(preargs, firstnode(preargs));
+	    if (!firstnode(preargs)) {
+		execcmd_getargs(preargs, args, eparams->htok);
+		if (!firstnode(preargs))
+		    break;
+	    }
+	    if ((cflags & BINF_COMMAND)) {
+		/*
+		 * Check for options to "command".
+		 * If just -p, this is handled here: use the default
+		 * path to execute.
+		 * If -v or -V, possibly with -p, dispatch to bin_whence
+		 * but with flag to indicate special handling of -p.
+		 * Otherwise, just leave marked as BINF_COMMAND
+		 * modifier with no additional action.
+		 */
+		LinkNode argnode, oldnode, pnode = NULL;
+		char *argdata, *cmdopt;
+		int has_p = 0, has_vV = 0, has_other = 0;
+		argnode = firstnode(preargs);
+		argdata = (char *) getdata(argnode);
+		while (IS_DASH(*argdata)) {
+		    /* Just to be definite, stop on single "-", too, */
+		    if (!argdata[1] ||
+			(IS_DASH(argdata[1]) && !argdata[2]))
+			break;
+		    for (cmdopt = argdata+1; *cmdopt; cmdopt++) {
+			switch (*cmdopt) {
+			case 'p':
+			    /*
+			     * If we've got this multiple times (command
+			     * -p -p) we'll treat the second -p as a
+			     * command because we only remove one below.
+			     * Don't think that's a big issue, and it's
+			     * also traditional behaviour.
+			     */
+			    has_p = 1;
+			    pnode = argnode;
+			    break;
+			case 'v':
+			case 'V':
+			    has_vV = 1;
+			    break;
+			default:
+			    has_other = 1;
+			    break;
+			}
+		    }
+		    if (has_other) {
+			/* Don't know how to handle this, so don't */
+			has_p = has_vV = 0;
 			break;
 		    }
+
+		    oldnode = argnode;
+		    argnode = nextnode(argnode);
+		    if (!argnode) {
+			execcmd_getargs(preargs, args, eparams->htok);
+			if (!(argnode = nextnode(oldnode)))
+			    break;
+		    }
+		    argdata = (char *) getdata(argnode);
 		}
-		if (!strcmp(next, "--"))
-		     uremnode(args, firstnode(args));
-	    }
-	    if ((cflags & BINF_EXEC) && nextnode(firstnode(args))) {
+		if (has_vV) {
+		    /*
+		     * Leave everything alone, dispatch to whence.
+		     * We need to put the name back in the list.
+		     */
+		    pushnode(preargs, "command");
+		    hn = &commandbn.node;
+		    is_builtin = 1;
+		    break;
+		} else if (has_p) {
+		    /* Use default path */
+		    use_defpath = 1;
+		    /*
+		     * We don't need this node as we're not treating
+		     * "command" as a builtin this time.
+		     */
+		    if (pnode)
+			uremnode(preargs, pnode);
+		}
+		/*
+		 * Else just any trailing
+		 * end-of-options marker.  This can only occur
+		 * if we just had -p or something including more
+		 * than just -p, -v and -V, in which case we behave
+		 * as if this is command [non-option-stuff].  This
+		 * isn't a good place for standard option handling.
+		 */
+		if (IS_DASH(argdata[0]) && IS_DASH(argdata[1]) && !argdata[2])
+		     uremnode(preargs, argnode);
+	    } else if (cflags & BINF_EXEC) {
 		/*
 		 * Check for compatibility options to exec builtin.
 		 * It would be nice to do these more generically,
 		 * but currently we don't have a mechanism for
 		 * precommand modifiers.
 		 */
-		char *next = (char *) getdata(nextnode(firstnode(args)));
+		LinkNode argnode = firstnode(preargs), oldnode;
+		char *argdata = (char *) getdata(argnode);
 		char *cmdopt, *exec_argv0 = NULL;
 		/*
 		 * Careful here: we want to make sure a final dash
@@ -2655,17 +2919,23 @@ execcmd(Estate state, int input, int output, int how, int last1)
 		 * people aren't likely to mix the option style
 		 * with the zsh style.
 		 */
-		while (next && *next == '-' && strlen(next) >= 2) {
-		    if (!firstnode(args)) {
+		while (argdata && IS_DASH(*argdata) && strlen(argdata) >= 2) {
+		    oldnode = argnode;
+		    argnode = nextnode(oldnode);
+		    if (!argnode) {
+			execcmd_getargs(preargs, args, eparams->htok);
+			argnode = nextnode(oldnode);
+		    }
+		    if (!argnode) {
 			zerr("exec requires a command to execute");
 			lastval = 1;
 			errflag |= ERRFLAG_ERROR;
 			goto done;
 		    }
-		    uremnode(args, firstnode(args));
-		    if (!strcmp(next, "--"))
+		    uremnode(preargs, oldnode);
+		    if (IS_DASH(argdata[0]) && IS_DASH(argdata[1]) && !argdata[2])
 			break;
-		    for (cmdopt = &next[1]; *cmdopt; ++cmdopt) {
+		    for (cmdopt = &argdata[1]; *cmdopt; ++cmdopt) {
 			switch (*cmdopt) {
 			case 'a':
 			    /* argument is ARGV0 string */
@@ -2674,21 +2944,25 @@ execcmd(Estate state, int input, int output, int how, int last1)
 				/* position on last non-NULL character */
 				cmdopt += strlen(cmdopt+1);
 			    } else {
-				if (!firstnode(args)) {
+				if (!argnode) {
 				    zerr("exec requires a command to execute");
 				    lastval = 1;
 				    errflag |= ERRFLAG_ERROR;
 				    goto done;
 				}
-				if (!nextnode(firstnode(args))) {
+				if (!nextnode(argnode))
+				    execcmd_getargs(preargs, args,
+						    eparams->htok);
+				if (!nextnode(argnode)) {
 				    zerr("exec flag -a requires a parameter");
 				    lastval = 1;
 				    errflag |= ERRFLAG_ERROR;
 				    goto done;
 				}
-				exec_argv0 = (char *)
-				    getdata(nextnode(firstnode(args)));
-				uremnode(args, firstnode(args));
+				exec_argv0 = (char *) getdata(argnode);
+				oldnode = argnode;
+				argnode = nextnode(argnode);
+				uremnode(args, oldnode);
 			    }
 			    break;
 			case 'c':
@@ -2704,8 +2978,9 @@ execcmd(Estate state, int input, int output, int how, int last1)
 			    return;
 			}
 		    }
-		    if (firstnode(args) && nextnode(firstnode(args)))
-			next = (char *) getdata(nextnode(firstnode(args)));
+		    if (!argnode)
+			break;
+		    argdata = (char *) getdata(argnode);
 		}
 		if (exec_argv0) {
 		    char *str, *s;
@@ -2717,21 +2992,41 @@ execcmd(Estate state, int input, int output, int how, int last1)
 		    zputenv(str);
 		}
 	    }
-	    uremnode(args, firstnode(args));
 	    hn = NULL;
 	    if ((cflags & BINF_COMMAND) && unset(POSIXBUILTINS))
 		break;
+	    if (!nonempty(preargs))
+		execcmd_getargs(preargs, args, eparams->htok);
 	}
-    }
+    } else
+	preargs = NULL;
 
     /* if we get this far, it is OK to pay attention to lastval again */
     if (noerrexit == 2 && !is_shfunc)
 	noerrexit = 0;
 
-    /* Do prefork substitutions */
-    esprefork = (assign || isset(MAGICEQUALSUBST)) ? PREFORK_TYPESET : 0;
-    if (args && htok)
-	prefork(args, esprefork, NULL);
+    /* Do prefork substitutions.
+     *
+     * Decide if we need "magic" handling of ~'s etc. in
+     * assignment-like arguments.
+     * - If magic_assign is set, we are using a builtin of the
+     *   tyepset family, but did not recognise this as a keyword,
+     *   so need guess-o-matic behaviour.
+     * - Otherwise, if we did recognise the keyword, we never need
+     *   guess-o-matic behaviour as the argument was properly parsed
+     *   as such.
+     * - Otherwise, use the behaviour specified by the MAGIC_EQUAL_SUBST
+     *   option.
+     */
+    esprefork = (magic_assign ||
+		 (isset(MAGICEQUALSUBST) && type != WC_TYPESET)) ?
+		 PREFORK_TYPESET : 0;
+    if (args) {
+	if (eparams->htok)
+	    prefork(args, esprefork, NULL);
+	if (preargs)
+	    args = joinlists(preargs, args);
+    }
 
     if (type == WC_SIMPLE || type == WC_TYPESET) {
 	int unglobbed = 0;
@@ -2874,8 +3169,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
 
     /* Get the text associated with this command. */
     if ((how & Z_ASYNC) ||
-	(!sfcontext && !sourcelevel && (jobbing || (how & Z_TIMED))))
-	text = getjobtext(state->prog, beg);
+	(!sfcontext && (jobbing || (how & Z_TIMED))))
+	text = getjobtext(state->prog, eparams->beg);
     else
 	text = NULL;
 
@@ -2933,7 +3228,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
 	if (is_shfunc)
 	    shf = (Shfunc)hn;
 	else {
-	    shf = loadautofn(state->prog->shf, 1, 0);
+	    shf = loadautofn(state->prog->shf, 1, 0, 0);
 	    if (shf)
 		state->prog->shf = shf;
 	    else {
@@ -3134,7 +3429,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
 	    forked = 1;
     }
 
-    if ((esglob = !(cflags & BINF_NOGLOB)) && args && htok) {
+    if ((esglob = !(cflags & BINF_NOGLOB)) && args && eparams->htok) {
 	LinkList oargs = args;
 	globlist(args, 0);
 	args = oargs;
@@ -3448,7 +3743,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
 	}
 	if (type == WC_FUNCDEF) {
 	    Eprog redir_prog;
-	    if (!redir && wc_code(*beg) == WC_REDIR)  {
+	    if (!redir && wc_code(*eparams->beg) == WC_REDIR)  {
 		/*
 		 * We're not using a redirection from the currently
 		 * parsed environment, which is what we'd do for an
@@ -3458,7 +3753,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
 		struct estate s;
 
 		s.prog = state->prog;
-		s.pc = beg;
+		s.pc = eparams->beg;
 		s.strs = state->prog->strs;
 
 		/*
@@ -3500,7 +3795,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
 		     * if it's got "command" in front.
 		     * If it's a normal command --- save.
 		     */
-		    if (is_shfunc || (hn->flags & BINF_PSPECIAL))
+		    if (is_shfunc || (hn->flags & (BINF_PSPECIAL|BINF_ASSIGN)))
 			do_save = (orig_cflags & BINF_COMMAND);
 		    else
 			do_save = 1;
@@ -3509,7 +3804,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
 		     * Save if it's got "command" in front or it's
 		     * not a magic-equals assignment.
 		     */
-		    if ((cflags & BINF_COMMAND) || !assign)
+		    if ((cflags & (BINF_COMMAND|BINF_ASSIGN)) || !magic_assign)
 			do_save = 1;
 		}
 		if (do_save && varspc)
@@ -3542,13 +3837,15 @@ execcmd(Estate state, int input, int output, int how, int last1)
 	    } else {
 		/* It's a builtin */
 		LinkList assigns = (LinkList)0;
+		int postassigns = eparams->postassigns;
 		if (forked)
 		    closem(FDT_INTERNAL);
 		if (postassigns) {
 		    Wordcode opc = state->pc;
-		    state->pc = assignspc;
+		    state->pc = eparams->assignspc;
 		    assigns = newlinklist();
 		    while (postassigns--) {
+			int htok;
 			wordcode ac = *state->pc++;
 			char *name = ecgetstr(state, EC_DUPTOK, &htok);
 			Asgment asg;
@@ -3657,7 +3954,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
 		    state->pc = opc;
 		}
 		dont_queue_signals();
-		lastval = execbuiltin(args, assigns, (Builtin) hn);
+		if (!errflag)
+		    lastval = execbuiltin(args, assigns, (Builtin) hn);
 		if (do_save & BINF_COMMAND)
 		    errflag &= ~ERRFLAG_ERROR;
 		restore_queue_signals(q);
@@ -3694,12 +3992,15 @@ execcmd(Estate state, int input, int output, int how, int last1)
 		restore_params(restorelist, removelist);
 
 	} else {
-	    if (!forked)
-		setiparam("SHLVL", --shlvl);
-	    if (do_exec) {
+	    if (!subsh) {
+	        /* for either implicit or explicit "exec", decrease $SHLVL
+		 * as we're now done as a shell */
+		if (!forked)
+		    setiparam("SHLVL", --shlvl);
+
 		/* If we are exec'ing a command, and we are not *
 		 * in a subshell, then save the history file.   */
-		if (!subsh && isset(RCS) && interact && !nohistsave)
+		if (do_exec && isset(RCS) && interact && !nohistsave)
 		    savehistfile(NULL, 1, HFILE_USE_OPTIONS);
 	    }
 	    if (type == WC_SIMPLE || type == WC_TYPESET) {
@@ -3790,6 +4091,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
 	 * classify as a builtin) we treat all errors as fatal.
 	 * The "command" builtin is not special so resets this behaviour.
 	 */
+	forked |= zsh_subshell;
     fatal:
 	if (redir_err || errflag) {
 	    if (!isset(INTERACTIVE)) {
@@ -4031,7 +4333,7 @@ gethere(char **strp, int typ)
 
 	parsestr(&buf);
 
-	if (!errflag) {
+	if (!(errflag & ERRFLAG_ERROR)) {
 	    /* Retain any user interrupt error */
 	    errflag = ef | (errflag & ERRFLAG_INT);
 	}
@@ -4284,11 +4586,26 @@ getoutputfile(char *cmd, char **eptr)
 	    untokenize(s);
     }
 
-    addfilelist(nam, 0);
+    if (!s)             /* Unclear why we need to do this before open() */
+	child_block();  /* but it has been so for a long time: leave it */
 
-    if (!s)
-	child_block();
-    fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600);
+    if ((fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600)) < 0) {
+	zerr("process substitution failed: %e", errno);
+	free(nam);
+	if (!s)
+	    child_unblock();
+	return NULL;
+    } else {
+	char *suffix = getsparam("TMPSUFFIX");
+	if (suffix && *suffix && !strstr(suffix, "/")) {
+	    suffix = dyncat(nam, unmeta(suffix));
+	    if (link(nam, suffix) == 0) {
+		addfilelist(nam, 0);
+		nam = ztrdup(suffix);
+	    }
+	}
+    }
+    addfilelist(nam, 0);
 
     if (s) {
 	/* optimised here-string */
@@ -4299,7 +4616,7 @@ getoutputfile(char *cmd, char **eptr)
 	return nam;
     }
 
-    if (fd < 0 || (cmdoutpid = pid = zfork(NULL)) == -1) {
+    if ((cmdoutpid = pid = zfork(NULL)) == -1) {
 	/* fork or open error */
 	child_unblock();
 	return nam;
@@ -4615,6 +4932,8 @@ exectime(Estate state, UNUSED(int do_exec))
 
 /* Define a shell function */
 
+static const char *const ANONYMOUS_FUNCTION_NAME = "(anon)";
+
 /**/
 static int
 execfuncdef(Estate state, Eprog redir_prog)
@@ -4688,6 +5007,7 @@ execfuncdef(Estate state, Eprog redir_prog)
 	shf = (Shfunc) zalloc(sizeof(*shf));
 	shf->funcdef = prog;
 	shf->node.flags = 0;
+	/* No dircache here, not a directory */
 	shf->filename = ztrdup(scriptfilename);
 	shf->lineno = lineno;
 	/*
@@ -4720,7 +5040,7 @@ execfuncdef(Estate state, Eprog redir_prog)
 		    freeeprog(shf->funcdef);
 		    if (shf->redir) /* shouldn't be */
 			freeeprog(shf->redir);
-		    zsfree(shf->filename);
+		    dircache_set(&shf->filename, NULL);
 		    zfree(shf, sizeof(*shf));
 		    state->pc = end;
 		    return 1;
@@ -4732,7 +5052,7 @@ execfuncdef(Estate state, Eprog redir_prog)
 
 	    if (!args)
 		args = newlinklist();
-	    shf->node.nam = "(anon)";
+	    shf->node.nam = (char *) ANONYMOUS_FUNCTION_NAME;
 	    pushnode(args, shf->node.nam);
 
 	    execshfunc(shf, args);
@@ -4751,7 +5071,7 @@ execfuncdef(Estate state, Eprog redir_prog)
 	    freeeprog(shf->funcdef);
 	    if (shf->redir) /* shouldn't be */
 		freeeprog(shf->redir);
-	    zsfree(shf->filename);
+	    dircache_set(&shf->filename, NULL);
 	    zfree(shf, sizeof(*shf));
 	    break;
 	} else {
@@ -4760,7 +5080,7 @@ execfuncdef(Estate state, Eprog redir_prog)
 		(signum = getsignum(s + 4)) != -1) {
 		if (settrap(signum, NULL, ZSIG_FUNC)) {
 		    freeeprog(shf->funcdef);
-		    zsfree(shf->filename);
+		    dircache_set(&shf->filename, NULL);
 		    zfree(shf, sizeof(*shf));
 		    state->pc = end;
 		    return 1;
@@ -4909,11 +5229,12 @@ execautofn_basic(Estate state, UNUSED(int do_exec))
      * defined yet.
      */
     if (funcstack && !funcstack->filename)
-	funcstack->filename = dupstring(shf->filename);
+	funcstack->filename = getshfuncfile(shf);
 
     oldscriptname = scriptname;
     oldscriptfilename = scriptfilename;
-    scriptname = scriptfilename = dupstring(shf->node.nam);
+    scriptname = dupstring(shf->node.nam);
+    scriptfilename = getshfuncfile(shf);
     execode(shf->funcdef, 1, 0, "loadautofunc");
     scriptname = oldscriptname;
     scriptfilename = oldscriptfilename;
@@ -4927,25 +5248,71 @@ execautofn(Estate state, UNUSED(int do_exec))
 {
     Shfunc shf;
 
-    if (!(shf = loadautofn(state->prog->shf, 1, 0)))
+    if (!(shf = loadautofn(state->prog->shf, 1, 0, 0)))
 	return 1;
 
     state->prog->shf = shf;
     return execautofn_basic(state, 0);
 }
 
+/*
+ * Helper function to install the source file name of a shell function
+ * just autoloaded.
+ *
+ * We attempt to do this efficiently as the typical case is the
+ * directory part is a well-known directory, which is cached, and
+ * the non-directory part is the same as the node name.
+ */
+
+/**/
+static void
+loadautofnsetfile(Shfunc shf, char *fdir)
+{
+    /*
+     * If shf->filename is already the load directory ---
+     * keep it as we can still use it to get the load file.
+     * This makes autoload with an absolute path particularly efficient.
+     */
+    if (!(shf->node.flags & PM_LOADDIR) ||
+	strcmp(shf->filename, fdir) != 0) {
+	/* Old directory name not useful... */
+	dircache_set(&shf->filename, NULL);
+	if (fdir) {
+	    /* ...can still cache directory */
+	    shf->node.flags |= PM_LOADDIR;
+	    dircache_set(&shf->filename, fdir);
+	} else {
+	    /* ...no separate directory part to cache, for some reason. */
+	    shf->node.flags &= ~PM_LOADDIR;
+	    shf->filename = ztrdup(shf->node.nam);
+	}
+    }
+}
+
 /**/
 Shfunc
-loadautofn(Shfunc shf, int fksh, int autol)
+loadautofn(Shfunc shf, int fksh, int autol, int current_fpath)
 {
     int noalias = noaliases, ksh = 1;
     Eprog prog;
-    char *fname;
+    char *fdir;			/* Directory path where func found */
 
     pushheap();
 
     noaliases = (shf->node.flags & PM_UNALIASED);
-    prog = getfpfunc(shf->node.nam, &ksh, &fname);
+    if (shf->filename && shf->filename[0] == '/' &&
+	(shf->node.flags & PM_LOADDIR))
+    {
+	char *spec_path[2];
+	spec_path[0] = dupstring(shf->filename);
+	spec_path[1] = NULL;
+	prog = getfpfunc(shf->node.nam, &ksh, &fdir, spec_path, 0);
+	if (prog == &dummy_eprog &&
+	    (current_fpath || (shf->node.flags & PM_CUR_FPATH)))
+	    prog = getfpfunc(shf->node.nam, &ksh, &fdir, NULL, 0);
+    }
+    else
+	prog = getfpfunc(shf->node.nam, &ksh, &fdir, NULL, 0);
     noaliases = noalias;
 
     if (ksh == 1) {
@@ -4964,7 +5331,6 @@ loadautofn(Shfunc shf, int fksh, int autol)
 	return NULL;
     }
     if (!prog) {
-	zsfree(fname);
 	popheap();
 	return NULL;
     }
@@ -4978,7 +5344,7 @@ loadautofn(Shfunc shf, int fksh, int autol)
 	    else
 		shf->funcdef = dupeprog(prog, 0);
 	    shf->node.flags &= ~PM_UNDEFINED;
-	    shf->filename = fname;
+	    loadautofnsetfile(shf, fdir);
 	} else {
 	    VARARR(char, n, strlen(shf->node.nam) + 1);
 	    strcpy(n, shf->node.nam);
@@ -4990,7 +5356,6 @@ loadautofn(Shfunc shf, int fksh, int autol)
 		zwarn("%s: function not defined by file", n);
 		locallevel++;
 		popheap();
-		zsfree(fname);
 		return NULL;
 	    }
 	}
@@ -5001,7 +5366,7 @@ loadautofn(Shfunc shf, int fksh, int autol)
 	else
 	    shf->funcdef = dupeprog(stripkshdef(prog, shf->node.nam), 0);
 	shf->node.flags &= ~PM_UNDEFINED;
-	shf->filename = fname;
+	loadautofnsetfile(shf, fdir);
     }
     popheap();
 
@@ -5165,8 +5530,20 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
 
 	if (flags & (PM_TAGGED|PM_TAGGED_LOCAL))
 	    opts[XTRACE] = 1;
-	else if (oflags & PM_TAGGED_LOCAL)
-	    opts[XTRACE] = 0;
+	else if (oflags & PM_TAGGED_LOCAL) {
+	    if (shfunc->node.nam == ANONYMOUS_FUNCTION_NAME /* pointer comparison */)
+		flags |= PM_TAGGED_LOCAL;
+	    else
+		opts[XTRACE] = 0;
+	}
+	if (flags & PM_WARNNESTED)
+	    opts[WARNNESTEDVAR] = 1;
+	else if (oflags & PM_WARNNESTED) {
+	    if (shfunc->node.nam == ANONYMOUS_FUNCTION_NAME)
+		flags |= PM_WARNNESTED;
+	    else
+		opts[WARNNESTEDVAR] = 0;
+	}
 	ooflags = oflags;
 	/*
 	 * oflags is static, because we compare it on the next recursive
@@ -5217,7 +5594,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
 	funcstack = &fstack;
 
 	fstack.flineno = shfunc->lineno;
-	fstack.filename = dupstring(shfunc->filename);
+	fstack.filename = getshfuncfile(shfunc);
 
 	prog = shfunc->funcdef;
 	if (prog->flags & EF_RUN) {
@@ -5285,6 +5662,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
 	    opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE];
 	    opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS];
 	    opts[LOCALLOOPS] = saveopts[LOCALLOOPS];
+	    opts[WARNNESTEDVAR] = saveopts[WARNNESTEDVAR];
 	}
 
 	if (opts[LOCALLOOPS]) {
@@ -5318,8 +5696,11 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
      * the only likely case where we need that second test is
      * when we have an "always" block.  The endparamscope() has
      * already happened, hence the "+1" here.
+     *
+     * If we are in an exit trap, finish it first... we wouldn't set
+     * exit_pending if we were already in one.
      */
-    if (exit_pending && exit_level >= locallevel+1) {
+    if (exit_pending && exit_level >= locallevel+1 && !in_exit_trap) {
 	if (locallevel > forklevel) {
 	    /* Still functions to return: force them to do so. */
 	    retflag = 1;
@@ -5367,6 +5748,7 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name)
 	if (!cont) {
 	    if (ou)
 		zfree(ou, ouu);
+	    unqueue_signals();
 	    return;
 	}
 	wrap = wrap->next;
@@ -5382,21 +5764,30 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name)
     unqueue_signals();
 }
 
-/* Search fpath for an undefined function.  Finds the file, and returns the *
- * list of its contents.                                                    */
+/*
+ * Search fpath for an undefined function.  Finds the file, and returns the
+ * list of its contents.
+ *
+ * If test is 0, load the function.
+ *
+ * If test_only is 1, don't load function, just test for it:
+ * Non-null return means function was found
+ *
+ * *fdir points to path at which found (as passed in, not duplicated)
+ */
 
 /**/
 Eprog
-getfpfunc(char *s, int *ksh, char **fname)
+getfpfunc(char *s, int *ksh, char **fdir, char **alt_path, int test_only)
 {
-    char **pp, buf[PATH_MAX];
+    char **pp, buf[PATH_MAX+1];
     off_t len;
     off_t rlen;
     char *d;
     Eprog r;
     int fd;
 
-    pp = fpath;
+    pp = alt_path ? alt_path : fpath;
     for (; *pp; pp++) {
 	if (strlen(*pp) + strlen(s) + 1 >= PATH_MAX)
 	    continue;
@@ -5404,9 +5795,9 @@ getfpfunc(char *s, int *ksh, char **fname)
 	    sprintf(buf, "%s/%s", *pp, s);
 	else
 	    strcpy(buf, s);
-	if ((r = try_dump_file(*pp, s, buf, ksh))) {
-	    if (fname)
-		*fname = ztrdup(buf);
+	if ((r = try_dump_file(*pp, s, buf, ksh, test_only))) {
+	    if (fdir)
+		*fdir = *pp;
 	    return r;
 	}
 	unmetafy(buf, NULL);
@@ -5414,6 +5805,12 @@ getfpfunc(char *s, int *ksh, char **fname)
 	    struct stat st;
 	    if (!fstat(fd, &st) && S_ISREG(st.st_mode) &&
 		(len = lseek(fd, 0, 2)) != -1) {
+		if (test_only) {
+		    close(fd);
+		    if (fdir)
+			*fdir = *pp;
+		    return &dummy_eprog;
+		}
 		d = (char *) zalloc(len + 1);
 		lseek(fd, 0, 0);
 		if ((rlen = read(fd, d, len)) >= 0) {
@@ -5427,8 +5824,8 @@ getfpfunc(char *s, int *ksh, char **fname)
 		    r = parse_string(d, 1);
 		    scriptname = oldscriptname;
 
-		    if (fname)
-			*fname = ztrdup(buf);
+		    if (fdir)
+			*fdir = *pp;
 
 		    zfree(d, len + 1);
 
@@ -5441,7 +5838,7 @@ getfpfunc(char *s, int *ksh, char **fname)
 		close(fd);
 	}
     }
-    return &dummy_eprog;
+    return test_only ? NULL : &dummy_eprog;
 }
 
 /* Handle the most common type of ksh-style autoloading, when doing a      *
@@ -5519,7 +5916,7 @@ cancd(char *s)
     char *t;
 
     if (*s != '/') {
-	char sbuf[PATH_MAX], **cp;
+	char sbuf[PATH_MAX+1], **cp;
 
 	if (cancd2(s))
 	    return s;
@@ -5600,6 +5997,7 @@ execsave(void)
     es->trapisfunc = trapisfunc;
     es->traplocallevel = traplocallevel;
     es->noerrs = noerrs;
+    es->this_noerrexit = this_noerrexit;
     es->underscore = ztrdup(zunderscore);
     es->next = exstack;
     exstack = es;
@@ -5634,6 +6032,7 @@ execrestore(void)
     trapisfunc = en->trapisfunc;
     traplocallevel = en->traplocallevel;
     noerrs = en->noerrs;
+    this_noerrexit = en->this_noerrexit;
     setunderscore(en->underscore);
     zsfree(en->underscore);
     free(en);
diff --git a/Src/glob.c b/Src/glob.c
index c15bcd195..aeb5fd204 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -41,7 +41,10 @@
 typedef struct gmatch *Gmatch;
 
 struct gmatch {
+    /* Metafied file name */
     char *name;
+    /* Unmetafied file name; embedded nulls can't occur in file names */
+    char *uname;
     /*
      * Array of sort strings:  one for each GS_EXEC sort type in
      * the glob qualifiers.
@@ -280,7 +283,7 @@ addpath(char *s, int l)
 static int
 statfullpath(const char *s, struct stat *st, int l)
 {
-    char buf[PATH_MAX];
+    char buf[PATH_MAX+1];
 
     DPUTS(strlen(s) + !*s + pathpos - pathbufcwd >= PATH_MAX,
 	  "BUG: statfullpath(): pathname too long");
@@ -776,7 +779,7 @@ parsepat(char *str)
 
     /* Now there is no (#X) in front, we can check the path. */
     if (!pathbuf)
-	pathbuf = zalloc(pathbufsz = PATH_MAX);
+	pathbuf = zalloc(pathbufsz = PATH_MAX+1);
     DPUTS(pathbufcwd, "BUG: glob changed directory");
     if (*str == '/') {		/* pattern has absolute path */
 	str++;
@@ -911,7 +914,8 @@ gmatchcmp(Gmatch a, Gmatch b)
     for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) {
 	switch (s->tp & ~GS_DESC) {
 	case GS_NAME:
-	    r = zstrcmp(b->name, a->name, gf_numsort ? SORTIT_NUMERICALLY : 0);
+	    r = zstrcmp(b->uname, a->uname,
+			gf_numsort ? SORTIT_NUMERICALLY : 0);
 	    break;
 	case GS_DEPTH:
 	    {
@@ -1170,7 +1174,7 @@ checkglobqual(char *str, int sl, int nobareglob, char **sp)
 }
 
 /* Main entry point to the globbing code for filename globbing. *
- * np points to a node in the list list which will be expanded  *
+ * np points to a node in the list which will be expanded  *
  * into a series of nodes.                                      */
 
 /**/
@@ -1278,14 +1282,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 		*ptr = '-';
 	while (*s && !newcolonmod) {
 	    func = (int (*) _((char *, Statptr, off_t, char *)))0;
-	    if (idigit(*s)) {
-		/* Store numeric argument for qualifier */
-		func = qualflags;
-		data = 0;
-		sdata = NULL;
-		while (idigit(*s))
-		    data = data * 010 + (*s++ - '0');
-	    } else if (*s == ',') {
+	    if (*s == ',') {
 		/* A comma separates alternative sets of qualifiers */
 		s++;
 		sense = 0;
@@ -1317,6 +1314,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 		    sense ^= 1;
 		    break;
 		case '-':
+		case Dash:
 		    /* Toggle matching of symbolic links */
 		    sense ^= 2;
 		    break;
@@ -1611,7 +1609,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 			    ++s;
 		    }
 		    /* See if it's greater than, equal to, or less than */
-		    if ((g_range = *s == '+' ? 1 : *s == '-' ? -1 : 0))
+		    if ((g_range = *s == '+' ? 1 : IS_DASH(*s) ? -1 : 0))
 			++s;
 		    data = qgetnum(&s);
 		    break;
@@ -1859,6 +1857,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 	int nexecs = 0;
 	struct globsort *sortp;
 	struct globsort *lastsortp = gf_sortlist + gf_nsorts;
+	Gmatch gmptr;
 
 	/* First find out if there are any GS_EXECs, counting them. */
 	for (sortp = gf_sortlist; sortp < lastsortp; sortp++)
@@ -1910,6 +1909,23 @@ zglob(LinkList list, LinkNode np, int nountok)
 	    }
 	}
 
+	/*
+	 * Where necessary, create unmetafied version of names
+	 * for comparison.  If no Meta characters just point
+	 * to original string.  All on heap.
+	 */
+	for (gmptr = matchbuf; gmptr < matchptr; gmptr++)
+	{
+	    if (strchr(gmptr->name, Meta))
+	    {
+		int dummy;
+		gmptr->uname = dupstring(gmptr->name);
+		unmetafy(gmptr->uname, &dummy);
+	    } else {
+		gmptr->uname = gmptr->name;
+	    }
+	}
+
 	/* Sort arguments in to lexical (and possibly numeric) order. *
 	 * This is reversed to facilitate insertion into the list.    */
 	qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch),
@@ -2010,13 +2026,13 @@ hasbraces(char *str)
 		if (bracechardots(str-1, NULL, NULL))
 		    return 1;
 		lbr = str - 1;
-		if (*str == '-')
+		if (IS_DASH(*str))
 		    str++;
 		while (idigit(*str))
 		    str++;
 		if (*str == '.' && str[1] == '.') {
 		    str++; str++;
-		    if (*str == '-')
+		    if (IS_DASH(*str))
 			str++;
 		    while (idigit(*str))
 			str++;
@@ -2025,7 +2041,7 @@ hasbraces(char *str)
 			return 1;
 		    else if (*str == '.' && str[1] == '.') {
 			str++; str++;
-			if (*str == '-')
+			if (IS_DASH(*str))
 			    str++;
 			while (idigit(*str))
 			    str++;
@@ -2108,7 +2124,7 @@ xpandredir(struct redir *fn, LinkList redirtab)
 	fn->name = s;
 	untokenize(s);
 	if (fn->type == REDIR_MERGEIN || fn->type == REDIR_MERGEOUT) {
-	    if (s[0] == '-' && !s[1])
+	    if (IS_DASH(s[0]) && !s[1])
 		fn->type = REDIR_CLOSE;
 	    else if (s[0] == 'p' && !s[1])
 		fn->fd2 = -2;
@@ -2314,12 +2330,14 @@ xpandbraces(LinkList list, LinkNode *np)
 	     * str+1 is the first number in the range, dots+2 the last,
 	     * and dots2+2 is the increment if that's given. */
 	    /* TODO: sorry about this */
-	    int minw = (str[1] == '0' || (str[1] == '-' && str[2] == '0'))
+	    int minw = (str[1] == '0' ||
+			(IS_DASH(str[1]) && str[2] == '0'))
 		       ? wid1
-		       : (dots[2] == '0' || (dots[2] == '-' && dots[3] == '0'))
+		       : (dots[2] == '0' ||
+			  (IS_DASH(dots[2]) && dots[3] == '0'))
 		       ? wid2
 		       : (dots2 && (dots2[2] == '0' ||
-				    (dots2[2] == '-' && dots2[3] == '0')))
+				    (IS_DASH(dots2[2]) && dots2[3] == '0')))
 		       ? wid3
 		       : 0;
 	    if (rincr < 0) {
@@ -2377,7 +2395,8 @@ xpandbraces(LinkList list, LinkNode *np)
 		c2 = ztokens[c2 - STOUC(Pound)];
 	    if ((char) c2 == Meta)
 		c2 = 32 ^ p[1];
-	    if (c1 == '-' && lastch >= 0 && p < str2 && lastch <= (int)c2) {
+	    if (IS_DASH((char)c1) && lastch >= 0 &&
+		p < str2 && lastch <= (int)c2) {
 		while (lastch < (int)c2)
 		    ccl[lastch++] = 1;
 		lastch = -1;
@@ -2447,13 +2466,20 @@ xpandbraces(LinkList list, LinkNode *np)
 int
 matchpat(char *a, char *b)
 {
-    Patprog p = patcompile(b, PAT_STATIC, NULL);
+    Patprog p;
+    int ret;
 
-    if (!p) {
+    queue_signals();	/* Protect PAT_STATIC */
+
+    if (!(p = patcompile(b, PAT_STATIC, NULL))) {
 	zerr("bad pattern: %s", b);
-	return 0;
-    }
-    return pattry(p, a);
+	ret = 0;
+    } else
+      ret = pattry(p, a);
+
+    unqueue_signals();
+
+    return ret;
 }
 
 /* do the ${foo%%bar}, ${foo#bar} stuff */
@@ -2903,7 +2929,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
 	     * move forward along string until we get a match. *
 	     * Again there's no optimisation.                  */
 	    mb_charinit();
-	    for (ioff = 0, t = s, umlen = umltot; t < send ; ioff++) {
+	    for (ioff = 0, t = s, umlen = umltot; t <= send ; ioff++) {
 		set_pat_start(p, t-s);
 		if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) {
 		    *sp = get_match_ret(&imd, t-s, umltot);
@@ -2911,6 +2937,8 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
 		}
 		if (fl & SUB_START)
 		    break;
+		if (t == send)
+		    break;
 		umlen -= iincchar(&t, send - t);
 	    }
 	    if (!(fl & SUB_START) && pattrylen(p, send, 0, 0,
@@ -2943,7 +2971,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
 	    do {
 		/* loop over all matches for global substitution */
 		matched = 0;
-		for (; t < send; ioff++) {
+		for (; t <= send; ioff++) {
 		    /* Find the longest match from this position. */
 		    set_pat_start(p, t-s);
 		    if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) {
@@ -2992,15 +3020,19 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
 			 * which is already marked for replacement.
 			 */
 			matched = 1;
+			if (t == send)
+			    break;
 			while (t < mpos) {
 			    ioff++;
 			    umlen -= iincchar(&t, send - t);
 			}
 			break;
 		    }
+		    if (t == send)
+			break;
 		    umlen -= iincchar(&t, send - t);
 		}
-	    } while (matched);
+	    } while (matched && t < send);
 	    /*
 	     * check if we can match a blank string, if so do it
 	     * at the start.  Goodness knows if this is a good idea
@@ -3484,6 +3516,10 @@ zshtokenize(char *s, int flags)
     for (; *s; s++) {
       cont:
 	switch (*s) {
+	case Meta:
+	    /* skip both Meta and following character */
+	    s++;
+	    break;
 	case Bnull:
 	case Bnullkeep:
 	case '\\':
@@ -3502,7 +3538,7 @@ zshtokenize(char *s, int flags)
 	    }
 	    t = s;
 	    while (idigit(*++s));
-	    if (*s != '-')
+	    if (!IS_DASH(*s))
 		goto cont;
 	    while (idigit(*++s));
 	    if (*s != '>')
diff --git a/Src/hashtable.c b/Src/hashtable.c
index 2d5af5be0..c34744cd8 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -889,7 +889,7 @@ freeshfuncnode(HashNode hn)
 	freeeprog(shf->funcdef);
     if (shf->redir)
 	freeeprog(shf->redir);
-    zsfree(shf->filename);
+    dircache_set(&shf->filename, NULL);
     if (shf->sticky) {
 	if (shf->sticky->n_on_opts)
 	    zfree(shf->sticky->on_opts,
@@ -926,10 +926,13 @@ printshfuncnode(HashNode hn, int printflags)
 	       (f->node.flags & PM_UNDEFINED) ?
 	       " is an autoload shell function" :
 	       " is a shell function");
-	if (f->filename && (printflags & PRINT_WHENCE_VERBOSE) &&
-	    strcmp(f->filename, f->node.nam) != 0) {
+	if ((printflags & PRINT_WHENCE_VERBOSE) && f->filename) {
 	    printf(" from ");
 	    quotedzputs(f->filename, stdout);
+	    if (f->node.flags & PM_LOADDIR) {
+		printf("/");
+		quotedzputs(f->node.nam, stdout);
+	    }
 	}
 	putchar('\n');
 	return;
@@ -949,16 +952,20 @@ printshfuncnode(HashNode hn, int printflags)
 	    zoutputtab(stdout);
 	}
 	if (!t) {
-	    char *fopt = "UtTkz";
+	    char *fopt = "UtTkzc";
 	    int flgs[] = {
 		PM_UNALIASED, PM_TAGGED, PM_TAGGED_LOCAL,
-		PM_KSHSTORED, PM_ZSHSTORED, 0
+		PM_KSHSTORED, PM_ZSHSTORED, PM_CUR_FPATH, 0
 	    };
 	    int fl;;
 
 	    zputs("builtin autoload -X", stdout);
 	    for (fl=0;fopt[fl];fl++)
 		if (f->node.flags & flgs[fl]) putchar(fopt[fl]);
+	    if (f->filename && (f->node.flags & PM_LOADDIR)) {
+		putchar(' ');
+		zputs(f->filename, stdout);
+	    }
 	} else {
 	    zputs(t, stdout);
 	    zsfree(t);
@@ -1037,6 +1044,24 @@ printshfuncexpand(HashNode hn, int printflags, int expand)
     text_expand_tabs = save_expand;
 }
 
+/*
+ * Get a heap-duplicated name of the shell function, for
+ * use in tracing.
+ */
+
+/**/
+mod_export char *
+getshfuncfile(Shfunc shf)
+{
+    if (shf->node.flags & PM_LOADDIR) {
+	return zhtricat(shf->filename, "/", shf->node.nam);
+    } else if (shf->filename) {
+	return dupstring(shf->filename);
+    } else {
+	return NULL;
+    }
+}
+
 /**************************************/
 /* Reserved Word Hash Table Functions */
 /**************************************/
@@ -1291,9 +1316,9 @@ printaliasnode(HashNode hn, int printflags)
 	else if (a->node.flags & ALIAS_GLOBAL)
 	    printf("-g ");
 
-	/* If an alias begins with `-', then we must output `-- ' *
+	/* If an alias begins with `-' or `+', then we must output `-- '
 	 * first, so that it is not interpreted as an option.     */
-	if(a->node.nam[0] == '-')
+	if(a->node.nam[0] == '-' || a->node.nam[0] == '+')
 	    printf("-- ");
     }
 
@@ -1423,6 +1448,7 @@ freehistdata(Histent he, int unlink)
     if (!(he->node.flags & (HIST_DUP | HIST_TMPSTORE)))
 	removehashnode(histtab, he->node.nam);
 
+    DPUTS(he->node.nam == chline, "Attempt to free chline in history data");
     zsfree(he->node.nam);
     if (he->nwords)
 	zfree(he->words, he->nwords*2*sizeof(short));
@@ -1438,3 +1464,150 @@ freehistdata(Histent he, int unlink)
 	}
     }
 }
+
+
+/***********************************************************************
+ * Directory name cache mechanism
+ *
+ * The idea of this is that there are various shell structures,
+ * notably functions, that record the directories with which they
+ * are associated.  Rather than store the full string each time,
+ * we store a pointer to the same location and count the references.
+ * This is optimised so that retrieval is quick at the expense of
+ * searching the list when setting up the structure, which is a much
+ * rarer operation.
+ *
+ * There is nothing special about the fact that the strings are
+ * directories, except for the assumptions for efficiency that many
+ * structures will point to the same one, and that there are not too
+ * many different directories associated with the shell.
+ **********************************************************************/
+
+struct dircache_entry
+{
+    /* Name of directory in cache */
+    char *name;
+    /* Number of references to it */
+    int refs;
+};
+
+/*
+ * dircache is the cache, of length dircache_size.
+ * dircache_lastentry is the last entry used, an optimisation
+ * for multiple references to the same directory, e.g
+ * "autoload /blah/blah/\*".
+ */
+static struct dircache_entry *dircache, *dircache_lastentry;
+static int dircache_size;
+
+/*
+ * Set *name to point to a cached version of value.
+ * value is copied so may come from any source.
+ *
+ * If value is NULL, look for the existing value of *name (safe if this
+ * too is NULL) and remove a reference to it from the cache. If it's
+ * not found in the cache, it's assumed to be an allocated string and
+ * freed --- this currently occurs for a shell function that's been
+ * loaded as the filename is now a full path, not just a directory,
+ * though we may one day optimise this to a cached directory plus a
+ * name, too.  Note --- the function does *not* otherwise check
+ * if *name points to something already cached, so this is
+ * necessary any time *name may already be in the cache.
+ */
+
+/**/
+mod_export void
+dircache_set(char **name, char *value)
+{
+    struct dircache_entry *dcptr, *dcnew;
+
+    if (!value) {
+	if (!*name)
+	    return;
+	if (!dircache_size) {
+	    zsfree(*name);
+	    *name = NULL;
+	    return;
+	}
+
+	for (dcptr = dircache; dcptr < dircache + dircache_size; dcptr++)
+	{
+	    /* Must be a pointer much, not a string match */
+	    if (*name == dcptr->name)
+	    {
+		--dcptr->refs;
+		if (!dcptr->refs) {
+		    ptrdiff_t ind = dcptr - dircache;
+		    zsfree(dcptr->name);
+		    --dircache_size;
+
+		    if (!dircache_size) {
+			zfree(dircache, sizeof(*dircache));
+			dircache = NULL;
+			dircache_lastentry = NULL;
+			*name = NULL;
+			return;
+		    }
+		    dcnew = (struct dircache_entry *)
+			zalloc(dircache_size * sizeof(*dcnew));
+		    if (ind)
+			memcpy(dcnew, dircache, ind * sizeof(*dcnew));
+		    if (ind < dircache_size)
+			memcpy(dcnew + ind, dcptr + 1,
+			       (dircache_size - ind) * sizeof(*dcnew));
+		    zfree(dircache, (dircache_size+1)*sizeof(*dcnew));
+		    dircache = dcnew;
+		    dircache_lastentry = NULL;
+		}
+		*name = NULL;
+		return;
+	    }
+	}
+	zsfree(*name);
+	*name = NULL;
+    } else {
+	/*
+	 * As the function path has been resolved to a particular
+	 * location, we'll store it as an absolute path.
+	 */
+	if (*value != '/') {
+	    value = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP),
+			     "/", value);
+	    value = xsymlink(value, 1);
+	}
+	/*
+	 * We'll maintain the cache at exactly the right size rather
+	 * than overallocating.  The rationale here is that typically
+	 * we'll get a lot of functions in a small number of directories
+	 * so the complexity overhead of maintaining a separate count
+	 * isn't really matched by the efficiency gain.
+ 	 */
+	if (dircache_lastentry &&
+	    !strcmp(value, dircache_lastentry->name)) {
+	    *name = dircache_lastentry->name;
+	    ++dircache_lastentry->refs;
+	    return;
+	} else if (!dircache_size) {
+	    dircache_size = 1;
+	    dcptr = dircache =
+		(struct dircache_entry *)zalloc(sizeof(*dircache));
+	} else {
+	    for (dcptr = dircache; dcptr < dircache + dircache_size; dcptr++)
+	    {
+		if (!strcmp(value, dcptr->name)) {
+		    *name = dcptr->name;
+		    ++dcptr->refs;
+		    return;
+		}
+	    }
+	    ++dircache_size;
+	    dircache = (struct dircache_entry *)
+		zrealloc(dircache, sizeof(*dircache) * dircache_size);
+	    dcptr = dircache + dircache_size - 1;
+	}
+	dcptr->name = ztrdup(value);
+	*name = dcptr->name;
+	dcptr->refs = 1;
+	dircache_lastentry = dcptr;
+    }
+}
diff --git a/Src/hist.c b/Src/hist.c
index 5fc40bd67..4c1039b67 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -87,6 +87,9 @@ mod_export zlong curhist;
 /**/
 struct histent curline;
 
+/***/
+int curline_linked;
+
 /* current line count of allocated history entries */
 
 /**/
@@ -261,6 +264,9 @@ hist_context_save(struct hist_stack *hs, int toplevel)
      */
     hs->cstack = cmdstack;
     hs->csp = cmdsp;
+    hs->curline_linked = curline_linked;
+
+    unlinkcurline();
 
     stophist = 0;
     chline = NULL;
@@ -300,6 +306,9 @@ hist_context_restore(const struct hist_stack *hs, int toplevel)
 	zfree(cmdstack, CMDSTACKSZ);
     cmdstack = hs->cstack;
     cmdsp = hs->csp;
+    unlinkcurline();
+    if (hs->curline_linked)
+	linkcurline();
 }
 
 /*
@@ -653,6 +662,7 @@ histsubchar(int c)
 		(c == '}' ||  c == ';' || c == '\'' || c == '"' || c == '`')) {
 	      /* Neither event nor word designator, no expansion */
 	      safeinungetc(c);
+	      unqueue_signals();
 	      return bangchar;
 	    }
 	    *ptr = 0;
@@ -989,6 +999,7 @@ nohwe(void)
 
 /* these functions handle adding/removing curline to/from the hist_ring */
 
+/**/
 static void
 linkcurline(void)
 {
@@ -1001,11 +1012,15 @@ linkcurline(void)
 	hist_ring = &curline;
     }
     curline.histnum = ++curhist;
+    curline_linked = 1;
 }
 
+/**/
 static void
 unlinkcurline(void)
 {
+    if (!curline_linked)
+	return;
     curline.up->down = curline.down;
     curline.down->up = curline.up;
     if (hist_ring == &curline) {
@@ -1015,6 +1030,7 @@ unlinkcurline(void)
 	    hist_ring = curline.up;
     }
     curhist--;
+    curline_linked = 0;
 }
 
 /* initialize the history mechanism */
@@ -1034,10 +1050,11 @@ hbegin(int dohist)
 	stophist = (!interact || unset(SHINSTDIN)) ? 2 : 0;
     else
 	stophist = 0;
+    DPUTS(chline != NULL, "chline set at start of history");
     /*
      * pws: We used to test for "|| (inbufflags & INP_ALIAS)"
      * in this test, but at this point we don't have input
-     * set up up so this can trigger unnecessarily.
+     * set up so this can trigger unnecessarily.
      * I don't see how the test at this point could ever be
      * useful, since we only get here when we're initialising
      * the history mechanism, before we've done any input.
@@ -1291,11 +1308,10 @@ putoldhistentryontop(short keep_going)
 Histent
 prepnexthistent(void)
 {
-    Histent he; 
-    int curline_in_ring = hist_ring == &curline;
+    Histent he;
+    int relink_curline = curline_linked;
 
-    if (curline_in_ring)
-	unlinkcurline();
+    unlinkcurline();
     if (hist_ring && hist_ring->node.flags & HIST_TMPSTORE) {
 	curhist--;
 	freehistnode(&hist_ring->node);
@@ -1319,7 +1335,7 @@ prepnexthistent(void)
 	he = hist_ring;
     }
     he->histnum = ++curhist;
-    if (curline_in_ring)
+    if (relink_curline)
 	linkcurline();
     return he;
 }
@@ -1394,8 +1410,7 @@ hend(Eprog prog)
     queue_signals();
     if (histdone & HISTFLAG_SETTY)
 	settyinfo(&shttyinfo);
-    if (!(histactive & HA_NOINC))
-	unlinkcurline();
+    unlinkcurline();
     if (histactive & HA_NOINC) {
 	zfree(chline, hlinesz);
 	zfree(chwords, chwordlen*sizeof(short));
@@ -1417,7 +1432,7 @@ hend(Eprog prog)
 	DPUTS(hptr < chline, "History end pointer off start of line");
 	*hptr = '\0';
     }
-    {
+    if (*chline) {
 	LinkList hookargs = newlinklist();
 	int save_errflag = errflag;
 	errflag = 0;
@@ -1426,6 +1441,7 @@ hend(Eprog prog)
 	addlinknode(hookargs, chline);
 	callhookfunc("zshaddhistory", hookargs, 1, &hookret);
 
+	errflag &= ~ERRFLAG_ERROR;
 	errflag |= save_errflag;
     }
     /* For history sharing, lock history file once for both read and write */
@@ -1842,7 +1858,7 @@ chrealpath(char **junkptr)
 # ifdef REALPATH_ACCEPTS_NULL
     char *lastpos, *nonreal, *real;
 # else
-    char *lastpos, *nonreal, pathbuf[PATH_MAX];
+    char *lastpos, *nonreal, pathbuf[PATH_MAX+1];
     char *real = pathbuf;
 # endif
 #endif
@@ -3622,7 +3638,7 @@ int
 pushhiststack(char *hf, zlong hs, zlong shs, int level)
 {
     struct histsave *h;
-    int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline;
+    int relink_curline = curline_linked;
 
     if (histsave_stack_pos == histsave_stack_size) {
 	histsave_stack_size += 5;
@@ -3630,8 +3646,7 @@ pushhiststack(char *hf, zlong hs, zlong shs, int level)
 			    histsave_stack_size * sizeof (struct histsave));
     }
 
-    if (curline_in_ring)
-	unlinkcurline();
+    unlinkcurline();
 
     h = &histsave_stack[histsave_stack_pos++];
 
@@ -3666,7 +3681,7 @@ pushhiststack(char *hf, zlong hs, zlong shs, int level)
     savehistsiz = shs;
     inithist(); /* sets histtab */
 
-    if (curline_in_ring)
+    if (relink_curline)
 	linkcurline();
 
     return histsave_stack_pos;
@@ -3678,13 +3693,12 @@ int
 pophiststack(void)
 {
     struct histsave *h;
-    int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline;
+    int relink_curline = curline_linked;
 
     if (histsave_stack_pos == 0)
 	return 0;
 
-    if (curline_in_ring)
-	unlinkcurline();
+    unlinkcurline();
 
     deletehashtable(histtab);
     zsfree(lasthist.text);
@@ -3707,7 +3721,7 @@ pophiststack(void)
     histsiz = h->histsiz;
     savehistsiz = h->savehistsiz;
 
-    if (curline_in_ring)
+    if (relink_curline)
 	linkcurline();
 
     return histsave_stack_pos + 1;
diff --git a/Src/init.c b/Src/init.c
index 20a07eb0a..d8c26aca2 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -376,12 +376,12 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp,
 	    *argv = "--";
 	while (*++*argv) {
 	    if (**argv == '-') {
-		if(!argv[0][1]) {
+		if (!argv[0][1]) {
 		    /* The pseudo-option `--' signifies the end of options. */
 		    argv++;
 		    goto doneoptions;
 		}
-		if(*argv != args+1 || **argv != '-')
+		if (nam || *argv != args+1 || **argv != '-')
 		    goto badoptionstring;
 		/* GNU-style long options */
 		++*argv;
@@ -712,7 +712,7 @@ init_term(void)
     if (tgetent(termbuf, term) != TGETENT_SUCCESS)
 #endif
     {
-	if (isset(INTERACTIVE))
+	if (interact)
 	    zerr("can't find terminal definition for %s", term);
 	errflag &= ~ERRFLAG_ERROR;
 	termflags |= TERM_BAD;
@@ -790,7 +790,7 @@ init_term(void)
 	    tcstr[TCCLEARSCREEN] = ztrdup("\14");
 	    tclen[TCCLEARSCREEN] = 1;
 	}
-	rprompt_indent = 1;
+	rprompt_indent = 1; /* If you change this, update rprompt_indent_unsetfn() */
 	/* The following is an attempt at a heuristic,
 	 * but it fails in some cases */
 	/* rprompt_indent = ((hasam && !hasbw) || hasye || !tccan(TCLEFT)); */
@@ -1205,19 +1205,22 @@ run_init_scripts(void)
 	if (islogin)
 	    source("/etc/profile");
 	if (unset(PRIVILEGED)) {
-	    char *s = getsparam("ENV");
 	    if (islogin)
 		sourcehome(".profile");
-	    noerrs = 2;
-	    if (s) {
-		s = dupstring(s);
-		if (!parsestr(&s)) {
-		    singsub(&s);
-		    noerrs = 0;
-		    source(s);
+
+	    if (interact) {
+		noerrs = 2;
+		char *s = getsparam("ENV");
+		if (s) {
+		    s = dupstring(s);
+		    if (!parsestr(&s)) {
+			singsub(&s);
+			noerrs = 0;
+			source(s);
+		    }
 		}
+		noerrs = 0;
 	    }
-	    noerrs = 0;
 	} else
 	    source("/etc/suid_profile");
     } else {
@@ -1227,7 +1230,7 @@ run_init_scripts(void)
 
 	if (isset(RCS) && unset(PRIVILEGED))
 	{
-	    if (isset(INTERACTIVE)) {
+	    if (interact) {
 		/*
 		 * Always attempt to load the newuser module to perform
 		 * checks for new zsh users.  Don't care if we can't load it.
@@ -1439,8 +1442,10 @@ sourcehome(char *s)
     queue_signals();
     if (EMULATION(EMULATE_SH|EMULATE_KSH) || !(h = getsparam_u("ZDOTDIR"))) {
 	h = home;
-	if (!h)
+	if (!h) {
+	    unqueue_signals();
 	    return;
+	}
     }
 
     {
diff --git a/Src/input.c b/Src/input.c
index eb968ea72..92abaec92 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -670,3 +670,30 @@ ingetptr(void)
 {
     return inbufptr;
 }
+
+/*
+ * Check if the current input line, including continuations, is
+ * expanding an alias.  This does not detect alias expansions that
+ * have been fully processed and popped from the input stack.
+ * If there is an alias, the most recently expanded is returned,
+ * else NULL.
+ */
+
+/**/
+char *input_hasalias(void)
+{
+    int flags = inbufflags;
+    struct instacks *instackptr = instacktop;
+
+    for (;;)
+    {
+	if (!(flags & INP_CONT))
+	    break;
+	instackptr--;
+	if (instackptr->alias)
+	    return instackptr->alias->node.nam;
+	flags = instackptr->flags;
+    }
+
+    return NULL;
+}
diff --git a/Src/jobs.c b/Src/jobs.c
index 2a9dbe7d6..d1b98ac4d 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -128,7 +128,7 @@ makerunning(Job jn)
     Process pn;
 
     jn->stat &= ~STAT_STOPPED;
-    for (pn = jn->procs; pn; pn = pn->next)
+    for (pn = jn->procs; pn; pn = pn->next) {
 #if 0
 	if (WIFSTOPPED(pn->status) && 
 	    (!(jn->stat & STAT_SUPERJOB) || pn->next))
@@ -136,6 +136,7 @@ makerunning(Job jn)
 #endif
         if (WIFSTOPPED(pn->status))
 	    pn->status = SP_RUNNING;
+    }
 
     if (jn->stat & STAT_SUPERJOB)
 	makerunning(jobtab + jn->other);
@@ -231,12 +232,13 @@ super_job(int sub)
 static int
 handle_sub(int job, int fg)
 {
+    /* job: superjob; sj: subjob. */
     Job jn = jobtab + job, sj = jobtab + jn->other;
 
     if ((sj->stat & STAT_DONE) || (!sj->procs && !sj->auxprocs)) {
 	struct process *p;
-		    
-	for (p = sj->procs; p; p = p->next)
+
+	for (p = sj->procs; p; p = p->next) {
 	    if (WIFSIGNALED(p->status)) {
 		if (jn->gleader != mypgrp && jn->procs->next)
 		    killpg(jn->gleader, WTERMSIG(p->status));
@@ -246,6 +248,7 @@ handle_sub(int job, int fg)
 		kill(sj->other, WTERMSIG(p->status));
 		break;
 	    }
+	}
 	if (!p) {
 	    int cp;
 
@@ -884,37 +887,62 @@ should_report_time(Job j)
     struct value vbuf;
     Value v;
     char *s = "REPORTTIME";
-    zlong reporttime;
+    zlong reporttime = -1;
+#ifdef HAVE_GETRUSAGE
+    char *sm = "REPORTMEMORY";
+    zlong reportmemory = -1;
+#endif
 
     /* if the time keyword was used */
     if (j->stat & STAT_TIMED)
 	return 1;
 
     queue_signals();
-    if (!(v = getvalue(&vbuf, &s, 0)) ||
-	(reporttime = getintvalue(v)) < 0) {
-	unqueue_signals();
-	return 0;
-    }
+    if ((v = getvalue(&vbuf, &s, 0)))
+	reporttime = getintvalue(v);
+#ifdef HAVE_GETRUSAGE
+    if ((v = getvalue(&vbuf, &sm, 0)))
+	reportmemory = getintvalue(v);
+#endif
     unqueue_signals();
+    if (reporttime < 0
+#ifdef HAVE_GETRUSAGE
+	&& reportmemory < 0
+#endif
+	)
+	return 0;
     /* can this ever happen? */
     if (!j->procs)
 	return 0;
     if (zleactive)
 	return 0;
 
+    if (reporttime >= 0)
+    {
 #ifdef HAVE_GETRUSAGE
-    reporttime -= j->procs->ti.ru_utime.tv_sec + j->procs->ti.ru_stime.tv_sec;
-    if (j->procs->ti.ru_utime.tv_usec +
-	j->procs->ti.ru_stime.tv_usec >= 1000000)
-	reporttime--;
-    return reporttime <= 0;
+	reporttime -= j->procs->ti.ru_utime.tv_sec +
+	    j->procs->ti.ru_stime.tv_sec;
+	if (j->procs->ti.ru_utime.tv_usec +
+	    j->procs->ti.ru_stime.tv_usec >= 1000000)
+	    reporttime--;
+	if (reporttime <= 0)
+	    return 1;
 #else
-    {
-	clktck = get_clktck();
-	return ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime);
+	{
+	    clktck = get_clktck();
+	    if ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime)
+		return 1;
+	}
+#endif
     }
+
+#ifdef HAVE_GETRUSAGE
+    if (reportmemory >= 0 &&
+	j->procs->ti.ru_maxrss / 1024 > reportmemory)
+	return 1;
 #endif
+
+    return 0;
 }
 
 /* !(lng & 3) means jobs    *
@@ -1291,6 +1319,11 @@ deletejob(Job jn, int disowning)
 	attachtty(mypgrp);
 	adjustwinsize(0);
     }
+    if (jn->stat & STAT_SUPERJOB) {
+	Job jno = jobtab + jn->other;
+	if (jno->stat & STAT_SUBJOB)
+	    jno->stat |= STAT_SUBJOB_ORPHANED;
+    }
 
     freejob(jn, 1);
 }
@@ -1447,7 +1480,7 @@ zwaitjob(int job, int wait_cmd)
 	     */
 	    pipecleanfilelist(jn->filelist, 0);
 	}
-	while (!errflag && jn->stat &&
+	while (!(errflag & ERRFLAG_ERROR) && jn->stat &&
 	       !(jn->stat & STAT_DONE) &&
 	       !(interact && (jn->stat & STAT_STOPPED))) {
 	    signal_suspend(SIGCHLD, wait_cmd);
@@ -1469,6 +1502,13 @@ zwaitjob(int job, int wait_cmd)
 	      that's the one related to ^C.  But that doesn't work.
 	      There's something more here we don't understand.  --pws
 
+	      The change above to ignore ERRFLAG_INT in the loop test
+	      solves a problem wherein child processes that ignore the
+	      INT signal were never waited-for.  Clearing the flag here
+	      still seems the wrong thing, but perhaps ERRFLAG_INT
+	      should be saved and restored around signal_suspend() to
+	      prevent it being lost within a signal trap?  --Bart
+
            errflag = 0; */
 
 	    if (subsh) {
@@ -2527,6 +2567,10 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func))
 	argv++;
     }
 
+    /* Discard the standard "-" and "--" option breaks */
+    if (*argv && (*argv)[0] == '-' && (!(*argv)[1] || (*argv)[1] == '-'))
+	argv++;
+
     if (!*argv) {
     	zwarnnam(nam, "not enough arguments");
 	return 1;
diff --git a/Src/lex.c b/Src/lex.c
index 25b372a3c..b2d9b3f42 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -613,7 +613,7 @@ gettok(void)
     if (lexstop)
 	return (errflag) ? LEXERR : ENDINPUT;
     isfirstln = 0;
-    if ((lexflags & LEXFLAGS_ZLE))
+    if ((lexflags & LEXFLAGS_ZLE) && !(inbufflags & INP_ALIAS))
 	wordbeg = inbufct - (qbang && c == bangchar);
     hwbegin(-1-(qbang && c == bangchar));
     /* word includes the last character read and possibly \ before ! */
@@ -1359,17 +1359,13 @@ gettokstr(int c, int sub)
 	case LX2_DASH:
 	    /*
 	     * - shouldn't be treated as a special character unless
-	     * we're in a pattern.  Howeve,simply  counting "[" doesn't
-	     * work as []a-z] is a valid expression and we don't know
-	     * down here what this "[" is for as $foo[stuff] is valid
-	     * in zsh.  So just detect an opening [, which is enough
-	     * to turn this into a pattern; the Dash will be harmlessly
-	     * untokenised if not wanted.
+	     * we're in a pattern.  Unfortunately, working out for
+	     * sure in complicated expressions whether we're in a
+	     * pattern is tricky.  So we'll make it special and
+	     * turn it back any time we don't need it special.
+	     * This is not ideal as it's a lot of work.
 	     */
-	    if (seen_brct)
-		c = Dash;
-           else
-               c = '-';
+	    c = Dash;
            break;
        case LX2_BANG:
            /*
@@ -1795,6 +1791,10 @@ gotword(void)
 	if (zlemetacs >= nwb) {
 	    wb = nwb;
 	    we = nwe;
+	} else {
+	    wb = zlemetacs + addedx;
+	    if (we < wb)
+		we = wb;
 	}
 	lexflags = 0;
     }
@@ -1839,9 +1839,9 @@ checkalias(void)
 	    suf > zshlextext && suf[-1] != Meta &&
 	    (an = (Alias)sufaliastab->getnode(sufaliastab, suf+1)) &&
 	    !an->inuse && incmdpos) {
-	    inpush(dupstring(zshlextext), INP_ALIAS, NULL);
+	    inpush(dupstring(zshlextext), INP_ALIAS, an);
 	    inpush(" ", INP_ALIAS, NULL);
-	    inpush(an->text, INP_ALIAS, an);
+	    inpush(an->text, INP_ALIAS, NULL);
 	    lexstop = 0;
 	    return 1;
 	}
@@ -2060,9 +2060,7 @@ skipcomm(void)
     int new_lexstop, new_lex_add_raw;
     int save_infor = infor;
     struct lexbufstate new_lexbuf;
-    int noalias = noaliases;
 
-    noaliases = 1;
     infor = 0;
     cmdpush(CS_CMDSUBST);
     SETPARBEGIN
@@ -2134,8 +2132,17 @@ skipcomm(void)
     lexflags &= ~LEXFLAGS_ZLE;
     dbparens = 0;	/* restored by zcontext_restore_partial() */
 
-    if (!parse_event(OUTPAR) || tok != OUTPAR)
-	lexstop = 1;
+    if (!parse_event(OUTPAR) || tok != OUTPAR) {
+	if (strin) {
+	    /*
+	     * Get the rest of the string raw since we don't
+	     * know where this token ends.
+	     */
+	    while (!lexstop)
+		(void)ingetc();
+	} else
+	    lexstop = 1;
+    }
      /* Outpar lexical token gets added in caller if present */
 
     /*
@@ -2180,7 +2187,6 @@ skipcomm(void)
 	SETPAREND
     cmdpop();
     infor = save_infor;
-    noaliases = noalias;
 
     return lexstop;
 #endif
diff --git a/Src/linklist.c b/Src/linklist.c
index 3aa8125d9..85d9bb367 100644
--- a/Src/linklist.c
+++ b/Src/linklist.c
@@ -348,6 +348,35 @@ newsizedlist(int size)
 }
 
 /*
+ * Join two linked lists.  Neither may be null, though either
+ * may be empty.
+ *
+ * It is assumed the pieces come from the heap, but if not it is
+ * safe to free LinkList second.
+ */
+
+/**/
+mod_export LinkList
+joinlists(LinkList first, LinkList second)
+{
+    LinkNode moveme = firstnode(second);
+    if (moveme) {
+	if (firstnode(first)) {
+	    LinkNode anchor = lastnode(first);
+	    anchor->next = moveme;
+	    moveme->prev = anchor;
+	} else {
+	    first->list.first = moveme;
+	    moveme->prev = &first->node;
+	}
+	first->list.last = second->list.last;
+
+	second->list.first = second->list.last = NULL;
+    }
+    return first;
+}
+
+/*
  * Return the node whose data is the pointer "dat", else NULL.
  * Can be used as a boolean test.
  */
diff --git a/Src/loop.c b/Src/loop.c
index 19d7f733e..f7eae307b 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -208,6 +208,7 @@ execfor(Estate state, int do_exec)
     loops--;
     simple_pline = old_simple_pline;
     state->pc = end;
+    this_noerrexit = 1;
     return lastval;
 }
 
@@ -289,6 +290,8 @@ execselect(Estate state, UNUSED(int do_exec))
 	    	}
 	    } else
 		str = (char *)getlinknode(bufstack);
+            if (!str && !errflag)
+                setsparam("REPLY", ztrdup("")); /* EOF (user pressed Ctrl+D) */
 	    if (!str || errflag) {
 		if (breaks)
 		    breaks--;
@@ -333,6 +336,7 @@ execselect(Estate state, UNUSED(int do_exec))
     loops--;
     simple_pline = old_simple_pline;
     state->pc = end;
+    this_noerrexit = 1;
     return lastval;
 }
 
@@ -437,13 +441,12 @@ execwhile(Estate state, UNUSED(int do_exec))
             if (!((lastval == 0) ^ isuntil)) {
                 if (breaks)
                     breaks--;
-                lastval = oldval;
+		if (!retflag)
+		    lastval = oldval;
                 break;
             }
-            if (retflag) {
-                lastval = oldval;
+            if (retflag)
                 break;
-            }
 
 	    /* In case the loop body is also a functional no-op,
 	     * make sure signal handlers recognize ^C as above. */
@@ -471,6 +474,7 @@ execwhile(Estate state, UNUSED(int do_exec))
     popheap();
     loops--;
     state->pc = end;
+    this_noerrexit = 1;
     return lastval;
 }
 
@@ -522,6 +526,7 @@ execrepeat(Estate state, UNUSED(int do_exec))
     loops--;
     simple_pline = old_simple_pline;
     state->pc = end;
+    this_noerrexit = 1;
     return lastval;
 }
 
@@ -568,9 +573,11 @@ execif(Estate state, int do_exec)
 	cmdpop();
     } else {
 	noerrexit = olderrexit;
-	lastval = 0;
+	if (!retflag)
+	    lastval = 0;
     }
     state->pc = end;
+    this_noerrexit = 1;
 
     return lastval;
 }
@@ -582,7 +589,7 @@ execcase(Estate state, int do_exec)
     Wordcode end, next;
     wordcode code = state->pc[-1];
     char *word, *pat;
-    int npat, save, nalts, ialt, patok;
+    int npat, save, nalts, ialt, patok, anypatok;
     Patprog *spprog, pprog;
 
     end = state->pc + WC_CASE_SKIP(code);
@@ -590,7 +597,7 @@ execcase(Estate state, int do_exec)
     word = ecgetstr(state, EC_DUP, NULL);
     singsub(&word);
     untokenize(word);
-    lastval = 0;
+    anypatok = 0;
 
     cmdpush(CS_CASE);
     while (state->pc < end) {
@@ -613,7 +620,9 @@ execcase(Estate state, int do_exec)
 	    spprog = state->prog->pats + npat;
 	    pprog = NULL;
 	    pat = NULL;
-	
+
+	    queue_signals();
+
 	    if (isset(XTRACE)) {
 		int htok = 0;
 		pat = dupstring(ecrawstr(state->prog, state->pc, &htok));
@@ -647,9 +656,11 @@ execcase(Estate state, int do_exec)
 		    *spprog = pprog;
 	    }
 	    if (pprog && pattry(pprog, word))
-		patok = 1;
+		patok = anypatok = 1;
 	    state->pc += 2;
 	    nalts--;
+
+	    unqueue_signals();
 	}
 	state->pc += 2 * nalts;
 	if (isset(XTRACE)) {
@@ -660,7 +671,7 @@ execcase(Estate state, int do_exec)
 	    execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) &&
 				do_exec));
 	    while (!retflag && wc_code(code) == WC_CASE &&
-		   WC_CASE_TYPE(code) == WC_CASE_AND) {
+		   WC_CASE_TYPE(code) == WC_CASE_AND && state->pc < end) {
 		state->pc = next;
 		code = *state->pc++;
 		next = state->pc + WC_CASE_SKIP(code);
@@ -678,6 +689,10 @@ execcase(Estate state, int do_exec)
 
     state->pc = end;
 
+    if (!anypatok)
+	lastval = 0;
+    this_noerrexit = 1;
+
     return lastval;
 }
 
diff --git a/Src/math.c b/Src/math.c
index 37981cf22..f9613001a 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -463,7 +463,7 @@ lexconstant(void)
     char *nptr;
 
     nptr = ptr;
-    if (*nptr == '-')
+    if (IS_DASH(*nptr))
 	nptr++;
 
     if (*nptr == '0') {
@@ -527,7 +527,7 @@ lexconstant(void)
 	}
 	if (*nptr == 'e' || *nptr == 'E') {
 	    nptr++;
-	    if (*nptr == '+' || *nptr == '-')
+	    if (*nptr == '+' || IS_DASH(*nptr))
 		nptr++;
 	    while (idigit(*nptr) || *nptr == '_')
 		nptr++;
@@ -599,7 +599,8 @@ zzlex(void)
 	    }
 	    return (unary) ? UPLUS : PLUS;
 	case '-':
-	    if (*ptr == '-') {
+	case Dash:
+	    if (IS_DASH(*ptr)) {
 		ptr++;
 		return (unary) ? PREMINUS : POSTMINUS;
 	    }
@@ -974,7 +975,7 @@ callmathfunc(char *o)
     a[strlen(a) - 1] = '\0';
 
     if ((f = getmathfunc(n, 1))) {
-	if (f->flags & MFF_STR) {
+	if ((f->flags & (MFF_STR|MFF_USERFUNC)) == MFF_STR) {
 	    return f->sfunc(n, a, f->funcid);
 	} else {
 	    int argc = 0;
@@ -987,22 +988,34 @@ callmathfunc(char *o)
 		addlinknode(l, n);
 	    }
 
-	    while (iblank(*a))
-		a++;
+	    if (f->flags & MFF_STR) {
+		if (!*a) {
+		    addlinknode(l, dupstring(""));
+		    argc++;
+		}
+	    } else {
+		while (iblank(*a))
+		    a++;
+	    }
 	    while (*a) {
 		if (*a) {
 		    argc++;
 		    if (f->flags & MFF_USERFUNC) {
 			/* need to pass strings */
 			char *str;
-			marg = mathevall(a, MPREC_ARG, &a);
-			if (marg.type & MN_FLOAT) {
-			    /* convfloat is off the heap */
-			    str = convfloat(marg.u.d, 0, 0, NULL);
+			if (f->flags & MFF_STR) {
+			    str = dupstring(a);
+			    a = "";
 			} else {
-			    char buf[BDIGBUFSIZE];
-			    convbase(buf, marg.u.l, 10);
-			    str = dupstring(buf);
+			    marg = mathevall(a, MPREC_ARG, &a);
+			    if (marg.type & MN_FLOAT) {
+				/* convfloat is off the heap */
+				str = convfloat(marg.u.d, 0, 0, NULL);
+			    } else {
+				char buf[BDIGBUFSIZE];
+				convbase(buf, marg.u.l, 10);
+				str = dupstring(buf);
+			    }
 			}
 			addlinknode(l, str);
 		    } else {
diff --git a/Src/mem.c b/Src/mem.c
index e31145e1e..840bbb6e4 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -157,13 +157,13 @@ mod_export Heapid last_heap_id;
  * Assumes old_heaps() will come along and restore it later
  * (outputs an error if old_heaps() is called out of sequence).
  */
-LinkList heaps_saved;
+static LinkList heaps_saved;
 
 /*
  * Debugging verbosity.  This must be set from a debugger.
  * An 'or' of bits from the enum heap_debug_verbosity.
  */
-volatile int heap_debug_verbosity;
+static volatile int heap_debug_verbosity;
 
 /*
  * Generate a heap identifier that's unique up to unsigned integer wrap.
@@ -497,7 +497,8 @@ popheap(void)
 		    continue;
 		}
 		h->next = NULL;
-	    }
+	    } else if (hl == h)	/* This is the last arena of all */
+		hl = NULL;
 #ifdef USE_MMAP
 	    munmap((void *) h, h->size);
 #else
@@ -903,27 +904,36 @@ memory_validate(Heapid heap_id)
 
     queue_signals();
     for (h = heaps; h; h = h->next) {
-	if (h->heap_id == heap_id)
+	if (h->heap_id == heap_id) {
+	    unqueue_signals();
 	    return 0;
+	}
 	for (hs = heaps->sp; hs; hs = hs->next) {
-	    if (hs->heap_id == heap_id)
+	    if (hs->heap_id == heap_id) {
+		unqueue_signals();
 		return 0;
+	    }
 	}
     }
 
     if (heaps_saved) {
 	for (node = firstnode(heaps_saved); node; incnode(node)) {
 	    for (h = (Heap)getdata(node); h; h = h->next) {
-		if (h->heap_id == heap_id)
+		if (h->heap_id == heap_id) {
+		    unqueue_signals();
 		    return 0;
+		}
 		for (hs = heaps->sp; hs; hs = hs->next) {
-		    if (hs->heap_id == heap_id)
+		    if (hs->heap_id == heap_id) {
+			unqueue_signals();
 			return 0;
+		    }
 		}
 	    }
 	}
     }
 
+    unqueue_signals();
     return 1;
 }
 /**/
@@ -966,18 +976,10 @@ zalloc(size_t size)
 mod_export void *
 zshcalloc(size_t size)
 {
-    void *ptr;
-
+    void *ptr = zalloc(size);
     if (!size)
 	size = 1;
-    queue_signals();
-    if (!(ptr = (void *) malloc(size))) {
-	zerr("fatal error: out of memory");
-	exit(1);
-    }
-    unqueue_signals();
     memset(ptr, 0, size);
-
     return ptr;
 }
 
diff --git a/Src/module.c b/Src/module.c
index 368254c29..41f142adb 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -2242,6 +2242,7 @@ load_module(char const *name, Feature_enables enablesarr, int silent)
 	return 0;
     }
     if (m->node.flags & MOD_BUSY) {
+	unqueue_signals();
 	zerr("circular dependencies for module ;%s", name);
 	return 1;
     }
@@ -3350,6 +3351,8 @@ setfeatureenables(Module m, Features f, int *e)
     if (f->mf_size) {
 	if (setmathfuncs(m->node.nam, f->mf_list, f->mf_size, e))
 	    ret = 1;
+	if (e)
+	    e += f->mf_size;
     }
     if (f->pd_size) {
 	if (setparamdefs(m->node.nam, f->pd_list, f->pd_size, e))
diff --git a/Src/options.c b/Src/options.c
index 18619c851..2b5795bab 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -78,6 +78,7 @@ mod_export HashTable optiontab;
  */
 static struct optname optns[] = {
 {{NULL, "aliases",	      OPT_EMULATE|OPT_ALL},	 ALIASESOPT},
+{{NULL, "aliasfuncdef",       OPT_EMULATE|OPT_BOURNE},	 ALIASFUNCDEF},
 {{NULL, "allexport",	      OPT_EMULATE},		 ALLEXPORT},
 {{NULL, "alwayslastprompt",   OPT_ALL},			 ALWAYSLASTPROMPT},
 {{NULL, "alwaystoend",	      0},			 ALWAYSTOEND},
@@ -257,6 +258,7 @@ static struct optname optns[] = {
 {{NULL, "verbose",	      0},			 VERBOSE},
 {{NULL, "vi",		      0},			 VIMODE},
 {{NULL, "warncreateglobal",   OPT_EMULATE},		 WARNCREATEGLOBAL},
+{{NULL, "warnnestedvar",      OPT_EMULATE},		 WARNNESTEDVAR},
 {{NULL, "xtrace",	      0},			 XTRACE},
 {{NULL, "zle",		      OPT_SPECIAL},		 USEZLE},
 {{NULL, "braceexpand",	      OPT_ALIAS}, /* ksh/bash */ -IGNOREBRACES},
@@ -645,7 +647,7 @@ bin_setopt(char *nam, char **args, UNUSED(Options ops), int isun)
 
 	    /* Expand the current arg. */
 	    tokenize(s);
-	    if (!(pprog = patcompile(s, PAT_STATIC, NULL))) {
+	    if (!(pprog = patcompile(s, PAT_HEAPDUP, NULL))) {
 		zwarnnam(nam, "bad pattern: %s", *args);
 		continue;
 	    }
diff --git a/Src/params.c b/Src/params.c
index 2e4dd4ee6..8d6cd0ee5 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -89,6 +89,7 @@ char *ifs,		/* $IFS         */
      *postedit,		/* $POSTEDIT    */
      *term,		/* $TERM        */
      *zsh_terminfo,     /* $TERMINFO    */
+     *zsh_terminfodirs, /* $TERMINFO_DIRS */
      *ttystrname,	/* $TTY         */
      *pwd;		/* $PWD         */
 
@@ -129,6 +130,11 @@ struct timeval shtimer;
 /**/
 mod_export int termflags;
 
+/* Forward declaration */
+
+static void
+rprompt_indent_unsetfn(Param pm, int exp);
+
 /* Standard methods for get/set/unset pointers in parameters */
 
 /**/
@@ -210,6 +216,8 @@ static const struct gsu_scalar term_gsu =
 { termgetfn, termsetfn, stdunsetfn };
 static const struct gsu_scalar terminfo_gsu =
 { terminfogetfn, terminfosetfn, stdunsetfn };
+static const struct gsu_scalar terminfodirs_gsu =
+{ terminfodirsgetfn, terminfodirssetfn, stdunsetfn };
 static const struct gsu_scalar wordchars_gsu =
 { wordcharsgetfn, wordcharssetfn, stdunsetfn };
 static const struct gsu_scalar ifs_gsu =
@@ -240,6 +248,9 @@ static const struct gsu_integer argc_gsu =
 static const struct gsu_array pipestatus_gsu =
 { pipestatgetfn, pipestatsetfn, stdunsetfn };
 
+static const struct gsu_integer rprompt_indent_gsu =
+{ intvargetfn, zlevarsetfn, rprompt_indent_unsetfn };
+
 /* Nodes for special parameters for parameter hash table */
 
 #ifdef HAVE_UNION_INIT
@@ -285,8 +296,9 @@ IPDEF2("histchars", histchars_gsu, PM_DONTIMPORT),
 IPDEF2("HOME", home_gsu, PM_UNSET),
 IPDEF2("TERM", term_gsu, PM_UNSET),
 IPDEF2("TERMINFO", terminfo_gsu, PM_UNSET),
+IPDEF2("TERMINFO_DIRS", terminfodirs_gsu, PM_UNSET),
 IPDEF2("WORDCHARS", wordchars_gsu, 0),
-IPDEF2("IFS", ifs_gsu, PM_DONTIMPORT),
+IPDEF2("IFS", ifs_gsu, PM_DONTIMPORT | PM_RESTRICTED),
 IPDEF2("_", underscore_gsu, PM_DONTIMPORT),
 IPDEF2("KEYBOARD_HACK", keyboard_hack_gsu, PM_DONTIMPORT),
 IPDEF2("0", argzero_gsu, 0),
@@ -325,7 +337,7 @@ IPDEF4("ZSH_SUBSHELL", &zsh_subshell),
 #define IPDEF5U(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(F),10,0,0,NULL,NULL,NULL,0}
 IPDEF5("COLUMNS", &zterm_columns, zlevar_gsu),
 IPDEF5("LINES", &zterm_lines, zlevar_gsu),
-IPDEF5U("ZLE_RPROMPT_INDENT", &rprompt_indent, zlevar_gsu),
+IPDEF5U("ZLE_RPROMPT_INDENT", &rprompt_indent, rprompt_indent_gsu),
 IPDEF5("SHLVL", &shlvl, varinteger_gsu),
 
 /* Don't import internal integer status variables. */
@@ -334,8 +346,9 @@ IPDEF6("OPTIND", &zoptind, varinteger_gsu),
 IPDEF6("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu),
 IPDEF6("TRY_BLOCK_INTERRUPT", &try_interrupt, varinteger_gsu),
 
-#define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,0,NULL,NULL,NULL,0}
-#define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,0,NULL,NULL,NULL,0}
+#define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
+#define IPDEF7R(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_DONTIMPORT_SUID},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
+#define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0}
 IPDEF7("OPTARG", &zoptarg),
 IPDEF7("NULLCMD", &nullcmd),
 IPDEF7U("POSTEDIT", &postedit),
@@ -347,10 +360,21 @@ IPDEF7("PS2", &prompt2),
 IPDEF7U("RPS2", &rprompt2),
 IPDEF7U("RPROMPT2", &rprompt2),
 IPDEF7("PS3", &prompt3),
-IPDEF7("PS4", &prompt4),
+IPDEF7R("PS4", &prompt4),
 IPDEF7("SPROMPT", &sprompt),
 
-#define IPDEF8(A,B,C,D) {{NULL,A,D|PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(colonarr_gsu),0,0,0,NULL,C,NULL,0}
+#define IPDEF9F(A,B,C,D) {{NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(vararray_gsu),0,0,NULL,C,NULL,0}
+#define IPDEF9(A,B,C) IPDEF9F(A,B,C,0)
+IPDEF9F("*", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
+IPDEF9F("@", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
+
+/*
+ * This empty row indicates the end of parameters available in
+ * all emulations.
+ */
+{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
+
+#define IPDEF8(A,B,C,D) {{NULL,A,D|PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(colonarr_gsu),0,0,NULL,C,NULL,0}
 IPDEF8("CDPATH", &cdpath, "cdpath", 0),
 IPDEF8("FIGNORE", &fignore, "fignore", 0),
 IPDEF8("FPATH", &fpath, "fpath", 0),
@@ -363,18 +387,7 @@ IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY),
 /* MODULE_PATH is not imported for security reasons */
 IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED),
 
-#define IPDEF9F(A,B,C,D) {{NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(vararray_gsu),0,0,0,NULL,C,NULL,0}
-#define IPDEF9(A,B,C) IPDEF9F(A,B,C,0)
-IPDEF9F("*", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
-IPDEF9F("@", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
-
-/*
- * This empty row indicates the end of parameters available in
- * all emulations.
- */
-{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,0,NULL,NULL,NULL,0},
-
-#define IPDEF10(A,B) {{NULL,A,PM_ARRAY|PM_SPECIAL},BR(NULL),GSU(B),10,0,0,NULL,NULL,NULL,0}
+#define IPDEF10(A,B) {{NULL,A,PM_ARRAY|PM_SPECIAL},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0}
 
 /*
  * The following parameters are not available in sh/ksh compatibility *
@@ -413,6 +426,26 @@ IPDEF10("pipestatus", pipestatus_gsu),
 };
 
 /*
+ * Alternative versions of colon-separated path parameters for
+ * sh emulation.  These don't link to the array versions.
+ */
+static initparam special_params_sh[] = {
+IPDEF8("CDPATH", &cdpath, NULL, 0),
+IPDEF8("FIGNORE", &fignore, NULL, 0),
+IPDEF8("FPATH", &fpath, NULL, 0),
+IPDEF8("MAILPATH", &mailpath, NULL, 0),
+IPDEF8("WATCH", &watch, NULL, 0),
+IPDEF8("PATH", &path, NULL, PM_RESTRICTED),
+IPDEF8("PSVAR", &psvar, NULL, 0),
+IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, NULL, PM_READONLY),
+
+/* MODULE_PATH is not imported for security reasons */
+IPDEF8("MODULE_PATH", &module_path, NULL, PM_DONTIMPORT|PM_RESTRICTED),
+
+{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
+};
+
+/*
  * Special way of referring to the positional parameters.  Unlike $*
  * and $@, this is not readonly.  This parameter is not directly
  * visible in user space.
@@ -628,6 +661,36 @@ getvaluearr(Value v)
 	return NULL;
 }
 
+/* Return whether the variable is set         *
+ * checks that array slices are within range  *
+ * used for [[ -v ... ]] condition test       */
+
+/**/
+int
+issetvar(char *name)
+{
+    struct value vbuf;
+    Value v;
+    int slice;
+    char **arr;
+
+    if (!(v = getvalue(&vbuf, &name, 1)) || *name)
+	return 0; /* no value or more chars after the variable name */
+    if (v->isarr & ~SCANPM_ARRONLY)
+	return v->end > 1; /* for extracted elements, end gives us a count */
+
+    slice = v->start != 0 || v->end != -1;
+    if (PM_TYPE(v->pm->node.flags) != PM_ARRAY || !slice)
+	return !slice && !(v->pm->node.flags & PM_UNSET);
+
+    if (!v->end) /* empty array slice */
+	return 0;
+    /* get the array and check end is within range */
+    if (!(arr = getvaluearr(v)))
+	return 0;
+    return arrlen_ge(arr, v->end < 0 ? - v->end : v->end);
+}
+
 /*
  * Split environment string into (name, value) pair.
  * this is used to avoid in-place editing of environment table
@@ -662,19 +725,27 @@ split_env_string(char *env, char **name, char **value)
 	return 0;
 }
 
-int
-arrcachelen(Param pm)
+/**
+ * Check parameter flags to see if parameter shouldn't be imported
+ * from environment at start.
+ *
+ * return 1: don't import: 0: ok to import.
+ */
+static int dontimport(int flags)
 {
-    int len;
-
-    len = pm->length;
-    if (len == 0 && pm->u.arr) {
-	len = arrlen(pm->u.arr);
-	pm->length = len;
-    }
-    return len;
+    /* If explicitly marked as don't export */
+    if (flags & PM_DONTIMPORT)
+	return 1;
+    /* If value already exported */
+    if (flags & PM_EXPORTED)
+	return 1;
+    /* If security issue when importing and running with some privilege */
+    if ((flags & PM_DONTIMPORT_SUID) && isset(PRIVILEGED))
+	return 1;
+    /* OK to import */
+    return 0;
 }
-    
+
 /* Set up parameter hash table.  This will add predefined  *
  * parameter entries as well as setting up parameter table *
  * entries for environment variables we inherit.           */
@@ -704,9 +775,13 @@ createparamtable(void)
     /* Add the special parameters to the hash table */
     for (ip = special_params; ip->node.nam; ip++)
 	paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip);
-    if (!EMULATION(EMULATE_SH|EMULATE_KSH))
+    if (EMULATION(EMULATE_SH|EMULATE_KSH)) {
+	for (ip = special_params_sh; ip->node.nam; ip++)
+	    paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip);
+    } else {
 	while ((++ip)->node.nam)
 	    paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip);
+    }
 
     argvparam = (Param) &argvparam_pm;
 
@@ -766,8 +841,13 @@ createparamtable(void)
 	    envp2 = environ; *envp2; envp2++) {
 	if (split_env_string(*envp2, &iname, &ivalue)) {
 	    if (!idigit(*iname) && isident(iname) && !strchr(iname, '[')) {
+		/*
+		 * Parameters that aren't already in the parameter table
+		 * aren't special to the shell, so it's always OK to
+		 * import.  Otherwise, check parameter flags.
+		 */
 		if ((!(pm = (Param) paramtab->getnode(paramtab, iname)) ||
-		     !(pm->node.flags & PM_DONTIMPORT || pm->node.flags & PM_EXPORTED)) &&
+		     !dontimport(pm->node.flags)) &&
 		    (pm = assignsparam(iname, metafy(ivalue, -1, META_DUP),
 				       ASSPM_ENV_IMPORT))) {
 		    pm->node.flags |= PM_EXPORTED;
@@ -785,7 +865,7 @@ createparamtable(void)
     }
     popheap();
 #ifndef USE_SET_UNSET_ENV
-    *envp = '\0';
+    *envp = NULL;
 #endif
     opts[ALLEXPORT] = oae;
 
@@ -898,7 +978,10 @@ createparam(char *name, int flags)
 		zerr("%s: restricted", name);
 		return NULL;
 	    }
-	    if (!(oldpm->node.flags & PM_UNSET) || (oldpm->node.flags & PM_SPECIAL)) {
+	    if (!(oldpm->node.flags & PM_UNSET) ||
+		(oldpm->node.flags & PM_SPECIAL) ||
+		/* POSIXBUILTINS horror: we need to retain 'export' flags */
+		(isset(POSIXBUILTINS) && (oldpm->node.flags & PM_EXPORTED))) {
 		oldpm->node.flags &= ~PM_UNSET;
 		if ((oldpm->node.flags & PM_SPECIAL) && oldpm->ename) {
 		    Param altpm =
@@ -1127,7 +1210,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
        int *prevcharlen, int *nextcharlen)
 {
     int hasbeg = 0, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash;
-    int keymatch = 0, needtok = 0, arglen, len;
+    int keymatch = 0, needtok = 0, arglen, len, inpar = 0;
     char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt, c;
     zlong num = 1, beg = 0, r = 0, quote_arg = 0;
     Patprog pprog = NULL;
@@ -1266,8 +1349,9 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
     }
 
     for (t = s, i = 0;
-	 (c = *t) && ((c != Outbrack &&
-		       (ishash || c != ',')) || i); t++) {
+	 (c = *t) &&
+	     ((c != Outbrack && (ishash || c != ',')) || i || inpar);
+	 t++) {
 	/* Untokenize inull() except before brackets and double-quotes */
 	if (inull(c)) {
 	    c = t[1];
@@ -1288,6 +1372,10 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
 	    i++;
 	else if (c == ']' || c == Outbrack)
 	    i--;
+	if (c == '(' || c == Inpar)
+	    inpar++;
+	else if (c == ')' || c == Outpar)
+	    inpar--;
 	if (ispecial(c))
 	    needtok = 1;
     }
@@ -1723,6 +1811,18 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w,
     return r;
 }
 
+/*
+ * Parse a subscript.
+ *
+ * pptr: In/Out parameter.  On entry, *ptr points to a "[foo]" string.  On exit
+ * it will point one past the closing bracket.
+ *
+ * v: In/Out parameter.  Its .start and .end members (at least) will be updated
+ * with the parsed indices.
+ *
+ * flags: can be either SCANPM_DQUOTED or zero.  Other bits are not used.
+ */
+
 /**/
 int
 getindex(char **pptr, Value v, int flags)
@@ -1929,7 +2029,9 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
 	*s++ = '$';
     else if (c == Star)
 	*s++ = '*';
-    else if (c == '#' || c == '-' || c == '?' || c == '$' ||
+    else if (IS_DASH(c))
+	*s++ = '-';
+    else if (c == '#' || c == '?' || c == '$' ||
 	     c == '!' || c == '@' || c == '*')
 	s++;
     else
@@ -2026,6 +2128,7 @@ getstrvalue(Value v)
 {
     char *s, **ss;
     char buf[BDIGBUFSIZE];
+    int len;
 
     if (!v)
 	return hcalloc(1);
@@ -2050,21 +2153,10 @@ getstrvalue(Value v)
 	if (v->isarr)
 	    s = sepjoin(ss, NULL, 1);
 	else {
-	    if (v->pm->node.flags & PM_CACHELEN) {
-		int len = arrcachelen(v->pm);
-		if (v->pm->node.flags & PM_CHECKLEN)
-		    assert(v->pm->length == arrlen(ss));
-		if (v->start < 0)
-		    v->start += len;
-		s = (v->start >= len || v->start < 0) ?
-		    (char *) hcalloc(1) : ss[v->start];
-	    } else {
-		int len = arrlen(ss);
-		if (v->start < 0)
-		    v->start += len;
-		s = (v->start >= len || v->start < 0) ?
-		    (char *) hcalloc(1) : ss[v->start];
-	    }
+	    if (v->start < 0)
+		v->start += arrlen(ss);
+	    s = (arrlen_le(ss, v->start) || v->start < 0) ?
+		(char *) hcalloc(1) : ss[v->start];
 	}
 	return s;
     case PM_INTEGER:
@@ -2213,23 +2305,27 @@ getstrvalue(Value v)
     if (v->start == 0 && v->end == -1)
 	return s;
 
+    len = strlen(s);
     if (v->start < 0) {
-	v->start += strlen(s);
+	v->start += len;
 	if (v->start < 0)
 	    v->start = 0;
     }
     if (v->end < 0) {
-	v->end += strlen(s);
+	v->end += len;
 	if (v->end >= 0) {
 	    char *eptr = s + v->end;
 	    if (*eptr)
 		v->end += MB_METACHARLEN(eptr);
 	}
     }
-    s = (v->start > (int)strlen(s)) ? dupstring("") : dupstring(s + v->start);
+
+    s = (v->start > len) ? dupstring("") :
+	dupstring_wlen(s + v->start, len - v->start);
+
     if (v->end <= v->start)
 	s[0] = '\0';
-    else if (v->end - v->start <= (int)strlen(s))
+    else if (v->end - v->start <= len - v->start)
 	s[v->end - v->start] = '\0';
 
     return s;
@@ -2258,38 +2354,35 @@ getarrvalue(Value v)
     s = getvaluearr(v);
     if (v->start == 0 && v->end == -1)
 	return s;
-    if (v->pm->node.flags & PM_CACHELEN) {
-	int len = arrcachelen(v->pm);
-	if (v->pm->node.flags & PM_CHECKLEN)
-	    assert(v->pm->length == arrlen(s));
-	if (v->start < 0)
-	    v->start += v->pm->length;
-	if (v->end < 0)
-	    v->end += v->pm->length + 1;
-	if (v->start > v->pm->length || v->start < 0)
-	    s = arrdup(nular);
-	else
-	    s = arrdup(s + v->start);
-	if (v->end <= v->start)
-	    s[0] = NULL;
-	//XXX[badarrays] s just changed above but here we use the same
-	//               cached length possible cause of problems
-	else if (v->end - v->start <= v->pm->length)
-	    s[v->end - v->start] = NULL;
-    } else {
-	if (v->start < 0)
-	   v->start += arrlen(s);
-	if (v->end < 0)
-	   v->end += arrlen(s) + 1;
-	if (v->start > arrlen(s) || v->start < 0)
-	   s = arrdup(nular);
-	else
-	   s = arrdup(s + v->start);
-	if (v->end <= v->start)
-	   s[0] = NULL;
-	else if (v->end - v->start <= arrlen(s))
-	   s[v->end - v->start] = NULL;
+    if (v->start < 0)
+	v->start += arrlen(s);
+    if (v->end < 0)
+	v->end += arrlen(s) + 1;
+
+    /* Null if 1) array too short, 2) index still negative */
+    if (v->end <= v->start) {
+	s = arrdup_max(nular, 0);
+    }
+    else if (v->start < 0) {
+	s = arrdup_max(nular, 1);
+    }
+    else if (arrlen_le(s, v->start)) {
+	/* Handle $ary[i,i] consistently for any $i > $#ary
+	 * and $ary[i,j] consistently for any $j > $i > $#ary
+	 */
+	s = arrdup_max(nular, v->end - (v->start + 1));
+    }
+    else {
+        /* Copy to a point before the end of the source array:
+         * arrdup_max will copy at most v->end - v->start elements,
+         * starting from v->start element. Original code said:
+	 *  s[v->end - v->start] = NULL
+         * which means that there are exactly the same number of
+         * elements as the value of the above *0-based* index.
+         */
+	s = arrdup_max(s + v->start, v->end - v->start);
     }
+
     return s;
 }
 
@@ -2418,10 +2511,11 @@ assignstrvalue(Value v, char *val, int flags)
 		v->pm->width = strlen(val);
 	} else {
 	    char *z, *x;
-	    int zlen;
+            int zlen, vlen, newsize;
+
+            z = v->pm->gsu.s->getfn(v->pm);
+            zlen = strlen(z);
 
-	    z = dupstring(v->pm->gsu.s->getfn(v->pm));
-	    zlen = strlen(z);
 	    if ((v->flags & VALFLAG_INV) && unset(KSHARRAYS))
 		v->start--, v->end--;
 	    if (v->start < 0) {
@@ -2451,12 +2545,34 @@ assignstrvalue(Value v, char *val, int flags)
 	    }
 	    else if (v->end > zlen)
 		v->end = zlen;
-	    x = (char *) zalloc(v->start + strlen(val) + zlen - v->end + 1);
-	    strncpy(x, z, v->start);
-	    strcpy(x + v->start, val);
-	    strcat(x + v->start, z + v->end);
-	    v->pm->gsu.s->setfn(v->pm, x);
-	    zsfree(val);
+
+            vlen = strlen(val);
+            /* Characters preceding start index +
+               characters of what is assigned +
+               characters following end index */
+            newsize = v->start + vlen + (zlen - v->end);
+
+            /* Does new size differ? */
+            if (newsize != zlen || v->pm->gsu.s->setfn != strsetfn) {
+                x = (char *) zalloc(newsize + 1);
+                strncpy(x, z, v->start);
+                strcpy(x + v->start, val);
+                strcat(x + v->start, z + v->end);
+                v->pm->gsu.s->setfn(v->pm, x);
+            } else {
+		Param pm = v->pm;
+                /* Size doesn't change, can limit actions to only
+                 * overwriting bytes in already allocated string */
+                strncpy(z + v->start, val, vlen);
+		/* Implement remainder of strsetfn */
+		if (!(pm->node.flags & PM_HASHELEM) &&
+		    ((pm->node.flags & PM_NAMEDDIR) ||
+		     isset(AUTONAMEDIRS))) {
+		    pm->node.flags |= PM_NAMEDDIR;
+		    adduserdir(pm->node.nam, z, 0, 0);
+		}
+            }
+            zsfree(val);
 	}
 	break;
     case PM_INTEGER:
@@ -2641,24 +2757,85 @@ setarrvalue(Value v, char **val)
 	    v->end = v->start;
 
 	post_assignment_length = v->start + arrlen(val);
-	if (v->end <= pre_assignment_length)
-	    post_assignment_length += pre_assignment_length - v->end + 1;
-
-	p = new = (char **) zshcalloc(sizeof(char *)
-		                      * (post_assignment_length + 1));
-
-	for (i = 0; i < v->start; i++)
-	    *p++ = i < pre_assignment_length ? ztrdup(*q++) : ztrdup("");
-	for (r = val; *r;)
-	    *p++ = ztrdup(*r++);
-	if (v->end < pre_assignment_length)
-	    for (q = old + v->end; *q;)
-		*p++ = ztrdup(*q++);
-	*p = NULL;
-
-	v->pm->gsu.a->setfn(v->pm, new);
-	v->pm->length = post_assignment_length;
-	freearray(val);
+	if (v->end < pre_assignment_length) {
+	    /* 
+	     * Allocate room for array elements between the end of the slice `v'
+	     * and the original array's end.
+	     */
+	    post_assignment_length += pre_assignment_length - v->end;
+	}
+
+	if (pre_assignment_length == post_assignment_length
+	    && v->pm->gsu.a->setfn == arrsetfn
+	    /* ... and isn't something that arrsetfn() treats specially */
+	    && 0 == (v->pm->node.flags & (PM_SPECIAL|PM_UNIQUE))
+	    && NULL == v->pm->ename)
+	{
+	    /* v->start is 0-based */
+	    p = old + v->start;
+	    for (r = val; *r;) {
+		/* Free previous string */
+		zsfree(*p);
+		/* Give away ownership of the string */
+		*p++ = *r++;
+	    }
+	} else {
+            /* arr+=( ... )
+             * arr[${#arr}+x,...]=( ... ) */
+            if (post_assignment_length > pre_assignment_length &&
+                    pre_assignment_length <= v->start &&
+                    pre_assignment_length > 0 &&
+                    v->pm->gsu.a->setfn == arrsetfn)
+            {
+                p = new = (char **) zrealloc(old, sizeof(char *)
+                                           * (post_assignment_length + 1));
+
+                p += pre_assignment_length; /* after old elements */
+
+                /* Consider 1 < 0, case for a=( 1 ); a[1,..] =
+                 *          1 < 1, case for a=( 1 ); a[2,..] = */
+                if (pre_assignment_length < v->start) {
+                    for (i = pre_assignment_length; i < v->start; i++) {
+                        *p++ = ztrdup("");
+                    }
+                }
+
+                for (r = val; *r;) {
+                    /* Give away ownership of the string */
+                    *p++ = *r++;
+                }
+
+                /* v->end doesn't matter:
+                 * a=( 1 2 ); a[4,100]=( a b ); echo "${(q@)a}"
+                 * 1 2 '' a b */
+                *p = NULL;
+
+                v->pm->u.arr = NULL;
+                v->pm->gsu.a->setfn(v->pm, new);
+            } else {
+                p = new = (char **) zalloc(sizeof(char *)
+                                           * (post_assignment_length + 1));
+                for (i = 0; i < v->start; i++)
+                    *p++ = i < pre_assignment_length ? ztrdup(*q++) : ztrdup("");
+                for (r = val; *r;) {
+                    /* Give away ownership of the string */
+                    *p++ = *r++;
+                }
+                if (v->end < pre_assignment_length)
+                    for (q = old + v->end; *q;)
+                        *p++ = ztrdup(*q++);
+                *p = NULL;
+
+                v->pm->gsu.a->setfn(v->pm, new);
+            }
+
+	    DPUTS2(p - new != post_assignment_length, "setarrvalue: wrong allocation: %d 1= %lu",
+		   post_assignment_length, (unsigned long)(p - new));
+	}
+
+        /* Ownership of all strings has been
+         * given away, can plainly free */
+	free(val);
     }
 }
 
@@ -2773,20 +2950,51 @@ gethkparam(char *s)
     return NULL;
 }
 
+/*
+ * Function behind WARNCREATEGLOBAL and WARNNESTEDVAR option.
+ *
+ * For WARNNESTEDVAR:
+ * Called when the variable is created.
+ * Apply heuristics to see if this variable was just created
+ * globally but in a local context.
+ *
+ * For WARNNESTEDVAR:
+ * Called when the variable already exists and is set.
+ * Apply heuristics to see if this variable is setting
+ * a variable that was created in a less nested function
+ * or globally.
+ */
+
 /**/
 static void
-check_warn_create(Param pm, const char *pmtype)
+check_warn_pm(Param pm, const char *pmtype, int created,
+	      int may_warn_about_nested_vars)
 {
     Funcstack i;
 
-    if (pm->level != 0 || (pm->node.flags & PM_SPECIAL))
+    if (!may_warn_about_nested_vars && !created)
+	return;
+
+    if (created && isset(WARNCREATEGLOBAL)) {
+	if (locallevel <= forklevel || pm->level != 0)
+	    return;
+    } else if (!created && isset(WARNNESTEDVAR)) {
+	if (pm->level >= locallevel)
+	    return;
+    } else
+	return;
+
+    if (pm->node.flags & PM_SPECIAL)
 	return;
 
     for (i = funcstack; i; i = i->prev) {
 	if (i->tp == FS_FUNC) {
+	    char *msg;
 	    DPUTS(!i->name, "funcstack entry with no name");
-	    zwarn("%s parameter %s created globally in function %s",
-		  pmtype, pm->node.nam, i->name);
+	    msg = created ?
+		"%s parameter %s created globally in function %s" :
+		"%s parameter %s set in enclosing scope in function %s";
+	    zwarn(msg, pmtype, pm->node.nam, i->name);
 	    break;
 	}
     }
@@ -2802,7 +3010,7 @@ assignsparam(char *s, char *val, int flags)
     char *ss, *copy, *var;
     size_t lvar;
     mnumber lhs, rhs;
-    int sstart;
+    int sstart, created = 0;
 
     if (!isident(s)) {
 	zerr("not an identifier: %s", s);
@@ -2813,32 +3021,38 @@ assignsparam(char *s, char *val, int flags)
     queue_signals();
     if ((ss = strchr(s, '['))) {
 	*ss = '\0';
-	if (!(v = getvalue(&vbuf, &s, 1)))
+	if (!(v = getvalue(&vbuf, &s, 1))) {
 	    createparam(t, PM_ARRAY);
-	else {
+	    created = 1;
+	} else {
 	    if (v->pm->node.flags & PM_READONLY) {
 		zerr("read-only variable: %s", v->pm->node.nam);
 		*ss = '[';
 		zsfree(val);
+		unqueue_signals();
 		return NULL;
 	    }
-	    flags &= ~ASSPM_WARN_CREATE;
+	    /*
+	     * Parameter defined here is a temporary bogus one.
+	     * Don't warn about anything.
+	     */
+	    flags &= ~ASSPM_WARN;
 	}
 	*ss = '[';
 	v = NULL;
     } else {
-	if (!(v = getvalue(&vbuf, &s, 1)))
+	if (!(v = getvalue(&vbuf, &s, 1))) {
 	    createparam(t, PM_SCALAR);
-	else if ((((v->pm->node.flags & PM_ARRAY) && !(flags & ASSPM_AUGMENT)) ||
+	    created = 1;
+	} else if ((((v->pm->node.flags & PM_ARRAY) && !(flags & ASSPM_AUGMENT)) ||
 	    	 (v->pm->node.flags & PM_HASHED)) &&
 		 !(v->pm->node.flags & (PM_SPECIAL|PM_TIED)) && 
 		 unset(KSHARRAYS)) {
 	    unsetparam(t);
 	    createparam(t, PM_SCALAR);
+	    /* not regarded as a new creation */
 	    v = NULL;
 	}
-	else
-	    flags &= ~ASSPM_WARN_CREATE;
     }
     if (!v && !(v = getvalue(&vbuf, &t, 1))) {
 	unqueue_signals();
@@ -2846,8 +3060,8 @@ assignsparam(char *s, char *val, int flags)
 	/* errflag |= ERRFLAG_ERROR; */
 	return NULL;
     }
-    if (flags & ASSPM_WARN_CREATE)
-	check_warn_create(v->pm, "scalar");
+    if (flags & ASSPM_WARN)
+	check_warn_pm(v->pm, "scalar", created, 1);
     if (flags & ASSPM_AUGMENT) {
 	if (v->start == 0 && v->end == -1) {
 	    switch (PM_TYPE(v->pm->node.flags)) {
@@ -2931,9 +3145,7 @@ assignsparam(char *s, char *val, int flags)
 mod_export Param
 setsparam(char *s, char *val)
 {
-    return assignsparam(
-	s, val, isset(WARNCREATEGLOBAL) && locallevel > forklevel ?
-	ASSPM_WARN_CREATE : 0);
+    return assignsparam(s, val, ASSPM_WARN);
 }
 
 /**/
@@ -2944,6 +3156,8 @@ assignaparam(char *s, char **val, int flags)
     Value v;
     char *t = s;
     char *ss;
+    int created = 0;
+    int may_warn_about_nested_vars = 1;
 
     if (!isident(s)) {
 	zerr("not an identifier: %s", s);
@@ -2954,10 +3168,12 @@ assignaparam(char *s, char **val, int flags)
     queue_signals();
     if ((ss = strchr(s, '['))) {
 	*ss = '\0';
-	if (!(v = getvalue(&vbuf, &s, 1)))
+	if (!(v = getvalue(&vbuf, &s, 1))) {
 	    createparam(t, PM_ARRAY);
-	else
-	    flags &= ~ASSPM_WARN_CREATE;
+	    created = 1;
+	} else {
+	    may_warn_about_nested_vars = 0;
+	}
 	*ss = '[';
 	if (v && PM_TYPE(v->pm->node.flags) == PM_HASHED) {
 	    unqueue_signals();
@@ -2969,9 +3185,10 @@ assignaparam(char *s, char **val, int flags)
 	}
 	v = NULL;
     } else {
-	if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING)))
+	if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) {
 	    createparam(t, PM_ARRAY);
-	else if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED)) &&
+	    created = 1;
+	} else if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED)) &&
 		 !(v->pm->node.flags & (PM_SPECIAL|PM_TIED))) {
 	    int uniq = v->pm->node.flags & PM_UNIQUE;
 	    if (flags & ASSPM_AUGMENT) {
@@ -2991,8 +3208,6 @@ assignaparam(char *s, char **val, int flags)
 	    createparam(t, PM_ARRAY | uniq);
 	    v = NULL;
 	}
-	else
-	    flags &= ~ASSPM_WARN_CREATE;
     }
     if (!v)
 	if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) {
@@ -3002,8 +3217,8 @@ assignaparam(char *s, char **val, int flags)
 	    return NULL;
 	}
 
-    if (flags & ASSPM_WARN_CREATE)
-	check_warn_create(v->pm, "array");
+    if (flags & ASSPM_WARN)
+	check_warn_pm(v->pm, "array", created, may_warn_about_nested_vars);
     if (flags & ASSPM_AUGMENT) {
     	if (v->start == 0 && v->end == -1) {
 	    if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) {
@@ -3049,9 +3264,7 @@ assignaparam(char *s, char **val, int flags)
 mod_export Param
 setaparam(char *s, char **aval)
 {
-    return assignaparam(
-	s, aval, isset(WARNCREATEGLOBAL) && locallevel > forklevel ?
-	ASSPM_WARN_CREATE : 0);
+    return assignaparam(s, aval, ASSPM_WARN);
 }
 
 /**/
@@ -3079,9 +3292,8 @@ sethparam(char *s, char **val)
 	return NULL;
     queue_signals();
     if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) {
-	DPUTS(!v, "BUG: assigning to undeclared associative array");
 	createparam(t, PM_HASHED);
-	checkcreate = isset(WARNCREATEGLOBAL) && locallevel > forklevel;
+	checkcreate = 1;
     } else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED) &&
 	     !(v->pm->node.flags & PM_SPECIAL)) {
 	unsetparam(t);
@@ -3095,8 +3307,7 @@ sethparam(char *s, char **val)
 	    /* errflag |= ERRFLAG_ERROR; */
 	    return NULL;
 	}
-    if (checkcreate)
-	check_warn_create(v->pm, "associative array");
+    check_warn_pm(v->pm, "associative array", checkcreate, 1);
     setarrvalue(v, val);
     unqueue_signals();
     return v->pm;
@@ -3105,11 +3316,12 @@ sethparam(char *s, char **val)
 
 /*
  * Set a generic shell number, floating point or integer.
+ * Option to warn on setting.
  */
 
 /**/
-Param
-setnparam(char *s, mnumber val)
+mod_export Param
+assignnparam(char *s, mnumber val, int flags)
 {
     struct value vbuf;
     Value v;
@@ -3158,16 +3370,44 @@ setnparam(char *s, mnumber val)
 	if (!(v = getvalue(&vbuf, &t, 1))) {
 	    DPUTS(!v, "BUG: value not found for new parameter");
 	    /* errflag |= ERRFLAG_ERROR; */
+	    unqueue_signals();
 	    return NULL;
 	}
-	if (!was_unset && isset(WARNCREATEGLOBAL) && locallevel > forklevel)
-	    check_warn_create(v->pm, "numeric");
+	if (flags & ASSPM_WARN)
+	    check_warn_pm(v->pm, "numeric", !was_unset, 1);
+    } else {
+	if (flags & ASSPM_WARN)
+	    check_warn_pm(v->pm, "numeric", 0, 1);
     }
     setnumvalue(v, val);
     unqueue_signals();
     return v->pm;
 }
 
+/*
+ * Set a generic shell number, floating point or integer.
+ * Warn on setting based on option.
+ */
+
+/**/
+mod_export Param
+setnparam(char *s, mnumber val)
+{
+    return assignnparam(s, val, ASSPM_WARN);
+}
+
+/* Simplified interface to assignnparam */
+
+/**/
+mod_export Param
+assigniparam(char *s, zlong val, int flags)
+{
+    mnumber mnval;
+    mnval.type = MN_INTEGER;
+    mnval.u.l = val;
+    return assignnparam(s, mnval, flags);
+}
+
 /* Simplified interface to setnparam */
 
 /**/
@@ -3177,7 +3417,7 @@ setiparam(char *s, zlong val)
     mnumber mnval;
     mnval.type = MN_INTEGER;
     mnval.u.l = val;
-    return setnparam(s, mnval);
+    return assignnparam(s, mnval, ASSPM_WARN);
 }
 
 /*
@@ -3196,10 +3436,7 @@ setiparam_no_convert(char *s, zlong val)
      */
     char buf[BDIGBUFSIZE];
     convbase(buf, val, 10);
-    return assignsparam(
-	s, ztrdup(buf),
-	isset(WARNCREATEGLOBAL) && locallevel > forklevel ?
-	ASSPM_WARN_CREATE : 0);
+    return assignsparam(s, ztrdup(buf), ASSPM_WARN);
 }
 
 /* Unset a parameter */
@@ -3219,7 +3456,11 @@ unsetparam(char *s)
     unqueue_signals();
 }
 
-/* Unset a parameter */
+/* Unset a parameter
+ *
+ * altflag: if true, don't remove pm->ename from the environment
+ * exp: See stdunsetfn()
+ */
 
 /**/
 mod_export int
@@ -3416,6 +3657,8 @@ strsetfn(Param pm, char *x)
 	pm->node.flags |= PM_NAMEDDIR;
 	adduserdir(pm->node.nam, x, 0, 0);
     }
+    /* If you update this function, you may need to update the
+     * `Implement remainder of strsetfn' block in assignstrvalue(). */
 }
 
 /* Function to get value of an array parameter */
@@ -3443,6 +3686,8 @@ arrsetfn(Param pm, char **x)
     /* Arrays tied to colon-arrays may need to fix the environment */
     if (pm->ename && x)
 	arrfixenv(pm->ename, x);
+    /* If you extend this function, update the list of conditions in
+     * setarrvalue(). */
 }
 
 /* Function to get value of an association parameter */
@@ -3579,6 +3824,16 @@ zlevarsetfn(Param pm, zlong x)
 	adjustwinsize(2 + (p == &zterm_columns));
 }
 
+
+/* Implements gsu_integer.unsetfn for ZLE_RPROMPT_INDENT; see stdunsetfn() */
+
+static void
+rprompt_indent_unsetfn(Param pm, int exp)
+{
+    stdunsetfn(pm, exp);
+    rprompt_indent = 1; /* Keep this in sync with init_term() */
+}
+
 /* Function to set value of generic special scalar    *
  * parameter.  data is pointer to a character pointer *
  * representing the scalar (string).                  */
@@ -3678,8 +3933,7 @@ colonarrsetfn(Param pm, char *x)
 	*dptr = colonsplit(x, pm->node.flags & PM_UNIQUE);
     else
 	*dptr = mkarray(NULL);
-    if (pm->ename)
-	arrfixenv(pm->node.nam, *dptr);
+    arrfixenv(pm->node.nam, *dptr);
     zsfree(x);
 }
 
@@ -4035,7 +4289,7 @@ uidsetfn(UNUSED(Param pm), zlong x)
 {
 #ifdef HAVE_SETUID
     if (setuid((uid_t)x))
-	zwarn("failed to change user ID: %e", errno);
+	zerr("failed to change user ID: %e", errno);
 #endif
 }
 
@@ -4056,7 +4310,7 @@ euidsetfn(UNUSED(Param pm), zlong x)
 {
 #ifdef HAVE_SETEUID
     if (seteuid((uid_t)x))
-	zwarn("failed to change effective user ID: %e", errno);
+	zerr("failed to change effective user ID: %e", errno);
 #endif
 }
 
@@ -4077,7 +4331,7 @@ gidsetfn(UNUSED(Param pm), zlong x)
 {
 #ifdef HAVE_SETUID
     if (setgid((gid_t)x))
-	zwarn("failed to change group ID: %e", errno);
+	zerr("failed to change group ID: %e", errno);
 #endif
 }
 
@@ -4098,7 +4352,7 @@ egidsetfn(UNUSED(Param pm), zlong x)
 {
 #ifdef HAVE_SETEUID
     if (setegid((gid_t)x))
-	zwarn("failed to change effective group ID: %e", errno);
+	zerr("failed to change effective group ID: %e", errno);
 #endif
 }
 
@@ -4434,7 +4688,7 @@ void
 homesetfn(UNUSED(Param pm), char *x)
 {
     zsfree(home);
-    if (x && isset(CHASELINKS) && (home = xsymlink(x)))
+    if (x && isset(CHASELINKS) && (home = xsymlink(x, 0)))
 	zsfree(x);
     else
 	home = x ? x : ztrdup("");
@@ -4533,6 +4787,33 @@ terminfosetfn(Param pm, char *x)
     term_reinit_from_pm();
 }
 
+/* Function to get value of special parameter `TERMINFO_DIRS' */
+
+/**/
+char *
+terminfodirsgetfn(UNUSED(Param pm))
+{
+    return zsh_terminfodirs ? zsh_terminfodirs : dupstring("");
+}
+
+/* Function to set value of special parameter `TERMINFO_DIRS' */
+
+/**/
+void
+terminfodirssetfn(Param pm, char *x)
+{
+    zsfree(zsh_terminfodirs);
+    zsh_terminfodirs = x;
+
+    /*
+     * terminfo relies on the value being exported before
+     * we reinitialise the terminal.  This is a bit inefficient.
+     */
+    if ((pm->node.flags & PM_EXPORTED) && x)
+	addenv(pm, x);
+
+    term_reinit_from_pm();
+}
 /* Function to get value for special parameter `pipestatus' */
 
 /**/
@@ -4768,6 +5049,7 @@ addenv(Param pm, char *value)
      if (pm->env)
          zsfree(pm->env);
      pm->env = newenv;
+     pm->node.flags |= PM_EXPORTED;
 #else
     /*
      * Under Cygwin we must use putenv() to maintain consistency.
@@ -5263,10 +5545,6 @@ printparamvalue(Param p, int printflags)
 {
     char *t, **u;
 
-    if (p->node.flags & PM_AUTOLOAD) {
-	putchar('\n');
-	return;
-    }
     if (printflags & PRINT_KV_PAIR)
 	putchar(' ');
     else
@@ -5350,9 +5628,13 @@ printparamnode(HashNode hn, int printflags)
 	     */
 	    printflags |= PRINT_NAMEONLY;
 	}
+	else if (p->node.flags & PM_EXPORTED)
+	    printflags |= PRINT_NAMEONLY;
 	else
 	    return;
     }
+    if (p->node.flags & PM_AUTOLOAD)
+	printflags |= PRINT_NAMEONLY;
 
     if (printflags & PRINT_TYPESET) {
 	if ((p->node.flags & (PM_READONLY|PM_SPECIAL)) ==
@@ -5364,7 +5646,15 @@ printparamnode(HashNode hn, int printflags)
 	     */
 	    return;
 	}
-	printf("typeset ");
+	if (locallevel && p->level >= locallevel) {
+	    printf("typeset ");	    /* printf("local "); */
+	} else if ((p->node.flags & PM_EXPORTED) &&
+		   !(p->node.flags & (PM_ARRAY|PM_HASHED))) {
+	    printf("export ");
+	} else if (locallevel) {
+	    printf("typeset -g ");
+	} else
+	    printf("typeset ");
     }
 
     /* Print the attributes of the parameter */
@@ -5377,7 +5667,9 @@ printparamnode(HashNode hn, int printflags)
 	    if (pmptr->flags & PMTF_TEST_LEVEL) {
 		if (p->level)
 		    doprint = 1;
-	    } else if (p->node.flags & pmptr->binflag)
+	    } else if ((pmptr->binflag != PM_EXPORTED || p->level ||
+			(p->node.flags & (PM_LOCAL|PM_ARRAY|PM_HASHED))) &&
+		       (p->node.flags & pmptr->binflag))
 		doprint = 1;
 
 	    if (doprint) {
@@ -5389,9 +5681,8 @@ printparamnode(HashNode hn, int printflags)
 			}
 			putchar(pmptr->typeflag);
 		    }
-		} else {
+		} else
 		    printf("%s ", pmptr->string);
-		}
 		if ((pmptr->flags & PMTF_USE_BASE) && p->base) {
 		    printf("%d ", p->base);
 		    doneminus = 0;
diff --git a/Src/parse.c b/Src/parse.c
index 94ac04922..8769baae4 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -309,7 +309,6 @@ parse_context_restore(const struct parse_stack *ps, int toplevel)
     incond = ps->incond;
     inredir = ps->inredir;
     incasepat = ps->incasepat;
-    incasepat = ps->incasepat;
     isnewlin = ps->isnewlin;
     infor = ps->infor;
     inrepeat_ = ps->inrepeat_;
@@ -595,7 +594,7 @@ par_event(int endtok)
     if (tok == ENDINPUT)
 	return 0;
     if (tok == endtok)
-	return 0;
+	return 1;
 
     p = ecadd(0);
 
@@ -1739,6 +1738,7 @@ par_simple(int *cmplx, int nr)
 {
     int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0;
     int c = *cmplx, nrediradd, assignments = 0, ppost = 0, is_typeset = 0;
+    char *hasalias = input_hasalias();
     wordcode postassigns = 0;
 
     r = ecused;
@@ -1810,6 +1810,8 @@ par_simple(int *cmplx, int nr)
 	} else
 	    break;
 	zshlex();
+	if (!hasalias)
+	    hasalias = input_hasalias();
     }
     if (tok == AMPER || tok == AMPERBANG)
 	YYERROR(oecused);
@@ -1834,12 +1836,14 @@ par_simple(int *cmplx, int nr)
 
 		if (*ptr == Outbrace && ptr > tokstr + 1)
 		{
-		    if (itype_end(tokstr+1, IIDENT, 0) >= ptr - 1)
+		    if (itype_end(tokstr+1, IIDENT, 0) >= ptr)
 		    {
 			char *toksave = tokstr;
 			char *idstring = dupstrpfx(tokstr+1, eptr-tokstr-1);
 			redir_var = 1;
 			zshlex();
+			if (!hasalias)
+			    hasalias = input_hasalias();
 
 			if (IS_REDIROP(tok) && tokfd == -1)
 			{
@@ -1875,6 +1879,8 @@ par_simple(int *cmplx, int nr)
 		    argc++;
 		}
 		zshlex();
+		if (!hasalias)
+		    hasalias = input_hasalias();
 	    }
 	} else if (IS_REDIROP(tok)) {
 	    *cmplx = c = 1;
@@ -1903,6 +1909,8 @@ par_simple(int *cmplx, int nr)
 	    ecstr(name);
 	    ecstr(str);
 	    zshlex();
+	    if (!hasalias)
+		hasalias = input_hasalias();
 	} else if (tok == ENVARRAY) {
 	    int n, parr;
 
@@ -1937,6 +1945,11 @@ par_simple(int *cmplx, int nr)
 	    /* Error if preceding assignments */
 	    if (assignments || postassigns)
 		YYERROR(oecused);
+	    if (hasalias && !isset(ALIASFUNCDEF) && argc &&
+		hasalias != input_hasalias()) {
+		zwarn("defining function based on alias `%s'", hasalias);
+		YYERROR(oecused);
+	    }
 
 	    *cmplx = c;
 	    lineno = 0;
@@ -2017,10 +2030,21 @@ par_simple(int *cmplx, int nr)
 		/* Unnamed function */
 		int parg = ecadd(0);
 		ecadd(0);
-		while (tok == STRING) {
-		    ecstr(tokstr);
-		    argc++;
-		    zshlex();
+		while (tok == STRING || IS_REDIROP(tok)) {
+		    if (tok == STRING)
+		    {
+			ecstr(tokstr);
+			argc++;
+			zshlex();
+		    } else {
+			*cmplx = c = 1;
+			nrediradd = par_redir(&r, NULL);
+			p += nrediradd;
+			if (ppost)
+			    ppost += nrediradd;
+			sr += nrediradd;
+			parg += nrediradd;
+		    }
 		}
 		if (argc > 0)
 		    *cmplx = 1;
@@ -2130,7 +2154,7 @@ par_redir(int *rp, char *idstring)
 	 * the definition of WC_REDIR_WORDS. */
 	ecispace(r, ncodes);
 	*rp = r + ncodes;
-	ecbuf[r] = WCB_REDIR(type);
+	ecbuf[r] = WCB_REDIR(type | REDIR_FROM_HEREDOC_MASK);
 	ecbuf[r + 1] = fd1;
 
 	/*
@@ -2304,6 +2328,19 @@ par_cond_1(void)
 }
 
 /*
+ * Return 1 if condition matches.  This also works for non-elided options.
+ *
+ * input is test string, may begin - or Dash.
+ * cond is condition following the -.
+ */
+static int check_cond(const char *input, const char *cond)
+{
+    if (!IS_DASH(input[0]))
+	return 0;
+    return !strcmp(input + 1, cond);
+}
+
+/*
  * cond_2	: BANG cond_2
 				| INPAR { SEPER } cond_2 { SEPER } OUTPAR
 				| STRING STRING STRING
@@ -2329,7 +2366,7 @@ par_cond_2(void)
 	    s1 = tokstr;
 	    condlex();
 	    /* ksh behavior: [ -t ] means [ -t 1 ]; bash disagrees */
-	    if (unset(POSIXBUILTINS) && !strcmp(s1, "-t"))
+	    if (unset(POSIXBUILTINS) && check_cond(s1, "t"))
 		return par_cond_double(s1, dupstring("1"));
 	    return par_cond_double(dupstring("-n"), s1);
 	}
@@ -2339,7 +2376,7 @@ par_cond_2(void)
 	    if (!strcmp(*testargs, "=")  ||
 		!strcmp(*testargs, "==") ||
 		!strcmp(*testargs, "!=") ||
-		(**testargs == '-' && get_cond_num(*testargs + 1) >= 0)) {
+		(IS_DASH(**testargs) && get_cond_num(*testargs + 1) >= 0)) {
 		s1 = tokstr;
 		condlex();
 		s2 = tokstr;
@@ -2361,8 +2398,8 @@ par_cond_2(void)
 	 * In "test" compatibility mode, "! -a ..." and "! -o ..."
 	 * are treated as "[string] [and] ..." and "[string] [or] ...".
 	 */
-	if (!(n_testargs > 1 &&
-	      (!strcmp(*testargs, "-a") || !strcmp(*testargs, "-o"))))
+	if (!(n_testargs > 1 && (check_cond(*testargs, "a") ||
+				 check_cond(*testargs, "o"))))
 	{
 	    condlex();
 	    ecadd(WCB_COND(COND_NOT, 0));
@@ -2384,9 +2421,9 @@ par_cond_2(void)
 	return r;
     }
     s1 = tokstr;
-    dble = (s1 && *s1 == '-'
+    dble = (s1 && IS_DASH(*s1)
 	    && (!n_testargs
-		|| strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1)
+		|| strspn(s1+1, "abcdefghknoprstuvwxzLONGS") == 1)
 	    && !s1[2]);
     if (tok != STRING) {
 	/* Check first argument for [[ STRING ]] re-interpretation */
@@ -2398,7 +2435,7 @@ par_cond_2(void)
 	    YYERROR(ecused);
     }
     condlex();
-    if (n_testargs == 2 && tok != STRING && tokstr && s1[0] == '-') {
+    if (n_testargs == 2 && tok != STRING && tokstr && IS_DASH(s1[0])) {
 	/*
 	 * Something like "test -z" followed by a token.
 	 * We'll turn the token into a string (we've also
@@ -2433,9 +2470,9 @@ par_cond_2(void)
 	} else
 	    YYERROR(ecused);
     }
-    s2 = tokstr;   
+    s2 = tokstr;
     if (!n_testargs)
-	dble = (s2 && *s2 == '-' && !s2[2]);
+	dble = (s2 && IS_DASH(*s2) && !s2[2]);
     incond++;			/* parentheses do globbing */
     do condlex(); while (COND_SEP());
     incond--;			/* parentheses do grouping */
@@ -2463,9 +2500,9 @@ par_cond_2(void)
 static int
 par_cond_double(char *a, char *b)
 {
-    if (a[0] != '-' || !a[1])
+    if (!IS_DASH(a[0]) || !a[1])
 	COND_ERROR("parse error: condition expected: %s", a);
-    else if (!a[2] && strspn(a+1, "abcdefgknoprstuwxzhLONGS") == 1) {
+    else if (!a[2] && strspn(a+1, "abcdefgknoprstuvwxzhLONGS") == 1) {
 	ecadd(WCB_COND(a[1], 0));
 	ecstr(b);
     } else {
@@ -2498,12 +2535,17 @@ par_cond_triple(char *a, char *b, char *c)
 {
     int t0;
 
-    if ((b[0] == Equals || b[0] == '=') &&
-	(!b[1] || ((b[1] == Equals || b[1] == '=') && !b[2]))) {
+    if ((b[0] == Equals || b[0] == '=') && !b[1]) {
 	ecadd(WCB_COND(COND_STREQ, 0));
 	ecstr(a);
 	ecstr(c);
 	ecadd(ecnpats++);
+    } else if ((b[0] == Equals || b[0] == '=') &&
+	       (b[1] == Equals || b[1] == '=') && !b[2]) {
+	ecadd(WCB_COND(COND_STRDEQ, 0));
+	ecstr(a);
+	ecstr(c);
+	ecadd(ecnpats++);
     } else if (b[0] == '!' && (b[1] == Equals || b[1] == '=') && !b[2]) {
 	ecadd(WCB_COND(COND_STRNEQ, 0));
 	ecstr(a);
@@ -2516,7 +2558,7 @@ par_cond_triple(char *a, char *b, char *c)
 	ecadd(WCB_COND(COND_REGEX, 0));
 	ecstr(a);
 	ecstr(c);
-    } else if (b[0] == '-') {
+    } else if (IS_DASH(b[0])) {
 	if ((t0 = get_cond_num(b + 1)) > -1) {
 	    ecadd(WCB_COND(t0 + COND_NT, 0));
 	    ecstr(a);
@@ -2527,7 +2569,7 @@ par_cond_triple(char *a, char *b, char *c)
 	    ecstr(a);
 	    ecstr(c);
 	}
-    } else if (a[0] == '-' && a[1]) {
+    } else if (IS_DASH(a[0]) && a[1]) {
 	ecadd(WCB_COND(COND_MOD, 2));
 	ecstr(a);
 	ecstr(b);
@@ -2542,7 +2584,7 @@ par_cond_triple(char *a, char *b, char *c)
 static int
 par_cond_multi(char *a, LinkList l)
 {
-    if (a[0] != '-' || !a[1])
+    if (!IS_DASH(a[0]) || !a[1])
 	COND_ERROR("condition expected: %s", a);
     else {
 	LinkNode n;
@@ -3238,10 +3280,10 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags)
     for (hlen = FD_PRELEN, tlen = 0; *files; files++) {
 	struct stat st;
 
-	if (!strcmp(*files, "-k")) {
+	if (check_cond(*files, "k")) {
 	    flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_KSHLOAD;
 	    continue;
-	} else if (!strcmp(*files, "-z")) {
+	} else if (check_cond(*files, "z")) {
 	    flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_ZSHLOAD;
 	    continue;
 	}
@@ -3320,7 +3362,7 @@ cur_add_func(char *nam, Shfunc shf, LinkList names, LinkList progs,
 	    return 1;
 	}
 	noaliases = (shf->node.flags & PM_UNALIASED);
-	if (!(prog = getfpfunc(shf->node.nam, NULL, NULL)) ||
+	if (!(prog = getfpfunc(shf->node.nam, NULL, NULL, NULL, 0)) ||
 	    prog == &dummy_eprog) {
 	    noaliases = ona;
 	    zwarnnam(nam, "can't load function: %s", shf->node.nam);
@@ -3395,6 +3437,7 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map,
 
 	for (; *names; names++) {
 	    tokenize(pat = dupstring(*names));
+	    /* Signal-safe here, caller queues signals */
 	    if (!(pprog = patcompile(pat, PAT_STATIC, NULL))) {
 		zwarnnam(nam, "bad pattern: %s", *names);
 		close(dfd);
@@ -3562,7 +3605,7 @@ load_dump_file(char *dump, struct stat *sbuf, int other, int len)
 
 /**/
 Eprog
-try_dump_file(char *path, char *name, char *file, int *ksh)
+try_dump_file(char *path, char *name, char *file, int *ksh, int test_only)
 {
     Eprog prog;
     struct stat std, stc, stn;
@@ -3571,7 +3614,7 @@ try_dump_file(char *path, char *name, char *file, int *ksh)
 
     if (strsfx(FD_EXT, path)) {
 	queue_signals();
-	prog = check_dump_file(path, NULL, name, ksh);
+	prog = check_dump_file(path, NULL, name, ksh, test_only);
 	unqueue_signals();
 	return prog;
     }
@@ -3590,14 +3633,14 @@ try_dump_file(char *path, char *name, char *file, int *ksh)
     if (!rd &&
 	(rc || std.st_mtime > stc.st_mtime) &&
 	(rn || std.st_mtime > stn.st_mtime) &&
-	(prog = check_dump_file(dig, &std, name, ksh))) {
+	(prog = check_dump_file(dig, &std, name, ksh, test_only))) {
 	unqueue_signals();
 	return prog;
     }
     /* No digest file. Now look for the per-function compiled file. */
     if (!rc &&
 	(rn || stc.st_mtime > stn.st_mtime) &&
-	(prog = check_dump_file(wc, &stc, name, ksh))) {
+	(prog = check_dump_file(wc, &stc, name, ksh, test_only))) {
 	unqueue_signals();
 	return prog;
     }
@@ -3625,7 +3668,7 @@ try_source_file(char *file)
 
     if (strsfx(FD_EXT, file)) {
 	queue_signals();
-	prog = check_dump_file(file, NULL, tail, NULL);
+	prog = check_dump_file(file, NULL, tail, NULL, 0);
 	unqueue_signals();
 	return prog;
     }
@@ -3636,7 +3679,7 @@ try_source_file(char *file)
 
     queue_signals();
     if (!rc && (rn || stc.st_mtime > stn.st_mtime) &&
-	(prog = check_dump_file(wc, &stc, tail, NULL))) {
+	(prog = check_dump_file(wc, &stc, tail, NULL, 0))) {
 	unqueue_signals();
 	return prog;
     }
@@ -3649,7 +3692,8 @@ try_source_file(char *file)
 
 /**/
 static Eprog
-check_dump_file(char *file, struct stat *sbuf, char *name, int *ksh)
+check_dump_file(char *file, struct stat *sbuf, char *name, int *ksh,
+		int test_only)
 {
     int isrec = 0;
     Wordcode d;
@@ -3691,6 +3735,11 @@ check_dump_file(char *file, struct stat *sbuf, char *name, int *ksh)
     if ((h = dump_find_func(d, name))) {
 	/* Found the name. If the file is already mapped, return the eprog,
 	 * otherwise map it and just go up. */
+	if (test_only)
+	{
+	    /* This is all we need.  Just return dummy. */
+	    return &dummy_eprog;
+	}
 
 #ifdef USE_MMAP
 
@@ -3727,7 +3776,7 @@ check_dump_file(char *file, struct stat *sbuf, char *name, int *ksh)
 
 #endif
 
-	    {
+	{
 	    Eprog prog;
 	    Patprog *pp;
 	    int np, fd, po = h->npats * sizeof(Patprog);
diff --git a/Src/pattern.c b/Src/pattern.c
index 4e2f2369f..fc7c73739 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -668,13 +668,9 @@ patcompile(char *exp, int inflags, char **endexp)
 			    if (imeta(*mtest))
 				nmeta++;
 			if (nmeta) {
-			    char *oldpatout = patout;
 			    patadd(NULL, 0, nmeta, 0);
-			    /*
-			     * Yuk.
-			     */
 			    p = (Patprog)patout;
-			    opnd = patout + (opnd - oldpatout);
+			    opnd = dupstring_wlen(opnd, oplen);
 			    dst = patout + startoff;
 			}
 
@@ -686,6 +682,8 @@ patcompile(char *exp, int inflags, char **endexp)
 				*dst++ = *opnd++;
 			    }
 			}
+			/* Only one string in a PAT_PURES, so now done. */
+			break;
 		    }
 		}
 		p->size = dst - patout;
@@ -1523,7 +1521,7 @@ patcomppiece(int *flagp, int paren)
 		patparse = nptr;
 		len |= 1;
 	    }
-	    DPUTS(*patparse != '-', "BUG: - missing from numeric glob");
+	    DPUTS(!IS_DASH(*patparse), "BUG: - missing from numeric glob");
 	    patparse++;
 	    if (idigit(*patparse)) {
 		to = (zrange_t) zstrtol((char *)patparse,
@@ -3627,7 +3625,7 @@ mb_patmatchrange(char *range, wchar_t ch, int zmb_ind, wint_t *indptr, int *mtp)
 		    return 1;
 		break;
 	    case PP_PRINT:
-		if (iswprint(ch))
+		if (WC_ISPRINT(ch))
 		    return 1;
 		break;
 	    case PP_PUNCT:
diff --git a/Src/prompt.c b/Src/prompt.c
index e811f8e42..a8bb10329 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -395,11 +395,11 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
 			test = 1;
 		    break;
 		case 'v':
-		    if (arrlen(psvar) >= arg)
+		    if (arrlen_ge(psvar, arg))
 			test = 1;
 		    break;
 		case 'V':
-		    if (arrlen(psvar) >= arg) {
+		    if (psvar && *psvar && arrlen_ge(psvar, arg)) {
 			if (*psvar[(arg ? arg : 1) - 1])
 			    test = 1;
 		    }
@@ -491,8 +491,10 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
 		if (!arg)
 		    arg++;
 		queue_signals();
-		if (!(hostnam = getsparam("HOST")))
+		if (!(hostnam = getsparam("HOST"))) {
+		    unqueue_signals();
 		    break;
+		}
 		if (arg < 0) {
 		    for (ss = hostnam + strlen(hostnam); ss > hostnam; ss--)
 			if (ss[-1] == '.' && !++arg)
@@ -523,8 +525,6 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
 		break;
 	    case 'b':
 		txtchangeset(txtchangep, TXTNOBOLDFACE, TXTBOLDFACE);
-		txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT);
-		txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE);
 		txtunset(TXTBOLDFACE);
 		tsetcap(TCALLATTRSOFF, TSC_PROMPT|TSC_DIRTY);
 		break;
@@ -542,7 +542,8 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
 		arg = parsecolorchar(arg, 1);
 		if (arg >= 0 && !(arg & TXTNOFGCOLOUR)) {
 		    txtchangeset(txtchangep, arg & TXT_ATTR_FG_ON_MASK,
-				 TXTNOFGCOLOUR);
+				 TXTNOFGCOLOUR | TXT_ATTR_FG_COL_MASK);
+		    txtunset(TXT_ATTR_FG_COL_MASK);
 		    txtset(arg & TXT_ATTR_FG_ON_MASK);
 		    set_colour_attribute(arg, COL_SEQ_FG, TSC_PROMPT);
 		    break;
@@ -557,7 +558,8 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
 		arg = parsecolorchar(arg, 0);
 		if (arg >= 0 && !(arg & TXTNOBGCOLOUR)) {
 		    txtchangeset(txtchangep, arg & TXT_ATTR_BG_ON_MASK,
-				 TXTNOBGCOLOUR);
+				 TXTNOBGCOLOUR | TXT_ATTR_BG_COL_MASK);
+		    txtunset(TXT_ATTR_BG_COL_MASK);
 		    txtset(arg & TXT_ATTR_BG_ON_MASK);
 		    set_colour_attribute(arg, COL_SEQ_BG, TSC_PROMPT);
 		    break;
@@ -736,7 +738,7 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
 		    arg = 1;
 		else if (arg < 0)
 		    arg += arrlen(psvar) + 1;
-		if (arg > 0 && arrlen(psvar) >= arg)
+		if (arg > 0 && arrlen_ge(psvar, arg))
 		    stradd(psvar[arg - 1]);
 		break;
 	    case 'E':
@@ -918,6 +920,7 @@ addbufspc(int need)
 	if(need & 255)
 	    need = (need | 255) + 1;
 	bv->buf = realloc(bv->buf, bv->bufspc += need);
+	memset(bv->buf + bv->bufspc - need, 0, need);
 	bv->bp = bv->buf + bo;
 	if(bo1 != -1)
 	    bv->bp1 = bv->buf + bo1;
@@ -1041,6 +1044,10 @@ tsetcap(int cap, int flags)
 		tsetcap(TCSTANDOUTBEG, flags);
 	    if (txtisset(TXTUNDERLINE))
 		tsetcap(TCUNDERLINEBEG, flags);
+	    if (txtisset(TXTFGCOLOUR))
+		set_colour_attribute(txtattrmask, COL_SEQ_FG, TSC_PROMPT);
+	    if (txtisset(TXTBGCOLOUR))
+		set_colour_attribute(txtattrmask, COL_SEQ_BG, TSC_PROMPT);
 	}
     }
 }
diff --git a/Src/signals.c b/Src/signals.c
index 2eefc07de..cad40f4eb 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -55,6 +55,11 @@ mod_export Eprog siglists[VSIGCOUNT];
 /**/
 mod_export int nsigtrapped;
 
+/* Running an exit trap? */
+
+/**/
+int in_exit_trap;
+
 /*
  * Flag that exit trap has been set in POSIX mode.
  * The setter's expectation is therefore that it is run
@@ -72,6 +77,10 @@ mod_export int queueing_enabled, queue_front, queue_rear;
 mod_export int signal_queue[MAX_QUEUE_SIZE];
 /**/
 mod_export sigset_t signal_mask_queue[MAX_QUEUE_SIZE];
+#ifdef DEBUG
+/**/
+mod_export int queue_in;
+#endif
 
 /* Variables used by trap queueing */
 
@@ -518,6 +527,11 @@ wait_for_processes(void)
 #if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE)
 		struct timezone dummy_tz;
 		gettimeofday(&pn->endtime, &dummy_tz);
+#ifdef WIFCONTINUED
+		if (WIFCONTINUED(status))
+		    pn->status = SP_RUNNING;
+		else
+#endif
 		pn->status = status;
 		pn->ti = ru;
 #else
@@ -646,6 +660,7 @@ zhandler(int sig)
 		inerrflush();
 		check_cursh_sig(SIGINT);
             }
+	    lastval = 128 + SIGINT;
         }
         break;
 
@@ -723,7 +738,7 @@ killjb(Job jn, int sig)
 {
     Process pn;
     int err = 0;
- 
+
     if (jobbing) {
         if (jn->stat & STAT_SUPERJOB) {
             if (sig == SIGCONT) {
@@ -731,11 +746,21 @@ killjb(Job jn, int sig)
                     if (killpg(pn->pid, sig) == -1)
 			if (kill(pn->pid, sig) == -1 && errno != ESRCH)
 			    err = -1;
- 
+
+		/*
+		 * Note this does not kill the last process,
+		 * which is assumed to be the one controlling the
+		 * subjob, i.e. the forked zsh that was originally
+		 * list_pipe_pid...
+		 */
                 for (pn = jn->procs; pn->next; pn = pn->next)
                     if (kill(pn->pid, sig) == -1 && errno != ESRCH)
 			err = -1;
 
+		/*
+		 * ...we only continue that once the external processes
+		 * currently associated with the subjob are finished.
+		 */
 		if (!jobtab[jn->other].procs && pn)
 		    if (kill(pn->pid, sig) == -1 && errno != ESRCH)
 			err = -1;
@@ -744,7 +769,7 @@ killjb(Job jn, int sig)
             }
             if (killpg(jobtab[jn->other].gleader, sig) == -1 && errno != ESRCH)
 		err = -1;
-		
+
 	    if (killpg(jn->gleader, sig) == -1 && errno != ESRCH)
 		err = -1;
 
@@ -796,7 +821,11 @@ dosavetrap(int sig, int level)
 	    newshf->node.nam = ztrdup(shf->node.nam);
 	    newshf->node.flags = shf->node.flags;
 	    newshf->funcdef = dupeprog(shf->funcdef, 0);
-	    newshf->filename = ztrdup(shf->filename);
+	    if (shf->node.flags & PM_LOADDIR) {
+		dircache_set(&newshf->filename, shf->filename);
+	    } else {
+		newshf->filename = ztrdup(shf->filename);
+	    }
 	    if (shf->sticky) {
 		newshf->sticky = sticky_emulation_dup(shf->sticky, 0);
 	    } else
@@ -1411,7 +1440,13 @@ dotrap(int sig)
 
     dont_queue_signals();
 
+    if (sig == SIGEXIT)
+	++in_exit_trap;
+
     dotrapargs(sig, sigtrapped+sig, funcprog);
 
+    if (sig == SIGEXIT)
+	--in_exit_trap;
+
     restore_queue_signals(q);
 }
diff --git a/Src/signals.h b/Src/signals.h
index d68096891..1904f4326 100644
--- a/Src/signals.h
+++ b/Src/signals.h
@@ -82,8 +82,6 @@
 
 #define MAX_QUEUE_SIZE 128
 
-#define queue_signals()    (queueing_enabled++)
-
 #define run_queued_signals() do { \
     while (queue_front != queue_rear) {      /* while signals in queue */ \
 	sigset_t oset; \
@@ -94,12 +92,35 @@
     } \
 } while (0)
 
+#ifdef DEBUG
+
+#define queue_signals()    (queue_in++, queueing_enabled++)
+
 #define unqueue_signals()  do { \
     DPUTS(!queueing_enabled, "BUG: unqueue_signals called but not queueing"); \
+    --queue_in; \
     if (!--queueing_enabled) run_queued_signals(); \
 } while (0)
 
-#define queue_signal_level() queueing_enabled
+#define dont_queue_signals() do { \
+    queue_in = queueing_enabled; \
+    queueing_enabled = 0; \
+    run_queued_signals(); \
+} while (0)
+
+#define restore_queue_signals(q) do { \
+    DPUTS2(queueing_enabled && queue_in != q, \
+         "BUG: q = %d != queue_in = %d", q, queue_in); \
+    queue_in = (queueing_enabled = (q)); \
+} while (0)
+
+#else /* !DEBUG */
+
+#define queue_signals()    (queueing_enabled++)
+
+#define unqueue_signals()  do { \
+    if (!--queueing_enabled) run_queued_signals(); \
+} while (0)
 
 #define dont_queue_signals() do { \
     queueing_enabled = 0; \
@@ -108,6 +129,10 @@
 
 #define restore_queue_signals(q) (queueing_enabled = (q))
 
+#endif /* DEBUG */
+
+#define queue_signal_level() queueing_enabled
+
 #ifdef BSD_SIGNALS
 #define signal_block(S) sigblock(S)
 #else
diff --git a/Src/string.c b/Src/string.c
index 04e7446c9..9e14ef949 100644
--- a/Src/string.c
+++ b/Src/string.c
@@ -41,6 +41,37 @@ dupstring(const char *s)
     return t;
 }
 
+/* Duplicate string on heap when length is known */
+
+/**/
+mod_export char *
+dupstring_wlen(const char *s, unsigned len)
+{
+    char *t;
+
+    if (!s)
+	return NULL;
+    t = (char *) zhalloc(len + 1);
+    memcpy(t, s, len);
+    t[len] = '\0';
+    return t;
+}
+
+/* Duplicate string on heap, returning length of string */
+
+/**/
+mod_export char *
+dupstring_glen(const char *s, unsigned *len_ret)
+{
+    char *t;
+
+    if (!s)
+	return NULL;
+    t = (char *) zhalloc((*len_ret = strlen((char *)s)) + 1);
+    strcpy(t, s);
+    return t;
+}
+
 /**/
 mod_export char *
 ztrdup(const char *s)
diff --git a/Src/subst.c b/Src/subst.c
index 7081d467d..a73ac4737 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -415,7 +415,9 @@ globlist(LinkList list, int nountok)
 	next = nextnode(node);
 	zglob(list, node, nountok);
     }
-    if (badcshglob == 1)
+    if (noerrs)
+	badcshglob = 0;
+    else if (badcshglob == 1)
 	zerr("no match");
 }
 
@@ -448,7 +450,7 @@ singsub(char **s)
  * NULL to use IFS).  The return value is true iff the expansion resulted
  * in an empty list.
  *
- * *ms_flags is set to bits in the enum above as neeed.
+ * *ms_flags is set to bits in the enum above as needed.
  */
 
 /**/
@@ -483,6 +485,8 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep,
 	for ( ; *x; x += l) {
 	    int rawc = -1;
 	    convchar_t c;
+	    if (*x == Dash)
+		*x = '-';
 	    if (itok(STOUC(*x))) {
 		/* token, can't be separator, must be single byte */
 		rawc = *x;
@@ -624,22 +628,22 @@ filesub(char **namptr, int assign)
 char *
 equalsubstr(char *str, int assign, int nomatch)
 {
-    char *pp, *cnam, *cmdstr, *ret;
+    char *pp, *cnam, *cmdstr;
 
     for (pp = str; !isend2(*pp); pp++)
 	;
     cmdstr = dupstrpfx(str, pp-str);
     untokenize(cmdstr);
     remnulargs(cmdstr);
-    if (!(cnam = findcmd(cmdstr, 1))) {
+    if (!(cnam = findcmd(cmdstr, 1, 0))) {
 	if (nomatch)
 	    zerr("%s not found", cmdstr);
 	return NULL;
     }
-    ret = dupstring(cnam);
     if (*pp)
-	ret = dyncat(ret, pp);
-    return ret;
+	return dyncat(cnam, pp);
+    else
+	return cnam;		/* already duplicated */
 }
 
 /**/
@@ -652,6 +656,8 @@ filesubstr(char **namptr, int assign)
 	char *ptr, *tmp, *res, *ptr2;
 	int val;
 
+	if (str[1] == Dash)
+	    str[1] = '-';
 	val = zstrtol(str + 1, &ptr, 10);
 	if (isend(str[1])) {   /* ~ */
 	    *namptr = dyncat(home ? home : "", str + 1);
@@ -688,19 +694,19 @@ filesubstr(char **namptr, int assign)
 	    *namptr = dyncat(ds, ptr);
 	    return 1;
 	} else if ((ptr = itype_end(str+1, IUSER, 0)) != str+1) {   /* ~foo */
-	    char *hom, save;
+	    char *untok, *hom;
 
-	    save = *ptr;
-	    if (!isend(save))
+	    if (!isend(*ptr))
 		return 0;
-	    *ptr = 0;
-	    if (!(hom = getnameddir(++str))) {
-		if (isset(NOMATCH))
-		    zerr("no such user or named directory: %s", str);
-		*ptr = save;
+	    untok = dupstring(++str);
+	    untok[ptr-str] = 0;
+	    untokenize(untok);
+
+	    if (!(hom = getnameddir(untok))) {
+		if (isset(NOMATCH) && isset(EXECOPT))
+		    zerr("no such user or named directory: %s", untok);
 		return 0;
 	    }
-	    *ptr = save;
 	    *namptr = dyncat(hom, ptr);
 	    return 1;
 	}
@@ -1766,7 +1772,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
      */
     c = *s;
     if (itype_end(s, IIDENT, 1) == s && *s != '#' && c != Pound &&
-	c != '-' && c != '!' && c != '$' && c != String && c != Qstring &&
+	!IS_DASH(c) &&
+	c != '!' && c != '$' && c != String && c != Qstring &&
 	c != '?' && c != Quest &&
 	c != '*' && c != Star && c != '@' && c != '{' &&
 	c != Inbrace && c != '=' && c != Equals && c != Hat &&
@@ -1895,13 +1902,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 		    if (quotetype == QT_DOLLARS ||
 			quotetype == QT_BACKSLASH_PATTERN)
 			goto flagerr;
-		    if (s[1] == '-' || s[1] == '+') {
+		    if (IS_DASH(s[1]) || s[1] == '+') {
 			if (quotemod)
 			    goto flagerr;
 			s++;
 			quotemod = 1;
-			quotetype = (*s == '-') ? QT_SINGLE_OPTIONAL :
-			    QT_QUOTEDZPUTS;
+			quotetype = (*s == '+') ? QT_QUOTEDZPUTS :
+			    QT_SINGLE_OPTIONAL;
 		    } else {
 			if (quotetype == QT_SINGLE_OPTIONAL) {
 			    /* extra q's after '-' not allowed */
@@ -2208,9 +2215,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 		     * properly in the first place we wouldn't
 		     * have this nonsense.
 		     */
-		    || ((cc == '#' || cc == Pound) &&
-			s[2] == Outbrace)
-		    || cc == '-' || (cc == ':' && s[2] == '-')
+		    || ((cc == '#' || cc == Pound) && s[2] == Outbrace)
+		    || IS_DASH(cc)
+		    || (cc == ':' && IS_DASH(s[2]))
 		    || (isstring(cc) && (s[2] == Inbrace || s[2] == Inpar)))) {
 	    getlen = 1 + whichlen, s++;
 	    /*
@@ -2372,7 +2379,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	 * This is the inner handling for the case referred to above
 	 * where we have something like ${${(P)name}...}.
 	 *
-	 * Treat this as as a normal value here; all transformations on
+	 * Treat this as a normal value here; all transformations on
 	 * result are in outer instance.
 	 */
 	aspar = 0;
@@ -2558,12 +2565,17 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 		    if (v->pm->node.flags & PM_CHECKLEN)
 			assert(tmplen == arrlen(v->pm->gsu.a->getfn(v->pm)));
 		} else
-		    tmplen = arrlen(v->pm->gsu.a->getfn(v->pm));
+		    tmplen = -1;
 
-		if (v->start < 0)
+		if (v->start < 0) {
+		    tmplen = arrlen(v->pm->gsu.a->getfn(v->pm));
 		    v->start += tmplen + ((v->flags & VALFLAG_INV) ? 1 : 0);
-		if (!(v->flags & VALFLAG_INV) &&
-		    (v->start >= tmplen || v->start < 0))
+		}
+		if (!(v->flags & VALFLAG_INV))
+		    if (v->start < 0 ||
+			(tmplen != -1
+			 ? v->start >= tmplen
+			 : arrlen_le(v->pm->gsu.a->getfn(v->pm), v->start)))
 		    vunset = 1;
 	    }
 	    if (!vunset) {
@@ -2604,14 +2616,17 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
      * Again, this duplicates tests for characters we're about to
      * examine properly later on.
      */
-    if (inbrace &&
-	(c = *s) != '-' && c != '+' && c != ':' && c != '%'  && c != '/' &&
-	c != '=' && c != Equals &&
-	c != '#' && c != Pound &&
-	c != '?' && c != Quest &&
-	c != '}' && c != Outbrace) {
-	zerr("bad substitution");
-	return NULL;
+    if (inbrace) {
+	c = *s;
+	if (!IS_DASH(c) &&
+	    c != '+' && c != ':' && c != '%'  && c != '/' &&
+	    c != '=' && c != Equals &&
+	    c != '#' && c != Pound &&
+	    c != '?' && c != Quest &&
+	    c != '}' && c != Outbrace) {
+	    zerr("bad substitution");
+	    return NULL;
+	}
     }
     /*
      * Join arrays up if we're in quotes and there isn't some
@@ -2689,8 +2704,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
     /* Check for ${..?..} or ${..=..} or one of those. *
      * Only works if the name is in braces.            */
 
-    if (inbrace && ((c = *s) == '-' ||
-		    c == '+' ||
+    if (inbrace && ((c = *s) == '+' ||
+		    IS_DASH(c) ||
 		    c == ':' ||	/* i.e. a doubled colon */
 		    c == '=' || c == Equals ||
 		    c == '%' ||
@@ -2801,6 +2816,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	    vunset = 1;
 	/* Fall Through! */
 	case '-':
+	case Dash:
 	    if (vunset) {
 		int split_flags;
 		val = dupstring(s);
@@ -2900,6 +2916,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 			    aval = paramvalarr(pm->gsu.h->getfn(pm), hkeys|hvals);
 		    } else
 			setaparam(idbeg, a);
+		    isarr = 1;
+		    arrasg = 0;
 		} else {
 		    untokenize(val);
 		    setsparam(idbeg, ztrdup(val));
@@ -2924,6 +2942,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
                 if (isset(EXECOPT)) {
                     *idend = '\0';
                     zerr("%s: %s", idbeg, *s ? s : "parameter not set");
+                    /*
+                     * In interactive shell we need to return to
+                     * top-level prompt --- don't clear this error
+                     * after handling a command as we do with
+                     * most errors.
+                     */
+                    errflag |= ERRFLAG_HARD;
                     if (!interact) {
                         if (mypid == getpid()) {
                             /*
@@ -3060,7 +3085,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 		ziplen = 1;
 		ziplen = !!sval;
 	    }
-	    if (!isarr) aval = mkarray(val);
+	    if (!isarr) {
+		aval = mkarray(val);
+		isarr = 1;
+	    }
 	    if (zip) {
 		char **out;
 		int alen, outlen, i = 0;
@@ -3082,7 +3110,6 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 		    out[i*2] = NULL;
 		    aval = out;
 		    copied = 1;
-		    isarr = 1;
 		}
 	    } else {
 		if (unset(UNSET)) {
@@ -3460,13 +3487,26 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
      * exception is that ${name:-word} and ${name:+word} will have already
      * done any requested splitting of the word value with quoting preserved.
      */
-    if (ssub || (spbreak && isarr >= 0) || spsep || sep) {
+    if (ssub || spbreak || spsep || sep) {
+	int force_split = !ssub && (spbreak || spsep);
 	if (isarr) {
-	    val = sepjoin(aval, sep, 1);
-	    isarr = 0;
-	    ms_flags = 0;
+	    /* sep non-null here means F or j flag, force join */
+	    if (nojoin == 0 || sep) {
+		val = sepjoin(aval, sep, 1);
+		isarr = 0;
+	    } else if (force_split &&
+		       (spsep || nojoin == 2 || (!ifs && isarr < 0))) {
+		/* Hack to simulate splitting individual elements:
+		 * forced joining as previously determined, or
+		 * join on what we later use to forcibly split
+		 */
+		val = sepjoin(aval, (nojoin == 1 ? NULL : spsep), 1);
+		isarr = 0;
+	    }
+	    if (!isarr)
+		ms_flags = 0;
 	}
-	if (!ssub && (spbreak || spsep)) {
+	if (force_split && !isarr) {
 	    aval = sepsplit(val, spsep, 0, 1);
 	    if (!aval || !aval[0])
 		val = dupstring("");
@@ -3533,7 +3573,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	}
 	/*
 	 * TODO:  It would be really quite nice to abstract the
-	 * isarr and !issarr code into a function which gets
+	 * isarr and !isarr code into a function which gets
 	 * passed a pointer to a function with the effect of
 	 * the promptexpand bit.  Then we could use this for
 	 * a lot of stuff and bury val/aval/isarr inside a structure
@@ -3609,20 +3649,20 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 		    char *tmp;
 
 		    for (; *ap; ap++) {
-			tmp = quotestring(*ap, NULL, quotetype);
+			tmp = quotestring(*ap, quotetype);
 			sl = strlen(tmp);
 			*ap = (char *) zhalloc(pre + sl + post + 1);
 			strcpy((*ap) + pre, tmp);
 			if (pre)
 			    ap[0][pre - 1] = ap[0][pre + sl] =
 				(quotetype != QT_DOUBLE ? '\'' : '"');
-			ap[0][pre + sl + 1] = '\0';
+			ap[0][pre + sl + post] = '\0';
 			if (quotetype == QT_DOLLARS)
 			  ap[0][0] = '$';
 		    }
 		} else
 		    for (; *ap; ap++)
-			*ap = quotestring(*ap, NULL, QT_BACKSLASH_SHOWNULL);
+			*ap = quotestring(*ap, QT_BACKSLASH_SHOWNULL);
 	    } else {
 		int one = noerrs, oef = errflag, haserr = 0;
 
@@ -3652,18 +3692,18 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 		} else if (quotetype > QT_BACKSLASH) {
 		    int sl;
 		    char *tmp;
-		    tmp = quotestring(val, NULL, quotetype);
+		    tmp = quotestring(val, quotetype);
 		    sl = strlen(tmp);
-		    val = (char *) zhalloc(pre + sl + 2);
+		    val = (char *) zhalloc(pre + sl + post + 1);
 		    strcpy(val + pre, tmp);
 		    if (pre)
 			val[pre - 1] = val[pre + sl] =
 			    (quotetype != QT_DOUBLE ? '\'' : '"');
-		    val[pre + sl + 1] = '\0';
+		    val[pre + sl + post] = '\0';
 		    if (quotetype == QT_DOLLARS)
 		      val[0] = '$';
 		} else
-		    val = quotestring(val, NULL, QT_BACKSLASH_SHOWNULL);
+		    val = quotestring(val, QT_BACKSLASH_SHOWNULL);
 	    } else {
 		int one = noerrs, oef = errflag, haserr;
 
@@ -3750,6 +3790,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
      * as a scalar.)
      */
 
+    if (isarr && ssub) {
+	/* prefork() wants a scalar, so join no matter what else */
+	val = sepjoin(aval, NULL, 1);
+	isarr = 0;
+	l->list.flags &= ~LF_ARRAY;
+    }
+
     /*
      * If a multsub result had whitespace at the start and we're
      * splitting and there's a previous string, now's the time to do so.
@@ -3763,6 +3810,16 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 	insertlinknode(l, n, dupstring(fstr)); /* appended, no incnode */
 	*fstr = '\0';
     }
+    if (arrasg && !isarr) {
+	/*
+	 * Caller requested this be forced to an array even if scalar.
+	 * Any point in distinguishing arrasg == 2 (assoc array) here?
+	 */
+	l->list.flags |= LF_ARRAY;
+	aval = hmkarray(val);
+	isarr = 1;
+	DPUTS(!val, "value is NULL in paramsubst, empty array");
+    }
     if (isarr) {
 	char *x;
 	char *y;
@@ -4028,6 +4085,19 @@ arithsubst(char *a, char **bptr, char *rest)
     return t;
 }
 
+/* This function implements colon modifiers.
+ *
+ * STR is an in/out parameter.  On entry it is the string (e.g., path)
+ * to modified.  On return it is the modified path.
+ *
+ * PTR is an in/out parameter.  On entry it contains the string of colon
+ * modifiers.  On return it points past the last recognised modifier.
+ *
+ * Example:
+ *     ENTRY:   *str is "."   *ptr is ":AN"
+ *     RETURN:  *str is "/home/foobar" (equal to $PWD)   *ptr points to the "N"
+ */
+
 /**/
 void
 modify(char **str, char **ptr)
@@ -4063,6 +4133,7 @@ modify(char **str, char **ptr)
 	    case 'u':
 	    case 'q':
 	    case 'Q':
+	    case 'P':
 		c = **ptr;
 		break;
 
@@ -4254,7 +4325,7 @@ modify(char **str, char **ptr)
 			    subst(&copy, hsubl, hsubr, gbal);
 			break;
 		    case 'q':
-			copy = quotestring(copy, NULL, QT_BACKSLASH_SHOWNULL);
+			copy = quotestring(copy, QT_BACKSLASH_SHOWNULL);
 			break;
 		    case 'Q':
 			{
@@ -4269,6 +4340,16 @@ modify(char **str, char **ptr)
 			    untokenize(copy);
 			}
 			break;
+		    case 'P':
+			if (*copy != '/') {
+			    char *here = zgetcwd();
+			    if (here[strlen(here)-1] != '/')
+				copy = zhtricat(metafy(here, -1, META_HEAPDUP), "/", copy);
+			    else
+				copy = dyncat(here, copy);
+			}
+			copy = xsymlink(copy, 1);
+			break;
 		    }
 		    tc = *tt;
 		    *tt = '\0';
@@ -4330,7 +4411,7 @@ modify(char **str, char **ptr)
 			subst(str, hsubl, hsubr, gbal);
 		    break;
 		case 'q':
-		    *str = quotestring(*str, NULL, QT_BACKSLASH);
+		    *str = quotestring(*str, QT_BACKSLASH);
 		    break;
 		case 'Q':
 		    {
@@ -4345,6 +4426,16 @@ modify(char **str, char **ptr)
 			untokenize(*str);
 		    }
 		    break;
+		case 'P':
+		    if (**str != '/') {
+			char *here = zgetcwd();
+			if (here[strlen(here)-1] != '/')
+			    *str = zhtricat(metafy(here, -1, META_HEAPDUP), "/", *str);
+			else
+			    *str = dyncat(here, *str);
+		    }
+		    *str = xsymlink(*str, 1);
+		    break;
 		}
 	    }
 	    if (rec < 0) {
diff --git a/Src/text.c b/Src/text.c
index cf6d3eb42..3658b1bc6 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -46,7 +46,7 @@ int text_expand_tabs;
  * et seq. in zsh.h.
  */
 static const char *cond_binary_ops[] = {
-    "=", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
+    "=", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
     "-ne", "-lt", "-gt", "-le", "-ge", "=~", NULL
 };
 
@@ -934,6 +934,7 @@ gettext2(Estate state)
 			    taddstr(" ");
 			    taddstr(ecgetstr(state, EC_NODUP, NULL));
 			    if (ctype == COND_STREQ ||
+				ctype == COND_STRDEQ ||
 				ctype == COND_STRNEQ)
 				state->pc++;
 			} else {
@@ -1068,11 +1069,11 @@ getredirs(LinkList redirs)
 		     */
 		    if (!has_token(f->name)) {
 			taddchr('\'');
-			taddstr(quotestring(f->name, NULL, QT_SINGLE));
+			taddstr(quotestring(f->name, QT_SINGLE));
 			taddchr('\'');
 		    } else {
 			taddchr('"');
-			taddstr(quotestring(f->name, NULL, QT_DOUBLE));
+			taddstr(quotestring(f->name, QT_DOUBLE));
 			taddchr('"');
 		    }
 		    if (sav)
diff --git a/Src/utils.c b/Src/utils.c
index 28e78c149..579ab3be8 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -84,7 +84,15 @@ set_widearray(char *mb_array, Widechar_array wca)
 
 	mb_charinit();
 	while (*mb_array) {
-	    int mblen = mb_metacharlenconv(mb_array, &wci);
+	    int mblen;
+
+	    if (STOUC(*mb_array) <= 0x7f) {
+		mb_array++;
+		*wcptr++ = (wchar_t)*mb_array;
+		continue;
+	    }
+
+	    mblen = mb_metacharlenconv(mb_array, &wci);
 
 	    if (!mblen)
 		break;
@@ -621,7 +629,7 @@ wcs_nicechar_sel(wchar_t c, size_t *widthp, char **swidep, int quotable)
     }
 
     s = buf;
-    if (!iswprint(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
+    if (!WC_ISPRINT(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
 	if (c == 0x7f) {
 	    if (quotable) {
 		*s++ = '\\';
@@ -726,7 +734,7 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep)
 /**/
 mod_export int is_wcs_nicechar(wchar_t c)
 {
-    if (!iswprint(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
+    if (!WC_ISPRINT(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) {
 	if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20)
 	    return 1;
 	if (c >= 0x80) {
@@ -801,9 +809,9 @@ findpwd(char *s)
     char *t;
 
     if (*s == '/')
-	return xsymlink(s);
+	return xsymlink(s, 0);
     s = tricat((pwd[1]) ? pwd : "", "/", s);
-    t = xsymlink(s);
+    t = xsymlink(s, 0);
     zsfree(s);
     return t;
 }
@@ -837,7 +845,7 @@ ispwd(char *s)
     return 0;
 }
 
-static char xbuf[PATH_MAX*2];
+static char xbuf[PATH_MAX*2+1];
 
 /**/
 static char **
@@ -876,9 +884,9 @@ static int
 xsymlinks(char *s, int full)
 {
     char **pp, **opp;
-    char xbuf2[PATH_MAX*3], xbuf3[PATH_MAX*2];
+    char xbuf2[PATH_MAX*3+1], xbuf3[PATH_MAX*2+1];
     int t0, ret = 0;
-    zulong xbuflen = strlen(xbuf);
+    zulong xbuflen = strlen(xbuf), pplen;
 
     opp = pp = slashsplit(s);
     for (; xbuflen < sizeof(xbuf) && *pp && ret >= 0; pp++) {
@@ -899,10 +907,18 @@ xsymlinks(char *s, int full)
 	    xbuflen--;
 	    continue;
 	}
-	sprintf(xbuf2, "%s/%s", xbuf, *pp);
+	/* Includes null byte. */
+	pplen = strlen(*pp) + 1;
+	if (xbuflen + pplen + 1 > sizeof(xbuf2)) {
+	    *xbuf = 0;
+	    ret = -1;
+	    break;
+	}
+	memcpy(xbuf2, xbuf, xbuflen);
+	xbuf2[xbuflen] = '/';
+	memcpy(xbuf2 + xbuflen + 1, *pp, pplen);
 	t0 = readlink(unmeta(xbuf2), xbuf3, PATH_MAX);
 	if (t0 == -1) {
-	    zulong pplen = strlen(*pp) + 1;
 	    if ((xbuflen += pplen) < sizeof(xbuf)) {
 		strcat(xbuf, "/");
 		strcat(xbuf, *pp);
@@ -969,11 +985,13 @@ xsymlinks(char *s, int full)
 /*
  * expand symlinks in s, and remove other weird things:
  * note that this always expands symlinks.
+ *
+ * 'heap' indicates whether to malloc() or allocate on the heap.
  */
 
 /**/
 char *
-xsymlink(char *s)
+xsymlink(char *s, int heap)
 {
     if (*s != '/')
 	return NULL;
@@ -981,8 +999,8 @@ xsymlink(char *s)
     if (xsymlinks(s + 1, 1) < 0)
 	zwarn("path expansion failed, using root directory");
     if (!*xbuf)
-	return ztrdup("/");
-    return ztrdup(xbuf);
+	return heap ? dupstring("/") : ztrdup("/");
+    return heap ? dupstring(xbuf) : ztrdup(xbuf);
 }
 
 /**/
@@ -993,7 +1011,7 @@ print_if_link(char *s, int all)
 	*xbuf = '\0';
 	if (all) {
 	    char *start = s + 1;
-	    char xbuflink[PATH_MAX];
+	    char xbuflink[PATH_MAX+1];
 	    for (;;) {
 		if (xsymlinks(start, 0) > 0) {
 		    printf(" -> ");
@@ -1045,9 +1063,9 @@ substnamedir(char *s)
     Nameddir d = finddir(s);
 
     if (!d)
-	return quotestring(s, NULL, QT_BACKSLASH);
+	return quotestring(s, QT_BACKSLASH);
     return zhtricat("~", d->node.nam, quotestring(s + strlen(d->dir),
-						  NULL, QT_BACKSLASH));
+						  QT_BACKSLASH));
 }
 
 
@@ -1130,7 +1148,7 @@ finddir(char *s)
 	if(homenode.diff==1)
 	    homenode.diff = 0;
 	if(!finddir_full)
-	    finddir_full = zalloc(ffsz = PATH_MAX);
+	    finddir_full = zalloc(ffsz = PATH_MAX+1);
 	finddir_full[0] = 0;
 	return finddir_last = NULL;
     }
@@ -1157,7 +1175,7 @@ finddir(char *s)
     scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 0);
 
     ares = subst_string_by_hook("zsh_directory_name", "d", finddir_full);
-    if (ares && arrlen(ares) >= 2 &&
+    if (ares && arrlen_ge(ares, 2) &&
 	(len = (int)zstrtol(ares[1], NULL, 10)) > finddir_best) {
 	/* better duplicate this string since it's come from REPLY */
 	finddir_last = (Nameddir)hcalloc(sizeof(struct nameddir));
@@ -1220,13 +1238,13 @@ adduserdir(char *s, char *t, int flags, int always)
 	 * named directory, since these are sometimes used for
 	 * special purposes.
 	 */
-	nd->dir = ztrdup(t);
+	nd->dir = metafy(t, -1, META_DUP);
     } else
-	nd->dir = ztrduppfx(t, eptr - t);
+	nd->dir = metafy(t, eptr - t, META_DUP);
     /* The variables PWD and OLDPWD are not to be displayed as ~PWD etc. */
     if (!strcmp(s, "PWD") || !strcmp(s, "OLDPWD"))
 	nd->node.flags |= ND_NOABBREV;
-    nameddirtab->addnode(nameddirtab, ztrdup(s), nd);
+    nameddirtab->addnode(nameddirtab, metafy(s, -1, META_DUP), nd);
 }
 
 /* Get a named directory: this function can cause a directory name *
@@ -1260,7 +1278,7 @@ getnameddir(char *name)
 	/* Retrieve an entry from the password table/database for this user. */
 	struct passwd *pw;
 	if ((pw = getpwnam(name))) {
-	    char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir)
+	    char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir, 0)
 		: ztrdup(pw->pw_dir);
 	    if (dir) {
 		adduserdir(name, dir, ND_USERNAME, 1);
@@ -1634,7 +1652,7 @@ checkmailpath(char **s)
 	} else if (S_ISDIR(st.st_mode)) {
 	    LinkList l;
 	    DIR *lock = opendir(unmeta(*s));
-	    char buf[PATH_MAX * 2], **arr, **ap;
+	    char buf[PATH_MAX * 2 + 1], **arr, **ap;
 	    int ct = 1;
 
 	    if (lock) {
@@ -2162,6 +2180,7 @@ gettempfile(const char *prefix, int use_heap, char **tempname)
 #if HAVE_MKSTEMP
     char *suffix = prefix ? ".XXXXXX" : "XXXXXX";
 
+    queue_signals();
     if (!prefix && !(prefix = getsparam("TMPPREFIX")))
 	prefix = DEFAULT_TMPPREFIX;
     if (use_heap)
@@ -2178,6 +2197,7 @@ gettempfile(const char *prefix, int use_heap, char **tempname)
 #else
     int failures = 0;
 
+    queue_signals();
     do {
 	if (!(fn = gettempname(prefix, use_heap))) {
 	    fd = -1;
@@ -2191,6 +2211,8 @@ gettempfile(const char *prefix, int use_heap, char **tempname)
     } while (errno == EEXIST && ++failures < 16);
 #endif
     *tempname = fn;
+
+    unqueue_signals();
     return fd;
 }
 
@@ -2280,6 +2302,46 @@ arrlen(char **s)
     return count;
 }
 
+/* Return TRUE iff arrlen(s) >= lower_bound, but more efficiently. */
+
+/**/
+mod_export char
+arrlen_ge(char **s, unsigned lower_bound)
+{
+    while (lower_bound--)
+	if (!*s++)
+	    return 0 /* FALSE */;
+
+    return 1 /* TRUE */;
+}
+
+/* Return TRUE iff arrlen(s) > lower_bound, but more efficiently. */
+
+/**/
+mod_export char
+arrlen_gt(char **s, unsigned lower_bound)
+{
+    return arrlen_ge(s, 1+lower_bound);
+}
+
+/* Return TRUE iff arrlen(s) <= upper_bound, but more efficiently. */
+
+/**/
+mod_export char
+arrlen_le(char **s, unsigned upper_bound)
+{
+    return arrlen_lt(s, 1+upper_bound);
+}
+
+/* Return TRUE iff arrlen(s) < upper_bound, but more efficiently. */
+
+/**/
+mod_export char
+arrlen_lt(char **s, unsigned upper_bound)
+{
+    return !arrlen_ge(s, upper_bound);
+}
+
 /* Skip over a balanced pair of parenthesis. */
 
 /**/
@@ -2322,7 +2384,7 @@ zstrtol_underscore(const char *s, char **t, int base, int underscore)
     while (inblank(*s))
 	s++;
 
-    if ((neg = (*s == '-')))
+    if ((neg = IS_DASH(*s)))
 	s++;
     else if (*s == '+')
 	s++;
@@ -2534,7 +2596,7 @@ read_poll(int fd, int *readchar, int polltty, zlong microseconds)
 #endif
 #endif
 
-    if (fd >= 0 && ret < 0) {
+    if (fd >= 0 && ret < 0 && !errflag) {
 	/*
 	 * Final attempt: set non-blocking read and try to read a character.
 	 * Praise Bill, this works under Cygwin (nothing else seems to).
@@ -2890,9 +2952,7 @@ mod_export void
 spckword(char **s, int hist, int cmd, int ask)
 {
     char *t, *correct_ignore;
-    int x;
     char ic = '\0';
-    int ne;
     int preflen = 0;
     int autocd = cmd && isset(AUTOCD) && strcmp(*s, ".") && strcmp(*s, "..");
 
@@ -2961,6 +3021,7 @@ spckword(char **s, int hist, int cmd, int ask)
     } else {
 	guess = *s;
 	if (*guess == Tilde || *guess == String) {
+	    int ne;
 	    ic = *guess;
 	    if (!*++t)
 		return;
@@ -3005,6 +3066,7 @@ spckword(char **s, int hist, int cmd, int ask)
     if (errflag)
 	return;
     if (best && (int)strlen(best) > 1 && strcmp(best, guess)) {
+	int x;
 	if (ic) {
 	    char *u;
 	    if (preflen) {
@@ -3034,14 +3096,14 @@ spckword(char **s, int hist, int cmd, int ask)
 		free(pptbuf);
 		fflush(shout);
 		zbeep();
-		x = getquery("nyae \t", 0);
+		x = getquery("nyae", 0);
 		if (cmd && x == 'n')
 		    pathchecked = path;
 	    } else
 		x = 'n';
 	} else
 	    x = 'y';
-	if (x == 'y' || x == ' ' || x == '\t') {
+	if (x == 'y') {
 	    *s = dupstring(best);
 	    if (hist)
 		hwrep(best);
@@ -3905,7 +3967,7 @@ inittyptab(void)
 #endif
     /* typtab['.'] |= IIDENT; */ /* Allow '.' in variable names - broken */
     typtab['_'] = IIDENT | IUSER;
-    typtab['-'] = typtab['.'] = IUSER;
+    typtab['-'] = typtab['.'] = typtab[STOUC(Dash)] = IUSER;
     typtab[' '] |= IBLANK | INBLANK;
     typtab['\t'] |= IBLANK | INBLANK;
     typtab['\n'] |= INBLANK;
@@ -4103,42 +4165,50 @@ itype_end(const char *ptr, int itype, int once)
 	(itype != IIDENT || !isset(POSIXIDENTIFIERS))) {
 	mb_charinit();
 	while (*ptr) {
-	    wint_t wc;
-	    int len = mb_metacharlenconv(ptr, &wc);
-
-	    if (!len)
-		break;
-
-	    if (wc == WEOF) {
-		/* invalid, treat as single character */
-		int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr);
-		/* in this case non-ASCII characters can't match */
-		if (chr > 127 || !zistype(chr,itype))
-		    break;
-	    } else if (len == 1 && isascii(*ptr)) {
-		/* ASCII: can't be metafied, use standard test */
+	    int len;
+	    if (itok(*ptr)) {
+		/* Not untokenised yet --- can happen in raw command line */
+		len = 1;
 		if (!zistype(*ptr,itype))
 		    break;
 	    } else {
-		/*
-		 * Valid non-ASCII character.
-		 */
-		switch (itype) {
-		case IWORD:
-		    if (!iswalnum(wc) &&
-			!wmemchr(wordchars_wide.chars, wc,
-				 wordchars_wide.len))
-			return (char *)ptr;
-		    break;
+		wint_t wc;
+		len = mb_metacharlenconv(ptr, &wc);
 
-		case ISEP:
-		    if (!wmemchr(ifs_wide.chars, wc, ifs_wide.len))
-			return (char *)ptr;
+		if (!len)
 		    break;
 
-		default:
-		    if (!iswalnum(wc))
-			return (char *)ptr;
+		if (wc == WEOF) {
+		    /* invalid, treat as single character */
+		    int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr);
+		    /* in this case non-ASCII characters can't match */
+		    if (chr > 127 || !zistype(chr,itype))
+			break;
+		} else if (len == 1 && isascii(*ptr)) {
+		    /* ASCII: can't be metafied, use standard test */
+		    if (!zistype(*ptr,itype))
+			break;
+		} else {
+		    /*
+		     * Valid non-ASCII character.
+		     */
+		    switch (itype) {
+		    case IWORD:
+			if (!iswalnum(wc) &&
+			    !wmemchr(wordchars_wide.chars, wc,
+				     wordchars_wide.len))
+			    return (char *)ptr;
+			break;
+
+		    case ISEP:
+			if (!wmemchr(ifs_wide.chars, wc, ifs_wide.len))
+			    return (char *)ptr;
+			break;
+
+		    default:
+			if (!iswalnum(wc))
+			    return (char *)ptr;
+		    }
 		}
 	    }
 	    ptr += len;
@@ -4183,6 +4253,32 @@ arrdup(char **s)
     return y;
 }
 
+/* Duplicate at most max elements of the array s with heap memory */
+
+/**/
+mod_export char **
+arrdup_max(char **s, unsigned max)
+{
+    char **x, **y, **send;
+    int len = 0;
+
+    if (max)
+	len = arrlen(s);
+
+    /* Limit has sense only if not equal to len */
+    if (max > len)
+        max = len;
+
+    y = x = (char **) zhalloc(sizeof(char *) * (max + 1));
+
+    send = s + max;
+    while (s < send)
+	*x++ = dupstring(*s++);
+    *x = NULL;
+
+    return y;
+}
+
 /**/
 mod_export char **
 zarrdup(char **s)
@@ -4700,6 +4796,41 @@ unmeta(const char *file_name)
 }
 
 /*
+ * Unmetafy just one character and store the number of bytes it occupied.
+ */
+/**/
+mod_export convchar_t
+unmeta_one(const char *in, int *sz)
+{
+    convchar_t wc;
+    int newsz;
+#ifdef MULTIBYTE_SUPPORT
+    mbstate_t wstate;
+#endif
+
+    if (!sz)
+	sz = &newsz;
+    *sz = 0;
+
+    if (!in || !*in)
+	return 0;
+
+#ifdef MULTIBYTE_SUPPORT
+    memset(&wstate, 0, sizeof(wstate));
+    *sz = mb_metacharlenconv_r(in, &wc, &wstate);
+#else
+    if (in[0] == Meta) {
+      *sz = 2;
+      wc = STOUC(in[1] ^ 32);
+    } else {
+      *sz = 1;
+      wc = STOUC(in[0]);
+    }
+#endif
+    return wc;
+}
+
+/*
  * Unmetafy and compare two strings, comparing unsigned character values.
  * "a\0" sorts after "a".
  *
@@ -5040,8 +5171,10 @@ mb_niceformat(const char *s, FILE *stream, char **outstrp, int flags)
 	    cnt = 1;
 	    /* FALL THROUGH */
 	default:
-	    if (c == L'\'' && (flags & NICEFLAG_QUOTE))
+	    if (c == L'\'' && (flags & NICEFLAG_QUOTE)) {
 		fmt = "\\'";
+		newl = 2;
+	    }
 	    else
 		fmt = wcs_nicechar_sel(c, &newl, NULL, flags & NICEFLAG_QUOTE);
 	    break;
@@ -5176,6 +5309,12 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp)
     const char *ptr;
     wchar_t wc;
 
+    if (STOUC(*s) <= 0x7f) {
+	if (wcp)
+	    *wcp = (wint_t)*s;
+	return 1;
+    }
+
     for (ptr = s; *ptr; ) {
 	if (*ptr == Meta) {
 	    inchar = *++ptr ^ 32;
@@ -5228,7 +5367,7 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp)
 mod_export int
 mb_metacharlenconv(const char *s, wint_t *wcp)
 {
-    if (!isset(MULTIBYTE)) {
+    if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) {
 	/* treat as single byte, possibly metafied */
 	if (wcp)
 	    *wcp = (wint_t)(*s == Meta ? s[1] ^ 32 : *s);
@@ -5275,7 +5414,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
     char inchar, *laststart;
     size_t ret;
     wchar_t wc;
-    int num, num_in_char;
+    int num, num_in_char, complete;
 
     if (!isset(MULTIBYTE))
 	return ztrlen(ptr);
@@ -5283,6 +5422,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
     laststart = ptr;
     ret = MB_INVALID;
     num = num_in_char = 0;
+    complete = 1;
 
     memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
     while (*ptr && !(eptr && ptr >= eptr)) {
@@ -5291,6 +5431,18 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
 	else
 	    inchar = *ptr;
 	ptr++;
+
+	if (complete && STOUC(inchar) <= STOUC(0x7f)) {
+	    /*
+	     * We rely on 7-bit US-ASCII as a subset, so skip
+	     * multibyte handling if we have such a character.
+	     */
+	    num++;
+	    laststart = ptr;
+	    num_in_char = 0;
+	    continue;
+	}
+
 	ret = mbrtowc(&wc, &inchar, 1, &mb_shiftstate);
 
 	if (ret == MB_INCOMPLETE) {
@@ -5310,6 +5462,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
 	     * so we don't count characters twice.
 	     */
 	    num_in_char++;
+	    complete = 0;
 	} else {
 	    if (ret == MB_INVALID) {
 		/* Reset, treat as single character */
@@ -5332,6 +5485,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr)
 		num++;
 	    laststart = ptr;
 	    num_in_char = 0;
+	    complete = 1;
 	}
     }
 
@@ -5354,6 +5508,12 @@ mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp)
     const char *ptr;
     wchar_t wc;
 
+    if (slen && STOUC(*s) <= 0x7f) {
+	if (wcp)
+	    *wcp = (wint_t)*s;
+	return 1;
+    }
+
     for (ptr = s; slen;  ) {
 	inchar = *ptr;
 	ptr++;
@@ -5389,7 +5549,7 @@ mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp)
 mod_export int
 mb_charlenconv(const char *s, int slen, wint_t *wcp)
 {
-    if (!isset(MULTIBYTE)) {
+    if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) {
 	if (wcp)
 	    *wcp = (wint_t)*s;
 	return 1;
@@ -5605,10 +5765,6 @@ addunprintable(char *v, const char *u, const char *uend)
 /*
  * Quote the string s and return the result as a string from the heap.
  *
- * If e is non-zero, the
- * pointer it points to may point to a position in s and in e the position
- * of the corresponding character in the quoted string is returned.
- * 
  * The last argument is a QT_ value defined in zsh.h other than QT_NONE.
  *
  * Most quote styles other than backslash assume the quotes are to
@@ -5621,13 +5777,13 @@ addunprintable(char *v, const char *u, const char *uend)
 
 /**/
 mod_export char *
-quotestring(const char *s, char **e, int instring)
+quotestring(const char *s, int instring)
 {
     const char *u;
     char *v;
     int alloclen;
     char *buf;
-    int sf = 0, shownull = 0;
+    int shownull = 0;
     /*
      * quotesub is used with QT_SINGLE_OPTIONAL.
      * quotesub = 0:  mechanism not active
@@ -5698,10 +5854,6 @@ quotestring(const char *s, char **e, int instring)
 	while (*u) {
 	    uend = u + MB_METACHARLENCONV(u, &cc);
 
-	    if (e && !sf && *e <= u) {
-		*e = v;
-		sf = 1;
-	    }
 	    if (
 #ifdef MULTIBYTE_SUPPORT
 		cc != WEOF &&
@@ -5728,11 +5880,6 @@ quotestring(const char *s, char **e, int instring)
 	}
     } else if (instring == QT_BACKSLASH_PATTERN) {
 	while (*u) {
-	    if (e && !sf && *e == u) {
-		*e = v;
-		sf = 1;
-	    }
-
 	    if (ipattern(*u))
 		*v++ = '\\';
 	    *v++ = *u++;
@@ -5751,8 +5898,6 @@ quotestring(const char *s, char **e, int instring)
 	 */
 	while (*u) {
 	    int dobackslash = 0;
-	    if (e && *e == u)
-		*e = v, sf = 1;
 	    if (*u == Tick || *u == Qtick) {
 		char c = *u++;
 
@@ -5940,10 +6085,6 @@ quotestring(const char *s, char **e, int instring)
 	*v++ = '\'';
     *v = '\0';
 
-    if (e && *e == u)
-	*e = v, sf = 1;
-    DPUTS(e && !sf, "BUG: Wild pointer *e in quotestring()");
-
     v = dupstring(buf);
     zfree(buf, alloclen);
     return v;
@@ -6020,7 +6161,9 @@ quotedzputs(char const *s, FILE *stream)
 	} else
 	    *ptr++ = '\'';
 	while(*s) {
-	    if (*s == Meta)
+	    if (*s == Dash)
+		c = '-';
+	    else if (*s == Meta)
 		c = *++s ^ 32;
 	    else
 		c = *s;
@@ -6057,7 +6200,9 @@ quotedzputs(char const *s, FILE *stream)
     } else {
 	/* use Bourne-style quoting, avoiding empty quoted strings */
 	while (*s) {
-	    if (*s == Meta)
+	    if (*s == Dash)
+		c = '-';
+	    else if (*s == Meta)
 		c = *++s ^ 32;
 	    else
 		c = *s;
@@ -6818,7 +6963,7 @@ strsfx(char *s, char *t)
 static int
 upchdir(int n)
 {
-    char buf[PATH_MAX];
+    char buf[PATH_MAX+1];
     char *s;
     int err = -1;
 
diff --git a/Src/watch.c b/Src/watch.c
index c804913ad..cd7dc643d 100644
--- a/Src/watch.c
+++ b/Src/watch.c
@@ -87,6 +87,15 @@
 
 #if !defined(WATCH_STRUCT_UTMP) && defined(HAVE_STRUCT_UTMPX) && defined(REAL_UTMPX_FILE)
 # define WATCH_STRUCT_UTMP struct utmpx
+# if defined(HAVE_SETUTXENT) && defined(HAVE_GETUTXENT) && defined(HAVE_ENDUTXENT)
+#  define setutent setutxent
+#  define getutent getutxent
+#  define endutent endutxent
+#  ifndef HAVE_GETUTENT
+#   define HAVE_GETUTENT 1
+#  endif
+# endif
+
 /*
  * In utmpx, the ut_name field is replaced by ut_user.
  * Howver, on some systems ut_name may already be defined this
@@ -141,9 +150,9 @@ char const * const default_watchfmt = DEFAULT_WATCHFMT;
 #  define WATCH_WTMP_FILE "/dev/null"
 # endif
 
-static int wtabsz;
-static WATCH_STRUCT_UTMP *wtab;
-static time_t lastutmpcheck;
+static int wtabsz = 0;
+static WATCH_STRUCT_UTMP *wtab = NULL;
+static time_t lastutmpcheck = 0;
 
 /* get the time of login/logout for WATCH */
 
@@ -473,34 +482,60 @@ ucmp(WATCH_STRUCT_UTMP *u, WATCH_STRUCT_UTMP *v)
 /* initialize the user List */
 
 /**/
-static void
-readwtab(void)
+static int
+readwtab(WATCH_STRUCT_UTMP **head, int initial_sz)
 {
     WATCH_STRUCT_UTMP *uptr;
-    int wtabmax = 32;
+    int wtabmax = initial_sz < 2 ? 32 : initial_sz;
+    int sz = 0;
+# ifdef HAVE_GETUTENT
+    WATCH_STRUCT_UTMP *tmp;
+# else
     FILE *in;
+# endif
 
-    wtabsz = 0;
+    uptr = *head = (WATCH_STRUCT_UTMP *)
+	zalloc(wtabmax * sizeof(WATCH_STRUCT_UTMP));
+# ifdef HAVE_GETUTENT
+    setutent();
+    while ((tmp = getutent()) != NULL) {
+	memcpy(uptr, tmp, sizeof (WATCH_STRUCT_UTMP));
+# else
     if (!(in = fopen(WATCH_UTMP_FILE, "r")))
-	return;
-    uptr = wtab = (WATCH_STRUCT_UTMP *)zalloc(wtabmax * sizeof(WATCH_STRUCT_UTMP));
-    while (fread(uptr, sizeof(WATCH_STRUCT_UTMP), 1, in))
+	return 0;
+    while (fread(uptr, sizeof(WATCH_STRUCT_UTMP), 1, in)) {
+# endif
 # ifdef USER_PROCESS
-	if   (uptr->ut_type == USER_PROCESS)
+	if (uptr->ut_type == USER_PROCESS)
 # else /* !USER_PROCESS */
-	if   (uptr->ut_name[0])
+	if (uptr->ut_name[0])
 # endif /* !USER_PROCESS */
 	{
 	    uptr++;
-	    if (++wtabsz == wtabmax)
-		uptr = (wtab = (WATCH_STRUCT_UTMP *)realloc((void *) wtab, (wtabmax *= 2) *
-						      sizeof(WATCH_STRUCT_UTMP))) + wtabsz;
+	    if (++sz == wtabmax) {
+		uptr = (WATCH_STRUCT_UTMP *)
+		    realloc(*head, (wtabmax *= 2) * sizeof(WATCH_STRUCT_UTMP));
+		if (uptr == NULL) {
+		    /* memory pressure - so stop consuming and use, what we have
+		     * Other option is to exit() here, as zmalloc does on error */
+		    sz--;
+		    break;
+		}
+		*head = uptr;
+		uptr += sz;
+	    }
 	}
+    }
+# ifdef HAVE_GETUTENT
+    endutent();
+# else
     fclose(in);
+# endif
 
-    if (wtabsz)
-	qsort((void *) wtab, wtabsz, sizeof(WATCH_STRUCT_UTMP),
+    if (sz)
+	qsort((void *) *head, sz, sizeof(WATCH_STRUCT_UTMP),
 	           (int (*) _((const void *, const void *)))ucmp);
+    return sz;
 }
 
 /* Check for login/logout events; executed before *
@@ -510,55 +545,28 @@ readwtab(void)
 void
 dowatch(void)
 {
-    FILE *in;
     WATCH_STRUCT_UTMP *utab, *uptr, *wptr;
     struct stat st;
     char **s;
     char *fmt;
-    int utabsz = 0, utabmax = wtabsz + 4;
-    int uct, wct;
+    int utabsz, uct, wct;
 
     s = watch;
 
     holdintr();
-    if (!wtab) {
-	readwtab();
-	noholdintr();
-	return;
-    }
+    if (!wtab)
+	wtabsz = readwtab(&wtab, 32);
     if ((stat(WATCH_UTMP_FILE, &st) == -1) || (st.st_mtime <= lastutmpcheck)) {
 	noholdintr();
 	return;
     }
     lastutmpcheck = st.st_mtime;
-    uptr = utab = (WATCH_STRUCT_UTMP *) zalloc(utabmax * sizeof(WATCH_STRUCT_UTMP));
-
-    if (!(in = fopen(WATCH_UTMP_FILE, "r"))) {
-	free(utab);
-	noholdintr();
-	return;
-    }
-    while (fread(uptr, sizeof *uptr, 1, in))
-# ifdef USER_PROCESS
-	if (uptr->ut_type == USER_PROCESS)
-# else /* !USER_PROCESS */
-	if (uptr->ut_name[0])
-# endif /* !USER_PROCESS */
-	{
-	    uptr++;
-	    if (++utabsz == utabmax)
-		uptr = (utab = (WATCH_STRUCT_UTMP *)realloc((void *) utab, (utabmax *= 2) *
-						      sizeof(WATCH_STRUCT_UTMP))) + utabsz;
-	}
-    fclose(in);
+    utabsz = readwtab(&utab, wtabsz + 4);
     noholdintr();
     if (errflag) {
 	free(utab);
 	return;
     }
-    if (utabsz)
-	qsort((void *) utab, utabsz, sizeof(WATCH_STRUCT_UTMP),
-	           (int (*) _((const void *, const void *)))ucmp);
 
     wct = wtabsz;
     uct = utabsz;
@@ -571,13 +579,14 @@ dowatch(void)
     queue_signals();
     if (!(fmt = getsparam_u("WATCHFMT")))
 	fmt = DEFAULT_WATCHFMT;
-    while ((uct || wct) && !errflag)
+    while ((uct || wct) && !errflag) {
 	if (!uct || (wct && ucmp(uptr, wptr) > 0))
 	    wct--, watchlog(0, wptr++, s, fmt);
 	else if (!wct || (uct && ucmp(uptr, wptr) < 0))
 	    uct--, watchlog(1, uptr++, s, fmt);
 	else
 	    uptr++, wptr++, wct--, uct--;
+    }
     unqueue_signals();
     free(wtab);
     wtab = utab;
diff --git a/Src/wcwidth9.h b/Src/wcwidth9.h
new file mode 100644
index 000000000..448f548e9
--- /dev/null
+++ b/Src/wcwidth9.h
@@ -0,0 +1,1325 @@
+#ifndef WCWIDTH9_H
+#define WCWIDTH9_H
+
+#include <stdlib.h>
+#include <stdbool.h>
+
+struct wcwidth9_interval {
+  long first;
+  long last;
+};
+
+static const struct wcwidth9_interval wcwidth9_private[] = {
+  {0x00e000, 0x00f8ff},
+  {0x0f0000, 0x0ffffd},
+  {0x100000, 0x10fffd},
+};
+
+static const struct wcwidth9_interval wcwidth9_nonprint[] = {
+  {0x0000, 0x001f},
+  {0x007f, 0x009f},
+  {0x00ad, 0x00ad},
+  {0x070f, 0x070f},
+  {0x180b, 0x180e},
+  {0x200b, 0x200f},
+  {0x2028, 0x2029},
+  {0x202a, 0x202e},
+  {0x206a, 0x206f},
+  {0xd800, 0xdfff},
+  {0xfeff, 0xfeff},
+  {0xfff9, 0xfffb},
+  {0xfffe, 0xffff},
+};
+
+static const struct wcwidth9_interval wcwidth9_combining[] = {
+  {0x0300, 0x036f},
+  {0x0483, 0x0489},
+  {0x0591, 0x05bd},
+  {0x05bf, 0x05bf},
+  {0x05c1, 0x05c2},
+  {0x05c4, 0x05c5},
+  {0x05c7, 0x05c7},
+  {0x0610, 0x061a},
+  {0x064b, 0x065f},
+  {0x0670, 0x0670},
+  {0x06d6, 0x06dc},
+  {0x06df, 0x06e4},
+  {0x06e7, 0x06e8},
+  {0x06ea, 0x06ed},
+  {0x0711, 0x0711},
+  {0x0730, 0x074a},
+  {0x07a6, 0x07b0},
+  {0x07eb, 0x07f3},
+  {0x0816, 0x0819},
+  {0x081b, 0x0823},
+  {0x0825, 0x0827},
+  {0x0829, 0x082d},
+  {0x0859, 0x085b},
+  {0x08d4, 0x08e1},
+  {0x08e3, 0x0903},
+  {0x093a, 0x093c},
+  {0x093e, 0x094f},
+  {0x0951, 0x0957},
+  {0x0962, 0x0963},
+  {0x0981, 0x0983},
+  {0x09bc, 0x09bc},
+  {0x09be, 0x09c4},
+  {0x09c7, 0x09c8},
+  {0x09cb, 0x09cd},
+  {0x09d7, 0x09d7},
+  {0x09e2, 0x09e3},
+  {0x0a01, 0x0a03},
+  {0x0a3c, 0x0a3c},
+  {0x0a3e, 0x0a42},
+  {0x0a47, 0x0a48},
+  {0x0a4b, 0x0a4d},
+  {0x0a51, 0x0a51},
+  {0x0a70, 0x0a71},
+  {0x0a75, 0x0a75},
+  {0x0a81, 0x0a83},
+  {0x0abc, 0x0abc},
+  {0x0abe, 0x0ac5},
+  {0x0ac7, 0x0ac9},
+  {0x0acb, 0x0acd},
+  {0x0ae2, 0x0ae3},
+  {0x0b01, 0x0b03},
+  {0x0b3c, 0x0b3c},
+  {0x0b3e, 0x0b44},
+  {0x0b47, 0x0b48},
+  {0x0b4b, 0x0b4d},
+  {0x0b56, 0x0b57},
+  {0x0b62, 0x0b63},
+  {0x0b82, 0x0b82},
+  {0x0bbe, 0x0bc2},
+  {0x0bc6, 0x0bc8},
+  {0x0bca, 0x0bcd},
+  {0x0bd7, 0x0bd7},
+  {0x0c00, 0x0c03},
+  {0x0c3e, 0x0c44},
+  {0x0c46, 0x0c48},
+  {0x0c4a, 0x0c4d},
+  {0x0c55, 0x0c56},
+  {0x0c62, 0x0c63},
+  {0x0c81, 0x0c83},
+  {0x0cbc, 0x0cbc},
+  {0x0cbe, 0x0cc4},
+  {0x0cc6, 0x0cc8},
+  {0x0cca, 0x0ccd},
+  {0x0cd5, 0x0cd6},
+  {0x0ce2, 0x0ce3},
+  {0x0d01, 0x0d03},
+  {0x0d3e, 0x0d44},
+  {0x0d46, 0x0d48},
+  {0x0d4a, 0x0d4d},
+  {0x0d57, 0x0d57},
+  {0x0d62, 0x0d63},
+  {0x0d82, 0x0d83},
+  {0x0dca, 0x0dca},
+  {0x0dcf, 0x0dd4},
+  {0x0dd6, 0x0dd6},
+  {0x0dd8, 0x0ddf},
+  {0x0df2, 0x0df3},
+  {0x0e31, 0x0e31},
+  {0x0e34, 0x0e3a},
+  {0x0e47, 0x0e4e},
+  {0x0eb1, 0x0eb1},
+  {0x0eb4, 0x0eb9},
+  {0x0ebb, 0x0ebc},
+  {0x0ec8, 0x0ecd},
+  {0x0f18, 0x0f19},
+  {0x0f35, 0x0f35},
+  {0x0f37, 0x0f37},
+  {0x0f39, 0x0f39},
+  {0x0f3e, 0x0f3f},
+  {0x0f71, 0x0f84},
+  {0x0f86, 0x0f87},
+  {0x0f8d, 0x0f97},
+  {0x0f99, 0x0fbc},
+  {0x0fc6, 0x0fc6},
+  {0x102b, 0x103e},
+  {0x1056, 0x1059},
+  {0x105e, 0x1060},
+  {0x1062, 0x1064},
+  {0x1067, 0x106d},
+  {0x1071, 0x1074},
+  {0x1082, 0x108d},
+  {0x108f, 0x108f},
+  {0x109a, 0x109d},
+  {0x135d, 0x135f},
+  {0x1712, 0x1714},
+  {0x1732, 0x1734},
+  {0x1752, 0x1753},
+  {0x1772, 0x1773},
+  {0x17b4, 0x17d3},
+  {0x17dd, 0x17dd},
+  {0x180b, 0x180d},
+  {0x1885, 0x1886},
+  {0x18a9, 0x18a9},
+  {0x1920, 0x192b},
+  {0x1930, 0x193b},
+  {0x1a17, 0x1a1b},
+  {0x1a55, 0x1a5e},
+  {0x1a60, 0x1a7c},
+  {0x1a7f, 0x1a7f},
+  {0x1ab0, 0x1abe},
+  {0x1b00, 0x1b04},
+  {0x1b34, 0x1b44},
+  {0x1b6b, 0x1b73},
+  {0x1b80, 0x1b82},
+  {0x1ba1, 0x1bad},
+  {0x1be6, 0x1bf3},
+  {0x1c24, 0x1c37},
+  {0x1cd0, 0x1cd2},
+  {0x1cd4, 0x1ce8},
+  {0x1ced, 0x1ced},
+  {0x1cf2, 0x1cf4},
+  {0x1cf8, 0x1cf9},
+  {0x1dc0, 0x1df5},
+  {0x1dfb, 0x1dff},
+  {0x20d0, 0x20f0},
+  {0x2cef, 0x2cf1},
+  {0x2d7f, 0x2d7f},
+  {0x2de0, 0x2dff},
+  {0x302a, 0x302f},
+  {0x3099, 0x309a},
+  {0xa66f, 0xa672},
+  {0xa674, 0xa67d},
+  {0xa69e, 0xa69f},
+  {0xa6f0, 0xa6f1},
+  {0xa802, 0xa802},
+  {0xa806, 0xa806},
+  {0xa80b, 0xa80b},
+  {0xa823, 0xa827},
+  {0xa880, 0xa881},
+  {0xa8b4, 0xa8c5},
+  {0xa8e0, 0xa8f1},
+  {0xa926, 0xa92d},
+  {0xa947, 0xa953},
+  {0xa980, 0xa983},
+  {0xa9b3, 0xa9c0},
+  {0xa9e5, 0xa9e5},
+  {0xaa29, 0xaa36},
+  {0xaa43, 0xaa43},
+  {0xaa4c, 0xaa4d},
+  {0xaa7b, 0xaa7d},
+  {0xaab0, 0xaab0},
+  {0xaab2, 0xaab4},
+  {0xaab7, 0xaab8},
+  {0xaabe, 0xaabf},
+  {0xaac1, 0xaac1},
+  {0xaaeb, 0xaaef},
+  {0xaaf5, 0xaaf6},
+  {0xabe3, 0xabea},
+  {0xabec, 0xabed},
+  {0xfb1e, 0xfb1e},
+  {0xfe00, 0xfe0f},
+  {0xfe20, 0xfe2f},
+  {0x101fd, 0x101fd},
+  {0x102e0, 0x102e0},
+  {0x10376, 0x1037a},
+  {0x10a01, 0x10a03},
+  {0x10a05, 0x10a06},
+  {0x10a0c, 0x10a0f},
+  {0x10a38, 0x10a3a},
+  {0x10a3f, 0x10a3f},
+  {0x10ae5, 0x10ae6},
+  {0x11000, 0x11002},
+  {0x11038, 0x11046},
+  {0x1107f, 0x11082},
+  {0x110b0, 0x110ba},
+  {0x11100, 0x11102},
+  {0x11127, 0x11134},
+  {0x11173, 0x11173},
+  {0x11180, 0x11182},
+  {0x111b3, 0x111c0},
+  {0x111ca, 0x111cc},
+  {0x1122c, 0x11237},
+  {0x1123e, 0x1123e},
+  {0x112df, 0x112ea},
+  {0x11300, 0x11303},
+  {0x1133c, 0x1133c},
+  {0x1133e, 0x11344},
+  {0x11347, 0x11348},
+  {0x1134b, 0x1134d},
+  {0x11357, 0x11357},
+  {0x11362, 0x11363},
+  {0x11366, 0x1136c},
+  {0x11370, 0x11374},
+  {0x11435, 0x11446},
+  {0x114b0, 0x114c3},
+  {0x115af, 0x115b5},
+  {0x115b8, 0x115c0},
+  {0x115dc, 0x115dd},
+  {0x11630, 0x11640},
+  {0x116ab, 0x116b7},
+  {0x1171d, 0x1172b},
+  {0x11c2f, 0x11c36},
+  {0x11c38, 0x11c3f},
+  {0x11c92, 0x11ca7},
+  {0x11ca9, 0x11cb6},
+  {0x16af0, 0x16af4},
+  {0x16b30, 0x16b36},
+  {0x16f51, 0x16f7e},
+  {0x16f8f, 0x16f92},
+  {0x1bc9d, 0x1bc9e},
+  {0x1d165, 0x1d169},
+  {0x1d16d, 0x1d172},
+  {0x1d17b, 0x1d182},
+  {0x1d185, 0x1d18b},
+  {0x1d1aa, 0x1d1ad},
+  {0x1d242, 0x1d244},
+  {0x1da00, 0x1da36},
+  {0x1da3b, 0x1da6c},
+  {0x1da75, 0x1da75},
+  {0x1da84, 0x1da84},
+  {0x1da9b, 0x1da9f},
+  {0x1daa1, 0x1daaf},
+  {0x1e000, 0x1e006},
+  {0x1e008, 0x1e018},
+  {0x1e01b, 0x1e021},
+  {0x1e023, 0x1e024},
+  {0x1e026, 0x1e02a},
+  {0x1e8d0, 0x1e8d6},
+  {0x1e944, 0x1e94a},
+  {0xe0100, 0xe01ef},
+};
+
+static const struct wcwidth9_interval wcwidth9_doublewidth[] = {
+  {0x1100, 0x115f},
+  {0x231a, 0x231b},
+  {0x2329, 0x232a},
+  {0x23e9, 0x23ec},
+  {0x23f0, 0x23f0},
+  {0x23f3, 0x23f3},
+  {0x25fd, 0x25fe},
+  {0x2614, 0x2615},
+  {0x2648, 0x2653},
+  {0x267f, 0x267f},
+  {0x2693, 0x2693},
+  {0x26a1, 0x26a1},
+  {0x26aa, 0x26ab},
+  {0x26bd, 0x26be},
+  {0x26c4, 0x26c5},
+  {0x26ce, 0x26ce},
+  {0x26d4, 0x26d4},
+  {0x26ea, 0x26ea},
+  {0x26f2, 0x26f3},
+  {0x26f5, 0x26f5},
+  {0x26fa, 0x26fa},
+  {0x26fd, 0x26fd},
+  {0x2705, 0x2705},
+  {0x270a, 0x270b},
+  {0x2728, 0x2728},
+  {0x274c, 0x274c},
+  {0x274e, 0x274e},
+  {0x2753, 0x2755},
+  {0x2757, 0x2757},
+  {0x2795, 0x2797},
+  {0x27b0, 0x27b0},
+  {0x27bf, 0x27bf},
+  {0x2b1b, 0x2b1c},
+  {0x2b50, 0x2b50},
+  {0x2b55, 0x2b55},
+  {0x2e80, 0x2e99},
+  {0x2e9b, 0x2ef3},
+  {0x2f00, 0x2fd5},
+  {0x2ff0, 0x2ffb},
+  {0x3000, 0x303e},
+  {0x3041, 0x3096},
+  {0x3099, 0x30ff},
+  {0x3105, 0x312d},
+  {0x3131, 0x318e},
+  {0x3190, 0x31ba},
+  {0x31c0, 0x31e3},
+  {0x31f0, 0x321e},
+  {0x3220, 0x3247},
+  {0x3250, 0x32fe},
+  {0x3300, 0x4dbf},
+  {0x4e00, 0xa48c},
+  {0xa490, 0xa4c6},
+  {0xa960, 0xa97c},
+  {0xac00, 0xd7a3},
+  {0xf900, 0xfaff},
+  {0xfe10, 0xfe19},
+  {0xfe30, 0xfe52},
+  {0xfe54, 0xfe66},
+  {0xfe68, 0xfe6b},
+  {0xff01, 0xff60},
+  {0xffe0, 0xffe6},
+  {0x16fe0, 0x16fe0},
+  {0x17000, 0x187ec},
+  {0x18800, 0x18af2},
+  {0x1b000, 0x1b001},
+  {0x1f004, 0x1f004},
+  {0x1f0cf, 0x1f0cf},
+  {0x1f18e, 0x1f18e},
+  {0x1f191, 0x1f19a},
+  {0x1f200, 0x1f202},
+  {0x1f210, 0x1f23b},
+  {0x1f240, 0x1f248},
+  {0x1f250, 0x1f251},
+  {0x1f300, 0x1f320},
+  {0x1f32d, 0x1f335},
+  {0x1f337, 0x1f37c},
+  {0x1f37e, 0x1f393},
+  {0x1f3a0, 0x1f3ca},
+  {0x1f3cf, 0x1f3d3},
+  {0x1f3e0, 0x1f3f0},
+  {0x1f3f4, 0x1f3f4},
+  {0x1f3f8, 0x1f43e},
+  {0x1f440, 0x1f440},
+  {0x1f442, 0x1f4fc},
+  {0x1f4ff, 0x1f53d},
+  {0x1f54b, 0x1f54e},
+  {0x1f550, 0x1f567},
+  {0x1f57a, 0x1f57a},
+  {0x1f595, 0x1f596},
+  {0x1f5a4, 0x1f5a4},
+  {0x1f5fb, 0x1f64f},
+  {0x1f680, 0x1f6c5},
+  {0x1f6cc, 0x1f6cc},
+  {0x1f6d0, 0x1f6d2},
+  {0x1f6eb, 0x1f6ec},
+  {0x1f6f4, 0x1f6f6},
+  {0x1f910, 0x1f91e},
+  {0x1f920, 0x1f927},
+  {0x1f930, 0x1f930},
+  {0x1f933, 0x1f93e},
+  {0x1f940, 0x1f94b},
+  {0x1f950, 0x1f95e},
+  {0x1f980, 0x1f991},
+  {0x1f9c0, 0x1f9c0},
+  {0x20000, 0x2fffd},
+  {0x30000, 0x3fffd},
+};
+
+static const struct wcwidth9_interval wcwidth9_ambiguous[] = {
+  {0x00a1, 0x00a1},
+  {0x00a4, 0x00a4},
+  {0x00a7, 0x00a8},
+  {0x00aa, 0x00aa},
+  {0x00ad, 0x00ae},
+  {0x00b0, 0x00b4},
+  {0x00b6, 0x00ba},
+  {0x00bc, 0x00bf},
+  {0x00c6, 0x00c6},
+  {0x00d0, 0x00d0},
+  {0x00d7, 0x00d8},
+  {0x00de, 0x00e1},
+  {0x00e6, 0x00e6},
+  {0x00e8, 0x00ea},
+  {0x00ec, 0x00ed},
+  {0x00f0, 0x00f0},
+  {0x00f2, 0x00f3},
+  {0x00f7, 0x00fa},
+  {0x00fc, 0x00fc},
+  {0x00fe, 0x00fe},
+  {0x0101, 0x0101},
+  {0x0111, 0x0111},
+  {0x0113, 0x0113},
+  {0x011b, 0x011b},
+  {0x0126, 0x0127},
+  {0x012b, 0x012b},
+  {0x0131, 0x0133},
+  {0x0138, 0x0138},
+  {0x013f, 0x0142},
+  {0x0144, 0x0144},
+  {0x0148, 0x014b},
+  {0x014d, 0x014d},
+  {0x0152, 0x0153},
+  {0x0166, 0x0167},
+  {0x016b, 0x016b},
+  {0x01ce, 0x01ce},
+  {0x01d0, 0x01d0},
+  {0x01d2, 0x01d2},
+  {0x01d4, 0x01d4},
+  {0x01d6, 0x01d6},
+  {0x01d8, 0x01d8},
+  {0x01da, 0x01da},
+  {0x01dc, 0x01dc},
+  {0x0251, 0x0251},
+  {0x0261, 0x0261},
+  {0x02c4, 0x02c4},
+  {0x02c7, 0x02c7},
+  {0x02c9, 0x02cb},
+  {0x02cd, 0x02cd},
+  {0x02d0, 0x02d0},
+  {0x02d8, 0x02db},
+  {0x02dd, 0x02dd},
+  {0x02df, 0x02df},
+  {0x0300, 0x036f},
+  {0x0391, 0x03a1},
+  {0x03a3, 0x03a9},
+  {0x03b1, 0x03c1},
+  {0x03c3, 0x03c9},
+  {0x0401, 0x0401},
+  {0x0410, 0x044f},
+  {0x0451, 0x0451},
+  {0x2010, 0x2010},
+  {0x2013, 0x2016},
+  {0x2018, 0x2019},
+  {0x201c, 0x201d},
+  {0x2020, 0x2022},
+  {0x2024, 0x2027},
+  {0x2030, 0x2030},
+  {0x2032, 0x2033},
+  {0x2035, 0x2035},
+  {0x203b, 0x203b},
+  {0x203e, 0x203e},
+  {0x2074, 0x2074},
+  {0x207f, 0x207f},
+  {0x2081, 0x2084},
+  {0x20ac, 0x20ac},
+  {0x2103, 0x2103},
+  {0x2105, 0x2105},
+  {0x2109, 0x2109},
+  {0x2113, 0x2113},
+  {0x2116, 0x2116},
+  {0x2121, 0x2122},
+  {0x2126, 0x2126},
+  {0x212b, 0x212b},
+  {0x2153, 0x2154},
+  {0x215b, 0x215e},
+  {0x2160, 0x216b},
+  {0x2170, 0x2179},
+  {0x2189, 0x2189},
+  {0x2190, 0x2199},
+  {0x21b8, 0x21b9},
+  {0x21d2, 0x21d2},
+  {0x21d4, 0x21d4},
+  {0x21e7, 0x21e7},
+  {0x2200, 0x2200},
+  {0x2202, 0x2203},
+  {0x2207, 0x2208},
+  {0x220b, 0x220b},
+  {0x220f, 0x220f},
+  {0x2211, 0x2211},
+  {0x2215, 0x2215},
+  {0x221a, 0x221a},
+  {0x221d, 0x2220},
+  {0x2223, 0x2223},
+  {0x2225, 0x2225},
+  {0x2227, 0x222c},
+  {0x222e, 0x222e},
+  {0x2234, 0x2237},
+  {0x223c, 0x223d},
+  {0x2248, 0x2248},
+  {0x224c, 0x224c},
+  {0x2252, 0x2252},
+  {0x2260, 0x2261},
+  {0x2264, 0x2267},
+  {0x226a, 0x226b},
+  {0x226e, 0x226f},
+  {0x2282, 0x2283},
+  {0x2286, 0x2287},
+  {0x2295, 0x2295},
+  {0x2299, 0x2299},
+  {0x22a5, 0x22a5},
+  {0x22bf, 0x22bf},
+  {0x2312, 0x2312},
+  {0x2460, 0x24e9},
+  {0x24eb, 0x254b},
+  {0x2550, 0x2573},
+  {0x2580, 0x258f},
+  {0x2592, 0x2595},
+  {0x25a0, 0x25a1},
+  {0x25a3, 0x25a9},
+  {0x25b2, 0x25b3},
+  {0x25b6, 0x25b7},
+  {0x25bc, 0x25bd},
+  {0x25c0, 0x25c1},
+  {0x25c6, 0x25c8},
+  {0x25cb, 0x25cb},
+  {0x25ce, 0x25d1},
+  {0x25e2, 0x25e5},
+  {0x25ef, 0x25ef},
+  {0x2605, 0x2606},
+  {0x2609, 0x2609},
+  {0x260e, 0x260f},
+  {0x261c, 0x261c},
+  {0x261e, 0x261e},
+  {0x2640, 0x2640},
+  {0x2642, 0x2642},
+  {0x2660, 0x2661},
+  {0x2663, 0x2665},
+  {0x2667, 0x266a},
+  {0x266c, 0x266d},
+  {0x266f, 0x266f},
+  {0x269e, 0x269f},
+  {0x26bf, 0x26bf},
+  {0x26c6, 0x26cd},
+  {0x26cf, 0x26d3},
+  {0x26d5, 0x26e1},
+  {0x26e3, 0x26e3},
+  {0x26e8, 0x26e9},
+  {0x26eb, 0x26f1},
+  {0x26f4, 0x26f4},
+  {0x26f6, 0x26f9},
+  {0x26fb, 0x26fc},
+  {0x26fe, 0x26ff},
+  {0x273d, 0x273d},
+  {0x2776, 0x277f},
+  {0x2b56, 0x2b59},
+  {0x3248, 0x324f},
+  {0xe000, 0xf8ff},
+  {0xfe00, 0xfe0f},
+  {0xfffd, 0xfffd},
+  {0x1f100, 0x1f10a},
+  {0x1f110, 0x1f12d},
+  {0x1f130, 0x1f169},
+  {0x1f170, 0x1f18d},
+  {0x1f18f, 0x1f190},
+  {0x1f19b, 0x1f1ac},
+  {0xe0100, 0xe01ef},
+  {0xf0000, 0xffffd},
+  {0x100000, 0x10fffd},
+};
+
+static const struct wcwidth9_interval wcwidth9_emoji_width[] = {
+  {0x1f1e6, 0x1f1ff},
+  {0x1f321, 0x1f321},
+  {0x1f324, 0x1f32c},
+  {0x1f336, 0x1f336},
+  {0x1f37d, 0x1f37d},
+  {0x1f396, 0x1f397},
+  {0x1f399, 0x1f39b},
+  {0x1f39e, 0x1f39f},
+  {0x1f3cb, 0x1f3ce},
+  {0x1f3d4, 0x1f3df},
+  {0x1f3f3, 0x1f3f5},
+  {0x1f3f7, 0x1f3f7},
+  {0x1f43f, 0x1f43f},
+  {0x1f441, 0x1f441},
+  {0x1f4fd, 0x1f4fd},
+  {0x1f549, 0x1f54a},
+  {0x1f56f, 0x1f570},
+  {0x1f573, 0x1f579},
+  {0x1f587, 0x1f587},
+  {0x1f58a, 0x1f58d},
+  {0x1f590, 0x1f590},
+  {0x1f5a5, 0x1f5a5},
+  {0x1f5a8, 0x1f5a8},
+  {0x1f5b1, 0x1f5b2},
+  {0x1f5bc, 0x1f5bc},
+  {0x1f5c2, 0x1f5c4},
+  {0x1f5d1, 0x1f5d3},
+  {0x1f5dc, 0x1f5de},
+  {0x1f5e1, 0x1f5e1},
+  {0x1f5e3, 0x1f5e3},
+  {0x1f5e8, 0x1f5e8},
+  {0x1f5ef, 0x1f5ef},
+  {0x1f5f3, 0x1f5f3},
+  {0x1f5fa, 0x1f5fa},
+  {0x1f6cb, 0x1f6cf},
+  {0x1f6e0, 0x1f6e5},
+  {0x1f6e9, 0x1f6e9},
+  {0x1f6f0, 0x1f6f0},
+  {0x1f6f3, 0x1f6f3},
+};
+
+static const struct wcwidth9_interval wcwidth9_not_assigned[] = {
+  {0x0378, 0x0379},
+  {0x0380, 0x0383},
+  {0x038b, 0x038b},
+  {0x038d, 0x038d},
+  {0x03a2, 0x03a2},
+  {0x0530, 0x0530},
+  {0x0557, 0x0558},
+  {0x0560, 0x0560},
+  {0x0588, 0x0588},
+  {0x058b, 0x058c},
+  {0x0590, 0x0590},
+  {0x05c8, 0x05cf},
+  {0x05eb, 0x05ef},
+  {0x05f5, 0x05ff},
+  {0x061d, 0x061d},
+  {0x070e, 0x070e},
+  {0x074b, 0x074c},
+  {0x07b2, 0x07bf},
+  {0x07fb, 0x07ff},
+  {0x082e, 0x082f},
+  {0x083f, 0x083f},
+  {0x085c, 0x085d},
+  {0x085f, 0x089f},
+  {0x08b5, 0x08b5},
+  {0x08be, 0x08d3},
+  {0x0984, 0x0984},
+  {0x098d, 0x098e},
+  {0x0991, 0x0992},
+  {0x09a9, 0x09a9},
+  {0x09b1, 0x09b1},
+  {0x09b3, 0x09b5},
+  {0x09ba, 0x09bb},
+  {0x09c5, 0x09c6},
+  {0x09c9, 0x09ca},
+  {0x09cf, 0x09d6},
+  {0x09d8, 0x09db},
+  {0x09de, 0x09de},
+  {0x09e4, 0x09e5},
+  {0x09fc, 0x0a00},
+  {0x0a04, 0x0a04},
+  {0x0a0b, 0x0a0e},
+  {0x0a11, 0x0a12},
+  {0x0a29, 0x0a29},
+  {0x0a31, 0x0a31},
+  {0x0a34, 0x0a34},
+  {0x0a37, 0x0a37},
+  {0x0a3a, 0x0a3b},
+  {0x0a3d, 0x0a3d},
+  {0x0a43, 0x0a46},
+  {0x0a49, 0x0a4a},
+  {0x0a4e, 0x0a50},
+  {0x0a52, 0x0a58},
+  {0x0a5d, 0x0a5d},
+  {0x0a5f, 0x0a65},
+  {0x0a76, 0x0a80},
+  {0x0a84, 0x0a84},
+  {0x0a8e, 0x0a8e},
+  {0x0a92, 0x0a92},
+  {0x0aa9, 0x0aa9},
+  {0x0ab1, 0x0ab1},
+  {0x0ab4, 0x0ab4},
+  {0x0aba, 0x0abb},
+  {0x0ac6, 0x0ac6},
+  {0x0aca, 0x0aca},
+  {0x0ace, 0x0acf},
+  {0x0ad1, 0x0adf},
+  {0x0ae4, 0x0ae5},
+  {0x0af2, 0x0af8},
+  {0x0afa, 0x0b00},
+  {0x0b04, 0x0b04},
+  {0x0b0d, 0x0b0e},
+  {0x0b11, 0x0b12},
+  {0x0b29, 0x0b29},
+  {0x0b31, 0x0b31},
+  {0x0b34, 0x0b34},
+  {0x0b3a, 0x0b3b},
+  {0x0b45, 0x0b46},
+  {0x0b49, 0x0b4a},
+  {0x0b4e, 0x0b55},
+  {0x0b58, 0x0b5b},
+  {0x0b5e, 0x0b5e},
+  {0x0b64, 0x0b65},
+  {0x0b78, 0x0b81},
+  {0x0b84, 0x0b84},
+  {0x0b8b, 0x0b8d},
+  {0x0b91, 0x0b91},
+  {0x0b96, 0x0b98},
+  {0x0b9b, 0x0b9b},
+  {0x0b9d, 0x0b9d},
+  {0x0ba0, 0x0ba2},
+  {0x0ba5, 0x0ba7},
+  {0x0bab, 0x0bad},
+  {0x0bba, 0x0bbd},
+  {0x0bc3, 0x0bc5},
+  {0x0bc9, 0x0bc9},
+  {0x0bce, 0x0bcf},
+  {0x0bd1, 0x0bd6},
+  {0x0bd8, 0x0be5},
+  {0x0bfb, 0x0bff},
+  {0x0c04, 0x0c04},
+  {0x0c0d, 0x0c0d},
+  {0x0c11, 0x0c11},
+  {0x0c29, 0x0c29},
+  {0x0c3a, 0x0c3c},
+  {0x0c45, 0x0c45},
+  {0x0c49, 0x0c49},
+  {0x0c4e, 0x0c54},
+  {0x0c57, 0x0c57},
+  {0x0c5b, 0x0c5f},
+  {0x0c64, 0x0c65},
+  {0x0c70, 0x0c77},
+  {0x0c84, 0x0c84},
+  {0x0c8d, 0x0c8d},
+  {0x0c91, 0x0c91},
+  {0x0ca9, 0x0ca9},
+  {0x0cb4, 0x0cb4},
+  {0x0cba, 0x0cbb},
+  {0x0cc5, 0x0cc5},
+  {0x0cc9, 0x0cc9},
+  {0x0cce, 0x0cd4},
+  {0x0cd7, 0x0cdd},
+  {0x0cdf, 0x0cdf},
+  {0x0ce4, 0x0ce5},
+  {0x0cf0, 0x0cf0},
+  {0x0cf3, 0x0d00},
+  {0x0d04, 0x0d04},
+  {0x0d0d, 0x0d0d},
+  {0x0d11, 0x0d11},
+  {0x0d3b, 0x0d3c},
+  {0x0d45, 0x0d45},
+  {0x0d49, 0x0d49},
+  {0x0d50, 0x0d53},
+  {0x0d64, 0x0d65},
+  {0x0d80, 0x0d81},
+  {0x0d84, 0x0d84},
+  {0x0d97, 0x0d99},
+  {0x0db2, 0x0db2},
+  {0x0dbc, 0x0dbc},
+  {0x0dbe, 0x0dbf},
+  {0x0dc7, 0x0dc9},
+  {0x0dcb, 0x0dce},
+  {0x0dd5, 0x0dd5},
+  {0x0dd7, 0x0dd7},
+  {0x0de0, 0x0de5},
+  {0x0df0, 0x0df1},
+  {0x0df5, 0x0e00},
+  {0x0e3b, 0x0e3e},
+  {0x0e5c, 0x0e80},
+  {0x0e83, 0x0e83},
+  {0x0e85, 0x0e86},
+  {0x0e89, 0x0e89},
+  {0x0e8b, 0x0e8c},
+  {0x0e8e, 0x0e93},
+  {0x0e98, 0x0e98},
+  {0x0ea0, 0x0ea0},
+  {0x0ea4, 0x0ea4},
+  {0x0ea6, 0x0ea6},
+  {0x0ea8, 0x0ea9},
+  {0x0eac, 0x0eac},
+  {0x0eba, 0x0eba},
+  {0x0ebe, 0x0ebf},
+  {0x0ec5, 0x0ec5},
+  {0x0ec7, 0x0ec7},
+  {0x0ece, 0x0ecf},
+  {0x0eda, 0x0edb},
+  {0x0ee0, 0x0eff},
+  {0x0f48, 0x0f48},
+  {0x0f6d, 0x0f70},
+  {0x0f98, 0x0f98},
+  {0x0fbd, 0x0fbd},
+  {0x0fcd, 0x0fcd},
+  {0x0fdb, 0x0fff},
+  {0x10c6, 0x10c6},
+  {0x10c8, 0x10cc},
+  {0x10ce, 0x10cf},
+  {0x1249, 0x1249},
+  {0x124e, 0x124f},
+  {0x1257, 0x1257},
+  {0x1259, 0x1259},
+  {0x125e, 0x125f},
+  {0x1289, 0x1289},
+  {0x128e, 0x128f},
+  {0x12b1, 0x12b1},
+  {0x12b6, 0x12b7},
+  {0x12bf, 0x12bf},
+  {0x12c1, 0x12c1},
+  {0x12c6, 0x12c7},
+  {0x12d7, 0x12d7},
+  {0x1311, 0x1311},
+  {0x1316, 0x1317},
+  {0x135b, 0x135c},
+  {0x137d, 0x137f},
+  {0x139a, 0x139f},
+  {0x13f6, 0x13f7},
+  {0x13fe, 0x13ff},
+  {0x169d, 0x169f},
+  {0x16f9, 0x16ff},
+  {0x170d, 0x170d},
+  {0x1715, 0x171f},
+  {0x1737, 0x173f},
+  {0x1754, 0x175f},
+  {0x176d, 0x176d},
+  {0x1771, 0x1771},
+  {0x1774, 0x177f},
+  {0x17de, 0x17df},
+  {0x17ea, 0x17ef},
+  {0x17fa, 0x17ff},
+  {0x180f, 0x180f},
+  {0x181a, 0x181f},
+  {0x1878, 0x187f},
+  {0x18ab, 0x18af},
+  {0x18f6, 0x18ff},
+  {0x191f, 0x191f},
+  {0x192c, 0x192f},
+  {0x193c, 0x193f},
+  {0x1941, 0x1943},
+  {0x196e, 0x196f},
+  {0x1975, 0x197f},
+  {0x19ac, 0x19af},
+  {0x19ca, 0x19cf},
+  {0x19db, 0x19dd},
+  {0x1a1c, 0x1a1d},
+  {0x1a5f, 0x1a5f},
+  {0x1a7d, 0x1a7e},
+  {0x1a8a, 0x1a8f},
+  {0x1a9a, 0x1a9f},
+  {0x1aae, 0x1aaf},
+  {0x1abf, 0x1aff},
+  {0x1b4c, 0x1b4f},
+  {0x1b7d, 0x1b7f},
+  {0x1bf4, 0x1bfb},
+  {0x1c38, 0x1c3a},
+  {0x1c4a, 0x1c4c},
+  {0x1c89, 0x1cbf},
+  {0x1cc8, 0x1ccf},
+  {0x1cf7, 0x1cf7},
+  {0x1cfa, 0x1cff},
+  {0x1df6, 0x1dfa},
+  {0x1f16, 0x1f17},
+  {0x1f1e, 0x1f1f},
+  {0x1f46, 0x1f47},
+  {0x1f4e, 0x1f4f},
+  {0x1f58, 0x1f58},
+  {0x1f5a, 0x1f5a},
+  {0x1f5c, 0x1f5c},
+  {0x1f5e, 0x1f5e},
+  {0x1f7e, 0x1f7f},
+  {0x1fb5, 0x1fb5},
+  {0x1fc5, 0x1fc5},
+  {0x1fd4, 0x1fd5},
+  {0x1fdc, 0x1fdc},
+  {0x1ff0, 0x1ff1},
+  {0x1ff5, 0x1ff5},
+  {0x1fff, 0x1fff},
+  {0x2065, 0x2065},
+  {0x2072, 0x2073},
+  {0x208f, 0x208f},
+  {0x209d, 0x209f},
+  {0x20bf, 0x20cf},
+  {0x20f1, 0x20ff},
+  {0x218c, 0x218f},
+  {0x23ff, 0x23ff},
+  {0x2427, 0x243f},
+  {0x244b, 0x245f},
+  {0x2b74, 0x2b75},
+  {0x2b96, 0x2b97},
+  {0x2bba, 0x2bbc},
+  {0x2bc9, 0x2bc9},
+  {0x2bd2, 0x2beb},
+  {0x2bf0, 0x2bff},
+  {0x2c2f, 0x2c2f},
+  {0x2c5f, 0x2c5f},
+  {0x2cf4, 0x2cf8},
+  {0x2d26, 0x2d26},
+  {0x2d28, 0x2d2c},
+  {0x2d2e, 0x2d2f},
+  {0x2d68, 0x2d6e},
+  {0x2d71, 0x2d7e},
+  {0x2d97, 0x2d9f},
+  {0x2da7, 0x2da7},
+  {0x2daf, 0x2daf},
+  {0x2db7, 0x2db7},
+  {0x2dbf, 0x2dbf},
+  {0x2dc7, 0x2dc7},
+  {0x2dcf, 0x2dcf},
+  {0x2dd7, 0x2dd7},
+  {0x2ddf, 0x2ddf},
+  {0x2e45, 0x2e7f},
+  {0x2e9a, 0x2e9a},
+  {0x2ef4, 0x2eff},
+  {0x2fd6, 0x2fef},
+  {0x2ffc, 0x2fff},
+  {0x3040, 0x3040},
+  {0x3097, 0x3098},
+  {0x3100, 0x3104},
+  {0x312e, 0x3130},
+  {0x318f, 0x318f},
+  {0x31bb, 0x31bf},
+  {0x31e4, 0x31ef},
+  {0x321f, 0x321f},
+  {0x32ff, 0x32ff},
+  {0x4db6, 0x4dbf},
+  {0x9fd6, 0x9fff},
+  {0xa48d, 0xa48f},
+  {0xa4c7, 0xa4cf},
+  {0xa62c, 0xa63f},
+  {0xa6f8, 0xa6ff},
+  {0xa7af, 0xa7af},
+  {0xa7b8, 0xa7f6},
+  {0xa82c, 0xa82f},
+  {0xa83a, 0xa83f},
+  {0xa878, 0xa87f},
+  {0xa8c6, 0xa8cd},
+  {0xa8da, 0xa8df},
+  {0xa8fe, 0xa8ff},
+  {0xa954, 0xa95e},
+  {0xa97d, 0xa97f},
+  {0xa9ce, 0xa9ce},
+  {0xa9da, 0xa9dd},
+  {0xa9ff, 0xa9ff},
+  {0xaa37, 0xaa3f},
+  {0xaa4e, 0xaa4f},
+  {0xaa5a, 0xaa5b},
+  {0xaac3, 0xaada},
+  {0xaaf7, 0xab00},
+  {0xab07, 0xab08},
+  {0xab0f, 0xab10},
+  {0xab17, 0xab1f},
+  {0xab27, 0xab27},
+  {0xab2f, 0xab2f},
+  {0xab66, 0xab6f},
+  {0xabee, 0xabef},
+  {0xabfa, 0xabff},
+  {0xd7a4, 0xd7af},
+  {0xd7c7, 0xd7ca},
+  {0xd7fc, 0xd7ff},
+  {0xfa6e, 0xfa6f},
+  {0xfada, 0xfaff},
+  {0xfb07, 0xfb12},
+  {0xfb18, 0xfb1c},
+  {0xfb37, 0xfb37},
+  {0xfb3d, 0xfb3d},
+  {0xfb3f, 0xfb3f},
+  {0xfb42, 0xfb42},
+  {0xfb45, 0xfb45},
+  {0xfbc2, 0xfbd2},
+  {0xfd40, 0xfd4f},
+  {0xfd90, 0xfd91},
+  {0xfdc8, 0xfdef},
+  {0xfdfe, 0xfdff},
+  {0xfe1a, 0xfe1f},
+  {0xfe53, 0xfe53},
+  {0xfe67, 0xfe67},
+  {0xfe6c, 0xfe6f},
+  {0xfe75, 0xfe75},
+  {0xfefd, 0xfefe},
+  {0xff00, 0xff00},
+  {0xffbf, 0xffc1},
+  {0xffc8, 0xffc9},
+  {0xffd0, 0xffd1},
+  {0xffd8, 0xffd9},
+  {0xffdd, 0xffdf},
+  {0xffe7, 0xffe7},
+  {0xffef, 0xfff8},
+  {0xfffe, 0xffff},
+  {0x1000c, 0x1000c},
+  {0x10027, 0x10027},
+  {0x1003b, 0x1003b},
+  {0x1003e, 0x1003e},
+  {0x1004e, 0x1004f},
+  {0x1005e, 0x1007f},
+  {0x100fb, 0x100ff},
+  {0x10103, 0x10106},
+  {0x10134, 0x10136},
+  {0x1018f, 0x1018f},
+  {0x1019c, 0x1019f},
+  {0x101a1, 0x101cf},
+  {0x101fe, 0x1027f},
+  {0x1029d, 0x1029f},
+  {0x102d1, 0x102df},
+  {0x102fc, 0x102ff},
+  {0x10324, 0x1032f},
+  {0x1034b, 0x1034f},
+  {0x1037b, 0x1037f},
+  {0x1039e, 0x1039e},
+  {0x103c4, 0x103c7},
+  {0x103d6, 0x103ff},
+  {0x1049e, 0x1049f},
+  {0x104aa, 0x104af},
+  {0x104d4, 0x104d7},
+  {0x104fc, 0x104ff},
+  {0x10528, 0x1052f},
+  {0x10564, 0x1056e},
+  {0x10570, 0x105ff},
+  {0x10737, 0x1073f},
+  {0x10756, 0x1075f},
+  {0x10768, 0x107ff},
+  {0x10806, 0x10807},
+  {0x10809, 0x10809},
+  {0x10836, 0x10836},
+  {0x10839, 0x1083b},
+  {0x1083d, 0x1083e},
+  {0x10856, 0x10856},
+  {0x1089f, 0x108a6},
+  {0x108b0, 0x108df},
+  {0x108f3, 0x108f3},
+  {0x108f6, 0x108fa},
+  {0x1091c, 0x1091e},
+  {0x1093a, 0x1093e},
+  {0x10940, 0x1097f},
+  {0x109b8, 0x109bb},
+  {0x109d0, 0x109d1},
+  {0x10a04, 0x10a04},
+  {0x10a07, 0x10a0b},
+  {0x10a14, 0x10a14},
+  {0x10a18, 0x10a18},
+  {0x10a34, 0x10a37},
+  {0x10a3b, 0x10a3e},
+  {0x10a48, 0x10a4f},
+  {0x10a59, 0x10a5f},
+  {0x10aa0, 0x10abf},
+  {0x10ae7, 0x10aea},
+  {0x10af7, 0x10aff},
+  {0x10b36, 0x10b38},
+  {0x10b56, 0x10b57},
+  {0x10b73, 0x10b77},
+  {0x10b92, 0x10b98},
+  {0x10b9d, 0x10ba8},
+  {0x10bb0, 0x10bff},
+  {0x10c49, 0x10c7f},
+  {0x10cb3, 0x10cbf},
+  {0x10cf3, 0x10cf9},
+  {0x10d00, 0x10e5f},
+  {0x10e7f, 0x10fff},
+  {0x1104e, 0x11051},
+  {0x11070, 0x1107e},
+  {0x110c2, 0x110cf},
+  {0x110e9, 0x110ef},
+  {0x110fa, 0x110ff},
+  {0x11135, 0x11135},
+  {0x11144, 0x1114f},
+  {0x11177, 0x1117f},
+  {0x111ce, 0x111cf},
+  {0x111e0, 0x111e0},
+  {0x111f5, 0x111ff},
+  {0x11212, 0x11212},
+  {0x1123f, 0x1127f},
+  {0x11287, 0x11287},
+  {0x11289, 0x11289},
+  {0x1128e, 0x1128e},
+  {0x1129e, 0x1129e},
+  {0x112aa, 0x112af},
+  {0x112eb, 0x112ef},
+  {0x112fa, 0x112ff},
+  {0x11304, 0x11304},
+  {0x1130d, 0x1130e},
+  {0x11311, 0x11312},
+  {0x11329, 0x11329},
+  {0x11331, 0x11331},
+  {0x11334, 0x11334},
+  {0x1133a, 0x1133b},
+  {0x11345, 0x11346},
+  {0x11349, 0x1134a},
+  {0x1134e, 0x1134f},
+  {0x11351, 0x11356},
+  {0x11358, 0x1135c},
+  {0x11364, 0x11365},
+  {0x1136d, 0x1136f},
+  {0x11375, 0x113ff},
+  {0x1145a, 0x1145a},
+  {0x1145c, 0x1145c},
+  {0x1145e, 0x1147f},
+  {0x114c8, 0x114cf},
+  {0x114da, 0x1157f},
+  {0x115b6, 0x115b7},
+  {0x115de, 0x115ff},
+  {0x11645, 0x1164f},
+  {0x1165a, 0x1165f},
+  {0x1166d, 0x1167f},
+  {0x116b8, 0x116bf},
+  {0x116ca, 0x116ff},
+  {0x1171a, 0x1171c},
+  {0x1172c, 0x1172f},
+  {0x11740, 0x1189f},
+  {0x118f3, 0x118fe},
+  {0x11900, 0x11abf},
+  {0x11af9, 0x11bff},
+  {0x11c09, 0x11c09},
+  {0x11c37, 0x11c37},
+  {0x11c46, 0x11c4f},
+  {0x11c6d, 0x11c6f},
+  {0x11c90, 0x11c91},
+  {0x11ca8, 0x11ca8},
+  {0x11cb7, 0x11fff},
+  {0x1239a, 0x123ff},
+  {0x1246f, 0x1246f},
+  {0x12475, 0x1247f},
+  {0x12544, 0x12fff},
+  {0x1342f, 0x143ff},
+  {0x14647, 0x167ff},
+  {0x16a39, 0x16a3f},
+  {0x16a5f, 0x16a5f},
+  {0x16a6a, 0x16a6d},
+  {0x16a70, 0x16acf},
+  {0x16aee, 0x16aef},
+  {0x16af6, 0x16aff},
+  {0x16b46, 0x16b4f},
+  {0x16b5a, 0x16b5a},
+  {0x16b62, 0x16b62},
+  {0x16b78, 0x16b7c},
+  {0x16b90, 0x16eff},
+  {0x16f45, 0x16f4f},
+  {0x16f7f, 0x16f8e},
+  {0x16fa0, 0x16fdf},
+  {0x16fe1, 0x16fff},
+  {0x187ed, 0x187ff},
+  {0x18af3, 0x1afff},
+  {0x1b002, 0x1bbff},
+  {0x1bc6b, 0x1bc6f},
+  {0x1bc7d, 0x1bc7f},
+  {0x1bc89, 0x1bc8f},
+  {0x1bc9a, 0x1bc9b},
+  {0x1bca4, 0x1cfff},
+  {0x1d0f6, 0x1d0ff},
+  {0x1d127, 0x1d128},
+  {0x1d1e9, 0x1d1ff},
+  {0x1d246, 0x1d2ff},
+  {0x1d357, 0x1d35f},
+  {0x1d372, 0x1d3ff},
+  {0x1d455, 0x1d455},
+  {0x1d49d, 0x1d49d},
+  {0x1d4a0, 0x1d4a1},
+  {0x1d4a3, 0x1d4a4},
+  {0x1d4a7, 0x1d4a8},
+  {0x1d4ad, 0x1d4ad},
+  {0x1d4ba, 0x1d4ba},
+  {0x1d4bc, 0x1d4bc},
+  {0x1d4c4, 0x1d4c4},
+  {0x1d506, 0x1d506},
+  {0x1d50b, 0x1d50c},
+  {0x1d515, 0x1d515},
+  {0x1d51d, 0x1d51d},
+  {0x1d53a, 0x1d53a},
+  {0x1d53f, 0x1d53f},
+  {0x1d545, 0x1d545},
+  {0x1d547, 0x1d549},
+  {0x1d551, 0x1d551},
+  {0x1d6a6, 0x1d6a7},
+  {0x1d7cc, 0x1d7cd},
+  {0x1da8c, 0x1da9a},
+  {0x1daa0, 0x1daa0},
+  {0x1dab0, 0x1dfff},
+  {0x1e007, 0x1e007},
+  {0x1e019, 0x1e01a},
+  {0x1e022, 0x1e022},
+  {0x1e025, 0x1e025},
+  {0x1e02b, 0x1e7ff},
+  {0x1e8c5, 0x1e8c6},
+  {0x1e8d7, 0x1e8ff},
+  {0x1e94b, 0x1e94f},
+  {0x1e95a, 0x1e95d},
+  {0x1e960, 0x1edff},
+  {0x1ee04, 0x1ee04},
+  {0x1ee20, 0x1ee20},
+  {0x1ee23, 0x1ee23},
+  {0x1ee25, 0x1ee26},
+  {0x1ee28, 0x1ee28},
+  {0x1ee33, 0x1ee33},
+  {0x1ee38, 0x1ee38},
+  {0x1ee3a, 0x1ee3a},
+  {0x1ee3c, 0x1ee41},
+  {0x1ee43, 0x1ee46},
+  {0x1ee48, 0x1ee48},
+  {0x1ee4a, 0x1ee4a},
+  {0x1ee4c, 0x1ee4c},
+  {0x1ee50, 0x1ee50},
+  {0x1ee53, 0x1ee53},
+  {0x1ee55, 0x1ee56},
+  {0x1ee58, 0x1ee58},
+  {0x1ee5a, 0x1ee5a},
+  {0x1ee5c, 0x1ee5c},
+  {0x1ee5e, 0x1ee5e},
+  {0x1ee60, 0x1ee60},
+  {0x1ee63, 0x1ee63},
+  {0x1ee65, 0x1ee66},
+  {0x1ee6b, 0x1ee6b},
+  {0x1ee73, 0x1ee73},
+  {0x1ee78, 0x1ee78},
+  {0x1ee7d, 0x1ee7d},
+  {0x1ee7f, 0x1ee7f},
+  {0x1ee8a, 0x1ee8a},
+  {0x1ee9c, 0x1eea0},
+  {0x1eea4, 0x1eea4},
+  {0x1eeaa, 0x1eeaa},
+  {0x1eebc, 0x1eeef},
+  {0x1eef2, 0x1efff},
+  {0x1f02c, 0x1f02f},
+  {0x1f094, 0x1f09f},
+  {0x1f0af, 0x1f0b0},
+  {0x1f0c0, 0x1f0c0},
+  {0x1f0d0, 0x1f0d0},
+  {0x1f0f6, 0x1f0ff},
+  {0x1f10d, 0x1f10f},
+  {0x1f12f, 0x1f12f},
+  {0x1f16c, 0x1f16f},
+  {0x1f1ad, 0x1f1e5},
+  {0x1f203, 0x1f20f},
+  {0x1f23c, 0x1f23f},
+  {0x1f249, 0x1f24f},
+  {0x1f252, 0x1f2ff},
+  {0x1f6d3, 0x1f6df},
+  {0x1f6ed, 0x1f6ef},
+  {0x1f6f7, 0x1f6ff},
+  {0x1f774, 0x1f77f},
+  {0x1f7d5, 0x1f7ff},
+  {0x1f80c, 0x1f80f},
+  {0x1f848, 0x1f84f},
+  {0x1f85a, 0x1f85f},
+  {0x1f888, 0x1f88f},
+  {0x1f8ae, 0x1f90f},
+  {0x1f91f, 0x1f91f},
+  {0x1f928, 0x1f92f},
+  {0x1f931, 0x1f932},
+  {0x1f93f, 0x1f93f},
+  {0x1f94c, 0x1f94f},
+  {0x1f95f, 0x1f97f},
+  {0x1f992, 0x1f9bf},
+  {0x1f9c1, 0x1ffff},
+  {0x2a6d7, 0x2a6ff},
+  {0x2b735, 0x2b73f},
+  {0x2b81e, 0x2b81f},
+  {0x2cea2, 0x2f7ff},
+  {0x2fa1e, 0xe0000},
+  {0xe0002, 0xe001f},
+  {0xe0080, 0xe00ff},
+  {0xe01f0, 0xeffff},
+  {0xffffe, 0xfffff},
+};
+
+#define WCWIDTH9_ARRAY_SIZE(arr) ((sizeof(arr)/sizeof((arr)[0])) / ((size_t)(!(sizeof(arr) % sizeof((arr)[0])))))
+
+static inline bool wcwidth9_intable(const struct wcwidth9_interval *table, size_t n_items, int c) {
+  int mid, bot, top;
+
+  if (c < table[0].first) {
+    return false;
+  }
+
+  bot = 0;
+  top = (int)(n_items - 1);
+  while (top >= bot) {
+    mid = (bot + top) / 2;
+
+    if (table[mid].last < c) {
+      bot = mid + 1;
+    } else if (table[mid].first > c) {
+      top = mid - 1;
+    } else {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+static inline int wcwidth9(int c) {
+  if (c == 0) {
+    return 0;
+  }
+  if (c < 0|| c > 0x10ffff) {
+    return -1;
+  }
+
+  if (wcwidth9_intable(wcwidth9_nonprint, WCWIDTH9_ARRAY_SIZE(wcwidth9_nonprint), c)) {
+    return -1;
+  }
+
+  if (wcwidth9_intable(wcwidth9_combining, WCWIDTH9_ARRAY_SIZE(wcwidth9_combining), c)) {
+    return 0;
+  }
+
+  if (wcwidth9_intable(wcwidth9_not_assigned, WCWIDTH9_ARRAY_SIZE(wcwidth9_not_assigned), c)) {
+    return -1;
+  }
+
+  if (wcwidth9_intable(wcwidth9_private, WCWIDTH9_ARRAY_SIZE(wcwidth9_private), c)) {
+    return -3;
+  }
+
+  if (wcwidth9_intable(wcwidth9_ambiguous, WCWIDTH9_ARRAY_SIZE(wcwidth9_ambiguous), c)) {
+    return -2;
+  }
+
+  if (wcwidth9_intable(wcwidth9_doublewidth, WCWIDTH9_ARRAY_SIZE(wcwidth9_doublewidth), c)) {
+    return 2;
+  }
+
+  if (wcwidth9_intable(wcwidth9_emoji_width, WCWIDTH9_ARRAY_SIZE(wcwidth9_emoji_width), c)) {
+    return 2;
+  }
+
+  return 1;
+}
+
+#endif /* WCWIDTH9_H */
diff --git a/Src/zsh.h b/Src/zsh.h
index fe88efe69..7df5add86 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -238,6 +238,16 @@ struct mathfunc {
 #define PATCHARS "#^*()|[]<>?~\\"
 
 /*
+ * Check for a possibly tokenized dash.
+ *
+ * A dash only needs to be a token in a character range, [a-z], but
+ * it's difficult in general to ensure that.  So it's turned into
+ * a token at the usual point in the lexer.  However, we need
+ * to check for a literal dash at many points.
+ */
+#define IS_DASH(x) ((x) == '-' || (x) == Dash)
+
+/*
  * Types of quote.  This is used in various places, so care needs
  * to be taken when changing them.  (Oooh, don't you look surprised.)
  * - Passed to quotestring() to indicate style.  This is the ultimate
@@ -489,6 +499,7 @@ typedef struct complist  *Complist;
 typedef struct conddef   *Conddef;
 typedef struct dirsav    *Dirsav;
 typedef struct emulation_options *Emulation_options;
+typedef struct execcmd_params *Execcmd_params;
 typedef struct features  *Features;
 typedef struct feature_enables  *Feature_enables;
 typedef struct funcstack *Funcstack;
@@ -622,27 +633,34 @@ struct timedfn {
 /* (1<<4) is used for Z_END, see the wordcode definitions */
 /* (1<<5) is used for Z_SIMPLE, see the wordcode definitions */
 
-/* Condition types. */
+/*
+ * Condition types.
+ *
+ * Careful when changing these: both cond_binary_ops in text.c and
+ * condstr in cond.c depend on these.  (The zsh motto is "two instances
+ * are better than one".  Or something.)
+ */
 
 #define COND_NOT    0
 #define COND_AND    1
 #define COND_OR     2
 #define COND_STREQ  3
-#define COND_STRNEQ 4
-#define COND_STRLT  5
-#define COND_STRGTR 6
-#define COND_NT     7
-#define COND_OT     8
-#define COND_EF     9
-#define COND_EQ    10
-#define COND_NE    11
-#define COND_LT    12
-#define COND_GT    13
-#define COND_LE    14
-#define COND_GE    15
-#define COND_REGEX 16
-#define COND_MOD   17
-#define COND_MODI  18
+#define COND_STRDEQ 4
+#define COND_STRNEQ 5
+#define COND_STRLT  6
+#define COND_STRGTR 7
+#define COND_NT     8
+#define COND_OT     9
+#define COND_EF    10
+#define COND_EQ    11
+#define COND_NE    12
+#define COND_LT    13
+#define COND_GT    14
+#define COND_LE    15
+#define COND_GE    16
+#define COND_REGEX 17
+#define COND_MOD   18
+#define COND_MODI  19
 
 typedef int (*CondHandler) _((char **, int));
 
@@ -976,7 +994,8 @@ struct jobfile {
 
 struct job {
     pid_t gleader;		/* process group leader of this job  */
-    pid_t other;		/* subjob id or subshell pid         */
+    pid_t other;		/* subjob id (SUPERJOB)
+				 * or subshell pid (SUBJOB) */
     int stat;                   /* see STATs below                   */
     char *pwd;			/* current working dir of shell when *
 				 * this job was spawned              */
@@ -1008,6 +1027,8 @@ struct job {
 #define STAT_SUBLEADER  (0x2000) /* is super-job, but leader is sub-shell */
 
 #define STAT_BUILTIN    (0x4000) /* job at tail of pipeline is a builtin */
+#define STAT_SUBJOB_ORPHANED (0x8000)
+                                 /* STAT_SUBJOB with STAT_SUPERJOB exited */
 
 #define SP_RUNNING -1		/* fake status for jobs currently running */
 
@@ -1059,6 +1080,7 @@ struct execstack {
     int trapisfunc;
     int traplocallevel;
     int noerrs;
+    int this_noerrexit;
     char *underscore;
 };
 
@@ -1221,7 +1243,9 @@ struct cmdnam {
 
 struct shfunc {
     struct hashnode node;
-    char *filename;             /* Name of file located in */
+    char *filename;             /* Name of file located in.
+				   For not yet autoloaded file, name
+				   of explicit directory, if not NULL. */
     zlong lineno;		/* line number in above file */
     Eprog funcdef;		/* function definition    */
     Eprog redir;                /* redirections to apply */
@@ -1381,6 +1405,21 @@ struct builtin {
  */
 #define BINF_ASSIGN		(1<<19)
 
+/**
+ * Parameters passed to execcmd().
+ * These are not opaque --- they are also used by the pipeline manager.
+ */
+struct execcmd_params {
+    LinkList args;		/* All command prefixes, arguments & options */
+    LinkList redir;		/* Redirections */
+    Wordcode beg;		/* The code at the start of the command */
+    Wordcode varspc;		/* The code for assignment parsed as such */
+    Wordcode assignspc;		/* The code for assignment parsed as typeset */
+    int type;			/* The WC_* type of the command */
+    int postassigns;		/* The number of assignspc assiguments */
+    int htok;			/* tokens in parameter list */
+};
+
 struct module {
     struct hashnode node;
     union {
@@ -1502,6 +1541,7 @@ struct patstralloc {
 
 /* Flags used in pattern matchers (Patprog) and passed down to patcompile */
 
+#define PAT_HEAPDUP	0x0000	/* Dummy flag for default behavior */
 #define PAT_FILE	0x0001	/* Pattern is a file name */
 #define PAT_FILET	0x0002	/* Pattern is top level file, affects ~ */
 #define PAT_ANY		0x0004	/* Match anything (cheap "*") */
@@ -1562,8 +1602,8 @@ struct zpc_disables_save {
     struct zpc_disables_save *next;
     /*
      * Bit vector of ZPC_COUNT disabled characters.
-     * We'll live dangerously and assumed ZPC_COUNT is no greater
-     * than the number of bits an an unsigned int.
+     * We'll live dangerously and assume ZPC_COUNT is no greater
+     * than the number of bits in an unsigned int.
      */
     unsigned int disables;
 };
@@ -1778,6 +1818,7 @@ struct tieddata {
 #define PM_READONLY	(1<<10)	/* readonly                                 */
 #define PM_TAGGED	(1<<11)	/* tagged                                   */
 #define PM_EXPORTED	(1<<12)	/* exported                                 */
+#define PM_ABSPATH_USED (1<<12) /* (function): loaded using absolute path   */
 
 /* The following are the same since they *
  * both represent -U option to typeset   */
@@ -1785,7 +1826,9 @@ struct tieddata {
 #define PM_UNALIASED	(1<<13)	/* do not expand aliases when autoloading   */
 
 #define PM_HIDE		(1<<14)	/* Special behaviour hidden by local        */
+#define PM_CUR_FPATH    (1<<14) /* (function): can use $fpath with filename */
 #define PM_HIDEVAL	(1<<15)	/* Value not shown in `typeset' commands    */
+#define PM_WARNNESTED   (1<<15) /* (function): non-recursive WARNNESTEDVAR  */
 #define PM_TIED 	(1<<16)	/* array tied to colon-path or v.v.         */
 #define PM_TAGGED_LOCAL (1<<16) /* (function): non-recursive PM_TAGGED      */
 
@@ -1795,6 +1838,9 @@ struct tieddata {
 #define PM_CHECKLEN     (1<<20) /* cached length is checked */
 
 /* Remaining flags do not correspond directly to command line arguments */
+#define PM_DONTIMPORT_SUID (1<<19) /* do not import if running setuid */
+#define PM_LOADDIR      (1<<19) /* (function) filename gives load directory */
+#define PM_SINGLE       (1<<20) /* special can only have a single instance  */
 #define PM_LOCAL	(1<<21) /* this parameter will be made local        */
 #define PM_SPECIAL	(1<<22) /* special builtin parameter                */
 #define PM_DONTIMPORT	(1<<23)	/* do not import this variable              */
@@ -1986,9 +2032,15 @@ struct paramdef {
  * Flags for assignsparam and assignaparam.
  */
 enum {
+    /* Add to rather than override value */
     ASSPM_AUGMENT = 1 << 0,
+    /* Test for warning if creating global variable in function */
     ASSPM_WARN_CREATE = 1 << 1,
-    ASSPM_ENV_IMPORT = 1 << 2
+    /* Test for warning if using nested variable in function */
+    ASSPM_WARN_NESTED = 1 << 2,
+    ASSPM_WARN = (ASSPM_WARN_CREATE|ASSPM_WARN_NESTED),
+    /* Import from environment, so exercise care evaluating value */
+    ASSPM_ENV_IMPORT = 1 << 3,
 };
 
 /* node for named directory hash table (nameddirtab) */
@@ -2196,6 +2248,7 @@ struct histent {
 enum {
     OPT_INVALID,
     ALIASESOPT,
+    ALIASFUNCDEF,
     ALLEXPORT,
     ALWAYSLASTPROMPT,
     ALWAYSTOEND,
@@ -2369,6 +2422,7 @@ enum {
     VERBOSE,
     VIMODE,
     WARNCREATEGLOBAL,
+    WARNNESTEDVAR,
     XTRACE,
     USEZLE,
     DVORAK,
@@ -2570,7 +2624,7 @@ struct ttyinfo {
 
 #define txtchangeisset(T,X)	((T) & (X))
 #define txtchangeget(T,A)	(((T) & A ## _MASK) >> A ## _SHIFT)
-#define txtchangeset(T, X, Y)	((void)(T && (*T |= (X), *T &= ~(Y))))
+#define txtchangeset(T, X, Y)	((void)(T && (*T &= ~(Y), *T |= (X))))
 
 /*
  * For outputting sequences to change colour: specify foreground
@@ -2802,7 +2856,14 @@ enum errflag_bits {
     /*
      * User interrupt.
      */
-    ERRFLAG_INT = 2
+    ERRFLAG_INT = 2,
+    /*
+     * Hard error --- return to top-level prompt in interactive
+     * shell.  In non-interactive shell we'll typically already
+     * have exited.  This is reset by "errflag = 0" in
+     * loop(toplevel = 1, ...).
+     */
+    ERRFLAG_HARD = 4
 };
 
 /***********/
@@ -2873,6 +2934,7 @@ struct hist_stack {
     void (*addtoline) _((int));
     unsigned char *cstack;
     int csp;
+    int curline_linked;
 };
 
 /*
@@ -3099,8 +3161,8 @@ typedef wint_t convchar_t;
  * much what the definition tells us.  However, we happen to know this
  * works on MacOS which doesn't define that.
  */
-#if defined(BROKEN_WCWIDTH) && (defined(__STDC_ISO_10646__) || defined(__APPLE__))
-#define WCWIDTH(wc)	mk_wcwidth(wc)
+#ifdef ENABLE_UNICODE9
+#define WCWIDTH(wc)	u9_wcwidth(wc)
 #else
 #define WCWIDTH(wc)	wcwidth(wc)
 #endif
@@ -3145,15 +3207,7 @@ typedef wint_t convchar_t;
  * sense throughout the shell.  I am not aware of a way of
  * detecting the Unicode trait in standard libraries.
  */
-#ifdef BROKEN_WCWIDTH
-/*
- * We can't be quite sure the wcwidth we've provided is entirely
- * in agreement with the system's, so be extra safe.
- */
-#define IS_COMBINING(wc)	(wc != 0 && WCWIDTH(wc) == 0 && !iswcntrl(wc))
-#else
 #define IS_COMBINING(wc)	(wc != 0 && WCWIDTH(wc) == 0)
-#endif
 /*
  * Test for the base of a combining character.
  *
diff --git a/Src/zsh_system.h b/Src/zsh_system.h
index 811340d42..5339b496f 100644
--- a/Src/zsh_system.h
+++ b/Src/zsh_system.h
@@ -37,7 +37,7 @@
 #endif
 #endif
 
-#if defined(__linux) || defined(__GNU__) || defined(__GLIBC__) || defined(LIBC_MUSL)
+#if defined(__linux) || defined(__GNU__) || defined(__GLIBC__) || defined(LIBC_MUSL) || defined(__CYGWIN__)
 /*
  * Turn on numerous extensions.
  * This is in order to get the functions for manipulating /dev/ptmx.
@@ -728,7 +728,7 @@ extern char **environ;
  * We always need setenv and unsetenv in pairs, because
  * we don't know how to do memory management on the values set.
  */
-#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV)
+#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) && !defined(__APPLE__)
 # define USE_SET_UNSET_ENV
 #endif
 
@@ -882,6 +882,10 @@ extern short ospeed;
 # endif
 #endif
 
+#ifdef HAVE_SRAND_DETERMINISTIC
+# define srand srand_deterministic
+#endif
+
 #ifdef ZSH_VALGRIND
 # include "valgrind/valgrind.h"
 # include "valgrind/memcheck.h"
diff --git a/Src/ztype.h b/Src/ztype.h
index 76589b152..ae7236774 100644
--- a/Src/ztype.h
+++ b/Src/ztype.h
@@ -72,7 +72,11 @@
 
 #ifdef MULTIBYTE_SUPPORT
 #define WC_ZISTYPE(X,Y) wcsitype((X),(Y))
-#define WC_ISPRINT(X)	iswprint(X)
+# ifdef ENABLE_UNICODE9
+#  define WC_ISPRINT(X)	u9_iswprint(X)
+# else
+#  define WC_ISPRINT(X)	iswprint(X)
+# endif
 #else
 #define WC_ZISTYPE(X,Y)	zistype((X),(Y))
 #define WC_ISPRINT(X)	isprint(X)
diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst
index 7eedfa6e0..9625a15bc 100644
--- a/Test/A01grammar.ztst
+++ b/Test/A01grammar.ztst
@@ -103,16 +103,64 @@
 0:`exec' with -a option, no space
 >/bin/SPLOOSH
 
+  (
+    opts=(-a /bin/WHOOOSH)
+    exec $opts /bin/sh -c 'echo $0'
+  )
+0:`exec' with -a option from expansion
+>/bin/WHOOOSH
+
   (export FOO=bar; exec -c /bin/sh -c 'echo x${FOO}x')
 0:`exec' with -c option
 >xx
 
+  (\exec /bin/sh -c 'echo Test one'; print Not reached)
+  ('exec' /bin/sh -c 'echo Test two'; print Not reached)
+  (\exec -c /bin/sh -c 'echo Test three'; print Not reached)
+0:precommand modifiers with quotes
+>Test one
+>Test two
+>Test three
+
   cat() { echo Function cat executed; }
   command cat && unfunction cat
 0:`command' precommand modifier
 <External command cat executed
 >External command cat executed
 
+  (command -p echo this is output)
+  (\command -p echo this is more output)
+  ('command' -p echo this is yet more output)
+0: command -p without -v or -V
+>this is output
+>this is more output
+>this is yet more output
+
+  command -pv cat
+  command -pv echo
+  command -p -V cat
+  command -p -V -- echo
+0:command -p in combination
+*>*/cat
+>echo
+>cat is /*/cat
+>echo is a shell builtin
+
+  args=(
+  'command -pv cat'
+  'command -pv echo'
+  'command -p -V cat'
+  'command -p -V -- echo'
+  )
+  for arg in $args; do
+    ${=arg}
+  done
+0:command -p in combination, using expansion
+*>*/cat
+>echo
+>cat is /*/cat
+>echo is a shell builtin
+
   cd() { echo Not cd at all; }
   builtin cd . && unfunction cd
 0:`builtin' precommand modifier
@@ -283,6 +331,14 @@
 >2
 >3
 
+  case whatever in
+  (*) print yeah, right ;&
+  esac
+  print but well
+0:'case', redundant final ";&"
+>yeah, right
+>but well
+
 ## Select now reads from stdin if the shell is not interactive.
 ## Its own output goes to stderr.
   (COLUMNS=80 LINES=3
@@ -738,3 +794,35 @@
 >	print Stuff here
 >}
 >Stuff here
+
+  (exit 37)
+  case $? in
+    (37) echo $?
+    ;;
+  esac
+0:case retains exit status for execution of cases
+>37
+
+  false
+  case stuff in
+    (nomatch) foo
+    ;;
+  esac
+  echo $?
+0:case sets exit status to zero if no patterns are matched
+>0
+
+  case match in
+    (match) true; false; (exit 37)
+    ;;
+  esac
+  echo $?
+0:case keeps exit status of last command executed in compound-list
+>37
+
+  x=1
+  x=2 | echo $x
+  echo $x
+0:Assignment-only current shell commands in LHS of pipelin
+>1
+>1
diff --git a/Test/A02alias.ztst b/Test/A02alias.ztst
index 49e47567c..e68e93e0d 100644
--- a/Test/A02alias.ztst
+++ b/Test/A02alias.ztst
@@ -82,6 +82,7 @@
 0:Global aliasing quotes
 > a string S 
 *>*5*echo S a string S "
+# "
 # Note there is a trailing space on the "> a string S " line
 
   (
@@ -104,3 +105,35 @@
 >0
 ?(eval):2: invalid alias 'x=y' encountered while printing aliases
 # Currently, 'alias -L' returns 0 in this case.  Perhaps it should return 1.
+
+  alias -s mysuff='print -r "You said it.";'
+  eval 'thingummy.mysuff'
+127:No endless loop with suffix alias in command position
+>You said it.
+?(eval):1: command not found: thingummy.mysuff
+
+  alias +x; alias -z
+1:error message has the correct sign
+?(eval):alias:1: bad option: +x
+?(eval):alias:1: bad option: -z
+
+  # Usual issue that aliases aren't expanded until we
+  # trigger a new parse...
+  (alias badalias=notacommand
+  eval 'badalias() { print does not work; }')
+1:ALIAS_FUNC_DEF off by default.
+?(eval):1: defining function based on alias `badalias'
+?(eval):1: parse error near `()'
+
+  (alias goodalias=isafunc
+  setopt ALIAS_FUNC_DEF
+  eval 'goodalias() { print does now work; }'
+  isafunc)
+0:ALIAS_FUNC_DEF causes the icky behaviour to be avaliable
+>does now work
+
+  (alias thisisokthough='thisworks() { print That worked; }'
+  eval thisisokthough
+  thisworks)
+0:NO_ALIAS_FUNC_DEF works if the alias is a complete definition
+>That worked
diff --git a/Test/A04redirect.ztst b/Test/A04redirect.ztst
index d7fe22fb0..cb82751ce 100644
--- a/Test/A04redirect.ztst
+++ b/Test/A04redirect.ztst
@@ -165,6 +165,15 @@
 ?About to close a second time
 *?\(eval\):*: failed to close file descriptor *
 
+  eval $'fn-varid() { print {\x18}<<0 }'
+  { which -x2 fn-varid; fn-varid } | tr $'\x18' '?'
+0:Regression test for off-by-one in varid check
+>fn-varid () {
+>  print {?} <<0
+>0
+>}
+>{?}
+
   print foo >&-
 0:'>&-' redirection
 
@@ -475,6 +484,18 @@
 >Nothing output yet
 >I just read any old rubbish
 
+  print you cannot be serious >input1
+  () {
+    local var
+    read var
+    print $1 $var $2
+  } <input1 Shirley >output1 dude
+  print Nothing output yet
+  cat output1
+0:anonymous function redirections mixed with argument
+>Nothing output yet
+>Shirley you cannot be serious dude
+
   redirfn() {
     local var
     read var
@@ -586,3 +607,18 @@
 >x
 >bar
 >y
+
+  fn-here-pipe() {
+    cat <<-HERE |& cat
+	FOO
+	HERE
+  }
+  fn-here-pipe
+  which fn-here-pipe
+0:Combination of HERE-document and |&
+>FOO
+>fn-here-pipe () {
+>	cat <<HERE 2>&1 | cat
+>FOO
+>HERE
+>}
diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst
index 52738181d..0804691a7 100644
--- a/Test/A05execution.ztst
+++ b/Test/A05execution.ztst
@@ -284,6 +284,7 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline
   select x; do :; done; echo $?
   select x in; do :; done; echo $?
   select x in _*_; do :; done; echo $?
+  unsetopt ERR_EXIT NULL_GLOB
 0:The status of "select" is zero when the loop body does not execute
 >0
 >0
@@ -297,3 +298,15 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline
 0:Background job exit does not affect reaping foreground job
 >CHLD
 >OK
+
+# Regression test for workers/39839 and workers/39844
+  () { if return 11; then :; fi }; echo $?
+  () { while return 13; do :; done }; echo $?
+  () { until return 17; do :; done }; echo $?
+  () { until false; do return 19; done }; echo $?
+0:"return" in "if" or "while" conditional
+>11
+>13
+>17
+>19
+
diff --git a/Test/A06assign.ztst b/Test/A06assign.ztst
index da4e3b0fe..fd2b4177c 100644
--- a/Test/A06assign.ztst
+++ b/Test/A06assign.ztst
@@ -133,6 +133,72 @@
 >1 2 42 43 44 5
 >1 2 42 100 99 5
 
+# (subsection: append to array)
+
+ array=( )
+ array[5,6]=( 1 2 3 )
+ print $array
+ print "${(q@)array}"
+0:Append to empty array by range
+>1 2 3
+>'' '' '' '' 1 2 3
+
+ array=( a )
+ array[5,6]=( 1 2 3 )
+ print $array
+ print "${(q@)array}"
+0:Append to 1-element array by range
+>a 1 2 3
+>a '' '' '' 1 2 3
+
+ array=( a b )
+ array[5,6]=( 1 2 3 )
+ print $array
+ print "${(q@)array}"
+0:Append to 2-element array by range
+>a b 1 2 3
+>a b '' '' 1 2 3
+
+ array=( a b )
+ array[5,5]=( 1 2 3 )
+ print $array
+ print "${(q@)array}"
+0:Append to 2-element array by [a,a] range
+>a b 1 2 3
+>a b '' '' 1 2 3
+
+ array=( a b c d )
+ array[5,6]=( 1 2 3 )
+ print $array
+ print "${(q@)array}"
+0:Append array by range, continuously
+>a b c d 1 2 3
+>a b c d 1 2 3
+
+ array=( a b c d )
+ array[5,5]=( 1 2 3 )
+ print $array
+ print "${(q@)array}"
+0:Append array by [a,a] range, continuously
+>a b c d 1 2 3
+>a b c d 1 2 3
+
+ array=( )
+ array+=( 1 2 3 )
+ print $array
+ print "${(q@)array}"
+0:Append empty array via +=
+>1 2 3
+>1 2 3
+
+ array=( a )
+ array+=( 1 2 3 )
+ print $array
+ print "${(q@)array}"
+0:Append 1-element array via +=
+>a 1 2 3
+>a 1 2 3
+
 # tests of var+=scalar
 
  s+=foo
@@ -489,3 +555,143 @@
  print $array
 0:slice beyond length of array
 >FIRST
+
+# tests of string assignments
+
+ a="abc"
+ a[1]=x
+ print $a
+0:overwrite first character in string
+>xbc
+
+ a="abc"
+ a[2]="x"
+ print $a
+0:overwrite middle character in string
+>axc
+
+ a="abc"
+ a[3]="x"
+ print $a
+0:overwrite last character in string
+>abx
+
+ a="abc"
+ a[-1]="x"
+ print $a
+0:overwrite -1 character in string
+>abx
+
+ a="abc"
+ a[-2]="x"
+ print $a
+0:overwrite -2 character (middle) in string
+>axc
+
+ a="ab"
+ a[-2]="x"
+ print $a
+0:overwrite -2 character (first) in string
+>xb
+
+ a="abc"
+ a[-3]="x"
+ print $a
+0:overwrite -3 character (first) in string
+>xbc
+
+ a="abc"
+ a[-4]="x"
+ print $a
+0:overwrite -4 character (before first) in string
+>xabc
+
+ a="abc"
+ a[-5]="x"
+ print $a
+0:overwrite -5 character (before-before first) in string
+>xabc
+
+ a="abc"
+ a[-4,0]="x"
+ print $a
+0:overwrite [-4,0] characters (before first) in string
+>xabc
+
+ a="abc"
+ a[-4,-4]="x"
+ print $a
+0:overwrite [-4,-4] character (before first) in string
+>xabc
+
+ a="abc"
+ a[-40,-30]="x"
+ print $a
+0:overwrite [-40,-30] characters (far before first) in string
+>xabc
+
+ a="abc"
+ a[-40,1]="x"
+ print $a
+0:overwrite [-40,1] characters in short string
+>xbc
+
+ a="abc"
+ a[-40,40]="x"
+ print $a
+0:overwrite [-40,40] characters in short string
+>x
+
+ a="abc"
+ a[2,40]="x"
+ print $a
+0:overwrite [2,40] characters in short string
+>ax
+
+ a="abc"
+ a[2,-1]="x"
+ print $a
+0:overwrite [2,-1] characters in short string
+>ax
+
+ a="abc"
+ a[-2,-1]="x"
+ print $a
+0:overwrite [-2,-1] characters in short string
+>ax
+
+ a="a"
+ a[-1]="xx"
+ print $a
+0:overwrite [-1] character with "xx"
+>xx
+
+ a="a"
+ a[-2]="xx"
+ print $a
+0:overwrite [-2] character (before first) with "xx"
+>xxa
+
+ a="a"
+ a[2]="xx"
+ print $a
+0:overwrite [2] character (after last) with "xx"
+>axx
+
+ a=""
+ a[1]="xx"
+ print $a
+0:overwrite [1] character (string: "") with "xx"
+>xx
+
+ a=""
+ a[-1]="xx"
+ print $a
+0:overwrite [-1] character (string: "") with "xx"
+>xx
+
+ a=""
+ a[2]="xx"
+ print $a
+0:overwrite [2] character (string: "") with "xx"
+>xx
diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst
index d6d24210b..b27bb4f6b 100644
--- a/Test/B02typeset.ztst
+++ b/Test/B02typeset.ztst
@@ -454,7 +454,7 @@
  fn() { typeset -p array nonexistent; }
  fn
 1:declare -p shouldn't create scoped values
->typeset -a array=( foo bar )
+>typeset -g -a array=( foo bar )
 ?fn:typeset: no such variable: nonexistent
 
  unsetopt typesetsilent
@@ -490,7 +490,7 @@
 ?0
 ?(eval):5: read-only variable: pbro
 ?(eval):6: read-only variable: pbro
-?typeset -r pbro
+?typeset -g -r pbro
 ?0
 ?(eval):10: read-only variable: pbro
 
@@ -711,3 +711,13 @@
   typeset isreadonly=still
 1:typeset returns status 1 if setting readonly variable
 ?(eval):2: read-only variable: isreadonly
+
+  if (( UID )); then
+    UID=$((UID+1)) date; echo "Status is printed, $?"
+  else
+    ZTST_skip="cannot test setuid error when tests run as superuser"
+  fi
+0:when cannot change UID, the command isn't run
+# 'date' did not run.
+>Status is printed, 1
+*?*: failed to change user ID: *
diff --git a/Test/B03print.ztst b/Test/B03print.ztst
index befe2f2dd..c65568ad9 100644
--- a/Test/B03print.ztst
+++ b/Test/B03print.ztst
@@ -308,5 +308,29 @@
  printf -v foo "%s\0%s-" into the breach
  typeset -p foo
 0:print and printf into a variable
->typeset foo='once more'
->typeset foo=$'into\C-@the-breach\C-@-'
+>typeset -g foo='once more'
+>typeset -g foo=$'into\C-@the-breach\C-@-'
+
+ typeset -a foo
+ print -f '%2$d %4s' -v foo one 1 two 2 three 3
+ typeset -p foo
+0:printf into an array variable
+>typeset -a foo=( '1  one' '2  two' '3 three' )
+
+ typeset -a foo
+ print -f '%s' -v foo string
+ typeset -p foo
+0:printf to an array variable without format string reuse
+>typeset foo=string
+
+ printf -
+ printf - -
+ printf --
+ printf -- -
+ printf -- --
+ printf -x -v foo
+ # Final print for newline on stdout
+ print
+0:regression test of printf with assorted ambiguous options or formats
+>------x
+?(eval):printf:3: not enough arguments
diff --git a/Test/B09hash.ztst b/Test/B09hash.ztst
index 49f304838..7b5dfb43e 100644
--- a/Test/B09hash.ztst
+++ b/Test/B09hash.ztst
@@ -69,3 +69,11 @@
 >one=/first/directory
 >two=/directory/the/second
 >three=/noch/ein/verzeichnis
+
+  hash -d t-t=/foo
+  i="~t-t"
+  print ~t-t/bar
+  print ${~i}/rab
+0:Dashes are untokenized in directory hash names
+>/foo/bar
+>/foo/rab
diff --git a/Test/C02cond.ztst b/Test/C02cond.ztst
index b0e84ddeb..38525016c 100644
--- a/Test/C02cond.ztst
+++ b/Test/C02cond.ztst
@@ -11,9 +11,9 @@
   typeset -gi isnfs
   [[ "$(find . -prune -fstype nfs 2>/dev/null)" == "." ]] && isnfs=1
   if (( isnfs )) &&
-    (cd -q ${TMPPREFIX:h} >/dev/null 2>&1 &&
+    (cd -q ${ZTST_tmp} >/dev/null 2>&1 &&
      [[ "$(find . -prune -fstype nfs 2>/dev/null)" != "." ]]); then
-    filetmpprefix=${TMPPREFIX}-$$-
+    filetmpprefix=${ZTST_tmp}/condtest-$$-
     isnfs=0
   else
     filetmpprefix=
@@ -146,29 +146,30 @@
 
 # can't be bothered with -S
 
-  if [[ ${mtab::="$({mount || /sbin/mount})"} = *[(]?*[)] ]]; then
+  if [[ ${mtab::="$({mount || /sbin/mount || /usr/sbin/mount} 2>/dev/null)"} = *[(]?*[)] ]]; then
     print -u $ZTST_fd 'This test takes two seconds...'
   else
     unmodified_ls="$(ls -lu $unmodified)"
     print -u $ZTST_fd 'This test takes up to 60 seconds...'
   fi
+  sleep 2
+  touch $newnewnew
   if [[ $OSTYPE == "cygwin" ]]; then
     ZTST_skip="[[ -N file ]] not supported on Cygwin"
   elif (( isnfs )); then
     ZTST_skip="[[ -N file ]] not supported with NFS"
   elif { (( ! $+unmodified_ls )) &&
-         { sleep 2; cat $unmodified } &&
+         cat $unmodified &&
          { df -k -- ${$(print -r -- "$mtab" |
                         awk '/noatime/ {print $1,$3}'):-""} | tr -s ' ' |
            fgrep -- "$(df -k . | tail -1 | tr -s ' ')" } >&/dev/null } ||
        { (( $+unmodified_ls )) && SECONDS=0 &&
-         ! until (( SECONDS >= 60 )); do
+         ! until (( SECONDS >= 58 )); do
              ZTST_hashmark; sleep 2; cat $unmodified
              [[ $unmodified_ls != "$(ls -lu $unmodified)" ]] && break
 	   done }; then
     ZTST_skip="[[ -N file ]] not supported with noatime file system"
   else
-    touch $newnewnew
     [[ -N $newnewnew && ! -N $unmodified ]]
   fi
 0:-N cond
@@ -268,6 +269,25 @@ F:Failures in these cases do not indicate a problem in the shell.
 0:-nt shouldn't abort on non-existent files
 >status = 1
 
+  str='string' empty=''
+  [[ -v IFS && -v str && -v empty && ! -v str[3] && ! -v not_a_variable ]]
+0:-v cond
+
+  arr=( 1 2 3 4 ) empty=()
+  [[ -v arr && -v arr[1,4] && -v arr[1] && -v arr[4] && -v arr[-4] &&
+    -v arr[(i)3] && ! -v arr[(i)x] &&
+    ! -v arr[0] && ! -v arr[5] && ! -v arr[-5] && ! -v arr[2][1] &&
+    ! -v arr[3]extra && -v empty && ! -v empty[1] ]]
+0:-v cond with array
+
+  typeset -A assoc=( key val num 4 )
+  [[ -v assoc && -v assoc[key] && -v assoc[(i)*] && -v assoc[(I)*] &&
+    ! -v assoc[x] && ! -v assoc[key][1] ]]
+0:-v cond with association
+
+  () { [[ -v 0 && -v 1 && -v 2 && ! -v 3 ]] } arg ''
+0:-v cond with positional parameters
+
 # core dumps on failure
   if zmodload zsh/regex 2>/dev/null; then
      echo >regex_test.sh 'if [[ $# = 1 ]]; then
@@ -413,6 +433,13 @@ F:Failures in these cases do not indicate a problem in the shell.
 >OK 4
 >OK 5
 
+  fn() { [[ 'a' == 'b' || 'b' = 'c' || 'c' != 'd' ]] }
+  which -x2 fn
+0: = and == appear as input
+>fn () {
+>  [[ 'a' == 'b' || 'b' = 'c' || 'c' != 'd' ]]
+>}
+
 %clean
   # This works around a bug in rm -f in some versions of Cygwin
   chmod 644 unmodish
diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst
index 83c05aa08..759401225 100644
--- a/Test/C03traps.ztst
+++ b/Test/C03traps.ztst
@@ -476,7 +476,7 @@
     fi
   }
   fn
-0:ERRRETURN not triggered in if condition
+0:ERR_RETURN not triggered in if condition
 >Oh, yes
 
   fn() {
@@ -490,7 +490,7 @@
     fi
   }
   fn
-1:ERRRETURN in "if"
+1:ERR_RETURN in "if"
 
   fn() {
     emulate -L zsh
@@ -503,7 +503,7 @@
     fi
   }
   fn
-1:ERRRETURN in "else" branch (regression test)
+1:ERR_RETURN in "else" branch (regression test)
 
   $ZTST_testdir/../Src/zsh -f =(<<<"
   if false; then
@@ -515,10 +515,268 @@
     print Yes
   fi
   ")
-0:ERRRETURN when false "if" is the first statement in an "else" (regression)
+0:ERR_RETURN when false "if" is the first statement in an "else" (regression)
 >Yes
 F:Must be tested with a top-level script rather than source or function
 
+  fn() {
+      emulate -L zsh
+      setopt errreturn
+      print before
+      false
+      print after
+  }
+  fn
+1:ERR_RETURN, basic case
+>before
+
+  fn() {
+      emulate -L zsh
+      setopt errreturn
+      print before
+      ! true
+      ! false
+      print after
+  }
+  fn
+0:ERR_RETURN with "!"
+>before
+>after
+
+  fn() {
+      emulate -L zsh
+      setopt errreturn
+      print before
+      ! true
+      ! false
+      false
+      print after
+  }
+  fn
+1:ERR_RETURN with "!" and a following false
+>before
+
+  fn() {
+      emulate -L zsh
+      setopt errreturn
+      print before
+      ! if true; then
+        false
+      fi
+      print after
+  }
+  fn
+0:ERR_RETURN with "!" suppressed inside complex structure
+>before
+>after
+
+  fn() {
+      emulate -L zsh
+      setopt errreturn
+      print before
+      if true; then
+        false
+      fi
+      print after
+  }
+  fn
+1:ERR_RETURN with no "!" suppression (control case)
+>before
+
+  (setopt err_return
+    fn() {
+      print before-in
+      false && false
+    }
+    print before-out
+    fn
+    print after-out
+  )
+1:ERR_RETURN with "&&" in function (regression test)
+>before-out
+>before-in
+
+  (setopt err_return
+    fn() {
+      print before-in
+      false && false
+      print after-in
+    }
+    print before-out
+    fn
+    print after-out
+  )
+0:ERR_RETURN not triggered on LHS of "&&" in function
+>before-out
+>before-in
+>after-in
+>after-out
+
+  (setopt err_return
+    fn() {
+      print before-in
+      true && false
+      print after-in
+    }
+    print before-out
+    fn
+    print after-out
+  )
+1:ERR_RETURN triggered on RHS of "&&" in function
+>before-out
+>before-in
+
+  (setopt err_exit
+  for x in y; do
+    false && true
+  done
+  print OK
+  )
+0:ERR_EXIT not triggered by status 1 at end of for
+>OK
+
+  (setopt err_exit
+  integer x=0
+  while (( ! x++ )); do
+    false && true
+  done
+  print OK
+  )
+0:ERR_EXIT not triggered by status 1 at end of while
+>OK
+
+  (setopt err_exit
+  repeat 1; do
+    false && true
+  done
+  print OK
+  )
+0:ERR_EXIT not triggered by status 1 at end of repeat
+>OK
+
+  (setopt err_exit
+  if true; then
+    false && true
+  fi
+  print OK
+  )
+0:ERR_EXIT not triggered by status 1 at end of if
+>OK
+
+  (setopt err_exit
+  {
+    false && true
+  }
+  print OK
+  )
+0:ERR_EXIT not triggered by status 1 at end of { }
+>OK
+
+  (setopt err_exit
+  for x in y; do
+    false
+  done
+  print OK
+  )
+1:ERR_EXIT triggered by status 1 within for
+
+  (setopt err_exit
+  integer x=0
+  while (( ! x++ )); do
+    false
+  done
+  print OK
+  )
+1:ERR_EXIT triggered by status 1 within while
+
+  (setopt err_exit
+  repeat 1; do
+    false
+  done
+  print OK
+  )
+1:ERR_EXIT triggered by status 1 within repeat
+
+  (setopt err_exit
+  if true; then
+    false
+  fi
+  print OK
+  )
+1:ERR_EXIT triggered by status 1 within if
+
+  (setopt err_exit
+  {
+    false
+  }
+  print OK
+  )
+1:ERR_EXIT triggered by status 1 within { }
+
+  (setopt err_exit
+  () {
+     false && true
+     print Still functioning
+     false && true
+  }
+  print OK
+  )
+1:ERR_EXIT triggered by status 1 at end of anon func
+>Still functioning
+
+  if zmodload zsh/system 2>/dev/null; then
+  (
+    trap 'echo TERM; exit 2' TERM
+    trap 'echo EXIT' EXIT
+    kill -s TERM "$sysparams[pid]"
+    echo 'FATAL: we should never get here!' 1>&2
+    exit 1
+  )
+  else
+    ZTST_skip="zsh/system library not found."
+  fi
+2:EXIT trap from TERM trap
+>TERM
+>EXIT
+
+  # Should not get "hello" in the single quotes.
+  (
+    trap "echo hello" EXIT;
+    { :; } | { read line; print "'$line'"; }
+  )
+0:EXIT trap not called in LHS of pipeline: Shell construct on LHS
+>''
+>hello
+
+  (
+    trap "echo hello" EXIT;
+    cat </dev/null | { read line; print "'$line'"; }
+  )
+0:EXIT trap not called in LHS of pipeline: External command on LHS
+>''
+>hello
+
+  $ZTST_testdir/../Src/zsh -f =(<<<"
+    trap handler EXIT
+    handler() {
+      echoa
+      echo b
+    }
+    echoa() {
+      echo a
+    }
+    exit0() {
+      exit
+    }
+    main() {
+      exit0
+    }
+    main
+  ")
+0:No early exit from nested function in EXIT trap.
+>a
+>b
+
 %clean
 
   rm -f TRAPEXIT
diff --git a/Test/C04funcdef.ztst b/Test/C04funcdef.ztst
index 496577f6c..6a675e0b4 100644
--- a/Test/C04funcdef.ztst
+++ b/Test/C04funcdef.ztst
@@ -2,6 +2,10 @@
 
   mkdir funcdef.tmp
   cd funcdef.tmp
+  setopt chaselinks
+  cd .
+  unsetopt chaselinks
+  mydir=$PWD
 
 %test
 
@@ -98,6 +102,24 @@
 >4
 >5
 
+  strmathfunc() {
+    if [[ $0 = stralpha ]]; then
+       set -- ${1//[^[:alpha:]]}
+    fi
+    (( $#1 ))
+  }
+  functions -Ms strlen 1 1 strmathfunc
+  functions -Ms stralpha 1 1 strmathfunc
+  print $(( strlen(this, is, a, raw, string) ))
+  print $(( strlen() ))
+  print $(( stralpha(this, is, a, raw, string) ))
+  print $(( stralpha() ))
+0:User-defined math functions, string arguments
+>24
+>0
+>16
+>0
+
   command_not_found_handler() {
     print "Great News!  I've handled the command:"
     print "$1"
@@ -120,14 +142,13 @@
      print "Your command:" >&2
      print "$1" >&2
      print "has gone down the tubes.  Sorry." >&2
-     return 1
+     return 42
   }
   ThisCommandDoesNotExistEither
-127:Command not found handler, failure
+42:Command not found handler, failure
 ?Your command:
 ?ThisCommandDoesNotExistEither
 ?has gone down the tubes.  Sorry.
-?(eval):7: command not found: ThisCommandDoesNotExistEither
 
   local variable=outside
   print "I am $variable"
@@ -321,6 +342,179 @@
 >  print oops was successfully autoloaded
 >}
 
+  (
+    fpath=(.)
+    printf '%s\n' 'oops(){}' 'ninjas-earring(){}' 'oops "$@"' >oops
+    autoload oops
+    oops
+    whence -v oops
+  )
+0q:whence -v of zsh-style autoload
+>oops is a shell function from $mydir/oops
+
+  (
+    fpath=(.)
+    mkdir extra
+    print 'print "I have been loaded by explicit path."' >extra/spec
+    autoload -Uz $PWD/extra/spec
+    spec
+  )
+0:autoload with explicit path
+>I have been loaded by explicit path.
+
+  (
+    fpath=(.)
+    print 'print "I have been loaded by default path."' >def
+    autoload -Uz $PWD/extra/def
+    def
+  )
+1:autoload with explicit path with function in normal path, no -d
+?(eval):5: def: function definition file not found
+
+  (
+    fpath=(.)
+    autoload -dUz $PWD/extra/def
+    def
+  )
+0:autoload with explicit path with function in normal path, with -d
+>I have been loaded by default path.
+
+  (
+    cd extra
+    fpath=(.)
+    autoload -r spec
+    cd ..
+    spec
+  )
+0:autoload -r
+>I have been loaded by explicit path.
+
+  (
+    cd extra
+    fpath=(.)
+    autoload -r def
+    cd ..
+    def
+  )
+0:autoload -r is permissive
+>I have been loaded by default path.
+
+  (
+    cd extra
+    fpath=(.)
+    autoload -R def
+  )
+1:autoload -R is not permissive
+?(eval):4: def: function definition file not found
+
+  (
+    spec() { autoload -XUz $PWD/extra; }
+    spec
+  )
+0:autoload -X with path
+>I have been loaded by explicit path.
+
+# The line number 1 here and in the next test seems suspect,
+# but this example proves it's not down to the new features
+# being tested here.
+  (
+    fpath=(.)
+    cod() { autoload -XUz; }
+    cod
+  )
+1:autoload -X with no path, failure
+?(eval):1: cod: function definition file not found
+
+  (
+    fpath=(.)
+    def() { autoload -XUz $PWD/extra; }
+    def
+  )
+1:autoload -X with wrong path and no -d
+?(eval):1: def: function definition file not found
+
+  (
+    fpath=(.)
+    def() { autoload -dXUz $PWD/extra; }
+    def
+  )
+0:autoload -dX with path
+>I have been loaded by default path.
+
+  (
+    fpath=(.)
+    print 'loadthisfunc() { autoload -X }' >loadthisfunc_sourceme
+    print 'print Function was loaded correctly.' >loadthisfunc
+    source $PWD/loadthisfunc_sourceme
+    loadthisfunc
+  )
+0: autoload -X interaction with absolute filename used for source location
+>Function was loaded correctly.
+
+  (
+    fpath=()
+    mkdir extra2
+    for f in fun2a fun2b; do
+      print "print $f" >extra2/$f
+    done
+    repeat 3; do
+      autoload $PWD/extra2/fun2{a,b} $PWD/extra/spec
+      fun2a
+      fun2b
+      spec
+      unfunction fun2a fun2b spec
+      autoload $PWD/extra2/fun2{a,b} $PWD/extra/spec
+      spec
+      fun2b
+      fun2a
+      unfunction fun2a fun2b spec
+    done
+  )
+0: Exercise the directory name cache for autoloads
+>fun2a
+>fun2b
+>I have been loaded by explicit path.
+>I have been loaded by explicit path.
+>fun2b
+>fun2a
+>fun2a
+>fun2b
+>I have been loaded by explicit path.
+>I have been loaded by explicit path.
+>fun2b
+>fun2a
+>fun2a
+>fun2b
+>I have been loaded by explicit path.
+>I have been loaded by explicit path.
+>fun2b
+>fun2a
+
+  not_trashed() { print This function was not trashed; }
+  autoload -Uz /foo/bar/not_trashed
+  not_trashed
+0:autoload with absolute path doesn't trash loaded function
+>This function was not trashed
+
+  # keep spec from getting loaded in parent shell for simplicity
+  (
+    if whence spec; then print spec already loaded >&2; exit 1; fi
+    autoload -Uz $PWD/spec
+    autoload -Uz $PWD/extra/spec
+    spec
+  )
+0:autoload with absolute path can be overridden if not yet loaded
+>I have been loaded by explicit path.
+
+  (
+    if whence spec; then print spec already loaded >&2; exit 1; fi
+    autoload -Uz $PWD/extra/spec
+    autoload spec
+    spec
+  )
+0:autoload with absolute path not cancelled by bare autoload
+>I have been loaded by explicit path.
+
 %clean
 
  rm -f file.in file.out
diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst
index 2638e2438..11f18dc71 100644
--- a/Test/D01prompt.ztst
+++ b/Test/D01prompt.ztst
@@ -82,9 +82,12 @@
 # We could test for that, but we can't be bothered.
 # I hope LC_ALL is enough to make the format what's expected.
 
+# The $date2 assignment tests that %s is interpreted as a printf format
+# string, rather than as a prompt escape (end standout).
+
   LC_ALL=C
   date1=$(print -P %w)
-  date2=$(print -P %W)
+  date2=$(print -P -f %s %W)
   date3=$(print -P %D)
   if [[ $date1 != [A-Z][a-z][a-z][[:blank:]]##[0-9]## ]]; then
     print "Date \`$date1' is not in the form \`Day DD' (e.g. \`Mon 1'"
@@ -196,8 +199,17 @@
 ?+zsh_directory_name:1> emulate -L zsh
 ?+zsh_directory_name:2> setopt extendedglob
 ?+zsh_directory_name:3> local -a match mbegin mend
-?+zsh_directory_name:4> [[ d == n ]]
-?+zsh_directory_name:12> [[ <parent>/very_long_directory_name == (#b)(*)/very_long_directory_name ]]
+?+zsh_directory_name:4> [[ d = n ]]
+?+zsh_directory_name:12> [[ <parent>/very_long_directory_name = (#b)(*)/very_long_directory_name ]]
 ?+zsh_directory_name:14> return 0
 ?+fn:7> local d='~[<parent>:l]'
 ?+fn:8> print '~[<parent>:l]'
+
+# Test that format strings are not subject to prompt expansion
+ print -P -f '%%Sfoo%%s\n' bar
+0:print -P -f
+>%Sfoo%s
+
+  print ${(%U)Y-%(v}
+0:Regression test for test on empty psvar
+>
diff --git a/Test/D02glob.ztst b/Test/D02glob.ztst
index a6b704a8e..08b71dc8e 100644
--- a/Test/D02glob.ztst
+++ b/Test/D02glob.ztst
@@ -654,4 +654,49 @@
   [[ "z" != [$~cset] ]] || print Fail 4
   [[ "1" = [$~cset] ]] || print Fail 5
   [[ "b" != [$~cset] ]] || print Fail 6
-0:character set specified as active variabe
+0:character set specified as active variable
+
+ () { print -l -- $@:a } / /..{,/} /1 /nonexistent/..{,/} /deeper/nonexistent/..{,/}
+0:modifier ':a' doesn't require existence
+>/
+>/
+>/
+>/1
+>/
+>/
+>/deeper
+>/deeper
+
+ () { set -- ${PWD}/$^@; print -l -- $@:A } glob.tmp/nonexistent/foo/bar/baz
+0:modifier ':A' doesn't require existence
+*>*/glob.tmp/nonexistent/foo/bar/baz
+
+ ln -s dir3/subdir glob.tmp/link
+ () {
+   print ${1:A} | grep glob.tmp
+ } glob.tmp/link/../../hello
+ rm glob.tmp/link
+0:modifier ':A' resolves '..' components before symlinks
+# There should be no output
+
+ ln -s dir3/subdir glob.tmp/link
+ () {
+   print ${1:P}
+ } glob.tmp/link/../../hello/world
+ rm glob.tmp/link
+0:modifier ':P' resolves symlinks before '..' components
+*>*glob.tmp/hello/world
+
+ # This is a bit brittle as it depends on PATH_MAX.
+ # We could use sysconf..
+ bad_pwd="/${(l:16000:: :):-}"
+ print ${bad_pwd:P}
+0:modifier ':P' with path too long
+?(eval):4: path expansion failed, using root directory
+>/
+
+ foo=a
+ value="ac"
+ print ${value//[${foo}b-z]/x}
+0:handling of - range in complicated pattern context
+>xx
diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst
index 7cb297e0c..d5798b5b9 100644
--- a/Test/D04parameter.ztst
+++ b/Test/D04parameter.ztst
@@ -82,6 +82,11 @@
 >wasnull2d
 >wasnull2d
 
+  unset array
+  print ${#${(A)=array=word}}
+0:${#${(A)=array=word}} counts array elements
+>1
+
   (print ${set1:?okhere}; print ${unset1:?exiting1}; print not reached;)
   (print ${null1?okhere}; print ${null1:?exiting2}; print not reached;)
 1:${...:?...}, ${...?...}
@@ -90,6 +95,21 @@
 ?(eval):1: unset1: exiting1
 ?(eval):2: null1: exiting2
 
+  PROMPT="" $ZTST_testdir/../Src/zsh -fis <<<'
+  unsetopt PROMPT_SP
+  PS1="" PS2="" PS3="" PS4="" RPS1="" RPS2=""
+  exec 2>&1
+  foo() {
+      print ${1:?no arguments given}
+      print not reached
+  }
+  foo
+  print reached
+  ' 2>/dev/null
+0:interactive shell returns to top level on ${...?...} error
+*>*foo:1: 1: no arguments given
+>reached
+
   print ${set1:+word1} ${set1+word2} ${null1:+word3} ${null1+word4}
   print ${unset1:+word5} ${unset1+word6}
 0:${...:+...}, ${...+...}
@@ -612,6 +632,19 @@
 >;
 >(( echo 42 
 
+  # From parse error on it's not possible to split.
+  # Just check we get the complete string.
+  foo='echo $(|||) bar'
+  print -rl ${(z)foo}
+0:$($(z)} with parse error in command substitution.
+>echo
+>$(|||) bar
+
+  foo=$'\x06ZUI\x1f text-field example: \x1azuitfieldtfield1_1\x1a\'\'\x1a\'\'\x1a1\x1aZUI\\[my_tfield1_width\\]\x1aZUI\\[my_tfield1_start\\]\x1aZUI\\[my_tfield1_data\\]\x1c'
+  print "${#${(z@)foo}}"
+0:Test real-world data that once seemed to fail
+>4
+
   psvar=(dog)
   setopt promptsubst
   foo='It shouldn'\''t $(happen) to a %1v.'
@@ -825,6 +858,7 @@
   foo='b* e*'
   print ${(e)~foo}
   print ${(e)~=foo}
+  setopt nomatch
 0:Rule 10: Re-Evaluation
 >b* e*
 >boringfile evenmoreboringfile
@@ -1713,6 +1747,26 @@
 >2
 >2
 
+  # SHLVL is incremented twice and decremented once in between.
+  SHLVL=1
+  $ZTST_testdir/../Src/zsh -fc $ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"'
+  $ZTST_testdir/../Src/zsh -fc '('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL")'
+  $ZTST_testdir/../Src/zsh -fc '( ('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"))'
+0:SHLVL decremented upon implicit exec optimisation
+>2
+>2
+>2
+
+  # SHLVL is incremented twice with no decrement in between.
+  SHLVL=1
+  $ZTST_testdir/../Src/zsh -fc '('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"); exit'
+  $ZTST_testdir/../Src/zsh -fc '(exec '$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"); exit'
+  $ZTST_testdir/../Src/zsh -fc '( ('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"); exit)'
+0:SHLVL not decremented upon exec in subshells
+>3
+>3
+>3
+
 # The following tests the return behaviour of parsestr/parsestrnoerr
   alias param-test-alias='print $'\''\x45xpanded in substitution'\' 
   param='$(param-test-alias)'
@@ -1920,3 +1974,209 @@
   print $array
 0:"-" works after "[" in same expression (Dash problem)
 >foo two three
+
+  (
+  setopt shwordsplit
+  set -- whim:wham:whom
+  IFS=:
+  print -l $@
+  )
+0:Splitting of $@ on IFS: single element
+>whim
+>wham
+>whom
+
+  (
+  setopt shwordsplit
+  set -- one:two bucklemy:shoe
+  IFS=:
+  print -l $@
+  )
+0:Splitting of $@ on IFS: multiple elements
+# No forced joining in this case
+>one
+>two
+>bucklemy
+>shoe
+
+  (
+  set -- one:two bucklemy:shoe
+  print -l ${(s.:.)@}
+  )
+0:Splitting of $@ on (s): multiple elements
+# Forced joining in this case
+>one
+>two bucklemy
+>shoe
+
+  (
+  set -- one:two bucklemy:shoe
+  print -l ${(@s.:.)@}
+  )
+0:Splitting of $@ on (@s): multiple elements
+# Forced non-joining in this case
+>one
+>two
+>bucklemy
+>shoe
+
+  (
+  set -- one:two bucklemy:shoe
+  IFS=
+  setopt shwordsplit
+  print -l ${@} ${(s.:.)*} ${(s.:.j.-.)*}
+  )
+0:Joining of $@ does not happen when IFS is empty, but splitting $* does
+>one:two
+>bucklemy:shoe
+>one
+>twobucklemy
+>shoe
+>one
+>two-bucklemy
+>shoe
+
+  (
+  set -- "one two" "bucklemy shoe"
+  IFS=
+  setopt shwordsplit rcexpandparam
+  print -l "X${(@j.-.)*}"
+  )
+0:Use of @ does not prevent forced join with j
+>Xone two-bucklemy shoe
+
+  () { print -r -- "${(q)1}" "${(b)1}" "${(qq)1}" } '=foo'
+0:(q) and (b) quoting deal with the EQUALS option
+>\=foo =foo '=foo'
+
+  args() { print $#; }
+  a=(foo)
+  args "${a[3,-1]}"
+  args "${(@)a[3,-1]}"
+0:Out-of-range multiple array subscripts with quoting, with and without (@)
+>1
+>0
+
+  a='~-/'; echo $~a
+0:Regression: "-" became Dash in workers/37689, breaking ~- expansion
+*>*
+F:We do not care what $OLDPWD is, as long as it does not cause an error
+
+  (
+  set -- one 'two three' four
+  for ifs in default null unset; do
+    for wordsplit in native sh; do
+      print -r -- "--- $ifs IFS, $wordsplit splitting ---"
+      case $ifs in
+      default) IFS=$' \t\n\00' ;;
+      null)    IFS= ;;
+      unset)   unset -v IFS ;;
+      esac
+      case $wordsplit in
+      native)  unsetopt shwordsplit ;;
+      sh)      setopt shwordsplit ;;
+      esac
+      for testcmd in 'var=$@' 'var=$*' 'var="$@"' 'var="$*"'; do
+	print -r -- "> $testcmd"
+        eval "$testcmd"
+	printf '[%s]\n' "${var[@]}"
+      done
+    done
+  done
+  )
+0:Assigning $@, $*, "$@", "$*" to var with various shwordsplit/IFS settings
+>--- default IFS, native splitting ---
+>> var=$@
+>[one two three four]
+>> var=$*
+>[one two three four]
+>> var="$@"
+>[one two three four]
+>> var="$*"
+>[one two three four]
+>--- default IFS, sh splitting ---
+>> var=$@
+>[one two three four]
+>> var=$*
+>[one two three four]
+>> var="$@"
+>[one two three four]
+>> var="$*"
+>[one two three four]
+>--- null IFS, native splitting ---
+>> var=$@
+>[onetwo threefour]
+>> var=$*
+>[onetwo threefour]
+>> var="$@"
+>[onetwo threefour]
+>> var="$*"
+>[onetwo threefour]
+>--- null IFS, sh splitting ---
+>> var=$@
+>[onetwo threefour]
+>> var=$*
+>[onetwo threefour]
+>> var="$@"
+>[onetwo threefour]
+>> var="$*"
+>[onetwo threefour]
+>--- unset IFS, native splitting ---
+>> var=$@
+>[one two three four]
+>> var=$*
+>[one two three four]
+>> var="$@"
+>[one two three four]
+>> var="$*"
+>[one two three four]
+>--- unset IFS, sh splitting ---
+>> var=$@
+>[one two three four]
+>> var=$*
+>[one two three four]
+>> var="$@"
+>[one two three four]
+>> var="$*"
+>[one two three four]
+F:As of this writing, var=$@ and var="$@" with null IFS have unspecified
+F:behavior, see http://austingroupbugs.net/view.php?id=888
+
+  () {
+    setopt localoptions extendedglob
+    [[ $- = [[:alnum:]]## ]] || print Failed 1
+    [[ ${-} = [[:alnum:]]## ]] || print Failed 2
+  }
+0:$- expansion correctly handles Dash token
+
+  a=(1 "" 3)
+  print -rl -- "${(@)a//*/x}"
+  a=""
+  print -rl -- "${(@)a//*/y}"
+0:Zero-length string match in parameter substitution
+>x
+>x
+>x
+>y
+
+  a=(1 "" 3)
+  print -rl -- "${(@)a//#%*/x}"
+  a=""
+  print -rl -- "${(@)a//#%*/y}"
+0:Zero-length string match at end
+>x
+>x
+>x
+>y
+
+  my_width=6
+  my_index=1
+  my_options=Option1
+  hyperlink=$'\034'"MYID"$'\034'"DATA1"$'\034'"DATA2"$'\034'"DATA3"$'\034'"my_width"$'\034'"my_index"$'\034'"my_options"$'\02'
+  array=( $hyperlink "Regular text" $hyperlink )
+  array=( "${array[@]//(#b)$'\034'[^$'\034']#$'\034'[^$'\034']#$'\034'[^$'\034']#$'\034'[^$'\034']#$'\034'([^$'\034']#)$'\034'([^$'\034']#)$'\034'([^$'\02']#)$'\02'/${(mr:${(P)${(Q)match[1]}}:: :)${(As:;:)${(P)${(Q)match[3]}}}[${(P)${(Q)match[2]}}]}}" )
+  print -rl -- "${array[@]}"
+0:Test substitution that uses P,Q,A,s,r,m flags
+>Option
+>Regular text
+>Option
diff --git a/Test/D06subscript.ztst b/Test/D06subscript.ztst
index 144923667..f0a858b1c 100644
--- a/Test/D06subscript.ztst
+++ b/Test/D06subscript.ztst
@@ -266,3 +266,10 @@
 >of the gang
 >of the gang
 >of the gang
+
+ string='abcde'
+ twoarg() { return $(( $2 - $1 )) }
+ functions -M twoarg
+ print ${string[1,twoarg(1,4)]}
+0:Commas inside parentheses do not confuse subscripts
+>abc
diff --git a/Test/D07multibyte.ztst b/Test/D07multibyte.ztst
index 39ba5ef8b..e20315340 100644
--- a/Test/D07multibyte.ztst
+++ b/Test/D07multibyte.ztst
@@ -500,18 +500,6 @@
 # aren't quite double width, but the arithmetic is correct.
 # It appears just to be an effect of the font.
 
-  if zmodload zsh/regex 2>/dev/null; then
-    [[ $'\ua0' =~ '^.$' ]] && print OK
-    [[ $'\ua0' =~ $'^\ua0$' ]] && print OK
-    [[ $'\ua0'X =~ '^X$' ]] || print OK
-  else
-    ZTST_skip="regexp library not found."
-  fi
-0:Ensure no confusion on metafied input to regex module
->OK
->OK
->OK
-
   () {
      emulate -L zsh
      setopt errreturn
@@ -553,3 +541,47 @@
 0:${(q+)...} with printable multibyte characters
 >ホ
 >'She said "ホ".  I said "You can'\''t '\''ホ'\'' me!'
+
+#  This will silently succeed if zsh/parameter isn't available
+  (zmodload zsh/parameter >/dev/null 2>&1
+  f() {
+    : $(:)
+    "↓"
+  }
+  : $functions)
+0:Multibyte handling of functions parameter
+
+# c1=U+0104 (Ą) and c2=U+0120 (Ġ) are chosen so that
+#   u1 = utf8(c1) = c4 84  <  u2 = utf8(c2) = c4 a0
+#   metafy(u1) = c4 83 a4  >  metafy(u2) = c4 83 80
+# in both UTF-8 and ASCII collations (the latter is used in macOS
+# and some versions of BSDs).
+  local -a names=( $'\u0104' $'\u0120' )
+  print -o $names
+  mkdir -p colltest
+  cd colltest
+  touch $names
+  print ?
+0:Sorting of metafied characters
+>Ą Ġ
+>Ą Ġ
+
+  printf '%q%q\n' 你你
+0:printf %q and quotestring and general metafy / token madness
+>你你
+
+# This test is kept last as it introduces an additional
+# dependency on the system regex library.
+  if zmodload zsh/regex 2>/dev/null; then
+    [[ $'\ua0' =~ '^.$' ]] && print OK
+    [[ $'\ua0' =~ $'^\ua0$' ]] && print OK
+    [[ $'\ua0'X =~ '^X$' ]] || print OK
+  else
+    ZTST_skip="regexp library not found."
+  fi
+0:Ensure no confusion on metafied input to regex module
+>OK
+>OK
+>OK
+F:A failure here may indicate the system regex library does not
+F:support character sets outside the portable 7-bit range.
diff --git a/Test/D08cmdsubst.ztst b/Test/D08cmdsubst.ztst
index 89e725966..4e0759e35 100644
--- a/Test/D08cmdsubst.ztst
+++ b/Test/D08cmdsubst.ztst
@@ -153,3 +153,27 @@
   eval 'foo echo this just works, OK\?)'
 0:backtracking within command string parsing with alias still pending
 >this just works, OK?
+
+  (
+    set errexit
+    show_nargs() { print $#; }
+    print a $() b
+    print c "$()" d
+  )
+0:Empty $() is a valid empty substitution.
+>a b
+>c  d
+
+  empty=$() && print "'$empty'"
+0:Empty $() is a valid assignment
+>''
+
+  (
+    setopt ignoreclosebraces
+    alias OPEN='{' CLOSE='};'
+    eval '{ OPEN print hi; CLOSE }
+    var=$({ OPEN print bye; CLOSE}) && print $var'
+  )
+0:Alias expansion needed in parsing substituions
+>hi
+>bye
diff --git a/Test/E01options.ztst b/Test/E01options.ztst
index 40e96afc9..dac9430cc 100644
--- a/Test/E01options.ztst
+++ b/Test/E01options.ztst
@@ -784,6 +784,10 @@
 >unsetting option...
 ?(eval):14: no such file or directory: pathtestdir/findme
 
+  (setopt pathdirs; path+=( /usr/bin ); type ./env)
+1:whence honours PATH_DIRS option
+>./env not found
+
   setopt posixbuiltins
   PATH= command -v print
   PATH= command -V print
@@ -800,6 +804,20 @@
 >print is a shell builtin
 ?(eval):8: command not found: print
 
+  (
+     setopt posixbuiltins
+     opts=()
+     command $opts print foo
+     opts=(-v)
+     command $opts print
+     opts=(-V)
+     command $opts print
+  )
+0:command with options from expansion
+>foo
+>print
+>print is a shell builtin
+
   # With non-special command: original value restored
   # With special builtin: new value kept
   # With special builtin preceeded by "command": original value restored.
@@ -1114,7 +1132,8 @@
     integer foo6=9
     (( foo6=10 ))
   }
-  fn
+  # don't pollute the test environment with the variables...
+  (fn)
 0:WARN_CREATE_GLOBAL option
 ?fn:3: scalar parameter foo1 created globally in function fn
 ?fn:5: scalar parameter foo1 created globally in function fn
@@ -1129,6 +1148,103 @@
   fn
 0:WARN_CREATE_GLOBAL negative cases
 
+  (
+    foo1=global1 foo2=global2 foo3=global3 foo4=global4
+    integer foo5=5
+    # skip foo6, defined in fn_wnv
+    foo7=(one two)
+    fn_wnv() {
+       # warns
+       foo1=bar1
+       # doesn't warn
+       local foo2=bar3
+       unset foo2
+       # still doesn't warn
+       foo2=bar4
+       # doesn't warn
+       typeset -g foo3=bar5
+       # warns
+       foo3=bar6
+       fn2() {
+          # warns if global option, not attribute
+          foo3=bar6
+       }
+       fn2
+       # doesn't warn
+       foo4=bar7 =true
+       # warns
+       (( foo5=8 ))
+       integer foo6=9
+       # doesn't warn
+       (( foo6=10 ))
+       foo7[3]=three
+       foo7[4]=(four)
+    }
+    print option off >&2
+    fn_wnv
+    print option on >&2
+    setopt warnnestedvar
+    fn_wnv
+    unsetopt warnnestedvar
+    print function attribute on >&2
+    functions -W fn_wnv
+    fn_wnv
+    print all off again >&2
+    functions +W fn_wnv
+    fn_wnv
+  )
+0:WARN_NESTED_VAR option
+?option off
+?option on
+?fn_wnv:2: scalar parameter foo1 set in enclosing scope in function fn_wnv
+?fn_wnv:11: scalar parameter foo3 set in enclosing scope in function fn_wnv
+?fn2:2: scalar parameter foo3 set in enclosing scope in function fn2
+?fn_wnv:20: numeric parameter foo5 set in enclosing scope in function fn_wnv
+?function attribute on
+?fn_wnv:2: scalar parameter foo1 set in enclosing scope in function fn_wnv
+?fn_wnv:11: scalar parameter foo3 set in enclosing scope in function fn_wnv
+?fn_wnv:20: numeric parameter foo5 set in enclosing scope in function fn_wnv
+?all off again
+
+
+  (
+    setopt warnnestedvar
+    () {
+      typeset -A a
+      : ${a[hello world]::=foo}
+      print ${(t)a}
+      key="hello world"
+      print $a[$key]
+    }
+  )
+0:No false positive on parameter used with subscripted assignment
+>association-local
+>foo
+
+  (
+    setopt warnnestedvar
+    () {
+      local var=(one two)
+      () { var=three; }
+      print $var
+    }
+  )
+0:Warn when changing type of nested variable: array to scalar.
+?(anon): scalar parameter var set in enclosing scope in function (anon)
+>three
+
+  (
+    setopt warnnestedvar
+    () {
+      local var=three
+      () { var=(one two); }
+      print $var
+    }
+  )
+0:Warn when changing type of nested variable: scalar to array.
+?(anon): array parameter var set in enclosing scope in function (anon)
+>one two
+
 # This really just tests if XTRACE is egregiously broken.
 # To test it properly would need a full set of its own.
   fn() { print message; }
diff --git a/Test/E02xtrace.ztst b/Test/E02xtrace.ztst
index 093a587bd..da6191cd0 100644
--- a/Test/E02xtrace.ztst
+++ b/Test/E02xtrace.ztst
@@ -120,10 +120,29 @@
 ?+./fnfile:3> print This is fn.
 
  set -x
+ [[ 'f o' == 'f x'* || 'b r' != 'z o' && 'squashy sound' < 'squishy sound' ]]
  [[ 'f o' = 'f x'* || 'b r' != 'z o' && 'squashy sound' < 'squishy sound' ]]
  [[ -e nonexistentfile || ( -z '' && -t 3 ) ]]
  set +x
 0:Trace for conditions
 ?+(eval):2> [[ 'f o' == f\ x* || 'b r' != z\ o && 'squashy sound' < 'squishy sound' ]]
-?+(eval):3> [[ -e nonexistentfile || -z '' && -t 3 ]]
-?+(eval):4> set +x
+?+(eval):3> [[ 'f o' = f\ x* || 'b r' != z\ o && 'squashy sound' < 'squishy sound' ]]
+?+(eval):4> [[ -e nonexistentfile || -z '' && -t 3 ]]
+?+(eval):5> set +x
+
+  # Part 1: Recurses into nested anonymous functions
+  fn() {
+    () { () { true } }
+  }
+  functions -T fn
+  fn
+  # Part 2: Doesn't recurse into named functions
+  gn() { true }
+  fn() { gn }
+  functions -T fn
+  fn
+0:tracing recurses into anonymous functions
+?+fn:1> '(anon)'
+?+(anon):0> '(anon)'
+?+(anon):0> true
+?+fn:0> gn
diff --git a/Test/V01zmodload.ztst b/Test/V01zmodload.ztst
index 349ae9c89..092f9d1c7 100644
--- a/Test/V01zmodload.ztst
+++ b/Test/V01zmodload.ztst
@@ -271,6 +271,80 @@
 0:Listing feature autoloads includes unloaded modules
 >zmodload -Fa zsh/zftp b:zftp
 
+  if ! zmodload zsh/system >/dev/null 2>&1; then
+    ZTST_skip="zsh/system module not available"
+  else
+    zmodload -lF zsh/system
+    zmodload -F zsh/system -p:errnos
+    print ${+errnos}
+    zmodload -lF zsh/system
+    zmodload -F zsh/system +p:errnos
+    print ${+errnos}
+    zmodload -lF zsh/system
+  fi
+0:Regression tests for index bug with math functions.
+>+b:syserror
+>+b:sysread
+>+b:syswrite
+>+b:sysopen
+>+b:sysseek
+>+b:zsystem
+>+f:systell
+>+p:errnos
+>+p:sysparams
+>0
+>+b:syserror
+>+b:sysread
+>+b:syswrite
+>+b:sysopen
+>+b:sysseek
+>+b:zsystem
+>+f:systell
+>-p:errnos
+>+p:sysparams
+>1
+>+b:syserror
+>+b:sysread
+>+b:syswrite
+>+b:sysopen
+>+b:sysseek
+>+b:zsystem
+>+f:systell
+>+p:errnos
+>+p:sysparams
+
+  if ! zmodload zsh/system >/dev/null 2>&1; then
+    ZTST_skip="zsh/system module not available"
+  else
+    zmodload -F zsh/system -f:systell
+    zmodload -lF zsh/system
+    (print $(( systell(-1) )))
+    zmodload -F zsh/system +f:systell
+    zmodload -lF zsh/system
+    (print $(( systell(-1) )))
+  fi
+1:Module Features for math functions
+>+b:syserror
+>+b:sysread
+>+b:syswrite
+>+b:sysopen
+>+b:sysseek
+>+b:zsystem
+>-f:systell
+>+p:errnos
+>+p:sysparams
+>+b:syserror
+>+b:sysread
+>+b:syswrite
+>+b:sysopen
+>+b:sysseek
+>+b:zsystem
+>+f:systell
+>+p:errnos
+>+p:sysparams
+?(eval):6: unknown function: systell
+?(eval):9: file descriptor out of range
+
 %clean
 
  eval "$deps"
diff --git a/Test/V06parameter.ztst b/Test/V06parameter.ztst
index c7df35dce..27d587852 100644
--- a/Test/V06parameter.ztst
+++ b/Test/V06parameter.ztst
@@ -1,15 +1,22 @@
+%prep
+
+  setopt chaselinks
+  cd .
+  unsetopt chaselinks
+  mydir=$PWD
+
 %test
 
   print 'print In sourced file
-  print $LINENO + $functrace + $funcsourcetrace
+  print $LINENO + $functrace + ${funcsourcetrace}
   ' >sourcedfile
   print -r -- 'print Started functrace.zsh
   module_path=(./Modules)
-  print $LINENO + $functrace + $funcsourcetrace
+  print $LINENO + $functrace + ${funcsourcetrace}
   :
   fn() {
     print Inside function $0
-    print $LINENO + $functrace + $funcsourcetrace
+    print $LINENO + $functrace + ${funcsourcetrace}
   }
   :
   fn
@@ -17,7 +24,7 @@
   fpath=(. $fpath)
   :
   echo '\''print Inside $0
-    print $LINENO + $functrace + $funcsourcetrace
+    print $LINENO + $functrace + ${funcsourcetrace}
   '\'' >autofn
   :
   autoload autofn
@@ -26,15 +33,15 @@
   autofn
   . ./sourcedfile' >functrace.zsh
   $ZTST_testdir/../Src/zsh +Z -f ./functrace.zsh
-0:Function tracing
+0q:Function tracing
 >Started functrace.zsh
 >3 + +
 >Inside function fn
 >2 + ./functrace.zsh:10 + ./functrace.zsh:5
 >Inside autofn
->2 + ./functrace.zsh:20 + ./autofn:0
+>2 + ./functrace.zsh:20 + $mydir/autofn:0
 >Inside autofn
->2 + ./functrace.zsh:21 + ./autofn:0
+>2 + ./functrace.zsh:21 + $mydir/autofn:0
 >In sourced file
 >2 + ./functrace.zsh:22 + ./sourcedfile:0
 
@@ -66,6 +73,25 @@
 >./rocky3.zsh:13 (eval):2
 >./rocky3.zsh:14 ./rocky3.zsh:14
 
+  (
+    fpath=($PWD)
+    print "print I have been autoloaded" >myfunc
+    autoload $PWD/myfunc
+    print ${functions_source[myfunc]}
+    myfunc
+    print ${functions_source[myfunc]}
+  )
+0q: $functions_source
+>$mydir/myfunc
+>I have been autoloaded
+>$mydir/myfunc
+
+ functions+=(a 'echo foo'); a
+ functions+=(a 'echo bar'); a
+0:$functions can be appended to twice
+>foo
+>bar
+
 %clean
 
- rm -f autofn functrace.zsh rocky3.zsh sourcedfile
+ rm -f autofn functrace.zsh rocky3.zsh sourcedfile myfunc
diff --git a/Test/V09datetime.ztst b/Test/V09datetime.ztst
index 7905155d8..ffad96c04 100644
--- a/Test/V09datetime.ztst
+++ b/Test/V09datetime.ztst
@@ -8,19 +8,21 @@
     # It's not clear this skip_extensions is correct, but the
     # format in question is causing problems on Solaris.
     # We'll revist this after the release.
-    [[ "$(strftime %^_10B 0)" = "   JANUARY" ]] || skip_extensions=1
-    [[ "$(LC_TIME=ja_JP.UTF-8 strftime %OS 1)" = 一 ]] || skip_japanese=1
+    [[ "$(strftime %^_10B 0 2>/dev/null)" = "   JANUARY" ]] || skip_extensions=1
+    [[ "$(LC_TIME=ja_JP.UTF-8 strftime %OS 1 2>/dev/null)" = 一 ]] || skip_japanese=1
   else
     ZTST_unimplemented="can't load the zsh/datetime module for testing"
   fi
 
 %test
 
+  strftime '' 0
   strftime %y 0
   strftime %Y 1000000000
   strftime %x 1200000000
   strftime %X 1200000001
 0:basic format specifiers
+>
 >70
 >2001
 >01/10/08
@@ -61,6 +63,8 @@
       strftime '%^_10B' 0
       strftime %03Ey 650000000
       strftime %-Oe 0
+      # width=400 is too wide and should cause an error
+      strftime %400d 0 2> /dev/null || echo OK
     )
   fi
 0:various extensions
@@ -68,7 +72,13 @@
 >   JANUARY
 >090
 >1
+>OK
 
   print -r -- ${(V)"$(strftime $'%Y\0%m\0%d' 100000000)"}
 0:Embedded nulls
 >1973^@03^@03
+
+# We assume '%@' is not a valid format on any OSs.
+# The result can be '%@' (Linux), '@' (BSDs) or an error (Cygwin).
+  [[ $(strftime '%@' 0 2> /dev/null) == (%|)@ || $? != 0 ]]
+0:bad format specifier
diff --git a/Test/V10private.ztst b/Test/V10private.ztst
index 320e35764..78ecd48ea 100644
--- a/Test/V10private.ztst
+++ b/Test/V10private.ztst
@@ -15,11 +15,6 @@
  (zmodload -u zsh/param/private && zmodload zsh/param/private)
 0:unload and reload the module without crashing
 
- ZTST_verbose=0 $ZTST_exe +Z -f $ZTST_srcdir/ztst.zsh private.TMP/B02
-0:typeset still works with zsh/param/private module loaded
-*>*
-*>*
-
  typeset scalar_test=toplevel
  () {
   print $scalar_test
@@ -129,7 +124,7 @@
 0:private hides value from surrounding scope in nested scope
 >typeset -a hash_test=( top level )
 >typeset -A hash_test=( in function )
->typeset -a hash_test=( top level )
+>typeset -g -a hash_test=( top level )
 >array-local top level
 >top level
 F:note "typeset" rather than "private" in output from outer
@@ -295,6 +290,15 @@ F:future revision will create a global with this assignment
  () { private -h SECONDS }
 0:private parameter may hide a special parameter
 
+ if (( UID )); then
+   ZTST_verbose=0 $ZTST_exe +Z -f $ZTST_srcdir/ztst.zsh private.TMP/B02
+ else
+   ZTST_skip="cannot re-run typeset tests when tests run as superuser"
+ fi
+0:typeset still works with zsh/param/private module loaded
+*>*
+*>*
+
 %clean
 
   rm -r private.TMP
diff --git a/Test/V11db_gdbm.ztst b/Test/V11db_gdbm.ztst
new file mode 100644
index 000000000..6d74cef2c
--- /dev/null
+++ b/Test/V11db_gdbm.ztst
@@ -0,0 +1,327 @@
+# Tests for the zsh/db/gdbm module.
+# This contains literal UTF-8 characters; if editing, use
+# UTF-8 mode.
+
+%prep
+
+ modname="zsh/db/gdbm"
+ dbfile=db.gdbm
+ if ! zmodload $modname 2>/dev/null; then
+   ZTST_unimplemented="can't load $modname module for testing"
+ fi
+ rm -f db.gdbm
+
+%test
+
+ (zmodload -u $modname && zmodload $modname)
+0:unload and reload the module without crashing
+
+ ztie -d db/gdbm -f $dbfile dbase
+ zuntie dbase
+0:create the database
+
+ ztie -r -d db/gdbm -f $dbfile dbase
+ zuntie -u dbase
+0:open the database read-only
+
+ ztie -d db/gdbm -f $dbfile dbase
+ dbase[testkey]=testdata
+ zuntie dbase
+ ztie -r -d db/gdbm -f $dbfile dbase
+ echo $dbase[testkey]
+ zuntie -u dbase
+0:store key in database
+>testdata
+
+ ztie -d db/gdbm -f $dbfile dbase2
+ unset 'dbase2[testkey]'
+ zuntie dbase2
+ ztie -d db/gdbm -f $dbfile dbase
+ echo $dbase[testkey]
+ zuntie dbase
+0:remove key from database (different variables)
+>
+
+ ztie -d db/gdbm -f $dbfile dbase
+ dbase[testkey]=testdata
+ zuntie dbase
+ ztie -r -d db/gdbm -f $dbfile dbase
+ echo $dbase[testkey]
+ zuntie -u dbase
+ ztie -d db/gdbm -f $dbfile dbase
+ unset 'dbase[testkey]'
+ zuntie dbase
+ ztie -r -d db/gdbm -f $dbfile dbase
+ echo $dbase[testkey]
+ zuntie -u dbase
+0:store & remove key from database (the same variables)
+>testdata
+>
+
+ ztie -d db/gdbm -f $dbfile dbase
+ dbase[testkey]=testdata
+ dbase[testkey2]=$dbase[testkey]
+ dbase[testkey3]=$dbase[testkey]x$dbase[testkey2]
+ zuntie dbase
+ ztie -d db/gdbm -f $dbfile dbase
+ echo $dbase[testkey]
+ echo $dbase[testkey2]
+ echo $dbase[testkey3]
+ zuntie dbase
+0:store 2 keys fetching 1st
+>testdata
+>testdata
+>testdataxtestdata
+
+ ztie -d db/gdbm -f $dbfile dbase
+ val=$dbase[testkey2]
+ unset 'dbase[testkey2]'
+ echo $val
+ zuntie dbase
+0:unset key that was fetched
+>testdata
+
+ ztie -r -d db/gdbm -f $dbfile dbase
+ local -a result=( "${(kv)dbase[@]}" )
+ print -rl -- "${(o)result[@]}"
+ zuntie -u dbase
+0:scan read-only tied hash, directly assign local -a
+>testdata
+>testdataxtestdata
+>testkey
+>testkey3
+
+ ztie -d db/gdbm -f $dbfile dbase
+ dbase=( a a )
+ print -rl -- "${(kv)dbase[@]}"
+ zuntie dbase
+0:Use scan directly, read-write mode
+>a
+>a
+
+ ztie -d db/gdbm -f $dbfile dbase
+ dbase=( a b c d )
+ zuntie dbase
+ ztie -d db/gdbm -f $dbfile dbase
+ result=( "${(kv)dbase[@]}" )
+ print -rl -- "${(o)result[@]}"
+ zuntie dbase
+0:replace hash / database, scan
+>a
+>b
+>c
+>d
+
+ ztie -d db/gdbm -f $dbfile dbase
+ local -a arr
+ arr=( "${dbase[@]}" )
+ print -rl -- "${(o)arr[@]}"
+ zuntie dbase
+0:scan with no (kv)
+>b
+>d
+
+ ztie -d db/gdbm -f $dbfile dbase
+ result=( "${(k)dbase[@]}" )
+ print -rl -- "${(o)result[@]}"
+ zuntie dbase
+0:scan with keys only (k)
+>a
+>c
+
+ ztie -d db/gdbm -f $dbfile dbase
+ result=( "${(v)dbase[@]}" )
+ print -rl -- "${(o)result[@]}"
+ zuntie dbase
+0:scan with keys only explicit (v)
+>b
+>d
+
+ rm -f $dbfile
+ ztie -r -d db/gdbm -f $dbfile dbase 2>/dev/null
+1:read-only open non-existent database
+
+ ztie -d db/gdbm -f $dbfile dbase
+ dbase+=( a b )
+ echo $dbase[a]
+ zuntie dbase
+ ztie -r -d db/gdbm -f $dbfile dbase
+ echo $dbase[a]
+ result=( "${(kv)dbase[@]}" )
+ print -rl -- "${(o)result[@]}"
+ zuntie -u dbase
+ ztie -d db/gdbm -f $dbfile dbase
+ dbase+=( c d )
+ echo $dbase[a]
+ echo $dbase[c]
+ result=( "${(kv)dbase[@]}" )
+ print -rl -- "${(o)result[@]}"
+ zuntie dbase
+ ztie -r -d db/gdbm -f $dbfile dbase
+ echo $dbase[a]
+ echo $dbase[c]
+ result=( "${(kv)dbase[@]}" )
+ print -rl -- "${(o)result[@]}"
+ zuntie -u dbase
+0:Append with +=( ), also with existing data, also (kv) scan
+>b
+>b
+>a
+>b
+>b
+>d
+>a
+>b
+>c
+>d
+>b
+>d
+>a
+>b
+>c
+>d
+
+ ztie -d db/gdbm -f $dbfile dbase
+ echo ${(t)dbase}
+ zuntie dbase
+0:Type of tied parameter
+>association-special
+
+ typeset -ga dbase
+ ztie -d db/gdbm -f $dbfile dbase
+ echo ${(t)dbase}
+ zuntie dbase
+0:Type of tied parameter, with preceding unset
+>association-special
+
+ local -a dbase
+ ztie -d db/gdbm -f $dbfile dbase
+ echo ${(t)dbase}
+ zuntie dbase
+0:Type of tied parameter, with local parameter already existing
+>association-local-special
+
+ local -a dbase
+ dbase=( fromarray )
+ () {
+     local -a dbase
+     ztie -d db/gdbm -f $dbfile dbase
+     echo ${(t)dbase}
+     zuntie dbase
+ }
+ echo $dbase[1]
+ ztie -d db/gdbm -f $dbfile dbase2
+ echo "Can connect, so untie happened:" $dbase2[a]
+ zuntie dbase2
+0:Test of automatic untie (use of local scope) and of scoping
+>association-local-special
+>fromarray
+>Can connect, so untie happened: b
+
+ echo $zgdbm_tied ${#zgdbm_tied}
+ ztie -r -d db/gdbm -f $dbfile dbase
+ echo $zgdbm_tied ${#zgdbm_tied}
+ ztie -d db/gdbm -f ${dbfile}2 dbase2
+ echo $zgdbm_tied ${#zgdbm_tied}
+ zuntie -u dbase
+ echo $zgdbm_tied ${#zgdbm_tied}
+ zuntie dbase2
+ echo $zgdbm_tied ${#zgdbm_tied}
+0:zgdbm_tied parameter
+>0
+>dbase 1
+>dbase dbase2 2
+>dbase2 1
+>0
+
+ unset zgdbm_tied 2>/dev/null
+1:unset of read-only zgdbm_tied parameter
+
+ ztie -d db/gdbm -f $dbfile dbase
+ dbase[漢字]=漢字
+ echo $dbase[漢字]
+ zuntie dbase
+ ztie -r -d db/gdbm -f $dbfile dbase
+ echo $dbase[漢字]
+ zuntie -u dbase
+0:Unicode test
+>漢字
+>漢字
+
+ key="ab"$'\0'"ef"
+ ztie -d db/gdbm -f $dbfile dbase
+ dbase[$key]=value
+ echo $dbase[$key]
+ zuntie dbase
+ ztie -r -d db/gdbm -f $dbfile dbase
+ echo $dbase[$key]
+ zuntie -u dbase
+ ztie -d db/gdbm -f $dbfile dbase
+ dbase[$key]=$key
+ zuntie dbase
+ ztie -d db/gdbm -f $dbfile dbase
+ [[ "$dbase[$key]" = "$key" ]] && echo correct
+ zuntie dbase
+0:Metafication of $'\0'
+>value
+>value
+>correct
+
+ ztie -d db/gdbm -f $dbfile dbase
+ dbase=( 漢字 漢字 )
+ echo $dbase[漢字]
+ zuntie dbase
+ ztie -d db/gdbm -f $dbfile dbase
+ echo $dbase[漢字]
+ zuntie dbase
+ key="ab"$'\0'"ef"
+ ztie -d db/gdbm -f $dbfile dbase
+ dbase+=( $key $key )
+ zuntie dbase
+ ztie -r -d db/gdbm -f $dbfile dbase
+ [[ "$dbase[$key]" = "$key" ]] && echo correct
+ zuntie -u dbase
+0:Unicode & metafication test, different hash access
+>漢字
+>漢字
+>correct
+
+ ztie -d db/gdbm -f $dbfile dbase
+ dbase=( 漢字 漢字 )
+ zuntie dbase
+ ztie -d db/gdbm -f $dbfile dbase
+ noglob print -rl ${(kv)dbase[@]}
+ zuntie dbase
+0:Hash scanning and metafication
+>漢字
+>漢字
+
+ ztie -d db/gdbm -f $dbfile dbase
+ noglob print -rl ${(okv)dbase[@]}
+ zuntie dbase
+0:Sorted hash scanning and metafication
+>漢字
+>漢字
+
+ ztie -d db/gdbm -f $dbfile dbase
+ zgdbmpath dbase
+ [[ $REPLY = */Test/db.gdbm ]] && echo correct
+ zuntie dbase
+ ztie -r -d db/gdbm -f $dbfile dbase
+ zgdbmpath dbase
+ [[ $REPLY = */Test/db.gdbm ]] && echo correct
+ zuntie -u dbase
+0:zgdbmpath builtin
+>correct
+>correct
+
+ ztie -d db/gdbm -f $dbfile dbase
+ fun() { while read line; do echo $line; done }
+ eval "dbase[testkey]=value1" | fun
+ echo $dbase[testkey]
+0:Test store in forked Zsh
+>value1
+
+%clean
+
+  rm -f ${dbfile}*
diff --git a/Test/X02zlevi.ztst b/Test/X02zlevi.ztst
index ced70300f..d3b533490 100644
--- a/Test/X02zlevi.ztst
+++ b/Test/X02zlevi.ztst
@@ -130,6 +130,22 @@
 >long
 >CURSOR: 0
 
+  zletest $'one two\e03rX$.'
+0:repeat replace chars at the end of the line consumes the replace char
+>BUFFER: XXX two
+>CURSOR: 6
+
+  zletest $'one two three\e02rxw3.w.'
+0:numeric argument to repeat replaces change count
+>BUFFER: xxe xxx xxxee
+>CURSOR: 10
+
+  zletest $'one two three four five six seven eight\e.03d2wk.1.'
+0:numeric args to both action and movement are multiplied (and saved for any repeat)
+>BUFFER: eight
+>seven eight
+>CURSOR: 0
+
   zletest $'yankee doodle\ebhDyy0"1P'
 0:paste register 1 to get last deletion
 >BUFFER:  doodleyankee
@@ -178,6 +194,11 @@
 >
 >CURSOR: 0
 
+  zletest $'123456789\exxxxxxxxx"1P.........'
+0:repeat advances to next killring register
+>BUFFER: 9987654321
+>CURSOR: 0
+
   zletest $'Z\exayankee doodle\e"_db0"_yeP'
 0:yank and delete to black hole register
 >BUFFER: Zyankee e
@@ -244,6 +265,12 @@
 >BUFFER: binging
 >CURSOR: 3
 
+  print -u $ZTST_fd 'This test may hang the shell when it fails...'
+  zletest $'worm\erdhd..'
+0:use of vi-repeat as the motion and repeat after a failed change
+>BUFFER: wodd
+>CURSOR: 2
+
   zpty_run 'bindkey "^_" undo'
   zletest $'undoc\037e'
 0:use of undo in vi insert mode
diff --git a/Test/Y01completion.ztst b/Test/Y01completion.ztst
index 1568369c8..113a45076 100644
--- a/Test/Y01completion.ztst
+++ b/Test/Y01completion.ztst
@@ -9,6 +9,7 @@
     cd comp.tmp
     comptestinit -z $ZTST_testdir/../Src/zsh &&
     {
+      comptesteval 'compdef _tst tst'
       mkdir dir1 &&
       mkdir dir2 &&
       touch file1 &&
@@ -77,6 +78,29 @@ F:regression test workers/32182
 >FI:{file2}
 F:regression test workers/31611
 
+  {
+    mkdir 'A(B)' 'A(B)/C'
+    comptest $'cd "A(B)\t\t'
+    comptesteval 'cd "A(B)/C"'
+    comptest $'cd ../\t'
+  } always {
+    rmdir 'A(B)/C' 'A(B)'
+  }
+0:directory name is not a glob qualifier
+>line: {cd "A(B)/}{}
+>line: {cd "A(B)/C/}{}
+>line: {cd ../C/}{}
+
+  comptesteval "_tst() { compadd -U -s : -S / -I . word; compstate[to_end]= }"
+  comptest $'tst .\C-b\t'
+0:allow for suffixes when moving cursor to end of match (with ignored suffix)
+>line: {tst word:/}{.}
+
+  comptesteval "_tst() { compadd -s : -S / word; compstate[to_end]= }"
+  comptest $'tst \t'
+0:allow for suffixes when moving cursor to end of match (without ignored suffix)
+>line: {tst word:/}{}
+
 %clean
 
   zmodload -ui zsh/zpty
diff --git a/Test/Y03arguments.ztst b/Test/Y03arguments.ztst
index 0147c7d14..25bb96b84 100644
--- a/Test/Y03arguments.ztst
+++ b/Test/Y03arguments.ztst
@@ -40,6 +40,14 @@
 >NO:{a}
 >NO:{b}
 
+ tst_arguments ':desc2:((a\:a\ value b\:other\\\\value))'
+ comptest $'tst \t'
+0:a and b with descriptions
+>line: {tst }{}
+>DESCRIPTION:{desc2}
+>NO:{a  -- a value}
+>NO:{b  -- other\value}
+
  tst_arguments ':desc1:(arg1)' ':desc2:(arg2)' ':desc3:(arg3)'
  comptest $'tst \t\t\t\C-w\C-w\C-w\C-d'
 0:three arguments
@@ -56,6 +64,20 @@
 >line: {tst arg1 }{}
 >MESSAGE:{no more arguments}
 
+ tst_arguments -a '2:desc2:(arg2)'
+ comptest $'tst a1\t \t'
+0:second argument but no first argument
+>line: {tst a1}{}
+>MESSAGE:{no more arguments}
+>line: {tst a1 arg2 }{}
+
+ tst_arguments '2:desc2:(arg2)' '*:rest:(rest)'
+ comptest $'tst \t\t\t'
+0:second and rest arguments but no first argument
+>line: {tst rest }{}
+>line: {tst rest arg2 }{}
+>line: {tst rest arg2 rest }{}
+
  tst_arguments '-\+[opt]'
  comptest $'tst -\C-d'
 0:-+
@@ -74,6 +96,14 @@
 >line: {tst +o -o }{}
 >MESSAGE:{no arguments}
 
+ tst_arguments +-o
+ comptest $'tst \t'
+0:option beginning with + and -, specified the other way around
+>line: {tst }{}
+>DESCRIPTION:{option}
+>NO:{+o}
+>NO:{-o}
+
  tst_arguments '-o:1:(a):2:(b)'
  comptest $'tst \t\t\t'
 0:two option arguments
@@ -81,6 +111,74 @@
 >line: {tst -o a }{}
 >line: {tst -o a b }{}
 
+ tst_arguments '!-x:arg:(ok)'
+ comptest $'tst -x \t'
+0:option argument to ignored option
+>line: {tst -x ok }{}
+
+ tst_arguments '!-a' -b
+ comptest $'tst -\t'
+0:ignored option not completed
+>line: {tst -b }{}
+
+ tst_arguments +x +y '!+z' ':arg:(x)'
+ comptest $'tst +z \t'
+0:ignored option is not taken to be the normal argument
+>line: {tst +z x }{}
+
+ tst_arguments --known --other
+ comptest $'tst --unknown -\t'
+0:unrecognised option has no effect on proceedings with no normal arguments
+>line: {tst --unknown --}{}
+
+ tst_arguments +x +y ':arg:(x)'
+ comptest $'tst +z \t'
+0:unrecognised option is taken to be the normal argument
+>line: {tst +z +}{}
+
+ tst_arguments '*-a:value:(1)'
+ comptest $'tst -a\t\t -a=\t'
+0:option argument follows in next argument
+>line: {tst -a }{}
+>line: {tst -a 1 }{}
+>line: {tst -a 1 -a=}{}
+>MESSAGE:{no arguments}
+
+ tst_arguments '*-a+:value:(1)'
+ comptest $'tst -a\t -a \t -a=\t'
+0:option argument either direct or in following argument
+>line: {tst -a1 }{}
+>line: {tst -a1 -a 1 }{}
+>line: {tst -a1 -a 1 -a=}{}
+
+ tst_arguments '*-a-:value:(1)'
+ comptest $'tst -a\t -a \t=\t'
+0:option argument follows directly
+>line: {tst -a1 }{}
+>line: {tst -a1 -a -a}{}
+>line: {tst -a1 -a -a=}{}
+
+ tst_arguments '*-a=:value:(1)'
+ comptest $'tst -a\t\t -a \t'
+0:option argument follows optional equals
+>line: {tst -a=}{}
+>line: {tst -a=1 }{}
+>line: {tst -a=1 -a 1 }{}
+
+ tst_arguments -s '*-a=:value:(1)'
+ comptest $'tst -a\t-a=\t -a \t'
+0:option argument follows optional equals, with -s
+>line: {tst -a1 }{}
+>line: {tst -a1 -a=1 }{}
+>line: {tst -a1 -a=1 -a 1 }{}
+
+ tst_arguments '*-a=-:value:(1)'
+ comptest $'tst -a\t\t-a \t'
+0:option argument follows mandatory equals
+>line: {tst -a=}{}
+>line: {tst -a=1 }{}
+>line: {tst -a=1 -a -a=}{}
+
  tst_arguments '-x:arg:'
  comptest $'tst -x\t'
 0:sticky option argument
@@ -99,6 +197,11 @@
 >DESCRIPTION:{option}
 >NO:{-x}
 
+ tst_arguments '-x' ": :_guard '[0-9]#' number"
+ comptest $'tst -\t'
+0:argument beginning with minus, guard on rest argument
+>line: {tst -x }{}
+
  tst_arguments '-o::optarg:(oa)' ':arg1:(a1)'
  comptest $'tst -o\t\t'
 0:optional option argument
@@ -110,20 +213,69 @@
 >NO:{a1}
 
  tst_arguments '-o:*a:a:(a)' ':A:(A)' ':B:(B)'
- comptest $'tst A -o a \t'
+ comptest $'tst A -o a \t\C-W\C-w-a -b -c a \t'
 0:variable length option arguments
 >line: {tst A -o a B }{}
+>line: {tst A -o -a -b -c a B }{}
 
  tst_arguments -s '-a' '-b' ':descr:{compadd - $+opt_args[-a]}'
  comptest $'tst -ab \t'
 0:opt_args
 >line: {tst -ab 1 }{}
 
+ tst_arguments '-a:one: :two' ':descr:{compadd -Q - $opt_args[-a]}'
+ comptest $'tst -a 1:x \\2 \t'
+0:opt_args with multiple arguments and quoting of colons and backslashes
+>line: {tst -a 1:x \2 1\:x:\\2 }{}
+
+ tst_arguments -a -b
+ comptest $'tst  rest -\t\C-w\eb\C-b-\t'
+0:option completion with rest arguments on the line but not in the specs
+>line: {tst  rest -}{}
+>line: {tst -}{ rest }
+>DESCRIPTION:{option}
+>NO:{-a}
+>NO:{-b}
+
  tst_arguments '-a' '*::rest:{compadd - -b}'
  comptest $'tst arg -\t'
 0:rest arguments
 >line: {tst arg -b }{}
 
+ tst_arguments -a :more '*:rest:{ compadd - $words }'
+ comptest $'tst x -a rest \t'
+0:rest arguments with single colon
+>line: {tst x -a rest }{}
+>NO:{-a}
+>NO:{rest}
+>NO:{tst}
+>NO:{x}
+
+ tst_arguments -a :more '*::rest:{ compadd - $words }'
+ comptest $'tst x -a rest \t\eb\eb\eb\et\C-E \t'
+0:rest arguments with two colons
+>line: {tst x -a rest rest }{}
+>line: {tst -a x rest rest }{}
+>NO:{rest}
+>NO:{x}
+
+ tst_arguments -a -b :more '*:::rest:{ compadd - $words }'
+ comptest $'tst -b x -a -x rest \t'
+0:rest arguments with three colons
+>line: {tst -b x -a -x rest }{}
+>NO:{-x}
+>NO:{rest}
+
+ tst_arguments -a ::more '*:::rest:{ compadd - $words }'
+ comptest $'tst -a opt rest \t'
+0:rest arguments with three colons following optional argument
+>line: {tst -a opt rest rest }{}
+
+ tst_arguments -a::arg '*:::rest:{ compadd - $words }'
+ comptest $'tst -a opt rest \t'
+0:rest arguments with three colons following optional argument to an option
+>line: {tst -a opt rest rest }{}
+
  tst_arguments '-e:*last:::b:{compadd "${(j:,:)words}"}' ':arg1:(arg1)'
  comptest $'tst -\t\tla\t\C-hst\t\t\eb\eb\C-b\t\t'
 0:words array in rest arguments
@@ -135,14 +287,14 @@
 >line: {tst -e ,last }{ last arg1}
 >line: {tst -e ,last ,last,,last }{ last arg1}
 
- tst_arguments -s '-d+:msg1:' '*::msg2:{compadd $CURRENT}' 
+ tst_arguments -s '-d+:msg1:' '*::msg2:{compadd $CURRENT}'
  comptest $'tst add \t\t\t'
 0:opt_args
 >line: {tst add 2 }{}
 >line: {tst add 2 3 }{}
 >line: {tst add 2 3 4 }{}
 
- tst_arguments -s '-a' '-b' '-c' ':words:compadd - abyyy abzzz' 
+ tst_arguments -s '-a' '-b' '-c' ':words:compadd - abyyy abzzz'
  comptest $'tst ab\t'
 0:options and words (zsh-workers:12257)
 >line: {tst ab}{}
@@ -150,6 +302,384 @@
 >NO:{abyyy}
 >NO:{abzzz}
 
+ tst_arguments -M 'm:{j}={y}' -y -n ':yes/no:(y n)'
+ comptest $'tst j\t\eb-\C-e\t'
+0:matcher applies to options but not rest arguments
+>line: {tst j}{}
+>line: {tst -y }{}
+
+ tst_arguments -M 'm:{j}={y}' -y -n - set1 -i - set2 -k
+ comptest $'tst -k -j\t'
+0:matcher in combination with sets (implies separate cadef structure)
+>line: {tst -k -y }{}
+
+ tst_arguments -x :word
+ comptest $'tst -- -\t'
+0:option after --
+>line: {tst -- -x }{}
+
+ tst_arguments -S -x ':word:()'
+ comptest $'tst -- -\t'
+0:disallowed option after --
+>line: {tst -- -}{}
+
+ tst_arguments -S -x ':word:()'
+ comptest $'tst - --\eB\C-b\t'
+0:allowed option before --
+>line: {tst -x }{ --}
+
+ tst_arguments -x :word
+ comptest $'tst word -\t'
+0:option after a word
+>line: {tst word -x }{}
+
+ tst_arguments -A'-*' -x :word
+ comptest $'tst word -\t'
+0:option after word that doesn't match -A pattern, no space before pattern
+>line: {tst word -}{}
+>MESSAGE:{no more arguments}
+
+ tst_arguments -A '-*' -x ':word:(-word)'
+ comptest $'tst  word\eB\C-b-\t'
+0:option before a word that doesn't match -A pattern, separate -A from pattern
+>line: {tst -}{ word}
+>DESCRIPTION:{word}
+>NO:{-word}
+>DESCRIPTION:{option}
+>NO:{-x}
+
+ tst_arguments -A '-*' -h -V -a '*: :(-x more)'
+ comptest $'tst -a -x m\t'
+0:continue completion after rest argument that looks like an option
+>line: {tst -a -x more }{}
+
+ tst_arguments '*-v'
+ comptest $'tst -v -\t'
+0:repeatable options
+>line: {tst -v -v }{}
+
+ tst_arguments -A '-*' - help -h -V - other -a '*: :(-x more)'
+ comptest $'tst -a -x m\t'
+0:continue completion after rest argument that looks like an option (with sets)
+>line: {tst -a -x more }{}
+
+  tst_arguments - '(help)' -h -V - other -a '*:rest:(1 2 3)'
+  comptest $'tst -h \t-a \t'
+0:foreign option disables whole set (without -A)
+>line: {tst -h }{}
+>MESSAGE:{no arguments}
+>line: {tst -h -a }{}
+
+  tst_arguments -A "-*" - '(help)' -h -V - other -a '*:rest:(1 2 3)'
+  comptest $'tst -h \t-a \t'
+0:foreign option disables whole set (with -A)
+>line: {tst -h }{}
+>MESSAGE:{no arguments}
+>line: {tst -h -a }{}
+
+ tst_arguments '(-C)-a' - set1 -C -v - set2 '(-a)-C' -w
+ comptest $'tst -a -\t' $'\C-w\C-w-C -\t'
+0:exclude option common to two sets and from one common option
+>line: {tst -a -}{}
+>DESCRIPTION:{option}
+>NO:{-v}
+>NO:{-w}
+>line: {tst -C -}{}
+>DESCRIPTION:{option}
+>NO:{-a}
+>NO:{-v}
+>NO:{-w}
+
+# _arguments doesn't know what rest arguments might be so any non-option
+# might apply: for the one set, it accepts "a"
+ tst_arguments -e - one -o '*:number:(1 2)' - two '(-e)*:letter:(a b)'
+ comptest $'tst \t' $'a -\t'
+0:rest argument rule in two sets
+>line: {tst }{}
+>DESCRIPTION:{letter}
+>NO:{a}
+>NO:{b}
+>DESCRIPTION:{number}
+>NO:{1}
+>NO:{2}
+>line: {tst  a -}{}
+>DESCRIPTION:{option}
+>NO:{-e}
+>NO:{-o}
+
+ tst_arguments '(set1-: set2-* set3-1)-a' - set1 '1: :(1)' '2: :(2)' - set2 '*:rest:(rest)' - set3 '1:num:(num)' '*: :(allowable)' - set4 ': :(allowed)'
+ comptest $'tst -a \t'
+0:exclude various forms of rest argument in set specific form
+>line: {tst -a allow}{}
+
+ tst_arguments '(-v)-a' '(set1--m -a)-b' - '(set1)' '( -a )-m' '( )-n' - set2 '(-w)*-v' -w
+ comptest $'tst -a -\t' $'\C-w\C-w-'{b,m,v}$' -\t'
+0:exclusion lists
+>line: {tst -a -}{}
+>DESCRIPTION:{option}
+>NO:{-b}
+>NO:{-m}
+>NO:{-n}
+>NO:{-w}
+>line: {tst -b -}{}
+>DESCRIPTION:{option}
+>NO:{-n}
+>NO:{-v}
+>NO:{-w}
+>line: {tst -m -b }{}
+>line: {tst -v -}{}
+>DESCRIPTION:{option}
+>NO:{-a}
+>NO:{-b}
+>NO:{-v}
+
+ tst_arguments -a - set1 -d - set2 '(set2)-m' -n -o ':arg:(x)' - set2 -x
+ comptest $'tst -m \t'
+0:exclude own set from an option
+>line: {tst -m -a }{}
+
+ tst_arguments - set1 '(set2)-a' -m -n - set2 -a -t -u
+ comptest $'tst -a -\t'
+0:exclude later set from an option common to both
+>line: {tst -a -}{}
+>DESCRIPTION:{option}
+>NO:{-m}
+>NO:{-n}
+>NO:{-t}
+>NO:{-u}
+
+ tst_arguments - set2 -a -t -u - set1 '(set2)-a' -m -n
+ comptest $'tst -a -\t'
+0:exclude earlier set from an option common to both
+>line: {tst -a -}{}
+>DESCRIPTION:{option}
+>NO:{-m}
+>NO:{-n}
+>NO:{-t}
+>NO:{-u}
+
+ tst_arguments -x - '(set1)' -a -b - '(set2)' -m -n
+ comptest $'tst -m -\t'
+0:single option sets are still mutually exclusive
+>line: {tst -m -x }{}
+
+ tst_arguments '(set-c set-g)-a' '(set)-b' -c + grp -g - set -s
+ comptest $'tst -a -b -\t'
+0:excluding a set doesn't exclude common options as part of the set
+>line: {tst -a -b -}{}
+>DESCRIPTION:{option}
+>NO:{-c}
+>NO:{-g}
+
+ tst_arguments '(-)-h' -a -b -c
+ comptest $'tst -h -\t'
+0:exclude all other options
+>line: {tst -h -}{}
+>MESSAGE:{no arguments}
+
+ tst_arguments -a '(-a)-b'
+ comptest $'tst - -b\C-b\C-b\C-b\t'
+0:exclusion only applies to later words
+>line: {tst -}{ -b}
+>DESCRIPTION:{option}
+>NO:{-a}
+>NO:{-b}
+F:shouldn't offer -b as it is already on the command-line
+
+ tst_arguments -s : '(-d)-a' -b -c -d
+ comptest $'tst -ab\t\C-h -\t\eb\eb \C-b-\t'
+0:exclusion with clumped options, in, after and before
+>line: {tst -abc}{}
+>line: {tst -ab -c }{}
+>line: {tst -}{ -ab -c}
+>DESCRIPTION:{option}
+>NO:{-a}
+>NO:{-b}
+>NO:{-c}
+>NO:{-d}
+
+ tst_arguments -s '(-conf)-c' '-conf' '-f' '(-)--long' --longer
+ comptest $'tst -c\t\C-h-long\t'
+0:don't prematurely exclude option that current word is a prefix of
+>line: {tst -c}{}
+>DESCRIPTION:{option}
+>NO:{-conf}
+>NO:{-f}
+>line: {tst --long}{}
+>DESCRIPTION:{option}
+>NO:{--long}
+>NO:{--longer}
+
+ tst_arguments -s '(set)-c' - set '-conf' '-f'
+ comptest $'tst -c\t'
+0:don't prematurely exclude option that current word is a prefix of (with sets)
+>line: {tst -conf }{}
+
+ tst_arguments -s : -ad '(-d)-a' -b -ca -d
+ comptest $'tst -ad\t-b\t'
+0:option clumping mixed with real option that looks like clump
+>line: {tst -ad }{}
+>line: {tst -ad -b}{}
+>DESCRIPTION:{option}
+>NO:{-a}
+>NO:{-d}
+
+ tst_arguments -s : '(-d)-a+:arg:(b)' '(-c)-b' -c -d
+ comptest $'tst -ab\t-\t'
+0:option clumping mixed with direct argument
+>line: {tst -ab }{}
+>line: {tst -ab -}{}
+>DESCRIPTION:{option}
+>NO:{-b}
+>NO:{-c}
+
+ tst_arguments '-a:arg' -b '(-b)-c'
+ comptest $'tst -a -c -\t'
+0:exclusion with option argument that looks like an option
+>line: {tst -a -c -}{}
+>MESSAGE:{no arguments}
+F:The current behaviour is wrong; the correct expected output is:
+F:>line: {tst -a -c -}{}
+F:>DESCRIPTION:{option}
+F:>NO:{-b}
+F:>NO:{-c}
+
+ tst_arguments + grp1 -a -b - onlyset '(-a grp3--y grp2 grp4--)-m' -n + grp2 -u -v + grp3 -x -y + grp4 -0 ':rest'
+ comptest $'tst -m -\t'
+0:exclude group options
+>line: {tst -m -}{}
+>DESCRIPTION:{rest}
+>DESCRIPTION:{option}
+>NO:{-b}
+>NO:{-n}
+>NO:{-x}
+
+ tst_arguments -x + '(grp1)' -a -b + '(grp2)' -m -n
+ comptest $'tst -m -\t'
+0:single option groups are not mutually exclusive
+>line: {tst -m -}{}
+>DESCRIPTION:{option}
+>NO:{-a}
+>NO:{-b}
+>NO:{-x}
+
+ tst_arguments '(grp1 grp2-2)-a' '(grp1-*)-b' + grp1 ':first' '*:rest' + grp2 ':second'
+ comptest $'tst -a \t\eb\C-w\C-e x y \t'
+0:exclude rest args listed within a group
+>line: {tst -a -b }{}
+>line: {tst -b x y -a }{}
+
+ tst_arguments '(grp--m)-a' + grp '-m:value:(a b c)' + agrp '-m:other:(1 2 3)'
+ comptest $'tst -m \t\C-w-a -m \t'
+0:two forms of same option in different groups
+>line: {tst -m }{}
+>DESCRIPTION:{value}
+>NO:{a}
+>NO:{b}
+>NO:{c}
+>line: {tst -a -m }{}
+>DESCRIPTION:{other}
+>NO:{1}
+>NO:{2}
+>NO:{3}
+F:should offer both sets of arguments in first case
+
+ tst_arguments '(grp--m)-x' + '(grp)' -a -b -c ':val:(1 2 3)'
+ comptest $'tst 1 -\t'
+0:normal argument excludes options in internally mutually exclusive group
+>line: {tst 1 -x }{}
+
+ tst_arguments -s -a - set1 -c -s - set2 -c -t + grp -d
+ comptest $'tst -s\t -\t'
+0:mix sets, groups and option stacking
+>line: {tst -s}{}
+>DESCRIPTION:{option}
+>NO:{-a}
+>NO:{-c}
+>NO:{-d}
+>NO:{-t}
+>line: {tst -s -}{}
+>DESCRIPTION:{option}
+>NO:{-a}
+>NO:{-c}
+>NO:{-d}
+F:shouldn't offer -t in the first case (with stacked options)
+
+ tst_arguments -s '(set-a set--b grp-a grp--b grp-)-a' - set-a -s - set--b -t + grp-a -g + grp--b -h + grp- -i
+ comptest $'tst -a\t'
+0:sets and groups with - in their name
+>line: {tst -a}{}
+>DESCRIPTION:{option}
+>NO:{-h}
+>NO:{-t}
+
+ tst_arguments --abc --aah :arg:
+ comptesteval 'setopt bashautolist automenu'
+ comptest $'tst --a\t\t\t'
+0:with message and bashautolist, a third tab will get menu completion
+>line: {tst --a}{}
+>line: {tst --a}{}
+>DESCRIPTION:{arg}
+>DESCRIPTION:{option}
+>NO:{--aah}
+>NO:{--abc}
+>line: {tst --aah}{}
+
+ tst_arguments --abc --aah :arg:
+ comptesteval 'setopt bashautolist noautomenu'
+ comptest $'tst --a\t\t\t'
+0:with message and bashautolist, a third tab is needed to display the list
+>line: {tst --a}{}
+>line: {tst --a}{}
+>DESCRIPTION:{arg}
+>DESCRIPTION:{option}
+>NO:{--aah}
+>NO:{--abc}
+>line: {tst --a}{}
+
+ tst_arguments --abc --aah :arg:
+ comptesteval 'setopt nobashautolist noautomenu'
+ comptest $'tst --\t\t'
+0:with message and noautomenu second tab redisplays the list
+>line: {tst --}{}
+>DESCRIPTION:{arg}
+>DESCRIPTION:{option}
+>NO:{--aah}
+>NO:{--abc}
+>line: {tst --}{}
+>DESCRIPTION:{arg}
+>DESCRIPTION:{option}
+>NO:{--aah}
+>NO:{--abc}
+
+ tst_arguments --abc --aah :arg:
+ comptesteval 'setopt nobashautolist automenu'
+ comptest $'tst --\t\t'
+0:with message two tabs will start menu completion
+>line: {tst --}{}
+>DESCRIPTION:{arg}
+>DESCRIPTION:{option}
+>NO:{--aah}
+>NO:{--abc}
+>line: {tst --aah}{}
+
+ tst_arguments --abc --aah :arg:
+ comptesteval 'zstyle ":completion:*::::" completer _oldlist _complete'
+ comptest $'tst --\t\t'
+0:with message and _oldlist, two tabs will start menu completion
+>line: {tst --}{}
+>DESCRIPTION:{arg}
+>DESCRIPTION:{option}
+>NO:{--aah}
+>NO:{--abc}
+>line: {tst --aah}{}
+
+ tst_arguments '--prefix=: :(one two)' '--prop=: :(three four)' --pre
+ comptest $'tst --pre=o\t --=\t'
+0:partial completion of option with an argument
+>line: {tst --prefix=one }{}
+>line: {tst --prefix=one --prop=}{}
+
 %clean
 
   zmodload -ui zsh/zpty
diff --git a/Test/ztst.zsh b/Test/ztst.zsh
index cdd84b55d..0b2679927 100755
--- a/Test/ztst.zsh
+++ b/Test/ztst.zsh
@@ -104,19 +104,23 @@ fpath=( $ZTST_srcdir/../Functions/*~*/CVS(/)
         $ZTST_srcdir/../Completion/*/*~*/CVS(/) )
 
 : ${TMPPREFIX:=/tmp/zsh}
+ZTST_tmp=${TMPPREFIX}.ztst.$$
+if ! rm -f $ZTST_tmp || ! mkdir -p $ZTST_tmp || ! chmod go-w $ZTST_tmp; then
+  print "Can't create $ZTST_tmp for exclusive use." >&2
+  exit 1
+fi
 # Temporary files for redirection inside tests.
-ZTST_in=${TMPPREFIX}.ztst.in.$$
+ZTST_in=${ZTST_tmp}/ztst.in
 # hold the expected output
-ZTST_out=${TMPPREFIX}.ztst.out.$$
-ZTST_err=${TMPPREFIX}.ztst.err.$$
+ZTST_out=${ZTST_tmp}/ztst.out
+ZTST_err=${ZTST_tmp}/ztst.err
 # hold the actual output from the test
-ZTST_tout=${TMPPREFIX}.ztst.tout.$$
-ZTST_terr=${TMPPREFIX}.ztst.terr.$$
+ZTST_tout=${ZTST_tmp}/ztst.tout
+ZTST_terr=${ZTST_tmp}/ztst.terr
 
 ZTST_cleanup() {
   cd $ZTST_testdir
-  rm -rf $ZTST_testdir/dummy.tmp $ZTST_testdir/*.tmp(N) \
-    ${TMPPREFIX}.ztst*$$(N)
+  rm -rf $ZTST_testdir/dummy.tmp $ZTST_testdir/*.tmp(N) ${ZTST_tmp}
 }
 
 # This cleanup always gets performed, even if we abort.  Later,
@@ -330,10 +334,10 @@ ZTST_diff() {
       diff_ret=1
     fi
   else
-    diff_out=$(diff "$@")
+    diff_out=$(diff -a "$@")
     diff_ret="$?"
     if [[ "$diff_ret" != "0" ]]; then
-      print -r "$diff_out"
+      print -r -- "$diff_out"
     fi
   fi
 
@@ -458,7 +462,7 @@ $(<$ZTST_terr)"
 	rm -rf $ZTST_out
 	print -r -- "${(e)substlines}" >$ZTST_out
       fi
-      if [[ $ZTST_flags != *d* ]] && ! ZTST_diff $diff_out -c $ZTST_out $ZTST_tout; then
+      if [[ $ZTST_flags != *d* ]] && ! ZTST_diff $diff_out -u $ZTST_out $ZTST_tout; then
 	ZTST_testfailed "output differs from expected as shown above for:
 $ZTST_code${$(<$ZTST_terr):+
 Error output:
@@ -470,7 +474,7 @@ $(<$ZTST_terr)}"
 	rm -rf $ZTST_err
 	print -r -- "${(e)substlines}" >$ZTST_err
       fi
-      if [[ $ZTST_flags != *D* ]] && ! ZTST_diff $diff_err -c $ZTST_err $ZTST_terr; then
+      if [[ $ZTST_flags != *D* ]] && ! ZTST_diff $diff_err -u $ZTST_err $ZTST_terr; then
 	ZTST_testfailed "error output differs from expected as shown above for:
 $ZTST_code"
 	return 1
diff --git a/Util/check-tmux-state b/Util/check-tmux-state
index 4cba36070..2c6106203 100644
--- a/Util/check-tmux-state
+++ b/Util/check-tmux-state
@@ -58,7 +58,7 @@ __tmux-window-options
 # Subcommand helper functions are defined like "function _tmux-foo() {"
 # in the _tmux function definition file.
 typeset -a supported_commands
-supported_commands=( $( grep 'function *\<_tmux-' $func |
+supported_commands=( $( grep '^_tmux-[^(]*() *{$' $func |
                         sed -e 's,^.*\<_tmux-,,' -e 's,(.*$,,' ) )
 
 # Ask tmux for available commands:
diff --git a/Util/helpfiles b/Util/helpfiles
index 699ca8321..9e837fe2d 100755
--- a/Util/helpfiles
+++ b/Util/helpfiles
@@ -19,7 +19,7 @@
 # This script is called automatically during `make install'
 # unless specified otherwise.
 
-# For usage and and more information see zshcontrib(1).
+# For usage and more information see zshcontrib(1).
 
 sub Usage {
     print(STDERR "Usage: helpfiles zshbuiltins.1 dest-dir [link-file]\n");
diff --git a/configure.ac b/configure.ac
index 9ce3a4589..88da89e3e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1305,10 +1305,11 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \
 	       setlocale \
 	       uname \
 	       signgam tgamma \
+	       scalbn \
 	       putenv getenv setenv unsetenv xw\
 	       brk sbrk \
 	       pathconf sysconf \
-	       tgetent tigetflag tigetnum tigetstr setupterm initscr \
+	       tgetent tigetflag tigetnum tigetstr setupterm initscr resize_term \
 	       getcchar setcchar waddwstr wget_wch win_wch use_default_colors \
 	       pcre_compile pcre_study pcre_exec \
 	       nl_langinfo \
@@ -1322,7 +1323,9 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \
 	       realpath canonicalize_file_name \
 	       symlink getcwd \
 	       cygwin_conv_path \
-	       nanosleep)
+	       nanosleep \
+	       srand_deterministic \
+	       setutxent getutxent endutxent getutent)
 AC_FUNC_STRCOLL
 
 AH_TEMPLATE([REALPATH_ACCEPTS_NULL],
@@ -2033,6 +2036,8 @@ AC_CACHE_VAL(zsh_cv_cs_path,
   zsh_cv_cs_path=`getconf _CS_PATH`
 elif getconf CS_PATH >/dev/null 2>&1; then
   zsh_cv_cs_path=`getconf CS_PATH`
+elif getconf PATH >/dev/null 2>&1; then
+  zsh_cv_cs_path=`getconf PATH`
 else
   zsh_cv_cs_path="/bin:/usr/bin"
 fi])
@@ -2305,21 +2310,6 @@ AH_TEMPLATE([HAVE_FIFOS],
 if test x$zsh_cv_sys_fifo = xyes; then
   AC_DEFINE(HAVE_FIFOS)
 fi
-dnl ---------------------
-dnl echo style of /bin/sh
-dnl ---------------------
-AC_CACHE_CHECK(if echo in /bin/sh interprets escape sequences,
-zsh_cv_prog_sh_echo_escape,
-[if test "`/bin/sh -c \"echo '\\n'\"`" = "\\n"; then
-  zsh_cv_prog_sh_echo_escape=no
-else
-  zsh_cv_prog_sh_echo_escape=yes
-fi])
-AH_TEMPLATE([SH_USE_BSD_ECHO],
-[Define to 1 if /bin/sh does not interpret \ escape sequences.])
-if test x$zsh_cv_prog_sh_echo_escape = xno; then
-  AC_DEFINE(SH_USE_BSD_ECHO)
-fi
 
 dnl -----------
 dnl test for whether link() works
@@ -2587,16 +2577,33 @@ wmemcpy wmemmove wmemset; do
 ])
 AH_TEMPLATE([MULTIBYTE_SUPPORT],
 [Define to 1 if you want support for multibyte character sets.])
-AH_TEMPLATE([BROKEN_WCWIDTH],
-[Define to 1 if the wcwidth() function is present but broken.])
+
+dnl
+dnl unicode9 support
+dnl
+AH_TEMPLATE([ENABLE_UNICODE9],
+[Define to 1 if you want use unicode9 character widths.])
+AC_ARG_ENABLE(unicode9,
+AC_HELP_STRING([--enable-unicode9], [compile with unicode9 character widths]),
+[if test x$enableval = xyes; then
+  AC_DEFINE(ENABLE_UNICODE9)
+fi])
+
 AH_TEMPLATE([BROKEN_ISPRINT],
 [Define to 1 if the isprint() function is broken under UTF-8 locale.])
+
 if test x$zsh_cv_c_unicode_support = xyes; then
   AC_DEFINE(MULTIBYTE_SUPPORT)
 
-  dnl Test for a wcwidth() implementation that gives the wrong width for
-  dnl zero-width combining characters.
-  dnl For the test we use a combining acute accent (\u0301).
+  dnl Test if wcwidth() and/or iswprint() is broken for
+  dnl   zero-width combining characters, or
+  dnl   some characters in the Latin Extended-B.
+  dnl If either of the functions is broken, both functions will be replaced
+  dnl by the ones from wcwidth9.h by defining ENABLE_UNICODE9. We will do
+  dnl this only if __STDC_ISO_10646__ is defined (or if building on macOS,
+  dnl where __STDC_ISO_10646__ is not defined but wchar_t is UCS).
+  dnl For the test we use a combining acute accent (\u0301) or
+  dnl a LATIN SMALL LETTER L WITH CURL (\u0234).
   dnl We input it as UTF-8 since that is the standard we can rely
   dnl upon most:  we can't rely on a wchar_t being stored as a
   dnl Unicode code point on all systems.
@@ -2605,9 +2612,8 @@ if test x$zsh_cv_c_unicode_support = xyes; then
   dnl - the programme compiled, linked and ran
   dnl - we successfully set a UTF-8 locale
   dnl - the locale we set plausibly converted the UTF-8 string
-  dnl   for a zero-width combining character (the only way to be
-  dnl   100% sure would be to output it and ask if it looked right)
-  dnl - the converted wide character gave a non-zero width.
+  dnl   into the correct wide character
+  dnl - but wcwidth() or iswprint() is broken for the converted wide character.
   dnl locale -a is a fallback; on most systems we should find en_US.UTF-8.
   [locale_prog='char *my_locales[] = {
   "en_US.UTF-8", "en_GB.UTF-8", "en.UTF-8", '
@@ -2619,32 +2625,38 @@ if test x$zsh_cv_c_unicode_support = xyes; then
   #include <stdlib.h>
   #include <locale.h>
   #include <wchar.h>
+  #include <wctype.h>
 
   int main() {
     char **localep;
     char comb_acute_mb[] = { (char)0xcc, (char)0x81 };
+    char u_0234[] = { (char)0xc8, (char)0xb4 };
     wchar_t wc;
+  #if !defined(__STDC_ISO_10646__) && !defined(__APPLE__)
+    return 1;
+  #endif
 
     for (localep = my_locales; *localep; localep++)
-      if (setlocale(LC_ALL, *localep) &&
-          mbtowc(&wc, comb_acute_mb, 2) == 2)
+      if (setlocale(LC_ALL, *localep))
 	  break;
     if (!*localep)
       return 1;
-    if (wcwidth(wc) == 0)
-      return 1;
-    return 0;
+    if (mbtowc(&wc, comb_acute_mb, 2) == 2 && (wcwidth(wc) != 0 || !iswprint(wc)))
+      return 0;
+    if (mbtowc(&wc, u_0234, 2) == 2 && (wcwidth(wc) != 1 || !iswprint(wc)))
+      return 0;
+    return 1;
   }
   "]
 
-  AC_CACHE_CHECK(if the wcwidth() function is broken,
+  AC_CACHE_CHECK(if the wcwidth() and/or iswprint() functions are broken,
   zsh_cv_c_broken_wcwidth,
   [AC_TRY_RUN([$locale_prog],
   zsh_cv_c_broken_wcwidth=yes,
   zsh_cv_c_broken_wcwidth=no,
   zsh_cv_c_broken_wcwidth=no)])
   if test x$zsh_cv_c_broken_wcwidth = xyes; then
-    AC_DEFINE(BROKEN_WCWIDTH)
+    AC_DEFINE(ENABLE_UNICODE9)
   fi
 
   dnl Check if isprint() behaves correctly under UTF-8 locale.