diff options
author | Axel Beckert <abe@deuxchevaux.org> | 2015-08-22 01:55:58 +0200 |
---|---|---|
committer | Axel Beckert <abe@deuxchevaux.org> | 2015-08-22 01:55:58 +0200 |
commit | 02f6e25bfcd5feb9a093377dda0dd549cdf5c309 (patch) | |
tree | 9a25e61122b3fa0d0a1ff68b5ef05c775ff78b1e | |
parent | e04a19735ffc8523b93b33074f685ad4e2c92e0c (diff) | |
parent | 881474edcb223ac22a08d81a824809c33ca3a9c9 (diff) | |
download | zsh-02f6e25bfcd5feb9a093377dda0dd549cdf5c309.tar.gz zsh-02f6e25bfcd5feb9a093377dda0dd549cdf5c309.zip |
Merge tag 'zsh-5.0.8-test-2' into debian
186 files changed, 7610 insertions, 1956 deletions
@@ -1,3 +1,1064 @@ +2015-08-21 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * unposted: Config/version.mk, Src/parse.c, Src/Zle/zle_misc.c: + update to 5.0.8-test-2 and fix some exports. + +2015-08-21 Peter Stephenson <p.stephenson@samsung.com> + + * 36264: Src/glob.c: pathbuf is apparently metafied; document + this and unmetafy for system calls. + + * unposted: README, Etc/FAQ.yo: update latest release from 5.0.8 + to 5.1 in descriptive text. + + * 36262: Src/builin.c, Src/compat.c, Src/utils.c, + Test/D07multibyte.ztst: replace 36232: the unmeta() + was needed at a place higher up. Add test. + + * 36250, tweaked: README, NEWS: highlight bracketed paste mode; + next version will be 5.1 rather than 5.0.9. + +2015-08-20 Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp> + + * 36243: Completion/Unix/Type/_pids: fix a few problems of + "_pids -m pattern" + +2015-08-19 Daniel Shahaf <d.s@daniel.shahaf.name> + + * 36247: Completion/Unix/Command/_git: __git_objects: Complete + HEAD:./foo correctly in worktree subdir + + * 36237: Completion/Unix/Command/_git: __git_objects: Complete + HEAD:foo correctly in worktree subdir + + * 36236: Completion/Unix/Command/_git: _git-log: Complete + multiple revspecs + + * 36236: Completion/Unix/Command/_git: _git-log: complete + 'git rm'd files + +2015-08-19 Oliver Kiddle <opk@zsh.org> + + * 36148: Completion/Unix/Command/_git: _git-log: Complete flags + after positional argument + +2015-08-19 Peter Stephenson <p.stephenson@samsung.com> + + * 36241: Test/V09datetime.ztst: Test for 36227. + +2015-08-19 Daniel Shahaf <d.s@daniel.shahaf.name> + + * 36208: Completion/Unix/Command/_subversion: _subversion: + Complete a few more option switches + +2015-08-18 Peter Stephenson <p.stephenson@samsung.com> + + * 36232: Src/compat.c: Unmeta an additional chdir(). + + * 36227: Src/Builtins/sched.c, Src/Modules/datetime.c, + Src/Modules/stat.c, Src/builtin.c, Src/prompt.c, Src/utils.c, + Src/watch.c: real fix for metafication problem in ztrftime: + unmetafy fmt on input and metafy return value with correct + length. + + * unposted: revert 36222, not the correct fix. + + * 36222: Src/Modules/datetime.c: unmetafy output from strftime. + + * 36218: Src/files.c: use unmetafied file name for mkdir() + system call. + + * 36216: Doc/Zsh/zle.yo: Simpler saving of UNDO_LIMIT_NO. + +2015-08-17 Barton E. Schaefer <schaefer@zsh.org> + + * 36212: Functions/Zle/narrow-to-region: save and restore HISTNO + along with other editor state, for sanity of "zle undo" + +2015-08-18 brian m. carlson <sandals@crustytoothpaste.net> + + * 36209: Functions/VCS_Info/Backends/VCS_INFO_get_data_git: + vcs_info: avoid grep error message when file is missing + +2015-08-17 Daniel Shahaf <d.s@daniel.shahaf.name> + + * unposted: Completion/Linux/Command/_sshfs: _sshfs: Fix + completion of options with values + + * 36158: Completion/Linux/Command/_sshfs: _sshfs: Fix completion + of and after -o + + * 36109: Src/Zle/zle_misc.c: bracketed-paste: change quoting + style + + * 36186: Etc/FAQ.yo: FAQ (3.24): Update for bracketed paste + +2015-08-17 Mikael Magnusson <mikachu@gmail.com> + + * 36172: Completion/Unix/Command/_imagemagick: Use $formats a + bit more correctly + +2015-08-17 Barton E. Schaefer <schaefer@zsh.org> + + * unposted (cf. 36200): Completion/Base/Completer/_expand: back out + backslash handling change from 34961. + +2015-08-17 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * unposted: Doc/Zsh/contrib.yo: use of $f in zmv. + +2015-08-17 Oliver Kiddle <opk@zsh.org> + + * 36165: Completion/Unix/Type/_files: simplify file-patterns + default to a single pattern + +2015-08-17 Barton E. Schaefer <schaefer@zsh.org> + + * unposted: Functions/Zle/url-quote-magic: mention + bracketed-paste-magic in comment + +2015-08-17 Mikael Magnusson <mikachu@gmail.com> + + * 36177: Completion/Zsh/Command/_setopt: complete printexitvalue + both ways + + * 36183: Completion/Unix/Command/_ssh: update to 7.0 + +2015-08-16 Barton E. Schaefer <schaefer@zsh.org> + + * 36195, 36196: Functions/Zle/bracketed-paste-magic: wrapper of + bracketed-paste for compatibilty with url-quote-magic and other + self-insert wrapper widgets + +2015-08-15 Barton E. Schaefer <schaefer@zsh.org> + + * 36180: Src/jobs.c: avoid infinite job stop/continue loop on + "wait PID" for a background job + +2015-08-15 Mikael Magnusson <mikachu@gmail.com> + + * Eric Cook: 36091: Completion/Unix/Command/_ncftp: search + XDG_DATA_HOME for bookmarks saved by lftp + +2015-08-14 Oliver Kiddle <opk@zsh.org> + + * 36146: Completion/Zsh/Type/_command_names, + Completion/Zsh/Command/_zmodload: avoid mix of -/ and -g with + _files as that just overrides user preferences + +2015-08-13 Daniel Shahaf <d.s@daniel.shahaf.name> + + * unposted: Completion/Unix/Command/_subversion: _subversion: + Complete property names for propdel + +2015-08-13 Oliver Kiddle <opk@zsh.org> + + * 36131: Functions/Zle/narrow-to-region, Src/Zle/zle_utils.c: + make use of undo limits; call mkundoent() when $UNDO_CHANGE_NO is + referenced for a clear change number marking the current state + + * Eric Cook: 36113: Completion/Unix/Type/_find_net_interfaces: + ip(8) may add suffixes which is not good for completion matches + + * Wieland Hoffmann: 36123: Completion/Unix/Command/_git: + protect against word splitting in __git_is_committish_range + +2015-08-12 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * Kamil Dudka: 36106: Src/Zle/zle_keymap.c: fix const parameter. + +2015-08-12 Oliver Kiddle <opk@zsh.org> + + * 36127: Completion/Zsh/Context/_brace_parameter, + Completion/Unix/Command/_git, Completion/Unix/Command/_ifconfig, + Completion/Unix/Command/_kvno, Completion/Unix/Command/_mh, + Completion/Unix/Command/_rm, Completion/Unix/Command/_ssh, + Completion/Unix/Command/_stty, Completion/Unix/Command/_vim, + Completion/Unix/Type/_pdf, Completion/Unix/Command/_clay: + assorted minor completion function changes + + * 36125: Src/Zle/zle_hist.c: don't set history context in get-line + + * 35834 (tweaked): Src/Zle/zle_misc.c: strip a final newline from + pasted text: inserting is hard to tell apart from accepting it + +2015-08-12 Mikael Magnusson <mikachu@gmail.com> + + * 36077: Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr, + Functions/VCS_Info/Backends/VCS_INFO_get_data_hg, + Functions/VCS_Info/Backends/VCS_INFO_get_data_p4, + Functions/VCS_Info/Backends/VCS_INFO_get_data_svk, + Functions/VCS_Info/Backends/VCS_INFO_get_data_svn, + Functions/VCS_Info/VCS_INFO_formats, + Functions/VCS_Info/VCS_INFO_hook, + Functions/VCS_Info/VCS_INFO_quilt, + Functions/VCS_Info/VCS_INFO_set, Functions/VCS_Info/vcs_info, + Functions/VCS_Info/vcs_info_lastmsg, + Functions/VCS_Info/vcs_info_printsys: vcs_info: stop exporting + everything + + * 36078: Doc/Zsh/contrib.yo: vcs_info: Adjust documentation for + no longer exporting + + * 36116: Completion/Unix/Command/_git: various fixes + + * 36117: Completion/Unix/Command/_wget: complete headers for + --header and add --no-use-server-timestamps + + * 36120: Completion/Unix/Command/_sort: Fix syntax error + + * 36119: Completion/Zsh/Command/_strftime: Add completion for + zsh/datetime's strftime builtin + +2015-08-11 Barton E. Schaefer <schaefer@zsh.org> + + * 36108: NEWS: list of major changes so far since 5.0.8 + + * 36104: Src/exec.c, Src/jobs.c: change order of child_block() + and dont_queue_signals() to resolve yet another race condition + + * 36092: Doc/Zsh/mod_zpty.yo, Src/Modules/zpty.c: return the pty + master file descriptor in $REPLY for use with "zle -F" etc. + + * 36090: Src/init.c: keep signals queued for preprompt() + +2015-08-11 Oliver Kiddle <opk@zsh.org> + + * 36096: Src/Zle/zle_move.c: special . mark in vi mode + + * 36044: Doc/Zsh/zle.yo, Src/Zle/iwidgets.list, Src/Zle/zle_keymap.c, + Src/Zle/zle_move.c: deactivate-region widget for escape in visual mode + + * 35952: Jun-ichi Takimoto: Src/Zle/zle_bindings.c: bind escape in + vi mode so it is a full key sequence and later keys aren't dropped + +2015-08-11 Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp> + + * 36067: Doc/Zsh/builtins.yo, Doc/Zsh/compsys.yo, + Doc/Zsh/contrib.yo, Doc/Zsh/expn.yo: fix typo and format + +2015-08-10 Barton E. Schaefer <schaefer@zsh.org> + + * 36084: Src/glob.c, Src/text.c: use zrealloc() consistently + + * 36079: Src/signals.c: do not allow update_job() and its helpers + to run the signal queue while we are processing a job exit. + +2015-08-10 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * 36083: Src/builtin.c, Test/B02typeset.ztst: set array value + when consistently retying scalar and array. + +2015-08-10 Peter Stephenson <p.stephenson@samsung.com> + + * 36074: Src/signals.c: Don't rely on implicit value for + saving status of background process. + +2015-08-10 Frank Terbeck <ft@bewatermyfriend.org> + + * 36046: Completion/Unix/Command/_tmux: _tmux: Update command line + options + + * 36048: Completion/Unix/Command/_tmux: _tmux: Update options for + supported commands + + * 36062: Completion/Unix/Command/_tmux: _tmux: Add support for new + sub-commands + + * 36063: Completion/Unix/Command/_tmux: _tmux: Remove dead code + + * 36050: Completion/Unix/Command/_tmux: _tmux: Don't unset, set + empty in local scope + + * 36058: Completion/Unix/Command/_tmux: _tmux: No need to unset + local variables + + * 36057: Completion/Unix/Command/_tmux: _tmux: Replay all + arguments when dispatching to new _tmux() + + * 36054: Completion/Unix/Command/_tmux: _tmux: "local -x" serves + no purpose + + * 36049: Completion/Unix/Command/_tmux: _tmux: options => + session_options + + * 36056: Util/check-tmux-state: Add helper script to check state + of _tmux completion + + * 36052: Completion/Unix/Command/_tmux: _tmux: Remove old + sub-commands and their aliases + + * 36064: Completion/Unix/Command/_tmux: _tmux: Add new command + aliases + + * 36047: Completion/Unix/Command/_tmux: _tmux: Fix options with + changed scope + + * 36051: Completion/Unix/Command/_tmux: _tmux: Remove support for + old options + + * 36059: Completion/Unix/Command/_tmux: _tmux: Add new session + options + + * 36055: Completion/Unix/Command/_tmux: _tmux: Add support for new + server options + + * 36061: Completion/Unix/Command/_tmux: _tmux: Add support for new + window options + + * 36060: Completion/Unix/Command/_tmux: _tmux: Update TODO + + * 36070: Completion/Unix/Command/_tmux: _tmux: Update bell-action + and prefix options + + * 36069: Completion/Unix/Command/_tmux: _tmux: Fix \ooo display in + completion list + + * 36075: Util/check-tmux-state: Disable "local" keyword in + script to make data retrieval work + +2015-08-10 Peter Stephenson <p.stephenson@samsung.com> + + * 36045: Test/A05execution.ztst: make effect of failures + in wait test cleare. + + * not quite posted: Src/Modules/socket.c, Src/Modules/tcp.c: + check for error return before testing for EINTR. + + * Joshua Krusell: 36039: Src/Modules/socket.c, + Src/Modules/tcp.c: Restart socket commands on EINTR. + + * 35885: Doc/Zsh/mod_system.zo: document how to close + fd's opened with sysopen. + +2015-08-09 Barton E. Schaefer <schaefer@zsh.org> + + * 36033: Src/glob.c, Src/pattern.c: a few more queue_signals() + to protect global state changes + +2015-08-09 Mikael Magnusson <mikachu@gmail.com> + + * unposted: Test/C03traps.ztst: add tests for outputting traps + specified with SIG prefix or numbers fixed by 36008. + +2015-08-09 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * 26027: Src/builtin.c: fix retying variables in opposite + order (should be an error). + + * 36025:Src/Zle/zle_tricky.c: Catch another case of context in + get_comp_string(): after "foo=bar; setopt " we didn't recognise + "setopt" as the command. Attempt to do this without actually + understanding what the command context code is attempting to do + after a command separator. + +2015-08-09 Barton E. Schaefer <schaefer@zsh.org> + + * 36026: Src/Modules/zftp.c: zero freed pointers in cleanup_() + in case the module is re-loaded after unloading. + + * 36022: Src/loop.c: fix bug that some loop constructs could + not be interrupted if all they did was variable assignments or + math expressions + + * 36022: Src/exec.c, Src/init.c, Src/input.c, Src/parse.c, + Src/signals.c: revise signal queueing to better control the + circumtances in which trap handlers are executed + +2015-08-08 Daniel Shahaf <d.s@daniel.shahaf.name> + + * 36008: Src/builtin.c: trap: Fix listing of traps created + under non-alias non-canonical signal spelling + +2015-08-08 Mikael Magnusson <mikachu@gmail.com> + + * 35954: Doc/Zsh/mod_system.yo: sysopen -u is not optional + +2015-08-09 Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp> + + * 35929: Src/jobs.c: protect FDT_PROC_SUBST by #ifdef + +2015-08-08 Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp> + + * 35928: Src/utils.c, Doc/Zsh/prompt.yo: date/time format such + as '%-m' should work even if strftime(3) doesn't support it. + Also clarify document. + +2015-08-05 Mikael Magnusson <mikachu@gmail.com> + + * 35989: Completion/Unix/Command/_ssh: update to 6.9 + + * unposted: Completion/Unix/Command/_ssh: use -q for the + option suffix + + * Christian Hesse: 35991: Completion/Unix/Command/_ssh: add ssh + option FingerprintHash + +2015-08-05 Oliver Kiddle <opk@zsh.org> + + * Eric Cook: 35973: Completion/BSD/Command/_systat, + Completion/Unix/Command/_vmstat: new completions + + * Eric Cook: 35972: Completion/Unix/Command/_ssh: allow for + Directive=Value format in .ssh/config + + * Christian Hesse: 35986: Completion/Unix/Command/_ssh: + add ssh option UpdateHostKeys + + * unposted (c.f. 35902): Functions/Zle/incremental-complete-word: + use - after zle -U in case $key starts with - + +2015-08-04 Daniel Shahaf <d.s@daniel.shahaf.name> + + * unposted: Src/builtin.c: Typo fix. + +2015-08-02 Daniel Hahler <zsh@thequod.de> + + * 35970: Completion/Unix/Command/_git, + Completion/Zsh/Context/_brace_parameter: completion: fix typos in _git + and _brace_parameter. + +2015-08-01 Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp> + + * 35957: Completion/Unix/Command/_make: fix _make-expandVars(), + and use variables set in the command line and environment. + +2015-07-31 Oliver Kiddle <opk@zsh.org> + + * 35963: Src/Modules/system.c: simplify condition found by + coverity scan to include tautology + + * Matthew Martin: 35960 (tweaked): Completion/Unix/Command/_find: + complete correct options for OpenBSD's find + +2015-07-29 Barton E. Schaefer <schaefer@zsh.org> + + * 35953: Src/lex.c, Test/A01grammar.ztst: fix handling of command + substitution in math context, particularly in "for ((...))" + +2015-07-28 Barton E. Schaefer <schaefer@zsh.org> + + * 35947: Etc/zsh-development-guide: update discussion of module + wrappers, some examples + +2015-07-27 Barton E. Schaefer <schaefer@zsh.org> + + * 35937: Functions/Misc/zargs: wait for process IDs instead of + job numbers in parallel mode, in case zargs is in a pipeline + +2015-07-26 Daniel Shahaf <d.s@daniel.shahaf.name> + + * 35274 (plus tweaks): Completion/BSD/Command/_watch-snoop + Completion/Unix/Command/_watch: completion: Add FreeBSD's + watch(1) + +2015-07-26 Oliver Kiddle <opk@zsh.org> + + * 35901: Completion/X/Type/_x_font: complete full names as removing + later components was broken for aliases and unwanted in any case + +2015-07-26 Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp> + + * Antony Lee: 35899: Completion/Unix/Type/_python_modules: + faster method of generating list of python modules + +2015-07-25 Barton E. Schaefer <schaefer@zsh.org> + + * unposted: Functions/Prompts/prompt_bart_setup: add RPS2 handling, + update help text, clean up indentation whitespace + +2015-07-25 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * 35910: Src/input.c: Don't add to raw lex buffer if lex + stopped. Fixes crash on completion of incomplete + math expresion e.g. '$((3*4)'. + + * 35909: Src/utils.c: fix $((...) completion in _expand by + normalising quoting of the math expression containing tokens. + + * 35908: Src/ZLe/zle_tricky.c: fix $((...)) completion + by expand-or-complete widget. + +2015-07-24 Barton E. Schaefer <schaefer@zsh.org> + + * 35896: Doc/Zsh/params.yo, Doc/Zsh/zle.yo: index zle_highlight + + * 35882: Src/Zle/iwidgets.list: read-command has ZLE_NOTCOMMAND + +2015-07-24 Daniel Shahaf <d.s@daniel.shahaf.name> + + * 35810: Etc/FAQ.yo: FAQ += edit-command-line + + * 35809: Util/zyodl.vim: zyodl.vim: Fix nested parentheses + + * 35807: Functions/Zle/edit-command-line: edit-command-line: + Fix for non-7bit-data (after 35769) + +2015-07-24 Oliver Kiddle <opk@zsh.org> + + * 35865: Doc/Zsh/prompt.yo: document %^ prompt expansion + + * 35864: Doc/Zsh/mod_system.yo, Src/Modules/system.c, + Src/Modules/system.mdd: add sysopen, sysseek and systell to + system module + + * 35879: Eric Cook: Completion/Linux/Command/_btrfs: fix for + subcommands that don't have a second subcommand + +2015-07-24 Peter Stephenson <p.stephenson@samsung.com> + + * 35883: Src/hist.c: Don't attempt to back up over history if + expanding alias. + +2015-07-23 Peter Stephenson <p.stephenson@samsung.com> + + * 35873: Test/D03procsubst.ztst: add original test from 35847: + + * 35872: Src/utils.c: protect against NULL pointers in unmeta(). + + * 35849: Src/exec.c, Src/jobs.c, Test/D03procsubst.ztst: close + file descriptors from process substitution in parent after + fork. + + * 35854: Stc/hist.c: ensure character unget doesn't cause + infinite recursion. + + * 35793: Src/zsh.h: avoid undefined behaviour shifting signed + number. + +2015-07-23 Oliver Kiddle <opk@zsh.org> + + * 35753: Completion/Zsh/Type/_ps1234: allow for numbers after + the ( in ternary expressions and fix backslash quoting + + * 35867: Completion/Unix/Command/_patchutils: new completion + + * 35866: Completion/Unix/Command/_gzip: complete also for pigz + + * 35866: Completion/Unix/Command/_zcat: allow for NetBSD gzip + + * 35824: Doc/Zsh/zle.yo, Src/Zle/iwidgets.list, Src/Zle/zle.h, + Src/Zle/zle_misc.c, Src/Zle/zle_refresh.c: allow highlighting of + just pasted text, put text from bracketed paste in cut buffers + + * 35815: Functions/Misc/nslookup: fix for newer nslookup + + * 35814: Src/Zle/zle_main.c: POSTEDIT needs to be unmetafied + +2015-07-22 Barton E. Schaefer <schaefer@zsh.org> + + * 35839: Joshua Krusell <js.shirin@gmail.com>: + Src/Modules/socket.c, Src/Modules/tcp.c: fix select polling in + ztcp and zsocket + + * unposted: Completion/Zsh/Type/_directory_stack: move some more + recent code so an old comment is connected to the correct bit of + older code + + * users/20324: Completion/Zsh/Type/_directory_stack: add (D) flag + for tilde-fied completion listing + + * 35826: Src/Modules/newuser.c, Src/Modules/zftp.c, + Src/Zle/complist.c, Src/Zle/zle_misc.c, Src/init.c, Src/params.c, + Src/utils.c, Src/watch.c: add getsparam_u() to return unmetafied + string, use it for a number of references to non-special params + + * 35823: Functions/Zle/narrow-to-region: fix handling of MARK + and CURSOR, clean up documentary comment + +2015-07-22 Barton E. Schaefer <schaefer@zsh.org> + + * unposted: Test/B02typeset.ztst: fix another test for 35581 + + * 35582: Test/A06assign.ztst, Test/B02typeset.ztst: test for 35581 + + * 35581: Src/params.c: output array assignments with spaces inside + the parens + +2015-07-16 Barton E. Schaefer <schaefer@zsh.org> + + * 35805: Test/E01options.ztst: tests for 35799 + +2015-07-15 Barton E. Schaefer <schaefer@zsh.org> + + * 35799: Src/params.c: with NO_EXEC, parse parameter subscript + expressions to correctly balance enclosing braces, but do not + perform the subscripting + +2015-07-15 Mikael Magnusson <mikachu@gmail.com> + + * unposted: Completion/Zsh/Type/_ps1234: unposted: _ps1234: + Fix ternary test char completion + +2015-07-14 Oliver Kiddle <opk@zsh.org> + + * 35789: (tweaked c.f. Bart: 35791) Completion/Unix/Type/_files: + duplicate glob qualifier handling from _path_files so it is + called once and not for each file pattern + +2015-07-13 Daniel Shahaf <d.s@daniel.shahaf.name> + + * 35790: Util/zyodl.vim: zyodl.vim: Be compatible with Vim 6. + + * 35775: Doc/Zsh/.vimrc Etc/zsh-development-guide Util/zyodl.vim: + New zsh-specific yodl vim syntax highlighting. + +2015-07-13 Daniel Shahaf <d.s@daniel.shahaf.name> + + * 35769: Functions/Zle/edit-command-line: edit-command-line: + Go to the right byte offset + +2015-07-13 Oliver Kiddle <opk@zsh.org> + + * 35737: (tweaked c.f. Peter: 35759): Src/Zle/zle_utils.c, + Functions/Zle/read-from-minibuffer: use new undo limit for + minibuffer and beep when limit is reached + + * 35756: Completion/Zsh/Type/_ps1234: use the actual + colours in the completion list for terminal colours + + * 35774: Completion/Unix/Type/_dates: fix double + formatting of date explanation message + +2015-07-13 Daniel Hahler <zsh@thequod.de> + + * 35779: Completion/Unix/Command/_git: _git-config: remove + extraneous "local expl". + +2015-07-12 Daniel Shahaf <d.s@daniel.shahaf.name> + + * unposted: Test/B01cd.ztst: Typo + +2015-07-10 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * unposted: Test/B01cd.ztst: notes on ZTST_unimplemented: and + ZTST_skip. + +2015-07-10 Mikael Magnusson <mikachu@gmail.com> + + * 35761: Test/V09datetime.ztst: Make sure to skip alternate + format extension test properly + +2015-07-10 Peter Stephenson <p.stephenson@samsung.com> + + * unposted: another ZTST_skip. + + * 35760: Test/A01grammar.ztst, Test/C02cond.ztst, + Test/V09datetime.ztst, Test/ztst.zsh: ZTST_skip + can be used to skip test case. + +2015-07-09 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * 35708: Doc/Zsh/zle.yo, Src/Zle/zle_params.c, + Src/Zle/zle_utils.c; UNDO_LIMIT_NO. + +2015-07-09 Peter Stephenson <p.stephenson@samsung.com> + + * 35751: Src/exec.c, Test/C03traps.ztst: fix ERR_RETURN / + ERR_EXIT in else branch of if. + +2015-07-09 Mikael Magnusson <mikachu@gmail.com> + + * 35745: Src/utils.c, Test/V09datetime.ztst: ztrftime: Pass + everything unhandled to the system strftime() + + * unposted: (c.f. Jun T: 35754): Test/V09datetime.ztst: Make + V09 set a known timezone + +2015-07-09 Oliver Kiddle <opk@zsh.org> + + * 35748: Completion/Zsh/Type/_ps1234, + Completion/Zsh/Command/_print: fixes to prompt spec completion + where it contains date formats + + * 35749: (c.f. Jun T: 35732): Completion/Unix/Type/_date_formats: + fix exclusion list for flags/modifiers + +2015-07-08 Oliver Kiddle <opk@zsh.org> + + * 35729: Completion/Zsh/Command/_compadd: new compadd completion + + * Matthew Martin: 35727: Completion/Unix/Type/_diff_options: + support OpenBSD for diff options + +2015-07-07 Oliver Kiddle <opk@zsh.org> + + * 35700: Completion/Unix/Type/_date_formats: complete OS specific + date format specifiers and put % in a prefix + + * 35718: Completion/Unix/Command/_find, Completion/Unix/Type/_dates, + Completion/Zsh/Type/_globquals: new calendar style date completion + +2015-07-07 Peter Stephenson <p.stephenson@samsung.com> + + * unposted: Src/Zle/complete.c: remove compiler warning. + +2015-07-06 Barton E. Schaefer <schaefer@zsh.org> + + * unposted: Doc/Zsh/expn.yo: alphabetize ${(b)...} flag + + * 35709: Doc/Zsh/expn.yo: explain :s/// modifier contexts + + * 35694: Src/subst.c, Test/D04parameter.ztst: fix handling of + history modifiers (specifically :s/l/r/) when applied across all + elements of an array parameter value + +2015-07-06 Oliver Kiddle <opk@zsh.org> + + * 35704: Doc/Zsh/compwid.yo, Src/Zle/compcore.c, Src/Zle/complete.c: + compadd -E 0 should imply -J and -2 + +2015-07-06 Mikael Magnusson <mikachu@gmail.com> + + * 35696: Completion/Unix/Type/_ps1234: complete colors for %F{ + and %K{ + + * 35697: Completion/Unix/Type/_date_formats, + Completion/Unix/Type/_ps1234: Complete strftime formats for %D{} + +2015-07-06 Daniel Shahaf <d.s@daniel.shahaf.name> + + * 35441: Etc/zsh-development-guide: Make devs' ChangeLog + generator scripts discoverable + +2015-07-05 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * 35692: Src/Zle/computil.c: cfp_matcher_range used wrong raw + character if it couldn't be converted to wchar_t. + + * 35688: Src/text.c: Slightly more nearly definitive version of + turning wordcode case statements back to text. + +2015-07-04 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * 35682: Src/text.c, plus Test/A01grammar.ztst: turning case + statements parsed as multiple alternatives back into text. + +2015-07-03 Oliver Kiddle <opk@zsh.org> + + * 35681: Src/Modules/datetime.c: fix day of month initialisation + + * 35674: Src/Zle/zle_main.c: make an undo event for + initial buffer contents + +2015-07-03 Mikael Magnusson <mikachu@gmail.com> + + * 35673: Completion/Zsh/Function/_zargs: Improve _zargs + +2015-07-02 Peter Stephenson <p.stephenson@samsung.com> + + * Han Pingtian: 35665: Src/Zle/complete.c: better error handling + for command line match processing. + + * Eric Cook: 35663: Completion/Unix/Command/_zpool: fix for + changed interface. + + * 35668: Src/input.c, Src/lex.c, Src/zsh.h, + Test/D03procsubst.ztst: replacement for 35667: + use input flag to stop unwanted backtracking, + additional test. + +2015-07-01 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * 35667: Src/lex.c, Src/zsh.h, Test/D03procsubst.ztst, + Test/D08cmdsubst.ztst: fix command expansion which + starts but does not finish within alias. + +2015-06-29 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * unposted: Doc/Zsh/options.yo: fix formatting typo. + + * 35655: Doc/Zsh/options.yo, Src/exec.c, Src/options.c, + Src/zsh.h: APPEND_CREATE option gives POSIX compatible behaviour + for NO_CLOBBER. + +2015-06-28 Barton E. Schaefer <schaefer@zsh.org> + + * 35660: Functions/Prompts/prompt_adam1_setup: avoid perl, fix help + +2015-06-29 Oliver Kiddle <opk@zsh.org> + + * 35637: Src/Zle/zle.h, Src/Zle/zle_main.c, Src/Zle/zle_tricky.c: + remove the now unused module hook for reverse-menu-complete + + * 35627: Src/Zle/compcore.c, Src/Zle/compctl.c, Src/Zle/complete.c, + Src/Zle/complist.c, Src/Zle/compresult.c, Src/Zle/zle_tricky.c: + make reverse-menu-complete start with last match in menu selection + + * 35623: Src/Zle/complist.c: fix menu-selection where initial + selection would not be displayed without scrolling + +2015-06-28 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * 35643: Src/parse.c: redirections after typeset assignments + were broken. + +2015-06-27 Barton E. Schaefer <schaefer@zsh.org> + + * 35642: Src/context.c: signal queueing in zcontext_save_partial() + + * 35634: Src/exec.c: handle failure of gettempname() in namedpipe() + +2015-06-27 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * 35635: Src/builtin.c: a bit more consistency about + making elements of things local. + + * users/20281: Completion/Base/Core/_main_complete: + don't require MULTIFUNCDEF as _main_complete is parsed + before we can ensure it's set. + + * 35623: Src/builtin.c, Src/zsh.h, Test/B02typeset.ztst: 35610 + was incomplete: all is_array asignments should be treated as + having a value. + +2015-06-26 Barton E. Schaefer <schaefer@zsh.org> + + * 35626: Doc/Zsh/options.yo: update POSIX_CD to note that it is on + in ksh and sh emulations + +2015-06-26 Peter Stephenson <p.stephenson@samsung.com> + + * 35615: Src/lex.c, Src/Zle/zle_tricky.c: + fix completion after typeset. + + * 35613: Src/builtin.c, Test/B02typeset: handle + array slices in typeset. + +2015-06-25 Peter Stephenson <p.stephenson@samsung.com> + + * 35610: Src/builtin.c: typeset is silent if + arguments is x=() even if array x already exists. + + * 35604: Src/exec.c, Test/B02typeset: create + empty arrays in typeset with array=(). + +2015-06-24 Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp> + + * 35550: Src/Zle/complist.c: fix backspace in interactive + mode of menu-select + +2015-06-24 Peter Stephenson <p.stephenson@samsung.com> + + * 35514: Han Pingtian: Src/pattern.c: [[:foo:]] + tests in completion weren't specific enough about + foo. + + * 35590: Src/text.c, Test/B02typeset.ztst: tweak and + test text output for typeset. + + * various culminating in 35586, c.f. commits on typeset-array + branch: Config/version.mk, Doc/Zsh/builtins.yo, + Doc/Zsh/grammar.yo, Doc/Zsh/options.yo, NEWS, README, + Src/builtin.c, Src/exec.c, Src/hashtable.c, Src/lex.c, + Src/options.c, Src/parse.c, Src/text.c, Src/zsh.h, + Test/B02typeset.ztst, Test/D01prompt.ztst, Test/E01options.ztst: + Implement assignment handling for typeset etc. when matched as + reserved words. Document and test. + +2015-06-23 Peter Stephenson <p.stephenson@samsung.com> + + * 35573: Completion/compinit: turn off POSIX_BUILTINS + in completion, needed to get usable OPTIND behaviour. + + * unposted: Src/Zle/zle_misc.c: add missing "void" argument + list. + +2015-06-23 Oliver Kiddle <opk@zsh.org> + + * 35568: Completion/Zsh/Type/_file_descriptors: use symlinks in + /proc/$$/path on Solaris and procstat on FreeBSD + + * 35529: Completion/Unix/Command/_java: fix filename completion + after java -jar + +2015-06-22 Peter Stephenson <p.stephenson@samsung.com> + + * 35566: Src/Modules/regex.c: didn't handle faile + parenthesis matches. + +2015-06-22 Mikael Magnusson <mikachu@gmail.com> + + * 35554: Doc/Zsh/contrib.yo: Document narrow-to-region -l and -r. + + * 35558: Functions/Zle/narrow-to-region: narrow-to-region: + Use unique parameter names for new feature. + +2015-06-21 Peter Stephenson <p.w.stephenson@ntlworld.com> + + * 35545: Functions/Zle/narrow-to-region: Add ability to return + buffer components from narrowed region. + +2015-06-19 Oliver Kiddle <opk@zsh.org> + + * 35531: Completion/Unix/Command/_adb: fallback on file completion + + * 35527: Completion/Unix/Type/_email_addresses, + Completion/Zsh/Command/_fc, Completion/Zsh/Command/_zpty: + use list-separator style in cases where separator was hardcoded + + * 35528: Completion/Unix/Type/_pids: allow for IPREFIX + +2015-06-18 Oliver Kiddle <opk@zsh.org> + + * 35474, 35492: Doc/Zsh/params.yo, Doc/Zsh/zle.yo, + Src/Zle/complist.c, Src/Zle/iwidgets.list, Src/Zle/zle_hist.c, + Src/Zle/zle_keymap.c, Src/Zle/zle_main.c: support the + bracketed paste mode of newer terminal emulators + + * 35487, 35496: Doc/Zsh/zle.yo, Src/Zle/complist.c, + Src/Zle/zle_hist.c: don't reinstate previous incremental search + string when search direction changes + + * 35510: Completion/Unix/Command/_git: use consistent + formatting for git subcommands + + * 35504: Completion/Unix/Command/_git: complete % placeholders + for git log --format + + * 35521: Completion/Zsh/Type/_file_descriptors: sort matches + numerically and use pfiles on Solaris + + * Eric Cook: 35490: Completion/Zsh/Type/_file_descriptors: + silence errors and avoid blank match due to missing local + +2015-06-17 Mikael Magnusson <mikachu@gmail.com> + + * 35477: Completion/Unix/Command/_gdb: _gdb: Allow 'core' to + occur anywhere in a coredump filename + + * 35476: Src/params.c: Allow setting $0 when POSIX_ARGZERO is + not set + +2015-06-16 Barton E. Schaefer <schaefer@zsh.org> + + * 35493: Src/exec.c: erase $_ when the command line is an assignment + + * 35482: Src/init.c: be consistent about how argzero is allocated + +2015-06-14 Barton E. Schaefer <schaefer@zsh.org> + + * 35469: Functions/Zle/smart-insert-last-word: test UNDO_CHANGE_NO + to determine whether to start over or use the next history event + + * 35467: Completion/Unix/Command/_du: complete files for non-GNU du + +2015-06-12 Peter Stephenson <p.stephenson@samsung.com> + + * 35448: Src/Modules/curses.c, Src/Modules/regex.c, + Src/Zle/complist.c, Src/Zle/zle_utils.c, Src/builtin.c, + Src/glob.c, Src/hist.c, Src/prompt.c, Src/utils.c, Src/zsh.h, + Test/D07multibyte.ztst: Add non-metafied character length + handling and use this for regex module. Add test. + Rename mb_metacharinit() to mb_charinit() since it doesn't + involve metafied characters. + +2015-06-11 Peter Stephenson <p.stephenson@samsung.com> + + * 35442: Doc/Zsh/options.yo: multibyte option now on + everywhere by default. + +2015-06-09 Oliver Kiddle <opk@zsh.org> + + * 35418: Doc/Zsh/compsys.yo: fix usage synopsis for _describe + to be clear that a single description is used + +2015-06-08 Peter Stephenson <p.stephenson@samsung.com> + + * 35416: Src/options.c: Turn on MULTIBYTE in all + emulations, including sh. + +2015-06-08 Daniel Hahler <zsh@thequod.de> + + * 35216: Completion/Unix/Command/_git: _git-checkout: do not call + __git_commits twice. + +2015-06-07 Oliver Kiddle <opk@zsh.org> + + * 35412: Src/builtin.c, Test/B03print.ztst: fix for - flag + when formating strings with printf that was broken in 34841 + +2015-06-06 Barton E. Schaefer <schaefer@zsh.org> + + * 35350: Completion/Zsh/Command/_fc, Doc/Zsh/builtins.yo, + Src/builtin.c: extend "fc -I" to cover listing of "internal" + history events only + + * 35163: Completion/Base/Core/_main_complete, + Completion/Base/Core/_setup: move "show-ambiguity" style to _setup + so that more precise context can be applied + +2015-06-05 Peter Stephenson <p.stephenson@samsung.com> + + * 35386: Doc/Zsh/builtins.yo, Doc/Zsh/contrib.yo, + Functions/Misc/zed, Src/builtin.c, Src/hashtable.c, + Src/pattern.c, Src/text.c, Src/utils.c, Src/zsh.h: + expand tabs for function output in functions, whence, where, + which and also zed -f using -x num option. + +2015-06-03 Oliver Kiddle <opk@zsh.org> + + * 35360 (replacing 35357): configure.ac, Src/Modules/zpty.c: + fix for configuring zpty on FreeBSD without pty.ko loaded + +2015-06-03 Peter Stephenson <p.stephenson@samsung.com> + + * 35374: Test/D07multibyte.ztst: add tab expansion test with + double width characters. + +2015-06-02 Peter Stephenson <p.stephenson@samsung.com> + + * 35369: Test/A06assign.ztst, Test/E01options.ztst: better + GLOB_ASSIGN testing. + +2015-06-02 Mikael Magnusson <mikachu@gmail.com> + + * 35365: Src/exec.c: GLOB_ASSIGN should only affect scalar + assignments, this regressed in 33816. + +2015-06-02 Peter Stephenson <p.stephenson@samsung.com> + + * users/20243: Completion/compinit: turn off GLOB_ASSIGN in + completion system. + +2015-06-02 Daniel Shahaf <d.s@daniel.shahaf.name> + + * 35270: Completion/Base/Utility/_describe: _describe: Document + a known issue described in 35229 (which is the same thread as + 35127 and 34768) + + * 35271: Completion/Unix/Command/_beep: New completion: + beep. + + * unposted: Doc/Zsh/options.yo: Typo fix + +2015-06-02 Peter Stephenson <p.stephenson@samsung.com> + + * 35359 (plus changed error strings in tests): Src/math.c, + Test/C01arith.ztst: fix math parsing problem that trailing ")" + wasn't detected; also improve error messages to indicate they + refer to math expressions. + + * Baptiste Daroussin: 35357: Src/Modules/zpty.c: + HAVE_POSIX_OPENPT is needed for FreeBSD zpty. + + * 35353: Doc/Zsh/builtins.yo, Src/builtin.c, Src/utils.c, + Test/B03print.ztst: print -x and print -X expand tabs. + +2015-06-02 Oliver Kiddle <opk@zsh.org> + + * 35356: Completion/Unix/Type/_ttys, Completion/Unix/Command/_ps, + Completion/Unix/Command/_last, Completion/Unix/Command/_pgrep, + Completion/Unix/Command/_gdb: factor out completion of tty devices + + * Adrien Vergé: 35352 (tweaked): Completion/Redhat/Command/_dnf: + completion for new Redhat package manager + +2015-06-01 Peter Stephenson <p.stephenson@samsung.com> + + * unposted: Config/version.mk: update to 5.0.8-dev-0 so + dev installs don't trash release. + 2015-05-31 Peter Stephenson <p.w.stephenson@ntlworld.com> * unposted: Config/version.mk: release 5.0.8, finally. diff --git a/Completion/BSD/Command/_systat b/Completion/BSD/Command/_systat new file mode 100644 index 000000000..c8348c7f7 --- /dev/null +++ b/Completion/BSD/Command/_systat @@ -0,0 +1,88 @@ +#compdef systat + +local -a screens args opts +local pre +case $OSTYPE in + freebsd*) + pre=- + screens=( + 'pigs:processes consuming the most CPU time' + icmp{6,}':ICMP statistics' + ip{6,}':IP and UDP statistics' + 'tcp:TCP statistics' + 'iostat:processor and disk statistics' + 'swap:swap space statistics' + 'vmstat:virtual memory statistics' + 'netstat:network connection statistics' + 'ifstat:network traffic statistics' + ) + ;; + openbsd*) + screens=( + 'buckets:kernel malloc(9) bucket statistics' + 'cpu:per CPU, usage statistics' + 'ifstat:interface statistics' + 'iostat:disk throughput statistics' + 'malloc:malloc(9) type statistics' + 'mbufs:mbuf usage statistics' + 'netstat:network connection statistics' + 'nfsclient:NFS client statistics' + 'nfsserver:NFS server statistics' + 'pf:pf(4) filter statistics' + 'pigs:processes consuming the most CPU time' + 'pool:pool(9) statistics' + 'queues:pf(4) queue statistics' + 'rules:pf(4) rule statistics' + 'sensors:display hardware sensors values' + 'states:pf(4) states statistics' + 'swap:swap space usage' + 'vmstat:virtual memory statistics' + ) + opts=( + '-a[display all lines]' + '-B[raw, non-interactive mode (two screen updates)]' + '-b[raw, non-interactive mode (one screen update)]' + '-d[exit after `count'\'' updates]:count' + '-i[interactive mode]' + '-N[resolve network addresses to names]' + '-n[do not resolve network addresses to names]' + '-s[screen refresh interval]:refresh interval (seconds)' + '-w[maximum width of output in raw mode]:width' + ) + ;; + netbsd*) + screens=( + 'all:cycle through all displays' + 'bufcache:filesystem buffer statistics' + 'df:disk usage statistics' + 'inet.icmp:ICMP statistics' + 'inet.ip:IPv4 and UDP statistics' + 'inet.tcp:TCP statistics' + 'inet.tcpsyn:TCP ``syncache'\'\'' statistics' + 'inet6.ip6:IPv6 statistics' + 'iostat:disk throughput statistics' + 'mbufs:mbuf usage statistics' + 'netstat:network connection statistics' + 'pigs:processes consuming the most CPU time' + 'ps:``ps -aux'\'\'' in a loop' + 'swap:swap space usage' + 'syscall:per system call statistics' + 'vmstat:virtual memory statistics' + ) + opts=( + '-M[alternative source to extract values from]:core:_files' + '-N[alternative source to extract the name list from]:system:_files' + '-n[do not resolve IP addresses]' + '-w[refresh interval]:refresh interval' + '-t[the amount of refreshes for each screen in '\''all'\'' display mode]:turns' + ) +esac + +if (( $#screens )); then + _arguments -M 'r:|.=* r:|=*' : $opts \ + '1:systat(1) displays:(( ${pre}$^screens ))' \ + '2:refresh interval' + return +fi + +_default diff --git a/Completion/BSD/Command/_watch-snoop b/Completion/BSD/Command/_watch-snoop new file mode 100644 index 000000000..182b6bb34 --- /dev/null +++ b/Completion/BSD/Command/_watch-snoop @@ -0,0 +1,13 @@ +#autoload + +# watch [-cinotW] [-f snpdev] [tty] + +_arguments -w -S -s : \ + "-c[reconnect on close]" \ + "-f:snp(4) device: " \ + "-i[force interactive mode even when stdout is not a tty]" \ + "-n[disable the ability to switch the watched tty interactively]" \ + "-o[reconnect on overflow]" \ + "-t[print date and time at start]" \ + "-W[allow write access to observed tty]" \ + ":tty device:_ttys -D" diff --git a/Completion/Base/Completer/_expand b/Completion/Base/Completer/_expand index 3c76e1328..e52144cb7 100644 --- a/Completion/Base/Completer/_expand +++ b/Completion/Base/Completer/_expand @@ -87,7 +87,7 @@ if [[ "$force" = *s* ]] || setopt aliases eval 'exp=( ${${(e)exp//\\[ -]/ }//(#b)([ \\ +]/ }//(#b)([ ])/\\$match[1]} )' 2>/dev/null setopt NO_aliases else diff --git a/Completion/Base/Core/_main_complete b/Completion/Base/Core/_main_complete index 977ab49ee..9c4cfac85 100644 --- a/Completion/Base/Core/_main_complete +++ b/Completion/Base/Core/_main_complete @@ -36,7 +36,8 @@ local func funcs ret=1 tmp _compskip format nm call match min max i num\ _saved_list="${compstate[list]}" \ _saved_insert="${compstate[insert]}" \ _saved_colors="$ZLS_COLORS" \ - _saved_colors_set=${+ZLS_COLORS} + _saved_colors_set=${+ZLS_COLORS} \ + _ambiguous_color='' # _precommand sets this to indicate we are following a precommand modifier local -a precommands @@ -140,11 +141,16 @@ _completer_num=1 # We assume localtraps to be in effect here ... integer SECONDS=0 -TRAPINT TRAPQUIT() { +TRAPINT() { zle -M "Killed by signal in ${funcstack[2]} after ${SECONDS}s"; zle -R return 130 } +TRAPQUIT() { + zle -M "Killed by signal in ${funcstack[2]} after ${SECONDS}s"; + zle -R + return 131 +} # Call the pre-functions. @@ -349,12 +355,11 @@ elif [[ nm -eq 0 && -z "$_comp_mesg" && compadd -x "$mesg" fi -if zstyle -s ":completion:${curcontext}:" show-ambiguity tmp; then - local prefix=${${compstate[unambiguous]}[1,${compstate[unambiguous_cursor]}-1]} +if [[ -n "$_ambiguous_color" ]]; then local toquote='[=\(\)\|~^?*[\]#<>]' - [[ $tmp = (yes|true|on) ]] && tmp=4 + local prefix=${${compstate[unambiguous]}[1,${compstate[unambiguous_cursor]}-1]} [[ -n $prefix ]] && - _comp_colors+=( "=(#i)${prefix[1,-2]//?/(}${prefix[1,-2]//(#m)?/${MATCH/$~toquote/\\$MATCH}|)}${prefix[-1]//(#m)$~toquote/\\$MATCH}(#b)(?|)*==$tmp" ) + _comp_colors+=( "=(#i)${prefix[1,-2]//?/(}${prefix[1,-2]//(#m)?/${MATCH/$~toquote/\\$MATCH}|)}${prefix[-1]//(#m)$~toquote/\\$MATCH}(#b)(?|)*==$_ambiguous_color" ) fi [[ "$_comp_force_list" = always || diff --git a/Completion/Base/Core/_setup b/Completion/Base/Core/_setup index d85ebb885..ca975332f 100644 --- a/Completion/Base/Core/_setup +++ b/Completion/Base/Core/_setup @@ -9,8 +9,7 @@ if zstyle -a ":completion:${curcontext}:$1" list-colors val; then if [[ "$1" = default ]]; then _comp_colors=( "$val[@]" ) else - _comp_colors=( "$_comp_colors[@]" - "(${2})${(@)^val:#(|\(*\)*)}" "${(M@)val:#\(*\)*}" ) + _comp_colors+=( "(${2})${(@)^val:#(|\(*\)*)}" "${(M@)val:#\(*\)*}" ) fi # Here is the problem mentioned in _main_complete. @@ -23,6 +22,13 @@ elif [[ "$1" = default ]]; then unset ZLS_COLORS ZLS_COLOURS fi +# What we'd like is to test that the show-ambiguity style pattern is more +# specific than the list-colors style pattern, but that's not possible yet +if zstyle -s ":completion:${curcontext}:$1" show-ambiguity val; then + zmodload -i zsh/complist + [[ $val = (yes|true|on) ]] && _ambiguous_color=4 || _ambiguous_color=$val +fi + if zstyle -t ":completion:${curcontext}:$1" list-packed; then compstate[list]="${compstate[list]} packed" elif [[ $? -eq 1 ]]; then diff --git a/Completion/Base/Utility/_describe b/Completion/Base/Utility/_describe index ab7200517..76ab1d995 100644 --- a/Completion/Base/Utility/_describe +++ b/Completion/Base/Utility/_describe @@ -1,5 +1,12 @@ #autoload +# ### Note: Calling this function twice during one completion operation, such +# ### that in each call there exists a pair of items having the same description +# ### as each other, and the two calls specify the same $_type, currently leads +# ### to garbled output; see workers/35229 (May 2015) and its thread (which also +# ### discusses at least two other issues, that may or may not be related to +# ### this one). + # This can be used to add options or values with descriptions as matches. local _opt _expl _tmpm _tmpd _mlen _noprefix diff --git a/Completion/Linux/Command/_btrfs b/Completion/Linux/Command/_btrfs index 77deeefcb..33b5c76f9 100644 --- a/Completion/Linux/Command/_btrfs +++ b/Completion/Linux/Command/_btrfs @@ -1,12 +1,12 @@ #compdef btrfs # based on Btrfs v3.12+20131125 -local curcontext="$curcontext" curstate state line expl grp cmd ret=1 +local curcontext="$curcontext" curstate state line expl grp cmd cont shift ret=1 local -a cmds_1 cmds_2 cmds_3 cmds_4 cmds_5 cmds_6 cmds_7 cmds_8 cmds_9 cmds_10 local -a groups args -groups=( subvolume filesystem device scrub balance inspect-internal - quota qgroup replace rescue check restore send receive +groups=( subvolume filesystem device scrub balance inspect-internal + quota qgroup replace rescue check restore send receive help version ) cmds_1=( create delete list snapshot get-default set-default find-new show help ) cmds_2=( df show sync defragment resize label balance help ) @@ -36,79 +36,32 @@ while (( $#state )); do _wanted command-groups expl 'btrfs command group' compadd -a groups && ret=0 ;; cmds) - : $words - local grp=${groups[(i)$words[2]*]} - : $grp + grp=${groups[(i)$words[2]*]} (( grp && grp <= 14 )) || return 1 - curcontext="${curcontext%:*:*}:$service-${groups[grp]}:" - case $grp in - 11) - # btrfs check - args+=( - {-s,--support}'[specify superblock]:superblock: _message "superblock"' - '--repair[try to repair the filesystem]' - '--init-csum-tree[create a new CRC tree]' - '--init-extent-tree[create a new extent tree]' - '1:path:_files -/' - ) - ;| - 12) - # btrfs replace - args+=( - '-s[get snapshots]' - '-x[get extended attributes]' - '-v[verbose]' - '-i[ignore errors]' - '-o[overwrite]' - '-t[tree location]:tree:_message "tree location"' - '-f[filesystem location]:filesystem:_message "filesystem location"' - '-u[super mirror]:mirror:_message "super mirror"' - '-r[root objectid]:objectid:_message "root objectid"' - '-d[find dir]' - '-l[list tree roots]' - '--path-regex[restore matching filenames]:regex:_message "regex"' - '1:device:_files -/' - '2:path:_files -/' - ) - ;| - 13) - # btrfs send - args+=( - '*-v[verbose mode]' - '-p[send incremental stream]:parent:_files -/' - '*-c[use snapshot as clone source]:clone:_files -/' - '-f[output file]:file:_files' - '1:subvol:_files -/' - ) - ;| - 14) - # btrfs receive - args+=( - '*-v[verbose mode]' - '-f[input file]:file: _files' - '-e[terminate after <end cmd>]' - '1:mount:->mounts' - ) - ;| - <11-14>) - (( CURRENT-- )); shift words; curcontext="${curcontext%:*:*}:$service-${group[grp]}:" - _arguments -C "$args[@]" && ret=0 - ;; - <0-10>) - _wanted commands expl command compadd -a cmds_$grp && ret=0 - ;; - esac - ;; + cont=${groups[grp]} + curcontext="${curcontext%:*:*}:$service-${cont}:" + if (( grp <= 10 )); then + _wanted commands expl command compadd -a cmds_$grp && ret=0 + continue + fi + ;& args) - : $words - local grp=${groups[(i)$words[1]*]} - (( grp && grp <= 15 )) || return 1 - local group=cmds_$grp - local cmd=${${(P)group}[(i)$words[2]*]} - (( cmd )) || return 1 - curcontext="${curcontext%:*:*}:$service-${groups[grp]}-${${(P)group}[cmd]}:" + if [[ $curstate != cmds ]]; then + grp=${groups[(i)$words[1]*]} + (( grp && grp <= 15 )) || return 1 + cont=${groups[grp]} + if (( grp <= 10 )); then + local group=cmds_$grp + local cmd=${${(P)group}[(i)$words[2]*]} + (( cmd )) || return 1 + cont+=:${${(P)group}[cmd]} + else + shift=1 + fi + curcontext="${curcontext%:*:*}:$service-${cont/:/-}:" + fi args=( '(-)--help[print help information]' ) - case ${groups[grp]}:${${(P)group}[cmd]} in + case ${cont} in filesystem:balance) if (( CURRENT == 3 )); then state+=cmds @@ -118,19 +71,19 @@ while (( $#state )); do state+=args fi continue - ;; + ;; subvolume:create) args+=( '1:destination:->mounts' );; subvolume:delete) args+=( '1:subvolume:_files -/' );; - subvolume:snapshot) - args+=( + subvolume:snapshot) + args+=( '-r[readonly snapshot]' '*-i[assign to qgroup]:qgroup: _message "qgroup"' '1:source directory:_files -/' '2:snapshot name or destination:_files -/' ) - ;; - subvolume:list) - args+=( + ;; + subvolume:list) + args+=( '-p[include parent ID in output]' '-a[include all subvolumes]' '-c[include ogeneration of the subvolume]' @@ -145,8 +98,8 @@ while (( $#state )); do '-C[subvolume ogeneration is more or less than]:ogen: _guard "(|+|-)[0-9]#"' '--sort=-[list in order]:sort:_values -s "," sort rootid gen ogen path' '1:path:->mounts' - ) - ;; + ) + ;; subvolume:set-default) args+=( '1:id:_guard "[0-9]#" id' '2:path:->mounts' );; subvolume:get-default) args+=( '1:path:_files -/' );; subvolume:find-new) args+=( '1:subvol:_files -/' '2:lastgen: _message "last gen"' );; @@ -161,27 +114,27 @@ while (( $#state )); do '-l[defragment limited number of bytes]:length (bytes)' '-t[defragment only files over a certain size]:minimum size (bytes)' '*:file:_files' - ) - ;; + ) + ;; filesystem:label) args+=( '1:device:_files -g "*(d)"' '2:label' );; - filesystem:show) - args+=( - '(1 -)'{-d,--all-devices}'[scan all devices in /dev]' + filesystem:show) + args+=( + '(1 -)'{-d,--all-devices}'[scan all devices in /dev]' '(1 -)'{-m,--mounted}'[show only mounted filesystems]' '1: :_guard "^-*" uuid or label' - ) - ;; - device:(add|delete)) - args+=( - '1:device:_files -g "*(d)"' + ) + ;; + device:(add|delete)) + args+=( + '1:device:_files -g "*(d)"' '2:path:->mounts' ) - [[ ${${(P)group}[cmd]} == add ]] && - args+=( + [[ ${${(P)group}[cmd]} == add ]] && + args+=( {-K,--nodiscard}'[do not perform discard]' {-f,--force}'[force overwrite of existing filesystem]' - ) - ;; + ) + ;; device:scan) args+=( '(1 -)--all-devices[scan all devices in /dev]' '1:device:_files -g "*(d)"' );; device:stats) args+=( "1:device or mountpoint:_files -g '*(d,/)'" '-z[reset stats when done]' );; device:ready) args+=( '1:device: _files -g "*(d)"' );; @@ -195,9 +148,9 @@ while (( $#state )); do '-c[set ioprio class]:class:(( 0\:none 1\:realtime 2\:best-effort 3\:idle))' '-n[set ioprio classdata]:classdata:(0 1 2 3 4 5 6 7)' '1:path or device:_files' - ) - [[ ${${(P)group}[cmd]} == start ]] && args+=( '-R[raw print mode]' ) - ;; + ) + [[ ${${(P)group}[cmd]} == start ]] && args+=( '-R[raw print mode]' ) + ;; scrub:cancel) args+=( '1:path or device' );; scrub:status) args+=( '-d[separate statistics for each device]' '1:path or device:_files' );; balance:start) @@ -208,23 +161,23 @@ while (( $#state )); do '-v[verbose mode]' '-f[force reducing of metadata integrity]' '1:path:_files -/' - ) - ;; - balance:status) args+=( '-v[verbose mode]' '2:path:_files -/' );; - balance:(pause|cancel|resume)) args+=( '2:path:_files -/' );; + ) + ;; + balance:status) args+=( '-v[verbose mode]' '1:path:_files -/' );; + balance:(pause|cancel|resume)) args+=( '1:path:_files -/' );; quota:(enable|disable)) args+=( '1:path:_files -/' );; - quota:rescan) - args+=( + quota:rescan) + args+=( '-s[show status of currently running rescan]' '-w[wait for rescan to finish]' - '1:path:_files -/' - ) - ;; - qgroup:(assign|remove)) args+=( '1:source path:_files -/' + '1:path:_files -/' + ) + ;; + qgroup:(assign|remove)) args+=( '1:source path:_files -/' '2:destination path:_files -/' '3:path:_files -/' );; - qgroup:(create|destroy)) args+=( '1:qgroupid:' '2:path:_files -/' );; - qgroup:show) - args+=( + qgroup:(create|destroy)) args+=( '1:qgroupid:' '2:path:_files -/' );; + qgroup:show) + args+=( '-p[print parent qgroup id]' '-c[print child qgroup id]' '-r[print max referenced size of qgroup]' @@ -232,19 +185,19 @@ while (( $#state )); do '-F[list impacted qgroups\(include ancestral qgroups\)]' '-f[list impacted qgroups\(exclude ancestral qgroups\)]' '--sort=-[sort qgroups]:sort:_values -s , sort \ - qgroupid rfer excl max_rfer max_excl' + qgroupid rfer excl max_rfer max_excl' '1:path:_files -/' - ) - ;; - qgroup:limit) - args+=( - '-c[limit amount of data after compression]' - '-e[limit space exclusively to qgroup]' - ':size or none: _message "size or none"' - ':qgroup id or path:_files -/' - ':path:_files -/' - ) - ;; + ) + ;; + qgroup:limit) + args+=( + '-c[limit amount of data after compression]' + '-e[limit space exclusively to qgroup]' + ':size or none: _message "size or none"' + ':qgroup id or path:_files -/' + ':path:_files -/' + ) + ;; replace:start) args+=( '-r[read from <srcdev> only]:srcdev:_files' @@ -253,8 +206,8 @@ while (( $#state )); do ':srcdev or devid:_files' ':target:_files' ':path:->mounts' - ) - ;; + ) + ;; replace:status) args+=( '-1[print once]' ':path:->mounts' );; replace:cancel) args+=( ':path:->mounts' );; inspect*:inode*) args+=( '-v[verbose mode]' '1:inode:_files' '2:path:_files -/' );; @@ -266,28 +219,74 @@ while (( $#state )); do '-s[buffer size]:buffer size:' '1:logical address:_files' '2:filesystem path:_files -/' - ) - ;; + ) + ;; inspect*:rootid) args+=( '1:path:_files -/' );; rescue:(chunk|super)-recover) args+=( '-y[assume yes to every question]' '-v[verbose mode]' - ) - [[ ${${(P)group}[cmd]} == chunk-recover ]] && args+=('-h[display help]') - ;; + ) + [[ ${${(P)group}[cmd]} == chunk-recover ]] && args+=('-h[display help]') + ;; subvolume:get-default) ;& *:sync) ;& *:df) args+=( '1:path:->mounts' );; + check) + args+=( + {-s,--support}'[specify superblock]:superblock' + '--repair[try to repair the filesystem]' + '--init-csum-tree[create a new CRC tree]' + '--init-extent-tree[create a new extent tree]' + '1:path:_files -/' + ) + ;; + restore) + args+=( + '-s[get snapshots]' + '-x[get extended attributes]' + '-v[verbose]' + '-i[ignore errors]' + '-o[overwrite]' + '-t[tree location]:tree' + '-f[filesystem location]:filesystem' + '-u[super mirror]:mirror' + '-r[root objectid]:objectid' + '-d[find dir]' + '-l[list tree roots]' + '--path-regex[restore matching filenames]:regex' + '1:device:_files -/' + '2:path:_files -/' + ) + ;; + send) + args+=( + '*-v[verbose mode]' + '-p[send incremental stream]:parent:_files -/' + '*-c[use snapshot as clone source]:clone:_files -/' + '-f[output file]:file:_files' + '1:subvolume:_files -/' + ) + ;; + receive) + args+=( + '*-v[verbose mode]' + '-f[input file]:file: _files' + '-e[terminate after <end cmd>]' + '1:mount:->mounts' + ) + ;; *) args+=( '*: :_default' );; # fallback for unknown subcommands esac - shift words - (( CURRENT-- )) + if ! (( shift )); then + shift words + (( CURRENT-- )) + fi _arguments -C "$args[@]" && ret=0 ;; mounts) _wanted mount-points expl 'mount point' compadd \ - ${${${(M)${(f)"$(</etc/mtab)"}:#*btrfs*}#* }%% *} && ret=0 + ${${${(M)${(f)"$(</etc/mtab)"}:#*btrfs*}#* }%% *} && ret=0 ;; filters) state=() diff --git a/Completion/Linux/Command/_sshfs b/Completion/Linux/Command/_sshfs index c6a8e102d..534e806e3 100644 --- a/Completion/Linux/Command/_sshfs +++ b/Completion/Linux/Command/_sshfs @@ -1,14 +1,39 @@ #compdef sshfs -_arguments \ +local context 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:_values -s , "sshfs or fuse or mount options" reconnect sshfs_sync no_readahead sshfs_debug cache=:cache\ setting:(yes no) cache_timeout=:seconds: cache_stat_timeout=:seconds: cache_dir_timeout=:seconds: cache_link_timeout=:seconds: 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' \ + '-o:options:->options' \ '-d[debug]' \ '-f[foreground]' \ '-s[disable multithreaded operation]' \ '-r[mount read-only]' \ '-h[help]' \ ':remote directory:_user_at_host -S:' \ - ':mountpoint:_files -/' + ':mountpoint:_files -/' && ret=0 + +if [[ $state == options ]]; then + _values -s , "sshfs or fuse or mount options" \ + reconnect sshfs_sync no_readahead sshfs_debug \ + 'cache:cache setting:(yes no)' \ + cache_timeout:seconds: \ + cache_stat_timeout:seconds: \ + cache_dir_timeout:seconds: \ + cache_link_timeout:seconds: \ + '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 +fi + +return ret diff --git a/Completion/Redhat/Command/_dnf b/Completion/Redhat/Command/_dnf new file mode 100644 index 000000000..297c95ae9 --- /dev/null +++ b/Completion/Redhat/Command/_dnf @@ -0,0 +1,278 @@ +#compdef dnf + +# Main dispatcher +_dnf() { + _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]:config 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:_dnf_disabled_repos_list' \ + '*--disablerepo=[disable one or more repositories]:disable repos:_dnf_enabled_repos_list' \ + {*-x,*--exclude=}'[exclude package(s) by name or glob]:exclude packages' \ + '--version[show dnf version]' \ + '--obsoletes[enable obsoletes processing during updates]' \ + '--nogpgcheck[disable gpg signature checking]' \ + '--noplugins[disable dnf plugins]' \ + '--disablepresto[disable Presto plugin and don''''t download any deltarpms]' \ + '*::dnf command:_dnf_command' +} + +(( $+functions[_dnf_command] )) || _dnf_command() { + local -a _dnf_cmds + _dnf_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 dnf cache" + "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 dnf cache" + "provides:find out which package provides some feature or file" + "whatprovides:find out which package provides some feature or file" + "search:find any packages matching pattern" + "shell:enter the 'dnf shell'" + "update:update one or more packages" + "upgrade:upgrade one or more packages" + ) + + if (( CURRENT == 1 )); then + _describe -t commands 'dnf command' _dnf_cmds || compadd "$@" + else + local curcontext="$curcontext" + + cmd="${${_dnf_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%:*:*}:dnf-${cmd}:" + + local update_policy + zstyle -s ":completion:${curcontext}:" cache-policy update_policy + if [[ -z "$update_policy" ]]; then + zstyle ":completion:${curcontext}:" cache-policy _dnf_caching_policy + fi + + _call_function ret _dnf_$cmd || _message 'no more arguments' + else + _message "unknown dnf command: $words[1]" + fi + return ret + fi +} + +# Fills the all pkg cache +_dnf_all_pkgs() { + if ( [[ ${+_all_pkgs} -eq 0 ]] || _cache_invalid ALL ) && + ! _retrieve_cache ALL; + then + _all_pkgs=( $(dnf -C list all | sed 's/\s.*//' | grep '\.' 2>/dev/null) ) + _store_cache ALL _all_pkgs + fi +} + +# Fills the installed pkg cache +_dnf_installed_pkgs() { + if ( [[ ${+_installed_pkgs} -eq 0 ]] || _cache_invalid INSTALLED ) && + ! _retrieve_cache INSTALLED; + then + _installed_pkgs=( $(dnf -C list installed | sed 's/\s.*//' | grep '\.' 2>/dev/null) ) + _store_cache INSTALLED _installed_pkgs + fi +} + +# Fills the available pkg cache +_dnf_available_pkgs() { + if ( [[ ${+_available_pkgs} -eq 0 ]] || _cache_invalid AVAILABLE ) && + ! _retrieve_cache AVAILABLE; + then + _available_pkgs=( $(dnf -C list available | sed 's/\s.*//' | grep '\.' 2>/dev/null) ) + _store_cache AVAILABLE _available_pkgs + fi +} + +# Fills the upgrade pkg cache +_dnf_upgrade_pkgs() +{ + if ( [[ ${+_upgrade_pkgs} -eq 0 ]] || _cache_invalid UPGRADE ) && + ! _retrieve_cache UPGRADE; + then + _upgrade_pkgs=( $(dnf -C list upgrade | sed 's/\s.*//' | grep '\.' 2>/dev/null) ) + _store_cache UPGRADE _upgrade_pkgs + fi +} + +# Gets the list of defined repos +__dnf_repos() { + local trepo + local -a tarray + tarray=( $(egrep -h '(^\[.*\]|^enabled.*=)' /etc/dnf.repos.d/*.repo /etc/dnf.conf 2>/dev/null | 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_dnf_repos+=($trepo) + elif [[ "$line" = "enabled=0" ]]; then + disabled_dnf_repos+=($trepo) + elif [[ "$line" != "main" ]]; then + trepo=$line + fi + done +} + +(( $+functions[_dnf_disabled_repos_list] )) || _dnf_disabled_repos_list() { + local -a enabled_dnf_repos disabled_dnf_repos + __dnf_repos + _sequence compadd "$@" - -a disabled_dnf_repos +} + +(( $+functions[_dnf_enabled_repos_list] )) || _dnf_enabled_repos_list() { + local -a enabled_dnf_repos disabled_dnf_repos + __dnf_repos + _sequence compadd "$@" - -a enabled_dnf_repos +} + +# Completion function for erase|remove +(( $+functions[_dnf_erase] )) || _dnf_erase() { + _dnf_installed_pkgs + compadd "$@" -a -- _installed_pkgs +} + +# Completion function for install +(( $+functions[_dnf_install] )) || _dnf_install() { + if ! [[ $PREFIX == */* ]]; then + _dnf_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 - _dnf_localinstall + fi + (( ret )) || break + done + return ret +} + +# Completion function for localinstall +(( $+functions[_dnf_localinstall] )) || _dnf_localinstall() { + _files -/ -g '(#i)*.rpm(-.)' +} + +# Completion function for localupdate +(( $+functions[_dnf_localupdate] )) || _dnf_localupdate() { + _files -/ -g '(#i)*.rpm(-.)' +} + +# Completion function for update/upgrade +(( $+functions[_dnf_update] )) || _dnf_update() { + _dnf_upgrade_pkgs + compadd "$@" -a -- _upgrade_pkgs +} + +_dnf_all() { + _dnf_all_pkgs + compadd "$@" -a -- _all_pkgs +} + +_dnf_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 dnf repository" + "obsoletes:packages installed that are obsoleted" + "recent:packages recently added to repositories" + ) + + if (( CURRENT == 2 )); then + _describe -t dnf-list-subcmds "dnf info/list sub-commands" listlist || _dnf_all + else + local subcmd + subcmd="${${listlist[(r)$words[2]:*]%%:*}}" + # offer packages selected by the subcommand + case $subcmd in + all) _dnf_all;; + installed) _dnf_erase;; + available) _dnf_install;; + updates) _dnf_update;; + esac + fi +} + +# Completion function for list +(( $+functions[_dnf_list] )) || _dnf_list() { + _dnf_list_or_info +} + +# Completion function for info +(( $+functions[_dnf_info] )) || _dnf_info() { + _dnf_list_or_info +} + +# Completion function for provides|whatprovides +(( $+functions[_dnf_provides] )) || _dnf_provides() { + _files +} + +# Completion function for clean +(( $+functions[_dnf_clean] )) || _dnf_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 dnf-clean-subcmds "dnf clean sub-commands" cleanlist + fi +} + +_dnf_caching_policy() { + local _dnfrepomds + local -a oldp + + # rebuild if cache is more than a week old + oldp=( "$1"(mw+1) ) + (( $#oldp )) && return 0 + + _dnfrepomds=( /var/cache/dnf/**/repomd.xml ) + + if (( $#_dnfrepomds )); then + for repo in $_dnfrepomds; do + [[ "$repo" -nt "$1" ]] && return 0 + done + fi + + return 1 +} + +_dnf "$@" diff --git a/Completion/Unix/Command/_adb b/Completion/Unix/Command/_adb index e687762d3..88aca2464 100644 --- a/Completion/Unix/Command/_adb +++ b/Completion/Unix/Command/_adb @@ -76,7 +76,8 @@ _adb() { '(-d -e )-s[serial]: :_adb_device_serial' \ '( -e -s)-d[device]' \ '(-d -s)-e[emulator]' \ - '*:"options":_adb_options_handler' + '1:"options":_adb_options_handler' \ + '*: : _default' return; } @@ -99,27 +100,30 @@ _adb_dispatch_command () { fi case ${curcontext} in - (*:adb:shell) + (*:adb-shell) (( $+functions[_adb_dispatch_shell] )) && _adb_dispatch_shell ;; - (*:adb:connect|*:adb:disconnect) + (*:adb-connect|*:adb-disconnect) (( $+functions[_adb_dispatch_connection_handling] )) && _adb_dispatch_connection_handling ;; - (*:adb:logcat) + (*:adb-logcat) (( $+functions[_adb_dispatch_logcat] )) && _adb_dispatch_logcat ;; - (*:adb:push) + (*:adb-push) (( $+functions[_adb_dispatch_push] )) && _adb_dispatch_push ;; - (*:adb:pull) + (*:adb-pull) (( $+functions[_adb_dispatch_pull] )) && _adb_dispatch_pull ;; - (*:adb:install) + (*:adb-install) (( $+functions[_adb_dispatch_install] )) && _adb_dispatch_install ;; - (*:adb:uninstall) + (*:adb-uninstall) (( $+functions[_adb_dispatch_uninstall] )) && _adb_dispatch_uninstall ;; + (*:adb-*) + _default + ;; (*) _arguments \ '(-d -e)-s["serial"]: :_adb_device_serial' \ @@ -143,7 +147,7 @@ _adb_sanitize_context () { done ##expand unquoted to remove sparse elements mywords=( ${mywords[@]} ) - curcontext="${curcontext}${mywords[-1]}" + (( $#mywords )) && curcontext="${curcontext%:*}-${mywords[-1]}:" } (( $+functions[_adb_device_specification] )) || @@ -373,9 +377,9 @@ _adb_dispatch_connection_handling () { fi } -(( $+functions[adb_check_log_redirect] )) || +(( $+functions[_adb_check_log_redirect] )) || _adb_check_log_redirect () { - LOG_REDIRECT=${$(adb ${=ADB_DEVICE_SPECIFICATION} shell getprop log.redirect-stdio)// + LOG_REDIRECT=${$(adb ${=ADB_DEVICE_SPECIFICATION} shell getprop log.redirect-stdio 2>/dev/null)// /} [[ ${LOG_REDIRECT[1,4]} == "true" ]] && _message -r "Notice: stdio log redirection enabled on the device, so some completions will not work" } diff --git a/Completion/Unix/Command/_beep b/Completion/Unix/Command/_beep new file mode 100644 index 000000000..c49c09726 --- /dev/null +++ b/Completion/Unix/Command/_beep @@ -0,0 +1,50 @@ +#compdef _beep + +# beep [--verbose | --debug] [-e device | --device device] [-f N] [-l N] +# [-r N] [-d N] [-D N] [-s] [-c] +# beep [ OPTIONS ] [-n] [--new] [ OPTIONS ] +# beep [-h] [--help] +# beep [-v] [-V] [--version] + +# We support the -n/--new restart flag, when it is in its own word, by +# stripping all words from the command line that are after the first -n +# following words[CURRENT] or before the first -n preceding it. As far as +# _arguments knows, the -n flag does not exist, and argv should specify +# a single beeping specification. + +## Strip $words[2] through the first -n preceding $words[CURRENT]. +integer specstart +while specstart=$words[(i)(-n|--new)] + (( specstart < CURRENT )) +do + words[2,specstart]=() + (( CURRENT -= specstart - 1)) +done +unset specstart + +## Strip everything in $words after the first -n following $words[CURRENT]. +integer specend +while specend=$words[(I)(-n|--new)] + (( specend > CURRENT )) +do + words[specend,$#words]=() +done +unset specend + +local -a args +args=( + "(--verbose --debug)"{--verbose,--debug}"[enable debug output]" + "(-e --device)"{-e+,--device=}"[specify device to use]:device (default /dev/tty0, /dev/vc/0)" + "-f+:frequency (Hz) as integer or float" + "-l+:duration (ms)" + "-r+:number of repetitions" + "(-D)-d+[delay between beeps]:delay (ms)" + "(-d)-D+[delay after each beep]:delay (ms)" + "(-n --new)"{-n,--new}"[start a new beeping specification]" + "-s[cat, and beep after each line]" + "-c[cat, and beep after each character]" + "(-)"{-h,--help}"[display usage info]" + "(-)"{-v,-V,--version}"[display version info]" +) + +_arguments -s -S -w : $args diff --git a/Completion/Unix/Command/_clay b/Completion/Unix/Command/_clay index 71f05bf64..581338b8b 100644 --- a/Completion/Unix/Command/_clay +++ b/Completion/Unix/Command/_clay @@ -38,5 +38,5 @@ _arguments -C \ "-e:compile and run <source> (implies -run)" \ "-M-:import <module>.*; for -e" \ "-v[display version info]" \ - ":program file:_files -g '*.clay'" + ":program file:_files -g '*.clay(-.)'" diff --git a/Completion/Unix/Command/_du b/Completion/Unix/Command/_du index d8871cd8d..4065a20de 100644 --- a/Completion/Unix/Command/_du +++ b/Completion/Unix/Command/_du @@ -74,5 +74,5 @@ else do [[ $OSTYPE = $~pattern ]] && args+=( $arg ) done - _arguments -s -A "-*" $args + _arguments -s -A "-*" $args '*:file:_files' fi diff --git a/Completion/Unix/Command/_find b/Completion/Unix/Command/_find index 8f80e36cf..e736f32cb 100644 --- a/Completion/Unix/Command/_find +++ b/Completion/Unix/Command/_find @@ -1,6 +1,7 @@ #compdef find gfind -local variant args +local curcontext="$curcontext" state_descr variant +local -a state line args alts _pick_variant -r variant gnu=GNU $OSTYPE -version @@ -26,12 +27,12 @@ case $variant in '*-print0' ) ;| + solaris*|freebsd*|dragonfly*|darwin*|gnu) + args+=( '*-mount' ) + ;| netbsd*|freebsd*|dragonfly*|darwin*|gnu) args+=( '(-H -L)-P[never follow symlinks]' ) ;| - netbsd*|freebsd*|dragonfly*|openbsd*|darwin*|gnu) - args+=( '-d[depth first traversal]' ) - ;| darwin*|freebsd*|gnu) args+=( '*-Bmin:birth time (minutes)' @@ -39,11 +40,13 @@ case $variant in '*-Btime:birth time (hours)' ) ;| - freebsd*|dragonfly*|darwin*|openbsd*|gnu) + netbsd*|freebsd*|dragonfly*|openbsd*|darwin*|gnu) args+=( + '-d[depth first traversal]' '*-anewer:file to compare (access time):_files' '*-cnewer:file to compare (inode change time):_files' - '*-mnewer:file to compare (modification time):_files' + '*-empty' + '*-execdir:program: _command_names -e:*\;::program arguments: _normal' '*-maxdepth:maximum search depth' '*-mindepth:minimum search depth' '*-path:path pattern to search:' @@ -52,8 +55,6 @@ case $variant in freebsd*|dragonfly*|darwin*|gnu) args+=( '*-delete' - '*-empty' - '*-execdir:program: _command_names -e:*\;::program arguments: _normal' '*-gid:numeric group ID' '*-uid:numeric user ID' '*-noleaf' @@ -65,6 +66,7 @@ case $variant in '*-wholename:full path pattern to search' \ '*-iwholename:full path pattern to search (case insensitive)' '*-ignore_readdir_race' + '*-mnewer:file to compare (modification time):_files' '*-noignore_readdir_race' '*-okdir:program: _command_names -e:*\;::program arguments: _normal' '*-samefile:file to compare inode:_files' \ @@ -79,9 +81,13 @@ case $variant in netbsd*|freebsd*|dragonfly*|darwin*) args+=( '-E[use extended regular expressions with -regex/-iregex]' + '-s[traverse directories in sorted order]' + ) + ;| + netbsd*|freebsd*|dragonfly*|openbsd*|darwin*) + args+=( '-X[warn if filename contains characters special to xargs]' '-f[specify file hierarchy to traverse]:path:_directories' - '-s[traverse directories in sorted order]' "-x[don't span filesystems]" '*-flags:flags:_chflags' ) @@ -114,11 +120,11 @@ case $variant in ;; esac -_arguments $args \ +_arguments -C $args \ '(-L -P)-H[only follow symlinks when resolving command-line arguments]' \ '(-H -P)-L[follow symlinks]' \ - '*-atime:access time (days)' \ - '*-ctime:inode change time (days)' \ + '*-atime:access time (days):->times' \ + '*-ctime:inode change time (days):->times' \ '*-depth' \ '*-exec:program: _command_names -e:*\;::program arguments: _normal' \ '*-follow' \ @@ -127,8 +133,7 @@ _arguments $args \ '*-inum:inode number:' \ '*-links:number of links:' \ '*-ls' \ - '*-mount' \ - '*-mtime:modification time (days)' \ + '*-mtime:modification time (days):->times' \ '*-name:name pattern' \ '*-newer:file to compare (modification time):_files' \ '*-nogroup' \ @@ -143,3 +148,13 @@ _arguments $args \ '*-xdev' \ '*-a' '*-o' \ '*: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 - + '' -" ) + fi + alts+=( "times:${state_descr}:_dates -f d" ) + _alternative $alts +fi diff --git a/Completion/Unix/Command/_gdb b/Completion/Unix/Command/_gdb index 48c9d654f..e9c333925 100644 --- a/Completion/Unix/Command/_gdb +++ b/Completion/Unix/Command/_gdb @@ -5,17 +5,19 @@ local cur="$words[CURRENT]" prev w list ret=1 expl [[ "$PREFIX" = --* ]] && _arguments -- '*=(CORE|SYM)FILE:core file:_files' \ '*=EXECFILE:executable:_files -g \*\(-\*\)' \ - '*=TTY:terminal device:compadd /dev/tty\*' && return 0 + '*=TTY:terminal device:_ttys' && return 0 if compset -P '-(cd|directory)='; then _files -/ elif compset -P '-tty='; then - _wanted devices expl 'terminal device' compadd - /dev/tty* + _wanted devices expl 'terminal device' _ttys elif compset -P '-(exec|se)='; then _description files expl executable _files "$expl[@]" -g '*(-*)' elif compset -P '-(symbols|core|command)='; then _files +elif compset -P '--pid='; then + _pids elif [[ "$PREFIX" = -* ]]; then _tags options while _tags; do @@ -47,7 +49,7 @@ else done if [[ $#w -gt 1 ]]; then - _alternative "files:: _files -g '*core(-.)'" "processes:: _pids -m ${w[1]:t}" + _alternative "files:: _files -g '*core*(-.)'" "processes:: _pids -m ${w[1]:t}" else _description files expl executable _files "$expl[@]" -g '*(-*)' diff --git a/Completion/Unix/Command/_git b/Completion/Unix/Command/_git index b8edc109e..0d705a9da 100644 --- a/Completion/Unix/Command/_git +++ b/Completion/Unix/Command/_git @@ -132,7 +132,7 @@ _git-archive () { declare -a backend_args - if (( words[(I)--format=*] > 0 && words[(I)--format=*] < CURRENT )); then + if (( words[(b:CURRENT-1:I)--format=*] )); then case ${words[$words[(I)--format=*]]#--format=} in (zip) backend_args=( @@ -442,7 +442,7 @@ _git-checkout () { '(-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)' \ + '(-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]' \ '(-)--[start file arguments]' \ '*:: :->branch-or-tree-ish-or-file' && ret=0 @@ -456,14 +456,13 @@ _git-checkout () { [[ $line[CURRENT] = -* ]] && return if (( CURRENT == 1 )) && [[ -z $opt_args[(I)--] ]]; then # TODO: Allow A...B - local branch_arg='branches::__git_revisions' \ + local branch_arg='' \ remote_branch_noprefix_arg='remote branches::__git_remote_branch_names_noprefix' \ tree_ish_arg='tree-ishs::__git_tree_ishs' \ file_arg='modified-files::__git_modified_files' if [[ -n ${opt_args[(I)-b|-B|--orphan|--detach]} ]]; then remote_branch_noprefix_arg= - tree_ish_arg= file_arg= elif [[ -n $opt_args[(I)--track] ]]; then branch_arg='remote-branches::__git_remote_branch_names' @@ -471,7 +470,6 @@ _git-checkout () { tree_ish_arg= file_arg= elif [[ -n ${opt_args[(I)--ours|--theirs|-m|--conflict|--patch]} ]]; then - branch_arg= remote_branch_noprefix_arg= fi @@ -924,7 +922,7 @@ _git-grep () { # TODO: Need to implement -<num> as a shorthand for -C<num> _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 treacked files]' \ + '(--cached)--no-index[search files in current directory, not just tracked files]' \ '(--exclude-standard)--no-exclude-standard[also search in ignored files]' \ '(--no-exclude-standard)--exclude-standard[exclude files standard ignore mechanisms]' \ '--untracked[search in untracked files]' \ @@ -950,7 +948,7 @@ _git-grep () { '(-z --null)'{-z,--null}'[output \0 after filenames]' \ '(-c --count)'{-c,--count}'[show number of matching lines in files]' \ '( --no-color)--color=-[color matches]:: :__git_color_whens' \ - '(--color )---no-color[do not color matches]' \ + '(--color )--no-color[do not color matches]' \ '--break[prefix the line number to matching lines]' \ '--heading[show the filename above the matches]' \ '(-A --after-context)'{-A,--after-context=}'[show <num> trailing lines, and separate groups of matches]: :__git_guard_number lines' \ @@ -1089,33 +1087,36 @@ _git-log () { $revision_options \ '-L+[trace the evolution of a line range or regex within a file]:range' \ '(-)--[start file arguments]' \ - '*:: :->commit-range-or-file' && ret=0 + '1: :->first-commit-ranges-or-files' \ + '*: :->commit-ranges-or-files' && ret=0 case $state in - (commit-range-or-file) - case $CURRENT in - (1) - if [[ -n ${opt_args[(I)--]} ]]; then - __git_cached_files && ret=0 - else - _alternative \ - 'commit-ranges::__git_commit_ranges' \ - 'cached-files::__git_cached_files' && ret=0 - fi - ;; - (*) - # TODO: Write a wrapper function that checks whether we have a - # committish range or comittish and calls __git_tree_files - # appropriately. - if __git_is_committish_range $line[1]; then - __git_tree_files ${PREFIX:-.} $(__git_committish_range_last $line[1]) && ret=0 - elif __git_is_committish $line[1]; then - __git_tree_files ${PREFIX:-.} $line[1] && ret=0 - else - __git_cached_files && ret=0 - fi - ;; - esac + (first-commit-ranges-or-files) + if [[ -n ${opt_args[(I)--]} ]]; then + __git_tree_files ${PREFIX:-.} HEAD && ret=0 + else + _alternative \ + 'commit-ranges::__git_commit_ranges' \ + 'cached-files::__git_tree_files ${PREFIX:-.} HEAD' && ret=0 + fi + ;; + (commit-ranges-or-files) + # Multiple revspecs are permitted. + if [[ -z ${opt_args[(I)--]} ]]; then + __git_commit_ranges "$@" && ret=0 + fi + + # TODO: Write a wrapper function that checks whether we have a + # committish range or comittish and calls __git_tree_files + # appropriately. + if __git_is_committish_range $line[1]; then + __git_tree_files ${PREFIX:-.} $(__git_committish_range_last $line[1]) && ret=0 + elif __git_is_committish $line[1]; then + __git_tree_files ${PREFIX:-.} $line[1] && ret=0 + else + __git_tree_files ${PREFIX:-.} HEAD && ret=0 + fi + ;; esac return ret @@ -1287,7 +1288,7 @@ _git-push () { '(--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"))' \ - ':: :__git_any_repositories' \ + ': :__git_any_repositories' \ '*: :__git_ref_specs' && ret=0 case $state in @@ -2925,7 +2926,6 @@ _git-config () { (*) # TODO: Do we need to set up a _requested/_next_label? declare -a action - local expl _description values expl "$parts[2]" eval "action=($parts[4])" "$action[1]" "$expl[@]" "${(@)action[2,-1]}" && ret=0 @@ -4976,8 +4976,8 @@ __git_is_treeish () { (( $+functions[__git_is_committish_range] )) || __git_is_committish_range () { [[ $1 == *..(.|)* ]] || return 1 - local first=$(__git_committish_range_first $1) - local last=$(__git_committish_range_last $1) + local first="$(__git_committish_range_first $1)" + local last="$(__git_committish_range_last $1)" [[ $first != *..* && $last != *..* ]] && \ __git_is_committish $first && \ __git_is_committish $last @@ -5025,7 +5025,16 @@ __git_ignore_line_inside_arguments () { (( $+functions[_git_commands] )) || _git_commands () { - local -a main_porcelain_commands + local -a cmdtypes + cmdtypes=( main_porcelain_commands user_commands + third_party_commands ancillary_manipulator_commands + ancillary_interrogator_commands interaction_commands + plumbing_manipulator_commands plumbing_interrogator_commands + plumbing_sync_commands plumbing_sync_helper_commands + plumbing_internal_helper_commands + ) + local -a $cmdtypes + main_porcelain_commands=( add:'add file contents to index' am:'apply patches from a mailbox' @@ -5064,7 +5073,6 @@ _git_commands () { submodule:'initialize, update, or inspect submodules' tag:'create, list, delete or verify tag object signed with GPG') - local -a ancillary_manipulator_commands ancillary_manipulator_commands=( config:'get and set repository or global options' fast-export:'data exporter' @@ -5079,7 +5087,6 @@ _git_commands () { repack:'pack unpacked objects in a repository' replace:'create, list, delete refs to replace objects') - local -a ancillary_interrogator_commands ancillary_interrogator_commands=( blame:'show what revision and author last modified each line of a file' cherry:'find commits not merged upstream' @@ -5097,7 +5104,6 @@ _git_commands () { verify-tag:'check GPG signature of tags' whatchanged:'show commit-logs and differences they introduce') - local -a interaction_commands interaction_commands=( archimport:'import an Arch repository into git' cvsexportcommit:'export a single commit to a CVS checkout' @@ -5109,7 +5115,6 @@ _git_commands () { send-email:'send collection of patches as emails' svn:'bidirectional operation between a Subversion repository and git') - local -a plumbing_manipulator_commands plumbing_manipulator_commands=( apply:'apply patch to files and/or to index' checkout-index:'copy files from index to working directory' @@ -5129,7 +5134,6 @@ _git_commands () { update-ref:'update object name stored in a reference safely' write-tree:'create tree from the current index') - local -a plumbing_interrogator_commands plumbing_interrogator_commands=( cat-file:'provide content or type information for repository objects' diff-files:'compare files in working tree and index' @@ -5149,7 +5153,6 @@ _git_commands () { var:'show git logical variable' verify-pack:'validate packed git archive files') - local -a plumbing_sync_commands plumbing_sync_commands=( daemon:'run a really simple server for git repositories' fetch-pack:'receive missing objects from another repository' @@ -5157,7 +5160,6 @@ _git_commands () { send-pack:'push objects over git protocol to another repository' update-server-info:'update auxiliary information file to help dumb servers') - local -a plumbing_sync_helper_commands plumbing_sync_helper_commands=( http-fetch:'download from remote git repository via HTTP' http-push:'push objects over HTTP/DAV to another repository' @@ -5167,7 +5169,6 @@ _git_commands () { upload-archive:'send archive back to git-archive' upload-pack:'send objects packed back to git fetch-pack') - local -a plumbing_internal_helper_commands plumbing_internal_helper_commands=( check-attr:'display gitattributes information' check-ignore:'debug gitignore/exclude files' @@ -5180,91 +5181,41 @@ _git_commands () { patch-id:'compute unique ID for a patch' stripspace:'filter out empty lines') - local -a user_commands zstyle -a :completion:$curcontext: user-commands user_commands - local -a third_party_commands local command for command in $_git_third_party_commands; do (( $+commands[git-${command%%:*}] )) && third_party_commands+=$command done - local -a aliases unique_aliases + local -a aliases __git_extract_aliases - local alias - for alias in $aliases; do - local name=${alias%%:*} - (( main_porcelain_commands[(I)$name:*] || - user_commands[(I)$name:*] || - third_party_commands[(I)$name:*] || - ancillary_manipulator_commands[(I)$name:*] || - ancillary_interrogator_commands[(I)$name:*] || - interaction_commands[(I)$name:*] || - plumbing_manipulator_commands[(I)$name:*] || - plumbing_interrogator_commands[(I)$name:*] || - plumbing_sync_commands[(I)$name:*] || - plumbing_sync_helper_commands[(I)$name:*] || - plumbing_internal_helper_commands[(I)$name:*] )) || unique_aliases+=$alias + local cmdtype len dup sep + local -a allcmds allmatching alts disp expl + + zstyle -s ":completion:${curcontext}:" list-separator sep || sep=-- + for cmdtype in $cmdtypes aliases; do + if [[ $cmdtype = aliases ]]; then + for dup in ${${aliases%:*}:*allcmds}; do + aliases=( ${aliases:#$dup:*} ) + done + fi + local -a ${cmdtype}_m + set -A ${cmdtype}_m ${(P)cmdtype%%:*} + allcmds+=( ${(P)${:-${cmdtype}_m}} ) done - - integer ret=1 - - _tags \ - aliases \ - main-porcelain-commands \ - user-commands \ - third-party-commands \ - ancillary-manipulator-commands \ - ancillary-interrogator-commands \ - interaction-commands \ - plumbing-manipulator-commands \ - plumbing-interrogator-commands \ - plumbing-sync-commands \ - plumbing-sync-helper-commands \ - plumbing-internal-helper-commands - - while _tags; do - - _requested aliases && \ - _describe -t aliases 'alias' unique_aliases && ret=0 - - _requested main-porcelain-commands && \ - _describe -t main-porcelain-commands 'main porcelain command' main_porcelain_commands && ret=0 - - _requested user-commands && \ - _describe -t user-commands 'user command' user_commands && ret=0 - - _requested third-party-commands && \ - _describe -t third-party-commands 'third-party command' third_party_commands && ret=0 - - _requested ancillary-manipulator-commands && \ - _describe -t ancillary-manipulator-commands 'ancillary manipulator command' ancillary_manipulator_commands && ret=0 - - _requested ancillary-interrogator-commands && \ - _describe -t ancillary-interrogator-commands 'ancillary interrogator command' ancillary_interrogator_commands && ret=0 - - _requested interaction-commands && \ - _describe -t interaction-commands 'interaction command' interaction_commands && ret=0 - - _requested plumbing-manipulator-commands && \ - _describe -t plumbing-manipulator-commands 'plumbing manipulator command' plumbing_manipulator_commands && ret=0 - - _requested plumbing-interrogator-commands && \ - _describe -t plumbing-interrogator-commands 'plumbing interrogator command' plumbing_interrogator_commands && ret=0 - - _requested plumbing-sync-commands && \ - _describe -t plumbing-sync-commands 'plumbing sync command' plumbing_sync_commands && ret=0 - - _requested plumbing-sync-helper-commands && \ - _describe -t plumbing-sync-helper-commands 'plumbing sync helper command' plumbing_sync_helper_commands && ret=0 - - _requested plumbing-internal-helper-commands && \ - _describe -t plumbing-internal-helper-commands 'plumbing internal helper command' plumbing_internal_helper_commands && ret=0 - - (( ret )) || break + zstyle -T ":completion:${curcontext}:" verbose && disp=(-ld '${cmdtype}_d') + _description '' expl '' # get applicable matchers + compadd "$expl[@]" -O allmatching -a allcmds + len=${#${(O)allmatching//?/.}[1]} # length of longest match + for cmdtype in aliases $cmdtypes; do + local -a ${cmdtype}_d + (( $#disp )) && set -A ${cmdtype}_d \ + ${${(Pr.COLUMNS-4.)cmdtype/(#s)(#m)[^:]##:/${(r.len.)MATCH[1,-2]} $sep }%% #} + alts+=( "${cmdtype//_/-}:${${cmdtype//_/ }%%(e|)s}:compadd ${(e)disp} -a ${cmdtype}_m" ) done - return ret + _alternative $alts } (( $+functions[__git_aliases] )) || @@ -5789,7 +5740,11 @@ __git_tree_ishs () { __git_objects () { compset -P '*:' if [[ -n $IPREFIX ]]; then - __git_tree_files "$PREFIX" "${IPREFIX%:}" + if compset -P ./ ; then + __git_tree_files "$PREFIX" "${IPREFIX%:./}" + else + __git_tree_files --root-relative "$PREFIX" "${IPREFIX%:}" + fi else _alternative \ 'revisions::__git_revisions' \ @@ -6034,12 +5989,23 @@ __git_changed_files () { 'changed-in-working-tree-files::__git_changed-in-working-tree_files' } +# __git_tree_files [--root-relative] FSPATH TREEISH [TREEISH...] [COMPADD OPTIONS] +# +# Complete [presently: a single level of] repository files under FSPATH. +# FSPATH is interpreted as a directory path within each TREEISH. +# FSPATH is relative to cwd, unless --root-relative is specified, in +# which case it is relative to the repository root. (( $+functions[__git_tree_files] )) || __git_tree_files () { local multi_parts_opts local tree Path integer at_least_one_tree_added local -a tree_files compadd_opts + local -a extra_args + + if [[ $1 == --root-relative ]]; then + extra_args+=(--full-tree) + fi zparseopts -D -E -a compadd_opts V: J: 1 2 n f X: M: P: S: r: R: q F: @@ -6047,7 +6013,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 --name-only -z $tree $Path 2>/dev/null)"}) + tree_files+=(${(ps:\0:)"$(_call_program tree-files git ls-tree $extra_args --name-only -z $tree $Path 2>/dev/null)"}) __git_command_successful $pipestatus && (( at_least_one_tree_added = 1 )) done @@ -6271,6 +6237,95 @@ __git_setup_diff_stage_options () { ) } +(( $+functions[__git_format_placeholders] )) || +__git_format_placeholders() { + local sep + local -a disp names placeholders expl + if compset -P 'format:'; then + compset -P '(%[^acgCG]|%?[^%]|[^%])#' + if compset -P '%C'; then + _wanted colors expl color compadd reset red green blue + return + fi + if [[ -prefix %G ]]; then + placeholders=( + 'GG:raw verification message' + 'G?:indicate [G]ood, [B]ad, [U]ntrusted or [N]o signature' + 'GS:name of signer' + 'GK:signing key' + ) + disp=( -l ) + elif [[ -prefix %g ]]; then + placeholders=( + gD:'reflog selector' + gd:'short reflog selector' + gn:'reflog identity' + gs:'reflog subject' + ) + disp=( -l ) + elif [[ $PREFIX = (#b)%([ac]) ]]; then + placeholders=( + n:'name' + N:'name (use .mailmap)' + e:'email' + E:'email (use .mailmap)' + d:'date' + D:'date, RFC2822 style' + r:'date, relative' + t:'date, UNIX timestamp' + i:'date, like ISO 8601' + I:'date, strict ISO 8601' + ) + placeholders=( $match[1]$^placeholders ) + else + placeholders=( + H:commit\ hash + h:'abbreviated commit hash' + T:'tree hash' + t:'abbreviated tree hash' + P:'parent hash' + p:'abbreviated parent hash' + a:'author details' + c:'committer details' + d:'ref name in brackets' + D:'ref name' + e:encoding + s:subject + f:'sanitized subject' + g:reflog + b:body + B:'raw body' + N:notes + G:GPG\ details + C:color + m:mark + n:newline + %:raw\ % + x:'hex code' + w:'switch line wrapping' + ) + fi + names=( ${placeholders%%:*} ) + if zstyle -T ":completion:${curcontext}:" verbose; then + zstyle -s ":completion:${curcontext}:" list-separator sep || sep=-- + zformat -a placeholders " $sep " $placeholders + disp+=(-d placeholders) + else + disp=() + fi + _wanted placeholders expl placeholder \ + compadd -p % -S '' "$disp[@]" "$@" - "$names[@]" + else + _describe -t formats format '( oneline:"commit-ids and subject of messages" + short:"few headers and only subject of messages" + medium:"most parts of messages" + full:"all parts of commit messages" + fuller:"like full and includes dates" + email:"use email headers like From and Subject" + raw:"the raw commits" )' -- '( format:"specify own format" )' -S ':' + fi +} + (( $+functions[__git_setup_revision_options] )) || __git_setup_revision_options () { local -a diff_options @@ -6278,16 +6333,7 @@ __git_setup_revision_options () { revision_options=( $diff_options - # TODO: format pretty print format is a lot more advanced than this. - # TODO: You can't actually specify --format without a format. - '(-v --header)'{--pretty=-,--format=-}'[pretty print commit messages]::format:((oneline\:"commit-ids and subject of messages" - short\:"few headers and only subject of messages" - medium\:"most parts of messages" - full\:"all parts of commit messages" - fuller\:"like full and includes dates" - email\:"use email headers like From and Subject" - raw\:"the raw commits" - format\:"specify own format"))' + '(-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' @@ -6329,8 +6375,8 @@ __git_setup_revision_options () { '*--not[reverses meaning of ^ prefix for revisions that follow]' '--all[show all commits from refs]' '--branches=-[show all commits from refs/heads]::pattern' - '--tags=[-show all commits from refs/tags]::pattern' - '--remotes=[-show all commits from refs/remotes]::pattern' + '--tags=-[show all commits from refs/tags]::pattern' + '--remotes=-[show all commits from refs/remotes]::pattern' '--glob=[show all commits from refs matching glob]:pattern' '--exclude=[do not include refs matching glob]:pattern' '--exclude=[do not include refs matching glob]:pattern' diff --git a/Completion/Unix/Command/_gzip b/Completion/Unix/Command/_gzip index 927d31d45..f9fd17819 100644 --- a/Completion/Unix/Command/_gzip +++ b/Completion/Unix/Command/_gzip @@ -1,37 +1,57 @@ -#compdef gzip gunzip gzcat=gunzip -redirect-,<,gunzip=gunzip -redirect-,>,gzip=gunzip -redirect-,<,gzip=gzip -value-,GZIP,-default- +#compdef gzip gunzip gzcat=gunzip pigz unpigz -redirect-,<,gunzip=gunzip -redirect-,>,gzip=gunzip -redirect-,<,gzip=gzip -value-,GZIP,-default- local decompress files expl curcontext="$curcontext" state line ret=1 +local -a pigz typeset -A opt_args +local excl="--decompress --uncompress -1 -2 -3 -4 -5 -6 -7 -8 -9 -11 --best" files=( '*:files:->files' ) case "$service" in +unpigz|pigz) + pigz=( + '(-K --zip)'{-K,--zip}'[compress to PKWare zip format]' + '(-b --blocksize)'{-b+,--blocksize}'[set compression block size]:size (KiB)' + '(-p --processes)'{-p,--processes}'[specify number of processes to use]' + '(-z --zlib)'{-z,--zlib}'[compress to zlib (.zz) format]' + '(-T --no-time)'{-T,--no-time}"[don't store/restore modification time in/from header]" + ) +;| +pigz) + pigz+=( + '(-i --independent)'{-i,--independent}'[compress blocks independently to allow for damage recovery]' + "($excl)-11" + '(--rsyncable)-R[make rsync-friendly archive]' + "($excl)"{-F,--first}'[do iterations first, before block split]' + "($excl)"{-I+,--iterations}'[specify number of iterations for optimization]:iterations [15]' + "($excl)"{-M+,--maxsplits}'[specify maximum number of split blocks]:split blocks [15]' + "($excl)"{-O,--oneblock}"[don't split into smaller blocks]" + ) +;| *GZIP*) compset -q words=( fake "$words[@]" ) (( CURRENT++ )) files=() ;& -gunzip|zcat) +gunzip|zcat|unpigz) decompress=yes ;& -gzip) +gzip|pigz) _arguments -C -s -S \ '(--to-stdout --stdout)-c[write on standard output]' \ '(-c --stdout)--to-stdout[write on standard output]' \ '(-c --to-stdout)--stdout[write on standard output]' \ - '(--decompress --uncompress)-d[decompress]' \ - '(-d --uncompress)--decompress[decompress]' \ - '(-d --decompress)--uncompress[decompress]' \ + "($excl)"{-d,--decompress,--uncompress}'[decompress]' \ '(--force)-f[force overwrite]' \ '(-f)--force[force overwrite]' \ - '(--help)-h[display help message]' \ - '(-h)--help[display help message]' \ + '(- *)'{-h,--help}'[display help message]' \ + "(--keep)-k[don't delete input files]" \ + "(-k)--keep[don't delete input files]" \ '(--list)-l[list compressed file contents]' \ '(-l)--list[list compressed file contents]' \ - '(--license)-L[display software license]' \ - '(-L)--license[display software license]' \ - '(--no-name)-n[do not save or restore the original name and time stamp]' \ - '(-n)--no-name[do not save or restore the original name and time stamp]' \ + '(- *)'{-L,--license}'[display software license]' \ + "(--no-name)-n[don't save or restore the original name and time stamp]" \ + "(-n)--no-name[don't save or restore the original name and time stamp]" \ '(--name)-N[save or restore the original name and time stamp]' \ '(-N)--name[save or restore the original name and time stamp]' \ '(--quiet --silent)-q[suppress all warnings]' \ @@ -39,27 +59,16 @@ gzip) '(-q --quiet)--silent[suppress all warnings]' \ '(--recursive)-r[operate recursively on directories]' \ '(-r)--recursive[operate recursively on directories]' \ - '--rsyncable[make rsync-friendly archive]' \ + '(-R)--rsyncable[make rsync-friendly archive]' \ '(--suffix)-S+[specify suffix for compressed files]:suffix:' \ '(-S)--suffix=[specify suffix for compressed files]:suffix:' \ '(--test)-t[test compressed file integrity]' \ '(-t)--test[test compressed file integrity]' \ '(--verbose)-v[verbose mode]' \ '(-v)--verbose[verbose mode]' \ - '(--version)-V[display version number]' \ - '(-V)--version[display version number]' \ - '( -1 -2 -3 -4 -5 -6 -7 -8 -9 --best)--fast' \ - '(--fast -2 -3 -4 -5 -6 -7 -8 -9 --best)-1' \ - '(--fast -1 -3 -4 -5 -6 -7 -8 -9 --best)-2' \ - '(--fast -1 -2 -4 -5 -6 -7 -8 -9 --best)-3' \ - '(--fast -1 -2 -3 -5 -6 -7 -8 -9 --best)-4' \ - '(--fast -1 -2 -3 -4 -6 -7 -8 -9 --best)-5' \ - '(--fast -1 -2 -3 -4 -5 -7 -8 -9 --best)-6' \ - '(--fast -1 -2 -3 -4 -5 -6 -8 -9 --best)-7' \ - '(--fast -1 -2 -3 -4 -5 -6 -7 -9 --best)-8' \ - '(--fast -1 -2 -3 -4 -5 -6 -7 -8 --best)-9' \ - '(--fast -1 -2 -3 -4 -5 -6 -7 -8 -9 )--best' \ - "$files[@]" && ret=0 + '(- *)'{-V,--version}'[display version number]' \ + "($excl)"-{-fast,1,2,3,4,5,6,7,8,9,-best} \ + "$pigz[@]" "$files[@]" && ret=0 ;; esac diff --git a/Completion/Unix/Command/_ifconfig b/Completion/Unix/Command/_ifconfig index 49b018841..0c81bce10 100644 --- a/Completion/Unix/Command/_ifconfig +++ b/Completion/Unix/Command/_ifconfig @@ -65,7 +65,7 @@ esac _arguments -C "$args[@]" \ '-a[apply to all interfaces]' \ - '1:network interface:_net_interfaces' \ + '1:network interface:_net_interfaces -r ": \t\n\-"' \ '::address family:(atalk ether inet inet6 ax25 ddp ipx netrom)' \ '*:option:->options' && ret=0 diff --git a/Completion/Unix/Command/_imagemagick b/Completion/Unix/Command/_imagemagick index 115cb01e4..1fc6089c8 100644 --- a/Completion/Unix/Command/_imagemagick +++ b/Completion/Unix/Command/_imagemagick @@ -1,6 +1,7 @@ #compdef animate composite combine convert display identify import mogrify montage xtp -local state line expl formats curcontext="$curcontext" +local state line expl curcontext="$curcontext" +local -a formats typeset -A opt_args # Things that could be improved: @@ -11,10 +12,10 @@ 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 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//:/|})(-.)" + _files "$@" -g "*.(#i)(${(j:|:)formats})(-.)" return fi @@ -444,7 +445,7 @@ case "$service" in '*-filter:filter type for resizing:(Point Box Triangle Hermite Hanning Hamming Blackman Gaussian Quadratic Cubic Catrom Mitchell Lanczos Bessel Sinc)' \ '*-flip[vertical mirror image]' \ '*-flop[horizontal mirror image]' \ - "*-format:output file format:(${formats//:/ })" \ + "*-format:output file format:($formats)" \ '*-font:annotation font:_x_font' \ '*-frame:border dimensions (<width>x<height>+<out>+<in>)' \ '*-fuzz:maximum distance for equal colors' \ diff --git a/Completion/Unix/Command/_java b/Completion/Unix/Command/_java index 7b1dce18f..2aef15a25 100644 --- a/Completion/Unix/Command/_java +++ b/Completion/Unix/Command/_java @@ -7,45 +7,76 @@ typeset -A opt_args tmpassoc jdb_args=() case "$service" in +javac|jar) + if compset -P @; then + _wanted files expl 'option file' _files + return + fi + ;| # continue javac) _arguments -C \ '-g-[generate debugging information]:debug:->debug' \ + '-A-[specify option to annotation processors]:option' \ + '-implicit\:-[control generation of class files for implicitly loaded sources]:implicit:(class none)' \ '-nowarn[generate no warnings]' \ '-verbose[output messages about what the compiler is doing]' \ '-deprecation[output source locations where deprecated APIs are used]' \ - '-classpath[specify where to find user class files]:class path:->classpath' \ + '(-cp -classpath)'{-cp,-classpath}'[specify where to find user class files]:class path:->classpath' \ '-sourcepath[specify where to find input source files]:source path:->sourcepath' \ '-bootclasspath[override location of bootstrap class files]:bootstrap class path:->bootstrapclasspath' \ '-extdirs[override location of installed extensions]:extensions directories:->extdirs' \ '-d[specify where to place generated class files]:directory:_files -/' \ '-encoding[specify character encoding used by source files]:encoding:->encoding' \ - '-source[provide source compatibility with specified release]:release:(1.{2..5})' \ + '-proc\:-[control annotation processing]:annotation processing:(none only)' \ + '-processor[specify annotation processors to run]:class:_files' \ + '-processorpath[specify where to find annotation processors]:directory:_directories' \ + '-s[specify directory for generated source files]:directory:_directories' \ + '-source[provide source compatibility with specified release]:release:(1.{2..8} {5..8})' \ '-target[specify VM version]:release:(1.{1..5})' \ - '-help[print a synopsis of standard options]' \ + '(-)-help[print a synopsis of standard options]' \ + '(-)-version[print version information]' \ + '(-)-X[display information about non-standard options]' \ '*:java source file:_files -g \*.java\(-.\)' && return 0 ;; jdb) jdb_args=( '-host[specify host to connect to]:host:_hosts' - '-password[specify password]:password:' + '-password[specify password]:password' + '-attach[attach to running VM]:address' + '-listen[wait for VM to connect]:address' + '-listenany[wait for VM to connect at any available address]' + '-launch[launch VM immediately]' + '-listconnectors[list the connectors available in this VM]' + '-connect[connect to target VM]:connector' + -dbgtrace -tclient -tserver + '-J-[java runtime option]:option' ) ;& java) _arguments -C \ "$jdb_args[@]" \ + -client -server -d32 -d64 \ + '-agentlib\:-:agent library' \ + '-agentpath\:-:path:_directories' \ + '-javaagent\:-:path:_directories' \ '(-cp -classpath)'{-cp,-classpath}'[specify path for user class files]:class path:->classpath' \ '-D-[specify a property]:property:->property' \ + \*{-enableassertions,-ea}-::class \ + \*{-disableassertions,-da}-::class \ + '(-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]' \ - '-version[print version]' \ - '-help[print help message]' \ - '(- 1)-jar[specify a program capsulated as jar]:jar:_files -g \*.jar\(-.\)' \ + '(- 1)-version[print version]' \ + '-showversion[print version and contrinue]' \ + '(- 1)-'{\?,help}'[print help message]' \ + '(- 1)-X-[non-standard java option]:option' \ + '(- 1)-jar[specify a program encapsulated as jar]:jar:_files -g \*.jar\(-.\)' \ '(-):class:_java_class -m main ${(kv)opt_args[(i)(-classpath|-cp)]}' \ - '*::args: _normal' \ + '*::args:= _normal' \ && return 0 ;; @@ -111,16 +142,20 @@ jar) '(c t x )u[update archive]' \ 'f[specify archive file]' \ 'v[verbose mode]' \ - 'm[specify manifest file]' \ + '(e)m[specify manifest file]' \ + '(m)e[specify class of for application entry point]' \ '0[store only without using ZIP compression]' \ - 'M[do not create manifest file]' && return + 'M[do not create manifest file]' \ + 'i[generate index information for specified jar files]' && return else jar_cmd="${words[2]#-}" tmpassoc=( m ':manifest file:_files' + e ':main class' f ':archive file:_files -g "*.([ejw]ar|zip)(-.)"' ) - _arguments -C \ + _arguments '*-C[directory of class file]:directory:_directories' \ + '-J-[java runtime option]:option' \ "${jar_cmd/[^-]*/:dummy:}" \ ${${(s::)jar_cmd}/(#b)(?)/$tmpassoc[$match[1]]} \ '*:file:->jararg' && return 0 @@ -140,6 +175,7 @@ javah|javah_g) '-bootclasspath[specify path for bootstrap class files]:bootstrap class path:->bootstrapclasspath' \ '-old[generate old JDK1.0-style header files]' \ '-force[force output]' \ + '-J-[java runtime option]:option' \ '*:class:_java_class -m main ${(kv)opt_args[(i)-classpath]}' && return 0 ;; @@ -558,20 +594,14 @@ docsrc) ;; jararg) - if [[ -prefix - ]]; then - tmp=('-C:chdir') - _describe -o 'option' tmp -- && return - elif [[ "$words[CURRENT - 2]" == -C ]]; then + if [[ "$words[CURRENT - 2]" == -C ]]; then _wanted file expl 'input file' _files -W "($words[CURRENT - 1])" && return - elif [[ "$words[CURRENT - 1]" == -C ]]; then - _wanted directories expl 'chdir to' _files -/ && return elif [[ $words[2] = *x* ]]; then jf="$words[3]" if [[ $jf != $_jar_cache_name && -f $jf ]]; then _jar_cache_list=("${(@f)$($words[1] tf $jf)}") _jar_cache_name=$jf fi - _wanted files expl 'file from archive' _multi_parts / _jar_cache_list && return else _wanted files expl 'input file' _files && return diff --git a/Completion/Unix/Command/_kvno b/Completion/Unix/Command/_kvno index 285aab3c8..782d9e6fc 100644 --- a/Completion/Unix/Command/_kvno +++ b/Completion/Unix/Command/_kvno @@ -11,8 +11,7 @@ _arguments -C \ ':principal:->principal' && ret=0 if [[ $state = principal ]]; then - if [[ -prefix host/ ]]; then - compset -P host/ + if compset -P host/; then _hosts && ret=0 else _alternative \ diff --git a/Completion/Unix/Command/_last b/Completion/Unix/Command/_last index 706d82f26..f198e0fff 100644 --- a/Completion/Unix/Command/_last +++ b/Completion/Unix/Command/_last @@ -9,7 +9,7 @@ for pattern arg in \ '((free|net|open)bsd*|darwin*|dragonfly*|linux-gnu)' '-h[limit sessions by hostname]:host:_hosts' \ '((free|open)bsd*|linux-gnu)' '-s[report duration in seconds]' \ '(freebsd*|openbsd*)' '-d[limit sessions to those active at snapshot time]:time ([[CC]YY][MMDD]hhmm[.SS])' \ - '((net|free|open)bsd*|darwin*|dragonfly*)' '-t[limit sessions by tty]:tty:compadd -a ttys' \ + '((net|free|open)bsd*|darwin*|dragonfly*)' '-t[limit sessions by tty]:tty:_ttys -D' \ 'openbsd*' '-c[calculate total time]' \ '^darwin*' '-f[specify account file]:file:_files' \ '(solaris*|linux-gnu|freebsd*|openbsd*)' '-n[specify number of lines to show]:number' \ @@ -45,7 +45,7 @@ case $OSTYPE in esac if [[ $OSTYPE = (linux-gnu|solaris*) ]]; then - args+=( '*:arg: _alternative "users:user:_users" "ttys:tty:compadd -a ttys"' ) + args+=( '*:arg: _alternative "users:user:_users" "ttys:tty:_ttys -d" "ttys:tty:(reboot)"' ) else args+=( '*:user:_users' ) fi diff --git a/Completion/Unix/Command/_make b/Completion/Unix/Command/_make index c14a34c58..48befa749 100644 --- a/Completion/Unix/Command/_make +++ b/Completion/Unix/Command/_make @@ -4,58 +4,68 @@ # are used in those targets and their dependencies. _make-expandVars() { - local open close var val front ret tmp=$1 + local open close var val front='' rest=$1 - front=${tmp%%\$*} - case $tmp in - (\(*) # Variable of the form $(foobar) - open='(' - close=')' - ;; - - ({*) # ${foobar} - open='{' - close='}' - ;; - - ([[:alpha:]]*) # $foobar. This is exactly $(f)oobar. - open='' - close='' - var=${(s::)var[1]} - ;; - - (\$*) # Escaped $. - print -- "${front}\$$(_make-expandVars ${tmp#\$})" - return - ;; + while [[ $rest == (#b)[^$]#($)* ]]; do + front=$front${rest[1,$mbegin[1]-1]} + rest=${rest[$mbegin[1],-1]} - (*) # Nothing left to substitute. - print -- $tmp - return - ;; - esac - - if [[ -n $open ]] - then - var=${tmp#$open} - var=${var%%$close*} - fi + case $rest[2] in + ($) # '$$'. may not appear in target and variable's value + front=$front\$\$ + rest=${rest[3,-1]} + continue + ;; + (\() # Variable of the form $(foobar) + open='(' + close=')' + ;; + ({) # ${foobar} + open='{' + close='}' + ;; + ([[:alpha:]]) # $foobar. This is exactly $(f)oobar. + open='' + close='' + var=$rest[2] + ;; + (*) # bad parameter name + print -- $front$rest + return 1 + ;; + esac - case $var in - ([[:alnum:]_]#) - val=${VARIABLES[$var]} - ret=${ret//\$$open$var$close/$val} - ;; + if [[ -n $open ]]; then + if [[ $rest == \$$open(#b)([[:alnum:]_]##)(#B)$close* ]]; then + var=$match + else # unmatched () or {}, or bad parameter name + print -- $front$rest + return 1 + fi + fi - (*) - # Improper variable name. No replacement. - # I'm not sure if this is desired behavior. - front+="\$$open$var$close" - ret=${ret/\$$open$var$close/} - ;; - esac + val='' + if [[ -n ${VAR_ARGS[(i)$var]} ]]; then + val=${VAR_ARGS[$var]} + else + if [[ -n $opt_args[(I)(-e|--environment-overrides)] ]]; then + if [[ $parameters[$var] == scalar-export* ]]; then + val=${(P)var} + elif [[ -n ${VARIABLES[(i)$var]} ]]; then + val=${VARIABLES[$var]} + fi + else + if [[ -n ${VARIABLES[(i)$var]} ]]; then + val=${VARIABLES[$var]} + elif [[ $parameters[$var] == scalar-export* ]]; then + val=${(P)var} + fi + fi + fi + rest=${rest//\$$open$var$close/$val} + done - print -- "${front}$(_make-expandVars ${ret})" + print -- ${front}${rest} } _make-parseMakefile () { @@ -84,16 +94,9 @@ _make-parseMakefile () { # TARGET: dependencies # TARGET1 TARGET2 TARGET3: dependencies - ([[:alnum:]][^$TAB:=]#:[^=]*) - input=$(_make-expandVars $input) - target=${input%%:*} - dep=${input#*:} - dep=${(z)dep} - dep="$dep" - for tmp in ${(z)target} - do - TARGETS[$tmp]=$dep - done + ([[*?[:alnum:]$][^$TAB:=%]#:[^=]*) + target=$(_make-expandVars ${input%%:*}) + TARGETS+=( ${(z)target} ) ;; # Include another makefile @@ -150,9 +153,18 @@ _make() { local prev="$words[CURRENT-1]" file expl tmp is_gnu dir incl match local context state state_descr line local -a option_specs - local -A TARGETS VARIABLES opt_args + local -A VARIABLES VAR_ARGS opt_args + local -aU TARGETS keys local ret=1 + # VAR=VAL on the current command line + for tmp in $words; do + if [[ $tmp == (#b)([[:alnum:]_]##)=(*) ]]; then + VAR_ARGS[${tmp[$mbegin[1],$mend[1]]}]=${(e)tmp[$mbegin[2],$mend[2]]} + fi + done + keys=( ${(k)VAR_ARGS} ) # to be used in 'compadd -F keys' + _pick_variant -r is_gnu gnu=GNU unix -v -f if [[ $is_gnu == gnu ]] @@ -275,9 +287,9 @@ _make() { while _tags do _requested targets expl 'make targets' \ - compadd -- ${(k)TARGETS} && ret=0 + compadd -Q -- $TARGETS && ret=0 _requested variables expl 'make variables' \ - compadd -S '=' -- ${(k)VARIABLES} && ret=0 + compadd -S '=' -F keys -- ${(k)VARIABLES} && ret=0 done fi esac diff --git a/Completion/Unix/Command/_mh b/Completion/Unix/Command/_mh index 48177982e..3eddd41a6 100644 --- a/Completion/Unix/Command/_mh +++ b/Completion/Unix/Command/_mh @@ -1,4 +1,4 @@ -#compdef ali anno burst comp dist flist flists folder folders forw inc mark mhlist mhmail mhn mhparam mhpath mhshow mhstore msgchk next packf pick prev refile repl rmf rmm scan show sortm whom +#compdef ali anno burst comp dist flist flists fmttest folder folders forw fnext fprev inc mark mhfixmsg mhlist mhmail mhn mhparam mhpath mhshow mhstore msgchk new next packf pick prev refile repl rmf rmm scan show sortm whom if [[ -z $commands[mhpath] ]]; then _message "MH commands are not available" @@ -73,7 +73,9 @@ elif [[ $service = mhparam ]]; then elif [[ $service = ali ]]; then _email_addresses -n MH elif compset -P '*:'; then - _message -e number 'number of messages' + _alternative \ + 'sequences:sub-sequence:(first last cur prev next)' + 'number: : _message -e number "number of messages"' else # Generate sequences. local foldnam folddir f sequences mhneg ret=1 @@ -95,6 +97,7 @@ else sequences=( ${${(f)"$(mark $foldnam 2>/dev/null)"}%%:*} ) mhneg="$(mhparam Sequence-Negation)" && sequences=( {,$mhneg}$^sequences ) sequences+=( all first last prev next ) + [[ $service = mhpath ]] && sequences+=( new ) _tags sequences while _tags; do while _next_label sequences expl sequence; do diff --git a/Completion/Unix/Command/_ncftp b/Completion/Unix/Command/_ncftp index 93de404aa..763a61344 100644 --- a/Completion/Unix/Command/_ncftp +++ b/Completion/Unix/Command/_ncftp @@ -1,6 +1,12 @@ #compdef ncftp lftp -local expl bookmarks=$HOME/.$service/bookmarks +local expl bookmarks XDG_DATA_HOME=${XDG_DATA_HOME:-$HOME/.local/share} + +if [[ $service = lftp ]]; then + bookmarks=$XDG_DATA_HOME/$service/bookmarks +else + bookmarks=$HOME/.$service/bookmarks +fi if [[ -f $bookmarks ]]; then bookmarks=(${"${(f)$(<$bookmarks)}"%%[[:space:],]*}) diff --git a/Completion/Unix/Command/_patchutils b/Completion/Unix/Command/_patchutils new file mode 100644 index 000000000..323c0a730 --- /dev/null +++ b/Completion/Unix/Command/_patchutils @@ -0,0 +1,106 @@ +#compdef combinediff interdiff filterdiff flipdiff grepdiff lsdiff splitdiff unwrapdiff + +local args +args=( + '(-)--help[display help information]' + '(-)--version[display version information]' +) + +case $service in + (inter|combine|filter|flip|ls|grep)diff) + args+=( + '(-p --strip-match=)'{-p,--strip-match=}'[specify number of path prefix components to strip]:number of path prefix components to strip' + '(-z --decompress)'{-z,--decompress}'[decompress .gz and .bz2 files]' + ) + ;| + interdiff|combinediff|flipdiff) + args+=( + '(-q --quiet)'{-q,--quiet}'[quieter output]' + '(-U --unified)'{-U,--unified=}'[specify lines of context to include]:lines of context' + \*{-d,--drop-context=}"[don't include context on files matching pattern]:pattern:_files" + '(-w --ignore-all-space)'{-w,--ignore-all-space}'[ignore all whitespace changes in patches]' + '(-B --ignore-blank-lines)'{-B,--ignore-blank-lines}'[ignore changes whose lines are all blank]' + '(-i --ignore-case)'{-i,--ignore-case}'[ignore case differences]' + '(-b --ignore-space-change)'{-b,--ignore-space-change}'[ignore changes in the amount of whitespace]' + '!(--in-place)--interpolate' '!-h' + '!(--no-revert-omitted --in-place)--combinediff' + '!(--no-revert-omitted)--flip' + '1:diff 1:_files' '2:diff 2:_files' + ) + ;| + interdiff) + args+=( + "--no-revert-omitted[don't revert files changed in only the first patch]" + ) + ;; + flipdiff) args+=( '--in-place[write output to original input files]' ) ;; + filterdiff|grepdiff|lsdiff) + args+=( + '(-i --include)'{-i,--include}'[include only files matching pattern]:pattern:_files' + '(-x --exclude)'{-x,--exclude}'[exclude files matching pattern]:pattern:_files' + '(-# --hunks)'{-#+,--hunks=}'[only list hunks within specified range]:range' + '--lines=[only list hunks containing lines within specified range]:range' + '(-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' + ) + ;| + grepdiff|lsdiff) + args+=( + '(-n --line-number)'{-n,--line-number}'[show line number at which each patch begins]' + '(-N --number-files)'{-N,--number-files}'[show file number before each filename]' + '(-s --status)'{-s,--status}'[mark added, modified and removed files]' + ) + ;| + lsdiff) + args+=( + '(-E --empty-files-as-removed)'{-E,--empty-files-as-removed}'[treat empty files as absent]' + \*{-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) + args+=( + '(-I --include-from-file)'{-I+,--include-from-file=}'[include only files matching pattern listed in specified file]:file:_files' + '--annotate[annotate each hunk with the filename and hunk number]' + '--format=[use specified output format]:format:(unified context)' + '--addnewprefix=[insert specified path prefix before new file path names]:prefix:_directories' + '--addoldprefix=[insert specified path prefix before original file path names]:prefix:_directories' + '--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' + '(-v --verbose --clean)'{-v,--verbose}'[always show non-diff lines in output]' + '(-v --verbose)--clean[always remove all non-diff lines from output]' + ) + ;; + grepdiff) + args+=( + '(-E --extended-regexp)'{-E,--extended-regexp}'[use extended regular expressions]' + '(-H --with-filename -h --no-filename)'{-h,--no-filename}"[don't print the name of the patch file containing each patch]" + '(-f --file)'{-f+,--file=}'[read regular expressions from file]:file:_files' + '--output-matching=[display the matching hunk- or file-level diffs]:level:(hunk file)' + ) + ;; + splitdiff) + args+=( + '-a[split every single file level patch]' + '-d[create file names such as a_b.c.patch for a patch that modifies a/b.c]' + '-D[write output files into specified directory]:_directories' + '-p[specify number of path prefix components to strip]:number of path prefix components to strip' + "-E[don't use .patch filename extension when writing output files]" + '1:diff file:_files' + ) + ;; + unwrapdiff) + args+=( '-v[verbose operation]' '*:diff file:_files' ) + ;; +esac + +_arguments -s $args + diff --git a/Completion/Unix/Command/_pgrep b/Completion/Unix/Command/_pgrep index d3dcd1812..0b7d23d4d 100644 --- a/Completion/Unix/Command/_pgrep +++ b/Completion/Unix/Command/_pgrep @@ -13,7 +13,7 @@ arguments=('-P[parent process id]:parent process id:->ppid' '-M[extract the name list from the specified core]:files:_files' '-N[extract the name list from the specified system]:files:_files' '-s[match only session id]:session id:->sid' - '-t[match only controlled by terminal]:terminal device:->tty' + '-t[match only controlled by terminal]:terminal device:_sequence _ttys -d' '-T[match only in processes specified routing table in rtable]' '-u[match only effective user id]:user:_users' '-U[match only real user id]:user:_users' @@ -69,12 +69,6 @@ arguments=( ${(M)arguments:#(|\*)(|\(*\))-[$optchars]*} _arguments -C -s -w $arguments && ret=0 case $state in - (tty) - local -a ttys - ttys=( /dev/tty*(N) /dev/pts/*(N) ) - _sequence -s , _wanted tty expl 'terminal device' compadd - ${ttys#/dev/} - ;; - (sid) if [[ $OSTYPE == openbsd* ]]; then break diff --git a/Completion/Unix/Command/_ps b/Completion/Unix/Command/_ps index e557b27b9..6f67f7933 100644 --- a/Completion/Unix/Command/_ps +++ b/Completion/Unix/Command/_ps @@ -31,7 +31,7 @@ args=( '*-G+[select processes by real group]:group:_sequence -s , _groups' '*-g+[select processes by effective group or session]:group:_sequence -s , _groups' '*-s+[select processes by session leaders]:session leader:_sequence -s , _pids' - '*-t+[select processes by attached terminal]:tty:_sequence -s , compadd - /dev/pts/<->(N\:s/\\/dev\\//) /dev/tty*(N\:t)' + '*-t+[select processes by attached terminal]:tty:_sequence -s , _ttys -D' '*-u+[select processes by effective user]:user:_sequence -s , _users' '*-U+[select processes by real user]:user:_sequence -s , _users' '-o+[specify output format]:property:_sequence -s , _ps_props -' @@ -163,8 +163,7 @@ if (( CURRENT > 1 )) && [[ $OSTYPE != solaris* || ( $OSTYPE = linux-gnu && $word *J) _sequence _jails -0 && return ;; *[MNW]) _files && return ;; *t) - _wanted -C option-t-1 ttys expl tty _sequence -s , \ - compadd - /dev/pts/<->(N\:s/\\/dev\\//) /dev/tty*(N\:t) && return + _wanted -C option-t-1 ttys expl tty _sequence -s , _ttys -D && return ;; *p) _wanted -C option-p-1 processes expl 'process ID' _sequence -s , _pids && return;; *U) _wanted -C option-U-1 users expl user _sequence -s , _users && return ;; diff --git a/Completion/Unix/Command/_rm b/Completion/Unix/Command/_rm index 4728ad464..4d0dbdb3f 100644 --- a/Completion/Unix/Command/_rm +++ b/Completion/Unix/Command/_rm @@ -40,7 +40,7 @@ fi local curcontext=$curcontext state line ret=1 declare -A opt_args -_arguments -C $opts \ +_arguments -C -s $opts \ $args && ret=0 case $state in diff --git a/Completion/Unix/Command/_sort b/Completion/Unix/Command/_sort index 2e7f0a01a..1ad57f442 100644 --- a/Completion/Unix/Command/_sort +++ b/Completion/Unix/Command/_sort @@ -55,7 +55,7 @@ case $variant in netbsd*|dragonfly*) args+=( "${ordering}-l[sort by string length of field]" - "(-s)-S[don't use stable sort" + "(-s)-S[don't use stable sort]" ) ;| openbsd*) diff --git a/Completion/Unix/Command/_ssh b/Completion/Unix/Command/_ssh index 2be5672da..a66702a65 100644 --- a/Completion/Unix/Command/_ssh +++ b/Completion/Unix/Command/_ssh @@ -1,6 +1,6 @@ #compdef ssh slogin=ssh scp ssh-add ssh-agent ssh-keygen sftp ssh-copy-id -# Completions currently based on OpenSSH 6.0 (released on 2012-04-22). +# 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 @@ -39,8 +39,10 @@ _ssh () { '(-P)-b+[specify interface to transmit on]:bind address:_bind_addresses' \ '-D+[specify a dynamic port forwarding]:dynamic port forwarding:->dynforward' \ '-e+[set escape character]:escape character (or `none'\''):' \ + '-E[append log output to file instead of stderr]:_files' \ '(-n)-f[go to background]' \ '-g[allow remote hosts to connect to local forwarded ports]' \ + '-G[output configuration and exit]' \ '-I+[specify smartcard device]:device:_files' \ '-K[enable GSSAPI-based authentication and forwarding]' \ '-k[disable forwarding of GSSAPI credentials]' \ @@ -56,11 +58,12 @@ _ssh () { '(-v)*-q[quiet operation]' \ '*-R[specify remote port forwarding]:remote port forwarding:->forward' \ '-S+[specify location of control socket for connection sharing]:path to control socket:_files' \ + '-Q[query parameters]:parameter type:((cipher\:"supported symmetric ciphers" cipher-auth\:"supported symmetric ciphers that support authenticated encryption" mac\:"supported message integrity codes" kex\:"key exchange algorithms" key\:"key types" protocol-version\:"supported SSH protocol versions"))' \ '(-1)-s[invoke subsystem]' \ '(-1 -t)-T[disable pseudo-tty allocation (protocol version 2 only)]' \ '(-T)-t[force pseudo-tty allocation]' \ '-V[show version number]' \ - '(-q)*-v[verbose mode]' \ + '(-q)*-v[verbose mode (multiple increase verbosity, up to 3)]' \ '-W[forward standard input and output to host]:stdinout forward:->hostport' \ '-w[request tunnel device forwarding]:local_tun[\:remote_tun] (integer or "any"):' \ '(-x -Y)-X[enable (untrusted) X11 forwarding]' \ @@ -105,7 +108,7 @@ _ssh () { ;; ssh-keygen) cmds=( -p -i -e -y -c -l -B -D -U ) - _arguments \ + _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)" \ @@ -147,17 +150,36 @@ _ssh () { case "$lstate" in option) if compset -P '*='; then - case "$IPREFIX" in - *(#i)(afstokenpassing|batchmode|challengeresponseauthentication|checkhostip|clearallforwardings|compression|enablesshkeysign|exitonforwardfailure|fallbacktorsh|forward(agent|x11)|forwardx11trusted|gatewayports|gssapiauthentication|gssapidelegatecredentials|gssapitrustdns|hashknownhosts|hostbasedauthentication|identitiesonly|kbdinteractiveauthentication|(tcp|)keepalive|nohostauthenticationforlocalhost|passwordauthentication|permitlocalcommand|pubkeyauthentication|rhosts(|rsa)authentication|rsaauthentication|usersh|kerberos(authentication|tgtpassing)|useprivilegedport|visualhostkey)=*) + case "${IPREFIX#-o}" in + (#i)(ciphers|macs|kexalgorithms|hostkeyalgorithms|pubkeyacceptedkeytypes|hostbasedkeytypes)=) + if ! compset -P +; then + _wanted append expl 'append to default' compadd + && ret=0 + fi + ;; + esac + case "${IPREFIX#-o}" in + (#i)(afstokenpassing|batchmode|canonicalizefallbacklocal|challengeresponseauthentication|checkhostip|clearallforwardings|compression|enablesshkeysign|exitonforwardfailure|fallbacktorsh|forward(agent|x11)|forwardx11trusted|gatewayports|gssapiauthentication|gssapidelegatecredentials|gssapitrustdns|hashknownhosts|hostbasedauthentication|identitiesonly|kbdinteractiveauthentication|(tcp|)keepalive|nohostauthenticationforlocalhost|passwordauthentication|permitlocalcommand|proxyusefdpass|pubkeyauthentication|rhosts(|rsa)authentication|rsaauthentication|streamlocalbindunlink|usersh|kerberos(authentication|tgtpassing)|useprivilegedport|visualhostkey)=*) _wanted values expl 'truth value' compadd yes no && ret=0 ;; - *(#i)addressfamily=*) + (#i)addressfamily=*) _wanted values expl 'address family' compadd any inet inet6 && ret=0 ;; - *(#i)bindaddress=*) + (#i)bindaddress=*) _wanted bind-addresses expl 'bind address' _bind_addresses && ret=0 ;; - *(#i)ciphers=*) + (#i)canonicaldomains=*) + _message -e 'canonical domains (space separated)' && ret=0 + ;; + (#i)canonicalizehostname=*) + _wanted values expl 'truthish value' compadd yes no always && ret=0 + ;; + (#i)canonicalizemaxdots=*) + _message -e 'number of dots' && ret=0 + ;; + (#i)canonicalizepermittedcnames=*) + _message -e 'CNAME rule list (source_domain_list:target_domain_list, each pattern list comma separated)' && ret=0 + ;; + (#i)ciphers=*) _values -s , 'encryption cipher' \ '3des-cbc' \ 'aes128-cbc' \ @@ -178,48 +200,52 @@ _ssh () { 'rijndael-cbc@lysator.liu.se' \ && ret=0 ;; - *(#i)cipher=*) + (#i)cipher=*) _wanted values expl 'encryption cipher (protocol version 1)' \ compadd blowfish 3des des idea arcfour tss none && ret=0 ;; - *(#i)compressionlevel=*) + (#i)compressionlevel=*) _values 'compression level' {1..9} && ret=0 ;; - *(#i)connectionattempts=*) + (#i)connectionattempts=*) _message -e 'connection attempts' && ret=0 ;; - *(#i)connecttimeout=*) + (#i)connecttimeout=*) _message -e 'connection timeout' && ret=0 ;; - *(#i)controlmaster=*) + (#i)controlmaster=*) _wanted values expl 'truthish value' compadd yes no auto autoask && ret=0 ;; - *(#i)controlpath=*) + (#i)controlpath=*) _description files expl 'path to control socket' _files "$expl[@]" && ret=0 ;; - *(#i)controlpersist=*) + (#i)controlpersist=*) _message -e 'timeout' ret=0 _wanted values expl 'truth value' compadd yes no && ret=0 ;; - *(#i)escapechar=*) + (#i)escapechar=*) _message -e 'escape character (or `none'\'')' ret=0 ;; - *(#i)forwardx11timeout=*) + (#i)fingerprinthash=*) + _values 'fingerprint hash algorithm' \ + md5 ripemd160 sha1 sha256 sha384 sha512 && ret=0 + ;; + (#i)forwardx11timeout=*) _message -e 'timeout' ret=0 ;; - *(#i)globalknownhostsfile=*) + (#i)globalknownhostsfile=*) _description files expl 'global file with known hosts' _files "$expl[@]" && ret=0 ;; - *(#i)hostname=*) + (#i)hostname=*) _wanted hosts expl 'real host name to log into' _ssh_hosts && ret=0 ;; - *(#i)hostkeyalgorithms=*) - _values -s , 'host key algorithms' \ + (#i)(hostbasedkeytypes|hostkeyalgorithms|pubkeyacceptedkeytypes)=*) + _values -s , 'key types' \ 'ecdsa-sha2-nistp256-cert-v01@openssh.com' \ 'ecdsa-sha2-nistp384-cert-v01@openssh.com' \ 'ecdsa-sha2-nistp521-cert-v01@openssh.com' \ @@ -235,11 +261,14 @@ _ssh () { 'ssh-rsa' \ 'ssh-dss' && ret=0 ;; - *(#i)identityfile=*) + (#i)identityfile=*) _description files expl 'SSH identity file' _files "$expl[@]" && ret=0 ;; - *(#i)ipqos=*) + (#i)ignoreunknown=*) + _message -e 'pattern list' && ret=0 + ;; + (#i)ipqos=*) local descr if [[ $PREFIX = *\ *\ * ]]; then return 1; fi if compset -P '* '; then @@ -252,105 +281,115 @@ _ssh () { 'cs0' 'cs1' 'cs2' 'cs3' 'cs4' 'cs5' 'cs6' 'cs7' 'ef' \ 'lowdelay' 'throughput' 'reliability' && ret=0 ;; - *(#i)(local|remote)forward=*) + (#i)(local|remote)forward=*) state=forward ;; - *(#i)dynamicforward=*) + (#i)dynamicforward=*) state=dynforward ;; - *(#i)kbdinteractivedevices=*) + (#i)kbdinteractivedevices=*) _values -s , 'keyboard-interactive authentication methods' \ 'bsdauth' 'pam' 'skey' && ret=0 ;; - *(#i)kexalgorithms=*) + (#i)kexalgorithms=*) _values -s , 'KEX algorithms' \ ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521 \ diffie-hellman-group-exchange-sha256 \ diffie-hellman-group-exchange-sha1 \ diffie-hellman-group14-sha1 diffie-hellman-group1-sha1 && ret=0 ;; - *(#i)localcommand=*) + (#i)localcommand=*) _description commands expl 'run command locally after connecting' _command_names && ret=0 ;; - *(#i)loglevel=*) + (#i)loglevel=*) _values 'log level' QUIET FATAL ERROR INFO VERBOSE\ DEBUG DEBUG1 DEBUG2 DEBUG3 && ret=0 ;; - *(#i)macs=*) + (#i)macs=*) state=macs ;; - *(#i)numberofpasswordprompts=*) + (#i)numberofpasswordprompts=*) _message -e 'number of password prompts' ret=0 ;; - *(#i)pkcs11provider=*) + (#i)pkcs11provider=*) _description files expl 'PKCS#11 shared library' _files -g '*.so' "$expl[@]" && ret=0 ;; - *(#i)port=*) + (#i)port=*) _message -e 'port number on remote host' ret=0 ;; - *(#i)preferredauthentications=*) + (#i)preferredauthentications=*) _values -s , 'authentication method' gssapi-with-mic \ hostbased publickey keyboard-interactive password && ret=0 ;; - *(#i)protocol=*) + (#i)protocol=*) _values -s , 'protocol version' \ '1' \ '2' && ret=0 ;; - *(#i)proxycommand=*) + (#i)proxycommand=*) compset -q shift 1 words (( CURRENT-- )) _normal && ret=0 ;; - *(#i)rekeylimit=*) + (#i)rekeylimit=*) _message -e 'maximum number of bytes transmitted before renegotiating session key' ret=0 ;; - *(#i)requesttty=*) + (#i)requesttty=*) _values 'request a pseudo-tty' \ 'no[never request a TTY]' \ 'yes[always request a TTY when stdin is a TTY]' \ 'force[always request a TTY]' \ 'auto[request a TTY when opening a login session]' && ret=0 ;; - *(#i)sendenv=*) + (#i)revokedhostkeys=*) + _description files expl 'revoked host keys file' + _files "$expl[@]" && ret=0 + ;; + (#i)sendenv=*) _wanted envs expl 'environment variable' _parameters -g 'scalar*export*' && ret=0 ;; - *(#i)serveralivecountmax=*) + (#i)serveralivecountmax=*) _message -e 'number of alive messages without replies before disconnecting' ret=0 ;; - *(#i)serveraliveinterval=*) + (#i)serveraliveinterval=*) _message -e 'timeout in seconds since last data was received to send alive message' ret=0 ;; - *(#i)(stricthostkeychecking|verifyhostkeydns)=*) - _wanted values expl 'checking type' compadd yes no ask && ret=0 + (#i)streamlocalbindmask=*) + _message -e 'octal mask' && ret=0 + ;; + (#i)(stricthostkeychecking|verifyhostkeydns|updatehostkeys)=*) + _wanted values expl 'truthish value' compadd yes no ask && ret=0 + ;; + (#i)transport=*) + _values 'transport protocol' TCP SCTP && ret=0 ;; - *(#i)tunnel=*) + (#i)tunnel=*) _values 'request device forwarding' \ 'yes' \ 'point-to-point' \ 'ethernet' \ 'no' && ret=0 ;; - *(#i)tunneldevice=*) + (#i)tunneldevice=*) _message -e 'local_tun[:remote_tun] (integer or "any")' ret=0 ;; - *(#i)userknownhostsfile=*) + (#i)userknownhostsfile=*) _description files expl 'user file with known hosts' _files "$expl[@]" && ret=0 ;; - *(#i)user=*) + (#i)user=*) _wanted users expl 'user to log in as' _ssh_users && ret=0 ;; - *(#i)xauthlocation=*) + (#i)xauthlocation=*) _description files expl 'xauth program' _files "$expl[@]" -g '*(-*)' && ret=0 ;; @@ -358,10 +397,15 @@ _ssh () { else # old options are after the empty "\"-line _wanted values expl 'configure file option' \ - compadd -M 'm:{a-z}={A-Z}' -S '=' - \ + compadd -M 'm:{a-z}={A-Z}' -q -S '=' - \ AddressFamily \ BatchMode \ BindAddress \ + CanonicalDomains \ + CanonicalizeFallbackLocal \ + CanonicalizeHostname \ + CanonicalizeMaxDots \ + CanonicalizePermittedCNAMEs \ ChallengeResponseAuthentication \ CheckHostIP \ Cipher \ @@ -378,6 +422,7 @@ _ssh () { EnableSSHKeysign \ EscapeChar \ ExitOnForwardFailure \ + FingerprintHash \ ForwardAgent \ ForwardX11 \ ForwardX11Timeout \ @@ -390,11 +435,13 @@ _ssh () { HashKnownHosts \ Host \ HostbasedAuthentication \ + HostbasedKeyTypes \ HostKeyAlgorithms \ HostKeyAlias \ HostName \ IdentitiesOnly \ IdentityFile \ + IgnoreUnknown \ IPQoS \ KbdInteractiveAuthentication \ KbdInteractiveDevices \ @@ -412,19 +459,26 @@ _ssh () { PreferredAuthentications \ Protocol \ ProxyCommand \ + ProxyUseFdpass \ + PubkeyAcceptedKeyTypes \ PubkeyAuthentication \ RekeyLimit \ RemoteForward \ RequestTTY \ + RevokedHostKeys \ RhostsRSAAuthentication \ RSAAuthentication \ SendEnv \ ServerAliveCountMax \ ServerAliveInterval \ + StreamLocalBindMask \ + StreamLocalBindUnlink \ StrictHostKeyChecking \ TCPKeepAlive \ + Transport \ Tunnel \ TunnelDevice \ + UpdateHostKeys \ UsePrivilegedPort \ User \ UserKnownHostsFile \ @@ -573,8 +627,8 @@ _ssh_hosts () { config="$HOME/.ssh/config" fi if [[ -r $config ]]; then - local IFS=$'\t ' key hosts host - while read key hosts; do + 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 diff --git a/Completion/Unix/Command/_stty b/Completion/Unix/Command/_stty index f40cd856e..b5545ebcd 100644 --- a/Completion/Unix/Command/_stty +++ b/Completion/Unix/Command/_stty @@ -14,5 +14,5 @@ else parmrk inpck istrip inlcr igncr icrnl iuclc ixon ixany ixoff \ imaxbel isig icanon xcase echo echoe echok echonl noflsh \ tostop echoctl echoprt echoke flusho pending iexten opost \ - olcuc onlcr ocrnl onocr onlret ofill ofdel + olcuc onlcr ocrnl onocr onlret ofill ofdel raw sane fi diff --git a/Completion/Unix/Command/_subversion b/Completion/Unix/Command/_subversion index 188a81367..1cffc3c04 100644 --- a/Completion/Unix/Command/_subversion +++ b/Completion/Unix/Command/_subversion @@ -50,9 +50,27 @@ _svn () { args=( ${=${${${(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; print -rn $i\\\\:$j $j\\\\:$j "" `"'))' ) + done while (( idx=$args[(I)*--c(l|hangelist):arg:] )); do 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' ) + done + while (( idx=$args[(I)*--depth:arg:] )); do + 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' ) + 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)' ) + 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]"' ) + done _store_cache svn-${cmd}-args args fi @@ -98,7 +116,7 @@ _svn () { (mergeinfo) args[(r)--show-revs:arg:]=( '--show-revs=:revisions:(merged eligible)' ) ;; - (propget|propedit) + (propget|propedit|propdel) args+=( '1:property name:_svn_props' '2:target: _alternative "files:file:_files" "urls:URL:_svn_urls"' diff --git a/Completion/Unix/Command/_tmux b/Completion/Unix/Command/_tmux index f0cc4be37..49c2b63ed 100644 --- a/Completion/Unix/Command/_tmux +++ b/Completion/Unix/Command/_tmux @@ -40,6 +40,14 @@ # # The configuration for subcommand completions may be done in # this context: ':completion:*:*:tmux-<sub-command>:*:*' +# +# TODO: +# +# - Implement __tmux-format +# - Implement __tmux-style (possibly using existing helpers like +# __tmux-attributes and __tmux-colours) +# - in _tmux-list-panes, use __tmux-windows or __tmux-sessions +# depending on -s is among the sub-commands current command line. # Global variables; setup the first time _tmux is called. # For $_tmux_commands[] generation, see the very end of this file. @@ -51,6 +59,8 @@ _tmux_aliasmap=( attach attach-session detach detach-client has has-session + lockc lock-client + locks lock-session lsc list-clients lscm list-commands ls list-sessions @@ -67,23 +77,26 @@ _tmux_aliasmap=( breakp break-pane capturep capture-pane displayp display-panes - downp down-pane findw find-window joinp join-pane killp kill-pane killw kill-window last last-window + lastp last-pane linkw link-window lsp list-panes lsw list-windows + movep move-pane movew move-window neww new-window nextl next-layout next next-window pipep pipe-pane prev previous-window + prevl previous-layout renamew rename-window resizep resize-pane + respawnp respawn-pane respawnw respawn-window rotatew rotate-window selectl select-layout @@ -93,7 +106,6 @@ _tmux_aliasmap=( swapp swap-pane swapw swap-window unlinkw unlink-window - upp up-pane # key bindings bind bind-key @@ -117,7 +129,6 @@ _tmux_aliasmap=( # buffers clearhist clear-history - copyb copy-buffer deleteb delete-buffer lsb list-buffers loadb load-buffer @@ -131,6 +142,7 @@ _tmux_aliasmap=( lock lock-server run run-shell info server-info + wait wait-for ) # --- Sub-command functions --- @@ -154,6 +166,7 @@ function _tmux-attach-session() { local -a args 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' @@ -189,6 +202,8 @@ function _tmux-break-pane() { 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} @@ -198,25 +213,80 @@ 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 - __tmux-choose-stuff + 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 - __tmux-choose-stuff + 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 - __tmux-choose-stuff + 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() { @@ -238,6 +308,7 @@ function _tmux-command-prompt() { 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' @@ -246,6 +317,9 @@ function _tmux-command-prompt() { 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" } @@ -255,46 +329,16 @@ function _tmux-confirm-before() { 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 - __tmux-lastarg ${state} 'command_and_args' 1 "command string" -} - -function _tmux-copy-buffer() { - [[ -n ${tmux_describe} ]] && print "Copy session paste buffers" && return - local state session - local -a args - local -ax bopts - - args=( - '-a[choose a source buffer index]:buffer:->srcbuf' - '-b[choose a destination buffer index]:buffer:->dstbuf' - '-s[choose a source session]:session:->srcsession' - '-t[choose a destination session]:session:->dstsession' - ) - _arguments ${args} - - case ${state} in - ((src|dst)session) - __tmux-sessions - return - ;; - (srcbuf) - session="$(__tmux-get-optarg -s "${words[@]}")" - ;; - (srcbuf) - session="$(__tmux-get-optarg -t "${words[@]}")" - ;; - esac - if [[ -n ${session} ]]; then - bopts=( -t ${session} ) - __tmux-buffers + if [[ ${state} == 'prompt' ]]; then + _message 'prompt string' return fi - bopts=() - __tmux-buffers + __tmux-lastarg ${state} 'command_and_args' 1 "command string" } function _tmux-copy-mode() { @@ -309,39 +353,20 @@ function _tmux-copy-mode() { function _tmux-delete-buffer() { [[ -n ${tmux_describe} ]] && print "Delete a paste buffer" && return - local state session local -a args - local -ax bopts - - args=( - '-b[choose a target buffer index]:panes:->buffer' - '-t[choose a target session]:panes:->session' - ) - _arguments ${args} - - case ${state} in - (session) - __tmux-sessions - return - ;; - (buffer) - session="$(__tmux-get-optarg -t "${words[@]}")" - ;; - (*) return ;; - esac - if [[ -n ${session} ]]; then - bopts=( -t ${session} ) - __tmux-buffers - return - fi - bopts=() - __tmux-buffers + 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=('-t[choose a target client]:clients:__tmux-clients') + 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} } @@ -349,6 +374,7 @@ 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' @@ -364,18 +390,15 @@ function _tmux-display-panes() { _arguments ${args} } -function _tmux-down-pane() { - [[ -n ${tmux_describe} ]] && print "Move down a pane" && return - local -a args - args=('-t[choose a target pane]:panes:__tmux-panes') - _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' ) @@ -394,6 +417,8 @@ 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:' ) @@ -404,6 +429,7 @@ 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]' @@ -419,7 +445,7 @@ function _tmux-kill-pane() { [[ -n ${tmux_describe} ]] && print "Destroy a given pane" && return local -a args args=( - '-a[kill all panes, except current]' + '-a[kill all panes except the one specified by -t]' '-t[choose a target pane]:panes:__tmux-panes' ) _arguments ${args} @@ -433,17 +459,34 @@ function _tmux-kill-server() { function _tmux-kill-session() { [[ -n ${tmux_describe} ]] && print "Destroy a given session" && return local -a args - args=('-t[choose a target session]:sessions:__tmux-sessions') + 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=('-t[choose a target window]:windows:__tmux-windows') + 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 @@ -466,13 +509,18 @@ function _tmux-link-window() { function _tmux-list-buffers() { [[ -n ${tmux_describe} ]] && print "List paste buffers of a session" && return local -a args - args=('-t[choose a session]:sessions:__tmux-sessions') + 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 - __tmux-nothing-else + 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() { @@ -490,52 +538,43 @@ function _tmux-list-keys() { function _tmux-list-panes() { [[ -n ${tmux_describe} ]] && print "List panes of a window" && return local -a args - args=('-t[choose a window]:windows:__tmux-windows') + 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 - __tmux-nothing-else + 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=('-t[choose a session]:sessions:__tmux-sessions') + 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 state session local -a args - local -ax bopts args=( - '-b[choose a target buffer index]:panes:->buffer' - '-t[choose a target session]:panes:->session' + '-b[choose a target buffer index]:panes:__tmux-buffers' '1:file name:_files -g "*(-.)"' ) - _arguments ${args} - - case ${state} in - (session) - __tmux-sessions - return - ;; - (buffer) - session="$(__tmux-get-optarg -t "${words[@]}")" - ;; - (*) return ;; - esac - if [[ -n ${session} ]]; then - bopts=( -t ${session} ) - __tmux-buffers - return - fi - bopts=() - __tmux-buffers + _arguments ${args} && return } function _tmux-lock-client() { @@ -557,12 +596,30 @@ function _tmux-lock-session() { _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} @@ -572,11 +629,17 @@ function _tmux-new-session() { [[ -n ${tmux_describe} ]] && print "Create a new session" && return local -a args args=( - '-d[do not attach new session to current terminal]' '-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"' '*:: :_command' ) _arguments -s ${args} @@ -586,9 +649,13 @@ 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' '*:: :_command' ) @@ -606,7 +673,7 @@ 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 activity]' + '-a[move to the next window with an alert]' '-t[choose target session]:session:__tmux-sessions' ) _arguments ${args} @@ -616,9 +683,11 @@ 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]' - '-b[choose buffer]:source buffer:__tmux-buffers' + '-s[specify separator]:separator:' '-t[choose target window]:window:__tmux-windows' ) _arguments ${args} @@ -647,7 +716,7 @@ 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 activity]' + '-a[move to the previous window with an alert]' '-t[choose target session]:session:__tmux-sessions' ) _arguments ${args} @@ -656,7 +725,10 @@ function _tmux-previous-window() { function _tmux-refresh-client() { [[ -n ${tmux_describe} ]] && print "Refresh a client" && return local -a args - args=('-t[choose target client]:client:__tmux-clients') + args=( + '-S[Only update the client'\''s status bar]' + '-t[choose target client]:client:__tmux-clients' + ) _arguments ${args} } @@ -690,11 +762,25 @@ function _tmux-resize-pane() { '-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} } +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:_command' + ) + _arguments ${args} +} + function _tmux-respawn-window() { [[ -n ${tmux_describe} ]] && print "Reuse a window in which a command has exited" && return local -a args @@ -719,43 +805,31 @@ function _tmux-rotate-window() { function _tmux-run-shell() { [[ -n ${tmux_describe} ]] && print "Execute a command without creating a new window" && return - _command + local -a args + args=( + '-b[run shell command in background]' + '-t[choose target pane]:pane:__tmux-panes' + '*::command:_command' + ) + _arguments ${args} } function _tmux-save-buffer() { [[ -n ${tmux_describe} ]] && print "Save a paste buffer to a file" && return - local state session local -a args - local -ax bopts args=( - '-b[choose a target buffer index]:buffer:->buffer' - '-t[choose a target session]:buffer:->session' + '-a[append to rather than overwriting file]' + '-b[choose a target buffer index]:buffer:__tmux-buffers' ) - _arguments ${args} - - case ${state} in - (session) - __tmux-sessions - return - ;; - (buffer) - session="$(__tmux-get-optarg -t "${words[@]}")" - ;; - (*) return ;; - esac - if [[ -n ${session} ]]; then - bopts=( -t ${session} ) - __tmux-buffers - return - fi - bopts=() - __tmux-buffers + _arguments ${args} && return } 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' ) @@ -765,21 +839,29 @@ function _tmux-select-layout() { function _tmux-select-pane() { [[ -n ${tmux_describe} ]] && print "Make a pane the active one in the window" && return local -a args - args=('-t[choose a target pane]:panes:__tmux-panes') - _arguments ${args} && return -} - -function _tmux-select-prompt() { - [[ -n ${tmux_describe} ]] && print "Open a prompt to enter a window index" && return - local -a args - args=('-t[choose a target client]:clients:__tmux-clients') + 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=('-t[choose a target window]:windows:__tmux-windows') + 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 } @@ -788,6 +870,8 @@ function _tmux-send-keys() { 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' ) @@ -798,7 +882,10 @@ function _tmux-send-keys() { function _tmux-send-prefix() { [[ -n ${tmux_describe} ]] && print "Send the prefix key to a window" && return local -a args - args=('-t[choose a target pane]:panes:__tmux-panes') + args=( + '-2[send secondary prefix key]' + '-t[choose a target pane]:panes:__tmux-panes' + ) _arguments ${args} } @@ -809,33 +896,16 @@ function _tmux-server-info() { function _tmux-set-buffer() { [[ -n ${tmux_describe} ]] && print "Set contents of a paster buffer" && return - local state session + local state local -a args - local -ax bopts - args=( - '-b[choose a target buffer index]:panes:->buffer' - '-t[choose a target session]:panes:->session' + '-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} - - case ${state} in - (session) - __tmux-sessions - return - ;; - (buffer) - session="$(__tmux-get-optarg -t "${words[@]}")" - ;; - (*) return ;; - esac - if [[ -n ${session} ]]; then - bopts=( -t ${session} ) - __tmux-buffers - return - fi - bopts=() - __tmux-buffers + _arguments ${args} && return + __tmux-lastarg ${state} 'data' 1 "data" } function _tmux-set-environment() { @@ -904,33 +974,9 @@ function _tmux-set-window-option() { function _tmux-show-buffer() { [[ -n ${tmux_describe} ]] && print "Display the contents of a paste buffer" && return - local state session local -a args - local -ax bopts - - args=( - '-b[choose a target buffer index]:panes:->buffer' - '-t[choose a target session]:panes:->session' - ) - _arguments ${args} - - case ${state} in - (session) - __tmux-sessions - return - ;; - (buffer) - session="$(__tmux-get-optarg -t "${words[@]}")" - ;; - (*) return ;; - esac - if [[ -n ${session} ]]; then - bopts=( -t ${session} ) - __tmux-buffers - return - fi - bopts=() - __tmux-buffers + args=('-b[choose a target buffer index]:panes:->buffer') + _arguments ${args} && return } function _tmux-show-environment() { @@ -945,7 +991,13 @@ function _tmux-show-environment() { function _tmux-show-messages() { [[ -n ${tmux_describe} ]] && print "Show client"\'"s message log" && return - args=('-t[choose target client]:client:__tmux-clients') + 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} } @@ -978,12 +1030,14 @@ 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_pane is correct here. The behaviour was changed + # 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. @@ -1034,6 +1088,10 @@ function _tmux-switch-client() { 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} @@ -1046,6 +1104,7 @@ function _tmux-unbind-key() { 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' @@ -1071,11 +1130,18 @@ function _tmux-unlink-window() { _arguments ${args} } -function _tmux-up-pane() { - [[ -n ${tmux_describe} ]] && print "Move up a pane" && return +function _tmux-wait-for() { + [[ -n ${tmux_describe} ]] && print "Wait for an event or trigger it" && return + local state local -a args - args=('-t[choose a target pane]:panes:__tmux-panes') - _arguments ${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" } # --- Utility functions --- @@ -1110,18 +1176,6 @@ function __tmux-bound-keys() { _describe -t keys 'keys' keys } -function __tmux-choose-stuff() { - # choose-{client,session,window} accept exactly the same arguments, so... - local curcontext="${curcontext}" state - local -a args - args=( - '-t[choose a target pane]:panes:__tmux-panes' - '*:: :->tmpl' - ) - _arguments ${args} && return - __tmux-lastarg ${state} 'tmpl' 1 "tmux command template" -} - function __tmux-clients() { local expl local -a clients @@ -1129,6 +1183,10 @@ function __tmux-clients() { _describe -t clients 'clients' clients } +function __tmux-format() { + _message 'not implemented yet' +} + function __tmux-colours() { local -a colnames colnames=( default black red green yellow blue magenta cyan white colourN:"replace N by a number between 0 and 255" ) @@ -1197,13 +1255,14 @@ function __tmux-option-guard() { int_guard='_guard "[0-9]#" "'${opt}': numeric value"' if [[ ${mode} == 'session' ]]; then options=( + 'assume-paste-time:'${int_guard} 'base-index:'${int_guard} - 'bell-action:DESC:any none current' - 'buffer-limit:'${int_guard} + 'bell-action:DESC:any none current other' + 'bell-on-alert:DESC:on off' 'default-command:MSG:command string' - 'default-path:MSG:path name' 'default-shell:MSG:shell executable' - 'default-terminal:MSG:terminal string' + 'destroy-unattached:DESC:on off' + 'detach-on-destroy:DESC:on off' 'display-panes-colour:__tmux-colours' 'display-panes-active-colour:__tmux-colours' 'display-panes-time:'${int_guard} @@ -1212,78 +1271,85 @@ function __tmux-option-guard() { 'lock-after-time:'${int_guard} 'lock-command:MSG:command string' 'lock-server:DESC:on off' - 'message-attr:__tmux-attributes' - 'message-bg:__tmux-colours' - 'message-fg:__tmux-colours' - 'message-limit:'${int_guard} - 'mouse-select-pane:DESC:on off' - 'pane-border-bg:__tmux-colours' - 'pane-border-fg:__tmux-colours' - 'pane-active-border-bg:__tmux-colours' - 'pane-active-border-fg:__tmux-colours' - 'prefix:MSG:comma-seperated key list' + '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' - 'status-attr:__tmux-attributes' - 'status-bg:__tmux-colours' - 'status-fg:__tmux-colours' 'status-interval:'${int_guard} 'status-justify:DESC:left centre right' 'status-keys:DESC:vi emacs' 'status-left:MSG:format string' - 'status-left-attr:__tmux-attributes' - 'status-left-bg:__tmux-colours' - 'status-left-fg:__tmux-colours' 'status-left-length:'${int_guard} + 'status-left-style:__tmux-style' + 'status-position:DESC:top bottom' 'status-right:MSG:format string' - 'status-right-attr:__tmux-attributes' - 'status-right-bg:__tmux-colours' - 'status-right-fg:__tmux-colours' 'status-right-length:'${int_guard} + 'status-right-style:__tmux-style' + 'status-style:__tmux-style' 'status-utf8:DESC:on off' - 'terminal-overrides:MSG:overrides string' 'update-environment:MSG:string listing env. variables' 'visual-activity:DESC:on off' 'visual-bell:DESC:on off' - 'visual-content:DESC:on off' + 'visual-silence:DESC:on off' + 'word-separators:MSG:separator string' ) elif [[ ${mode} == 'server' ]]; then options=( + 'buffer-limit:'${int_guard} + 'default-terminal:MSG:terminal string' 'escape-time:'${int_guard} + 'exit-unattached:DESC:on off' + 'focus-events:DESC:on off' + 'history-file:_path-files -g "*(-.)"' + 'message-limit:'${int_guard} 'quiet:DESC:on off' + 'set-clipboard:DESC:on off' + 'terminal-overrides:MSG:overrides string' ) else options=( 'aggressive-resize:DESC:on off' + 'allow-rename:DESC:on off' 'alternate-screen:DESC:on off' 'automatic-rename:DESC:on off' + 'automatic-rename-format:DESC:__tmux-format' 'clock-mode-colour:__tmux-colours' 'clock-mode-style:DESC:12 24' 'force-height:'${int_guard} 'force-width:'${int_guard} 'main-pane-height:'${int_guard} 'main-pane-width:'${int_guard} - 'mode-attr:__tmux-attributes' - 'mode-bg:__tmux-colours' - 'mode-fg:__tmux-colours' 'mode-keys:DESC:vi emacs' - 'mode-mouse:DESC:on off' + 'mode-style:__tmux-style' 'monitor-activity:DESC:on off' - 'monitor-content:MSG:fnmatch(3) pattern' + 'monitor-silence:DESC:on off' + 'other-pane-height:'${int_guard} + 'other-pane-width:'${int_guard} + 'pane-active-border-style:__tmux-style' + 'pane-base-index:'${int_guard} + 'pane-border-style:__tmux-style' 'remain-on-exit:DESC:on off' 'synchronize-panes:DESC:on off' 'utf8:DESC:on off' - 'window-status-attr:__tmux-attributes' - 'window-status-bg:__tmux-colours' - 'window-status-current-attr:__tmux-attributes' - 'window-status-current-bg:__tmux-colours' - 'window-status-current-fg:__tmux-colours' + 'window-active-style:__tmux-style' + 'window-status-activity-style:__tmux-style' + 'window-status-bell-style:__tmux-style' 'window-status-current-format:MSG:status format string' - 'window-status-fg:__tmux-colours' + 'window-status-current-style:__tmux-style' 'window-status-format:MSG:status format string' + 'window-status-last-style:__tmux-style' + 'window-status-separator:MSG:separator string' + 'window-status-style:__tmux-style' + 'window-style:__tmux-style' + 'wrap-seach:DESC:on off' 'xterm-keys:DESC:on off' ) fi @@ -1310,16 +1376,17 @@ function __tmux-option-guard() { esac } -function __tmux-options() { - local -a tmux_options - tmux_options=( +function __tmux-session-options() { + local -a tmux_session_options + tmux_session_options=( + 'assume-paste-time:assume keys are pasted instead of typed if this fast' 'base-index:define where to start numbering' 'bell-action:set action on window bell' - 'buffer-limit:number of buffers kept per session' + 'bell-on-alert:ring the terminal bell when an alert occurs' 'default-command:default command for new windows' - 'default-path:default working directory' 'default-shell:default shell executable' - 'default-terminal:default terminal definition string' + 'destroy-unattached:destroy session if no client is attached' + 'detach-on-destroy:detach client if attached session is destroyed' 'display-panes-colour:colour used for display-panes' 'display-panes-active-colour:colour for active pane in display-panes' 'display-panes-time:time (in msecs) of display-panes output' @@ -1328,45 +1395,37 @@ function __tmux-options() { 'lock-after-time:lock sessions after N seconds' 'lock-command:command to run for locking a client' 'lock-server:make lock-after-time lock the server instead of sessions' - 'message-attr:set status line message attributes' - 'message-bg:set status line message background colour' - 'message-fg:set status line message foreground colour' - 'message-limit:set size of message log per client' - 'mouse-select-pane:make mouse clicks select window panes' - 'pane-border-bg:set pane border foreground colour' - 'pane-border-fg:set pane border background colour' - 'pane-active-border-bg:set active pane border foreground colour' - 'pane-active-border-fg:set active pane border background colour' - 'prefix:comma seperated line of keys accepted as prefix key' + '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' - 'status-attr:status bar attributes' - 'status-bg:status bar background colour' - 'status-fg:status bar foreground colour' 'status-interval:interval (in seconds) for status bar updates' 'status-justify:position of the window list in status bar' 'status-keys:mode to use in status bar modes (vi/emacs)' 'status-left:format to use left in status bar' - 'status-left-attr:attribute for the left part of the status bar' - 'status-left-bg:background colour of the left part of the status bar' - 'status-left-fg:foreground colour of the left part of the status bar' 'status-left-length:maximum length of the left part of the status bar' + 'status-left-style:style of left part of status line' + 'status-position:status line position' 'status-right:format to use right in status bar' - 'status-right-attr:attribute for the right part of the status bar' - 'status-right-bg:background colour of the right part of the status bar' - 'status-right-fg:foreground colour of the right part of the status bar' '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' - 'terminal-overrides:override terminal descriptions' '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-content:display status line messages upon content changes' + 'visual-silence:print a message if monitor-silence is on' + 'word-separators:string of characters considered word separators' ) - _describe -t tmux-options 'tmux option' tmux_options + _describe -t tmux-options 'tmux session option' tmux_session_options } function __tmux-options-complete() { @@ -1375,7 +1434,7 @@ function __tmux-options-complete() { case ${state} in name_or_value) if (( CURRENT == 1 )) && [[ ${mode} == 'session' ]]; then - __tmux-options + __tmux-session-options elif (( CURRENT == 1 )) && [[ ${mode} == 'server' ]]; then __tmux-server-options elif (( CURRENT == 1 )) && [[ ${mode} == 'window' ]]; then @@ -1413,8 +1472,16 @@ function __tmux-panes() { function __tmux-server-options() { local -a tmux_server_options tmux_server_options=( + 'buffer-limit:number of buffers kept per session' + 'default-terminal:default terminal definition string' 'escape-time:set timeout to detect single escape characters (in msecs)' + 'exit-unattached:make server exit if it has no attached clients' + 'focus-events:request focus events from terminal' + 'history-file:tmux command history file name' + 'message-limit:set size of message log per client' 'quiet:enable/disable the display of various informational messages' + 'set-clipboard:use esc sequences to set terminal clipboard' + 'terminal-overrides:override terminal descriptions' ) _describe -t tmux-server-options 'tmux server option' tmux_server_options } @@ -1435,36 +1502,47 @@ function __tmux-socket-name() { _wanted socket expl 'socket name' compadd ${expl} -- ${socks} } +function __tmux-style() { + _message 'not implemented yet' +} + function __tmux-window-options() { local -a tmux_window_options tmux_window_options=( 'aggressive-resize:aggressively resize windows' + 'allow-rename:allow programs to change window titles' 'alternate-screen:allow alternate screen feature to be used' 'automatic-rename:attempt to automatically rename windows' + 'automatic-rename-format:format for automatic renames' 'clock-mode-colour:set clock colour' 'clock-mode-style:set clock hour format (12/24)' 'force-height:force a windows to a certain height' 'force-width:force a windows to a certain width' 'main-pane-height:set height for main-* layouts' 'main-pane-width:set width for main-* layouts' - 'mode-attr:set window modes attributes' - 'mode-bg:set window modes background colour' - 'mode-fg:set window modes foreground colour' 'mode-keys:mode to use in copy and choice modes (vi/emacs)' - 'mode-mouse:use mouse in modes' + 'mode-style:set window modes style' 'monitor-activity:monitor window activity' - 'monitor-content:monitor window contents for a fnmatch(3) pattern' + 'monitor-silence:monitor window for inactivity' + 'other-pane-height:height of other panes' + '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-style:style of border pane' 'remain-on-exit:do not 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-status-attr:set status line attributes for a window' - 'window-status-bg:set status line background for a window' - 'window-status-current-attr:set status line attributes for active window' - 'window-status-current-bg:set status line background for active window' - 'window-status-current-fg:set status line foreground for active 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' 'window-status-current-format:set status line format for active window' - 'window-status-fg:set status line foreground for a window' + 'window-status-current-style:style of current window in status bar' 'window-status-format:set status line format for all but the active window' + 'window-status-last-style:style of last window in status bar' + 'window-status-separator:separator drawn between windows in status line' + 'window-status-style:general status bar style' + 'window-style:style of window' + 'wrap-search:search wrap around at the end of a pane' 'xterm-keys:generate xterm-style function key sequences' ) _describe -t tmux-window-options 'tmux window option' tmux_window_options @@ -1492,20 +1570,21 @@ function _tmux() { local curcontext="${curcontext}" local mode state ret=1 local -a args - local -x tmuxcommand - unset tmux_describe + 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' - '-q[do not send informational messages]' '-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 @@ -1551,7 +1630,7 @@ function _tmux() { # description generation follows; only done on 1st _tmux call. local f desc local -A rev -local -x tmux_describe +local tmux_describe tmux_describe='yes, please' for f in ${(k)_tmux_aliasmap} ; do rev+=( ${_tmux_aliasmap[$f]} $f ) @@ -1561,6 +1640,5 @@ for f in ${(M)${(k)functions}:#_tmux-*} ; do _tmux_commands+=( "${f#_tmux-}${desc:+:$desc}" ) [[ -n ${rev[${f#_tmux-}]} ]] && _tmux_aliases+=( "${rev[${f#_tmux-}]}${desc:+:$desc}" ) done -unset desc f rev tmux_describe -_tmux +_tmux "$@" diff --git a/Completion/Unix/Command/_vim b/Completion/Unix/Command/_vim index 007671be8..dbc946cb1 100644 --- a/Completion/Unix/Command/_vim +++ b/Completion/Unix/Command/_vim @@ -1,4 +1,4 @@ -#compdef vim exim gvim gex gview nvim rvim rview rgvim rgview evim eview vimdiff gvimdiff +#compdef vim gvim gex gview nvim rvim rview rgvim rgview evim eview vimdiff gvimdiff (( $+functions[_vim_files] )) || _vim_files () { diff --git a/Completion/Unix/Command/_vmstat b/Completion/Unix/Command/_vmstat new file mode 100644 index 000000000..02fa6be64 --- /dev/null +++ b/Completion/Unix/Command/_vmstat @@ -0,0 +1,83 @@ +#compdef vmstat + +local -a specs +case $OSTYPE in + *linux*) + specs=( + '(-a --active)'{-a,--active}'[active/inactive memory]' + '(-f --forks)'{-f,--forks}'[number of forks since boot]' + '(-m --slabs)'{-m,--slabs}'[slabinfo]' + '(-n --one-header)'{-n,--one-header}'[do not redisplay header]' + '(-s --stats)'{-s,--stats}'[event counter statistics]' + '(-d --disk)'{-d,--disk}'[disk statistics]' + '(-D --disk-sum)'{-D,--disk-sum}'[summarize disk statistics]' + '(-p --partition)'{-p,--partition}'[partition specific statistics]:partition:_files' + '(-S --unit)'{-S+,--unit}'[define display unit]:unit prefix:(( k\:1000 K\:1024 m\:1000000 M\:1048576 ))' + '(-w --wide)'{-w,--wide}'[wide output]' + '(-t --timestamp)'{-t,--timestamp}'[show timestamp]' + '1:delay' '2:count' + ) + ;; + freebsd*) + 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' + '-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' + '-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' + '-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 + +if (( $#specs )); then + local curcontext=$curcontext state state_descr line ret=1 + typeset -A {opt,val}_args + + _arguments -C -s -w -A '-*' : "$specs[@]" && ret=0 + + if [[ $state == devices ]]; then + local -a types + types=( + 'da[direct access devices]' 'sa[sequential access devices]' + 'printer[printers]' 'proc[processor devices]' + 'worm[write once read multiple devices]' 'cd[CD devices]' + 'scanner[scanner devices]' 'optical[optical memory devices]' + 'changer[medium changer devices]' 'comm[communication devices]' + 'array[storage array devices]' 'enclosure[enclosure services devices]' + 'floppy[floppy devices]' 'IDE[Integrated Drive Electronics devices]' + 'SCSI[Small Computer System Interface devices]' + 'other[any other device interface]' 'pass[passthrough devices]' + ) + _values -C -s , 'device type' "$types[@]" && ret=0 + fi + return ret +fi + +_default diff --git a/Completion/Unix/Command/_watch b/Completion/Unix/Command/_watch new file mode 100644 index 000000000..a8d29403f --- /dev/null +++ b/Completion/Unix/Command/_watch @@ -0,0 +1,9 @@ +#compdef watch + +# watch(1) has completely different semantics on freebsd compared to linux, hence: +case $OSTYPE in + (freebsd*|dragonfly*) _watch-snoop "$@";; + (*) _default;; +esac + +# NOTREACHED diff --git a/Completion/Unix/Command/_wget b/Completion/Unix/Command/_wget index b8ca2fd93..b6feab581 100644 --- a/Completion/Unix/Command/_wget +++ b/Completion/Unix/Command/_wget @@ -63,7 +63,7 @@ _arguments -C -s \ '--default-page=[specify default page name, normally index.html]' \ '(--adjust-extension -E)'{--adjust-extension,-E}'[save all HTML/CSS documents with proper extensions]' \ "--ignore-length[ignore \`Content-Length' header field]" \ - '*--header=:string' \ + '*--header=[send a custom HTTP header]:header:->header' \ '--max-redirect=:number' \ '--proxy-user=:user' \ '--proxy-password=:password' \ @@ -132,6 +132,7 @@ _arguments -C -s \ '--no-clobber' \ '--no-directories' \ '--no-host-directories' \ + '--no-use-server-timestamps[do not set timestamp to server provided value]' \ '--htmlify=:htmlify:' \ '--no:no:->noflags' \ '*:URL:_urls' && return 0 @@ -156,4 +157,42 @@ case "$state" in '(unix)windows' \ '(unix windows)nocontrol' ;; + header) + local -a headers + headers=( + Accept{,-{Charset,Encoding,Language,Datetime}} + Authorization + Cache-Control + Connection + Cookie + Content-{Length,MD5,Type} + Date + Expect + From + Host + If-Match + If-Modified-Since + If-None-Match + If-Range + If-Unmodified-Since + Max-Forwards + Pragma + Proxy-Authorization + Range + Referer + TE + Upgrade + User-Agent + Via + Warning + X-Requested-With + X-Do-Not-Track + DNT + X-Forwarded-For + X-ATT-DeviceId + X-Wap-Profile + ) + headers=($^headers\\:\ ) + _describe -t header 'HTTP header' headers + ;; esac diff --git a/Completion/Unix/Command/_zcat b/Completion/Unix/Command/_zcat index 6dd4ecd7b..cf927507b 100644 --- a/Completion/Unix/Command/_zcat +++ b/Completion/Unix/Command/_zcat @@ -1,6 +1,6 @@ #compdef zcat -if _pick_variant gnu=GNU unix --license; then +if _pick_variant gz='(GNU|NetBSD)' unix --license; then _gzip "$@" else _compress "$@" diff --git a/Completion/Unix/Command/_zpool b/Completion/Unix/Command/_zpool index 03ebd0642..f2116dc2e 100644 --- a/Completion/Unix/Command/_zpool +++ b/Completion/Unix/Command/_zpool @@ -86,7 +86,7 @@ _zpool() { ) if [[ $service == "zpool" ]]; then - _arguments -C -A "-*" \ + _arguments -C \ '-\?[show help information]' \ '1:subcommand:compadd -a subcmds' \ '*:: :->subcmd' && return diff --git a/Completion/Unix/Type/_date_formats b/Completion/Unix/Type/_date_formats new file mode 100644 index 000000000..09f8cab52 --- /dev/null +++ b/Completion/Unix/Type/_date_formats @@ -0,0 +1,106 @@ +#autoload + +local flag +local -aU specs +local -A exclusion + +exclusion=( + 'E' '[cCgGxXyY]' + 'O' '[BdeHImMSuUVwWy]' + '-' '[OEdegHIjklmMSUz]' + '_' '[OEdgHIjmMSUz]' + '0' '[Oekl]' + '^' '[OEaAbBchP]' + '#' '[OEaAbBchpPrXZ]' +) + +compset -P '(%[0-9EO_\\^#-]#[^0-9%EO_\\^#-]|[^%])#' +compset -S '%*' +specs=( + 'a:abbreviated day name' + 'A:full day name' + 'b: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)' + '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)' + 's:seconds since the epoch' + 'S:seconds (00-60)' + 't:tab' + 'T:24-hour notation with seconds (%H:%M:%S)' + 'u:day of week (1-7, 1=monday)' + 'U:week number of current year, sunday based (00-53)' + 'V:ISO 8601 week number of current year, week 1 has 4 days in current year (01-53)' + 'w:day of week (0-6, 0=sunday)' + 'W:week number of current year, monday based (00-53)' + 'x:locale dependent date representation without time' + 'X:locale dependent time representation without date' + 'y:2-digit year (00-99)' + 'Y:full year' + 'z:UTC offset' + 'Z:timezone name' + '%:A %' +) + +case $OSTYPE in + freebsd*|linux-gnu|solaris2.<11->) + specs+=( + "-: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+=( + 'P:lower case locale dependent am/pm' + ) + ;| + freebsd*) + specs+=( 'v:date in short form (%e-%b-%Y)' ) + ;| + solaris2.<11->|freebsd*) + specs+=( '+:localized representation of date and time' ) + ;; + solaris2.<-10>) + specs=( ${specs:#[EOs]:*} ) + ;; +esac + +if [[ $1 == zsh ]]; then + specs+=( + 'f:day of month (1-31)' + 'K:hour (0-23)' + 'L:hour (0-12)' + '.:fractional part of seconds since epoch' + "-:don't pad numeric values" + ) +fi + +for flag in ${(s..)PREFIX#%}; do + (( $+exclusion[$flag] )) && specs=( ${(M)specs:#${~exclusion[$flag]}:*} ) +done + +_describe -t date-format-specifier 'date format specifier' specs \ + -p "${(Q)PREFIX:-%}" -S '' +_message -e date-format-precision 'precision for %%. (1-6)' diff --git a/Completion/Unix/Type/_dates b/Completion/Unix/Type/_dates new file mode 100644 index 000000000..83397b45a --- /dev/null +++ b/Completion/Unix/Type/_dates @@ -0,0 +1,126 @@ +#autoload + +# Options: +# -f format : specify strftime format for matches +# -f s/m/h/d/w/M : specify relative format +# -F : select a future rather than past date + +# Styles: +# 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 + +local -a disp cand expl +local userformat format spacer=1 spacing month monstart skip match +local d day daysecs extra preclude r ri col +local -a starts skips +local -i start now mult +local -i columns=$(( (COLUMNS+4) / 32 )) rows=LINES-4 offset=0 +local -a days=( Mo Tu We Th Fr Sa Su ) +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 +format=${userformat:-${format[2]:-%F}} + +zstyle -a ':completion:$curcontext:dates' max-matches-length r +for ri in $r; do + [[ $ri = [0-9]##% ]] && (( ri = LINES * .${ri%%%} )) + (( ri < rows )) && (( rows=ri )) +done +(( rows = rows / 8 )) +zmodload -i zsh/datetime || rows=0 + +_tags dates || return 0 +_comp_mesg=yes +_description -2V -x dates expl date +compadd "${@:/-X/-x}" "$expl[@]" - +[[ -n $PREFIX$SUFFIX ]] && return 0 +(( rows )) || return 0 +compstate[list]='packed rows' + +if [[ $WIDGET = _next_tags ]]; then + typeset -g -i _next_tags_line + typeset -g -i _next_tags_date=$(( HISTNO == _next_tags_line ? _next_tags_date+1 : 1)) + _next_tags_line=HISTNO + (( offset = _next_tags_date*rows*columns )) +fi + +(( now=EPOCHSECONDS )) +strftime -s year '%Y' $now +strftime -s month '%m' $now +(( offset = future*offset + year*12 + month + ((future == 1) ? rows*columns-2 : -1) )) +for ((;rows;rows--)); do + disp=() mlabels="" + for ((col=1;col<=columns;col++)); do + (( start = offset + col - rows * columns )) + strftime -r -s monstart '%Y%m' $(( start/12 ))$(( 1 + start % 12 )) + strftime -s skip '%w' $(( monstart-86400 )) + starts[col]=$monstart + skips[col]=$skip + disp+=( $days ' ' ) + + mfmt='%B' + strftime -s mlabel '%m' $monstart + [[ $mlabel = 01 ]] && mfmt+=' %Y' + strftime -s mlabel "$mfmt" $monstart + + mlabels+="${(r.(col == columns) ? 28 : 32.):-${(l.(26-$#mlabel)/2.)}$mlabel}" + done + (( spacing = COLUMNS - 32 * columns + 2 )) + disp[-1]="${(l.spacing.)}" + (( spacing < 2 )) && spacer=0 disp[-1]=() + expl[1+expl[(i)-V]]=dates-$rows + compadd -x "$mlabels" "$expl[@]" -d disp -E $(( $#disp )) + + for ((line=0;line<6;line++)); do + for ((col=1;col<=columns;col++)); do + if (( skips[col] && !line )); then + disp=(); disp[skips[col]]='' + compadd -x "$mlabels" "$expl[@]" -d disp -E $skips[col] + (( skip=skips[col] )) + else + skip=0 + fi + disp=() cand=() + (( extra = (col == columns) ? spacer : 1 )) + (( preclude = 0 )) + for ((d=1;d<=7-skip;d++)); do + (( day = d+7*line+skip-skips[col] )) + (( daysecs = starts[col] + 86400 * (day - 1) )) + strftime -s realday '%d' $daysecs + if (( realday != day )); then + (( extra+=8-d )) + break + fi + (( mult = -future * (now - daysecs) + (future == 1 ? 86400 : 0) )) + case $format in + s) (( match = mult )) ;; + m) (( match = mult / 60 )) ;; + h) (( match = mult / 3600 )) ;; + d) (( match = mult / 86400 )) ;; + w) (( match = mult / 604800 )) ;; + M) (( match = mult / 2592000 )) ;; + *) strftime -s match - $format $daysecs ;; + esac + disp+=( "${(l.2.)day}" ) + if (( future < 0 && now < daysecs )); then + (( extra++ )) + elif (( future > 0 && (now - daysecs) > 86400 )); then + (( preclude++ )) + else + (( (now - daysecs) < 86400 && (now - daysecs) > 0 )) && + compstate[insert]=menu:$(( compstate[nmatches] + $#disp )) + cand+=( "$match" ) + fi + done + if (( preclude )); then + compadd -x "$mlabels" "$expl[@]" -E $preclude -d disp + shift preclude disp + fi + compadd -x "$mlabels" -U -i "$IPREFIX" -I "$ISUFFIX" "$expl[@]" "$@" -d disp -E $extra -a cand + done + done +done diff --git a/Completion/Unix/Type/_diff_options b/Completion/Unix/Type/_diff_options index d76c265ca..6af392a13 100644 --- a/Completion/Unix/Type/_diff_options +++ b/Completion/Unix/Type/_diff_options @@ -101,33 +101,63 @@ if _pick_variant -c $cmd gnu=GNU unix -v; then '--help[display help info]' \ "$@" else + of='-c -e -f' case $OSTYPE in solaris2.<9->) + of+=' -u -U' args=( - '(-c -e -f -C -U)-u[output a unified diff]' - '(-c -e -f -C -u)-U[output a unified diff]:lines of context' + "($of)-u[output a unified diff]" + "($of)-U[output a unified diff]:lines of context" ) ;& solaris*) + of+=' -C -h -n -D' args+=( '-i[case insensitive]' '-t[expand tabs to spaces]' '-w[ignore all white space]' - '(-c -e -f -n -u -U -h -D)-C+[output a context diff]:lines of context' - '(-c -e -f -n -u -U -C -D)-h[do a fast, half-hearted job]' - '(-c -e -f -u -U -h -C -D)-n[reversed ed script]' - '(-c -e -f -n -u -U -h -C)-D[output merged file with preprocessor directives]:preprocessor symbol' + "($of)-C+[output a context diff]:lines of context" + "($of)-h[do a fast, half-hearted job]" + "($of)-n[reversed ed script]" + "($of)-D[output merged file with preprocessor directives]:preprocessor symbol" '-l[output through pr]' '-s[report on identical files]' '-S+[set first file in comparison]:start with file:_files' ) ;; + openbsd*) + of+=' -n -q -u -C -D -U' + args=( + "($of)-n[produce an rcsdiff(1)-compatible diff]" + "($of)-q[only print a line when the files differ; does not produce a list of changes]" + "($of)-u[produce a unified diff with 3 lines of context]" + "($of)-C+[produce a context diff]:number of lines of context" + "($of)-D[produce a merged file with preprocessor directives]:preprocessor symbol" + "($of)-U+[produce a unified diff]:number of lines of context" + '-a[treat all files as ASCII text]' + '-d[try to produce the smallest diff possible]' + '-I[ignore changes whose lines match the extended regular expression]:extended regular expression pattern' + '-i[ignore case]' + '*-L[print a label instead of the file name and time]:label' + '-l[long output format (paginate with pr(1))]' + '-p[show characters from the last line before the context]' + '-T[consistently align tabs]' + '-t[expand tabs in output lines]' + '-w[like -b, but totally ignore whitespace]' + '-N[treat absent files in either directory as if they were empty]' + '-P[treat absent files in the second directory as if they were empty]' + '-S[start a directory diff from a file name]:file name:_files' + '-s[report files that are the same]' + '*-X[exclude files and subdirectories whose basenames match lines in a file]:file name:_files' + '-x[exclude files and subdirectories whose basenames match a pattern]:pattern' + ) + ;; esac - - _arguments "$args[@]" \ - "(-e -f -u -n)-c[output a context diff]" \ - "(-c -f -u -n)-e[output an ed script]" \ - "(-c -e -u -n)-f[output a reversed ed script]" \ + + _arguments -s "$args[@]" \ + "($of)-c[output a context diff]" \ + "($of)-e[output an ed script]" \ + "($of)-f[output a reversed ed script]" \ '-b[skip trailing white spaces]' \ '-r[recursively compare subdirectories]' \ "$@" diff --git a/Completion/Unix/Type/_email_addresses b/Completion/Unix/Type/_email_addresses index 926e8b4e3..7d5c942de 100644 --- a/Completion/Unix/Type/_email_addresses +++ b/Completion/Unix/Type/_email_addresses @@ -88,7 +88,7 @@ _email-local() { _email_addresses() { local -a plugins reply list args local -A opts files - local plugin rcfile muttrc expl ret fret + local plugin rcfile muttrc expl sep ret fret local __specialx='][()<>@,;:\\".' local __spacex=" " # Space, tab @@ -159,7 +159,8 @@ _email_addresses() { if (( fret == 300 )); then if (( ! $+opts[-c] )) && [[ $opts[-n] = $plugin ]]; then - zformat -a list ' -- ' "${reply[@]}" + zstyle -s ":completion:${curcontext}:$curtag" list-separator sep || sep=-- + zformat -a list " $sep " "${reply[@]}" _wanted mail-aliases expl 'alias' compadd "$@" \ -d list - ${reply%%:*} && ret=0 else diff --git a/Completion/Unix/Type/_files b/Completion/Unix/Type/_files index e628cb39e..fe0780a57 100644 --- a/Completion/Unix/Type/_files +++ b/Completion/Unix/Type/_files @@ -1,6 +1,24 @@ #compdef -redirect-,-default-,-default- -local opts tmp glob pat pats expl tag i def descr end ign ret=1 match tried +local -a match mbegin mend +local ret=1 + +# Look for glob qualifiers. This is duplicated from _path_files because +# we don't want to complete them multiple times (for each file pattern). +if _have_glob_qual $PREFIX; then + compset -p ${#match[1]} + if [[ $_comp_caller_options[extendedglob] == on ]] && compset -P '\#'; then + _globflags && ret=0 + else + if [[ $_comp_caller_options[extendedglob] == on ]]; then + _describe -t globflags "glob flag" '(\#:introduce\ glob\ flag)' -Q -S '' && ret=0 + fi + _globquals && ret=0 + fi + return ret +fi + +local opts tmp glob pat pats expl tag i def descr end ign tried local type sdef ignvars ignvar prepath oprefix rfiles rfile zparseopts -a opts \ @@ -15,6 +33,8 @@ if (( $tmp[(I)-g*] )); then # add `#q' to the beginning of any glob qualifier if not there already [[ "$glob" = (#b)(*\()([^\|\~]##\)) && $match[2] != \#q* ]] && glob="${match[1]}#q${match[2]}" +elif [[ $type = */* ]]; then + glob="*(-/)" fi tmp=$opts[(I)-F] if (( tmp )); then @@ -33,59 +53,21 @@ else fi if zstyle -a ":completion:${curcontext}:" file-patterns tmp; then - [[ "$type" = */* ]] && glob="$glob,*(-/)" pats=() for i in ${tmp//\%p/${${glob:-\*}//:/\\:}}; do if [[ $i = *[^\\]:* ]]; then - pats=( "$pats[@]" " $i " ) + pats+=( " $i " ) else - pats=( "$pats[@]" " ${i}:files " ) + pats+=( " ${i}:files " ) fi done elif zstyle -t ":completion:${curcontext}:" list-dirs-first; then - if [[ "$type" = *g* ]]; then - - # add `^-/' after `#q' glob qualifier if not there already - if [[ "$glob" = (#b)(*\(\#q)(*\)) ]]; then - [[ $match[2] != \^-/* ]] && - glob="${match[1]}^-/,${match[2]}" - else - glob="$glob(#q^-/)" - fi - - pats=( " *(-/):directories:directories ${glob//:/\\:}:globbed-files" ) - elif [[ "$type" = */* ]] then - pats=( '*(-/):directories ' '*:all-files ' ) - else - pats=( '*(-/):directories:directories *(^-/):other-files ' ) - fi + pats=( " *(-/):directories:directory ${${glob:-*}//:/\\:}(#q^-/):globbed-files" '*:all-files' ) else - if [[ "$type" = *g* ]]; then - # People prefer to have directories shown on first try as default. # Even if the calling function didn't use -/. - # - # if [[ "$type" = */* ]]; then - - pats=( " ${glob//:/\\:}:globbed-files *(-/):directories" '*:all-files ' - - ### We could allow _next_tags to offer only globbed-files or directories - ### by adding: - ### " ${glob//:/\\:}:only-globbed-files " ' *(-/):only-directories ' - - ) - - # else - # pats=( " ${glob//:/\\:}:globbed-files " - # '*(-/):directories ' '*:all-files ' ) - # fi - - elif [[ "$type" = */* ]]; then - pats=( '*(-/):directories ' '*:all-files ' ) - else - pats=( '*:all-files ' ) - fi + pats=( "${${glob:-*}//:/\\:}:globbed-files *(-/):directories" '*:all-files ' ) fi tried=() diff --git a/Completion/Unix/Type/_find_net_interfaces b/Completion/Unix/Type/_find_net_interfaces index 0c7033519..3f5db6b9a 100644 --- a/Completion/Unix/Type/_find_net_interfaces +++ b/Completion/Unix/Type/_find_net_interfaces @@ -21,22 +21,15 @@ case $OSTYPE in ;; darwin*|freebsd*|dragonfly*) net_intf_list=( $(ifconfig -l) ) ;; irix*) net_intf_list=( ${${${(f)"$(/usr/etc/netstat -i)"}%% *}[2,-1]} ) ;; - *linux*) - if (( $+commands[ip] )); then - net_intf_list=( ${${(m)${(f)"$(ip -o link)"}#*: }%%: *} ) - fi - ;& - *) - if [[ ${#net_intf_list} -eq 0 ]]; then # linux's deprecated ifconfig may truncate long interface names - net_intf_list=( $(ifconfig -a 2>/dev/null | sed -n 's/^\([^ :]*\).*/\1/p') ) - if [[ -d /proc/sys/net/ipv4/conf ]]; then - # On linux we used to use the following as the default. - # However, we now use ip or ifconfig since it finds additional devices such + net_intf_list=( $(_call_program interfaces "ifconfig -a 2>/dev/null | sed -n 's/^\([^ :]*\).*/\1/p'") ) + if (( ${#net_intf_list} == 0 )) && [[ -d /proc/sys/net/ipv4/conf ]]; then + # On linux we used to use the following as the default, without /sys/class/net/*. + # However, we now use ifconfig since it finds additional devices such # as tunnels. So only do this if that didn't work. - net_intf_list=( /proc/sys/net/ipv4/conf/*~*(all|default)(N:t) ) + typeset -gU net_intf_list + net_intf_list=( /proc/sys/net/ipv4/conf/*~*(all|default)(N:t) /sys/class/net/*(N:t) ) fi - fi ;; esac diff --git a/Completion/Unix/Type/_pdf b/Completion/Unix/Type/_pdf index 60cee84ee..5fda42a12 100644 --- a/Completion/Unix/Type/_pdf +++ b/Completion/Unix/Type/_pdf @@ -1,4 +1,4 @@ -#compdef pdf2dsc pdf2ps pdfimages pdfinfo pdftopbm pdftops pdftotext pdfopt pdffonts kpdf apvlv epdfview +#compdef pdf2dsc pdf2ps pdfimages pdfinfo pdftopbm pdftops pdftotext pdfopt pdffonts kpdf apvlv epdfview mupdf local expl ext='' diff --git a/Completion/Unix/Type/_pids b/Completion/Unix/Type/_pids index cf9000538..8edb33529 100644 --- a/Completion/Unix/Type/_pids +++ b/Completion/Unix/Type/_pids @@ -9,13 +9,13 @@ _tags processes || return 1 if [[ "$1" = -m ]]; then all=() - match="*[[:blank:]]${PREFIX}[0-9]#${SUFFIX}[[:blank:]]*[/[:blank:]]${2}*" + match="(*[[:blank:]]|)${PREFIX}[0-9]#${SUFFIX}[[:blank:]]*(/|[[:blank:]]-(#c,1))${2}([[:blank:]]*|)" shift 2 elif [[ "$PREFIX$SUFFIX" = ([%-]*|[0-9]#) ]]; then all=() match="(*[[:blank:]]|)${PREFIX}[0-9]#${SUFFIX}[[:blank:]]*" else - all=(-U) + all=(-P "$IPREFIX" -S "$ISUFFIX" -U) match="*[[:blank:]]*[[/[:blank:]]$PREFIX*$SUFFIX*" nm="$compstate[nmatches]" fi diff --git a/Completion/Unix/Type/_ps1234 b/Completion/Unix/Type/_ps1234 deleted file mode 100644 index 13c90b50c..000000000 --- a/Completion/Unix/Type/_ps1234 +++ /dev/null @@ -1,109 +0,0 @@ -#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 expl paren - -if [[ -n $compstate[quote] ]]; then - paren='(' -else - paren='\(' -fi - -if [[ $PREFIX == *%(-|)<-># ]]; then - specs=( - 'm:hostname up to first .' - '_:status of parser' - 'd:current working directory' - '/:current working directory' - '~:current working directory, with ~ replacement' - 'N:name of current script or shell function' - 'x:name of file containing code being executed' - 'c:deprecated' - '.:deprecated' - 'C:deprecated' - 'F:start using fg color' - 'K:start using bg color' - 'G:counts as extra character inside %{...%}' - ) - if [[ $PREFIX == *% ]]; then - if [[ $service == -value-,SPROMPT,* ]]; then - specs+=( - 'r:suggested correction' - 'R:corrected string' - ) - fi - specs+=( - '%:A %' - '):A )' - 'l:current line (tty) with /dev/tty stripped' - 'M:full hostname' - 'n:username' - 'y:current line (tty)' - '#:a # when root, % otherwise' - '?:return status of last command' - 'h:current history event number' - '!:current history event number' - 'i:current line number' - 'I:current source line number' - 'j:number of jobs' - 'L:$SHLVL' - 'D:date in yy-mm-dd format' - 'T:current time of day, 24-hour format' - 't:current time of day, 12-hour am/pm format' - '@:current time of day, 12-hour am/pm format' - '*:current time of day, 24-hour format with seconds' - 'w:the date in day-dd format' - 'W:the date in mm/dd/yy format' - 'D{}:format string like strftime' - 'B:start bold' - 'b:stop bold' - 'E:clear to end of line' - 'U:start underline' - 'u:stop underline' - 'S:start standout' - 's:stop standout' - 'f:reset fg color' - 'k:reset bg color' - '{:start literal escape sequence' - '}:stop literal escape sequence' - 'v:value from $psvar array' - '(:ternary expression %(x.true-string.false-string)' - '<<:truncation from left %len<string<' - '>>:truncation from right %len>string>' - '[]:truncation from who knows where' - ) - fi - compset -P "*" - _describe -t prompt-format-specifier 'prompt format specifier' specs -S '' - _message -e prompt-format-specifier number -elif [[ $PREFIX == *%$paren(-|)<-># ]]; then - specs=( - '!:running with privileges' - '#:effective uid' - '?:exit status' - '_:at least n shell constructs started' - 'C:at least n path elements' - '/:at least n path elements' - '.:at least n path elements' - 'c:at least n path elements' - '~:at least n path elements' - 'D:month' - 'd:day of month' - 'g:effective gid' - 'j:number of jobs' - 'L:SHLVL' - 'l:number of characters already printed' - 'S:SECONDS parameter at least n' - 'T:current hour' - 't:current minute' - 'v:psvar has at least n elements' - 'V:element n of psvar is set and non-empty' - 'w:day of week (Sunday = 0)' - ) - compset -P "*" - _describe -t ternary-prompt-expression 'ternary prompt format test character' specs -S '' - _message -e ternary-prompt-expression number -else - _describe -t prompt-format-specifier 'prompt format specifier' '(%)' -S '' - _default "$@" -fi diff --git a/Completion/Unix/Type/_python_modules b/Completion/Unix/Type/_python_modules index b30848d83..e82f8efbf 100644 --- a/Completion/Unix/Type/_python_modules +++ b/Completion/Unix/Type/_python_modules @@ -26,10 +26,8 @@ _python_modules () { if ( [[ ${(P)+array_name} -eq 0 ]] || _cache_invalid $cache_id ) && ! _retrieve_cache $cache_id; then - local script='import sys, pydoc -def f(p,m,d): - if m.find(".") < 0: sys.stdout.write(m+"\n") -pydoc.ModuleScanner().run(f)' + local script='import pkgutil +for importer, name, ispkg in pkgutil.iter_modules(): print(name)' typeset -agU $array_name set -A $array_name \ diff --git a/Completion/Unix/Type/_ttys b/Completion/Unix/Type/_ttys new file mode 100644 index 000000000..5e5598570 --- /dev/null +++ b/Completion/Unix/Type/_ttys @@ -0,0 +1,19 @@ +#autoload + +# Options: +# +# -d strip /dev/ prefix from matches +# -D matches allowed with or without /dev/ prefix + +local -a ttys expl pre +local stripdev optdev + +zparseopts -D -K -E d=stripdev D=optdev + +ttys=( /dev/tty?*(N) /dev/pts/^ptmx(N) ) +ttys=( ${ttys#/dev/} ) +[[ -z $stripdev ]] && pre=( -p /dev/ ) + +_description ttys expl 'tty' +[[ -n $optdev ]] && compadd "$@" "$expl[@]" -M 'r:|/=* r:|=*' -a ttys && return +compadd "$@" "$expl[@]" "$pre[@]" -M 'r:|/=* r:|=*' -a ttys diff --git a/Completion/X/Type/_x_font b/Completion/X/Type/_x_font index a363b2775..1202d821e 100644 --- a/Completion/X/Type/_x_font +++ b/Completion/X/Type/_x_font @@ -9,8 +9,8 @@ _tags fonts || return 1 if (( ! $+_font_cache )); then typeset -gU _font_cache - _font_cache=( "${(@)^${(@f)$(_call_program fonts xlsfonts 2> /dev/null)}%%--*}--" ) + _font_cache=( ${(f)"$(_call_program fonts xlsfonts)"} ) fi _wanted fonts expl font \ - compadd -M 'r:|-=* r:|=*' "$@" -S '' -a - _font_cache + compadd -M 'r:|-=* r:|=*' "$@" -a - _font_cache diff --git a/Completion/Zsh/Command/_compadd b/Completion/Zsh/Command/_compadd new file mode 100644 index 000000000..03fed1854 --- /dev/null +++ b/Completion/Zsh/Command/_compadd @@ -0,0 +1,52 @@ +#compdef compadd + +local curcontext="$curcontext" state line ret=1 +typeset -A opt_args + +_arguments -C -s -S -A "-*" \ + '-P+[specify prefix]:prefix' \ + '-S+[specify suffix]:suffix' \ + '-p+[specify hidden prefix]:hidden prefix' \ + '-s+[specify hidden suffix]:hidden suffix' \ + '-i+[specify ignored prefix]:ignored prefix' \ + '-I+[specify ignored suffix]:ignored suffix' \ + '(-k)-a[matches are elements of specified arrays]' \ + '(-a)-k[matches are keys of specified associative arrays]' \ + '-d+[specify display strings]:array:_parameters -g "*array*"' \ + '-l[list display strings one per line, not in columns]' \ + '-o[order matches by display string not by match string]' \ + '(-1 -E)-J+[specify match group]:group' \ + '-V+[specify unsorted match group]:group' \ + '(-J -E)-1[remove only consecutive duplicates from group]' \ + '-2[preserve all duplicates]' \ + '(-x)-X[specify explanation]:explanation' \ + '(-X)-x[specify unconditional explanation]:explanation' \ + '-q[make suffix autoremovable]' \ + '-r+[specify character class for suffix autoremoval]:character class' \ + '-R+[specify function for suffix autoremoval]:function:_functions' \ + '-f[mark matches as being files]' \ + '-e[mark matches as being parameters]' \ + '-W[specify location for matches marked as files]' \ + '-F+[specify array of ignore patterns]:array:_parameters -g "*array*"' \ + '-Q[disable quoting of matchines]' \ + '*-M[specify matching specifications]' \ + '-n[hide matches in completion listing]' \ + '-U[disable internal matching of completion candidates]' \ + '-O+[populate array with matches instead of adding them]:array:_parameters -g "*array*"' \ + '-A+[populate array with expanded matches instead of adding them]:array:_parameters -g "*array*"' \ + '-D+[delete elements from array corresponding to non-matching candidates]:array:_parameters -g "*array*"' \ + '-C[add special match that expands to all other matches]' \ + '(-1 -J)-E+[add specified number of display only matches]:number' \ + '*:candidate:->candidates' && ret=0 + +if [[ -n $state ]]; then + if (( $+opt_args[-a] )); then + _parameters -g "*array*" && ret=0 + elif (( $+opt_args[-k] )); then + _parameters -g "*assoc*" && ret=0 + else + _message -e candidate candidates + fi +fi + +return ret diff --git a/Completion/Zsh/Command/_fc b/Completion/Zsh/Command/_fc index c2987211e..68456cc3d 100644 --- a/Completion/Zsh/Command/_fc +++ b/Completion/Zsh/Command/_fc @@ -1,7 +1,7 @@ #compdef fc history r local curcontext="$curcontext" state state_descr line ret=1 -local list events num cmd +local events num cmd sep typeset -A opt_args local fc_common fc_hist fc_r @@ -12,8 +12,10 @@ words=( "${(@)words[1,CURRENT-1]:#*=*}" "${(@)words[CURRENT,-1]}" ) fc_common=( -s -S - '(-A -R -W -I -p -P)-r[reverse order of the commands]' - '(-A -R -W -I -e -p -P)-n[suppress line numbers]' + '(-A -R -W -p -P)-I[include internal (new) events only]' + '(-A -R -W -p -P)-L[include local events only]' + '(-A -R -W -p -P)-r[reverse order of the events]' + '(-A -R -W -e -p -P)-n[suppress line numbers]' ) if [[ -n ${words[(r)-[pa](|[ap])]} ]]; then @@ -32,20 +34,20 @@ else fi fc_hist=( - '(-A -R -W -I -a -p -P 2)-m[treat argument as a pattern]' - '(-A -R -W -I -e -f -E -i -t -a -p -P)-d[print time-stamps]' - '(-A -R -W -I -e -d -E -i -t -a -p -P)-f[mm/dd/yyyy format time-stamps]' - '(-A -R -W -I -e -d -f -i -t -a -p -P)-E[dd.mm.yyyy format time-stamps]' - '(-A -R -W -I -e -d -f -E -t -a -p -P)-i[yyyy-mm-dd format time-stamps]' - '(-A -R -W -I -e -d -f -E -i -a -p -P)-t[print time-stamps in specified format]:date format' - '(-A -R -W -I -e -a -p -P)-D[print elapsed times]' + '(-A -R -W -a -p -P 2)-m[treat argument as a pattern]' + '(-A -R -W -e -f -E -i -t -a -p -P)-d[print time-stamps]' + '(-A -R -W -e -d -E -i -t -a -p -P)-f[mm/dd/yyyy format time-stamps]' + '(-A -R -W -e -d -f -i -t -a -p -P)-E[dd.mm.yyyy format time-stamps]' + '(-A -R -W -e -d -f -E -t -a -p -P)-i[yyyy-mm-dd format time-stamps]' + '(-A -R -W -e -d -f -E -i -a -p -P)-t[print time-stamps in specified format]:date format' + '(-A -R -W -e -a -p -P)-D[print elapsed times]' '(-A -R -W -I -e -d -f -i -l -m -n -r -D -E -t -P)-a[with -p, automatically pop history on function return]' '(-A -R -W -I -e -d -f -i -l -m -n -r -D -E -t -P)-p[push current history to stack]' '(- *)-P[pop history from stack]' ) -fc_r='(-A -R -W -I -e)-l[list resulting commands on stdout]' +fc_r='(-A -R -W -e)-l[list resulting commands on stdout]' case $service in history) @@ -56,31 +58,29 @@ case $service in ;; *) _arguments "$fc_common[@]" "$fc_hist[@]" "$fc_r" \ - '(-A -R -W -I -a -l -n -d -f -E -i -r -t -D -p -P)-e+[specify editor to invoke]:editor to invoke:_command_names -e' \ - '(-a -l -m -e -r -n -d -f -t -E -i -R -D -A -W -p -P *)-'{\ + '(-A -R -W -a -l -n -d -f -E -i -r -t -D -p -P)-e+[specify editor to invoke]:editor to invoke:_command_names -e' \ + '(-a -l -L -m -e -r -n -d -f -t -E -i -R -D -A -W -p -P *)-'{\ 'R[read history from file]',\ 'A[append history to file]',\ -'W[write history to file]',\ -'I[read/write new events only]'} && ret=0 +'W[write history to file]'} && ret=0 ;; esac if [[ -n $state ]]; then + zstyle -s ":completion:${curcontext}:" list-separator sep || sep=-- if [[ -z ${line:#*=*} ]] && compset -P '*='; then _message -e replacements 'replacement' elif [[ -prefix [0-9] ]]; then - events=( ${(ps.\0.)"$(printf '%s:%s\0' ${(kv)history})"} ) - zformat -a list " -- " "$events[@]" - _wanted -2V events expl "$state_descr" compadd -M "B:0=" -ld list - \ - "${events[@]%%:*}" + events=( ${(0)"$(printf "%-${#HISTNO}.${#HISTNO}s $sep %s\0" "${(kv)history[@]}")"} ) + _wanted -2V events expl "$state_descr" compadd -M "B:0=" -ld events - \ + "${events[@]%% *}" elif [[ -prefix - ]]; then - for num cmd in ${(kv)history}; do + for num cmd in "${(kv@)history}"; do (( num=num - HISTNO )) - events+=( $num:$cmd ) + events+=( "${(r.1+$#HISTNO.)num} $sep $cmd" ) done - zformat -a list " -- " "$events[@]" - _wanted -2V events expl "$state_descr" compadd -ld list - \ - "${events[@]%%:*}" + _wanted -2V events expl "$state_descr" compadd -ld events - \ + "${events[@]%% *}" else _wanted events expl "$state_descr" compadd -S '' - \ ${${history%%[=[:IFS:]]*}:#[0-9-]*} || _guard "[0-9]#" event diff --git a/Completion/Zsh/Command/_print b/Completion/Zsh/Command/_print index 405393355..1eba13e88 100644 --- a/Completion/Zsh/Command/_print +++ b/Completion/Zsh/Command/_print @@ -11,7 +11,7 @@ if [[ $service = print ]]; then pflag='(-s -u -z)-p[print arguments to input of coprocess]' if [[ -n ${words[1,CURRENT][(r)-*P*]} ]]; then - rest='*: :_ps1234' + rest='*: :->prompt' else rest='*: :_default' fi @@ -48,16 +48,17 @@ if [[ $state = printf ]]; then fi if [[ $state = printfformat ]]; then - if [[ ${(Q)PREFIX} = *%((-|)<->|[-#0 +*.])# ]]; then + if [[ ${(Q)PREFIX} = *%[0-9\$#\ +*.\'-]# ]]; then local -a specs specs=( '#:alternate form' - '0:zeropad to length n' + '0:zero pad to length' '-:left adjust result' ' :leave one space in front of positive number from signed conversion' '+:always place sign before a number from signed conversion' '*:field width in next argument' '.:precision' + "':thousand separators" 'c:print the first character of the argument' 's:print the argument as a string' {d,i}':signed decimal number or with leading " numeric value of following character' @@ -73,12 +74,16 @@ if [[ $state = printfformat ]]; then 'q:as %s but shell quote result' ) compset -P "*" - _describe -t print-format-specifier 'print format specifier' specs -S '' - _message -e print-format-specifier 'number' + _describe -t print-format-specifiers 'print format specifier' specs -S '' + _message -e print-format-specifiers 'number' else - _describe -t print-format-specifier 'print format specifier' '(%)' -S '' + _wanted print-format-specifiers expl 'print format specifier' compadd -S '' % fi ret=0 +elif [[ $state = prompt ]]; then + _default && ret=0 + # complete prompt specifiers without interfering too much with default completion + (( $#compstate[unambiguous] <= $#PREFIX || ! $#PREFIX )) && _ps1234 && ret=0 fi return ret diff --git a/Completion/Zsh/Command/_setopt b/Completion/Zsh/Command/_setopt index fb38d1da6..86c0965f9 100644 --- a/Completion/Zsh/Command/_setopt +++ b/Completion/Zsh/Command/_setopt @@ -2,8 +2,9 @@ local expl ret=1 local -a onopts offopts -onopts=( ${(k)_comp_caller_options[(R)on]} ) -offopts=( ${(k)_comp_caller_options[(R)off]} ) +onopts=( ${(k)_comp_caller_options[(R)on]} printexitvalue ) +offopts=( ${(k)_comp_caller_options[(R)off]} printexitvalue ) +typeset -U onopts offopts case $service in setopt) onopts=(no$onopts) ;; unsetopt) offopts=(no$offopts) ;; diff --git a/Completion/Zsh/Command/_strftime b/Completion/Zsh/Command/_strftime new file mode 100644 index 000000000..0be7b078d --- /dev/null +++ b/Completion/Zsh/Command/_strftime @@ -0,0 +1,12 @@ +#compdef strftime + +local ret=1 expl + +_arguments -S -A '-*' -s \ + '-q[run quietly]' \ + '-r[reverse lookup using strptime]' \ + '-s+[assign result to parameter]:param:_parameters' \ + '1:format: _date_formats' \ + '2:epoch time (or date string with -r)' && ret=0 + +return ret diff --git a/Completion/Zsh/Command/_zmodload b/Completion/Zsh/Command/_zmodload index e144b981e..57fb990e9 100644 --- a/Completion/Zsh/Command/_zmodload +++ b/Completion/Zsh/Command/_zmodload @@ -68,7 +68,7 @@ else _requested loadedmodules expl 'loaded modules' \ compadd -k 'modules[(R)loaded]' && ret=0 _requested files expl 'module file' \ - _files -W module_path -/g '*.(dll|s[ol]|bundle)(:r)' && ret=0 + _files -W module_path -g '*.(dll|s[ol]|bundle)(:r)' && ret=0 _requested aliases expl 'module alias' \ compadd "$suf[@]" -k 'modules[(R)alias*]' && ret=0 done diff --git a/Completion/Zsh/Command/_zpty b/Completion/Zsh/Command/_zpty index ef4ac4bb1..99251aa0a 100644 --- a/Completion/Zsh/Command/_zpty +++ b/Completion/Zsh/Command/_zpty @@ -1,6 +1,6 @@ #compdef zpty -local state line list names expl curcontext="$curcontext" +local state line list names expl sep curcontext="$curcontext" typeset -A opt_args _arguments -C -s -S \ @@ -50,8 +50,9 @@ if [[ $state = name ]]; then fi list=( ${${(f)"$(zpty)"}#*\) } ) names=( ${list%%:*} ) - if zstyle -T ":completion:${curcontext}" verbose; then - zformat -a list ' --' ${${(f)"$(zpty)"}#*\) } + if zstyle -T ":completion:${curcontext}:" verbose; then + zstyle -s ":completion:${curcontext}:" list-separator sep || sep=-- + zformat -a list " $sep" ${${(f)"$(zpty)"}#*\) } _wanted names expl 'zpty command name' compadd -d list -a names else _wanted names expl 'zpty command name' compadd -a names diff --git a/Completion/Zsh/Context/_brace_parameter b/Completion/Zsh/Context/_brace_parameter index 3955cb7a4..9eb3465ae 100644 --- a/Completion/Zsh/Context/_brace_parameter +++ b/Completion/Zsh/Context/_brace_parameter @@ -152,7 +152,7 @@ if [[ $PREFIX = *'${('[^\)]# ]]; then "F:join arrays with newlines" "g:process echo array sequences (needs options)" "i:sort case-insensitively" - "k:subsitute keys of associative arrays" + "k:substitute keys of associative arrays" "L:lower case all letters" "n:sort decimal integers numerically" "o:sort in ascending order (lexically if no other sort option)" @@ -192,7 +192,7 @@ elif compset -P '*:([\|\*\^]|\^\^)'; then elif compset -P '*:'; then flags=( '-:substitute alternate value if parameter is null' - '+:susbtitute alternate value if parameter is non-null' + '+:substitute alternate value if parameter is non-null' '=:substitute and assign alternate value if parameter is null' '\:=:unconditionally assign value to parameter' '?:print error if parameter is set and non-null' diff --git a/Completion/Zsh/Function/_zargs b/Completion/Zsh/Function/_zargs index f1f87b447..c24b276f2 100644 --- a/Completion/Zsh/Function/_zargs +++ b/Completion/Zsh/Function/_zargs @@ -1,24 +1,48 @@ #compdef zargs -value-,-default-,-command- -# atom smasher - jan 2011 -local arguments +local arguments eofstr pos=$((CURRENT)) numeofs=0 ret=1 cmdpos=1 -arguments=( $arguments[@] - '(--eof -e)'{--eof=,-e+}'[change the end-of-input-args string from "--" to eof-str]' - '(--exit, -x)'{--exit,-x}'[exit if the size (see --max-chars) is exceeded]' - '--help[print summary and exit]' - '(--interactive, -p)'{--interactive,-p}'[prompt before executing each command line]' - '(--max-args, -n)'{--max-args=,-n+}'[use at most max-args arguments per command line]' - '(--max-chars, -s)'{--max-chars=,-s+}'[use at most max-chars characters per command line]' - '(--max-lines, -l)'{--max-lines=,-l+}'[use at most max-lines of the input-args per command line]' - '(--max-procs, -P)'{--max-procs=,-P+}'[run up to max-procs command lines in the background at once]' - '(--no-run-if-empty, -r)'{--no-run-if-empty,-r}'[do nothing if there are no input arguments before the eof-str]' - '(--null, -0)'{--null,-0}'[split each input-arg at null bytes, for xargs compatibility]' - '(--replace, -i)'{--replace=,-i}'[substitute replace-str in the initial-args by each initial-arg]' - '(--verbose, -t)'{--verbose,-t}'[print each command line to stderr before executing it]' - '--version[print the version number of zargs and exit]' -) +#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 +# probably also be confused if the command at the end takes a -e, --eof= or -- +eofstr=${${${${words[(r)(--eof=*|-e*)]}#--eof=}#-e}:---} +while { + pos=$(( words[(b:pos-1:I)$eofstr] )) + (( numeofs == 0 )) && (( cmdpos = pos )) + (( pos )) && (( numeofs++ )) + (( pos )) +} {} +case $numeofs in + 0) + #zargs arguments + arguments=( + '(--eof -e)'{--eof=,-e+}'[change the end-of-input-args string from "--" to eof-str]' + '(--exit, -x)'{--exit,-x}'[exit if the size (see --max-chars) is exceeded]' + '--help[print summary and exit]' + '(--interactive, -p)'{--interactive,-p}'[prompt before executing each command line]' + '(--max-args, -n)'{--max-args=,-n+}'[use at most max-args arguments per command line]' + '(--max-chars, -s)'{--max-chars=,-s+}'[use at most max-chars characters per command line]' + '(--max-lines, -l)'{--max-lines=,-l+}'[use at most max-lines of the input-args per command line]' + '(--max-procs, -P)'{--max-procs=,-P+}'[run up to max-procs command lines in the background at once]' + '(--no-run-if-empty, -r)'{--no-run-if-empty,-r}'[do nothing if there are no input arguments before the eof-str]' + '(--null, -0)'{--null,-0}'[split each input-arg at null bytes, for xargs compatibility]' + '(--replace, -i)'{--replace=,-i}'[substitute replace-str in the initial-args by each initial-arg]' + '(--verbose, -t)'{--verbose,-t}'[print each command line to stderr before executing it]' + '--version[print the version number of zargs and exit]' + ) + _arguments -S -s $arguments[@] && ret=0 + ;; + 1) + #argument list for command + _files && ret=0 + ;; + *) + #command and command arguments + shift cmdpos words + (( CURRENT -= cmdpos )) + _normal + ;; +esac -_arguments -S -s $arguments[@] - -_command_names -e +return ret diff --git a/Completion/Zsh/Type/_command_names b/Completion/Zsh/Type/_command_names index d9fc62dfe..940f341cb 100644 --- a/Completion/Zsh/Type/_command_names +++ b/Completion/Zsh/Type/_command_names @@ -17,9 +17,7 @@ defs=( ) [[ -n "$path[(r).]" || $PREFIX = */* ]] && - defs=( "$defs[@]" - 'executables:executable file or directory:_path_files -/g \*\(-\*\)' - ) + defs+=( 'executables:executable file:_files -g \*\(-\*\)' ) if [[ "$1" = -e ]]; then shift diff --git a/Completion/Zsh/Type/_directory_stack b/Completion/Zsh/Type/_directory_stack index 8a4cf675a..e84115a64 100644 --- a/Completion/Zsh/Type/_directory_stack +++ b/Completion/Zsh/Type/_directory_stack @@ -12,14 +12,14 @@ local expl list lines revlines disp sep ### we decided against this, for now... #! zstyle -T ":completion:${curcontext}:directory-stack" prefix-needed || -zstyle -s ":completion:${curcontext}:directory-stack" list-separator sep || sep=-- - [[ $PREFIX = [-+]* ]] || return 1 +zstyle -s ":completion:${curcontext}:directory-stack" list-separator sep || sep=-- + if zstyle -T ":completion:${curcontext}:directory-stack" verbose; then # get the list of directories with their canonical number # and turn the lines into an array, removing the current directory - lines=("${dirstack[@]}") + lines=("${(D)dirstack[@]}") if [[ ( $PREFIX[1] = - && ! -o pushdminus ) || ( $PREFIX[1] = + && -o pushdminus ) ]]; then diff --git a/Completion/Zsh/Type/_file_descriptors b/Completion/Zsh/Type/_file_descriptors index 3e251b733..3e9f4968b 100644 --- a/Completion/Zsh/Type/_file_descriptors +++ b/Completion/Zsh/Type/_file_descriptors @@ -1,31 +1,59 @@ #autoload -local i fds expl list link sep +local i fds expl disp link sep +local -a list proc -fds=( /dev/fd/<0-9>(N:t) ) +fds=( /dev/fd/<3->(N:t) ) +fds=( ${(n)fds} ) -if zstyle -T ":completion:${curcontext}:" verbose && [[ -h /proc/$$/fd/$fds[1] ]]; then - zstyle -s ":completion:${curcontext}:" list-separator sep || sep=-- +if zstyle -T ":completion:${curcontext}:file-descriptors" verbose; then + zstyle -s ":completion:${curcontext}:file-descriptors" list-separator sep || sep=-- - if zmodload -F zsh/stat b:zstat; then - for i in "${fds[@]}"; do - zstat +link -A link /proc/$$/fd/$i - list+=( "$i $sep ${link[1]}" ) - done - elif (( $+commands[readlink] )); then - for i in "${fds[@]}"; do - list+=( "$i $sep $(readlink /proc/$$/fd/$i)" ) - done - else - for i in "${fds[@]}"; do - list+=( "$i $sep $(ls -l /proc/$$/fd/$i|sed 's/.*-> //' )" ) - done + if [[ $OSTYPE = freebsd* ]]; then + fds=( ${(f)"$(procstat -f $$|awk -v OFS=: '$3>2 && $3~/[0-9]/ {print $3,$10}')"} ) + zformat -a list " $sep " $fds + fds=( ${fds%%:*} ) + elif + proc=( /proc/$$/(fd|path)/<->(@N[-1]:h) ) + [[ -n $proc ]] + then + if zmodload -F zsh/stat b:zstat; then + for i in "${fds[@]}"; do + if zstat +link -A link $proc/$i; then + list+=( "${(r.$#fds[-1].)i} $sep ${(D)link[1]}" ) + else + fds[(i)$i]=() + fi + done + elif (( $+commands[readlink] )); then + for i in "${fds[@]}"; do + if link=$(readlink $proc/$i); then + list+=( "${(r.$#fds[-1].)i} $sep ${(D)link}" ) + else + fds[(i)$i]=() + fi + done + else + for i in "${fds[@]}"; do + if link=$(ls -l $proc/$i); then + list+=( "${(r.$#fds[-1].)i} $sep ${(D)link#* -> }" ) + else + fds[(i)$i]=() + fi + done + fi 2>/dev/null fi - if (( $list[(I)* $sep ?*] )); then - _wanted file-descriptors expl 'file descriptor' compadd "$@" -d list -a - fds - return + if (( list[(I)* $sep ?*] )); then + list=( + "${(r.$#fds[-1].):-0} $sep standard input" + "${(r.$#fds[-1].):-1} $sep standard output" + "${(r.$#fds[-1].):-2} $sep standard error" $list + ) + disp=( -d list ) fi fi +fds=( 0 1 2 $fds ) -_wanted file-descriptors expl 'file descriptor' compadd -a "$@" - fds +_description -V file-descriptors expl 'file descriptor' +compadd $disp "${@/-J/-V}" "$expl[@]" -a fds diff --git a/Completion/Zsh/Type/_globquals b/Completion/Zsh/Type/_globquals index 042b27400..5cdb8f7c4 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 +local -a alts tdisp sdisp local -A specmap while [[ -n $PREFIX ]]; do @@ -117,14 +117,15 @@ while [[ -n $PREFIX ]]; do alts=() timespec=$PREFIX[1] if ! compset -P '[Mwhmsd]' && [[ -z $PREFIX ]]; then - alts+=("time-specifiers:time specifier:\ -((M\:months w\:weeks h\:hours m:\minutes s\:seconds d\:days))") + tdisp=( seconds minutes hours days weeks Months ) + alts+=( "time-specifiers:time specifier:compadd -E 0 -d tdisp -S '' - s m h d w M" ) fi if ! compset -P '[-+]' && [[ -z $PREFIX ]]; then - alts+=("senses:sense:((-\:less\ than +\:more\ than))") + sdisp=( before exactly since ) + alts+=("senses:sense:compadd -E 0 -d sdisp -S '' - + '' -") fi specmap=( M months w weeks h hours m minutes s seconds '(|+|-|d)' days) - alts+=('digits:digit ('${${specmap[(K)$timespec]}:-invalid time specifier}'):' ) + alts+=('digits:digit ('${${specmap[(K)$timespec]}:-invalid time specifier}'):_dates -f ${${timespec/[-+]/d}:-d} -S ""' ) _alternative $alts return fi diff --git a/Completion/Zsh/Type/_ps1234 b/Completion/Zsh/Type/_ps1234 new file mode 100644 index 000000000..8edf0d0cb --- /dev/null +++ b/Completion/Zsh/Type/_ps1234 @@ -0,0 +1,170 @@ +#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 expl grp cols bs suf pre changed=1 ret=1 +local -A ansi + +[[ -z $compstate[quote] ]] && bs='\' + +# first strip off any complete prompt specifications leaving only the +# current, incomplete, one +while (( changed )); do + changed=0 + compset -P '%[DFK](\\|){[^}]#}' && changed=1 # formats with arg: %x{...} + compset -P '%[0-9-\\]#[^DFK(0-9-<>\\\[]' && changed=1 # normal formats + compset -P '%[0-9-\\]#(<[^<]#<|>[^>]#>|\[[^\]]#\])' && changed=1 # truncations + compset -P '%[0-9-\\]#(\\|)\([0-9-]#[^0-9]?|[^%]' && changed=1 # start of ternary + compset -P '[^%]##' && changed=1 # sundry other characters + # %D/%F/%K without a following { ... } + [[ $PREFIX = %(-|)<->#[DFK](\\[^{]|[^{\\])* ]] && + compset -P '%[0-9\\-]#[DFK]' && changed=1 +done +[[ $PREFIX = %(-|)<->[FK](#e) ]] && compset -P '*' # F/K with number + +if compset -P '%[FK]'; then + # this should use -P but that somehow causes single quotes to be stripped + compset -P '(\\|){' || pre=( -p '{' ) + compset -S '(\\|)}*' || suf=( -S "$bs}" ) + ansi=( + black 30 + red 31 + green 32 + yellow 33 + blue 34 + magenta 35 + cyan 36 + white 37 + default 39 + ) + + _description -V ansi-colors expl 'ansi color' + grp="$expl[expl[(i)-V]+1]" + _comp_colors+=( ${(ps.\0.)"$(printf "($grp)=%s=%s\0" ${(kv)ansi})"} ) + compadd "$expl[@]" $suf $pre -k ansi && ret=0 + if (( $#suf )) && compset -P "(<->|%v)"; then + _wanted ansi-colors expl 'closing brace' compadd -S '' \} && ret=0 + elif (( $+terminfo[colors] )); then + (( cols = $terminfo[colors] - 1 )) + (( cols = cols > 255 ? 255 : cols )) + _description -V terminal-colors expl 'terminal color' + grp="$expl[expl[(i)-V]+1]" + compadd "$expl[@]" $suf $pre {0..$cols} + for c in {0..$cols}; do + _comp_colors+=( "($grp)=${c}=${${$(print -P "%F{$c}")#?\[}%m}" ) + done + else + _message -e terminal-colors "number" + fi +fi + +if compset -P '%[0-9-\\]#(\\|)\([0-9-]#[^0-9]'; then + compset -S '*' + _delimiters && ret=0 +elif compset -P '%[0-9-\\]#[<>\]]'; then + _message -e replacements 'replacement string' +elif compset -P '%[0-9-\\]#(\\|)\([0-9-]#'; then + compset -S '[.:+/-%]*' || suf=( -S . ) + compset -S '*' + specs=( + '!:running with privileges' + '#:effective uid' + '?:exit status' + '_:at least n shell constructs started' + 'C:at least n path elements' + '/:at least n path elements' + '.:at least n path elements' + 'c:at least n path elements' + '~:at least n path elements' + 'D:month' + 'd:day of month' + 'g:effective gid' + 'j:number of jobs' + 'L:SHLVL' + 'l:number of characters already printed' + 'S:SECONDS parameter at least n' + 'T:current hour' + 't:current minute' + 'v:psvar has at least n elements' + 'V:element n of psvar is set and non-empty' + 'w:day of week (Sunday = 0)' + ) + [[ $IPREFIX != *- ]] && _describe -t ternary-prompt-expressions \ + 'ternary prompt format test character' specs $suf && ret=0 + _message -e numbers number +elif compset -P '%D(\\|){'; then + compset -S '(\\|)}*' + _date_formats zsh && ret=0 +elif [[ -prefix '%' ]] || + ! zstyle -t ":completion:${curcontext}:prompt-format-specifiers" prefix-needed +then + specs=( + 'm:hostname up to first .' + '_:status of parser' + '^:reversed status of parser' + 'd:current working directory' + '/:current working directory' + '~:current working directory, with ~ replacement' + 'N:name of current script or shell function' + 'x:name of file containing code being executed' + 'c:deprecated' + '.:deprecated' + 'C:deprecated' + 'F:start using fg color' + 'K:start using bg color' + 'G:counts as extra character inside %{...%}' + '(:ternary expression %(x.true-string.false-string)' + ) + compset -P '%' || pre=( -p '%' ) + if ! compset -P '(-|)<->'; then + if [[ $service == -value-,SPROMPT,* ]]; then + specs+=( + 'r:suggested correction' + 'R:corrected string' + ) + fi + specs+=( + '%:A %' + '):A )' + 'l:current line (tty) with /dev/tty stripped' + 'M:full hostname' + 'n:username' + 'y:current line (tty)' + '#:a # when root, % otherwise' + '?:return status of last command' + 'h:current history event number' + '!:current history event number' + 'i:current line number' + 'I:current source line number' + 'j:number of jobs' + 'L:$SHLVL' + 'D:date in yy-mm-dd format' + 'T:current time of day, 24-hour format' + 't:current time of day, 12-hour am/pm format' + '@:current time of day, 12-hour am/pm format' + '*:current time of day, 24-hour format with seconds' + 'w:the date in day-dd format' + 'W:the date in mm/dd/yy format' + 'D{:format string like strftime' + 'B:start bold' + 'b:stop bold' + 'E:clear to end of line' + 'U:start underline' + 'u:stop underline' + 'S:start standout' + 's:stop standout' + 'f:reset fg color' + 'k:reset bg color' + '{:start literal escape sequence' + '}:stop literal escape sequence' + 'v:value from $psvar array' + '<:truncation from left %len<string<' + '>:truncation from right %len>string>' + '[:truncation from who knows where' + ) + fi + _describe -t prompt-format-specifiers 'prompt format specifier' \ + specs -S '' $pre && ret=0 + (( ! $#pre )) && _message -e prompt-format-specifiers number +fi + +return ret diff --git a/Completion/compinit b/Completion/compinit index 9470c92f6..4b9a77853 100644 --- a/Completion/compinit +++ b/Completion/compinit @@ -142,6 +142,7 @@ _comp_options=( NO_cshnullglob NO_cshjunkiequotes NO_errexit + NO_globassign NO_globsubst NO_histsubstpattern NO_ignorebraces @@ -151,6 +152,7 @@ _comp_options=( NO_kshtypeset NO_markdirs NO_octalzeroes + NO_posixbuiltins NO_shwordsplit NO_shglob NO_warncreateglobal diff --git a/Config/version.mk b/Config/version.mk index defcf262e..99a749ebf 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.0.8 -VERSION_DATE='May 31, 2015' +VERSION=5.0.8-test-2 +VERSION_DATE='August 21, 2015' diff --git a/Doc/Zsh/.vimrc b/Doc/Zsh/.vimrc new file mode 100644 index 000000000..6544cd450 --- /dev/null +++ b/Doc/Zsh/.vimrc @@ -0,0 +1,4 @@ +augroup filetypedetect + autocmd BufRead,BufNewFile **/Doc/Zsh/*.yo set ft=zyodl | source <sfile>:h:h:h/Util/zyodl.vim + "autocmd BufRead,BufNewFile **/Etc/FAQ.yo set ft=zyodl | source <sfile>:h:h:h/Util/zyodl.vim +augroup END diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 1fcc7c2b7..5bbe7e70b 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -662,8 +662,8 @@ findex(fc) cindex(history, editing) cindex(editing history) redef(SPACES)(0)(tt(ifztexi(NOTRANS(@ @ @ @ @ @ ))ifnztexi( ))) -xitem(tt(fc) [ tt(-e) var(ename) ] [-L] [ tt(-m) var(match) ] [ var(old)tt(=)var(new) ... ] [ var(first) [ var(last) ] ]) -xitem(tt(fc -l )[ tt(-LnrdfEiD) ] [ tt(-t) var(timefmt) ] [ tt(-m) var(match) ]) +xitem(tt(fc) [ tt(-e) var(ename) ] [ tt(-LI) ] [ tt(-m) var(match) ] [ var(old)tt(=)var(new) ... ] [ var(first) [ var(last) ] ]) +xitem(tt(fc -l )[ tt(-LI) ] [ tt(-nrdfEiD) ] [ tt(-t) var(timefmt) ] [ tt(-m) var(match) ]) xitem(SPACES()[ var(old)tt(=)var(new) ... ] [ var(first) [ var(last) ] ]) xitem(tt(fc -p )[ tt(-a) ] [ var(filename) [ var(histsize) [ var(savehistsize) ] ] ]) xitem(tt(fc) tt(-P)) @@ -674,31 +674,24 @@ shell is interactive. Usually this is detected automatically, but it can be forced by setting the tt(interactive) option when starting the shell. -Select a range of commands from var(first) to var(last) from the -history list. -The arguments var(first) and var(last) may be specified as a -number or as a string. A negative number is used as an offset -to the current history event number. -A string specifies the most recent event beginning with the given string. -All substitutions var(old)tt(=)var(new), if any, are then performed -on the commands. +The first two forms of this command select a range of events from +var(first) to var(last) from the history list. The arguments var(first) +and var(last) may be specified as a number or as a string. A negative +number is used as an offset to the current history event number. A string +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. -If the tt(-L) flag is given, only the local history is considered (see +In addition to the 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 tt(SHARE_HISTORY) in ifzman(zmanref(zshoptions))\ -ifnzman(noderef(Description of Options))). -If the tt(-m) flag is given, the first argument is taken as a -pattern (should be quoted) and only the history events matching this -pattern are considered. - -When the tt(-l) flag is given, the resulting commands are listed on -standard output. -Otherwise the editor program var(ename) is invoked on a file containing -these history events. If var(ename) is not given, the value -of the parameter tt(FCEDIT) is used; if that is not set the value of the -parameter tt(EDITOR) is used; if that is not set a builtin default, usually -`tt(vi)' is used. If var(ename) is `tt(-)', -no editor is invoked. When editing is complete, the edited -command is executed. +ifnzman(noderef(Description of Options)) -- note that tt($HISTFILE) is +considered local when read at startup)) +sitem(tt(-m))(takes the first argument as a pattern (should be quoted) and +only the history events matching this pattern are considered) +endsitem() If var(first) is not specified, it will be set to -1 (the most recent event), or to -16 if the tt(-l) flag is given. @@ -708,32 +701,39 @@ However, if the current event has added entries to the history with `tt(print -s)' or `tt(fc -R)', then the default var(last) for tt(-l) includes all new history entries since the current event began. -The flag tt(-r) reverses the order of the commands and the -flag tt(-n) suppresses command numbers when listing. +When the tt(-l) flag is given, the resulting events are listed on +standard output. Otherwise the editor program var(ename) is invoked on a +file containing these history events. If var(ename) is not given, the +value of the parameter tt(FCEDIT) is used; if that is not set the value of +the parameter tt(EDITOR) is used; if that is not set a builtin default, +usually `tt(vi)' is used. If var(ename) is `tt(-)', no editor is invoked. +When editing is complete, the edited command is executed. + +The flag tt(-r) reverses the order of the events and the +flag tt(-n) suppresses event numbers when listing. Also when listing, startsitem() -sitem(tt(-d))(prints timestamps for each command) +sitem(tt(-d))(prints timestamps for each event) sitem(tt(-f))(prints full time-date stamps in the US -`var(MM)tt(/)var(DD)tt(/)var(YY) var(hh):var(mm)' format) +`var(MM)tt(/)var(DD)tt(/)var(YY) var(hh)tt(:)var(mm)' format) sitem(tt(-E))(prints full time-date stamps in the European -`var(dd)tt(.)var(mm)tt(.)var(yyyy) var(hh):var(mm)' format) +`var(dd)tt(.)var(mm)tt(.)var(yyyy) var(hh)tt(:)var(mm)' format) sitem(tt(-i))(prints full time-date stamps in ISO8601 -`var(yyyy)tt(-)var(mm)tt(-)var(dd) var(hh):var(mm)' format) +`var(yyyy)tt(-)var(mm)tt(-)var(dd) var(hh)tt(:)var(mm)' format) sitem(tt(-t) var(fmt))(prints time and date stamps in the given format; var(fmt) is formatted with the strftime function with the zsh extensions described for the tt(%D{)var(string)tt(}) prompt format in ifzman(the section EXPANSION OF PROMPT SEQUENCES in zmanref(zshmisc))\ ifnzman(noderef(Prompt Expansion)). The resulting formatted string must be -no more than 256 characters or will not be printed. +no more than 256 characters or will not be printed ) sitem(tt(-D))(prints elapsed times; may be combined with one of the -options above.) +options above) endsitem() cindex(history, stack) cindex(stack, history) - `tt(fc -p)' pushes the current history list onto a stack and switches to a new history list. If the tt(-a) option is also specified, this history list will be automatically popped when the current function scope is exited, which @@ -791,11 +791,18 @@ 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) ] [ var(name) ... ]) +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 -M) [ tt(-m) var(pattern) ... ]) item(tt(functions +M) [ tt(-m) ] var(mathfn) ... )( -Equivalent to tt(typeset -f), with the exception of the tt(-M) option. +Equivalent to tt(typeset -f), with the exception of the tt(-x) and +tt(-M) 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. + Use of the tt(-M) option may not be combined with any of the options handled by tt(typeset -f). @@ -1106,7 +1113,7 @@ tt(popd) that do not change the environment seen by an interactive user. ) findex(print) xitem(tt(print )[ tt(-abcDilmnNoOpPrsSz) ] [ tt(-u) var(n) ] [ tt(-f) var(format) ] [ tt(-C) var(cols) ]) -item(SPACES()[ tt(-R) [ tt(-en) ]] [ var(arg) ... ])( +item(SPACES()[ tt(-xX) var(tab-stop) ] [ tt(-R) [ tt(-en) ]] [ var(arg) ... ])( With the `tt(-f)' option the arguments are printed as described by tt(printf). With no flags or with the flag `tt(-)', the arguments are printed on the standard output as described by tt(echo), with the following differences: @@ -1201,6 +1208,27 @@ tt(HIST_LEX_WORDS) option active. item(tt(-u) var(n))( Print the arguments to file descriptor var(n). ) +item(tt(-x) var(tab-stop))( +Expand leading tabs on each line of output in the printed string +assuming a tab stop every var(tab-stop) characters. This is appropriate +for formatting code that may be indented with tabs. Note that leading +tabs of any argument to print, not just the first, are expanded, even if +tt(print) is using spaces to separate arguments (the column count +is maintained across arguments but may be incorrect on output +owing to previous unexpanded tabs). + +The start of the output of each print command is assumed to be aligned +with a tab stop. Widths of multibyte characters are handled if the +option tt(MULTIBYTE) is in effect. This option is ignored if other +formatting options are in effect, namely column alignment or +tt(printf) style, or if output is to a special location such as shell +history or the command line editor. +) +item(tt(-X) var(tab-stop))( +This is similar to tt(-x), except that all tabs in the printed string +are expanded. This is appropriate if tabs in the arguments are +being used to produce a table format. +) item(tt(-z))( Push the arguments onto the editing buffer stack, separated by spaces. ) @@ -1699,10 +1727,10 @@ cindex(parameters, declaring) redef(SPACES)(0)(tt(ifztexi(NOTRANS(@ @ @ @ @ @ @ @ ))ifnztexi( ))) xitem(tt(typeset )[ {tt(PLUS())|tt(-)}tt(AHUaghlmprtux) ] \ [ {tt(PLUS())|tt(-)}tt(EFLRZi) [ var(n) ] ]) -xitem(SPACES()[ tt(+) | var(name)[tt(=)var(value)] ... ]) +xitem(SPACES()[ tt(+) ] [ var(name)[tt(=)var(value)] ... ]) xitem(tt(typeset )tt(-T) [ {tt(PLUS())|tt(-)}tt(Uglprux) ] [ {tt(PLUS())|tt(-)}tt(LRZ) [ var(n) ] ]) -xitem(SPACES()[ tt(+) | var(SCALAR)[tt(=)var(value)] var(array) [ var(sep) ] ]) -item(tt(typeset) tt(-f) [ {tt(PLUS())|tt(-)}tt(TUkmtuz) ] [ tt(+) | var(name) ... ])( +xitem(SPACES()[ tt(+) | var(SCALAR)[tt(=)var(value)] var(array)[tt(=LPAR())var(value) ...tt(RPAR())] [ var(sep) ] ]) +item(tt(typeset) tt(-f) [ {tt(PLUS())|tt(-)}tt(TUkmtuz) ] [ tt(+) ] [ var(name) ... ])( Set or display attributes and values for shell parameters. A parameter is created for each var(name) that does not already refer @@ -1715,22 +1743,72 @@ ifnzman(noderef(Local Parameters))\ retain their special attributes when made local. For each var(name)tt(=)var(value) assignment, the parameter -var(name) is set to var(value). Note that arrays currently cannot be -assigned in tt(typeset) expressions, only scalars and integers. Unless -the option tt(KSH_TYPESET) is set, normal expansion rules apply to -assignment arguments, so var(value) may be split into separate words; if -the option is set, assignments which can be recognised when expansion is -performed are treated as single words. For example the command -tt(typeset vbl=$(echo one two)) is treated as having one argument if -tt(KSH_TYPESET) is set, but otherwise is treated as having the two arguments -tt(vbl=one) and tt(two). +var(name) is set to var(value). All forms of the command +handle scalar assignment. + +If any of the reserved words tt(declare), tt(export), tt(float), +tt(integer), tt(local), tt(readonly) or tt(typeset) is matched when the +line is parsed (N.B. not when it is executed) the shell will try to parse +arguments as assignments, except that the `tt(+=)' syntax and the +tt(GLOB_ASSIGN) option are not supported. This has two major differences +from normal command line argument parsing: array assignment is possible, +and scalar values after tt(=) are not split further into words even if +expanded (regardless of the setting of the tt(KSH_TYPESET) option; this +option is obsolete). Here is an example: + +example(# Reserved word parsing +typeset svar=$(echo one word) avar=(several words)) + +The above creates a scalar parameter tt(svar) and an array +parameter tt(avar) as if the assignments had been + +example(svar="one word" +avar=(several words)) + +On the other hand: + +example(# Normal builtin interface +builtin typeset svar=$(echo two words)) + +The tt(builtin) keyword causes the above to use the standard builtin +interface to tt(typeset) in which argument parsing is perfomed in the same +way as for other commands. This example creates a scalar tt(svar) +containing the value tt(two) and another scalar parameter tt(words) with +no value. An array value in this case would either cause an error or be +treated as an obscure set of glob qualifiers. + +Arbitrary arguments are allowed if they take the form of assignments +after command line expansion; however, these only perform scalar +assignment: + +example(var='svar=val' +typeset $var) + +The above sets the scalar parameter tt(svar) to the value tt(val). +Parentheses around the value within tt(var) would not cause array +assignment as they will be treated as ordinary characters when tt($var) +is substituted. Any non-trivial expansion in the name part of the +assignment causes the argument to be treated in this fashion: + +example(typeset {var1,var2,var3}=name) + +The above syntax is valid, and has the expected effect of setting the +three parameters to the same value, but the command line is parsed as +a set of three normal command line arguments to tt(typeset) after +expansion. Hence it is not possible to assign to multiple arrays by +this means. + +Note that each interface to any of the commands my be disabled +separately. For example, `tt(disable -r typeset)' disables the reserved +word interface to tt(typeset), exposing the builtin interface, while +`tt(disable typeset)' disables the builtin. If the shell option tt(TYPESET_SILENT) is not set, for each remaining -var(name) that refers to a parameter that is set, the name and value of the -parameter are printed in the form of an assignment. Nothing is printed for -newly-created parameters, or when any attribute flags listed below are -given along with the var(name). Using `tt(PLUS())' instead of minus to -introduce an attribute turns it off. +var(name) that refers to a parameter that is already set, the name and +value of the parameter are printed in the form of an assignment. +Nothing is printed for newly-created parameters, or when any attribute +flags listed below are given along with the var(name). Using +`tt(PLUS())' instead of minus to introduce an attribute turns it off. If no var(name) is present, the names and values of all parameters are printed. In this case the attribute flags restrict the display to only @@ -1801,7 +1879,7 @@ 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) [ var(sep) ] ])( +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. Otherwise the tt(-T) option requires zero, two, or three arguments to be present. With no arguments, the list of parameters created in this @@ -1811,10 +1889,13 @@ together in the manner of tt($PATH) and tt($path). The optional third argument is a single-character separator which will be used to join the elements of the array to form the scalar; if absent, a colon is used, as with tt($PATH). Only the first character of the separator is significant; -any remaining characters are ignored. +any remaining characters are ignored. Multibyte characters are not +yet supported. + +Only one of the scalar and array parameters may be assigned an initial +value (the restrictions on assignment forms described above also apply). -Only the scalar parameter may be assigned an initial value. Both the -scalar and the array may otherwise be manipulated as normal. If one is +Both the scalar and the array may be manipulated as normal. If one is unset, the other will automatically be unset too. There is no way of untying the variables without unsetting them, nor of converting the type of one of them with another tt(typeset) command; tt(+T) does not work, @@ -1906,6 +1987,9 @@ function is first referenced; see noderef(Functions). The tt(-k) and tt(-z) flags make the function be loaded using ksh-style or zsh-style autoloading respectively. If neither is given, the setting of the tt(KSH_AUTOLOAD) option determines how the function is loaded. + +Note that the builtin tt(functions) provides the same basic capabilities +as tt(typeset -f) but gives access to a few extra options. ) item(tt(-h))( Hide: only useful for special parameters (those marked `<S>' in the table in @@ -2159,7 +2243,7 @@ the user is potentially interested in both, so this problem is intrinsic to process IDs. ) findex(whence) -item(tt(whence) [ tt(-vcwfpamsS) ] var(name) ...)( +item(tt(whence) [ tt(-vcwfpamsS) ] [ tt(-x) var(num) ] var(name) ...)( For each var(name), indicate how it would be interpreted if used as a command name. @@ -2212,14 +2296,19 @@ As tt(-s), but if the pathname had to be resolved by following multiple symlinks, the intermediate steps are printed, too. The symlink resolved at each step might be anywhere in the path. ) +item(tt(-x) var(num))( +Expand tabs when outputting shell functions using the tt(-c) option. +This has the same effect as the tt(-x) option to the tt(functions) +builtin. +) enditem() ) findex(where) -item(tt(where) [ tt(-wpmsS) ] var(name) ...)( +item(tt(where) [ tt(-wpmsS) ] [ tt(-x) var(num) ] var(name) ...)( Equivalent to tt(whence -ca). ) findex(which) -item(tt(which) [ tt(-wpamsS) ] var(name) ...)( +item(tt(which) [ tt(-wpamsS) ] [ tt(-x) var(num) ] var(name) ...)( Equivalent to tt(whence -c). ) findex(zcompile) diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo index a081ea32c..d067795dd 100644 --- a/Doc/Zsh/compsys.yo +++ b/Doc/Zsh/compsys.yo @@ -3530,37 +3530,109 @@ xitem(SPACES()[ tt(:) ] var(spec) ...) item(tt(_arguments) [ var(opt) ... ] tt(-)tt(-) [ tt(-i) var(pats) ] [ tt(-s) var(pair) ] [ var(helpspec) ... ])( This function can be used to give a complete specification for completion for a command whose arguments follow standard UNIX option and argument -conventions. Options to tt(_arguments) itself must be in separate words, -i.e. tt(-s -w), not tt(-sw). +conventions. -When calling tt(_arguments), all var(spec)s that describe options of the -analyzed command line must precede all var(spec)s that describe non-option -(aka "normal") arguments of the analyzed line. To avoid ambiguity, all +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 options to tt(_arguments) itself may be separated from the var(spec) forms by a single colon. -The tt(-s -w -W -A) and tt(-S) options describe how parsing of the command -line should proceed, and are discussed in context below. The `tt(-)tt(-)' +The `tt(-)tt(-)' form is used to intuit var(spec) forms from the help output of the command being analyzed, and is described in detail below. The var(opts) for the `tt(-)tt(-)' form are otherwise the same options as the first form. Note that `tt(-s)' following `tt(-)tt(-)' has a distinct meaning from `tt(-s)' preceding `tt(-)tt(-)', and both may appear. -With the option tt(-n), tt(_arguments) sets the parameter tt(NORMARG) +The option switches tt(-s), tt(-S), tt(-A), tt(-w), and tt(-W) affect how +tt(_arguments) parses the analyzed command line's options. These switches are +useful for commands with standard argument parsing. + +The options of tt(_arguments) have the following meanings: + +startitem() +item(tt(-n))( +With this option, tt(_arguments) sets the parameter tt(NORMARG) to the position of the first normal argument in the tt($words) array, i.e. the position after the end of the options. If that argument has not been reached, tt(NORMARG) is set to tt(-1). The caller should declare `tt(integer NORMARG)' if the tt(-n) option is passed; otherwise the parameter is not used. +) +item(tt(-s))( +Enable em(option stacking) for single-letter options, whereby multiple +single-letter options may be combined into a single word. For example, +the two options `tt(-x)' and `tt(-y)' may be combined into +a single word `tt(-xy)'. By default, every word corresponds to a single +option name (`tt(-xy)' is a single option named `tt(xy)'). + +Options beginning with a single hyphen or plus sign are eligible for stacking; +words beginning with two hyphens are not. + +Note that tt(-s) after tt(-)tt(-) has a different meaning, which is documented +in the segment entitled `Deriving var(spec) forms from the help output'. +) +item(tt(-w))( +In combination with tt(-s), allow option stacking +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. +) +item(tt(-W))( +This option takes tt(-w) a stage further: it is possible to +complete single-letter options even after an argument that occurs in the +same word. However, it depends on the action performed whether options +will really be completed at this point. For more control, use a +utility function like tt(_guard) as part of the action. +) +item(tt(-C))( +Modify the tt(curcontext) parameter for an action of the form `tt(->)var(state)'. +This is discussed in detail below. +) +item(tt(-R))( +Return status 300 instead of zero when a tt($state) is to +be handled, in the `tt(->)var(string)' syntax. +) +item(tt(-S))( +Do not complete options after a `tt(-)tt(-)' appearing on the line, +and ignore the `tt(-)tt(-)'. For example, with tt(-S), in the line -The option `tt(-M) var(matchspec)' sets a match specification to use to -completion option names and values. The default var(matchspec) is: +example(foobar -x -- -y) +the `tt(-x)' is considered an option, the `tt(-y)' is considered an +argument, and the `tt(-)tt(-)' is considered to be neither. +) +item(tt(-A) var(pat))( +Do not complete options after the first non-option +argument on the line. var(pat) is a pattern matching +all strings which are not to be taken as arguments. For example, to make +tt(_arguments) stop completing options after the first normal argument, but +ignoring all strings starting with a hyphen even if they are not described +by one of the var(optspec)s, the form is `tt(-A "-*")'. +) +item(tt(-O) var(name))( +Pass the elements of the array var(name) as arguments to functions called to +execute var(action)s. +This is discussed in detail below. +) +item(tt(-M) var(matchspec))( +Use the match specification var(matchspec) for completing option names and values. +The default var(matchspec) allows partial word completion after `tt(_)' and +`tt(-)', such as completing `tt(-f-b)' to `tt(-foo-bar)'. The default +var(matchspec) is: example(tt(r:|[_-]=* r:|=*)) +) +enditem() -This allows partial word completion after `tt(_)' and `tt(-)', for example -`tt(-f-b)' can be completed to `tt(-foo-bar)'. +em(var(spec)s: overview) Each of the following forms is a var(spec) describing individual sets of options or arguments on the command line being analyzed. @@ -3601,26 +3673,6 @@ This describes an option. The colon indicates handling for one or more arguments to the option; if it is not present, the option is assumed to take no arguments. -By default, options are multi-character name, one `tt(-)var(word)' per -option. With tt(-s), options may be single characters, with more than -one option per word, although words starting with two hyphens, such as -`tt(-)tt(-prefix)', are still considered complete option names. This is -suitable for standard GNU options. - -The combination of tt(-s) with tt(-w) allows single-letter options to be -combined in a single word 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. - -The option tt(-W) takes this a stage further: it is possible to -complete single-letter options even after an argument that occurs in the -same word. However, it depends on the action performed whether options -will really be completed at this point. For more control, use a -utility function like tt(_guard) as part of the action. - The following forms are available for the initial var(optspec), whether or not the option has arguments. @@ -3682,23 +3734,6 @@ enditem() It is possible for options with a literal `tt(PLUS())' or `tt(=)' to appear, but that character must be quoted, for example `tt(-\+)'. -The options tt(-S) and tt(-A) are available to simplify the specifications -for commands with standard option parsing. With tt(-S), no option will be -completed after a `tt(-)tt(-)' appearing on its own on the line; this -argument will otherwise be ignored; hence in the line - -example(foobar -x -- -y) - -the `tt(-x)' is considered an option but the `tt(-y)' is considered an -argument, while the `tt(-)tt(-)' is considered to be neither. - -With tt(-A), no options will be completed after the first non-option -argument on the line. The tt(-A) must be followed by a pattern matching -all strings which are not to be taken as arguments. For example, to make -tt(_arguments) stop completing options after the first normal argument, but -ignoring all strings starting with a hyphen even if they are not described -by one of the var(optspec)s, the form is `tt(-A "-*")'. - Each var(optarg) following an var(optspec) must take one of the following forms: @@ -3719,7 +3754,7 @@ xitem(tt(:*)var(pattern)tt(::)var(message)tt(:)var(action)) item(tt(:*)var(pattern)tt(:::)var(message)tt(:)var(action))( This describes multiple arguments. Only the last var(optarg) for an option taking multiple arguments may be -given in this form. If the var(pattern) is empty (i.e., tt(:*:)), all +given in this form. If the var(pattern) is empty (i.e. tt(:*:)), all the remaining words on the line are to be completed as described by the var(action); otherwise, all the words up to and including a word matching the var(pattern) are to be completed using the var(action). @@ -3766,15 +3801,17 @@ given by an array, and tt(_arguments) is called repeatedly for more specific contexts: on the first call `tt(_arguments $global_options)' is used, and on subsequent calls `tt(_arguments !$^global_options)'. +em(var(spec)s: actions) + In each of the forms above the var(action) determines how completions should be generated. Except for the `tt(->)var(string)' 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 option `tt(-O) var(name)' specifies the name of an array whose elements -will be passed as arguments to functions called to execute var(action)s. -For example, this can be used to pass the same set of options for the +The functions called to execute var(action)s will be called with the 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. The forms for var(action) are as follows. @@ -3918,6 +3955,8 @@ 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) + 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. @@ -3964,6 +4003,8 @@ 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. +em(Deriving var(spec) forms from the help output) + The option `tt(-)tt(-)' allows tt(_arguments) to work out the names of long options that support the `tt(-)tt(-help)' option which is standard in many GNU commands. The command word is called with the argument @@ -4036,6 +4077,8 @@ as `tt(-)tt(-enable-foo)', but the script also accepts the negated form example(_arguments -- -s "LPAR()(#s)--enable- --disable-RPAR()") +em(Miscellaneous notes) + Finally, note that tt(_arguments) generally expects to be the primary function handling any completion for which it is used. It may have side effects which change the treatment of any matches added by other functions @@ -4158,7 +4201,7 @@ the functions for the fields if they are called. 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) ... ]) -item(SPACES()[ tt(-)tt(-) var(descr) var(name1) [ var(name2) ] [ var(opt) ... ] ... ])( +item(SPACES()[ tt(-)tt(-) var(name1) [ var(name2) ] [ var(opt) ... ] ... ])( This function associates completions with descriptions. Multiple groups separated by tt(-)tt(-) can be supplied, potentially with different completion options var(opt)s. diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo index 0c0a15d41..40cabea88 100644 --- a/Doc/Zsh/compwid.yo +++ b/Doc/Zsh/compwid.yo @@ -697,7 +697,9 @@ format completion lists and to make explanatory string be shown in completion lists (since empty matches can be given display strings with the tt(-d) option). And because all but one empty string would otherwise be removed, this option implies the tt(-V) and tt(-2) -options (even if an explicit tt(-J) option is given). +options (even if an explicit tt(-J) option is given). This can be +important to note as it affects the name space into which matches are +added. ) xitem(tt(-)) item(tt(-)tt(-))( diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index 8b6b7d3b7..4e87d4116 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -756,7 +756,7 @@ revision number. This style lets you modify how that string should look. ) kindex(nvcsformats) item(tt(nvcsformats))( -These "formats" are exported when we didn't detect a version control system +These "formats" are set when we didn't detect a version control system for the current directory or tt(vcs_info) was disabled. This is useful if you want tt(vcs_info) to completely take over the generation of your prompt. You would do something like tt(PS1='${vcs_info_msg_0_}') to @@ -775,7 +775,7 @@ behavior using hooks. kindex(max-exports) item(tt(max-exports))( Defines the maximum number of -tt(vcs_info_msg_*_) variables tt(vcs_info) will export. +tt(vcs_info_msg_*_) variables tt(vcs_info) will set. ) kindex(enable) item(tt(enable))( @@ -1224,16 +1224,15 @@ All functions named tt(VCS_INFO_*) are for internal use only. subsect(Variable Description) startitem() -item(tt(${vcs_info_msg_)var(N)tt(_)}) (Note the trailing underscore)) -( +item(tt(${vcs_info_msg_)var(N)tt(_}) (Note the trailing underscore))( Where var(N) is an integer, e.g., tt(vcs_info_msg_0_). These variables are the storage for the informational message the last tt(vcs_info) call has assembled. These are strongly connected to the tt(formats), tt(actionformats) and tt(nvcsformats) styles described above. Those styles are lists. The first member of that list gets expanded into tt(${vcs_info_msg_0_}), the second into tt(${vcs_info_msg_1_}) -and the Nth into tt(${vcs_info_msg_N-1_}). These parameters are -exported into the environment. (See the tt(max-exports) style above.) +and the Nth into tt(${vcs_info_msg_N-1_}). (See the tt(max-exports) +style above.) ) enditem() @@ -2272,7 +2271,8 @@ tindex(narrow-to-region) tindex(narrow-to-region-invisible) redef(SPACES)(0)(tt(ifztexi(NOTRANS(@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ ))ifnztexi( ))) xitem(tt(narrow-to-region )[ tt(-p) var(pre) ] [ tt(-P) var(post) ]) -xitem(SPACES()[ tt(-S) var(statepm) | tt(-R) var(statepm) ] [ tt(-n) ] [ var(start) var(end) ]) +xitem(SPACES()[ tt(-S) var(statepm) | tt(-R) var(statepm) | [ tt(-l) var(lbufvar) ] [ tt(-r) var(rbufvar) ] ]) +xitem(SPACES()[ tt(-n) ] [ var(start) var(end) ]) item(tt(narrow-to-region-invisible))( Narrow the editable portion of the buffer to the region between the cursor and the mark, which may be in either order. The region may not be empty. @@ -2308,9 +2308,15 @@ parameter, except that parameters beginning with the prefix tt(_ntr_) are reserved for use within tt(narrow-to-region). Typically the parameter will be local to the calling function. +The options tt(-l) var(lbufvar) and tt(-r) var(rbufvar) may be used to +specify parameters where the widget will store the resulting text from +the operation. The parameter var(lbufvar) will contain tt(LBUFFER) +and var(rbufvar) will contain tt(RBUFFER). Neither of these two options +may be used with tt(-S) or tt(-R). + tt(narrow-to-region-invisible) is a simple widget which calls tt(narrow-to-region) with arguments which replace any text outside the -region with `tt(...)'. +region with `tt(...)'. It does not take any arguments. The display is restored (and the widget returns) upon any zle command which would usually cause the line to be accepted or aborted. Hence an @@ -3561,7 +3567,7 @@ set to the ANSI terminal escapes that turn off all attributes and turn on bold intensity, respectively. ) findex(fned) -item(tt(fned) var(name))( +item(tt(fned) [ tt(-x) var(num) ] var(name))( Same as tt(zed -f). This function does not appear in the zsh distribution, but can be created by linking tt(zed) to the name tt(fned) in some directory in your tt(fpath). @@ -3749,7 +3755,7 @@ the difference in function between tt(zargs) and tt(xargs)) or run tt(zargs) with the tt(-)tt(-help) option. ) findex(zed) -xitem(tt(zed) [ tt(-f) ] var(name)) +xitem(tt(zed) [ tt(-f) [ tt(-x) var(num) ] ] var(name)) item(tt(zed -b))( This function uses the ZLE editor to edit a file or function. @@ -3758,7 +3764,10 @@ If the tt(-f) option is given, the name is taken to be that of a function; if the function is marked for autoloading, tt(zed) searches for it in the tt(fpath) and loads it. Note that functions edited this way are installed into the current shell, but em(not) written back to the -autoload file. +autoload file. In this case the tt(-x) option specifies that leading +tabs indenting the function according to syntax should be converted into +the given number of spaces; `tt(-x 2)' is consistent with the layout +of functions distributed with the shell. Without tt(-f), var(name) is the path name of the file to edit, which need not exist; it is created on write, if necessary. @@ -3825,7 +3834,12 @@ The pattern is always treated as an tt(EXTENDED_GLOB) pattern. Any file whose name is not changed by the substitution is simply ignored. Any error (a substitution resulted in an empty string, two substitutions gave the same result, the destination was an existing regular file and tt(-f) -was not given) causes the entire function to abort without doing anything. +was not given) causes the entire function to abort without doing +anything. + +In addition to pattern replacement, the variable tt($f) can be referrred +to in the second (replacement) argument. This makes it possible to +use variable substitution to alter the argument; see examples below. Options: @@ -3874,6 +3888,10 @@ example(zmv -v '(* *)' '${1// /_}') For any file in the current directory with at least one space in the name, replace every space by an underscore and display the commands executed. +example(zmv -v '* *' '${f// /_}') + +This does exactly the same by referring to the file name stored in tt($f). + For more complete examples and other implementation details, see the tt(zmv) source file, usually located in one of the directories named in your tt(fpath), or in tt(Functions/Misc/zmv) in the zsh distribution. diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 7d4e6fccb..d44b40a3b 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -319,6 +319,19 @@ forms of expansion. Note that if a `tt(&)' is used within glob qualifiers an extra backslash is needed as a tt(&) is a special character in this case. +Also note that the order of expansions affects the interpretation of +var(l) and var(r). When used in a history expansion, which occurs before +any other expansions, var(l) and var(r) are treated as literal strings +(except as explained for tt(HIST_SUBST_PATTERN) below). When used in +parameter expansion, the replacement of var(r) into the parameter's value +is done first, and then any additional process, parameter, command, +arithmetic, or brace references are applied, which may evaluate those +substitutions and expansions more than once if var(l) appears more than +once in the starting value. When used in a glob qualifier, any +substitutions or expansions are performed once at the time the qualifier +is parsed, even before the `tt(:s)' expression itself is divided into +var(l) and var(r) sides. + If the option tt(HIST_SUBST_PATTERN) is set, var(l) is treated as a pattern of the usual form described in ifzman(the section FILENAME GENERATION below)\ @@ -917,6 +930,25 @@ array index order. Note that `tt(a)' is therefore equivalent to the default but `tt(Oa)' is useful for obtaining an array's elements in reverse order. ) +item(tt(b))( +Quote with backslashes only characters that are special to pattern +matching. This is useful when the contents of the variable are to be +tested using tt(GLOB_SUBST), including the tt(${~)var(...)tt(}) switch. + +Quoting using one of the tt(q) family of flags does not work +for this purpose since quotes are not stripped from non-pattern +characters by tt(GLOB_SUBST). In other words, + +example(pattern=${(q)str} +[[ $str = ${~pattern} ]]) + +works if tt($str) is `tt(a*b)' but not if it is `tt(a b)', whereas + +example(pattern=${(b)str} +[[ $str = ${~pattern} ]]) + +is always true for any possible value of tt($str). +) item(tt(c))( With tt(${#)var(name)tt(}), count the total number of characters in an array, as if the elements were concatenated with spaces between them. This is not @@ -1021,25 +1053,6 @@ form of single quoting is used that only quotes the string if needed to protect special characters. Typically this form gives the most readable output. ) -item(tt(b))( -Quote with backslashes only characters that are special to pattern -matching. This is useful when the contents of the variable are to be -tested using tt(GLOB_SUBST), including the tt(${~)var(...)tt(}) switch. - -Quoting using one of the tt(q) family of flags does not work -for this purpose since quotes are not stripped from non-pattern -characters by tt(GLOB_SUBST). In other words, - -example(pattern=${(q)str} -[[ $str = ${~pattern} ]]) - -works if tt($str) is tt('a*b') but not if it is tt('a b'), whereas - -example(pattern=${(b)str} -[[ $str = ${~pattern} ]]) - -is always true for any possible value of tt($str). -) item(tt(Q))( Remove one level of quotes from the resulting words. ) diff --git a/Doc/Zsh/grammar.yo b/Doc/Zsh/grammar.yo index 4476fc392..83968fedf 100644 --- a/Doc/Zsh/grammar.yo +++ b/Doc/Zsh/grammar.yo @@ -472,7 +472,8 @@ word of a command unless quoted or disabled using tt(disable -r): tt(do done esac then elif else fi for case if while function repeat time until -select coproc nocorrect foreach end ! [[ { }) +select coproc nocorrect foreach end ! [[ { } +declare export float integer local readonly typeset) Additionally, `tt(})' is recognized in any position if neither the tt(IGNORE_BRACES) option nor the tt(IGNORE_CLOSE_BRACES) option is set. diff --git a/Doc/Zsh/mod_system.yo b/Doc/Zsh/mod_system.yo index 7f9c011a7..7f2009b43 100644 --- a/Doc/Zsh/mod_system.yo +++ b/Doc/Zsh/mod_system.yo @@ -28,6 +28,52 @@ system's range), a return status of 1 indicates an error in the parameters, and a return status of 2 indicates the error name was not recognised (no message is printed for this). ) +findex(sysopen) +redef(SPACES)(0)(tt(ifztexi(NOTRANS(@ @ @ @ @ @ @ @ ))ifnztexi( ))) +xitem(tt(sysopen) [ tt(-arw) ] [ tt(-m) var(permissions) ] [ tt(-o) var(options) ]) +item(SPACES()tt(-u) var(fd) var(file))( +This command opens a file. The tt(-r), tt(-w) and tt(-a) flags indicate +whether the file should be opened for reading, writing and appending, +respectively. The tt(-m) option allows the initial permissions to use when +creating a file to be specified in octal form. The file descriptor is +specified with tt(-u). Either an explicit file descriptor in the range 0 to 9 can +be specified or a variable name can be given to which the file descriptor +number will be assigned. + +The tt(-o) option allows various system specific options to be +specified as a comma-separated list. The following is a list of possible +options. Note that, depending on the system, some may not be available. +startitem() +item(tt(cloexec))( +mark file to be closed when other programs are executed +) +xitem(tt(create)) +item(tt(creat))( +create file if it does not exist +) +item(tt(excl))( +create file, error if it already exists +) +item(tt(noatime))( +suppress updating of the file atime +) +item(tt(nofollow))( +fail if var(file) is a symbolic link +) +item(tt(sync))( +request that writes wait until data has been physically written +) +xitem(tt(truncate)) +item(tt(trunc))( +truncate file to size 0 +) +enditem() + +To close the file, use one of the following: + +example(tt(exec {)var(fd)tt(}<&-) +tt(exec {)var(fd)tt(}>&-)) +) findex(sysread) redef(SPACES)(0)(tt(ifztexi(NOTRANS(@ @ @ @ @ @ @ @ ))ifnztexi( ))) xitem(tt(sysread )[ tt(-c) var(countvar) ] [ tt(-i) var(infd) ] [ tt(-o) var(outfd) ]) @@ -89,6 +135,14 @@ usual rules; no write to var(outfd) is attempted. ) enditem() ) +item(tt(sysseek) [ tt(-u) var(fd) ] [ tt(-w) tt(start)|tt(end)|tt(current) ] var(offset))( +The current file position at which future reads and writes will take place is +adjusted to the specified byte offset. The var(offset) is evaluated as a math +expression. The tt(-u) option allows the file descriptor to be specified. By +default the offset is specified relative to the start or the file but, with the +tt(-w) option, it is possible to specify that the offset should be relative to +the current position or the end of the file. +) item(tt(syswrite) [ tt(-c) var(countvar) ] [ tt(-o) var(outfd) ] var(data))( The data (a single string of bytes) are written to the file descriptor var(outfd), or 1 if that is not given, using the tt(write) system call. @@ -161,6 +215,15 @@ version of the shell before it was implemented). ) enditem() +subsect(Math Functions) + +startitem() +item(tt(systell+LPAR()var(fd)RPAR()))( +The systell math function returns the current file position for the file +descriptor passed as an argument. +) +enditem() + subsect(Parameters) startitem() diff --git a/Doc/Zsh/mod_zpty.yo b/Doc/Zsh/mod_zpty.yo index 340f98314..44b375a3c 100644 --- a/Doc/Zsh/mod_zpty.yo +++ b/Doc/Zsh/mod_zpty.yo @@ -18,6 +18,15 @@ characters are echoed. With the tt(-b) option, input to and output from the pseudo-terminal are made non-blocking. + +The shell parameter tt(REPLY) is set to the file descriptor assigned to +the master side of the pseudo-terminal. This allows the terminal to be +monitored with ZLE descriptor handlers (see ifzman(zmanref(zshzle))\ +ifnzman(noderef(Zle Builtins))) or manipulated with tt(sysread) and +tt(syswrite) (see ifzman(THE ZSH/SYSTEM MODULE in zmanref(zshmodules))\ +ifnzman(noderef(The zsh/system Module))). em(Warning): Use of tt(sysread) +and tt(syswrite) is em(not) recommended, use tt(zpty -r) and tt(zpty -w) +unless you know exactly what you are doing. ) item(tt(zpty) tt(-d) [ var(name) ... ])( The second form, with the tt(-d) option, is used to delete commands diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo index 4dd68c9cb..abd0f8715 100644 --- a/Doc/Zsh/options.yo +++ b/Doc/Zsh/options.yo @@ -124,7 +124,7 @@ pindex(POSIXCD) pindex(NO_POSIX_CD) pindex(NOPOSIXCD) cindex(CDPATH, order of checking) -item(tt(POSIX_CD))( +item(tt(POSIX_CD) <K> <S>)( Modifies the behaviour of tt(cd), tt(chdir) and tt(pushd) commands to make them more compatible with the POSIX standard. The behaviour with the option unset is described in the documentation for the tt(cd) @@ -634,7 +634,7 @@ pindex(NO_MULTIBYTE) pindex(NOMULTIBYTE) cindex(characters, multibyte, in expansion and globbing) cindex(multibyte characters, in expansion and globbing) -item(tt(MULTIBYTE) <C> <K> <Z>)( +item(tt(MULTIBYTE) <D>)( Respect multibyte characters when found in strings. When this option is set, strings are examined using the system library to determine how many bytes form a character, depending @@ -642,10 +642,8 @@ on the current locale. This affects the way characters are counted in pattern matching, parameter values and various delimiters. The option is on by default if the shell was compiled with -tt(MULTIBYTE_SUPPORT) except in tt(sh) emulation; otherwise it is off by -default and has no effect if turned on. The mode is off in tt(sh) -emulation for compatibility but for interactive use may need to be -turned on if the terminal interprets multibyte characters. +tt(MULTIBYTE_SUPPORT); otherwise it is off by default and has no effect +if turned on. If the option is off a single byte is always treated as a single character. This setting is designed purely for examining strings @@ -1096,10 +1094,12 @@ pindex(NOCLOBBER) cindex(clobbering, of files) cindex(file clobbering, allowing) item(tt(CLOBBER) (tt(PLUS()C), ksh: tt(PLUS()C)) <D>)( -Allows `tt(>)' redirection to truncate existing files, -and `tt(>>)' to create files. -Otherwise `tt(>!)' or `tt(>|)' must be used to truncate a file, -and `tt(>>!)' or `tt(>>|)' to create a file. +Allows `tt(>)' redirection to truncate existing files. +Otherwise `tt(>!)' or `tt(>|)' must be used to truncate a file. + +If the option is not set, and the option tt(APPEND_CREATE) is also +not set, `tt(>>!)' or `tt(>>|)' must be used to create a file. +If either option is set, `tt(>>)' may be used. ) pindex(CORRECT) pindex(NO_CORRECT) @@ -1794,6 +1794,21 @@ enditem() subsect(Shell Emulation) startitem() +pindex(APPEND_CREATE) +pindex(NO_APPEND_CREATE) +pindex(APPENDCREATE) +pindex(NOAPPENDCREATE) +cindex(clobbering, POSIX compatibility) +cindex(file clobbering, POSIX compatibility) +cindex(no clobber, POSIX compatible) +item(tt(APPEND_CREATE) <K> <S>)( +This option only applies when tt(NO_CLOBBER) (-tt(C)) is in effect. + +If this option is not set, the shell will report an error when a +append redirection (tt(>>)) is used on a file that does not already +exists (the traditional zsh behaviour of tt(NO_CLOBBER)). If the option +is set, no error is reported (POSIX behaviour). +) pindex(BASH_REMATCH) pindex(NO_BASH_REMATCH) pindex(BASHREMATCH) @@ -1930,7 +1945,13 @@ pindex(KSHTYPESET) pindex(NOKSHTYPESET) cindex(argument splitting, in typeset etc.) cindex(ksh, argument splitting in typeset) -item(tt(KSH_TYPESET) <K>)( +item(tt(KSH_TYPESET))( +This option is now obsolete: a better appropximation to the behaviour of +other shells is obtained with the reserved word interface to +tt(declare), tt(export), tt(float), tt(integer), tt(local), tt(readonly) +and tt(typeset). Note that the option is only applied when the reserved +word interface is em(not) in use. + Alters the way arguments to the tt(typeset) family of commands, including tt(declare), tt(export), tt(float), tt(integer), tt(local) and tt(readonly), are processed. Without this option, zsh will perform normal @@ -2057,7 +2078,7 @@ of shell parameters and modules). In addition, setting this option limits the effect of parameter substitution with no braces, so that the expression tt($#) is treated as the parameter tt($#) even if followed by a valid parameter name. -When it is unset, zsh allows expresions of the form tt($#)var(name) +When it is unset, zsh allows expressions of the form tt($#)var(name) to refer to the length of tt($)var(name), even for special variables, for example in expressions such as tt($#-) and tt($#*). diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index eb3eb367e..a4067883a 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -1642,6 +1642,29 @@ item(tt(ZDOTDIR))( The directory to search for shell startup files (.zshrc, etc), if not tt($HOME). ) +vindex(zle_bracketed_paste) +cindex(bracketed paste) +cindex(enabling bracketed paste) +item(tt(zle_bracketed_paste))( +Many terminal emulators have a feature that allows applications to +identify when text is pasted into the terminal rather than being typed +normally. For ZLE, this means that special characters such as tabs +and newlines can be inserted instead of invoking editor commands. +Furthermore, pasted text forms a single undo event and if the region is +active, pasted text will replace the region. + +This two-element array contains the terminal escape sequences for +enabling and disabling the feature. These escape sequences are used to +enable bracketed paste when ZLE is active and disable it at other times. +Unsetting the parameter has the effect of ensuring that bracketed paste +remains disabled. +) +vindex(zle_highlight) +item(tt(zle_highlight))( +An array describing contexts in which ZLE should highlight the input text. +See ifzman(em(Character Highlighting) in zmanref(zshzle))\ +ifnzman(noderef(Character Highlighting)). +) vindex(ZLE_LINE_ABORTED) item(tt(ZLE_LINE_ABORTED))( This parameter is set by the line editor when an error occurs. It diff --git a/Doc/Zsh/prompt.yo b/Doc/Zsh/prompt.yo index 307587678..3c8f2a094 100644 --- a/Doc/Zsh/prompt.yo +++ b/Doc/Zsh/prompt.yo @@ -92,6 +92,10 @@ print as many as there are. This is most useful in prompts tt(PS2) for continuation lines and tt(PS4) for debugging with the tt(XTRACE) option; in the latter case it will also work non-interactively. ) +item(tt(%^))( +The status of the parser in reverse. This is the same as `tt(%_)' other than +the order of strings. It is often used in tt(RPS2). +) xitem(tt(%d)) item(tt(%/))( Current working directory. If an integer follows the `tt(%)', @@ -202,9 +206,10 @@ The GNU extension that a `tt(-)' between the tt(%) and the format character causes a leading zero or space to be stripped is handled directly by the shell for the format characters tt(d), tt(f), tt(H), tt(k), tt(l), tt(m), tt(M), tt(S) and tt(y); any other format -characters are provided to tt(strftime+LPAR()RPAR()) with any leading `tt(-)', -present, so the handling is system dependent. Further GNU -extensions are not supported at present. +characters are provided to the system's strftime+LPAR()3RPAR() +with any leading `tt(-)' present, so the handling is system dependent. +Further GNU (or other) extensions are also passed to strftime+LPAR()3RPAR() +and may work if the system supports them. ) enditem() diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo index 16d661f06..c06e2260e 100644 --- a/Doc/Zsh/zle.yo +++ b/Doc/Zsh/zle.yo @@ -29,6 +29,7 @@ line editor. See ifzman(em(Parameters Used By The Shell) in zmanref(zshparam))\ ifnzman(noderef(Parameters Used By The Shell)). +vindex(zle_highlight, use of) The parameter tt(zle_highlight) is also used by the line editor; see ifzman(em(Character Highlighting) below)\ ifnzman(noderef(Character Highlighting)). Highlighting @@ -960,6 +961,24 @@ A number representing the state of the undo history. The only use of this is passing as an argument to the tt(undo) widget in order to undo back to the recorded point. Read-only. ) +vindex(UNDO_LIMIT_NO) +item(tt(UNDO_LIMIT_NO) (integer))( +A number corresponding to an existing change in the undo history; +compare tt(UNDO_CHANGE_NO). If this is set to a value greater +than zero, the tt(undo) command will not allow the line to +be undone beyond the given change number. It is still possible +to use `tt(zle undo) var(change)' in a widget to undo beyond +that point; in that case, it will not be possible to undo at +all until tt(UNDO_LIMIT_NO) is reduced. Set to 0 to disable the limit. + +A typical use of this variable in a widget function is as follows (note +the additional function scope is required): + +example(LPAR()RPAR() { + local UNDO_LIMIT_NO=$UNDO_CHANGE_NO + # Perform some form of recursive edit. +}) +) vindex(WIDGET) item(tt(WIDGET) (scalar))( The name of the widget currently being executed; read-only. @@ -1360,7 +1379,9 @@ item(tt(clear-screen))( Clear the screen, remaining in incremental search mode. ) item(tt(history-incremental-search-backward))( -Find the next occurrence of the contents of the mini-buffer. +Find the next occurrence of the contents of the mini-buffer. If the +mini-buffer is empty, the most recent previously used search string is +reinstated. ) item(tt(history-incremental-search-forward))( Invert the sense of the search. @@ -2057,6 +2078,18 @@ tindex(beep) item(tt(beep))( Beep, unless the tt(BEEP) option is unset. ) +tindex(bracketed-paste) +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. +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. + +See also the tt(zle_bracketed_paste) parameter. +) tindex(vi-cmd-mode) item(tt(vi-cmd-mode) (tt(^X^V)) (unbound) (tt(^[)))( Enter command mode; that is, select the `tt(vicmd)' keymap. @@ -2072,6 +2105,11 @@ tindex(clear-screen) item(tt(clear-screen) (tt(^L ESC-^L)) (tt(^L)) (tt(^L)))( Clear the screen and redraw the prompt. ) +tindex(deactivate-region) +item(tt(deactivate-region))( +Make the current region inactive. This disables vim-style visual +selection mode if it is active. +) tindex(describe-key-briefly) item(tt(describe-key-briefly))( Reads a key sequence, then prints the function bound to that sequence. @@ -2319,7 +2357,8 @@ item(tt(undo) (tt(^_ ^Xu ^X^U)) (tt(u)) (unbound))( Incrementally undo the last text modification. When called from a user-defined widget, takes an optional argument indicating a previous state of the undo history as returned by the tt(UNDO_CHANGE_NO) variable; -modifications are undone until that state is reached. +modifications are undone until that state is reached, subject to +any limit imposed by the tt(UNDO_LIMIT_NO) variable. Note that when invoked from vi command mode, the full prior change made in insert mode is reverted, the changes having been merged when command mode was @@ -2422,6 +2461,7 @@ enditem() texinode(Character Highlighting)()(Zle Widgets)(Zsh Line Editor) sect(Character Highlighting) +vindex(zle_highlight, setting) The line editor has the ability to highlight characters or regions of the line that have a particular significance. This is controlled by the array parameter tt(zle_highlight), if it has been set by the user. @@ -2475,6 +2515,9 @@ a directory name. Note that suffix removal is configurable; the circumstances under which the suffix will be removed may differ for different completions. ) +item(tt(paste))( +Following a command to paste text, the characters that were inserted. +) enditem() tt(zle_highlight) may contain additional fields for controlling how diff --git a/Etc/FAQ.yo b/Etc/FAQ.yo index c23dff5b2..9b7e558a6 100644 --- a/Etc/FAQ.yo +++ b/Etc/FAQ.yo @@ -126,6 +126,7 @@ Chapter 3: How to get various things to work 3.25. How do I get coloured prompts on my colour xterm? 3.26. Why is my output duplicated with `tt(foo 2>&1 >foo.out | bar)'? 3.27. What are these `^' and `~' pattern characters, anyway? +3.28. How do I edit the input buffer in $EDITOR? Chapter 4: The mysteries of completion 4.1. What is completion? @@ -305,7 +306,7 @@ sect(On what machines will it run?) sect(What's the latest version?) - Zsh 5.0.8 is the latest production version. For details of all the + Zsh 5.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 @@ -777,6 +778,27 @@ label(23) mytt(function) to define a function, which doesn't expand aliases. It is possible to argue for extra warnings somewhere in this mess. + One workaround for this is to use the "function" keyword instead: + verb( + alias l='/bin/ls -F' + function l { /bin/ls -la "$@" | more } + ) + The mytt(l) after mytt(function) is not expanded. Note you don't need + the mytt(LPAR()RPAR()) in this case, although it's harmless. + + You need to be careful if you are defining a function with multiple + names; most people don't need to do this, so it's an unusual problem, + but in case you do you should be aware that in versions of the shell + before 5.1 names after the first em(were) expanded: + verb( + function a b c { ... } + ) + Here, mytt(b) and mytt(c), but not mytt(a), have aliases expanded. + This oddity was fixed in version 5.1. + + The rest of this item assumes you use the (more common, + but equivalent) mytt(LPAR()RPAR()) definitions. + Bart Schaefer's rule is: Define first those aliases you expect to use in the body of a function, but define the function first if the alias has the same name as the function. @@ -1701,11 +1723,17 @@ sect(What's wrong with cut and paste on my xterm?) already active, and needs to be turned off when the first command is executed. The shell doesn't even know if the remaining text is input to a command or for the shell, so there's simply nothing it can do. + However, if you have problems you can trick it: type `tt({)' on a line by itself, then paste the input, then type `tt(})' on a line by itself. The shell will not execute anything until the final brace is read; all input is read as continuation lines (this may require the fixes referred to above in order to be reliable). + + As of 5.0.9, this trick is not necessary on terminal emulators that + support the em(bracketed paste) feature (this includes most modern + terminal emulators). See the description of tt($zle_bracketed_paste) + in the tt(zshparam) manual page for details. ) @@ -1924,6 +1952,22 @@ label(327) ) +sect(How do I edit the input buffer in $EDITOR?) +label(328) + + When typing a long command interactively, it's possible to edit it in $EDITOR + before execution by using the tt(edit-command-line) ZLE widget. For example, + after putting + verb( + autoload -U edit-command-line; + zle -N edit-command-line; + bindkey '^Fc' edit-command-line; + ) + in your tt(~/.zshrc), typing mytt(^F c) will open the entered-so-far + command-line for editing. The command will not be automatically executed; + quitting the editor will only return to zsh's command-line editing mode. + + chapter(The mysteries of completion) diff --git a/Etc/zsh-development-guide b/Etc/zsh-development-guide index 7f5266bd9..cbbc798d3 100644 --- a/Etc/zsh-development-guide +++ b/Etc/zsh-development-guide @@ -70,6 +70,19 @@ avoided further changes to our workflow. a commit to the master repository. Don't create a separate change for this: amend the existing commit in your local repository. + Several developers use scripts to automate part or all of the ChangeLog + workflow: + + Subject: helper script for making ChangeLog entries + X-Seq: 33835, 33872, 34912 + http://www.zsh.org/mla/workers/2014/msg01622.html + http://www.zsh.org/mla/workers/2014/msg01659.html + http://www.zsh.org/mla/workers/2015/msg00836.html + + Subject: Re: _git commit object name completion + X-Seq: 35414 + http://www.zsh.org/mla/workers/2015/msg01338.html + * Do not merge your private feature branches onto the master branch: a linear history without merge commits is simpler to follow (and to bisect). @@ -738,7 +751,7 @@ the other things that can be defined by modules: /**/ int - boot_foo(Module m) + boot_(Module m) { int ret; @@ -748,7 +761,7 @@ the other things that can be defined by modules: ... /**/ int - cleanup_foo(Module m) + cleanup_(Module m) { deletehookdefs(m->nam, foohooks, sizeof(foohooks)/sizeof(*foohooks)); ... @@ -789,14 +802,37 @@ The definition is simple: }; The macro `WRAPDEF(...)' gets the C-function as its only argument. -This function should be defined like: +The `boot_()' function must install wrappers by calling `addwrapper()' +like so: + + /**/ + int + boot_(Module m) + { + int ret; + + ret = addwrapper(m, wrapper); + ... + } + +The `cleanup_()' function should then remove the wrappers again: + + /**/ + int + cleanup_(Module m) + { + deletewrapper(m, wrapper); + ... + } + +The wrapper function should be defined like: /**/ static int - ex_wrapper(List list, FuncWrap w, char *name) + ex_wrapper(Eprog prog, FuncWrap w, char *name) { ... - runshfunc(list, w, name); + runshfunc(prog, w, name); ... return 0; } @@ -815,11 +851,11 @@ finished: /**/ static int - ex_wrapper(List list, FuncWrap w, char *name) + ex_wrapper(Eprog prog, FuncWrap w, char *name) { if (wrapper_need_to_run) { ... - runshfunc(list, w, name); + runshfunc(prog, w, name); ... return 0; } @@ -907,6 +943,9 @@ Documentation surrounding text. No extra newlines after the opening or before the closing parenthesis are required. +A syntax highlighting file for Vim is included, just source tt(Doc/Zsh/.vimrc) +before editing one of the Doc/Zsh/*.yo files. + Module names ------------ diff --git a/Functions/Misc/nslookup b/Functions/Misc/nslookup index 150bd035c..8c11909d5 100644 --- a/Functions/Misc/nslookup +++ b/Functions/Misc/nslookup @@ -26,8 +26,7 @@ zstyle -s ':nslookup' pager tmp && zpty nslookup command nslookup "${(q)@}" -zpty -r nslookup line '* -> ' +zpty -r nslookup line '*> ' print -nr "$line" while line=''; vared -he "$pmpt[@]" line; do diff --git a/Functions/Misc/zargs b/Functions/Misc/zargs index 71fd42835..28ebca78f 100644 --- a/Functions/Misc/zargs +++ b/Functions/Misc/zargs @@ -73,7 +73,7 @@ emulate -L zsh || return 1 local -a opts eof n s l P i -local ZARGS_VERSION="1.4" +local ZARGS_VERSION="1.5" if zparseopts -a opts -D -- \ -eof::=eof e::=eof \ @@ -254,7 +254,7 @@ then bg='&' if zmodload -i zsh/parameter 2>/dev/null then - wait='wait %${(k)^jobstates[(R)running:*]}' + wait='wait ${${jobstates[(R)running:*]/#*:/}/%=*/}' else wait='wait' fi diff --git a/Functions/Misc/zed b/Functions/Misc/zed index c2caaf3f5..010b69bee 100644 --- a/Functions/Misc/zed +++ b/Functions/Misc/zed @@ -9,8 +9,9 @@ local var opt zed_file_name # We do not want timeout while we are editing a file integer TMOUT=0 okargs=1 fun bind +local -a expand -while getopts "fb" opt; do +while getopts "fbx:" opt; do case $opt in (f) fun=1 @@ -19,6 +20,14 @@ while getopts "fb" opt; do (b) bind=1 ;; + + (x) + if [[ $OPTARG != <-> ]]; then + print -r "Integer expected after -x: $OPTARG" >&2 + return 1 + fi + expand=(-x $OPTARG) + ;; esac done shift $(( OPTIND - 1 )) @@ -29,8 +38,8 @@ shift $(( OPTIND - 1 )) if (( $# != okargs )); then echo 'Usage: zed filename -zed -f function -zed -b' +zed -f [ -x N ] function +zed -b' >&2 return 1 fi @@ -71,7 +80,7 @@ fi setopt localoptions nobanghist if ((fun)) then - var="$(functions $1)" + var="$(functions $expand $1)" # If function is undefined but autoloadable, load it if [[ $var = *\#\ undefined* ]] then var="$(autoload +X $1; functions $1)" diff --git a/Functions/Prompts/prompt_adam1_setup b/Functions/Prompts/prompt_adam1_setup index 034641fb8..aca0e59f1 100644 --- a/Functions/Prompts/prompt_adam1_setup +++ b/Functions/Prompts/prompt_adam1_setup @@ -14,8 +14,6 @@ This theme works best with a dark background. Recommended fonts for this theme: nexus or vga or similar. If you don't have any of these, then specify the `plain' option to use 7-bit replacements for the 8-bit characters. - -And you probably thought adam1 was overkill. EOF } @@ -27,8 +25,9 @@ prompt_adam1_setup () { base_prompt="%K{$prompt_adam1_color1}%n@%m%k " post_prompt="%b%f%k" - base_prompt_no_color=$(echo "$base_prompt" | perl -pe "s/%(K{.*?}|k)//g") - post_prompt_no_color=$(echo "$post_prompt" | perl -pe "s/%(K{.*?}|k)//g") + setopt localoptions extendedglob + base_prompt_no_color="${base_prompt//(%K{[^\\\}]#\}|%k)/}" + post_prompt_no_color="${post_prompt//(%K{[^\\\}]#\}|%k)/}" add-zsh-hook precmd prompt_adam1_precmd } diff --git a/Functions/Prompts/prompt_bart_setup b/Functions/Prompts/prompt_bart_setup index 6cbbb71c7..45fcffed4 100644 --- a/Functions/Prompts/prompt_bart_setup +++ b/Functions/Prompts/prompt_bart_setup @@ -48,9 +48,13 @@ prompt_bart_help () { When RPS1 (RPROMPT) is set before this prompt is selected and a fifth color is specified, that color is turned on before RPS1 is displayed and reset after it. Other color changes within RPS1, if - any, remain in effect. + any, remain in effect. This also applies to RPS2 (RPROMPT2). + If a fifth color is specified and there is no RPS2, PS2 (PROMPT2) + is colored and moved to RPS2. Changes to RPS1/RPS2 are currently + not reverted when the theme is switched off. These work best with + the TRANSIENT_RPROMPT option, which must be set separately. - This prompt hijacks psvar[7] through [9] to avoid having to reset + This theme hijacks psvar[7] through [9] to avoid having to reset the entire PS1 string on every command. It uses TRAPWINCH to set the position of the upper right prompt on a window resize, so the prompt may not match the window width until the next command. @@ -107,9 +111,9 @@ prompt_bart_precmd () { ((PSCOL == 1)) || { PSCOL=1 ; prompt_bart_ps1 } if [[ -o promptcr ]] then - # Emulate the 4.3.x promptsp option if it isn't available - eval '[[ -o promptsp ]] 2>/dev/null' || - print -nP "${(l.COLUMNS.. .)}\e[s${(pl.COLUMNS..\b.)}%E\e[u" >$TTY + # Emulate the 4.3.x promptsp option if it isn't available + eval '[[ -o promptsp ]] 2>/dev/null' || + print -nP "${(l.COLUMNS.. .)}\e[s${(pl.COLUMNS..\b.)}%E\e[u" >$TTY else IFS='[;' read -s -d R escape\?$'\e[6n' lineno PSCOL <$TTY fi ((PSCOL == 1)) || prompt_bart_ps1 @@ -146,7 +150,7 @@ prompt_bart_ps1 () { # Assemble the new prompt ps1=( ${(f)PS1} ) # Split the existing prompt at newlines ps1=( - "%$[COLUMNS-PSCOL]>..>" # Begin truncation (upper left prompt) + "%$[COLUMNS-PSCOL]>..>" # Begin truncation (upper left prompt) "$host" "$hist1" # Empty when too wide for one line "$dir" @@ -176,8 +180,8 @@ prompt_bart_setup () { # A few extra niceties ... repeat 1 case "$1:l" in (off|disable) - add-zsh-hook -D precmd "prompt_*_precmd" - add-zsh-hook -D preexec "prompt_*_preexec" + add-zsh-hook -D precmd "prompt_*_precmd" + add-zsh-hook -D preexec "prompt_*_preexec" functions[TRAPWINCH]="${functions[TRAPWINCH]//prompt_bart_winch}" [[ $prompt_theme[1] = bart ]] && PS1=${${(f)PS1}[-1]} return 1 @@ -201,9 +205,22 @@ prompt_bart_setup () { prompt_bart_ps1 - # No RPS1 by default because prompt_off_setup doesn't fix it. - (($#RPS1 && $# > 4)) && RPS1="%F{$5}$RPS1%f" - + if (($# > 4)) + then + # No RPS1 by default because prompt_off_setup doesn't fix it. + if (($#RPS1)) + then + RPS1="%F{$5}$RPS1%f" + fi + # RPS2 is less obvious so don't mind that it's not restored. + if (($#RPS2)) + then + RPS2="%F{$5}$RPS2%f" + else + RPS2="%F{$5}<${${PS2//\%_/%^}%> }%f" + PS2='' + fi + fi # Paste our special commands into precmd and TRAPWINCH add-zsh-hook precmd prompt_bart_precmd diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr b/Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr index cae1a3b08..e8c8e81de 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_bzr @@ -6,7 +6,7 @@ setopt localoptions noksharrays extendedglob NO_shwordsplit local bzrbase bzrbr bzr_changes bzr_type local -a bzrinfo -local -xA hook_com bzr_info +local -A hook_com bzr_info VCS_INFO_bzr_get_info() { bzrinfo=( ${(s.:.)$( ${vcs_comm[cmd]} version-info --custom \ diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git index c348da2a7..3fc861eeb 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_git +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_git @@ -210,7 +210,9 @@ elif [[ -d "${gitdir}/rebase-merge" ]]; then # remove action git_patches_applied+=("${${(s: :)p}[2,-1]}") done - git_patches_unapplied=(${(f)"$(grep -v '^$' "${patchdir}/git-rebase-todo" | grep -v '^#')"}) + if [[ -f "${patchdir}/git-rebase-todo" ]] ; then + git_patches_unapplied=(${(f)"$(grep -v '^$' "${patchdir}/git-rebase-todo" | grep -v '^#')"}) + fi VCS_INFO_git_handle_patches elif [[ -d "${gitdir}/rebase-apply" ]]; then # Fake patch names for all but current patch diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg index 1274ca337..f35ad5965 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_hg @@ -16,7 +16,7 @@ local -a hgid_args defrevformat defbranchformat \ hgbmarks mqpatches mqseries mqguards mqunapplied hgmisc \ i_patchguards i_negguards i_posguards -local -xA hook_com +local -A hook_com hgbase=${vcs_comm[basedir]} rrn=${hgbase:t} diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_p4 b/Functions/VCS_Info/Backends/VCS_INFO_get_data_p4 index 430cfa6f0..329884982 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_p4 +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_p4 @@ -6,7 +6,7 @@ setopt localoptions extendedglob local p4base a b local -A p4info -local -xA hook_com +local -A hook_com ${vcs_comm[cmd]} info | while IFS=: read a b; do p4info[${a// /_}]="${b## #}"; done p4base=${vcs_comm[basedir]} diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_svk b/Functions/VCS_Info/Backends/VCS_INFO_get_data_svk index 6107a14f3..1d2d22ffb 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_svk +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_svk @@ -4,7 +4,7 @@ setopt localoptions NO_shwordsplit local svkbranch svkbase -local -xA hook_com +local -A hook_com svkbase=${vcs_comm[basedir]} rrn=${svkbase:t} diff --git a/Functions/VCS_Info/Backends/VCS_INFO_get_data_svn b/Functions/VCS_Info/Backends/VCS_INFO_get_data_svn index e56afee02..e1334f661 100644 --- a/Functions/VCS_Info/Backends/VCS_INFO_get_data_svn +++ b/Functions/VCS_Info/Backends/VCS_INFO_get_data_svn @@ -8,7 +8,7 @@ setopt localoptions noksharrays extendedglob NO_shwordsplit local svnbase svnbranch a b rrn local -i rc local -A svninfo parentinfo cwdinfo -local -xA hook_com +local -A hook_com svnbase="."; svninfo=() diff --git a/Functions/VCS_Info/VCS_INFO_formats b/Functions/VCS_Info/VCS_INFO_formats index 138091944..4d0dd75c2 100644 --- a/Functions/VCS_Info/VCS_INFO_formats +++ b/Functions/VCS_Info/VCS_INFO_formats @@ -5,7 +5,7 @@ setopt localoptions noksharrays NO_shwordsplit local msg tmp local -i i -local -xA hook_com +local -A hook_com # The _origs are needed because hooks can change values and there would # be no way to get the originals back for later hooks (a hook is run for # each message, that's being created). @@ -68,7 +68,7 @@ if [[ -n ${hook_com[unstaged]} ]] ; then fi if [[ ${quiltmode} != 'standalone' ]] && VCS_INFO_hook "pre-addon-quilt"; then - local -x REPLY + local REPLY VCS_INFO_quilt addon hook_com[quilt]="${REPLY}" unset REPLY diff --git a/Functions/VCS_Info/VCS_INFO_hook b/Functions/VCS_Info/VCS_INFO_hook index 479f5968b..94ae63017 100644 --- a/Functions/VCS_Info/VCS_INFO_hook +++ b/Functions/VCS_Info/VCS_INFO_hook @@ -3,8 +3,8 @@ ## Distributed under the same BSD-ish license as zsh itself. local hook static func -local -x context hook_name -local -xi ret +local context hook_name +local -i ret local -a hooks tmp local -i debug diff --git a/Functions/VCS_Info/VCS_INFO_quilt b/Functions/VCS_Info/VCS_INFO_quilt index 34ff1edbf..bc71cfb7d 100644 --- a/Functions/VCS_Info/VCS_INFO_quilt +++ b/Functions/VCS_Info/VCS_INFO_quilt @@ -86,9 +86,9 @@ function VCS_INFO_quilt() { local mode="$1" local patches pc tmp qstring root local -i ret - local -x context + local context local -a applied unapplied all applied_string unapplied_string quiltcommand quilt_env - local -Ax hook_com + local -A hook_com context=":vcs_info:${vcs}.quilt-${mode}:${usercontext}:${rrn}" zstyle -t "${context}" use-quilt || return 1 @@ -183,7 +183,7 @@ function VCS_INFO_quilt() { VCS_INFO_set ;; (addon) - # When VCS_INFO_quilt() is called with "addon" a "local -x REPLY" variable + # When VCS_INFO_quilt() is called with "addon" a "local REPLY" variable # should be in place. That variable can be unset after it's being used. REPLY="${qstring}" ;; diff --git a/Functions/VCS_Info/VCS_INFO_set b/Functions/VCS_Info/VCS_INFO_set index 5087be43f..484c7937d 100644 --- a/Functions/VCS_Info/VCS_INFO_set +++ b/Functions/VCS_Info/VCS_INFO_set @@ -8,7 +8,7 @@ local -i i j if [[ $1 == '--nvcs' ]] ; then [[ $2 == '-preinit-' ]] && (( maxexports == 0 )) && (( maxexports = 1 )) for i in {0..$((maxexports - 1))} ; do - typeset -gx vcs_info_msg_${i}_= + typeset -g vcs_info_msg_${i}_= done VCS_INFO_nvcsformats $2 [[ $2 != '-preinit-' ]] && VCS_INFO_hook "no-vcs" @@ -17,12 +17,12 @@ fi (( ${#msgs} - 1 < 0 )) && return 0 for i in {0..$(( ${#msgs} - 1 ))} ; do (( j = i + 1 )) - typeset -gx vcs_info_msg_${i}_=${msgs[$j]} + typeset -g vcs_info_msg_${i}_=${msgs[$j]} done if (( i < maxexports )) ; then for j in {$(( i + 1 ))..${maxexports}} ; do - [[ -n ${(P)${:-vcs_info_msg_${j}_}} ]] && typeset -gx vcs_info_msg_${j}_= + [[ -n ${(P)${:-vcs_info_msg_${j}_}} ]] && typeset -g vcs_info_msg_${j}_= done fi return 0 diff --git a/Functions/VCS_Info/vcs_info b/Functions/VCS_Info/vcs_info index 46938691d..350b189e9 100644 --- a/Functions/VCS_Info/vcs_info +++ b/Functions/VCS_Info/vcs_info @@ -51,10 +51,11 @@ vcs_info () { local pat local -i found retval local -a enabled disabled dps - local -x usercontext vcs rrn quiltmode LC_MESSAGES - local -ix maxexports - local -ax msgs - local -Ax vcs_comm hook_com backend_misc user_data + local usercontext vcs rrn quiltmode + local -x LC_MESSAGES + local -i maxexports + local -a msgs + local -A vcs_comm hook_com backend_misc user_data LC_MESSAGES=C if [[ -n ${LC_ALL} ]]; then diff --git a/Functions/VCS_Info/vcs_info_lastmsg b/Functions/VCS_Info/vcs_info_lastmsg index ddfaaf88b..438567cef 100644 --- a/Functions/VCS_Info/vcs_info_lastmsg +++ b/Functions/VCS_Info/vcs_info_lastmsg @@ -5,7 +5,7 @@ emulate -L zsh local -i i -local -ix maxexports +local -i maxexports VCS_INFO_maxexports for i in {0..$((maxexports - 1))} ; do diff --git a/Functions/VCS_Info/vcs_info_printsys b/Functions/VCS_Info/vcs_info_printsys index f29061463..b44b9c9b4 100644 --- a/Functions/VCS_Info/vcs_info_printsys +++ b/Functions/VCS_Info/vcs_info_printsys @@ -7,7 +7,7 @@ setopt extendedglob local sys local -a disabled enabled -local -Ax vcs_comm +local -A vcs_comm zstyle -a ":vcs_info:-init-:${1:-default}:-all-" "enable" enabled (( ${#enabled} == 0 )) && enabled=( all ) diff --git a/Functions/Zle/bracketed-paste-magic b/Functions/Zle/bracketed-paste-magic new file mode 100644 index 000000000..da106d1ac --- /dev/null +++ b/Functions/Zle/bracketed-paste-magic @@ -0,0 +1,192 @@ +# Starting with zsh-5.0.9, ZLE began to recognize the "bracketed paste" +# capability of terminal emulators, that is, the sequences $'\e[200~' to +# start a paste and $'\e[201~' to indicate the end of the pasted text. +# Pastes are handled by the bracketed-paste widget and insert literally +# into the editor buffer rather than being interpreted as keystrokes. +# +# This disables some common usages where the self-insert widget has been +# replaced in order to accomplish some extra processing. An example is +# the contributed url-quote-magic widget. The bracketed-paste-magic +# widget replaces bracketed-paste with a wrapper that re-enables these +# self-insert actions, and other actions as selected by the zstyles +# described below. +# +# Setup: +# autoload -Uz bracketed-paste-magic +# zle -N bracketed-paste bracketed-paste-magic + +# The following zstyles may be set to control processing of pasted text. +# +# 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. +# 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 +# function, otherwise it becomes set by default) or has no value, no +# widgets are active and the pasted text is inserted literally. If the +# value includes undefined-key, any unknown sequences are discarded from +# the pasted text. +# +# 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. +# Note that this is a list of literal key sequences, not patterns. +# This style is in context :bracketed-paste-magic and has no default. +# +# paste-init +# paste-finish +# Also looked up in the context :bracketed-paste-magic, these styles +# each are a list of function names. They are executed in widget +# context but are called as functions (NOT as widgets with "zle name"). +# They also run in zsh emulation context set by bracketed-paste-magic. +# As with hooks, the functions are called in order until one of them +# returns a nonzero exit status. The parameter PASTED contains the +# current state of the pasted text, other ZLE parameters are as usual. +# Although a nonzero status stops each list of functions, it does NOT +# abort the entire paste operation; use "zle send-break" for that. + +# IMPORTANT: During processing of the paste (after paste-init and +# before paste-finish), BUFFER starts empty and history is restricted, +# so cursor motions etc. may not pass outside of the pasted content. +# However, the paste-init functions have access to the full history and +# the original BUFFER, so they may for example move words from BUFFER +# into PASTED to make those words visible to the active-widgets. + +# Establish default values for styles, but only if not already set +zstyle -m :bracketed-paste-magic active-widgets '*' || + zstyle ':bracketed-paste-magic' active-widgets 'self-*' + +# Helper/example paste-init for exposing a word prefix inside PASTED. +# Useful with url-quote-magic if you have http://... on the line and +# are pasting additional text on the end of the URL. +# +# Usage: +# zstyle :bracketed-paste-magic paste-init backward-extend-paste +# +# TODO: rewrite this using match-words-by-style +# +backward-extend-paste() { + : emulate -LR zsh # Already set by bracketed-paste-magic + integer bep_mark=$MARK bep_region=$REGION_ACTIVE + if (( REGION_ACTIVE && MARK < CURSOR )); then + zle .exchange-point-and-mark + fi + if (( CURSOR )); then + local -a bep_words=( ${(z)LBUFFER} ) + if [[ -n $bep_words[-1] && $LBUFFER = *$bep_words[-1] ]]; then + PASTED=$bep_words[-1]$PASTED + LBUFFER=${LBUFFER%${bep_words[-1]}} + fi + fi + if (( MARK > bep_mark )); then + zle .exchange-point-and-mark + fi + REGION_ACTIVE=$bep_region +} + +# Example paste-finish for quoting the pasted text. +# +# Usage e.g.: +# zstyle :bracketed-paste-magic paste-finish quote-paste +# zstyle :bracketed-paste-magic:finish quote-style qqq +# +# Using "zstyle -e" to examine $PASTED lets you choose different quotes +# depending on context. +# +# To forcibly turn off numeric prefix quoting, use e.g.: +# zstyle :bracketed-paste-magic:finish quote-style none +# +quote-paste() { + : emulate -LR zsh # Already set by bracketed-paste-magic + local qstyle + # If there's a quoting style, be sure .bracketed-paste leaves it alone + zstyle -s :bracketed-paste-magic:finish quote-style qstyle && NUMERIC=1 + case $qstyle in + (b) PASTED=${(b)PASTED};; + (q-) PASTED=${(q-)PASTED};; + (\\|q) PASTED=${(q)PASTED};; + (\'|qq) PASTED=${(qq)PASTED};; + (\"|qqq) PASTED=${(qqq)PASTED};; + (\$|qqqq) PASTED=${(qqqq)PASTED};; + (Q) PASTED=${(Q)PASTED};; + esac +} + +# Now the actual function + +bracketed-paste-magic() { + emulate -LR zsh + local -a bpm_hooks bpm_inactive + local PASTED bpm_func bpm_active + + # Set PASTED and run the paste-init functions + zle .bracketed-paste PASTED + if zstyle -a :bracketed-paste-magic paste-init bpm_hooks; then + for bpm_func in $bpm_hooks; do + if (( $+functions[$bpm_func] )); then + $bpm_func || break + fi + 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 + while [[ -n $PASTED ]] && zle .read-command; do + PASTED=${PASTED#$KEYS} + if [[ $KEYS = ${(~j:|:)${(b)bpm_inactive}} ]]; then + zle .self-insert-unmeta + else + case $REPLY in + (${~bpm_active}) zle $REPLY;; + (*) zle .self-insert-unmeta;; + esac + fi + done + PASTED=$BUFFER + 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 + if (( $+functions[$bpm_func] )); then + $bpm_func || break + fi + done + fi + + # Reprocess $PASTED as an actual paste this time + zle -U - $PASTED$'\e[201~' # append paste-end marker + zle .bracketed-paste + zle .split-undo + + # Arrange to display highlighting if necessary + if [[ -n ${(M)zle_highlight:#paste:*} ]]; then + zle -R + zle .read-command && zle -U - $KEYS + fi +} + +# Handle zsh autoloading conventions +if [[ $zsh_eval_context = *loadautofunc && ! -o kshautoload ]]; then + bracketed-paste-magic "$@" +fi diff --git a/Functions/Zle/edit-command-line b/Functions/Zle/edit-command-line index 100af9601..2c7f34b8b 100644 --- a/Functions/Zle/edit-command-line +++ b/Functions/Zle/edit-command-line @@ -8,7 +8,20 @@ () { exec </dev/tty - ${=${VISUAL:-${EDITOR:-vi}}} $1 + + # Compute the cursor's position in bytes, not characters. + setopt localoptions nomultibyte + integer byteoffset=$(( $#PREBUFFER + $#LBUFFER + 1 )) + + # Open the editor, placing the cursor at the right place if we know how. + local editor=${${VISUAL:-${EDITOR:-vi}}} + case $editor in + (*vim*) ${=editor} -c "normal! ${byteoffset}go" -- $1;; + (*emacs*) ${=editor} $1 -eval "(goto-char ${byteoffset})";; + (*) ${=editor} $1;; + esac + + # Replace the buffer with the editor output. print -Rz - "$(<$1)" } =(<<<"$PREBUFFER$BUFFER") diff --git a/Functions/Zle/incremental-complete-word b/Functions/Zle/incremental-complete-word index 67a9d4744..ccc007543 100644 --- a/Functions/Zle/incremental-complete-word +++ b/Functions/Zle/incremental-complete-word @@ -69,7 +69,7 @@ incremental-complete-word() { '#key' -ne '#\\C-g' ]]; do twid=$wid if [[ "$key" = ${~stop} ]]; then - zle -U "$key" + zle -U - "$key" return elif [[ "$key" = ${~brk} ]]; then return diff --git a/Functions/Zle/narrow-to-region b/Functions/Zle/narrow-to-region index 86fd7ac13..261d821a9 100644 --- a/Functions/Zle/narrow-to-region +++ b/Functions/Zle/narrow-to-region @@ -5,39 +5,52 @@ # Optionally accepts exactly two arguments, which are used instead of # $CURSOR and $MARK as limits to the range. # +# Upon exit, $MARK is always the start of the edited range and $CURSOR +# the end of the range, even if they began in the opposite order. +# # Other options: -# -p pretext show `pretext' instead of the buffer text before the region. -# -P posttext show `posttext' instead of the buffer text after the region. -# Either or both may be empty. +# -p pretext show "pretext" instead of the buffer text before the region. +# -P posttext show "posttext" instead of the buffer text after the region. +# Either or both may be empty. # -n Only replace the text before or after the region with # the -p or -P options if the text was not empty. +# -l lbufvar $lbufvar is assigned the value of $LBUFFER and +# -r rbufvar $rbufvar is assigned the value of $RBUFFER +# from any recursive edit (i.e. not with -S or -R). Neither +# lbufvar nor rbufvar may begin with the prefix "_ntr_". # -S statevar # -R statevar -# Save or restore the state in/from the parameter named statevar. In -# either case no recursive editing takes place; this will typically be -# done within the calling function between calls with -S and -R. The -# statevar may not begin with the prefix _ntr_ which is reserved for -# parameters within narrow-to-region. +# Save or restore the state in/from the parameter named statevar. In +# either case no recursive editing takes place; this will typically be +# done within the calling function between calls with -S and -R. The +# statevar may not begin with the prefix "_ntr_" which is reserved for +# parameters within narrow-to-region. -emulate -L zsh -setopt extendedglob +# set the minimum of options to avoid changing behaviour away from +# user preferences from within recursive-edit +setopt localoptions noshwordsplit noksharrays -local _ntr_lbuffer _ntr_rbuffer +local _ntr_newbuf _ntr_lbuf_return _ntr_rbuf_return local _ntr_predisplay=$PREDISPLAY _ntr_postdisplay=$POSTDISPLAY +integer _ntr_savelim=UNDO_LIMIT_NO _ntr_changeno _ntr_histno=HISTNO integer _ntr_start _ntr_end _ntr_swap _ntr_cursor=$CURSOR _ntr_mark=$MARK integer _ntr_stat local _ntr_opt _ntr_pretext _ntr_posttext _ntr_usepretext _ntr_useposttext -local _ntr_nonempty _ntr_save _ntr_restore +local _ntr_nonempty _ntr_save _ntr_restore _ntr_lbuffer _ntr_rbuffer -while getopts "np:P:R:S:" _ntr_opt; do +while getopts "l:np:P:r:R:S:" _ntr_opt; do case $_ntr_opt in + (l) _ntr_lbuf_return=$OPTARG + ;; (n) _ntr_nonempty=1 ;; (p) _ntr_pretext=$OPTARG _ntr_usepretext=1 ;; (P) _ntr_posttext=$OPTARG _ntr_useposttext=1 ;; + (r) _ntr_rbuf_return=$OPTARG + ;; (R) _ntr_restore=$OPTARG ;; (S) _ntr_save=$OPTARG @@ -49,7 +62,8 @@ while getopts "np:P:R:S:" _ntr_opt; do done (( OPTIND > 1 )) && shift $(( OPTIND - 1 )) -if [[ $_ntr_restore = _ntr_* || $_ntr_save = _ntr_* ]]; then +if [[ $_ntr_restore = _ntr_* || $_ntr_save = _ntr_* || + $_ntr_lbuf_return = _ntr_* || $_ntr_rbuf_return = _ntr_* ]]; then zle -M "$0: _ntr_ prefix is reserved" >&2 return 1 fi @@ -58,7 +72,7 @@ if [[ -n $_ntr_save || -z $_ntr_restore ]]; then if (( $# )); then if (( $# != 2 )); then - zle -M "$0: supply zero or two arguments" + zle -M "$0: supply zero or two arguments" >&2 return 1 fi _ntr_start=$1 @@ -74,50 +88,68 @@ if [[ -n $_ntr_save || -z $_ntr_restore ]]; then _ntr_end=_ntr_swap fi - (( _ntr_end++, _ntr_cursor -= _ntr_start, _ntr_mark -= _ntr_start )) + (( _ntr_cursor -= _ntr_start, _ntr_mark -= _ntr_start )) _ntr_lbuffer=${BUFFER[1,_ntr_start]} if [[ -z $_ntr_usepretext || ( -n $_ntr_nonempty && -z $_ntr_lbuffer ) ]] then _ntr_pretext=$_ntr_lbuffer fi - _ntr_rbuffer=${BUFFER[_ntr_end,-1]} + _ntr_rbuffer=${BUFFER[_ntr_end+1,-1]} if [[ -z $_ntr_useposttext || ( -n $_ntr_nonempty && -z $_ntr_rbuffer ) ]] then _ntr_posttext=$_ntr_rbuffer fi + _ntr_changeno=$UNDO_CHANGE_NO PREDISPLAY="$_ntr_predisplay$_ntr_pretext" POSTDISPLAY="$_ntr_posttext$_ntr_postdisplay" - BUFFER=${BUFFER[_ntr_start+1,_ntr_end-1]} - CURSOR=$_ntr_cursor - MARK=$_ntr_mark if [[ -n $_ntr_save ]]; then - eval "$_ntr_save=(${(qq)_ntr_predisplay} ${(qq)_ntr_postdisplay} -${(qq)_ntr_lbuffer} ${(qq)_ntr_rbuffer})" || return 1 + builtin typeset -ga $_ntr_save + set -A $_ntr_save "${_ntr_predisplay}" "${_ntr_postdisplay}" \ + "${_ntr_savelim}" "${_ntr_changeno}" \ + "${_ntr_start}" "${_ntr_end}" "${_ntr_histno}" || return 1 fi + + BUFFER=${BUFFER[_ntr_start+1,_ntr_end]} + CURSOR=$_ntr_cursor + MARK=$_ntr_mark + zle split-undo + UNDO_LIMIT_NO=$UNDO_CHANGE_NO fi if [[ -z $_ntr_save && -z $_ntr_restore ]]; then zle recursive-edit _ntr_stat=$? + + [[ -n $_ntr_lbuf_return ]] && + builtin typeset -g ${_ntr_lbuf_return}="${LBUFFER}" + [[ -n $_ntr_rbuf_return ]] && + builtin typeset -g ${_ntr_rbuf_return}="${RBUFFER}" fi if [[ -n $_ntr_restore || -z $_ntr_save ]]; then if [[ -n $_ntr_restore ]]; then - if ! eval "_ntr_predisplay=\${${_ntr_restore}[1]} -_ntr_postdisplay=\${${_ntr_restore}[2]} -_ntr_lbuffer=\${${_ntr_restore}[3]} -_ntr_rbuffer=\${${_ntr_restore}[4]}"; then - zle -M Failed. + if ! { _ntr_predisplay="${${(@P)_ntr_restore}[1]}" + _ntr_postdisplay="${${(@P)_ntr_restore}[2]}" + _ntr_savelim="${${(@P)_ntr_restore}[3]}" + _ntr_changeno="${${(@P)_ntr_restore}[4]}" + _ntr_start="${${(@P)_ntr_restore}[5]}" + _ntr_end="${${(@P)_ntr_restore}[6]}" + _ntr_histno="${${(@P)_ntr_restore}[7]}" }; then + zle -M Failed. >&2 return 1 fi fi + _ntr_newbuf="$BUFFER" + HISTNO=_ntr_histno + zle undo $_ntr_changeno PREDISPLAY=$_ntr_predisplay POSTDISPLAY=$_ntr_postdisplay - LBUFFER="$_ntr_lbuffer$LBUFFER" - RBUFFER="$RBUFFER$_ntr_rbuffer" + BUFFER[_ntr_start+1,_ntr_end]="$_ntr_newbuf" + (( MARK = _ntr_start, CURSOR = _ntr_start + ${#_ntr_newbuf} )) + UNDO_LIMIT_NO=_ntr_savelim fi return $_ntr_stat diff --git a/Functions/Zle/read-from-minibuffer b/Functions/Zle/read-from-minibuffer index 8fec1105e..09dc68f97 100644 --- a/Functions/Zle/read-from-minibuffer +++ b/Functions/Zle/read-from-minibuffer @@ -20,7 +20,7 @@ done (( OPTIND > 1 )) && shift $(( OPTIND - 1 )) local readprompt="$1" lbuf_init="$2" rbuf_init="$3" -integer changeno=$UNDO_CHANGE_NO +integer savelim=$UNDO_LIMIT_NO changeno=$UNDO_CHANGE_NO { # Use anonymous function to make sure special values get restored, @@ -43,6 +43,8 @@ integer changeno=$UNDO_CHANGE_NO else local NUMERIC unset NUMERIC + zle split-undo + UNDO_LIMIT_NO=$UNDO_CHANGE_NO zle recursive-edit -K main stat=$? (( stat )) || REPLY=$BUFFER @@ -52,6 +54,7 @@ integer changeno=$UNDO_CHANGE_NO # This removes the edits relating to the read from the undo history. # These aren't useful once we get back to the main editing buffer. zle undo $changeno + UNDO_LIMIT_NO=savelim } return $stat diff --git a/Functions/Zle/smart-insert-last-word b/Functions/Zle/smart-insert-last-word index 27b0849ee..67adea760 100644 --- a/Functions/Zle/smart-insert-last-word +++ b/Functions/Zle/smart-insert-last-word @@ -47,13 +47,15 @@ setopt extendedglob nohistignoredups zle auto-suffix-retain # Not strictly necessary: -# (($+_ilw_hist)) || integer -g _ilw_hist _ilw_count _ilw_cursor _ilw_lcursor +# (($+_ilw_hist)) || integer -g _ilw_hist _ilw_count _ilw_cursor _ilw_lcursor _ilw_changeno integer cursor=$CURSOR lcursor=$CURSOR local lastcmd pattern numeric=$NUMERIC # Save state for repeated calls -if (( HISTNO == _ilw_hist && cursor == _ilw_cursor )); then +if (( HISTNO == _ilw_hist && cursor == _ilw_cursor && + UNDO_CHANGE_NO == _ilw_changeno )) +then NUMERIC=$[_ilw_count+1] lcursor=$_ilw_lcursor else @@ -61,7 +63,8 @@ else _ilw_lcursor=$lcursor fi # Handle the up to three arguments of .insert-last-word -if (( $+1 )); then +if (( $+1 )) +then if (( $+3 )); then ((NUMERIC = -($1))) else @@ -117,3 +120,6 @@ fi LBUFFER[lcursor+1,cursor+1]=$lastcmd[-NUMERIC] _ilw_cursor=$CURSOR + +# This is necessary to update UNDO_CHANGE_NO immediately +zle split-undo && _ilw_changeno=$UNDO_CHANGE_NO diff --git a/Functions/Zle/url-quote-magic b/Functions/Zle/url-quote-magic index fbcc7c19c..362d15c44 100644 --- a/Functions/Zle/url-quote-magic +++ b/Functions/Zle/url-quote-magic @@ -8,6 +8,12 @@ # autoload -Uz url-quote-magic # zle -N self-insert url-quote-magic +# As of zsh-5.0.9, the following may also be necessary in order to apply +# quoting to copy-pasted URLs: +# autload -Uz bracketed-paste-magic +# zle -N bracketed-paste bracketed-paste-magic +# See also backward-extend-paste in bracketed-paste-magic source file. + # A number of zstyles may be set to control the quoting behavior. # # url-metas @@ -4,7 +4,54 @@ CHANGES FROM PREVIOUS VERSIONS OF ZSH Note also the list of incompatibilities in the README file. -Changes from 5.0.7 to 5.0.8 +Changes from 5.0.8 to 5.1 +------------------------- + +The builtins declare, export, local, readonly and typeset +now have corresponding reserved words. When used in +this form, the builtin syntax is extended so that assignments +following the reserved word are treated similarly to +assignments that appear at the start of the command line. +For example, + local scalar=`echo one word` array=(several words) +creates a local "scalar" containing the text "one word" +and an array "array" containing the words "several" +"words". + +- The print builtin has new options -x and -X to expand tabs. + +- Several new command completions and numerous updates to others. + +- Options to "fc" to segregate internal and shared history. + +- All emulations including "sh" use multibyte by default; several + repairs to multibyte handling. + +- ZLE supports "bracketed paste" mode to avoid interpreting pasted + newlines as accept-line. Pastes can be highlighted for visibility + and to make it more obvious whether accept-line has occurred. + +- Improved (though still not perfect) POSIX compatibility for getopts + builtin when POSIX_BUILTINS is set. + +- New setopt APPEND_CREATE for POSIX-compatible NO_CLOBBER behavior. + +- Completion of date values now displays in a calendar format when + the complist module is available. Controllable by zstyle. + +- New parameter UNDO_LIMIT_NO for more control over ZLE undo repeat. + +- Several repairs/improvements to the contributed narrow-to-region + ZLE function. + +- Many changes to child-process and signal handling to eliminate race + conditions and avoid deadlocks on descriptor and memory management. + +- New builtin sysopen in zsh/system module for detailed control of + file descriptor modes. + + +Changes from 5.0.0 to 5.0.8 --------------------------- - Global aliases can be created for syntactic tokens such as command @@ -47,9 +94,6 @@ Changes from 5.0.7 to 5.0.8 - Some rationalisations have been made to the zsh/db/gdbm module that should make it more useful and predictable in operation. -Changes from 5.0.0 to 5.0.7 ---------------------------- - - Numeric constants encountered in mathematical expressions (but not other contexts) can contain underscores as separators that will be ignored on evaluation, as allowed in other scripting languages. For example, @@ -5,11 +5,10 @@ THE Z SHELL (ZSH) Version ------- -This is version 5.0.8 of the shell. This is a stable release. -There are no significant new features since 5.0.7, but there -are many bugfixes and some significant internal improvements, notably -a more predictable effect for keyboard interrupts and proper parsing -of $(...) expressions. +This is version 5.1 of the shell. This is a stable release. There are +a few visible improvements since 5.0.8 as well as many bugfixes. Note +in particular the two changes highlighted under "Incompatibilites +between 5.0.8 and 5.1" below. See NEWS for more information. Installing Zsh -------------- @@ -30,6 +29,32 @@ 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. +Incompatibilites between 5.0.8 and 5.1 +-------------------------------------- + +The default behaviour when text is pasted into an X Windows terminal has +changed significantly (unless you are using a very old terminal emulator +that doesn't support this mode). Now, the new "bracketed paste mode" +treats all the pasted text as literal characters. This means, in +particular, that a newline is simply inserted as a visible newline; you +need to hit Return on the keyboard to execute the pasted text in one go. +See the description of zle_bracketed_paste in the zshparams manual for +more. "unset zle_bracketed_paste" restores the previous behaviour. + +As noted in NEWS, the builtins declare, export, float, integer, local, +readonly and typeset now have corresponding reserved words that provide +true assignment semantics instead of an approximation by means of normal +command line arguments. It is hoped that this additional consistency +provides a more natural interface. However, compatbility with older +versions of zsh can be obtained by turning off the reserved word +interface, exposing the builtin interface: + + disable -r declare export float integer local readonly typeset + +This is also necessary in the unusual eventuality that the builtins are +to be overridden by shell functions, since reserved words take +precedence over functions. + Incompatibilites between 5.0.7 and 5.0.8 ---------------------------------------- diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c index bcf7661f4..5d5dac6b7 100644 --- a/Src/Builtins/sched.c +++ b/Src/Builtins/sched.c @@ -220,7 +220,8 @@ bin_sched(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) endstr = "-- "; else endstr = ""; - printf("%3d %s %s%s%s\n", sn, tbuf, flagstr, endstr, sch->cmd); + printf("%3d %s %s%s%s\n", sn, tbuf, flagstr, endstr, + unmeta(sch->cmd)); } return 0; } else if (!argptr[1]) { diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c index 41ad2c6e4..62dbd55ea 100644 --- a/Src/Modules/curses.c +++ b/Src/Modules/curses.c @@ -765,7 +765,7 @@ zccmd_string(const char *nam, char **args) w = (ZCWin)getdata(node); #ifdef HAVE_WADDWSTR - mb_metacharinit(); + mb_charinit(); wptr = wstr = zhalloc((strlen(str)+1) * sizeof(wchar_t)); while (*str && (clen = mb_metacharlenconv(str, &wc))) { diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c index 63a04dc89..86c61cf1c 100644 --- a/Src/Modules/datetime.c +++ b/Src/Modules/datetime.c @@ -53,10 +53,12 @@ reverse_strftime(char *nam, char **argv, char *scalar, int quiet) * to use the current timezone. This is probably the best guess; * it's the one that will cause dates and times output by strftime * without the -r option and without an explicit timezone to be - * converted back correctly. + * converted back correctly. Additionally, tm_mday is set to 1 + * as that and not 0 corresponds to the first of the month. */ (void)memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; + tm.tm_mday = 1; endp = strptime(argv[1], argv[0], &tm); if (!endp) { @@ -96,7 +98,7 @@ reverse_strftime(char *nam, char **argv, char *scalar, int quiet) static int output_strftime(char *nam, char **argv, Options ops, UNUSED(int func)) { - int bufsize, x; + int bufsize, x, len; char *endptr = NULL, *scalar = NULL, *buffer; time_t secs; struct tm *t; @@ -129,16 +131,19 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func)) bufsize = strlen(argv[0]) * 8; buffer = zalloc(bufsize); + len = 0; for (x=0; x < 4; x++) { - if (ztrftime(buffer, bufsize, argv[0], t, 0L) >= 0) + if ((len = ztrftime(buffer, bufsize, argv[0], t, 0L)) >= 0) break; buffer = zrealloc(buffer, bufsize *= 2); } + DPUTS(len < 0, "bad output from ztrftime"); if (scalar) { - setsparam(scalar, metafy(buffer, -1, META_DUP)); + setsparam(scalar, metafy(buffer, len, META_DUP)); } else { - printf("%s\n", buffer); + fwrite(buffer, 1, len, stdout); + putchar('\n'); } zfree(buffer, bufsize); diff --git a/Src/Modules/files.c b/Src/Modules/files.c index f86b9c1e9..dbcff6307 100644 --- a/Src/Modules/files.c +++ b/Src/Modules/files.c @@ -131,7 +131,7 @@ domkdir(char *nam, char *path, mode_t mode, int p) return 0; } oumask = umask(0); - err = mkdir(path, mode) ? errno : 0; + err = mkdir(rpath, mode) ? errno : 0; umask(oumask); if(!err) return 0; diff --git a/Src/Modules/newuser.c b/Src/Modules/newuser.c index 71902da7d..efdb2abba 100644 --- a/Src/Modules/newuser.c +++ b/Src/Modules/newuser.c @@ -67,7 +67,7 @@ check_dotfile(const char *dotdir, const char *fname) int boot_(UNUSED(Module m)) { - const char *dotdir = getsparam("ZDOTDIR"); + const char *dotdir = getsparam_u("ZDOTDIR"); const char *spaths[] = { #ifdef SITESCRIPT_DIR SITESCRIPT_DIR, diff --git a/Src/Modules/regex.c b/Src/Modules/regex.c index ce57de986..16cc77f30 100644 --- a/Src/Modules/regex.c +++ b/Src/Modules/regex.c @@ -115,6 +115,7 @@ zcond_regex_match(char **a, int id) } else { zlong offs; char *ptr; + int clen, leftlen; m = matches; s = metafy(lhstr + m->rm_so, m->rm_eo - m->rm_so, META_DUP); @@ -123,19 +124,25 @@ zcond_regex_match(char **a, int id) * Count the characters before the match. */ ptr = lhstr; + leftlen = m->rm_so; offs = 0; - MB_METACHARINIT(); - while (ptr < lhstr + m->rm_so) { + MB_CHARINIT(); + while (leftlen) { offs++; - ptr += MB_METACHARLEN(ptr); + clen = MB_CHARLEN(ptr, leftlen); + ptr += clen; + leftlen -= clen; } setiparam("MBEGIN", offs + !isset(KSHARRAYS)); /* * Add on the characters in the match. */ - while (ptr < lhstr + m->rm_eo) { + leftlen = m->rm_eo - m->rm_so; + while (leftlen) { offs++; - ptr += MB_METACHARLEN(ptr); + clen = MB_CHARLEN(ptr, leftlen); + ptr += clen; + leftlen -= clen; } setiparam("MEND", offs + !isset(KSHARRAYS) - 1); if (nelem) { @@ -148,20 +155,31 @@ zcond_regex_match(char **a, int id) ++n, ++m, ++bptr, ++eptr) { char buf[DIGBUFSIZE]; + if (m->rm_so < 0 || m->rm_eo < 0) { + *bptr = ztrdup("-1"); + *eptr = ztrdup("-1"); + continue; + } ptr = lhstr; + leftlen = m->rm_so; offs = 0; /* Find the start offset */ - MB_METACHARINIT(); - while (ptr < lhstr + m->rm_so) { + MB_CHARINIT(); + while (leftlen) { offs++; - ptr += MB_METACHARLEN(ptr); + clen = MB_CHARLEN(ptr, leftlen); + ptr += clen; + leftlen -= clen; } convbase(buf, offs + !isset(KSHARRAYS), 10); *bptr = ztrdup(buf); /* Continue to the end offset */ - while (ptr < lhstr + m->rm_eo) { + leftlen = m->rm_eo - m->rm_so; + while (leftlen ) { offs++; - ptr += MB_METACHARLEN(ptr); + clen = MB_CHARLEN(ptr, leftlen); + ptr += clen; + leftlen -= clen; } convbase(buf, offs + !isset(KSHARRAYS) - 1, 10); *eptr = ztrdup(buf); diff --git a/Src/Modules/socket.c b/Src/Modules/socket.c index 6c70d3166..65b87d7dd 100644 --- a/Src/Modules/socket.c +++ b/Src/Modules/socket.c @@ -175,7 +175,7 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) tv.tv_sec = 0; tv.tv_usec = 0; - if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv))) return 1; + if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv)) == 0) return 1; else if (ret == -1) { zwarnnam(nam, "select error: %e", errno); @@ -191,8 +191,11 @@ bin_zsocket(char *nam, char **args, Options ops, UNUSED(int func)) } len = sizeof(soun); - if ((rfd = accept(lfd, (struct sockaddr *)&soun, &len)) == -1) - { + do { + rfd = accept(lfd, (struct sockaddr *)&soun, &len); + } while (rfd < 0 && errno == EINTR && !errflag); + + if (rfd == -1) { zwarnnam(nam, "could not accept connection: %e", errno); return 1; } diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c index 6fc53894c..396177149 100644 --- a/Src/Modules/stat.c +++ b/Src/Modules/stat.c @@ -197,8 +197,11 @@ stattimeprint(time_t tim, char *outbuf, int flags) } if (flags & STF_STRING) { char *oend = outbuf + strlen(outbuf); - ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) : - localtime(&tim), 0L); + /* Where the heck does "40" come from? */ + int len = ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) : + localtime(&tim), 0L); + if (len > 0) + metafy(oend, len, META_NOALLOC); if (flags & STF_RAW) strcat(oend, ")"); } diff --git a/Src/Modules/system.c b/Src/Modules/system.c index f6a21d160..1ab1fb17e 100644 --- a/Src/Modules/system.c +++ b/Src/Modules/system.c @@ -279,6 +279,182 @@ bin_syswrite(char *nam, char **args, Options ops, UNUSED(int func)) } +static struct { char *name; int oflag; } openopts[] = { +#ifdef O_CLOEXEC + { "cloexec", O_CLOEXEC }, +#else +# ifdef FD_CLOEXEC + { "cloexec", 0 }, /* this needs to be first in the table */ +# endif +#endif +#ifdef O_NOFOLLOW + { "nofollow", O_NOFOLLOW }, +#endif +#ifdef O_SYNC + { "sync", O_SYNC }, +#endif +#ifdef O_NOATIME + { "noatime", O_NOATIME }, +#endif + { "excl", O_EXCL | O_CREAT }, + { "creat", O_CREAT }, + { "create", O_CREAT }, + { "truncate", O_TRUNC }, + { "trunc", O_TRUNC } +}; + +/**/ +static int +bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func)) +{ + int read = OPT_ISSET(ops, 'r'); + int write = OPT_ISSET(ops, 'w'); + int append = OPT_ISSET(ops, 'a') ? O_APPEND : 0; + int flags = O_NOCTTY | append | ((append || write) ? + (read ? O_RDWR : O_WRONLY) : O_RDONLY); + char *opt, *ptr, *nextopt, *fdvar; + int o, fd, explicit = -1; + mode_t perms = 0666; +#if defined(FD_CLOEXEC) && !defined(O_CLOEXEC) + int fdflags; +#endif + + if (!OPT_ISSET(ops, 'u')) { + zwarnnam(nam, "file descriptor not specified"); + return 1; + } + + /* file descriptor, either 0-9 or a variable name */ + fdvar = OPT_ARG(ops, 'u'); + if (idigit(*fdvar) && !fdvar[1]) { + explicit = atoi(fdvar); + } else if (!isident(fdvar)) { + zwarnnam(nam, "not an identifier: %s", fdvar); + return 1; + } + + /* open options */ + if (OPT_ISSET(ops, 'o')) { + opt = OPT_ARG(ops, 'o'); + while (opt) { + if (!strncasecmp(opt, "O_", 2)) /* ignore initial O_ */ + opt += 2; + if ((nextopt = strchr(opt, ','))) + *nextopt++ = '\0'; + for (o = sizeof(openopts)/sizeof(*openopts) - 1; o >= 0 && + strcasecmp(openopts[o].name, opt); o--) {} + if (o < 0) { + zwarnnam(nam, "unsupported option: %s\n", opt); + return 1; + } +#if defined(FD_CLOEXEC) && !defined(O_CLOEXEC) + if (!openopts[o].oflag) + fdflags = FD_CLOEXEC; +#endif + flags |= openopts[o].oflag; + opt = nextopt; + } + } + + /* -m: permissions or mode for created files */ + if (OPT_ISSET(ops, 'm')) { + ptr = opt = OPT_ARG(ops, 'm'); + while (*ptr >= '0' && *ptr <= '7') ptr++; + if (*ptr || ptr - opt < 3) { + zwarnnam(nam, "invalid mode %s", opt); + return 1; + } + perms = zstrtol(opt, 0, 8); /* octal number */ + } + + if (flags & O_CREAT) + fd = open(*args, flags, perms); + else + fd = open(*args, flags); + + if (fd == -1) { + zwarnnam(nam, "can't open file %s: %e", *args, errno); + return 1; + } + fd = (explicit > -1) ? redup(fd, explicit) : movefd(fd); + if (fd == -1) { + zwarnnam(nam, "can't open file %s", *args); + return 1; + } + +#if defined(FD_CLOEXEC) && !defined(O_CLOEXEC) + if (fdflags) + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + if (explicit == -1) { + fdtable[fd] = FDT_EXTERNAL; + setiparam(fdvar, fd); + /* if setting the variable failed, close fd to avoid leak */ + if (errflag) + zclose(fd); + } + + return 0; +} + + +/* + * Return values of bin_sysseek: + * 0 Success + * 1 Error in parameters to command + * 2 Error on seek, ERRNO set by system + */ + +/**/ +static int +bin_sysseek(char *nam, char **args, Options ops, UNUSED(int func)) +{ + int w = SEEK_SET, fd = 0; + char *whence; + off_t pos; + + /* -u: file descriptor if not stdin */ + if (OPT_ISSET(ops, 'u')) { + fd = getposint(OPT_ARG(ops, 'u'), nam); + if (fd < 0) + return 1; + } + + /* -w: whence - starting point of seek */ + if (OPT_ISSET(ops, 'w')) { + whence = OPT_ARG(ops, 'w'); + if (!(strcasecmp(whence, "current") && strcmp(whence, "1"))) + w = SEEK_CUR; + else if (!(strcasecmp(whence, "end") && strcmp(whence, "2"))) + w = SEEK_END; + else if (strcasecmp(whence, "start") && strcmp(whence, "0")) { + zwarnnam(nam, "unknown argument to -w: %s", whence); + return 1; + } + } + + pos = (off_t)mathevali(*args); + return (lseek(fd, pos, w) == -1) ? 2 : 0; +} + +/**/ +static mnumber +math_systell(UNUSED(char *name), int argc, mnumber *argv, UNUSED(int id)) +{ + int fd = (argv->type == MN_INTEGER) ? argv->u.l : (int)argv->u.d; + mnumber ret; + ret.type = MN_INTEGER; + ret.u.l = 0; + + if (fd < 0) { + zerr("file descriptor out of range"); + return ret; + } + ret.u.l = lseek(fd, 0, SEEK_CUR); + return ret; +} + + /* * Return values of bin_syserror: * 0 Successfully processed error @@ -542,6 +718,8 @@ static struct builtin bintab[] = { BUILTIN("syserror", 0, bin_syserror, 0, 1, 0, "e:p:", NULL), BUILTIN("sysread", 0, bin_sysread, 0, 1, 0, "c:i:o:s:t:", NULL), BUILTIN("syswrite", 0, bin_syswrite, 1, 1, 0, "c:o:", NULL), + BUILTIN("sysopen", 0, bin_sysopen, 1, 1, 0, "rwau:o:m:", NULL), + BUILTIN("sysseek", 0, bin_sysseek, 1, 1, 0, "u:w:", NULL), BUILTIN("zsystem", 0, bin_zsystem, 1, -1, 0, NULL, NULL) }; @@ -611,6 +789,9 @@ scanpmsysparams(UNUSED(HashTable ht), ScanFunc func, int flags) func(&spm.node, flags); } +static struct mathfunc mftab[] = { + NUMMATHFUNC("systell", math_systell, 1, 1, 0) +}; static struct paramdef partab[] = { SPECIALPMDEF("errnos", PM_ARRAY|PM_READONLY, @@ -622,7 +803,7 @@ static struct paramdef partab[] = { static struct features module_features = { bintab, sizeof(bintab)/sizeof(*bintab), NULL, 0, - NULL, 0, + mftab, sizeof(mftab)/sizeof(*mftab), partab, sizeof(partab)/sizeof(*partab), 0 }; diff --git a/Src/Modules/system.mdd b/Src/Modules/system.mdd index 46f02d166..eed0c1b9d 100644 --- a/Src/Modules/system.mdd +++ b/Src/Modules/system.mdd @@ -2,7 +2,7 @@ name=zsh/system link=dynamic load=no -autofeatures="b:sysread b:syswrite b:syserror p:errnos" +autofeatures="b:sysread b:syswrite b:sysopen b:sysseek b:syserror p:errnos f:systell" objects="system.o errnames.o" diff --git a/Src/Modules/tcp.c b/Src/Modules/tcp.c index 0d9522047..bc1765da1 100644 --- a/Src/Modules/tcp.c +++ b/Src/Modules/tcp.c @@ -519,7 +519,7 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) tv.tv_sec = 0; tv.tv_usec = 0; - if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv))) return 1; + if ((ret = select(lfd+1, &rfds, NULL, NULL, &tv)) == 0) return 1; else if (ret == -1) { zwarnnam(nam, "select error: %e", errno); @@ -536,8 +536,11 @@ bin_ztcp(char *nam, char **args, Options ops, UNUSED(int func)) sess = zts_alloc(ZTCP_INBOUND); len = sizeof(sess->peer.in); - if ((rfd = accept(lfd, (struct sockaddr *)&sess->peer.in, &len)) == -1) - { + do { + rfd = accept(lfd, (struct sockaddr *)&sess->peer.in, &len); + } while (rfd < 0 && errno == EINTR && !errflag); + + if (rfd == -1) { zwarnnam(nam, "could not accept connection: %e", errno); tcp_close(sess); return 1; diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index 09d4bd703..bd51512f9 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -731,7 +731,7 @@ zfgetmsg(void) stopit = (*ptr++ != '-'); queue_signals(); - if (!(verbose = getsparam("ZFTP_VERBOSE"))) + if (!(verbose = getsparam_u("ZFTP_VERBOSE"))) verbose = ""; if (strchr(verbose, lastcodestr[0])) { /* print the whole thing verbatim */ @@ -1785,7 +1785,7 @@ zftp_open(char *name, char **args, int flags) char *hname; alarm(0); queue_signals(); - if ((hname = getsparam("ZFTP_HOST")) && *hname) + if ((hname = getsparam_u("ZFTP_HOST")) && *hname) zwarnnam(name, "timeout connecting to %s", hname); else zwarnnam(name, "timeout on host name lookup"); @@ -3077,7 +3077,7 @@ bin_zftp(char *name, char **args, UNUSED(Options ops), UNUSED(int func)) } queue_signals(); - if ((prefs = getsparam("ZFTP_PREFS"))) { + if ((prefs = getsparam_u("ZFTP_PREFS"))) { zfprefs = 0; for (ptr = prefs; *ptr; ptr++) { switch (toupper(STOUC(*ptr))) { @@ -3149,9 +3149,11 @@ zftp_cleanup(void) zfclose(zfsess != cursess); } zsfree(lastmsg); + lastmsg = NULL; zfunsetparam("ZFTP_SESSION"); freelinklist(zfsessions, (FreeFunc) freesession); zfree(zfstatusp, sizeof(int)*zfsesscnt); + zfstatusp = NULL; } static int diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 7b6130c6f..12e42b5bd 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -463,6 +463,8 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) #endif errno == EINTR)); + setiparam("REPLY", master); + return 0; } diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index d4051bda0..ae3a64074 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -327,9 +327,7 @@ do_completion(UNUSED(Hookdef dummy), Compldat dat) haspattern = 0; complistmax = getiparam("LISTMAX"); zsfree(complastprompt); - complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) || - (unset(ALWAYSLASTPROMPT) && zmult != 1)) ? - "yes" : ""); + complastprompt = ztrdup(isset(ALWAYSLASTPROMPT) ? "yes" : ""); dolastprompt = 1; zsfree(complist); complist = ztrdup(isset(LISTROWSFIRST) ? @@ -975,7 +973,7 @@ makecomplist(char *s, int incmd, int lst) mnum = 0; unambig_mnum = -1; isuf = NULL; - insmnum = 1; + insmnum = zmult; #if 0 /* group-numbers in compstate[insert] */ insgnum = 1; @@ -2051,7 +2049,7 @@ addmatches(Cadata dat, char **argv) Heap oldheap; SWITCHHEAPS(oldheap, compheap) { - if (dat->dummies) + if (dat->dummies >= 0) dat->aflags = ((dat->aflags | CAF_NOSORT | CAF_UNIQCON) & ~CAF_UNIQALL); @@ -2536,7 +2534,7 @@ addmatches(Cadata dat, char **argv) addmatch("<all>", dat->flags | CMF_ALL, &disp, 1); hasallmatch = 1; } - while (dat->dummies--) + while (dat->dummies-- > 0) addmatch("", dat->flags | CMF_DUMMY, &disp, 0); } SWITCHBACKHEAPS(oldheap); diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 189582d22..bac533e7e 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -1803,7 +1803,7 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat) mnum = 0; unambig_mnum = -1; isuf = NULL; - insmnum = 1; + insmnum = zmult; #if 0 /* group-numbers in compstate[insert] */ insgnum = 1; diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index ea5e41f5f..ee4e5b0a5 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -267,8 +267,12 @@ parse_cmatcher(char *name, char *s) s++; if (!*s || !*++s) { - if (name) - zwarnnam(name, "missing line pattern"); + if (name) { + if (both) + zwarnnam(name, "missing right anchor"); + else + zwarnnam(name, "missing line pattern"); + } return pcm_err; } } else @@ -288,6 +292,7 @@ parse_cmatcher(char *name, char *s) if ((fl & CMF_RIGHT) && !fl2 && (!*s || !*++s)) { if (name) zwarnnam(name, "missing right anchor"); + return pcm_err; } else if (!(fl & CMF_RIGHT) || fl2) { if (!*s) { if (name) @@ -313,8 +318,7 @@ parse_cmatcher(char *name, char *s) return pcm_err; } s++; - } else - right = NULL; + } if (*s == '*') { if (!(fl & (CMF_LEFT | CMF_RIGHT))) { @@ -537,7 +541,7 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) dat.match = NULL; dat.flags = 0; dat.aflags = CAF_MATCH; - dat.dummies = 0; + dat.dummies = -1; for (; *argv && **argv == '-'; argv++) { if (!(*argv)[1]) { @@ -1625,7 +1629,6 @@ boot_(Module m) addhookfunc("before_complete", (Hookfn) before_complete); addhookfunc("after_complete", (Hookfn) after_complete); addhookfunc("accept_completion", (Hookfn) accept_last); - addhookfunc("reverse_menu", (Hookfn) reverse_menu); addhookfunc("list_matches", (Hookfn) list_matches); addhookfunc("invalidate_list", (Hookfn) invalidate_list); (void)addhookdefs(m, comphooks, sizeof(comphooks)/sizeof(*comphooks)); @@ -1640,7 +1643,6 @@ cleanup_(Module m) deletehookfunc("before_complete", (Hookfn) before_complete); deletehookfunc("after_complete", (Hookfn) after_complete); deletehookfunc("accept_completion", (Hookfn) accept_last); - deletehookfunc("reverse_menu", (Hookfn) reverse_menu); deletehookfunc("list_matches", (Hookfn) list_matches); deletehookfunc("invalidate_list", (Hookfn) invalidate_list); (void)deletehookdefs(m, comphooks, diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index f54206619..fd90ccb31 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -507,8 +507,8 @@ getcols() max_caplen = lr_caplen = 0; mcolors.flags = 0; queue_signals(); - if (!(s = getsparam("ZLS_COLORS")) && - !(s = getsparam("ZLS_COLOURS"))) { + if (!(s = getsparam_u("ZLS_COLORS")) && + !(s = getsparam_u("ZLS_COLOURS"))) { for (i = 0; i < NUM_COLS; i++) mcolors.files[i] = filecol(""); mcolors.pats = NULL; @@ -728,7 +728,7 @@ clnicezputs(int do_colors, char *s, int ml) if (do_colors) initiscol(); - mb_metacharinit(); + mb_charinit(); while (umleft > 0) { size_t cnt = eol ? MB_INVALID : mbrtowc(&cc, uptr, umleft, &mbs); @@ -2071,6 +2071,7 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat) memset(mgtab, 0, i * sizeof(Cmgroup)); mlastcols = mcols = zterm_columns; mlastlines = mlines = listdat.nlines; + mmtabp = 0; } last_cap = (char *) zhalloc(max_caplen + 1); *last_cap = '\0'; @@ -2269,41 +2270,16 @@ msearchpop(int *backp) } static Cmatch ** -msearch(Cmatch **ptr, int ins, int back, int rep, int *wrapp) +msearch(Cmatch **ptr, char *ins, int back, int rep, int *wrapp) { -#ifdef MULTIBYTE_SUPPORT - /* MB_CUR_MAX may not be constant */ - VARARR(char, s, MB_CUR_MAX+1); -#else - char s[2]; -#endif Cmatch **p, *l = NULL, m; int x = mcol, y = mline; int ex, ey, wrap = 0, owrap = (msearchstate & MS_WRAPPED); msearchpush(ptr, back); - if (ins) { -#ifdef MULTIBYTE_SUPPORT - if (lastchar_wide_valid) - { - mbstate_t mbs; - int len; - - memset(&mbs, 0, sizeof(mbs)); - len = wcrtomb(s, lastchar_wide, &mbs); - if (len < 0) - len = 0; - s[len] = '\0'; - } else -#endif - { - s[0] = lastchar; - s[1] = '\0'; - } - - msearchstr = dyncat(msearchstr, s); - } + if (ins) + msearchstr = dyncat(msearchstr, ins); if (back) { ex = mcols - 1; ey = -1; @@ -2587,6 +2563,8 @@ domenuselect(Hookdef dummy, Chdata dat) } p = mmtabp; pg = mgtabp; + if (!p) /* selected match not in display, find line */ + continue; minfo.cur = *p; minfo.group = *pg; if (setwish) @@ -2603,6 +2581,7 @@ domenuselect(Hookdef dummy, Chdata dat) getk: if (!do_last_key) { + zmult = 1; cmd = getkeycmd(); if (mtab_been_reallocated) { do_last_key = 1; @@ -2680,7 +2659,7 @@ domenuselect(Hookdef dummy, Chdata dat) s->nbrbeg = nbrbeg; s->nbrend = nbrend; s->nmatches = nmatches; - s->origline = origline; + s->origline = dupstring(origline); s->origcs = origcs; s->origll = origll; s->status = dupstring(status); @@ -2811,7 +2790,7 @@ domenuselect(Hookdef dummy, Chdata dat) s->nbrbeg = nbrbeg; s->nbrend = nbrend; s->nmatches = nmatches; - s->origline = origline; + s->origline = dupstring(origline); s->origcs = origcs; s->origll = origll; s->status = dupstring(status); @@ -3273,38 +3252,74 @@ domenuselect(Hookdef dummy, Chdata dat) cmd == Th(z_historyincrementalsearchbackward) || ((mode == MM_FSEARCH || mode == MM_BSEARCH) && (cmd == Th(z_selfinsert) || - cmd == Th(z_selfinsertunmeta)))) { + cmd == Th(z_selfinsertunmeta) || + cmd == Th(z_bracketedpaste)))) { Cmatch **np, **op = p; int was = (mode == MM_FSEARCH || mode == MM_BSEARCH); - int ins = (cmd == Th(z_selfinsert) || cmd == Th(z_selfinsertunmeta)); + int ins = (cmd == Th(z_selfinsert) || cmd == Th(z_selfinsertunmeta) || + cmd == Th(z_bracketedpaste)); int back = (cmd == Th(z_historyincrementalsearchbackward)); int wrap; do { + char *toins = NULL; +#ifdef MULTIBYTE_SUPPORT + /* MB_CUR_MAX may not be constant */ + VARARR(char, insert, MB_CUR_MAX+1); +#else + char insert[2]; +#endif if (was) { p += wishcol - mcol; mcol = wishcol; } if (!ins) { if (was) { - if (!*msearchstr && lastsearch) { + if (!*msearchstr && lastsearch && + back == (mode == MM_BSEARCH)) { msearchstr = dupstring(lastsearch); mode = 0; } } else { msearchstr = ""; msearchstack = NULL; + msearchstate = MS_OK; } - } - if (cmd == Th(z_selfinsertunmeta)) { - fixunmeta(); - } + } else { + if (cmd == Th(z_selfinsertunmeta)) { + fixunmeta(); + } + if (cmd == Th(z_bracketedpaste)) { + toins = bracketedstring(); + } else { + toins = insert; +#ifdef MULTIBYTE_SUPPORT + if (lastchar_wide_valid) + { + mbstate_t mbs; + int len; + + memset(&mbs, 0, sizeof(mbs)); + len = wcrtomb(s, lastchar_wide, &mbs); + if (len < 0) + len = 0; + insert[len] = '\0'; + } else +#endif + { + insert[0] = lastchar; + insert[1] = '\0'; + } + } + } wrap = 0; - np = msearch(p, ins, (ins ? (mode == MM_BSEARCH) : back), + np = msearch(p, toins, (ins ? (mode == MM_BSEARCH) : back), (was && !ins), &wrap); if (!ins) mode = (back ? MM_BSEARCH : MM_FSEARCH); + else if (cmd == Th(z_bracketedpaste)) + free(toins); if (*msearchstr) { zsfree(lastsearch); diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index 9f383f4b8..7fec7c804 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -1220,25 +1220,39 @@ do_menucmp(int lst) was_meta = 1; /* Otherwise go to the next match in the array... */ - do { - if (!*++(minfo.cur)) { - do { - if (!(minfo.group = (minfo.group)->next)) { - minfo.group = amatches; + while (zmult) { + do { + if (zmult > 0) { + if (!*++(minfo.cur)) { + do { + if (!(minfo.group = (minfo.group)->next)) { + minfo.group = amatches; #ifdef ZSH_HEAP_DEBUG - if (memory_validate(minfo.group->heap_id)) { - HEAP_ERROR(minfo.group->heap_id); - } + if (memory_validate(minfo.group->heap_id)) { + HEAP_ERROR(minfo.group->heap_id); + } #endif + } + } while (!(minfo.group)->mcount); + minfo.cur = minfo.group->matches; } - } while (!(minfo.group)->mcount); - minfo.cur = minfo.group->matches; - } - } while ((menuacc && - !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) || - ((*minfo.cur)->flags & CMF_DUMMY) || - (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) && - (!(*minfo.cur)->str || !*(*minfo.cur)->str))); + } else { + if (minfo.cur == (minfo.group)->matches) { + do { + if (!(minfo.group = (minfo.group)->prev)) + minfo.group = lmatches; + } while (!(minfo.group)->mcount); + minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1; + } else + minfo.cur--; + } + } while ((menuacc && + !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) || + ((*minfo.cur)->flags & CMF_DUMMY) || + (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) && + (!(*minfo.cur)->str || !*(*minfo.cur)->str))); + zmult -= (0 < zmult) - (zmult < 0); + } /* ... and insert it into the command line. */ do_single(*minfo.cur); @@ -1246,43 +1260,6 @@ do_menucmp(int lst) unmetafy_line(); } -/**/ -int -reverse_menu(UNUSED(Hookdef dummy), UNUSED(void *dummy2)) -{ - int was_meta; - - if (minfo.cur == NULL) - return 1; - - do { - if (minfo.cur == (minfo.group)->matches) { - do { - if (!(minfo.group = (minfo.group)->prev)) - minfo.group = lmatches; - } while (!(minfo.group)->mcount); - minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1; - } else - minfo.cur--; - } while ((menuacc && - !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)) || - ((*minfo.cur)->flags & CMF_DUMMY) || - (((*minfo.cur)->flags & (CMF_NOLIST | CMF_MULT)) && - (!(*minfo.cur)->str || !*(*minfo.cur)->str))); - /* May already be metafied if called from within a selection */ - if (zlemetaline == NULL) { - metafy_line(); - was_meta = 0; - } - else - was_meta = 1; - do_single(*(minfo.cur)); - if (!was_meta) - unmetafy_line(); - - return 0; -} - /* Accepts the current completion and starts a new arg, * * with the next completions. This gives you a way to * * accept several selections from the list of matches. */ diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 27938c17f..e5db0867b 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -4196,7 +4196,7 @@ cfp_matcher_range(Cmatcher *ms, char *add) addlen = MB_METACHARLENCONV(add, &addc); #ifdef MULTIBYTE_SUPPORT if (addc == WEOF) - addc = (wchar_t)(*p == Meta ? p[1] ^ 32 : *p); + addc = (wchar_t)(*add == Meta ? add[1] ^ 32 : *add); #endif if (!(m = *mp)) { diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list index b41661a7d..2b2654c5d 100644 --- a/Src/Zle/iwidgets.list +++ b/Src/Zle/iwidgets.list @@ -28,12 +28,14 @@ "beginning-of-history", beginningofhistory, 0 "beginning-of-line", beginningofline, 0 "beginning-of-line-hist", beginningoflinehist, 0 +"bracketed-paste", bracketedpaste, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_YANKBEFORE "capitalize-word", capitalizeword, 0 "clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "copy-prev-word", copyprevword, ZLE_KEEPSUFFIX "copy-prev-shell-word", copyprevshellword, ZLE_KEEPSUFFIX "copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX +"deactivate-region", deactivateregion, 0 "delete-char", deletechar, ZLE_KEEPSUFFIX "delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "delete-word", deleteword, ZLE_KEEPSUFFIX @@ -93,7 +95,7 @@ "quoted-insert", quotedinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX "quote-line", quoteline, 0 "quote-region", quoteregion, 0 -"read-command", readcommand, 0 +"read-command", readcommand, ZLE_NOTCOMMAND "recursive-edit", recursiveedit, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "redisplay", redisplay, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "redo", redo, ZLE_KEEPSUFFIX diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index 3c652909e..59f459185 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -352,8 +352,7 @@ struct brinfo { #define BEFORECOMPLETEHOOK (zlehooks + 2) #define AFTERCOMPLETEHOOK (zlehooks + 3) #define ACCEPTCOMPHOOK (zlehooks + 4) -#define REVERSEMENUHOOK (zlehooks + 5) -#define INVALIDATELISTHOOK (zlehooks + 6) +#define INVALIDATELISTHOOK (zlehooks + 5) /* complete hook data struct */ @@ -430,8 +429,9 @@ struct region_highlight { * 0: region between point and mark * 1: isearch region * 2: suffix + * 3: pasted text */ -#define N_SPECIAL_HIGHLIGHTS (3) +#define N_SPECIAL_HIGHLIGHTS (4) #ifdef MULTIBYTE_SUPPORT diff --git a/Src/Zle/zle_bindings.c b/Src/Zle/zle_bindings.c index 2ae8c8764..55863db1b 100644 --- a/Src/Zle/zle_bindings.c +++ b/Src/Zle/zle_bindings.c @@ -317,7 +317,7 @@ int vicmdbind[128] = { /* ^X */ z_undefinedkey, /* ^Y */ z_undefinedkey, /* ^Z */ z_undefinedkey, - /* ^[ */ z_undefinedkey, + /* ^[ */ z_beep, /* ^\ */ z_undefinedkey, /* ^] */ z_undefinedkey, /* ^^ */ z_undefinedkey, diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c index cc66f99ae..c61b4ef0e 100644 --- a/Src/Zle/zle_hist.c +++ b/Src/Zle/zle_hist.c @@ -894,10 +894,8 @@ zgetline(UNUSED(char **args)) free(s); free(lineadd); clearlist = 1; - if (stackhist != -1) { - histline = stackhist; - stackhist = -1; - } + /* not restoring stackhist as we're inserting into current line */ + stackhist = -1; } return 0; } @@ -1598,7 +1596,7 @@ doisearch(char **args, int dir, int pattern) dir = odir; skip_pos = 1; rpt: - if (!sbptr && previous_search_len) { + if (!sbptr && previous_search_len && dir == odir) { if (previous_search_len > sibuf - FIRST_SEARCH_CHAR - 2) { ibuf = hrealloc((char *)ibuf, sibuf, (sibuf + previous_search_len)); @@ -1620,6 +1618,21 @@ doisearch(char **args, int dir, int pattern) feep = 1; else goto ins; + } else if (cmd == Th(z_bracketedpaste)) { + char *paste = bracketedstring(); + set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos, + zlemetacs, sbptr, dir, nomatch); + size_t pastelen = strlen(paste); + if (sbptr + pastelen >= sibuf - FIRST_SEARCH_CHAR - 2) { + int oldsize = sibuf; + sibuf += (pastelen >= sibuf) ? pastelen + 1 : sibuf; + ibuf = hrealloc(ibuf, oldsize, sibuf); + sbuf = ibuf + FIRST_SEARCH_CHAR; + } + strcpy(sbuf + sbptr, paste); + sbptr += pastelen; + patprog = NULL; + free(paste); } else if (cmd == Th(z_acceptsearch)) { break; } else { diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index c6fae251d..5b4189faa 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -540,7 +540,7 @@ reselectkeymap(void) /**/ mod_export int -bindkey(Keymap km, char *seq, Thingy bind, char *str) +bindkey(Keymap km, const char *seq, Thingy bind, char *str) { Key k; int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]); @@ -1363,6 +1363,7 @@ default_bindings(void) } /* escape in operator pending cancels the operation */ bindkey(oppmap, "\33", refthingy(t_vicmdmode), NULL); + bindkey(vismap, "\33", refthingy(t_deactivateregion), NULL); bindkey(vismap, "o", refthingy(t_exchangepointandmark), NULL); bindkey(vismap, "p", refthingy(t_putreplaceselection), NULL); bindkey(vismap, "x", refthingy(t_videlete), NULL); @@ -1400,6 +1401,11 @@ default_bindings(void) bindkey(emap, "\30\30", refthingy(t_exchangepointandmark), NULL); bindkey(emap, "\30=", refthingy(t_whatcursorposition), NULL); + /* bracketed paste applicable to all keymaps */ + bindkey(emap, "\33[200~", refthingy(t_bracketedpaste), NULL); + bindkey(vmap, "\33[200~", refthingy(t_bracketedpaste), NULL); + bindkey(amap, "\33[200~", refthingy(t_bracketedpaste), NULL); + /* emacs mode: ESC sequences, all taken from the meta binding table */ buf[0] = '\33'; buf[2] = 0; diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index cec44c0ed..e610ae1f3 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -1119,7 +1119,7 @@ zlecore(void) char * zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) { - char *s; + char *s, **bracket; int old_errno = errno; int tmout = getiparam("TMOUT"); @@ -1206,6 +1206,7 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) histline = stackhist; stackhist = -1; } + handleundo(); } /* * If main is linked to the viins keymap, we need to register @@ -1248,6 +1249,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) zlecallhook(init, NULL); + if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2) + fputs(*bracket, shout); + zrefresh(); zlecore(); @@ -1257,6 +1261,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) "ZLE_VARED_ABORTED" : "ZLE_LINE_ABORTED", zlegetline(NULL, NULL)); + if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2) + fputs(bracket[1], shout); + if (done && !exit_pending && !errflag) zlecallhook(finish, NULL); @@ -1858,7 +1865,7 @@ trashzle(void) clearflag = listshown = 0; } if (postedit) - fprintf(shout, "%s", postedit); + fprintf(shout, "%s", unmeta(postedit)); fflush(shout); resetneeded = 1; if (!(zlereadflags & ZLRF_NOSETTY)) @@ -1986,8 +1993,6 @@ mod_export struct hookdef zlehooks[] = { HOOKDEF("after_complete", NULL, 0), /* ACCEPTCOMPHOOK */ HOOKDEF("accept_completion", NULL, 0), - /* REVERSEMENUHOOK */ - HOOKDEF("reverse_menu", NULL, 0), /* INVALIDATELISTHOOK */ HOOKDEF("invalidate_list", NULL, 0), }; @@ -2004,6 +2009,8 @@ static struct features module_features = { int setup_(UNUSED(Module m)) { + char **bpaste; + /* Set up editor entry points */ zle_entry_ptr = zle_main_entry; zle_load_state = 1; @@ -2028,6 +2035,11 @@ setup_(UNUSED(Module m)) clwords = (char **) zshcalloc((clwsize = 16) * sizeof(char *)); + bpaste = zshcalloc(3*sizeof(char *)); + bpaste[0] = ztrdup("\033[?2004h"); + bpaste[1] = ztrdup("\033[?2004l"); + setaparam("zle_bracketed_paste", bpaste); + return 0; } diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c index 4669ef2ad..2d1862813 100644 --- a/Src/Zle/zle_misc.c +++ b/Src/Zle/zle_misc.c @@ -517,10 +517,12 @@ copyregionaskill(char **args) /* * kct: index into kill ring, or -1 for original cutbuffer of yank. - * yankb, yanke: mark the start and end of last yank in editing buffer. * yankcs marks the cursor position preceding the last yank */ -static int kct, yankb, yanke, yankcs; +static int kct, yankcs; + +/**/ +int yankb, yanke; /* mark the start and end of last yank in editing buffer. */ /* The original cutbuffer, either cutbuf or one of the vi buffers. */ static Cutbuffer kctbuf; @@ -736,6 +738,71 @@ yankpop(UNUSED(char **args)) } /**/ +mod_export char * +bracketedstring(void) +{ + static const char endesc[] = "\033[201~"; + int endpos = 0; + size_t psize = 64; + char *pbuf = zalloc(psize); + size_t current = 0; + int next, timeout; + + while (endesc[endpos]) { + if (current + 1 >= psize) + pbuf = zrealloc(pbuf, psize *= 2); + if ((next = getbyte(1L, &timeout)) == EOF) + break; + if (!endpos || next != endesc[endpos++]) + endpos = (next == *endesc); + if (imeta(next)) { + pbuf[current++] = Meta; + pbuf[current++] = next ^ 32; + } else if (next == '\r') + pbuf[current++] = '\n'; + else + pbuf[current++] = next; + } + pbuf[current-endpos] = '\0'; + return pbuf; +} + +/**/ +int +bracketedpaste(char **args) +{ + char *pbuf = bracketedstring(); + + if (*args) { + setsparam(*args, pbuf); + } else { + int n; + ZLE_STRING_T wpaste; + wpaste = stringaszleline((zmult == 1) ? pbuf : + quotestring(pbuf, NULL, QT_SINGLE_OPTIONAL), 0, &n, NULL, NULL); + cuttext(wpaste, n, CUT_REPLACE); + if (!(zmod.flags & MOD_VIBUF)) { + kct = -1; + kctbuf = &cutbuf; + zmult = 1; + if (region_active) + killregion(zlenoargs); + /* Chop a final newline if its insertion would be hard to + * distinguish by the user from the line being accepted. */ + else if (n > 1 && zlecontext != ZLCON_VARED && + (zlecs + (insmode ? 0 : n - 1)) >= zlell && + wpaste[n-1] == ZWC('\n')) + n--; + yankcs = yankb = zlecs; + doinsert(wpaste, n); + yanke = zlecs; + } + free(pbuf); free(wpaste); + } + return 0; +} + +/**/ int overwritemode(UNUSED(char **args)) { @@ -1264,6 +1331,22 @@ executenamedcommand(char *prmt) if (listed) clearlist = listshown = 1; curlist = 0; + } else if (cmd == Th(z_bracketedpaste)) { + char *insert = bracketedstring(); + size_t inslen = strlen(insert); + if (len + inslen > NAMLEN) + feep = 1; + else { + strcpy(ptr, insert); + len += inslen; + ptr += inslen; + if (listed) { + clearlist = listshown = 1; + listed = 0; + } else + curlist = 0; + } + free(insert); } else { if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) { Thingy r; @@ -1484,13 +1567,13 @@ makesuffix(int n) { char *suffixchars; - if (!(suffixchars = getsparam("ZLE_REMOVE_SUFFIX_CHARS"))) + if (!(suffixchars = getsparam_u("ZLE_REMOVE_SUFFIX_CHARS"))) suffixchars = " \t\n;&|"; addsuffixstring(SUFTYP_POSSTR, 0, suffixchars, n); /* Do this second so it takes precedence */ - if ((suffixchars = getsparam("ZLE_SPACE_SUFFIX_CHARS")) && *suffixchars) + if ((suffixchars = getsparam_u("ZLE_SPACE_SUFFIX_CHARS")) && *suffixchars) addsuffixstring(SUFTYP_POSSTR, SUFFLAGS_SPACE, suffixchars, n); suffixlen = n; diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c index d751c4333..f49df8647 100644 --- a/Src/Zle/zle_move.c +++ b/Src/Zle/zle_move.c @@ -555,6 +555,13 @@ visuallinemode(UNUSED(char **args)) return 0; } +/**/ +int +deactivateregion(UNUSED(char **args)) +{ + region_active = 0; + return 0; +} /**/ int @@ -876,24 +883,36 @@ int vigotomark(UNUSED(char **args)) { ZLE_INT_T ch; + int *markcs, *markhist = 0; int oldcs = zlecs; int oldline = histline; + int tmpcs, tmphist; ch = getfullchar(0); - if (ch == ZWC('\'') || ch == ZWC('`')) - ch = 26; - else { - if (ch < ZWC('a') || ch > ZWC('z')) - return 1; - ch -= ZWC('a'); - } - if (!vimarkline[ch]) - return 1; - if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0, 0)) { - vimarkline[ch] = 0; + if (ch == ZWC('\'') || ch == ZWC('`')) { + markhist = vimarkline + 26; + markcs = vimarkcs + 26; + } else if (ch == ZWC('.') && curchange->prev) { + /* position cursor where it was after the last change. not exactly + * what vim does but close enough */ + tmpcs = curchange->prev->new_cs; + tmphist = curchange->prev->hist; + markcs = &tmpcs; + markhist = &tmphist; + } else if (ch >= ZWC('a') && ch <= ZWC('z')) { + markhist = vimarkline + (ch - ZWC('a')); + markcs = vimarkcs + (ch - ZWC('a')); + } else return 1; + if (markhist) { + if (!*markhist) + return 1; + if (histline != *markhist && !zle_goto_hist(*markhist, 0, 0)) { + *markhist = 0; + return 1; + } } - zlecs = vimarkcs[ch]; + zlecs = *markcs; vimarkcs[26] = oldcs; vimarkline[26] = oldline; if (zlecs > zlell) diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c index ce4b0724d..b84e72088 100644 --- a/Src/Zle/zle_params.c +++ b/Src/Zle/zle_params.c @@ -95,6 +95,8 @@ static const struct gsu_integer region_active_gsu = { get_region_active, set_region_active, zleunsetfn }; static const struct gsu_integer undo_change_no_gsu = { get_undo_current_change, NULL, zleunsetfn }; +static const struct gsu_integer undo_limit_no_gsu = +{ get_undo_limit_change, set_undo_limit_change, zleunsetfn }; static const struct gsu_array killring_gsu = { get_killring, set_killring, unset_killring }; @@ -137,6 +139,7 @@ static struct zleparam { { "region_highlight", PM_ARRAY, GSU(region_highlight_gsu), NULL }, { "UNDO_CHANGE_NO", PM_INTEGER | PM_READONLY, GSU(undo_change_no_gsu), NULL }, + { "UNDO_LIMIT_NO", PM_INTEGER, GSU(undo_limit_no_gsu), NULL }, { "WIDGET", PM_SCALAR | PM_READONLY, GSU(widget_gsu), NULL }, { "WIDGETFUNC", PM_SCALAR | PM_READONLY, GSU(widgetfunc_gsu), NULL }, { "WIDGETSTYLE", PM_SCALAR | PM_READONLY, GSU(widgetstyle_gsu), NULL }, diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index fe337993f..78046fb7b 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -318,6 +318,7 @@ zle_set_highlight(void) int region_atr_on_set = 0; int isearch_atr_on_set = 0; int suffix_atr_on_set = 0; + int paste_atr_on_set = 0; struct region_highlight *rhp; special_atr_on = default_atr_on = 0; @@ -337,7 +338,8 @@ zle_set_highlight(void) for (; *atrs; atrs++) { if (!strcmp(*atrs, "none")) { /* reset attributes for consistency... usually unnecessary */ - special_atr_on = default_atr_on = 0; + special_atr_on = default_atr_on = + paste_atr_on_set = 0; special_atr_on_set = region_atr_on_set = isearch_atr_on_set = suffix_atr_on_set = 1; } else if (strpfx("default:", *atrs)) { @@ -354,6 +356,9 @@ zle_set_highlight(void) } else if (strpfx("suffix:", *atrs)) { match_highlight(*atrs + 7, &(region_highlights[2].atr)); suffix_atr_on_set = 1; + } else if (strpfx("paste:", *atrs)) { + match_highlight(*atrs + 6, &(region_highlights[3].atr)); + paste_atr_on_set = 1; } } } @@ -367,6 +372,7 @@ zle_set_highlight(void) region_highlights[1].atr = TXTUNDERLINE; if (!suffix_atr_on_set) region_highlights[2].atr = TXTBOLDFACE; + /* paste defaults to 0 */ allocate_colour_buffer(); } @@ -1073,6 +1079,13 @@ zrefresh(void) region_highlights[2].start = region_highlights[2].end = -1; } + if (lastcmd & ZLE_YANK) { + region_highlights[3].start = yankb; + region_highlights[3].end = yanke; + } else { + region_highlights[3].start = region_highlights[3].end = -1; + } + if (clearlist && listshown > 0) { if (tccan(TCCLEAREOD)) { int ovln = vln, ovcs = vcs; diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index f18ad170e..b87b99b00 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -345,17 +345,8 @@ mod_export int reversemenucomplete(char **args) { wouldinstab = 0; - if (!menucmp) { - menucomplete(args); - /* - * Drop through, since we are now on the first item instead of - * the last. We've already updated the display, so this is a - * bit inefficient, but it's simple and it works. - */ - } - - runhookdef(REVERSEMENUHOOK, NULL); - return 0; + zmult = -zmult; + return menucomplete(args); } /**/ @@ -730,11 +721,12 @@ docomplete(int lst) } } } - if (lst == COMP_EXPAND_COMPLETE) + if (lst == COMP_EXPAND_COMPLETE) { do { /* Check if there is a parameter expression. */ for (; *q && *q != String; q++); - if (*q == String && q[1] != Inpar && q[1] != Inbrack) { + if (*q == String && q[1] != Inpar && q[1] != Inparmath && + q[1] != Inbrack) { if (*++q == Inbrace) { if (! skipparens(Inbrace, Outbrace, &q) && q == s + zlemetacs - wb) @@ -778,6 +770,7 @@ docomplete(int lst) } else break; } while (q < s + zlemetacs - wb); + } if (lst == COMP_EXPAND_COMPLETE) { /* If it is still not clear if we should use expansion or * * completion and there is a `$' or a backtick in the word, * @@ -1182,14 +1175,30 @@ get_comp_string(void) do { qsub = noword = 0; - lincmd = ((incmdpos && !ins && !incond) || - (oins == 2 && wordpos == 2) || - (ins == 3 && wordpos == 1)); + /* + * pws: added cmdtok == NULLTOK test as fallback for detecting + * we haven't had a command yet. This is a cop out: it's needed + * after SEPER because of bizarre and incomprehensible dance + * that we otherwise do involving the "ins" flag when you might + * have thought we'd just reset everything because we're now + * considering a new command. Consequently, although this looks + * relatively harmless by itself, it's probably incomplete. + */ + lincmd = (incmdpos && !ins && !incond) || + (oins == 2 && wordpos == 2) || + (ins == 3 && wordpos == 1) || + (cmdtok == NULLTOK && !incond); linredir = (inredir && !ins); oins = ins; /* Get the next token. */ if (linarr) incmdpos = 0; + /* + * Arrange to parse assignments after typeset etc... + * but not if we're already in an array. + */ + if (cmdtok == TYPESET) + intypeset = !linarr; ctxtlex(); if (tok == LEXERR) { @@ -1272,10 +1281,11 @@ get_comp_string(void) tt0 = NULLTOK; } if (lincmd && (tok == STRING || tok == FOR || tok == FOREACH || - tok == SELECT || tok == REPEAT || tok == CASE)) { + tok == SELECT || tok == REPEAT || tok == CASE || + tok == TYPESET)) { /* The lexer says, this token is in command position, so * * store the token string (to find the right compctl). */ - ins = (tok == REPEAT ? 2 : (tok != STRING)); + ins = (tok == REPEAT ? 2 : (tok != STRING && tok != TYPESET)); zsfree(cmdstr); cmdstr = ztrdup(tokstr); cmdtok = tok; @@ -1290,7 +1300,7 @@ get_comp_string(void) * handle completing multiple SEPER-ated command positions on * the same command line, e.g., pipelines. */ - ins = (cmdtok != STRING); + ins = (cmdtok != STRING && cmdtok != TYPESET); } if (!lexflags && tt0 == NULLTOK) { /* This is done when the lexer reached the word the cursor is on. */ @@ -1436,7 +1446,7 @@ get_comp_string(void) we = wb = zlemetacs; clwpos = clwnum; t0 = STRING; - } else if (t0 == STRING) { + } else if (t0 == STRING || t0 == TYPESET) { /* We found a simple string. */ s = ztrdup(clwords[clwpos]); } else if (t0 == ENVSTRING) { @@ -1492,7 +1502,7 @@ get_comp_string(void) zlemetaline = tmp; zlemetall = strlen(zlemetaline); } - if (t0 != STRING && inwhat != IN_MATH) { + if (t0 != STRING && t0 != TYPESET && inwhat != IN_MATH) { if (tmp) { tmp = NULL; linptr = zlemetaline; diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index e4ab97a54..d1d320613 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -1288,7 +1288,7 @@ showmsg(char const *msg) p = unmetafy(umsg, &ulen); memset(&mbs, 0, sizeof mbs); - mb_metacharinit(); + mb_charinit(); while (ulen > 0) { char const *n; if (*p == '\n') { @@ -1405,9 +1405,9 @@ static struct change *nextchanges, *endnextchanges; /**/ zlong undo_changeno; -/* If non-zero, the last increment to undo_changeno was for the variable */ +/* If positive, don't undo beyond this point */ -static int undo_set_by_variable; +zlong undo_limitno; /**/ void @@ -1418,8 +1418,7 @@ initundo(void) curchange->prev = curchange->next = NULL; curchange->del = curchange->ins = NULL; curchange->dell = curchange->insl = 0; - curchange->changeno = undo_changeno = 0; - undo_set_by_variable = 0; + curchange->changeno = undo_changeno = undo_limitno = 0; lastline = zalloc((lastlinesz = linesz) * ZLE_CHAR_SIZE); ZS_memcpy(lastline, zleline, (lastll = zlell)); lastcs = zlecs; @@ -1545,7 +1544,6 @@ mkundoent(void) ch->prev = NULL; } ch->changeno = ++undo_changeno; - undo_set_by_variable = 0; endnextchanges = ch; } @@ -1580,12 +1578,13 @@ undo(char **args) struct change *prev = curchange->prev; if(!prev) return 1; - if (prev->changeno < last_change) - break; - if (unapplychange(prev)) - curchange = prev; - else + if (prev->changeno <= last_change) break; + if (prev->changeno <= undo_limitno && !*args) + return 1; + if (!unapplychange(prev) && last_change >= 0) + unapplychange(prev); + curchange = prev; } while (last_change >= (zlong)0 || (curchange->flags & CH_PREV)); setlastline(); return 0; @@ -1735,16 +1734,22 @@ zlecallhook(char *name, char *arg) zlong get_undo_current_change(UNUSED(Param pm)) { - if (undo_set_by_variable) { - /* We were the last to increment this, doesn't need another one. */ - return undo_changeno; - } - undo_set_by_variable = 1; - /* - * Increment the number in case a change is in progress; - * we don't want to back off what's already been done when - * we return to this change number. This eliminates any - * problem about the point where a change is numbered. - */ - return ++undo_changeno; + /* add entry for any pending changes */ + mkundoent(); + setlastline(); + return undo_changeno; +} + +/**/ +zlong +get_undo_limit_change(UNUSED(Param pm)) +{ + return undo_limitno; +} + +/**/ +void +set_undo_limit_change(UNUSED(Param pm), zlong value) +{ + undo_limitno = value; } diff --git a/Src/builtin.c b/Src/builtin.c index 9358e8b1f..3d34aa74c 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -53,7 +53,7 @@ static struct builtin builtins[] = BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL), BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL), BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL), - BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL), + BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL), BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL), BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL), BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL), @@ -62,7 +62,7 @@ static struct builtin builtins[] = BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL), BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL), BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL), - BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"), + BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, BIN_EXPORT, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"), BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL), /* * We used to behave as if the argument to -e was optional. @@ -71,8 +71,8 @@ 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, bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"), - BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUz", 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("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), @@ -82,11 +82,11 @@ static struct builtin builtins[] = #endif BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "adDEfiLmnpPrt:", "l"), - BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lprtux", "i"), + BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lprtux", "i"), BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL), BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL), BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL), - BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL), + BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL), BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL), BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL), @@ -99,14 +99,14 @@ static struct builtin builtins[] = #endif 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:z-", NULL), + BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "abcC:Df:ilmnNoOpPrRsSu:x:X:z-", NULL), BUILTIN("printf", 0, bin_print, 1, -1, BIN_PRINTF, NULL, 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), - BUILTIN("r", 0, bin_fc, 0, -1, BIN_R, "nrlL", NULL), + BUILTIN("r", 0, bin_fc, 0, -1, BIN_R, "IlLnr", NULL), BUILTIN("read", 0, bin_read, 0, -1, 0, "cd:ek:%lnpqrst:%zu:AE", NULL), - BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"), + BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"), BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "df", "r"), BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL), BUILTIN("set", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_set, 0, -1, 0, NULL, NULL), @@ -120,7 +120,7 @@ static struct builtin builtins[] = BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL), BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL), BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"), - BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL), + BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL), BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL), BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL), BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"), @@ -128,9 +128,9 @@ static struct builtin builtins[] = BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, 0, "fmv", NULL), BUILTIN("unsetopt", 0, bin_setopt, 0, -1, BIN_UNSETOPT, NULL, NULL), BUILTIN("wait", 0, bin_fg, 0, -1, BIN_WAIT, NULL, NULL), - BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSw", NULL), - BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsSw", "ca"), - BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsSw", "c"), + BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSwx:", NULL), + BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsSwx:", "ca"), + BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsSwx:", "c"), BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "AFRILP:abcfdilmpue", NULL), BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUMRcmzka", NULL), }; @@ -246,7 +246,7 @@ new_optarg(Options ops) /**/ int -execbuiltin(LinkList args, Builtin bn) +execbuiltin(LinkList args, LinkList assigns, Builtin bn) { char *pp, *name, *optstr; int flags, sense, argc, execop, xtr = isset(XTRACE); @@ -443,11 +443,46 @@ execbuiltin(LinkList args, Builtin bn) fputc(' ', xtrerr); quotedzputs(*fullargv++, xtrerr); } + if (assigns) { + LinkNode node; + for (node = firstnode(assigns); node; incnode(node)) { + Asgment asg = (Asgment)node; + fputc(' ', xtrerr); + quotedzputs(asg->name, xtrerr); + if (asg->is_array) { + LinkNode arrnode; + fprintf(xtrerr, "=("); + if (asg->value.array) { + for (arrnode = firstnode(asg->value.array); + arrnode; + incnode(arrnode)) { + fputc(' ', xtrerr); + quotedzputs((char *)getdata(arrnode), xtrerr); + } + } + fprintf(xtrerr, " )"); + } else if (asg->value.scalar) { + fputc('=', xtrerr); + quotedzputs(asg->value.scalar, xtrerr); + } + } + } fputc('\n', xtrerr); fflush(xtrerr); } /* call the handler function, and return its return value */ - return (*(bn->handlerfunc)) (name, argv, &ops, bn->funcid); + if (flags & BINF_ASSIGN) + { + /* + * Takes two sets of arguments. + */ + HandlerFuncAssign assignfunc = (HandlerFuncAssign)bn->handlerfunc; + return (*(assignfunc)) (name, argv, assigns, &ops, bn->funcid); + } + else + { + return (*(bn->handlerfunc)) (name, argv, &ops, bn->funcid); + } } } @@ -1128,7 +1163,7 @@ cd_try_chdir(char *pfix, char *dest, int hard) * or a parent directory is renamed in the interim. */ if (lchdir(buf, NULL, hard) && - (pfix || *dest == '/' || lchdir(dest, NULL, hard))) { + (pfix || *dest == '/' || lchdir(unmeta(dest), NULL, hard))) { free(buf); return NULL; } @@ -1435,10 +1470,6 @@ bin_fc(char *nam, char **argv, Options ops, int func) unqueue_signals(); return 0; } - if (OPT_ISSET(ops,'I')) { - zwarnnam(nam, "-I requires one of -R/-W/-A"); - return 1; - } if (zleactive) { zwarnnam(nam, "no interactive history within ZLE"); @@ -1456,12 +1487,13 @@ bin_fc(char *nam, char **argv, Options ops, int func) if (!asgf) asgf = asgl = a; else { - asgl->next = a; + asgl->node.next = &a->node; asgl = a; } a->name = *argv; - a->value = s; - a->next = NULL; + a->is_array = 0; + a->value.scalar = s; + a->node.next = a->node.prev = NULL; argv++; } /* interpret and check first history line specifier */ @@ -1635,8 +1667,8 @@ fcsubs(char **sp, struct asgment *sub) /* loop through the linked list */ while (sub) { oldstr = sub->name; - newstr = sub->value; - sub = sub->next; + newstr = sub->value.scalar; + sub = (Asgment)sub->node.next; oldpos = s; /* loop over occurences of oldstr in s, replacing them with newstr */ while ((newpos = (char *)strstr(oldpos, oldstr))) { @@ -1672,7 +1704,7 @@ static int fclist(FILE *f, Options ops, zlong first, zlong last, struct asgment *subs, Patprog pprog, int is_command) { - int fclistdone = 0; + int fclistdone = 0, xflags = 0; zlong tmp; char *s, *tdfmt, *timebuf; Histent ent; @@ -1722,11 +1754,19 @@ fclist(FILE *f, Options ops, zlong first, zlong last, tdfmt = timebuf = NULL; } + /* xflags exclude events */ + if (OPT_ISSET(ops,'L')) { + xflags |= HIST_FOREIGN; + } + if (OPT_ISSET(ops,'I')) { + xflags |= HIST_READ; + } + for (;;) { - if (!OPT_ISSET(ops,'L') || !(ent->node.flags & HIST_FOREIGN)) - s = dupstring(ent->node.nam); - else + if (ent->node.flags & xflags) s = NULL; + else + s = dupstring(ent->node.nam); /* this if does the pattern matching, if required */ if (s && (!pprog || pattry(pprog, s))) { /* perform substitution */ @@ -1743,9 +1783,12 @@ fclist(FILE *f, Options ops, zlong first, zlong last, command, if required */ if (tdfmt != NULL) { struct tm *ltm; + int len; ltm = localtime(&ent->stim); - if (ztrftime(timebuf, 256, tdfmt, ltm, 0L)) - fprintf(f, "%s ", timebuf); + if ((len = ztrftime(timebuf, 256, tdfmt, ltm, 0L)) >= 0) { + fwrite(timebuf, 1, len, f); + fprintf(f, " "); + } } /* display the time taken by the command, if required */ if (OPT_ISSET(ops,'D')) { @@ -1782,7 +1825,7 @@ fclist(FILE *f, Options ops, zlong first, zlong last, if (!fclistdone) { if (subs) zwarnnam("fc", "no substitutions performed"); - else if (OPT_ISSET(ops,'L') || pprog) + else if (xflags || pprog) zwarnnam("fc", "no matching events found"); return 1; } @@ -1816,13 +1859,22 @@ fcedit(char *ename, char *fn) /**/ static Asgment -getasg(char *s) +getasg(char ***argvp, LinkList assigns) { + char *s = **argvp; static struct asgment asg; /* sanity check for valid argument */ - if (!s) + if (!s) { + if (assigns) { + Asgment asgp = (Asgment)firstnode(assigns); + if (!asgp) + return NULL; + (void)uremnode(assigns, &asgp->node); + return asgp; + } return NULL; + } /* check if name is empty */ if (*s == '=') { @@ -1830,6 +1882,7 @@ getasg(char *s) return NULL; } asg.name = s; + asg.is_array = 0; /* search for `=' */ for (; *s && *s != '='; s++); @@ -1837,11 +1890,12 @@ getasg(char *s) /* found `=', so return with a value */ if (*s) { *s = '\0'; - asg.value = s + 1; + asg.value.scalar = s + 1; } else { /* didn't find `=', so we only have a name */ - asg.value = NULL; + asg.value.scalar = NULL; } + (*argvp)++; return &asg; } @@ -1923,10 +1977,10 @@ typeset_setwidth(const char * name, Param pm, Options ops, int on, int always) /**/ static Param typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), - int on, int off, int roff, char *value, Param altpm, + int on, int off, int roff, Asgment asg, Param altpm, Options ops, int joinchar) { - int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly; + int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly, dont_set = 0; char *subscript; /* @@ -1971,7 +2025,24 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), /* attempting a type conversion, or making a tied colonarray? */ tc = 0; - if (usepm || newspecial != NS_NONE) { + if (ASG_ARRAYP(asg) && PM_TYPE(on) == PM_SCALAR && + !(usepm && (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)))) + on |= PM_ARRAY; + if (usepm && ASG_ARRAYP(asg) && newspecial == NS_NONE && + PM_TYPE(pm->node.flags) != PM_ARRAY && + PM_TYPE(pm->node.flags) != PM_HASHED) { + if (on & (PM_EFLOAT|PM_FFLOAT|PM_INTEGER)) { + zerrnam(cname, "%s: can't assign array value to non-array", pname); + return NULL; + } + if (pm->node.flags & PM_SPECIAL) { + zerrnam(cname, "%s: can't assign array value to non-array special", pname); + return NULL; + } + tc = 1; + usepm = 0; + } + else if (usepm || newspecial != NS_NONE) { int chflags = ((off & pm->node.flags) | (on & ~pm->node.flags)) & (PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED| PM_ARRAY|PM_TIED|PM_AUTOLOAD); @@ -2019,7 +2090,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), tc = 0; /* but don't do a normal conversion */ } } else if (!setsecondstype(pm, on, off)) { - if (value && !(pm = setsparam(pname, ztrdup(value)))) + if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar)))) return NULL; usepm = 1; err = 0; @@ -2045,7 +2116,7 @@ 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)) && - !value) + !ASG_VALUEP(asg)) on |= PM_UNSET; else if (usepm && (pm->node.flags & PM_READONLY) && !(on & PM_READONLY)) { @@ -2064,8 +2135,15 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), * ii. we are creating a new local parameter */ if (usepm) { + if (asg->is_array ? + !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) : + (asg->value.scalar && (PM_TYPE(pm->node.flags & + (PM_ARRAY|PM_HASHED))))) { + zerrnam(cname, "%s: inconsistent type for assignment", pname); + return NULL; + } on &= ~PM_LOCAL; - if (!on && !roff && !value) { + if (!on && !roff && !ASG_VALUEP(asg)) { if (OPT_ISSET(ops,'p')) paramtab->printnode(&pm->node, PRINT_TYPESET); else if (!OPT_ISSET(ops,'g') && @@ -2119,15 +2197,18 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } if (!(pm->node.flags & (PM_ARRAY|PM_HASHED))) { if (pm->node.flags & PM_EXPORTED) { - if (!(pm->node.flags & PM_UNSET) && !pm->env && !value) + if (!(pm->node.flags & PM_UNSET) && !pm->env && !ASG_VALUEP(asg)) addenv(pm, getsparam(pname)); } else if (pm->env && !(pm->node.flags & PM_HASHELEM)) delenv(pm); - if (value && !(pm = setsparam(pname, ztrdup(value)))) + DPUTS(ASG_ARRAYP(asg), "BUG: typeset got array value where scalar expected"); + if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar)))) + return NULL; + } else if (asg->is_array) { + if (!(pm = setaparam(pname, asg->value.array ? + zlinklist2array(asg->value.array) : + mkarray(NULL)))) return NULL; - } else if (value) { - zwarnnam(cname, "can't assign new value for array %s", pname); - return NULL; } pm->node.flags |= (on & PM_READONLY); if (OPT_ISSET(ops,'p')) @@ -2135,6 +2216,13 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), return pm; } + if (asg->is_array ? + !(on & (PM_ARRAY|PM_HASHED)) : + (asg->value.scalar && (on & (PM_ARRAY|PM_HASHED)))) { + zerrnam(cname, "%s: inconsistent type for assignment", pname); + return NULL; + } + /* * We're here either because we're creating a new parameter, * or we're adding a parameter at a different local level, @@ -2154,9 +2242,14 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), /* * Try to carry over a value, but not when changing from, * to, or between non-scalar types. + * + * (We can do better now, but it does have user-visible + * implications.) */ - if (!value && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED))) - value = dupstring(getsparam(pname)); + if (!ASG_VALUEP(asg) && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED))) { + asg->value.scalar = dupstring(getsparam(pname)); + asg->is_array = 0; + } /* pname may point to pm->nam which is about to disappear */ pname = dupstring(pname); unsetparam_pm(pm, 0, 1); @@ -2235,7 +2328,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), zerrnam(cname, "%s: can't create readonly array elements", pname); return NULL; - } else if (on & PM_LOCAL) { + } else if ((on & PM_LOCAL) && locallevel) { *subscript = 0; pm = (Param) (paramtab == realparamtab ? gethashnode2(paramtab, pname) : @@ -2247,21 +2340,30 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), return NULL; } } - if (PM_TYPE(on) == PM_SCALAR) { + if (PM_TYPE(on) == PM_SCALAR && !ASG_ARRAYP(asg)) { /* * This will either complain about bad identifiers, or will set * a hash element or array slice. This once worked by accident, * creating a stray parameter along the way via createparam(), * now called below in the isident() branch. */ - if (!(pm = setsparam(pname, ztrdup(value ? value : "")))) + if (!(pm = setsparam(pname, ztrdup(asg->value.scalar ? asg->value.scalar : "")))) return NULL; - value = NULL; + dont_set = 1; + asg->is_array = 0; + keeplocal = 0; + on = pm->node.flags; + } else if (PM_TYPE(on) == PM_ARRAY && ASG_ARRAYP(asg)) { + if (!(pm = setaparam(pname, asg->value.array ? + zlinklist2array(asg->value.array) : + mkarray(NULL)))) + return NULL; + dont_set = 1; keeplocal = 0; on = pm->node.flags; } else { zerrnam(cname, - "%s: array elements must be scalar", pname); + "%s: inconsistent array element or slice assignment", pname); return NULL; } } @@ -2327,10 +2429,19 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), pm->level = keeplocal; else if (on & PM_LOCAL) pm->level = locallevel; - if (value && !(pm->node.flags & (PM_ARRAY|PM_HASHED))) { + if (ASG_VALUEP(asg) && !dont_set) { Param ipm = pm; - if (!(pm = setsparam(pname, ztrdup(value)))) - return NULL; + if (pm->node.flags & (PM_ARRAY|PM_HASHED)) { + DPUTS(!ASG_ARRAYP(asg), "BUG: inconsistent scalar value for array"); + if (!(pm=setaparam(pname, asg->value.array ? + zlinklist2array(asg->value.array) : + mkarray(NULL)))) + return NULL; + } else { + DPUTS(ASG_ARRAYP(asg), "BUG: inconsistent array value for scalar"); + if (!(pm = setsparam(pname, ztrdup(asg->value.scalar)))) + return NULL; + } if (pm != ipm) { DPUTS(ipm->node.flags != pm->node.flags, "BUG: parameter recreated with wrong flags"); @@ -2367,12 +2478,6 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } } pm->node.flags |= (on & PM_READONLY); - if (value && (pm->node.flags & (PM_ARRAY|PM_HASHED))) { - zerrnam(cname, "%s: can't assign initial value for array", pname); - /* the only safe thing to do here seems to be unset the param */ - unsetparam_pm(pm, 0, 1); - return NULL; - } if (OPT_ISSET(ops,'p')) paramtab->printnode(&pm->node, PRINT_TYPESET); @@ -2380,11 +2485,18 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), return pm; } -/* declare, export, integer, local, readonly, typeset */ +/* + * declare, export, float, integer, local, readonly, typeset + * + * Note the difference in interface from most builtins, covered by the + * BINF_ASSIGN builtin flag. This is only made use of by builtins + * called by reserved word, which only covers declare, local, readonly + * and typeset. Otherwise assigns is NULL. + */ /**/ int -bin_typeset(char *name, char **argv, Options ops, int func) +bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) { Param pm; Asgment asg; @@ -2393,6 +2505,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) int on = 0, off = 0, roff, bit = PM_ARRAY; int i; int returnval = 0, printflags = 0; + int hasargs; /* hash -f is really the builtin `functions' */ if (OPT_ISSET(ops,'f')) @@ -2445,7 +2558,8 @@ bin_typeset(char *name, char **argv, Options ops, int func) /* Given no arguments, list whatever the options specify. */ if (OPT_ISSET(ops,'p')) printflags |= PRINT_TYPESET; - if (!*argv) { + hasargs = *argv != NULL || (assigns && firstnode(assigns)); + if (!hasargs) { if (!OPT_ISSET(ops,'p')) { if (!(on|roff)) printflags |= PRINT_TYPE; @@ -2464,9 +2578,9 @@ bin_typeset(char *name, char **argv, Options ops, int func) if (on & PM_TIED) { Param apm; - struct asgment asg0; - char *oldval = NULL; - int joinchar; + struct asgment asg0, asg2; + char *oldval = NULL, *joinstr; + int joinchar, nargs; if (OPT_ISSET(ops,'m')) { zwarnnam(name, "incompatible options for -T"); @@ -2474,34 +2588,41 @@ bin_typeset(char *name, char **argv, Options ops, int func) return 1; } on &= ~off; - if (!argv[1] || (argv[2] && argv[3])) { + nargs = arrlen(argv) + (assigns ? countlinknodes(assigns) : 0); + if (nargs < 2) { zwarnnam(name, "-T requires names of scalar and array"); unqueue_signals(); return 1; } + if (nargs > 3) { + zwarnnam(name, "too many arguments for -T"); + unqueue_signals(); + return 1; + } - /* - * Third argument, if given, is character used to join - * the elements of the array in the scalar. - */ - if (!argv[2]) - joinchar = ':'; - else if (!*argv[2]) - joinchar = 0; - else if (*argv[2] == Meta) - joinchar = argv[2][1] ^ 32; - else - joinchar = *argv[2]; - - if (!(asg = getasg(argv[0]))) { + if (!(asg = getasg(&argv, assigns))) { unqueue_signals(); return 1; } asg0 = *asg; - if (!(asg = getasg(argv[1]))) { + if (ASG_ARRAYP(&asg0)) { + unqueue_signals(); + zwarnnam(name, "first argument of tie must be scalar: %s", + asg0.name); + return 1; + } + + if (!(asg = getasg(&argv, assigns))) { + unqueue_signals(); + return 1; + } + if (!ASG_ARRAYP(asg) && asg->value.scalar) { unqueue_signals(); + zwarnnam(name, "second argument of tie must be array: %s", + asg->name); return 1; } + if (!strcmp(asg0.name, asg->name)) { unqueue_signals(); zerrnam(name, "can't tie a variable to itself: %s", asg0.name); @@ -2512,6 +2633,36 @@ bin_typeset(char *name, char **argv, Options ops, int func) zerrnam(name, "can't tie array elements: %s", asg0.name); return 1; } + if (ASG_VALUEP(asg) && ASG_VALUEP(&asg0)) { + unqueue_signals(); + zerrnam(name, "only one tied parameter can have value: %s", asg0.name); + return 1; + } + + /* + * Third argument, if given, is character used to join + * the elements of the array in the scalar. + */ + if (*argv) + joinstr = *argv; + else if (assigns && firstnode(assigns)) { + Asgment nextasg = (Asgment)firstnode(assigns); + if (ASG_ARRAYP(nextasg) || ASG_VALUEP(nextasg)) { + zwarnnam(name, "third argument of tie must be join character"); + unqueue_signals(); + return 1; + } + joinstr = nextasg->name; + } else + joinstr = NULL; + if (!joinstr) + joinchar = ':'; + else if (!*joinstr) + joinchar = 0; + else if (*joinstr == Meta) + joinchar = joinstr[1] ^ 32; + else + joinchar = *joinstr; /* * Keep the old value of the scalar. We need to do this * here as if it is already tied to the same array it @@ -2526,15 +2677,19 @@ bin_typeset(char *name, char **argv, Options ops, int func) && (locallevel == pm->level || !(on & PM_LOCAL))) { if (pm->node.flags & PM_TIED) { unqueue_signals(); - if (!strcmp(asg->name, pm->ename)) { + if (PM_TYPE(pm->node.flags) != PM_SCALAR) { + zwarnnam(name, "already tied as non-scalar: %s", asg0.name); + } else if (!strcmp(asg->name, pm->ename)) { /* * Already tied in the fashion requested. */ struct tieddata *tdp = (struct tieddata*)pm->u.data; /* Update join character */ tdp->joinchar = joinchar; - if (asg0.value) - setsparam(asg0.name, ztrdup(asg0.value)); + if (asg0.value.scalar) + setsparam(asg0.name, ztrdup(asg0.value.scalar)); + else if (asg->value.array) + setaparam(asg->name, zlinklist2array(asg->value.array)); return 0; } else { zwarnnam(name, "can't tie already tied scalar: %s", @@ -2542,7 +2697,8 @@ bin_typeset(char *name, char **argv, Options ops, int func) } return 1; } - if (!asg0.value && !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED))) + if (!asg0.value.scalar && !asg->value.array && + !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED))) oldval = ztrdup(getsparam(asg0.name)); on |= (pm->node.flags & PM_EXPORTED); } @@ -2550,12 +2706,18 @@ bin_typeset(char *name, char **argv, Options ops, int func) * Create the tied array; this is normal except that * it has the PM_TIED flag set. Do it first because * we need the address. + * + * Don't attempt to set it yet, it's too early + * to be exported properly. */ + asg2.name = asg->name; + asg2.is_array = 0; + asg2.value.array = (LinkList)0; if (!(apm=typeset_single(name, asg->name, (Param)paramtab->getnode(paramtab, asg->name), func, (on | PM_ARRAY) & ~PM_EXPORTED, - off, roff, asg->value, NULL, ops, 0))) { + off, roff, &asg2, NULL, ops, 0))) { if (oldval) zsfree(oldval); unqueue_signals(); @@ -2568,7 +2730,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) if (!(pm=typeset_single(name, asg0.name, (Param)paramtab->getnode(paramtab, asg0.name), - func, on, off, roff, asg0.value, apm, + func, on, off, roff, &asg0, apm, ops, joinchar))) { if (oldval) zsfree(oldval); @@ -2587,7 +2749,9 @@ bin_typeset(char *name, char **argv, Options ops, int func) if (apm->ename) zsfree(apm->ename); apm->ename = ztrdup(asg0.name); - if (oldval) + if (asg->value.array) + setaparam(asg->name, zlinklist2array(asg->value.array)); + else if (oldval) setsparam(asg0.name, oldval); unqueue_signals(); @@ -2607,18 +2771,18 @@ bin_typeset(char *name, char **argv, Options ops, int func) printflags |= PRINT_NAMEONLY; } - while ((asg = getasg(*argv++))) { + while ((asg = getasg(&argv, assigns))) { LinkList pmlist = newlinklist(); LinkNode pmnode; tokenize(asg->name); /* expand argument */ if (!(pprog = patcompile(asg->name, 0, NULL))) { untokenize(asg->name); - zwarnnam(name, "bad pattern : %s", argv[-1]); + zwarnnam(name, "bad pattern : %s", asg->name); returnval = 1; continue; } - if (OPT_PLUS(ops,'m') && !asg->value) { + if (OPT_PLUS(ops,'m') && !ASG_VALUEP(asg)) { scanmatchtable(paramtab, pprog, 1, on|roff, 0, paramtab->printnode, printflags); continue; @@ -2644,7 +2808,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) { pm = (Param) getdata(pmnode); if (!typeset_single(name, pm->node.nam, pm, func, on, off, roff, - asg->value, NULL, ops, 0)) + asg, NULL, ops, 0)) returnval = 1; } } @@ -2653,7 +2817,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) } /* Take arguments literally. Don't glob */ - while ((asg = getasg(*argv++))) { + while ((asg = getasg(&argv, assigns))) { HashNode hn = (paramtab == realparamtab ? gethashnode2(paramtab, asg->name) : paramtab->getnode(paramtab, asg->name)); @@ -2667,7 +2831,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) continue; } if (!typeset_single(name, asg->name, (Param)hn, - func, on, off, roff, asg->value, NULL, + func, on, off, roff, asg, NULL, ops, 0)) returnval = 1; } @@ -2749,7 +2913,7 @@ bin_functions(char *name, char **argv, Options ops, int func) Patprog pprog; Shfunc shf; int i, returnval = 0; - int on = 0, off = 0, pflags = 0, roff; + int on = 0, off = 0, pflags = 0, roff, expand = 0; /* Do we have any flags defined? */ if (OPT_PLUS(ops,'u')) @@ -2785,11 +2949,23 @@ bin_functions(char *name, char **argv, Options ops, int func) } 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))) { zwarnnam(name, "invalid option(s)"); return 1; } + if (OPT_ISSET(ops,'x')) { + char *eptr; + expand = (int)zstrtol(OPT_ARG(ops,'x'), &eptr, 10); + if (*eptr) { + zwarnnam(name, "number expected after -x"); + return 1; + } + if (expand == 0) /* no indentation at all */ + expand = -1; + } + if (OPT_PLUS(ops,'f') || roff || OPT_ISSET(ops,'+')) pflags |= PRINT_NAMEONLY; @@ -2948,8 +3124,8 @@ bin_functions(char *name, char **argv, Options ops, int func) } else { if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u')) on &= ~PM_UNDEFINED; - scanhashtable(shfunctab, 1, on|off, DISABLED, shfunctab->printnode, - pflags); + scanshfunc(1, on|off, DISABLED, shfunctab->printnode, + pflags, expand); } unqueue_signals(); return ret; @@ -2965,8 +3141,8 @@ bin_functions(char *name, char **argv, Options ops, int func) /* with no options, just print all functions matching the glob pattern */ queue_signals(); if (!(on|off) && !OPT_ISSET(ops,'X')) { - scanmatchtable(shfunctab, pprog, 1, 0, DISABLED, - shfunctab->printnode, pflags); + scanmatchshfunc(pprog, 1, 0, DISABLED, + shfunctab->printnode, pflags, expand); } else { /* apply the options to all functions matching the glob pattern */ for (i = 0; i < shfunctab->hsize; i++) { @@ -3008,7 +3184,7 @@ bin_functions(char *name, char **argv, Options ops, int func) returnval = 1; } else /* no flags, so just print */ - shfunctab->printnode(&shf->node, pflags); + printshfuncexpand(&shf->node, pflags, expand); } else if (on & PM_UNDEFINED) { int signum = -1, ok = 1; @@ -3222,6 +3398,7 @@ bin_whence(char *nam, char **argv, Options ops, int func) int aliasflags; int csh, all, v, wd; int informed = 0; + int expand = 0; char *cnam, **allmatched = 0; /* Check some option information */ @@ -3230,6 +3407,17 @@ bin_whence(char *nam, char **argv, Options ops, int func) all = OPT_ISSET(ops,'a'); wd = OPT_ISSET(ops,'w'); + if (OPT_ISSET(ops,'x')) { + char *eptr; + expand = (int)zstrtol(OPT_ARG(ops,'x'), &eptr, 10); + if (*eptr) { + zwarnnam(nam, "number expected after -x"); + return 1; + } + if (expand == 0) /* no indentation at all */ + expand = -1; + } + if (OPT_ISSET(ops,'w')) printflags |= PRINT_WHENCE_WORD; else if (OPT_ISSET(ops,'c')) @@ -3286,8 +3474,8 @@ bin_whence(char *nam, char **argv, Options ops, int func) /* and shell functions... */ informed += - scanmatchtable(shfunctab, pprog, 1, 0, DISABLED, - shfunctab->printnode, printflags); + scanmatchshfunc(pprog, 1, 0, DISABLED, + shfunctab->printnode, printflags, expand); /* and builtins. */ informed += @@ -3342,7 +3530,7 @@ bin_whence(char *nam, char **argv, Options ops, int func) } /* Look for shell function */ if ((hn = shfunctab->getnode(shfunctab, *argv))) { - shfunctab->printnode(hn, printflags); + printshfuncexpand(hn, printflags, expand); informed = 1; if (!all) continue; @@ -3486,7 +3674,7 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func)) } queue_signals(); - for (;*argv;++argv) { + while (*argv) { void *hn; if (OPT_ISSET(ops,'m')) { /* with the -m option, treat the argument as a glob pattern */ @@ -3501,12 +3689,12 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func)) } continue; } - if (!(asg = getasg(*argv))) { + if (!(asg = getasg(&argv, NULL))) { zwarnnam(name, "bad assignment"); returnval = 1; - } else if (asg->value) { + } else if (ASG_VALUEP(asg)) { if(isset(RESTRICTED)) { - zwarnnam(name, "restricted: %s", asg->value); + zwarnnam(name, "restricted: %s", asg->value.scalar); returnval = 1; } else { /* The argument is of the form foo=bar, * @@ -3522,12 +3710,12 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func)) } else { Nameddir nd = hn = zshcalloc(sizeof *nd); nd->node.flags = 0; - nd->dir = ztrdup(asg->value); + nd->dir = ztrdup(asg->value.scalar); } } else { Cmdnam cn = hn = zshcalloc(sizeof *cn); cn->node.flags = HASHED; - cn->u.cmd = ztrdup(asg->value); + cn->u.cmd = ztrdup(asg->value.scalar); } ht->addnode(ht, ztrdup(asg->name), hn); if(OPT_ISSET(ops,'v')) @@ -3733,12 +3921,12 @@ bin_alias(char *name, char **argv, Options ops, UNUSED(int func)) /* Take arguments literally. Don't glob */ queue_signals(); - while ((asg = getasg(*argv++))) { - if (asg->value && !OPT_ISSET(ops,'L')) { + while ((asg = getasg(&argv, NULL))) { + if (asg->value.scalar && !OPT_ISSET(ops,'L')) { /* The argument is of the form foo=bar and we are not * * forcing a listing with -L, so define an alias */ ht->addnode(ht, ztrdup(asg->name), - createaliasnode(ztrdup(asg->value), flags1)); + createaliasnode(ztrdup(asg->value.scalar), flags1)); } else if ((a = (Alias) ht->getnode(ht, asg->name))) { /* display alias if appropriate */ if (!type_opts || ht == sufaliastab || @@ -4208,11 +4396,40 @@ bin_print(char *name, char **args, Options ops, int func) return 0; } - for (; *args; args++, len++) { - fwrite(*args, *len, 1, fout); - if (args[1]) - fputc(OPT_ISSET(ops,'l') ? '\n' : - OPT_ISSET(ops,'N') ? '\0' : ' ', fout); + if (OPT_HASARG(ops, 'x') || OPT_HASARG(ops, 'X')) { + char *eptr; + int expand, startpos = 0; + int all = OPT_HASARG(ops, 'X'); + char *xarg = all ? OPT_ARG(ops, 'X') : OPT_ARG(ops, 'x'); + + expand = (int)zstrtol(xarg, &eptr, 10); + if (*eptr || expand <= 0) { + zwarnnam(name, "positive integer expected after -%c: %s", 'x', + xarg); + return 1; + } + for (; *args; args++, len++) { + startpos = zexpandtabs(*args, *len, expand, startpos, fout, + all); + if (args[1]) { + if (OPT_ISSET(ops, 'l')) { + fputc('\n', fout); + startpos = 0; + } else if (OPT_ISSET(ops,'N')) { + fputc('\0', fout); + } else { + fputc(' ', fout); + startpos++; + } + } + } + } else { + for (; *args; args++, len++) { + fwrite(*args, *len, 1, fout); + if (args[1]) + fputc(OPT_ISSET(ops,'l') ? '\n' : + OPT_ISSET(ops,'N') ? '\0' : ' ', fout); + } } if (!(OPT_ISSET(ops,'n') || nnl)) fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout); @@ -4461,7 +4678,7 @@ bin_print(char *name, char **args, Options ops, int func) lleft -= chars; ptr += chars; } - if (width > 0 && flags[2]) width = -width; + if (width > 0 && flags[3]) width = -width; if (width > 0 && lchars < width) count += fprintf(fout, "%*c", width - lchars, ' '); count += fwrite(b, 1, lbytes, fout); @@ -4525,7 +4742,7 @@ bin_print(char *name, char **args, Options ops, int func) convchar_t cc; #ifdef MULTIBYTE_SUPPORT if (isset(MULTIBYTE)) { - mb_metacharinit(); + mb_charinit(); (void)mb_metacharlenconv(metafy(curarg+1, curlen-1, META_USEHEAP), &cc); } @@ -5500,7 +5717,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) wint_t wi; if (isset(MULTIBYTE)) { - mb_metacharinit(); + mb_charinit(); (void)mb_metacharlenconv(delimstr, &wi); } else @@ -6355,8 +6572,14 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) zwarnnam(name, "undefined signal: %s", *argv); break; } - if (!strcmp(sigs[sig], *argv)) + if (idigit(**argv) || + !strcmp(sigs[sig], *argv) || + (!strncmp("SIG", *argv, 3) && !strcmp(sigs[sig], *argv+3))) { + /* The signal was specified by number or by canonical name (with + * or without SIG prefix). + */ flags = 0; + } else { /* * Record that the signal is used under an assumed name. diff --git a/Src/compat.c b/Src/compat.c index b3a8b063c..db468529a 100644 --- a/Src/compat.c +++ b/Src/compat.c @@ -454,8 +454,13 @@ zgetcwd(void) return ret; } -/* chdir with arbitrary long pathname. Returns 0 on success, -1 on normal * - * failure and -2 when chdir failed and the current directory is lost. */ +/* + * chdir with arbitrary long pathname. Returns 0 on success, -1 on normal * + * failure and -2 when chdir failed and the current directory is lost. + * + * This is to be treated as if at system level, so dir is unmetafied but + * terminated by a NULL. + */ /**/ mod_export int diff --git a/Src/context.c b/Src/context.c index 1b8741f46..2dc8d3b89 100644 --- a/Src/context.c +++ b/Src/context.c @@ -53,6 +53,8 @@ zcontext_save_partial(int parts) { struct context_stack *cs; + queue_signals(); + cs = (struct context_stack *)malloc(sizeof(struct context_stack)); if (parts & ZCONTEXT_HIST) { @@ -67,6 +69,8 @@ zcontext_save_partial(int parts) cs->next = cstack; cstack = cs; + + unqueue_signals(); } /* save context in full */ diff --git a/Src/exec.c b/Src/exec.c index 9f163a627..45f1c66f0 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1115,15 +1115,20 @@ execsimple(Estate state) if (code == WC_ASSIGN) { cmdoutval = 0; addvars(state, state->pc - 1, 0); + setunderscore(""); if (isset(XTRACE)) { fputc('\n', xtrerr); fflush(xtrerr); } lv = (errflag ? errflag : cmdoutval); - } else if (code == WC_FUNCDEF) { - lv = execfuncdef(state, NULL); } else { - lv = (execfuncs[code - WC_CURSH])(state, 0); + int q = queue_signal_level(); + dont_queue_signals(); + if (code == WC_FUNCDEF) + lv = execfuncdef(state, NULL); + else + lv = (execfuncs[code - WC_CURSH])(state, 0); + restore_queue_signals(q); } thisjob = otj; @@ -1157,6 +1162,8 @@ execlist(Estate state, int dont_change_job, int exiting) */ int oldnoerrexit = noerrexit; + queue_signals(); + cj = thisjob; old_pline_level = pline_level; old_list_pipe = list_pipe; @@ -1350,7 +1357,13 @@ execlist(Estate state, int dont_change_job, int exiting) state->pc--; sublist_done: - noerrexit = oldnoerrexit; + /* + * See hairy code near the end of execif() for the + * following. "noerrexit == 2" only applies until + * we hit execcmd on the way down. We're now + * on the way back up, so don't restore it. + */ + noerrexit = (oldnoerrexit == 2) ? 0 : oldnoerrexit; if (sigtrapped[SIGDEBUG] && !isset(DEBUGBEFORECMD) && !donedebug) { /* @@ -1421,6 +1434,8 @@ sublist_done: /* Make sure this doesn't get executed again. */ sigtrapped[SIGEXIT] = 0; } + + unqueue_signals(); } /* Execute a pipeline. * @@ -1449,6 +1464,14 @@ execpline(Estate state, wordcode slcode, int how, int last1) else if (slflags & WC_SUBLIST_NOT) last1 = 0; + /* If trap handlers are allowed to run here, they may start another + * external job in the middle of us starting this one, which can + * result in jobs being reaped before their job table entries have + * been initialized, which in turn leads to waiting forever for + * jobs that no longer exist. So don't do that. + */ + queue_signals(); + pj = thisjob; ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0; child_block(); @@ -1461,6 +1484,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) */ if ((thisjob = newjob = initjob()) == -1) { child_unblock(); + unqueue_signals(); return 1; } if (how & Z_TIMED) @@ -1516,6 +1540,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) else spawnjob(); child_unblock(); + unqueue_signals(); /* Executing background code resets shell status */ return lastval = 0; } else { @@ -1573,15 +1598,18 @@ execpline(Estate state, wordcode slcode, int how, int last1) } if (!(jn->stat & STAT_LOCKED)) { updated = hasprocs(thisjob); - waitjobs(); + waitjobs(); /* deals with signal queue */ child_block(); } else updated = 0; if (!updated && list_pipe_job && hasprocs(list_pipe_job) && !(jobtab[list_pipe_job].stat & STAT_STOPPED)) { + int q = queue_signal_level(); child_unblock(); child_block(); + dont_queue_signals(); + restore_queue_signals(q); } if (list_pipe_child && jn->stat & STAT_DONE && @@ -1665,6 +1693,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) break; } child_unblock(); + unqueue_signals(); if (list_pipe && (lastval & 0200) && pj >= 0 && (!(jn->stat & STAT_INUSE) || (jn->stat & STAT_DONE))) { @@ -2264,14 +2293,14 @@ addvars(Estate state, Wordcode pc, int addflags) state->pc = opc; return; } - if (!isstr || (isset(GLOBASSIGN) && + if (!isstr || (isset(GLOBASSIGN) && isstr && haswilds((char *)getdata(firstnode(vl))))) { globlist(vl, 0); /* Unset the parameter to force it to be recreated * as either scalar or array depending on how many * matches were found for the glob. */ - if (isset(GLOBASSIGN)) + if (isset(GLOBASSIGN) && isstr) unsetparam(name); } if (errflag) { @@ -2436,13 +2465,13 @@ execcmd(Estate state, int input, int output, int how, int last1) 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; + 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; + Wordcode beg = state->pc, varspc, assignspc = (Wordcode)0; FILE *oxtrerr = xtrerr, *newxtrerr = NULL; doneps4 = 0; @@ -2463,8 +2492,28 @@ execcmd(Estate state, int input, int output, int how, int last1) /* 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. */ - args = (type == WC_SIMPLE ? - ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok) : NULL); + switch (type) { + case WC_SIMPLE: + args = ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok); + 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++) { + code = *state->pc; + DPUTS(wc_code(code) != WC_ASSIGN, + "BUG: miscounted typeset assignments"); + state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ? + 3 : WC_ASSIGN_NUM(code) + 2); + } + break; + + default: + args = NULL; + } + /* * If assignment but no command get the status from variable * assignment. @@ -2487,7 +2536,7 @@ execcmd(Estate state, int input, int output, int how, int last1) /* If the command begins with `%', then assume it is a * * reference to a job in the job table. */ - if (type == WC_SIMPLE && args && nonempty(args) && + if ((type == WC_SIMPLE || type == WC_TYPESET) && args && nonempty(args) && *(char *)peekfirst(args) == '%') { if (how & Z_DISOWN) { oautocont = opts[AUTOCONTINUE]; @@ -2516,20 +2565,32 @@ 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) { + if (type == WC_SIMPLE || type == WC_TYPESET) { while (args && nonempty(args)) { char *cmdarg = (char *) peekfirst(args); checked = !has_token(cmdarg); if (!checked) break; - if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) && - (hn = shfunctab->getnode(shfunctab, cmdarg))) { - is_shfunc = 1; - break; - } - if (!(hn = builtintab->getnode(builtintab, cmdarg))) { - checked = !(cflags & BINF_BUILTIN); - break; + if (type == WC_TYPESET && + (hn = builtintab->getnode2(builtintab, cmdarg))) { + /* + * If reserved word for typeset command found (and so + * enabled), use regardless of whether builtin is + * enabled as we share the implementation. + * + * Reserved words take precedence over shell functions. + */ + checked = 1; + } else { + if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) && + (hn = shfunctab->getnode(shfunctab, cmdarg))) { + is_shfunc = 1; + break; + } + if (!(hn = builtintab->getnode(builtintab, cmdarg))) { + checked = !(cflags & BINF_BUILTIN); + break; + } } orig_cflags |= cflags; cflags &= ~BINF_BUILTIN & ~BINF_COMMAND; @@ -2660,7 +2721,7 @@ execcmd(Estate state, int input, int output, int how, int last1) if (args && htok) prefork(args, esprefork); - if (type == WC_SIMPLE) { + if (type == WC_SIMPLE || type == WC_TYPESET) { int unglobbed = 0; for (;;) { @@ -2896,7 +2957,7 @@ execcmd(Estate state, int input, int output, int how, int last1) return; } - if (type == WC_SIMPLE && !nullexec) { + if ((type == WC_SIMPLE || type == WC_TYPESET) && !nullexec) { char *s; char trycd = (isset(AUTOCD) && isset(SHINSTDIN) && (!redir || empty(redir)) && args && !empty(args) && @@ -3008,6 +3069,7 @@ execcmd(Estate state, int input, int output, int how, int last1) addproc(pid, text, 0, &bgtime); if (oautocont >= 0) opts[AUTOCONTINUE] = oautocont; + pipecleanfilelist(jobtab[thisjob].filelist, 1); return; } /* pid == 0 */ @@ -3282,7 +3344,8 @@ execcmd(Estate state, int input, int output, int how, int last1) fil = -1; else if (IS_APPEND_REDIR(fn->type)) fil = open(unmeta(fn->name), - (unset(CLOBBER) && !IS_CLOBBER_REDIR(fn->type)) ? + ((unset(CLOBBER) && unset(APPENDCREATE)) && + !IS_CLOBBER_REDIR(fn->type)) ? O_WRONLY | O_APPEND | O_NOCTTY : O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY, 0666); else @@ -3350,6 +3413,7 @@ execcmd(Estate state, int input, int output, int how, int last1) fflush(xtrerr); } } else if (isset(EXECOPT) && !errflag) { + int q = queue_signal_level(); /* * We delay the entersubsh() to here when we are exec'ing * the current shell (including a fake exec to run a builtin then @@ -3390,11 +3454,14 @@ execcmd(Estate state, int input, int output, int how, int last1) } else redir_prog = NULL; + dont_queue_signals(); lastval = execfuncdef(state, redir_prog); + restore_queue_signals(q); } else if (type >= WC_CURSH) { if (last1 == 1) do_exec = 1; + dont_queue_signals(); if (type == WC_AUTOFN) { /* * We pre-loaded this to get any redirs. @@ -3403,6 +3470,7 @@ execcmd(Estate state, int input, int output, int how, int last1) lastval = execautofn_basic(state, do_exec); } else lastval = (execfuncs[type - WC_CURSH])(state, do_exec); + restore_queue_signals(q); } else if (is_builtin || is_shfunc) { LinkList restorelist = 0, removelist = 0; /* builtin or shell function */ @@ -3452,13 +3520,126 @@ execcmd(Estate state, int input, int output, int how, int last1) if (is_shfunc) { /* It's a shell function */ - pipecleanfilelist(filelist); + pipecleanfilelist(filelist, 0); execshfunc((Shfunc) hn, args); } else { /* It's a builtin */ + LinkList assigns = (LinkList)0; if (forked) closem(FDT_INTERNAL); - lastval = execbuiltin(args, (Builtin) hn); + if (postassigns) { + Wordcode opc = state->pc; + state->pc = assignspc; + assigns = newlinklist(); + while (postassigns--) { + wordcode ac = *state->pc++; + char *name = ecgetstr(state, EC_DUPTOK, &htok); + Asgment asg; + local_list1(svl); + + DPUTS(wc_code(ac) != WC_ASSIGN, + "BUG: bad assignment list for typeset"); + if (htok) { + init_list1(svl, name); + if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR && + WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) { + char *data; + /* + * Special case: this is a name only, so + * it's not required to be a single + * expansion. Furthermore, for + * consistency with the builtin + * interface, it may expand into + * scalar assignments: + * ass=(one=two three=four) + * typeset a=b $ass + */ + /* Unused dummy value for name */ + (void)ecgetstr(state, EC_DUPTOK, &htok); + prefork(&svl, PREFORK_TYPESET); + if (errflag) { + state->pc = opc; + break; + } + globlist(&svl, 0); + if (errflag) { + state->pc = opc; + break; + } + while ((data = ugetnode(&svl))) { + char *ptr; + asg = (Asgment)zhalloc(sizeof(struct asgment)); + asg->is_array = 0; + if ((ptr = strchr(data, '='))) { + *ptr++ = '\0'; + asg->name = data; + asg->value.scalar = ptr; + } else { + asg->name = data; + asg->value.scalar = NULL; + } + uaddlinknode(assigns, &asg->node); + } + continue; + } + prefork(&svl, PREFORK_SINGLE); + name = empty(&svl) ? "" : + (char *)getdata(firstnode(&svl)); + } + untokenize(name); + asg = (Asgment)zhalloc(sizeof(struct asgment)); + asg->name = name; + if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR) { + char *val = ecgetstr(state, EC_DUPTOK, &htok); + asg->is_array = 0; + if (WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) { + /* Fake assignment, no value */ + asg->value.scalar = NULL; + } else { + if (htok) { + init_list1(svl, val); + prefork(&svl, PREFORK_SINGLE|PREFORK_ASSIGN); + if (errflag) { + state->pc = opc; + break; + } + /* + * No globassign for typeset + * arguments, thank you + */ + val = empty(&svl) ? "" : + (char *)getdata(firstnode(&svl)); + } + untokenize(val); + asg->value.scalar = val; + } + } else { + asg->is_array = 1; + asg->value.array = + ecgetlist(state, WC_ASSIGN_NUM(ac), + EC_DUPTOK, &htok); + if (asg->value.array) + { + prefork(asg->value.array, PREFORK_ASSIGN); + if (errflag) { + state->pc = opc; + break; + } + globlist(asg->value.array, 0); + if (errflag) { + state->pc = opc; + break; + } + } + } + + uaddlinknode(assigns, &asg->node); + } + state->pc = opc; + } + dont_queue_signals(); + lastval = execbuiltin(args, assigns, (Builtin) hn); + restore_queue_signals(q); fflush(stdout); if (save[1] == -2) { if (ferror(stdout)) { @@ -3500,7 +3681,7 @@ execcmd(Estate state, int input, int output, int how, int last1) if (!subsh && isset(RCS) && interact && !nohistsave) savehistfile(NULL, 1, HFILE_USE_OPTIONS); } - if (type == WC_SIMPLE) { + if (type == WC_SIMPLE || type == WC_TYPESET) { if (varspc) { int addflags = ADDVAR_EXPORT|ADDVAR_RESTRICT; if (forked) @@ -3531,7 +3712,7 @@ execcmd(Estate state, int input, int output, int how, int last1) DPUTS(varspc, "BUG: assignment before complex command"); list_pipe = 0; - pipecleanfilelist(filelist); + pipecleanfilelist(filelist, 0); /* If we're forked (and we should be), no need to return */ DPUTS(last1 != 1 && !forked, "BUG: not exiting?"); DPUTS(type != WC_SUBSH, "Not sure what we're doing."); @@ -4133,6 +4314,10 @@ namedpipe(void) { char *tnam = gettempname(NULL, 1); + if (!tnam) { + zerr("failed to create named pipe: %e", errno); + return NULL; + } # ifdef HAVE_MKFIFO if (mkfifo(tnam, 0600) < 0){ # else @@ -4664,11 +4849,9 @@ execshfunc(Shfunc shf, LinkList args) if ((osfc = sfcontext) == SFC_NONE) sfcontext = SFC_DIRECT; xtrerr = stderr; - unqueue_signals(); doshfunc(shf, args, 0); - queue_signals(); sfcontext = osfc; free(cmdstack); cmdstack = ocs; @@ -4883,6 +5066,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) static int funcdepth; #endif + queue_signals(); /* Lots of memory and global state changes coming */ + pushheap(); oargv0 = NULL; @@ -5105,6 +5290,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) } popheap(); + unqueue_signals(); + /* * Exit with a tidy up. * Only leave if we're at the end of the appropriate function --- @@ -5143,6 +5330,8 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name) int cont, ouu; char *ou; + queue_signals(); + ou = zalloc(ouu = underscoreused); if (ou) memcpy(ou, zunderscore, underscoreused); @@ -5164,12 +5353,14 @@ runshfunc(Eprog prog, FuncWrap wrap, char *name) wrap = wrap->next; } startparamscope(); - execode(prog, 1, 0, "shfunc"); + execode(prog, 1, 0, "shfunc"); /* handles signal unqueueing */ if (ou) { setunderscore(ou); zfree(ou, ouu); } endparamscope(); + + unqueue_signals(); } /* Search fpath for an undefined function. Finds the file, and returns the * diff --git a/Src/glob.c b/Src/glob.c index 057d44a17..dea1bf50e 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -102,8 +102,14 @@ int badcshglob; /**/ int pathpos; /* position in pathbuf (needed by pattern code) */ +/* + * pathname buffer (needed by pattern code). + * It is currently believed the string in here is stored metafied and is + * unmetafied temporarily as needed by system calls. + */ + /**/ -char *pathbuf; /* pathname buffer (needed by pattern code) */ +char *pathbuf; typedef struct stat *Statptr; /* This makes the Ultrix compiler happy. Go figure. */ @@ -216,22 +222,26 @@ static struct globdata curglobdata; #define save_globstate(N) \ do { \ + queue_signals(); \ memcpy(&(N), &curglobdata, sizeof(struct globdata)); \ (N).gd_pathpos = pathpos; \ (N).gd_pathbuf = pathbuf; \ (N).gd_glob_pre = glob_pre; \ (N).gd_glob_suf = glob_suf; \ pathbuf = NULL; \ + unqueue_signals(); \ } while (0) #define restore_globstate(N) \ do { \ + queue_signals(); \ zfree(pathbuf, pathbufsz); \ memcpy(&curglobdata, &(N), sizeof(struct globdata)); \ pathpos = (N).gd_pathpos; \ pathbuf = (N).gd_pathbuf; \ glob_pre = (N).gd_glob_pre; \ glob_suf = (N).gd_glob_suf; \ + unqueue_signals(); \ } while (0) /* pathname component in filename patterns */ @@ -253,7 +263,7 @@ addpath(char *s, int l) { DPUTS(!pathbuf, "BUG: pathbuf not initialised"); while (pathpos + l + 1 >= pathbufsz) - pathbuf = realloc(pathbuf, pathbufsz *= 2); + pathbuf = zrealloc(pathbuf, pathbufsz *= 2); while (l--) pathbuf[pathpos++] = *s++; pathbuf[pathpos++] = '/'; @@ -491,7 +501,7 @@ scanner(Complist q, int shortcircuit) if (l >= PATH_MAX) return; - err = lchdir(pathbuf + pathbufcwd, &ds, 0); + err = lchdir(unmeta(pathbuf + pathbufcwd), &ds, 0); if (err == -1) return; if (err) { @@ -513,7 +523,7 @@ scanner(Complist q, int shortcircuit) else if (!strcmp(str, "..")) { struct stat sc, sr; - add = (stat("/", &sr) || stat(pathbuf, &sc) || + add = (stat("/", &sr) || stat(unmeta(pathbuf), &sc) || sr.st_ino != sc.st_ino || sr.st_dev != sc.st_dev); } @@ -560,7 +570,7 @@ scanner(Complist q, int shortcircuit) DPUTS(pathpos == pathbufcwd, "BUG: filename longer than PATH_MAX"); - err = lchdir(pathbuf + pathbufcwd, &ds, 0); + err = lchdir(unmeta(pathbuf + pathbufcwd), &ds, 0); if (err == -1) break; if (err) { @@ -2237,7 +2247,7 @@ xpandbraces(LinkList list, LinkNode *np) #ifdef MULTIBYTE_SUPPORT char *ncptr; int nclen; - mb_metacharinit(); + mb_charinit(); ncptr = wcs_nicechar(cend, NULL, NULL); nclen = strlen(ncptr); p = zhalloc(lenalloc + nclen); @@ -2805,7 +2815,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * ... now we know whether it's worth looking for the * shortest, which we do by brute force. */ - mb_metacharinit(); + mb_charinit(); for (t = s, umlen = 0; t < s + mlen; ) { set_pat_end(p, *t); if (pattrylen(p, s, t - s, umlen, 0)) { @@ -2831,7 +2841,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * so that match, mbegin, mend and MATCH, MBEGIN, MEND are * correct. */ - mb_metacharinit(); + mb_charinit(); tmatch = NULL; for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) { set_pat_start(p, t-s); @@ -2855,7 +2865,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, /* Largest possible match at tail of string: * * move forward along string until we get a match. * * Again there's no optimisation. */ - mb_metacharinit(); + mb_charinit(); for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) { set_pat_start(p, t-s); if (pattrylen(p, t, s + l - t, umlen, ioff)) { @@ -2889,7 +2899,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, } ioff = 0; /* offset into string */ umlen = umltot; - mb_metacharinit(); + mb_charinit(); do { /* loop over all matches for global substitution */ matched = 0; @@ -2986,7 +2996,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, */ nmatches = 0; tmatch = NULL; - mb_metacharinit(); + mb_charinit(); for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) { set_pat_start(p, t-s); if (pattrylen(p, t, s + l - t, umlen, ioff)) { @@ -3002,7 +3012,7 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, * We need to find the n'th last match. */ n = nmatches - n; - mb_metacharinit(); + mb_charinit(); for (ioff = 0, t = s, umlen = umltot; t < s + l; ioff++) { set_pat_start(p, t-s); if (pattrylen(p, t, s + l - t, umlen, ioff) && diff --git a/Src/hashtable.c b/Src/hashtable.c index ab381cc6a..90739a882 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -937,13 +937,17 @@ printshfuncnode(HashNode hn, int printflags) quotedzputs(f->node.nam, stdout); if (f->funcdef || f->node.flags & PM_UNDEFINED) { - printf(" () {\n\t"); - if (f->node.flags & PM_UNDEFINED) - printf("%c undefined\n\t", hashchar); - else + printf(" () {\n"); + zoutputtab(stdout); + if (f->node.flags & PM_UNDEFINED) { + printf("%c undefined\n", hashchar); + zoutputtab(stdout); + } else t = getpermtext(f->funcdef, NULL, 1); - if (f->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL)) - printf("%c traced\n\t", hashchar); + if (f->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL)) { + printf("%c traced\n", hashchar); + zoutputtab(stdout); + } if (!t) { char *fopt = "UtTkz"; int flgs[] = { @@ -959,11 +963,12 @@ printshfuncnode(HashNode hn, int printflags) zputs(t, stdout); zsfree(t); if (f->funcdef->flags & EF_RUN) { - printf("\n\t"); + printf("\n"); + zoutputtab(stdout); quotedzputs(f->node.nam, stdout); printf(" \"$@\""); } - } + } printf("\n}"); } else { printf(" () { }"); @@ -979,6 +984,59 @@ printshfuncnode(HashNode hn, int printflags) putchar('\n'); } +/* + * Wrap scanmatchtable for shell functions with optional + * expansion of leading tabs. + * expand = 0 is standard: use hard tabs. + * expand > 0 uses that many spaces. + * expand < 0 uses no identation. + * + * Note this function and the following two are called with + * interrupts queued, so saving and restoring text_expand_tabs + * is safe. + */ + +/**/ +mod_export int +scanmatchshfunc(Patprog pprog, int sorted, int flags1, int flags2, + ScanFunc scanfunc, int scanflags, int expand) +{ + int ret, save_expand; + + save_expand = text_expand_tabs; + text_expand_tabs = expand; + ret = scanmatchtable(shfunctab, pprog, sorted, flags1, flags2, + scanfunc, scanflags); + text_expand_tabs = save_expand; + + return ret; +} + +/* Wrap scanhashtable to expand tabs for shell functions */ + +/**/ +mod_export int +scanshfunc(int sorted, int flags1, int flags2, + ScanFunc scanfunc, int scanflags, int expand) +{ + return scanmatchshfunc(NULL, sorted, flags1, flags2, + scanfunc, scanflags, expand); +} + +/* Wrap shfunctab->printnode to expand tabs */ + +/**/ +mod_export void +printshfuncexpand(HashNode hn, int printflags, int expand) +{ + int save_expand; + + save_expand = text_expand_tabs; + text_expand_tabs = expand; + shfunctab->printnode(hn, printflags); + text_expand_tabs = save_expand; +} + /**************************************/ /* Reserved Word Hash Table Functions */ /**************************************/ @@ -992,22 +1050,29 @@ static struct reswd reswds[] = { {{NULL, "}", 0}, OUTBRACE}, {{NULL, "case", 0}, CASE}, {{NULL, "coproc", 0}, COPROC}, + {{NULL, "declare", 0}, TYPESET}, {{NULL, "do", 0}, DOLOOP}, {{NULL, "done", 0}, DONE}, {{NULL, "elif", 0}, ELIF}, {{NULL, "else", 0}, ELSE}, {{NULL, "end", 0}, ZEND}, {{NULL, "esac", 0}, ESAC}, + {{NULL, "export", 0}, TYPESET}, {{NULL, "fi", 0}, FI}, + {{NULL, "float", 0}, TYPESET}, {{NULL, "for", 0}, FOR}, {{NULL, "foreach", 0}, FOREACH}, {{NULL, "function", 0}, FUNC}, {{NULL, "if", 0}, IF}, + {{NULL, "integer", 0}, TYPESET}, + {{NULL, "local", 0}, TYPESET}, {{NULL, "nocorrect", 0}, NOCORRECT}, + {{NULL, "readonly", 0}, TYPESET}, {{NULL, "repeat", 0}, REPEAT}, {{NULL, "select", 0}, SELECT}, {{NULL, "then", 0}, THEN}, {{NULL, "time", 0}, TIME}, + {{NULL, "typeset", 0}, TYPESET}, {{NULL, "until", 0}, UNTIL}, {{NULL, "while", 0}, WHILE}, {{NULL, NULL, 0}, 0} diff --git a/Src/hist.c b/Src/hist.c index bd03c4f11..75e809c48 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -136,6 +136,7 @@ mod_export int hist_skip_flags; #define HA_NOINC (1<<1) /* Don't store, curhist not incremented */ #define HA_INWORD (1<<2) /* We're inside a word, don't add start and end markers */ +#define HA_UNGET (1<<3) /* Recursively ungetting */ /* Array of word beginnings and endings in current history line. */ @@ -904,8 +905,14 @@ ihungetc(int c) while (!lexstop && !errflag) { if (hptr[-1] != (char) c && stophist < 4 && - hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\') - hungetc('\n'), hungetc('\\'); + hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\' && + !(histactive & HA_UNGET) && + (inbufflags & (INP_ALIAS|INP_HIST)) != INP_ALIAS) { + histactive |= HA_UNGET; + hungetc('\n'); + hungetc('\\'); + histactive &= ~HA_UNGET; + } if (expanding) { zlemetacs--; @@ -2000,7 +2007,7 @@ casemodify(char *str, int how) VARARR(char, mbstr, MB_CUR_MAX); mbstate_t ps; - mb_metacharinit(); + mb_charinit(); memset(&ps, 0, sizeof(ps)); while (*str) { wint_t wc; diff --git a/Src/init.c b/Src/init.c index 102276a64..22db4b3b2 100644 --- a/Src/init.c +++ b/Src/init.c @@ -105,6 +105,7 @@ loop(int toplevel, int justonce) Eprog prog; int err, non_empty = 0; + queue_signals(); pushheap(); if (!toplevel) zcontext_save(); @@ -218,6 +219,7 @@ loop(int toplevel, int justonce) if (((!interact || sourcelevel) && errflag) || retflag) break; if (isset(SINGLECOMMAND) && toplevel) { + dont_queue_signals(); if (sigtrapped[SIGEXIT]) dotrap(SIGEXIT); exit(lastval); @@ -229,6 +231,7 @@ loop(int toplevel, int justonce) if (!toplevel) zcontext_restore(); popheap(); + unqueue_signals(); if (err) return LOOP_ERROR; @@ -1117,8 +1120,9 @@ setupshin(char *runscript) exit(127); } scriptfilename = sfname; - zsfree(argzero); /* ztrdup'd in parseargs */ - argzero = runscript; + sfname = argzero; /* copy to avoid race condition */ + argzero = ztrdup(runscript); + zsfree(sfname); /* argzero ztrdup'd in parseargs */ } /* * We only initialise line numbering once there is a script to @@ -1425,7 +1429,7 @@ sourcehome(char *s) char *h; queue_signals(); - if (EMULATION(EMULATE_SH|EMULATE_KSH) || !(h = getsparam("ZDOTDIR"))) { + if (EMULATION(EMULATE_SH|EMULATE_KSH) || !(h = getsparam_u("ZDOTDIR"))) { h = home; if (!h) return; diff --git a/Src/input.c b/Src/input.c index 4a5bf89c6..eb968ea72 100644 --- a/Src/input.c +++ b/Src/input.c @@ -141,16 +141,19 @@ shingetline(void) int c; char buf[BUFSIZ]; char *p; + int q = queue_signal_level(); p = buf; - winch_unblock(); for (;;) { + winch_unblock(); + dont_queue_signals(); do { errno = 0; c = fgetc(bshin); } while (c < 0 && errno == EINTR); if (c < 0 || c == '\n') { winch_block(); + restore_queue_signals(q); if (c == '\n') *p++ = '\n'; if (p > buf) { @@ -167,12 +170,13 @@ shingetline(void) *p++ = c; if (p >= buf + BUFSIZ - 1) { winch_block(); + queue_signals(); line = zrealloc(line, ll + (p - buf) + 1); memcpy(line + ll, buf, p - buf); ll += p - buf; line[ll] = '\0'; p = buf; - winch_unblock(); + unqueue_signals(); } } } @@ -222,7 +226,8 @@ ingetc(void) if (inputline()) break; } - zshlex_raw_add(lastc); + if (!lexstop) + zshlex_raw_add(lastc); return lastc; } @@ -376,6 +381,8 @@ inputline(void) static void inputsetline(char *str, int flags) { + queue_signals(); + if ((inbufflags & INP_FREE) && inbuf) { free(inbuf); } @@ -393,6 +400,8 @@ inputsetline(char *str, int flags) else inbufct = inbufleft; inbufflags = flags; + + unqueue_signals(); } /* @@ -591,7 +600,7 @@ inpoptop(void) * history is before, but they're both pushed onto * the input stack. */ - if ((inbufflags & (INP_ALIAS|INP_HIST)) == INP_ALIAS) + if ((inbufflags & (INP_ALIAS|INP_HIST|INP_RAW_KEEP)) == INP_ALIAS) zshlex_raw_back(); } } diff --git a/Src/jobs.c b/Src/jobs.c index 948f61b01..b47ba8c60 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -1179,7 +1179,7 @@ addfilelist(const char *name, int fd) /**/ void -pipecleanfilelist(LinkList filelist) +pipecleanfilelist(LinkList filelist, int proc_subst_only) { LinkNode node; @@ -1188,7 +1188,12 @@ pipecleanfilelist(LinkList filelist) node = firstnode(filelist); while (node) { Jobfile jf = (Jobfile)getdata(node); - if (jf->is_fd) { + if (jf->is_fd && + (!proc_subst_only +#ifdef FDT_PROC_SUBST + || fdtable[jf->u.fd] == FDT_PROC_SUBST +#endif + )) { LinkNode next = nextnode(node); zclose(jf->u.fd); (void)remnode(filelist, node); @@ -1379,10 +1384,17 @@ waitforpid(pid_t pid, int wait_cmd) dont_queue_signals(); child_block(); /* unblocked in signal_suspend() */ queue_traps(wait_cmd); + + /* This function should never be called with a pid that is not a + * child of the current shell. Consequently, if kill(0, pid) + * fails here with ESRCH, the child has already been reaped. In + * the loop body, we expect this to happen in signal_suspend() + * via zhandler(), after which this test terminates the loop. + */ while (!errflag && (kill(pid, 0) >= 0 || errno != ESRCH)) { if (first) first = 0; - else + else if (!wait_cmd) kill(pid, SIGCONT); last_signal = -1; @@ -1415,9 +1427,9 @@ zwaitjob(int job, int wait_cmd) int q = queue_signal_level(); Job jn = jobtab + job; - dont_queue_signals(); child_block(); /* unblocked during signal_suspend() */ queue_traps(wait_cmd); + dont_queue_signals(); if (jn->procs || jn->auxprocs) { /* if any forks were done */ jn->stat |= STAT_LOCKED; if (jn->stat & STAT_CHANGED) @@ -1433,7 +1445,7 @@ zwaitjob(int job, int wait_cmd) * we can't deadlock on the fact that those still exist, so * that's not a problem. */ - pipecleanfilelist(jn->filelist); + pipecleanfilelist(jn->filelist, 0); } while (!errflag && jn->stat && !(jn->stat & STAT_DONE) && @@ -1473,9 +1485,9 @@ zwaitjob(int job, int wait_cmd) pipestats[0] = lastval; numpipestats = 1; } + restore_queue_signals(q); unqueue_traps(); child_unblock(); - restore_queue_signals(q); return 0; } @@ -1623,7 +1635,7 @@ spawnjob(void) deletejob(jobtab + thisjob, 0); else { jobtab[thisjob].stat |= STAT_LOCKED; - pipecleanfilelist(jobtab[thisjob].filelist); + pipecleanfilelist(jobtab[thisjob].filelist, 0); } thisjob = -1; } @@ -339,6 +339,7 @@ ctxtlex(void) incmdpos = 1; break; case STRING: + case TYPESET: /* case ENVSTRING: */ case ENVARRAY: case OUTPAR: @@ -1182,7 +1183,7 @@ gettokstr(int c, int sub) c = Outpar; } } else if (peek != ENVSTRING && - incmdpos && !bct && !brct) { + (incmdpos || intypeset) && !bct && !brct) { char *t = tokstr; if (idigit(*t)) while (++t < lexbuf.ptr && idigit(*t)); @@ -1200,7 +1201,7 @@ gettokstr(int c, int sub) t++; if (t == lexbuf.ptr) { e = hgetc(); - if (e == '(' && incmdpos) { + if (e == '(') { *lexbuf.ptr = '\0'; return ENVARRAY; } @@ -1387,7 +1388,7 @@ dquote_parse(char endchar, int sub) { int pct = 0, brct = 0, bct = 0, intick = 0, err = 0; int c; - int math = endchar == ')' || endchar == ']'; + int math = endchar == ')' || endchar == ']' || infor; int zlemath = math && zlemetacs > zlemetall + addedx - inbufct; while (((c = hgetc()) != endchar || bct || @@ -1994,8 +1995,10 @@ skipcomm(void) #else char *new_tokstr; int new_lexstop, new_lex_add_raw; + int save_infor = infor; struct lexbufstate new_lexbuf; + infor = 0; cmdpush(CS_CMDSUBST); SETPARBEGIN add(Inpar); @@ -2020,6 +2023,18 @@ skipcomm(void) new_tokstr = tokstr; new_lexbuf = lexbuf; + /* + * If we're expanding an alias at this point, we need the whole + * remaining text as part of the string for the command in + * parentheses, so don't backtrack. This is different from the + * usual case where the alias is fully within the command, where + * we want the unexpanded text so that it will be expanded + * again when the command in the parentheses is executed. + * + * I never wanted to be a software engineer, you know. + */ + if (inbufflags & INP_ALIAS) + inbufflags |= INP_RAW_KEEP; zcontext_save_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE); hist_in_word(1); } else { @@ -2052,6 +2067,7 @@ skipcomm(void) * the recursive parsing. */ lexflags &= ~LEXFLAGS_ZLE; + dbparens = 0; /* restored by zcontext_restore_partial() */ if (!parse_event(OUTPAR) || tok != OUTPAR) lexstop = 1; @@ -2098,6 +2114,7 @@ skipcomm(void) if (!lexstop) SETPAREND cmdpop(); + infor = save_infor; return lexstop; #endif diff --git a/Src/loop.c b/Src/loop.c index e4e8e2df8..4def9b652 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -56,6 +56,10 @@ execfor(Estate state, int do_exec) char *name, *str, *cond = NULL, *advance = NULL; zlong val = 0; LinkList vars = NULL, args = NULL; + int old_simple_pline = simple_pline; + + /* See comments in execwhile() */ + simple_pline = 1; end = state->pc + WC_FOR_SKIP(code); @@ -69,10 +73,12 @@ execfor(Estate state, int do_exec) fprintf(xtrerr, "%s\n", str2); fflush(xtrerr); } - if (!errflag) + if (!errflag) { matheval(str); + } if (errflag) { state->pc = end; + simple_pline = old_simple_pline; return 1; } cond = ecgetstr(state, EC_NODUP, &ctok); @@ -85,12 +91,14 @@ execfor(Estate state, int do_exec) if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) { state->pc = end; + simple_pline = old_simple_pline; return 0; } if (htok) { execsubst(args); if (errflag) { state->pc = end; + simple_pline = old_simple_pline; return 1; } } @@ -198,6 +206,7 @@ execfor(Estate state, int do_exec) popheap(); cmdpop(); loops--; + simple_pline = old_simple_pline; state->pc = end; return lastval; } @@ -214,6 +223,10 @@ execselect(Estate state, UNUSED(int do_exec)) FILE *inp; size_t more; LinkList args; + int old_simple_pline = simple_pline; + + /* See comments in execwhile() */ + simple_pline = 1; end = state->pc + WC_FOR_SKIP(code); name = ecgetstr(state, EC_NODUP, NULL); @@ -229,18 +242,21 @@ execselect(Estate state, UNUSED(int do_exec)) if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) { state->pc = end; + simple_pline = old_simple_pline; return 0; } if (htok) { execsubst(args); if (errflag) { state->pc = end; + simple_pline = old_simple_pline; return 1; } } } if (!args || empty(args)) { state->pc = end; + simple_pline = old_simple_pline; return 0; } loops++; @@ -315,6 +331,7 @@ execselect(Estate state, UNUSED(int do_exec)) popheap(); fclose(inp); loops--; + simple_pline = old_simple_pline; state->pc = end; return lastval; } @@ -382,6 +399,7 @@ execwhile(Estate state, UNUSED(int do_exec)) Wordcode end, loop; wordcode code = state->pc[-1]; int olderrexit, oldval, isuntil = (WC_WHILE_TYPE(code) == WC_WHILE_UNTIL); + int old_simple_pline = simple_pline; end = state->pc + WC_WHILE_SKIP(code); olderrexit = noerrexit; @@ -396,8 +414,6 @@ execwhile(Estate state, UNUSED(int do_exec)) /* This is an empty loop. Make sure the signal handler sets the * flags and then just wait for someone hitting ^C. */ - int old_simple_pline = simple_pline; - simple_pline = 1; while (!breaks) @@ -409,7 +425,14 @@ execwhile(Estate state, UNUSED(int do_exec)) for (;;) { state->pc = loop; noerrexit = 1; + + /* In case the test condition is a functional no-op, + * make sure signal handlers recognize ^C to end the loop. */ + simple_pline = 1; + execlist(state, 1, 0); + + simple_pline = old_simple_pline; noerrexit = olderrexit; if (!((lastval == 0) ^ isuntil)) { if (breaks) @@ -421,7 +444,14 @@ execwhile(Estate state, UNUSED(int do_exec)) lastval = oldval; break; } + + /* In case the loop body is also a functional no-op, + * make sure signal handlers recognize ^C as above. */ + simple_pline = 1; + execlist(state, 1, 0); + + simple_pline = old_simple_pline; if (breaks) { breaks--; if (breaks || !contflag) @@ -452,6 +482,10 @@ execrepeat(Estate state, UNUSED(int do_exec)) wordcode code = state->pc[-1]; int count, htok = 0; char *tmp; + int old_simple_pline = simple_pline; + + /* See comments in execwhile() */ + simple_pline = 1; end = state->pc + WC_REPEAT_SKIP(code); @@ -484,6 +518,7 @@ execrepeat(Estate state, UNUSED(int do_exec)) cmdpop(); popheap(); loops--; + simple_pline = old_simple_pline; state->pc = end; return lastval; } diff --git a/Src/math.c b/Src/math.c index 97a97b32b..977e92345 100644 --- a/Src/math.c +++ b/Src/math.c @@ -407,6 +407,13 @@ mathevall(char *s, enum prec_type prec_tp, char **ep) stack[0].val.type = MN_INTEGER; stack[0].val.u.l = 0; mathparse(prec_tp == MPREC_TOP ? TOPPREC : ARGPREC); + /* + * Internally, we parse the contents of parentheses at top + * precedence... so we can return a parenthesis here if + * there are too many at the end. + */ + if (mtok == M_OUTPAR && !errflag) + zerr("bad math expression: unexpected ')'"); *ep = ptr; DPUTS(!errflag && sp > 0, "BUG: math: wallabies roaming too freely in outback"); @@ -791,7 +798,7 @@ zzlex(void) ptr++; if (!*ptr) { - zerr("character missing after ##"); + zerr("bad math expression: character missing after ##"); return EOI; } ptr = getkeystring(ptr, NULL, GETKEYS_MATH, &v); @@ -914,7 +921,7 @@ setmathvar(struct mathvalue *mvp, mnumber v) mvp->pval = NULL; } if (!mvp->lval) { - zerr("lvalue required"); + zerr("bad math expression: lvalue required"); v.type = MN_INTEGER; v.u.l = 0; return v; @@ -1256,7 +1263,7 @@ op(int what) /* Error if (-num ** b) and b is not an integer */ double tst = (double)(zlong)b.u.d; if (tst != b.u.d) { - zerr("imaginary power"); + zerr("bad math expression: imaginary power"); return; } } @@ -1338,7 +1345,7 @@ op(int what) push(((a.type & MN_FLOAT) ? a.u.d : a.u.l) ? b : c, NULL, 0); break; case COLON: - zerr("':' without '?'"); + zerr("bad math expression: ':' without '?'"); break; case PREPLUS: if (spval->type & MN_FLOAT) @@ -1355,7 +1362,7 @@ op(int what) setmathvar(stack + sp, *spval); break; default: - zerr("out of integers"); + zerr("bad math expression: out of integers"); return; } } @@ -1525,7 +1532,7 @@ mathparse(int pc) mathparse(TOPPREC); if (mtok != M_OUTPAR) { if (!errflag) - zerr("')' expected"); + zerr("bad math expression: ')' expected"); return; } break; @@ -1543,7 +1550,7 @@ mathparse(int pc) noeval--; if (mtok != COLON) { if (!errflag) - zerr("':' expected"); + zerr("bad math expression: ':' expected"); return; } if (q) diff --git a/Src/options.c b/Src/options.c index 3e3e07474..1fb102f1d 100644 --- a/Src/options.c +++ b/Src/options.c @@ -81,6 +81,7 @@ static struct optname optns[] = { {{NULL, "allexport", OPT_EMULATE}, ALLEXPORT}, {{NULL, "alwayslastprompt", OPT_ALL}, ALWAYSLASTPROMPT}, {{NULL, "alwaystoend", 0}, ALWAYSTOEND}, +{{NULL, "appendcreate", OPT_EMULATE|OPT_BOURNE}, APPENDCREATE}, {{NULL, "appendhistory", OPT_ALL}, APPENDHISTORY}, {{NULL, "autocd", OPT_EMULATE}, AUTOCD}, {{NULL, "autocontinue", 0}, AUTOCONTINUE}, @@ -172,7 +173,7 @@ static struct optname optns[] = { {{NULL, "kshautoload", OPT_EMULATE|OPT_BOURNE}, KSHAUTOLOAD}, {{NULL, "kshglob", OPT_EMULATE|OPT_KSH}, KSHGLOB}, {{NULL, "kshoptionprint", OPT_EMULATE|OPT_KSH}, KSHOPTIONPRINT}, -{{NULL, "kshtypeset", OPT_EMULATE|OPT_KSH}, KSHTYPESET}, +{{NULL, "kshtypeset", 0}, KSHTYPESET}, {{NULL, "kshzerosubscript", 0}, KSHZEROSUBSCRIPT}, {{NULL, "listambiguous", OPT_ALL}, LISTAMBIGUOUS}, {{NULL, "listbeep", OPT_ALL}, LISTBEEP}, @@ -192,7 +193,7 @@ static struct optname optns[] = { {{NULL, "monitor", OPT_SPECIAL}, MONITOR}, {{NULL, "multibyte", #ifdef MULTIBYTE_SUPPORT - OPT_EMULATE|OPT_ZSH|OPT_CSH|OPT_KSH + OPT_ALL #else 0 #endif diff --git a/Src/params.c b/Src/params.c index 98541a6da..00f43e47d 100644 --- a/Src/params.c +++ b/Src/params.c @@ -196,7 +196,7 @@ static const struct gsu_integer ttyidle_gsu = { ttyidlegetfn, nullintsetfn, stdunsetfn }; static const struct gsu_scalar argzero_gsu = -{ argzerogetfn, nullstrsetfn, nullunsetfn }; +{ argzerogetfn, argzerosetfn, nullunsetfn }; static const struct gsu_scalar username_gsu = { usernamegetfn, usernamesetfn, stdunsetfn }; static const struct gsu_scalar dash_gsu = @@ -1116,14 +1116,12 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, Patprog pprog = NULL; /* - * If in NO_EXEC mode, the parameters won't be set up - * properly, so there's no point even doing any sanity checking. - * Just return 0 now. + * If in NO_EXEC mode, the parameters won't be set up properly, + * so just pretend everything is a hash for subscript parsing */ - if (unset(EXECOPT)) - return 0; - ishash = (v->pm && PM_TYPE(v->pm->node.flags) == PM_HASHED); + ishash = (unset(EXECOPT) || + (v->pm && PM_TYPE(v->pm->node.flags) == PM_HASHED)); if (prevcharlen) *prevcharlen = 1; if (nextcharlen) @@ -1278,8 +1276,18 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, } if (!c) return 0; - s = dupstrpfx(s, t - s); *str = tt = t; + + /* + * If in NO_EXEC mode, the parameters won't be set up properly, + * so there's no additional sanity checking we can do. + * Just return 0 now. + */ + if (unset(EXECOPT)) + return 0; + + s = dupstrpfx(s, t - s); + /* If we're NOT reverse subscripting, strip the inull()s so brackets * * are not backslashed after parsestr(). Otherwise leave them alone * * so that the brackets will be escaped when we patcompile() or when * @@ -2631,6 +2639,15 @@ getsparam(char *s) return getstrvalue(v); } +/**/ +mod_export char * +getsparam_u(char *s) +{ + if ((s = getsparam(s))) + return unmetafy(s, NULL); + return s; +} + /* Retrieve an array parameter */ /**/ @@ -3963,7 +3980,7 @@ setlang(char *x) struct localename *ln; char *x2; - if ((x2 = getsparam("LC_ALL")) && *x2) + if ((x2 = getsparam_u("LC_ALL")) && *x2) return; /* @@ -3977,10 +3994,10 @@ setlang(char *x) * from this is meaningless. So just all $LANG to show through in * that case. */ - setlocale(LC_ALL, x ? x : ""); + setlocale(LC_ALL, x ? unmeta(x) : ""); queue_signals(); for (ln = lc_names; ln->name; ln++) - if ((x = getsparam(ln->name)) && *x) + if ((x = getsparam_u(ln->name)) && *x) setlocale(ln->category, x); unqueue_signals(); } @@ -3996,7 +4013,7 @@ lc_allsetfn(Param pm, char *x) * that with any LC_* that are set. */ if (!x || !*x) { - x = getsparam("LANG"); + x = getsparam_u("LANG"); if (x && *x) { queue_signals(); setlang(x); @@ -4004,7 +4021,7 @@ lc_allsetfn(Param pm, char *x) } } else - setlocale(LC_ALL, x); + setlocale(LC_ALL, unmeta(x)); } /**/ @@ -4012,7 +4029,7 @@ void langsetfn(Param pm, char *x) { strsetfn(pm, x); - setlang(x); + setlang(unmeta(x)); } /**/ @@ -4038,12 +4055,27 @@ lcsetfn(Param pm, char *x) if (x && *x) { for (ln = lc_names; ln->name; ln++) if (!strcmp(ln->name, pm->node.nam)) - setlocale(ln->category, x); + setlocale(ln->category, unmeta(x)); } unqueue_signals(); } #endif /* USE_LOCALE */ +/* Function to set value for special parameter `0' */ + +/**/ +static void +argzerosetfn(UNUSED(Param pm), char *x) +{ + if (x) { + if (!isset(POSIXARGZERO)) { + zsfree(argzero); + argzero = ztrdup(x); + } + zsfree(x); + } +} + /* Function to get value for special parameter `0' */ /**/ @@ -5076,8 +5108,10 @@ printparamvalue(Param p, int printflags) break; case PM_ARRAY: /* array */ - if (!(printflags & PRINT_KV_PAIR)) + if (!(printflags & PRINT_KV_PAIR)) { putchar('('); + putchar(' '); + } u = p->gsu.a->getfn(p); if(*u) { quotedzputs(*u++, stdout); @@ -5086,13 +5120,17 @@ printparamvalue(Param p, int printflags) quotedzputs(*u++, stdout); } } - if (!(printflags & PRINT_KV_PAIR)) + if (!(printflags & PRINT_KV_PAIR)) { + putchar(' '); putchar(')'); + } break; case PM_HASHED: /* association */ - if (!(printflags & PRINT_KV_PAIR)) + if (!(printflags & PRINT_KV_PAIR)) { putchar('('); + putchar(' '); + } { HashTable ht = p->gsu.h->getfn(p); if (ht) diff --git a/Src/parse.c b/Src/parse.c index c932851d9..7c2d20250 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -63,6 +63,11 @@ int isnewlin; /**/ int infor; +/* != 0 if parsing arguments of typeset etc. */ + +/**/ +mod_export int intypeset; + /* list of here-documents */ /**/ @@ -118,11 +123,20 @@ struct heredocs *hdocs; * WC_ASSIGN * - data contains type (scalar, array) and number of array-elements * - followed by name and value + * Note variant for WC_TYPESET assignments: WC_ASSIGN_INC indicates + * a name with no equals, not an =+ which isn't valid here. * * WC_SIMPLE * - data contains the number of arguments (plus command) * - followed by strings * + * WC_TYPESET + * Variant of WC_SIMPLE used when TYPESET reserved word found. + * - data contains the number of string arguments (plus command) + * - followed by strings + * - followed by number of assignments + * - followed by assignments if non-zero number. + * * WC_SUBSH * - data unused * - followed by list @@ -257,6 +271,7 @@ parse_context_save(struct parse_stack *ps, int toplevel) ps->incasepat = incasepat; ps->isnewlin = isnewlin; ps->infor = infor; + ps->intypeset = intypeset; ps->hdocs = hdocs; ps->eclen = eclen; @@ -290,6 +305,7 @@ parse_context_restore(const struct parse_stack *ps, int toplevel) incasepat = ps->incasepat; isnewlin = ps->isnewlin; infor = ps->infor; + intypeset = ps->intypeset; hdocs = ps->hdocs; eclen = ps->eclen; @@ -430,7 +446,7 @@ init_parse_status(void) * between the two it's a bit ambiguous. We clear them when * using the lexical analyser for strings as well as here. */ - incasepat = incond = inredir = infor = 0; + incasepat = incond = inredir = infor = intypeset = 0; incmdpos = 1; } @@ -440,6 +456,8 @@ init_parse_status(void) void init_parse(void) { + queue_signals(); + if (ecbuf) zfree(ecbuf, eclen); ecbuf = (Wordcode) zalloc((eclen = EC_INIT_SIZE) * sizeof(wordcode)); @@ -450,6 +468,8 @@ init_parse(void) ecnfunc = 0; init_parse_status(); + + unqueue_signals(); } /* Build eprog. */ @@ -472,6 +492,8 @@ bld_eprog(int heap) Eprog ret; int l; + queue_signals(); + ecadd(WCB_END()); ret = heap ? (Eprog) zhalloc(sizeof(*ret)) : (Eprog) zalloc(sizeof(*ret)); @@ -495,6 +517,8 @@ bld_eprog(int heap) zfree(ecbuf, eclen); ecbuf = NULL; + unqueue_signals(); + return ret; } @@ -992,6 +1016,7 @@ par_cmd(int *cmplx, int zsh_construct) incmdpos = 1; incasepat = 0; incond = 0; + intypeset = 0; return 1; } @@ -1575,9 +1600,9 @@ par_funcdef(int *cmplx) p = ecadd(0); ecadd(0); - incmdpos = 1; while (tok == STRING) { - if (*tokstr == Inbrace && !tokstr[1]) { + if ((*tokstr == Inbrace || *tokstr == '{') && + !tokstr[1]) { tok = INBRACE; break; } @@ -1590,6 +1615,7 @@ par_funcdef(int *cmplx) ecadd(0); nocorrect = 0; + incmdpos = 1; if (tok == INOUTPAR) zshlex(); while (tok == SEPER) @@ -1709,7 +1735,8 @@ static int 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; + int c = *cmplx, nrediradd, assignments = 0, ppost = 0, is_typeset = 0; + wordcode postassigns = 0; r = ecused; for (;;) { @@ -1717,31 +1744,32 @@ par_simple(int *cmplx, int nr) *cmplx = c = 1; nocorrect = 1; } else if (tok == ENVSTRING) { - char *p, *name, *str; + char *ptr, *name, *str; name = tokstr; - for (p = tokstr; *p && *p != Inbrack && *p != '=' && *p != '+'; - p++); - if (*p == Inbrack) skipparens(Inbrack, Outbrack, &p); - if (*p == '+') { - *p++ = '\0'; + for (ptr = tokstr; + *ptr && *ptr != Inbrack && *ptr != '=' && *ptr != '+'; + ptr++); + if (*ptr == Inbrack) skipparens(Inbrack, Outbrack, &ptr); + if (*ptr == '+') { + *ptr++ = '\0'; ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0)); } else ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0)); - - if (*p == '=') { - *p = '\0'; - str = p + 1; + + if (*ptr == '=') { + *ptr = '\0'; + str = ptr + 1; } else equalsplit(tokstr, &str); - for (p = str; *p; p++) { + for (ptr = str; *ptr; ptr++) { /* * We can't treat this as "simple" if it contains * expansions that require process subsitution, since then * we need process handling. */ - if (p[1] == Inpar && - (*p == Equals || *p == Inang || *p == OutangProc)) { + if (ptr[1] == Inpar && + (*ptr == Equals || *ptr == Inang || *ptr == OutangProc)) { *cmplx = 1; break; } @@ -1786,14 +1814,18 @@ par_simple(int *cmplx, int nr) p = ecadd(WCB_SIMPLE(0)); for (;;) { - if (tok == STRING) { + if (tok == STRING || tok == TYPESET) { int redir_var = 0; *cmplx = 1; incmdpos = 0; + if (tok == TYPESET) + intypeset = is_typeset = 1; + if (!isset(IGNOREBRACES) && *tokstr == Inbrace) { + /* Look for redirs of the form {var}>file etc. */ char *eptr = tokstr + strlen(tokstr) - 1; char *ptr = eptr; @@ -1824,15 +1856,74 @@ par_simple(int *cmplx, int nr) if (!redir_var) { - ecstr(tokstr); - argc++; + if (postassigns) { + /* + * We're in the variable part of a typeset, + * but this doesn't have an assignment. + * We'll parse it as if it does, but mark + * it specially with WC_ASSIGN_INC. + */ + postassigns++; + ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0)); + ecstr(tokstr); + ecstr(""); /* TBD can possibly optimise out */ + } else { + ecstr(tokstr); + argc++; + } zshlex(); } } else if (IS_REDIROP(tok)) { *cmplx = c = 1; nrediradd = par_redir(&r, NULL); p += nrediradd; + if (ppost) + ppost += nrediradd; sr += nrediradd; + } else if (tok == ENVSTRING) { + char *ptr, *name, *str; + + if (!postassigns++) + ppost = ecadd(0); + + name = tokstr; + for (ptr = tokstr; *ptr && *ptr != Inbrack && *ptr != '=' && *ptr != '+'; + ptr++); + if (*ptr == Inbrack) skipparens(Inbrack, Outbrack, &ptr); + ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0)); + + if (*ptr == '=') { + *ptr = '\0'; + str = ptr + 1; + } else + equalsplit(tokstr, &str); + ecstr(name); + ecstr(str); + zshlex(); + } else if (tok == ENVARRAY) { + int n, parr; + + if (!postassigns++) + ppost = ecadd(0); + + parr = ecadd(0); + ecstr(tokstr); + cmdpush(CS_ARRAY); + /* + * Careful here: this must be the typeset case, + * but we need to tell the lexer not to look + * for assignments until we've finished the + * present one. + */ + intypeset = 0; + zshlex(); + n = par_nl_wordlist(); + ecbuf[parr] = WCB_ASSIGN(WC_ASSIGN_ARRAY, WC_ASSIGN_NEW, n); + cmdpop(); + intypeset = 1; + if (tok != OUTPAR) + YYERROR(oecused); + zshlex(); } else if (tok == INOUTPAR) { zlong oldlineno = lineno; int onp, so, oecssub = ecssub; @@ -1841,7 +1932,7 @@ par_simple(int *cmplx, int nr) if (!isset(MULTIFUNCDEF) && argc > 1) YYERROR(oecused); /* Error if preceding assignments */ - if (assignments) + if (assignments || postassigns) YYERROR(oecused); *cmplx = c; @@ -1947,9 +2038,18 @@ par_simple(int *cmplx, int nr) return 0; } incmdpos = 1; + intypeset = 0; - if (!isfunc) - ecbuf[p] = WCB_SIMPLE(argc); + if (!isfunc) { + if (is_typeset) { + ecbuf[p] = WCB_TYPESET(argc); + if (postassigns) + ecbuf[ppost] = postassigns; + else + ecadd(0); + } else + ecbuf[p] = WCB_SIMPLE(argc); + } return sr + 1; } diff --git a/Src/pattern.c b/Src/pattern.c index 4e5e8a110..7d38988a0 100644 --- a/Src/pattern.c +++ b/Src/pattern.c @@ -520,6 +520,8 @@ patcompile(char *exp, int inflags, char **endexp) char *lng, *strp = NULL; Patprog p; + queue_signals(); + startoff = sizeof(struct patprog); /* Ensure alignment of start of program string */ startoff = (startoff + sizeof(union upat) - 1) & ~(sizeof(union upat) - 1); @@ -582,8 +584,10 @@ patcompile(char *exp, int inflags, char **endexp) if (!strp || (*strp && *strp != '/')) { /* No, do normal compilation. */ strp = NULL; - if (patcompswitch(0, &flags) == 0) + if (patcompswitch(0, &flags) == 0) { + unqueue_signals(); return NULL; + } } else { /* * Yes, copy the string, and skip compilation altogether. @@ -715,6 +719,8 @@ patcompile(char *exp, int inflags, char **endexp) if (endexp) *endexp = patparse; + + unqueue_signals(); return p; } @@ -1113,8 +1119,8 @@ range_type(char *start, int len) const char **csp; for (csp = colon_stuffs; *csp; csp++) { - if (!strncmp(start, *csp, len)) - return (csp - colon_stuffs) + PP_FIRST; + if (strlen(*csp) == len && !strncmp(start, *csp, len)) + return (csp - colon_stuffs) + PP_FIRST; } return PP_UNKWN; @@ -2202,20 +2208,15 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen, if ((patglobflags & GF_MATCHREF) && !(patflags & PAT_FILE)) { char *str = ztrduppfx(patinstart, patinlen); - char *ptr = patinstart; - int mlen = 0; + int mlen; /* * Count the characters. We're not using CHARSUB() - * because the string is still metafied. We're - * not using mb_metastrlen() because that expects - * the string to be null terminated. + * because the string is still metafied. */ MB_METACHARINIT(); - while (ptr < patinstart + patinlen) { - mlen++; - ptr += MB_METACHARLEN(ptr); - } + mlen = MB_METASTRLEN2END(patinstart, 0, + patinstart + patinlen); setsparam("MATCH", str); setiparam("MBEGIN", diff --git a/Src/prompt.c b/Src/prompt.c index ffc1d0df2..be067ee7e 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -271,7 +271,7 @@ static int putpromptchar(int doprint, int endchar, unsigned int *txtchangep) { char *ss, *hostnam; - int t0, arg, test, sep, j, numjobs; + int t0, arg, test, sep, j, numjobs, len; struct tm *tm; struct timezone dummy_tz; struct timeval tv; @@ -673,12 +673,14 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep) */ for(j = 0, t0 = strlen(tmfmt)*8; j < 3; j++, t0*=2) { addbufspc(t0); - if (ztrftime(bv->bp, t0, tmfmt, tm, tv.tv_usec) >= 0) + if ((len = ztrftime(bv->bp, t0, tmfmt, tm, tv.tv_usec)) + >= 0) break; } /* There is enough room for this because addbufspc(t0) * allocates room for t0 * 2 bytes. */ - metafy(bv->bp, -1, META_NOALLOC); + if (len >= 0) + metafy(bv->bp, len, META_NOALLOC); bv->bp += strlen(bv->bp); zsfree(tmbuf); break; @@ -964,7 +966,7 @@ stradd(char *d) /* FALL THROUGH */ default: /* Take full wide character in one go */ - mb_metacharinit(); + mb_charinit(); pc = wcs_nicechar(cc, NULL, NULL); break; } diff --git a/Src/signals.c b/Src/signals.c index 3950ad1a2..f45c1860c 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -487,6 +487,12 @@ wait_for_processes(void) break; } + /* This is necessary to be sure queueing_enabled > 0 when + * we enter printjob() from update_job(), so that we don't + * decrement to zero in should_report_time() and improperly + * run other handlers in the middle of processing this one */ + queue_signals(); + /* * Find the process and job containing this pid and * update it. @@ -528,8 +534,16 @@ wait_for_processes(void) * and is not equal to the current foreground job. */ if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) && - jn - jobtab != thisjob) - addbgstatus(pid, (int)lastval2); + jn - jobtab != thisjob) { + int val = (WIFSIGNALED(status) ? + 0200 | WTERMSIG(status) : + (WIFSTOPPED(status) ? + 0200 | WEXITSTATUS(status) : + WEXITSTATUS(status))); + addbgstatus(pid, val); + } + + unqueue_signals(); } } @@ -1207,6 +1221,8 @@ dotrapargs(int sig, int *sigtr, void *sigfn) } } + queue_signals(); /* Any time we manage memory or global state */ + intrap++; *sigtr |= ZSIG_IGNORED; @@ -1244,7 +1260,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn) sfcontext = SFC_SIGNAL; incompfunc = 0; - doshfunc((Shfunc)sigfn, args, 1); + doshfunc((Shfunc)sigfn, args, 1); /* manages signal queueing */ sfcontext = osc; incompfunc= old_incompfunc; freelinklist(args, (FreeFunc) NULL); @@ -1254,7 +1270,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn) trap_state = TRAP_STATE_PRIMED; trapisfunc = isfunc = 0; - execode((Eprog)sigfn, 1, 0, "trap"); + execode((Eprog)sigfn, 1, 0, "trap"); /* manages signal queueing */ } runhookdef(AFTERTRAPHOOK, NULL); @@ -1321,6 +1337,8 @@ dotrapargs(int sig, int *sigtr, void *sigfn) if (*sigtr != ZSIG_IGNORED) *sigtr &= ~ZSIG_IGNORED; intrap--; + + unqueue_signals(); } /* Standard call to execute a trap for a given signal. */ diff --git a/Src/subst.c b/Src/subst.c index 81d34d28a..021d23444 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -3834,8 +3834,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) y = dupstring(nulstring); insertlinknode(l, n, (void *) y), incnode(n); } - if (eval) - n = on; + /* This used to omit restoring of *str and instead test + * if (eval) + * n = on; + * but that causes strange behavior of history modifiers when + * applied across all values of an array. What is magic about + * eval here that *str seemed not to need restoring? + */ + *str = getdata(n = on); } else { /* * Scalar value. Handle last minute transformations diff --git a/Src/text.c b/Src/text.c index 958303c68..3978a26a9 100644 --- a/Src/text.c +++ b/Src/text.c @@ -30,6 +30,16 @@ #include "zsh.mdh" #include "text.pro" +/* + * If non-zero, expand syntactically significant leading tabs in text + * to this number of spaces. + * + * If negative, don't output leading whitespace at all. + */ + +/**/ +int text_expand_tabs; + static char *tptr, *tbuf, *tlim, *tpending; static int tsiz, tindent, tnewlins, tjob; @@ -67,7 +77,7 @@ taddpending(char *str1, char *str2) */ if (tpending) { int oldlen = strlen(tpending); - tpending = realloc(tpending, len + oldlen); + tpending = zrealloc(tpending, len + oldlen); sprintf(tpending + oldlen, "%s%s", str1, str2); } else { tpending = (char *)zalloc(len); @@ -100,7 +110,7 @@ taddchr(int c) tptr--; return; } - tbuf = realloc(tbuf, tsiz *= 2); + tbuf = zrealloc(tbuf, tsiz *= 2); tlim = tbuf + tsiz; tptr = tbuf + tsiz / 2; } @@ -120,7 +130,7 @@ taddstr(char *s) if (!tbuf) return; - tbuf = realloc(tbuf, tsiz *= 2); + tbuf = zrealloc(tbuf, tsiz *= 2); tlim = tbuf + tsiz; tptr = tbuf + x; } @@ -145,6 +155,48 @@ taddlist(Estate state, int num) } } +/* add an assignment */ + +static void +taddassign(wordcode code, Estate state, int typeset) +{ + /* name */ + taddstr(ecgetstr(state, EC_NODUP, NULL)); + /* value... maybe */ + if (WC_ASSIGN_TYPE2(code) == WC_ASSIGN_INC) { + if (typeset) { + /* dummy assignment --- just var name */ + (void)ecgetstr(state, EC_NODUP, NULL); + taddchr(' '); + return; + } + taddchr('+'); + } + taddchr('='); + if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) { + taddchr('('); + taddlist(state, WC_ASSIGN_NUM(code)); + taddstr(") "); + } else { + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddchr(' '); + } +} + +/* add a number of assignments from typeset */ + +/**/ +static void +taddassignlist(Estate state, wordcode count) +{ + if (count) + taddchr(' '); + while (count--) { + wordcode code = *state->pc++; + taddassign(code, state, 1); + } +} + /* add a newline, or something equivalent, to the text buffer */ /**/ @@ -156,8 +208,16 @@ taddnl(int no_semicolon) if (tnewlins) { tdopending(); taddchr('\n'); - for (t0 = 0; t0 != tindent; t0++) - taddchr('\t'); + for (t0 = 0; t0 != tindent; t0++) { + if (text_expand_tabs >= 0) { + if (text_expand_tabs) { + int t1; + for (t1 = 0; t1 < text_expand_tabs; t1++) + taddchr(' '); + } else + taddchr('\t'); + } + } } else if (no_semicolon) { taddstr(" "); } else { @@ -165,6 +225,30 @@ taddnl(int no_semicolon) } } +/* + * Output a tab that may be expanded as part of a leading set. + * Note this is not part of the text framework; it's for + * code that needs to output its own tabs that are to be + * consistent with those from getpermtext(). + * + * Note these tabs are only expected to be useful at the + * start of the line, so we make no attempt to count columns. + */ + +/**/ +void +zoutputtab(FILE *outf) +{ + if (text_expand_tabs < 0) + return; + if (text_expand_tabs) { + int i; + for (i = 0; i < text_expand_tabs; i++) + fputc(' ', outf); + } else + fputc('\t', outf); +} + /* get a permanent textual representation of n */ /**/ @@ -397,22 +481,17 @@ gettext2(Estate state) } break; case WC_ASSIGN: - taddstr(ecgetstr(state, EC_NODUP, NULL)); - if (WC_ASSIGN_TYPE2(code) == WC_ASSIGN_INC) taddchr('+'); - taddchr('='); - if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) { - taddchr('('); - taddlist(state, WC_ASSIGN_NUM(code)); - taddstr(") "); - } else { - taddstr(ecgetstr(state, EC_NODUP, NULL)); - taddchr(' '); - } + taddassign(code, state, 0); break; case WC_SIMPLE: taddlist(state, WC_SIMPLE_ARGC(code)); stack = 1; break; + case WC_TYPESET: + taddlist(state, WC_TYPESET_ARGC(code)); + taddassignlist(state, *state->pc++); + stack = 1; + break; case WC_SUBSH: if (!s) { taddstr("("); @@ -602,7 +681,7 @@ gettext2(Estate state) case WC_CASE: if (!s) { Wordcode end = state->pc + WC_CASE_SKIP(code); - wordcode nalts; + wordcode ialts; taddstr("case "); taddstr(ecgetstr(state, EC_NODUP, NULL)); @@ -616,6 +695,7 @@ gettext2(Estate state) taddstr("esac"); stack = 1; } else { + Wordcode prev_pc; tindent++; if (tnewlins) taddnl(0); @@ -623,21 +703,23 @@ gettext2(Estate state) taddchr(' '); taddstr("("); code = *state->pc++; - nalts = *state->pc++; - while (nalts--) { + prev_pc = state->pc++; + ialts = *prev_pc; + while (ialts--) { taddstr(ecgetstr(state, EC_NODUP, NULL)); state->pc++; - if (nalts) + if (ialts) taddstr(" | "); } taddstr(") "); tindent++; n = tpush(code, 0); n->u._case.end = end; - n->pop = (state->pc - 2 + WC_CASE_SKIP(code) >= end); + n->pop = (prev_pc + WC_CASE_SKIP(code) >= end); } } else if (state->pc < s->u._case.end) { - wordcode nalts; + Wordcode prev_pc; + wordcode ialts; dec_tindent(); switch (WC_CASE_TYPE(code)) { case WC_CASE_OR: @@ -658,17 +740,18 @@ gettext2(Estate state) taddchr(' '); taddstr("("); code = *state->pc++; - nalts = *state->pc++; - while (nalts--) { + prev_pc = state->pc++; + ialts = *prev_pc; + while (ialts--) { taddstr(ecgetstr(state, EC_NODUP, NULL)); state->pc++; - if (nalts) + if (ialts) taddstr(" | "); } taddstr(") "); tindent++; s->code = code; - s->pop = ((state->pc - 2 + WC_CASE_SKIP(code)) >= + s->pop = (prev_pc + WC_CASE_SKIP(code) >= s->u._case.end); } else { dec_tindent(); diff --git a/Src/utils.c b/Src/utils.c index 271c800fd..4c4dc55cd 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -82,7 +82,7 @@ set_widearray(char *mb_array, Widechar_array wca) wchar_t *wcptr = tmpwcs; wint_t wci; - mb_metacharinit(); + mb_charinit(); while (*mb_array) { int mblen = mb_metacharlenconv(mb_array, &wci); @@ -248,7 +248,7 @@ VA_DCL VA_START(ap, message); VA_GET_ARG(ap, message, const char *); - if ((filename = getsparam("ZSH_DEBUG_LOG")) != NULL && + if ((filename = getsparam_u("ZSH_DEBUG_LOG")) != NULL && (file = fopen(filename, "a")) != NULL) { zerrmsg(file, message, ap); fclose(file); @@ -332,7 +332,7 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) case 'c': num = va_arg(ap, int); #ifdef MULTIBYTE_SUPPORT - mb_metacharinit(); + mb_charinit(); zputs(wcs_nicechar(num, NULL, NULL), file); #else zputs(nicechar(num), file); @@ -461,12 +461,13 @@ static mbstate_t mb_shiftstate; /* * Initialise multibyte state: called before a sequence of - * wcs_nicechar() or mb_metacharlenconv(). + * wcs_nicechar(), mb_metacharlenconv(), or + * mb_charlenconv(). */ /**/ mod_export void -mb_metacharinit(void) +mb_charinit(void) { memset(&mb_shiftstate, 0, sizeof(mb_shiftstate)); } @@ -500,7 +501,7 @@ mb_metacharinit(void) * (but not both). (Note the complication that the wide character * part may contain metafied characters.) * - * The caller needs to call mb_metacharinit() before the first call, to + * The caller needs to call mb_charinit() before the first call, to * set up the multibyte shift state for a range of characters. */ @@ -1948,7 +1949,8 @@ extern char *_mktemp(char *); /* Get a unique filename for use as a temporary file. If "prefix" is * NULL, the name is relative to $TMPPREFIX; If it is non-NULL, the * unique suffix includes a prefixed '.' for improved readability. If - * "use_heap" is true, we allocate the returned name on the heap. */ + * "use_heap" is true, we allocate the returned name on the heap. + * The string passed as "prefix" is expected to be metafied. */ /**/ mod_export char * @@ -1975,6 +1977,9 @@ gettempname(const char *prefix, int use_heap) return ret; } +/* The gettempfile() "prefix" is expected to be metafied, see hist.c + * and gettempname(). */ + /**/ mod_export int gettempfile(const char *prefix, int use_heap, char **tempname) @@ -2873,6 +2878,10 @@ ztrftimebuf(int *bufsizeptr, int decr) * not enough memory --- and return -1. Not guaranteed to be portable, * since the strftime() interface doesn't make any guarantees about * the state of the buffer if it returns zero. + * + * fmt is metafied, but we need to unmetafy it on the fly to + * pass into strftime / combine with the output from strftime. + * The return value in buf is not metafied. */ /**/ @@ -2882,7 +2891,7 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) int hr12; #ifdef HAVE_STRFTIME int decr; - char tmp[4]; + char *fmtstart; #else static char *astr[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; @@ -2893,12 +2902,22 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) char *origbuf = buf; - while (*fmt) - if (*fmt == '%') { + while (*fmt) { + if (*fmt == Meta) { + int chr = fmt[1] ^ 32; + if (ztrftimebuf(&bufsize, 1)) + return -1; + *buf++ = chr; + fmt += 2; + } else if (*fmt == '%') { int strip; int digs = 3; +#ifdef HAVE_STRFTIME + fmtstart = +#endif fmt++; + if (*fmt == '-') { strip = 1; fmt++; @@ -2923,6 +2942,21 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) */ if (ztrftimebuf(&bufsize, 2)) return -1; +#ifdef HAVE_STRFTIME + /* Our internal handling doesn't handle padding and other gnu extensions, + * so here we detect them and pass over to strftime(). We don't want + * to do this unconditionally though, as we have some extensions that + * strftime() doesn't have (%., %f, %L and %K) */ +morefmt: + if (!((fmt - fmtstart == 1) || (fmt - fmtstart == 2 && strip) || *fmt == '.')) { + while (*fmt && strchr("OE^#_-0123456789", *fmt)) + fmt++; + if (*fmt) { + fmt++; + goto strftimehandling; + } + } +#endif switch (*fmt++) { case '.': if (ztrftimebuf(&bufsize, digs)) @@ -2938,10 +2972,10 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) sprintf(buf, "%0*ld", digs, usec); buf += digs; break; - case 'd': - if (tm->tm_mday > 9 || !strip) - *buf++ = '0' + tm->tm_mday / 10; - *buf++ = '0' + tm->tm_mday % 10; + case '\0': + /* Guard against premature end of string */ + *buf++ = '%'; + fmt--; break; case 'f': strip = 1; @@ -2982,6 +3016,11 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) *buf++ = '0' + (hr12 % 10); break; + case 'd': + if (tm->tm_mday > 9 || !strip) + *buf++ = '0' + tm->tm_mday / 10; + *buf++ = '0' + tm->tm_mday % 10; + break; case 'm': if (tm->tm_mon > 8 || !strip) *buf++ = '0' + (tm->tm_mon + 1) / 10; @@ -3002,18 +3041,9 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) *buf++ = '0' + (tm->tm_year / 10) % 10; *buf++ = '0' + tm->tm_year % 10; break; - case '\0': - /* Guard against premature end of string */ - *buf++ = '%'; - fmt--; - break; #ifndef HAVE_STRFTIME case 'Y': { - /* - * Not worth handling this natively if - * strftime has it. - */ int year, digits, testyear; year = tm->tm_year + 1900; digits = 1; @@ -3047,24 +3077,51 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) if (fmt[-1] != '%') *buf++ = fmt[-1]; #else + case 'E': + case 'O': + case '^': + case '#': + case '_': + case '-': + case '0' ... '9': + goto morefmt; +strftimehandling: default: /* * Remember we've already allowed for two characters * in the accounting in bufsize (but nowhere else). */ - *buf = '\1'; - sprintf(tmp, strip ? "%%-%c" : "%%%c", fmt[-1]); - if (!strftime(buf, bufsize + 2, tmp, tm)) { - if (*buf) { - buf[0] = '\0'; - return -1; + int size = fmt - fmtstart; + char *tmp, *last; + tmp = zhalloc(size + 1); + strncpy(tmp, fmtstart, size); + last = fmt-1; + if (*last == Meta) { + /* + * This is for consistency in counting: + * a metafiable character isn't actually + * a valid strftime descriptor. + * + * Previous characters were explicitly checked, + * so can't be metafied. + */ + *last = *++fmt ^ 32; + } + tmp[size] = '\0'; + *buf = '\1'; + if (!strftime(buf, bufsize + 2, tmp, tm)) + { + if (*buf) { + buf[0] = '\0'; + return -1; + } + return 0; } - return 0; + decr = strlen(buf); + buf += decr; + bufsize -= decr - 2; } - decr = strlen(buf); - buf += decr; - bufsize -= decr - 2; #endif break; } @@ -3073,6 +3130,7 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) return -1; *buf++ = *fmt++; } + } *buf = '\0'; return buf - origbuf; } @@ -3555,7 +3613,7 @@ zbeep(void) { char *vb; queue_signals(); - if ((vb = getsparam("ZBEEP"))) { + if ((vb = getsparam_u("ZBEEP"))) { int len; vb = getkeystring(vb, &len, GETKEYS_BINDKEY, NULL); write_loop(SHTTY, vb, len); @@ -3832,7 +3890,7 @@ itype_end(const char *ptr, int itype, int once) #ifdef MULTIBYTE_SUPPORT if (isset(MULTIBYTE) && (itype != IIDENT || !isset(POSIXIDENTIFIERS))) { - mb_metacharinit(); + mb_charinit(); while (*ptr) { wint_t wc; int len = mb_metacharlenconv(ptr, &wc); @@ -4367,7 +4425,10 @@ unmeta(const char *file_name) char *p; const char *t; int newsz, meta; - + + if (!file_name) + return NULL; + meta = 0; for (t = file_name; *t; t++) { if (*t == Meta) @@ -4471,9 +4532,37 @@ ztrlen(char const *s) for (l = 0; *s; l++) { if (*s++ == Meta) { #ifdef DEBUG - if (! *s) + if (! *s) { fprintf(stderr, "BUG: unexpected end of string in ztrlen()\n"); - else + break; + } else +#endif + s++; + } + } + return l; +} + +#ifndef MULTIBYTE_SUPPORT +/* + * ztrlen() but with explicit end point for non-null-terminated + * segments. eptr may not be NULL. + */ + +/**/ +mod_export int +ztrlenend(char const *s, char const *eptr) +{ + int l; + + for (l = 0; s < eptr; l++) { + if (*s++ == Meta) { +#ifdef DEBUG + if (! *s) { + fprintf(stderr, + "BUG: unexpected end of string in ztrlenend()\n"); + break; + } else #endif s++; } @@ -4481,6 +4570,8 @@ ztrlen(char const *s) return l; } +#endif /* MULTIBYTE_SUPPORT */ + /* Subtract two pointers in a metafied string. */ /**/ @@ -4879,11 +4970,16 @@ mb_metacharlenconv(const char *s, wint_t *wcp) * If width is 1, return total character width rather than number. * If width is greater than 1, return 1 if character has non-zero width, * else 0. + * + * Ends if either *ptr is '\0', the normal case (eptr may be NULL for + * this), or ptr is eptr (i.e. *eptr is where the null would be if null + * terminated) for strings not delimited by nulls --- note these are + * still metafied. */ /**/ mod_export int -mb_metastrlen(char *ptr, int width) +mb_metastrlenend(char *ptr, int width, char *eptr) { char inchar, *laststart; size_t ret; @@ -4898,7 +4994,7 @@ mb_metastrlen(char *ptr, int width) num = num_in_char = 0; memset(&mb_shiftstate, 0, sizeof(mb_shiftstate)); - while (*ptr) { + while (*ptr && !(eptr && ptr >= eptr)) { if (*ptr == Meta) inchar = *++ptr ^ 32; else @@ -4937,6 +5033,65 @@ mb_metastrlen(char *ptr, int width) return num + num_in_char; } +/* + * The equivalent of mb_metacharlenconv_r() for + * strings that aren't metafied and hence have + * explicit lengths. + */ + +/**/ +mod_export int +mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp) +{ + size_t ret = MB_INVALID; + char inchar; + const char *ptr; + wchar_t wc; + + for (ptr = s; slen; ) { + inchar = *ptr; + ptr++; + slen--; + ret = mbrtowc(&wc, &inchar, 1, mbsp); + + if (ret == MB_INVALID) + break; + if (ret == MB_INCOMPLETE) + continue; + if (wcp) + *wcp = wc; + return ptr - s; + } + + if (wcp) + *wcp = WEOF; + /* No valid multibyte sequence */ + memset(mbsp, 0, sizeof(*mbsp)); + if (ptr > s) { + return 1; /* Treat as single byte character */ + } else + return 0; /* Probably shouldn't happen */ +} + +/* + * The equivalent of mb_metacharlenconv() for + * strings that aren't metafied and hence have + * explicit lengths; + */ + +/**/ +mod_export int +mb_charlenconv(const char *s, int slen, wint_t *wcp) +{ + if (!isset(MULTIBYTE)) { + if (wcp) + *wcp = (wint_t)*s; + return 1; + } + + return mb_charlenconv_r(s, slen, wcp, &mb_shiftstate); +} + /**/ #else @@ -4961,8 +5116,127 @@ metacharlenconv(const char *x, int *c) return 1; } +/* Simple replacement for mb_charlenconv */ + /**/ +mod_export int +charlenconv(const char *x, int len, int *c) +{ + if (!len) { + if (c) + *c = '\0'; + return 0; + } + + if (c) + *c = (char)*x; + return 1; +} + +/**/ +#endif /* MULTIBYTE_SUPPORT */ + +/* + * Expand tabs to given width, with given starting position on line. + * len is length of unmetafied string in bytes. + * Output to fout. + * Return the end position on the line, i.e. if this is 0 modulo width + * the next character is aligned with a tab stop. + * + * If all is set, all tabs are expanded, else only leading tabs. + */ + +/**/ +mod_export int +zexpandtabs(const char *s, int len, int width, int startpos, FILE *fout, + int all) +{ + int at_start = 1; + +#ifdef MULTIBYTE_SUPPORT + mbstate_t mbs; + size_t ret; + wchar_t wc; + + memset(&mbs, 0, sizeof(mbs)); +#endif + + while (len) { + if (*s == '\t') { + if (all || at_start) { + s++; + len--; + if (width <= 0 || !(startpos % width)) { + /* always output at least one space */ + fputc(' ', fout); + startpos++; + } + if (width <= 0) + continue; /* paranoia */ + while (startpos % width) { + fputc(' ', fout); + startpos++; + } + } else { + /* + * Leave tab alone. + * Guess width to apply... we might get this wrong. + * This is only needed if there's a following string + * that needs tabs expanding, which is unusual. + */ + startpos += width - startpos % width; + s++; + len--; + fputc('\t', fout); + } + continue; + } else if (*s == '\n' || *s == '\r') { + fputc(*s, fout); + s++; + len--; + startpos = 0; + at_start = 1; + continue; + } + + at_start = 0; +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) { + const char *sstart = s; + ret = mbrtowc(&wc, s, len, &mbs); + if (ret == MB_INVALID) { + /* Assume single character per character */ + memset(&mbs, 0, sizeof(mbs)); + s++; + len--; + } else if (ret == MB_INCOMPLETE) { + /* incomplete at end --- assume likewise, best we've got */ + s++; + len--; + } else { + s += ret; + len -= (int)ret; + } + if (ret == MB_INVALID || ret == MB_INCOMPLETE) { + startpos++; + } else { + int wcw = WCWIDTH(wc); + if (wcw > 0) /* paranoia */ + startpos += wcw; + } + fwrite(sstart, s - sstart, 1, fout); + + continue; + } #endif /* MULTIBYTE_SUPPORT */ + fputc(*s, fout); + s++; + len--; + startpos++; + } + + return startpos; +} /* check for special characters in the string */ @@ -5301,7 +5575,25 @@ quotestring(const char *s, char **e, int instring) /* Needs to be passed straight through. */ if (dobackslash) *v++ = '\\'; - *v++ = *u++; + if (*u == Inparmath) { + /* + * Already syntactically quoted: don't + * add more. + */ + int inmath = 1; + *v++ = *u++; + for (;;) { + char uc = *u; + *v++ = *u++; + if (uc == '\0') + break; + else if (uc == Outparmath && !--inmath) + break; + else if (uc == Inparmath) + ++inmath; + } + } else + *v++ = *u++; continue; } @@ -6148,10 +6440,15 @@ init_dirsav(Dirsav d) d->dirfd = d->level = -1; } -/* Change directory, without following symlinks. Returns 0 on success, -1 * - * on failure. Sets errno to ENOTDIR if any symlinks are encountered. If * - * fchdir() fails, or the current directory is unreadable, we might end up * - * in an unwanted directory in case of failure. */ +/* + * Change directory, without following symlinks. Returns 0 on success, -1 + * on failure. Sets errno to ENOTDIR if any symlinks are encountered. If + * fchdir() fails, or the current directory is unreadable, we might end up + * in an unwanted directory in case of failure. + * + * path is an unmetafied but null-terminated string, as needed by system + * calls. + */ /**/ mod_export int diff --git a/Src/watch.c b/Src/watch.c index fe409f91a..c804913ad 100644 --- a/Src/watch.c +++ b/Src/watch.c @@ -237,6 +237,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) time_t timet; struct tm *tm; char *fm2; + int len; # ifdef WATCH_UTMP_UT_HOST char *p; int i; @@ -330,7 +331,9 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) } timet = getlogtime(u, inout); tm = localtime(&timet); - ztrftime(buf, 40, fm2, tm, 0L); + len = ztrftime(buf, 40, fm2, tm, 0L); + if (len > 0) + metafy(buf, len, META_NOALLOC); printf("%s", (*buf == ' ') ? buf + 1 : buf); break; case '%': @@ -566,7 +569,7 @@ dowatch(void) return; } queue_signals(); - if (!(fmt = getsparam("WATCHFMT"))) + if (!(fmt = getsparam_u("WATCHFMT"))) fmt = DEFAULT_WATCHFMT; while ((uct || wct) && !errflag) if (!uct || (wct && ucmp(uptr, wptr) > 0)) @@ -336,7 +336,8 @@ enum lextok { THEN, /* then */ TIME, /* time */ /* 60 */ UNTIL, /* until */ - WHILE /* while */ + WHILE, /* while */ + TYPESET /* typeset or similar */ }; /* Redirection types. If you modify this, you may also have to modify * @@ -424,6 +425,7 @@ enum { #define INP_HISTCONT (1<<5) /* stack is continued from history expn. */ #define INP_LINENO (1<<6) /* update line number */ #define INP_APPEND (1<<7) /* Append new lines to allow backup */ +#define INP_RAW_KEEP (1<<8) /* Input needed in raw mode even if alias */ /* Flags for metafy */ #define META_REALLOC 0 @@ -671,14 +673,6 @@ struct multio { int fds[MULTIOUNIT]; /* list of src/dests redirected to/from this fd */ }; -/* structure for foo=bar assignments */ - -struct asgment { - struct asgment *next; - char *name; - char *value; -}; - /* lvalue for variable assignment/expansion */ struct value { @@ -789,23 +783,24 @@ struct eccstr { #define WC_REDIR 4 #define WC_ASSIGN 5 #define WC_SIMPLE 6 -#define WC_SUBSH 7 -#define WC_CURSH 8 -#define WC_TIMED 9 -#define WC_FUNCDEF 10 -#define WC_FOR 11 -#define WC_SELECT 12 -#define WC_WHILE 13 -#define WC_REPEAT 14 -#define WC_CASE 15 -#define WC_IF 16 -#define WC_COND 17 -#define WC_ARITH 18 -#define WC_AUTOFN 19 -#define WC_TRY 20 +#define WC_TYPESET 7 +#define WC_SUBSH 8 +#define WC_CURSH 9 +#define WC_TIMED 10 +#define WC_FUNCDEF 11 +#define WC_FOR 12 +#define WC_SELECT 13 +#define WC_WHILE 14 +#define WC_REPEAT 15 +#define WC_CASE 16 +#define WC_IF 17 +#define WC_COND 18 +#define WC_ARITH 19 +#define WC_AUTOFN 20 +#define WC_TRY 21 /* increment as necessary */ -#define WC_COUNT 21 +#define WC_COUNT 22 #define WCB_END() wc_bld(WC_END, 0) @@ -849,6 +844,12 @@ struct eccstr { #define WC_ASSIGN_SCALAR 0 #define WC_ASSIGN_ARRAY 1 #define WC_ASSIGN_NEW 0 +/* + * In normal assignment, this indicate += to append. + * In assignment following a typeset, where that's not allowed, + * we overload this to indicate a variable without an + * assignment. + */ #define WC_ASSIGN_INC 1 #define WC_ASSIGN_NUM(C) (wc_data(C) >> 2) #define WCB_ASSIGN(T,A,N) wc_bld(WC_ASSIGN, ((T) | ((A) << 1) | ((N) << 2))) @@ -856,6 +857,9 @@ struct eccstr { #define WC_SIMPLE_ARGC(C) wc_data(C) #define WCB_SIMPLE(N) wc_bld(WC_SIMPLE, (N)) +#define WC_TYPESET_ARGC(C) wc_data(C) +#define WCB_TYPESET(N) wc_bld(WC_TYPESET, (N)) + #define WC_SUBSH_SKIP(C) wc_data(C) #define WCB_SUBSH(O) wc_bld(WC_SUBSH, (O)) @@ -1140,6 +1144,32 @@ struct alias { /* is this an alias for suffix handling? */ #define ALIAS_SUFFIX (1<<2) +/* structure for foo=bar assignments */ + +struct asgment { + struct linknode node; + char *name; + int is_array; + union { + char *scalar; + LinkList array; + } value; +}; + +/* + * Assignment is array? + */ +#define ASG_ARRAYP(asg) ((asg)->is_array) + +/* + * Assignment has value? + * If the assignment is an arrray, then it certainly has a value --- we + * can only tell if there's an expicit assignment. + */ + +#define ASG_VALUEP(asg) (ASG_ARRAYP(asg) || \ + ((asg)->value.scalar != (char *)0)) + /* node in command path hash table (cmdnamtab) */ struct cmdnam { @@ -1268,6 +1298,7 @@ struct options { */ typedef int (*HandlerFunc) _((char *, char **, Options, int)); +typedef int (*HandlerFuncAssign) _((char *, char **, LinkList, Options, int)); #define NULLBINCMD ((HandlerFunc) 0) struct builtin { @@ -1311,6 +1342,12 @@ struct builtin { * does not terminate options. */ #define BINF_HANDLES_OPTS (1<<18) +/* + * Handles the assignement interface. The argv list actually contains + * two nested litsts, the first of normal arguments, and the second of + * assignment structures. + */ +#define BINF_ASSIGN (1<<19) struct module { struct hashnode node; @@ -1715,9 +1752,10 @@ struct tieddata { * necessarily want to match multiple * elements */ -#define SCANPM_ISVAR_AT ((-1)<<15) /* "$foo[@]"-style substitution - * Only sign bit is significant - */ +/* "$foo[@]"-style substitution + * Only sign bit is significant + */ +#define SCANPM_ISVAR_AT ((int)(((unsigned int)-1)<<15)) /* * Flags for doing matches inside parameter substitutions, i.e. @@ -2084,6 +2122,7 @@ enum { CHASELINKS, CHECKJOBS, CLOBBER, + APPENDCREATE, COMBININGCHARS, COMPLETEALIASES, COMPLETEINWORD, @@ -2779,6 +2818,7 @@ struct parse_stack { int incasepat; int isnewlin; int infor; + int intypeset; int eclen, ecused, ecnpats; Wordcode ecbuf; @@ -2921,14 +2961,22 @@ enum { #define AFTERTRAPHOOK (zshhooks + 2) #ifdef MULTIBYTE_SUPPORT +/* Metafied input */ #define nicezputs(str, outs) (void)mb_niceformat((str), (outs), NULL, 0) -#define MB_METACHARINIT() mb_metacharinit() +#define MB_METACHARINIT() mb_charinit() typedef wint_t convchar_t; #define MB_METACHARLENCONV(str, cp) mb_metacharlenconv((str), (cp)) #define MB_METACHARLEN(str) mb_metacharlenconv(str, NULL) -#define MB_METASTRLEN(str) mb_metastrlen(str, 0) -#define MB_METASTRWIDTH(str) mb_metastrlen(str, 1) -#define MB_METASTRLEN2(str, widthp) mb_metastrlen(str, widthp) +#define MB_METASTRLEN(str) mb_metastrlenend(str, 0, NULL) +#define MB_METASTRWIDTH(str) mb_metastrlenend(str, 1, NULL) +#define MB_METASTRLEN2(str, widthp) mb_metastrlenend(str, widthp, NULL) +#define MB_METASTRLEN2END(str, widthp, eptr) \ + mb_metastrlenend(str, widthp, eptr) + +/* Unmetafined input */ +#define MB_CHARINIT() mb_charinit() +#define MB_CHARLENCONV(str, len, cp) mb_charlenconv((str), (len), (cp)) +#define MB_CHARLEN(str, len) mb_charlenconv((str), (len), NULL) /* * We replace broken implementations with one that uses Unicode @@ -3011,6 +3059,11 @@ typedef int convchar_t; #define MB_METASTRLEN(str) ztrlen(str) #define MB_METASTRWIDTH(str) ztrlen(str) #define MB_METASTRLEN2(str, widthp) ztrlen(str) +#define MB_METASTRLEN2END(str, widthp, eptr) ztrlenend(str, eptr) + +#define MB_CHARINIT() +#define MB_CHARLENCONV(str, len, cp) charlenconv((str), (len), (cp)) +#define MB_CHARLEN(str, len) ((len) ? 1 : 0) #define WCWIDTH_WINT(c) (1) diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index 50058e25d..7eedfa6e0 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -13,6 +13,19 @@ # # Tests for `Simple Commands and Pipelines' # + + # Test skipping early to ensure we run the remainder... + if [[ -n $ZTST_test_skip ]]; then + ZTST_skip="Test system verification for skipping" + else + print "This is standard output" + print "This is standard error" >&2 + false + fi +1:Test skipping if ZTST_test_skip is set +>This is standard output +?This is standard error + echo foo | cat | sed 's/foo/bar/' 0:Basic pipeline handling >bar @@ -169,6 +182,12 @@ >1 >2 + for (( $(true); ; )); do break; done + for (( ; $(true); )); do break; done + for (( ; ; $(true) )); do break; done + for (( ; $((1)); )); do break; done +0:regression test, nested cmdsubst in arithmetic `for' loop + for keyvar valvar in key1 val1 key2 val2; do print key=$keyvar val=$valvar done @@ -695,3 +714,27 @@ 0:Balanced parentheses and spaces with zsh pattern >It worked >That worked, too + + fn() { + typeset ac_file="the else branch" + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) break;; + *) + ;; + esac + print Stuff here + } + which fn + fn +0:Long case with parsed alternatives turned back into text +>fn () { +> typeset ac_file="the else branch" +> case $ac_file in +> (*.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj) ;; +> (*.*) break ;; +> (*) ;; +> esac +> print Stuff here +>} +>Stuff here diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst index cc2d34d23..d5501bb33 100644 --- a/Test/A05execution.ztst +++ b/Test/A05execution.ztst @@ -266,9 +266,11 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline wait $two print $? wait $one -1:The status of recently exited background jobs is recorded + print $? +0:The status of recently exited background jobs is recorded >3 >2 +>1 # Regression test for workers/34060 (patch in 34065) setopt ERR_EXIT NULL_GLOB diff --git a/Test/A06assign.ztst b/Test/A06assign.ztst index 0ad9a0aca..302659c7e 100644 --- a/Test/A06assign.ztst +++ b/Test/A06assign.ztst @@ -419,21 +419,40 @@ >worldliness >world - integer i n x + (integer i n x float f setopt globassign i=tmpfile1 - n=tmp* + n=tmpf* x=*2 f=2+2 - typeset -p i n x f + typeset -p i n x f) 0:GLOB_ASSIGN with numeric types >typeset -i i=0 >typeset -a n ->n=(tmpfile1 tmpfile2) +>n=( tmpfile1 tmpfile2 ) >typeset x=tmpfile2 >typeset -E f=4.000000000e+00 + setopt globassign + foo=tmpf* + print $foo + unsetopt globassign + foo=tmpf* + print $foo +0:GLOB_ASSIGN option +>tmpfile1 tmpfile2 +>tmpf* + + (setopt globassign + typeset -A foo + touch gatest1 gatest2 + foo=(gatest*) + print ${(t)foo} + rm -rf gatest*) +0:GLOB_ASSIGN doesn't monkey with type if not scalar assignment. +>association-local + A=(first second) A="${A[*]}" /bin/sh -c 'echo $A' print -l "${A[@]}" diff --git a/Test/B01cd.ztst b/Test/B01cd.ztst index c7920dd05..a5b3769f1 100644 --- a/Test/B01cd.ztst +++ b/Test/B01cd.ztst @@ -70,7 +70,7 @@ # the expected status returned by the code when run, or - if it is # irrelevant. An optional set of single-letter flags follows the status # or -. The following are understood: -# d Don't diff stdout against the expected stdout. +# . d Don't diff stdout against the expected stdout. # D Don't diff stderr against the expected stderr. # q All redirection lines given in the test script (not the lines # actually produced by the test) are subject to ordinary quoted shell @@ -93,6 +93,18 @@ # ones which may arise from the environment rather than from the shell # itself. (The example below isn't particularly useful as errors with # `cd' are unusual.) +# +# A couple of features aren't used in this file, but are usefuil in cases +# where features may not be available so should not be tested. They boh +# take the form of variables. Note that to keep the test framework simple +# there is no magic in setting the variables: the chunk of code being +# executed needs to avoid executing any test code by appropriate structure +# (typically "if"). In both cases, the value of the variable is output +# as a warning that the test was skipped. +# ZTST_unimplemented: Set this in the %prep phase if the entire test file +# is to be skipped. +# ZTST_skip: Set this in any test case if that single test case is to be +# skipped. Testing resumes at the next test case in the same file. cd cdtst.tmp/sub/fake && pwd && print $PWD diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index 57a7caa12..2b8e5445c 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -22,6 +22,8 @@ %prep + mkdir typeset.tmp && cd typeset.tmp + setopt noglob scalar=scalar @@ -231,7 +233,7 @@ typeset -T THIS will not work 1:Tied array syntax -?(eval):typeset:1: -T requires names of scalar and array +?(eval):typeset:1: too many arguments for -T local array[2]=x 1:Illegal local array element assignment @@ -451,7 +453,7 @@ fn 1:declare -p shouldn't create scoped values >typeset -a array ->array=(foo bar) +>array=( foo bar ) ?fn:typeset: no such variable: nonexistent unsetopt typesetsilent @@ -465,9 +467,12 @@ print $tied_array typeset -T TIED_SCALAR=goo:car tied_array print $tied_array + typeset -T TIED_SCALAR tied_array=(poo par) + print $TIED_SCALAR 0:retying arrays to same array works >foo bar >goo car +>poo:par ( setopt POSIXBUILTINS @@ -502,9 +507,206 @@ typeset -pm 'r[12]' 0:readonly -p output >typeset -a a1 ->a1=(one two) +>a1=( one two ) >typeset -ar a1 >typeset -a a2 ->a2=(three four) +>a2=( three four ) >typeset -r r1=yes >typeset -r r2=no + + one=hidden two=hidden three=hidden four=hidden five=hidden + fn() { + local bleugh="four=vier" + typeset -R10 one=eins two=(zwei dio) three $bleugh five=(cinq cinque) + three=drei + print -l $one $two $three $four $five + } + fn + print -l $one $two $three $four $five +0:typeset reserved word interface: basic +> eins +>zwei +>dio +> drei +> vier +>cinq +>cinque +>hidden +>hidden +>hidden +>hidden +>hidden + + ( + setopt glob + mkdir -p arrayglob + touch arrayglob/{one,two,three,four,five,six,seven} + fn() { + typeset array=(arrayglob/[tf]*) + print -l ${array:t} + # + typeset {first,second,third}=the_same_value array=( + extends + over + multiple + lines + ) + print -l $first $second $third "$array" + # + integer i=$(echo 1 + 2 + 3 + 4) + print $i + # + # only noted by accident this was broken.. + # we need to turn off special recognition + # of assignments within assignments... + typeset careful=( i=1 j=2 k=3 ) + print -l $careful + } + fn + ) +0:typeset reserved word, more complicated cases +>five +>four +>three +>two +>the_same_value +>the_same_value +>the_same_value +>extends over multiple lines +>10 +>i=1 +>j=2 +>k=3 + + ( + # reserved word is recognised at parsing. + # yes, this is documented. + # anyway, that means we need to + # re-eval the function... + fn=' + fn() { + typeset foo=`echo one word=two` + print $foo + print $word + } + ' + print reserved + eval $fn; fn + print builtin + disable -r typeset + eval $fn; fn + enable -r typeset + disable typeset + print reserved + eval $fn; fn + ) +0:reserved word and builtin interfaces +>reserved +>one word=two +> +>builtin +>one +>two +>reserved +>one word=two +> + + fn() { + emulate -L zsh + setopt typeset_silent + local k + typeset -A hash=(k1 v1 k2 v2) + typeset foo=word array=(more than one word) + for k in ${(ko)hash}; do + print $k $hash[$k] + done + print -l $foo $array + typeset -A hash + typeset foo array + for k in ${(ko)hash}; do + print $k $hash[$k] + done + print -l $foo $array + typeset hash=(k3 v3 k4 v4) array=(odd number here) + for k in ${(ko)hash}; do + print $k $hash[$k] + done + print -l $array + } + fn +0:typeset preserves existing variable types +>k1 v1 +>k2 v2 +>word +>more +>than +>one +>word +>k1 v1 +>k2 v2 +>word +>more +>than +>one +>word +>k3 v3 +>k4 v4 +>odd +>number +>here + + fn() { typeset foo bar thing=this stuff=(that other) more=woevva; } + which -x2 fn + fn2() { typeset assignfirst=(why not); } + which -x2 fn2 +0:text output from typeset +>fn () { +> typeset foo bar thing=this stuff=(that other) more=woevva +>} +>fn2 () { +> typeset assignfirst=(why not) +>} + + fn() { + typeset array=() + print ${(t)array} ${#array} + typeset gnothergarray=() gnothergarray[1]=yes gnothergarray[2]=no + print -l ${(t)gnothergarray} $gnothergarray + } + fn +0:can set empty array +>array-local 0 +>array-local +>yes +>no + + array=(nothing to see here) + fn() { + typeset array=(one two three four five) + typeset array[2,4]=(umm er) + print ${#array} $array + typeset array[2,3]=() + print ${#array} $array + } + fn + print ${#array} $array +0:can update array slices in typeset +>4 one umm er five +>2 one five +>4 nothing to see here + + array=(no really nothing here) + fn() { + typeset array=() array[2]=two array[4]=four + typeset -p array + typeset array=() array[3]=three array[1]=one + typeset -p array + } + fn + print $array +0:setting empty array in typeset +>typeset -a array +>array=( '' two '' four ) +>typeset -a array +>array=( one '' three ) +>no really nothing here diff --git a/Test/B03print.ztst b/Test/B03print.ztst index 48574c227..eb79c4ddb 100644 --- a/Test/B03print.ztst +++ b/Test/B03print.ztst @@ -169,11 +169,15 @@ 0:%n count zeroed on format reuse >1 -# this may fill spec string with '%0+- #*.*lld\0' - 13 characters - printf '%1$0+- #-08.5dx\n' 123 +# this may fill spec string with '%0'+- #*.*lld\0' - 14 characters + printf '%1$0'"'+- #-08.5dx\n" 123 0:maximal length format specification >+00123 x + printf "x:%-20s:y\n" fubar +0:left-justification of string +>x:fubar :y + printf '%*smorning\n' -5 good 0:negative width specified >good morning @@ -284,3 +288,16 @@ >610062 >6100 >61 + + foo=$'one\ttwo\tthree\tfour\n' + foo+=$'\tone\ttwo\tthree\tfour\n' + foo+=$'\t\tone\t\ttwo\t\tthree\t\tfour' + print -x4 $foo + print -X4 $foo +0:Tab expansion by print +>one two three four +> one two three four +> one two three four +>one two three four +> one two three four +> one two three four diff --git a/Test/C01arith.ztst b/Test/C01arith.ztst index d284e0869..c7bd81fc3 100644 --- a/Test/C01arith.ztst +++ b/Test/C01arith.ztst @@ -69,11 +69,11 @@ print $(( 3 ? 2 )) 1:parsing ternary (1) -?(eval):1: ':' expected +?(eval):1: bad math expression: ':' expected print $(( 3 ? 2 : 1 : 4 )) 1:parsing ternary (2) -?(eval):1: ':' without '?' +?(eval):1: bad math expression: ':' without '?' print $(( 0, 4 ? 3 : 1, 5 )) 0:comma operator @@ -86,7 +86,7 @@ print $((##)) 1:## without following character -?(eval):1: character missing after ## +?(eval):1: bad math expression: character missing after ## print $((## )) 0:## followed by a space @@ -126,7 +126,7 @@ print $(( 13 = 42 )) 1:bad lvalue -?(eval):1: lvalue required +?(eval):1: bad math expression: lvalue required x=/bar (( x = 32 )) @@ -395,3 +395,17 @@ >6 >7 >120 + + foo="(1)" + print $((foo)) + print $(($foo)) + print $(((2))) + foo="3)" + (print $((foo))) 2>&1 + (print $(($foo))) 2>&1 +1: Good and bad trailing parentheses +>1 +>1 +>2 +>(eval):6: bad math expression: unexpected ')' +>(eval):7: bad math expression: unexpected ')' diff --git a/Test/C02cond.ztst b/Test/C02cond.ztst index 02fa4d473..e9a596a22 100644 --- a/Test/C02cond.ztst +++ b/Test/C02cond.ztst @@ -151,14 +151,11 @@ cat $unmodified touch $newnewnew if [[ $OSTYPE == "cygwin" ]]; then - print -u$ZTST_fd "Warning: not testing [[ -N file ]] (not supported on Cygwin)" - true + ZTST_skip="[[ -N file ]] not supported on Cygwin" elif (( isnfs )); then - print -u$ZTST_fd "Warning: not testing [[ -N file ]] (not supported with NFS)" - true + ZTST_skip="[[ -N file ]] not supported with NFS" elif test -f /etc/mtab && { grep $(df . 2>/dev/null| tail -n1 | awk '{print $1}') /etc/mtab | grep -q noatime; }; then - print -u$ZTST_fd "Warning: not testing [[ -N file ]] (not supported with noatime file system)" - true + ZTST_skip="[[ -N file ]] not supported with noatime file system" else [[ -N $newnewnew && ! -N $unmodified ]] fi diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst index 757f75ca4..d179dc46d 100644 --- a/Test/C03traps.ztst +++ b/Test/C03traps.ztst @@ -64,6 +64,17 @@ 0:EXIT traps on a script >Exited. + trap - + trap + trap int INT + trap sigterm SIGTERM + trap quit 3 + trap +0: Outputting traps correctly +>trap -- int INT +>trap -- quit QUIT +>trap -- sigterm TERM + fn1() { trap - trap @@ -399,6 +410,46 @@ ) 1:ERREXIT in loop with simple commands + fn() { + emulate -L zsh + setopt errreturn + if false; then + false + print No. + else + print Oh, yes + fi + } + fn +0:ERRRETURN not triggered in if condition +>Oh, yes + + fn() { + emulate -L zsh + setopt errreturn + if true; then + false + print No. + else + print No, no. + fi + } + fn +1:ERRRETURN in "if" + + fn() { + emulate -L zsh + setopt errreturn + if false; then + print No. + else + false + print No, no. + fi + } + fn +1:ERRRETURN in "else" branch (regression test) + %clean rm -f TRAPEXIT diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst index 3074efe60..2638e2438 100644 --- a/Test/D01prompt.ztst +++ b/Test/D01prompt.ztst @@ -199,5 +199,5 @@ ?+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:7> local d='~[<parent>:l]' ?+fn:8> print '~[<parent>:l]' diff --git a/Test/D03procsubst.ztst b/Test/D03procsubst.ztst index c763f6e0f..ca8d56ff5 100644 --- a/Test/D03procsubst.ztst +++ b/Test/D03procsubst.ztst @@ -88,6 +88,7 @@ print something=${:-=(echo 'C,D),(F,G)'} 1: Graceful handling of bad substitution in enclosed context ?(eval):1: unterminated `=(...)' +# '` () { print -n "first: " @@ -115,3 +116,36 @@ 0:Process substitution as anonymous function argument >Execute a complicated order first >This line was brought to you by the letters F and D + + alias foo='cat <(' + eval 'foo echo this is bound to work)' +0:backtacking within command string parsing with alias still pending +>this is bound to work + + alias foo='cat <( print' + eval 'foo here is some output)' +0:full alias expanded when substitution starts in alias +>here is some output + + if ! (mkfifo test_pipe >/dev/null 2>&1); then + ZTST_skip="mkfifo not available" + else + echo 1 | tee >(cat > test_pipe) | (){ + local pipein + read pipein <test_pipe + print $pipein + read pipein + print $pipein + } + fi +0:proc subst fd in forked subshell closed in parent +>1 +>1 + + if [[ ! -e test_pipe ]]; then + ZTST_skip="mkfifo not available" + else + echo 1 | tee >(cat > test_pipe) | paste - test_pipe + fi +0:proc subst fd in forked subshell closed in parent (external command) +>1 1 diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index d06a73afd..c7d506a51 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -1069,6 +1069,11 @@ >a:b a b >x:y:z + typeset -T tied1 tied2 + + typeset -T tied2 tied1 + +1:Attempts to swap tied variables are safe but futile +?(eval):typeset:2: already tied as non-scalar: tied2 + string='look for a match in here' if [[ ${string%%(#b)(match)*} = "look for a " ]]; then print $match[1] $mbegin[1] $mend[1] $string[$mbegin[1],$mend[1]] @@ -1711,3 +1716,12 @@ 0:Avoid confusion after overloaded characters in braceless substitution in sh >13 >0-1 + + a="aaa bab cac" + b=d + echo $a:gs/a/${b}/ + a=(aaa bab cac) + echo $a:gs/a/${b}/ +0:History modifier works the same for scalar and array substitution +>ddd bdb cdc +>ddd bdb cdc diff --git a/Test/D07multibyte.ztst b/Test/D07multibyte.ztst index 33e76bee7..0e3e98d38 100644 --- a/Test/D07multibyte.ztst +++ b/Test/D07multibyte.ztst @@ -475,3 +475,36 @@ . ./test_bad_param) 127:Invalid parameter name with following tokenized input ?./test_bad_param:1: command not found: $\M-i# + + lines=$'one\tZSH\tthree\nfour\tfive\tsix' + print -X8 -r -- $lines +0:Tab expansion with extra-wide characters +>one ZSH three +>four five six +# This doesn't look aligned in my editor because actually the characters +# aren't quite double width, but the arithmetic is correct. +# It appears just to be an effect of the font. + + if zmodload -i 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 + local cdpath=(.) + mkdir ホ + cd ホ + cd .. + cd ./ホ + cd .. + } +0:cd with special characters diff --git a/Test/D08cmdsubst.ztst b/Test/D08cmdsubst.ztst index a4c69a010..89e725966 100644 --- a/Test/D08cmdsubst.ztst +++ b/Test/D08cmdsubst.ztst @@ -148,3 +148,8 @@ ) after 0:Parsing of command substitution with ummatched parentheses: with frills >before start Universe began with u and ended with a crunch end after + + alias foo='echo $(' + eval 'foo echo this just works, OK\?)' +0:backtracking within command string parsing with alias still pending +>this just works, OK? diff --git a/Test/E01options.ztst b/Test/E01options.ztst index 5c453c80b..16279b88a 100644 --- a/Test/E01options.ztst +++ b/Test/E01options.ztst @@ -362,6 +362,20 @@ echo ${unset_var?Not an error}) 0:NO_EXEC should not test for unset variables + (setopt noexec + : ${${string%[aeiou]*}/(#m)?(#e)/${(U)MATCH}} Rule 1 + : ${array[4,5][1][2,3]} Rule 2 + : ${${(P)foo[1,6]}[1,3]} Rule 3 + : "${${(@)array}[1,2]}" Rule 5 + : "${(@)${(@)array}[1,2]#?}" Rule 6 + : ${(el.20..X.)${bar}} Rule 11 success case) +0:NO_EXEC handles parameter substitution examples + + (setopt noexec + : ${(el.20..X.)$bar} Rule 11 failure case) +1:NO_EXEC does recognize bad substitution syntax +*?* bad substitution + setopt NO_eval_lineno eval 'print $LINENO' setopt eval_lineno @@ -473,15 +487,7 @@ >outside2 scalar >inside3 scalar-export - setopt globassign - foo=tmp* - print $foo - unsetopt globassign - foo=tmp* - print $foo -0:GLOB_ASSIGN option ->tmpcd tmpfile1 tmpfile2 ->tmp* +# GLOB_ASSIGN is tested in A06assign.ztst. mkdir onlysomefiles touch onlysomefiles/.thisfile onlysomefiles/thatfile @@ -578,6 +584,15 @@ >unset >globassign + # This test is now somewhat artificial as + # KSH_TYPESET only applies to the builtin + # interface. Tests to the more standard + # reserved word interface appear elsewhere. + ( + # reserved words are handled during parsing, + # hence eval... + disable -r typeset + eval ' setopt kshtypeset ktvars=(ktv1 ktv2) typeset ktfoo=`echo arg1 arg2` $ktvars @@ -588,6 +603,8 @@ print $noktfoo print $+noktarg1 $+noktarg2 unset ktfoo ktv1 ktv2 noktfoo noktarg2 + ' + ) 0:KSH_TYPESET option >1 1 0 >arg1 arg2 diff --git a/Test/V09datetime.ztst b/Test/V09datetime.ztst new file mode 100644 index 000000000..c9351995e --- /dev/null +++ b/Test/V09datetime.ztst @@ -0,0 +1,71 @@ +%prep + + if ! (zmodload zsh/datetime >/dev/null 2>/dev/null); then + ZTST_unimplemented="can't load the zsh/datetime module for testing" + fi + setopt multibyte + zmodload zsh/datetime + unset LC_ALL + LC_TIME=C + TZ=UTC+0 + [[ "$(strftime %04y 1)" = "0070" ]] || skip_extensions=1 + [[ "$(LC_TIME=ja_JP.UTF-8 strftime %OS 1)" = 一 ]] || skip_japanese=1 + +%test + + strftime %y 0 + strftime %Y 1000000000 + strftime %x 1200000000 + strftime %X 1200000001 +0:basic format specifiers +>70 +>2001 +>01/10/08 +>21:20:01 + + strftime %-m_%f_%K_%L 1181100000 + strftime %6. 0 +0:zsh extensions +>6_6_3_3 +>000000 + + if [[ $skip_extensions = 1 ]]; then + ZTST_skip="strftime extensions not supported" + elif [[ $skip_japanese = 1 ]]; then + ZTST_skip="Japanese UTF-8 locale not supported" + else + ( + LC_TIME=ja_JP.UTF-8 + strftime %Ey 1000000000 + strftime %Oy 1000000000 + strftime %Ex 1000000000 + strftime %OS 1000000000 + strftime %03Ey 650000000 + ) + fi +0:alternate format extensions +>13 +>一 +>平成13年09月09日 +>四十 +>002 + + if [[ $skip_extensions = 1 ]]; then + ZTST_skip="strftime extensions not supported" + else + ( + strftime '%#A' 0 + strftime '%^_10B' 0 + strftime %03Ey 650000000 + strftime %-Oe 0 + ) + fi +0:various extensions +>THURSDAY +> JANUARY +>090 +>1 + + print ${(V)"$(strftime $'%Y\0%m\0%d' 100000000)"} +0:Embedded nulls +>1973^@03^@03 diff --git a/Test/ztst.zsh b/Test/ztst.zsh index 74111f6cc..ce89a83ce 100755 --- a/Test/ztst.zsh +++ b/Test/ztst.zsh @@ -343,6 +343,7 @@ ZTST_diff() { ZTST_test() { local last match mbegin mend found substlines local diff_out diff_err + local ZTST_skip while true; do rm -f $ZTST_in $ZTST_out $ZTST_err @@ -427,6 +428,16 @@ $ZTST_curline" ZTST_execchunk <$ZTST_in >$ZTST_tout 2>$ZTST_terr + if [[ -n $ZTST_skip ]]; then + ZTST_verbose 0 "Test case skipped: $ZTST_skip" + ZTST_skip= + if [[ -n $last ]]; then + break + else + continue + fi + fi + # First check we got the right status, if specified. if [[ $ZTST_xstatus != - && $ZTST_xstatus != $ZTST_status ]]; then ZTST_testfailed "bad status $ZTST_status, expected $ZTST_xstatus from: diff --git a/Util/check-tmux-state b/Util/check-tmux-state new file mode 100644 index 000000000..4cba36070 --- /dev/null +++ b/Util/check-tmux-state @@ -0,0 +1,220 @@ +#!/bin/zsh -f + +# Tmux has lots of options and sub-commands. It's very tedious to manually +# check if the actual command's idea of all this matches the completion +# function. So this is a helper script that automates checking the state of +# _tmux. +# +# You need to call it like this, with a running tmux server: +# +# zsh -f check-tmux-state <path-to-tmux-binary> <path-to-_tmux-function> +# +# The script will tell you the differences in available and supported +# sub-commands, command aliases, server options, session options and +# window-options. +# +# It also checks if options have moved from one scope to another. If this +# happens, then the option in question also appears in the "new/old" listings +# of the involved scopes. First fix the scope changes, then the "new/old" lists +# are accurate. + +emulate zsh +setopt extended_glob null_glob no_octal_zeroes + +if (( $#argv != 2 )); then + printf 'usage: zsh -f check-tmux-state <tmux-binary> <_tmux-function>\n' + exit 1 +fi + +printf ' -!- Checking status of _tmux completion function definition -!-\n' + +autoload -Uz colors +colors + +tmux=$1 +func=$2 + +differences=none + +# We'll source the _tmux file and call a bunch of its functions to gather +# information. For that, we need to put a few stubs into place so sourcing the +# file doesn't blow up in our face. + +# We need to disable the new "local" keyword to make our data retrieval trick +# work: +disable -r local + +function _arguments () { } +function _describe () { } +function local () { } + +typeset -A rev + +source $func +__tmux-server-options +__tmux-session-options +__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 | + sed -e 's,^.*\<_tmux-,,' -e 's,(.*$,,' ) ) + +# Ask tmux for available commands: +typeset -a available_commands +available_commands=( $( $tmux list-commands | cut -f1 -d' ' ) ) + +# Ask tmux for available aliases: +typeset -A available_aliases +available_aliases=( $( $tmux list-commands | + grep '^[a-z-]* *(' | + sed -e 's,^\([a-z-]*\) *(\([a-z-]*\))\(.*\)$,\2 \1,' ) ) + +# Gather information about options: +typeset -a supported_session_options +supported_session_options=( ${"${tmux_session_options[@]}"%%:*} ) +typeset -a available_session_options +available_session_options=( $( $tmux show-options -g | cut -f1 -d' ' ) ) + +typeset -a available_server_options +supported_server_options=( ${"${tmux_server_options[@]}"%%:*} ) +typeset -a supported_server_options +available_server_options=( $( $tmux show-options -s -g | cut -f1 -d' ' ) ) + +typeset -a supported_window_options +supported_window_options=( ${"${tmux_window_options[@]}"%%:*} ) +typeset -a available_window_options +available_window_options=( $( $tmux show-options -w -g | cut -f1 -d' ' ) ) + +typeset -a supported available + +function find_new () { + local i + new=() + for i in "${available[@]}"; do + [[ -z ${supported[(r)$i]} ]] && new+=( $i ) + done +} + +function find_old () { + local i + old=() + for i in "${supported[@]}"; do + [[ -z ${available[(r)$i]} ]] && old+=( $i ) + done +} + +function compare_sets() { + name=$1 + local -a old new + new=() + old=() + find_old + find_new + if (( $#old > 0 )) || (( $#new > 0 )); then + printf '\n%sDifferences with %s:%s\n' ${fg[yellow]} $name $reset_color + differences=some + if (( $#new > 0 )); then + printf '%sNew:%s' ${fg[green]} $reset_color + printf ' %s' "${new[@]}" + printf '\n' + fi + if (( $#old > 0 )); then + printf '%sOld:%s' ${fg[red]} $reset_color + printf ' %s' "${old[@]}" + printf '\n' + fi + fi +} + +function find_changed_scope() { + name=$1 + local -a changes + local i av + changes=() + for i in "${supported[@]}"; do + av=${available[(r)$i]} + [[ -n $av ]] && changes+=( $av ) + done + if (( $#changes > 0 )); then + differences=some + printf '\n%sDifferences with scope %s:%s\n' \ + ${fg[yellow]} $name $reset_color + printf '%sChanged:%s' ${fg[green]} $reset_color + printf ' %s' "${changes[@]}" + printf '\n' + fi +} + +supported=( "${supported_session_options[@]}" ) +available=( "${available_server_options[@]}" ) +find_changed_scope 'session=>server' + +supported=( "${supported_server_options[@]}" ) +available=( "${available_session_options[@]}" ) +find_changed_scope 'server=>session' + +supported=( "${supported_window_options[@]}" ) +available=( "${available_session_options[@]}" ) +find_changed_scope 'window=>session' + +supported=( "${supported_session_options[@]}" ) +available=( "${available_window_options[@]}" ) +find_changed_scope 'session=>window' + +supported=( "${supported_window_options[@]}" ) +available=( "${available_server_options[@]}" ) +find_changed_scope 'window=>server' + +supported=( "${supported_server_options[@]}" ) +available=( "${available_window_options[@]}" ) +find_changed_scope 'server=>window' + +supported=( "${supported_commands[@]}" ) +available=( "${available_commands[@]}" ) +compare_sets commands + +supported=( "${supported_session_options[@]}" ) +available=( "${available_session_options[@]}" ) +compare_sets session_options + +supported=( "${supported_server_options[@]}" ) +available=( "${available_server_options[@]}" ) +compare_sets server_options + +supported=( "${supported_window_options[@]}" ) +available=( "${available_window_options[@]}" ) +compare_sets window_options + +typeset -a alias_messages +for i in "${(k)_tmux_aliasmap[@]}"; do + su=${_tmux_aliasmap[$i]} + av=${available_aliases[$i]} + if [[ -z $av ]]; then + alias_messages+=( "${fg[red]}Old alias${reset_color}: $i ($su)" ) + elif [[ $av != $su ]]; then + alias_messages+=( "Changed alias $i: is ($av) was ($su)" ) + fi +done +for i in "${(k)available_aliases[@]}"; do + su=${_tmux_aliasmap[$i]} + av=${available_aliases[$i]} + if [[ -z $su ]]; then + alias_messages+=( "${fg[green]}New alias${reset_color}: $i ($av)" ) + fi +done +if (( $#alias_messages > 0 )); then + differences=some + printf '\n%sDifferences with %s:%s\n' ${fg[yellow]} "aliases" $reset_color + for i in "${alias_messages[@]}"; do + printf '%s\n' $i + done +fi + +if [[ $differences == none ]]; then + printf '\n... _tmux seems to be up to date!\n' +else + printf '\n' +fi +exit 0 diff --git a/Util/zyodl.vim b/Util/zyodl.vim new file mode 100644 index 000000000..b67bfa54e --- /dev/null +++ b/Util/zyodl.vim @@ -0,0 +1,81 @@ + +"" A Vim syntax highlighting file for Doc/Zsh/*.yo + +" To try this, run: +" cd Doc/Zsh && vim --cmd "source ./.vimrc" zle.yo +" (This sources the file <Doc/Zsh/.vimrc>.) +" +" To install this permanently: +" 1. Copy this file to ~/.vim/syntax/zyodl.vim +" 2. Create ~/.vim/filetype.vim as explained in ":help new-filetype" case C. +" 3. Add the following command to ~/.vim/filetype.vim: +" autocmd BufRead,BufNewFile **/Doc/Zsh/*.yo setfiletype zyodl + +"" Test case: +" texinode()()()() +" chapter(foo) +" vindex(foo) +" foo tt(foo) var(foo) bf(foo) em(foo) foo +" xitem(foo) +" item(foo)(foo) +" sitem(foo)(foo foo) +" example(print *.c+LPAR()#q:s/#%+LPAR()#b+RPAR()s+LPAR()*+RPAR().c/'S${match[1]}.C'/+RPAR()) +" ifzman(zmanref(zshmisc))ifnzman(noderef(Redirection)) +" LPAR()foo 42 foo+RPAR() +" chapter(foo (foo) foo) +" chapter(foo (foo (foo) foo) foo) bar +" +" sitem(foo)(foo (foo) foo) +" sitem(foo)(foo (foo) foo) +" +" sitem(foo)(foo tt(foo) foo) # nested underline + +if exists("b:current_syntax") + finish +endif + +"" Syntax groups: +syn clear +syn cluster zyodlInline contains=zyodlTt,zyodlVar,zyodlBold,zyodlEmph,zyodlCond +syn region zyodlTt start="\<tt(" end=")" contains=zyodlSpecial,zyodlParenthetical +syn region zyodlVar start="\<var(" end=")" contains=zyodlSpecial,zyodlParenthetical +syn region zyodlBold start="\<bf(" end=")" contains=zyodlSpecial,zyodlParenthetical +syn region zyodlEmph start="\<em(" end=")" contains=zyodlSpecial,zyodlParenthetical +syn region zyodlIndex start="\<.index(" end=")" contains=zyodlSpecial +syn match zyodlSpecial "+\?\<\(LPAR\|RPAR\|PLUS\)()" +syn match zyodlNumber "\d\+" +syn region zyodlItem start="\<xitem(" end=")" contains=zyodlSpecial,@zyodlInline +syn region zyodlItem start="\<item(" end=")" contains=zyodlSpecial,@zyodlInline +syn region zyodlExample start="\<example(" end=")" contains=zyodlSpecial +syn region zyodlTitle start="\<\(chapter\|subsect\|sect\)(" end=")" contains=zyodlSpecial,@zyodlInline,zyodlParenthetical +syn match zyodlTitle "^texinode(.*$" +syn region zyodlParenthetical start="\w\@<!(" end=")" transparent contained contains=zyodlParenthetical + +" zyodlCond doesn't contain zyodlParenthetical, since section names (probably) don't have parentheticals. +syn region zyodlCond start="\<\(ifzman\|ifnzman\)(" end=")" contains=zyodlRef,zyodlSpecial,@zyodlInline +syn region zyodlRef start="\<\(zmanref\|noderef\)(" end=")" + +" zyodlSItemArg2 should use zyodlParenthetical instead of the 'skip=' +syn keyword zyodlKeyword sitem nextgroup=zyodlSItemArg1 +syn region zyodlSItemArg1 oneline start="(" end=")" contains=zyodlSpecial,@zyodlInline nextgroup=zyodlSItemArg2 contained +syn region zyodlSItemArg2 start="(" end=")" contains=zyodlSpecial,@zyodlInline contained skip="\w\@<!([^)]*)" + +"" Highlight groups: +hi def link zyodlTt Constant +hi def link zyodlVar Identifier +" Not ':hi def link zyodlBold Bold' since there's no such group. +hi def zyodlBold gui=bold cterm=bold +hi def link zyodlEmph Type +hi def link zyodlIndex Comment +hi def link zyodlSpecial Special +hi def link zyodlNumber Number +hi def link zyodlItem Keyword +hi def link zyodlExample String +hi def link zyodlTitle Title +hi def link zyodlCond Conditional +hi def link zyodlRef Include +hi def link zyodlSItemArg1 Macro +hi def link zyodlSItemArg2 Underlined + +let b:current_syntax = "zyodl" + diff --git a/configure.ac b/configure.ac index 6a99aec1d..d7db8ba8d 100644 --- a/configure.ac +++ b/configure.ac @@ -2526,7 +2526,7 @@ dnl these is by defining _GNU_SOURCE. dnl ------- AH_TEMPLATE([USE_DEV_PTMX], [Define to 1 if all the kit for using /dev/ptmx for ptys is available.]) -if test x$ac_cv_have_dev_ptmx = xyes && \ +if test x$ac_cv_have_dev_ptmx = xyes -o x$ac_cv_func_posix_openpt = xyes && \ test x$ac_cv_func_grantpt = xyes && \ test x$ac_cv_func_unlockpt = xyes && \ test x$ac_cv_func_ptsname = xyes; then |