From 000e00871830cd15de032c80e2b62946cf19445c Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Thu, 8 May 2025 18:49:34 -0500 Subject: adding missing dotfiles and folders - profile.d/ - bashrc - authinfo.gpg - .zsh/ --- dotfiles/system/.authcode | 1 + dotfiles/system/.authinfo.gpg | Bin 0 -> 385 bytes dotfiles/system/.bashrc | 57 + dotfiles/system/.profile.d/auto-tmux-session.sh | 12 + dotfiles/system/.profile.d/chronographic.sh | 116 + dotfiles/system/.profile.d/dd.sh | 19 + dotfiles/system/.profile.d/display.sh | 9 + dotfiles/system/.profile.d/emacs.sh | 33 + dotfiles/system/.profile.d/extract.sh | 27 + dotfiles/system/.profile.d/framework.sh | 10 + dotfiles/system/.profile.d/freebsd.sh | 10 + dotfiles/system/.profile.d/fzf.sh | 76 + dotfiles/system/.profile.d/git.sh | 24 + dotfiles/system/.profile.d/media.sh | 41 + .../dic/dictd_www.dict.org_web1913.dict.dz | Bin 0 -> 27330569 bytes .../.stardict/dic/dictd_www.dict.org_web1913.idx | Bin 0 -> 3024035 bytes .../dic/dictd_www.dict.org_web1913.idx.oft | Bin 0 -> 20062 bytes .../.stardict/dic/dictd_www.dict.org_web1913.ifo | 8 + dotfiles/system/.tmux.conf | 88 + dotfiles/system/.zsh/README.md | 138 + dotfiles/system/.zsh/fzf-tab.zsh | 389 + dotfiles/system/.zsh/lib/-ftb-colorize | 34 + dotfiles/system/.zsh/lib/-ftb-fzf | 102 + dotfiles/system/.zsh/lib/-ftb-generate-complist | 113 + dotfiles/system/.zsh/lib/-ftb-generate-header | 35 + dotfiles/system/.zsh/lib/-ftb-generate-query | 36 + dotfiles/system/.zsh/lib/ftb-switch-group | 38 + dotfiles/system/.zsh/lib/ftb-tmux-popup | 83 + dotfiles/system/.zsh/lib/zsh-ls-colors/LICENSE | 21 + dotfiles/system/.zsh/lib/zsh-ls-colors/README.md | 114 + dotfiles/system/.zsh/lib/zsh-ls-colors/demo | 65 + .../system/.zsh/lib/zsh-ls-colors/ls-colors.zsh | 186 + dotfiles/system/.zsh/modules/.cvsignore | 16 + dotfiles/system/.zsh/modules/.distfiles | 4 + dotfiles/system/.zsh/modules/.editorconfig | 15 + dotfiles/system/.zsh/modules/.gitignore | 155 + dotfiles/system/.zsh/modules/.preconfig | 7 + dotfiles/system/.zsh/modules/Config/.cvsignore | 2 + dotfiles/system/.zsh/modules/Config/.distfiles | 2 + dotfiles/system/.zsh/modules/Config/aczshoot.m4 | 8 + dotfiles/system/.zsh/modules/Config/clean.mk | 43 + dotfiles/system/.zsh/modules/Config/config.mk | 42 + dotfiles/system/.zsh/modules/Config/defs.mk.in | 114 + dotfiles/system/.zsh/modules/Config/installfns.sh | 74 + .../system/.zsh/modules/Config/uninstallfns.sh | 59 + dotfiles/system/.zsh/modules/Config/version.mk | 31 + dotfiles/system/.zsh/modules/LICENCE | 37 + dotfiles/system/.zsh/modules/Makefile.in | 87 + dotfiles/system/.zsh/modules/RECOMPILE_REQUEST | 1 + dotfiles/system/.zsh/modules/Src/.cvsignore | 35 + dotfiles/system/.zsh/modules/Src/.distfiles | 2 + dotfiles/system/.zsh/modules/Src/.exrc | 2 + dotfiles/system/.zsh/modules/Src/.indent.pro | 27 + dotfiles/system/.zsh/modules/Src/Makefile.in | 164 + dotfiles/system/.zsh/modules/Src/Makemod.in.in | 192 + dotfiles/system/.zsh/modules/Src/aloxaf/.cvsignore | 18 + dotfiles/system/.zsh/modules/Src/aloxaf/.distfiles | 2 + dotfiles/system/.zsh/modules/Src/aloxaf/.exrc | 2 + dotfiles/system/.zsh/modules/Src/aloxaf/.gitignore | 8 + dotfiles/system/.zsh/modules/Src/aloxaf/fzftab.c | 546 + dotfiles/system/.zsh/modules/Src/aloxaf/fzftab.mdd | 7 + dotfiles/system/.zsh/modules/Src/builtin.c | 7236 +++++++++ dotfiles/system/.zsh/modules/Src/compat.c | 742 + dotfiles/system/.zsh/modules/Src/exec.c | 6250 ++++++++ dotfiles/system/.zsh/modules/Src/glob.c | 3913 +++++ dotfiles/system/.zsh/modules/Src/hashtable.c | 1617 +++ dotfiles/system/.zsh/modules/Src/hashtable.h | 69 + dotfiles/system/.zsh/modules/Src/init.c | 1792 +++ dotfiles/system/.zsh/modules/Src/input.c | 701 + dotfiles/system/.zsh/modules/Src/jobs.c | 2894 ++++ dotfiles/system/.zsh/modules/Src/lex.c | 2203 +++ dotfiles/system/.zsh/modules/Src/loop.c | 795 + dotfiles/system/.zsh/modules/Src/makepro.awk | 166 + dotfiles/system/.zsh/modules/Src/mem.c | 1899 +++ dotfiles/system/.zsh/modules/Src/mkbltnmlst.sh | 116 + dotfiles/system/.zsh/modules/Src/mkmakemod.sh | 468 + dotfiles/system/.zsh/modules/Src/module.c | 3641 +++++ dotfiles/system/.zsh/modules/Src/options.c | 955 ++ dotfiles/system/.zsh/modules/Src/params.c | 5884 ++++++++ dotfiles/system/.zsh/modules/Src/parse.c | 3977 +++++ dotfiles/system/.zsh/modules/Src/pattern.c | 4336 ++++++ dotfiles/system/.zsh/modules/Src/prompt.c | 2046 +++ dotfiles/system/.zsh/modules/Src/prototypes.h | 134 + dotfiles/system/.zsh/modules/Src/signals.c | 1479 ++ dotfiles/system/.zsh/modules/Src/signals.h | 142 + dotfiles/system/.zsh/modules/Src/signames1.awk | 19 + dotfiles/system/.zsh/modules/Src/signames2.awk | 106 + dotfiles/system/.zsh/modules/Src/string.c | 213 + dotfiles/system/.zsh/modules/Src/utils.c | 7520 ++++++++++ dotfiles/system/.zsh/modules/Src/wcwidth9.h | 1325 ++ dotfiles/system/.zsh/modules/Src/zsh.h | 3305 +++++ dotfiles/system/.zsh/modules/Src/zsh.mdd | 147 + dotfiles/system/.zsh/modules/Src/zsh.rc | 8 + dotfiles/system/.zsh/modules/Src/zsh_system.h | 900 ++ dotfiles/system/.zsh/modules/Src/ztype.h | 89 + dotfiles/system/.zsh/modules/Test/.cvsignore | 3 + dotfiles/system/.zsh/modules/Test/.distfiles | 2 + dotfiles/system/.zsh/modules/Test/A01grammar.ztst | 790 + dotfiles/system/.zsh/modules/Test/A02alias.ztst | 139 + dotfiles/system/.zsh/modules/Test/A03quoting.ztst | 80 + dotfiles/system/.zsh/modules/Test/A04redirect.ztst | 588 + .../system/.zsh/modules/Test/A05execution.ztst | 312 + dotfiles/system/.zsh/modules/Test/A06assign.ztst | 631 + dotfiles/system/.zsh/modules/Test/A07control.ztst | 165 + dotfiles/system/.zsh/modules/Test/B01cd.ztst | 144 + dotfiles/system/.zsh/modules/Test/B02typeset.ztst | 723 + dotfiles/system/.zsh/modules/Test/B03print.ztst | 336 + dotfiles/system/.zsh/modules/Test/B04read.ztst | 112 + dotfiles/system/.zsh/modules/Test/B05eval.ztst | 34 + dotfiles/system/.zsh/modules/Test/B06fc.ztst | 25 + dotfiles/system/.zsh/modules/Test/B07emulate.ztst | 253 + dotfiles/system/.zsh/modules/Test/B08shift.ztst | 33 + dotfiles/system/.zsh/modules/Test/B09hash.ztst | 79 + dotfiles/system/.zsh/modules/Test/C01arith.ztst | 422 + dotfiles/system/.zsh/modules/Test/C02cond.ztst | 448 + dotfiles/system/.zsh/modules/Test/C03traps.ztst | 761 + dotfiles/system/.zsh/modules/Test/C04funcdef.ztst | 502 + dotfiles/system/.zsh/modules/Test/C05debug.ztst | 159 + dotfiles/system/.zsh/modules/Test/D01prompt.ztst | 203 + dotfiles/system/.zsh/modules/Test/D02glob.ztst | 688 + .../system/.zsh/modules/Test/D03procsubst.ztst | 151 + .../system/.zsh/modules/Test/D04parameter.ztst | 2058 +++ dotfiles/system/.zsh/modules/Test/D05array.ztst | 112 + .../system/.zsh/modules/Test/D06subscript.ztst | 268 + .../system/.zsh/modules/Test/D07multibyte.ztst | 587 + dotfiles/system/.zsh/modules/Test/D08cmdsubst.ztst | 169 + dotfiles/system/.zsh/modules/Test/D09brace.ztst | 114 + dotfiles/system/.zsh/modules/Test/E01options.ztst | 1313 ++ dotfiles/system/.zsh/modules/Test/E02xtrace.ztst | 148 + dotfiles/system/.zsh/modules/Test/Makefile.in | 75 + dotfiles/system/.zsh/modules/Test/README | 30 + .../system/.zsh/modules/Test/V02zregexparse.ztst | 382 + dotfiles/system/.zsh/modules/Test/V03mathfunc.ztst | 141 + dotfiles/system/.zsh/modules/Test/V04features.ztst | 172 + dotfiles/system/.zsh/modules/Test/V05styles.ztst | 143 + dotfiles/system/.zsh/modules/Test/V07pcre.ztst | 139 + dotfiles/system/.zsh/modules/Test/V08zpty.ztst | 29 + dotfiles/system/.zsh/modules/Test/V09datetime.ztst | 74 + dotfiles/system/.zsh/modules/Test/V10private.ztst | 304 + dotfiles/system/.zsh/modules/Test/W01history.ztst | 60 + dotfiles/system/.zsh/modules/Test/comptest | 177 + dotfiles/system/.zsh/modules/Test/runtests.zsh | 27 + dotfiles/system/.zsh/modules/Test/ztst.zsh | 547 + dotfiles/system/.zsh/modules/aclocal.m4 | 77 + dotfiles/system/.zsh/modules/aczsh.m4 | 690 + dotfiles/system/.zsh/modules/config.guess | 1501 ++ dotfiles/system/.zsh/modules/config.h.in | 1242 ++ dotfiles/system/.zsh/modules/config.sub | 1705 +++ dotfiles/system/.zsh/modules/configure | 14547 +++++++++++++++++++ dotfiles/system/.zsh/modules/configure.ac | 3213 ++++ dotfiles/system/.zsh/modules/copy_from_zsh_src.zsh | 29 + dotfiles/system/.zsh/modules/install-sh | 507 + dotfiles/system/.zsh/modules/mkinstalldirs | 162 + dotfiles/system/.zsh/modules/patch_cfgac.diff | 73 + dotfiles/system/.zsh/modules/stamp-h.in | 1 + 155 files changed, 109273 insertions(+) create mode 100644 dotfiles/system/.authcode create mode 100644 dotfiles/system/.authinfo.gpg create mode 100644 dotfiles/system/.bashrc create mode 100644 dotfiles/system/.profile.d/auto-tmux-session.sh create mode 100644 dotfiles/system/.profile.d/chronographic.sh create mode 100644 dotfiles/system/.profile.d/dd.sh create mode 100644 dotfiles/system/.profile.d/display.sh create mode 100644 dotfiles/system/.profile.d/emacs.sh create mode 100644 dotfiles/system/.profile.d/extract.sh create mode 100644 dotfiles/system/.profile.d/framework.sh create mode 100644 dotfiles/system/.profile.d/freebsd.sh create mode 100644 dotfiles/system/.profile.d/fzf.sh create mode 100644 dotfiles/system/.profile.d/git.sh create mode 100644 dotfiles/system/.profile.d/media.sh create mode 100644 dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.dict.dz create mode 100644 dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.idx create mode 100644 dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.idx.oft create mode 100644 dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.ifo create mode 100644 dotfiles/system/.tmux.conf create mode 100644 dotfiles/system/.zsh/README.md create mode 100644 dotfiles/system/.zsh/fzf-tab.zsh create mode 100644 dotfiles/system/.zsh/lib/-ftb-colorize create mode 100755 dotfiles/system/.zsh/lib/-ftb-fzf create mode 100644 dotfiles/system/.zsh/lib/-ftb-generate-complist create mode 100644 dotfiles/system/.zsh/lib/-ftb-generate-header create mode 100644 dotfiles/system/.zsh/lib/-ftb-generate-query create mode 100644 dotfiles/system/.zsh/lib/ftb-switch-group create mode 100755 dotfiles/system/.zsh/lib/ftb-tmux-popup create mode 100644 dotfiles/system/.zsh/lib/zsh-ls-colors/LICENSE create mode 100644 dotfiles/system/.zsh/lib/zsh-ls-colors/README.md create mode 100755 dotfiles/system/.zsh/lib/zsh-ls-colors/demo create mode 100644 dotfiles/system/.zsh/lib/zsh-ls-colors/ls-colors.zsh create mode 100644 dotfiles/system/.zsh/modules/.cvsignore create mode 100644 dotfiles/system/.zsh/modules/.distfiles create mode 100644 dotfiles/system/.zsh/modules/.editorconfig create mode 100644 dotfiles/system/.zsh/modules/.gitignore create mode 100755 dotfiles/system/.zsh/modules/.preconfig create mode 100644 dotfiles/system/.zsh/modules/Config/.cvsignore create mode 100644 dotfiles/system/.zsh/modules/Config/.distfiles create mode 100644 dotfiles/system/.zsh/modules/Config/aczshoot.m4 create mode 100644 dotfiles/system/.zsh/modules/Config/clean.mk create mode 100644 dotfiles/system/.zsh/modules/Config/config.mk create mode 100644 dotfiles/system/.zsh/modules/Config/defs.mk.in create mode 100755 dotfiles/system/.zsh/modules/Config/installfns.sh create mode 100755 dotfiles/system/.zsh/modules/Config/uninstallfns.sh create mode 100644 dotfiles/system/.zsh/modules/Config/version.mk create mode 100644 dotfiles/system/.zsh/modules/LICENCE create mode 100644 dotfiles/system/.zsh/modules/Makefile.in create mode 100644 dotfiles/system/.zsh/modules/RECOMPILE_REQUEST create mode 100644 dotfiles/system/.zsh/modules/Src/.cvsignore create mode 100644 dotfiles/system/.zsh/modules/Src/.distfiles create mode 100644 dotfiles/system/.zsh/modules/Src/.exrc create mode 100644 dotfiles/system/.zsh/modules/Src/.indent.pro create mode 100644 dotfiles/system/.zsh/modules/Src/Makefile.in create mode 100644 dotfiles/system/.zsh/modules/Src/Makemod.in.in create mode 100644 dotfiles/system/.zsh/modules/Src/aloxaf/.cvsignore create mode 100644 dotfiles/system/.zsh/modules/Src/aloxaf/.distfiles create mode 100644 dotfiles/system/.zsh/modules/Src/aloxaf/.exrc create mode 100644 dotfiles/system/.zsh/modules/Src/aloxaf/.gitignore create mode 100644 dotfiles/system/.zsh/modules/Src/aloxaf/fzftab.c create mode 100644 dotfiles/system/.zsh/modules/Src/aloxaf/fzftab.mdd create mode 100644 dotfiles/system/.zsh/modules/Src/builtin.c create mode 100644 dotfiles/system/.zsh/modules/Src/compat.c create mode 100644 dotfiles/system/.zsh/modules/Src/exec.c create mode 100644 dotfiles/system/.zsh/modules/Src/glob.c create mode 100644 dotfiles/system/.zsh/modules/Src/hashtable.c create mode 100644 dotfiles/system/.zsh/modules/Src/hashtable.h create mode 100644 dotfiles/system/.zsh/modules/Src/init.c create mode 100644 dotfiles/system/.zsh/modules/Src/input.c create mode 100644 dotfiles/system/.zsh/modules/Src/jobs.c create mode 100644 dotfiles/system/.zsh/modules/Src/lex.c create mode 100644 dotfiles/system/.zsh/modules/Src/loop.c create mode 100644 dotfiles/system/.zsh/modules/Src/makepro.awk create mode 100644 dotfiles/system/.zsh/modules/Src/mem.c create mode 100644 dotfiles/system/.zsh/modules/Src/mkbltnmlst.sh create mode 100644 dotfiles/system/.zsh/modules/Src/mkmakemod.sh create mode 100644 dotfiles/system/.zsh/modules/Src/module.c create mode 100644 dotfiles/system/.zsh/modules/Src/options.c create mode 100644 dotfiles/system/.zsh/modules/Src/params.c create mode 100644 dotfiles/system/.zsh/modules/Src/parse.c create mode 100644 dotfiles/system/.zsh/modules/Src/pattern.c create mode 100644 dotfiles/system/.zsh/modules/Src/prompt.c create mode 100644 dotfiles/system/.zsh/modules/Src/prototypes.h create mode 100644 dotfiles/system/.zsh/modules/Src/signals.c create mode 100644 dotfiles/system/.zsh/modules/Src/signals.h create mode 100644 dotfiles/system/.zsh/modules/Src/signames1.awk create mode 100644 dotfiles/system/.zsh/modules/Src/signames2.awk create mode 100644 dotfiles/system/.zsh/modules/Src/string.c create mode 100644 dotfiles/system/.zsh/modules/Src/utils.c create mode 100644 dotfiles/system/.zsh/modules/Src/wcwidth9.h create mode 100644 dotfiles/system/.zsh/modules/Src/zsh.h create mode 100644 dotfiles/system/.zsh/modules/Src/zsh.mdd create mode 100644 dotfiles/system/.zsh/modules/Src/zsh.rc create mode 100644 dotfiles/system/.zsh/modules/Src/zsh_system.h create mode 100644 dotfiles/system/.zsh/modules/Src/ztype.h create mode 100644 dotfiles/system/.zsh/modules/Test/.cvsignore create mode 100644 dotfiles/system/.zsh/modules/Test/.distfiles create mode 100644 dotfiles/system/.zsh/modules/Test/A01grammar.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/A02alias.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/A03quoting.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/A04redirect.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/A05execution.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/A06assign.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/A07control.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/B01cd.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/B02typeset.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/B03print.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/B04read.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/B05eval.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/B06fc.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/B07emulate.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/B08shift.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/B09hash.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/C01arith.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/C02cond.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/C03traps.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/C04funcdef.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/C05debug.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/D01prompt.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/D02glob.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/D03procsubst.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/D04parameter.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/D05array.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/D06subscript.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/D07multibyte.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/D08cmdsubst.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/D09brace.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/E01options.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/E02xtrace.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/Makefile.in create mode 100644 dotfiles/system/.zsh/modules/Test/README create mode 100644 dotfiles/system/.zsh/modules/Test/V02zregexparse.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/V03mathfunc.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/V04features.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/V05styles.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/V07pcre.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/V08zpty.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/V09datetime.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/V10private.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/W01history.ztst create mode 100644 dotfiles/system/.zsh/modules/Test/comptest create mode 100644 dotfiles/system/.zsh/modules/Test/runtests.zsh create mode 100755 dotfiles/system/.zsh/modules/Test/ztst.zsh create mode 100644 dotfiles/system/.zsh/modules/aclocal.m4 create mode 100644 dotfiles/system/.zsh/modules/aczsh.m4 create mode 100755 dotfiles/system/.zsh/modules/config.guess create mode 100644 dotfiles/system/.zsh/modules/config.h.in create mode 100755 dotfiles/system/.zsh/modules/config.sub create mode 100755 dotfiles/system/.zsh/modules/configure create mode 100644 dotfiles/system/.zsh/modules/configure.ac create mode 100755 dotfiles/system/.zsh/modules/copy_from_zsh_src.zsh create mode 100755 dotfiles/system/.zsh/modules/install-sh create mode 100755 dotfiles/system/.zsh/modules/mkinstalldirs create mode 100644 dotfiles/system/.zsh/modules/patch_cfgac.diff create mode 100644 dotfiles/system/.zsh/modules/stamp-h.in diff --git a/dotfiles/system/.authcode b/dotfiles/system/.authcode new file mode 100644 index 0000000..d67b6ee --- /dev/null +++ b/dotfiles/system/.authcode @@ -0,0 +1 @@ +be251501 diff --git a/dotfiles/system/.authinfo.gpg b/dotfiles/system/.authinfo.gpg new file mode 100644 index 0000000..1c54564 Binary files /dev/null and b/dotfiles/system/.authinfo.gpg differ diff --git a/dotfiles/system/.bashrc b/dotfiles/system/.bashrc new file mode 100644 index 0000000..95d78ff --- /dev/null +++ b/dotfiles/system/.bashrc @@ -0,0 +1,57 @@ +#!/bin/bash +# cjennings .bashrc + +# tells shellcheck not to follow references to other files +# shellcheck source=/dev/null + +# If not running interactively, don't do anything +case $- in + *i*) ;; + *) return;; +esac + +# env variables, aliases, and functions that are not bash specific +source "$HOME"/.profile + +# don't put duplicate lines or lines starting with space in the history. +HISTCONTROL=ignoreboth + +# infinite history +HISTSIZE=HISTFILESIZE= + +# append and reload the history after each command +PROMPT_COMMAND="history -a; history -n" + +# ignore the following commands from the history +HISTIGNORE="ls:ll:cd:pwd:bg:fg:history" + +# for setting history length see HISTSIZE and HISTFILESIZE in bash(1) +HISTSIZE=100000 +HISTFILESIZE=10000000 + +# append to the history file, don't overwrite it +shopt -s histappend + +# cd to directory by typing its name +shopt -s autocd + +# check window size after each command + update LINES and COLUMNS values. +shopt -s checkwinsize + +# enable programmable completion features +if ! shopt -oq posix; then + if [ -f /usr/share/bash-completion/bash_completion ]; then + . /usr/share/bash-completion/bash_completion + elif [ -f /etc/bash_completion ]; then + . /etc/bash_completion + fi +fi +if [ -f /etc/bash_completion ]; then + . /etc/bash_completion +fi + +export PS1="[\d, \t] \u@\H:\w \n$ " + +source "$HOME"/.profile + +[ -f "$HOME"/.fzf.bash ] && source "$HOME"/.fzf.bash diff --git a/dotfiles/system/.profile.d/auto-tmux-session.sh b/dotfiles/system/.profile.d/auto-tmux-session.sh new file mode 100644 index 0000000..2f17085 --- /dev/null +++ b/dotfiles/system/.profile.d/auto-tmux-session.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# auto-tmux-session.sh +# Craig Jennings +# if TRAMP (ssh dumb terminal), simplify prompt, and start tmux. + +# start tmux when logging in via ssh +if [[ -z "$TMUX" ]] && [ "$SSH_CONNECTION" != "" ]; then + export TERM="xterm-mono" + # if dumb terminal (i.e., tramp), then set a simple prompt, otherwise set monochrome TERM and start tmux + tmux attach-session -t "$USER" || tmux new-session -s "$USER" +fi diff --git a/dotfiles/system/.profile.d/chronographic.sh b/dotfiles/system/.profile.d/chronographic.sh new file mode 100644 index 0000000..d53fe2b --- /dev/null +++ b/dotfiles/system/.profile.d/chronographic.sh @@ -0,0 +1,116 @@ +#!/bin/sh + +# chronographic.sh +# Craig Jennings +# quick and dirty terminal clock, timer, alarm and stopwatch functions + +alias beep='paplay $BEEP' +export BEEP="/usr/share/sounds/freedesktop/stereo/bell.oga" + +## +## CLOCK +## + +clock() { + while true; do tput clear; echo ""; date +" %l : %M %p" | figlet -f roman -w 200 ; sleep 1; done +} + +## +## TIMER +## + +timer() { + # Ensure we have at least two arguments + if [ "$#" -lt 2 ]; then + echo "Pass the time and a notification. Example: timer 1h30m Parking expiring" + return 1 + fi + message="${@:2}" + start_time=$(date +"%H:%M:%S %p") + printf "\nStarting %s timer at $start_time\n" "$1" + snore "$1" && paplay "$BEEP" && notify-send "Timer" "$message" && echo "" +} + +## +## ALARM +## + +alarm() { + # Ensure we have two or more arguments + if [ "$#" -lt 2 ]; then + echo "Pass both the time and a message. Example: alarm 1:45pm Time to eat!" + return 1 + fi + + # Validate the first argument is a valid time + ! date -d "$1" >/dev/null 2>&1 && echo "Invalid time: $1" && return 1 + + # silently schedule the command using 'at' command + echo "paplay \$BEEP && notify-send \"Alarm\" \"$@\"" | at "$1" >> /dev/null + echo "Alarm '${@:2}' is queued for $1." && echo "To see all alarms, issue the command: atq" && echo "" +} + +## +## STOPWATCH FUNCTIONS +## + +sw_start_time=0 +sw_started=0 + +swreset() { + sw_start_time=0 + sw_started=0 + echo "Stopwatch reset" +} + +swshow() { + if [ "$sw_started" = 0 ] ; then + echo "Error: Stopwatch not started" >&2 && return 1 + fi + + current_time=$(date +%s) + elapsed_time=$((current_time - sw_start_time)) + + if (( elapsed_time < 60 )); then + # Display elapsed time in seconds + echo "Elapsed time: $elapsed_time seconds" + elif (( elapsed_time < 3600 )); then + # Display elapsed time in minutes and seconds + minutes=$((elapsed_time / 60)) + seconds=$((elapsed_time % 60)) + echo "Elapsed time: $minutes minutes, $seconds seconds" + else + # Display elapsed time in hours, minutes, and seconds + hours=$((elapsed_time / 3600)) + minutes=$(((elapsed_time / 60) % 60)) + seconds=$((elapsed_time % 60)) + echo "Elapsed time: $hours hours, $minutes minutes, and $seconds seconds" + fi +} + +swstop() { + swshow + swreset +} + +swstart() { + if [ "$sw_started" = 1 ] ; then + printf "Stopwatch is already started. Reset? (y/n): " + read -r answer + if [ "$answer" = "y" -o "$answer" = "Y" ]; then + swreset + # continue on to start the new timer + elif [ "$answer" = "n" -o "$answer" = "N" ]; then + echo "Stopwatch not reset." + swshow + # return to avoid restarting the timer + return + else + echo "Error: Invalid input. Exiting." >&2 && return 1 + fi + fi + + sw_started=1 + sw_start_time=$(date +%s) + printf "Stopwatch started at %s\n\n" "$(date +"%H:%M:%S %p")" +} diff --git a/dotfiles/system/.profile.d/dd.sh b/dotfiles/system/.profile.d/dd.sh new file mode 100644 index 0000000..5390a65 --- /dev/null +++ b/dotfiles/system/.profile.d/dd.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +# dd.sh +# Craig Jennings +# takes input and output files as arguments +# prints parameters needed for dd + + +dd_with_bs() { + OUT_DIR=$(dirname "$2") + if [ ! -e "$1" -o ! -e "$OUT_DIR" ]; then + echo "$1 or $OUT_DIR doesn't exist" + return 1 + fi + IN_BS=$(stat -c "%o" "$1") + OUT_BS=$(stat -c "%o" "$OUT_DIR") + + echo dd \"if=$1\" \"of=$2\" \"ibs=$IN_BS\" \"obs=$OUT_BS\" +} diff --git a/dotfiles/system/.profile.d/display.sh b/dotfiles/system/.profile.d/display.sh new file mode 100644 index 0000000..ce5016e --- /dev/null +++ b/dotfiles/system/.profile.d/display.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# display.sh +# Craig Jennings +# UI Appearance settings, sourced by .profile + +# Theme +export GTK_THEME=Adwaita:dark +export QT_QPA_PLATFORMTHEME=qt5ct diff --git a/dotfiles/system/.profile.d/emacs.sh b/dotfiles/system/.profile.d/emacs.sh new file mode 100644 index 0000000..c70d928 --- /dev/null +++ b/dotfiles/system/.profile.d/emacs.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# emacs.sh +# Craig Jennings +# Emacs specific settings, sourced by .profile + +### INFO FILES +export INFOPATH="$HOME/.config/emacs/info:/usr/share/info:/usr/local/share/info" + +### WORKAROUND +# https://unix.stackexchange.com/questions/230238/x-applications-warn-couldnt-connect-to-accessibility-bus-on-stderr +# emacs/gtk interaction bug workaround +export NO_AT_BRIDGE=1 + +# WAKE UTILITY +# useful when emacs or elisp doesn't timeout. +alias emacswake='for i in `seq 1 500`; do killall -s USR2 emacs; done' # wake emacs from a freeze + +### VTERM +# Vterm uses some features (e.g., directory-tracking and prompt-tracking or message passing) that require shell-side configurations. +# This functions enables the shell to send information to vterm via properly escaped sequences. + +vterm_printf(){ + if [ -n "$TMUX" ] && ([ "${TERM%%-*}" = "tmux" ] || [ "${TERM%%-*}" = "screen" ] ); then + # Tell tmux to pass the escape sequences through + printf "\ePtmux;\e\e]%s\007\e\\" "$1" + elif [ "${TERM%%-*}" = "screen" ]; then + # GNU screen (screen, screen-256color, screen-256color-bce) + printf "\eP\e]%s\007\e\\" "$1" + else + printf "\e]%s\e\\" "$1" + fi +} diff --git a/dotfiles/system/.profile.d/extract.sh b/dotfiles/system/.profile.d/extract.sh new file mode 100644 index 0000000..5fce587 --- /dev/null +++ b/dotfiles/system/.profile.d/extract.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# extract.sh +# Craig Jennings +# convenience extraction function + +## EXTRACT + +extract () { + if [ -f "$1" ] ; then + case "$1" in + *.tar.bz2) tar xjf "$1" ;; + *.tar.gz) tar xzf "$1" ;; + *.bz2) bunzip2 "$1" ;; + *.rar) rar x "$1" ;; + *.gz) gunzip "$1" ;; + *.tar) tar xf "$1" ;; + *.tbz2) tar xjf "$1" ;; + *.tgz) tar xzf "$1" ;; + *.zip) unzip "$1" ;; + *.Z) uncompress "$1" ;; + *) echo "$1 cannot be extracted via extract()" ;; + esac + else + echo "$1 is not a valid file" + fi +} diff --git a/dotfiles/system/.profile.d/framework.sh b/dotfiles/system/.profile.d/framework.sh new file mode 100644 index 0000000..2b8895e --- /dev/null +++ b/dotfiles/system/.profile.d/framework.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# framework.sh +# Craig Jennings +# settings for Framework 13 specific issues + +# HiDPI scale settings for Framework 13 Laptop +export GDK_DPI_SCALE=1 # text scaling only. +export GDK_SCALE=1 # this is an integer, 1 or 2 etc +export QT_AUTO_SCREEN_SCALE_FACTOR=1 diff --git a/dotfiles/system/.profile.d/freebsd.sh b/dotfiles/system/.profile.d/freebsd.sh new file mode 100644 index 0000000..8d12031 --- /dev/null +++ b/dotfiles/system/.profile.d/freebsd.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# freebsd.sh +# Craig Jennings +# Settings FreeBSD specific, sourced by .profile + +# make delete do the right thing for freebsd +if echo "$(uname)" | grep -q "FreeBSD"; then + bindkey "\e[3~" delete-char +fi diff --git a/dotfiles/system/.profile.d/fzf.sh b/dotfiles/system/.profile.d/fzf.sh new file mode 100644 index 0000000..5a13dd4 --- /dev/null +++ b/dotfiles/system/.profile.d/fzf.sh @@ -0,0 +1,76 @@ +#!/bin/sh + +# fzf.sh +# Craig Jennings +# fuzzy find settings and utilities, sourced by .profile + +### SETTINGS +export FZF_DEFAULT_OPTS='--height=70%' +export FZF_DEFAULT_COMMAND='rg --files' +export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND" +export FZF_DEFAULT_COMMAND='rg --files --no-ignore-vcs --hidden' + + +### NAVIGATION + +# cdff - change directory find file +# change to the directory where the file resides. +cdff() { + + file=$(fzf +m -q "$1") + dir=$(dirname "$file") + cd "$dir" || exit +} + +# CD to a directory with fzf +cdd () { + destdir=$(find "${1:-.}" -path '*/\.*' -prune \ + -o -type d -print 2> /dev/null | fzf +m) && + cd "$destdir" +} + +### SYSTEM ADMIN + +# Kill a process with fzf +kp () { + pid=$(ps -ef | sed 1d | eval "fzf ${FZF_DEFAULT_OPTS} -m --header='[kill:process]'" | awk '{print $2}') + + if [ "x$pid" != "x" ] + then + echo "$pid" | xargs kill -${1:-9} + kp + fi +} + +# list available packages, show info in preview, and install selection +yinstall() { + yay -Slq | fzf --multi --preview 'yay -Si {1}' | xargs -ro yay -S --noconfirm +} + + +yinstall-skipverify() { + yay -Slq | fzf --multi --preview 'yay -Si {1}' | xargs -ro yay -S --noconfirm --mflags --skipinteg +} + +# list installed packages, show info in preview, and remove selection +yrm() { + yay -Qq | fzf --multi --preview 'yay -Qi {1}' | xargs -ro yay -Rns +} + +# find-in-file - usage: fif +fif() { + if [ ! "$#" -gt 0 ]; then echo "Need a string to search for!"; return 1; fi + rg --files-with-matches --no-messages "$1" | fzf --preview "highlight -O ansi -l {} 2> /dev/null | rg --colors 'match:bg:yellow' --ignore-case --pretty --context 10 '$1' || rg --ignore-case --pretty --context 10 '$1' {}" +} + +### CONVENIENCE + +# Find a book in the calibre library and open it in emacs client. +# previously: find ~/books \( -iname \*.epub -o -iname \*.pdf -o -iname \*.djvu \) | fzf | xargs emacs +bk() { + bkfile=$(find ~/sync/books/ \( -iname "*.pdf" -o -iname "*.epub" -o -iname "*.djvu" \) -print | fzf) + if [ -n "$bkfile" ]; then + emacsclient -c -a '' "$bkfile" & + fi +} + diff --git a/dotfiles/system/.profile.d/git.sh b/dotfiles/system/.profile.d/git.sh new file mode 100644 index 0000000..7332ba1 --- /dev/null +++ b/dotfiles/system/.profile.d/git.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# git.sh +# Craig Jennings +# git settings and convenience aliases, sourced by .profile + +alias gitlog="git log --graph --pretty=format:'%Cred%h%Creset %an: %s - %Creset %C(yellow)%d%Creset %Cgreen(%cr)%Creset' --abbrev-commit --date=relative" +alias gitstatus='git status -sb ' +alias gitcom='git commit -m ' +alias gitpp='git pull --prune ' # clean up any orphaned git objects +alias gittagbydate="git for-each-ref --sort=creatordate --format '%(refname) %(creatordate)' refs/tags" + +gitsp() { + git stash && git pull && git stash pop +} + +gitck() { + git checkout "$(git branch --all | fzf| tr -d '[:space:]')" +} + +gitdiff() { + preview="git diff $@ --color=always -- {-1}" + git diff "$@" --name-only | fzf -m --ansi --preview "$preview" +} diff --git a/dotfiles/system/.profile.d/media.sh b/dotfiles/system/.profile.d/media.sh new file mode 100644 index 0000000..3add213 --- /dev/null +++ b/dotfiles/system/.profile.d/media.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +# media.sh +# Craig Jennings +# utilities for working with media (music, video, books, etc.) + + +### TERMINAL MPV ALIASES +alias play='mpv --no-video' +alias shuffle='mpv --no-video --shuffle ' + + +### YOUTUBE AND TIDAL +# Leverage task spooler to download url targets serially in the background. +# example : % stdl "https://youtu.be/gv-3Y7CcUUo" +# note that zshell urls must be quoted; not so for bash. + +# tidal-dl +alias tdl="tidal-dl -l" +alias ttdl="tsp tidal-dl -l" + +# youtube-dl +alias yt="yt-dlp --ignore-config --no-playlist --add-metadata -i -o '%(channel)s-%(title)s.%(ext)s'" +alias tyt="tsp yt-dlp --ignore-config --no-playlist --add-metadata -i -o '%(channel)s-%(title)s.%(ext)s'" + +alias ytp="yt-dlp --ignore-config --yes-playlist --add-metadata -i -o '%(channel)s-%(playlist_title)s-%(playlist_index)s-%(title)s.%(ext)s'" +alias tytp="tsp yt-dlp --ignore-config --yes-playlist --add-metadata -i -o '%(channel)s-%(playlist_title)s-%(playlist_index)s-%(title)s.%(ext)s'" + +alias yta="yt-dlp --ignore-config --no-playlist -x -f bestaudio/best -o '%(artist)s-%(title)s.%(ext)s'" +alias tyta="tsp yt-dlp --ignore-config --no-playlist -x -f bestaudio/best -o '%(artist)s-%(title)s.%(ext)s'" + +alias ytap="yt-dlp --ignore-config --yes-playlist -x -f bestaudio/best -o '%(playlist_index)s-%(artist)s-%(title)s.%(ext)s'" +alias tytap="tsp yt-dlp --ignore-config --yes-playlist -x -f bestaudio/best -o '%(playlist_index)s-%(artist)s-%(title)s.%(ext)s'" + + +### SOUND FILE CONVERSION +# converts audible aax to other formats. Requires AAXtoMP3 script in dotfiles. + +alias aax2flac='AAXtoMP3 -f ' +alias aax2mp3='AAXtoMP3 -c -e:mp3 ' +alias aax2opus='AAXtoMP3 --opus -t . -c ' diff --git a/dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.dict.dz b/dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.dict.dz new file mode 100644 index 0000000..b234473 Binary files /dev/null and b/dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.dict.dz differ diff --git a/dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.idx b/dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.idx new file mode 100644 index 0000000..bc5ce32 Binary files /dev/null and b/dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.idx differ diff --git a/dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.idx.oft b/dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.idx.oft new file mode 100644 index 0000000..2db9b2b Binary files /dev/null and b/dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.idx.oft differ diff --git a/dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.ifo b/dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.ifo new file mode 100644 index 0000000..69d64df --- /dev/null +++ b/dotfiles/system/.stardict/dic/dictd_www.dict.org_web1913.ifo @@ -0,0 +1,8 @@ +StarDict's dict ifo file +version=2.4.2 +wordcount=160161 +idxfilesize=3024035 +bookname=Webster's Revised Unabridged Dictionary (1913) +description=Made by Hu Zheng +date=2007.8.28 +sametypesequence=m diff --git a/dotfiles/system/.tmux.conf b/dotfiles/system/.tmux.conf new file mode 100644 index 0000000..ea3bc0d --- /dev/null +++ b/dotfiles/system/.tmux.conf @@ -0,0 +1,88 @@ +# note: refresh the list of plugins = prefix + capital I + +# enable mouse support +set -g mouse on + +# enabling passthrough for kitty image rendering +set -g allow-passthrough on + +# make scrolling with wheels work +bind -n WheelUpPane if-shell -F -t = "#{mouse_any_flag}" "send-keys -M" "if -Ft= '#{pane_in_mode}' 'send-keys -M' 'select-pane -t=; copy-mode -e; send-keys -M'" +bind -n WheelDownPane select-pane -t= \; send-keys -M + +# list of plugins +set -g @plugin 'tmux-plugins/tpm' +set -g @plugin 'tmux-plugins/tmux-open' +set -g @plugin 'tmux-plugins/tmux-logging' +set -g @plugin 'tmux-plugins/tmux-yank' +set -g @plugin 'tmux-plugins/tmux-sessionist' +set -g @plugin 'MunifTanjim/tmux-mode-indicator' +set -g @plugin 'MunifTanjim/tmux-suspend' +set-option -g @plugin 'b0o/tmux-autoreload' + +# yank to the clipboard when selecting with the mouse +set -g @yank_selection_mouse 'clipboard' + +# plugin Settings +set -g @mighty-scroll-interval 3 +set -g @mighty-scroll-by-line 'man fzf' +set -g @mighty-scroll-select-pane off + +# put tmux mode indicator on right +set -g status-right '%Y-%m-%d %H:%M #{tmux_mode_indicator}' + +# gruvbox compatible colours +set -g status-fg colour0 +set -g status-bg colour15 + +# number the panes and match keyboard order +set -g base-index 1 +set -g pane-base-index 1 +set -g renumber-windows on + +# address vim mode switching delay (http://superuser.com/a/252717/65504) +set -s escape-time 0 + +# tmux messages are displayed for 4 seconds +set -g display-time 4000 + +# upgrade $TERM +set -g default-terminal "screen-256color" + +# prefix r reloads config +bind r source-file ~/.tmux.conf \; display "tmux config reloaded" + +# keep path with new window +bind c new-window -c "#{pane_current_path}" + +# split window horizontally +bind-key "|" split-window -h -c "#{pane_current_path}" +bind-key "\\" split-window -fh -c "#{pane_current_path}" + +# split window vertically +bind-key "-" split-window -v -c "#{pane_current_path}" +bind-key "_" split-window -fv -c "#{pane_current_path}" + +# easier and faster switching between next/prev window +bind C-p previous-window +bind C-n next-window + +# prefix shift + W moves to last window +# note: prefix shift S moves to last session with sessionist plugin +bind W last-window + +# increase scrollback buffer +set-option -g history-limit 50000 + +# don't allow suspending client +unbind-key C-z + +# emacs key bindings in tmux command prompt (prefix + :) +set -g status-keys emacs + +# install tmux plugins automatically when tmux is started +if "test ! -d ~/.tmux/plugins/tpm" \ + "run 'git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm && ~/.tmux/plugins/tpm/bin/install_plugins'" + +# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf) +run '~/.tmux/plugins/tpm/tpm' diff --git a/dotfiles/system/.zsh/README.md b/dotfiles/system/.zsh/README.md new file mode 100644 index 0000000..9130c1a --- /dev/null +++ b/dotfiles/system/.zsh/README.md @@ -0,0 +1,138 @@ +# fzf-tab + +[![CI](https://github.com/Aloxaf/fzf-tab/workflows/ci/badge.svg)](https://github.com/Aloxaf/fzf-tab/actions?query=workflow%3Aci) +[![GitHub license](https://img.shields.io/github/license/Aloxaf/fzf-tab)](https://github.com/Aloxaf/fzf-tab/blob/master/LICENSE) + +Replace zsh's default completion selection menu with fzf! + +[![asciicast](https://asciinema.org/a/293849.svg)](https://asciinema.org/a/293849) + + +**Table of Contents** + +- [fzf-tab](#fzf-tab) +- [Install](#install) + - [Manual](#manual) + - [Antigen](#antigen) + - [Zinit](#zinit) + - [Oh-My-Zsh](#oh-my-zsh) + - [Prezto](#prezto) +- [Usage](#usage) + - [Configure](#configure) + - [Binary module](#binary-module) +- [Difference from other plugins](#difference-from-other-plugins) +- [Compatibility with other plugins](#compatibility-with-other-plugins) +- [Related projects](#related-projects) + + + +# Install + +**NOTE: fzf-tab needs to be loaded after `compinit`, but before plugins which will wrap widgets, such as [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions) or [fast-syntax-highlighting](https://github.com/zdharma/fast-syntax-highlighting)!!** + +### Manual + +First, clone this repository. + +```zsh +git clone https://github.com/Aloxaf/fzf-tab ~/somewhere +``` + +Then add the following line to your `~/.zshrc`. + +```zsh +source ~/somewhere/fzf-tab.plugin.zsh +``` + +### Antigen + +```zsh +antigen bundle Aloxaf/fzf-tab +``` + +### Zinit + +```zsh +zinit light Aloxaf/fzf-tab +``` + +### Oh-My-Zsh + +Clone this repository to your custom directory and then add `fzf-tab` to your plugin list. + +```zsh +git clone https://github.com/Aloxaf/fzf-tab ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/fzf-tab +``` + +### Prezto + +Clone this repository to your contrib directory and then add `fzf-tab` to your module list in `.zpreztorc`. + +```zsh +git clone https://github.com/Aloxaf/fzf-tab $ZPREZTODIR/contrib/fzf-tab +``` + +# Usage + +Just press Tab as usual~ + +Available keybindings: + +- Ctrl+Space: select multiple results, can be configured by `fzf-bindings` tag + +- F1/F2: switch between groups, can be configured by `switch-group` tag + +- /: trigger continuous completion (useful when completing a deep path), can be configured by `continuous-trigger` tag + +Available commands: + +- `disable-fzf-tab`: disable fzf-tab and fallback to compsys + +- `enable-fzf-tab`: enable fzf-tab + +- `toggle-fzf-tab`: toggle the state of fzf-tab. This is also a zle widget. + +## Configure + +A common configuration is: + +```zsh +# disable sort when completing `git checkout` +zstyle ':completion:*:git-checkout:*' sort false +# set descriptions format to enable group support +zstyle ':completion:*:descriptions' format '[%d]' +# set list-colors to enable filename colorizing +zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS} +# preview directory's content with exa when completing cd +zstyle ':fzf-tab:complete:cd:*' fzf-preview 'exa -1 --color=always $realpath' +# switch group using `,` and `.` +zstyle ':fzf-tab:*' switch-group ',' '.' +``` + +For more information, please see [Wiki#Configuration](https://github.com/Aloxaf/fzf-tab/wiki/Configuration). + +## Binary module + +By default, fzf-tab uses [zsh-ls-colors](https://github.com/xPMo/zsh-ls-colors) to parse and apply ZLS_COLORS if you have set the `list-colors` tag. + +However, it is a pure zsh script and is slow if you have too many files to colorize. +fzf-tab is shipped with a binary module to speed up this process. You can build it with `build-fzf-tab-module`, then it will be enabled automatically. + +# Difference from other plugins + +fzf-tab doesn't do "complete", it just shows you the results of the default completion system. + +So it works EVERYWHERE (variables, function names, directory stack, in-word completion, etc.). +And most of your configuration for default completion system is still valid. + +# Compatibility with other plugins + +Some plugins may also bind "^I" to their custom widget, like [fzf/shell/completion.zsh](https://github.com/junegunn/fzf/blob/master/shell/completion.zsh) or [ohmyzsh/lib/completion.zsh](https://github.com/ohmyzsh/ohmyzsh/blob/master/lib/completion.zsh#L61-L73). + +By default, fzf-tab will call the widget previously bound to "^I" to get the completion list. So there is no problem in most cases, unless fzf-tab is initialized before a plugin which doesn't handle the previous binding properly. + +So if you find your fzf-tab doesn't work properly, **please make sure it is the last plugin to bind "^I"** (If you don't know what I mean, just put it to the end of your plugin list). + +# Related projects + +- https://github.com/lincheney/fzf-tab-completion (fzf tab completion for zsh, bash and GNU readline apps) diff --git a/dotfiles/system/.zsh/fzf-tab.zsh b/dotfiles/system/.zsh/fzf-tab.zsh new file mode 100644 index 0000000..ed2767d --- /dev/null +++ b/dotfiles/system/.zsh/fzf-tab.zsh @@ -0,0 +1,389 @@ +# temporarily change options +'builtin' 'local' '-a' '_ftb_opts' +[[ ! -o 'aliases' ]] || _ftb_opts+=('aliases') +[[ ! -o 'sh_glob' ]] || _ftb_opts+=('sh_glob') +[[ ! -o 'no_brace_expand' ]] || _ftb_opts+=('no_brace_expand') +'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand' + +# thanks Valodim/zsh-capture-completion +-ftb-compadd() { + # parse all options + local -A apre hpre dscrs _oad + local -a isfile _opts __ expl + zparseopts -E -a _opts P:=apre p:=hpre d:=dscrs X+:=expl O:=_oad A:=_oad D:=_oad f=isfile \ + i: S: s: I: x: r: R: W: F: M+: E: q e Q n U C \ + J:=__ V:=__ a=__ l=__ k=__ o=__ 1=__ 2=__ + + # just delegate and leave if any of -O, -A or -D are given or fzf-tab is not enabled + if (( $#_oad != 0 || ! IN_FZF_TAB )); then + builtin compadd "$@" + return + fi + + # store matches in $__hits and descriptions in $__dscr + local -a __hits __dscr + if (( $#dscrs == 1 )); then + __dscr=( "${(@P)${(v)dscrs}}" ) + fi + builtin compadd -A __hits -D __dscr "$@" + local ret=$? + if (( $#__hits == 0 )); then + return $ret + fi + + # store $curcontext for furthur usage + _ftb_curcontext=${curcontext#:} + + # only store the fist `-X` + expl=$expl[2] + + # keep order of group description + [[ -n $expl ]] && _ftb_groups+=$expl + + # store these values in _ftb_compcap + local -a keys=(apre hpre PREFIX SUFFIX IPREFIX ISUFFIX) + local key expanded __tmp_value=$'<\0>' # placeholder + for key in $keys; do + expanded=${(P)key} + if [[ -n $expanded ]]; then + __tmp_value+=$'\0'$key$'\0'$expanded + fi + done + if [[ -n $expl ]]; then + # store group index + __tmp_value+=$'\0group\0'$_ftb_groups[(ie)$expl] + fi + if [[ -n $isfile ]]; then + # NOTE: need a extra ${} here or ~ expansion won't work + __tmp_value+=$'\0realdir\0'${${(Qe)~${:-$IPREFIX$hpre}}} + fi + _opts+=("${(@kv)apre}" "${(@kv)hpre}" $isfile) + __tmp_value+=$'\0args\0'${(pj:\1:)_opts} + + if (( $+builtins[fzf-tab-compcap-generate] )); then + fzf-tab-compcap-generate __hits __dscr __tmp_value + else + # dscr - the string to show to users + # word - the string to be inserted + local dscr word i + for i in {1..$#__hits}; do + word=$__hits[i] dscr=$__dscr[i] + if [[ -n $dscr ]]; then + dscr=${dscr//$'\n'} + elif [[ -n $word ]]; then + dscr=$word + fi + _ftb_compcap+=$dscr$'\2'$__tmp_value$'\0word\0'$word + done + fi + + # tell zsh that the match is successful + builtin compadd -U -qS '' -R -ftb-remove-space '' +} + +# when insert multi results, a whitespace will be added to each result +# remove left space of our fake result because I can't remove right space +# FIXME: what if the left char is not whitespace: `echo $widgets[\t` +-ftb-remove-space() { + [[ $LBUFFER[-1] == ' ' ]] && LBUFFER[-1]='' +} + +-ftb-zstyle() { + zstyle $1 ":fzf-tab:$_ftb_curcontext" ${@:2} +} + +-ftb-complete() { + local -a _ftb_compcap + local -Ua _ftb_groups + local choice choices _ftb_curcontext continuous_trigger print_query accept_line bs=$'\2' nul=$'\0' + local ret=0 + + # must run with user options; don't move `emulate -L zsh` above this line + (( $+builtins[fzf-tab-compcap-generate] )) && fzf-tab-compcap-generate -i + COLUMNS=500 _ftb__main_complete "$@" || ret=$? + (( $+builtins[fzf-tab-compcap-generate] )) && fzf-tab-compcap-generate -o + + emulate -L zsh -o extended_glob + + local _ftb_query _ftb_complist=() _ftb_headers=() command opts + -ftb-generate-complist # sets `_ftb_complist` + + case $#_ftb_complist in + 0) return 1;; + # NOTE: won't trigger continuous completion + 1) choices=("EXPECT_KEY" "${_ftb_compcap[1]%$bs*}");; + *) + -ftb-generate-query # sets `_ftb_query` + -ftb-generate-header # sets `_ftb_headers` + -ftb-zstyle -s continuous-trigger continuous_trigger || continuous_trigger=/ + -ftb-zstyle -s print-query print_query || print_query=alt-enter + -ftb-zstyle -s accept-line accept_line + + choices=("${(@f)"$(builtin print -rl -- $_ftb_headers $_ftb_complist | -ftb-fzf)"}") + ret=$? + # choices=(query_string expect_key returned_word) + + # insert query string directly + if [[ $choices[2] == $print_query ]] || [[ -n $choices[1] && $#choices == 1 ]] ; then + local -A v=("${(@0)${_ftb_compcap[1]}}") + local -a args=("${(@ps:\1:)v[args]}") + [[ -z $args[1] ]] && args=() # don't pass an empty string + IPREFIX=$v[IPREFIX] PREFIX=$v[PREFIX] SUFFIX=$v[SUFFIX] ISUFFIX=$v[ISUFFIX] + # NOTE: should I use `-U` here?, ../f\tabcd -> ../abcd + builtin compadd "${args[@]:--Q}" -Q -- $choices[1] + + compstate[list]= + compstate[insert]= + if (( $#choices[1] > 0 )); then + compstate[insert]='2' + [[ $RBUFFER == ' '* ]] || compstate[insert]+=' ' + fi + return $ret + fi + choices[1]=() + + choices=("${(@)${(@)choices%$nul*}#*$nul}") + + unset CTXT + ;; + esac + + if [[ -n $choices[1] && $choices[1] == $continuous_trigger ]]; then + typeset -gi _ftb_continue=1 + fi + + if [[ -n $choices[1] && $choices[1] == $accept_line ]]; then + typeset -gi _ftb_accept=1 + fi + choices[1]=() + + for choice in "$choices[@]"; do + local -A v=("${(@0)${_ftb_compcap[(r)${(b)choice}$bs*]#*$bs}}") + local -a args=("${(@ps:\1:)v[args]}") + [[ -z $args[1] ]] && args=() # don't pass an empty string + IPREFIX=$v[IPREFIX] PREFIX=$v[PREFIX] SUFFIX=$v[SUFFIX] ISUFFIX=$v[ISUFFIX] + builtin compadd "${args[@]:--Q}" -Q -- "$v[word]" + done + + compstate[list]= + compstate[insert]= + if (( $#choices == 1 )); then + compstate[insert]='2' + [[ $RBUFFER == ' '* ]] || compstate[insert]+=' ' + elif (( $#choices > 1 )); then + compstate[insert]='all' + fi + return $ret +} + +fzf-tab-debug() { + (( $+_ftb_debug_cnt )) || typeset -gi _ftb_debug_cnt + local tmp=${TMPPREFIX:-/tmp/zsh}-$$-fzf-tab-$(( ++_ftb_debug_cnt )).log + local -i debug_fd=-1 IN_FZF_TAB=1 + { + exec {debug_fd}>&2 2>| $tmp + local -a debug_indent; debug_indent=( '%'{3..20}'(e. .)' ) + local PROMPT4 PS4="${(j::)debug_indent}+%N:%i> " + setopt xtrace + : $ZSH_NAME $ZSH_VERSION + zle .fzf-tab-orig-$_ftb_orig_widget + unsetopt xtrace + if (( debug_fd != -1 )); then + zle -M "fzf-tab-debug: Trace output left in $tmp" + fi + } always { + (( debug_fd != -1 )) && exec 2>&$debug_fd {debug_fd}>&- + } +} + +fzf-tab-complete() { + # this name must be ugly to avoid clashes + local -i _ftb_continue=1 _ftb_accept=0 ret=0 + # hide the cursor until finishing completion, so that users won't see cursor up and down + # NOTE: MacOS Terminal doesn't support civis & cnorm + echoti civis >/dev/tty 2>/dev/null + while (( _ftb_continue )); do + _ftb_continue=0 + local IN_FZF_TAB=1 + { + zle .fzf-tab-orig-$_ftb_orig_widget + ret=$? + } always { + IN_FZF_TAB=0 + } + if (( _ftb_continue )); then + zle .split-undo + zle .reset-prompt + zle -R + zle fzf-tab-dummy + fi + done + echoti cnorm >/dev/tty 2>/dev/null + zle .redisplay + (( _ftb_accept )) && zle .accept-line + return $ret +} + +# this function does nothing, it is used to be wrapped by other plugins like f-sy-h. +# this make it possible to call the wrapper function without causing any other side effects. +fzf-tab-dummy() { } + +zle -N fzf-tab-debug +zle -N fzf-tab-complete +zle -N fzf-tab-dummy + +disable-fzf-tab() { + emulate -L zsh -o extended_glob + (( $+_ftb_orig_widget )) || return 0 + + bindkey '^I' $_ftb_orig_widget + case $_ftb_orig_list_grouped in + 0) zstyle ':completion:*' list-grouped false ;; + 1) zstyle ':completion:*' list-grouped true ;; + 2) zstyle -d ':completion:*' list-grouped ;; + esac + unset _ftb_orig_widget _ftb_orig_list_groupded + + # unhook compadd so that _approximate can work properply + unfunction compadd 2>/dev/null + + functions[_main_complete]=$functions[_ftb__main_complete] + functions[_approximate]=$functions[_ftb__approximate] + + # Don't remove .fzf-tab-orig-$_ftb_orig_widget as we won't be able to reliably + # create it if enable-fzf-tab is called again. +} + +enable-fzf-tab() { + emulate -L zsh -o extended_glob + (( ! $+_ftb_orig_widget )) || disable-fzf-tab + + typeset -g _ftb_orig_widget="${${$(builtin bindkey '^I')##* }:-expand-or-complete}" + if (( ! $+widgets[.fzf-tab-orig-$_ftb_orig_widget] )); then + # Widgets that get replaced by compinit. + local compinit_widgets=( + complete-word + delete-char-or-list + expand-or-complete + expand-or-complete-prefix + list-choices + menu-complete + menu-expand-or-complete + reverse-menu-complete + ) + # Note: We prefix the name of the widget with '.' so that it doesn't get wrapped. + if [[ $widgets[$_ftb_orig_widget] == builtin && + $compinit_widgets[(Ie)$_ftb_orig_widget] != 0 ]]; then + # We are initializing before compinit and being asked to fall back to a completion + # widget that isn't defined yet. Create our own copy of the widget ahead of time. + zle -C .fzf-tab-orig-$_ftb_orig_widget .$_ftb_orig_widget _main_complete + else + # Copy the widget before it's wrapped by zsh-autosuggestions and zsh-syntax-highlighting. + zle -A $_ftb_orig_widget .fzf-tab-orig-$_ftb_orig_widget + fi + fi + + zstyle -t ':completion:*' list-grouped false + typeset -g _ftb_orig_list_grouped=$? + + zstyle ':completion:*' list-grouped false + bindkey '^I' fzf-tab-complete + bindkey '^X.' fzf-tab-debug + + # make sure we can copy them + autoload +X -Uz _main_complete _approximate + + # hook compadd + functions[compadd]=$functions[-ftb-compadd] + + # hook _main_complete to trigger fzf-tab + functions[_ftb__main_complete]=$functions[_main_complete] + function _main_complete() { -ftb-complete "$@" } + + # TODO: This is not a full support, see #47 + # _approximate will also hook compadd + # let it call -ftb-compadd instead of builtin compadd so that fzf-tab can capture result + # make sure _approximate has been loaded. + functions[_ftb__approximate]=$functions[_approximate] + function _approximate() { + # if not called by fzf-tab, don't do anything with compadd + (( ! IN_FZF_TAB )) || unfunction compadd + _ftb__approximate + (( ! IN_FZF_TAB )) || functions[compadd]=$functions[-ftb-compadd] + } +} + +toggle-fzf-tab() { + emulate -L zsh -o extended_glob + if (( $+_ftb_orig_widget )); then + disable-fzf-tab + else + enable-fzf-tab + fi +} + +build-fzf-tab-module() { + local MACOS + if [[ ${OSTYPE} == darwin* ]]; then + MACOS=true + fi + pushd $FZF_TAB_HOME/modules + CPPFLAGS=-I/usr/local/include CFLAGS="-g -Wall -O2" LDFLAGS=-L/usr/local/lib ./configure --disable-gdbm --without-tcsetpgrp ${MACOS:+DL_EXT=bundle} + make -j + popd +} + +zmodload zsh/zutil +zmodload zsh/mapfile +zmodload -F zsh/stat b:zstat + +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" +FZF_TAB_HOME="${0:A:h}" + +source "$FZF_TAB_HOME"/lib/zsh-ls-colors/ls-colors.zsh fzf-tab-lscolors + +typeset -ga _ftb_group_colors=( + $'\x1b[94m' $'\x1b[32m' $'\x1b[33m' $'\x1b[35m' $'\x1b[31m' $'\x1b[38;5;27m' $'\x1b[36m' + $'\x1b[38;5;100m' $'\x1b[38;5;98m' $'\x1b[91m' $'\x1b[38;5;80m' $'\x1b[92m' + $'\x1b[38;5;214m' $'\x1b[38;5;165m' $'\x1b[38;5;124m' $'\x1b[38;5;120m' +) + +# init +() { + emulate -L zsh -o extended_glob + + fpath+=($FZF_TAB_HOME/lib) + + autoload -Uz -- $FZF_TAB_HOME/lib/-#ftb*(:t) + + if (( $+FZF_TAB_COMMAND || $+FZF_TAB_OPTS || $+FZF_TAB_QUERY || $+FZF_TAB_SINGLE_GROUP || $+fzf_tab_preview_init )) \ + || zstyle -m ":fzf-tab:*" command '*' \ + || zstyle -m ":fzf-tab:*" extra-opts '*'; then + print -P "%F{red}%B[fzf-tab] Sorry, your configuration is not supported anymore\n" \ + "See https://github.com/Aloxaf/fzf-tab/pull/132 for more information%f%b" + fi + + if [[ -n $FZF_TAB_HOME/modules/Src/aloxaf/fzftab.(so|bundle)(#qN) ]]; then + module_path+=("$FZF_TAB_HOME/modules/Src") + zmodload aloxaf/fzftab + + if [[ $FZF_TAB_MODULE_VERSION != "0.2.2" ]]; then + zmodload -u aloxaf/fzftab + local rebuild + print -Pn "%F{yellow}fzftab module needs to be rebuild, rebuild now?[Y/n]:%f" + read -q rebuild + if [[ $rebuild == y ]]; then + build-fzf-tab-module + zmodload aloxaf/fzftab + fi + fi + fi +} + +enable-fzf-tab +zle -N toggle-fzf-tab + +# restore options +(( ${#_ftb_opts} )) && setopt ${_ftb_opts[@]} +'builtin' 'unset' '_ftb_opts' diff --git a/dotfiles/system/.zsh/lib/-ftb-colorize b/dotfiles/system/.zsh/lib/-ftb-colorize new file mode 100644 index 0000000..3b1909b --- /dev/null +++ b/dotfiles/system/.zsh/lib/-ftb-colorize @@ -0,0 +1,34 @@ +#!/hint/zsh +emulate -L zsh -o cbases -o octalzeroes + +local REPLY +local -a reply stat lstat + +# fzf-tab-lscolors::match-by $1 lstat follow +zstat -A lstat -L -- $1 +# follow symlink +(( lstat[3] & 0170000 )) && zstat -A stat -- $1 2>/dev/null + +fzf-tab-lscolors::from-mode "$1" "$lstat[3]" $stat[3] +# fall back to name +[[ -z $REPLY ]] && fzf-tab-lscolors::from-name $1 + +# If this is a symlink +if [[ -n $lstat[14] ]]; then + local sym_color=$REPLY + local rsv_color=$REPLY + local rsv=$lstat[14] + # If this is not a broken symlink + if [[ -e $rsv ]]; then + # fzf-tab-lscolors::match-by $rsv stat + zstat -A stat -- $rsv + fzf-tab-lscolors::from-mode $rsv $stat[3] + # fall back to name + [[ -z $REPLY ]] && fzf-tab-lscolors::from-name $rsv + rsv_color=$REPLY + fi + dpre=$'\033[0m\033['$sym_color'm' + dsuf+=$'\033[0m -> \033['$rsv_color'm'$rsv +else + dpre=$'\033[0m\033['$REPLY'm' +fi diff --git a/dotfiles/system/.zsh/lib/-ftb-fzf b/dotfiles/system/.zsh/lib/-ftb-fzf new file mode 100755 index 0000000..19adf04 --- /dev/null +++ b/dotfiles/system/.zsh/lib/-ftb-fzf @@ -0,0 +1,102 @@ +#!/hint/zsh + +local tmp_dir=${TMPPREFIX:-/tmp/zsh}-fzf-tab-$USER +[[ -d $tmp_dir ]] || command mkdir $tmp_dir + +local ftb_preview_init=" +zmodload zsh/mapfile +local -a _ftb_compcap=(\"\${(@f)mapfile[$tmp_dir/compcap.$$]}\") +local -a _ftb_groups=(\"\${(@f)mapfile[$tmp_dir/groups.$$]}\") +local bs=\$'\2' +# get descriptoin +export desc=\${\${\"\$(<{f})\"%\$'\0'*}#*\$'\0'} +# get ctxt for current completion +local -A ctxt=(\"\${(@0)\${_ftb_compcap[(r)\${(b)desc}\$bs*]#*\$bs}}\") +# get group +if (( \$+ctxt[group] )); then + export group=\$_ftb_groups[\$ctxt[group]] +fi +# get original word +export word=\${(Q)ctxt[word]} +# get real path if it is file +if (( \$+ctxt[realdir] )); then + export realpath=\${ctxt[realdir]}\$word +fi +" +local binds=tab:down,btab:up,change:top,ctrl-space:toggle +local fzf_command fzf_flags fzf_preview debug_command tmp switch_group fzf_pad +local ret=0 + +-ftb-zstyle -s fzf-command fzf_command || fzf_command=fzf +-ftb-zstyle -a fzf-bindings tmp && binds+=,${(j:,:)tmp} +-ftb-zstyle -a fzf-flags fzf_flags +-ftb-zstyle -s fzf-preview fzf_preview +-ftb-zstyle -a switch-group switch_group || switch_group=(F1 F2) +-ftb-zstyle -s fzf-pad fzf_pad || fzf_pad=2 + +-ftb-zstyle -a debug-command debug_command && { + ${(eX)debug_command} $fzf_flags + return +} + +print -rl -- $_ftb_compcap > $tmp_dir/compcap.$$ +print -rl -- $_ftb_groups > $tmp_dir/groups.$$ +print -r -- ${ftb_preview_init/{f}/\$1} > $tmp_dir/ftb_preview_init.$$ + +binds=${binds//{_FTB_INIT_}/. $tmp_dir/ftb_preview_init.$$ {f} $'\n'} + +local -i header_lines=$#_ftb_headers +local -i lines=$(( $#_ftb_compcap + fzf_pad + header_lines )) +local reload_command="$commands[zsh] -f $FZF_TAB_HOME/lib/ftb-switch-group $$ $header_lines $tmp_dir" + +# detect if we will use tmux popup +local use_tmux_popup=0 +if [[ $fzf_command == "ftb-tmux-popup" ]]; then + use_tmux_popup=1 +fi + +if (( ! use_tmux_popup )); then + # fzf will cause the current line to refresh, so move the cursor down. + echoti cud1 >/dev/tty + # reset cursor before call fzf + echoti cnorm >/dev/tty 2>/dev/null +fi + +cat > $tmp_dir/completions.$$ + +local dd='gdd' +if (( ${+commands[$dd]} == 0 )) ; then + dd='dd' +fi +if (( ${+commands[$dd]} == 0 )) ; then + dd='true' # nop if dd is not installed +fi + +_ftb_query="${_ftb_query}$(command "$dd" bs=1G count=1 status=none iflag=nonblock < /dev/tty 2>/dev/null)" || true + +$fzf_command \ + --ansi \ + --bind=$binds \ + --bind="${switch_group[1]}:reload($reload_command -1),${switch_group[2]}:reload($reload_command 1)" \ + --color=hl:$(( header_lines == 0 ? 188 : 255 )) \ + --cycle \ + --delimiter='\x00' \ + --expect=$continuous_trigger,$print_query,$accept_line \ + --header-lines=$header_lines \ + --height=${FZF_TMUX_HEIGHT:=$(( lines > LINES / 3 * 2 ? LINES / 3 * 2 : lines ))} \ + --layout=reverse \ + --multi \ + --nth=2,3 \ + --print-query \ + --query=$_ftb_query \ + --tiebreak=begin \ + ${fzf_preview:+--preview=$ftb_preview_init$fzf_preview} \ + $fzf_flags < $tmp_dir/completions.$$ || ret=$? + +if (( ! use_tmux_popup )); then + echoti civis >/dev/tty 2>/dev/null + echoti cuu1 >/dev/tty +fi + +command rm $tmp_dir/*.$$ 2>/dev/null +return $ret diff --git a/dotfiles/system/.zsh/lib/-ftb-generate-complist b/dotfiles/system/.zsh/lib/-ftb-generate-complist new file mode 100644 index 0000000..42dd033 --- /dev/null +++ b/dotfiles/system/.zsh/lib/-ftb-generate-complist @@ -0,0 +1,113 @@ +#!/hint/zsh + +local dsuf dpre k _v filepath first_word show_group default_color prefix bs=$'\b' +local -a list_colors group_colors tcandidates reply match mbegin mend +local -i same_word=1 colorful=0 +local -Ua duplicate_groups=() +local -A word_map=() + +(( $#_ftb_compcap == 0 )) && return + +-ftb-zstyle -s show-group show_group || show_group=full +-ftb-zstyle -s default-color default_color || default_color=$'\x1b[37m' +-ftb-zstyle -s prefix prefix || { + zstyle -m ':completion:*:descriptions' format '*' && prefix='ยท' +} +-ftb-zstyle -a group-colors group_colors || group_colors=($_ftb_group_colors) +zstyle -a ":completion:$_ftb_curcontext" list-colors list_colors + +# init colorize +if (( $+builtins[fzf-tab-candidates-generate] )); then + fzf-tab-candidates-generate -i list_colors +else + local -A namecolors=(${(@s:=:)${(@s.:.)list_colors}:#[[:alpha:]][[:alpha:]]=*}) + local -A modecolors=(${(@Ms:=:)${(@s.:.)list_colors}:#[[:alpha:]][[:alpha:]]=*}) + (( $#namecolors == 0 && $#modecolors == 0 )) && list_colors=() +fi + +if (( $#_ftb_groups == 1 )); then + -ftb-zstyle -m single-group prefix || prefix='' + -ftb-zstyle -m single-group color || group_colors=("$default_color") +fi + +if (( $+builtins[fzf-tab-candidates-generate] )); then + fzf-tab-candidates-generate +else + for k _v in "${(@ps:\2:)_ftb_compcap}"; do + local -A v=("${(@0)_v}") + [[ $v[word] == ${first_word:=$v[word]} ]] || same_word=0 + + # add character and color to describe the type of the files + dsuf='' dpre='' + if (( $+v[realdir] )); then + filepath=$v[realdir]${(Q)v[word]} + if [[ -d $filepath ]]; then + dsuf=/ + fi + # add color and resolve symlink if have list-colors + # detail: http://zsh.sourceforge.net/Doc/Release/Zsh-Modules.html#The-zsh_002fcomplist-Module + if (( $#list_colors )) && [[ -a $filepath || -L $filepath ]]; then + -ftb-colorize $filepath + colorful=1 + elif [[ -L $filepath ]]; then + dsuf=@ + fi + if [[ $options[list_types] == off ]]; then + dsuf='' + fi + fi + + # add color to description if they have group index + if (( $+v[group] )); then + local color=$group_colors[$v[group]] + # add a hidden group index at start of string to keep group order when sorting + # first group index is for builtin sort, sencond is for GNU sort + tcandidates+=$v[group]$'\b'$color$prefix$dpre$'\0'$v[group]$'\b'$k$'\0'$dsuf + else + tcandidates+=$default_color$dpre$'\0'$k$'\0'$dsuf + fi + + # check group with duplicate member + if [[ $show_group == brief ]]; then + if (( $+word_map[$v[word]] && $+v[group] )); then + duplicate_groups+=$v[group] # add this group + duplicate_groups+=$word_map[$v[word]] # add previous group + fi + word_map[$v[word]]=$v[group] + fi + done +fi + +(( same_word )) && tcandidates[2,-1]=() + +# sort and remove sort group or other index +zstyle -T ":completion:$_ftb_curcontext" sort +if (( $? != 1 )); then + if (( colorful )); then + # if enable list_colors, we should skip the first field + if [[ ${commands[sort]:A:t} != (|busybox*) ]]; then + # this is faster but doesn't work if `find` is from busybox + tcandidates=(${(f)"$(command sort -u -t '\0' -k 2 <<< ${(pj:\n:)tcandidates})"}) + else + # slower but portable + tcandidates=(${(@o)${(@)tcandidates:/(#b)([^$'\0']#)$'\0'(*)/$match[2]$'\0'$match[1]}}) + tcandidates=(${(@)tcandidates/(#b)(*)$'\0'([^$'\0']#)/$match[2]$'\0'$match[1]}) + fi + else + tcandidates=("${(@o)tcandidates}") + fi +fi +typeset -gUa _ftb_complist=("${(@)tcandidates//[0-9]#$bs}") + +# hide needless group +if (( $#_ftb_groups )); then + local i to_hide indexs=({1..$#_ftb_groups}) + case $show_group in + brief) to_hide=(${indexs:|duplicate_groups}) ;; + none) to_hide=($indexs) ;; + esac + for i in $to_hide; do + # NOTE: _ftb_groups is unique array + _ftb_groups[i]="__hide__$i" + done +fi diff --git a/dotfiles/system/.zsh/lib/-ftb-generate-header b/dotfiles/system/.zsh/lib/-ftb-generate-header new file mode 100644 index 0000000..a54fee1 --- /dev/null +++ b/dotfiles/system/.zsh/lib/-ftb-generate-header @@ -0,0 +1,35 @@ +#!/hint/zsh + +typeset -ga _ftb_headers=() +local i tmp group_colors +local -i mlen=0 len=0 + +if (( $#_ftb_groups == 1 )) && { ! -ftb-zstyle -m single-group "header" }; then + return +fi + +# calculate the max column width +for i in $_ftb_groups; do + (( $#i > mlen )) && mlen=$#i +done +mlen+=1 + +-ftb-zstyle -a group-colors group_colors || group_colors=($_ftb_group_colors) + +for (( i=1; i<=$#_ftb_groups; i++ )); do + [[ $_ftb_groups[i] == "__hide__"* ]] && continue + + if (( len + $#_ftb_groups[i] > COLUMNS - 5 )); then + _ftb_headers+=$tmp + tmp='' && len=0 + fi + if (( len + mlen > COLUMNS - 5 )); then + # the last column doesn't need padding + _ftb_headers+=$tmp$group_colors[i]$_ftb_groups[i]$'\033[00m' + tmp='' && len=0 + else + tmp+=$group_colors[i]${(r:$mlen:)_ftb_groups[i]}$'\033[00m' + len+=$mlen + fi +done +(( $#tmp )) && _ftb_headers+=$tmp diff --git a/dotfiles/system/.zsh/lib/-ftb-generate-query b/dotfiles/system/.zsh/lib/-ftb-generate-query new file mode 100644 index 0000000..87ebb75 --- /dev/null +++ b/dotfiles/system/.zsh/lib/-ftb-generate-query @@ -0,0 +1,36 @@ +#!/hint/zsh + +local key qtype tmp query_string +typeset -g _ftb_query= +-ftb-zstyle -a query-string query_string || query_string=(prefix input first) +for qtype in $query_string; do + if [[ $qtype == prefix ]]; then + # find the longest common prefix among descriptions + local -a keys=(${_ftb_compcap%$'\2'*}) + tmp=$keys[1] + local MATCH match mbegin mend prefix=(${(s::)tmp}) + for key in ${keys:1}; do + (( $#tmp )) || break + [[ $key == $tmp* ]] && continue + # interpose characters from the current common prefix and $key and see how + # many pairs of equal characters we get at the start of the resulting string + [[ ${(j::)${${(s::)key[1,$#tmp]}:^prefix}} =~ '^(((.)\3)*)' ]] + # truncate common prefix and maintain loop invariant: ${(s::)tmp} == $prefix + tmp[$#MATCH/2+1,-1]="" + prefix[$#MATCH/2+1,-1]=() + done + elif [[ $qtype == input ]]; then + local fv=${_ftb_compcap[1]#*$'\2'} + local -A v=("${(@0)fv}") + tmp=$v[PREFIX] + if (( $RBUFFER[(i)$v[SUFFIX]] != 1 )); then + tmp=${tmp/%$v[SUFFIX]} + fi + tmp=${${tmp#$v[hpre]}#$v[apre]} + fi + if (( $query_string[(I)longest] )); then + (( $#tmp > $#_ftb_query )) && _ftb_query=$tmp + elif [[ -n $tmp ]]; then + _ftb_query=$tmp && break + fi +done diff --git a/dotfiles/system/.zsh/lib/ftb-switch-group b/dotfiles/system/.zsh/lib/ftb-switch-group new file mode 100644 index 0000000..8d06956 --- /dev/null +++ b/dotfiles/system/.zsh/lib/ftb-switch-group @@ -0,0 +1,38 @@ +#!/hint/zsh +emulate -L zsh -o extended_glob + +zmodload zsh/mapfile + +# receive arguments +local pid=$1 header_lines=$2 tmp_dir=$3 offset=$@[-1] + +# read completion list +local -a list=(${(f)mapfile[$tmp_dir/completions.$pid]}) + +# get total group count +if (( $#list > 10000 )); then + local -Ua total=(${(f)"$(print -l ${list:$header_lines} | grep -a -oP '^\x1b\[[0-9;]*m')"}) +else + local -Ua total=(${(M)${list:$header_lines}#$'\x1b['[0-9;]#*m}) +fi + +# get current group index, start from 2 +local current=2 +if [[ -f $tmp_dir/current-group.$pid ]]; then + current=$(( $(<$tmp_dir/current-group.$pid) + offset )) +fi +(( current > $#total )) && current=1 +(( current == 0 )) && current=$#total +echo $current > $tmp_dir/current-group.$pid + +# print headers +if (( header_lines != 0 )); then + print -l ${list[1,header_lines]/${total[current]}/$'\x1b[1m'} +fi + +# print current group +if (( $#list > 10000 )); then + print -l ${list:$header_lines} | grep -a -F "${total[current]}" +else + print -l ${(M)${list:$header_lines}:#${total[current]}*} +fi diff --git a/dotfiles/system/.zsh/lib/ftb-tmux-popup b/dotfiles/system/.zsh/lib/ftb-tmux-popup new file mode 100755 index 0000000..7e74d3c --- /dev/null +++ b/dotfiles/system/.zsh/lib/ftb-tmux-popup @@ -0,0 +1,83 @@ +#!/hint/zsh +# Show results with tmux popup +# Example usage: +# zstyle ':fzf-tab:*' fzf-command ftb-tmux-popup +# zstyle ':fzf-tab:*' popup-pad 0 0 +# It can also be used as a standalone tool, like: +# ls | ftb-tmux-popup +emulate -L zsh -o extended_glob + +# import min +autoload -Uz zmathfunc +zmathfunc + +: ${tmp_dir:=${TMPPREFIX:-/tmp/zsh}-fzf-tab-$USER} + +# fallback to fzf if it is not running in tmux +if (( ! $+TMUX_PANE )); then + fzf $@ + return +fi + +local ret=0 + +local -a fzf_opts=($@) +fzf_opts=(${${fzf_opts/--height*}/--layout*}) + +# get position of cursor and size of window +local -a tmp=($(command tmux display-message -p "#{pane_top} #{cursor_y} #{pane_left} #{cursor_x} #{window_height} #{window_width}")) +local cursor_y=$((tmp[1] + tmp[2])) cursor_x=$((tmp[3] + tmp[4])) window_height=$tmp[5] window_width=$tmp[6] + +# if not called by fzf-tab +if (( ! $+IN_FZF_TAB )); then + [[ -d $tmp_dir ]] || mkdir -p $tmp_dir + cat > $tmp_dir/completions.$$ +fi + +local text REPLY comp_lines comp_length length popup_pad + +zstyle -a ":fzf-tab:$_ftb_curcontext" popup-pad popup_pad || popup_pad=(0 0) + +# get the size of content, note we should remove all ANSI color code +comp_lines=$(( ${#${(f)mapfile[$tmp_dir/completions.$$]}} + $popup_pad[2] )) +if (( comp_lines <= 500 )); then + comp_length=0 + for line in ${(f)mapfile[$tmp_dir/completions.$$]}; do + length=${(m)#${(S)line//$'\x1b['[0-9]#*m}} + (( length >= comp_length )) && comp_length=$length + done +else + # FIXME: can't get the correct width of CJK characters. + comp_length=$(command sed 's/\x1b\[[0-9;]*m//g;s/\x00//g' $tmp_dir/completions.$$ | command awk 'length > max_length { max_length = length; } END { print max_length }') +fi +comp_length=$(( comp_length + $popup_pad[1] )) + +local popup_height popup_y popup_width popup_x + +# calculate the popup height and y position +if (( cursor_y * 2 > window_height )); then + # show above the cursor + popup_height=$(( min(comp_lines + 4, cursor_y) )) + popup_y=$cursor_y +else + # show below the cursor + popup_height=$(( min(comp_lines + 4, window_height - cursor_y) )) + popup_y=$(( cursor_y + popup_height + 1 )) + fzf_opts+=(--layout=reverse) +fi + +# calculate the popup width and x position +popup_width=$(( min(comp_length + 5, window_width) )) +popup_x=$(( cursor_x + popup_width > window_width ? window_width - popup_width : cursor_x )) + +echo -E "$commands[fzf] ${(qq)fzf_opts[@]} < $tmp_dir/completions.$$ > $tmp_dir/result-$$" > $tmp_dir/fzf-$$ +{ + tmux popup -x $popup_x -y $popup_y \ + -w $popup_width -h $popup_height \ + -d $PWD -E ". $tmp_dir/fzf-$$" || ret=$? + echo -E "$(<$tmp_dir/result-$$)" +} always { + command rm $tmp_dir/*-$$ + (( $+IN_FZF_TAB )) || command rm $tmp_dir/completions.$$ +} +return $ret diff --git a/dotfiles/system/.zsh/lib/zsh-ls-colors/LICENSE b/dotfiles/system/.zsh/lib/zsh-ls-colors/LICENSE new file mode 100644 index 0000000..940b4c2 --- /dev/null +++ b/dotfiles/system/.zsh/lib/zsh-ls-colors/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Gamma + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dotfiles/system/.zsh/lib/zsh-ls-colors/README.md b/dotfiles/system/.zsh/lib/zsh-ls-colors/README.md new file mode 100644 index 0000000..7736ce6 --- /dev/null +++ b/dotfiles/system/.zsh/lib/zsh-ls-colors/README.md @@ -0,0 +1,114 @@ +# zsh-ls-colors + +![Demo screenshot](https://raw.githubusercontent.com/xPMo/zsh-ls-colors/image/demo.png) + +A zsh library to use `LS_COLORS` in scripts or other plugins. + +For a simple demo, see the `demo` script in this repo. + +For more advanced usage, +instructions are located at top of the source files for `from-mode` and `from-name`. +If a use case isn't adequately covered, +please open an issue! + +## Using zsh-ls-colors in a plugin + +You can use this as a submodule or a subtree. + +### submodule: + +```sh +# Add (only once) +git submodule add git://github.com/xPMo/zsh-ls-colors.git ls-colors +git commit -m 'Add ls-colors as submodule' + +# Update +cd ls-colors +git fetch +git checkout origin/master +cd .. +git commit ls-colors -m 'Update ls-colors to latest' +``` + +### Subtree: + +```sh +# Initial add +git subtree add --prefix=ls-colors/ --squash -m 'Add ls-colors as a subtree' \ + git://github.com/xPMo/zsh-ls-colors.git master + +# Update +git subtree pull --prefix=ls-colors/ --squash -m 'Update ls-colors to latest' \ + git://github.com/xPMo/zsh-ls-colors.git master + + +# Or, after adding a remote: +git remote add ls-colors git://github.com/xPMo/zsh-ls-colors.git + +# Initial add +git subtree add --prefix=ls-colors/ --squash -m 'Add ls-colors as a subtree' ls-colors master + +# Update +git subtree pull --prefix=ls-colors/ --squash -m 'Update ls-colors to latest' ls-colors master +``` + +### Function namespacing + +Since functions are a public namespace, +this plugin allows you to customize the preifix for your plugin: + +```zsh +# load functions as my-lscolors::{init,match-by,from-name,from-mode} +source ${0:h}/ls-colors/ls-colors.zsh my-lscolors +``` + +### Parameter namespacing + +While indirect parameter expansion exists with `${(P)var}`, +it doesn't play nicely with array parameters. + +There are multiple strategies to prevent unnecessary re-parsing: + +```zsh +# Call once when loading. +# Pollutes global namespace but prevents re-parsing +ls-color::init +``` + +```zsh +# Don't call init at all and only use ::match-by. +# Doesn't pollute global namespace but reparses LS_COLORS on every call +ls-color::match-by $file lstat +``` + +```zsh +# Initialize within a scope with local parameters. +# Best for not polluting global namespace when multiple filenames need to be parsed. +(){ + local -A namecolors modecolors + ls-color::init + + for arg; do + ... + done +} +``` + +```zsh +# Serialize: +typeset -g LS_COLORS_CACHE_FILE=$(mktemp) +(){ + local -A namecolors modecolors + ls-color::init + typeset -p modecolors namecolors >| $LS_COLORS_CACHE_FILE + zcompile $LS_COLORS_CACHE_FILE +} + +my-function(){ + local -A namecolors modecolors + source $LS_COLORS_CACHE_FILE + + ... +} +``` + diff --git a/dotfiles/system/.zsh/lib/zsh-ls-colors/demo b/dotfiles/system/.zsh/lib/zsh-ls-colors/demo new file mode 100755 index 0000000..a5e468d --- /dev/null +++ b/dotfiles/system/.zsh/lib/zsh-ls-colors/demo @@ -0,0 +1,65 @@ +#!/usr/bin/env zsh +# set $0 (ref: zdharma.org/Zsh-100-Commits-Club/Zsh-Plugin-Standard.html#zero-handling) +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" + +# load library functions +source ls-colors.zsh '' + +# to name the functions with a different namespace +# call source with a different argument +#source my-plugin::ls + +# init (sets modecolors and namecolors) +# You have options. Either you can pollute global namespace: +ls-color::init +# Or you can have ::match-by re-parse colors on every call +: # (do nothing) +# Or if you have multiple calls, you can parse colors once for a scope: +(){ + local -A modecolors namecolors + ls-color::init + + for arg; do + ls-color::match-by $arg lstat + : do something else + done +} + + +# colors can also be added for other globs after init as well: +namecolors[*.md]='01' # bold markdown files + +# EXTENDED_GLOB is enabled when matching, so things like this are possible: +namecolors[(#i)(*/|)license(|.*)]='04' # underline LICENSE, or license.txt, or similar + +local file reply +# color each file in the argument list +for file; do + ls-color::match-by $file all + # point to symlink resolution if it exists + print '\e['$reply[1]'m'$file'\e[0m'${reply[2]:+' โ†’ \e['$reply[3]'m'$reply[2]'\e[0m'} +done + +# ======================= +# Alternate manual method: +for file; do + ls-color::match-by $file lstat follow + if [[ $reply[2] ]]; then + # This is a symlink + symlink_color=$reply[1] + # If broken, use link color for destination + resolved_color=$reply[1] + resolved=$reply[2] + if [[ -e $file ]]; then + # Not broken, update destination color + ls-color::match-by $file stat + resolved_color=$reply[1] + fi + print '\e['$symlink_color'm'$file'\e[0m โ†’ \e['$resolved_color'm'$resolved'\e[0m' + else + # This is not a symlink + print '\e['$reply[1]'m'$file'\e[0m' + fi +done + diff --git a/dotfiles/system/.zsh/lib/zsh-ls-colors/ls-colors.zsh b/dotfiles/system/.zsh/lib/zsh-ls-colors/ls-colors.zsh new file mode 100644 index 0000000..276a7bb --- /dev/null +++ b/dotfiles/system/.zsh/lib/zsh-ls-colors/ls-colors.zsh @@ -0,0 +1,186 @@ +#!/usr/bin/env zsh + +# set the prefix for all functions +local pfx=${1:-'ls-color'} + +# {{{ From mode +# Usage: +# $1: filename +# $2: The value of struct stat st_mode +# If empty, modecolors lookup will be skipped +# $3: (If symlink) The value of struct stat st_mode +# for the target of $1's symlink. If unset, +# interpret as a broken link. +# Sets REPLY to the console code +${pfx}::from-mode () { + + emulate -L zsh + setopt cbases octalzeroes extendedglob + + [[ -z $2 ]] && return 1 + + local -i reg=0 + local -a codes + + local -i st_mode=$(($2)) + # See man 7 inode for more info + # file type + case $(( st_mode & 0170000 )) in + $(( 0140000 )) ) codes=( $modecolors[so] ) ;; + $(( 0120000 )) ) # symlink, special handling + if ! (($+3)); then + REPLY=$modecolors[or] + elif [[ $modecolors[ln] = target ]]; then + "$0" "$1" "${@:3}" + else + REPLY=$modecolors[ln] + fi + return + ;; + $(( 0100000 )) ) codes=( ); reg=1 ;; # regular file + $(( 0060000 )) ) codes=( $modecolors[bd] ) ;; + $(( 0040000 )) ) codes=( $modecolors[di] ) ;; + $(( 0020000 )) ) codes=( $modecolors[cd] ) ;; + $(( 0010000 )) ) codes=( $modecolors[pi] ) ;; + esac + + # setuid/setgid/sticky/other-writable + (( st_mode & 04000 )) && codes+=( $modecolors[su] ) + (( st_mode & 02000 )) && codes+=( $modecolors[sg] ) + (( ! reg )) && case $(( st_mode & 01002 )) in + # sticky + $(( 01000 )) ) codes+=( $modecolors[st] ) ;; + # other-writable + $(( 00002 )) ) codes+=( $modecolors[ow] ) ;; + # other-writable and sticky + $(( 01002 )) ) codes+=( $modecolors[tw] ) ;; + esac + + # executable + if (( ! $#codes )); then + (( st_mode & 0111 )) && codes+=( $modecolors[ex] ) + fi + + # return nonzero if no matching code + [[ ${REPLY::=${(j:;:)codes}} ]] +} # }}} +# {{{ From name +# Usage: +# $1: filename +# +# Sets REPLY to the console code +${pfx}::from-name () { + + emulate -L zsh + setopt extendedglob + + # Return non-zero if no keys match + [[ ${REPLY::=$namecolors[(k)$1]} ]] +} # }}} +# {{{ Init +# WARNING: initializes namecolors and modecolors in global scope +${pfx}::init () { + emulate -L zsh + + # Use $1 if provided, otherwise use LS_COLORS + # Use LSCOLORS on BSD + local LS_COLORS=${1:-${LS_COLORS:-$LSCOLORS}} + + # read in LS_COLORS + typeset -gA namecolors=(${(@s:=:)${(@s.:.)LS_COLORS}:#[[:alpha:]][[:alpha:]]=*}) + typeset -gA modecolors=(${(@Ms:=:)${(@s.:.)LS_COLORS}:#[[:alpha:]][[:alpha:]]=*}) +} +# }}} +# {{{ Match by +# Usage: +# $1: filename +# Optional (must be $2): g[lobal]: Use existing stat | lstat in parent scope +# ${@:2}: Append to reply: +# - l[stat] : Look up using lstat (don't follow symlink), if empty match name +# - s[tat] : Look up using stat (do follow symlink), if empty match name +# - n[ame] : Only match name +# - f[ollow]: Get resolution path of symlink +# - L[stat] : Same as above but don't match name +# - S[tat] : Same as above but don't match name +# - a[ll] : If a broken symlink: lstat follow lstat +# : If a symlink : lstat follow stat +# : Otherwise : lstat +# - A[ll] : If a broken symlink: Lstat follow Lstat +# : If a symlink : Lstat follow Stat +# : Otherwise : Lstat +# +# or returns non-zero +${pfx}::match-by () { + emulate -L zsh + setopt extendedglob cbases octalzeroes + + local arg REPLY name=$1 pfx=${0%::match-by} + shift + + # init in local scope if not using global params + if ! [[ -v namecolors && -v modecolors ]]; then + local -A namecolors modecolors + ${pfx}::init + fi + + if [[ ${1:l} = (g|global) ]]; then + shift + else + local -a stat lstat + declare -ga reply=() + fi + + zmodload -F zsh/stat b:zstat + for arg; do + case ${arg[1]:l} in + n|name) + ${pfx}::from-name $name + reply+=("$REPLY") + ;; + l|lstat) + (($#lstat)) || zstat -A lstat -L $name || return 1 + if ((lstat[3] & 0170000 )); then + # follow symlink + (($#stat)) || zstat -A stat $name 2>/dev/null + fi + ${pfx}::from-mode "$name" "$lstat[3]" $stat[3] + if [[ $REPLY || ${2[1]} = L ]]; then + reply+=("$REPLY") + else # fall back to name + "$0" "$name" g n + fi + ;; + s|stat) + (($#stat)) || zstat -A stat $name || return 1 + ${pfx}::from-mode $name $stat[3] + reply+=("$REPLY") + if [[ $REPLY || ${arg[1]} = S ]]; then + reply+=("$REPLY") + else # fall back to name + "$0" "$name" g n + fi + ;; + f|follow) + (($#lstat)) || zstat -A lstat -L $name || return 1 + reply+=("$lstat[14]") + ;; + a|all) + # Match case + "$0" "$name" g ${${${arg[1]%a}:+L}:-l} + # won't append if empty + reply+=($lstat[14]) + # $stat[14] will be empty if not a symlink + if [[ $lstat[14] ]]; then + if [[ -e $name ]]; then + "$0" "$name" g ${${${arg[1]%a}:+S}:-s} + else + reply+=($reply[-2]) + fi + fi + ;; + *) return 2 ;; + esac + done +} +# }}} +# vim: set foldmethod=marker: diff --git a/dotfiles/system/.zsh/modules/.cvsignore b/dotfiles/system/.zsh/modules/.cvsignore new file mode 100644 index 0000000..95cdc58 --- /dev/null +++ b/dotfiles/system/.zsh/modules/.cvsignore @@ -0,0 +1,16 @@ +Makefile +META-FAQ +config.cache +config.h +config.h.in +config.log +config.modules +config.modules.sh +config.status +configure +cscope.out +stamp-h +stamp-h.in +autom4te.cache +*.swp +.git diff --git a/dotfiles/system/.zsh/modules/.distfiles b/dotfiles/system/.zsh/modules/.distfiles new file mode 100644 index 0000000..d618a77 --- /dev/null +++ b/dotfiles/system/.zsh/modules/.distfiles @@ -0,0 +1,4 @@ +DISTFILES_SRC=' + META-FAQ + configure config.h.in stamp-h.in +' diff --git a/dotfiles/system/.zsh/modules/.editorconfig b/dotfiles/system/.zsh/modules/.editorconfig new file mode 100644 index 0000000..808512e --- /dev/null +++ b/dotfiles/system/.zsh/modules/.editorconfig @@ -0,0 +1,15 @@ +# Top-most editorconfig file + +root = true + +[*] +end_of_line = lf +tab_width = 8 +indent_size = 2 +indent_style = tab + +[ChangeLog] +indent_size = 8 + +[*.[ch]] +indent_size = 4 diff --git a/dotfiles/system/.zsh/modules/.gitignore b/dotfiles/system/.zsh/modules/.gitignore new file mode 100644 index 0000000..f420136 --- /dev/null +++ b/dotfiles/system/.zsh/modules/.gitignore @@ -0,0 +1,155 @@ +Makefile +tags +TAGS +*.o +*.o.c +*.orig +*.a +*.so +*.dll +*~ +.*.sw? +\#* + +/META-FAQ +/config.cache +/config.h +/config.log +/config.modules +/config.modules.sh +/config.status +/config.status.lineno +/cscope.out +/stamp-h +/autom4te.cache + +Config/defs.mk + +CVS +.#* + +Doc/help +Doc/help.txt +Doc/help/[_a-zA-Z0-9]* + +Doc/intro.pdf +Doc/intro.ps +Doc/intro.a4.pdf +Doc/intro.a4.ps +Doc/intro.us.pdf +Doc/intro.us.ps +Doc/version.yo +Doc/texi2html.conf +Doc/zsh*.1 +Doc/zsh.texi +Doc/zsh.info* +Doc/*.html +Doc/zsh.aux +Doc/zsh.toc +Doc/zsh.cp +Doc/zsh.cps +Doc/zsh.fn +Doc/zsh.fns +Doc/zsh.ky +Doc/zsh.kys +Doc/zsh.pg +Doc/zsh.pgs +Doc/zsh.vr +Doc/zsh.vrs +Doc/zsh.log +Doc/zsh.dvi +Doc/zsh_a4.dvi +Doc/zsh_us.dvi +Doc/zsh.tp +Doc/zsh.tps +Doc/zsh.idx +Doc/zsh_*.ps +Doc/infodir +Doc/zsh.pdf +Doc/zsh_a4.pdf +Doc/zsh_us.pdf + +Doc/Zsh/modlist.yo +Doc/Zsh/modmenu.yo +Doc/Zsh/manmodmenu.yo + +Etc/FAQ +Etc/FAQ.html + +Src/*.epro +Src/*.export +Src/*.mdh +Src/*.mdh.tmp +Src/*.mdhi +Src/*.mdhs +Src/*.syms +Src/Makemod.in +Src/Makemod +Src/[_a-zA-Z0-9]*.pro +Src/ansi2knr +Src/bltinmods.list +Src/cscope.out +Src/libzsh.so* +Src/modules-bltin +Src/modules.index +Src/modules.index.tmp +Src/modules.stamp +Src/patchlevel.h +Src/sigcount.h +Src/signames.c +Src/signames2.c +Src/stamp-modobjs +Src/stamp-modobjs.tmp +Src/tags +Src/TAGS +Src/version.h +Src/zsh +Src/zsh.exe +Src/zshcurses.h +Src/zshpaths.h +Src/zshterm.h +Src/zshxmods.h + +Src/Builtins/Makefile.in +Src/Builtins/*.export +Src/Builtins/so_locations +Src/Builtins/*.pro +Src/Builtins/*.epro +Src/Builtins/*.syms +Src/Builtins/*.mdh +Src/Builtins/*.mdhi +Src/Builtins/*.mdhs +Src/Builtins/*.mdh.tmp +Src/Builtins/rlimits.h + +Src/Modules/Makefile.in +Src/Modules/*.export +Src/Modules/so_locations +Src/Modules/*.pro +Src/Modules/*.epro +Src/Modules/*.syms +Src/Modules/*.mdh +Src/Modules/*.mdhi +Src/Modules/*.mdhs +Src/Modules/*.mdh.tmp +Src/Modules/errnames.c +Src/Modules/errcount.h +Src/Modules/curses_keys.h + +Src/Zle/Makefile.in +Src/Zle/*.export +Src/Zle/so_locations +Src/Zle/*.pro +Src/Zle/*.epro +Src/Zle/*.syms +Src/Zle/*.mdh +Src/Zle/*.mdhi +Src/Zle/*.mdhs +Src/Zle/*.mdh.tmp +Src/Zle/thingies.list +Src/Zle/widgets.list +Src/Zle/zle_things.h +Src/Zle/zle_widget.h + +Test/*.tmp +/.project diff --git a/dotfiles/system/.zsh/modules/.preconfig b/dotfiles/system/.zsh/modules/.preconfig new file mode 100755 index 0000000..f9729bd --- /dev/null +++ b/dotfiles/system/.zsh/modules/.preconfig @@ -0,0 +1,7 @@ +#! /bin/sh + +set -e + +autoconf +autoheader +echo > stamp-h.in diff --git a/dotfiles/system/.zsh/modules/Config/.cvsignore b/dotfiles/system/.zsh/modules/Config/.cvsignore new file mode 100644 index 0000000..dd265a7 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Config/.cvsignore @@ -0,0 +1,2 @@ +defs.mk +*.swp diff --git a/dotfiles/system/.zsh/modules/Config/.distfiles b/dotfiles/system/.zsh/modules/Config/.distfiles new file mode 100644 index 0000000..f03668b --- /dev/null +++ b/dotfiles/system/.zsh/modules/Config/.distfiles @@ -0,0 +1,2 @@ +DISTFILES_SRC=' +' diff --git a/dotfiles/system/.zsh/modules/Config/aczshoot.m4 b/dotfiles/system/.zsh/modules/Config/aczshoot.m4 new file mode 100644 index 0000000..3b90c6c --- /dev/null +++ b/dotfiles/system/.zsh/modules/Config/aczshoot.m4 @@ -0,0 +1,8 @@ +AC_DEFUN([zsh_OOT], +[ +AC_CHECK_HEADERS(stdarg.h varargs.h termios.h termio.h) + +AC_TYPE_SIGNAL + +AC_DEFINE([ZSH_OOT_MODULE], [], [Out-of-tree module]) +]) diff --git a/dotfiles/system/.zsh/modules/Config/clean.mk b/dotfiles/system/.zsh/modules/Config/clean.mk new file mode 100644 index 0000000..918a84f --- /dev/null +++ b/dotfiles/system/.zsh/modules/Config/clean.mk @@ -0,0 +1,43 @@ +# +# Makefile fragment for cleanup +# +# Copyright (c) 1995-1997 Richard Coleman +# All rights reserved. +# +# Permission is hereby granted, without written agreement and without +# license or royalty fees, to use, copy, modify, and distribute this +# software and to distribute modified versions of this software for any +# purpose, provided that the above copyright notice and the following +# two paragraphs appear in all copies of this software. +# +# In no event shall Richard Coleman or the Zsh Development Group be liable +# to any party for direct, indirect, special, incidental, or consequential +# damages arising out of the use of this software and its documentation, +# even if Richard Coleman and the Zsh Development Group have been advised of +# the possibility of such damage. +# +# Richard Coleman and the Zsh Development Group specifically disclaim any +# warranties, including, but not limited to, the implied warranties of +# merchantability and fitness for a particular purpose. The software +# provided hereunder is on an "as is" basis, and Richard Coleman and the +# Zsh Development Group have no obligation to provide maintenance, +# support, updates, enhancements, or modifications. +# + +mostlyclean: mostlyclean-recursive mostlyclean-here +clean: clean-recursive clean-here +distclean: distclean-recursive distclean-here +realclean: realclean-recursive realclean-here + +mostlyclean-here: +clean-here: mostlyclean-here +distclean-here: clean-here +realclean-here: distclean-here + +mostlyclean-recursive clean-recursive distclean-recursive realclean-recursive: + @subdirs='$(SUBDIRS)'; if test -n "$$subdirs"; then \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$subdirs; do \ + (cd $$subdir && $(MAKE) $(MAKEDEFS) $$target) || exit 1; \ + done; \ + fi diff --git a/dotfiles/system/.zsh/modules/Config/config.mk b/dotfiles/system/.zsh/modules/Config/config.mk new file mode 100644 index 0000000..fd9abf6 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Config/config.mk @@ -0,0 +1,42 @@ +# +# Makefile fragment for building Makefiles +# +# Copyright (c) 1995-1997 Richard Coleman +# All rights reserved. +# +# Permission is hereby granted, without written agreement and without +# license or royalty fees, to use, copy, modify, and distribute this +# software and to distribute modified versions of this software for any +# purpose, provided that the above copyright notice and the following +# two paragraphs appear in all copies of this software. +# +# In no event shall Richard Coleman or the Zsh Development Group be liable +# to any party for direct, indirect, special, incidental, or consequential +# damages arising out of the use of this software and its documentation, +# even if Richard Coleman and the Zsh Development Group have been advised of +# the possibility of such damage. +# +# Richard Coleman and the Zsh Development Group specifically disclaim any +# warranties, including, but not limited to, the implied warranties of +# merchantability and fitness for a particular purpose. The software +# provided hereunder is on an "as is" basis, and Richard Coleman and the +# Zsh Development Group have no obligation to provide maintenance, +# support, updates, enhancements, or modifications. +# + +config: Makefile + @subdirs='$(SUBDIRS)'; for subdir in $$subdirs; do \ + (cd $$subdir && $(MAKE) $(MAKEDEFS) $@) || exit 1; \ + done + +CONFIG_INCS = \ +$(dir_top)/Config/clean.mk $(dir_top)/Config/config.mk \ +$(dir_top)/Config/defs.mk $(dir_top)/Config/version.mk + +Makefile: Makefile.in $(dir_top)/config.status $(CONFIG_INCS) + cd $(dir_top) && \ + $(SHELL) ./config.status `echo $(subdir)/$@ | sed 's%^./%%'` + +$(dir_top)/Config/defs.mk: $(sdir_top)/Config/defs.mk.in $(dir_top)/config.status + cd $(dir_top) && \ + $(SHELL) ./config.status Config/defs.mk diff --git a/dotfiles/system/.zsh/modules/Config/defs.mk.in b/dotfiles/system/.zsh/modules/Config/defs.mk.in new file mode 100644 index 0000000..2bc1748 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Config/defs.mk.in @@ -0,0 +1,114 @@ +# +# Basic Makefile definitions +# +# Copyright (c) 1995-1997 Richard Coleman +# All rights reserved. +# +# Permission is hereby granted, without written agreement and without +# license or royalty fees, to use, copy, modify, and distribute this +# software and to distribute modified versions of this software for any +# purpose, provided that the above copyright notice and the following +# two paragraphs appear in all copies of this software. +# +# In no event shall Richard Coleman or the Zsh Development Group be liable +# to any party for direct, indirect, special, incidental, or consequential +# damages arising out of the use of this software and its documentation, +# even if Richard Coleman and the Zsh Development Group have been advised of +# the possibility of such damage. +# +# Richard Coleman and the Zsh Development Group specifically disclaim any +# warranties, including, but not limited to, the implied warranties of +# merchantability and fitness for a particular purpose. The software +# provided hereunder is on an "as is" basis, and Richard Coleman and the +# Zsh Development Group have no obligation to provide maintenance, +# support, updates, enhancements, or modifications. +# + +# fundamentals +SHELL = /bin/sh +@SET_MAKE@ +EXEEXT = @EXEEXT@ + +# headers +ZSH_CURSES_H = @ZSH_CURSES_H@ +ZSH_TERM_H = @ZSH_TERM_H@ + +# install basename +tzsh = @tzsh@ + +# installation directories +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +MODDIR = $(libdir)/$(tzsh)/$(VERSION) +infodir = @infodir@ +mandir = @mandir@ +datarootdir = @datarootdir@ +datadir = @datadir@ +fndir = @fndir@ +fixed_sitefndir = @fixed_sitefndir@ +sitefndir = @sitefndir@ +scriptdir = @scriptdir@ +sitescriptdir = @sitescriptdir@ +htmldir = @htmldir@ +runhelpdir = @runhelpdir@ +runhelp = @runhelp@ + +# compilation +CC = @CC@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +DEFS = @DEFS@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +EXTRA_LDFLAGS = @EXTRA_LDFLAGS@ +DLCFLAGS = @DLCFLAGS@ +DLLDFLAGS = @DLLDFLAGS@ +LIBLDFLAGS = @LIBLDFLAGS@ +EXELDFLAGS = @EXELDFLAGS@ +LIBS = @LIBS@ +DL_EXT = @DL_EXT@ +DLLD = @DLLD@ +EXPOPT = @EXPOPT@ +IMPOPT = @IMPOPT@ + +# utilities +AWK = @AWK@ +ANSI2KNR = @ANSI2KNR@ +YODL = @YODL@ @YODL_OPTIONS@ +YODL2TXT = @YODL@2txt +YODL2HTML = @YODL@2html + +# install utility +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +# variables used in determining what to install +FUNCTIONS_SUBDIRS = @FUNCTIONS_SUBDIRS@ + +# Additional fpath entries (eg. for vendor specific directories). +additionalfpath = @additionalfpath@ + +# flags passed to recursive makes in subdirectories +MAKEDEFS = \ +prefix='$(prefix)' exec_prefix='$(exec_prefix)' bindir='$(bindir)' \ +libdir='$(libdir)' MODDIR='$(MODDIR)' infodir='$(infodir)' mandir='$(mandir)' \ +datadir='$(datadir)' fndir='$(fndir)' htmldir='$(htmldir)' runhelpdir='$(runhelpdir)' \ +CC='$(CC)' CPPFLAGS='$(CPPFLAGS)' DEFS='$(DEFS)' CFLAGS='$(CFLAGS)' \ +LDFLAGS='$(LDFLAGS)' EXTRA_LDFLAGS='$(EXTRA_LDFLAGS)' \ +DLCFLAGS='$(DLCFLAGS)' DLLDFLAGS='$(DLLDFLAGS)' \ +LIBLDFLAGS='$(LIBLDFLAGS)' EXELDFLAGS='$(EXELDFLAGS)' \ +LIBS='$(LIBS)' DL_EXT='$(DL_EXT)' DLLD='$(DLLD)' \ +AWK='$(AWK)' ANSI2KNR='$(ANSI2KNR)' \ +YODL='$(YODL)' YODL2TXT='$(YODL2TXT)' YODL2HTML='$(YODL2HTML)' \ +FUNCTIONS_INSTALL='$(FUNCTIONS_INSTALL)' tzsh='$(tzsh)' + +# override built-in suffix list +.SUFFIXES: + +# parallel build is not supported (pmake, gmake) +.NOTPARALLEL: + +# parallel build is not supported (dmake) +.NO_PARALLEL: diff --git a/dotfiles/system/.zsh/modules/Config/installfns.sh b/dotfiles/system/.zsh/modules/Config/installfns.sh new file mode 100755 index 0000000..149f359 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Config/installfns.sh @@ -0,0 +1,74 @@ +#!/bin/sh + +fndir=$DESTDIR$fndir +scriptdir=$DESTDIR$scriptdir + +/bin/sh $sdir_top/mkinstalldirs $fndir || exit 1; + +allfuncs="`grep ' functions=.' ${dir_top}/config.modules | + sed -e '/^#/d' -e '/ link=no/d' -e 's/^.* functions=//'`" + +allfuncs="`cd $sdir_top; echo ${allfuncs}`" + +test -d installfnsdir || mkdir installfnsdir + +# We now have a list of files, but we need to use `test -f' to check +# (1) the glob got expanded (2) we are not looking at directories. +for file in $allfuncs; do + if test -f $sdir_top/$file; then + case "$file" in + */CVS/*) continue;; + esac + if test x$FUNCTIONS_SUBDIRS != x && test x$FUNCTIONS_SUBDIRS != xno; then + case "$file" in + Completion/*/*) + subdir="`echo $file | sed -e 's%/[^/]*/[^/]*$%%'`" + instdir="$fndir/$subdir" + ;; + Completion/*) + instdir="$fndir/Completion" + ;; + Scripts/*) + instdir="$scriptdir" + ;; + *) + subdir="`echo $file | sed -e 's%/[^/]*$%%' -e 's%^Functions/%%'`" + instdir="$fndir/$subdir" + ;; + esac + else + case "$file" in + Scripts/*) + instdir="$scriptdir" + ;; + *) + instdir="$fndir" + ;; + esac + fi + basename=`basename $file` + ok=0 + if test -d $instdir || /bin/sh $sdir_top/mkinstalldirs $instdir; then + if sed "s|@runhelpdir@|$runhelpdir|" <$sdir_top/$file \ + >installfnsdir/$basename; then + if $INSTALL_DATA installfnsdir/$basename $instdir; then + ok=1 + fi + fi + fi + case $ok in + 0) + rm -rf installfnsdir + exit 1 + ;; + esac + read line < $sdir_top/$file + case "$line" in + '#!'*) + chmod +x $instdir/`echo $file | sed -e 's%^.*/%%'` + ;; + esac + fi +done + +rm -rf installfnsdir diff --git a/dotfiles/system/.zsh/modules/Config/uninstallfns.sh b/dotfiles/system/.zsh/modules/Config/uninstallfns.sh new file mode 100755 index 0000000..7c22388 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Config/uninstallfns.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +fndir=$DESTDIR$fndir +scriptdir=$DESTDIR$scriptdir + +allfuncs="`grep ' functions=' ${dir_top}/config.modules | + sed -e '/^#/d' -e '/ link=no/d' -e 's/^.* functions=//'`" + +allfuncs="`cd ${sdir_top}; echo ${allfuncs}`" + +case $fndir in + *$VERSION*) + # Version specific function directory, safe to remove completely. + rm -rf $fndir + ;; + *) # The following will only apply with a custom install directory + # with no version information. This is rather undesirable. + # But let's try and do the best we can. + # We now have a list of files, but we need to use `test -f' to check + # (1) the glob got expanded (2) we are not looking at directories. + for file in $allfuncs; do + case $file in + Scripts/*) + ;; + *) + if test -f $sdir_top/$file; then + if test x$FUNCTIONS_SUBDIRS != x -a x$FUNCTIONS_SUBDIRS != xno; then + file=`echo $file | sed -e 's%%^(Functions|Completion)/%'` + rm -f $fndir/$file + else + bfile="`echo $file | sed -e 's%^.*/%%'`" + rm -f "$fndir/$bfile" + fi + fi + ;; + esac + done + ;; +esac + +case $scriptdir in + *$VERSION*) + # $scriptdir might be the parent of fndir. + rm -rf $scriptdir + ;; + *) for file in $allfuncs; do + case $file in + Scripts/*) + if test -f $sdir_top/$file; then + bfile="`echo $file | sed -e 's%^.*/%%'`" + rm -f "$scriptdir/$bfile" + fi + ;; + esac + done + ;; +esac + +exit 0 diff --git a/dotfiles/system/.zsh/modules/Config/version.mk b/dotfiles/system/.zsh/modules/Config/version.mk new file mode 100644 index 0000000..0ebed5e --- /dev/null +++ b/dotfiles/system/.zsh/modules/Config/version.mk @@ -0,0 +1,31 @@ +# +# Makefile fragment for version numbers +# +# Copyright (c) 1995-1997 Richard Coleman +# All rights reserved. +# +# Permission is hereby granted, without written agreement and without +# license or royalty fees, to use, copy, modify, and distribute this +# software and to distribute modified versions of this software for any +# purpose, provided that the above copyright notice and the following +# two paragraphs appear in all copies of this software. +# +# In no event shall Richard Coleman or the Zsh Development Group be liable +# to any party for direct, indirect, special, incidental, or consequential +# damages arising out of the use of this software and its documentation, +# even if Richard Coleman and the Zsh Development Group have been advised of +# the possibility of such damage. +# +# Richard Coleman and the Zsh Development Group specifically disclaim any +# warranties, including, but not limited to, the implied warranties of +# merchantability and fitness for a particular purpose. The software +# provided hereunder is on an "as is" basis, and Richard Coleman and the +# Zsh Development Group have no obligation to provide maintenance, +# support, updates, enhancements, or modifications. +# + +# This must also serve as a shell script, so do not add spaces around the +# `=' signs. + +VERSION=5.3.1-dev-0 +VERSION_DATE='December 22, 2016' diff --git a/dotfiles/system/.zsh/modules/LICENCE b/dotfiles/system/.zsh/modules/LICENCE new file mode 100644 index 0000000..08fcf88 --- /dev/null +++ b/dotfiles/system/.zsh/modules/LICENCE @@ -0,0 +1,37 @@ +Unless otherwise noted in the header of specific files, files in this +distribution have the licence shown below. + +However, note that certain shell functions are licensed under versions +of the GNU General Public Licence. Anyone distributing the shell as a +binary including those files needs to take account of this. Search +shell functions for "Copyright" for specific copyright information. +None of the core functions are affected by this, so those files may +simply be omitted. + +-- + +The Z Shell is copyright (c) 1992-2017 Paul Falstad, Richard Coleman, +Zoltรกn Hidvรฉgi, Andrew Main, Peter Stephenson, Sven Wischnowsky, and +others. All rights reserved. Individual authors, whether or not +specifically named, retain copyright in all changes; in what follows, they +are referred to as `the Zsh Development Group'. This is for convenience +only and this body has no legal status. The Z shell is distributed under +the following licence; any provisions made in individual files take +precedence. + +Permission is hereby granted, without written agreement and without +licence or royalty fees, to use, copy, modify, and distribute this +software and to distribute modified versions of this software for any +purpose, provided that the above copyright notice and the following +two paragraphs appear in all copies of this software. + +In no event shall the Zsh Development Group be liable to any party for +direct, indirect, special, incidental, or consequential damages arising out +of the use of this software and its documentation, even if the Zsh +Development Group have been advised of the possibility of such damage. + +The Zsh Development Group specifically disclaim any warranties, including, +but not limited to, the implied warranties of merchantability and fitness +for a particular purpose. The software provided hereunder is on an "as is" +basis, and the Zsh Development Group have no obligation to provide +maintenance, support, updates, enhancements, or modifications. diff --git a/dotfiles/system/.zsh/modules/Makefile.in b/dotfiles/system/.zsh/modules/Makefile.in new file mode 100644 index 0000000..4f9aa1a --- /dev/null +++ b/dotfiles/system/.zsh/modules/Makefile.in @@ -0,0 +1,87 @@ +# +# Makefile for top level of zsh distribution +# +# Copyright (c) 1995-1997 Richard Coleman +# All rights reserved. +# +# Permission is hereby granted, without written agreement and without +# license or royalty fees, to use, copy, modify, and distribute this +# software and to distribute modified versions of this software for any +# purpose, provided that the above copyright notice and the following +# two paragraphs appear in all copies of this software. +# +# In no event shall Richard Coleman or the Zsh Development Group be liable +# to any party for direct, indirect, special, incidental, or consequential +# damages arising out of the use of this software and its documentation, +# even if Richard Coleman and the Zsh Development Group have been advised of +# the possibility of such damage. +# +# Richard Coleman and the Zsh Development Group specifically disclaim any +# warranties, including, but not limited to, the implied warranties of +# merchantability and fitness for a particular purpose. The software +# provided hereunder is on an "as is" basis, and Richard Coleman and the +# Zsh Development Group have no obligation to provide maintenance, +# support, updates, enhancements, or modifications. +# + +subdir = . +dir_top = . +SUBDIRS = Src + +@VERSION_MK@ + +# source/build directories +VPATH = @srcdir@ +sdir = @srcdir@ +sdir_top = @top_srcdir@ +INSTALL = @INSTALL@ + +@DEFS_MK@ + +# ========== DEPENDENCIES FOR BUILDING ========== + +# default target +all: config.h config.modules + cd Src && $(MAKE) $(MAKEDEFS) $@ + +# prepare module configuration +prep: + @cd Src && $(MAKE) $(MAKEDEFS) $@ + +# ========== DEPENDENCIES FOR CLEANUP ========== + +@CLEAN_MK@ + +distclean-here: + rm -f Makefile config.h config.status config.log config.cache config.modules config.modules.sh stamp-h Config/defs.mk + rm -rf autom4te.cache + +realclean-here: + cd $(sdir) && rm -f config.h.in stamp-h.in configure + +# ========== DEPENDENCIES FOR MAINTENANCE ========== + +@CONFIG_MK@ + +config: config.h + +config.status: $(sdir)/configure + $(SHELL) ./config.status --recheck + +$(sdir)/configure: $(sdir)/aclocal.m4 $(sdir)/aczsh.m4 $(sdir)/configure.ac + cd $(sdir) && autoconf + +config.h: stamp-h +stamp-h: $(sdir)/config.h.in config.status + cd $(dir_top) && $(SHELL) ./config.status config.h $@ + +config.modules: $(sdir)/config.h.in config.status config.modules.sh + cd $(dir_top) && $(SHELL) ./config.status $@ && \ + $(SHELL) ./config.modules.sh + +$(sdir)/config.h.in: $(sdir)/stamp-h.in +$(sdir)/stamp-h.in: $(sdir)/configure + cd $(sdir) && autoheader + echo > $(sdir)/stamp-h.in + +FORCE: diff --git a/dotfiles/system/.zsh/modules/RECOMPILE_REQUEST b/dotfiles/system/.zsh/modules/RECOMPILE_REQUEST new file mode 100644 index 0000000..cbf32b0 --- /dev/null +++ b/dotfiles/system/.zsh/modules/RECOMPILE_REQUEST @@ -0,0 +1 @@ +1580588806 diff --git a/dotfiles/system/.zsh/modules/Src/.cvsignore b/dotfiles/system/.zsh/modules/Src/.cvsignore new file mode 100644 index 0000000..47b3191 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/.cvsignore @@ -0,0 +1,35 @@ +*.dll +*.epro +*.export +*.mdh +*.mdh.tmp +*.mdhi +*.mdhs +*.o +*.o.c +*.so +*.swp +*.syms +Makefile +Makemod.in Makemod +[_a-zA-Z0-9]*.pro +ansi2knr +bltinmods.list +cscope.out +libzsh.so* +modules-bltin +modules.index +modules.index.tmp +modules.stamp +patchlevel.h +sigcount.h +signames.c signames2.c +stamp-modobjs +stamp-modobjs.tmp +tags TAGS +version.h +zsh +zshcurses.h +zshpaths.h +zshterm.h +zshxmods.h diff --git a/dotfiles/system/.zsh/modules/Src/.distfiles b/dotfiles/system/.zsh/modules/Src/.distfiles new file mode 100644 index 0000000..f03668b --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/.distfiles @@ -0,0 +1,2 @@ +DISTFILES_SRC=' +' diff --git a/dotfiles/system/.zsh/modules/Src/.exrc b/dotfiles/system/.zsh/modules/Src/.exrc new file mode 100644 index 0000000..91d0b39 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/.exrc @@ -0,0 +1,2 @@ +set ai +set sw=4 diff --git a/dotfiles/system/.zsh/modules/Src/.indent.pro b/dotfiles/system/.zsh/modules/Src/.indent.pro new file mode 100644 index 0000000..1b41514 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/.indent.pro @@ -0,0 +1,27 @@ +--dont-format-comments +--procnames-start-lines +--no-parameter-indentation +--indent-level4 +--line-comments-indentation4 +--cuddle-else +--brace-indent0 +--dont-star-comments +--blank-lines-after-declarations +--blank-lines-after-procedures +--no-blank-lines-after-commas +--comment-indentation33 +--declaration-comment-column33 +--no-comment-delimiters-on-blank-lines +--continuation-indentation4 +--case-indentation0 +--else-endif-column33 +--no-space-after-casts +--no-blank-before-sizeof +--declaration-indentation0 +--continue-at-parentheses +--no-space-after-function-call-names +--swallow-optional-blank-lines +--dont-space-special-semicolon +--tab-size8 +--line-length132 +--braces-on-if-line diff --git a/dotfiles/system/.zsh/modules/Src/Makefile.in b/dotfiles/system/.zsh/modules/Src/Makefile.in new file mode 100644 index 0000000..2987b64 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/Makefile.in @@ -0,0 +1,164 @@ +# +# Makefile for Src subdirectory +# +# Copyright (c) 1995-1997 Richard Coleman +# All rights reserved. +# +# Permission is hereby granted, without written agreement and without +# license or royalty fees, to use, copy, modify, and distribute this +# software and to distribute modified versions of this software for any +# purpose, provided that the above copyright notice and the following +# two paragraphs appear in all copies of this software. +# +# In no event shall Richard Coleman or the Zsh Development Group be liable +# to any party for direct, indirect, special, incidental, or consequential +# damages arising out of the use of this software and its documentation, +# even if Richard Coleman and the Zsh Development Group have been advised of +# the possibility of such damage. +# +# Richard Coleman and the Zsh Development Group specifically disclaim any +# warranties, including, but not limited to, the implied warranties of +# merchantability and fitness for a particular purpose. The software +# provided hereunder is on an "as is" basis, and Richard Coleman and the +# Zsh Development Group have no obligation to provide maintenance, +# support, updates, enhancements, or modifications. +# + +subdir = Src +dir_top = .. +SUBDIRS = + +@VERSION_MK@ + +# source/build directories +VPATH = @srcdir@ +sdir = @srcdir@ +sdir_top = @top_srcdir@ +INSTALL = @INSTALL@ +LN = @LN@ + +@DEFS_MK@ + +sdir_src = $(sdir) +dir_src = . + +# ========= DEPENDENCIES FOR BUILDING ========== + +LINK = $(CC) $(LDFLAGS) $(EXELDFLAGS) $(EXTRA_LDFLAGS) -o $@ +DLLINK = $(DLLD) $(LDFLAGS) $(LIBLDFLAGS) $(DLLDFLAGS) -o $@ + +all: zsh.export modules +.PHONY: all + +modules: headers +.PHONY: modules + +L = @L@ + +LSTMP = +LLIST = +NSTMP = stamp-modobjs +NLIST = `cat stamp-modobjs` + +LIBZSH = libzsh-$(VERSION).$(DL_EXT) +NIBZSH = +INSTLIB = @INSTLIB@ +UNINSTLIB = @UNINSTLIB@ + +ZSH_EXPORT = $(EXPOPT)zsh.export +ZSH_NXPORT = +ENTRYOBJ = modentry..o +NNTRYOBJ = + +LDRUNPATH = LD_RUN_PATH=$(libdir)/$(tzsh) +NDRUNPATH = + +EXTRAZSHOBJS = @EXTRAZSHOBJS@ + +stamp-modobjs: modobjs + @if cmp -s stamp-modobjs.tmp stamp-modobjs; then \ + rm -f stamp-modobjs.tmp; \ + echo "\`stamp-modobjs' is up to date."; \ + else \ + mv -f stamp-modobjs.tmp stamp-modobjs; \ + echo "Updated \`stamp-modobjs'."; \ + fi + +modobjs: headers rm-modobjs-tmp +.PHONY: modobjs + +rm-modobjs-tmp: + rm -f stamp-modobjs.tmp +.PHONY: rm-modobjs-tmp + +@CONFIG_MK@ + +Makemod: $(CONFIG_INCS) $(dir_top)/config.modules + @case $(sdir_top) in \ + /*) top_srcdir=$(sdir_top) ;; \ + *) top_srcdir=$(subdir)/$(sdir_top) ;; \ + esac; \ + export top_srcdir; \ + echo 'cd $(dir_top) && $(SHELL)' \ + '$$top_srcdir/$(subdir)/mkmakemod.sh $(subdir) Makemod'; \ + cd $(dir_top) && \ + $(SHELL) $$top_srcdir/$(subdir)/mkmakemod.sh $(subdir) Makemod +prep: Makemod + @$(MAKE) -f Makemod $(MAKEDEFS) prep || rm -f Makemod +.PHONY: prep + +FORCE: +.PHONY: FORCE + +# ========== LINKING IN MODULES ========== + +mymods.conf: + @echo Linking with the standard modules. + +modules: $(@E@NTRYOBJ) + +$(ENTRYOBJ): FORCE + @$(MAKE) -f Makemod $(MAKEDEFS) $@ + +# ========== DEPENDENCIES FOR CLEANUP ========== + +# Since module cleanup rules depend on Makemod, they come first. This +# forces module stuff to get cleaned before Makemod itself gets +# deleted. + +mostlyclean-here: + rm -f stamp-modobjs stamp-modobjs.tmp +.PHONY: mostlyclean-here + +clean-here: + rm -f modules.stamp zsh$(EXEEXT) + rm -f libzsh-*.$(DL_EXT) +.PHONY: clean-here + +distclean-here: + rm -f TAGS tags + rm -f Makefile +.PHONY: distclean-here + +mostlyclean: mostlyclean-modules +clean: clean-modules +distclean: distclean-modules +realclean: realclean-modules +.PHONY: mostlyclean clean distclean realclean + +# Don't remake Makemod just to delete things, even if it doesn't exist. +mostlyclean-modules clean-modules distclean-modules realclean-modules: + if test -f Makemod; then \ + $(MAKE) -f Makemod $(MAKEDEFS) `echo $@ | sed 's/-modules//'`; \ + fi; \ + exit 0 +.PHONY: mostlyclean-modules clean-modules distclean-modules \ + realclean-modules + +@CLEAN_MK@ + +# ========== RECURSIVE MAKES ========== + +modobjs modules headers proto zsh.export: Makemod + @$(MAKE) -f Makemod $(MAKEDEFS) $@ +.PHONY: headers proto diff --git a/dotfiles/system/.zsh/modules/Src/Makemod.in.in b/dotfiles/system/.zsh/modules/Src/Makemod.in.in new file mode 100644 index 0000000..ea0cdc3 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/Makemod.in.in @@ -0,0 +1,192 @@ +# +# Makemod.in.in +# +# Copyright (c) 1995-1997 Richard Coleman +# All rights reserved. +# +# Permission is hereby granted, without written agreement and without +# license or royalty fees, to use, copy, modify, and distribute this +# software and to distribute modified versions of this software for any +# purpose, provided that the above copyright notice and the following +# two paragraphs appear in all copies of this software. +# +# In no event shall Richard Coleman or the Zsh Development Group be liable +# to any party for direct, indirect, special, incidental, or consequential +# damages arising out of the use of this software and its documentation, +# even if Richard Coleman and the Zsh Development Group have been advised of +# the possibility of such damage. +# +# Richard Coleman and the Zsh Development Group specifically disclaim any +# warranties, including, but not limited to, the implied warranties of +# merchantability and fitness for a particular purpose. The software +# provided hereunder is on an "as is" basis, and Richard Coleman and the +# Zsh Development Group have no obligation to provide maintenance, +# support, updates, enhancements, or modifications. +# + +# ========== OVERRIDABLE VARIABLES ========== + +# subdir is done by mkmakemod.sh +# dir_top is done by mkmakemod.sh +# SUBDIRS is done by mkmakemod.sh + +@VERSION_MK@ + +# source/build directories +VPATH = @srcdir@ +sdir = @srcdir@ +sdir_top = @top_srcdir@ +INSTALL = @INSTALL@ + +@DEFS_MK@ + +sdir_src = $(sdir_top)/Src +dir_src = $(dir_top)/Src + +# ========== COMPILATION RULES ========== + +DNCFLAGS = + +COMPILE = $(CC) -c -I. -I$(dir_top)/Src -I$(sdir_top)/Src -I$(sdir_top)/Src/Zle -I$(sdir) $(CPPFLAGS) $(DEFS) $(CFLAGS) $(D@L@CFLAGS) +DLCOMPILE = $(CC) -c -I. -I$(dir_top)/Src -I$(sdir_top)/Src -I$(sdir_top)/Src/Zle -I$(sdir) $(CPPFLAGS) $(DEFS) -DMODULE $(CFLAGS) $(DLCFLAGS) +LINK = $(CC) $(LDFLAGS) $(EXELDFLAGS) $(EXTRA_LDFLAGS) -o $@ +DLLINK = $(DLLD) $(LDFLAGS) $(LIBLDFLAGS) $(DLLDFLAGS) -o $@ + +KNR_OBJ=.o +KNROBJ=._foo_ + +ANSIOBJ=.o +ANSI_OBJ=._foo_ + +.SUFFIXES: .c .$(DL_EXT) ..o .._foo_ .o ._foo_ .syms .pro .epro + +.c$(ANSI@U@OBJ): + $(COMPILE) -o $@ $< + @rm -f $(dir_src)/stamp-modobjs + +.c$(KNR@U@OBJ): + @ANSI2KNR@ $< > $@.c + $(COMPILE) -o $@ $@.c + rm -f $@.c + @rm -f $(dir_src)/stamp-modobjs + +.c.$(ANSI@U@OBJ): + $(DLCOMPILE) -o $@ $< + +.c.$(KNR@U@OBJ): + @ANSI2KNR@ $< > $@.c + $(DLCOMPILE) -o $@ $@.c + rm -f $@.c + +.c.syms: + $(AWK) -f $(sdir_src)/makepro.awk $< $(subdir) > $@ + +.syms.epro: + (echo '/* Generated automatically */'; sed -n '/^E/{s/^E//;p;}' < $<) \ + > $@ + (echo '/* Generated automatically */'; sed -n '/^L/{s/^L//;p;}' < $<) \ + > `echo $@ | sed 's/\.epro$$/.pro/'` + +PROTODEPS = $(sdir_src)/makepro.awk + +# ========== DEPENDENCIES FOR BUILDING ========== + +all: modobjs modules +.PHONY: all + +modobjs: $(MODOBJS) +modules: $(MODULES) +headers: $(MDHS) +proto: $(PROTOS) +.PHONY: modobjs modules headers proto + +prep: + @case $(sdir_top) in \ + /*) top_srcdir=$(sdir_top) ;; \ + *) top_srcdir=$(subdir)/$(sdir_top) ;; \ + esac; \ + export top_srcdir; \ + cd $(dir_top) || exit 1; \ + subdirs='$(SUBDIRS)'; \ + for subdir in $$subdirs; do \ + dir=$(subdir)/$$subdir; \ + test -d $$dir || mkdir $$dir; \ + $(SHELL) $$top_srcdir/Src/mkmakemod.sh $$dir Makefile || exit 1; \ + ( cd $$dir && $(MAKE) $(MAKEDEFS) $@ ) || exit 1; \ + done +.PHONY: prep + +headers: $(dir_src)/modules.stamp +$(dir_src)/modules.stamp: $(MDDS) + $(MAKE) -f $(makefile) $(MAKEDEFS) prep + echo 'timestamp for *.mdd files' > $@ +.PHONY: headers + +FORCE: +.PHONY: FORCE + +# ========== DEPENDENCIES FOR INSTALLING ========== + +install: install.bin install.modules +uninstall: uninstall.bin uninstall.modules +.PHONY: install uninstall + +install.bin: install.bin-here +uninstall.bin: uninstall.bin-here +install.modules: install.modules-here +uninstall.modules: uninstall.modules-here +.PHONY: install.bin uninstall.bin install.modules uninstall.modules + +install.bin-here uninstall.bin-here: +install.modules-here uninstall.modules-here: +.PHONY: install.bin-here install.modules-here + +# ========== DEPENDENCIES FOR CLEANUP ========== + +@CLEAN_MK@ + +mostlyclean-here: + rm -f *.o *.export *.$(DL_EXT) +.PHONY: mostlyclean-here + +clean-here: + rm -f *.o.c *.syms *.pro *.epro *.mdh *.mdhi *.mdhs *.mdh.tmp +.PHONY: clean-here + +distclean-here: + rm -f $(makefile) $(makefile).in +.PHONY: distclean-here + +# ========== RECURSIVE MAKES ========== + +install.bin uninstall.bin install.modules uninstall.modules \ +modobjs modules headers proto: + @subdirs='$(SUBDIRS)'; for subdir in $$subdirs; do \ + ( cd $$subdir && $(MAKE) $(MAKEDEFS) $@ ) || exit 1; \ + done + +# ========== DEPENDENCIES FOR MAINTENANCE ========== + +$(makefile): $(makefile).in $(dir_top)/config.status + @case $(sdir_top) in \ + /*) top_srcdir=$(sdir_top) ;; \ + *) top_srcdir=$(subdir)/$(sdir_top) ;; \ + esac; \ + export top_srcdir; \ + echo 'cd $(dir_top) && $(SHELL)' \ + '$$top_srcdir/Src/mkmakemod.sh -m $(subdir) $(makefile)'; \ + cd $(dir_top) && \ + $(SHELL) $$top_srcdir/Src/mkmakemod.sh -m $(subdir) $(makefile) + +$(makefile).in: $(sdir_src)/mkmakemod.sh $(sdir_src)/Makemod.in.in $(MDDS) \ + $(dir_top)/config.modules + @case $(sdir_top) in \ + /*) top_srcdir=$(sdir_top) ;; \ + *) top_srcdir=$(subdir)/$(sdir_top) ;; \ + esac; \ + export top_srcdir; \ + echo 'cd $(dir_top) && $(SHELL)' \ + '$$top_srcdir/Src/mkmakemod.sh -i $(subdir) $(makefile)'; \ + cd $(dir_top) && \ + $(SHELL) $$top_srcdir/Src/mkmakemod.sh -i $(subdir) $(makefile) + diff --git a/dotfiles/system/.zsh/modules/Src/aloxaf/.cvsignore b/dotfiles/system/.zsh/modules/Src/aloxaf/.cvsignore new file mode 100644 index 0000000..f72db84 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/aloxaf/.cvsignore @@ -0,0 +1,18 @@ +Makefile +Makefile.in +*.export +so_locations +*.pro +*.epro +*.syms +*.o +*.o.c +*.so +*.mdh +*.mdhi +*.mdhs +*.mdh.tmp +*.swp +errnames.c errcount.h +*.dll +curses_keys.h diff --git a/dotfiles/system/.zsh/modules/Src/aloxaf/.distfiles b/dotfiles/system/.zsh/modules/Src/aloxaf/.distfiles new file mode 100644 index 0000000..f03668b --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/aloxaf/.distfiles @@ -0,0 +1,2 @@ +DISTFILES_SRC=' +' diff --git a/dotfiles/system/.zsh/modules/Src/aloxaf/.exrc b/dotfiles/system/.zsh/modules/Src/aloxaf/.exrc new file mode 100644 index 0000000..91d0b39 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/aloxaf/.exrc @@ -0,0 +1,2 @@ +set ai +set sw=4 diff --git a/dotfiles/system/.zsh/modules/Src/aloxaf/.gitignore b/dotfiles/system/.zsh/modules/Src/aloxaf/.gitignore new file mode 100644 index 0000000..92f708e --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/aloxaf/.gitignore @@ -0,0 +1,8 @@ +Makefile.in +*.epro +*.export +*.mdh +*.mdhi +*.mdhs +*.pro +*.syms diff --git a/dotfiles/system/.zsh/modules/Src/aloxaf/fzftab.c b/dotfiles/system/.zsh/modules/Src/aloxaf/fzftab.c new file mode 100644 index 0000000..60b6330 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/aloxaf/fzftab.c @@ -0,0 +1,546 @@ +#include "fzftab.mdh" +#include "fzftab.pro" +#include +#include +#include +#include +#include + +const char* get_color(char* file, const struct stat* sb); +const char* get_suffix(char* file, const struct stat* sb); +const char* colorize_from_mode(char* file, const struct stat* sb); +const char* colorize_from_name(char* file); +char** fzf_tab_colorize(char* file); +int compile_patterns(char* nam, char** list_colors); +char* ftb_strcat(char* dst, int n, ...); + +/* Indixes into the terminal string arrays. */ +#define COL_NO 0 +#define COL_FI 1 +#define COL_DI 2 +#define COL_LN 3 +#define COL_PI 4 +#define COL_SO 5 +#define COL_BD 6 +#define COL_CD 7 +#define COL_OR 8 +#define COL_MI 9 +#define COL_SU 10 +#define COL_SG 11 +#define COL_TW 12 +#define COL_OW 13 +#define COL_ST 14 +#define COL_EX 15 +#define COL_LC 16 +#define COL_RC 17 +#define COL_EC 18 +#define COL_TC 19 +#define COL_SP 20 +#define COL_MA 21 +#define COL_HI 22 +#define COL_DU 23 +#define COL_SA 24 + +#define NUM_COLS 25 + +/* Names of the terminal strings. */ +static char* colnames[] = { "no", "fi", "di", "ln", "pi", "so", "bd", "cd", "or", "mi", "su", "sg", + "tw", "ow", "st", "ex", "lc", "rc", "ec", "tc", "sp", "ma", "hi", "du", "sa", NULL }; + +/* Default values. */ +static char* defcols[] + = { "0", "0", "1;31", "1;36", "33", "1;35", "1;33", "1;33", NULL, NULL, "37;41", "30;43", + "30;42", "34;42", "37;44", "1;32", "\033[", "m", NULL, "0", "0", "7", NULL, NULL, "0" }; + +static char* fzf_tab_module_version; + +struct pattern { + Patprog pat; + char color[50]; +}; + +static int opt_list_type = OPT_INVALID; +static int pat_cnt = 0; +static struct pattern* name_color = NULL; +static char mode_color[NUM_COLS][20]; + +// TODO: use ZLS_COLORS ? +int compile_patterns(char* nam, char** list_colors) +{ + // clean old name_color and set pat_cnt = 0 + if (pat_cnt != 0) { + while (--pat_cnt) { + freepatprog(name_color[pat_cnt].pat); + } + free(name_color); + } + // initialize mode_color with default value + for (int i = 0; i < NUM_COLS; i++) { + if (defcols[i]) { + strcpy(mode_color[i], defcols[i]); + } + } + // empty array, just return + if (list_colors == NULL) { + return 0; + } + + // how many pattens? + while (list_colors[pat_cnt] != NULL) { + pat_cnt++; + } + name_color = zshcalloc(pat_cnt * sizeof(struct pattern)); + + for (int i = 0; i < pat_cnt; i++) { + char* pat = ztrdup(list_colors[i]); + char* color = strrchr(pat, '='); + if (color == NULL) + continue; + *color = '\0'; + tokenize(pat); + remnulargs(pat); + + // mode=color + bool skip = false; + for (int j = 0; j < NUM_COLS; j++) { + if (strpfx(colnames[j], list_colors[i])) { + strcpy(mode_color[j], color + 1); + name_color[i].pat = NULL; + skip = true; + } + } + if (skip) { + continue; + } + + // name=color + if (!(name_color[i].pat = patcompile(pat, PAT_ZDUP, NULL))) { + zwarnnam(nam, "bad pattern: %s", list_colors[i]); + return 1; + } + + strcpy(name_color[i].color, color + 1); + free(pat); + } + return 0; +} + +// TODO: use zsh mod_export function `file_type` ? +const char* get_suffix(char* file, const struct stat* sb) +{ + struct stat sb2; + mode_t filemode = sb->st_mode; + + if (S_ISBLK(filemode)) + return "#"; + else if (S_ISCHR(filemode)) + return "%"; + else if (S_ISDIR(filemode)) + return "/"; + else if (S_ISFIFO(filemode)) + return "|"; + else if (S_ISLNK(filemode)) + if (strpfx(mode_color[COL_LN], "target")) { + if (stat(file, &sb2) == -1) { + return "@"; + } + return get_suffix(file, &sb2); + } else { + return "@"; + } + else if (S_ISREG(filemode)) + return (filemode & S_IXUGO) ? "*" : ""; + else if (S_ISSOCK(filemode)) + return "="; + else + return "?"; +} + +const char* get_color(char* file, const struct stat* sb) +{ + // no list-colors, return empty color + if (pat_cnt == 0) { + return ""; + } + const char* ret; + if ((ret = colorize_from_mode(file, sb)) || (ret = colorize_from_name(file))) { + return ret; + } + return mode_color[COL_FI]; +} + +const char* colorize_from_name(char* file) +{ + for (int i = 0; i < pat_cnt; i++) { + if (name_color && name_color[i].pat && pattry(name_color[i].pat, file)) { + return name_color[i].color; + } + } + return NULL; +} + +const char* colorize_from_mode(char* file, const struct stat* sb) +{ + struct stat sb2; + + switch (sb->st_mode & S_IFMT) { + case S_IFDIR: + return mode_color[COL_DI]; + case S_IFLNK: { + if (strpfx(mode_color[COL_LN], "target")) { + if (stat(file, &sb2) == -1) { + return mode_color[COL_OR]; + } + return get_color(file, &sb2); + } + } + case S_IFIFO: + return mode_color[COL_PI]; + case S_IFSOCK: + return mode_color[COL_SO]; + case S_IFBLK: + return mode_color[COL_BD]; + case S_IFCHR: + return mode_color[COL_CD]; + default: + break; + } + + if (sb->st_mode & S_ISUID) { + return mode_color[COL_SU]; + } else if (sb->st_mode & S_ISGID) { + return mode_color[COL_SG]; + // tw ow st ?? + } else if (sb->st_mode & S_IXUSR) { + return mode_color[COL_EX]; + } + + /* Check for suffix alias */ + size_t len = strlen(file); + /* shortest valid suffix format is a.b */ + if (len > 2) { + const char* suf = file + len - 1; + while (suf > file + 1) { + if (suf[-1] == '.') { + if (sufaliastab->getnode(sufaliastab, suf)) { + return mode_color[COL_SA]; + } + break; + } + suf--; + } + } + + return NULL; +} + +struct { + char** array; + size_t len; + size_t size; +} ftb_compcap; + +// Usage: +// initialize fzf-tab-generate-compcap -i +// output to _ftb_compcap fzf-tab-generate-compcap -o +// add a entry fzf-tab-generate-compcap word desc opts +static int bin_fzf_tab_compcap_generate(char* nam, char** args, Options ops, UNUSED(int func)) +{ + if (OPT_ISSET(ops, 'o')) { + // write final result to _ftb_compcap + setaparam("_ftb_compcap", ftb_compcap.array); + ftb_compcap.array = NULL; + return 0; + } else if (OPT_ISSET(ops, 'i')) { + // init + if (ftb_compcap.array) + freearray(ftb_compcap.array); + ftb_compcap.array = zshcalloc(1024 * sizeof(char*)); + ftb_compcap.len = 0, ftb_compcap.size = 1024; + return 0; + } + if (ftb_compcap.array == NULL) { + zwarnnam(nam, "please initialize it first"); + return 1; + } + + // get paramaters + char **words = getaparam(args[0]), **dscrs = getaparam(args[1]), *opts = getsparam(args[2]); + if (!words || !dscrs || !opts) { + zwarnnam(nam, "wrong argument"); + return 1; + } + + char *bs = metafy("\2", 1, META_DUP), *nul = metafy("\0word\0", 6, META_DUP); + size_t dscrs_cnt = arrlen(dscrs); + + for (int i = 0; words[i]; i++) { + // TODO: replace '\n' + char* buffer = zshcalloc(256 * sizeof(char)); + char* dscr = i < dscrs_cnt ? dscrs[i] : words[i]; + + buffer = ftb_strcat(buffer, 5, dscr, bs, opts, nul, words[i]); + ftb_compcap.array[ftb_compcap.len++] = buffer; + + if (ftb_compcap.len == ftb_compcap.size) { + ftb_compcap.array + = zrealloc(ftb_compcap.array, (1024 + ftb_compcap.size) * sizeof(char*)); + ftb_compcap.size += 1024; + memset(ftb_compcap.array + ftb_compcap.size - 1024, 0, 1024 * sizeof(char*)); + } + } + + zsfree(bs); + zsfree(nul); + + return 0; +} + +// cat n string, return the pointer to the new string +// `size` is the size of dst +// dst will be reallocated if is not big enough +char* ftb_strcat(char* dst, int n, ...) +{ + va_list valist; + va_start(valist, n); + + char* final = dst; +#ifdef __linux + size_t size = malloc_usable_size(dst); +#elif __APPLE__ + size_t size = malloc_size(dst); +#endif + size_t max_len = size - 1; + + for (int i = 0, idx = 0; i < n; i++) { + char* src = va_arg(valist, char*); + for (; *src != '\0'; dst++, src++, idx++) { + if (idx == max_len) { + size += size / 2; + final = zrealloc(final, size); + max_len = size - 1; + dst = &final[idx]; + } + *dst = *src; + } + } + *dst = '\0'; + va_end(valist); + + return final; +} + +// accept an +char** fzf_tab_colorize(char* file) +{ + // TODO: can avoid so many zalloc here? + file = unmeta(file); + + struct stat sb; + if (lstat(file, &sb) == -1) { + return NULL; + } + + const char* suffix = ""; + if (isset(opt_list_type)) { + suffix = get_suffix(file, &sb); + } + const char* color = get_color(file, &sb); + + char* symlink = ""; + const char* symcolor = ""; + if ((sb.st_mode & S_IFMT) == S_IFLNK) { + symlink = zalloc(PATH_MAX); + int end = readlink(file, symlink, PATH_MAX); + symlink[end] = '\0'; + if (stat(file, &sb) == -1) { + symcolor = mode_color[COL_OR]; + } else { + char tmp[PATH_MAX]; + realpath(file, tmp); + symcolor = get_color(file, &sb); + } + } + + char** reply = zshcalloc((4 + 1) * sizeof(char*)); + + if (strlen(color) != 0) { + reply[0] = zalloc(128); + reply[1] = zalloc(128); + sprintf(reply[0], "%s%s%s", mode_color[COL_LC], color, mode_color[COL_RC]); + sprintf(reply[1], "%s%s%s", mode_color[COL_LC], "0", mode_color[COL_RC]); + } else { + reply[0] = ztrdup(""); + reply[1] = ztrdup(""); + } + + reply[2] = ztrdup(suffix); + + if (symlink[0] != '\0') { + reply[3] = zalloc(PATH_MAX); + sprintf(reply[3], "%s%s%s%s%s%s%s", mode_color[COL_LC], symcolor, mode_color[COL_RC], + symlink, mode_color[COL_LC], "0", mode_color[COL_RC]); + free(symlink); + } else { + reply[3] = ztrdup(""); + } + for (int i = 0; i < 4; i++) { + reply[i] = metafy(reply[i], -1, META_REALLOC); + } + + return reply; +} + +static int bin_fzf_tab_candidates_generate(char* nam, char** args, Options ops, UNUSED(int func)) +{ + if (OPT_ISSET(ops, 'i')) { + // compile list_colors pattern + if (*args == NULL) { + zwarnnam(nam, "please specify an array"); + return 1; + } else { + char** array = getaparam(*args); + return compile_patterns(nam, array); + } + } + + char **ftb_compcap = getaparam("_ftb_compcap"), **group_colors = getaparam("group_colors"), + *default_color = getsparam("default_color"), *prefix = getsparam("prefix"); + + size_t ftb_compcap_len = arrlen(ftb_compcap); + + char *bs = metafy("\b", 1, META_DUP), *nul = metafy("\0", 1, META_DUP), + *soh = metafy("\2", 1, META_DUP); + + char **candidates = zshcalloc(sizeof(char*) * (ftb_compcap_len + 1)), + *filepath = zshcalloc(PATH_MAX * sizeof(char)), *dpre = zshcalloc(128), + *dsuf = zshcalloc(128); + + char* first_word = zshcalloc(512 * sizeof(char)); + int same_word = 1; + + for (int i = 0; i < ftb_compcap_len; i++) { + char *word = "", *group = NULL, *realdir = NULL; + strcpy(dpre, ""); + strcpy(dsuf, ""); + + // extract all the variables what we need + char** compcap = sepsplit(ftb_compcap[i], soh, 1, 0); + char* desc = compcap[0]; + char** info = sepsplit(compcap[1], nul, 1, 0); + + for (int j = 0; info[j]; j += 2) { + if (!strcmp(info[j], "word")) { + word = info[j + 1]; + // unquote word + parse_subst_string(word); + remnulargs(word); + untokenize(word); + } else if (!strcmp(info[j], "group")) { + group = info[j + 1]; + } else if (!strcmp(info[j], "realdir")) { + realdir = info[j + 1]; + } + } + + // check if all the words are the same + if (i == 0) { + first_word = ftb_strcat(first_word, 1, word); + } else if (same_word && strcmp(word, first_word) != 0) { + same_word = 0; + } + + // add character and color to describe the type of the files + if (realdir) { + filepath = ftb_strcat(filepath, 2, realdir, word); + char** reply = fzf_tab_colorize(filepath); + if (reply != NULL) { + dpre = ftb_strcat(dpre, 2, reply[1], reply[0]); + if (reply[3][0] != '\0') { + dsuf = ftb_strcat(dsuf, 4, reply[1], reply[2], " -> ", reply[3]); + } else { + dsuf = ftb_strcat(dsuf, 2, reply[1], reply[2]); + } + if (dpre[0] != '\0') { + setiparam("colorful", 1); + } + freearray(reply); + } + } + + char* result = zshcalloc(256 * sizeof(char)); + + // add color to description if they have group index + if (group) { + // use strtol ? + char* color = group_colors[atoi(group) - 1]; + // add prefix + result = ftb_strcat( + result, 11, group, bs, color, prefix, dpre, nul, group, bs, desc, nul, dsuf); + } else { + result = ftb_strcat(result, 6, default_color, dpre, nul, desc, nul, dsuf); + } + // quotedzputs(result, stdout); + // putchar('\n'); + candidates[i] = result; + + freearray(info); + freearray(compcap); + } + + setaparam("tcandidates", candidates); + setiparam("same_word", same_word); + zsfree(dpre); + zsfree(dsuf); + zsfree(filepath); + zsfree(first_word); + + return 0; +} + +static struct builtin bintab[] = { + BUILTIN("fzf-tab-compcap-generate", 0, bin_fzf_tab_compcap_generate, 0, -1, 0, "io", NULL), + BUILTIN("fzf-tab-candidates-generate", 0, bin_fzf_tab_candidates_generate, 0, -1, 0, "i", NULL), +}; + +static struct paramdef patab[] = { + STRPARAMDEF("FZF_TAB_MODULE_VERSION", &fzf_tab_module_version), +}; + +// clang-format off +static struct features module_features = { + bintab, sizeof(bintab) / sizeof(*bintab), + NULL, 0, + NULL, 0, + patab, sizeof(patab) / sizeof(*patab), + 0, +}; +// clang-format on + +int setup_(UNUSED(Module m)) { return 0; } + +int features_(Module m, char*** features) +{ + *features = featuresarray(m, &module_features); + return 0; +} + +int enables_(Module m, int** enables) { return handlefeatures(m, &module_features, enables); } + +int boot_(UNUSED(Module m)) +{ + fzf_tab_module_version = ztrdup("0.2.2"); + // different zsh version may have different value of list_type's index + // so query it dynamically + opt_list_type = optlookup("list_types"); + return 0; +} + +int cleanup_(UNUSED(Module m)) { return setfeatureenables(m, &module_features, NULL); } + +int finish_(UNUSED(Module m)) +{ + printf("fzf-tab module unloaded.\n"); + fflush(stdout); + return 0; +} diff --git a/dotfiles/system/.zsh/modules/Src/aloxaf/fzftab.mdd b/dotfiles/system/.zsh/modules/Src/aloxaf/fzftab.mdd new file mode 100644 index 0000000..371bb95 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/aloxaf/fzftab.mdd @@ -0,0 +1,7 @@ +name=aloxaf/fzftab +link=dynamic +load=no + +autofeatures="b:fzf-tab-colorize p:FZF_TAB_MODULE_VERSION" + +objects="fzftab.o" diff --git a/dotfiles/system/.zsh/modules/Src/builtin.c b/dotfiles/system/.zsh/modules/Src/builtin.c new file mode 100644 index 0000000..93fa911 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/builtin.c @@ -0,0 +1,7236 @@ +/* + * builtin.c - builtin commands + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +/* this is defined so we get the prototype for open_memstream */ +#define _GNU_SOURCE 1 + +#include "zsh.mdh" +#include "builtin.pro" + +/* Builtins in the main executable */ + +static struct builtin builtins[] = +{ + BIN_PREFIX("-", BINF_DASH), + BIN_PREFIX("builtin", BINF_BUILTIN), + BIN_PREFIX("command", BINF_COMMAND), + BIN_PREFIX("exec", BINF_EXEC), + BIN_PREFIX("noglob", BINF_NOGLOB), + BUILTIN("[", BINF_HANDLES_OPTS, bin_test, 0, -1, BIN_BRACKET, NULL, NULL), + BUILTIN(".", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL), + BUILTIN(":", BINF_PSPECIAL, bin_true, 0, -1, 0, NULL, NULL), + BUILTIN("alias", BINF_MAGICEQUALS | BINF_PLUSOPTS, bin_alias, 0, -1, 0, "Lgmrs", NULL), + BUILTIN("autoload", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "dmktrRTUwWXz", "u"), + BUILTIN("bg", 0, bin_fg, 0, -1, BIN_BG, NULL, NULL), + BUILTIN("break", BINF_PSPECIAL, bin_break, 0, 1, BIN_BREAK, NULL, NULL), + BUILTIN("bye", 0, bin_break, 0, 1, BIN_EXIT, NULL, NULL), + 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 | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmp:%rtuxz", 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), + BUILTIN("echo", BINF_SKIPINVALID, bin_print, 0, -1, BIN_ECHO, "neE", "-"), + BUILTIN("emulate", 0, bin_emulate, 0, -1, 0, "lLR", NULL), + 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 | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%TUZ:%afhi:%lp:%rtu", "xg"), + BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL), + /* + * We used to behave as if the argument to -e was optional. + * But that's actually not useful, so it's more consistent to + * cause an error. + */ + BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt:W", NULL), + BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL), + BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlp:%rtux", "E"), + BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMstTuUWx:z", NULL), + BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"), + BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL), + BUILTIN("hash", BINF_MAGICEQUALS, bin_hash, 0, -1, 0, "Ldfmrv", NULL), + +#ifdef ZSH_HASH_DEBUG + BUILTIN("hashinfo", 0, bin_hashinfo, 0, 0, 0, NULL, NULL), +#endif + + BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "adDEfiLmnpPrt:", "l"), + BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lp:%rtux", "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 | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lp:%rtux", NULL), + BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL), + BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL), + +#if defined(ZSH_MEM) & defined(ZSH_MEM_DEBUG) + BUILTIN("mem", 0, bin_mem, 0, 0, 0, "v", NULL), +#endif + +#if defined(ZSH_PAT_DEBUG) + BUILTIN("patdebug", 0, bin_patdebug, 1, -1, 0, "p", NULL), +#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:v:x:X:z-", NULL), + BUILTIN("printf", BINF_SKIPINVALID | BINF_SKIPDASH, bin_print, 1, -1, BIN_PRINTF, "v:", NULL), + BUILTIN("pushd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_PUSHD, "qsPL", NULL), + BUILTIN("pushln", 0, bin_print, 0, -1, BIN_PRINT, NULL, "-nz"), + BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL), + 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 | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, BIN_READONLY, "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), + BUILTIN("setopt", 0, bin_setopt, 0, -1, BIN_SETOPT, NULL, NULL), + BUILTIN("shift", BINF_PSPECIAL, bin_shift, 0, -1, 0, "p", NULL), + BUILTIN("source", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL), + BUILTIN("suspend", 0, bin_suspend, 0, 0, 0, "f", NULL), + BUILTIN("test", BINF_HANDLES_OPTS, bin_test, 0, -1, BIN_TEST, NULL, NULL), + BUILTIN("ttyctl", 0, bin_ttyctl, 0, 0, 0, "fu", NULL), + BUILTIN("times", BINF_PSPECIAL, bin_times, 0, 0, 0, NULL, NULL), + 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 | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmz", 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"), + BUILTIN("unhash", 0, bin_unhash, 1, -1, BIN_UNHASH, "adfms", NULL), + BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "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, "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:abcfdilmpsue", NULL), + BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUMRcmzka", NULL), +}; + +/****************************************/ +/* Builtin Command Hash Table Functions */ +/****************************************/ + +/* hash table containing builtin commands */ + +/**/ +mod_export HashTable builtintab; + +/**/ +void +createbuiltintable(void) +{ + builtintab = newhashtable(85, "builtintab", NULL); + + builtintab->hash = hasher; + builtintab->emptytable = NULL; + builtintab->filltable = NULL; + builtintab->cmpnodes = strcmp; + builtintab->addnode = addhashnode; + builtintab->getnode = gethashnode; + builtintab->getnode2 = gethashnode2; + builtintab->removenode = removehashnode; + builtintab->disablenode = disablehashnode; + builtintab->enablenode = enablehashnode; + builtintab->freenode = freebuiltinnode; + builtintab->printnode = printbuiltinnode; + + (void)addbuiltins("zsh", builtins, sizeof(builtins)/sizeof(*builtins)); +} + +/* Print a builtin */ + +/**/ +static void +printbuiltinnode(HashNode hn, int printflags) +{ + Builtin bn = (Builtin) hn; + + if (printflags & PRINT_WHENCE_WORD) { + printf("%s: builtin\n", bn->node.nam); + return; + } + + if (printflags & PRINT_WHENCE_CSH) { + printf("%s: shell built-in command\n", bn->node.nam); + return; + } + + if (printflags & PRINT_WHENCE_VERBOSE) { + printf("%s is a shell builtin\n", bn->node.nam); + return; + } + + /* default is name only */ + printf("%s\n", bn->node.nam); +} + +/**/ +static void +freebuiltinnode(HashNode hn) +{ + Builtin bn = (Builtin) hn; + + if(!(bn->node.flags & BINF_ADDED)) { + zsfree(bn->node.nam); + zsfree(bn->optstr); + zfree(bn, sizeof(struct builtin)); + } +} + +/**/ +void +init_builtins(void) +{ + if (!EMULATION(EMULATE_ZSH)) { + HashNode hn = reswdtab->getnode2(reswdtab, "repeat"); + if (hn) + reswdtab->disablenode(hn, 0); + } +} + +/* Make sure we have space for a new option and increment. */ + +#define OPT_ALLOC_CHUNK 16 + +/**/ +static int +new_optarg(Options ops) +{ + /* Argument index must be a non-zero 6-bit number. */ + if (ops->argscount == 63) + return 1; + if (ops->argsalloc == ops->argscount) { + char **newptr = + (char **)zhalloc((ops->argsalloc + OPT_ALLOC_CHUNK) * + sizeof(char *)); + if (ops->argsalloc) + memcpy(newptr, ops->args, ops->argsalloc * sizeof(char *)); + ops->args = newptr; + ops->argsalloc += OPT_ALLOC_CHUNK; + } + ops->argscount++; + return 0; +} + + +/* execute a builtin handler function after parsing the arguments */ + +/**/ +int +execbuiltin(LinkList args, LinkList assigns, Builtin bn) +{ + char *pp, *name, *optstr; + int flags, argc, execop, xtr = isset(XTRACE); + struct options ops; + + /* initialise options structure */ + memset(ops.ind, 0, MAX_OPS*sizeof(unsigned char)); + ops.args = NULL; + ops.argscount = ops.argsalloc = 0; + + /* initialize some local variables */ + name = (char *) ugetnode(args); + + if (!bn->handlerfunc) { + DPUTS(1, "Missing builtin detected too late"); + deletebuiltin(bn->node.nam); + return 1; + } + /* get some information about the command */ + flags = bn->node.flags; + optstr = bn->optstr; + + /* Set up the argument list. */ + /* count the arguments */ + argc = countlinknodes(args); + + { + /* + * Keep all arguments, including options, in an array. + * We don't actually need the option part of the argument + * after option processing, but it makes XTRACE output + * much simpler. + */ + VARARR(char *, argarr, argc + 1); + char **argv; + + /* + * Get the actual arguments, into argv. Remember argarr + * may be an array declaration, depending on the compiler. + */ + argv = argarr; + while ((*argv++ = (char *)ugetnode(args))); + argv = argarr; + + /* Sort out the options. */ + if (optstr) { + char *arg = *argv; + int sense; /* 1 for -x, 0 for +x */ + /* while arguments look like options ... */ + while (arg && + /* Must begin with - or maybe + */ + ((sense = (*arg == '-')) || + ((flags & BINF_PLUSOPTS) && *arg == '+'))) { + /* Digits aren't arguments unless the command says they are. */ + if (!(flags & BINF_KEEPNUM) && idigit(arg[1])) + break; + /* For cd and friends, a single dash is not an option. */ + if ((flags & BINF_SKIPDASH) && !arg[1]) + break; + if ((flags & BINF_DASHDASHVALID) && !strcmp(arg, "--")) { + /* + * Need to skip this before checking whether this is + * really an option. + */ + argv++; + break; + } + /* + * Unrecognised options to echo etc. are not really + * options. + * + * Note this flag is not smart enough to handle option + * arguments. In fact, ideally it shouldn't be added + * to any new builtins, to preserve standard option + * handling as much as possible. + */ + if (flags & BINF_SKIPINVALID) { + char *p = arg; + while (*++p && strchr(optstr, (int) *p)); + if (*p) + break; + } + /* handle -- or - (ops.ind['-']), and + + * (ops.ind['-'] and ops.ind['+']) */ + if (arg[1] == '-') + arg++; + if (!arg[1]) { + ops.ind['-'] = 1; + if (!sense) + ops.ind['+'] = 1; + } + /* save options in ops, as long as they are in bn->optstr */ + while (*++arg) { + char *optptr; + if ((optptr = strchr(optstr, execop = (int)*arg))) { + ops.ind[(int)*arg] = (sense) ? 1 : 2; + if (optptr[1] == ':') { + char *argptr = NULL; + if (optptr[2] == ':') { + if (arg[1]) + argptr = arg+1; + /* Optional argument in same word*/ + } else if (optptr[2] == '%') { + /* Optional numeric argument in same + * or next word. */ + if (arg[1] && idigit(arg[1])) + argptr = arg+1; + else if (argv[1] && idigit(*argv[1])) + argptr = arg = *++argv; + } else { + /* Mandatory argument */ + if (arg[1]) + argptr = arg+1; + else if ((arg = *++argv)) + argptr = arg; + else { + zwarnnam(name, "argument expected: -%c", + execop); + return 1; + } + } + if (argptr) { + if (new_optarg(&ops)) { + zwarnnam(name, + "too many option arguments"); + return 1; + } + ops.ind[execop] |= ops.argscount << 2; + ops.args[ops.argscount-1] = argptr; + while (arg[1]) + arg++; + } + } + } else + break; + } + /* The above loop may have exited on an invalid option. (We * + * assume that any option requiring metafication is invalid.) */ + if (*arg) { + if(*arg == Meta) + *++arg ^= 32; + zwarnnam(name, "bad option: %c%c", "+-"[sense], *arg); + return 1; + } + arg = *++argv; + /* for the "print" builtin, the options after -R are treated as + options to "echo" */ + if ((flags & BINF_PRINTOPTS) && ops.ind['R'] && + !ops.ind['f']) { + optstr = "ne"; + flags |= BINF_SKIPINVALID; + } + /* the option -- indicates the end of the options */ + if (ops.ind['-']) + break; + } + } else if (!(flags & BINF_HANDLES_OPTS) && *argv && + !strcmp(*argv, "--")) { + ops.ind['-'] = 1; + argv++; + } + + /* handle built-in options, for overloaded handler functions */ + if ((pp = bn->defopts)) { + while (*pp) { + /* only if not already set */ + if (!ops.ind[(int)*pp]) + ops.ind[(int)*pp] = 1; + pp++; + } + } + + /* Fix the argument count by subtracting option arguments */ + argc -= argv - argarr; + + if (errflag) { + errflag &= ~ERRFLAG_ERROR; + return 1; + } + + /* check that the argument count lies within the specified bounds */ + if (argc < bn->minargs || (argc > bn->maxargs && bn->maxargs != -1)) { + zwarnnam(name, (argc < bn->minargs) + ? "not enough arguments" : "too many arguments"); + return 1; + } + + /* display execution trace information, if required */ + if (xtr) { + /* Use full argument list including options for trace output */ + char **fullargv = argarr; + printprompt4(); + fprintf(xtrerr, "%s", name); + while (*fullargv) { + 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->flags & ASG_ARRAY) { + fprintf(xtrerr, "=("); + if (asg->value.array) { + if (asg->flags & ASG_KEY_VALUE) { + LinkNode keynode, valnode; + keynode = firstnode(asg->value.array); + for (;;) { + if (!keynode) + break; + valnode = nextnode(keynode); + if (!valnode) + break; + fputc('[', xtrerr); + quotedzputs((char *)getdata(keynode), + xtrerr); + fprintf(stderr, "]="); + quotedzputs((char *)getdata(valnode), + xtrerr); + keynode = nextnode(valnode); + } + } else { + LinkNode arrnode; + 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 */ + 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); + } + } +} + +/* Enable/disable an element in one of the internal hash tables. * + * With no arguments, it lists all the currently enabled/disabled * + * elements in that particular hash table. */ + +/**/ +int +bin_enable(char *name, char **argv, Options ops, int func) +{ + HashTable ht; + HashNode hn; + ScanFunc scanfunc; + Patprog pprog; + int flags1 = 0, flags2 = 0; + int match = 0, returnval = 0; + + /* Find out which hash table we are working with. */ + if (OPT_ISSET(ops,'p')) { + return pat_enables(name, argv, func == BIN_ENABLE); + } else if (OPT_ISSET(ops,'f')) + ht = shfunctab; + else if (OPT_ISSET(ops,'r')) + ht = reswdtab; + else if (OPT_ISSET(ops,'s')) + ht = sufaliastab; + else if (OPT_ISSET(ops,'a')) + ht = aliastab; + else + ht = builtintab; + + /* Do we want to enable or disable? */ + if (func == BIN_ENABLE) { + flags2 = DISABLED; + scanfunc = ht->enablenode; + } else { + flags1 = DISABLED; + scanfunc = ht->disablenode; + } + + /* Given no arguments, print the names of the enabled/disabled elements * + * in this hash table. If func == BIN_ENABLE, then scanhashtable will * + * print nodes NOT containing the DISABLED flag, else scanhashtable will * + * print nodes containing the DISABLED flag. */ + if (!*argv) { + queue_signals(); + scanhashtable(ht, 1, flags1, flags2, ht->printnode, 0); + unqueue_signals(); + return 0; + } + + /* With -m option, treat arguments as glob patterns. */ + if (OPT_ISSET(ops,'m')) { + for (; *argv; argv++) { + queue_signals(); + + /* parse pattern */ + tokenize(*argv); + if ((pprog = patcompile(*argv, PAT_STATIC, 0))) + match += scanmatchtable(ht, pprog, 0, 0, 0, scanfunc, 0); + else { + untokenize(*argv); + zwarnnam(name, "bad pattern : %s", *argv); + returnval = 1; + } + unqueue_signals(); + } + /* If we didn't match anything, we return 1. */ + if (!match) + returnval = 1; + return returnval; + } + + /* Take arguments literally -- do not glob */ + queue_signals(); + for (; *argv; argv++) { + if ((hn = ht->getnode2(ht, *argv))) { + scanfunc(hn, 0); + } else { + zwarnnam(name, "no such hash table element: %s", *argv); + returnval = 1; + } + } + unqueue_signals(); + return returnval; +} + +/* set: either set the shell options, or set the shell arguments, * + * or declare an array, or show various things */ + +/**/ +int +bin_set(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) +{ + int action, optno, array = 0, hadopt = 0, + hadplus = 0, hadend = 0, sort = 0; + char **x, *arrayname = NULL; + + /* Obsolescent sh compatibility: set - is the same as set +xv * + * and set - args is the same as set +xv -- args */ + if (!EMULATION(EMULATE_ZSH) && *args && **args == '-' && !args[0][1]) { + dosetopt(VERBOSE, 0, 0, opts); + dosetopt(XTRACE, 0, 0, opts); + if (!args[1]) + return 0; + } + + /* loop through command line options (begins with "-" or "+") */ + while (*args && (**args == '-' || **args == '+')) { + action = (**args == '-'); + hadplus |= !action; + if(!args[0][1]) + *args = "--"; + while (*++*args) { + if(**args == Meta) + *++*args ^= 32; + if(**args != '-' || action) + hadopt = 1; + /* The pseudo-option `--' signifies the end of options. */ + if (**args == '-') { + hadend = 1; + args++; + goto doneoptions; + } else if (**args == 'o') { + if (!*++*args) + args++; + if (!*args) { + printoptionstates(hadplus); + inittyptab(); + return 0; + } + if(!(optno = optlookup(*args))) + zerrnam(nam, "no such option: %s", *args); + else if(dosetopt(optno, action, 0, opts)) + zerrnam(nam, "can't change option: %s", *args); + break; + } else if(**args == 'A') { + if(!*++*args) + args++; + array = action ? 1 : -1; + arrayname = *args; + if (!arrayname) + goto doneoptions; + else if (!isset(KSHARRAYS)) + { + args++; + goto doneoptions; + } + break; + } else if (**args == 's') + sort = action ? 1 : -1; + else { + if (!(optno = optlookupc(**args))) + zerrnam(nam, "bad option: -%c", **args); + else if(dosetopt(optno, action, 0, opts)) + zerrnam(nam, "can't change option: -%c", **args); + } + } + args++; + } + if (errflag) + return 1; + doneoptions: + inittyptab(); + + /* Show the parameters, possibly with values */ + queue_signals(); + if (!arrayname) + { + if (!hadopt && !*args) + scanhashtable(paramtab, 1, 0, 0, paramtab->printnode, + hadplus ? PRINT_NAMEONLY : 0); + + if (array) { + /* display arrays */ + scanhashtable(paramtab, 1, PM_ARRAY, 0, paramtab->printnode, + hadplus ? PRINT_NAMEONLY : 0); + } + if (!*args && !hadend) { + unqueue_signals(); + return 0; + } + } + if (sort) + strmetasort(args, sort < 0 ? SORTIT_BACKWARDS : 0, NULL); + if (array) { + /* create an array with the specified elements */ + char **a = NULL, **y; + int len = arrlen(args); + + if (array < 0 && (a = getaparam(arrayname)) && arrlen_gt(a, len)) { + a += len; + len += arrlen(a); + } + for (x = y = zalloc((len + 1) * sizeof(char *)); len--;) { + if (!*args) + args = a; + *y++ = ztrdup(*args++); + } + *y++ = NULL; + setaparam(arrayname, x); + } else { + /* set shell arguments */ + freearray(pparams); + pparams = zarrdup(args); + } + unqueue_signals(); + return 0; +} + +/**** directory-handling builtins ****/ + +/**/ +int doprintdir = 0; /* set in exec.c (for autocd) */ + +/* pwd: display the name of the current directory */ + +/**/ +int +bin_pwd(UNUSED(char *name), UNUSED(char **argv), Options ops, UNUSED(int func)) +{ + if (OPT_ISSET(ops,'r') || OPT_ISSET(ops,'P') || + (isset(CHASELINKS) && !OPT_ISSET(ops,'L'))) + printf("%s\n", zgetcwd()); + else { + zputs(pwd, stdout); + putchar('\n'); + } + return 0; +} + +/* the directory stack */ + +/**/ +mod_export LinkList dirstack; + +/* dirs: list the directory stack, or replace it with a provided list */ + +/**/ +int +bin_dirs(UNUSED(char *name), char **argv, Options ops, UNUSED(int func)) +{ + LinkList l; + + queue_signals(); + /* with -v, -p or no arguments display the directory stack */ + if (!(*argv || OPT_ISSET(ops,'c')) || OPT_ISSET(ops,'v') || + OPT_ISSET(ops,'p')) { + LinkNode node; + char *fmt; + int pos = 1; + + /* with the -v option, display a numbered list, starting at zero */ + if (OPT_ISSET(ops,'v')) { + printf("0\t"); + fmt = "\n%d\t"; + /* with the -p option, display entries one per line */ + } else if (OPT_ISSET(ops,'p')) + fmt = "\n"; + else + fmt = " "; + if (OPT_ISSET(ops,'l')) + zputs(pwd, stdout); + else + fprintdir(pwd, stdout); + for (node = firstnode(dirstack); node; incnode(node)) { + printf(fmt, pos++); + if (OPT_ISSET(ops,'l')) + zputs(getdata(node), stdout); + else + fprintdir(getdata(node), stdout); + + } + unqueue_signals(); + putchar('\n'); + return 0; + } + /* replace the stack with the specified directories */ + l = znewlinklist(); + while (*argv) + zaddlinknode(l, ztrdup(*argv++)); + freelinklist(dirstack, freestr); + dirstack = l; + unqueue_signals(); + return 0; +} + +/* cd, chdir, pushd, popd */ + +/**/ +void +set_pwd_env(void) +{ + Param pm; + + /* update the PWD and OLDPWD shell parameters */ + + pm = (Param) paramtab->getnode(paramtab, "PWD"); + if (pm && PM_TYPE(pm->node.flags) != PM_SCALAR) { + pm->node.flags &= ~PM_READONLY; + unsetparam_pm(pm, 0, 1); + } + + pm = (Param) paramtab->getnode(paramtab, "OLDPWD"); + if (pm && PM_TYPE(pm->node.flags) != PM_SCALAR) { + pm->node.flags &= ~PM_READONLY; + unsetparam_pm(pm, 0, 1); + } + + assignsparam("PWD", ztrdup(pwd), 0); + assignsparam("OLDPWD", ztrdup(oldpwd), 0); + + pm = (Param) paramtab->getnode(paramtab, "PWD"); + if (!(pm->node.flags & PM_EXPORTED)) + addenv(pm, pwd); + pm = (Param) paramtab->getnode(paramtab, "OLDPWD"); + if (!(pm->node.flags & PM_EXPORTED)) + addenv(pm, oldpwd); +} + +/* set if we are resolving links to their true paths */ +static int chasinglinks; + +/* The main pwd changing function. The real work is done by other * + * functions. cd_get_dest() does the initial argument processing; * + * cd_do_chdir() actually changes directory, if possible; cd_new_pwd() * + * does the ancillary processing associated with actually changing * + * directory. */ + +/**/ +int +bin_cd(char *nam, char **argv, Options ops, int func) +{ + LinkNode dir; + struct stat st1, st2; + + if (isset(RESTRICTED)) { + zwarnnam(nam, "restricted"); + return 1; + } + doprintdir = (doprintdir == -1); + + chasinglinks = OPT_ISSET(ops,'P') || + (isset(CHASELINKS) && !OPT_ISSET(ops,'L')); + queue_signals(); + zpushnode(dirstack, ztrdup(pwd)); + if (!(dir = cd_get_dest(nam, argv, OPT_ISSET(ops,'s'), func))) { + zsfree(getlinknode(dirstack)); + unqueue_signals(); + return 1; + } + cd_new_pwd(func, dir, OPT_ISSET(ops, 'q')); + + if (stat(unmeta(pwd), &st1) < 0) { + setjobpwd(); + zsfree(pwd); + pwd = NULL; + pwd = metafy(zgetcwd(), -1, META_DUP); + } else if (stat(".", &st2) < 0) { + if (chdir(unmeta(pwd)) < 0) + zwarn("unable to chdir(%s): %e", pwd, errno); + } else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) { + if (chasinglinks) { + setjobpwd(); + zsfree(pwd); + pwd = NULL; + pwd = metafy(zgetcwd(), -1, META_DUP); + } else if (chdir(unmeta(pwd)) < 0) + zwarn("unable to chdir(%s): %e", pwd, errno); + } + unqueue_signals(); + return 0; +} + +/* Get directory to chdir to */ + +/**/ +static LinkNode +cd_get_dest(char *nam, char **argv, int hard, int func) +{ + LinkNode dir = NULL; + LinkNode target; + char *dest; + + if (!argv[0]) { + if (func == BIN_POPD && !nextnode(firstnode(dirstack))) { + zwarnnam(nam, "directory stack empty"); + return NULL; + } + if (func == BIN_PUSHD && unset(PUSHDTOHOME)) + dir = nextnode(firstnode(dirstack)); + if (dir) + zinsertlinknode(dirstack, dir, getlinknode(dirstack)); + else if (func != BIN_POPD) { + if (!home) { + zwarnnam(nam, "HOME not set"); + return NULL; + } + zpushnode(dirstack, ztrdup(home)); + } + } else if (!argv[1]) { + int dd; + char *end; + + doprintdir++; + if (argv[0][1] && (argv[0][0] == '+' || argv[0][0] == '-') + && strspn(argv[0]+1, "0123456789") == strlen(argv[0]+1)) { + dd = zstrtol(argv[0] + 1, &end, 10); + if (*end == '\0') { + if ((argv[0][0] == '+') ^ isset(PUSHDMINUS)) + for (dir = firstnode(dirstack); dir && dd; dd--, incnode(dir)); + else + for (dir = lastnode(dirstack); dir != (LinkNode) dirstack && dd; + dd--, dir = prevnode(dir)); + if (!dir || dir == (LinkNode) dirstack) { + zwarnnam(nam, "no such entry in dir stack"); + return NULL; + } + } + } + if (!dir) + zpushnode(dirstack, ztrdup(strcmp(argv[0], "-") + ? (doprintdir--, argv[0]) : oldpwd)); + } else { + char *u, *d; + int len1, len2, len3; + + if (!(u = strstr(pwd, argv[0]))) { + zwarnnam(nam, "string not in pwd: %s", argv[0]); + return NULL; + } + len1 = strlen(argv[0]); + len2 = strlen(argv[1]); + len3 = u - pwd; + d = (char *)zalloc(len3 + len2 + strlen(u + len1) + 1); + strncpy(d, pwd, len3); + strcpy(d + len3, argv[1]); + strcat(d, u + len1); + zpushnode(dirstack, d); + doprintdir++; + } + + target = dir; + if (func == BIN_POPD) { + if (!dir) { + target = dir = firstnode(dirstack); + } else if (dir != firstnode(dirstack)) { + return dir; + } + dir = nextnode(dir); + } + if (!dir) { + dir = firstnode(dirstack); + } + if (!dir || !getdata(dir)) { + DPUTS(1, "Directory not set, not detected early enough"); + return NULL; + } + if (!(dest = cd_do_chdir(nam, getdata(dir), hard))) { + if (!target) + zsfree(getlinknode(dirstack)); + if (func == BIN_POPD) + zsfree(remnode(dirstack, dir)); + return NULL; + } + if (dest != (char *)getdata(dir)) { + zsfree(getdata(dir)); + setdata(dir, dest); + } + return target ? target : dir; +} + +/* Change to given directory, if possible. This function works out * + * exactly how the directory should be interpreted, including cdpath * + * and CDABLEVARS. For each possible interpretation of the given * + * path, this calls cd_try_chdir(), which attempts to chdir to that * + * particular path. */ + +/**/ +static char * +cd_do_chdir(char *cnam, char *dest, int hard) +{ + char **pp, *ret; + int hasdot = 0, eno = ENOENT; + /* + * nocdpath indicates that cdpath should not be used. + * This is the case iff dest is a relative path + * whose first segment is . or .., but if the path is + * absolute then cdpath won't be used anyway. + */ + int nocdpath; +#ifdef __CYGWIN__ + /* + * Normalize path under Cygwin to avoid messing with + * DOS style names with drives in them + */ + static char buf[PATH_MAX+1]; +#ifdef HAVE_CYGWIN_CONV_PATH + cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE, dest, buf, + PATH_MAX); +#else +#ifndef _SYS_CYGWIN_H + void cygwin_conv_to_posix_path(const char *, char *); +#endif + + cygwin_conv_to_posix_path(dest, buf); +#endif + dest = buf; +#endif + nocdpath = dest[0] == '.' && + (dest[1] == '/' || !dest[1] || (dest[1] == '.' && + (dest[2] == '/' || !dest[2]))); + + /* + * If we have an absolute path, use it as-is only + */ + if (*dest == '/') { + if ((ret = cd_try_chdir(NULL, dest, hard))) + return ret; + zwarnnam(cnam, "%e: %s", errno, dest); + return NULL; + } + + /* + * If cdpath is being used, check it for ".". + * Don't bother doing this if POSIXCD is set, we don't + * need to know (though it doesn't actually matter). + */ + if (!nocdpath && !isset(POSIXCD)) + for (pp = cdpath; *pp; pp++) + if (!(*pp)[0] || ((*pp)[0] == '.' && (*pp)[1] == '\0')) + hasdot = 1; + /* + * If + * (- there is no . in cdpath + * - or cdpath is not being used) + * - and the POSIXCD option is not set + * try the directory as-is (i.e. from .) + */ + if (!hasdot && !isset(POSIXCD)) { + if ((ret = cd_try_chdir(NULL, dest, hard))) + return ret; + if (errno != ENOENT) + eno = errno; + } + /* if cdpath is being used, try given directory relative to each element in + cdpath in turn */ + if (!nocdpath) + for (pp = cdpath; *pp; pp++) { + if ((ret = cd_try_chdir(*pp, dest, hard))) { + if (isset(POSIXCD)) { + /* + * For POSIX we need to print the directory + * any time CDPATH was used, except in the + * special case of an empty segment being + * treated as a ".". + */ + if (**pp) + doprintdir++; + } else { + if (strcmp(*pp, ".")) { + doprintdir++; + } + } + return ret; + } + if (errno != ENOENT) + eno = errno; + } + /* + * POSIX requires us to check "." after CDPATH rather than before. + */ + if (isset(POSIXCD)) { + if ((ret = cd_try_chdir(NULL, dest, hard))) + return ret; + if (errno != ENOENT) + eno = errno; + } + + /* handle the CDABLEVARS option */ + if ((ret = cd_able_vars(dest))) { + if ((ret = cd_try_chdir(NULL, ret,hard))) { + doprintdir++; + return ret; + } + if (errno != ENOENT) + eno = errno; + } + + /* If we got here, it means that we couldn't chdir to any of the + multitudinous possible paths allowed by zsh. We've run out of options! + Add more here! */ + zwarnnam(cnam, "%e: %s", eno, dest); + return NULL; +} + +/* If the CDABLEVARS option is set, return the new * + * interpretation of the given path. */ + +/**/ +char * +cd_able_vars(char *s) +{ + char *rest, save; + + if (isset(CDABLEVARS)) { + for (rest = s; *rest && *rest != '/'; rest++); + save = *rest; + *rest = 0; + s = getnameddir(s); + *rest = save; + + if (s && *rest) + s = dyncat(s, rest); + + return s; + } + return NULL; +} + +/* Attempt to change to a single given directory. The directory, * + * for the convenience of the calling function, may be provided in * + * two parts, which must be concatenated before attempting to chdir. * + * Returns NULL if the chdir fails. If the directory change is * + * possible, it is performed, and a pointer to the new full pathname * + * is returned. */ + +/**/ +static char * +cd_try_chdir(char *pfix, char *dest, int hard) +{ + char *buf; + int dlen, dochaselinks = 0; + + /* handle directory prefix */ + if (pfix && *pfix) { + if (*pfix == '/') { +#ifdef __CYGWIN__ +/* NB: Don't turn "/"+"bin" into "//"+"bin" by mistake! "//bin" may * + * not be what user really wants (probably wants "/bin"), but * + * "//bin" could be valid too (see fixdir())! This is primarily for * + * handling CDPATH correctly. Likewise for "//"+"bin" not becoming * + * "///bin" (aka "/bin"). */ + int root = pfix[1] == '\0' || (pfix[1] == '/' && pfix[2] == '\0'); + buf = tricat(pfix, ( root ? "" : "/" ), dest); +#else + buf = tricat(pfix, "/", dest); +#endif + } else { + int pfl = strlen(pfix); + dlen = strlen(pwd); + if (dlen == 1 && *pwd == '/') + dlen = 0; + buf = zalloc(dlen + pfl + strlen(dest) + 3); + if (dlen) + strcpy(buf, pwd); + buf[dlen] = '/'; + strcpy(buf + dlen + 1, pfix); + buf[dlen + 1 + pfl] = '/'; + strcpy(buf + dlen + pfl + 2, dest); + } + } else if (*dest == '/') + buf = ztrdup(dest); + else { + dlen = strlen(pwd); + if (pwd[dlen-1] == '/') + --dlen; + buf = zalloc(dlen + strlen(dest) + 2); + strcpy(buf, pwd); + buf[dlen] = '/'; + strcpy(buf + dlen + 1, dest); + } + + /* Normalise path. See the definition of fixdir() for what this means. + * We do not do this if we are chasing links. + */ + if (!chasinglinks) + dochaselinks = fixdir(buf); + else + unmetafy(buf, &dlen); + + /* We try the full path first. If that fails, try the + * argument to cd relatively. This is useful if the cwd + * or a parent directory is renamed in the interim. + */ + if (lchdir(buf, NULL, hard) && + (pfix || *dest == '/' || lchdir(unmeta(dest), NULL, hard))) { + free(buf); + return NULL; + } + /* the chdir succeeded, so decide if we should force links to be chased */ + if (dochaselinks) + chasinglinks = 1; + return metafy(buf, -1, META_NOALLOC); +} + +/* do the extra processing associated with changing directory */ + +/**/ +static void +cd_new_pwd(int func, LinkNode dir, int quiet) +{ + char *new_pwd, *s; + int dirstacksize; + + if (func == BIN_PUSHD) + rolllist(dirstack, dir); + new_pwd = remnode(dirstack, dir); + + if (func == BIN_POPD && firstnode(dirstack)) { + zsfree(new_pwd); + new_pwd = getlinknode(dirstack); + } else if (func == BIN_CD && unset(AUTOPUSHD)) + zsfree(getlinknode(dirstack)); + + if (chasinglinks) { + s = findpwd(new_pwd); + if (s) { + zsfree(new_pwd); + new_pwd = s; + } + } + if (isset(PUSHDIGNOREDUPS)) { + LinkNode n; + for (n = firstnode(dirstack); n; incnode(n)) { + if (!strcmp(new_pwd, getdata(n))) { + zsfree(remnode(dirstack, n)); + break; + } + } + } + + /* shift around the pwd variables, to make oldpwd and pwd relate to the + current (i.e. new) pwd */ + zsfree(oldpwd); + oldpwd = pwd; + setjobpwd(); + pwd = new_pwd; + set_pwd_env(); + + if (isset(INTERACTIVE) || isset(POSIXCD)) { + if (func != BIN_CD && isset(INTERACTIVE)) { + if (unset(PUSHDSILENT) && !quiet) + printdirstack(); + } else if (doprintdir) { + fprintdir(pwd, stdout); + putchar('\n'); + } + } + + /* execute the chpwd function */ + fflush(stdout); + fflush(stderr); + if (!quiet) + callhookfunc("chpwd", NULL, 1, NULL); + + dirstacksize = getiparam("DIRSTACKSIZE"); + /* handle directory stack sizes out of range */ + if (dirstacksize > 0) { + int remove = countlinknodes(dirstack) - + (dirstacksize < 2 ? 2 : dirstacksize); + while (remove-- >= 0) + zsfree(remnode(dirstack, lastnode(dirstack))); + } +} + +/* Print the directory stack */ + +/**/ +static void +printdirstack(void) +{ + LinkNode node; + + fprintdir(pwd, stdout); + for (node = firstnode(dirstack); node; incnode(node)) { + putchar(' '); + fprintdir(getdata(node), stdout); + } + putchar('\n'); +} + +/* Normalise a path. Segments consisting of ., and foo/.. * + * combinations, are removed and the path is unmetafied. + * Returns 1 if we found a ../ path which should force links to + * be chased, 0 otherwise. + */ + +/**/ +int +fixdir(char *src) +{ + char *dest = src, *d0 = dest; +#ifdef __CYGWIN__ + char *s0 = src; +#endif + /* This function is always called with n path containing at + * least one slash, either because one was input by the user or + * because the caller has prepended either pwd or a cdpath dir. + * If asked to make a relative change and pwd is set to ".", + * the current directory has been removed out from under us, + * so force links to be chased. + * + * Ordinarily we can't get here with "../" as the first component + * but handle the silly special case of ".." in cdpath. + * + * Order of comparisons here looks funny, but it short-circuits + * most rapidly in the event of a false condition. Set to 2 + * here so we still obey the (lack of) CHASEDOTS option after + * the first "../" is preserved (test chasedots > 1 below). + */ + int chasedots = (src[0] == '.' && pwd[0] == '.' && pwd[1] == '\0' && + (src[1] == '/' || (src[1] == '.' && src[2] == '/'))) * 2; + +/*** if have RFS superroot directory ***/ +#ifdef HAVE_SUPERROOT + /* allow /.. segments to remain */ + while (*src == '/' && src[1] == '.' && src[2] == '.' && + (!src[3] || src[3] == '/')) { + *dest++ = '/'; + *dest++ = '.'; + *dest++ = '.'; + src += 3; + } +#endif + + for (;;) { + /* compress multiple /es into single */ + if (*src == '/') { +#ifdef __CYGWIN__ + /* allow leading // under cygwin, but /// still becomes / */ + if (src == s0 && src[1] == '/' && src[2] != '/') + *dest++ = *src++; +#endif + *dest++ = *src++; + while (*src == '/') + src++; + } + /* if we are at the end of the input path, remove a trailing / (if it + exists), and return ct */ + if (!*src) { + while (dest > d0 + 1 && dest[-1] == '/') + dest--; + *dest = '\0'; + return chasedots; + } + if (src[0] == '.' && src[1] == '.' && + (src[2] == '\0' || src[2] == '/')) { + if (isset(CHASEDOTS) || chasedots > 1) { + chasedots = 1; + /* and treat as normal path segment */ + } else { + if (dest > d0 + 1) { + /* + * remove a foo/.. combination: + * first check foo exists, else return. + */ + struct stat st; + *dest = '\0'; + if (stat(d0, &st) < 0 || !S_ISDIR(st.st_mode)) { + char *ptrd, *ptrs; + if (dest == src) + *dest = '.'; + for (ptrs = src, ptrd = dest; *ptrs; ptrs++, ptrd++) + *ptrd = (*ptrs == Meta) ? (*++ptrs ^ 32) : *ptrs; + *ptrd = '\0'; + return 1; + } + for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--); + if (dest[-1] != '/') + dest--; + } + src++; + while (*++src == '/'); + continue; + } + } + if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) { + /* skip a . section */ + while (*++src == '/'); + } else { + /* copy a normal segment into the output */ + while (*src != '/' && *src != '\0') + if ((*dest++ = *src++) == Meta) + dest[-1] = *src++ ^ 32; + } + } + /* unreached */ +} + +/**/ +mod_export void +printqt(char *str) +{ + /* Print str, but turn any single quote into '\'' or ''. */ + for (; *str; str++) + if (*str == '\'') + printf(isset(RCQUOTES) ? "''" : "'\\''"); + else + putchar(*str); +} + +/**/ +mod_export void +printif(char *str, int c) +{ + /* If flag c has an argument, print that */ + if (str) { + printf(" -%c ", c); + quotedzputs(str, stdout); + } +} + +/**** history list functions ****/ + +/* fc, history, r */ + +/**/ +int +bin_fc(char *nam, char **argv, Options ops, int func) +{ + zlong first = -1, last = -1; + int retval; + char *s; + struct asgment *asgf = NULL, *asgl = NULL; + Patprog pprog = NULL; + + /* fc is only permitted in interactive shells */ +#ifdef FACIST_INTERACTIVE + if (!interact) { + zwarnnam(nam, "not interactive shell"); + return 1; + } +#endif + if (OPT_ISSET(ops,'p')) { + char *hf = ""; + zlong hs = DEFAULT_HISTSIZE; + zlong shs = 0; + int level = OPT_ISSET(ops,'a') ? locallevel : -1; + if (*argv) { + hf = *argv++; + if (*argv) { + char *check; + hs = zstrtol(*argv++, &check, 10); + if (*check) { + zwarnnam("fc", "HISTSIZE must be an integer"); + return 1; + } + if (*argv) { + shs = zstrtol(*argv++, &check, 10); + if (*check) { + zwarnnam("fc", "SAVEHIST must be an integer"); + return 1; + } + } else + shs = hs; + if (*argv) { + zwarnnam("fc", "too many arguments"); + return 1; + } + } else { + hs = histsiz; + shs = savehistsiz; + } + } + if (!pushhiststack(hf, hs, shs, level)) + return 1; + if (*hf) { + struct stat st; + if (stat(hf, &st) >= 0 || errno != ENOENT) + readhistfile(hf, 1, HFILE_USE_OPTIONS); + } + return 0; + } + if (OPT_ISSET(ops,'P')) { + if (*argv) { + zwarnnam("fc", "too many arguments"); + return 1; + } + return !saveandpophiststack(-1, HFILE_USE_OPTIONS); + } + /* with the -m option, the first argument is taken * + * as a pattern that history lines have to match */ + if (*argv && OPT_ISSET(ops,'m')) { + tokenize(*argv); + if (!(pprog = patcompile(*argv++, 0, NULL))) { + zwarnnam(nam, "invalid match pattern"); + return 1; + } + } + queue_signals(); + if (OPT_ISSET(ops,'R')) { + /* read history from a file */ + readhistfile(*argv, 1, OPT_ISSET(ops,'I') ? HFILE_SKIPOLD : 0); + unqueue_signals(); + return 0; + } + if (OPT_ISSET(ops,'W')) { + /* write history to a file */ + savehistfile(*argv, 1, OPT_ISSET(ops,'I') ? HFILE_SKIPOLD : 0); + unqueue_signals(); + return 0; + } + if (OPT_ISSET(ops,'A')) { + /* append history to a file */ + savehistfile(*argv, 1, HFILE_APPEND | + (OPT_ISSET(ops,'I') ? HFILE_SKIPOLD : 0)); + unqueue_signals(); + return 0; + } + + if (zleactive) { + unqueue_signals(); + zwarnnam(nam, "no interactive history within ZLE"); + return 1; + } + + /* put foo=bar type arguments into the substitution list */ + while (*argv && equalsplit(*argv, &s)) { + Asgment a = (Asgment) zhalloc(sizeof *a); + + if (!**argv) { + zwarnnam(nam, "invalid replacement pattern: =%s", s); + return 1; + } + if (!asgf) + asgf = asgl = a; + else { + asgl->node.next = &a->node; + asgl = a; + } + a->name = *argv; + a->flags = 0; + a->value.scalar = s; + a->node.next = a->node.prev = NULL; + argv++; + } + /* interpret and check first history line specifier */ + if (*argv) { + first = fcgetcomm(*argv); + if (first == -1) { + unqueue_signals(); + return 1; + } + argv++; + } + /* interpret and check second history line specifier */ + if (*argv) { + last = fcgetcomm(*argv); + if (last == -1) { + unqueue_signals(); + return 1; + } + argv++; + } + /* There is a maximum of two history specifiers. At least, there * + * will be as long as the history list is one-dimensional. */ + if (*argv) { + unqueue_signals(); + zwarnnam("fc", "too many arguments"); + return 1; + } + /* default values of first and last, and range checking */ + if (last == -1) { + if (OPT_ISSET(ops,'l') && first < curhist) { + /* + * When listing base our calculations on curhist, + * to show anything added since the edited history line. + * Also, in that case curhist will have been modified + * past the current history line; then we want to + * show everything, because the user expects to + * see the result of "print -s". Otherwise, we subtract + * -1 from the line, because the user doesn't usually expect + * to see the command line that caused history to be + * listed. + */ + last = (curline.histnum == curhist) ? addhistnum(curhist,-1,0) + : curhist; + if (last < firsthist()) + last = firsthist(); + } + else + last = first; + } + if (first == -1) { + /* + * When listing, we want to see everything that's been + * added to the history, including by print -s, so use + * curhist. + * When reexecuting, we want to restrict to the last edited + * command line to avoid giving the user a nasty turn + * if some helpful soul ran "print -s 'rm -rf /'". + */ + first = OPT_ISSET(ops,'l')? addhistnum(curhist,-16,0) + : addhistnum(curline.histnum,-1,0); + if (first < 1) + first = 1; + if (last < first) + last = first; + } + if (OPT_ISSET(ops,'l')) { + /* list the required part of the history */ + retval = fclist(stdout, ops, first, last, asgf, pprog, 0); + unqueue_signals(); + } + else { + /* edit history file, and (if successful) use the result as a new command */ + int tempfd; + FILE *out; + char *fil; + + retval = 1; + if ((tempfd = gettempfile(NULL, 1, &fil)) < 0 + || ((out = fdopen(tempfd, "w")) == NULL)) { + unqueue_signals(); + zwarnnam("fc", "can't open temp file: %e", errno); + } else { + /* + * Nasty behaviour results if we use the current history + * line here. Treat it as if it doesn't exist, unless + * that gives us an empty range. + */ + if (last >= curhist) { + last = curhist - 1; + if (first > last) { + unqueue_signals(); + zwarnnam("fc", + "current history line would recurse endlessly, aborted"); + fclose(out); + unlink(fil); + return 1; + } + } + ops->ind['n'] = 1; /* No line numbers here. */ + if (!fclist(out, ops, first, last, asgf, pprog, 1)) { + char *editor; + + if (func == BIN_R) + editor = "-"; + else if (OPT_HASARG(ops, 'e')) + editor = OPT_ARG(ops, 'e'); + else + editor = getsparam("FCEDIT"); + if (!editor) + editor = getsparam("EDITOR"); + if (!editor) + editor = DEFAULT_FCEDIT; + + unqueue_signals(); + if (fcedit(editor, fil)) { + if (stuff(fil)) + zwarnnam("fc", "%e: %s", errno, fil); + else { + loop(0,1); + retval = lastval; + } + } + } else + unqueue_signals(); + } + unlink(fil); + } + return retval; +} + +/* History handling functions: these are called by ZLE, as well as * + * the actual builtins. fcgetcomm() gets a history line, specified * + * either by number or leading string. fcsubs() performs a given * + * set of simple old=new substitutions on a given command line. * + * fclist() outputs a given range of history lines to a text file. */ + +/* get the history event associated with s */ + +/**/ +static zlong +fcgetcomm(char *s) +{ + zlong cmd; + + /* First try to match a history number. Negative * + * numbers indicate reversed numbering. */ + if ((cmd = atoi(s)) != 0 || *s == '0') { + if (cmd < 0) + cmd = addhistnum(curline.histnum,cmd,HIST_FOREIGN); + if (cmd < 0) + cmd = 0; + return cmd; + } + /* not a number, so search by string */ + cmd = hcomsearch(s); + if (cmd == -1) + zwarnnam("fc", "event not found: %s", s); + return cmd; +} + +/* Perform old=new substitutions. Uses the asgment structure from zsh.h, * + * which is essentially a linked list of string,replacement pairs. */ + +/**/ +static int +fcsubs(char **sp, struct asgment *sub) +{ + char *oldstr, *newstr, *oldpos, *newpos, *newmem, *s = *sp; + int subbed = 0; + + /* loop through the linked list */ + while (sub) { + oldstr = sub->name; + 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))) { + newmem = (char *) zhalloc(1 + (newpos - s) + + strlen(newstr) + strlen(newpos + strlen(oldstr))); + ztrncpy(newmem, s, newpos - s); + strcat(newmem, newstr); + oldpos = newmem + strlen(newmem); + strcat(newmem, newpos + strlen(oldstr)); + s = newmem; + subbed = 1; + } + } + *sp = s; + return subbed; +} + +/* Print a series of history events to a file. The file pointer is * + * given by f, and the required range of events by first and last. * + * subs is an optional list of foo=bar substitutions to perform on the * + * history lines before output. com is an optional comp structure * + * that the history lines are required to match. n, r, D and d are * + * options: n indicates that each line should be numbered. r indicates * + * that the lines should be output in reverse order (newest first). * + * D indicates that the real time taken by each command should be * + * output. d indicates that the time of execution of each command * + * should be output; d>1 means that the date should be output too; d>3 * + * means that mm/dd/yyyy form should be used for the dates, as opposed * + * to dd.mm.yyyy form; d>7 means that yyyy-mm-dd form should be used. */ + +/**/ +static int +fclist(FILE *f, Options ops, zlong first, zlong last, + struct asgment *subs, Patprog pprog, int is_command) +{ + int fclistdone = 0, xflags = 0; + zlong tmp; + char *s, *tdfmt, *timebuf; + Histent ent; + + /* reverse range if required */ + if (OPT_ISSET(ops,'r')) { + tmp = last; + last = first; + first = tmp; + } + if (is_command && first > last) { + zwarnnam("fc", "history events can't be executed backwards, aborted"); + if (f != stdout) + fclose(f); + return 1; + } + + ent = gethistent(first, first < last? GETHIST_DOWNWARD : GETHIST_UPWARD); + if (!ent || (first < last? ent->histnum > last : ent->histnum < last)) { + if (first == last) { + char buf[DIGBUFSIZE]; + convbase(buf, first, 10); + zwarnnam("fc", "no such event: %s", buf); + } else + zwarnnam("fc", "no events in that range"); + if (f != stdout) + fclose(f); + return 1; + } + + if (OPT_ISSET(ops,'d') || OPT_ISSET(ops,'f') || + OPT_ISSET(ops,'E') || OPT_ISSET(ops,'i') || + OPT_ISSET(ops,'t')) { + if (OPT_ISSET(ops,'t')) { + tdfmt = OPT_ARG(ops,'t'); + } else if (OPT_ISSET(ops,'i')) { + tdfmt = "%Y-%m-%d %H:%M"; + } else if (OPT_ISSET(ops,'E')) { + tdfmt = "%f.%-m.%Y %H:%M"; + } else if (OPT_ISSET(ops,'f')) { + tdfmt = "%-m/%f/%Y %H:%M"; + } else { + tdfmt = "%H:%M"; + } + timebuf = zhalloc(256); + } else { + tdfmt = timebuf = NULL; + } + + /* xflags exclude events */ + if (OPT_ISSET(ops,'L')) { + xflags |= HIST_FOREIGN; + } + if (OPT_ISSET(ops,'I')) { + xflags |= HIST_READ; + } + + for (;;) { + 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 */ + fclistdone |= (subs ? fcsubs(&s, subs) : 1); + + /* do numbering */ + if (!OPT_ISSET(ops,'n')) { + char buf[DIGBUFSIZE]; + convbase(buf, ent->histnum, 10); + fprintf(f, "%5s%c ", buf, + ent->node.flags & HIST_FOREIGN ? '*' : ' '); + } + /* output actual time (and possibly date) of execution of the + command, if required */ + if (tdfmt != NULL) { + struct tm *ltm; + int len; + ltm = localtime(&ent->stim); + 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')) { + long diff; + diff = (ent->ftim) ? ent->ftim - ent->stim : 0; + fprintf(f, "%ld:%02ld ", diff / 60, diff % 60); + } + + /* output the command */ + if (f == stdout) { + nicezputs(s, f); + putc('\n', f); + } else { + int len; + unmetafy(s, &len); + fwrite(s, 1, len, f); + putc('\n', f); + } + } + /* move on to the next history line, or quit the loop */ + if (first < last) { + if (!(ent = down_histent(ent)) || ent->histnum > last) + break; + } + else { + if (!(ent = up_histent(ent)) || ent->histnum < last) + break; + } + } + + /* final processing */ + if (f != stdout) + fclose(f); + if (!fclistdone) { + if (subs) + zwarnnam("fc", "no substitutions performed"); + else if (xflags || pprog) + zwarnnam("fc", "no matching events found"); + return 1; + } + return 0; +} + +/* edit a history file */ + +/**/ +static int +fcedit(char *ename, char *fn) +{ + char *s; + + if (!strcmp(ename, "-")) + return 1; + + s = tricat(ename, " ", fn); + execstring(s, 1, 0, "fc"); + zsfree(s); + + return !lastval; +} + +/**** parameter builtins ****/ + +/* Separate an argument into name=value parts, returning them in an * + * asgment structure. Because the asgment structure used is global, * + * only one of these can be active at a time. The string s gets placed * + * in this global structure, so it needs to be in permanent memory. */ + +/**/ +static Asgment +getasg(char ***argvp, LinkList assigns) +{ + char *s = **argvp; + static struct asgment asg; + + /* sanity check for valid argument */ + 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 == '=') { + zerr("bad assignment"); + return NULL; + } + asg.name = s; + asg.flags = 0; + + /* search for `=' */ + for (; *s && *s != '='; s++); + + /* found `=', so return with a value */ + if (*s) { + *s = '\0'; + asg.value.scalar = s + 1; + } else { + /* didn't find `=', so we only have a name */ + asg.value.scalar = NULL; + } + (*argvp)++; + return &asg; +} + +/* for new special parameters */ +enum { + NS_NONE, + NS_NORMAL, + NS_SECONDS +}; + +static const struct gsu_scalar tiedarr_gsu = +{ tiedarrgetfn, tiedarrsetfn, tiedarrunsetfn }; + +/* Install a base if we are turning on a numeric option with an argument */ + +static int +typeset_setbase(const char *name, Param pm, Options ops, int on, int always) +{ + char *arg = NULL; + + if ((on & PM_INTEGER) && OPT_HASARG(ops,'i')) + arg = OPT_ARG(ops,'i'); + else if ((on & PM_EFLOAT) && OPT_HASARG(ops,'E')) + arg = OPT_ARG(ops,'E'); + else if ((on & PM_FFLOAT) && OPT_HASARG(ops,'F')) + arg = OPT_ARG(ops,'F'); + + if (arg) { + char *eptr; + int base = (int)zstrtol(arg, &eptr, 10); + if (*eptr) { + if (on & PM_INTEGER) + zwarnnam(name, "bad base value: %s", arg); + else + zwarnnam(name, "bad precision value: %s", arg); + return 1; + } + if ((on & PM_INTEGER) && (base < 2 || base > 36)) { + zwarnnam(name, "invalid base (must be 2 to 36 inclusive): %d", + base); + return 1; + } + pm->base = base; + } else if (always) + pm->base = 0; + + return 0; +} + +/* Install a width if we are turning on a padding option with an argument */ + +static int +typeset_setwidth(const char * name, Param pm, Options ops, int on, int always) +{ + char *arg = NULL; + + if ((on & PM_LEFT) && OPT_HASARG(ops,'L')) + arg = OPT_ARG(ops,'L'); + else if ((on & PM_RIGHT_B) && OPT_HASARG(ops,'R')) + arg = OPT_ARG(ops,'R'); + else if ((on & PM_RIGHT_Z) && OPT_HASARG(ops,'Z')) + arg = OPT_ARG(ops,'Z'); + + if (arg) { + char *eptr; + pm->width = (int)zstrtol(arg, &eptr, 10); + if (*eptr) { + zwarnnam(name, "bad width value: %s", arg); + return 1; + } + } else if (always) + pm->width = 0; + + return 0; +} + +/* function to set a single parameter */ + +/**/ +static Param +typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), + int on, int off, int roff, Asgment asg, Param altpm, + Options ops, int joinchar) +{ + int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly, dont_set = 0; + char *subscript; + + /* + * Do we use the existing pm? Note that this isn't the end of the + * story, because if we try and create a new pm at the same + * locallevel as an unset one we use the pm struct anyway: that's + * handled in createparam(). Here we just avoid using it for the + * present tests if it's unset. + * + * POSIXBUILTINS horror: we need to retain the 'readonly' or 'export' + * flags of an unset parameter. + */ + usepm = pm && (!(pm->node.flags & PM_UNSET) || + (isset(POSIXBUILTINS) && + (pm->node.flags & (PM_READONLY|PM_EXPORTED)))); + + /* + * We need to compare types with an existing pm if special, + * even if that's unset + */ + if (!usepm && pm && (pm->node.flags & PM_SPECIAL)) + usepm = 2; /* indicate that we preserve the PM_UNSET flag */ + + /* + * Don't use an existing param if + * - the local level has changed, and + * - we are really locallizing the parameter + */ + if (usepm && locallevel != pm->level && (on & PM_LOCAL)) { + /* + * If the original parameter was special and we're creating + * a new one, we need to keep it special. + * + * The -h (hide) flag prevents an existing special being made + * local. It can be applied either to the special or in the + * typeset/local statement for the local variable. + */ + if ((pm->node.flags & PM_SPECIAL) + && !(on & PM_HIDE) && !(pm->node.flags & PM_HIDE & ~off)) + newspecial = NS_NORMAL; + usepm = 0; + } + + /* attempting a type conversion, or making a tied colonarray? */ + tc = 0; + 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); + /* keep the parameter if just switching between floating types */ + if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT))) + usepm = 0; + } + + /* + * Extra checks if converting the type of a parameter, or if + * trying to remove readonlyness. It's dangerous doing either + * with a special or a parameter which isn't loaded yet (which + * may be special when it is loaded; we can't tell yet). + */ + if ((readonly = + ((usepm || newspecial != NS_NONE) && + (off & pm->node.flags & PM_READONLY))) || + tc) { + if (pm->node.flags & PM_SPECIAL) { + int err = 1; + if (!readonly && !strcmp(pname, "SECONDS")) + { + /* + * We allow SECONDS to change type between integer + * and floating point. If we are creating a new + * local copy we check the type here and allow + * a new special to be created with that type. + * We then need to make sure the correct type + * for the special is restored at the end of the scope. + * If we are changing the type of an existing + * parameter, we do the whole thing here. + */ + if (newspecial != NS_NONE) + { + /* + * The first test allows `typeset' to copy the + * existing type. This is the usual behaviour + * for making special parameters local. + */ + if (PM_TYPE(on) == 0 || PM_TYPE(on) == PM_INTEGER || + PM_TYPE(on) == PM_FFLOAT || PM_TYPE(on) == PM_EFLOAT) + { + newspecial = NS_SECONDS; + err = 0; /* and continue */ + tc = 0; /* but don't do a normal conversion */ + } + } else if (!setsecondstype(pm, on, off)) { + if (asg->value.scalar && + !(pm = assignsparam( + pname, ztrdup(asg->value.scalar), 0))) + return NULL; + usepm = 1; + err = 0; + } + } + if (err) + { + zerrnam(cname, "%s: can't change type of a special parameter", + pname); + return NULL; + } + } else if (pm->node.flags & PM_AUTOLOAD) { + zerrnam(cname, "%s: can't change type of autoloaded parameter", + pname); + return NULL; + } + } + else if (newspecial != NS_NONE && strcmp(pname, "SECONDS") == 0) + newspecial = NS_SECONDS; + + if (isset(POSIXBUILTINS)) { + /* + * Stricter rules about retaining readonly attribute in this case. + */ + if ((on & (PM_READONLY|PM_EXPORTED)) && + (!usepm || (pm->node.flags & PM_UNSET)) && + !ASG_VALUEP(asg)) + on |= PM_UNSET; + else if (usepm && (pm->node.flags & PM_READONLY) && + !(on & PM_READONLY)) { + zerr("read-only variable: %s", pm->node.nam); + return NULL; + } + /* This is handled by createparam(): + if (usepm && (pm->node.flags & PM_EXPORTED) && !(off & PM_EXPORTED)) + on |= PM_EXPORTED; + */ + } + + /* + * A parameter will be local if + * 1. we are re-using an existing local parameter + * or + * 2. we are not using an existing parameter, but + * i. there is already a parameter, which will be hidden + * or + * ii. we are creating a new local parameter + */ + if (usepm) { + if ((asg->flags & ASG_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 && !ASG_VALUEP(asg)) { + if (OPT_ISSET(ops,'p')) + paramtab->printnode(&pm->node, PRINT_TYPESET); + else if (!OPT_ISSET(ops,'g') && + (unset(TYPESETSILENT) || OPT_ISSET(ops,'m'))) + paramtab->printnode(&pm->node, PRINT_INCLUDEVALUE); + return pm; + } + if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) { + zerrnam(cname, "%s: restricted", pname); + return pm; + } + if ((on & PM_UNIQUE) && !(pm->node.flags & PM_READONLY & ~off)) { + Param apm; + char **x; + if (PM_TYPE(pm->node.flags) == PM_ARRAY) { + x = (*pm->gsu.a->getfn)(pm); + uniqarray(x); + if (pm->node.flags & PM_SPECIAL) { + if (zheapptr(x)) + x = zarrdup(x); + (*pm->gsu.a->setfn)(pm, x); + } else if (pm->ename && x) + arrfixenv(pm->ename, x); + } else if (PM_TYPE(pm->node.flags) == PM_SCALAR && pm->ename && + (apm = + (Param) paramtab->getnode(paramtab, pm->ename))) { + x = (*apm->gsu.a->getfn)(apm); + uniqarray(x); + if (x) + arrfixenv(pm->node.nam, x); + } + } + if (usepm == 2) /* do not change the PM_UNSET flag */ + pm->node.flags = (pm->node.flags | (on & ~PM_READONLY)) & ~off; + else { + /* + * Keep unset if using readonly in POSIX mode. + */ + if (!(on & PM_READONLY) || !isset(POSIXBUILTINS)) + off |= PM_UNSET; + pm->node.flags = (pm->node.flags | + (on & ~PM_READONLY)) & ~off; + } + if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) { + if (typeset_setwidth(cname, pm, ops, on, 0)) + return NULL; + } + if (on & (PM_INTEGER | PM_EFLOAT | PM_FFLOAT)) { + if (typeset_setbase(cname, pm, ops, on, 0)) + return NULL; + } + if (!(pm->node.flags & (PM_ARRAY|PM_HASHED))) { + if (pm->node.flags & PM_EXPORTED) { + 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); + DPUTS(ASG_ARRAYP(asg), "BUG: typeset got array value where scalar expected"); + if (asg->value.scalar && + !(pm = assignsparam(pname, ztrdup(asg->value.scalar), 0))) + return NULL; + } else if (asg->flags & ASG_ARRAY) { + int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; + if (!(pm = assignaparam(pname, asg->value.array ? + zlinklist2array(asg->value.array) : + mkarray(NULL), flags))) + return NULL; + } + if (errflag) + return NULL; + pm->node.flags |= (on & PM_READONLY); + if (OPT_ISSET(ops,'p')) + paramtab->printnode(&pm->node, PRINT_TYPESET); + return pm; + } + + if ((asg->flags & ASG_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, + * or we're converting the type of a parameter. In the + * last case only, we need to delete the old parameter. + */ + if (tc) { + /* Maintain existing readonly/exported status... */ + on |= ~off & (PM_READONLY|PM_EXPORTED) & pm->node.flags; + /* ...but turn off existing readonly so we can delete it */ + pm->node.flags &= ~PM_READONLY; + /* + * If we're just changing the type, we should keep the + * variable at the current level of localness. + */ + keeplocal = pm->level; + /* + * 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 (!ASG_VALUEP(asg) && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED))) { + asg->value.scalar = dupstring(getsparam(pname)); + asg->flags = 0; + } + /* pname may point to pm->nam which is about to disappear */ + pname = dupstring(pname); + unsetparam_pm(pm, 0, 1); + } + + if (newspecial != NS_NONE) { + Param tpm, pm2; + if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) { + zerrnam(cname, "%s: restricted", pname); + return pm; + } + if (pm->node.flags & PM_SINGLE) { + zerrnam(cname, "%s: can only have a single instance", pname); + return pm; + } + /* + * For specials, we keep the same struct but zero everything. + * Maybe it would be easier to create a new struct but copy + * the get/set methods. + */ + tpm = (Param) zshcalloc(sizeof *tpm); + + tpm->node.nam = pm->node.nam; + if (pm->ename && + (pm2 = (Param) paramtab->getnode(paramtab, pm->ename)) && + pm2->level == locallevel) { + /* This is getting silly, but anyway: if one of a path/PATH + * pair has already been made local at the current level, we + * have to make sure that the other one does not have its value + * saved: since that comes from an internal variable it will + * already reflect the local value, so restoring it on exit + * would be wrong. + * + * This problem is also why we make sure we have a copy + * of the environment entry in tpm->env, rather than relying + * on the restored value to provide it. + */ + tpm->node.flags = pm->node.flags | PM_NORESTORE; + } else { + copyparam(tpm, pm, 1); + } + tpm->old = pm->old; + tpm->level = pm->level; + tpm->base = pm->base; + tpm->width = pm->width; + if (pm->env) + delenv(pm); + tpm->env = NULL; + + pm->old = tpm; + /* + * The remaining on/off flags should be harmless to use, + * because we've checked for unpleasant surprises above. + */ + pm->node.flags = (PM_TYPE(pm->node.flags) | on | PM_SPECIAL) & ~off; + /* + * Readonlyness of special parameters must be preserved. + */ + pm->node.flags |= tpm->node.flags & PM_READONLY; + if (newspecial == NS_SECONDS) { + /* We save off the raw internal value of the SECONDS var */ + tpm->u.dval = getrawseconds(); + setsecondstype(pm, on, off); + } + + /* + * Final tweak: if we've turned on one of the flags with + * numbers, we should use the appropriate integer. + */ + if (on & (PM_LEFT|PM_RIGHT_B|PM_RIGHT_Z)) { + if (typeset_setwidth(cname, pm, ops, on, 1)) + return NULL; + } + if (on & (PM_INTEGER|PM_EFLOAT|PM_FFLOAT)) { + if (typeset_setbase(cname, pm, ops, on, 1)) + return NULL; + } + } else if ((subscript = strchr(pname, '['))) { + if (on & PM_READONLY) { + zerrnam(cname, + "%s: can't create readonly array elements", pname); + return NULL; + } else if ((on & PM_LOCAL) && locallevel) { + *subscript = 0; + pm = (Param) (paramtab == realparamtab ? + /* getnode2() to avoid autoloading */ + paramtab->getnode2(paramtab, pname) : + paramtab->getnode(paramtab, pname)); + *subscript = '['; + if (!pm || pm->level != locallevel) { + zerrnam(cname, + "%s: can't create local array elements", pname); + return NULL; + } + } + 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 = assignsparam( + pname, + ztrdup(asg->value.scalar ? asg->value.scalar : ""), 0))) + return NULL; + dont_set = 1; + asg->flags = 0; + keeplocal = 0; + on = pm->node.flags; + } else if (PM_TYPE(on) == PM_ARRAY && ASG_ARRAYP(asg)) { + int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; + if (!(pm = assignaparam(pname, asg->value.array ? + zlinklist2array(asg->value.array) : + mkarray(NULL), flags))) + return NULL; + dont_set = 1; + keeplocal = 0; + on = pm->node.flags; + } else { + zerrnam(cname, + "%s: inconsistent array element or slice assignment", pname); + return NULL; + } + } + /* + * As we can hide existing parameters, we allow a name if + * it's not a normal identifier but is one of the special + * set found in the parameter table. The second test is + * because we can set individual positional parameters; + * however "0" is not a positional parameter and is OK. + * + * It would be neater to extend isident() and be clearer + * about where we allow various parameter types. It's + * not entirely clear to me isident() should reject + * specially named parameters given that it accepts digits. + */ + else if ((isident(pname) || paramtab->getnode(paramtab, pname)) + && (!idigit(*pname) || !strcmp(pname, "0"))) { + /* + * Create a new node for a parameter with the flags in `on' minus the + * readonly flag + */ + pm = createparam(pname, on & ~PM_READONLY); + if (!pm) { + if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | + PM_INTEGER | PM_EFLOAT | PM_FFLOAT)) + zerrnam(cname, "can't change variable attribute: %s", pname); + return NULL; + } + if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) { + if (typeset_setwidth(cname, pm, ops, on, 0)) + return NULL; + } + if (on & (PM_INTEGER | PM_EFLOAT | PM_FFLOAT)) { + if (typeset_setbase(cname, pm, ops, on, 0)) + return NULL; + } + } else { + if (idigit(*pname)) + zerrnam(cname, "not an identifier: %s", pname); + else + zerrnam(cname, "not valid in this context: %s", pname); + return NULL; + } + + if (altpm && PM_TYPE(pm->node.flags) == PM_SCALAR) { + /* + * It seems safer to set this here than in createparam(), + * to make sure we only ever use the colonarr functions + * when u.data is correctly set. + */ + struct tieddata *tdp = (struct tieddata *) + zalloc(sizeof(struct tieddata)); + if (!tdp) + return NULL; + tdp->joinchar = joinchar; + tdp->arrptr = &altpm->u.arr; + + pm->gsu.s = &tiedarr_gsu; + pm->u.data = tdp; + } + + if (keeplocal) + pm->level = keeplocal; + else if (on & PM_LOCAL) + pm->level = locallevel; + if (ASG_VALUEP(asg) && !dont_set) { + Param ipm = pm; + if (pm->node.flags & (PM_ARRAY|PM_HASHED)) { + char **arrayval; + int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; + if (!ASG_ARRAYP(asg)) { + /* + * Attempt to assign a scalar value to an array. + * This can happen if the array is special. + * We'll be lenient and guess what the user meant. + * This is how normal assigment works. + */ + if (*asg->value.scalar) { + /* Array with one value */ + arrayval = mkarray(ztrdup(asg->value.scalar)); + } else { + /* Empty array */ + arrayval = mkarray(NULL); + } + } else if (asg->value.array) + arrayval = zlinklist2array(asg->value.array); + else + arrayval = mkarray(NULL); + if (!(pm=assignaparam(pname, arrayval, flags))) + return NULL; + } else { + DPUTS(ASG_ARRAYP(asg), "BUG: inconsistent array value for scalar"); + if (!(pm = assignsparam(pname, ztrdup(asg->value.scalar), 0))) + return NULL; + } + if (pm != ipm) { + DPUTS(ipm->node.flags != pm->node.flags, + "BUG: parameter recreated with wrong flags"); + unsetparam_pm(ipm, 0, 1); + } + } else if (newspecial != NS_NONE && + !(pm->old->node.flags & (PM_NORESTORE|PM_READONLY))) { + /* + * We need to use the special setting function to re-initialise + * the special parameter to empty. + */ + switch (PM_TYPE(pm->node.flags)) { + case PM_SCALAR: + pm->gsu.s->setfn(pm, ztrdup("")); + break; + case PM_INTEGER: + /* + * Restricted integers are dangerous to initialize to 0, + * so don't do that. + */ + if (!(pm->old->node.flags & PM_RESTRICTED)) + pm->gsu.i->setfn(pm, 0); + break; + case PM_EFLOAT: + case PM_FFLOAT: + pm->gsu.f->setfn(pm, 0.0); + break; + case PM_ARRAY: + pm->gsu.a->setfn(pm, mkarray(NULL)); + break; + case PM_HASHED: + pm->gsu.h->setfn(pm, newparamtable(17, pm->node.nam)); + break; + } + } + pm->node.flags |= (on & PM_READONLY); + + if (OPT_ISSET(ops,'p')) + paramtab->printnode(&pm->node, PRINT_TYPESET); + + return pm; +} + +/* + * 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, LinkList assigns, Options ops, int func) +{ + Param pm; + Asgment asg; + Patprog pprog; + char *optstr = TYPESET_OPTSTR; + 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')) + return bin_functions(name, argv, ops, func); + + /* POSIX handles "readonly" specially */ + if (func == BIN_READONLY && isset(POSIXBUILTINS) && !OPT_PLUS(ops, 'g')) + ops->ind['g'] = 1; + + /* Translate the options into PM_* flags. * + * Unfortunately, this depends on the order * + * these flags are defined in zsh.h */ + for (; *optstr; optstr++, bit <<= 1) + { + int optval = STOUC(*optstr); + if (OPT_MINUS(ops,optval)) + on |= bit; + else if (OPT_PLUS(ops,optval)) + off |= bit; + } + roff = off; + + /* Sanity checks on the options. Remove conflicting options. */ + if (on & PM_FFLOAT) { + off |= PM_UPPER | PM_ARRAY | PM_HASHED | PM_INTEGER | PM_EFLOAT; + /* Allow `float -F' to work even though float sets -E by default */ + on &= ~PM_EFLOAT; + } + if (on & PM_EFLOAT) + off |= PM_UPPER | PM_ARRAY | PM_HASHED | PM_INTEGER | PM_FFLOAT; + if (on & PM_INTEGER) + off |= PM_UPPER | PM_ARRAY | PM_HASHED | PM_EFLOAT | PM_FFLOAT; + /* + * Allowing -Z with -L is a feature: left justify, suppressing + * leading zeroes. + */ + if (on & (PM_LEFT|PM_RIGHT_Z)) + off |= PM_RIGHT_B; + if (on & PM_RIGHT_B) + off |= PM_LEFT | PM_RIGHT_Z; + if (on & PM_UPPER) + off |= PM_LOWER; + if (on & PM_LOWER) + off |= PM_UPPER; + if (on & PM_HASHED) + off |= PM_ARRAY; + if (on & PM_TIED) + off |= PM_INTEGER | PM_EFLOAT | PM_FFLOAT | PM_ARRAY | PM_HASHED; + + on &= ~off; + + queue_signals(); + + /* Given no arguments, list whatever the options specify. */ + if (OPT_ISSET(ops,'p')) { + printflags |= PRINT_TYPESET; + if (OPT_HASARG(ops,'p')) { + char *eptr; + int pflag = (int)zstrtol(OPT_ARG(ops,'p'), &eptr, 10); + if (pflag == 1 && !*eptr) + printflags |= PRINT_LINE; + else if (pflag || *eptr) { + zwarnnam(name, "bad argument to -p: %s", OPT_ARG(ops,'p')); + unqueue_signals(); + return 1; + } + /* -p0 treated as -p for consistency */ + } + } + hasargs = *argv != NULL || (assigns && firstnode(assigns)); + if (!hasargs) { + if (!OPT_ISSET(ops,'p')) { + if (!(on|roff)) + printflags |= PRINT_TYPE; + if (roff || OPT_ISSET(ops,'+')) + printflags |= PRINT_NAMEONLY; + } + scanhashtable(paramtab, 1, on|roff, 0, paramtab->printnode, printflags); + unqueue_signals(); + return 0; + } + + if (!(OPT_ISSET(ops,'g') || OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m')) || + OPT_PLUS(ops,'g') || *name == 'l' || + (!isset(GLOBALEXPORT) && !OPT_ISSET(ops,'g'))) + on |= PM_LOCAL; + + if (on & PM_TIED) { + Param apm; + struct asgment asg0, asg2; + char *oldval = NULL, *joinstr; + int joinchar, nargs; + + if (OPT_ISSET(ops,'m')) { + zwarnnam(name, "incompatible options for -T"); + unqueue_signals(); + return 1; + } + on &= ~off; + 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; + } + + if (!(asg = getasg(&argv, assigns))) { + unqueue_signals(); + return 1; + } + asg0 = *asg; + 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); + return 1; + } + if (strchr(asg0.name, '[') || strchr(asg->name, '[')) { + unqueue_signals(); + 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 + * will be unset when we retie the array. This is all + * so that typeset -T is idempotent. + * + * We also need to remember here whether the damn thing is + * exported and pass that along. Isn't the world complicated? + */ + if ((pm = (Param) paramtab->getnode(paramtab, asg0.name)) + && !(pm->node.flags & PM_UNSET) + && (locallevel == pm->level || !(on & PM_LOCAL))) { + if (pm->node.flags & PM_TIED) { + unqueue_signals(); + 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; + int flags = (asg->flags & ASG_KEY_VALUE) ? + ASSPM_KEY_VALUE : 0; + /* Update join character */ + tdp->joinchar = joinchar; + if (asg0.value.scalar) + assignsparam(asg0.name, ztrdup(asg0.value.scalar), 0); + else if (asg->value.array) + assignaparam( + asg->name, zlinklist2array(asg->value.array),flags); + return 0; + } else { + zwarnnam(name, "can't tie already tied scalar: %s", + asg0.name); + } + return 1; + } + 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); + } + /* + * 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.flags = 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, &asg2, NULL, ops, 0))) { + if (oldval) + zsfree(oldval); + unqueue_signals(); + return 1; + } + /* + * Create the tied colonarray. We make it as a normal scalar + * and fix up the oddities later. + */ + if (!(pm=typeset_single(name, asg0.name, + (Param)paramtab->getnode(paramtab, + asg0.name), + func, on, off, roff, &asg0, apm, + ops, joinchar))) { + if (oldval) + zsfree(oldval); + unsetparam_pm(apm, 1, 1); + unqueue_signals(); + return 1; + } + + /* + * pm->ename is only deleted when the struct is, so + * we need to free it here if it already exists. + */ + if (pm->ename) + zsfree(pm->ename); + pm->ename = ztrdup(asg->name); + if (apm->ename) + zsfree(apm->ename); + apm->ename = ztrdup(asg0.name); + if (asg->value.array) { + int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; + assignaparam(asg->name, zlinklist2array(asg->value.array), flags); + } else if (oldval) + assignsparam(asg0.name, oldval, 0); + unqueue_signals(); + + return 0; + } + if (off & PM_TIED) { + unqueue_signals(); + zerrnam(name, "use unset to remove tied variables"); + return 1; + } + + /* With the -m option, treat arguments as glob patterns */ + if (OPT_ISSET(ops,'m')) { + if (!OPT_ISSET(ops,'p')) { + if (!(on|roff)) + printflags |= PRINT_TYPE; + if (!on) + printflags |= PRINT_NAMEONLY; + } + + 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", asg->name); + returnval = 1; + continue; + } + if (OPT_PLUS(ops,'m') && !ASG_VALUEP(asg)) { + scanmatchtable(paramtab, pprog, 1, on|roff, 0, + paramtab->printnode, printflags); + continue; + } + /* + * Search through the parameter table and change all parameters + * matching the glob pattern to have these flags and/or value. + * Bad news: if the parameter gets altered, e.g. by + * a type conversion, then paramtab can be shifted around, + * so we need to store the parameters to alter on a separate + * list for later use. + */ + for (i = 0; i < paramtab->hsize; i++) { + for (pm = (Param) paramtab->nodes[i]; pm; + pm = (Param) pm->node.next) { + if (((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) || + (pm->node.flags & PM_UNSET)) + continue; + if (pattry(pprog, pm->node.nam)) + addlinknode(pmlist, pm); + } + } + for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) { + pm = (Param) getdata(pmnode); + if (!typeset_single(name, pm->node.nam, pm, func, on, off, roff, + asg, NULL, ops, 0)) + returnval = 1; + } + } + unqueue_signals(); + return returnval; + } + + /* Take arguments literally. Don't glob */ + while ((asg = getasg(&argv, assigns))) { + HashNode hn = (paramtab == realparamtab ? + /* getnode2() to avoid autoloading */ + paramtab->getnode2(paramtab, asg->name) : + paramtab->getnode(paramtab, asg->name)); + if (OPT_ISSET(ops,'p')) { + if (hn) + paramtab->printnode(hn, printflags); + else { + zwarnnam(name, "no such variable: %s", asg->name); + returnval = 1; + } + continue; + } + if (!typeset_single(name, asg->name, (Param)hn, + func, on, off, roff, asg, NULL, + ops, 0)) + returnval = 1; + } + unqueue_signals(); + return returnval; +} + +/* Helper for bin_functions() when run as "autoload -X" */ + +/**/ +int +eval_autoload(Shfunc shf, char *name, Options ops, int func) +{ + if (!(shf->node.flags & PM_UNDEFINED)) + return 1; + + if (shf->funcdef) { + freeeprog(shf->funcdef); + shf->funcdef = &dummy_eprog; + } + if (OPT_MINUS(ops,'X')) { + char *fargv[3]; + fargv[0] = name; + fargv[1] = "\"$@\""; + fargv[2] = 0; + shf->funcdef = mkautofn(shf); + return bin_eval(name, fargv, ops, func); + } + + return !loadautofn(shf, (OPT_ISSET(ops,'k') ? 2 : + (OPT_ISSET(ops,'z') ? 0 : 1)), 1, + OPT_ISSET(ops,'d')); +} + +/* Helper for bin_functions() for -X and -r options */ + +/**/ +static int +check_autoload(Shfunc shf, char *name, Options ops, int func) +{ + if (OPT_ISSET(ops,'X')) + { + return eval_autoload(shf, name, ops, func); + } + if ((OPT_ISSET(ops,'r') || OPT_ISSET(ops,'R')) && + (shf->node.flags & PM_UNDEFINED)) + { + char *dir_path; + if (shf->filename && (shf->node.flags & PM_LOADDIR)) { + char *spec_path[2]; + spec_path[0] = shf->filename; + spec_path[1] = NULL; + if (getfpfunc(shf->node.nam, NULL, &dir_path, spec_path, 1)) { + /* shf->filename is already correct. */ + return 0; + } + if (!OPT_ISSET(ops,'d')) { + if (OPT_ISSET(ops,'R')) { + zerr("%s: function definition file not found", + shf->node.nam); + return 1; + } + return 0; + } + } + if (getfpfunc(shf->node.nam, NULL, &dir_path, NULL, 1)) { + dircache_set(&shf->filename, NULL); + if (*dir_path != '/') { + dir_path = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), + "/", dir_path); + dir_path = xsymlink(dir_path, 1); + } + dircache_set(&shf->filename, dir_path); + shf->node.flags |= PM_LOADDIR; + return 0; + } + if (OPT_ISSET(ops,'R')) { + zerr("%s: function definition file not found", + shf->node.nam); + return 1; + } + /* with -r, we don't flag an error, just let it be found later. */ + } + return 0; +} + +/* List a user-defined math function. */ +static void +listusermathfunc(MathFunc p) +{ + int showargs; + + if (p->module) + showargs = 3; + else if (p->maxargs != (p->minargs ? p->minargs : -1)) + showargs = 2; + else if (p->minargs) + showargs = 1; + else + showargs = 0; + + printf("functions -M%s %s", (p->flags & MFF_STR) ? "s" : "", p->name); + if (showargs) { + printf(" %d", p->minargs); + showargs--; + } + if (showargs) { + printf(" %d", p->maxargs); + showargs--; + } + if (showargs) { + /* + * function names are not required to consist of ident characters + */ + putchar(' '); + quotedzputs(p->module, stdout); + showargs--; + } + putchar('\n'); +} + + +static void +add_autoload_function(Shfunc shf, char *funcname) +{ + char *nam; + if (*funcname == '/' && funcname[1] && + (nam = strrchr(funcname, '/')) && nam[1] && + (shf->node.flags & PM_UNDEFINED)) { + char *dir; + nam = strrchr(funcname, '/'); + if (nam == funcname) { + dir = "/"; + } else { + *nam++ = '\0'; + dir = funcname; + } + dircache_set(&shf->filename, NULL); + dircache_set(&shf->filename, dir); + shf->node.flags |= PM_LOADDIR; + shf->node.flags |= PM_ABSPATH_USED; + shfunctab->addnode(shfunctab, ztrdup(nam), shf); + } else { + Shfunc shf2; + Funcstack fs; + const char *calling_f = NULL; + char buf[PATH_MAX+1]; + + /* Find calling function */ + for (fs = funcstack; fs; fs = fs->prev) { + if (fs->tp == FS_FUNC && fs->name && (!shf->node.nam || 0 != strcmp(fs->name,shf->node.nam))) { + calling_f = fs->name; + break; + } + } + + /* Get its directory */ + if (calling_f) { + /* Should contain load directory, and be loaded via absolute path */ + if ((shf2 = (Shfunc) shfunctab->getnode2(shfunctab, calling_f)) + && (shf2->node.flags & PM_LOADDIR) && (shf2->node.flags & PM_ABSPATH_USED) + && shf2->filename) + { + if (strlen(shf2->filename) + strlen(funcname) + 1 < PATH_MAX) + { + sprintf(buf, "%s/%s", shf2->filename, funcname); + /* Set containing directory if the function file + * exists (do normal FPATH processing otherwise) */ + if (!access(buf, R_OK)) { + dircache_set(&shf->filename, NULL); + dircache_set(&shf->filename, shf2->filename); + shf->node.flags |= PM_LOADDIR; + shf->node.flags |= PM_ABSPATH_USED; + } + } + } + } + + shfunctab->addnode(shfunctab, ztrdup(funcname), shf); + } +} + +/* Display or change the attributes of shell functions. * + * If called as autoload, it will define a new autoloaded * + * (undefined) shell function. */ + +/**/ +int +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, expand = 0; + + /* Do we have any flags defined? */ + if (OPT_PLUS(ops,'u')) + off |= PM_UNDEFINED; + else if (OPT_MINUS(ops,'u') || OPT_ISSET(ops,'X')) + on |= PM_UNDEFINED; + if (OPT_MINUS(ops,'U')) + on |= PM_UNALIASED|PM_UNDEFINED; + else if (OPT_PLUS(ops,'U')) + off |= PM_UNALIASED; + if (OPT_MINUS(ops,'t')) + on |= PM_TAGGED; + else if (OPT_PLUS(ops,'t')) + off |= PM_TAGGED; + if (OPT_MINUS(ops,'T')) + on |= PM_TAGGED_LOCAL; + else if (OPT_PLUS(ops,'T')) + off |= PM_TAGGED_LOCAL; + if (OPT_MINUS(ops,'W')) + on |= PM_WARNNESTED; + else if (OPT_PLUS(ops,'W')) + off |= PM_WARNNESTED; + roff = off; + if (OPT_MINUS(ops,'z')) { + on |= PM_ZSHSTORED; + off |= PM_KSHSTORED; + } else if (OPT_PLUS(ops,'z')) { + off |= PM_ZSHSTORED; + roff |= PM_ZSHSTORED; + } + if (OPT_MINUS(ops,'k')) { + on |= PM_KSHSTORED; + off |= PM_ZSHSTORED; + } else if (OPT_PLUS(ops,'k')) { + off |= PM_KSHSTORED; + roff |= PM_KSHSTORED; + } + if (OPT_MINUS(ops,'d')) { + on |= PM_CUR_FPATH; + off |= PM_CUR_FPATH; + } else if (OPT_PLUS(ops,'d')) { + off |= PM_CUR_FPATH; + roff |= PM_CUR_FPATH; + } + + if ((off & PM_UNDEFINED) || (OPT_ISSET(ops,'k') && OPT_ISSET(ops,'z')) || + (OPT_ISSET(ops,'x') && !OPT_HASARG(ops,'x')) || + (OPT_MINUS(ops,'X') && (OPT_ISSET(ops,'m') || !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; + + if (OPT_MINUS(ops,'M') || OPT_PLUS(ops,'M')) { + MathFunc p, q, prev; + /* + * Add/remove/list function as mathematical. + */ + if (on || off || pflags || OPT_ISSET(ops,'X') || OPT_ISSET(ops,'u') + || OPT_ISSET(ops,'U') || OPT_ISSET(ops,'w')) { + zwarnnam(name, "invalid option(s)"); + return 1; + } + if (!*argv) { + /* List functions. */ + queue_signals(); + for (p = mathfuncs; p; p = p->next) + if (p->flags & MFF_USERFUNC) + listusermathfunc(p); + unqueue_signals(); + } else if (OPT_ISSET(ops,'m')) { + /* List matching functions. */ + for (; *argv; argv++) { + queue_signals(); + tokenize(*argv); + if ((pprog = patcompile(*argv, PAT_STATIC, 0))) { + for (p = mathfuncs, q = NULL; p; q = p) { + MathFunc next; + do { + next = NULL; + if ((p->flags & MFF_USERFUNC) && + pattry(pprog, p->name)) { + if (OPT_PLUS(ops,'M')) { + next = p->next; + removemathfunc(q, p); + p = next; + } else + listusermathfunc(p); + } + /* if we deleted one, retry with the new p */ + } while (next); + if (p) + p = p->next; + } + } else { + untokenize(*argv); + zwarnnam(name, "bad pattern : %s", *argv); + returnval = 1; + } + unqueue_signals(); + } + } else if (OPT_PLUS(ops,'M')) { + /* Delete functions. -m is allowed but is handled above. */ + for (; *argv; argv++) { + queue_signals(); + for (p = mathfuncs, q = NULL; p; q = p, p = p->next) { + if (!strcmp(p->name, *argv)) { + if (!(p->flags & MFF_USERFUNC)) { + zwarnnam(name, "+M %s: is a library function", + *argv); + returnval = 1; + break; + } + removemathfunc(q, p); + break; + } + } + unqueue_signals(); + } + } else { + /* Add a function */ + int minargs, maxargs; + char *funcname = *argv++; + char *modname = NULL; + char *ptr; + + if (OPT_ISSET(ops,'s')) { + minargs = maxargs = 1; + } else { + minargs = 0; + maxargs = -1; + } + + ptr = itype_end(funcname, IIDENT, 0); + if (idigit(*funcname) || funcname == ptr || *ptr) { + zwarnnam(name, "-M %s: bad math function name", funcname); + return 1; + } + + if (*argv) { + minargs = (int)zstrtol(*argv, &ptr, 0); + if (minargs < 0 || *ptr) { + zwarnnam(name, "-M: invalid min number of arguments: %s", + *argv); + return 1; + } + if (OPT_ISSET(ops,'s') && minargs != 1) { + zwarnnam(name, "-Ms: must take a single string argument"); + return 1; + } + maxargs = minargs; + argv++; + } + if (*argv) { + maxargs = (int)zstrtol(*argv, &ptr, 0); + if (maxargs < -1 || + (maxargs != -1 && maxargs < minargs) || + *ptr) { + zwarnnam(name, + "-M: invalid max number of arguments: %s", + *argv); + return 1; + } + if (OPT_ISSET(ops,'s') && maxargs != 1) { + zwarnnam(name, "-Ms: must take a single string argument"); + return 1; + } + argv++; + } + if (*argv) + modname = *argv++; + if (*argv) { + zwarnnam(name, "-M: too many arguments"); + return 1; + } + + p = (MathFunc)zshcalloc(sizeof(struct mathfunc)); + p->name = ztrdup(funcname); + p->flags = MFF_USERFUNC; + if (OPT_ISSET(ops,'s')) + p->flags |= MFF_STR; + p->module = modname ? ztrdup(modname) : NULL; + p->minargs = minargs; + p->maxargs = maxargs; + + queue_signals(); + for (q = mathfuncs, prev = NULL; q; prev = q, q = q->next) { + if (!strcmp(q->name, funcname)) { + removemathfunc(prev, q); + break; + } + } + + p->next = mathfuncs; + mathfuncs = p; + unqueue_signals(); + } + + return returnval; + } + + if (OPT_MINUS(ops,'X')) { + Funcstack fs; + char *funcname = NULL; + int ret; + if (*argv && argv[1]) { + zwarnnam(name, "-X: too many arguments"); + return 1; + } + queue_signals(); + for (fs = funcstack; fs; fs = fs->prev) { + if (fs->tp == FS_FUNC) { + /* + * dupstring here is paranoia but unlikely to be + * problematic + */ + funcname = dupstring(fs->name); + break; + } + } + if (!funcname) + { + zerrnam(name, "bad autoload"); + ret = 1; + } else { + if ((shf = (Shfunc) shfunctab->getnode(shfunctab, funcname))) { + DPUTS(!shf->funcdef, + "BUG: Calling autoload from empty function"); + } else { + shf = (Shfunc) zshcalloc(sizeof *shf); + shfunctab->addnode(shfunctab, ztrdup(funcname), shf); + } + if (*argv) { + dircache_set(&shf->filename, NULL); + dircache_set(&shf->filename, *argv); + on |= PM_LOADDIR; + } + shf->node.flags = on; + ret = eval_autoload(shf, funcname, ops, func); + } + unqueue_signals(); + return ret; + } else if (!*argv) { + /* If no arguments given, we will print functions. If flags * + * are given, we will print only functions containing these * + * flags, else we'll print them all. */ + int ret = 0; + + queue_signals(); + if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u')) + on &= ~PM_UNDEFINED; + scanshfunc(1, on|off, DISABLED, shfunctab->printnode, + pflags, expand); + unqueue_signals(); + return ret; + } + + /* With the -m option, treat arguments as glob patterns */ + if (OPT_ISSET(ops,'m')) { + on &= ~PM_UNDEFINED; + for (; *argv; argv++) { + queue_signals(); + /* expand argument */ + tokenize(*argv); + if ((pprog = patcompile(*argv, PAT_STATIC, 0))) { + /* with no options, just print all functions matching the glob pattern */ + if (!(on|off) && !OPT_ISSET(ops,'X')) { + 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++) { + for (shf = (Shfunc) shfunctab->nodes[i]; shf; + shf = (Shfunc) shf->node.next) + if (pattry(pprog, shf->node.nam) && + !(shf->node.flags & DISABLED)) { + shf->node.flags = (shf->node.flags | + (on & ~PM_UNDEFINED)) & ~off; + if (check_autoload(shf, shf->node.nam, + ops, func)) { + returnval = 1; + } + } + } + } + } else { + untokenize(*argv); + zwarnnam(name, "bad pattern : %s", *argv); + returnval = 1; + } + unqueue_signals(); + } + return returnval; + } + + /* Take the arguments literally -- do not glob */ + queue_signals(); + for (; *argv; argv++) { + if (OPT_ISSET(ops,'w')) + returnval = dump_autoload(name, *argv, on, ops, func); + else if ((shf = (Shfunc) shfunctab->getnode(shfunctab, *argv))) { + /* if any flag was given */ + if (on|off) { + /* turn on/off the given flags */ + shf->node.flags = (shf->node.flags | (on & ~PM_UNDEFINED)) & ~off; + if (check_autoload(shf, shf->node.nam, ops, func)) + returnval = 1; + } else + /* no flags, so just print */ + printshfuncexpand(&shf->node, pflags, expand); + } else if (on & PM_UNDEFINED) { + int signum = -1, ok = 1; + + if (!strncmp(*argv, "TRAP", 4) && + (signum = getsignum(*argv + 4)) != -1) { + /* + * Because of the possibility of alternative names, + * we must remove the trap explicitly. + */ + removetrapnode(signum); + } + + if (**argv == '/') { + char *base = strrchr(*argv, '/') + 1; + if (*base && + (shf = (Shfunc) shfunctab->getnode(shfunctab, base))) { + char *dir; + /* turn on/off the given flags */ + shf->node.flags = + (shf->node.flags | (on & ~PM_UNDEFINED)) & ~off; + if (shf->node.flags & PM_UNDEFINED) { + /* update path if not yet loaded */ + if (base == *argv + 1) + dir = "/"; + else { + dir = *argv; + base[-1] = '\0'; + } + dircache_set(&shf->filename, NULL); + dircache_set(&shf->filename, dir); + } + if (check_autoload(shf, shf->node.nam, ops, func)) + returnval = 1; + continue; + } + } + + /* Add a new undefined (autoloaded) function to the * + * hash table with the corresponding flags set. */ + shf = (Shfunc) zshcalloc(sizeof *shf); + shf->node.flags = on; + shf->funcdef = mkautofn(shf); + shfunc_set_sticky(shf); + add_autoload_function(shf, *argv); + + if (signum != -1) { + if (settrap(signum, NULL, ZSIG_FUNC)) { + shfunctab->removenode(shfunctab, *argv); + shfunctab->freenode(&shf->node); + returnval = 1; + ok = 0; + } + } + + if (ok && check_autoload(shf, shf->node.nam, ops, func)) + returnval = 1; + } else + returnval = 1; + } + unqueue_signals(); + return returnval; +} + +/**/ +Eprog +mkautofn(Shfunc shf) +{ + Eprog p; + + p = (Eprog) zalloc(sizeof(*p)); + p->len = 5 * sizeof(wordcode); + p->prog = (Wordcode) zalloc(p->len); + p->strs = NULL; + p->shf = shf; + p->npats = 0; + p->nref = 1; /* allocated from permanent storage */ + p->pats = (Patprog *) p->prog; + p->flags = EF_REAL; + p->dump = NULL; + + p->prog[0] = WCB_LIST((Z_SYNC | Z_END), 0); + p->prog[1] = WCB_SUBLIST(WC_SUBLIST_END, 0, 3); + p->prog[2] = WCB_PIPE(WC_PIPE_END, 0); + p->prog[3] = WCB_AUTOFN(); + p->prog[4] = WCB_END(); + + return p; +} + +/* unset: unset parameters */ + +/**/ +int +bin_unset(char *name, char **argv, Options ops, int func) +{ + Param pm, next; + Patprog pprog; + char *s; + int match = 0, returnval = 0; + int i; + + /* unset -f is the same as unfunction */ + if (OPT_ISSET(ops,'f')) + return bin_unhash(name, argv, ops, func); + + /* with -m option, treat arguments as glob patterns */ + if (OPT_ISSET(ops,'m')) { + while ((s = *argv++)) { + queue_signals(); + /* expand */ + tokenize(s); + if ((pprog = patcompile(s, PAT_STATIC, NULL))) { + /* Go through the parameter table, and unset any matches */ + for (i = 0; i < paramtab->hsize; i++) { + for (pm = (Param) paramtab->nodes[i]; pm; pm = next) { + /* record pointer to next, since we may free this one */ + next = (Param) pm->node.next; + if ((!(pm->node.flags & PM_RESTRICTED) || + unset(RESTRICTED)) && + pattry(pprog, pm->node.nam)) { + unsetparam_pm(pm, 0, 1); + match++; + } + } + } + } else { + untokenize(s); + zwarnnam(name, "bad pattern : %s", s); + returnval = 1; + } + unqueue_signals(); + } + /* If we didn't match anything, we return 1. */ + if (!match) + returnval = 1; + return returnval; + } + + /* do not glob -- unset the given parameter */ + queue_signals(); + while ((s = *argv++)) { + char *ss = strchr(s, '['), *subscript = 0; + if (ss) { + char *sse; + *ss = 0; + if ((sse = parse_subscript(ss+1, 1, ']'))) { + *sse = 0; + subscript = dupstring(ss+1); + *sse = ']'; + remnulargs(subscript); + untokenize(subscript); + } + } + if ((ss && !subscript) || !isident(s)) { + if (ss) + *ss = '['; + zerrnam(name, "%s: invalid parameter name", s); + returnval = 1; + continue; + } + pm = (Param) (paramtab == realparamtab ? + /* getnode2() to avoid autoloading */ + paramtab->getnode2(paramtab, s) : + paramtab->getnode(paramtab, s)); + /* + * Unsetting an unset variable is not an error. + * This appears to be reasonably standard behaviour. + */ + if (!pm) + continue; + else if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) { + zerrnam(name, "%s: restricted", pm->node.nam); + returnval = 1; + } else if (ss) { + if (PM_TYPE(pm->node.flags) == PM_HASHED) { + HashTable tht = paramtab; + if ((paramtab = pm->gsu.h->getfn(pm))) + unsetparam(subscript); + paramtab = tht; + } else if (PM_TYPE(pm->node.flags) == PM_SCALAR || + PM_TYPE(pm->node.flags) == PM_ARRAY) { + struct value vbuf; + vbuf.isarr = (PM_TYPE(pm->node.flags) == PM_ARRAY ? + SCANPM_ARRONLY : 0); + vbuf.pm = pm; + vbuf.flags = 0; + vbuf.start = 0; + vbuf.end = -1; + vbuf.arr = 0; + *ss = '['; + if (getindex(&ss, &vbuf, SCANPM_ASSIGNING) == 0 && + vbuf.pm && !(vbuf.pm->node.flags & PM_UNSET)) { + if (PM_TYPE(pm->node.flags) == PM_SCALAR) { + setstrvalue(&vbuf, ztrdup("")); + } else { + /* start is after the element for reverse index */ + int start = vbuf.start - !!(vbuf.flags & VALFLAG_INV); + if (arrlen_gt(vbuf.pm->u.arr, start)) { + char *arr[2]; + arr[0] = ""; + arr[1] = 0; + setarrvalue(&vbuf, zarrdup(arr)); + } + } + } + returnval = errflag; + errflag &= ~ERRFLAG_ERROR; + } else { + zerrnam(name, "%s: invalid element for unset", s); + returnval = 1; + } + } else { + if (unsetparam_pm(pm, 0, 1)) + returnval = 1; + } + if (ss) + *ss = '['; + } + unqueue_signals(); + return returnval; +} + +/* type, whence, which, command */ + +static LinkList matchednodes; + +static void +fetchcmdnamnode(HashNode hn, UNUSED(int printflags)) +{ + Cmdnam cn = (Cmdnam) hn; + addlinknode(matchednodes, cn->node.nam); +} + +/**/ +int +bin_whence(char *nam, char **argv, Options ops, int func) +{ + HashNode hn; + Patprog pprog; + int returnval = 0; + int printflags = 0; + int aliasflags; + int csh, all, v, wd; + int informed = 0; + int expand = 0; + char *cnam, **allmatched = 0; + + /* Check some option information */ + csh = OPT_ISSET(ops,'c'); + v = OPT_ISSET(ops,'v'); + 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')) + printflags |= PRINT_WHENCE_CSH; + else if (OPT_ISSET(ops,'v')) + printflags |= PRINT_WHENCE_VERBOSE; + else + printflags |= PRINT_WHENCE_SIMPLE; + if (OPT_ISSET(ops,'f')) + printflags |= PRINT_WHENCE_FUNCDEF; + + if (func == BIN_COMMAND) + if (OPT_ISSET(ops,'V')) { + printflags = aliasflags = PRINT_WHENCE_VERBOSE; + v = 1; + } else { + aliasflags = PRINT_LIST; + printflags = PRINT_WHENCE_SIMPLE; + v = 0; + } + else + aliasflags = printflags; + + /* With -m option -- treat arguments as a glob patterns */ + if (OPT_ISSET(ops,'m')) { + cmdnamtab->filltable(cmdnamtab); + if (all) { + pushheap(); + matchednodes = newlinklist(); + } + queue_signals(); + for (; *argv; argv++) { + /* parse the pattern */ + tokenize(*argv); + if (!(pprog = patcompile(*argv, PAT_STATIC, NULL))) { + untokenize(*argv); + zwarnnam(nam, "bad pattern : %s", *argv); + returnval = 1; + continue; + } + if (!OPT_ISSET(ops,'p')) { + /* -p option is for path search only. * + * We're not using it, so search for ... */ + + /* aliases ... */ + informed += + scanmatchtable(aliastab, pprog, 1, 0, DISABLED, + aliastab->printnode, printflags); + + /* and reserved words ... */ + informed += + scanmatchtable(reswdtab, pprog, 1, 0, DISABLED, + reswdtab->printnode, printflags); + + /* and shell functions... */ + informed += + scanmatchshfunc(pprog, 1, 0, DISABLED, + shfunctab->printnode, printflags, expand); + + /* and builtins. */ + informed += + scanmatchtable(builtintab, pprog, 1, 0, DISABLED, + builtintab->printnode, printflags); + } + /* Done search for `internal' commands, if the -p option * + * was not used. Now search the path. */ + informed += + scanmatchtable(cmdnamtab, pprog, 1, 0, 0, + (all ? fetchcmdnamnode : cmdnamtab->printnode), + printflags); + run_queued_signals(); + } + unqueue_signals(); + if (all) { + allmatched = argv = zlinklist2array(matchednodes); + matchednodes = NULL; + popheap(); + } else + return returnval || !informed; + } + + /* Take arguments literally -- do not glob */ + queue_signals(); + for (; *argv; argv++) { + if (!OPT_ISSET(ops,'p') && !allmatched) { + char *suf; + + /* Look for alias */ + if ((hn = aliastab->getnode(aliastab, *argv))) { + aliastab->printnode(hn, aliasflags); + informed = 1; + if (!all) + continue; + } + /* Look for suffix alias */ + if ((suf = strrchr(*argv, '.')) && suf[1] && + suf > *argv && suf[-1] != Meta && + (hn = sufaliastab->getnode(sufaliastab, suf+1))) { + sufaliastab->printnode(hn, printflags); + informed = 1; + if (!all) + continue; + } + /* Look for reserved word */ + if ((hn = reswdtab->getnode(reswdtab, *argv))) { + reswdtab->printnode(hn, printflags); + informed = 1; + if (!all) + continue; + } + /* Look for shell function */ + if ((hn = shfunctab->getnode(shfunctab, *argv))) { + printshfuncexpand(hn, printflags, expand); + informed = 1; + if (!all) + continue; + } + /* Look for builtin command */ + if ((hn = builtintab->getnode(builtintab, *argv))) { + builtintab->printnode(hn, printflags); + informed = 1; + if (!all) + continue; + } + /* Look for commands that have been added to the * + * cmdnamtab with the builtin `hash foo=bar'. */ + if ((hn = cmdnamtab->getnode(cmdnamtab, *argv)) && (hn->flags & HASHED)) { + cmdnamtab->printnode(hn, printflags); + informed = 1; + if (!all) + continue; + } + } + + /* Option -a is to search the entire path, * + * rather than just looking for one match. */ + if (all && **argv != '/') { + char **pp, *buf; + + pushheap(); + for (pp = path; *pp; pp++) { + if (**pp) { + buf = zhtricat(*pp, "/", *argv); + } else buf = dupstring(*argv); + + if (iscom(buf)) { + if (wd) { + printf("%s: command\n", *argv); + } else { + if (v && !csh) { + zputs(*argv, stdout), fputs(" is ", stdout); + quotedzputs(buf, stdout); + } else + zputs(buf, stdout); + if (OPT_ISSET(ops,'s') || OPT_ISSET(ops, 'S')) + print_if_link(buf, OPT_ISSET(ops, 'S')); + fputc('\n', stdout); + } + informed = 1; + } + } + if (!informed && (wd || v || csh)) { + /* this is information and not an error so, as in csh, use stdout */ + zputs(*argv, stdout); + puts(wd ? ": none" : " not found"); + returnval = 1; + } + popheap(); + } else if (func == BIN_COMMAND && OPT_ISSET(ops,'p') && + (hn = builtintab->getnode(builtintab, *argv))) { + /* + * Special case for "command -p[vV]" which needs to + * show a builtin in preference to an external command. + */ + builtintab->printnode(hn, printflags); + informed = 1; + } else if ((cnam = findcmd(*argv, 1, + func == BIN_COMMAND && + OPT_ISSET(ops,'p')))) { + /* Found external command. */ + if (wd) { + printf("%s: command\n", *argv); + } else { + if (v && !csh) { + zputs(*argv, stdout), fputs(" is ", stdout); + quotedzputs(cnam, stdout); + } else + zputs(cnam, stdout); + if (OPT_ISSET(ops,'s') || OPT_ISSET(ops,'S')) + print_if_link(cnam, OPT_ISSET(ops,'S')); + fputc('\n', stdout); + } + informed = 1; + } else { + /* Not found at all. That's not an error as such so this goes to stdout */ + if (v || csh || wd) + zputs(*argv, stdout), puts(wd ? ": none" : " not found"); + returnval = 1; + } + } + if (allmatched) + freearray(allmatched); + + unqueue_signals(); + return returnval || !informed; +} + +/**** command & named directory hash table builtins ****/ + +/***************************************************************** + * hash -- explicitly hash a command. * + * 1) Given no arguments, list the hash table. * + * 2) The -m option prints out commands in the hash table that * + * match a given glob pattern. * + * 3) The -f option causes the entire path to be added to the * + * hash table (cannot be combined with any arguments). * + * 4) The -r option causes the entire hash table to be discarded * + * (cannot be combined with any arguments). * + * 5) Given argument of the form foo=bar, add element to command * + * hash table, so that when `foo' is entered, then `bar' is * + * executed. * + * 6) Given arguments not of the previous form, add it to the * + * command hash table as if it were being executed. * + * 7) The -d option causes analogous things to be done using * + * the named directory hash table. * + *****************************************************************/ + +/**/ +int +bin_hash(char *name, char **argv, Options ops, UNUSED(int func)) +{ + HashTable ht; + Patprog pprog; + Asgment asg; + int returnval = 0; + int printflags = 0; + + if (OPT_ISSET(ops,'d')) + ht = nameddirtab; + else + ht = cmdnamtab; + + if (OPT_ISSET(ops,'r') || OPT_ISSET(ops,'f')) { + /* -f and -r can't be used with any arguments */ + if (*argv) { + zwarnnam("hash", "too many arguments"); + return 1; + } + + /* empty the hash table */ + if (OPT_ISSET(ops,'r')) + ht->emptytable(ht); + + /* fill the hash table in a standard way */ + if (OPT_ISSET(ops,'f')) + ht->filltable(ht); + + return 0; + } + + if (OPT_ISSET(ops,'L')) printflags |= PRINT_LIST; + + /* Given no arguments, display current hash table. */ + if (!*argv) { + queue_signals(); + scanhashtable(ht, 1, 0, 0, ht->printnode, printflags); + unqueue_signals(); + return 0; + } + + queue_signals(); + while (*argv) { + void *hn; + if (OPT_ISSET(ops,'m')) { + /* with the -m option, treat the argument as a glob pattern */ + tokenize(*argv); /* expand */ + if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) { + /* display matching hash table elements */ + scanmatchtable(ht, pprog, 1, 0, 0, ht->printnode, printflags); + } else { + untokenize(*argv); + zwarnnam(name, "bad pattern : %s", *argv); + returnval = 1; + } + argv++; + continue; + } + if (!(asg = getasg(&argv, NULL))) { + zwarnnam(name, "bad assignment"); + returnval = 1; + break; + } else if (ASG_VALUEP(asg)) { + if(isset(RESTRICTED)) { + zwarnnam(name, "restricted: %s", asg->value.scalar); + returnval = 1; + } else { + /* The argument is of the form foo=bar, * + * so define an entry for the table. */ + if(OPT_ISSET(ops,'d')) { + /* shouldn't return NULL if asg->name is not NULL */ + if (*itype_end(asg->name, IUSER, 0)) { + zwarnnam(name, + "invalid character in directory name: %s", + asg->name); + returnval = 1; + continue; + } else { + Nameddir nd = hn = zshcalloc(sizeof *nd); + nd->node.flags = 0; + nd->dir = ztrdup(asg->value.scalar); + } + } else { + Cmdnam cn = hn = zshcalloc(sizeof *cn); + cn->node.flags = HASHED; + cn->u.cmd = ztrdup(asg->value.scalar); + } + ht->addnode(ht, ztrdup(asg->name), hn); + if(OPT_ISSET(ops,'v')) + ht->printnode(hn, 0); + } + } else if (!(hn = ht->getnode2(ht, asg->name))) { + /* With no `=value' part to the argument, * + * work out what it ought to be. */ + if(OPT_ISSET(ops,'d')) { + if(!getnameddir(asg->name)) { + zwarnnam(name, "no such directory name: %s", asg->name); + returnval = 1; + } + } else { + if (!hashcmd(asg->name, path)) { + zwarnnam(name, "no such command: %s", asg->name); + returnval = 1; + } + } + if(OPT_ISSET(ops,'v') && (hn = ht->getnode2(ht, asg->name))) + ht->printnode(hn, 0); + } else if(OPT_ISSET(ops,'v')) + ht->printnode(hn, 0); + } + unqueue_signals(); + return returnval; +} + +/* unhash: remove specified elements from a hash table */ + +/**/ +int +bin_unhash(char *name, char **argv, Options ops, int func) +{ + HashTable ht; + HashNode hn, nhn; + Patprog pprog; + int match = 0, returnval = 0, all = 0; + int i; + + /* Check which hash table we are working with. */ + if (func == BIN_UNALIAS) { + if (OPT_ISSET(ops,'s')) + ht = sufaliastab; /* suffix aliases */ + else + ht = aliastab; /* aliases */ + if (OPT_ISSET(ops, 'a')) { + if (*argv) { + zwarnnam(name, "-a: too many arguments"); + return 1; + } + all = 1; + } else if (!*argv) { + zwarnnam(name, "not enough arguments"); + return 1; + } + } else if (OPT_ISSET(ops,'d')) + ht = nameddirtab; /* named directories */ + else if (OPT_ISSET(ops,'f')) + ht = shfunctab; /* shell functions */ + else if (OPT_ISSET(ops,'s')) + ht = sufaliastab; /* suffix aliases, must precede aliases */ + else if (func == BIN_UNHASH && (OPT_ISSET(ops,'a'))) + ht = aliastab; /* aliases */ + else + ht = cmdnamtab; /* external commands */ + + if (all) { + queue_signals(); + for (i = 0; i < ht->hsize; i++) { + for (hn = ht->nodes[i]; hn; hn = nhn) { + /* record pointer to next, since we may free this one */ + nhn = hn->next; + ht->freenode(ht->removenode(ht, hn->nam)); + } + } + unqueue_signals(); + return 0; + } + + /* With -m option, treat arguments as glob patterns. * + * "unhash -m '*'" is legal, but not recommended. */ + if (OPT_ISSET(ops,'m')) { + for (; *argv; argv++) { + queue_signals(); + /* expand argument */ + tokenize(*argv); + if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) { + /* remove all nodes matching glob pattern */ + for (i = 0; i < ht->hsize; i++) { + for (hn = ht->nodes[i]; hn; hn = nhn) { + /* record pointer to next, since we may free this one */ + nhn = hn->next; + if (pattry(pprog, hn->nam)) { + ht->freenode(ht->removenode(ht, hn->nam)); + match++; + } + } + } + } else { + untokenize(*argv); + zwarnnam(name, "bad pattern : %s", *argv); + returnval = 1; + } + unqueue_signals(); + } + /* If we didn't match anything, we return 1. */ + if (!match) + returnval = 1; + return returnval; + } + + /* Take arguments literally -- do not glob */ + queue_signals(); + for (; *argv; argv++) { + if ((hn = ht->removenode(ht, *argv))) { + ht->freenode(hn); + } else if (func == BIN_UNSET && isset(POSIXBUILTINS)) { + /* POSIX: unset: "Unsetting a variable or function that was * + * not previously set shall not be considered an error." */ + returnval = 0; + } else { + zwarnnam(name, "no such hash table element: %s", *argv); + returnval = 1; + } + } + unqueue_signals(); + return returnval; +} + +/**** alias builtins ****/ + +/* alias: display or create aliases. */ + +/**/ +int +bin_alias(char *name, char **argv, Options ops, UNUSED(int func)) +{ + Alias a; + Patprog pprog; + Asgment asg; + int returnval = 0; + int flags1 = 0, flags2 = DISABLED; + int printflags = 0; + int type_opts; + HashTable ht = aliastab; + + /* Did we specify the type of alias? */ + type_opts = OPT_ISSET(ops, 'r') + OPT_ISSET(ops, 'g') + + OPT_ISSET(ops, 's'); + if (type_opts) { + if (type_opts > 1) { + zwarnnam(name, "illegal combination of options"); + return 1; + } + if (OPT_ISSET(ops,'g')) + flags1 |= ALIAS_GLOBAL; + else + flags2 |= ALIAS_GLOBAL; + if (OPT_ISSET(ops, 's')) { + /* + * Although we keep suffix aliases in a different table, + * it is useful to be able to distinguish Alias structures + * without reference to the table, so we have a separate + * flag, too. + */ + flags1 |= ALIAS_SUFFIX; + ht = sufaliastab; + } else + flags2 |= ALIAS_SUFFIX; + } + + if (OPT_ISSET(ops,'L')) + printflags |= PRINT_LIST; + else if (OPT_PLUS(ops,'g') || OPT_PLUS(ops,'r') || OPT_PLUS(ops,'s') || + OPT_PLUS(ops,'m') || OPT_ISSET(ops,'+')) + printflags |= PRINT_NAMEONLY; + + /* In the absence of arguments, list all aliases. If a command * + * line flag is specified, list only those of that type. */ + if (!*argv) { + queue_signals(); + scanhashtable(ht, 1, flags1, flags2, ht->printnode, printflags); + unqueue_signals(); + return 0; + } + + /* With the -m option, treat the arguments as * + * glob patterns of aliases to display. */ + if (OPT_ISSET(ops,'m')) { + for (; *argv; argv++) { + queue_signals(); + tokenize(*argv); /* expand argument */ + if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) { + /* display the matching aliases */ + scanmatchtable(ht, pprog, 1, flags1, flags2, + ht->printnode, printflags); + } else { + untokenize(*argv); + zwarnnam(name, "bad pattern : %s", *argv); + returnval = 1; + } + unqueue_signals(); + } + return returnval; + } + + /* Take arguments literally. Don't glob */ + queue_signals(); + 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.scalar), flags1)); + } else if ((a = (Alias) ht->getnode(ht, asg->name))) { + /* display alias if appropriate */ + if (!type_opts || ht == sufaliastab || + (OPT_ISSET(ops,'r') && + !(a->node.flags & (ALIAS_GLOBAL|ALIAS_SUFFIX))) || + (OPT_ISSET(ops,'g') && (a->node.flags & ALIAS_GLOBAL))) + ht->printnode(&a->node, printflags); + } else + returnval = 1; + } + unqueue_signals(); + return returnval; +} + + +/**** miscellaneous builtins ****/ + +/* true, : (colon) */ + +/**/ +int +bin_true(UNUSED(char *name), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func)) +{ + return 0; +} + +/* false builtin */ + +/**/ +int +bin_false(UNUSED(char *name), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func)) +{ + return 1; +} + +/* the zle buffer stack */ + +/**/ +mod_export LinkList bufstack; + +/* echo, print, printf, pushln */ + +#define print_val(VAL) \ + if (prec >= 0) \ + count += fprintf(fout, spec, width, prec, VAL); \ + else \ + count += fprintf(fout, spec, width, VAL); + +/* + * Because of the use of getkeystring() to interpret the arguments, + * the elements of args spend a large part of the function unmetafied + * with the lengths in len. This may have seemed a good idea once. + * As we are stuck with this for now, we need to be very careful + * deciding what state args is in. + */ + +/**/ +int +bin_print(char *name, char **args, Options ops, int func) +{ + int flen, width, prec, type, argc, n, narg, curlen = 0; + int nnl = 0, fmttrunc = 0, ret = 0, maxarg = 0, nc = 0; + int flags[6], *len, visarr = 0; + char *start, *endptr, *c, *d, *flag, *buf = NULL, spec[14], *fmt = NULL; + char **first, **argp, *curarg, *flagch = "'0+- #", save = '\0', nullstr = '\0'; + size_t rcount = 0, count = 0; + size_t *cursplit = 0, *splits = 0; + FILE *fout = stdout; +#ifdef HAVE_OPEN_MEMSTREAM + size_t mcount; +#define ASSIGN_MSTREAM(BUF,FOUT) \ + do { \ + if ((FOUT = open_memstream(&BUF, &mcount)) == NULL) { \ + zwarnnam(name, "open_memstream failed"); \ + return 1; \ + } \ + } while (0) + /* + * Some implementations of open_memstream() have a bug such that, + * if fflush() is followed by fclose(), another NUL byte is written + * to the buffer at the wrong position. Therefore we must fclose() + * before reading. + */ +#define READ_MSTREAM(BUF,FOUT) \ + ((fclose(FOUT) == 0) ? mcount : (size_t)-1) +#define CLOSE_MSTREAM(FOUT) 0 + +#else /* simulate HAVE_OPEN_MEMSTREAM */ + +#define ASSIGN_MSTREAM(BUF,FOUT) \ + do { \ + int tempfd; \ + char *tmpf; \ + if ((tempfd = gettempfile(NULL, 1, &tmpf)) < 0) { \ + zwarnnam(name, "can't open temp file: %e", errno); \ + return 1; \ + } \ + unlink(tmpf); \ + if ((FOUT = fdopen(tempfd, "w+")) == NULL) { \ + close(tempfd); \ + zwarnnam(name, "can't open temp file: %e", errno); \ + return 1; \ + } \ + } while (0) +#define READ_MSTREAM(BUF,FOUT) \ + ((((count = ftell(FOUT)), (BUF = (char *)zalloc(count + 1))) && \ + ((fseek(FOUT, 0L, SEEK_SET) == 0) && !(BUF[count] = '\0')) && \ + (fread(BUF, 1, count, FOUT) == count)) ? count : (size_t)-1) +#define CLOSE_MSTREAM(FOUT) fclose(FOUT) + +#endif + +#define IS_MSTREAM(FOUT) \ + (FOUT != stdout && \ + (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s') || OPT_ISSET(ops,'v'))) + + /* Testing EBADF special-cases >&- redirections */ +#define CLOSE_CLEANLY(FOUT) \ + (IS_MSTREAM(FOUT) ? CLOSE_MSTREAM(FOUT) == 0 : \ + ((FOUT == stdout) ? (fflush(FOUT) == 0 || errno == EBADF) : \ + (fclose(FOUT) == 0))) /* implies error for -u on a closed fd */ + + Histent ent; + mnumber mnumval; + double doubleval; + int intval; + zlong zlongval; + zulong zulongval; + char *stringval; + + /* Error check option combinations and option arguments */ + + if (OPT_ISSET(ops, 'z') + + OPT_ISSET(ops, 's') + OPT_ISSET(ops, 'S') + + OPT_ISSET(ops, 'v') > 1) { + zwarnnam(name, "only one of -s, -S, -v, or -z allowed"); + return 1; + } + if ((OPT_ISSET(ops, 'z') | OPT_ISSET(ops, 's') | OPT_ISSET(ops, 'S')) + + (OPT_ISSET(ops, 'c') | OPT_ISSET(ops, 'C')) > 1) { + zwarnnam(name, "-c or -C not allowed with -s, -S, or -z"); + return 1; + } + if ((OPT_ISSET(ops, 'z') | OPT_ISSET(ops, 'v') | + OPT_ISSET(ops, 's') | OPT_ISSET(ops, 'S')) + + (OPT_ISSET(ops, 'p') | OPT_ISSET(ops, 'u')) > 1) { + zwarnnam(name, "-p or -u not allowed with -s, -S, -v, or -z"); + return 1; + } + /* + if (OPT_ISSET(ops, 'f') && + (OPT_ISSET(ops, 'S') || OPT_ISSET(ops, 'c') || OPT_ISSET(ops, 'C'))) { + zwarnnam(name, "-f not allowed with -c, -C, or -S"); + return 1; + } + */ + + /* -C -- number of columns */ + if (!fmt && OPT_ISSET(ops,'C')) { + char *eptr, *argptr = OPT_ARG(ops,'C'); + nc = (int)zstrtol(argptr, &eptr, 10); + if (*eptr) { + zwarnnam(name, "number expected after -%c: %s", 'C', argptr); + return 1; + } + if (nc <= 0) { + zwarnnam(name, "invalid number of columns: %s", argptr); + return 1; + } + } + + if (func == BIN_PRINTF) { + if (!strcmp(*args, "--") && !*++args) { + zwarnnam(name, "not enough arguments"); + return 1; + } + fmt = *args++; + } else if (func == BIN_ECHO && isset(BSDECHO)) + ops->ind['E'] = 1; + else if (OPT_HASARG(ops,'f')) + fmt = OPT_ARG(ops,'f'); + if (fmt) + fmt = getkeystring(fmt, &flen, OPT_ISSET(ops,'b') ? GETKEYS_BINDKEY : + GETKEYS_PRINTF_FMT, &fmttrunc); + + first = args; + + /* -m option -- treat the first argument as a pattern and remove + * arguments not matching */ + if (OPT_ISSET(ops,'m')) { + Patprog pprog; + char **t, **p; + + if (!*args) { + zwarnnam(name, "no pattern specified"); + return 1; + } + queue_signals(); + tokenize(*args); + if (!(pprog = patcompile(*args, PAT_STATIC, NULL))) { + untokenize(*args); + zwarnnam(name, "bad pattern: %s", *args); + unqueue_signals(); + return 1; + } + for (t = p = ++args; *p; p++) + if (pattry(pprog, *p)) + *t++ = *p; + *t = NULL; + first = args; + unqueue_signals(); + if (fmt && !*args) return 0; + } + /* compute lengths, and interpret according to -P, -D, -e, etc. */ + argc = arrlen(args); + len = (int *) hcalloc(argc * sizeof(int)); + for (n = 0; n < argc; n++) { + /* first \ sequences */ + if (fmt || + (!OPT_ISSET(ops,'e') && + (OPT_ISSET(ops,'R') || OPT_ISSET(ops,'r') || OPT_ISSET(ops,'E')))) + unmetafy(args[n], &len[n]); + else { + int escape_how; + if (OPT_ISSET(ops,'b')) + escape_how = GETKEYS_BINDKEY; + else if (func != BIN_ECHO && !OPT_ISSET(ops,'e')) + escape_how = GETKEYS_PRINT; + else + escape_how = GETKEYS_ECHO; + args[n] = getkeystring(args[n], &len[n], escape_how, &nnl); + if (nnl) { + /* If there was a \c escape, make this the last arg. */ + argc = n + 1; + args[argc] = NULL; + } + } + /* -P option -- interpret as a prompt sequence */ + if (OPT_ISSET(ops,'P')) { + /* + * promptexpand uses permanent storage: to avoid + * messy memory management, stick it on the heap + * instead. + */ + char *str = unmetafy( + promptexpand(metafy(args[n], len[n], META_NOALLOC), + 0, NULL, NULL, NULL), + &len[n]); + args[n] = dupstrpfx(str, len[n]); + free(str); + } + /* -D option -- interpret as a directory, and use ~ */ + if (OPT_ISSET(ops,'D')) { + Nameddir d; + + queue_signals(); + /* TODO: finddir takes a metafied file */ + d = finddir(args[n]); + if (d) { + int dirlen = strlen(d->dir); + char *arg = zhalloc(len[n] - dirlen + strlen(d->node.nam) + 2); + sprintf(arg, "~%s%s", d->node.nam, args[n] + dirlen); + args[n] = arg; + len[n] = strlen(args[n]); + } + unqueue_signals(); + } + } + + /* -o and -O -- sort the arguments */ + if (OPT_ISSET(ops,'o') || OPT_ISSET(ops,'O')) { + int flags; + + if (fmt && !*args) + return 0; + flags = OPT_ISSET(ops,'i') ? SORTIT_IGNORING_CASE : 0; + if (OPT_ISSET(ops,'O')) + flags |= SORTIT_BACKWARDS; + strmetasort(args, flags, len); + } + + /* -u and -p -- output to other than standard output */ + if ((OPT_HASARG(ops,'u') || OPT_ISSET(ops,'p')) && + /* rule out conflicting options -- historical precedence */ + ((!fmt && (OPT_ISSET(ops,'c') || OPT_ISSET(ops,'C'))) || + !(OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 'v') || + OPT_ISSET(ops, 's') || OPT_ISSET(ops, 'S')))) { + int fdarg, fd; + + if (OPT_ISSET(ops, 'p')) { + fdarg = coprocout; + if (fdarg < 0) { + zwarnnam(name, "-p: no coprocess"); + return 1; + } + } else { + char *argptr = OPT_ARG(ops,'u'), *eptr; + /* Handle undocumented feature that -up worked */ + if (!strcmp(argptr, "p")) { + fdarg = coprocout; + if (fdarg < 0) { + zwarnnam(name, "-p: no coprocess"); + return 1; + } + } else { + fdarg = (int)zstrtol(argptr, &eptr, 10); + if (*eptr) { + zwarnnam(name, "number expected after -u: %s", argptr); + return 1; + } + } + } + + if ((fd = dup(fdarg)) < 0) { + zwarnnam(name, "bad file number: %d", fdarg); + return 1; + } + if ((fout = fdopen(fd, "w")) == 0) { + close(fd); + zwarnnam(name, "bad mode on fd %d", fd); + return 1; + } + } + + if (OPT_ISSET(ops, 'v') || + (fmt && (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s')))) + ASSIGN_MSTREAM(buf,fout); + + /* -c -- output in columns */ + if (!fmt && (OPT_ISSET(ops,'c') || OPT_ISSET(ops,'C'))) { + int l, nr, sc, n, t, i; +#ifdef MULTIBYTE_SUPPORT + int *widths; + + if (isset(MULTIBYTE)) { + int *wptr; + + /* + * We need the character widths to align output in + * columns. + */ + wptr = widths = (int *) zhalloc(argc * sizeof(int)); + for (i = 0; i < argc && args[i]; i++, wptr++) { + int l = len[i], width = 0; + char *aptr = args[i]; + mbstate_t mbs; + + memset(&mbs, 0, sizeof(mbstate_t)); + while (l > 0) { + wchar_t wc; + size_t cnt; + int wcw; + + /* + * Prevent misaligned columns due to escape sequences by + * skipping over them. Octals \033 and \233 are the + * possible escape characters recognized by ANSI. + * + * It ought to be possible to do this in the case + * of prompt expansion by propagating the information + * about escape sequences (currently we strip this + * out). + */ + if (*aptr == '\033' || *aptr == '\233') { + for (aptr++, l--; + l && !isalpha(STOUC(*aptr)); + aptr++, l--) + ; + aptr++; + l--; + continue; + } + + cnt = mbrtowc(&wc, aptr, l, &mbs); + + if (cnt == MB_INCOMPLETE || cnt == MB_INVALID) + { + /* treat as ordinary string */ + width += l; + break; + } + wcw = WCWIDTH(wc); + /* treat unprintable as 0 */ + if (wcw > 0) + width += wcw; + /* skip over NUL normally */ + if (cnt == 0) + cnt = 1; + aptr += cnt; + l -= cnt; + } + widths[i] = width; + } + } + else + widths = len; +#else + int *widths = len; +#endif + + if (OPT_ISSET(ops,'C')) { + /* + * n: number of elements + * nc: number of columns (above) + * nr: number of rows + */ + n = arrlen(args); + nr = (n + nc - 1) / nc; + + /* + * i: loop counter + * l: maximum length seen + * + * Ignore lengths in last column since they don't affect + * the separation. + */ + for (i = l = 0; i < argc; i++) { + if (OPT_ISSET(ops, 'a')) { + if ((i % nc) == nc - 1) + continue; + } else { + if (i >= nr * (nc - 1)) + break; + } + if (l < widths[i]) + l = widths[i]; + } + sc = l + 2; + } + else + { + /* + * n: loop counter + * l: maximum length seen + */ + for (n = l = 0; n < argc; n++) + if (l < widths[n]) + l = widths[n]; + + /* + * sc: column width + * nc: number of columns (at least one) + */ + sc = l + 2; + nc = (zterm_columns + 1) / sc; + if (!nc) + nc = 1; + nr = (n + nc - 1) / nc; + } + + if (OPT_ISSET(ops,'a')) /* print across, i.e. columns first */ + n = 0; + for (i = 0; i < nr; i++) { + if (OPT_ISSET(ops,'a')) + { + int ic; + for (ic = 0; ic < nc && n < argc; ic++, n++) + { + fwrite(args[n], len[n], 1, fout); + l = widths[n]; + if (n < argc) + for (; l < sc; l++) + fputc(' ', fout); + } + } + else + { + n = i; + do { + fwrite(args[n], len[n], 1, fout); + l = widths[n]; + for (t = nr; t && n < argc; t--, n++); + if (n < argc) + for (; l < sc; l++) + fputc(' ', fout); + } while (n < argc); + } + fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout); + } + if (IS_MSTREAM(fout) && (rcount = READ_MSTREAM(buf,fout)) == -1) + ret = 1; + if (!CLOSE_CLEANLY(fout) || ret) { + zwarnnam(name, "write error: %e", errno); + ret = 1; + } + if (buf) { + /* assert: we must be doing -v at this point */ + queue_signals(); + if (ret) + free(buf); + else + setsparam(OPT_ARG(ops, 'v'), + metafy(buf, rcount, META_REALLOC)); + unqueue_signals(); + } + return ret; + } + + /* normal output */ + if (!fmt) { + if (OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 'v') || + OPT_ISSET(ops, 's') || OPT_ISSET(ops, 'S')) { + /* + * We don't want the arguments unmetafied after all. + */ + for (n = 0; n < argc; n++) + metafy(args[n], len[n], META_NOALLOC); + } + + /* -z option -- push the arguments onto the editing buffer stack */ + if (OPT_ISSET(ops,'z')) { + queue_signals(); + zpushnode(bufstack, sepjoin(args, NULL, 0)); + unqueue_signals(); + return 0; + } + /* -s option -- add the arguments to the history list */ + if (OPT_ISSET(ops,'s') || OPT_ISSET(ops,'S')) { + int nwords = 0, nlen, iwords; + char **pargs = args; + + queue_signals(); + while (*pargs++) + nwords++; + if (nwords) { + if (OPT_ISSET(ops,'S')) { + int wordsize; + short *words; + if (nwords > 1) { + zwarnnam(name, "option -S takes a single argument"); + unqueue_signals(); + return 1; + } + words = NULL; + wordsize = 0; + histsplitwords(*args, &words, &wordsize, &nwords, 1); + ent = prepnexthistent(); + ent->words = (short *)zalloc(nwords*sizeof(short)); + memcpy(ent->words, words, nwords*sizeof(short)); + free(words); + ent->nwords = nwords/2; + } else { + ent = prepnexthistent(); + ent->words = (short *)zalloc(nwords*2*sizeof(short)); + ent->nwords = nwords; + nlen = iwords = 0; + for (pargs = args; *pargs; pargs++) { + ent->words[iwords++] = nlen; + nlen += strlen(*pargs); + ent->words[iwords++] = nlen; + nlen++; + } + } + } else { + ent = prepnexthistent(); + ent->words = (short *)NULL; + } + ent->node.nam = zjoin(args, ' ', 0); + ent->stim = ent->ftim = time(NULL); + ent->node.flags = 0; + addhistnode(histtab, ent->node.nam, ent); + unqueue_signals(); + return 0; + } + + 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 || + (OPT_ISSET(ops, 'v') && !OPT_ISSET(ops, 'l')))) + fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout); + if (IS_MSTREAM(fout) && (rcount = READ_MSTREAM(buf,fout)) == -1) + ret = 1; + if (!CLOSE_CLEANLY(fout) || ret) { + zwarnnam(name, "write error: %e", errno); + ret = 1; + } + if (buf) { + /* assert: we must be doing -v at this point */ + queue_signals(); + if (ret) + free(buf); + else + setsparam(OPT_ARG(ops, 'v'), + metafy(buf, rcount, META_REALLOC)); + unqueue_signals(); + } + return ret; + } + + /* + * All the remaining code in this function is for printf-style + * output (printf itself, or print -f). We still have to handle + * special cases of printing to a ZLE buffer or the history, however. + */ + + if (OPT_ISSET(ops,'v')) { + struct value vbuf; + char* s = OPT_ARG(ops,'v'); + Value v = getvalue(&vbuf, &s, 0); + visarr = v && PM_TYPE(v->pm->node.flags) == PM_ARRAY; + } + /* printf style output */ + *spec = '%'; + argp = args; + do { + rcount = count; + if (argp > args && visarr) { /* reusing format string */ + if (!splits) + cursplit = splits = (size_t *)zhalloc(sizeof(size_t) * + (arrlen(args) / (argp - args) + 1)); + *cursplit++ = count; + } + if (maxarg) { + first += maxarg; + argc -= maxarg; + maxarg = 0; + } + for (c = fmt; c-fmt < flen; c++) { + if (*c != '%') { + putc(*c, fout); + ++count; + continue; + } + + start = c++; + if (*c == '%') { + putc('%', fout); + ++count; + continue; + } + + type = prec = -1; + width = 0; + curarg = NULL; + d = spec + 1; + + if (*c >= '1' && *c <= '9') { + narg = strtoul(c, &endptr, 0); + if (*endptr == '$') { + c = endptr + 1; + DPUTS(narg <= 0, "specified zero or negative arg"); + if (narg > argc) { + zwarnnam(name, "%d: argument specifier out of range", + narg); + if (fout != stdout) + fclose(fout); +#ifdef HAVE_OPEN_MEMSTREAM + if (buf) + free(buf); +#endif + return 1; + } else { + if (narg > maxarg) maxarg = narg; + curarg = *(first + narg - 1); + curlen = len[first - args + narg - 1]; + } + } + } + + /* copy only one of each flag as spec has finite size */ + memset(flags, 0, sizeof(flags)); + while (*c && (flag = strchr(flagch, *c))) { + if (!flags[flag - flagch]) { + flags[flag - flagch] = 1; + *d++ = *c; + } + c++; + } + + if (idigit(*c)) { + width = strtoul(c, &endptr, 0); + c = endptr; + } else if (*c == '*') { + if (idigit(*++c)) { + narg = strtoul(c, &endptr, 0); + if (*endptr == '$') { + c = endptr + 1; + if (narg > argc || narg <= 0) { + zwarnnam(name, + "%d: argument specifier out of range", + narg); + if (fout != stdout) + fclose(fout); +#ifdef HAVE_OPEN_MEMSTREAM + if (buf) + free(buf); +#endif + return 1; + } else { + if (narg > maxarg) maxarg = narg; + argp = first + narg - 1; + } + } + } + if (*argp) { + width = (int)mathevali(*argp++); + if (errflag) { + errflag &= ~ERRFLAG_ERROR; + ret = 1; + } + } + } + *d++ = '*'; + + if (*c == '.') { + if (*++c == '*') { + if (idigit(*++c)) { + narg = strtoul(c, &endptr, 0); + if (*endptr == '$') { + c = endptr + 1; + if (narg > argc || narg <= 0) { + zwarnnam(name, + "%d: argument specifier out of range", + narg); + if (fout != stdout) + fclose(fout); +#ifdef HAVE_OPEN_MEMSTREAM + if (buf) + free(buf); +#endif + return 1; + } else { + if (narg > maxarg) maxarg = narg; + argp = first + narg - 1; + } + } + } + + if (*argp) { + prec = (int)mathevali(*argp++); + if (errflag) { + errflag &= ~ERRFLAG_ERROR; + ret = 1; + } + } + } else if (idigit(*c)) { + prec = strtoul(c, &endptr, 0); + c = endptr; + } else + prec = 0; + if (prec >= 0) *d++ = '.', *d++ = '*'; + } + + /* ignore any size modifier */ + if (*c == 'l' || *c == 'L' || *c == 'h') c++; + + if (!curarg && *argp) { + curarg = *argp; + curlen = len[argp++ - args]; + } + d[1] = '\0'; + switch (*d = *c) { + case 'c': + if (curarg) + intval = *curarg; + else + intval = 0; + print_val(intval); + break; + case 's': + case 'b': + if (curarg) { + char *b, *ptr; + int lbytes, lchars, lleft; +#ifdef MULTIBYTE_SUPPORT + mbstate_t mbs; +#endif + + if (*c == 'b') { + b = getkeystring(metafy(curarg, curlen, META_USEHEAP), + &lbytes, + OPT_ISSET(ops,'b') ? GETKEYS_BINDKEY : + GETKEYS_PRINTF_ARG, &nnl); + } else { + b = curarg; + lbytes = curlen; + } + /* + * Handle width/precision here and use fwrite so that + * nul characters can be output. + * + * First, examine width of string given that it + * may contain multibyte characters. The output + * widths are for characters, so we need to count + * (in lchars). However, if we need to truncate + * the string we need the width in bytes (in lbytes). + */ + ptr = b; +#ifdef MULTIBYTE_SUPPORT + memset(&mbs, 0, sizeof(mbs)); +#endif + + for (lchars = 0, lleft = lbytes; lleft > 0; lchars++) { + int chars; + + if (lchars == prec) { + /* Truncate at this point. */ + lbytes = ptr - b; + break; + } +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) { + chars = mbrlen(ptr, lleft, &mbs); + if (chars < 0) { + /* + * Invalid/incomplete character at this + * point. Assume all the rest are a + * single byte. That's about the best we + * can do. + */ + lchars += lleft; + lbytes = (ptr - b) + lleft; + break; + } else if (chars == 0) { + /* NUL, handle as real character */ + chars = 1; + } + } + else /* use the non-multibyte code below */ +#endif + chars = 1; /* compiler can optimise this...*/ + lleft -= chars; + ptr += chars; + } + if (width > 0 && flags[3]) width = -width; + if (width > 0 && lchars < width) + count += fprintf(fout, "%*c", width - lchars, ' '); + count += fwrite(b, 1, lbytes, fout); + if (width < 0 && lchars < -width) + count += fprintf(fout, "%*c", -width - lchars, ' '); + if (nnl) { + /* If the %b arg had a \c escape, truncate the fmt. */ + flen = c - fmt + 1; + fmttrunc = 1; + } + } else if (width) + count += fprintf(fout, "%*c", width, ' '); + break; + case 'q': + stringval = curarg ? + quotestring(metafy(curarg, curlen, META_USEHEAP), + QT_BACKSLASH_SHOWNULL) : &nullstr; + *d = 's'; + print_val(unmetafy(stringval, &curlen)); + break; + case 'd': + case 'i': + type=1; + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + type=2; + break; + case 'o': + case 'u': + case 'x': + case 'X': + type=3; + break; + case 'n': + if (curarg) setiparam(curarg, count - rcount); + break; + default: + if (*c) { + save = c[1]; + c[1] = '\0'; + } + zwarnnam(name, "%s: invalid directive", start); + if (*c) c[1] = save; + /* Why do we care about a clean close here? */ + if (!CLOSE_CLEANLY(fout)) + zwarnnam(name, "write error: %e", errno); +#ifdef HAVE_OPEN_MEMSTREAM + if (buf) + free(buf); +#endif + return 1; + } + + if (type > 0) { + if (curarg && (*curarg == '\'' || *curarg == '"' )) { + convchar_t cc; +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) { + mb_charinit(); + (void)mb_metacharlenconv(metafy(curarg+1, curlen-1, + META_USEHEAP), &cc); + } + else + cc = WEOF; + if (cc == WEOF) + cc = (curlen > 1) ? STOUC(curarg[1]) : 0; +#else + cc = (curlen > 1) ? STOUC(curarg[1]) : 0; +#endif + if (type == 2) { + doubleval = cc; + print_val(doubleval); + } else { + intval = cc; + print_val(intval); + } + } else { + switch (type) { + case 1: +#ifdef ZSH_64_BIT_TYPE + *d++ = 'l'; +#endif + *d++ = 'l', *d++ = *c, *d = '\0'; + zlongval = (curarg) ? mathevali(curarg) : 0; + if (errflag) { + zlongval = 0; + errflag &= ~ERRFLAG_ERROR; + ret = 1; + } + print_val(zlongval) + break; + case 2: + if (curarg) { + char *eptr; + /* + * First attempt to parse as a floating + * point constant. If we go through + * a math evaluation, we can lose + * mostly unimportant information + * that people in standards organizations + * worry about. + */ + doubleval = strtod(curarg, &eptr); + /* + * If it didn't parse as a constant, + * parse it as an expression. + */ + if (*eptr != '\0') { + mnumval = matheval(curarg); + doubleval = (mnumval.type & MN_FLOAT) ? + mnumval.u.d : (double)mnumval.u.l; + } + } else doubleval = 0; + if (errflag) { + doubleval = 0; + errflag &= ~ERRFLAG_ERROR; + ret = 1; + } + /* force consistent form for Inf/NaN output */ + if (isnan(doubleval)) + count += fputs("nan", fout); + else if (isinf(doubleval)) + count += fputs((doubleval < 0.0) ? "-inf" : "inf", fout); + else + print_val(doubleval) + break; + case 3: +#ifdef ZSH_64_BIT_UTYPE + *d++ = 'l'; +#endif + *d++ = 'l', *d++ = *c, *d = '\0'; + if (!curarg) + zulongval = (zulong)0; + else if (!zstrtoul_underscore(curarg, &zulongval)) + zulongval = mathevali(curarg); + if (errflag) { + zulongval = 0; + errflag &= ~ERRFLAG_ERROR; + ret = 1; + } + print_val(zulongval) + } + } + } + if (maxarg && (argp - first > maxarg)) + maxarg = argp - first; + } + + if (maxarg) argp = first + maxarg; + /* if there are remaining args, reuse format string */ + } while (*argp && argp != first && !fmttrunc && !OPT_ISSET(ops,'r')); + + if (IS_MSTREAM(fout)) { + queue_signals(); + if ((rcount = READ_MSTREAM(buf,fout)) == -1) { + zwarnnam(name, "i/o error: %e", errno); + if (buf) + free(buf); + } else { + if (visarr && splits) { + char **arrayval = zshcalloc((cursplit - splits + 2) * sizeof(char *)); + for (;cursplit >= splits; cursplit--) { + int start = cursplit == splits ? 0 : cursplit[-1]; + arrayval[cursplit - splits] = + metafy(buf + start, count - start, META_DUP); + count = start; + } + setaparam(OPT_ARG(ops, 'v'), arrayval); + free(buf); + } else { + stringval = metafy(buf, rcount, META_REALLOC); + if (OPT_ISSET(ops,'z')) { + zpushnode(bufstack, stringval); + } else if (OPT_ISSET(ops,'v')) { + setsparam(OPT_ARG(ops, 'v'), stringval); + } else { + ent = prepnexthistent(); + ent->node.nam = stringval; + ent->stim = ent->ftim = time(NULL); + ent->node.flags = 0; + ent->words = (short *)NULL; + addhistnode(histtab, ent->node.nam, ent); + } + } + } + unqueue_signals(); + } + + if (!CLOSE_CLEANLY(fout)) + { + zwarnnam(name, "write error: %e", errno); + ret = 1; + } + return ret; +} + +/* shift builtin */ + +/**/ +int +bin_shift(char *name, char **argv, Options ops, UNUSED(int func)) +{ + int num = 1, l, ret = 0; + char **s; + + /* optional argument can be either numeric or an array */ + queue_signals(); + if (*argv && !getaparam(*argv)) { + num = mathevali(*argv++); + if (errflag) { + unqueue_signals(); + return 1; + } + } + + if (num < 0) { + unqueue_signals(); + zwarnnam(name, "argument to shift must be non-negative"); + return 1; + } + + if (*argv) { + for (; *argv; argv++) + if ((s = getaparam(*argv))) { + if (arrlen_lt(s, num)) { + zwarnnam(name, "shift count must be <= $#"); + ret++; + continue; + } + if (OPT_ISSET(ops,'p')) { + char **s2, **src, **dst; + int count; + l = arrlen(s); + src = s; + dst = s2 = (char **)zalloc((l - num + 1) * sizeof(char *)); + for (count = l - num; count; count--) + *dst++ = ztrdup(*src++); + *dst = NULL; + s = s2; + } else { + s = zarrdup(s + num); + } + setaparam(*argv, s); + } + } else { + if (num > (l = arrlen(pparams))) { + zwarnnam(name, "shift count must be <= $#"); + ret = 1; + } else { + s = zalloc((l - num + 1) * sizeof(char *)); + if (OPT_ISSET(ops,'p')) { + memcpy(s, pparams, (l - num) * sizeof(char *)); + s[l-num] = NULL; + while (num--) + zsfree(pparams[l-1-num]); + } else { + memcpy(s, pparams + num, (l - num + 1) * sizeof(char *)); + while (num--) + zsfree(pparams[num]); + } + zfree(pparams, (l + 1) * sizeof(char *)); + pparams = s; + } + } + unqueue_signals(); + return ret; +} + +/* + * Position of getopts option within OPTIND argument with multiple options. + */ + +/**/ +int optcind; + +/* getopts: automagical option handling for shell scripts */ + +/**/ +int +bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int func)) +{ + int lenstr, lenoptstr, quiet, lenoptbuf; + char *optstr = unmetafy(*argv++, &lenoptstr), *var = *argv++; + char **args = (*argv) ? argv : pparams; + char *str, optbuf[2] = " ", *p, opch; + + /* zoptind keeps count of the current argument number. The * + * user can set it to zero to start a new option parse. */ + if (zoptind < 1) { + /* first call */ + zoptind = 1; + optcind = 0; + } + if (arrlen_lt(args, zoptind)) + /* no more options */ + return 1; + + /* leading ':' in optstr means don't print an error message */ + quiet = *optstr == ':'; + optstr += quiet; + lenoptstr -= quiet; + + /* find place in relevant argument */ + str = unmetafy(dupstring(args[zoptind - 1]), &lenstr); + if (!lenstr) /* Definitely not an option. */ + return 1; + if(optcind >= lenstr) { + optcind = 0; + if(!args[zoptind++]) + return 1; + str = unmetafy(dupstring(args[zoptind - 1]), &lenstr); + } + if(!optcind) { + if(lenstr < 2 || (*str != '-' && *str != '+')) + return 1; + if(lenstr == 2 && str[0] == '-' && str[1] == '-') { + zoptind++; + return 1; + } + optcind++; + } + opch = str[optcind++]; + if(str[0] == '+') { + optbuf[0] = '+'; + lenoptbuf = 2; + } else + lenoptbuf = 1; + optbuf[lenoptbuf - 1] = opch; + + /* check for legality */ + if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) { + p = "?"; + err: + zsfree(zoptarg); + setsparam(var, ztrdup(p)); + if(quiet) { + zoptarg = metafy(optbuf, lenoptbuf, META_DUP); + } else { + zwarn(*p == '?' ? "bad option: %c%c" : + "argument expected after %c%c option", + "?-+"[lenoptbuf], opch); + zoptarg=ztrdup(""); + } + return 0; + } + + /* check for required argument */ + if(p[1] == ':') { + if(optcind == lenstr) { + if(!args[zoptind]) { + p = ":"; + goto err; + } + p = ztrdup(args[zoptind++]); + } else + p = metafy(str+optcind, lenstr-optcind, META_DUP); + /* + * Careful: I've just changed the following two lines from + * optcind = ztrlen(args[zoptind - 1]); + * and it's a rigorous theorem that every change in getopts breaks + * something. See zsh-workers/9095 for the bug fixed here. + * PWS 2000/05/02 + */ + optcind = 0; + zoptind++; + zsfree(zoptarg); + zoptarg = p; + } else { + zsfree(zoptarg); + zoptarg = ztrdup(""); + } + + setsparam(var, metafy(optbuf, lenoptbuf, META_DUP)); + return 0; +} + +/* Flag that we should exit the shell as soon as all functions return. */ +/**/ +mod_export int +exit_pending; + +/* Shell level at which we exit if exit_pending */ +/**/ +mod_export int +exit_level; + +/* break, bye, continue, exit, logout, return -- most of these take * + * one numeric argument, and the other (logout) is related to return. * + * (return is treated as a logout when in a login shell.) */ + +/**/ +int +bin_break(char *name, char **argv, UNUSED(Options ops), int func) +{ + int num = lastval, nump = 0, implicit; + + /* handle one optional numeric argument */ + implicit = !*argv; + if (*argv) { + num = mathevali(*argv++); + nump = 1; + } + + if (nump > 0 && (func == BIN_CONTINUE || func == BIN_BREAK) && num <= 0) { + zerrnam(name, "argument is not positive: %d", num); + return 1; + } + + switch (func) { + case BIN_CONTINUE: + if (!loops) { /* continue is only permitted in loops */ + zerrnam(name, "not in while, until, select, or repeat loop"); + return 1; + } + contflag = 1; /* FALLTHROUGH */ + case BIN_BREAK: + if (!loops) { /* break is only permitted in loops */ + zerrnam(name, "not in while, until, select, or repeat loop"); + return 1; + } + breaks = nump ? minimum(num,loops) : 1; + break; + case BIN_RETURN: + if ((isset(INTERACTIVE) && isset(SHINSTDIN)) + || locallevel || sourcelevel) { + retflag = 1; + breaks = loops; + lastval = num; + if (trap_state == TRAP_STATE_PRIMED && trap_return == -2 + /* + * With POSIX, "return" on its own in a trap doesn't + * update $? --- we keep the status from before the + * trap. + */ + && !(isset(POSIXTRAPS) && implicit)) { + trap_state = TRAP_STATE_FORCE_RETURN; + trap_return = lastval; + } + return lastval; + } + zexit(num, 0); /* else treat return as logout/exit */ + break; + case BIN_LOGOUT: + if (unset(LOGINSHELL)) { + zerrnam(name, "not login shell"); + return 1; + } + /*FALLTHROUGH*/ + case BIN_EXIT: + if (locallevel > forklevel && shell_exiting != -1) { + /* + * We don't exit directly from functions to allow tidying + * up, in particular EXIT traps. We still need to perform + * the usual interactive tests to see if we can exit at + * all, however. + * + * If we are forked, we exit the shell at the function depth + * at which we became a subshell, hence the comparison. + * + * If we are already exiting... give this all up as + * a bad job. + */ + if (stopmsg || (zexit(0,2), !stopmsg)) { + retflag = 1; + breaks = loops; + exit_pending = (num << 1) | 1; + exit_level = locallevel; + } + } else + zexit(num, 0); + break; + } + return 0; +} + +/* we have printed a 'you have stopped (running) jobs.' message */ + +/**/ +mod_export int stopmsg; + +/* check to see if user has jobs running/stopped */ + +/**/ +static void +checkjobs(void) +{ + int i; + + for (i = 1; i <= maxjob; i++) + if (i != thisjob && (jobtab[i].stat & STAT_LOCKED) && + !(jobtab[i].stat & STAT_NOPRINT) && + (isset(CHECKRUNNINGJOBS) || jobtab[i].stat & STAT_STOPPED)) + break; + if (i <= maxjob) { + if (jobtab[i].stat & STAT_STOPPED) { + +#ifdef USE_SUSPENDED + zerr("you have suspended jobs."); +#else + zerr("you have stopped jobs."); +#endif + + } else + zerr("you have running jobs."); + stopmsg = 1; + } +} + +/* + * -1 if the shell is already committed to exit. + * positive if zexit() was already called. + */ + +/**/ +int shell_exiting; + +/* exit the shell. val is the return value of the shell. * + * from_where is + * 1 if zexit is called because of a signal + * 2 if we can't actually exit yet (e.g. functions need + * terminating) but should perform the usual interactive tests. + */ + +/**/ +mod_export void +zexit(int val, int from_where) +{ + /* Don't do anything recursively: see below */ + if (shell_exiting == -1) + return; + + if (isset(MONITOR) && !stopmsg && from_where != 1) { + scanjobs(); /* check if jobs need printing */ + if (isset(CHECKJOBS)) + checkjobs(); /* check if any jobs are running/stopped */ + if (stopmsg) { + stopmsg = 2; + return; + } + } + /* Positive in_exit means we have been here before */ + if (from_where == 2 || (shell_exiting++ && from_where)) + return; + + /* + * We're now committed to exiting. Set shell_exiting to -1 to + * indicate we shouldn't do any recursive processing. + */ + shell_exiting = -1; + /* + * We want to do all remaining processing regardless of preceding + * errors, even user interrupts. + */ + errflag = 0; + + if (isset(MONITOR)) { + /* send SIGHUP to any jobs left running */ + killrunjobs(from_where == 1); + } + if (isset(RCS) && interact) { + if (!nohistsave) { + int writeflags = HFILE_USE_OPTIONS; + if (from_where == 1) + writeflags |= HFILE_NO_REWRITE; + saveandpophiststack(1, writeflags); + savehistfile(NULL, 1, writeflags); + } + if (islogin && !subsh) { + sourcehome(".zlogout"); +#ifdef GLOBAL_ZLOGOUT + if (isset(RCS) && isset(GLOBALRCS)) + source(GLOBAL_ZLOGOUT); +#endif + } + } + lastval = val; + /* + * Now we are committed to exiting any previous state + * is irrelevant. Ensure trap can run. + */ + errflag = intrap = 0; + if (sigtrapped[SIGEXIT]) + dotrap(SIGEXIT); + callhookfunc("zshexit", NULL, 1, NULL); + runhookdef(EXITHOOK, NULL); + if (opts[MONITOR] && interact && (SHTTY != -1)) { + release_pgrp(); + } + if (mypid != getpid()) + _exit(val); + else + exit(val); +} + +/* . (dot), source */ + +/**/ +int +bin_dot(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) +{ + char **old, *old0 = NULL; + int diddot = 0, dotdot = 0; + char *s, **t, *enam, *arg0, *buf; + struct stat st; + enum source_return ret; + + if (!*argv) + return 0; + old = pparams; + /* get arguments for the script */ + if (argv[1]) + pparams = zarrdup(argv + 1); + + enam = arg0 = ztrdup(*argv); + if (isset(FUNCTIONARGZERO)) { + old0 = argzero; + argzero = ztrdup(arg0); + } + s = unmeta(enam); + errno = ENOENT; + ret = SOURCE_NOT_FOUND; + /* for source only, check in current directory first */ + if (*name != '.' && access(s, F_OK) == 0 + && stat(s, &st) >= 0 && !S_ISDIR(st.st_mode)) { + diddot = 1; + ret = source(enam); + } + if (ret == SOURCE_NOT_FOUND) { + /* use a path with / in it */ + for (s = arg0; *s; s++) + if (*s == '/') { + if (*arg0 == '.') { + if (arg0 + 1 == s) + ++diddot; + else if (arg0[1] == '.' && arg0 + 2 == s) + ++dotdot; + } + ret = source(arg0); + break; + } + if (!*s || (ret == SOURCE_NOT_FOUND && + isset(PATHDIRS) && diddot < 2 && dotdot == 0)) { + pushheap(); + /* search path for script */ + for (t = path; *t; t++) { + if (!(*t)[0] || ((*t)[0] == '.' && !(*t)[1])) { + if (diddot) + continue; + diddot = 1; + buf = dupstring(arg0); + } else + buf = zhtricat(*t, "/", arg0); + + s = unmeta(buf); + if (access(s, F_OK) == 0 && stat(s, &st) >= 0 + && !S_ISDIR(st.st_mode)) { + ret = source(enam = buf); + break; + } + } + popheap(); + } + } + /* clean up and return */ + if (argv[1]) { + freearray(pparams); + pparams = old; + } + if (ret == SOURCE_NOT_FOUND) { + if (isset(POSIXBUILTINS)) { + /* hard error in POSIX (we'll exit later) */ + zerrnam(name, "%e: %s", errno, enam); + } else { + zwarnnam(name, "%e: %s", errno, enam); + } + } + zsfree(arg0); + if (old0) { + zsfree(argzero); + argzero = old0; + } + return ret == SOURCE_OK ? lastval : 128 - ret; +} + +/* + * common for bin_emulate and bin_eval + */ + +static int +eval(char **argv) +{ + Eprog prog; + char *oscriptname = scriptname; + int oineval = ineval, fpushed; + struct funcstack fstack; + + /* + * If EVALLINENO is not set, we use the line number of the + * environment and must flag this up to exec.c. Otherwise, + * we use a special script name to indicate the special line number. + */ + ineval = !isset(EVALLINENO); + if (!ineval) { + scriptname = "(eval)"; + fstack.prev = funcstack; + fstack.name = scriptname; + fstack.caller = funcstack ? funcstack->name : dupstring(argzero); + fstack.lineno = lineno; + fstack.tp = FS_EVAL; + + /* + * To get file line numbers, we need to know if parent is + * the original script/shell or a sourced file, in which + * case we use the line number raw, or a function or eval, + * in which case we need to deduce where that came from. + * + * This replicates the logic for working out the information + * for $funcfiletrace---eval is similar to an inlined function + * call from a tracing perspective. + */ + if (!funcstack || funcstack->tp == FS_SOURCE) { + fstack.flineno = fstack.lineno; + fstack.filename = fstack.caller; + } else { + fstack.flineno = funcstack->flineno + lineno; + /* + * Line numbers in eval start from 1, not zero, + * so offset by one to get line in file. + */ + if (funcstack->tp == FS_EVAL) + fstack.flineno--; + fstack.filename = funcstack->filename; + if (!fstack.filename) + fstack.filename = ""; + } + funcstack = &fstack; + + fpushed = 1; + } else + fpushed = 0; + + prog = parse_string(zjoin(argv, ' ', 1), 1); + if (prog) { + if (wc_code(*prog->prog) != WC_LIST) { + /* No code to execute */ + lastval = 0; + } else { + execode(prog, 1, 0, "eval"); + + if (errflag && !lastval) + lastval = errflag; + } + } else { + lastval = 1; + } + + if (fpushed) + funcstack = funcstack->prev; + + errflag &= ~ERRFLAG_ERROR; + scriptname = oscriptname; + ineval = oineval; + + return lastval; +} + +/* emulate: set emulation mode and optionally evaluate shell code */ + +/**/ +int +bin_emulate(char *nam, char **argv, Options ops, UNUSED(int func)) +{ + int opt_L = OPT_ISSET(ops, 'L'); + int opt_R = OPT_ISSET(ops, 'R'); + int opt_l = OPT_ISSET(ops, 'l'); + int saveemulation, savehackchar; + int ret = 1, new_emulation; + unsigned int savepatterns; + char saveopts[OPT_SIZE], new_opts[OPT_SIZE]; + char *cmd = 0; + const char *shname = *argv; + LinkList optlist; + LinkNode optnode; + Emulation_options save_sticky; + OptIndex *on_ptr, *off_ptr; + + /* without arguments just print current emulation */ + if (!shname) { + if (opt_L || opt_R) { + zwarnnam(nam, "not enough arguments"); + return 1; + } + + switch(SHELL_EMULATION()) { + case EMULATE_CSH: + shname = "csh"; + break; + + case EMULATE_KSH: + shname = "ksh"; + break; + + case EMULATE_SH: + shname = "sh"; + break; + + default: + shname = "zsh"; + break; + } + + printf("%s\n", shname); + return 0; + } + + /* with single argument set current emulation */ + if (!argv[1]) { + char *cmdopts; + if (opt_l) { + cmdopts = (char *)zhalloc(OPT_SIZE); + memcpy(cmdopts, opts, OPT_SIZE); + } else + cmdopts = opts; + emulate(shname, opt_R, &emulation, cmdopts); + if (opt_L) + cmdopts[LOCALOPTIONS] = cmdopts[LOCALTRAPS] = + cmdopts[LOCALPATTERNS] = 1; + if (opt_l) { + list_emulate_options(cmdopts, opt_R); + return 0; + } + clearpatterndisables(); + return 0; + } + + if (opt_l) { + zwarnnam(nam, "too many arguments for -l"); + return 1; + } + + argv++; + memcpy(saveopts, opts, sizeof(opts)); + memcpy(new_opts, opts, sizeof(opts)); + savehackchar = keyboardhackchar; + emulate(shname, opt_R, &new_emulation, new_opts); + optlist = newlinklist(); + if (parseopts(nam, &argv, new_opts, &cmd, optlist, 0)) { + ret = 1; + goto restore; + } + + /* parseopts() has consumed anything that looks like an option */ + if (*argv) { + zwarnnam(nam, "unknown argument %s", *argv); + goto restore; + } + + savepatterns = savepatterndisables(); + /* + * All emulations start with an empty set of pattern disables, + * hence no special "sticky" behaviour is required. + */ + clearpatterndisables(); + + saveemulation = emulation; + emulation = new_emulation; + memcpy(opts, new_opts, sizeof(opts)); + /* If "-c command" is given, evaluate command using specified + * emulation mode. + */ + if (cmd) { + if (opt_L) { + zwarnnam(nam, "option -L incompatible with -c"); + goto restore2; + } + *--argv = cmd; /* on stack, never free()d, see execbuiltin() */ + } else { + if (opt_L) + opts[LOCALOPTIONS] = opts[LOCALTRAPS] = opts[LOCALPATTERNS] = 1; + return 0; + } + + save_sticky = sticky; + sticky = hcalloc(sizeof(*sticky)); + sticky->emulation = emulation; + for (optnode = firstnode(optlist); optnode; incnode(optnode)) { + /* Data is index into new_opts */ + char *optptr = (char *)getdata(optnode); + if (*optptr) + sticky->n_on_opts++; + else + sticky->n_off_opts++; + } + if (sticky->n_on_opts) + on_ptr = sticky->on_opts = + zhalloc(sticky->n_on_opts * sizeof(*sticky->on_opts)); + else + on_ptr = NULL; + if (sticky->n_off_opts) + off_ptr = sticky->off_opts = zhalloc(sticky->n_off_opts * + sizeof(*sticky->off_opts)); + else + off_ptr = NULL; + for (optnode = firstnode(optlist); optnode; incnode(optnode)) { + /* Data is index into new_opts */ + char *optptr = (char *)getdata(optnode); + int optno = optptr - new_opts; + if (*optptr) + *on_ptr++ = optno; + else + *off_ptr++ = optno; + } + ret = eval(argv); + sticky = save_sticky; +restore2: + emulation = saveemulation; + memcpy(opts, saveopts, sizeof(opts)); + restorepatterndisables(savepatterns); +restore: + keyboardhackchar = savehackchar; + inittyptab(); /* restore banghist */ + return ret; +} + +/* eval: simple evaluation */ + +/**/ +mod_export int ineval; + +/**/ +int +bin_eval(UNUSED(char *nam), char **argv, UNUSED(Options ops), UNUSED(int func)) +{ + return eval(argv); +} + +static char *zbuf; +static int readfd; + +/* Read a character from readfd, or from the buffer zbuf. Return EOF on end of +file/buffer. */ + +/* read: get a line of input, or (for compctl functions) return some * + * useful data about the state of the editing line. The -E and -e * + * options mean that the result should be sent to stdout. -e means, * + * in addition, that the result should not actually be assigned to * + * the specified parameters. */ + +/**/ +int +bin_read(char *name, char **args, Options ops, UNUSED(int func)) +{ + char *reply, *readpmpt; + int bsiz, c = 0, gotnl = 0, al = 0, first, nchars = 1, bslash, keys = 0; + int haso = 0; /* true if /dev/tty has been opened specially */ + int isem = !strcmp(term, "emacs"), izle = zleactive; + char *buf, *bptr, *firstarg, *zbuforig; + LinkList readll = newlinklist(); + FILE *oshout = NULL; + int readchar = -1, val, resettty = 0; + struct ttyinfo saveti; + char d; + long izle_timeout = 0; +#ifdef MULTIBYTE_SUPPORT + wchar_t delim = L'\n', wc; + mbstate_t mbs; + char *laststart; + size_t ret; +#else + char delim = '\n'; +#endif + + if (OPT_HASARG(ops,c='k')) { + char *eptr, *optarg = OPT_ARG(ops,c); + nchars = (int)zstrtol(optarg, &eptr, 10); + if (*eptr) { + zwarnnam(name, "number expected after -%c: %s", c, optarg); + return 1; + } + } + /* This `*args++ : *args' looks a bit weird, but it works around a bug + * in gcc-2.8.1 under DU 4.0. */ + firstarg = (*args && **args == '?' ? *args++ : *args); + reply = *args ? *args++ : OPT_ISSET(ops,'A') ? "reply" : "REPLY"; + + if (OPT_ISSET(ops,'A') && *args) { + zwarnnam(name, "only one array argument allowed"); + return 1; + } + + /* handle compctl case */ + if(OPT_ISSET(ops,'l') || OPT_ISSET(ops,'c')) + return compctlreadptr(name, args, ops, reply); + + if ((OPT_ISSET(ops,'k') || OPT_ISSET(ops,'q')) && + !OPT_ISSET(ops,'u') && !OPT_ISSET(ops,'p')) { + if (!zleactive) { + if (SHTTY == -1) { + /* need to open /dev/tty specially */ + if ((SHTTY = open("/dev/tty", O_RDWR|O_NOCTTY)) != -1) { + haso = 1; + oshout = shout; + init_shout(); + } + } else if (!shout) { + /* We need an output FILE* on the tty */ + init_shout(); + } + /* We should have a SHTTY opened by now. */ + if (SHTTY == -1) { + /* Unfortunately, we didn't. */ + fprintf(stderr, "not interactive and can't open terminal\n"); + fflush(stderr); + return 1; + } + if (unset(INTERACTIVE)) + gettyinfo(&shttyinfo); + /* attach to the tty */ + attachtty(mypgrp); + if (!isem) + setcbreak(); + readfd = SHTTY; + } + keys = 1; + } else if (OPT_HASARG(ops,'u') && !OPT_ISSET(ops,'p')) { + /* -u means take input from the specified file descriptor. */ + char *eptr, *argptr = OPT_ARG(ops,'u'); + /* The old code handled -up, but that was never documented. Still...*/ + if (!strcmp(argptr, "p")) { + readfd = coprocin; + if (readfd < 0) { + zwarnnam(name, "-p: no coprocess"); + return 1; + } + } else { + readfd = (int)zstrtol(argptr, &eptr, 10); + if (*eptr) { + zwarnnam(name, "number expected after -%c: %s", 'u', argptr); + return 1; + } + } +#if 0 + /* This code is left as a warning to future generations --- pws. */ + for (readfd = 9; readfd && !OPT_ISSET(ops,readfd + '0'); --readfd); +#endif + izle = 0; + } else if (OPT_ISSET(ops,'p')) { + readfd = coprocin; + if (readfd < 0) { + zwarnnam(name, "-p: no coprocess"); + return 1; + } + izle = 0; + } else + readfd = izle = 0; + + if (OPT_ISSET(ops,'s') && SHTTY != -1) { + struct ttyinfo ti; + gettyinfo(&ti); + saveti = ti; + resettty = 1; +#ifdef HAS_TIO + ti.tio.c_lflag &= ~ECHO; +#else + ti.sgttyb.sg_flags &= ~ECHO; +#endif + settyinfo(&ti); + } + + /* handle prompt */ + if (firstarg) { + for (readpmpt = firstarg; + *readpmpt && *readpmpt != '?'; readpmpt++); + if (*readpmpt++) { + if (keys || isatty(0)) { + zputs(readpmpt, (shout ? shout : stderr)); + fflush(shout ? shout : stderr); + } + readpmpt[-1] = '\0'; + } + } + + if (OPT_ISSET(ops,'d')) { + char *delimstr = OPT_ARG(ops,'d'); +#ifdef MULTIBYTE_SUPPORT + wint_t wi; + + if (isset(MULTIBYTE)) { + mb_charinit(); + (void)mb_metacharlenconv(delimstr, &wi); + } + else + wi = WEOF; + if (wi != WEOF) + delim = (wchar_t)wi; + else + delim = (wchar_t)((delimstr[0] == Meta) ? + delimstr[1] ^ 32 : delimstr[0]); +#else + delim = (delimstr[0] == Meta) ? delimstr[1] ^ 32 : delimstr[0]; +#endif + if (SHTTY != -1) { + struct ttyinfo ti; + gettyinfo(&ti); + if (! resettty) { + saveti = ti; + resettty = 1; + } +#ifdef HAS_TIO + ti.tio.c_lflag &= ~ICANON; + ti.tio.c_cc[VMIN] = 1; + ti.tio.c_cc[VTIME] = 0; +#else + ti.sgttyb.sg_flags |= CBREAK; +#endif + settyinfo(&ti); + } + } + if (OPT_ISSET(ops,'t')) { + zlong timeout = 0; + if (OPT_HASARG(ops,'t')) { + mnumber mn = zero_mnumber; + mn = matheval(OPT_ARG(ops,'t')); + if (errflag) + return 1; + if (mn.type == MN_FLOAT) { + mn.u.d *= 1e6; + timeout = (zlong)mn.u.d; + } else { + timeout = (zlong)mn.u.l * (zlong)1000000; + } + } + if (izle) { + /* + * Timeout is in 100ths of a second rather than us. + * See calc_timeout() in zle_main for format of this. + */ + timeout = -(timeout/(zlong)10000 + 1L); + izle_timeout = (long)timeout; +#ifdef LONG_MAX + /* saturate if range exceeded */ + if ((zlong)izle_timeout != timeout) + izle_timeout = LONG_MAX; +#endif + } else { + if (readfd == -1 || + !read_poll(readfd, &readchar, keys && !zleactive, + timeout)) { + if (keys && !zleactive && !isem) + settyinfo(&shttyinfo); + else if (resettty && SHTTY != -1) + settyinfo(&saveti); + if (haso) { + fclose(shout); + shout = oshout; + SHTTY = -1; + } + return OPT_ISSET(ops,'q') ? 2 : 1; + } + } + } + +#ifdef MULTIBYTE_SUPPORT + memset(&mbs, 0, sizeof(mbs)); +#endif + + /* + * option -k means read only a given number of characters (default 1) + * option -q means get one character, and interpret it as a Y or N + */ + if (OPT_ISSET(ops,'k') || OPT_ISSET(ops,'q')) { + int eof = 0; + /* allocate buffer space for result */ +#ifdef MULTIBYTE_SUPPORT + bptr = buf = (char *)zalloc(nchars*MB_CUR_MAX+1); +#else + bptr = buf = (char *)zalloc(nchars+1); +#endif + + do { + if (izle) { + zleentry(ZLE_CMD_GET_KEY, izle_timeout, NULL, &val); + if (val < 0) { + eof = 1; + break; + } + *bptr = (char) val; +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) { + ret = mbrlen(bptr++, 1, &mbs); + if (ret == MB_INVALID) + memset(&mbs, 0, sizeof(mbs)); + /* treat invalid as single character */ + if (ret != MB_INCOMPLETE) + nchars--; + continue; + } else { + bptr++; + nchars--; + } +#else + bptr++; + nchars--; +#endif + } else { + /* If read returns 0, is end of file */ + if (readchar >= 0) { + *bptr = readchar; + val = 1; + readchar = -1; + } else { + while ((val = read(readfd, bptr, nchars)) < 0) { + if (errno != EINTR || + errflag || retflag || breaks || contflag) + break; + } + if (val <= 0) { + eof = 1; + break; + } + } + +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) { + while (val > 0) { + ret = mbrlen(bptr, val, &mbs); + if (ret == MB_INCOMPLETE) { + bptr += val; + break; + } else { + if (ret == MB_INVALID) { + memset(&mbs, 0, sizeof(mbs)); + /* treat as single byte */ + ret = 1; + } + else if (ret == 0) /* handle null as normal char */ + ret = 1; + else if (ret > (size_t)val) { + /* Some mbrlen()s return the full char len */ + ret = val; + } + nchars--; + val -= ret; + bptr += ret; + } + } + continue; + } +#endif + /* decrement number of characters read from number required */ + nchars -= val; + + /* increment pointer past read characters */ + bptr += val; + } + } while (nchars > 0); + + if (!izle && !OPT_ISSET(ops,'u') && !OPT_ISSET(ops,'p')) { + /* dispose of result appropriately, etc. */ + if (isem) + while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n'); + else { + settyinfo(&shttyinfo); + resettty = 0; + } + if (haso) { + fclose(shout); /* close(SHTTY) */ + shout = oshout; + SHTTY = -1; + } + } + + if (OPT_ISSET(ops,'q')) + { + /* + * Keep eof as status but status is now whether we read + * 'y' or 'Y'. If we timed out, status is 2. + */ + if (eof) + eof = 2; + else + eof = (bptr - buf != 1 || (buf[0] != 'y' && buf[0] != 'Y')); + buf[0] = eof ? 'n' : 'y'; + bptr = buf + 1; + } + if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) + fwrite(buf, bptr - buf, 1, stdout); + if (!OPT_ISSET(ops,'e')) + setsparam(reply, metafy(buf, bptr - buf, META_REALLOC)); + else + zfree(buf, bptr - buf + 1); + if (resettty && SHTTY != -1) + settyinfo(&saveti); + return eof; + } + + /* All possible special types of input have been exhausted. Take one line, + and assign words to the parameters until they run out. Leftover words go + onto the last parameter. If an array is specified, all the words become + separate elements of the array. */ + + zbuforig = zbuf = (!OPT_ISSET(ops,'z')) ? NULL : + (nonempty(bufstack)) ? (char *) getlinknode(bufstack) : ztrdup(""); + first = 1; + bslash = 0; + while (*args || (OPT_ISSET(ops,'A') && !gotnl)) { + sigset_t s = child_unblock(); + buf = bptr = (char *)zalloc(bsiz = 64); +#ifdef MULTIBYTE_SUPPORT + laststart = buf; + ret = MB_INCOMPLETE; +#endif + /* get input, a character at a time */ + while (!gotnl) { + c = zread(izle, &readchar, izle_timeout); + /* \ at the end of a line indicates a continuation * + * line, except in raw mode (-r option) */ +#ifdef MULTIBYTE_SUPPORT + if (c == EOF) { + /* not waiting to be completed any more */ + ret = 0; + break; + } + *bptr = (char)c; + if (isset(MULTIBYTE)) { + ret = mbrtowc(&wc, bptr, 1, &mbs); + if (!ret) /* NULL */ + ret = 1; + } else { + ret = 1; + wc = (wchar_t)c; + } + if (ret != MB_INCOMPLETE) { + if (ret == MB_INVALID) { + memset(&mbs, 0, sizeof(mbs)); + /* Treat this as a single character */ + wc = (wchar_t)c; + laststart = bptr; + } + if (bslash && wc == delim) { + bslash = 0; + continue; + } + if (wc == delim) + break; + /* + * `first' is non-zero if any separator we encounter is a + * non-whitespace separator, which means that anything + * (even an empty string) between, before or after separators + * is significant. If it is zero, we have a whitespace + * separator, which shouldn't cause extra empty strings to + * be emitted. Hence the test for (*buf || first) when + * we assign the result of reading a word. + */ + if (!bslash && wcsitype(wc, ISEP)) { + if (bptr != buf || + (!(c < 128 && iwsep(c)) && first)) { + first |= !(c < 128 && iwsep(c)); + break; + } + first |= !(c < 128 && iwsep(c)); + continue; + } + bslash = (wc == L'\\' && !bslash && !OPT_ISSET(ops,'r')); + if (bslash) + continue; + first = 0; + } + if (imeta(STOUC(*bptr))) { + bptr[1] = bptr[0] ^ 32; + bptr[0] = Meta; + bptr += 2; + } + else + bptr++; + if (ret != MB_INCOMPLETE) + laststart = bptr; +#else + if (c == EOF) + break; + if (bslash && c == delim) { + bslash = 0; + continue; + } + if (c == delim) + break; + /* + * `first' is non-zero if any separator we encounter is a + * non-whitespace separator, which means that anything + * (even an empty string) between, before or after separators + * is significant. If it is zero, we have a whitespace + * separator, which shouldn't cause extra empty strings to + * be emitted. Hence the test for (*buf || first) when + * we assign the result of reading a word. + */ + if (!bslash && isep(c)) { + if (bptr != buf || (!iwsep(c) && first)) { + first |= !iwsep(c); + break; + } + first |= !iwsep(c); + continue; + } + bslash = c == '\\' && !bslash && !OPT_ISSET(ops,'r'); + if (bslash) + continue; + first = 0; + if (imeta(c)) { + *bptr++ = Meta; + *bptr++ = c ^ 32; + } else + *bptr++ = c; +#endif + /* increase the buffer size, if necessary */ + if (bptr >= buf + bsiz - 1) { + int blen = bptr - buf; +#ifdef MULTIBYTE_SUPPORT + int llen = laststart - buf; +#endif + + buf = realloc(buf, bsiz *= 2); + bptr = buf + blen; +#ifdef MULTIBYTE_SUPPORT + laststart = buf + llen; +#endif + } + } + signal_setmask(s); +#ifdef MULTIBYTE_SUPPORT + if (c == EOF) { + gotnl = 1; + *bptr = '\0'; /* see below */ + } else if (ret == MB_INCOMPLETE) { + /* + * We can only get here if there is an EOF in the + * middle of a character... safest to keep the debris, + * I suppose. + */ + *bptr = '\0'; + } else { + if (wc == delim) + gotnl = 1; + *laststart = '\0'; + } +#else + if (c == delim || c == EOF) + gotnl = 1; + *bptr = '\0'; +#endif + /* dispose of word appropriately */ + if (OPT_ISSET(ops,'e') || + /* + * When we're doing an array assignment, we'll + * handle echoing at that point. In all other + * cases (including -A with no assignment) + * we'll do it here. + */ + (OPT_ISSET(ops,'E') && !OPT_ISSET(ops,'A'))) { + zputs(buf, stdout); + putchar('\n'); + } + if (!OPT_ISSET(ops,'e') && (*buf || first || gotnl)) { + if (OPT_ISSET(ops,'A')) { + addlinknode(readll, buf); + al++; + } else + setsparam(reply, buf); + } else + free(buf); + if (!OPT_ISSET(ops,'A')) + reply = *args++; + } + /* handle EOF */ + if (c == EOF) { + if (readfd == coprocin) { + close(coprocin); + close(coprocout); + coprocin = coprocout = -1; + } + } + /* final assignment (and display) of array parameter */ + if (OPT_ISSET(ops,'A')) { + char **pp, **p = NULL; + LinkNode n; + + p = (OPT_ISSET(ops,'e') ? (char **)NULL + : (char **)zalloc((al + 1) * sizeof(char *))); + + for (pp = p, n = firstnode(readll); n; incnode(n)) { + if (OPT_ISSET(ops,'E')) { + zputs((char *) getdata(n), stdout); + putchar('\n'); + } + if (p) + *pp++ = (char *)getdata(n); + else + zsfree(getdata(n)); + } + if (p) { + *pp++ = NULL; + setaparam(reply, p); + } + if (resettty && SHTTY != -1) + settyinfo(&saveti); + return c == EOF; + } + buf = bptr = (char *)zalloc(bsiz = 64); +#ifdef MULTIBYTE_SUPPORT + laststart = buf; + ret = MB_INCOMPLETE; +#endif + /* any remaining part of the line goes into one parameter */ + bslash = 0; + if (!gotnl) { + sigset_t s = child_unblock(); + for (;;) { + c = zread(izle, &readchar, izle_timeout); +#ifdef MULTIBYTE_SUPPORT + if (c == EOF) { + /* not waiting to be completed any more */ + ret = 0; + break; + } + *bptr = (char)c; + if (isset(MULTIBYTE)) { + ret = mbrtowc(&wc, bptr, 1, &mbs); + if (!ret) /* NULL */ + ret = 1; + } else { + ret = 1; + wc = (wchar_t)c; + } + if (ret != MB_INCOMPLETE) { + if (ret == MB_INVALID) { + memset(&mbs, 0, sizeof(mbs)); + /* Treat this as a single character */ + wc = (wchar_t)c; + laststart = bptr; + } + /* + * \ at the end of a line introduces a continuation line, + * except in raw mode (-r option) + */ + if (bslash && wc == delim) { + bslash = 0; + continue; + } + if (wc == delim && !zbuf) + break; + if (!bslash && bptr == buf && wcsitype(wc, ISEP)) { + if (c < 128 && iwsep(c)) + continue; + else if (!first) { + first = 1; + continue; + } + } + bslash = (wc == L'\\' && !bslash && !OPT_ISSET(ops,'r')); + if (bslash) + continue; + } + if (imeta(STOUC(*bptr))) { + bptr[1] = bptr[0] ^ 32; + bptr[0] = Meta; + bptr += 2; + } + else + bptr++; + if (ret != MB_INCOMPLETE) + laststart = bptr; +#else + /* \ at the end of a line introduces a continuation line, except in + raw mode (-r option) */ + if (bslash && c == delim) { + bslash = 0; + continue; + } + if (c == EOF || (c == delim && !zbuf)) + break; + if (!bslash && isep(c) && bptr == buf) { + if (iwsep(c)) + continue; + else if (!first) { + first = 1; + continue; + } + } + bslash = c == '\\' && !bslash && !OPT_ISSET(ops,'r'); + if (bslash) + continue; + if (imeta(c)) { + *bptr++ = Meta; + *bptr++ = c ^ 32; + } else + *bptr++ = c; +#endif + /* increase the buffer size, if necessary */ + if (bptr >= buf + bsiz - 1) { + int blen = bptr - buf; +#ifdef MULTIBYTE_SUPPORT + int llen = laststart - buf; +#endif + + buf = realloc(buf, bsiz *= 2); + bptr = buf + blen; +#ifdef MULTIBYTE_SUPPORT + laststart = buf + llen; +#endif + } + } + signal_setmask(s); + } +#ifdef MULTIBYTE_SUPPORT + if (ret != MB_INCOMPLETE) + bptr = laststart; +#endif + /* + * Strip trailing IFS whitespace. + * iwsep can only be certain single-byte ASCII bytes, but we + * must check the byte isn't metafied. + */ + while (bptr > buf) { + if (bptr > buf + 1 && bptr[-2] == Meta) { + /* non-ASCII, can't be IWSEP */ + break; + } else if (iwsep(bptr[-1])) + bptr--; + else + break; + } + *bptr = '\0'; + if (resettty && SHTTY != -1) + settyinfo(&saveti); + /* final assignment of reply, etc. */ + if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) { + zputs(buf, stdout); + putchar('\n'); + } + if (!OPT_ISSET(ops,'e')) + setsparam(reply, buf); + else + zsfree(buf); + if (zbuforig) { + char first = *zbuforig; + + zsfree(zbuforig); + if (!first) + return 1; + } else if (c == EOF) { + if (readfd == coprocin) { + close(coprocin); + close(coprocout); + coprocin = coprocout = -1; + } + return 1; + } + /* + * The following is to ensure a failure to set the parameter + * causes a non-zero status return. There are arguments for + * turning a non-zero status into errflag more widely. + */ + return errflag; +} + +/**/ +static int +zread(int izle, int *readchar, long izle_timeout) +{ + char cc, retry = 0; + int ret; + + if (izle) { + int c; + zleentry(ZLE_CMD_GET_KEY, izle_timeout, NULL, &c); + + return (c < 0 ? EOF : c); + } + /* use zbuf if possible */ + if (zbuf) { + /* If zbuf points to anything, it points to the next character in the + buffer. This may be a null byte to indicate EOF. If reading from the + buffer, move on the buffer pointer. */ + if (*zbuf == Meta) + return zbuf++, STOUC(*zbuf++ ^ 32); + else + return (*zbuf) ? STOUC(*zbuf++) : EOF; + } + if (*readchar >= 0) { + cc = *readchar; + *readchar = -1; + return STOUC(cc); + } + for (;;) { + /* read a character from readfd */ + ret = read(readfd, &cc, 1); + switch (ret) { + case 1: + /* return the character read */ + return STOUC(cc); + case -1: +#if defined(EAGAIN) || defined(EWOULDBLOCK) + if (!retry && readfd == 0 && ( +# ifdef EAGAIN + errno == EAGAIN +# ifdef EWOULDBLOCK + || +# endif /* EWOULDBLOCK */ +# endif /* EAGAIN */ +# ifdef EWOULDBLOCK + errno == EWOULDBLOCK +# endif /* EWOULDBLOCK */ + ) && setblock_stdin()) { + retry = 1; + continue; + } else +#endif /* EAGAIN || EWOULDBLOCK */ + if (errno == EINTR && !(errflag || retflag || breaks || contflag)) + continue; + break; + } + return EOF; + } +} + +/* holds arguments for testlex() */ +/**/ +char **testargs, **curtestarg; + +/* test, [: the old-style general purpose logical expression builtin */ + +/**/ +void +testlex(void) +{ + if (tok == LEXERR) + return; + + tokstr = *(curtestarg = testargs); + if (!*testargs) { + /* if tok is already zero, reading past the end: error */ + tok = tok ? NULLTOK : LEXERR; + return; + } else if (!strcmp(*testargs, "-o")) + tok = DBAR; + else if (!strcmp(*testargs, "-a")) + tok = DAMPER; + else if (!strcmp(*testargs, "!")) + tok = BANG; + else if (!strcmp(*testargs, "(")) + tok = INPAR; + else if (!strcmp(*testargs, ")")) + tok = OUTPAR; + else + tok = STRING; + testargs++; +} + +/**/ +int +bin_test(char *name, char **argv, UNUSED(Options ops), int func) +{ + char **s; + Eprog prog; + struct estate state; + int nargs, sense = 0, ret; + + /* if "test" was invoked as "[", it needs a matching "]" * + * which is subsequently ignored */ + if (func == BIN_BRACKET) { + for (s = argv; *s; s++); + if (s == argv || strcmp(s[-1], "]")) { + zwarnnam(name, "']' expected"); + return 2; + } + s[-1] = NULL; + } + /* an empty argument list evaluates to false (1) */ + if (!*argv) + return 1; + + /* + * Implement some XSI extensions to POSIX here. + * See + * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html + */ + nargs = arrlen(argv); + if (nargs == 3 || nargs == 4) + { + /* + * As parentheses are an extension, we need to be careful --- + * if this is a three-argument expression that could + * be a binary operator, prefer that. + */ + if (!strcmp(argv[0], "(") && !strcmp(argv[nargs-1],")") && + (nargs != 3 || !is_cond_binary_op(argv[1]))) { + argv[nargs-1] = NULL; + argv++; + } + if (nargs == 4 && !strcmp("!", argv[0])) { + sense = 1; + argv++; + } + } + + zcontext_save(); + testargs = argv; + tok = NULLTOK; + condlex = testlex; + testlex(); + prog = parse_cond(); + condlex = zshlex; + + if (errflag) { + errflag &= ~ERRFLAG_ERROR; + zcontext_restore(); + return 2; + } + + if (!prog || tok == LEXERR) { + zwarnnam(name, tokstr ? "parse error" : "argument expected"); + zcontext_restore(); + return 2; + } + zcontext_restore(); + + if (*curtestarg) { + zwarnnam(name, "too many arguments"); + return 2; + } + + /* syntax is OK, so evaluate */ + + state.prog = prog; + state.pc = prog->prog; + state.strs = prog->strs; + + ret = evalcond(&state, name); + if (ret < 2 && sense) + ret = ! ret; + + return ret; +} + +/* display a time, provided in units of 1/60s, as minutes and seconds */ +#define pttime(X) printf("%ldm%ld.%02lds",((long) (X))/(60 * clktck),\ + ((long) (X))/clktck%clktck,\ + ((long) (X))*100/clktck%100) + +/* times: display, in a two-line format, the times provided by times(3) */ + +/**/ +int +bin_times(UNUSED(char *name), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func)) +{ + struct tms buf; + long clktck = get_clktck(); + + /* get time accounting information */ + if (times(&buf) == -1) + return 1; + pttime(buf.tms_utime); /* user time */ + putchar(' '); + pttime(buf.tms_stime); /* system time */ + putchar('\n'); + pttime(buf.tms_cutime); /* user time, children */ + putchar(' '); + pttime(buf.tms_cstime); /* system time, children */ + putchar('\n'); + return 0; +} + +/* trap: set/unset signal traps */ + +/**/ +int +bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) +{ + Eprog prog; + char *arg, *s; + int sig; + + if (*argv && !strcmp(*argv, "--")) + argv++; + + /* If given no arguments, list all currently-set traps */ + if (!*argv) { + queue_signals(); + for (sig = 0; sig < VSIGCOUNT; sig++) { + if (sigtrapped[sig] & ZSIG_FUNC) { + HashNode hn; + + if ((hn = gettrapnode(sig, 0))) + shfunctab->printnode(hn, 0); + DPUTS(!hn, "BUG: I did not find any trap functions!"); + } else if (sigtrapped[sig]) { + const char *name = getsigname(sig); + if (!siglists[sig]) + printf("trap -- '' %s\n", name); + else { + s = getpermtext(siglists[sig], NULL, 0); + printf("trap -- "); + quotedzputs(s, stdout); + printf(" %s\n", name); + zsfree(s); + } + } + } + unqueue_signals(); + return 0; + } + + /* If we have a signal number, unset the specified * + * signals. With only -, remove all traps. */ + if ((getsignum(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) { + if (!*argv) { + for (sig = 0; sig < VSIGCOUNT; sig++) + unsettrap(sig); + } else { + for (; *argv; argv++) { + sig = getsignum(*argv); + if (sig == -1) { + zwarnnam(name, "undefined signal: %s", *argv); + break; + } + unsettrap(sig); + } + } + return *argv != NULL; + } + + /* Sort out the command to execute on trap */ + arg = *argv++; + if (!*arg) + prog = &dummy_eprog; + else if (!(prog = parse_string(arg, 1))) { + zwarnnam(name, "couldn't parse trap command"); + return 1; + } + + /* set traps */ + for (; *argv; argv++) { + Eprog t; + int flags; + + sig = getsignum(*argv); + if (sig == -1) { + zwarnnam(name, "undefined signal: %s", *argv); + break; + } + 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. + * If we ever have more than one alias per signal this + * will need improving. + */ + flags = ZSIG_ALIAS; + } + t = dupeprog(prog, 0); + if (settrap(sig, t, flags)) + freeeprog(t); + } + return *argv != NULL; +} + +/**/ +int +bin_ttyctl(UNUSED(char *name), UNUSED(char **argv), Options ops, UNUSED(int func)) +{ + if (OPT_ISSET(ops,'f')) + ttyfrozen = 1; + else if (OPT_ISSET(ops,'u')) + ttyfrozen = 0; + else + printf("tty is %sfrozen\n", ttyfrozen ? "" : "not "); + return 0; +} + +/* let -- mathematical evaluation */ + +/**/ +int +bin_let(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int func)) +{ + mnumber val = zero_mnumber; + + while (*argv) + val = matheval(*argv++); + /* Errors in math evaluation in let are non-fatal. */ + errflag &= ~ERRFLAG_ERROR; + /* should test for fabs(val.u.d) < epsilon? */ + return (val.type == MN_INTEGER) ? val.u.l == 0 : val.u.d == 0.0; +} + +/* umask command. umask may be specified as octal digits, or in the * + * symbolic form that chmod(1) uses. Well, a subset of it. Remember * + * that only the bottom nine bits of umask are used, so there's no * + * point allowing the set{u,g}id and sticky bits to be specified. */ + +/**/ +int +bin_umask(char *nam, char **args, Options ops, UNUSED(int func)) +{ + mode_t um; + char *s = *args; + + /* Get the current umask. */ + um = umask(0); + umask(um); + /* No arguments means to display the current setting. */ + if (!s) { + if (OPT_ISSET(ops,'S')) { + char *who = "ugo"; + + while (*who) { + char *what = "rwx"; + printf("%c=", *who++); + while (*what) { + if (!(um & 0400)) + putchar(*what); + um <<= 1; + what++; + } + putchar(*who ? ',' : '\n'); + } + } else { + if (um & 0700) + putchar('0'); + printf("%03o\n", (unsigned)um); + } + return 0; + } + + if (idigit(*s)) { + /* Simple digital umask. */ + um = zstrtol(s, &s, 8); + if (*s) { + zwarnnam(nam, "bad umask"); + return 1; + } + } else { + /* Symbolic notation -- slightly complicated. */ + int whomask, umaskop, mask; + + /* More than one symbolic argument may be used at once, each separated + by commas. */ + for (;;) { + /* First part of the argument -- who does this apply to? + u=owner, g=group, o=other. */ + whomask = 0; + while (*s == 'u' || *s == 'g' || *s == 'o' || *s == 'a') + if (*s == 'u') + s++, whomask |= 0700; + else if (*s == 'g') + s++, whomask |= 0070; + else if (*s == 'o') + s++, whomask |= 0007; + else if (*s == 'a') + s++, whomask |= 0777; + /* Default whomask is everyone. */ + if (!whomask) + whomask = 0777; + /* Operation may be +, - or =. */ + umaskop = (int)*s; + if (!(umaskop == '+' || umaskop == '-' || umaskop == '=')) { + if (umaskop) + zwarnnam(nam, "bad symbolic mode operator: %c", umaskop); + else + zwarnnam(nam, "bad umask"); + return 1; + } + /* Permissions mask -- r=read, w=write, x=execute. */ + mask = 0; + while (*++s && *s != ',') + if (*s == 'r') + mask |= 0444 & whomask; + else if (*s == 'w') + mask |= 0222 & whomask; + else if (*s == 'x') + mask |= 0111 & whomask; + else { + zwarnnam(nam, "bad symbolic mode permission: %c", *s); + return 1; + } + /* Apply parsed argument to um. */ + if (umaskop == '+') + um &= ~mask; + else if (umaskop == '-') + um |= mask; + else /* umaskop == '=' */ + um = (um | (whomask)) & ~mask; + if (*s == ',') + s++; + else + break; + } + if (*s) { + zwarnnam(nam, "bad character in symbolic mode: %c", *s); + return 1; + } + } + + /* Finally, set the new umask. */ + umask(um); + return 0; +} + +/* Generic builtin for facilities not available on this OS */ + +/**/ +mod_export int +bin_notavail(char *nam, UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func)) +{ + zwarnnam(nam, "not available on this system"); + return 1; +} diff --git a/dotfiles/system/.zsh/modules/Src/compat.c b/dotfiles/system/.zsh/modules/Src/compat.c new file mode 100644 index 0000000..7b5c441 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/compat.c @@ -0,0 +1,742 @@ +/* + * compat.c - compatibility routines for the deprived + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "compat.pro" + +/* Return pointer to first occurence of string t * + * in string s. Return NULL if not present. */ + +/**/ +#ifndef HAVE_STRSTR + +/**/ +char * +strstr(const char *s, const char *t) +{ + const char *p1, *p2; + + for (; *s; s++) { + for (p1 = s, p2 = t; *p2; p1++, p2++) + if (*p1 != *p2) + break; + if (!*p2) + return (char *)s; + } + return NULL; +} + +/**/ +#endif + + +/**/ +#ifndef HAVE_GETHOSTNAME + +/**/ +int +gethostname(char *name, size_t namelen) +{ + struct utsname uts; + + uname(&uts); + if(strlen(uts.nodename) >= namelen) { + errno = EINVAL; + return -1; + } + strcpy(name, uts.nodename); + return 0; +} + +/**/ +#endif + + +/**/ +#ifndef HAVE_GETTIMEOFDAY + +/**/ +int +gettimeofday(struct timeval *tv, struct timezone *tz) +{ + tv->tv_usec = 0; + tv->tv_sec = (long)time((time_t) 0); + return 0; +} + +/**/ +#endif + + +/* Provide clock time with nanoseconds */ + +/**/ +mod_export int +zgettime(struct timespec *ts) +{ + int ret = -1; + +#ifdef HAVE_CLOCK_GETTIME + struct timespec dts; + if (clock_gettime(CLOCK_REALTIME, &dts) < 0) { + zwarn("unable to retrieve time: %e", errno); + ret--; + } else { + ret++; + ts->tv_sec = (time_t) dts.tv_sec; + ts->tv_nsec = (long) dts.tv_nsec; + } +#endif + + if (ret) { + struct timeval dtv; + struct timezone dtz; + gettimeofday(&dtv, &dtz); + ret++; + ts->tv_sec = (time_t) dtv.tv_sec; + ts->tv_nsec = (long) dtv.tv_usec * 1000; + } + + return ret; +} + + +/* compute the difference between two calendar times */ + +/**/ +#ifndef HAVE_DIFFTIME + +/**/ +double +difftime(time_t t2, time_t t1) +{ + return ((double)t2 - (double)t1); +} + +/**/ +#endif + + +/**/ +#ifndef HAVE_STRERROR +extern char *sys_errlist[]; + +/* Get error message string associated with a particular * + * error number, and returns a pointer to that string. * + * This is not a particularly robust version of strerror. */ + +/**/ +char * +strerror(int errnum) +{ + return (sys_errlist[errnum]); +} + +/**/ +#endif + + +#if 0 +/* pathconf(_PC_PATH_MAX) is not currently useful to zsh. The value * + * returned varies depending on a number of factors, e.g. the amount * + * of memory available to the operating system at a given time; thus * + * it can't be used for buffer allocation, or even as an indication * + * of whether an attempt to use or create a given pathname may fail * + * at any future time. * + * * + * The call is also permitted to fail if the argument path is not an * + * existing directory, so even to make sense of that one must search * + * for a valid directory somewhere in the path and adjust. Even if * + * it succeeds, the return value is relative to the input directory, * + * and therefore potentially relative to the length of the shortest * + * path either to that directory or to our working directory. * + * * + * Finally, see the note below for glibc; detection of pathconf() is * + * not by itself an indication that it works reliably. */ + +/* The documentation for pathconf() says something like: * + * The limit is returned, if one exists. If the system does * + * not have a limit for the requested resource, -1 is * + * returned, and errno is unchanged. If there is an error, * + * -1 is returned, and errno is set to reflect the nature of * + * the error. * + * * + * System calls are not permitted to set errno to 0; but we must (or * + * some other flag value) in order to determine that the resource is * + * unlimited. What use is leaving errno unchanged? Instead, define * + * a wrapper that resets errno to 0 and returns 0 for "the system * + * does not have a limit," so that -1 always means a real error. */ + +/**/ +mod_export long +zpathmax(char *dir) +{ +#ifdef HAVE_PATHCONF + long pathmax; + + errno = 0; + if ((pathmax = pathconf(dir, _PC_PATH_MAX)) >= 0) { + /* Some versions of glibc pathconf return a hardwired value! */ + return pathmax; + } else if (errno == EINVAL || errno == ENOENT || errno == ENOTDIR) { + /* Work backward to find a directory, until we run out of path. */ + char *tail = strrchr(dir, '/'); + while (tail > dir && tail[-1] == '/') + --tail; + if (tail > dir) { + *tail = 0; + pathmax = zpathmax(dir); + *tail = '/'; + } else { + errno = 0; + if (tail) + pathmax = pathconf("/", _PC_PATH_MAX); + else + pathmax = pathconf(".", _PC_PATH_MAX); + } + if (pathmax > 0) { + long taillen = (tail ? strlen(tail) : (strlen(dir) + 1)); + if (taillen < pathmax) + return pathmax - taillen; + else + errno = ENAMETOOLONG; + } + } + if (errno) + return -1; + else + return 0; /* pathmax should be considered unlimited */ +#else + long dirlen = strlen(dir); + + /* The following is wrong if dir is not an absolute path. */ + return ((long) ((dirlen >= PATH_MAX) ? + ((errno = ENAMETOOLONG), -1) : + ((errno = 0), PATH_MAX - dirlen))); +#endif +} +#endif /* 0 */ + +/**/ +#ifdef HAVE_SYSCONF +/* + * This is replaced by a macro from system.h if not HAVE_SYSCONF. + * 0 is returned by sysconf if _SC_OPEN_MAX is unavailable; + * -1 is returned on error + * + * Neither of these should happen, but resort to OPEN_MAX rather + * than return 0 or -1 just in case. + * + * We'll limit the open maximum to ZSH_INITIAL_OPEN_MAX to + * avoid probing ridiculous numbers of file descriptors. + */ + +/**/ +mod_export long +zopenmax(void) +{ + long openmax; + + if ((openmax = sysconf(_SC_OPEN_MAX)) < 1) { + openmax = OPEN_MAX; + } else if (openmax > OPEN_MAX) { + /* On some systems, "limit descriptors unlimited" or the * + * equivalent will set openmax to a huge number. Unless * + * there actually is a file descriptor > OPEN_MAX already * + * open, nothing in zsh requires the true maximum, and in * + * fact it causes inefficiency elsewhere if we report it. * + * So, report the maximum of OPEN_MAX or the largest open * + * descriptor (is there a better way to find that?). */ + long i, j = OPEN_MAX; + if (openmax > ZSH_INITIAL_OPEN_MAX) + openmax = ZSH_INITIAL_OPEN_MAX; + for (i = j; i < openmax; i += (errno != EINTR)) { + errno = 0; + if (fcntl(i, F_GETFL, 0) < 0 && + (errno == EBADF || errno == EINTR)) + continue; + j = i; + } + openmax = j; + } + + return (max_zsh_fd > openmax) ? max_zsh_fd : openmax; +} + +/**/ +#endif + +/* + * Rationalise the current directory, returning the string. + * + * If "d" is not NULL, it is used to store information about the + * directory. The returned name is also present in d->dirname and is in + * permanently allocated memory. The handling of this case depends on + * whether the fchdir() system call is available; if it is, it is assumed + * the caller is able to restore the current directory. On successfully + * identifying the directory the function returns immediately rather + * than ascending the hierarchy. + * + * If "d" is NULL, no assumption about the caller's behaviour is + * made. The returned string is in heap memory. This case is + * always handled by changing directory up the hierarchy. + * + * On Cygwin or other systems where USE_GETCWD is defined (at the + * time of writing only QNX), we skip all the above and use the + * getcwd() system call. + */ + +/**/ +mod_export char * +zgetdir(struct dirsav *d) +{ + char nbuf[PATH_MAX+3]; + char *buf; + int bufsiz, pos; + struct stat sbuf; + ino_t pino; + dev_t pdev; +#if !defined(__CYGWIN__) && !defined(USE_GETCWD) + struct dirent *de; + DIR *dir; + dev_t dev; + ino_t ino; + int len; +#endif + + buf = zhalloc(bufsiz = PATH_MAX+1); + pos = bufsiz - 1; + buf[pos] = '\0'; + strcpy(nbuf, "../"); + if (stat(".", &sbuf) < 0) { + return NULL; + } + + /* Record the initial inode and device */ + pino = sbuf.st_ino; + pdev = sbuf.st_dev; + if (d) + d->ino = pino, d->dev = pdev; +#if !defined(__CYGWIN__) && !defined(USE_GETCWD) +#ifdef HAVE_FCHDIR + else +#endif + holdintr(); + + for (;;) { + /* Examine the parent of the current directory. */ + if (stat("..", &sbuf) < 0) + break; + + /* Inode and device of curtent directory */ + ino = pino; + dev = pdev; + /* Inode and device of current directory's parent */ + pino = sbuf.st_ino; + pdev = sbuf.st_dev; + + /* If they're the same, we've reached the root directory. */ + if (ino == pino && dev == pdev) { + if (!buf[pos]) + buf[--pos] = '/'; + if (d) { +#ifndef HAVE_FCHDIR + zchdir(buf + pos); + noholdintr(); +#endif + return d->dirname = ztrdup(buf + pos); + } + zchdir(buf + pos); + noholdintr(); + return buf + pos; + } + + /* Search the parent for the current directory. */ + if (!(dir = opendir(".."))) + break; + + while ((de = readdir(dir))) { + char *fn = de->d_name; + /* Ignore `.' and `..'. */ + if (fn[0] == '.' && + (fn[1] == '\0' || + (fn[1] == '.' && fn[2] == '\0'))) + continue; +#ifdef HAVE_STRUCT_DIRENT_D_STAT + if(de->d_stat.st_dev == dev && de->d_stat.st_ino == ino) { + /* Found the directory we're currently in */ + strncpy(nbuf + 3, fn, PATH_MAX); + break; + } +#else /* !HAVE_STRUCT_DIRENT_D_STAT */ +# ifdef HAVE_STRUCT_DIRENT_D_INO + if (dev != pdev || (ino_t) de->d_ino == ino) +# endif /* HAVE_STRUCT_DIRENT_D_INO */ + { + /* Maybe found directory, need to check device & inode */ + strncpy(nbuf + 3, fn, PATH_MAX); + lstat(nbuf, &sbuf); + if (sbuf.st_dev == dev && sbuf.st_ino == ino) + break; + } +#endif /* !HAVE_STRUCT_DIRENT_D_STAT */ + } + closedir(dir); + if (!de) + break; /* Not found */ + /* + * We get the "/" free just by copying from nbuf+2 instead + * of nbuf+3, which is where we copied the path component. + * This means buf[pos] is always a "/". + */ + len = strlen(nbuf + 2); + pos -= len; + while (pos <= 1) { + char *newbuf = zhalloc(2*bufsiz); + memcpy(newbuf + bufsiz, buf, bufsiz); + buf = newbuf; + pos += bufsiz; + bufsiz *= 2; + } + memcpy(buf + pos, nbuf + 2, len); +#ifdef HAVE_FCHDIR + if (d) + return d->dirname = ztrdup(buf + pos + 1); +#endif + if (chdir("..")) + break; + } + + /* + * Fix up the directory, if necessary. + * We're changing back down the hierarchy, ignore the + * "/" at buf[pos]. + */ + if (d) { +#ifndef HAVE_FCHDIR + if (buf[pos]) + zchdir(buf + pos + 1); + noholdintr(); +#endif + return NULL; + } + + if (buf[pos]) + zchdir(buf + pos + 1); + noholdintr(); + +#else /* __CYGWIN__, USE_GETCWD cases */ + + if (!getcwd(buf, bufsiz)) { + if (d) { + return NULL; + } + } else { + if (d) { + return d->dirname = ztrdup(buf); + } + return buf; + } +#endif + + /* + * Something bad happened. + * This has been seen when inside a special directory, + * such as the Netapp .snapshot directory, that doesn't + * appear as a directory entry in the parent directory. + * We'll just need our best guess. + * + * We only get here from zgetcwd(); let that fall back to pwd. + */ + + return NULL; +} + +/* + * Try to find the current directory. + * If we couldn't work it out internally, fall back to getcwd(). + * If it fails, fall back to pwd; if zgetcwd() is being used + * to set pwd, pwd should be NULL and we just return ".". + */ + +/**/ +char * +zgetcwd(void) +{ + char *ret = zgetdir(NULL); +#ifdef HAVE_GETCWD + if (!ret) { +#ifdef GETCWD_CALLS_MALLOC + char *cwd = getcwd(NULL, 0); + if (cwd) { + ret = dupstring(cwd); + free(cwd); + } +#else + char *cwdbuf = zalloc(PATH_MAX+1); + ret = getcwd(cwdbuf, PATH_MAX); + if (ret) + ret = dupstring(ret); + zfree(cwdbuf, PATH_MAX+1); +#endif /* GETCWD_CALLS_MALLOC */ + } +#endif /* HAVE_GETCWD */ + if (!ret) + ret = unmeta(pwd); + if (!ret) + ret = dupstring("."); + 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. + * + * This is to be treated as if at system level, so dir is unmetafied but + * terminated by a NULL. + */ + +/**/ +mod_export int +zchdir(char *dir) +{ + char *s; + int currdir = -2; + + for (;;) { + if (!*dir || chdir(dir) == 0) { +#ifdef HAVE_FCHDIR + if (currdir >= 0) + close(currdir); +#endif + return 0; + } + if ((errno != ENAMETOOLONG && errno != ENOMEM) || + strlen(dir) < PATH_MAX) + break; + for (s = dir + PATH_MAX - 1; s > dir && *s != '/'; s--) + ; + if (s == dir) + break; +#ifdef HAVE_FCHDIR + if (currdir == -2) + currdir = open(".", O_RDONLY|O_NOCTTY); +#endif + *s = '\0'; + if (chdir(dir) < 0) { + *s = '/'; + break; + } +#ifndef HAVE_FCHDIR + currdir = -1; +#endif + *s = '/'; + while (*++s == '/') + ; + dir = s; + } +#ifdef HAVE_FCHDIR + if (currdir >= 0) { + if (fchdir(currdir) < 0) { + close(currdir); + return -2; + } + close(currdir); + return -1; + } +#endif + return currdir == -2 ? -1 : -2; +} + +/* + * How to print out a 64 bit integer. This isn't needed (1) if longs + * are 64 bit, since ordinary %ld will work (2) if we couldn't find a + * 64 bit type anyway. + */ +/**/ +#ifdef ZSH_64_BIT_TYPE +/**/ +mod_export char * +output64(zlong val) +{ + static char llbuf[DIGBUFSIZE]; + convbase(llbuf, val, 0); + return llbuf; +} +/**/ +#endif /* ZSH_64_BIT_TYPE */ + +/**/ +#ifndef HAVE_STRTOUL + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Convert a string to an unsigned long integer. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ + +/**/ +unsigned long +strtoul(nptr, endptr, base) + const char *nptr; + char **endptr; + int base; +{ + const char *s; + unsigned long acc, cutoff; + int c; + int neg, any, cutlim; + + /* endptr may be NULL */ + + s = nptr; + do { + c = (unsigned char) *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + + cutoff = ULONG_MAX / (unsigned long)base; + cutlim = (int)(ULONG_MAX % (unsigned long)base); + for (acc = 0, any = 0;; c = (unsigned char) *s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) { + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + } else + break; + if (c >= base) + break; + if (any < 0) + continue; + if (acc > cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = ULONG_MAX; + errno = ERANGE; + } else { + any = 1; + acc *= (unsigned long)base; + acc += c; + } + } + if (neg && any > 0) + acc = -acc; + if (endptr != NULL) + *endptr = any ? s - 1 : nptr; + return (acc); +} + +/**/ +#endif /* HAVE_STRTOUL */ + +/**/ +#ifdef ENABLE_UNICODE9 +#include "./wcwidth9.h" + +/**/ +int +u9_wcwidth(wchar_t ucs) +{ + int w = wcwidth9(ucs); + if (w < -1) + return 1; + return w; +} + +/**/ +int +u9_iswprint(wint_t ucs) +{ + if (ucs == 0) + return 0; + return wcwidth9(ucs) != -1; +} + +/**/ +#endif /* ENABLE_UNICODE9 */ + +/**/ +#if defined(__APPLE__) && defined(BROKEN_ISPRINT) + +/**/ +int +isprint_ascii(int c) +{ + if (!strcmp(nl_langinfo(CODESET), "UTF-8")) + return (c >= 0x20 && c <= 0x7e); + else + return isprint(c); +} + +/**/ +#endif /* __APPLE__ && BROKEN_ISPRINT */ diff --git a/dotfiles/system/.zsh/modules/Src/exec.c b/dotfiles/system/.zsh/modules/Src/exec.c new file mode 100644 index 0000000..615a508 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/exec.c @@ -0,0 +1,6250 @@ +/* + * exec.c - command execution + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "exec.pro" + +/* Flags for last argument of addvars */ + +enum { + /* Export the variable for "VAR=val cmd ..." */ + ADDVAR_EXPORT = 1 << 0, + /* Apply restrictions for variable */ + ADDVAR_RESTRICT = 1 << 1, + /* Variable list is being restored later */ + ADDVAR_RESTORE = 1 << 2 +}; + +/* Structure in which to save values around shell function call */ + +struct funcsave { + char opts[OPT_SIZE]; + char *argv0; + int zoptind, lastval, optcind, numpipestats; + int *pipestats; + char *scriptname; + int breaks, contflag, loops, emulation, noerrexit, oflags, restore_sticky; + Emulation_options sticky; + struct funcstack fstack; +}; +typedef struct funcsave *Funcsave; + +/* + * used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT. + * Bits from noerrexit_bits. + */ + +/**/ +int noerrexit; + +/* used to suppress ERREXIT or ERRRETURN for one occurrence: 0 or 1 */ + +/**/ +int this_noerrexit; + +/* + * noerrs = 1: suppress error messages + * noerrs = 2: don't set errflag on parse error, either + */ + +/**/ +mod_export int noerrs; + +/* do not save history on exec and exit */ + +/**/ +int nohistsave; + +/* error flag: bits from enum errflag_bits */ + +/**/ +mod_export int errflag; + +/* + * State of trap return value. Value is from enum trap_state. + */ + +/**/ +int trap_state; + +/* + * Value associated with return from a trap. + * This is only active if we are inside a trap, else its value + * is irrelevant. It is initialised to -1 for a function trap and + * -2 for a non-function trap and if negative is decremented as + * we go deeper into functions and incremented as we come back up. + * The value is used to decide if an explicit "return" should cause + * a return from the caller of the trap; it does this by setting + * trap_return to a status (i.e. a non-negative value). + * + * In summary, trap_return is + * - zero unless we are in a trap + * - negative in a trap unless it has triggered. Code uses this + * to detect an active trap. + * - non-negative in a trap once it was triggered. It should remain + * non-negative until restored after execution of the trap. + */ + +/**/ +int trap_return; + +/* != 0 if this is a subshell */ + +/**/ +int subsh; + +/* != 0 if we have a return pending */ + +/**/ +mod_export int retflag; + +/**/ +long lastval2; + +/* The table of file descriptors. A table element is zero if the * + * corresponding fd is not used by the shell. It is greater than * + * 1 if the fd is used by a <(...) or >(...) substitution and 1 if * + * it is an internal file descriptor which must be closed before * + * executing an external command. The first ten elements of the * + * table is not used. A table element is set by movefd and cleard * + * by zclose. */ + +/**/ +mod_export unsigned char *fdtable; + +/* The allocated size of fdtable */ + +/**/ +int fdtable_size; + +/* The highest fd that marked with nonzero in fdtable */ + +/**/ +mod_export int max_zsh_fd; + +/* input fd from the coprocess */ + +/**/ +mod_export int coprocin; + +/* output fd from the coprocess */ + +/**/ +mod_export int coprocout; + +/* count of file locks recorded in fdtable */ + +/**/ +int fdtable_flocks; + + +/* != 0 if the line editor is active */ + +/**/ +mod_export int zleactive; + +/* pid of process undergoing 'process substitution' */ + +/**/ +pid_t cmdoutpid; + +/* pid of last process started by <(...), >(...) */ + +/**/ +mod_export pid_t procsubstpid; + +/* exit status of process undergoing 'process substitution' */ + +/**/ +int cmdoutval; + +/* + * This is set by an exiting $(...) substitution to indicate we need + * to retain the status. We initialize it to zero if we think we need + * to reset the status for a command. + */ + +/**/ +int use_cmdoutval; + +/* The context in which a shell function is called, see SFC_* in zsh.h. */ + +/**/ +mod_export int sfcontext; + +/* Stack to save some variables before executing a signal handler function */ + +/**/ +struct execstack *exstack; + +/* Stack with names of function calls, 'source' calls, and 'eval' calls + * currently active. */ + +/**/ +mod_export Funcstack funcstack; + +#define execerr() \ + do { \ + if (!forked) { \ + redir_err = lastval = 1; \ + goto done; \ + } else { \ + _exit(1); \ + } \ + } while (0) + +static int doneps4; +static char *STTYval; +static char *blank_env[] = { NULL }; + +/* Execution functions. */ + +static int (*execfuncs[WC_COUNT-WC_CURSH]) _((Estate, int)) = { + execcursh, exectime, NULL /* execfuncdef handled specially */, + execfor, execselect, + execwhile, execrepeat, execcase, execif, execcond, + execarith, execautofn, exectry +}; + +/* structure for command builtin for when it is used with -v or -V */ +static struct builtin commandbn = + BUILTIN("command", 0, bin_whence, 0, -1, BIN_COMMAND, "pvV", NULL); + +/* parse string into a list */ + +/**/ +mod_export Eprog +parse_string(char *s, int reset_lineno) +{ + Eprog p; + zlong oldlineno; + + zcontext_save(); + inpush(s, INP_LINENO, NULL); + strinbeg(0); + oldlineno = lineno; + if (reset_lineno) + lineno = 1; + p = parse_list(); + lineno = oldlineno; + if (tok == LEXERR && !lastval) + lastval = 1; + strinend(); + inpop(); + zcontext_restore(); + return p; +} + +/**/ +#ifdef HAVE_GETRLIMIT + +/* the resource limits for the shell and its children */ + +/**/ +mod_export struct rlimit current_limits[RLIM_NLIMITS], limits[RLIM_NLIMITS]; + +/**/ +mod_export int +zsetlimit(int limnum, char *nam) +{ + if (limits[limnum].rlim_max != current_limits[limnum].rlim_max || + limits[limnum].rlim_cur != current_limits[limnum].rlim_cur) { + if (setrlimit(limnum, limits + limnum)) { + if (nam) + zwarnnam(nam, "setrlimit failed: %e", errno); + limits[limnum] = current_limits[limnum]; + return -1; + } + current_limits[limnum] = limits[limnum]; + } + return 0; +} + +/**/ +mod_export int +setlimits(char *nam) +{ + int limnum; + int ret = 0; + + for (limnum = 0; limnum < RLIM_NLIMITS; limnum++) + if (zsetlimit(limnum, nam)) + ret++; + return ret; +} + +/**/ +#endif /* HAVE_GETRLIMIT */ + +/* fork and set limits */ + +/**/ +static pid_t +zfork(struct timeval *tv) +{ + pid_t pid; + struct timezone dummy_tz; + + /* + * Is anybody willing to explain this test? + */ + if (thisjob != -1 && thisjob >= jobtabsize - 1 && !expandjobtab()) { + zerr("job table full"); + return -1; + } + if (tv) + gettimeofday(tv, &dummy_tz); + /* + * Queueing signals is necessary on Linux because fork() + * manipulates mutexes, leading to deadlock in memory + * allocation. We don't expect fork() to be particularly + * zippy anyway. + */ + queue_signals(); + pid = fork(); + unqueue_signals(); + if (pid == -1) { + zerr("fork failed: %e", errno); + return -1; + } +#ifdef HAVE_GETRLIMIT + if (!pid) + /* set resource limits for the child process */ + setlimits(NULL); +#endif + return pid; +} + +/* + * Allen Edeln gebiet ich Andacht, + * Hohen und Niedern von Heimdalls Geschlecht; + * Ich will list_pipe's Wirken kuenden + * Die aeltesten Sagen, der ich mich entsinne... + * + * In most shells, if you do something like: + * + * cat foo | while read a; do grep $a bar; done + * + * the shell forks and executes the loop in the sub-shell thus created. + * In zsh this traditionally executes the loop in the current shell, which + * is nice to have if the loop does something to change the shell, like + * setting parameters or calling builtins. + * Putting the loop in a sub-shell makes life easy, because the shell only + * has to put it into the job-structure and then treats it as a normal + * process. Suspending and interrupting is no problem then. + * Some years ago, zsh either couldn't suspend such things at all, or + * it got really messed up when users tried to do it. As a solution, we + * implemented the list_pipe-stuff, which has since then become a reason + * for many nightmares. + * Pipelines like the one above are executed by the functions in this file + * which call each other (and sometimes recursively). The one above, for + * example would lead to a function call stack roughly like: + * + * execlist->execpline->execcmd->execwhile->execlist->execpline + * + * (when waiting for the grep, ignoring execpline2 for now). At this time, + * zsh has built two job-table entries for it: one for the cat and one for + * the grep. If the user hits ^Z at this point (and jobbing is used), the + * shell is notified that the grep was suspended. The list_pipe flag is + * used to tell the execpline where it was waiting that it was in a pipeline + * with a shell construct at the end (which may also be a shell function or + * several other things). When zsh sees the suspended grep, it forks to let + * the sub-shell execute the rest of the while loop. The parent shell walks + * up in the function call stack to the first execpline. There it has to find + * out that it has just forked and then has to add information about the sub- + * shell (its pid and the text for it) in the job entry of the cat. The pid + * is passed down in the list_pipe_pid variable. + * But there is a problem: the suspended grep is a child of the parent shell + * and can't be adopted by the sub-shell. So the parent shell also has to + * keep the information about this process (more precisely: this pipeline) + * by keeping the job table entry it created for it. The fact that there + * are two jobs which have to be treated together is remembered by setting + * the STAT_SUPERJOB flag in the entry for the cat-job (which now also + * contains a process-entry for the whole loop -- the sub-shell) and by + * setting STAT_SUBJOB in the job of the grep-job. With that we can keep + * sub-jobs from being displayed and we can handle an fg/bg on the super- + * job correctly. When the super-job is continued, the shell also wakes up + * the sub-job. But then, the grep will exit sometime. Now the parent shell + * has to remember not to try to wake it up again (in case of another ^Z). + * It also has to wake up the sub-shell (which suspended itself immediately + * after creation), so that the rest of the loop is executed by it. + * But there is more: when the sub-shell is created, the cat may already + * have exited, so we can't put the sub-shell in the process group of it. + * In this case, we put the sub-shell in the process group of the parent + * shell and in any case, the sub-shell has to put all commands executed + * by it into its own process group, because only this way the parent + * shell can control them since it only knows the process group of the sub- + * shell. Of course, this information is also important when putting a job + * in the foreground, where we have to attach its process group to the + * controlling tty. + * All this is made more difficult because we have to handle return values + * correctly. If the grep is signaled, its exit status has to be propagated + * back to the parent shell which needs it to set the exit status of the + * super-job. And of course, when the grep is signaled (including ^C), the + * loop has to be stopped, etc. + * The code for all this is distributed over three files (exec.c, jobs.c, + * and signals.c) and none of them is a simple one. So, all in all, there + * may still be bugs, but considering the complexity (with race conditions, + * signal handling, and all that), this should probably be expected. + */ + +/**/ +int list_pipe = 0, simple_pline = 0; + +static pid_t list_pipe_pid; +static struct timeval list_pipe_start; +static int nowait, pline_level = 0; +static int list_pipe_child = 0, list_pipe_job; +static char list_pipe_text[JOBTEXTSIZE]; + +/* execute a current shell command */ + +/**/ +static int +execcursh(Estate state, int do_exec) +{ + Wordcode end = state->pc + WC_CURSH_SKIP(state->pc[-1]); + + /* Skip word only used for try/always */ + state->pc++; + + /* + * The test thisjob != -1 was added because sometimes thisjob + * can be invalid at this point. The case in question was + * in a precmd function after operations involving background + * jobs. + * + * This is because sometimes we bypass job control to execute + * very simple functions via execssimple(). + */ + if (!list_pipe && thisjob != -1 && thisjob != list_pipe_job && + !hasprocs(thisjob)) + deletejob(jobtab + thisjob, 0); + cmdpush(CS_CURSH); + execlist(state, 1, do_exec); + cmdpop(); + + state->pc = end; + this_noerrexit = 1; + + return lastval; +} + +/* execve after handling $_ and #! */ + +#define POUNDBANGLIMIT 64 + +/**/ +static int +zexecve(char *pth, char **argv, char **newenvp) +{ + int eno; + static char buf[PATH_MAX * 2+1]; + char **eep; + + unmetafy(pth, NULL); + for (eep = argv; *eep; eep++) + if (*eep != pth) + unmetafy(*eep, NULL); + buf[0] = '_'; + buf[1] = '='; + if (*pth == '/') + strcpy(buf + 2, pth); + else + sprintf(buf + 2, "%s/%s", pwd, pth); + zputenv(buf); +#ifndef FD_CLOEXEC + closedumps(); +#endif + + if (newenvp == NULL) + newenvp = environ; + winch_unblock(); + execve(pth, argv, newenvp); + + /* If the execve returns (which in general shouldn't happen), * + * then check for an errno equal to ENOEXEC. This errno is set * + * if the process file has the appropriate access permission, * + * but has an invalid magic number in its header. */ + if ((eno = errno) == ENOEXEC || eno == ENOENT) { + char execvebuf[POUNDBANGLIMIT + 1], *ptr, *ptr2, *argv0; + int fd, ct, t0; + + if ((fd = open(pth, O_RDONLY|O_NOCTTY)) >= 0) { + argv0 = *argv; + *argv = pth; + execvebuf[0] = '\0'; + ct = read(fd, execvebuf, POUNDBANGLIMIT); + close(fd); + if (ct >= 0) { + if (execvebuf[0] == '#') { + if (execvebuf[1] == '!') { + for (t0 = 0; t0 != ct; t0++) + if (execvebuf[t0] == '\n') + break; + while (inblank(execvebuf[t0])) + execvebuf[t0--] = '\0'; + execvebuf[POUNDBANGLIMIT] = '\0'; + for (ptr = execvebuf + 2; *ptr && *ptr == ' '; ptr++); + for (ptr2 = ptr; *ptr && *ptr != ' '; ptr++); + if (eno == ENOENT) { + char *pprog; + if (*ptr) + *ptr = '\0'; + if (*ptr2 != '/' && + (pprog = pathprog(ptr2, NULL))) { + argv[-2] = ptr2; + argv[-1] = ptr + 1; + winch_unblock(); + execve(pprog, argv - 2, newenvp); + } + zerr("%s: bad interpreter: %s: %e", pth, ptr2, + eno); + } else if (*ptr) { + *ptr = '\0'; + argv[-2] = ptr2; + argv[-1] = ptr + 1; + winch_unblock(); + execve(ptr2, argv - 2, newenvp); + } else { + argv[-1] = ptr2; + winch_unblock(); + execve(ptr2, argv - 1, newenvp); + } + } else if (eno == ENOEXEC) { + argv[-1] = "sh"; + winch_unblock(); + execve("/bin/sh", argv - 1, newenvp); + } + } else if (eno == ENOEXEC) { + for (t0 = 0; t0 != ct; t0++) + if (!execvebuf[t0]) + break; + if (t0 == ct) { + argv[-1] = "sh"; + winch_unblock(); + execve("/bin/sh", argv - 1, newenvp); + } + } + } else + eno = errno; + *argv = argv0; + } else + eno = errno; + } + /* restore the original arguments and path but do not bother with * + * null characters as these cannot be passed to external commands * + * anyway. So the result is truncated at the first null char. */ + pth = metafy(pth, -1, META_NOALLOC); + for (eep = argv; *eep; eep++) + if (*eep != pth) + (void) metafy(*eep, -1, META_NOALLOC); + return eno; +} + +#define MAXCMDLEN (PATH_MAX*4) + +/* test whether we really want to believe the error number */ + +/**/ +static int +isgooderr(int e, char *dir) +{ + /* + * Maybe the directory was unreadable, or maybe it wasn't + * even a directory. + */ + return ((e != EACCES || !access(dir, X_OK)) && + e != ENOENT && e != ENOTDIR); +} + +/* + * Attempt to handle command not found. + * Return 0 if the condition was handled, non-zero otherwise. + */ + +/**/ +static int +commandnotfound(char *arg0, LinkList args) +{ + Shfunc shf = (Shfunc) + shfunctab->getnode(shfunctab, "command_not_found_handler"); + + if (!shf) { + lastval = 127; + return 1; + } + + pushnode(args, arg0); + lastval = doshfunc(shf, args, 1); + return 0; +} + +/* + * Search the default path for cmd. + * pbuf of length plen is the buffer to use. + * Return NULL if not found. + */ + +static char * +search_defpath(char *cmd, char *pbuf, int plen) +{ + char *ps = DEFAULT_PATH, *pe = NULL, *s; + + for (ps = DEFAULT_PATH; ps; ps = pe ? pe+1 : NULL) { + pe = strchr(ps, ':'); + if (*ps == '/') { + s = pbuf; + if (pe) { + if (pe - ps >= plen) + continue; + struncpy(&s, ps, pe-ps); + } else { + if (strlen(ps) >= plen) + continue; + strucpy(&s, ps); + } + *s++ = '/'; + if ((s - pbuf) + strlen(cmd) >= plen) + continue; + strucpy(&s, cmd); + if (iscom(pbuf)) + return pbuf; + } + } + return NULL; +} + +/* execute an external command */ + +/**/ +static void +execute(LinkList args, int flags, int defpath) +{ + Cmdnam cn; + char buf[MAXCMDLEN+1], buf2[MAXCMDLEN+1]; + char *s, *z, *arg0; + char **argv, **pp, **newenvp = NULL; + int eno = 0, ee; + + arg0 = (char *) peekfirst(args); + if (isset(RESTRICTED) && (strchr(arg0, '/') || defpath)) { + zerr("%s: restricted", arg0); + _exit(1); + } + + /* If the parameter STTY is set in the command's environment, * + * we first run the stty command with the value of this * + * parameter as it arguments. */ + if ((s = STTYval) && isatty(0) && (GETPGRP() == getpid())) { + char *t = tricat("stty", " ", s); + + STTYval = 0; /* this prevents infinite recursion */ + zsfree(s); + execstring(t, 1, 0, "stty"); + zsfree(t); + } else if (s) { + STTYval = 0; + zsfree(s); + } + + /* If ARGV0 is in the commands environment, we use * + * that as argv[0] for this external command */ + if (unset(RESTRICTED) && (z = zgetenv("ARGV0"))) { + setdata(firstnode(args), (void *) ztrdup(z)); + /* + * Note we don't do anything with the parameter structure + * for ARGV0: that's OK since we're about to exec or exit + * on failure. + */ +#ifdef USE_SET_UNSET_ENV + unsetenv("ARGV0"); +#else + delenvvalue(z - 6); +#endif + } else if (flags & BINF_DASH) { + /* Else if the pre-command `-' was given, we add `-' * + * to the front of argv[0] for this command. */ + sprintf(buf2, "-%s", arg0); + setdata(firstnode(args), (void *) ztrdup(buf2)); + } + + argv = makecline(args); + if (flags & BINF_CLEARENV) + newenvp = blank_env; + + /* + * Note that we don't close fd's attached to process substitution + * here, which should be visible to external processes. + */ + closem(FDT_XTRACE, 0); +#ifndef FD_CLOEXEC + if (SHTTY != -1) { + close(SHTTY); + SHTTY = -1; + } +#endif + child_unblock(); + if ((int) strlen(arg0) >= PATH_MAX) { + zerr("command too long: %s", arg0); + _exit(1); + } + for (s = arg0; *s; s++) + if (*s == '/') { + int lerrno = zexecve(arg0, argv, newenvp); + if (arg0 == s || unset(PATHDIRS) || + (arg0[0] == '.' && (arg0 + 1 == s || + (arg0[1] == '.' && arg0 + 2 == s)))) { + zerr("%e: %s", lerrno, arg0); + _exit((lerrno == EACCES || lerrno == ENOEXEC) ? 126 : 127); + } + break; + } + + /* for command -p, search the default path */ + if (defpath) { + char pbuf[PATH_MAX+1]; + char *dptr; + + if (!search_defpath(arg0, pbuf, PATH_MAX)) { + if (commandnotfound(arg0, args) == 0) + _exit(lastval); + zerr("command not found: %s", arg0); + _exit(127); + } + + ee = zexecve(pbuf, argv, newenvp); + + if ((dptr = strrchr(pbuf, '/'))) + *dptr = '\0'; + if (isgooderr(ee, *pbuf ? pbuf : "/")) + eno = ee; + + } else { + + if ((cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0))) { + char nn[PATH_MAX+1], *dptr; + + if (cn->node.flags & HASHED) + strcpy(nn, cn->u.cmd); + else { + for (pp = path; pp < cn->u.name; pp++) + if (!**pp || (**pp == '.' && (*pp)[1] == '\0')) { + ee = zexecve(arg0, argv, newenvp); + if (isgooderr(ee, *pp)) + eno = ee; + } else if (**pp != '/') { + z = buf; + strucpy(&z, *pp); + *z++ = '/'; + strcpy(z, arg0); + ee = zexecve(buf, argv, newenvp); + if (isgooderr(ee, *pp)) + eno = ee; + } + strcpy(nn, cn->u.name ? *(cn->u.name) : ""); + strcat(nn, "/"); + strcat(nn, cn->node.nam); + } + ee = zexecve(nn, argv, newenvp); + + if ((dptr = strrchr(nn, '/'))) + *dptr = '\0'; + if (isgooderr(ee, *nn ? nn : "/")) + eno = ee; + } + for (pp = path; *pp; pp++) + if (!(*pp)[0] || ((*pp)[0] == '.' && !(*pp)[1])) { + ee = zexecve(arg0, argv, newenvp); + if (isgooderr(ee, *pp)) + eno = ee; + } else { + z = buf; + strucpy(&z, *pp); + *z++ = '/'; + strcpy(z, arg0); + ee = zexecve(buf, argv, newenvp); + if (isgooderr(ee, *pp)) + eno = ee; + } + } + + if (eno) + zerr("%e: %s", eno, arg0); + else if (commandnotfound(arg0, args) == 0) + _exit(lastval); + else + zerr("command not found: %s", arg0); + _exit((eno == EACCES || eno == ENOEXEC) ? 126 : 127); +} + +#define RET_IF_COM(X) { if (iscom(X)) return docopy ? dupstring(X) : arg0; } + +/* + * Get the full pathname of an external command. + * If the second argument is zero, return the first argument if found; + * if non-zero, return the path using heap memory. (RET_IF_COM(X), + * above). + * If the third argument is non-zero, use the system default path + * instead of the current path. + */ + +/**/ +mod_export char * +findcmd(char *arg0, int docopy, int default_path) +{ + char **pp; + char *z, *s, buf[MAXCMDLEN]; + Cmdnam cn; + + if (default_path) + { + if (search_defpath(arg0, buf, MAXCMDLEN)) + return docopy ? dupstring(buf) : arg0; + return NULL; + } + cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0); + if (!cn && isset(HASHCMDS) && !isrelative(arg0)) + cn = hashcmd(arg0, path); + if ((int) strlen(arg0) > PATH_MAX) + return NULL; + if ((s = strchr(arg0, '/'))) { + RET_IF_COM(arg0); + if (arg0 == s || unset(PATHDIRS) || !strncmp(arg0, "./", 2) || + !strncmp(arg0, "../", 3)) { + return NULL; + } + } + if (cn) { + char nn[PATH_MAX+1]; + + if (cn->node.flags & HASHED) + strcpy(nn, cn->u.cmd); + else { + for (pp = path; pp < cn->u.name; pp++) + if (**pp != '/') { + z = buf; + if (**pp) { + strucpy(&z, *pp); + *z++ = '/'; + } + strcpy(z, arg0); + RET_IF_COM(buf); + } + strcpy(nn, cn->u.name ? *(cn->u.name) : ""); + strcat(nn, "/"); + strcat(nn, cn->node.nam); + } + RET_IF_COM(nn); + } + for (pp = path; *pp; pp++) { + z = buf; + if (**pp) { + strucpy(&z, *pp); + *z++ = '/'; + } + strcpy(z, arg0); + RET_IF_COM(buf); + } + return NULL; +} + +/* + * Return TRUE if the given path denotes an executable regular file, or a + * symlink to one. + */ + +/**/ +int +iscom(char *s) +{ + struct stat statbuf; + char *us = unmeta(s); + + return (access(us, X_OK) == 0 && stat(us, &statbuf) >= 0 && + S_ISREG(statbuf.st_mode)); +} + +/**/ +int +isreallycom(Cmdnam cn) +{ + char fullnam[MAXCMDLEN]; + + if (cn->node.flags & HASHED) + strcpy(fullnam, cn->u.cmd); + else if (!cn->u.name) + return 0; + else { + strcpy(fullnam, *(cn->u.name)); + strcat(fullnam, "/"); + strcat(fullnam, cn->node.nam); + } + return iscom(fullnam); +} + +/* + * Return TRUE if the given path contains a dot or dot-dot component + * and does not start with a slash. + */ + +/**/ +int +isrelative(char *s) +{ + if (*s != '/') + return 1; + for (; *s; s++) + if (*s == '.' && s[-1] == '/' && + (s[1] == '/' || s[1] == '\0' || + (s[1] == '.' && (s[2] == '/' || s[2] == '\0')))) + return 1; + return 0; +} + +/**/ +mod_export Cmdnam +hashcmd(char *arg0, char **pp) +{ + Cmdnam cn; + char *s, buf[PATH_MAX+1]; + char **pq; + + for (; *pp; pp++) + if (**pp == '/') { + s = buf; + struncpy(&s, *pp, PATH_MAX); + *s++ = '/'; + if ((s - buf) + strlen(arg0) >= PATH_MAX) + continue; + strcpy(s, arg0); + if (iscom(buf)) + break; + } + + if (!*pp) + return NULL; + + cn = (Cmdnam) zshcalloc(sizeof *cn); + cn->node.flags = 0; + cn->u.name = pp; + cmdnamtab->addnode(cmdnamtab, ztrdup(arg0), cn); + + if (isset(HASHDIRS)) { + for (pq = pathchecked; pq <= pp; pq++) + hashdir(pq); + pathchecked = pp + 1; + } + + return cn; +} + +/**/ +int +forklevel; + +/* Arguments to entersubsh() */ +enum { + /* Subshell is to be run asynchronously (else synchronously) */ + ESUB_ASYNC = 0x01, + /* + * Perform process group and tty handling and clear the + * (real) job table, since it won't be any longer valid + */ + ESUB_PGRP = 0x02, + /* Don't unset traps */ + ESUB_KEEPTRAP = 0x04, + /* This is only a fake entry to a subshell */ + ESUB_FAKE = 0x08, + /* Release the process group if pid is the shell's process group */ + ESUB_REVERTPGRP = 0x10, + /* Don't handle the MONITOR option even if previously set */ + ESUB_NOMONITOR = 0x20, + /* This is a subshell where job control is allowed */ + ESUB_JOB_CONTROL = 0x40 +}; + +/**/ +static void +entersubsh(int flags) +{ + int i, sig, monitor, job_control_ok; + + if (!(flags & ESUB_KEEPTRAP)) + for (sig = 0; sig < SIGCOUNT; sig++) + if (!(sigtrapped[sig] & ZSIG_FUNC)) + unsettrap(sig); + monitor = isset(MONITOR); + job_control_ok = monitor && (flags & ESUB_JOB_CONTROL) && isset(POSIXJOBS); + if (flags & ESUB_NOMONITOR) + opts[MONITOR] = 0; + if (!isset(MONITOR)) { + if (flags & ESUB_ASYNC) { + settrap(SIGINT, NULL, 0); + settrap(SIGQUIT, NULL, 0); + if (isatty(0)) { + close(0); + if (open("/dev/null", O_RDWR | O_NOCTTY)) { + zerr("can't open /dev/null: %e", errno); + _exit(1); + } + } + } + } else if (thisjob != -1 && (flags & ESUB_PGRP)) { + if (jobtab[list_pipe_job].gleader && (list_pipe || list_pipe_child)) { + if (setpgrp(0L, jobtab[list_pipe_job].gleader) == -1 || + killpg(jobtab[list_pipe_job].gleader, 0) == -1) { + jobtab[list_pipe_job].gleader = + jobtab[thisjob].gleader = (list_pipe_child ? mypgrp : getpid()); + setpgrp(0L, jobtab[list_pipe_job].gleader); + if (!(flags & ESUB_ASYNC)) + attachtty(jobtab[thisjob].gleader); + } + } + else if (!jobtab[thisjob].gleader || + setpgrp(0L, jobtab[thisjob].gleader) == -1) { + /* + * This is the standard point at which a newly started + * process gets put into the foreground by taking over + * the terminal. Note that in normal circumstances we do + * this only from the process itself. This only works if + * we are still ignoring SIGTTOU at this point; in this + * case ignoring the signal has the special effect that + * the operation is allowed to work (in addition to not + * causing the shell to be suspended). + */ + jobtab[thisjob].gleader = getpid(); + if (list_pipe_job != thisjob && + !jobtab[list_pipe_job].gleader) + jobtab[list_pipe_job].gleader = jobtab[thisjob].gleader; + setpgrp(0L, jobtab[thisjob].gleader); + if (!(flags & ESUB_ASYNC)) + attachtty(jobtab[thisjob].gleader); + } + } + if (!(flags & ESUB_FAKE)) + subsh = 1; + /* + * Increment the visible parameter ZSH_SUBSHELL even if this + * is a fake subshell because we are exec'ing at the end. + * Logically this should be equivalent to a real subshell so + * we don't hang out the dirty washing. + */ + zsh_subshell++; + if ((flags & ESUB_REVERTPGRP) && getpid() == mypgrp) + release_pgrp(); + shout = NULL; + if (flags & ESUB_NOMONITOR) { + /* + * Allowing any form of interactive signalling here is + * actively harmful as we are in a context where there is no + * control over the process. + */ + signal_ignore(SIGTTOU); + signal_ignore(SIGTTIN); + signal_ignore(SIGTSTP); + } else if (!job_control_ok) { + /* + * If this process is not going to be doing job control, + * we don't want to do special things with the corresponding + * signals. If it is, we need to keep the special behaviour: + * see note about attachtty() above. + */ + signal_default(SIGTTOU); + signal_default(SIGTTIN); + signal_default(SIGTSTP); + } + if (interact) { + signal_default(SIGTERM); + if (!(sigtrapped[SIGINT] & ZSIG_IGNORED)) + signal_default(SIGINT); + if (!(sigtrapped[SIGPIPE])) + signal_default(SIGPIPE); + } + if (!(sigtrapped[SIGQUIT] & ZSIG_IGNORED)) + signal_default(SIGQUIT); + /* + * sigtrapped[sig] == ZSIG_IGNORED for signals that remain ignored, + * but other trapped signals are temporarily blocked when intrap, + * and must be unblocked before continuing into the subshell. This + * is orthogonal to what the default handler for the signal may be. + * + * Start loop at 1 because 0 is SIGEXIT + */ + if (intrap) + for (sig = 1; sig < SIGCOUNT; sig++) + if (sigtrapped[sig] && sigtrapped[sig] != ZSIG_IGNORED) + signal_unblock(signal_mask(sig)); + if (!job_control_ok) + opts[MONITOR] = 0; + opts[USEZLE] = 0; + zleactive = 0; + /* + * If we've saved fd's for later restoring, we're never going + * to restore them now, so just close them. + */ + for (i = 10; i <= max_zsh_fd; i++) { + if (fdtable[i] & FDT_SAVED_MASK) + zclose(i); + } + if (flags & ESUB_PGRP) + clearjobtab(monitor); + get_usage(); + forklevel = locallevel; +} + +/* execute a string */ + +/**/ +mod_export void +execstring(char *s, int dont_change_job, int exiting, char *context) +{ + Eprog prog; + + pushheap(); + if (isset(VERBOSE)) { + zputs(s, stderr); + fputc('\n', stderr); + fflush(stderr); + } + if ((prog = parse_string(s, 0))) + execode(prog, dont_change_job, exiting, context); + popheap(); +} + +/**/ +mod_export void +execode(Eprog p, int dont_change_job, int exiting, char *context) +{ + struct estate s; + static int zsh_eval_context_len; + int alen; + + if (!zsh_eval_context_len) { + zsh_eval_context_len = 16; + alen = 0; + zsh_eval_context = (char **)zalloc(zsh_eval_context_len * + sizeof(*zsh_eval_context)); + } else { + alen = arrlen(zsh_eval_context); + if (zsh_eval_context_len == alen + 1) { + zsh_eval_context_len *= 2; + zsh_eval_context = zrealloc(zsh_eval_context, + zsh_eval_context_len * + sizeof(*zsh_eval_context)); + } + } + zsh_eval_context[alen] = context; + zsh_eval_context[alen+1] = NULL; + + s.prog = p; + s.pc = p->prog; + s.strs = p->strs; + useeprog(p); /* Mark as in use */ + + execlist(&s, dont_change_job, exiting); + + freeeprog(p); /* Free if now unused */ + + /* + * zsh_eval_context may have been altered by a recursive + * call, but that's OK since we're using the global value. + */ + zsh_eval_context[alen] = NULL; +} + +/* Execute a simplified command. This is used to execute things that + * will run completely in the shell, so that we can by-pass all that + * nasty job-handling and redirection stuff in execpline and execcmd. */ + +/**/ +static int +execsimple(Estate state) +{ + wordcode code = *state->pc++; + int lv, otj; + + if (errflag) + return (lastval = 1); + + if (!isset(EXECOPT)) + return lastval = 0; + + /* In evaluated traps, don't modify the line number. */ + if (!IN_EVAL_TRAP() && !ineval && code) + lineno = code - 1; + + code = wc_code(*state->pc++); + + /* + * Because we're bypassing job control, ensure the called + * code doesn't see the current job. + */ + otj = thisjob; + thisjob = -1; + + 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 { + 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; + + return lastval = lv; +} + +/* Main routine for executing a list. * + * exiting means that the (sub)shell we are in is a definite goner * + * after the current list is finished, so we may be able to exec the * + * last command directly instead of forking. If dont_change_job is * + * nonzero, then restore the current job number after executing the * + * list. */ + +/**/ +void +execlist(Estate state, int dont_change_job, int exiting) +{ + static int donetrap; + Wordcode next; + wordcode code; + int ret, cj, csp, ltype; + int old_pline_level, old_list_pipe, old_list_pipe_job; + char *old_list_pipe_text; + zlong oldlineno; + /* + * ERREXIT only forces the shell to exit if the last command in a && + * or || fails. This is the case even if an earlier command is a + * shell function or other current shell structure, so we have to set + * noerrexit here if the sublist is not of type END. + */ + int oldnoerrexit = noerrexit; + + queue_signals(); + + cj = thisjob; + old_pline_level = pline_level; + old_list_pipe = list_pipe; + old_list_pipe_job = list_pipe_job; + if (*list_pipe_text) + old_list_pipe_text = ztrdup(list_pipe_text); + else + old_list_pipe_text = NULL; + oldlineno = lineno; + + if (sourcelevel && unset(SHINSTDIN)) { + pline_level = list_pipe = list_pipe_job = 0; + *list_pipe_text = '\0'; + } + + /* Loop over all sets of comands separated by newline, * + * semi-colon or ampersand (`sublists'). */ + code = *state->pc++; + if (wc_code(code) != WC_LIST) { + /* Empty list; this returns status zero. */ + lastval = 0; + } + while (wc_code(code) == WC_LIST && !breaks && !retflag && !errflag) { + int donedebug; + int this_donetrap = 0; + this_noerrexit = 0; + + ltype = WC_LIST_TYPE(code); + csp = cmdsp; + + if (!IN_EVAL_TRAP() && !ineval) { + /* + * Ensure we have a valid line number for debugging, + * unless we are in an evaluated trap in which case + * we retain the line number from the context. + * This was added for DEBUGBEFORECMD but I've made + * it unconditional to keep dependencies to a minimum. + * + * The line number is updated for individual pipelines. + * This isn't necessary for debug traps since they only + * run once per sublist. + */ + wordcode code2 = *state->pc, lnp1 = 0; + if (ltype & Z_SIMPLE) { + lnp1 = code2; + } else if (wc_code(code2) == WC_SUBLIST) { + if (WC_SUBLIST_FLAGS(code2) == WC_SUBLIST_SIMPLE) + lnp1 = state->pc[1]; + else + lnp1 = WC_PIPE_LINENO(state->pc[1]); + } + if (lnp1) + lineno = lnp1 - 1; + } + + if (sigtrapped[SIGDEBUG] && isset(DEBUGBEFORECMD) && !intrap) { + Wordcode pc2 = state->pc; + int oerrexit_opt = opts[ERREXIT]; + Param pm; + opts[ERREXIT] = 0; + noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; + if (ltype & Z_SIMPLE) /* skip the line number */ + pc2++; + pm = assignsparam("ZSH_DEBUG_CMD", + getpermtext(state->prog, pc2, 0), + 0); + + exiting = donetrap; + ret = lastval; + dotrap(SIGDEBUG); + if (!retflag) + lastval = ret; + donetrap = exiting; + noerrexit = oldnoerrexit; + /* + * Only execute the trap once per sublist, even + * if the DEBUGBEFORECMD option changes. + */ + donedebug = isset(ERREXIT) ? 2 : 1; + opts[ERREXIT] = oerrexit_opt; + if (pm) + unsetparam_pm(pm, 0, 1); + } else + donedebug = intrap ? 1 : 0; + + /* Reset donetrap: this ensures that a trap is only * + * called once for each sublist that fails. */ + donetrap = 0; + if (ltype & Z_SIMPLE) { + next = state->pc + WC_LIST_SKIP(code); + if (donedebug != 2) + execsimple(state); + state->pc = next; + goto sublist_done; + } + + /* Loop through code followed by &&, ||, or end of sublist. */ + code = *state->pc++; + if (donedebug == 2) { + /* Skip sublist. */ + while (wc_code(code) == WC_SUBLIST) { + state->pc = state->pc + WC_SUBLIST_SKIP(code); + if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) + break; + code = *state->pc++; + } + donetrap = 1; + /* yucky but consistent... */ + goto sublist_done; + } + while (wc_code(code) == WC_SUBLIST) { + int isend = (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END); + next = state->pc + WC_SUBLIST_SKIP(code); + if (!oldnoerrexit) + noerrexit = isend ? 0 : NOERREXIT_EXIT | NOERREXIT_RETURN; + if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT) { + /* suppress errexit for "! this_command" */ + if (isend) + this_noerrexit = 1; + /* suppress errexit for ! */ + noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; + } + switch (WC_SUBLIST_TYPE(code)) { + case WC_SUBLIST_END: + /* End of sublist; just execute, ignoring status. */ + if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) + execsimple(state); + else + execpline(state, code, ltype, (ltype & Z_END) && exiting); + state->pc = next; + goto sublist_done; + break; + case WC_SUBLIST_AND: + /* If the return code is non-zero, we skip pipelines until * + * we find a sublist followed by ORNEXT. */ + if ((ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ? + execsimple(state) : + execpline(state, code, Z_SYNC, 0)))) { + state->pc = next; + code = *state->pc++; + next = state->pc + WC_SUBLIST_SKIP(code); + while (wc_code(code) == WC_SUBLIST && + WC_SUBLIST_TYPE(code) == WC_SUBLIST_AND) { + state->pc = next; + code = *state->pc++; + next = state->pc + WC_SUBLIST_SKIP(code); + } + if (wc_code(code) != WC_SUBLIST) { + /* We've skipped to the end of the list, not executing * + * the final pipeline, so don't perform error handling * + * for this sublist. */ + this_donetrap = 1; + goto sublist_done; + } else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) { + this_donetrap = 1; + /* + * Treat this in the same way as if we reached + * the end of the sublist normally. + */ + state->pc = next; + goto sublist_done; + } + } + cmdpush(CS_CMDAND); + break; + case WC_SUBLIST_OR: + /* If the return code is zero, we skip pipelines until * + * we find a sublist followed by ANDNEXT. */ + if (!(ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ? + execsimple(state) : + execpline(state, code, Z_SYNC, 0)))) { + state->pc = next; + code = *state->pc++; + next = state->pc + WC_SUBLIST_SKIP(code); + while (wc_code(code) == WC_SUBLIST && + WC_SUBLIST_TYPE(code) == WC_SUBLIST_OR) { + state->pc = next; + code = *state->pc++; + next = state->pc + WC_SUBLIST_SKIP(code); + } + if (wc_code(code) != WC_SUBLIST) { + /* We've skipped to the end of the list, not executing * + * the final pipeline, so don't perform error handling * + * for this sublist. */ + this_donetrap = 1; + goto sublist_done; + } else if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END) { + this_donetrap = 1; + /* + * Treat this in the same way as if we reached + * the end of the sublist normally. + */ + state->pc = next; + goto sublist_done; + } + } + cmdpush(CS_CMDOR); + break; + } + state->pc = next; + code = *state->pc++; + } + state->pc--; +sublist_done: + + /* + * See hairy code near the end of execif() for the + * following. "noerrexit " only applies until + * we hit execcmd on the way down. We're now + * on the way back up, so don't restore it. + */ + if (!(oldnoerrexit & NOERREXIT_UNTIL_EXEC)) + noerrexit = oldnoerrexit; + + if (sigtrapped[SIGDEBUG] && !isset(DEBUGBEFORECMD) && !donedebug) { + /* + * Save and restore ERREXIT for consistency with + * DEBUGBEFORECMD, even though it's not used. + */ + int oerrexit_opt = opts[ERREXIT]; + opts[ERREXIT] = 0; + noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; + exiting = donetrap; + ret = lastval; + dotrap(SIGDEBUG); + if (!retflag) + lastval = ret; + donetrap = exiting; + noerrexit = oldnoerrexit; + opts[ERREXIT] = oerrexit_opt; + } + + cmdsp = csp; + + /* Check whether we are suppressing traps/errexit * + * (typically in init scripts) and if we haven't * + * already performed them for this sublist. */ + if (!this_noerrexit && !donetrap && !this_donetrap) { + if (sigtrapped[SIGZERR] && lastval && + !(noerrexit & NOERREXIT_EXIT)) { + dotrap(SIGZERR); + donetrap = 1; + } + if (lastval) { + int errreturn = isset(ERRRETURN) && + (isset(INTERACTIVE) || locallevel || sourcelevel) && + !(noerrexit & NOERREXIT_RETURN); + int errexit = (isset(ERREXIT) || + (isset(ERRRETURN) && !errreturn)) && + !(noerrexit & NOERREXIT_EXIT); + if (errexit) { + if (sigtrapped[SIGEXIT]) + dotrap(SIGEXIT); + if (mypid != getpid()) + _exit(lastval); + else + exit(lastval); + } + if (errreturn) { + retflag = 1; + breaks = loops; + } + } + } + if (ltype & Z_END) + break; + code = *state->pc++; + } + pline_level = old_pline_level; + list_pipe = old_list_pipe; + list_pipe_job = old_list_pipe_job; + if (old_list_pipe_text) { + strcpy(list_pipe_text, old_list_pipe_text); + zsfree(old_list_pipe_text); + } else { + *list_pipe_text = '\0'; + } + lineno = oldlineno; + if (dont_change_job) + thisjob = cj; + + if (exiting && sigtrapped[SIGEXIT]) { + dotrap(SIGEXIT); + /* Make sure this doesn't get executed again. */ + sigtrapped[SIGEXIT] = 0; + } + + unqueue_signals(); +} + +/* Execute a pipeline. * + * last1 is a flag that this command is the last command in a shell * + * that is about to exit, so we can exec instead of forking. It gets * + * passed all the way down to execcmd() which actually makes the * + * decision. A 0 is always passed if the command is not the last in * + * the pipeline. This function assumes that the sublist is not NULL. * + * If last1 is zero but the command is at the end of a pipeline, we * + * pass 2 down to execcmd(). * + */ + +/**/ +static int +execpline(Estate state, wordcode slcode, int how, int last1) +{ + int ipipe[2], opipe[2]; + int pj, newjob; + int old_simple_pline = simple_pline; + int slflags = WC_SUBLIST_FLAGS(slcode); + wordcode code = *state->pc++; + static int lastwj, lpforked; + + if (wc_code(code) != WC_PIPE) + return lastval = (slflags & WC_SUBLIST_NOT) != 0; + 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(); + + /* + * Get free entry in job table and initialize it. This is currently + * the only call to initjob() (apart from a minor exception in + * clearjobtab()), so this is also the only place where we can + * expand the job table under us. + */ + if ((thisjob = newjob = initjob()) == -1) { + child_unblock(); + unqueue_signals(); + return 1; + } + if (how & Z_TIMED) + jobtab[thisjob].stat |= STAT_TIMED; + + if (slflags & WC_SUBLIST_COPROC) { + how = Z_ASYNC; + if (coprocin >= 0) { + zclose(coprocin); + zclose(coprocout); + } + if (mpipe(ipipe) < 0) { + coprocin = coprocout = -1; + slflags &= ~WC_SUBLIST_COPROC; + } else if (mpipe(opipe) < 0) { + close(ipipe[0]); + close(ipipe[1]); + coprocin = coprocout = -1; + slflags &= ~WC_SUBLIST_COPROC; + } else { + coprocin = ipipe[0]; + coprocout = opipe[1]; + fdtable[coprocin] = fdtable[coprocout] = FDT_UNUSED; + } + } + /* This used to set list_pipe_pid=0 unconditionally, but in things + * like `ls|if true; then sleep 20; cat; fi' where the sleep was + * stopped, the top-level execpline() didn't get the pid for the + * sub-shell because it was overwritten. */ + if (!pline_level++) { + list_pipe_pid = 0; + nowait = 0; + simple_pline = (WC_PIPE_TYPE(code) == WC_PIPE_END); + list_pipe_job = newjob; + } + lastwj = lpforked = 0; + execpline2(state, code, how, opipe[0], ipipe[1], last1); + pline_level--; + if (how & Z_ASYNC) { + lastwj = newjob; + + if (thisjob == list_pipe_job) + list_pipe_job = 0; + jobtab[thisjob].stat |= STAT_NOSTTY; + if (slflags & WC_SUBLIST_COPROC) { + zclose(ipipe[1]); + zclose(opipe[0]); + } + if (how & Z_DISOWN) { + pipecleanfilelist(jobtab[thisjob].filelist, 0); + deletejob(jobtab + thisjob, 1); + thisjob = -1; + } + else + spawnjob(); + child_unblock(); + unqueue_signals(); + /* Executing background code resets shell status */ + return lastval = 0; + } else { + if (newjob != lastwj) { + Job jn = jobtab + newjob; + int updated; + + if (newjob == list_pipe_job && list_pipe_child) + _exit(0); + + lastwj = thisjob = newjob; + + if (list_pipe || (pline_level && !(how & Z_TIMED))) + jn->stat |= STAT_NOPRINT; + + if (nowait) { + if(!pline_level) { + int jobsub; + struct process *pn, *qn; + + curjob = newjob; + DPUTS(!list_pipe_pid, "invalid list_pipe_pid"); + addproc(list_pipe_pid, list_pipe_text, 0, + &list_pipe_start); + + /* If the super-job contains only the sub-shell, the + sub-shell is the group leader. */ + if (!jn->procs->next || lpforked == 2) { + jn->gleader = list_pipe_pid; + jn->stat |= STAT_SUBLEADER; + /* + * Pick up any subjob that's still lying around + * as it's now our responsibility. + * If we find it we're a SUPERJOB. + */ + for (jobsub = 1; jobsub <= maxjob; jobsub++) { + Job jnsub = jobtab + jobsub; + if (jnsub->stat & STAT_SUBJOB_ORPHANED) { + jn->other = jobsub; + jn->stat |= STAT_SUPERJOB; + jnsub->stat &= ~STAT_SUBJOB_ORPHANED; + jnsub->other = list_pipe_pid; + } + } + } + for (pn = jobtab[jn->other].procs; pn; pn = pn->next) + if (WIFSTOPPED(pn->status)) + break; + + if (pn) { + for (qn = jn->procs; qn->next; qn = qn->next); + qn->status = pn->status; + } + + jn->stat &= ~(STAT_DONE | STAT_NOPRINT); + jn->stat |= STAT_STOPPED | STAT_CHANGED | STAT_LOCKED | + STAT_INUSE; + printjob(jn, !!isset(LONGLISTJOBS), 1); + } + else if (newjob != list_pipe_job) + deletejob(jn, 0); + else + lastwj = -1; + } + + errbrk_saved = 0; + for (; !nowait;) { + if (list_pipe_child) { + jn->stat |= STAT_NOPRINT; + makerunning(jn); + } + if (!(jn->stat & STAT_LOCKED)) { + updated = hasprocs(thisjob); + 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 && + lastval2 & 0200) + killpg(mypgrp, lastval2 & ~0200); + if (!list_pipe_child && !lpforked && !subsh && jobbing && + (list_pipe || last1 || pline_level) && + ((jn->stat & STAT_STOPPED) || + (list_pipe_job && pline_level && + (jobtab[list_pipe_job].stat & STAT_STOPPED)))) { + pid_t pid = 0; + int synch[2]; + struct timeval bgtime; + + /* + * A pipeline with the shell handling the right + * hand side was stopped. We'll fork to allow + * it to continue. + */ + if (pipe(synch) < 0 || (pid = zfork(&bgtime)) == -1) { + /* Failure */ + if (pid < 0) { + close(synch[0]); + close(synch[1]); + } else + zerr("pipe failed: %e", errno); + zleentry(ZLE_CMD_TRASH); + fprintf(stderr, "zsh: job can't be suspended\n"); + fflush(stderr); + makerunning(jn); + killjb(jn, SIGCONT); + thisjob = newjob; + } + else if (pid) { + /* + * Parent: job control is here. If the job + * started for the RHS of the pipeline is still + * around, then its a SUBJOB and the job for + * earlier parts of the pipeeline is its SUPERJOB. + * The newly forked shell isn't recorded as a + * separate job here, just as list_pipe_pid. + * If the superjob exits (it may already have + * done so, see child branch below), we'll use + * list_pipe_pid to form the basis of a + * replacement job --- see SUBLEADER code above. + */ + char dummy; + + lpforked = + (killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1); + list_pipe_pid = pid; + list_pipe_start = bgtime; + nowait = 1; + errflag |= ERRFLAG_ERROR; + breaks = loops; + close(synch[1]); + read_loop(synch[0], &dummy, 1); + close(synch[0]); + /* If this job has finished, we leave it as a + * normal (non-super-) job. */ + if (!(jn->stat & STAT_DONE)) { + jobtab[list_pipe_job].other = newjob; + jobtab[list_pipe_job].stat |= STAT_SUPERJOB; + jn->stat |= STAT_SUBJOB | STAT_NOPRINT; + jn->other = list_pipe_pid; /* see zsh.h */ + if (hasprocs(list_pipe_job)) + jn->gleader = jobtab[list_pipe_job].gleader; + } + if ((list_pipe || last1) && hasprocs(list_pipe_job)) + killpg(jobtab[list_pipe_job].gleader, SIGSTOP); + break; + } + else { + close(synch[0]); + entersubsh(ESUB_ASYNC); + /* + * At this point, we used to attach this process + * to the process group of list_pipe_job (the + * new superjob) any time that was still available. + * That caused problems in at least two + * cases because this forked shell was then + * suspended with the right hand side of the + * pipeline, and the SIGSTOP below suspended + * it a second time when it was continued. + * + * It's therefore not clear entirely why you'd ever + * do anything other than the following, but no + * doubt we'll find out... + */ + setpgrp(0L, mypgrp = getpid()); + close(synch[1]); + kill(getpid(), SIGSTOP); + list_pipe = 0; + list_pipe_child = 1; + opts[INTERACTIVE] = 0; + if (errbrk_saved) { + /* + * Keep any user interrupt bit in errflag. + */ + errflag = prev_errflag | (errflag & ERRFLAG_INT); + breaks = prev_breaks; + } + break; + } + } + else if (subsh && jn->stat & STAT_STOPPED) + thisjob = newjob; + else + break; + } + child_unblock(); + unqueue_signals(); + + if (list_pipe && (lastval & 0200) && pj >= 0 && + (!(jn->stat & STAT_INUSE) || (jn->stat & STAT_DONE))) { + deletejob(jn, 0); + jn = jobtab + pj; + if (jn->gleader) + killjb(jn, lastval & ~0200); + } + if (list_pipe_child || + ((jn->stat & STAT_DONE) && + (list_pipe || (pline_level && !(jn->stat & STAT_SUBJOB))))) + deletejob(jn, 0); + thisjob = pj; + } + else + unqueue_signals(); + if ((slflags & WC_SUBLIST_NOT) && !errflag) + lastval = !lastval; + } + if (!pline_level) + simple_pline = old_simple_pline; + return lastval; +} + +/* execute pipeline. This function assumes the `pline' is not NULL. */ + +/**/ +static void +execpline2(Estate state, wordcode pcode, + int how, int input, int output, int last1) +{ + struct execcmd_params eparams; + + if (breaks || retflag) + return; + + /* In evaluated traps, don't modify the line number. */ + if (!IN_EVAL_TRAP() && !ineval && WC_PIPE_LINENO(pcode)) + lineno = WC_PIPE_LINENO(pcode) - 1; + + if (pline_level == 1) { + if ((how & Z_ASYNC) || !sfcontext) + strcpy(list_pipe_text, + getjobtext(state->prog, + state->pc + (WC_PIPE_TYPE(pcode) == WC_PIPE_END ? + 0 : 1))); + else + list_pipe_text[0] = '\0'; + } + if (WC_PIPE_TYPE(pcode) == WC_PIPE_END) { + execcmd_analyse(state, &eparams); + execcmd_exec(state, &eparams, input, output, how, last1 ? 1 : 2, -1); + } else { + int pipes[2]; + int old_list_pipe = list_pipe; + Wordcode next = state->pc + (*state->pc); + + ++state->pc; + execcmd_analyse(state, &eparams); + + if (mpipe(pipes) < 0) { + /* FIXME */ + } + + addfilelist(NULL, pipes[0]); + execcmd_exec(state, &eparams, input, pipes[1], how, 0, pipes[0]); + zclose(pipes[1]); + state->pc = next; + + /* if another execpline() is invoked because the command is * + * a list it must know that we're already in a pipeline */ + cmdpush(CS_PIPE); + list_pipe = 1; + execpline2(state, *state->pc++, how, pipes[0], output, last1); + list_pipe = old_list_pipe; + cmdpop(); + } +} + +/* make the argv array */ + +/**/ +static char ** +makecline(LinkList list) +{ + LinkNode node; + char **argv, **ptr; + + /* A bigger argv is necessary for executing scripts */ + ptr = argv = 2 + (char **) hcalloc((countlinknodes(list) + 4) * + sizeof(char *)); + + if (isset(XTRACE)) { + if (!doneps4) + printprompt4(); + + for (node = firstnode(list); node; incnode(node)) { + *ptr++ = (char *)getdata(node); + quotedzputs(getdata(node), xtrerr); + if (nextnode(node)) + fputc(' ', xtrerr); + } + fputc('\n', xtrerr); + fflush(xtrerr); + } else { + for (node = firstnode(list); node; incnode(node)) + *ptr++ = (char *)getdata(node); + } + *ptr = NULL; + return (argv); +} + +/**/ +mod_export void +untokenize(char *s) +{ + if (*s) { + int c; + + while ((c = *s++)) + if (itok(c)) { + char *p = s - 1; + + if (c != Nularg) + *p++ = ztokens[c - Pound]; + + while ((c = *s++)) { + if (itok(c)) { + if (c != Nularg) + *p++ = ztokens[c - Pound]; + } else + *p++ = c; + } + *p = '\0'; + break; + } + } +} + + +/* + * Given a tokenized string, output it to standard output in + * such a way that it's clear which tokens are active. + * Hence Star becomes an unquoted "*", while a "*" becomes "\*". + * + * The code here is a kind of amalgamation of the tests in + * zshtokenize() and untokenize() with some outputting. + */ + +/**/ +void +quote_tokenized_output(char *str, FILE *file) +{ + char *s = str; + + for (; *s; s++) { + switch (*s) { + case Meta: + putc(*++s ^ 32, file); + continue; + + case Nularg: + /* Do nothing. I think. */ + continue; + + case '\\': + case '<': + case '>': + case '(': + case '|': + case ')': + case '^': + case '#': + case '~': + case '[': + case ']': + case '*': + case '?': + case '$': + case ' ': + putc('\\', file); + break; + + case '\t': + fputs("$'\\t'", file); + continue; + + case '\n': + fputs("$'\\n'", file); + continue; + + case '\r': + fputs("$'\\r'", file); + continue; + + case '=': + if (s == str) + putc('\\', file); + break; + + default: + if (itok(*s)) { + putc(ztokens[*s - Pound], file); + continue; + } + break; + } + + putc(*s, file); + } +} + +/* Check that we can use a parameter for allocating a file descriptor. */ + +static int +checkclobberparam(struct redir *f) +{ + struct value vbuf; + Value v; + char *s = f->varid; + int fd; + + if (!s) + return 1; + + if (!(v = getvalue(&vbuf, &s, 0))) + return 1; + + if (v->pm->node.flags & PM_READONLY) { + zwarn("can't allocate file descriptor to readonly parameter %s", + f->varid); + /* don't flag a system error for this */ + errno = 0; + return 0; + } + + /* + * We can't clobber the value in the parameter if it's + * already an opened file descriptor --- that means it's a decimal + * integer corresponding to an opened file descriptor, + * not merely an expression that evaluates to a file descriptor. + */ + if (!isset(CLOBBER) && (s = getstrvalue(v)) && + (fd = (int)zstrtol(s, &s, 10)) >= 0 && !*s && + fd <= max_zsh_fd && fdtable[fd] == FDT_EXTERNAL) { + zwarn("can't clobber parameter %s containing file descriptor %d", + f->varid, fd); + /* don't flag a system error for this */ + errno = 0; + return 0; + } + return 1; +} + +/* Open a file for writing redirection */ + +/**/ +static int +clobber_open(struct redir *f) +{ + struct stat buf; + int fd, oerrno; + + /* If clobbering, just open. */ + if (isset(CLOBBER) || IS_CLOBBER_REDIR(f->type)) + return open(unmeta(f->name), + O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0666); + + /* If not clobbering, attempt to create file exclusively. */ + if ((fd = open(unmeta(f->name), + O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0666)) >= 0) + return fd; + + /* If that fails, we are still allowed to open non-regular files. * + * Try opening, and if it's a regular file then close it again * + * because we weren't supposed to open it. */ + oerrno = errno; + if ((fd = open(unmeta(f->name), O_WRONLY | O_NOCTTY)) != -1) { + if(!fstat(fd, &buf) && !S_ISREG(buf.st_mode)) + return fd; + close(fd); + } + errno = oerrno; + return -1; +} + +/* size of buffer for tee and cat processes */ +#define TCBUFSIZE 4092 + +/* close an multio (success) */ + +/**/ +static void +closemn(struct multio **mfds, int fd, int type) +{ + if (fd >= 0 && mfds[fd] && mfds[fd]->ct >= 2) { + struct multio *mn = mfds[fd]; + char buf[TCBUFSIZE]; + int len, i; + pid_t pid; + struct timeval bgtime; + + /* + * We need to block SIGCHLD in case the process + * we are spawning terminates before the job table + * is set up to handle it. + */ + child_block(); + if ((pid = zfork(&bgtime))) { + for (i = 0; i < mn->ct; i++) + zclose(mn->fds[i]); + zclose(mn->pipe); + if (pid == -1) { + mfds[fd] = NULL; + child_unblock(); + return; + } + mn->ct = 1; + mn->fds[0] = fd; + addproc(pid, NULL, 1, &bgtime); + child_unblock(); + return; + } + /* pid == 0 */ + child_unblock(); + closeallelse(mn); + if (mn->rflag) { + /* tee process */ + while ((len = read(mn->pipe, buf, TCBUFSIZE)) != 0) { + if (len < 0) { + if (errno == EINTR) + continue; + else + break; + } + for (i = 0; i < mn->ct; i++) + write_loop(mn->fds[i], buf, len); + } + } else { + /* cat process */ + for (i = 0; i < mn->ct; i++) + while ((len = read(mn->fds[i], buf, TCBUFSIZE)) != 0) { + if (len < 0) { + if (errno == EINTR) + continue; + else + break; + } + write_loop(mn->pipe, buf, len); + } + } + _exit(0); + } else if (fd >= 0 && type == REDIR_CLOSE) + mfds[fd] = NULL; +} + +/* close all the mnodes (failure) */ + +/**/ +static void +closemnodes(struct multio **mfds) +{ + int i, j; + + for (i = 0; i < 10; i++) + if (mfds[i]) { + for (j = 0; j < mfds[i]->ct; j++) + zclose(mfds[i]->fds[j]); + mfds[i] = NULL; + } +} + +/**/ +static void +closeallelse(struct multio *mn) +{ + int i, j; + long openmax; + + openmax = fdtable_size; + + for (i = 0; i < openmax; i++) + if (mn->pipe != i) { + for (j = 0; j < mn->ct; j++) + if (mn->fds[j] == i) + break; + if (j == mn->ct) + zclose(i); + } +} + +/* + * A multio is a list of fds associated with a certain fd. + * Thus if you do "foo >bar >ble", the multio for fd 1 will have + * two fds, the result of open("bar",...), and the result of + * open("ble",....). + */ + +/* + * Add a fd to an multio. fd1 must be < 10, and may be in any state. + * fd2 must be open, and is `consumed' by this function. Note that + * fd1 == fd2 is possible, and indicates that fd1 was really closed. + * We effectively do `fd2 = movefd(fd2)' at the beginning of this + * function, but in most cases we can avoid an extra dup by delaying + * the movefd: we only >need< to move it if we're actually doing a + * multiple redirection. + * + * If varid is not NULL, we open an fd above 10 and set the parameter + * named varid to that value. fd1 is not used. + */ + +/**/ +static void +addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag, + char *varid) +{ + int pipes[2]; + + if (varid) { + /* fd will be over 10, don't touch mfds */ + fd1 = movefd(fd2); + if (fd1 == -1) { + zerr("cannot moved fd %d: %e", fd2, errno); + return; + } else { + fdtable[fd1] = FDT_EXTERNAL; + setiparam(varid, (zlong)fd1); + /* + * If setting the parameter failed, close the fd else + * it will leak. + */ + if (errflag) + zclose(fd1); + } + } else if (!mfds[fd1] || unset(MULTIOS)) { + if(!mfds[fd1]) { /* starting a new multio */ + mfds[fd1] = (struct multio *) zhalloc(sizeof(struct multio)); + if (!forked && save[fd1] == -2) { + if (fd1 == fd2) + save[fd1] = -1; + else { + int fdN = movefd(fd1); + /* + * fd1 may already be closed here, so + * ignore bad file descriptor error + */ + if (fdN < 0) { + if (errno != EBADF) { + zerr("cannot duplicate fd %d: %e", fd1, errno); + mfds[fd1] = NULL; + closemnodes(mfds); + return; + } + } else { + DPUTS(fdtable[fdN] != FDT_INTERNAL, + "Saved file descriptor not marked as internal"); + fdtable[fdN] |= FDT_SAVED_MASK; + } + save[fd1] = fdN; + } + } + } + if (!varid) + redup(fd2, fd1); + mfds[fd1]->ct = 1; + mfds[fd1]->fds[0] = fd1; + mfds[fd1]->rflag = rflag; + } else { + if (mfds[fd1]->rflag != rflag) { + zerr("file mode mismatch on fd %d", fd1); + closemnodes(mfds); + return; + } + if (mfds[fd1]->ct == 1) { /* split the stream */ + int fdN = movefd(fd1); + if (fdN < 0) { + zerr("multio failed for fd %d: %e", fd1, errno); + closemnodes(mfds); + return; + } + mfds[fd1]->fds[0] = fdN; + fdN = movefd(fd2); + if (fdN < 0) { + zerr("multio failed for fd %d: %e", fd2, errno); + closemnodes(mfds); + return; + } + mfds[fd1]->fds[1] = fdN; + if (mpipe(pipes) < 0) { + zerr("multio failed for fd %d: %e", fd2, errno); + closemnodes(mfds); + return; + } + mfds[fd1]->pipe = pipes[1 - rflag]; + redup(pipes[rflag], fd1); + mfds[fd1]->ct = 2; + } else { /* add another fd to an already split stream */ + int fdN; + if(!(mfds[fd1]->ct % MULTIOUNIT)) { + int new = sizeof(struct multio) + sizeof(int) * mfds[fd1]->ct; + int old = new - sizeof(int) * MULTIOUNIT; + mfds[fd1] = hrealloc((char *)mfds[fd1], old, new); + } + if ((fdN = movefd(fd2)) < 0) { + zerr("multio failed for fd %d: %e", fd2, errno); + closemnodes(mfds); + return; + } + mfds[fd1]->fds[mfds[fd1]->ct++] = fdN; + } + } +} + +/**/ +static void +addvars(Estate state, Wordcode pc, int addflags) +{ + LinkList vl; + int xtr, isstr, htok = 0; + char **arr, **ptr, *name; + int flags; + + Wordcode opc = state->pc; + wordcode ac; + local_list1(svl); + + /* + * Warn when creating a global without using typeset -g in a + * function. Don't do this if there is a list of variables marked + * to be restored after the command, since then the assignment + * is implicitly scoped. + */ + flags = !(addflags & ADDVAR_RESTORE) ? ASSPM_WARN : 0; + xtr = isset(XTRACE); + if (xtr) { + printprompt4(); + doneps4 = 1; + } + state->pc = pc; + while (wc_code(ac = *state->pc++) == WC_ASSIGN) { + int myflags = flags; + name = ecgetstr(state, EC_DUPTOK, &htok); + if (htok) + untokenize(name); + if (WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) + myflags |= ASSPM_AUGMENT; + if (xtr) + fprintf(xtrerr, + WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC ? "%s+=" : "%s=", name); + if ((isstr = (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR))) { + init_list1(svl, ecgetstr(state, EC_DUPTOK, &htok)); + vl = &svl; + } else { + vl = ecgetlist(state, WC_ASSIGN_NUM(ac), EC_DUPTOK, &htok); + if (errflag) { + state->pc = opc; + return; + } + } + + if (vl && htok) { + int prefork_ret = 0; + prefork(vl, (isstr ? (PREFORK_SINGLE|PREFORK_ASSIGN) : + PREFORK_ASSIGN), &prefork_ret); + if (errflag) { + state->pc = opc; + return; + } + if (prefork_ret & PREFORK_KEY_VALUE) + myflags |= ASSPM_KEY_VALUE; + if (!isstr || (isset(GLOBASSIGN) && isstr && + haswilds((char *)getdata(firstnode(vl))))) { + globlist(vl, prefork_ret); + /* 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) && isstr) + unsetparam(name); + if (errflag) { + state->pc = opc; + return; + } + } + } + if (isstr && (empty(vl) || !nextnode(firstnode(vl)))) { + Param pm; + char *val; + int allexp; + + if (empty(vl)) + val = ztrdup(""); + else { + untokenize(peekfirst(vl)); + val = ztrdup(ugetnode(vl)); + } + if (xtr) { + quotedzputs(val, xtrerr); + fputc(' ', xtrerr); + } + if ((addflags & ADDVAR_EXPORT) && !strchr(name, '[')) { + if ((addflags & ADDVAR_RESTRICT) && isset(RESTRICTED) && + (pm = (Param) paramtab->removenode(paramtab, name)) && + (pm->node.flags & PM_RESTRICTED)) { + zerr("%s: restricted", pm->node.nam); + zsfree(val); + state->pc = opc; + return; + } + if (strcmp(name, "STTY") == 0) { + zsfree(STTYval); + STTYval = ztrdup(val); + } + allexp = opts[ALLEXPORT]; + opts[ALLEXPORT] = 1; + if (isset(KSHARRAYS)) + unsetparam(name); + pm = assignsparam(name, val, myflags); + opts[ALLEXPORT] = allexp; + } else + pm = assignsparam(name, val, myflags); + if (errflag) { + state->pc = opc; + return; + } + continue; + } + if (vl) { + ptr = arr = (char **) zalloc(sizeof(char *) * + (countlinknodes(vl) + 1)); + + while (nonempty(vl)) + *ptr++ = ztrdup((char *) ugetnode(vl)); + } else + ptr = arr = (char **) zalloc(sizeof(char *)); + + *ptr = NULL; + if (xtr) { + fprintf(xtrerr, "( "); + for (ptr = arr; *ptr; ptr++) { + quotedzputs(*ptr, xtrerr); + fputc(' ', xtrerr); + } + fprintf(xtrerr, ") "); + } + assignaparam(name, arr, myflags); + if (errflag) { + state->pc = opc; + return; + } + } + state->pc = opc; +} + +/**/ +void +setunderscore(char *str) +{ + queue_signals(); + if (str && *str) { + int l = strlen(str) + 1, nl = (l + 31) & ~31; + + if (nl > underscorelen || (underscorelen - nl) > 64) { + zfree(zunderscore, underscorelen); + zunderscore = (char *) zalloc(underscorelen = nl); + } + strcpy(zunderscore, str); + underscoreused = l; + } else { + if (underscorelen > 128) { + zfree(zunderscore, underscorelen); + zunderscore = (char *) zalloc(underscorelen = 32); + } + *zunderscore = '\0'; + underscoreused = 1; + } + unqueue_signals(); +} + +/* These describe the type of expansions that need to be done on the words + * used in the thing we are about to execute. They are set in execcmd() and + * used in execsubst() which might be called from one of the functions + * called from execcmd() (like execfor() and so on). */ + +static int esprefork, esglob = 1; + +/**/ +void +execsubst(LinkList strs) +{ + if (strs) { + prefork(strs, esprefork, NULL); + if (esglob && !errflag) { + LinkList ostrs = strs; + globlist(strs, 0); + strs = ostrs; + } + } +} + +/* + * Check if a builtin requires an autoload and if so + * deal with it. This may return NULL. + */ + +/**/ +static HashNode +resolvebuiltin(const char *cmdarg, HashNode hn) +{ + if (!((Builtin) hn)->handlerfunc) { + char *modname = dupstring(((Builtin) hn)->optstr); + /* + * Ensure the module is loaded and the + * feature corresponding to the builtin + * is enabled. + */ + (void)ensurefeature(modname, "b:", + (hn->flags & BINF_AUTOALL) ? NULL : + hn->nam); + hn = builtintab->getnode(builtintab, cmdarg); + if (!hn) { + lastval = 1; + zerr("autoloading module %s failed to define builtin: %s", + modname, cmdarg); + return NULL; + } + } + return hn; +} + +/* + * We are about to execute a command at the lowest level of the + * hierarchy. Analyse the parameters from the wordcode. + */ + +/**/ +static void +execcmd_analyse(Estate state, Execcmd_params eparams) +{ + wordcode code; + int i; + + eparams->beg = state->pc; + eparams->redir = + (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL); + if (wc_code(*state->pc) == WC_ASSIGN) { + cmdoutval = 0; + eparams->varspc = state->pc; + while (wc_code((code = *state->pc)) == WC_ASSIGN) + state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ? + 3 : WC_ASSIGN_NUM(code) + 2); + } else + eparams->varspc = NULL; + + code = *state->pc++; + + eparams->type = wc_code(code); + eparams->postassigns = 0; + + /* It would be nice if we could use EC_DUPTOK instead of EC_DUP here. + * But for that we would need to check/change all builtins so that + * they don't modify their argument strings. */ + switch (eparams->type) { + case WC_SIMPLE: + eparams->args = ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, + &eparams->htok); + eparams->assignspc = NULL; + break; + + case WC_TYPESET: + eparams->args = ecgetlist(state, WC_TYPESET_ARGC(code), EC_DUP, + &eparams->htok); + eparams->postassigns = *state->pc++; + eparams->assignspc = state->pc; + for (i = 0; i < eparams->postassigns; i++) { + code = *state->pc; + DPUTS(wc_code(code) != WC_ASSIGN, + "BUG: miscounted typeset assignments"); + state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ? + 3 : WC_ASSIGN_NUM(code) + 2); + } + break; + + default: + eparams->args = NULL; + eparams->assignspc = NULL; + eparams->htok = 0; + break; + } +} + +/* + * Transfer the first node of args to preargs, performing + * prefork expansion on the way if necessary. + */ +static void execcmd_getargs(LinkList preargs, LinkList args, int expand) +{ + if (!firstnode(args)) { + return; + } else if (expand) { + local_list0(svl); + init_list0(svl); + /* not init_list1, as we need real nodes */ + addlinknode(&svl, uremnode(args, firstnode(args))); + /* Analysing commands, so vanilla options to prefork */ + prefork(&svl, 0, NULL); + joinlists(preargs, &svl); + } else { + addlinknode(preargs, uremnode(args, firstnode(args))); + } +} + +/**/ +static int +execcmd_fork(Estate state, int how, int type, Wordcode varspc, + LinkList *filelistp, char *text, int oautocont, + int close_if_forked) +{ + pid_t pid; + int synch[2], flags; + char dummy; + struct timeval bgtime; + + child_block(); + + if (pipe(synch) < 0) { + zerr("pipe failed: %e", errno); + return -1; + } else if ((pid = zfork(&bgtime)) == -1) { + close(synch[0]); + close(synch[1]); + lastval = 1; + errflag |= ERRFLAG_ERROR; + return -1; + } + if (pid) { + close(synch[1]); + read_loop(synch[0], &dummy, 1); + close(synch[0]); + if (how & Z_ASYNC) { + lastpid = (zlong) pid; + } else if (!jobtab[thisjob].stty_in_env && varspc) { + /* search for STTY=... */ + Wordcode p = varspc; + wordcode ac; + + while (wc_code(ac = *p) == WC_ASSIGN) { + if (!strcmp(ecrawstr(state->prog, p + 1, NULL), "STTY")) { + jobtab[thisjob].stty_in_env = 1; + break; + } + p += (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR ? + 3 : WC_ASSIGN_NUM(ac) + 2); + } + } + addproc(pid, text, 0, &bgtime); + if (oautocont >= 0) + opts[AUTOCONTINUE] = oautocont; + pipecleanfilelist(jobtab[thisjob].filelist, 1); + return pid; + } + + /* pid == 0 */ + close(synch[0]); + flags = ((how & Z_ASYNC) ? ESUB_ASYNC : 0) | ESUB_PGRP; + if ((type != WC_SUBSH) && !(how & Z_ASYNC)) + flags |= ESUB_KEEPTRAP; + if (type == WC_SUBSH && !(how & Z_ASYNC)) + flags |= ESUB_JOB_CONTROL; + *filelistp = jobtab[thisjob].filelist; + entersubsh(flags); + close(synch[1]); + zclose(close_if_forked); + + if (sigtrapped[SIGINT] & ZSIG_IGNORED) + holdintr(); + /* + * EXIT traps shouldn't be called even if we forked to run + * shell code as this isn't the main shell. + */ + sigtrapped[SIGEXIT] = 0; +#ifdef HAVE_NICE + /* Check if we should run background jobs at a lower priority. */ + if ((how & Z_ASYNC) && isset(BGNICE)) + if (nice(5) < 0) + zwarn("nice(5) failed: %e", errno); +#endif /* HAVE_NICE */ + + return 0; +} + +/* + * Execute a command at the lowest level of the hierarchy. + */ + +/**/ +static void +execcmd_exec(Estate state, Execcmd_params eparams, + int input, int output, int how, int last1, int close_if_forked) +{ + HashNode hn = NULL; + LinkList filelist = NULL; + LinkNode node; + Redir fn; + struct multio *mfds[10]; + char *text; + int save[10]; + int fil, dfil, is_cursh, do_exec = 0, redir_err = 0, i; + int nullexec = 0, magic_assign = 0, forked = 0, old_lastval; + int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0; + /* Various flags to the command. */ + int cflags = 0, orig_cflags = 0, checked = 0, oautocont = -1; + FILE *oxtrerr = xtrerr, *newxtrerr = NULL; + /* + * Retrieve parameters for quick reference (they are unique + * to us so we can modify the structure if we want). + */ + LinkList args = eparams->args; + LinkList redir = eparams->redir; + Wordcode varspc = eparams->varspc; + int type = eparams->type; + /* + * preargs comes from expanding the head of the args list + * in order to check for prefix commands. + */ + LinkList preargs; + + doneps4 = 0; + + /* + * If assignment but no command get the status from variable + * assignment. + */ + old_lastval = lastval; + if (!args && varspc) + lastval = errflag ? errflag : cmdoutval; + /* + * If there are arguments, we should reset the status for the + * command before execution---unless we are using the result of a + * command substitution, which will be indicated by setting + * use_cmdoutval to 1. We haven't kicked those off yet, so + * there's no race. + */ + use_cmdoutval = !args; + + for (i = 0; i < 10; i++) { + save[i] = -2; + mfds[i] = NULL; + } + + /* If the command begins with `%', then assume it is a * + * reference to a job in the job table. */ + if ((type == WC_SIMPLE || type == WC_TYPESET) && args && nonempty(args) && + *(char *)peekfirst(args) == '%') { + if (how & Z_DISOWN) { + oautocont = opts[AUTOCONTINUE]; + opts[AUTOCONTINUE] = 1; + } + pushnode(args, dupstring((how & Z_DISOWN) + ? "disown" : (how & Z_ASYNC) ? "bg" : "fg")); + how = Z_SYNC; + } + + /* If AUTORESUME is set, the command is SIMPLE, and doesn't have * + * any redirections, then check if it matches as a prefix of a * + * job currently in the job table. If it does, then we treat it * + * as a command to resume this job. */ + if (isset(AUTORESUME) && type == WC_SIMPLE && (how & Z_SYNC) && + args && nonempty(args) && (!redir || empty(redir)) && !input && + !nextnode(firstnode(args))) { + if (unset(NOTIFY)) + scanjobs(); + if (findjobnam(peekfirst(args)) != -1) + pushnode(args, dupstring("fg")); + } + + if ((how & Z_ASYNC) || output) { + /* + * If running in the background, or not the last command in a + * pipeline, we don't need any of the rest of this function to + * affect the state in the main shell, so fork immediately. + * + * In other cases we may need to process the command line + * a bit further before we make the decision. + */ + text = getjobtext(state->prog, eparams->beg); + switch (execcmd_fork(state, how, type, varspc, &filelist, + text, oautocont, close_if_forked)) { + case -1: + goto fatal; + case 0: + break; + default: + return; + } + last1 = forked = 1; + } else + text = NULL; + + /* Check if it's a builtin needing automatic MAGIC_EQUALS_SUBST * + * handling. Things like typeset need this. We can't detect the * + * command if it contains some tokens (e.g. x=ex; ${x}port), so this * + * only works in simple cases. has_token() is called to make sure * + * this really is a simple case. */ + if ((type == WC_SIMPLE || type == WC_TYPESET) && args) { + /* + * preargs contains args that have been expanded by prefork. + * Running execcmd_getargs() causes any argument available + * in args to be exanded where necessary and transferred to + * preargs. We call execcmd_getargs() every time we need to + * analyse an argument not available in preargs, though there is + * no guarantee a further argument will be available. + */ + preargs = newlinklist(); + execcmd_getargs(preargs, args, eparams->htok); + while (nonempty(preargs)) { + char *cmdarg = (char *) peekfirst(preargs); + checked = !has_token(cmdarg); + if (!checked) + break; + 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 (isset(POSIXBUILTINS) && (cflags & BINF_EXEC)) { + /* + * POSIX doesn't allow "exec" to operate on builtins + * or shell functions. + */ + break; + } 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; + cflags |= hn->flags; + if (!(hn->flags & BINF_PREFIX)) { + is_builtin = 1; + + /* autoload the builtin if necessary */ + if (!(hn = resolvebuiltin(cmdarg, hn))) { + if (forked) + _exit(lastval); + return; + } + if (type != WC_TYPESET) + magic_assign = (hn->flags & BINF_MAGICEQUALS); + break; + } + checked = 0; + /* + * We usually don't need the argument containing the + * precommand modifier itself. Exception: when "command" + * will implemented by a call to "whence", in which case + * we'll simply re-insert the argument. + */ + uremnode(preargs, firstnode(preargs)); + if (!firstnode(preargs)) { + execcmd_getargs(preargs, args, eparams->htok); + if (!firstnode(preargs)) + break; + } + if ((cflags & BINF_COMMAND)) { + /* + * Check for options to "command". + * If just -p, this is handled here: use the default + * path to execute. + * If -v or -V, possibly with -p, dispatch to bin_whence + * but with flag to indicate special handling of -p. + * Otherwise, just leave marked as BINF_COMMAND + * modifier with no additional action. + */ + LinkNode argnode, oldnode, pnode = NULL; + char *argdata, *cmdopt; + int has_p = 0, has_vV = 0, has_other = 0; + argnode = firstnode(preargs); + argdata = (char *) getdata(argnode); + while (IS_DASH(*argdata)) { + /* Just to be definite, stop on single "-", too, */ + if (!argdata[1] || + (IS_DASH(argdata[1]) && !argdata[2])) + break; + for (cmdopt = argdata+1; *cmdopt; cmdopt++) { + switch (*cmdopt) { + case 'p': + /* + * If we've got this multiple times (command + * -p -p) we'll treat the second -p as a + * command because we only remove one below. + * Don't think that's a big issue, and it's + * also traditional behaviour. + */ + has_p = 1; + pnode = argnode; + break; + case 'v': + case 'V': + has_vV = 1; + break; + default: + has_other = 1; + break; + } + } + if (has_other) { + /* Don't know how to handle this, so don't */ + has_p = has_vV = 0; + break; + } + + oldnode = argnode; + argnode = nextnode(argnode); + if (!argnode) { + execcmd_getargs(preargs, args, eparams->htok); + if (!(argnode = nextnode(oldnode))) + break; + } + argdata = (char *) getdata(argnode); + } + if (has_vV) { + /* + * Leave everything alone, dispatch to whence. + * We need to put the name back in the list. + */ + pushnode(preargs, "command"); + hn = &commandbn.node; + is_builtin = 1; + break; + } else if (has_p) { + /* Use default path */ + use_defpath = 1; + /* + * We don't need this node as we're not treating + * "command" as a builtin this time. + */ + if (pnode) + uremnode(preargs, pnode); + } + /* + * Else just any trailing + * end-of-options marker. This can only occur + * if we just had -p or something including more + * than just -p, -v and -V, in which case we behave + * as if this is command [non-option-stuff]. This + * isn't a good place for standard option handling. + */ + if (IS_DASH(argdata[0]) && IS_DASH(argdata[1]) && !argdata[2]) + uremnode(preargs, argnode); + } else if (cflags & BINF_EXEC) { + /* + * Check for compatibility options to exec builtin. + * It would be nice to do these more generically, + * but currently we don't have a mechanism for + * precommand modifiers. + */ + LinkNode argnode = firstnode(preargs), oldnode; + char *argdata = (char *) getdata(argnode); + char *cmdopt, *exec_argv0 = NULL; + /* + * Careful here: we want to make sure a final dash + * is passed through in order that it still behaves + * as a precommand modifier (zsh equivalent of -l). + * It has to be last, but I think that's OK since + * people aren't likely to mix the option style + * with the zsh style. + */ + while (argdata && IS_DASH(*argdata) && strlen(argdata) >= 2) { + oldnode = argnode; + argnode = nextnode(oldnode); + if (!argnode) { + execcmd_getargs(preargs, args, eparams->htok); + argnode = nextnode(oldnode); + } + if (!argnode) { + zerr("exec requires a command to execute"); + lastval = 1; + errflag |= ERRFLAG_ERROR; + goto done; + } + uremnode(preargs, oldnode); + if (IS_DASH(argdata[0]) && IS_DASH(argdata[1]) && !argdata[2]) + break; + for (cmdopt = &argdata[1]; *cmdopt; ++cmdopt) { + switch (*cmdopt) { + case 'a': + /* argument is ARGV0 string */ + if (cmdopt[1]) { + exec_argv0 = cmdopt+1; + /* position on last non-NULL character */ + cmdopt += strlen(cmdopt+1); + } else { + if (!argnode) { + zerr("exec requires a command to execute"); + lastval = 1; + errflag |= ERRFLAG_ERROR; + goto done; + } + if (!nextnode(argnode)) + execcmd_getargs(preargs, args, + eparams->htok); + if (!nextnode(argnode)) { + zerr("exec flag -a requires a parameter"); + lastval = 1; + errflag |= ERRFLAG_ERROR; + goto done; + } + exec_argv0 = (char *) getdata(argnode); + oldnode = argnode; + argnode = nextnode(argnode); + uremnode(args, oldnode); + } + break; + case 'c': + cflags |= BINF_CLEARENV; + break; + case 'l': + cflags |= BINF_DASH; + break; + default: + zerr("unknown exec flag -%c", *cmdopt); + lastval = 1; + errflag |= ERRFLAG_ERROR; + if (forked) + _exit(lastval); + return; + } + } + if (!argnode) + break; + argdata = (char *) getdata(argnode); + } + if (exec_argv0) { + char *str, *s; + exec_argv0 = dupstring(exec_argv0); + remnulargs(exec_argv0); + untokenize(exec_argv0); + size_t sz = strlen(exec_argv0); + str = s = zalloc(5 + 1 + sz + 1); + strcpy(s, "ARGV0="); + s+=6; + strcpy(s, exec_argv0); + zputenv(str); + } + } + hn = NULL; + if ((cflags & BINF_COMMAND) && unset(POSIXBUILTINS)) + break; + if (!nonempty(preargs)) + execcmd_getargs(preargs, args, eparams->htok); + } + } else + preargs = NULL; + + /* if we get this far, it is OK to pay attention to lastval again */ + if (noerrexit & NOERREXIT_UNTIL_EXEC) + noerrexit = 0; + + /* Do prefork substitutions. + * + * Decide if we need "magic" handling of ~'s etc. in + * assignment-like arguments. + * - If magic_assign is set, we are using a builtin of the + * tyepset family, but did not recognise this as a keyword, + * so need guess-o-matic behaviour. + * - Otherwise, if we did recognise the keyword, we never need + * guess-o-matic behaviour as the argument was properly parsed + * as such. + * - Otherwise, use the behaviour specified by the MAGIC_EQUAL_SUBST + * option. + */ + esprefork = (magic_assign || + (isset(MAGICEQUALSUBST) && type != WC_TYPESET)) ? + PREFORK_TYPESET : 0; + + if (args) { + if (eparams->htok) + prefork(args, esprefork, NULL); + if (preargs) + args = joinlists(preargs, args); + } + + if (type == WC_SIMPLE || type == WC_TYPESET) { + int unglobbed = 0; + + for (;;) { + char *cmdarg; + + if (!(cflags & BINF_NOGLOB)) + while (!checked && !errflag && args && nonempty(args) && + has_token((char *) peekfirst(args))) + zglob(args, firstnode(args), 0); + else if (!unglobbed) { + for (node = firstnode(args); node; incnode(node)) + untokenize((char *) getdata(node)); + unglobbed = 1; + } + + /* Current shell should not fork unless the * + * exec occurs at the end of a pipeline. */ + if ((cflags & BINF_EXEC) && last1) + do_exec = 1; + + /* Empty command */ + if (!args || empty(args)) { + if (redir && nonempty(redir)) { + if (do_exec) { + /* Was this "exec < foobar"? */ + nullexec = 1; + break; + } else if (varspc) { + nullexec = 2; + break; + } else if (!nullcmd || !*nullcmd || opts[CSHNULLCMD] || + (cflags & BINF_PREFIX)) { + zerr("redirection with no command"); + lastval = 1; + errflag |= ERRFLAG_ERROR; + if (forked) + _exit(lastval); + return; + } else if (!nullcmd || !*nullcmd || opts[SHNULLCMD]) { + if (!args) + args = newlinklist(); + addlinknode(args, dupstring(":")); + } else if (readnullcmd && *readnullcmd && + ((Redir) peekfirst(redir))->type == REDIR_READ && + !nextnode(firstnode(redir))) { + if (!args) + args = newlinklist(); + addlinknode(args, dupstring(readnullcmd)); + } else { + if (!args) + args = newlinklist(); + addlinknode(args, dupstring(nullcmd)); + } + } else if ((cflags & BINF_PREFIX) && (cflags & BINF_COMMAND)) { + lastval = 0; + if (forked) + _exit(lastval); + return; + } else { + /* + * No arguments. Reset the status if there were + * arguments before and no command substitution + * has provided a status. + */ + if (badcshglob == 1) { + zerr("no match"); + lastval = 1; + if (forked) + _exit(lastval); + return; + } + cmdoutval = use_cmdoutval ? lastval : 0; + if (varspc) { + /* Make sure $? is still correct for assignment */ + lastval = old_lastval; + addvars(state, varspc, 0); + } + if (errflag) + lastval = 1; + else + lastval = cmdoutval; + if (isset(XTRACE)) { + fputc('\n', xtrerr); + fflush(xtrerr); + } + if (forked) + _exit(lastval); + return; + } + } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) && do_exec) { + zerrnam("exec", "%s: restricted", + (char *) getdata(firstnode(args))); + lastval = 1; + if (forked) + _exit(lastval); + return; + } + + /* + * Quit looking for a command if: + * - there was an error; or + * - we checked the simple cases needing MAGIC_EQUAL_SUBST; or + * - we know we already found a builtin (because either: + * - we loaded a builtin from a module, or + * - we have determined there are options which would + * require us to use the "command" builtin); or + * - we aren't using POSIX and so BINF_COMMAND indicates a zsh + * precommand modifier is being used in place of the + * builtin + * - we are using POSIX and this is an EXEC, so we can't + * execute a builtin or function. + */ + if (errflag || checked || is_builtin || + (isset(POSIXBUILTINS) ? + (cflags & BINF_EXEC) : (cflags & BINF_COMMAND))) + break; + + cmdarg = (char *) peekfirst(args); + if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) && + (hn = shfunctab->getnode(shfunctab, cmdarg))) { + is_shfunc = 1; + break; + } + if (!(hn = builtintab->getnode(builtintab, cmdarg))) { + if (cflags & BINF_BUILTIN) { + zwarn("no such builtin: %s", cmdarg); + lastval = 1; + if (oautocont >= 0) + opts[AUTOCONTINUE] = oautocont; + if (forked) + _exit(lastval); + return; + } + break; + } + if (!(hn->flags & BINF_PREFIX)) { + is_builtin = 1; + + /* autoload the builtin if necessary */ + if (!(hn = resolvebuiltin(cmdarg, hn))) { + if (forked) + _exit(lastval); + return; + } + break; + } + cflags &= ~BINF_BUILTIN & ~BINF_COMMAND; + cflags |= hn->flags; + uremnode(args, firstnode(args)); + hn = NULL; + } + } + + if (errflag) { + if (!lastval) + lastval = 1; + if (oautocont >= 0) + opts[AUTOCONTINUE] = oautocont; + if (forked) + _exit(lastval); + return; + } + + /* Get the text associated with this command. */ + if (!text && + (!sfcontext && (jobbing || (how & Z_TIMED)))) + text = getjobtext(state->prog, eparams->beg); + + /* + * Set up special parameter $_ + * For execfuncdef we may need to take account of an + * anonymous function with arguments. + */ + if (type != WC_FUNCDEF) + setunderscore((args && nonempty(args)) ? + ((char *) getdata(lastnode(args))) : ""); + + /* Warn about "rm *" */ + if (type == WC_SIMPLE && interact && unset(RMSTARSILENT) && + isset(SHINSTDIN) && args && nonempty(args) && + nextnode(firstnode(args)) && !strcmp(peekfirst(args), "rm")) { + LinkNode node, next; + + for (node = nextnode(firstnode(args)); node && !errflag; node = next) { + char *s = (char *) getdata(node); + int l = strlen(s); + + next = nextnode(node); + if (s[0] == Star && !s[1]) { + if (!checkrmall(pwd)) { + errflag |= ERRFLAG_ERROR; + break; + } + } else if (l >= 2 && s[l - 2] == '/' && s[l - 1] == Star) { + char t = s[l - 2]; + int rmall; + + s[l - 2] = 0; + rmall = checkrmall(s); + s[l - 2] = t; + + if (!rmall) { + errflag |= ERRFLAG_ERROR; + break; + } + } + } + } + + if (type == WC_FUNCDEF) { + /* + * The first word of a function definition is a list of + * names. If this is empty, we're doing an anonymous function: + * in that case redirections are handled normally. + * If not, it's a function definition: then we don't do + * redirections here but pass in the list of redirections to + * be stored for recall with the function. + */ + if (*state->pc != 0) { + /* Nonymous, don't do redirections here */ + redir = NULL; + } + } else if (is_shfunc || type == WC_AUTOFN) { + Shfunc shf; + if (is_shfunc) + shf = (Shfunc)hn; + else { + shf = loadautofn(state->prog->shf, 1, 0, 0); + if (shf) + state->prog->shf = shf; + else { + /* + * This doesn't set errflag, so just return now. + */ + lastval = 1; + if (oautocont >= 0) + opts[AUTOCONTINUE] = oautocont; + if (forked) + _exit(lastval); + return; + } + } + /* + * A function definition may have a list of additional + * redirections to apply, so retrieve it. + */ + if (shf->redir) { + struct estate s; + LinkList redir2; + + s.prog = shf->redir; + s.pc = shf->redir->prog; + s.strs = shf->redir->strs; + redir2 = ecgetredirs(&s); + if (!redir) + redir = redir2; + else { + while (nonempty(redir2)) + addlinknode(redir, ugetnode(redir2)); + } + } + } + + if (errflag) { + lastval = 1; + if (oautocont >= 0) + opts[AUTOCONTINUE] = oautocont; + if (forked) + _exit(lastval); + return; + } + + if ((type == WC_SIMPLE || type == WC_TYPESET) && !nullexec) { + char *s; + char trycd = (isset(AUTOCD) && isset(SHINSTDIN) && + (!redir || empty(redir)) && args && !empty(args) && + !nextnode(firstnode(args)) && *(char *)peekfirst(args)); + + DPUTS((!args || empty(args)), "BUG: empty(args) in exec.c"); + if (!hn) { + /* Resolve external commands */ + char *cmdarg = (char *) peekfirst(args); + char **checkpath = pathchecked; + int dohashcmd = isset(HASHCMDS); + + hn = cmdnamtab->getnode(cmdnamtab, cmdarg); + if (hn && trycd && !isreallycom((Cmdnam)hn)) { + if (!(((Cmdnam)hn)->node.flags & HASHED)) { + checkpath = path; + dohashcmd = 1; + } + cmdnamtab->removenode(cmdnamtab, cmdarg); + cmdnamtab->freenode(hn); + hn = NULL; + } + if (!hn && dohashcmd && strcmp(cmdarg, "..")) { + for (s = cmdarg; *s && *s != '/'; s++); + if (!*s) + hn = (HashNode) hashcmd(cmdarg, checkpath); + } + } + + /* If no command found yet, see if it * + * is a directory we should AUTOCD to. */ + if (!hn && trycd && (s = cancd(peekfirst(args)))) { + peekfirst(args) = (void *) s; + pushnode(args, dupstring("--")); + pushnode(args, dupstring("cd")); + if ((hn = builtintab->getnode(builtintab, "cd"))) + is_builtin = 1; + } + } + + /* This is nonzero if the command is a current shell procedure? */ + is_cursh = (is_builtin || is_shfunc || nullexec || type >= WC_CURSH); + + /************************************************************************** + * Do we need to fork? We need to fork if: * + * 1) The command is supposed to run in the background. This * + * case is now handled above (forked = 1 here). (or) * + * 2) There is no `exec' flag, and either: * + * a) This is a builtin or shell function with output piped somewhere. * + * b) This is an external command and we can't do a `fake exec'. * + * * + * A `fake exec' is possible if we have all the following conditions: * + * 1) last1 flag is 1. This indicates that the current shell will not * + * be needed after the current command. This is typically the case * + * when the command is the last stage in a subshell, or is the * + * last command after the option `-c'. * + * 2) We don't have any traps set. * + * 3) We don't have any files to delete. * + * * + * The condition above for a `fake exec' will also work for a current * + * shell command such as a builtin, but doesn't really buy us anything * + * (doesn't save us a process), since it is already running in the * + * current shell. * + **************************************************************************/ + + if (!forked) { + if (!do_exec && + (((is_builtin || is_shfunc) && output) || + (!is_cursh && (last1 != 1 || nsigtrapped || havefiles() || + fdtable_flocks)))) { + switch (execcmd_fork(state, how, type, varspc, &filelist, + text, oautocont, close_if_forked)) { + case -1: + goto fatal; + case 0: + break; + default: + return; + } + forked = 1; + } else if (is_cursh) { + /* This is a current shell procedure that didn't need to fork. * + * This includes current shell procedures that are being exec'ed, * + * as well as null execs. */ + jobtab[thisjob].stat |= STAT_CURSH; + if (!jobtab[thisjob].procs) + jobtab[thisjob].stat |= STAT_NOPRINT; + if (is_builtin) + jobtab[thisjob].stat |= STAT_BUILTIN; + } else { + /* This is an exec (real or fake) for an external command. * + * Note that any form of exec means that the subshell is fake * + * (but we may be in a subshell already). */ + is_exec = 1; + /* + * If we are in a subshell environment anyway, say we're forked, + * even if we're actually not forked because we know the + * subshell is exiting. This ensures SHLVL reflects the current + * shell, and also optimises out any save/restore we'd need to + * do if we were returning to the main shell. + */ + if (type == WC_SUBSH) + forked = 1; + } + } + + if ((esglob = !(cflags & BINF_NOGLOB)) && args && eparams->htok) { + LinkList oargs = args; + globlist(args, 0); + args = oargs; + } + if (errflag) { + lastval = 1; + goto err; + } + + /* Make a copy of stderr for xtrace output before redirecting */ + fflush(xtrerr); + if (isset(XTRACE) && xtrerr == stderr && + (type < WC_SUBSH || type == WC_TIMED)) { + if ((newxtrerr = fdopen(movefd(dup(fileno(stderr))), "w"))) { + xtrerr = newxtrerr; + fdtable[fileno(xtrerr)] = FDT_XTRACE; + } + } + + /* Add pipeline input/output to mnodes */ + if (input) + addfd(forked, save, mfds, 0, input, 0, NULL); + if (output) + addfd(forked, save, mfds, 1, output, 1, NULL); + + /* Do process substitutions */ + if (redir) + spawnpipes(redir, nullexec); + + /* Do io redirections */ + while (redir && nonempty(redir)) { + fn = (Redir) ugetnode(redir); + + DPUTS(fn->type == REDIR_HEREDOC || fn->type == REDIR_HEREDOCDASH, + "BUG: unexpanded here document"); + if (fn->type == REDIR_INPIPE) { + if (!checkclobberparam(fn) || fn->fd2 == -1) { + if (fn->fd2 != -1) + zclose(fn->fd2); + closemnodes(mfds); + fixfds(save); + execerr(); + } + addfd(forked, save, mfds, fn->fd1, fn->fd2, 0, fn->varid); + } else if (fn->type == REDIR_OUTPIPE) { + if (!checkclobberparam(fn) || fn->fd2 == -1) { + if (fn->fd2 != -1) + zclose(fn->fd2); + closemnodes(mfds); + fixfds(save); + execerr(); + } + addfd(forked, save, mfds, fn->fd1, fn->fd2, 1, fn->varid); + } else { + int closed; + if (fn->type != REDIR_HERESTR && xpandredir(fn, redir)) + continue; + if (errflag) { + closemnodes(mfds); + fixfds(save); + execerr(); + } + if (isset(RESTRICTED) && IS_WRITE_FILE(fn->type)) { + zwarn("writing redirection not allowed in restricted mode"); + execerr(); + } + if (unset(EXECOPT)) + continue; + switch(fn->type) { + case REDIR_HERESTR: + if (!checkclobberparam(fn)) + fil = -1; + else + fil = getherestr(fn); + if (fil == -1) { + if (errno && errno != EINTR) + zwarn("can't create temp file for here document: %e", + errno); + closemnodes(mfds); + fixfds(save); + execerr(); + } + addfd(forked, save, mfds, fn->fd1, fil, 0, fn->varid); + break; + case REDIR_READ: + case REDIR_READWRITE: + if (!checkclobberparam(fn)) + fil = -1; + else if (fn->type == REDIR_READ) + fil = open(unmeta(fn->name), O_RDONLY | O_NOCTTY); + else + fil = open(unmeta(fn->name), + O_RDWR | O_CREAT | O_NOCTTY, 0666); + if (fil == -1) { + closemnodes(mfds); + fixfds(save); + if (errno != EINTR) + zwarn("%e: %s", errno, fn->name); + execerr(); + } + addfd(forked, save, mfds, fn->fd1, fil, 0, fn->varid); + /* If this is 'exec < file', read from stdin, * + * not terminal, unless `file' is a terminal. */ + if (nullexec == 1 && fn->fd1 == 0 && + isset(SHINSTDIN) && interact && !zleactive) + init_io(NULL); + break; + case REDIR_CLOSE: + if (fn->varid) { + char *s = fn->varid, *t; + struct value vbuf; + Value v; + int bad = 0; + + if (!(v = getvalue(&vbuf, &s, 0))) { + bad = 1; + } else if (v->pm->node.flags & PM_READONLY) { + bad = 2; + } else { + s = getstrvalue(v); + if (errflag) + bad = 1; + else { + fn->fd1 = zstrtol(s, &t, 0); + if (s == t) + bad = 1; + else if (*t) { + /* Check for base#number format */ + if (*t == '#' && *s != '0') + fn->fd1 = zstrtol(s = t+1, &t, fn->fd1); + if (s == t || *t) + bad = 1; + } + if (!bad && fn->fd1 <= max_zsh_fd) { + if (fn->fd1 >= 10 && + (fdtable[fn->fd1] & FDT_TYPE_MASK) == + FDT_INTERNAL) + bad = 3; + } + } + } + if (bad) { + const char *bad_msg[] = { + "parameter %s does not contain a file descriptor", + "can't close file descriptor from readonly parameter %s", + "file descriptor %d used by shell, not closed" + }; + if (bad > 2) + zwarn(bad_msg[bad-1], fn->fd1); + else + zwarn(bad_msg[bad-1], fn->varid); + execerr(); + } + } + /* + * Note we may attempt to close an fd beyond max_zsh_fd: + * OK as long as we never look in fdtable for it. + */ + closed = 0; + if (!forked && fn->fd1 < 10 && save[fn->fd1] == -2) { + save[fn->fd1] = movefd(fn->fd1); + if (save[fn->fd1] >= 0) { + /* + * The original fd is now closed, we don't need + * to do it below. + */ + closed = 1; + } + } + if (fn->fd1 < 10) + closemn(mfds, fn->fd1, REDIR_CLOSE); + /* + * Only report failures to close file descriptors + * if they're under user control as we don't know + * what the previous status of others was. + */ + if (!closed && zclose(fn->fd1) < 0 && fn->varid) { + zwarn("failed to close file descriptor %d: %e", + fn->fd1, errno); + } + break; + case REDIR_MERGEIN: + case REDIR_MERGEOUT: + if (fn->fd2 < 10) + closemn(mfds, fn->fd2, fn->type); + if (!checkclobberparam(fn)) + fil = -1; + else if (fn->fd2 > 9 && + /* + * If the requested fd is > max_zsh_fd, + * the shell doesn't know about it. + * Just assume the user knows what they're + * doing. + */ + (fn->fd2 <= max_zsh_fd && + ((fdtable[fn->fd2] != FDT_UNUSED && + fdtable[fn->fd2] != FDT_EXTERNAL) || + fn->fd2 == coprocin || + fn->fd2 == coprocout))) { + fil = -1; + errno = EBADF; + } else { + int fd = fn->fd2; + if(fd == -2) + fd = (fn->type == REDIR_MERGEOUT) ? coprocout : coprocin; + fil = movefd(dup(fd)); + } + if (fil == -1) { + char fdstr[DIGBUFSIZE]; + + closemnodes(mfds); + fixfds(save); + if (fn->fd2 != -2) + sprintf(fdstr, "%d", fn->fd2); + if (errno) + zwarn("%s: %e", fn->fd2 == -2 ? "coprocess" : fdstr, + errno); + execerr(); + } + addfd(forked, save, mfds, fn->fd1, fil, + fn->type == REDIR_MERGEOUT, fn->varid); + break; + default: + if (!checkclobberparam(fn)) + fil = -1; + else if (IS_APPEND_REDIR(fn->type)) + fil = open(unmeta(fn->name), + ((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 + fil = clobber_open(fn); + if(fil != -1 && IS_ERROR_REDIR(fn->type)) + dfil = movefd(dup(fil)); + else + dfil = 0; + if (fil == -1 || dfil == -1) { + if(fil != -1) + close(fil); + closemnodes(mfds); + fixfds(save); + if (errno && errno != EINTR) + zwarn("%e: %s", errno, fn->name); + execerr(); + } + addfd(forked, save, mfds, fn->fd1, fil, 1, fn->varid); + if(IS_ERROR_REDIR(fn->type)) + addfd(forked, save, mfds, 2, dfil, 1, NULL); + break; + } + /* May be error in addfd due to setting parameter. */ + if (errflag) { + closemnodes(mfds); + fixfds(save); + execerr(); + } + } + } + + /* We are done with redirection. close the mnodes, * + * spawning tee/cat processes as necessary. */ + for (i = 0; i < 10; i++) + if (mfds[i] && mfds[i]->ct >= 2) + closemn(mfds, i, REDIR_CLOSE); + + if (nullexec) { + /* + * If nullexec is 2, we have variables to add with the redirections + * in place. If nullexec is 1, we may have variables but they + * need the standard restore logic. + */ + if (varspc) { + LinkList restorelist = 0, removelist = 0; + if (!isset(POSIXBUILTINS) && nullexec != 2) + save_params(state, varspc, &restorelist, &removelist); + addvars(state, varspc, 0); + if (restorelist) + restore_params(restorelist, removelist); + } + lastval = errflag ? errflag : cmdoutval; + if (nullexec == 1) { + /* + * If nullexec is 1 we specifically *don't* restore the original + * fd's before returning. + */ + for (i = 0; i < 10; i++) + if (save[i] != -2) + zclose(save[i]); + goto done; + } + if (isset(XTRACE)) { + fputc('\n', xtrerr); + 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 + * exit) in case there is an error return. + */ + if (is_exec) { + int flags = ((how & Z_ASYNC) ? ESUB_ASYNC : 0) | + ESUB_PGRP | ESUB_FAKE; + if (type != WC_SUBSH) + flags |= ESUB_KEEPTRAP; + if ((do_exec || (type >= WC_CURSH && last1 == 1)) + && !forked) + flags |= ESUB_REVERTPGRP; + entersubsh(flags); + } + if (type == WC_FUNCDEF) { + Eprog redir_prog; + if (!redir && wc_code(*eparams->beg) == WC_REDIR) { + /* + * We're not using a redirection from the currently + * parsed environment, which is what we'd do for an + * anonymous function, but there are redirections we + * should store with the new function. + */ + struct estate s; + + s.prog = state->prog; + s.pc = eparams->beg; + s.strs = state->prog->strs; + + /* + * The copy uses the wordcode parsing area, so save and + * restore state. + */ + zcontext_save(); + redir_prog = eccopyredirs(&s); + zcontext_restore(); + } 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. + * So we execuate a simplified function here. + */ + 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; + int do_save = 0; + /* builtin or shell function */ + + if (!forked) { + if (isset(POSIXBUILTINS)) { + /* + * If it's a function or special builtin --- save + * if it's got "command" in front. + * If it's a normal command --- save. + */ + if (is_shfunc || (hn->flags & (BINF_PSPECIAL|BINF_ASSIGN))) + do_save = (orig_cflags & BINF_COMMAND); + else + do_save = 1; + } else { + /* + * Save if it's got "command" in front or it's + * not a magic-equals assignment. + */ + if ((cflags & (BINF_COMMAND|BINF_ASSIGN)) || !magic_assign) + do_save = 1; + } + if (do_save && varspc) + save_params(state, varspc, &restorelist, &removelist); + } + if (varspc) { + /* Export this if the command is a shell function, + * but not if it's a builtin. + */ + int flags = 0; + if (is_shfunc) + flags |= ADDVAR_EXPORT; + if (restorelist) + flags |= ADDVAR_RESTORE; + + addvars(state, varspc, flags); + if (errflag) { + if (restorelist) + restore_params(restorelist, removelist); + lastval = 1; + fixfds(save); + goto done; + } + } + + if (is_shfunc) { + /* It's a shell function */ + pipecleanfilelist(filelist, 0); + execshfunc((Shfunc) hn, args); + } else { + /* It's a builtin */ + LinkList assigns = (LinkList)0; + int postassigns = eparams->postassigns; + if (forked) + closem(FDT_INTERNAL, 0); + if (postassigns) { + Wordcode opc = state->pc; + state->pc = eparams->assignspc; + assigns = newlinklist(); + while (postassigns--) { + int htok; + 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, NULL); + 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->flags = 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, NULL); + 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->flags = 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, + NULL); + 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->flags = ASG_ARRAY; + asg->value.array = + ecgetlist(state, WC_ASSIGN_NUM(ac), + EC_DUPTOK, &htok); + if (asg->value.array) + { + if (!errflag) { + int prefork_ret = 0; + prefork(asg->value.array, PREFORK_ASSIGN, + &prefork_ret); + if (errflag) { + state->pc = opc; + break; + } + if (prefork_ret & PREFORK_KEY_VALUE) + asg->flags |= ASG_KEY_VALUE; + globlist(asg->value.array, prefork_ret); + } + if (errflag) { + state->pc = opc; + break; + } + } + } + + uaddlinknode(assigns, &asg->node); + } + state->pc = opc; + } + dont_queue_signals(); + if (!errflag) { + int ret = execbuiltin(args, assigns, (Builtin) hn); + /* + * In case of interruption assume builtin status + * is less useful than what interrupt set. + */ + if (!(errflag & ERRFLAG_INT)) + lastval = ret; + } + if (do_save & BINF_COMMAND) + errflag &= ~ERRFLAG_ERROR; + restore_queue_signals(q); + fflush(stdout); + if (save[1] == -2) { + if (ferror(stdout)) { + zwarn("write error: %e", errno); + clearerr(stdout); + } + } else + clearerr(stdout); + } + if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) && + lastval && !subsh) { +#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) + fprintf(stderr, "zsh: exit %lld\n", lastval); +#else + fprintf(stderr, "zsh: exit %ld\n", (long)lastval); +#endif + fflush(stderr); + } + + if (do_exec) { + if (subsh) + _exit(lastval); + + /* If we are exec'ing a command, and we are not in a subshell, * + * then check if we should save the history file. */ + if (isset(RCS) && interact && !nohistsave) + savehistfile(NULL, 1, HFILE_USE_OPTIONS); + exit(lastval); + } + if (restorelist) + restore_params(restorelist, removelist); + + } else { + if (!subsh) { + /* for either implicit or explicit "exec", decrease $SHLVL + * as we're now done as a shell */ + if (!forked) + setiparam("SHLVL", --shlvl); + + /* If we are exec'ing a command, and we are not * + * in a subshell, then save the history file. */ + if (do_exec && isset(RCS) && interact && !nohistsave) + savehistfile(NULL, 1, HFILE_USE_OPTIONS); + } + if (type == WC_SIMPLE || type == WC_TYPESET) { + if (varspc) { + int addflags = ADDVAR_EXPORT|ADDVAR_RESTRICT; + if (forked) + addflags |= ADDVAR_RESTORE; + addvars(state, varspc, addflags); + if (errflag) + _exit(1); + } + closem(FDT_INTERNAL, 0); + if (coprocin != -1) { + zclose(coprocin); + coprocin = -1; + } + if (coprocout != -1) { + zclose(coprocout); + coprocout = -1; + } +#ifdef HAVE_GETRLIMIT + if (!forked) + setlimits(NULL); +#endif + if (how & Z_ASYNC) { + zsfree(STTYval); + STTYval = 0; + } + execute(args, cflags, use_defpath); + } else { /* ( ... ) */ + DPUTS(varspc, + "BUG: assignment before complex command"); + list_pipe = 0; + 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."); + /* Skip word only used for try/always blocks */ + state->pc++; + execlist(state, 0, 1); + } + } + } + + err: + if (forked) { + /* + * So what's going on here then? Well, I'm glad you asked. + * + * If we create multios for use in a subshell we do + * this after forking, in this function above. That + * means that the current (sub)process is responsible + * for clearing them up. However, the processes won't + * go away until we have closed the fd's talking to them. + * Since we're about to exit the shell there's nothing + * to stop us closing all fd's (including the ones 0 to 9 + * that we usually leave alone). + * + * Then we wait for any processes. When we forked, + * we cleared the jobtable and started a new job just for + * any oddments like this, so if there aren't any we won't + * need to wait. The result of not waiting is that + * the multios haven't flushed the fd's properly, leading + * to obscure missing data. + * + * It would probably be cleaner to ensure that the + * parent shell handled multios, but that requires + * some architectural changes which are likely to be + * hairy. + */ + for (i = 0; i < 10; i++) + if (fdtable[i] != FDT_UNUSED) + close(i); + closem(FDT_UNUSED, 1); + if (thisjob != -1) + waitjobs(); + _exit(lastval); + } + fixfds(save); + + done: + if (isset(POSIXBUILTINS) && + (cflags & (BINF_PSPECIAL|BINF_EXEC)) && + !(orig_cflags & BINF_COMMAND)) { + /* + * For POSIX-compatible behaviour with special + * builtins (including exec which we don't usually + * classify as a builtin) we treat all errors as fatal. + * The "command" builtin is not special so resets this behaviour. + */ + forked |= zsh_subshell; + fatal: + if (redir_err || errflag) { + if (!isset(INTERACTIVE)) { + if (forked) + _exit(1); + else + exit(1); + } + errflag |= ERRFLAG_ERROR; + } + } + if (newxtrerr) { + fil = fileno(newxtrerr); + fclose(newxtrerr); + xtrerr = oxtrerr; + zclose(fil); + } + + zsfree(STTYval); + STTYval = 0; + if (oautocont >= 0) + opts[AUTOCONTINUE] = oautocont; +} + +/* Arrange to have variables restored. */ + +/**/ +static void +save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p) +{ + Param pm; + char *s; + wordcode ac; + + *restore_p = newlinklist(); + *remove_p = newlinklist(); + + while (wc_code(ac = *pc) == WC_ASSIGN) { + s = ecrawstr(state->prog, pc + 1, NULL); + if ((pm = (Param) paramtab->getnode(paramtab, s))) { + Param tpm; + if (pm->env) + delenv(pm); + if (!(pm->node.flags & PM_SPECIAL)) { + /* + * We used to remove ordinary parameters from the + * table, but that meant "HELLO=$HELLO shellfunc" + * failed because the expansion of $HELLO hasn't + * been done at this point. Instead, copy the + * parameter: in this case, we'll insert the + * copied parameter straight back into the parameter + * table so we want to be sure everything is + * properly set up and in permanent memory. + */ + tpm = (Param) zshcalloc(sizeof *tpm); + tpm->node.nam = ztrdup(pm->node.nam); + copyparam(tpm, pm, 0); + pm = tpm; + } else if (!(pm->node.flags & PM_READONLY) && + (unset(RESTRICTED) || !(pm->node.flags & PM_RESTRICTED))) { + /* + * In this case we're just saving parts of + * the parameter in a tempory, so use heap allocation + * and don't bother copying every detail. + */ + tpm = (Param) hcalloc(sizeof *tpm); + tpm->node.nam = pm->node.nam; + copyparam(tpm, pm, 1); + pm = tpm; + } + addlinknode(*remove_p, dupstring(s)); + addlinknode(*restore_p, pm); + } else + addlinknode(*remove_p, dupstring(s)); + + pc += (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR ? + 3 : WC_ASSIGN_NUM(ac) + 2); + } +} + +/* Restore saved parameters after executing a shfunc or builtin */ + +/**/ +static void +restore_params(LinkList restorelist, LinkList removelist) +{ + Param pm; + char *s; + + /* remove temporary parameters */ + while ((s = (char *) ugetnode(removelist))) { + if ((pm = (Param) paramtab->getnode(paramtab, s)) && + !(pm->node.flags & PM_SPECIAL)) { + pm->node.flags &= ~PM_READONLY; + unsetparam_pm(pm, 0, 0); + } + } + + if (restorelist) { + /* restore saved parameters */ + while ((pm = (Param) ugetnode(restorelist))) { + if (pm->node.flags & PM_SPECIAL) { + Param tpm = (Param) paramtab->getnode(paramtab, pm->node.nam); + + DPUTS(!tpm || PM_TYPE(pm->node.flags) != PM_TYPE(tpm->node.flags) || + !(pm->node.flags & PM_SPECIAL), + "BUG: in restoring special parameters"); + if (!pm->env && tpm->env) + delenv(tpm); + tpm->node.flags = pm->node.flags; + switch (PM_TYPE(pm->node.flags)) { + case PM_SCALAR: + tpm->gsu.s->setfn(tpm, pm->u.str); + break; + case PM_INTEGER: + tpm->gsu.i->setfn(tpm, pm->u.val); + break; + case PM_EFLOAT: + case PM_FFLOAT: + tpm->gsu.f->setfn(tpm, pm->u.dval); + break; + case PM_ARRAY: + tpm->gsu.a->setfn(tpm, pm->u.arr); + break; + case PM_HASHED: + tpm->gsu.h->setfn(tpm, pm->u.hash); + break; + } + pm = tpm; + } else { + paramtab->addnode(paramtab, pm->node.nam, pm); + } + if ((pm->node.flags & PM_EXPORTED) && ((s = getsparam(pm->node.nam)))) + addenv(pm, s); + } + } +} + +/* restore fds after redirecting a builtin */ + +/**/ +static void +fixfds(int *save) +{ + int old_errno = errno; + int i; + + for (i = 0; i != 10; i++) + if (save[i] != -2) + redup(save[i], i); + errno = old_errno; +} + +/* + * Close internal shell fds. + * + * Close any that are marked as used if "how" is FDT_UNUSED, else + * close any with the value "how". + * + * If "all" is zero, we'll skip cases where we need the file + * descriptor to be visible externally. + */ + +/**/ +mod_export void +closem(int how, int all) +{ + int i; + + for (i = 10; i <= max_zsh_fd; i++) + if (fdtable[i] != FDT_UNUSED && + /* + * Process substitution needs to be visible to user; + * fd's are explicitly cleaned up by filelist handling. + */ + (all || fdtable[i] != FDT_PROC_SUBST) && + (how == FDT_UNUSED || (fdtable[i] & FDT_TYPE_MASK) == how)) { + if (i == SHTTY) + SHTTY = -1; + zclose(i); + } +} + +/* convert here document into a here string */ + +/**/ +char * +gethere(char **strp, int typ) +{ + char *buf; + int bsiz, qt = 0, strip = 0; + char *s, *t, *bptr, c; + char *str = *strp; + + for (s = str; *s; s++) + if (inull(*s)) { + qt = 1; + break; + } + str = quotesubst(str); + untokenize(str); + if (typ == REDIR_HEREDOCDASH) { + strip = 1; + while (*str == '\t') + str++; + } + *strp = str; + bptr = buf = zalloc(bsiz = 256); + for (;;) { + t = bptr; + + while ((c = hgetc()) == '\t' && strip) + ; + for (;;) { + if (bptr >= buf + bsiz - 2) { + ptrdiff_t toff = t - buf; + ptrdiff_t bptroff = bptr - buf; + char *newbuf = realloc(buf, 2 * bsiz); + if (!newbuf) { + /* out of memory */ + zfree(buf, bsiz); + return NULL; + } + buf = newbuf; + t = buf + toff; + bptr = buf + bptroff; + bsiz *= 2; + } + if (lexstop || c == '\n') + break; + if (!qt && c == '\\') { + *bptr++ = c; + c = hgetc(); + if (c == '\n') { + bptr--; + c = hgetc(); + continue; + } + } + *bptr++ = c; + c = hgetc(); + } + *bptr = '\0'; + if (!strcmp(t, str)) + break; + if (lexstop) { + t = bptr; + break; + } + *bptr++ = '\n'; + } + *t = '\0'; + s = buf; + buf = dupstring(buf); + zfree(s, bsiz); + if (!qt) { + int ef = errflag; + + parsestr(&buf); + + if (!(errflag & ERRFLAG_ERROR)) { + /* Retain any user interrupt error */ + errflag = ef | (errflag & ERRFLAG_INT); + } + } + return buf; +} + +/* open here string fd */ + +/**/ +static int +getherestr(struct redir *fn) +{ + char *s, *t; + int fd, len; + + t = fn->name; + singsub(&t); + untokenize(t); + unmetafy(t, &len); + /* + * For real here-strings we append a newline, as if the + * string given was a complete command line. + * + * For here-strings from here documents, we use the original + * text exactly. + */ + if (!(fn->flags & REDIRF_FROM_HEREDOC)) + t[len++] = '\n'; + if ((fd = gettempfile(NULL, 1, &s)) < 0) + return -1; + write_loop(fd, t, len); + close(fd); + fd = open(s, O_RDONLY | O_NOCTTY); + unlink(s); + return fd; +} + +/* + * Test if some wordcode starts with a simple redirection of type + * redir_type. If it does, return the name of the file, copied onto + * the heap. If it doesn't, return NULL. + */ + +static char * +simple_redir_name(Eprog prog, int redir_type) +{ + Wordcode pc; + + pc = prog->prog; + if (prog != &dummy_eprog && + wc_code(pc[0]) == WC_LIST && (WC_LIST_TYPE(pc[0]) & Z_END) && + wc_code(pc[1]) == WC_SUBLIST && !WC_SUBLIST_FLAGS(pc[1]) && + WC_SUBLIST_TYPE(pc[1]) == WC_SUBLIST_END && + wc_code(pc[2]) == WC_PIPE && WC_PIPE_TYPE(pc[2]) == WC_PIPE_END && + wc_code(pc[3]) == WC_REDIR && WC_REDIR_TYPE(pc[3]) == redir_type && + !WC_REDIR_VARID(pc[3]) && + !pc[4] && + wc_code(pc[6]) == WC_SIMPLE && !WC_SIMPLE_ARGC(pc[6])) { + return dupstring(ecrawstr(prog, pc + 5, NULL)); + } + + return NULL; +} + +/* $(...) */ + +/**/ +LinkList +getoutput(char *cmd, int qt) +{ + Eprog prog; + int pipes[2]; + pid_t pid; + char *s; + + int onc = nocomments; + nocomments = (interact && unset(INTERACTIVECOMMENTS)); + prog = parse_string(cmd, 0); + nocomments = onc; + + if (!prog) + return NULL; + + if ((s = simple_redir_name(prog, REDIR_READ))) { + /* $(< word) */ + int stream; + LinkList retval; + int readerror; + + singsub(&s); + if (errflag) + return NULL; + untokenize(s); + if ((stream = open(unmeta(s), O_RDONLY | O_NOCTTY)) == -1) { + zwarn("%e: %s", errno, s); + lastval = cmdoutval = 1; + return newlinklist(); + } + retval = readoutput(stream, qt, &readerror); + if (readerror) { + zwarn("error when reading %s: %e", s, readerror); + lastval = cmdoutval = 1; + } + return retval; + } + if (mpipe(pipes) < 0) { + errflag |= ERRFLAG_ERROR; + cmdoutpid = 0; + return NULL; + } + child_block(); + cmdoutval = 0; + if ((cmdoutpid = pid = zfork(NULL)) == -1) { + /* fork error */ + zclose(pipes[0]); + zclose(pipes[1]); + errflag |= ERRFLAG_ERROR; + cmdoutpid = 0; + child_unblock(); + return NULL; + } else if (pid) { + LinkList retval; + + zclose(pipes[1]); + retval = readoutput(pipes[0], qt, NULL); + fdtable[pipes[0]] = FDT_UNUSED; + waitforpid(pid, 0); /* unblocks */ + lastval = cmdoutval; + return retval; + } + /* pid == 0 */ + child_unblock(); + zclose(pipes[0]); + redup(pipes[1], 1); + entersubsh(ESUB_PGRP|ESUB_NOMONITOR); + cmdpush(CS_CMDSUBST); + execode(prog, 0, 1, "cmdsubst"); + cmdpop(); + close(1); + _exit(lastval); + zerr("exit returned in child!!"); + kill(getpid(), SIGKILL); + return NULL; +} + +/* read output of command substitution */ + +/**/ +mod_export LinkList +readoutput(int in, int qt, int *readerror) +{ + LinkList ret; + char *buf, *ptr; + int bsiz, c, cnt = 0; + FILE *fin; + int q = queue_signal_level(); + + fin = fdopen(in, "r"); + ret = newlinklist(); + ptr = buf = (char *) hcalloc(bsiz = 64); + /* + * We need to be sensitive to SIGCHLD else we can be + * stuck forever with important processes unreaped. + * The case that triggered this was where the exiting + * process is group leader of the foreground process and we need + * to reclaim the terminal else ^C doesn't work. + */ + dont_queue_signals(); + child_unblock(); + while ((c = fgetc(fin)) != EOF || errno == EINTR) { + if (c == EOF) { + errno = 0; + clearerr(fin); + continue; + } + if (imeta(c)) { + *ptr++ = Meta; + c ^= 32; + cnt++; + } + if (++cnt >= bsiz) { + char *pp; + queue_signals(); + pp = (char *) hcalloc(bsiz *= 2); + dont_queue_signals(); + + memcpy(pp, buf, cnt - 1); + ptr = (buf = pp) + cnt - 1; + } + *ptr++ = c; + } + child_block(); + restore_queue_signals(q); + if (readerror) + *readerror = ferror(fin) ? errno : 0; + fclose(fin); + while (cnt && ptr[-1] == '\n') + ptr--, cnt--; + *ptr = '\0'; + if (qt) { + if (!cnt) { + *ptr++ = Nularg; + *ptr = '\0'; + } + addlinknode(ret, buf); + } else { + char **words = spacesplit(buf, 0, 1, 0); + + while (*words) { + if (isset(GLOBSUBST)) + shtokenize(*words); + addlinknode(ret, *words++); + } + } + return ret; +} + +/**/ +static Eprog +parsecmd(char *cmd, char **eptr) +{ + char *str; + Eprog prog; + + for (str = cmd + 2; *str && *str != Outpar; str++); + if (!*str || cmd[1] != Inpar) { + /* + * This can happen if the expression is being parsed + * inside another construct, e.g. as a value within ${..:..} etc. + * So print a proper error message instead of the not very + * useful but traditional "oops". + */ + char *errstr = dupstrpfx(cmd, 2); + untokenize(errstr); + zerr("unterminated `%s...)'", errstr); + return NULL; + } + *str = '\0'; + if (eptr) + *eptr = str+1; + if (!(prog = parse_string(cmd + 2, 0))) { + zerr("parse error in process substitution"); + return NULL; + } + return prog; +} + +/* =(...) */ + +/**/ +char * +getoutputfile(char *cmd, char **eptr) +{ + pid_t pid; + char *nam; + Eprog prog; + int fd; + char *s; + + if (thisjob == -1){ + zerr("process substitution %s cannot be used here", cmd); + return NULL; + } + if (!(prog = parsecmd(cmd, eptr))) + return NULL; + if (!(nam = gettempname(NULL, 1))) + return NULL; + + if ((s = simple_redir_name(prog, REDIR_HERESTR))) { + /* + * =(<<(...) */ + +/**/ +char * +getproc(char *cmd, char **eptr) +{ +#if !defined(HAVE_FIFOS) && !defined(PATH_DEV_FD) + zerr("doesn't look like your system supports FIFOs."); + return NULL; +#else + Eprog prog; + int out = *cmd == Inang; + char *pnam; + pid_t pid; + struct timeval bgtime; + +#ifndef PATH_DEV_FD + int fd; + if (thisjob == -1) { + zerr("process substitution %s cannot be used here", cmd); + return NULL; + } + if (!(pnam = namedpipe())) + return NULL; + if (!(prog = parsecmd(cmd, eptr))) + return NULL; + addfilelist(pnam, 0); + + if ((pid = zfork(&bgtime))) { + if (pid == -1) + return NULL; + if (!out) + addproc(pid, NULL, 1, &bgtime); + procsubstpid = pid; + return pnam; + } + closem(FDT_UNUSED, 0); + fd = open(pnam, out ? O_WRONLY | O_NOCTTY : O_RDONLY | O_NOCTTY); + if (fd == -1) { + zerr("can't open %s: %e", pnam, errno); + _exit(1); + } + entersubsh(ESUB_ASYNC|ESUB_PGRP); + redup(fd, out); +#else /* PATH_DEV_FD */ + int pipes[2], fd; + + if (thisjob == -1) { + zerr("process substitution %s cannot be used here", cmd); + return NULL; + } + pnam = zhalloc(strlen(PATH_DEV_FD) + 1 + DIGBUFSIZE); + if (!(prog = parsecmd(cmd, eptr))) + return NULL; + if (mpipe(pipes) < 0) + return NULL; + if ((pid = zfork(&bgtime))) { + sprintf(pnam, "%s/%d", PATH_DEV_FD, pipes[!out]); + zclose(pipes[out]); + if (pid == -1) + { + zclose(pipes[!out]); + return NULL; + } + fd = pipes[!out]; + fdtable[fd] = FDT_PROC_SUBST; + addfilelist(NULL, fd); + if (!out) + { + addproc(pid, NULL, 1, &bgtime); + } + procsubstpid = pid; + return pnam; + } + entersubsh(ESUB_ASYNC|ESUB_PGRP); + redup(pipes[out], out); + closem(FDT_UNUSED, 0); /* this closes pipes[!out] as well */ +#endif /* PATH_DEV_FD */ + + cmdpush(CS_CMDSUBST); + execode(prog, 0, 1, out ? "outsubst" : "insubst"); + cmdpop(); + zclose(out); + _exit(lastval); + return NULL; +#endif /* HAVE_FIFOS and PATH_DEV_FD not defined */ +} + +/* + * > >(...) or < <(...) (does not use named pipes) + * + * If the second argument is 1, this is part of + * an "exec < <(...)" or "exec > >(...)" and we shouldn't + * wait for the job to finish before continuing. + */ + +/**/ +static int +getpipe(char *cmd, int nullexec) +{ + Eprog prog; + int pipes[2], out = *cmd == Inang; + pid_t pid; + struct timeval bgtime; + char *ends; + + if (!(prog = parsecmd(cmd, &ends))) + return -1; + if (*ends) { + zerr("invalid syntax for process substitution in redirection"); + return -1; + } + if (mpipe(pipes) < 0) + return -1; + if ((pid = zfork(&bgtime))) { + zclose(pipes[out]); + if (pid == -1) { + zclose(pipes[!out]); + return -1; + } + if (!nullexec) + addproc(pid, NULL, 1, &bgtime); + procsubstpid = pid; + return pipes[!out]; + } + entersubsh(ESUB_PGRP); + redup(pipes[out], out); + closem(FDT_UNUSED, 0); /* this closes pipes[!out] as well */ + cmdpush(CS_CMDSUBST); + execode(prog, 0, 1, out ? "outsubst" : "insubst"); + cmdpop(); + _exit(lastval); + return 0; +} + +/* open pipes with fds >= 10 */ + +/**/ +static int +mpipe(int *pp) +{ + if (pipe(pp) < 0) { + zerr("pipe failed: %e", errno); + return -1; + } + pp[0] = movefd(pp[0]); + pp[1] = movefd(pp[1]); + return 0; +} + +/* + * Do process substitution with redirection + * + * If the second argument is 1, this is part of + * an "exec < <(...)" or "exec > >(...)" and we shouldn't + * wait for the job to finish before continuing. + * Likewise, we shouldn't wait if we are opening the file + * descriptor using the {fd}>>(...) notation since it stays + * valid for subsequent commands. + */ + +/**/ +static void +spawnpipes(LinkList l, int nullexec) +{ + LinkNode n; + Redir f; + char *str; + + n = firstnode(l); + for (; n; incnode(n)) { + f = (Redir) getdata(n); + if (f->type == REDIR_OUTPIPE || f->type == REDIR_INPIPE) { + str = f->name; + f->fd2 = getpipe(str, nullexec || f->varid); + } + } +} + +/* evaluate a [[ ... ]] */ + +/**/ +static int +execcond(Estate state, UNUSED(int do_exec)) +{ + int stat; + + state->pc--; + if (isset(XTRACE)) { + printprompt4(); + fprintf(xtrerr, "[["); + tracingcond++; + } + cmdpush(CS_COND); + stat = evalcond(state, NULL); + /* + * 2 indicates a syntax error. For compatibility, turn this + * into a shell error. + */ + if (stat == 2) + errflag |= ERRFLAG_ERROR; + cmdpop(); + if (isset(XTRACE)) { + fprintf(xtrerr, " ]]\n"); + fflush(xtrerr); + tracingcond--; + } + return stat; +} + +/* evaluate a ((...)) arithmetic command */ + +/**/ +static int +execarith(Estate state, UNUSED(int do_exec)) +{ + char *e; + mnumber val = zero_mnumber; + int htok = 0; + + if (isset(XTRACE)) { + printprompt4(); + fprintf(xtrerr, "(("); + } + cmdpush(CS_MATH); + e = ecgetstr(state, EC_DUPTOK, &htok); + if (htok) + singsub(&e); + if (isset(XTRACE)) + fprintf(xtrerr, " %s", e); + + val = matheval(e); + + cmdpop(); + + if (isset(XTRACE)) { + fprintf(xtrerr, " ))\n"); + fflush(xtrerr); + } + if (errflag) { + errflag &= ~ERRFLAG_ERROR; + return 2; + } + /* should test for fabs(val.u.d) < epsilon? */ + return (val.type == MN_INTEGER) ? val.u.l == 0 : val.u.d == 0.0; +} + +/* perform time ... command */ + +/**/ +static int +exectime(Estate state, UNUSED(int do_exec)) +{ + int jb; + + jb = thisjob; + if (WC_TIMED_TYPE(state->pc[-1]) == WC_TIMED_EMPTY) { + shelltime(); + return 0; + } + execpline(state, *state->pc++, Z_TIMED|Z_SYNC, 0); + thisjob = jb; + return lastval; +} + +/* Define a shell function */ + +static const char *const ANONYMOUS_FUNCTION_NAME = "(anon)"; + +/**/ +static int +execfuncdef(Estate state, Eprog redir_prog) +{ + Shfunc shf; + char *s = NULL; + int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 0, ret = 0; + int anon_func = 0; + Wordcode beg = state->pc, end; + Eprog prog; + Patprog *pp; + LinkList names; + + end = beg + WC_FUNCDEF_SKIP(state->pc[-1]); + names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok); + nprg = end - beg; + sbeg = *state->pc++; + nstrs = *state->pc++; + npats = *state->pc++; + + nprg = (end - state->pc); + plen = nprg * sizeof(wordcode); + len = plen + (npats * sizeof(Patprog)) + nstrs; + + if (htok && names) { + execsubst(names); + if (errflag) { + state->pc = end; + return 1; + } + } + + DPUTS(!names && redir_prog, + "Passing redirection to anon function definition."); + while (!names || (s = (char *) ugetnode(names))) { + if (!names) { + prog = (Eprog) zhalloc(sizeof(*prog)); + prog->nref = -1; /* on the heap */ + } else { + prog = (Eprog) zalloc(sizeof(*prog)); + prog->nref = 1; /* allocated from permanent storage */ + } + prog->npats = npats; + prog->len = len; + if (state->prog->dump || !names) { + if (!names) { + prog->flags = EF_HEAP; + prog->dump = NULL; + prog->pats = pp = (Patprog *) zhalloc(npats * sizeof(Patprog)); + } else { + prog->flags = EF_MAP; + incrdumpcount(state->prog->dump); + prog->dump = state->prog->dump; + prog->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog)); + } + prog->prog = state->pc; + prog->strs = state->strs + sbeg; + } else { + prog->flags = EF_REAL; + prog->pats = pp = (Patprog *) zalloc(len); + prog->prog = (Wordcode) (prog->pats + npats); + prog->strs = (char *) (prog->prog + nprg); + prog->dump = NULL; + memcpy(prog->prog, state->pc, plen); + memcpy(prog->strs, state->strs + sbeg, nstrs); + } + for (i = npats; i--; pp++) + *pp = dummy_patprog1; + prog->shf = NULL; + + shf = (Shfunc) zalloc(sizeof(*shf)); + shf->funcdef = prog; + shf->node.flags = 0; + /* No dircache here, not a directory */ + shf->filename = ztrdup(scriptfilename); + shf->lineno = + (funcstack && (funcstack->tp == FS_FUNC || + funcstack->tp == FS_EVAL)) ? + funcstack->flineno + lineno : + lineno; + /* + * redir_prog is permanently allocated --- but if + * this function has multiple names we need an additional + * one. Original redir_prog used with the last name + * because earlier functions are freed in case of duplicate + * names. + */ + if (names && nonempty(names) && redir_prog) + shf->redir = dupeprog(redir_prog, 0); + else { + shf->redir = redir_prog; + redir_prog = 0; + } + shfunc_set_sticky(shf); + + if (!names) { + /* + * Anonymous function, execute immediately. + * Function name is "(anon)". + */ + LinkList args; + + anon_func = 1; + shf->node.flags |= PM_ANONYMOUS; + + state->pc = end; + end += *state->pc++; + args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok); + + if (htok && args) { + execsubst(args); + if (errflag) { + freeeprog(shf->funcdef); + if (shf->redir) /* shouldn't be */ + freeeprog(shf->redir); + dircache_set(&shf->filename, NULL); + zfree(shf, sizeof(*shf)); + state->pc = end; + return 1; + } + } + + setunderscore((args && nonempty(args)) ? + ((char *) getdata(lastnode(args))) : ""); + + if (!args) + args = newlinklist(); + shf->node.nam = (char *) ANONYMOUS_FUNCTION_NAME; + pushnode(args, shf->node.nam); + + execshfunc(shf, args); + ret = lastval; + + if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) && + lastval) { +#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) + fprintf(stderr, "zsh: exit %lld\n", lastval); +#else + fprintf(stderr, "zsh: exit %ld\n", (long)lastval); +#endif + fflush(stderr); + } + + freeeprog(shf->funcdef); + if (shf->redir) /* shouldn't be */ + freeeprog(shf->redir); + dircache_set(&shf->filename, NULL); + zfree(shf, sizeof(*shf)); + break; + } else { + /* is this shell function a signal trap? */ + if (!strncmp(s, "TRAP", 4) && + (signum = getsignum(s + 4)) != -1) { + if (settrap(signum, NULL, ZSIG_FUNC)) { + freeeprog(shf->funcdef); + dircache_set(&shf->filename, NULL); + zfree(shf, sizeof(*shf)); + state->pc = end; + return 1; + } + + /* + * Remove the old node explicitly in case it has + * an alternative name + */ + removetrapnode(signum); + } + shfunctab->addnode(shfunctab, ztrdup(s), shf); + } + } + if (!anon_func) + setunderscore(""); + if (redir_prog) { + /* For completeness, shouldn't happen */ + freeeprog(redir_prog); + } + state->pc = end; + return ret; +} + +/* Duplicate a sticky emulation */ + +/**/ + +mod_export Emulation_options +sticky_emulation_dup(Emulation_options src, int useheap) +{ + Emulation_options newsticky = useheap ? + hcalloc(sizeof(*src)) : zshcalloc(sizeof(*src)); + newsticky->emulation = src->emulation; + if (src->n_on_opts) { + size_t sz = src->n_on_opts * sizeof(*src->on_opts); + newsticky->n_on_opts = src->n_on_opts; + newsticky->on_opts = useheap ? zhalloc(sz) : zalloc(sz); + memcpy(newsticky->on_opts, src->on_opts, sz); + } + if (src->n_off_opts) { + size_t sz = src->n_off_opts * sizeof(*src->off_opts); + newsticky->n_off_opts = src->n_off_opts; + newsticky->off_opts = useheap ? zhalloc(sz) : zalloc(sz); + memcpy(newsticky->off_opts, src->off_opts, sz); + } + + return newsticky; +} + +/* Set the sticky emulation attributes for a shell function */ + +/**/ + +mod_export void +shfunc_set_sticky(Shfunc shf) +{ + if (sticky) + shf->sticky = sticky_emulation_dup(sticky, 0); + else + shf->sticky = NULL; +} + + +/* Main entry point to execute a shell function. */ + +/**/ +static void +execshfunc(Shfunc shf, LinkList args) +{ + LinkList last_file_list = NULL; + unsigned char *ocs; + int ocsp, osfc; + + if (errflag) + return; + + /* thisjob may be invalid if we're called via execsimple: see execcursh */ + if (!list_pipe && thisjob != -1 && thisjob != list_pipe_job && + !hasprocs(thisjob)) { + /* Without this deletejob the process table * + * would be filled by a recursive function. */ + last_file_list = jobtab[thisjob].filelist; + jobtab[thisjob].filelist = NULL; + deletejob(jobtab + thisjob, 0); + } + + if (isset(XTRACE)) { + LinkNode lptr; + printprompt4(); + if (args) + for (lptr = firstnode(args); lptr; incnode(lptr)) { + if (lptr != firstnode(args)) + fputc(' ', xtrerr); + quotedzputs((char *)getdata(lptr), xtrerr); + } + fputc('\n', xtrerr); + fflush(xtrerr); + } + queue_signals(); + ocs = cmdstack; + ocsp = cmdsp; + cmdstack = (unsigned char *) zalloc(CMDSTACKSZ); + cmdsp = 0; + if ((osfc = sfcontext) == SFC_NONE) + sfcontext = SFC_DIRECT; + xtrerr = stderr; + + doshfunc(shf, args, 0); + + sfcontext = osfc; + free(cmdstack); + cmdstack = ocs; + cmdsp = ocsp; + + if (!list_pipe) + deletefilelist(last_file_list, 0); + unqueue_signals(); +} + +/* + * Function to execute the special type of command that represents an + * autoloaded shell function. The command structure tells us which + * function it is. This function is actually called as part of the + * execution of the autoloaded function itself, so when the function + * has been autoloaded, its list is just run with no frills. + * + * There are two cases because if we are doing all-singing, all-dancing + * non-simple code we load the shell function early in execcmd() (the + * action also present in the non-basic version) to check if + * there are redirections that need to be handled at that point. + * Then we call execautofn_basic() to do the rest. + */ + +/**/ +static int +execautofn_basic(Estate state, UNUSED(int do_exec)) +{ + Shfunc shf; + char *oldscriptname, *oldscriptfilename; + + shf = state->prog->shf; + + /* + * Probably we didn't know the filename where this function was + * defined yet. + */ + if (funcstack && !funcstack->filename) + funcstack->filename = getshfuncfile(shf); + + oldscriptname = scriptname; + oldscriptfilename = scriptfilename; + scriptname = dupstring(shf->node.nam); + scriptfilename = getshfuncfile(shf); + execode(shf->funcdef, 1, 0, "loadautofunc"); + scriptname = oldscriptname; + scriptfilename = oldscriptfilename; + + return lastval; +} + +/**/ +static int +execautofn(Estate state, UNUSED(int do_exec)) +{ + Shfunc shf; + + if (!(shf = loadautofn(state->prog->shf, 1, 0, 0))) + return 1; + + state->prog->shf = shf; + return execautofn_basic(state, 0); +} + +/* + * Helper function to install the source file name of a shell function + * just autoloaded. + * + * We attempt to do this efficiently as the typical case is the + * directory part is a well-known directory, which is cached, and + * the non-directory part is the same as the node name. + */ + +/**/ +static void +loadautofnsetfile(Shfunc shf, char *fdir) +{ + /* + * If shf->filename is already the load directory --- + * keep it as we can still use it to get the load file. + * This makes autoload with an absolute path particularly efficient. + */ + if (!(shf->node.flags & PM_LOADDIR) || + strcmp(shf->filename, fdir) != 0) { + /* Old directory name not useful... */ + dircache_set(&shf->filename, NULL); + if (fdir) { + /* ...can still cache directory */ + shf->node.flags |= PM_LOADDIR; + dircache_set(&shf->filename, fdir); + } else { + /* ...no separate directory part to cache, for some reason. */ + shf->node.flags &= ~PM_LOADDIR; + shf->filename = ztrdup(shf->node.nam); + } + } +} + +/**/ +Shfunc +loadautofn(Shfunc shf, int fksh, int autol, int current_fpath) +{ + int noalias = noaliases, ksh = 1; + Eprog prog; + char *fdir; /* Directory path where func found */ + + pushheap(); + + noaliases = (shf->node.flags & PM_UNALIASED); + if (shf->filename && shf->filename[0] == '/' && + (shf->node.flags & PM_LOADDIR)) + { + char *spec_path[2]; + spec_path[0] = dupstring(shf->filename); + spec_path[1] = NULL; + prog = getfpfunc(shf->node.nam, &ksh, &fdir, spec_path, 0); + if (prog == &dummy_eprog && + (current_fpath || (shf->node.flags & PM_CUR_FPATH))) + prog = getfpfunc(shf->node.nam, &ksh, &fdir, NULL, 0); + } + else + prog = getfpfunc(shf->node.nam, &ksh, &fdir, NULL, 0); + noaliases = noalias; + + if (ksh == 1) { + ksh = fksh; + if (ksh == 1) + ksh = (shf->node.flags & PM_KSHSTORED) ? 2 : + (shf->node.flags & PM_ZSHSTORED) ? 0 : 1; + } + + if (prog == &dummy_eprog) { + /* We're not actually in the function; decrement locallevel */ + locallevel--; + zwarn("%s: function definition file not found", shf->node.nam); + locallevel++; + popheap(); + return NULL; + } + if (!prog) { + popheap(); + return NULL; + } + if (ksh == 2 || (ksh == 1 && isset(KSHAUTOLOAD))) { + if (autol) { + prog->flags |= EF_RUN; + + freeeprog(shf->funcdef); + if (prog->flags & EF_MAP) + shf->funcdef = prog; + else + shf->funcdef = dupeprog(prog, 0); + shf->node.flags &= ~PM_UNDEFINED; + loadautofnsetfile(shf, fdir); + } else { + VARARR(char, n, strlen(shf->node.nam) + 1); + strcpy(n, shf->node.nam); + execode(prog, 1, 0, "evalautofunc"); + shf = (Shfunc) shfunctab->getnode(shfunctab, n); + if (!shf || (shf->node.flags & PM_UNDEFINED)) { + /* We're not actually in the function; decrement locallevel */ + locallevel--; + zwarn("%s: function not defined by file", n); + locallevel++; + popheap(); + return NULL; + } + } + } else { + freeeprog(shf->funcdef); + if (prog->flags & EF_MAP) + shf->funcdef = stripkshdef(prog, shf->node.nam); + else + shf->funcdef = dupeprog(stripkshdef(prog, shf->node.nam), 0); + shf->node.flags &= ~PM_UNDEFINED; + loadautofnsetfile(shf, fdir); + } + popheap(); + + return shf; +} + +/* + * Check if a sticky emulation differs from the current one. + */ + +/**/ + +int sticky_emulation_differs(Emulation_options sticky2) +{ + /* If no new sticky emulation, not a different emulation */ + if (!sticky2) + return 0; + /* If no current sticky emulation, different */ + if (!sticky) + return 1; + /* If basic emulation different, different */ + if (sticky->emulation != sticky2->emulation) + return 1; + /* If differing numbers of options, different */ + if (sticky->n_on_opts != sticky2->n_on_opts || + sticky->n_off_opts != sticky2->n_off_opts) + return 1; + /* + * We need to compare option arrays, if non-null. + * We made parseopts() create the list of options in option + * order to make this easy. + */ + /* If different options turned on, different */ + if (sticky->n_on_opts && + memcmp(sticky->on_opts, sticky2->on_opts, + sticky->n_on_opts * sizeof(*sticky->on_opts)) != 0) + return 1; + /* If different options turned on, different */ + if (sticky->n_off_opts && + memcmp(sticky->off_opts, sticky2->off_opts, + sticky->n_off_opts * sizeof(*sticky->off_opts)) != 0) + return 1; + return 0; +} + +/* + * execute a shell function + * + * name is the name of the function + * + * prog is the code to execute + * + * doshargs, if set, are parameters to pass to the function, + * in which the first element is the function name (even if + * FUNCTIONARGZERO is set as this is handled inside this function). + * + * If noreturnval is nonzero, then reset the current return + * value (lastval) to its value before the shell function + * was executed. However, in any case return the status value + * from the function (i.e. if noreturnval is not set, this + * will be the same as lastval). + */ + +/**/ +mod_export int +doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) +{ + char **pptab, **x; + int ret; + char *name = shfunc->node.nam; + int flags = shfunc->node.flags; + char *fname = dupstring(name); + Eprog prog; + static int oflags; + static int funcdepth; + Heap funcheap; + + queue_signals(); /* Lots of memory and global state changes coming */ + + NEWHEAPS(funcheap) { + /* + * Save data in heap rather than on stack to keep recursive + * function cost down --- use of heap memory should be efficient + * at this point. Saving is not actually massive. + */ + Funcsave funcsave = zhalloc(sizeof(struct funcsave)); + funcsave->scriptname = scriptname; + funcsave->argv0 = NULL; + funcsave->breaks = breaks; + funcsave->contflag = contflag; + funcsave->loops = loops; + funcsave->lastval = lastval; + funcsave->pipestats = NULL; + funcsave->numpipestats = numpipestats; + funcsave->noerrexit = noerrexit; + if (trap_state == TRAP_STATE_PRIMED) + trap_return--; + /* + * Suppression of ERR_RETURN is turned off in function scope. + */ + noerrexit &= ~NOERREXIT_RETURN; + if (noreturnval) { + /* + * Easiest to use the heap here since we're bracketed + * immediately by a pushheap/popheap pair. + */ + size_t bytes = sizeof(int)*numpipestats; + funcsave->pipestats = (int *)zhalloc(bytes); + memcpy(funcsave->pipestats, pipestats, bytes); + } + + starttrapscope(); + startpatternscope(); + + pptab = pparams; + if (!(flags & PM_UNDEFINED)) + scriptname = dupstring(name); + funcsave->zoptind = zoptind; + funcsave->optcind = optcind; + if (!isset(POSIXBUILTINS)) { + zoptind = 1; + optcind = 0; + } + + /* We need to save the current options even if LOCALOPTIONS is * + * not currently set. That's because if it gets set in the * + * function we need to restore the original options on exit. */ + memcpy(funcsave->opts, opts, sizeof(opts)); + funcsave->emulation = emulation; + funcsave->sticky = sticky; + + if (sticky_emulation_differs(shfunc->sticky)) { + /* + * Function is marked for sticky emulation. + * Enable it now. + * + * We deliberately do not do this if the sticky emulation + * in effect is the same as that requested. This enables + * option setting naturally within emulation environments. + * Note that a difference in EMULATE_FULLY (emulate with + * or without -R) counts as a different environment. + * + * This propagates the sticky emulation to subfunctions. + */ + sticky = sticky_emulation_dup(shfunc->sticky, 1); + emulation = sticky->emulation; + funcsave->restore_sticky = 1; + installemulation(emulation, opts); + if (sticky->n_on_opts) { + OptIndex *onptr; + for (onptr = sticky->on_opts; + onptr < sticky->on_opts + sticky->n_on_opts; + onptr++) + opts[*onptr] = 1; + } + if (sticky->n_off_opts) { + OptIndex *offptr; + for (offptr = sticky->off_opts; + offptr < sticky->off_opts + sticky->n_off_opts; + offptr++) + opts[*offptr] = 0; + } + /* All emulations start with pattern disables clear */ + clearpatterndisables(); + } else + funcsave->restore_sticky = 0; + + if (flags & (PM_TAGGED|PM_TAGGED_LOCAL)) + opts[XTRACE] = 1; + else if (oflags & PM_TAGGED_LOCAL) { + if (shfunc->node.nam == ANONYMOUS_FUNCTION_NAME /* pointer comparison */) + flags |= PM_TAGGED_LOCAL; + else + opts[XTRACE] = 0; + } + if (flags & PM_WARNNESTED) + opts[WARNNESTEDVAR] = 1; + else if (oflags & PM_WARNNESTED) { + if (shfunc->node.nam == ANONYMOUS_FUNCTION_NAME) + flags |= PM_WARNNESTED; + else + opts[WARNNESTEDVAR] = 0; + } + funcsave->oflags = oflags; + /* + * oflags is static, because we compare it on the next recursive + * call. Hence also we maintain a saved version for restoring + * the previous value of oflags after the call. + */ + oflags = flags; + opts[PRINTEXITVALUE] = 0; + if (doshargs) { + LinkNode node; + + node = firstnode(doshargs); + pparams = x = (char **) zshcalloc(((sizeof *x) * + (1 + countlinknodes(doshargs)))); + if (isset(FUNCTIONARGZERO)) { + funcsave->argv0 = argzero; + argzero = ztrdup(getdata(node)); + } + /* first node contains name regardless of option */ + node = node->next; + for (; node; node = node->next, x++) + *x = ztrdup(getdata(node)); + } else { + pparams = (char **) zshcalloc(sizeof *pparams); + if (isset(FUNCTIONARGZERO)) { + funcsave->argv0 = argzero; + argzero = ztrdup(argzero); + } + } + ++funcdepth; + if (zsh_funcnest >= 0 && funcdepth > zsh_funcnest) { + zerr("maximum nested function level reached; increase FUNCNEST?"); + lastval = 1; + goto undoshfunc; + } + funcsave->fstack.name = dupstring(name); + /* + * The caller is whatever is immediately before on the stack, + * unless we're at the top, in which case it's the script + * or interactive shell name. + */ + funcsave->fstack.caller = funcstack ? funcstack->name : + dupstring(funcsave->argv0 ? funcsave->argv0 : argzero); + funcsave->fstack.lineno = lineno; + funcsave->fstack.prev = funcstack; + funcsave->fstack.tp = FS_FUNC; + funcstack = &funcsave->fstack; + + funcsave->fstack.flineno = shfunc->lineno; + funcsave->fstack.filename = getshfuncfile(shfunc); + + prog = shfunc->funcdef; + if (prog->flags & EF_RUN) { + Shfunc shf; + + prog->flags &= ~EF_RUN; + + runshfunc(prog, NULL, funcsave->fstack.name); + + if (!(shf = (Shfunc) shfunctab->getnode(shfunctab, + (name = fname)))) { + zwarn("%s: function not defined by file", name); + if (noreturnval) + errflag |= ERRFLAG_ERROR; + else + lastval = 1; + goto doneshfunc; + } + prog = shf->funcdef; + } + runshfunc(prog, wrappers, funcsave->fstack.name); + doneshfunc: + funcstack = funcsave->fstack.prev; + undoshfunc: + --funcdepth; + if (retflag) { + /* + * This function is forced to return. + */ + retflag = 0; + /* + * The calling function isn't necessarily forced to return, + * but it should be made sensitive to ERR_EXIT and + * ERR_RETURN as the assumptions we made at the end of + * constructs within this function no longer apply. If + * there are cases where this is not true, they need adding + * to C03traps.ztst. + */ + this_noerrexit = 0; + breaks = funcsave->breaks; + } + freearray(pparams); + if (funcsave->argv0) { + zsfree(argzero); + argzero = funcsave->argv0; + } + pparams = pptab; + if (!isset(POSIXBUILTINS)) { + zoptind = funcsave->zoptind; + optcind = funcsave->optcind; + } + scriptname = funcsave->scriptname; + oflags = funcsave->oflags; + + endpatternscope(); /* before restoring old LOCALPATTERNS */ + + if (funcsave->restore_sticky) { + /* + * If we switched to an emulation environment just for + * this function, we interpret the option and emulation + * switch as being a firewall between environments. + */ + memcpy(opts, funcsave->opts, sizeof(opts)); + emulation = funcsave->emulation; + sticky = funcsave->sticky; + } else if (isset(LOCALOPTIONS)) { + /* restore all shell options except PRIVILEGED and RESTRICTED */ + funcsave->opts[PRIVILEGED] = opts[PRIVILEGED]; + funcsave->opts[RESTRICTED] = opts[RESTRICTED]; + memcpy(opts, funcsave->opts, sizeof(opts)); + emulation = funcsave->emulation; + } else { + /* just restore a couple. */ + opts[XTRACE] = funcsave->opts[XTRACE]; + opts[PRINTEXITVALUE] = funcsave->opts[PRINTEXITVALUE]; + opts[LOCALOPTIONS] = funcsave->opts[LOCALOPTIONS]; + opts[LOCALLOOPS] = funcsave->opts[LOCALLOOPS]; + opts[WARNNESTEDVAR] = funcsave->opts[WARNNESTEDVAR]; + } + + if (opts[LOCALLOOPS]) { + if (contflag) + zwarn("`continue' active at end of function scope"); + if (breaks) + zwarn("`break' active at end of function scope"); + breaks = funcsave->breaks; + contflag = funcsave->contflag; + loops = funcsave->loops; + } + + endtrapscope(); + + if (trap_state == TRAP_STATE_PRIMED) + trap_return++; + ret = lastval; + noerrexit = funcsave->noerrexit; + if (noreturnval) { + lastval = funcsave->lastval; + numpipestats = funcsave->numpipestats; + memcpy(pipestats, funcsave->pipestats, sizeof(int)*numpipestats); + } + } OLDHEAPS; + + unqueue_signals(); + + /* + * Exit with a tidy up. + * Only leave if we're at the end of the appropriate function --- + * not a nested function. As we usually skip the function body, + * the only likely case where we need that second test is + * when we have an "always" block. The endparamscope() has + * already happened, hence the "+1" here. + * + * If we are in an exit trap, finish it first... we wouldn't set + * exit_pending if we were already in one. + */ + if (exit_pending && exit_level >= locallevel+1 && !in_exit_trap) { + if (locallevel > forklevel) { + /* Still functions to return: force them to do so. */ + retflag = 1; + breaks = loops; + } else { + /* + * All functions finished: time to exit the shell. + * We already did the `stopmsg' test when the + * exit command was handled. + */ + stopmsg = 1; + zexit(exit_pending >> 1, 0); + } + } + + return ret; +} + +/* This finally executes a shell function and any function wrappers * + * defined by modules. This works by calling the wrapper function which * + * in turn has to call back this function with the arguments it gets. */ + +/**/ +mod_export void +runshfunc(Eprog prog, FuncWrap wrap, char *name) +{ + int cont, ouu; + char *ou; + + queue_signals(); + + ou = zalloc(ouu = underscoreused); + if (ou) + memcpy(ou, zunderscore, underscoreused); + + while (wrap) { + wrap->module->wrapper++; + cont = wrap->handler(prog, wrap->next, name); + wrap->module->wrapper--; + + if (!wrap->module->wrapper && + (wrap->module->node.flags & MOD_UNLOAD)) + unload_module(wrap->module); + + if (!cont) { + if (ou) + zfree(ou, ouu); + unqueue_signals(); + return; + } + wrap = wrap->next; + } + startparamscope(); + 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 + * list of its contents. + * + * If test is 0, load the function. + * + * If test_only is 1, don't load function, just test for it: + * Non-null return means function was found + * + * *fdir points to path at which found (as passed in, not duplicated) + */ + +/**/ +Eprog +getfpfunc(char *s, int *ksh, char **fdir, char **alt_path, int test_only) +{ + char **pp, buf[PATH_MAX+1]; + off_t len; + off_t rlen; + char *d; + Eprog r; + int fd; + + pp = alt_path ? alt_path : fpath; + for (; *pp; pp++) { + if (strlen(*pp) + strlen(s) + 1 >= PATH_MAX) + continue; + if (**pp) + sprintf(buf, "%s/%s", *pp, s); + else + strcpy(buf, s); + if ((r = try_dump_file(*pp, s, buf, ksh, test_only))) { + if (fdir) + *fdir = *pp; + return r; + } + unmetafy(buf, NULL); + if (!access(buf, R_OK) && (fd = open(buf, O_RDONLY | O_NOCTTY)) != -1) { + struct stat st; + if (!fstat(fd, &st) && S_ISREG(st.st_mode) && + (len = lseek(fd, 0, 2)) != -1) { + if (test_only) { + close(fd); + if (fdir) + *fdir = *pp; + return &dummy_eprog; + } + d = (char *) zalloc(len + 1); + lseek(fd, 0, 0); + if ((rlen = read(fd, d, len)) >= 0) { + char *oldscriptname = scriptname; + + close(fd); + d[rlen] = '\0'; + d = metafy(d, rlen, META_REALLOC); + + scriptname = dupstring(s); + r = parse_string(d, 1); + scriptname = oldscriptname; + + if (fdir) + *fdir = *pp; + + zfree(d, len + 1); + + return r; + } else + close(fd); + + zfree(d, len + 1); + } else + close(fd); + } + } + return test_only ? NULL : &dummy_eprog; +} + +/* Handle the most common type of ksh-style autoloading, when doing a * + * zsh-style autoload. Given the list read from an autoload file, and the * + * name of the function being defined, check to see if the file consists * + * entirely of a single definition for that function. If so, use the * + * contents of that definition. Otherwise, use the entire file. */ + +/**/ +Eprog +stripkshdef(Eprog prog, char *name) +{ + Wordcode pc; + wordcode code; + char *ptr1, *ptr2; + + if (!prog) + return NULL; + pc = prog->prog; + code = *pc++; + if (wc_code(code) != WC_LIST || + (WC_LIST_TYPE(code) & (Z_SYNC|Z_END|Z_SIMPLE)) != (Z_SYNC|Z_END|Z_SIMPLE)) + return prog; + pc++; + code = *pc++; + if (wc_code(code) != WC_FUNCDEF || *pc != 1) + return prog; + + /* + * See if name of function requested (name) is same as + * name of function in word code. name may still have "-" + * tokenised. The word code shouldn't, as function names should be + * untokenised, but reports say it sometimes does. + */ + ptr1 = name; + ptr2 = ecrawstr(prog, pc + 1, NULL); + while (*ptr1 && *ptr2) { + if (*ptr1 != *ptr2 && *ptr1 != Dash && *ptr1 != '-' && + *ptr2 != Dash && *ptr2 != '-') + break; + ptr1++; + ptr2++; + } + if (*ptr1 || *ptr2) + return prog; + + { + Eprog ret; + Wordcode end = pc + WC_FUNCDEF_SKIP(code); + int sbeg = pc[2], nstrs = pc[3], nprg, npats = pc[4], plen, len, i; + Patprog *pp; + + pc += 5; + + nprg = end - pc; + plen = nprg * sizeof(wordcode); + len = plen + (npats * sizeof(Patprog)) + nstrs; + + if (prog->flags & EF_MAP) { + ret = prog; + free(prog->pats); + ret->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog)); + ret->prog = pc; + ret->strs = prog->strs + sbeg; + } else { + ret = (Eprog) zhalloc(sizeof(*ret)); + ret->flags = EF_HEAP; + ret->pats = pp = (Patprog *) zhalloc(len); + ret->prog = (Wordcode) (ret->pats + npats); + ret->strs = (char *) (ret->prog + nprg); + memcpy(ret->prog, pc, plen); + memcpy(ret->strs, prog->strs + sbeg, nstrs); + ret->dump = NULL; + } + ret->len = len; + ret->npats = npats; + for (i = npats; i--; pp++) + *pp = dummy_patprog1; + ret->shf = NULL; + + return ret; + } +} + +/* check to see if AUTOCD applies here */ + +/**/ +static char * +cancd(char *s) +{ + int nocdpath = s[0] == '.' && + (s[1] == '/' || !s[1] || (s[1] == '.' && (s[2] == '/' || !s[1]))); + char *t; + + if (*s != '/') { + char sbuf[PATH_MAX+1], **cp; + + if (cancd2(s)) + return s; + if (access(unmeta(s), X_OK) == 0) + return NULL; + if (!nocdpath) + for (cp = cdpath; *cp; cp++) { + if (strlen(*cp) + strlen(s) + 1 >= PATH_MAX) + continue; + if (**cp) + sprintf(sbuf, "%s/%s", *cp, s); + else + strcpy(sbuf, s); + if (cancd2(sbuf)) { + doprintdir = -1; + return dupstring(sbuf); + } + } + if ((t = cd_able_vars(s))) { + if (cancd2(t)) { + doprintdir = -1; + return t; + } + } + return NULL; + } + return cancd2(s) ? s : NULL; +} + +/**/ +static int +cancd2(char *s) +{ + struct stat buf; + char *us, *us2 = NULL; + int ret; + + /* + * If CHASEDOTS and CHASELINKS are not set, we want to rationalize the + * path by removing foo/.. combinations in the logical rather than + * the physical path. If either is set, we test the physical path. + */ + if (!isset(CHASEDOTS) && !isset(CHASELINKS)) { + if (*s != '/') + us = tricat(pwd[1] ? pwd : "", "/", s); + else + us = ztrdup(s); + fixdir(us2 = us); + } else + us = unmeta(s); + ret = !(access(us, X_OK) || stat(us, &buf) || !S_ISDIR(buf.st_mode)); + if (us2) + free(us2); + return ret; +} + +/**/ +void +execsave(void) +{ + struct execstack *es; + + es = (struct execstack *) zalloc(sizeof(struct execstack)); + es->list_pipe_pid = list_pipe_pid; + es->nowait = nowait; + es->pline_level = pline_level; + es->list_pipe_child = list_pipe_child; + es->list_pipe_job = list_pipe_job; + strcpy(es->list_pipe_text, list_pipe_text); + es->lastval = lastval; + es->noeval = noeval; + es->badcshglob = badcshglob; + es->cmdoutpid = cmdoutpid; + es->cmdoutval = cmdoutval; + es->use_cmdoutval = use_cmdoutval; + es->procsubstpid = procsubstpid; + es->trap_return = trap_return; + es->trap_state = trap_state; + es->trapisfunc = trapisfunc; + es->traplocallevel = traplocallevel; + es->noerrs = noerrs; + es->this_noerrexit = this_noerrexit; + es->underscore = ztrdup(zunderscore); + es->next = exstack; + exstack = es; + noerrs = cmdoutpid = 0; +} + +/**/ +void +execrestore(void) +{ + struct execstack *en = exstack; + + DPUTS(!exstack, "BUG: execrestore() without execsave()"); + + queue_signals(); + exstack = exstack->next; + + list_pipe_pid = en->list_pipe_pid; + nowait = en->nowait; + pline_level = en->pline_level; + list_pipe_child = en->list_pipe_child; + list_pipe_job = en->list_pipe_job; + strcpy(list_pipe_text, en->list_pipe_text); + lastval = en->lastval; + noeval = en->noeval; + badcshglob = en->badcshglob; + cmdoutpid = en->cmdoutpid; + cmdoutval = en->cmdoutval; + use_cmdoutval = en->use_cmdoutval; + procsubstpid = en->procsubstpid; + trap_return = en->trap_return; + trap_state = en->trap_state; + trapisfunc = en->trapisfunc; + traplocallevel = en->traplocallevel; + noerrs = en->noerrs; + this_noerrexit = en->this_noerrexit; + setunderscore(en->underscore); + zsfree(en->underscore); + free(en); + + unqueue_signals(); +} diff --git a/dotfiles/system/.zsh/modules/Src/glob.c b/dotfiles/system/.zsh/modules/Src/glob.c new file mode 100644 index 0000000..ed2c90b --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/glob.c @@ -0,0 +1,3913 @@ +/* + * glob.c - filename generation + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "glob.pro" + +#if defined(OFF_T_IS_64_BIT) && defined(__GNUC__) +# define ALIGN64 __attribute__((aligned(8))) +#else +# define ALIGN64 +#endif + +/* flag for CSHNULLGLOB */ + +typedef struct gmatch *Gmatch; + +struct gmatch { + /* Metafied file name */ + char *name; + /* Unmetafied file name; embedded nulls can't occur in file names */ + char *uname; + /* + * Array of sort strings: one for each GS_EXEC sort type in + * the glob qualifiers. + */ + char **sortstrs; + off_t size ALIGN64; + long atime; + long mtime; + long ctime; + long links; + off_t _size ALIGN64; + long _atime; + long _mtime; + long _ctime; + long _links; +#ifdef GET_ST_ATIME_NSEC + long ansec; + long _ansec; +#endif +#ifdef GET_ST_MTIME_NSEC + long mnsec; + long _mnsec; +#endif +#ifdef GET_ST_CTIME_NSEC + long cnsec; + long _cnsec; +#endif +}; + +#define GS_NAME 1 +#define GS_DEPTH 2 +#define GS_EXEC 4 + +#define GS_SHIFT_BASE 8 + +#define GS_SIZE (GS_SHIFT_BASE) +#define GS_ATIME (GS_SHIFT_BASE << 1) +#define GS_MTIME (GS_SHIFT_BASE << 2) +#define GS_CTIME (GS_SHIFT_BASE << 3) +#define GS_LINKS (GS_SHIFT_BASE << 4) + +#define GS_SHIFT 5 +#define GS__SIZE (GS_SIZE << GS_SHIFT) +#define GS__ATIME (GS_ATIME << GS_SHIFT) +#define GS__MTIME (GS_MTIME << GS_SHIFT) +#define GS__CTIME (GS_CTIME << GS_SHIFT) +#define GS__LINKS (GS_LINKS << GS_SHIFT) + +#define GS_DESC (GS_SHIFT_BASE << (2*GS_SHIFT)) +#define GS_NONE (GS_SHIFT_BASE << (2*GS_SHIFT+1)) + +#define GS_NORMAL (GS_SIZE | GS_ATIME | GS_MTIME | GS_CTIME | GS_LINKS) +#define GS_LINKED (GS_NORMAL << GS_SHIFT) + +/**/ +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; + +typedef struct stat *Statptr; /* This makes the Ultrix compiler happy. Go figure. */ + +/* modifier for unit conversions */ + +#define TT_DAYS 0 +#define TT_HOURS 1 +#define TT_MINS 2 +#define TT_WEEKS 3 +#define TT_MONTHS 4 +#define TT_SECONDS 5 + +#define TT_BYTES 0 +#define TT_POSIX_BLOCKS 1 +#define TT_KILOBYTES 2 +#define TT_MEGABYTES 3 +#define TT_GIGABYTES 4 +#define TT_TERABYTES 5 + + +typedef int (*TestMatchFunc) _((char *, struct stat *, off_t, char *)); + +struct qual { + struct qual *next; /* Next qualifier, must match */ + struct qual *or; /* Alternative set of qualifiers to match */ + TestMatchFunc func; /* Function to call to test match */ + off_t data ALIGN64; /* Argument passed to function */ + int sense; /* Whether asserting or negating */ + int amc; /* Flag for which time to test (a, m, c) */ + int range; /* Whether to test <, > or = (as per signum) */ + int units; /* Multiplier for time or size, respectively */ + char *sdata; /* currently only: expression to eval */ +}; + +/* Prefix, suffix for doing zle trickery */ + +/**/ +mod_export char *glob_pre, *glob_suf; + +/* Element of a glob sort */ +struct globsort { + /* Sort type */ + int tp; + /* Sort code to eval, if type is GS_EXEC */ + char *exec; +}; + +/* Maximum entries in sort array */ +#define MAX_SORTS (12) + +/* struct to easily save/restore current state */ + +struct globdata { + int gd_pathpos; + char *gd_pathbuf; + + int gd_matchsz; /* size of matchbuf */ + int gd_matchct; /* number of matches found */ + int gd_pathbufsz; /* size of pathbuf */ + int gd_pathbufcwd; /* where did we chdir()'ed */ + Gmatch gd_matchbuf; /* array of matches */ + Gmatch gd_matchptr; /* &matchbuf[matchct] */ + char *gd_colonmod; /* colon modifiers in qualifier list */ + + /* Qualifiers pertaining to current pattern */ + struct qual *gd_quals; + + /* Other state values for current pattern */ + int gd_qualct, gd_qualorct; + int gd_range, gd_amc, gd_units; + int gd_gf_nullglob, gd_gf_markdirs, gd_gf_noglobdots, gd_gf_listtypes; + int gd_gf_numsort; + int gd_gf_follow, gd_gf_sorts, gd_gf_nsorts; + struct globsort gd_gf_sortlist[MAX_SORTS]; + LinkList gd_gf_pre_words, gd_gf_post_words; + + char *gd_glob_pre, *gd_glob_suf; +}; + +/* The variable with the current globbing state and convenience macros */ + +static struct globdata curglobdata; + +#define matchsz (curglobdata.gd_matchsz) +#define matchct (curglobdata.gd_matchct) +#define pathbufsz (curglobdata.gd_pathbufsz) +#define pathbufcwd (curglobdata.gd_pathbufcwd) +#define matchbuf (curglobdata.gd_matchbuf) +#define matchptr (curglobdata.gd_matchptr) +#define colonmod (curglobdata.gd_colonmod) +#define quals (curglobdata.gd_quals) +#define qualct (curglobdata.gd_qualct) +#define qualorct (curglobdata.gd_qualorct) +#define g_range (curglobdata.gd_range) +#define g_amc (curglobdata.gd_amc) +#define g_units (curglobdata.gd_units) +#define gf_nullglob (curglobdata.gd_gf_nullglob) +#define gf_markdirs (curglobdata.gd_gf_markdirs) +#define gf_noglobdots (curglobdata.gd_gf_noglobdots) +#define gf_listtypes (curglobdata.gd_gf_listtypes) +#define gf_numsort (curglobdata.gd_gf_numsort) +#define gf_follow (curglobdata.gd_gf_follow) +#define gf_sorts (curglobdata.gd_gf_sorts) +#define gf_nsorts (curglobdata.gd_gf_nsorts) +#define gf_sortlist (curglobdata.gd_gf_sortlist) +#define gf_pre_words (curglobdata.gd_gf_pre_words) +#define gf_post_words (curglobdata.gd_gf_post_words) + +/* and macros for save/restore */ + +#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 */ + +struct complist { + Complist next; + Patprog pat; + int closure; /* 1 if this is a (foo/)# */ + int follow; /* 1 to go thru symlinks */ +}; + +/* Add a component to pathbuf: This keeps track of how * + * far we are into a file name, since each path component * + * must be matched separately. */ + +/**/ +static void +addpath(char *s, int l) +{ + DPUTS(!pathbuf, "BUG: pathbuf not initialised"); + while (pathpos + l + 1 >= pathbufsz) + pathbuf = zrealloc(pathbuf, pathbufsz *= 2); + while (l--) + pathbuf[pathpos++] = *s++; + pathbuf[pathpos++] = '/'; + pathbuf[pathpos] = '\0'; +} + +/* stat the filename s appended to pathbuf. l should be true for lstat, * + * false for stat. If st is NULL, the file is only checked for existance. * + * s == "" is treated as s == ".". This is necessary since on most systems * + * foo/ can be used to reference a non-directory foo. Returns nonzero if * + * the file does not exists. */ + +/**/ +static int +statfullpath(const char *s, struct stat *st, int l) +{ + char buf[PATH_MAX+1]; + + DPUTS(strlen(s) + !*s + pathpos - pathbufcwd >= PATH_MAX, + "BUG: statfullpath(): pathname too long"); + strcpy(buf, pathbuf + pathbufcwd); + strcpy(buf + pathpos - pathbufcwd, s); + if (!*s && *buf) { + /* + * Don't add the '.' if the path so far is empty, since + * then we get bogus empty strings inserted as files. + */ + buf[pathpos - pathbufcwd] = '.'; + buf[pathpos - pathbufcwd + 1] = '\0'; + l = 0; + } + unmetafy(buf, NULL); + if (!st) { + char lbuf[1]; + return access(buf, F_OK) && (!l || readlink(buf, lbuf, 1) < 0); + } + return l ? lstat(buf, st) : stat(buf, st); +} + +/* This may be set by qualifier functions to an array of strings to insert + * into the list instead of the original string. */ + +static char **inserts; + +/* add a match to the list */ + +/**/ +static void +insert(char *s, int checked) +{ + struct stat buf, buf2, *bp; + char *news = s; + int statted = 0; + + queue_signals(); + inserts = NULL; + + if (gf_listtypes || gf_markdirs) { + /* Add the type marker to the end of the filename */ + mode_t mode; + checked = statted = 1; + if (statfullpath(s, &buf, 1)) { + unqueue_signals(); + return; + } + mode = buf.st_mode; + if (gf_follow) { + if (!S_ISLNK(mode) || statfullpath(s, &buf2, 0)) + memcpy(&buf2, &buf, sizeof(buf)); + statted |= 2; + mode = buf2.st_mode; + } + if (gf_listtypes || S_ISDIR(mode)) { + int ll = strlen(s); + + news = (char *) hcalloc(ll + 2); + strcpy(news, s); + news[ll] = file_type(mode); + news[ll + 1] = '\0'; + } + } + if (qualct || qualorct) { + /* Go through the qualifiers, rejecting the file if appropriate */ + struct qual *qo, *qn; + + if (!statted && statfullpath(s, &buf, 1)) { + unqueue_signals(); + return; + } + news = dyncat(pathbuf, news); + + statted = 1; + qo = quals; + for (qn = qo; qn && qn->func;) { + g_range = qn->range; + g_amc = qn->amc; + g_units = qn->units; + if ((qn->sense & 2) && !(statted & 2)) { + /* If (sense & 2), we're following links */ + if (!S_ISLNK(buf.st_mode) || statfullpath(s, &buf2, 0)) + memcpy(&buf2, &buf, sizeof(buf)); + statted |= 2; + } + bp = (qn->sense & 2) ? &buf2 : &buf; + /* Reject the file if the function returned zero * + * and the sense was positive (sense&1 == 0), or * + * vice versa. */ + if ((!((qn->func) (news, bp, qn->data, qn->sdata)) + ^ qn->sense) & 1) { + /* Try next alternative, or return if there are no more */ + if (!(qo = qo->or)) { + unqueue_signals(); + return; + } + qn = qo; + continue; + } + qn = qn->next; + } + } else if (!checked) { + if (statfullpath(s, &buf, 1)) { + unqueue_signals(); + return; + } + statted = 1; + news = dyncat(pathbuf, news); + } else + news = dyncat(pathbuf, news); + + while (!inserts || (news = dupstring(*inserts++))) { + if (colonmod) { + /* Handle the remainder of the qualifier: e.g. (:r:s/foo/bar/). */ + char *mod = colonmod; + modify(&news, &mod); + } + if (!statted && (gf_sorts & GS_NORMAL)) { + statfullpath(s, &buf, 1); + statted = 1; + } + if (!(statted & 2) && (gf_sorts & GS_LINKED)) { + if (statted) { + if (!S_ISLNK(buf.st_mode) || statfullpath(s, &buf2, 0)) + memcpy(&buf2, &buf, sizeof(buf)); + } else if (statfullpath(s, &buf2, 0)) + statfullpath(s, &buf2, 1); + statted |= 2; + } + matchptr->name = news; + if (statted & 1) { + matchptr->size = buf.st_size; + matchptr->atime = buf.st_atime; + matchptr->mtime = buf.st_mtime; + matchptr->ctime = buf.st_ctime; + matchptr->links = buf.st_nlink; +#ifdef GET_ST_ATIME_NSEC + matchptr->ansec = GET_ST_ATIME_NSEC(buf); +#endif +#ifdef GET_ST_MTIME_NSEC + matchptr->mnsec = GET_ST_MTIME_NSEC(buf); +#endif +#ifdef GET_ST_CTIME_NSEC + matchptr->cnsec = GET_ST_CTIME_NSEC(buf); +#endif + } + if (statted & 2) { + matchptr->_size = buf2.st_size; + matchptr->_atime = buf2.st_atime; + matchptr->_mtime = buf2.st_mtime; + matchptr->_ctime = buf2.st_ctime; + matchptr->_links = buf2.st_nlink; +#ifdef GET_ST_ATIME_NSEC + matchptr->_ansec = GET_ST_ATIME_NSEC(buf2); +#endif +#ifdef GET_ST_MTIME_NSEC + matchptr->_mnsec = GET_ST_MTIME_NSEC(buf2); +#endif +#ifdef GET_ST_CTIME_NSEC + matchptr->_cnsec = GET_ST_CTIME_NSEC(buf2); +#endif + } + matchptr++; + + if (++matchct == matchsz) { + matchbuf = (Gmatch)zrealloc((char *)matchbuf, + sizeof(struct gmatch) * (matchsz *= 2)); + + matchptr = matchbuf + matchct; + } + if (!inserts) + break; + } + unqueue_signals(); + return; +} + +/* Do the globbing: scanner is called recursively * + * with successive bits of the path until we've * + * tried all of it. */ + +/**/ +static void +scanner(Complist q, int shortcircuit) +{ + Patprog p; + int closure; + int pbcwdsav = pathbufcwd; + int errssofar = errsfound; + struct dirsav ds; + + if (!q || errflag) + return; + init_dirsav(&ds); + + if ((closure = q->closure)) { + /* (foo/)# - match zero or more dirs */ + if (q->closure == 2) /* (foo/)## - match one or more dirs */ + q->closure = 1; + else { + scanner(q->next, shortcircuit); + if (shortcircuit && shortcircuit == matchct) + return; + } + } + p = q->pat; + /* Now the actual matching for the current path section. */ + if (p->flags & PAT_PURES) { + /* + * It's a straight string to the end of the path section. + */ + char *str = (char *)p + p->startoff; + int l = p->patmlen; + + if (l + !l + pathpos - pathbufcwd >= PATH_MAX) { + int err; + + if (l >= PATH_MAX) + return; + err = lchdir(unmeta(pathbuf + pathbufcwd), &ds, 0); + if (err == -1) + return; + if (err) { + zerr("current directory lost during glob"); + return; + } + pathbufcwd = pathpos; + } + if (q->next) { + /* Not the last path section. Just add it to the path. */ + int oppos = pathpos; + + if (!errflag) { + int add = 1; + + if (q->closure && *pathbuf) { + if (!strcmp(str, ".")) + add = 0; + else if (!strcmp(str, "..")) { + struct stat sc, sr; + + add = (stat("/", &sr) || stat(unmeta(pathbuf), &sc) || + sr.st_ino != sc.st_ino || + sr.st_dev != sc.st_dev); + } + } + if (add) { + addpath(str, l); + if (!closure || !statfullpath("", NULL, 1)) { + scanner((q->closure) ? q : q->next, shortcircuit); + if (shortcircuit && shortcircuit == matchct) + return; + } + pathbuf[pathpos = oppos] = '\0'; + } + } + } else { + if (str[l]) + str = dupstrpfx(str, l); + insert(str, 0); + if (shortcircuit && shortcircuit == matchct) + return; + } + } else { + /* Do pattern matching on current path section. */ + char *fn = pathbuf[pathbufcwd] ? unmeta(pathbuf + pathbufcwd) : "."; + int dirs = !!q->next; + DIR *lock = opendir(fn); + char *subdirs = NULL; + int subdirlen = 0; + + if (lock == NULL) + return; + while ((fn = zreaddir(lock, 1)) && !errflag) { + /* prefix and suffix are zle trickery */ + if (!dirs && !colonmod && + ((glob_pre && !strpfx(glob_pre, fn)) + || (glob_suf && !strsfx(glob_suf, fn)))) + continue; + errsfound = errssofar; + if (pattry(p, fn)) { + /* if this name matchs the pattern... */ + if (pbcwdsav == pathbufcwd && + strlen(fn) + pathpos - pathbufcwd >= PATH_MAX) { + int err; + + DPUTS(pathpos == pathbufcwd, + "BUG: filename longer than PATH_MAX"); + err = lchdir(unmeta(pathbuf + pathbufcwd), &ds, 0); + if (err == -1) + break; + if (err) { + zerr("current directory lost during glob"); + break; + } + pathbufcwd = pathpos; + } + if (dirs) { + int l; + + /* + * If not the last component in the path: + * + * If we made an approximation in the new path segment, + * then it is possible we made too many errors. For + * example, (ab)#(cb)# will match the directory abcb + * with one error if allowed to, even though it can + * match with none. This will stop later parts of the + * path matching, so we need to check by reducing the + * maximum number of errors and seeing if the directory + * still matches. Luckily, this is not a terribly + * common case, since complex patterns typically occur + * in the last part of the path which is not affected + * by this problem. + */ + if (errsfound > errssofar) { + forceerrs = errsfound - 1; + while (forceerrs >= errssofar) { + errsfound = errssofar; + if (!pattry(p, fn)) + break; + forceerrs = errsfound - 1; + } + errsfound = forceerrs + 1; + forceerrs = -1; + } + if (closure) { + /* if matching multiple directories */ + struct stat buf; + + if (statfullpath(fn, &buf, !q->follow)) { + if (errno != ENOENT && errno != EINTR && + errno != ENOTDIR && !errflag) { + zwarn("%e: %s", errno, fn); + } + continue; + } + if (!S_ISDIR(buf.st_mode)) + continue; + } + l = strlen(fn) + 1; + subdirs = hrealloc(subdirs, subdirlen, subdirlen + l + + sizeof(int)); + strcpy(subdirs + subdirlen, fn); + subdirlen += l; + /* store the count of errors made so far, too */ + memcpy(subdirs + subdirlen, (char *)&errsfound, + sizeof(int)); + subdirlen += sizeof(int); + } else { + /* if the last filename component, just add it */ + insert(fn, 1); + if (shortcircuit && shortcircuit == matchct) { + closedir(lock); + return; + } + } + } + } + closedir(lock); + if (subdirs) { + int oppos = pathpos; + + for (fn = subdirs; fn < subdirs+subdirlen; ) { + int l = strlen(fn); + addpath(fn, l); + fn += l + 1; + memcpy((char *)&errsfound, fn, sizeof(int)); + fn += sizeof(int); + /* scan next level */ + scanner((q->closure) ? q : q->next, shortcircuit); + if (shortcircuit && shortcircuit == matchct) + return; + pathbuf[pathpos = oppos] = '\0'; + } + hrealloc(subdirs, subdirlen, 0); + } + } + if (pbcwdsav < pathbufcwd) { + if (restoredir(&ds)) + zerr("current directory lost during glob"); + zsfree(ds.dirname); + if (ds.dirfd >= 0) + close(ds.dirfd); + pathbufcwd = pbcwdsav; + } + return; +} + +/* This function tokenizes a zsh glob pattern */ + +/**/ +static Complist +parsecomplist(char *instr) +{ + Patprog p1; + Complist l1; + char *str; + int compflags = gf_noglobdots ? (PAT_FILE|PAT_NOGLD) : PAT_FILE; + + if (instr[0] == Star && instr[1] == Star) { + int shortglob = 0; + if (instr[2] == '/' || (instr[2] == Star && instr[3] == '/') + || (shortglob = isset(GLOBSTARSHORT))) { + /* Match any number of directories. */ + int follow; + + /* with three stars, follow symbolic links */ + follow = (instr[2] == Star); + /* + * With GLOBSTARSHORT, leave a star in place for the + * pattern inside the directory. + */ + instr += ((shortglob ? 1 : 3) + follow); + + /* Now get the next path component if there is one. */ + l1 = (Complist) zhalloc(sizeof *l1); + if ((l1->next = parsecomplist(instr)) == NULL) { + errflag |= ERRFLAG_ERROR; + return NULL; + } + l1->pat = patcompile(NULL, compflags | PAT_ANY, NULL); + l1->closure = 1; /* ...zero or more times. */ + l1->follow = follow; + return l1; + } + } + + /* Parse repeated directories such as (dir/)# and (dir/)## */ + if (*(str = instr) == zpc_special[ZPC_INPAR] && + !skipparens(Inpar, Outpar, (char **)&str) && + *str == zpc_special[ZPC_HASH] && str[-2] == '/') { + instr++; + if (!(p1 = patcompile(instr, compflags, &instr))) + return NULL; + if (instr[0] == '/' && instr[1] == Outpar && instr[2] == Pound) { + int pdflag = 0; + + instr += 3; + if (*instr == Pound) { + pdflag = 1; + instr++; + } + l1 = (Complist) zhalloc(sizeof *l1); + l1->pat = p1; + /* special case (/)# to avoid infinite recursion */ + l1->closure = (*((char *)p1 + p1->startoff)) ? 1 + pdflag : 0; + l1->follow = 0; + l1->next = parsecomplist(instr); + return (l1->pat) ? l1 : NULL; + } + } else { + /* parse single path component */ + if (!(p1 = patcompile(instr, compflags|PAT_FILET, &instr))) + return NULL; + /* then do the remaining path components */ + if (*instr == '/' || !*instr) { + int ef = *instr == '/'; + + l1 = (Complist) zhalloc(sizeof *l1); + l1->pat = p1; + l1->closure = 0; + l1->next = ef ? parsecomplist(instr+1) : NULL; + return (ef && !l1->next) ? NULL : l1; + } + } + errflag |= ERRFLAG_ERROR; + return NULL; +} + +/* turn a string into a Complist struct: this has path components */ + +/**/ +static Complist +parsepat(char *str) +{ + long assert; + int ignore; + + patcompstart(); + /* + * Check for initial globbing flags, so that they don't form + * a bogus path component. + */ + if ((*str == zpc_special[ZPC_INPAR] && str[1] == zpc_special[ZPC_HASH]) || + (*str == zpc_special[ZPC_KSH_AT] && str[1] == Inpar && + str[2] == zpc_special[ZPC_HASH])) { + str += (*str == Inpar) ? 2 : 3; + if (!patgetglobflags(&str, &assert, &ignore)) + return NULL; + } + + /* Now there is no (#X) in front, we can check the path. */ + if (!pathbuf) + pathbuf = zalloc(pathbufsz = PATH_MAX+1); + DPUTS(pathbufcwd, "BUG: glob changed directory"); + if (*str == '/') { /* pattern has absolute path */ + str++; + pathbuf[0] = '/'; + pathbuf[pathpos = 1] = '\0'; + } else /* pattern is relative to pwd */ + pathbuf[pathpos = 0] = '\0'; + + return parsecomplist(str); +} + +/* get number after qualifier */ + +/**/ +static off_t +qgetnum(char **s) +{ + off_t v = 0; + + if (!idigit(**s)) { + zerr("number expected"); + return 0; + } + while (idigit(**s)) + v = v * 10 + *(*s)++ - '0'; + return v; +} + +/* get mode spec after qualifier */ + +/**/ +static zlong +qgetmodespec(char **s) +{ + zlong yes = 0, no = 0, val, mask, t; + char *p = *s, c, how, end; + + if ((c = *p) == '=' || c == Equals || c == '+' || c == '-' || + c == '?' || c == Quest || (c >= '0' && c <= '7')) { + end = 0; + c = 0; + } else { + end = (c == '<' ? '>' : + (c == '[' ? ']' : + (c == '{' ? '}' : + (c == Inang ? Outang : + (c == Inbrack ? Outbrack : + (c == Inbrace ? Outbrace : c)))))); + p++; + } + do { + mask = 0; + while (((c = *p) == 'u' || c == 'g' || c == 'o' || c == 'a') && end) { + switch (c) { + case 'o': mask |= 01007; break; + case 'g': mask |= 02070; break; + case 'u': mask |= 04700; break; + case 'a': mask |= 07777; break; + } + p++; + } + how = ((c == '+' || c == '-') ? c : '='); + if (c == '+' || c == '-' || c == '=' || c == Equals) + p++; + val = 0; + if (mask) { + while ((c = *p++) != ',' && c != end) { + switch (c) { + case 'x': val |= 00111; break; + case 'w': val |= 00222; break; + case 'r': val |= 00444; break; + case 's': val |= 06000; break; + case 't': val |= 01000; break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + t = ((zlong) c - '0'); + val |= t | (t << 3) | (t << 6); + break; + default: + zerr("invalid mode specification"); + return 0; + } + } + if (how == '=' || how == '+') { + yes |= val & mask; + val = ~val; + } + if (how == '=' || how == '-') + no |= val & mask; + } else if (!(end && c == end) && c != ',' && c) { + t = 07777; + while ((c = *p) == '?' || c == Quest || + (c >= '0' && c <= '7')) { + if (c == '?' || c == Quest) { + t = (t << 3) | 7; + val <<= 3; + } else { + t <<= 3; + val = (val << 3) | ((zlong) c - '0'); + } + p++; + } + if (end && c != end && c != ',') { + zerr("invalid mode specification"); + return 0; + } + if (how == '=') { + yes = (yes & ~t) | val; + no = (no & ~t) | (~val & ~t); + } else if (how == '+') + yes |= val; + else + no |= val; + } else { + zerr("invalid mode specification"); + return 0; + } + } while (end && c != end); + + *s = p; + return ((yes & 07777) | ((no & 07777) << 12)); +} + +static int +gmatchcmp(Gmatch a, Gmatch b) +{ + int i; + off_t r = 0L; + struct globsort *s; + char **asortstrp = NULL, **bsortstrp = NULL; + + for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) { + switch (s->tp & ~GS_DESC) { + case GS_NAME: + r = zstrcmp(b->uname, a->uname, + gf_numsort ? SORTIT_NUMERICALLY : 0); + break; + case GS_DEPTH: + { + char *aptr = a->name, *bptr = b->name; + int slasha = 0, slashb = 0; + /* Count slashes. Trailing slashes don't count. */ + while (*aptr && *aptr == *bptr) + aptr++, bptr++; + /* Like I just said... */ + if ((!*aptr || !*bptr) && aptr > a->name && aptr[-1] == '/') + aptr--, bptr--; + if (*aptr) + for (; aptr[1]; aptr++) + if (*aptr == '/') { + slasha = 1; + break; + } + if (*bptr) + for (; bptr[1]; bptr++) + if (*bptr == '/') { + slashb = 1; + break; + } + r = slasha - slashb; + } + break; + case GS_EXEC: + if (!asortstrp) { + asortstrp = a->sortstrs; + bsortstrp = b->sortstrs; + } else { + asortstrp++; + bsortstrp++; + } + r = zstrcmp(*bsortstrp, *asortstrp, + gf_numsort ? SORTIT_NUMERICALLY : 0); + break; + case GS_SIZE: + r = b->size - a->size; + break; + case GS_ATIME: + r = a->atime - b->atime; +#ifdef GET_ST_ATIME_NSEC + if (!r) + r = a->ansec - b->ansec; +#endif + break; + case GS_MTIME: + r = a->mtime - b->mtime; +#ifdef GET_ST_MTIME_NSEC + if (!r) + r = a->mnsec - b->mnsec; +#endif + break; + case GS_CTIME: + r = a->ctime - b->ctime; +#ifdef GET_ST_CTIME_NSEC + if (!r) + r = a->cnsec - b->cnsec; +#endif + break; + case GS_LINKS: + r = b->links - a->links; + break; + case GS__SIZE: + r = b->_size - a->_size; + break; + case GS__ATIME: + r = a->_atime - b->_atime; +#ifdef GET_ST_ATIME_NSEC + if (!r) + r = a->_ansec - b->_ansec; +#endif + break; + case GS__MTIME: + r = a->_mtime - b->_mtime; +#ifdef GET_ST_MTIME_NSEC + if (!r) + r = a->_mnsec - b->_mnsec; +#endif + break; + case GS__CTIME: + r = a->_ctime - b->_ctime; +#ifdef GET_ST_CTIME_NSEC + if (!r) + r = a->_cnsec - b->_cnsec; +#endif + break; + case GS__LINKS: + r = b->_links - a->_links; + break; + } + if (r) + return (s->tp & GS_DESC) ? + (r < 0L ? 1 : -1) : + (r > 0L ? 1 : -1); + } + return 0; +} + +/* + * Duplicate a list of qualifiers using the `next' linkage (not the + * `or' linkage). Return the head element and set *last (if last non-NULL) + * to point to the last element of the new list. All allocation is on the + * heap (or off the heap?) + */ +static struct qual *dup_qual_list(struct qual *orig, struct qual **lastp) +{ + struct qual *qfirst = NULL, *qlast = NULL; + + while (orig) { + struct qual *qnew = (struct qual *)zhalloc(sizeof(struct qual)); + *qnew = *orig; + qnew->next = qnew->or = NULL; + + if (!qfirst) + qfirst = qnew; + if (qlast) + qlast->next = qnew; + qlast = qnew; + + orig = orig->next; + } + + if (lastp) + *lastp = qlast; + return qfirst; +} + + +/* + * Get a glob string for execution, following e, P or + qualifiers. + * Pointer is character after the e, P or +. + */ + +/**/ +static char * +glob_exec_string(char **sp) +{ + char sav, *tt, *sdata, *s = *sp; + int plus; + + if (s[-1] == '+') { + plus = 0; + tt = itype_end(s, IIDENT, 0); + if (tt == s) + { + zerr("missing identifier after `+'"); + return NULL; + } + } else { + tt = get_strarg(s, &plus); + if (!*tt) + { + zerr("missing end of string"); + return NULL; + } + } + + sav = *tt; + *tt = '\0'; + sdata = dupstring(s + plus); + untokenize(sdata); + *tt = sav; + if (sav) + *sp = tt + plus; + else + *sp = tt; + + return sdata; +} + +/* + * Insert a glob match. + * If there were words to prepend given by the P glob qualifier, do so. + */ +static void +insert_glob_match(LinkList list, LinkNode next, char *data) +{ + if (gf_pre_words) { + LinkNode added; + for (added = firstnode(gf_pre_words); added; incnode(added)) { + next = insertlinknode(list, next, dupstring(getdata(added))); + } + } + + next = insertlinknode(list, next, data); + + if (gf_post_words) { + LinkNode added; + for (added = firstnode(gf_post_words); added; incnode(added)) { + next = insertlinknode(list, next, dupstring(getdata(added))); + } + } +} + +/* + * Return + * 1 if str ends in bare glob qualifiers + * 2 if str ends in non-bare glob qualifiers (#q) + * 0 otherwise. + * + * str is the string to check. + * sl is its length (to avoid recalculation). + * nobareglob is 1 if bare glob qualifiers are not allowed. + * *sp, if sp is not null, will be a pointer to the opening parenthesis. + */ + +/**/ +int +checkglobqual(char *str, int sl, int nobareglob, char **sp) +{ + char *s; + int paren, ret = 1; + + if (str[sl - 1] != Outpar) + return 0; + + /* Check these are really qualifiers, not a set of * + * alternatives or exclusions. We can be more * + * lenient with an explicit (#q) than with a bare * + * set of qualifiers. */ + paren = 0; + for (s = str + sl - 2; *s && (*s != Inpar || paren); s--) { + switch (*s) { + case Outpar: + paren++; /*FALLTHROUGH*/ + case Bar: + if (!zpc_disables[ZPC_BAR]) + nobareglob = 1; + break; + case Tilde: + if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_TILDE]) + nobareglob = 1; + break; + case Inpar: + paren--; + break; + } + if (s == str) + break; + } + if (*s != Inpar) + return 0; + if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH] && s[1] == Pound) { + if (s[2] != 'q') + return 0; + ret = 2; + } else if (nobareglob) + return 0; + + if (sp) + *sp = s; + + return ret; +} + +/* Main entry point to the globbing code for filename globbing. * + * np points to a node in the list which will be expanded * + * into a series of nodes. */ + +/**/ +void +zglob(LinkList list, LinkNode np, int nountok) +{ + struct qual *qo, *qn, *ql; + LinkNode node = prevnode(np); + char *str; /* the pattern */ + int sl; /* length of the pattern */ + Complist q; /* pattern after parsing */ + char *ostr = (char *)getdata(np); /* the pattern before the parser */ + /* chops it up */ + int first = 0, end = -1; /* index of first match to return */ + /* and index+1 of the last match */ + struct globdata saved; /* saved glob state */ + int nobareglob = !isset(BAREGLOBQUAL); + int shortcircuit = 0; /* How many files to match; */ + /* 0 means no limit */ + + if (unset(GLOBOPT) || !haswilds(ostr) || unset(EXECOPT)) { + if (!nountok) + untokenize(ostr); + return; + } + save_globstate(saved); + + str = dupstring(ostr); + uremnode(list, np); + + /* quals will hold the complete list of qualifiers (file static). */ + quals = NULL; + /* + * qualct and qualorct indicate we have qualifiers in the last + * alternative, or a set of alternatives, respectively. They + * are not necessarily an accurate count, however. + */ + qualct = qualorct = 0; + /* + * colonmod is a concatenated list of all colon modifiers found in + * all sets of qualifiers. + */ + colonmod = NULL; + /* The gf_* flags are qualifiers which are applied globally. */ + gf_nullglob = isset(NULLGLOB); + gf_markdirs = isset(MARKDIRS); + gf_listtypes = gf_follow = 0; + gf_noglobdots = unset(GLOBDOTS); + gf_numsort = isset(NUMERICGLOBSORT); + gf_sorts = gf_nsorts = 0; + gf_pre_words = gf_post_words = NULL; + + /* Check for qualifiers */ + while (!nobareglob || + (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH])) { + struct qual *newquals; + char *s; + int sense, qualsfound; + off_t data; + char *sdata, *newcolonmod, *ptr; + int (*func) _((char *, Statptr, off_t, char *)); + + /* + * Initialise state variables for current file pattern. + * newquals is the root for the linked list of all qualifiers. + * qo is the root of the current list of alternatives. + * ql is the end of the current alternative where the `next' will go. + * qn is the current qualifier node to be added. + * + * Here is an attempt at a diagram. An `or' is added horizontally + * to the top line, a `next' at the bottom of the right hand line. + * `qn' is usually NULL unless a new `or' has just been added. + * + * quals -> x -> x -> qo + * | | | + * x x x + * | | + * x ql + * + * In fact, after each loop the complete set is in the file static + * `quals'. Then, if we have a second set of qualifiers, we merge + * the lists together. This is only tricky if one or both have an + * `or' in them; then we need to distribute over all alternatives. + */ + newquals = qo = qn = ql = NULL; + + sl = strlen(str); + if (!(qualsfound = checkglobqual(str, sl, nobareglob, &s))) + break; + + /* Real qualifiers found. */ + nobareglob = 1; + sense = 0; /* bit 0 for match (0)/don't match (1) */ + /* bit 1 for follow links (2), don't (0) */ + data = 0; /* Any numerical argument required */ + sdata = NULL; /* Any list argument required */ + newcolonmod = NULL; /* Contains trailing colon modifiers */ + + str[sl-1] = 0; + *s++ = 0; + if (qualsfound == 2) + s += 2; + for (ptr = s; *ptr; ptr++) + if (*ptr == Dash) + *ptr = '-'; + while (*s && !newcolonmod) { + func = (int (*) _((char *, Statptr, off_t, char *)))0; + if (*s == ',') { + /* A comma separates alternative sets of qualifiers */ + s++; + sense = 0; + if (qualct) { + qn = (struct qual *)hcalloc(sizeof *qn); + qo->or = qn; + qo = qn; + qualorct++; + qualct = 0; + ql = NULL; + } + } else { + switch (*s++) { + case ':': + /* Remaining arguments are history-type * + * colon substitutions, handled separately. */ + newcolonmod = s - 1; + untokenize(newcolonmod); + if (colonmod) { + /* remember we're searching backwards */ + colonmod = dyncat(newcolonmod, colonmod); + } else + colonmod = newcolonmod; + break; + case Hat: + case '^': + /* Toggle sense: go from positive to * + * negative match and vice versa. */ + sense ^= 1; + break; + case '-': + case Dash: + /* Toggle matching of symbolic links */ + sense ^= 2; + break; + case '@': + /* Match symbolic links */ + func = qualislnk; + break; + case Equals: + case '=': + /* Match sockets */ + func = qualissock; + break; + case 'p': + /* Match named pipes */ + func = qualisfifo; + break; + case '/': + /* Match directories */ + func = qualisdir; + break; + case '.': + /* Match regular files */ + func = qualisreg; + break; + case '%': + /* Match special files: block, * + * character or any device */ + if (*s == 'b') + s++, func = qualisblk; + else if (*s == 'c') + s++, func = qualischr; + else + func = qualisdev; + break; + case Star: + /* Match executable plain files */ + func = qualiscom; + break; + case 'R': + /* Match world-readable files */ + func = qualflags; + data = 0004; + break; + case 'W': + /* Match world-writeable files */ + func = qualflags; + data = 0002; + break; + case 'X': + /* Match world-executable files */ + func = qualflags; + data = 0001; + break; + case 'A': + func = qualflags; + data = 0040; + break; + case 'I': + func = qualflags; + data = 0020; + break; + case 'E': + func = qualflags; + data = 0010; + break; + case 'r': + /* Match files readable by current process */ + func = qualflags; + data = 0400; + break; + case 'w': + /* Match files writeable by current process */ + func = qualflags; + data = 0200; + break; + case 'x': + /* Match files executable by current process */ + func = qualflags; + data = 0100; + break; + case 's': + /* Match setuid files */ + func = qualflags; + data = 04000; + break; + case 'S': + /* Match setgid files */ + func = qualflags; + data = 02000; + break; + case 't': + func = qualflags; + data = 01000; + break; + case 'd': + /* Match device files by device number * + * (as given by stat's st_dev element). */ + func = qualdev; + data = qgetnum(&s); + break; + case 'l': + /* Match files with the given no. of hard links */ + func = qualnlink; + g_amc = -1; + goto getrange; + case 'U': + /* Match files owned by effective user ID */ + func = qualuid; + data = geteuid(); + break; + case 'G': + /* Match files owned by effective group ID */ + func = qualgid; + data = getegid(); + break; + case 'u': + /* Match files owned by given user id */ + func = qualuid; + /* either the actual uid... */ + if (idigit(*s)) + data = qgetnum(&s); + else { + /* ... or a user name */ + char sav, *tt; + int arglen; + + /* Find matching delimiters */ + tt = get_strarg(s, &arglen); + if (!*tt) { + zerr("missing delimiter for 'u' glob qualifier"); + data = 0; + } else { +#ifdef USE_GETPWNAM + struct passwd *pw; + sav = *tt; + *tt = '\0'; + + if ((pw = getpwnam(s + arglen))) + data = pw->pw_uid; + else { + zerr("unknown username '%s'", s + arglen); + data = 0; + } + *tt = sav; +#else /* !USE_GETPWNAM */ + sav = *tt; + *tt = '\0'; + zerr("unable to resolve non-numeric username '%s'", s + arglen); + *tt = sav; + data = 0; +#endif /* !USE_GETPWNAM */ + if (sav) + s = tt + arglen; + else + s = tt; + } + } + break; + case 'g': + /* Given gid or group id... works like `u' */ + func = qualgid; + /* either the actual gid... */ + if (idigit(*s)) + data = qgetnum(&s); + else { + /* ...or a delimited group name. */ + char sav, *tt; + int arglen; + + tt = get_strarg(s, &arglen); + if (!*tt) { + zerr("missing delimiter for 'g' glob qualifier"); + data = 0; + } else { +#ifdef USE_GETGRNAM + struct group *gr; + sav = *tt; + *tt = '\0'; + + if ((gr = getgrnam(s + arglen))) + data = gr->gr_gid; + else { + zerr("unknown group"); + data = 0; + } + *tt = sav; +#else /* !USE_GETGRNAM */ + sav = *tt; + zerr("unknown group"); + data = 0; +#endif /* !USE_GETGRNAM */ + if (sav) + s = tt + arglen; + else + s = tt; + } + } + break; + case 'f': + /* Match modes with chmod-spec. */ + func = qualmodeflags; + data = qgetmodespec(&s); + break; + case 'F': + func = qualnonemptydir; + break; + case 'M': + /* Mark directories with a / */ + if ((gf_markdirs = !(sense & 1))) + gf_follow = sense & 2; + break; + case 'T': + /* Mark types in a `ls -F' type fashion */ + if ((gf_listtypes = !(sense & 1))) + gf_follow = sense & 2; + break; + case 'N': + /* Nullglob: remove unmatched patterns. */ + gf_nullglob = !(sense & 1); + break; + case 'D': + /* Glob dots: match leading dots implicitly */ + gf_noglobdots = sense & 1; + break; + case 'n': + /* Numeric glob sort */ + gf_numsort = !(sense & 1); + break; + case 'Y': + { + /* Short circuit: limit number of matches */ + const char *s_saved = s; + shortcircuit = !(sense & 1); + if (shortcircuit) { + /* Parse the argument. */ + data = qgetnum(&s); + if ((shortcircuit = data) != data) { + /* Integer overflow */ + zerr("value too big: Y%s", s_saved); + restore_globstate(saved); + return; + } + } + break; + } + case 'a': + /* Access time in given range */ + g_amc = 0; + func = qualtime; + goto getrange; + case 'm': + /* Modification time in given range */ + g_amc = 1; + func = qualtime; + goto getrange; + case 'c': + /* Inode creation time in given range */ + g_amc = 2; + func = qualtime; + goto getrange; + case 'L': + /* File size (Length) in given range */ + func = qualsize; + g_amc = -1; + /* Get size multiplier */ + g_units = TT_BYTES; + if (*s == 'p' || *s == 'P') + g_units = TT_POSIX_BLOCKS, ++s; + else if (*s == 'k' || *s == 'K') + g_units = TT_KILOBYTES, ++s; + else if (*s == 'm' || *s == 'M') + g_units = TT_MEGABYTES, ++s; +#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT) + else if (*s == 'g' || *s == 'G') + g_units = TT_GIGABYTES, ++s; + else if (*s == 't' || *s == 'T') + g_units = TT_TERABYTES, ++s; +#endif + getrange: + /* Get time multiplier */ + if (g_amc >= 0) { + g_units = TT_DAYS; + if (*s == 'h') + g_units = TT_HOURS, ++s; + else if (*s == 'm') + g_units = TT_MINS, ++s; + else if (*s == 'w') + g_units = TT_WEEKS, ++s; + else if (*s == 'M') + g_units = TT_MONTHS, ++s; + else if (*s == 's') + g_units = TT_SECONDS, ++s; + else if (*s == 'd') + ++s; + } + /* See if it's greater than, equal to, or less than */ + if ((g_range = *s == '+' ? 1 : IS_DASH(*s) ? -1 : 0)) + ++s; + data = qgetnum(&s); + break; + + case 'o': + case 'O': + { + int t; + char *send; + + if (gf_nsorts == MAX_SORTS) { + zerr("too many glob sort specifiers"); + restore_globstate(saved); + return; + } + + /* usually just one character */ + send = s+1; + switch (*s) { + case 'n': t = GS_NAME; break; + case 'L': t = GS_SIZE; break; + case 'l': t = GS_LINKS; break; + case 'a': t = GS_ATIME; break; + case 'm': t = GS_MTIME; break; + case 'c': t = GS_CTIME; break; + case 'd': t = GS_DEPTH; break; + case 'N': t = GS_NONE; break; + case 'e': + case '+': + { + t = GS_EXEC; + if ((gf_sortlist[gf_nsorts].exec = + glob_exec_string(&send)) == NULL) + { + restore_globstate(saved); + return; + } + break; + } + default: + zerr("unknown sort specifier"); + restore_globstate(saved); + return; + } + if ((sense & 2) && + (t & (GS_SIZE|GS_ATIME|GS_MTIME|GS_CTIME|GS_LINKS))) + t <<= GS_SHIFT; /* HERE: GS_EXEC? */ + if (t != GS_EXEC) { + if (gf_sorts & t) { + zerr("doubled sort specifier"); + restore_globstate(saved); + return; + } + } + gf_sorts |= t; + gf_sortlist[gf_nsorts++].tp = t | + (((sense & 1) ^ (s[-1] == 'O')) ? GS_DESC : 0); + s = send; + break; + } + case '+': + case 'e': + { + char *tt; + + tt = glob_exec_string(&s); + + if (tt == NULL) { + data = 0; + } else { + func = qualsheval; + sdata = tt; + } + break; + } + case '[': + case Inbrack: + { + char *os = --s; + struct value v; + + v.isarr = SCANPM_WANTVALS; + v.pm = NULL; + v.end = -1; + v.flags = 0; + if (getindex(&s, &v, 0) || s == os) { + zerr("invalid subscript"); + restore_globstate(saved); + return; + } + first = v.start; + end = v.end; + break; + } + case 'P': + { + char *tt; + tt = glob_exec_string(&s); + + if (tt != NULL) + { + LinkList *words = sense & 1 ? &gf_post_words : &gf_pre_words; + if (!*words) + *words = newlinklist(); + addlinknode(*words, tt); + } + break; + } + default: + untokenize(--s); + zerr("unknown file attribute: %c", *s); + restore_globstate(saved); + return; + } + } + if (func) { + /* Requested test is performed by function func */ + if (!qn) + qn = (struct qual *)hcalloc(sizeof *qn); + if (ql) + ql->next = qn; + ql = qn; + if (!newquals) + newquals = qo = qn; + qn->func = func; + qn->sense = sense; + qn->data = data; + qn->sdata = sdata; + qn->range = g_range; + qn->units = g_units; + qn->amc = g_amc; + + qn = NULL; + qualct++; + } + if (errflag) { + restore_globstate(saved); + return; + } + } + + if (quals && newquals) { + /* Merge previous group of qualifiers with new set. */ + if (quals->or || newquals->or) { + /* The hard case. */ + struct qual *qorhead = NULL, *qortail = NULL; + /* + * Distribute in the most trivial way, by creating + * all possible combinations of the two sets and chaining + * these into one long set of alternatives given + * by qorhead and qortail. + */ + for (qn = newquals; qn; qn = qn->or) { + for (qo = quals; qo; qo = qo->or) { + struct qual *qfirst, *qlast; + int islast = !qn->or && !qo->or; + /* Generate first set of qualifiers... */ + if (islast) { + /* Last time round: don't bother copying. */ + qfirst = qn; + for (qlast = qfirst; qlast->next; + qlast = qlast->next) + ; + } else + qfirst = dup_qual_list(qn, &qlast); + /* ... link into new `or' chain ... */ + if (!qorhead) + qorhead = qfirst; + if (qortail) + qortail->or = qfirst; + qortail = qfirst; + /* ... and concatenate second set. */ + qlast->next = islast ? qo : dup_qual_list(qo, NULL); + } + } + quals = qorhead; + } else { + /* + * Easy: we can just chain the qualifiers together. + * This is an optimisation; the code above will work, too. + * We retain the original left to right ordering --- remember + * we are searching for sets of qualifiers from the right. + */ + qn = newquals; + for ( ; newquals->next; newquals = newquals->next) + ; + newquals->next = quals; + quals = qn; + } + } else if (newquals) + quals = newquals; + } + q = parsepat(str); + if (!q || errflag) { /* if parsing failed */ + restore_globstate(saved); + if (unset(BADPATTERN)) { + if (!nountok) + untokenize(ostr); + insertlinknode(list, node, ostr); + return; + } + errflag &= ~ERRFLAG_ERROR; + zerr("bad pattern: %s", ostr); + return; + } + if (!gf_nsorts) { + gf_sortlist[0].tp = gf_sorts = (shortcircuit ? GS_NONE : GS_NAME); + gf_nsorts = 1; + } + /* Initialise receptacle for matched files, * + * expanded by insert() where necessary. */ + matchptr = matchbuf = (Gmatch)zalloc((matchsz = 16) * + sizeof(struct gmatch)); + matchct = 0; + pattrystart(); + + /* The actual processing takes place here: matches go into * + * matchbuf. This is the only top-level call to scanner(). */ + scanner(q, shortcircuit); + + /* Deal with failures to match depending on options */ + if (matchct) + badcshglob |= 2; /* at least one cmd. line expansion O.K. */ + else if (!gf_nullglob) { + if (isset(CSHNULLGLOB)) { + badcshglob |= 1; /* at least one cmd. line expansion failed */ + } else if (isset(NOMATCH)) { + zerr("no matches found: %s", ostr); + zfree(matchbuf, 0); + restore_globstate(saved); + return; + } else { + /* treat as an ordinary string */ + untokenize(matchptr->name = dupstring(ostr)); + matchptr++; + matchct = 1; + } + } + + if (!(gf_sortlist[0].tp & GS_NONE)) { + /* + * Get the strings to use for sorting by executing + * the code chunk. We allow more than one of these. + */ + int nexecs = 0; + struct globsort *sortp; + struct globsort *lastsortp = gf_sortlist + gf_nsorts; + Gmatch gmptr; + + /* First find out if there are any GS_EXECs, counting them. */ + for (sortp = gf_sortlist; sortp < lastsortp; sortp++) + { + if (sortp->tp & GS_EXEC) + nexecs++; + } + + if (nexecs) { + Gmatch tmpptr; + int iexec = 0; + + /* Yes; allocate enough space for strings for each */ + for (tmpptr = matchbuf; tmpptr < matchptr; tmpptr++) + tmpptr->sortstrs = (char **)zhalloc(nexecs*sizeof(char*)); + + /* Loop over each one, incrementing iexec */ + for (sortp = gf_sortlist; sortp < lastsortp; sortp++) + { + /* Ignore unless this is a GS_EXEC */ + if (sortp->tp & GS_EXEC) { + Eprog prog; + + if ((prog = parse_string(sortp->exec, 0))) { + int ef = errflag, lv = lastval; + + /* Parsed OK, execute for each name */ + for (tmpptr = matchbuf; tmpptr < matchptr; tmpptr++) { + setsparam("REPLY", ztrdup(tmpptr->name)); + execode(prog, 1, 0, "globsort"); + if (!errflag) + tmpptr->sortstrs[iexec] = + dupstring(getsparam("REPLY")); + else + tmpptr->sortstrs[iexec] = tmpptr->name; + } + + /* Retain any user interrupt error status */ + errflag = ef | (errflag & ERRFLAG_INT); + lastval = lv; + } else { + /* Failed, let's be safe */ + for (tmpptr = matchbuf; tmpptr < matchptr; tmpptr++) + tmpptr->sortstrs[iexec] = tmpptr->name; + } + + iexec++; + } + } + } + + /* + * Where necessary, create unmetafied version of names + * for comparison. If no Meta characters just point + * to original string. All on heap. + */ + for (gmptr = matchbuf; gmptr < matchptr; gmptr++) + { + if (strchr(gmptr->name, Meta)) + { + int dummy; + gmptr->uname = dupstring(gmptr->name); + unmetafy(gmptr->uname, &dummy); + } else { + gmptr->uname = gmptr->name; + } + } + + /* Sort arguments in to lexical (and possibly numeric) order. * + * This is reversed to facilitate insertion into the list. */ + qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch), + (int (*) _((const void *, const void *)))gmatchcmp); + } + + if (first < 0) { + first += matchct; + if (first < 0) + first = 0; + } + if (end < 0) + end += matchct + 1; + else if (end > matchct) + end = matchct; + if ((end -= first) > 0) { + if (gf_sortlist[0].tp & GS_NONE) { + /* Match list was never reversed, so insert back to front. */ + matchptr = matchbuf + matchct - first - 1; + while (end-- > 0) { + /* insert matches in the arg list */ + insert_glob_match(list, node, matchptr->name); + matchptr--; + } + } else { + matchptr = matchbuf + matchct - first - end; + while (end-- > 0) { + /* insert matches in the arg list */ + insert_glob_match(list, node, matchptr->name); + matchptr++; + } + } + } else if (!badcshglob && !isset(NOMATCH) && matchct == 1) { + insert_glob_match(list, node, (--matchptr)->name); + } + zfree(matchbuf, 0); + + restore_globstate(saved); +} + +/* Return the trailing character for marking file types */ + +/**/ +mod_export char +file_type(mode_t filemode) +{ + if(S_ISBLK(filemode)) + return '#'; + else if(S_ISCHR(filemode)) + return '%'; + else if(S_ISDIR(filemode)) + return '/'; + else if(S_ISFIFO(filemode)) + return '|'; + else if(S_ISLNK(filemode)) + return '@'; + else if(S_ISREG(filemode)) + return (filemode & S_IXUGO) ? '*' : ' '; + else if(S_ISSOCK(filemode)) + return '='; + else + return '?'; +} + +/* check to see if str is eligible for brace expansion */ + +/**/ +mod_export int +hasbraces(char *str) +{ + char *lbr, *mbr, *comma; + + if (isset(BRACECCL)) { + /* In this case, any properly formed brace expression * + * will match and expand to the characters in between. */ + int bc, c; + + for (bc = 0; (c = *str); ++str) + if (c == Inbrace) { + if (!bc && str[1] == Outbrace) + *str++ = '{', *str = '}'; + else + bc++; + } else if (c == Outbrace) { + if (!bc) + *str = '}'; + else if (!--bc) + return 1; + } + return 0; + } + /* Otherwise we need to look for... */ + lbr = mbr = comma = NULL; + for (;;) { + switch (*str++) { + case Inbrace: + if (!lbr) { + if (bracechardots(str-1, NULL, NULL)) + return 1; + lbr = str - 1; + if (IS_DASH(*str)) + str++; + while (idigit(*str)) + str++; + if (*str == '.' && str[1] == '.') { + str++; str++; + if (IS_DASH(*str)) + str++; + while (idigit(*str)) + str++; + if (*str == Outbrace && + (idigit(lbr[1]) || idigit(str[-1]))) + return 1; + else if (*str == '.' && str[1] == '.') { + str++; str++; + if (IS_DASH(*str)) + str++; + while (idigit(*str)) + str++; + if (*str == Outbrace && + (idigit(lbr[1]) || idigit(str[-1]))) + return 1; + } + } + } else { + char *s = --str; + + if (skipparens(Inbrace, Outbrace, &str)) { + *lbr = *s = '{'; + if (comma) + str = comma; + if (mbr && mbr < str) + str = mbr; + lbr = mbr = comma = NULL; + } else if (!mbr) + mbr = s; + } + break; + case Outbrace: + if (!lbr) + str[-1] = '}'; + else if (comma) + return 1; + else { + *lbr = '{'; + str[-1] = '}'; + if (mbr) + str = mbr; + mbr = lbr = NULL; + } + break; + case Comma: + if (!lbr) + str[-1] = ','; + else if (!comma) + comma = str - 1; + break; + case '\0': + if (lbr) + *lbr = '{'; + if (!mbr && !comma) + return 0; + if (comma) + str = comma; + if (mbr && mbr < str) + str = mbr; + lbr = mbr = comma = NULL; + break; + } + } +} + +/* expand stuff like >>*.c */ + +/**/ +int +xpandredir(struct redir *fn, LinkList redirtab) +{ + char *nam; + struct redir *ff; + int ret = 0; + local_list1(fake); + + /* Stick the name in a list... */ + init_list1(fake, fn->name); + /* ...which undergoes all the usual shell expansions */ + prefork(&fake, isset(MULTIOS) ? 0 : PREFORK_SINGLE, NULL); + /* Globbing is only done for multios. */ + if (!errflag && isset(MULTIOS)) + globlist(&fake, 0); + if (errflag) + return 0; + if (nonempty(&fake) && !nextnode(firstnode(&fake))) { + /* Just one match, the usual case. */ + char *s = peekfirst(&fake); + fn->name = s; + untokenize(s); + if (fn->type == REDIR_MERGEIN || fn->type == REDIR_MERGEOUT) { + if (IS_DASH(s[0]) && !s[1]) + fn->type = REDIR_CLOSE; + else if (s[0] == 'p' && !s[1]) + fn->fd2 = -2; + else { + while (idigit(*s)) + s++; + if (!*s && s > fn->name) + fn->fd2 = zstrtol(fn->name, NULL, 10); + else if (fn->type == REDIR_MERGEIN) + zerr("file number expected"); + else + fn->type = REDIR_ERRWRITE; + } + } + } else if (fn->type == REDIR_MERGEIN) + zerr("file number expected"); + else { + if (fn->type == REDIR_MERGEOUT) + fn->type = REDIR_ERRWRITE; + while ((nam = (char *)ugetnode(&fake))) { + /* Loop over matches, duplicating the * + * redirection for each file found. */ + ff = (struct redir *) zhalloc(sizeof *ff); + *ff = *fn; + ff->name = nam; + addlinknode(redirtab, ff); + ret = 1; + } + } + return ret; +} + +/* + * Check for a brace expansion of the form {..}. + * On input str must be positioned at an Inbrace, but the sequence + * of characters beyond that has not necessarily been checked. + * Return 1 if found else 0. + * + * The other parameters are optionaland if the function returns 1 are + * used to return: + * - *c1p: the first character in the expansion. + * - *c2p: the final character in the expansion. + */ + +/**/ +static int +bracechardots(char *str, convchar_t *c1p, convchar_t *c2p) +{ + convchar_t cstart, cend; + char *pnext = str + 1, *pconv, convstr[2]; + if (itok(*pnext)) { + if (*pnext == Inbrace) + return 0; + convstr[0] = ztokens[*pnext - Pound]; + convstr[1] = '\0'; + pconv = convstr; + } else + pconv = pnext; + MB_METACHARINIT(); + pnext += MB_METACHARLENCONV(pconv, &cstart); + if ( +#ifdef MULTIBYTE_SUPPORT + cstart == WEOF || +#else + !cstart || +#endif + pnext[0] != '.' || pnext[1] != '.') + return 0; + pnext += 2; + if (!*pnext) + return 0; + if (itok(*pnext)) { + if (*pnext == Inbrace) + return 0; + convstr[0] = ztokens[*pnext - Pound]; + convstr[1] = '\0'; + pconv = convstr; + } else + pconv = pnext; + MB_METACHARINIT(); + pnext += MB_METACHARLENCONV(pconv, &cend); + if ( +#ifdef MULTIBYTE_SUPPORT + cend == WEOF || +#else + !cend || +#endif + *pnext != Outbrace) + return 0; + if (c1p) + *c1p = cstart; + if (c2p) + *c2p = cend; + return 1; +} + +/* brace expansion */ + +/**/ +mod_export void +xpandbraces(LinkList list, LinkNode *np) +{ + LinkNode node = (*np), last = prevnode(node); + char *str = (char *)getdata(node), *str3 = str, *str2; + int prev, bc, comma, dotdot; + + for (; *str != Inbrace; str++); + /* First, match up braces and see what we have. */ + for (str2 = str, bc = comma = dotdot = 0; *str2; ++str2) + if (*str2 == Inbrace) + ++bc; + else if (*str2 == Outbrace) { + if (--bc == 0) + break; + } else if (bc == 1) { + if (*str2 == Comma) + ++comma; /* we have {foo,bar} */ + else if (*str2 == '.' && str2[1] == '.') { + dotdot++; /* we have {num1..num2} */ + ++str2; + } + } + DPUTS(bc, "BUG: unmatched brace in xpandbraces()"); + if (!comma && dotdot) { + /* Expand range like 0..10 numerically: comma or recursive + brace expansion take precedence. */ + char *dots, *p, *dots2 = NULL; + LinkNode olast = last; + /* Get the first number of the range */ + zlong rstart, rend; + int err = 0, rev = 0, rincr = 1; + int wid1, wid2, wid3, strp; + convchar_t cstart, cend; + + if (bracechardots(str, &cstart, &cend)) { + int lenalloc; + /* + * This is a character range. + */ + if (cend < cstart) { + convchar_t ctmp = cend; + cend = cstart; + cstart = ctmp; + rev = 1; + } + uremnode(list, node); + strp = str - str3; + lenalloc = strp + strlen(str2+1) + 1; + do { +#ifdef MULTIBYTE_SUPPORT + char *ncptr; + int nclen; + mb_charinit(); + ncptr = wcs_nicechar(cend, NULL, NULL); + nclen = strlen(ncptr); + p = zhalloc(lenalloc + nclen); + memcpy(p, str3, strp); + memcpy(p + strp, ncptr, nclen); + strcpy(p + strp + nclen, str2 + 1); +#else + p = zhalloc(lenalloc + 1); + memcpy(p, str3, strp); + sprintf(p + strp, "%c", cend); + strcat(p + strp, str2 + 1); +#endif + insertlinknode(list, last, p); + if (rev) /* decreasing: add in reverse order. */ + last = nextnode(last); + } while (cend-- > cstart); + *np = nextnode(olast); + return; + } + + /* Get the first number of the range */ + rstart = zstrtol(str+1,&dots,10); + rend = 0; + wid1 = (dots - str) - 1; + wid2 = (str2 - dots) - 2; + wid3 = 0; + strp = str - str3; + + if (dots == str + 1 || *dots != '.' || dots[1] != '.') + err++; + else { + /* Get the last number of the range */ + rend = zstrtol(dots+2,&p,10); + if (p == dots+2) + err++; + /* check for {num1..num2..incr} */ + if (p != str2) { + wid2 = (p - dots) - 2; + dots2 = p; + if (dotdot == 2 && *p == '.' && p[1] == '.') { + rincr = zstrtol(p+2, &p, 10); + wid3 = p - dots2 - 2; + if (p != str2 || !rincr) + err++; + } else + err++; + } + } + if (!err) { + /* If either no. begins with a zero, pad the output with * + * zeroes. Otherwise, set min width to 0 to suppress them. + * str+1 is the first number in the range, dots+2 the last, + * and dots2+2 is the increment if that's given. */ + /* TODO: sorry about this */ + int minw = (str[1] == '0' || + (IS_DASH(str[1]) && str[2] == '0')) + ? wid1 + : (dots[2] == '0' || + (IS_DASH(dots[2]) && dots[3] == '0')) + ? wid2 + : (dots2 && (dots2[2] == '0' || + (IS_DASH(dots2[2]) && dots2[3] == '0'))) + ? wid3 + : 0; + if (rincr < 0) { + /* Handle negative increment */ + rincr = -rincr; + rev = !rev; + } + if (rstart > rend) { + /* Handle decreasing ranges correctly. */ + zlong rt = rend; + rend = rstart; + rstart = rt; + rev = !rev; + } else if (rincr > 1) { + /* when incr > 1, range is aligned to the highest number of str1, + * compensate for this so that it is aligned to the first number */ + rend -= (rend - rstart) % rincr; + } + uremnode(list, node); + for (; rend >= rstart; rend -= rincr) { + /* Node added in at end, so do highest first */ + p = dupstring(str3); +#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) + sprintf(p + strp, "%0*lld", minw, rend); +#else + sprintf(p + strp, "%0*ld", minw, (long)rend); +#endif + strcat(p + strp, str2 + 1); + insertlinknode(list, last, p); + if (rev) /* decreasing: add in reverse order. */ + last = nextnode(last); + } + *np = nextnode(olast); + return; + } + } + if (!comma && isset(BRACECCL)) { /* {a-mnop} */ + /* Here we expand each character to a separate node, * + * but also ranges of characters like a-m. ccl is a * + * set of flags saying whether each character is present; * + * the final list is in lexical order. */ + char ccl[256], *p; + unsigned char c1, c2; + unsigned int len, pl; + int lastch = -1; + + uremnode(list, node); + memset(ccl, 0, sizeof(ccl) / sizeof(ccl[0])); + for (p = str + 1; p < str2;) { + if (itok(c1 = *p++)) + c1 = ztokens[c1 - STOUC(Pound)]; + if ((char) c1 == Meta) + c1 = 32 ^ *p++; + if (itok(c2 = *p)) + c2 = ztokens[c2 - STOUC(Pound)]; + if ((char) c2 == Meta) + c2 = 32 ^ p[1]; + if (IS_DASH((char)c1) && lastch >= 0 && + p < str2 && lastch <= (int)c2) { + while (lastch < (int)c2) + ccl[lastch++] = 1; + lastch = -1; + } else + ccl[lastch = c1] = 1; + } + pl = str - str3; + len = pl + strlen(++str2) + 2; + for (p = ccl + 256; p-- > ccl;) + if (*p) { + c1 = p - ccl; + if (imeta(c1)) { + str = hcalloc(len + 1); + str[pl] = Meta; + str[pl+1] = c1 ^ 32; + strcpy(str + pl + 2, str2); + } else { + str = hcalloc(len); + str[pl] = c1; + strcpy(str + pl + 1, str2); + } + memcpy(str, str3, pl); + insertlinknode(list, last, str); + } + *np = nextnode(last); + return; + } + prev = str++ - str3; + str2++; + uremnode(list, node); + node = last; + /* Finally, normal comma expansion * + * str1{foo,bar}str2 -> str1foostr2 str1barstr2. * + * Any number of intervening commas is allowed. */ + for (;;) { + char *zz, *str4; + int cnt; + + for (str4 = str, cnt = 0; cnt || (*str != Comma && *str != + Outbrace); str++) { + if (*str == Inbrace) + cnt++; + else if (*str == Outbrace) + cnt--; + DPUTS(!*str, "BUG: illegal brace expansion"); + } + /* Concatenate the string before the braces (str3), the section * + * just found (str4) and the text after the braces (str2) */ + zz = (char *) hcalloc(prev + (str - str4) + strlen(str2) + 1); + ztrncpy(zz, str3, prev); + strncat(zz, str4, str - str4); + strcat(zz, str2); + /* and add this text to the argument list. */ + insertlinknode(list, node, zz); + incnode(node); + if (*str != Outbrace) + str++; + else + break; + } + *np = nextnode(last); +} + +/* check to see if a matches b (b is not a filename pattern) */ + +/**/ +int +matchpat(char *a, char *b) +{ + Patprog p; + int ret; + + queue_signals(); /* Protect PAT_STATIC */ + + if (!(p = patcompile(b, PAT_STATIC, NULL))) { + zerr("bad pattern: %s", b); + ret = 0; + } else + ret = pattry(p, a); + + unqueue_signals(); + + return ret; +} + +/* do the ${foo%%bar}, ${foo#bar} stuff */ +/* please do not laugh at this code. */ + +/* Having found a match in getmatch, decide what part of string + * to return. The matched part starts b characters into string imd->ustr + * and finishes e characters in: 0 <= b <= e <= imd->ulen on input + * (yes, empty matches should work). + * + * imd->flags is a set of the SUB_* matches defined in zsh.h from + * SUB_MATCH onwards; the lower parts are ignored. + * + * imd->replstr is the replacement string for a substitution + * + * imd->replstr is metafied and the values put in imd->repllist are metafied. + */ + +/**/ +static char * +get_match_ret(Imatchdata imd, int b, int e) +{ + char buf[80], *r, *p, *rr, *replstr = imd->replstr; + int ll = 0, bl = 0, t = 0, add = 0, fl = imd->flags, i; + + /* Account for b and e referring to unmetafied string */ + for (p = imd->ustr; p < imd->ustr + b; p++) + if (imeta(*p)) + add++; + b += add; + for (; p < imd->ustr + e; p++) + if (imeta(*p)) + add++; + e += add; + + /* Everything now refers to metafied lengths. */ + if (replstr || (fl & SUB_LIST)) { + if (fl & SUB_DOSUBST) { + replstr = dupstring(replstr); + singsub(&replstr); + untokenize(replstr); + } + if ((fl & (SUB_GLOBAL|SUB_LIST)) && imd->repllist) { + /* We are replacing the chunk, just add this to the list */ + Repldata rd = (Repldata) + ((fl & SUB_LIST) ? zalloc(sizeof(*rd)) : zhalloc(sizeof(*rd))); + rd->b = b; + rd->e = e; + rd->replstr = replstr; + if (fl & SUB_LIST) + zaddlinknode(imd->repllist, rd); + else + addlinknode(imd->repllist, rd); + return imd->mstr; + } + ll += strlen(replstr); + } + if (fl & SUB_MATCH) /* matched portion */ + ll += 1 + (e - b); + if (fl & SUB_REST) /* unmatched portion */ + ll += 1 + (imd->mlen - (e - b)); + if (fl & SUB_BIND) { + /* position of start of matched portion */ + sprintf(buf, "%d ", MB_METASTRLEN2END(imd->mstr, 0, imd->mstr+b) + 1); + ll += (bl = strlen(buf)); + } + if (fl & SUB_EIND) { + /* position of end of matched portion */ + sprintf(buf + bl, "%d ", + MB_METASTRLEN2END(imd->mstr, 0, imd->mstr+e) + 1); + ll += (bl = strlen(buf)); + } + if (fl & SUB_LEN) { + /* length of matched portion */ + sprintf(buf + bl, "%d ", MB_METASTRLEN2END(imd->mstr+b, 0, + imd->mstr+e)); + ll += (bl = strlen(buf)); + } + if (bl) + buf[bl - 1] = '\0'; + + rr = r = (char *) hcalloc(ll); + + if (fl & SUB_MATCH) { + /* copy matched portion to new buffer */ + for (i = b, p = imd->mstr + b; i < e; i++) + *rr++ = *p++; + t = 1; + } + if (fl & SUB_REST) { + /* Copy unmatched portion to buffer. If both portions * + * requested, put a space in between (why?) */ + if (t) + *rr++ = ' '; + /* there may be unmatched bits at both beginning and end of string */ + for (i = 0, p = imd->mstr; i < b; i++) + *rr++ = *p++; + if (replstr) + for (p = replstr; *p; ) + *rr++ = *p++; + for (i = e, p = imd->mstr + e; i < imd->mlen; i++) + *rr++ = *p++; + t = 1; + } + *rr = '\0'; + if (bl) { + /* if there was a buffer (with a numeric result), add it; * + * if there was other stuff too, stick in a space first. */ + if (t) + *rr++ = ' '; + strcpy(rr, buf); + } + return r; +} + +static Patprog +compgetmatch(char *pat, int *flp, char **replstrp) +{ + Patprog p; + /* + * Flags to pattern compiler: use static buffer since we only + * have one pattern at a time; we will try the must-match test ourselves, + * so tell the pattern compiler we are scanning. + */ + + /* int patflags = PAT_STATIC|PAT_SCAN|PAT_NOANCH;*/ + + /* Unfortunately, PAT_STATIC doesn't work if we have a replstr with + * something like ${x#...} in it which will be singsub()ed below because + * that would overwrite the pattern buffer. */ + + int patflags = PAT_SCAN|PAT_NOANCH | (*replstrp ? 0 : PAT_STATIC); + + /* + * Search is anchored to the end of the string if we want to match + * it all, or if we are matching at the end of the string and not + * using substrings. + */ + if ((*flp & SUB_ALL) || ((*flp & SUB_END) && !(*flp & SUB_SUBSTR))) + patflags &= ~PAT_NOANCH; + p = patcompile(pat, patflags, NULL); + if (!p) { + zerr("bad pattern: %s", pat); + return NULL; + } + if (*replstrp) { + if (p->patnpar || (p->globend & GF_MATCHREF)) { + /* + * Either backreferences or match references, so we + * need to re-substitute replstr each time round. + */ + *flp |= SUB_DOSUBST; + } else { + singsub(replstrp); + untokenize(*replstrp); + } + } + + return p; +} + +/* + * This is called from paramsubst to get the match for ${foo#bar} etc. + * fl is a set of the SUB_* flags defined in zsh.h + * *sp points to the string we have to modify. The n'th match will be + * returned in *sp. The heap is used to get memory for the result string. + * replstr is the replacement string from a ${.../orig/repl}, in + * which case pat is the original. + * + * n is now ignored unless we are looking for a substring, in + * which case the n'th match from the start is counted such that + * there is no more than one match from each position. + */ + +/**/ +int +getmatch(char **sp, char *pat, int fl, int n, char *replstr) +{ + Patprog p; + + if (!(p = compgetmatch(pat, &fl, &replstr))) + return 1; + + return igetmatch(sp, p, fl, n, replstr, NULL); +} + +/* + * This is the corresponding function for array variables. + * Matching is done with the same pattern on each element. + */ + +/**/ +void +getmatcharr(char ***ap, char *pat, int fl, int n, char *replstr) +{ + char **arr = *ap, **pp; + Patprog p; + + if (!(p = compgetmatch(pat, &fl, &replstr))) + return; + + *ap = pp = hcalloc(sizeof(char *) * (arrlen(arr) + 1)); + while ((*pp = *arr++)) + if (igetmatch(pp, p, fl, n, replstr, NULL)) + pp++; +} + +/* + * Match against str using pattern pp; return a list of + * Repldata matches in the linked list *repllistp; this is + * in permanent storage and to be freed by freematchlist() + */ + +/**/ +mod_export int +getmatchlist(char *str, Patprog p, LinkList *repllistp) +{ + char **sp = &str; + + /* + * We don't care if we have longest or shortest match, but SUB_LONG + * is cheaper since the pattern code does that by default. + * We need SUB_GLOBAL to get all matches. + * We need SUB_SUBSTR to scan through for substrings. + * We need SUB_LIST to activate the special handling of the list + * passed in. + */ + return igetmatch(sp, p, SUB_LONG|SUB_GLOBAL|SUB_SUBSTR|SUB_LIST, + 0, NULL, repllistp); +} + +static void +freerepldata(void *ptr) +{ + zfree(ptr, sizeof(struct repldata)); +} + +/**/ +mod_export void +freematchlist(LinkList repllist) +{ + freelinklist(repllist, freerepldata); +} + +/**/ +static void +set_pat_start(Patprog p, int offs) +{ + /* + * If we are messing around with the test string by advancing up + * it from the start, we need to tell the pattern matcher that + * a start-of-string assertion, i.e. (#s), should fail. Hence + * we test whether the offset of the real start of string from + * the actual start, passed as offs, is zero. + */ + if (offs) + p->flags |= PAT_NOTSTART; + else + p->flags &= ~PAT_NOTSTART; +} + +/**/ +static void +set_pat_end(Patprog p, char null_me) +{ + /* + * If we are messing around with the string by shortening it at the + * tail, we need to tell the pattern matcher that an end-of-string + * assertion, i.e. (#e), should fail. Hence we test whether + * the character null_me about to be zapped is or is not already a null. + */ + if (null_me) + p->flags |= PAT_NOTEND; + else + p->flags &= ~PAT_NOTEND; +} + +/**/ +#ifdef MULTIBYTE_SUPPORT + +/* + * Increment *tp over character which may be multibyte. + * Return number of bytes. + * All unmetafied here. + */ + +/**/ +static int iincchar(char **tp, int left) +{ + char *t = *tp; + int mbclen = mb_charlenconv(t, left, NULL); + *tp = t + mbclen; + + return mbclen; +} + +/**/ +static int +igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, + LinkList *repllistp) +{ + char *s = *sp, *t, *tmatch, *send; + /* + * Note that ioff counts (possibly multibyte) characters in the + * character set (Meta's are not included), while l counts characters in + * the metafied string. + * + * umlen is a counter for (unmetafied) byte lengths---neither characters + * nor raw byte indices; this is simply an optimisation for allocation. + * umltot is the full length of the string in this scheme. + * + * l is the raw string length, used together with any pointers into + * the string (typically t). + */ + int ioff, l = strlen(*sp), matched = 1, umltot = ztrlen(*sp); + int umlen, nmatches; + struct patstralloc patstralloc; + struct imatchdata imd; + + (void)patallocstr(p, s, l, umltot, 1, &patstralloc); + s = patstralloc.alloced; + DPUTS(!s, "forced patallocstr failed"); + send = s + umltot; + + imd.mstr = *sp; + imd.mlen = l; + imd.ustr = s; + imd.ulen = umltot; + imd.flags = fl; + imd.replstr = replstr; + imd.repllist = NULL; + + /* perform must-match test for complex closures */ + if (p->mustoff) + { + char *muststr = (char *)p + p->mustoff; + + matched = 0; + if (p->patmlen <= umltot) + { + for (t = s; t <= send - p->patmlen; t++) + { + if (!memcmp(muststr, t, p->patmlen)) { + matched = 1; + break; + } + } + } + } + + /* in case we used the prog before... */ + p->flags &= ~(PAT_NOTSTART|PAT_NOTEND); + + if (fl & SUB_ALL) { + int i = matched && pattrylen(p, s, umltot, 0, &patstralloc, 0); + if (!i) { + /* Perform under no-match conditions */ + umltot = 0; + imd.replstr = NULL; + } + *sp = get_match_ret(&imd, 0, umltot); + if (! **sp && (((fl & SUB_MATCH) && !i) || ((fl & SUB_REST) && i))) + return 0; + return 1; + } + if (matched) { + /* + * The default behaviour is to match at the start; this + * is modified by SUB_END and SUB_SUBSTR. SUB_END matches + * at the end of the string instead of the start. SUB_SUBSTR + * without SUB_END matches substrings searching from the start; + * with SUB_END it matches substrings searching from the end. + * + * The possibilities are further modified by whether we want the + * longest (SUB_LONG) or shortest possible match. + * + * SUB_START is only used in the case where we are also + * forcing a match at the end (SUB_END with no SUB_SUBSTR, + * with or without SUB_LONG), to indicate we should match + * the entire string. + */ + switch (fl & (SUB_END|SUB_LONG|SUB_SUBSTR)) { + case 0: + case SUB_LONG: + /* + * Largest/smallest possible match at head of string. + * First get the longest match... + */ + if (pattrylen(p, s, umltot, 0, &patstralloc, 0)) { + /* patmatchlen returns unmetafied length in this case */ + int mlen = patmatchlen(); + if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { + send = s + mlen; + /* + * ... now we know whether it's worth looking for the + * shortest, which we do by brute force. + */ + mb_charinit(); + for (t = s, umlen = 0; t < send; ) { + set_pat_end(p, *t); + if (pattrylen(p, s, umlen, 0, &patstralloc, 0)) { + mlen = patmatchlen(); + break; + } + umlen += iincchar(&t, send - t); + } + } + *sp = get_match_ret(&imd, 0, mlen); + return 1; + } + break; + + case SUB_END: + /* + * Smallest possible match at tail of string. + * As we can only be sure we've got wide characters right + * when going forwards, we need to match at every point + * until we fail and record the last successful match. + * + * It's important that we return the last successful match + * so that match, mbegin, mend and MATCH, MBEGIN, MEND are + * correct. + */ + mb_charinit(); + tmatch = NULL; + for (ioff = 0, t = s, umlen = umltot; t < send; ioff++) { + set_pat_start(p, t-s); + if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) + tmatch = t; + if (fl & SUB_START) + break; + umlen -= iincchar(&t, send - t); + } + if (tmatch) { + *sp = get_match_ret(&imd, tmatch - s, umltot); + return 1; + } + if (!(fl & SUB_START) && pattrylen(p, s + umltot, 0, 0, + &patstralloc, ioff)) { + *sp = get_match_ret(&imd, umltot, umltot); + return 1; + } + break; + + case (SUB_END|SUB_LONG): + /* Largest possible match at tail of string: * + * move forward along string until we get a match. * + * Again there's no optimisation. */ + mb_charinit(); + for (ioff = 0, t = s, umlen = umltot; t <= send ; ioff++) { + set_pat_start(p, t-s); + if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) { + *sp = get_match_ret(&imd, t-s, umltot); + return 1; + } + if (fl & SUB_START) + break; + if (t == send) + break; + umlen -= iincchar(&t, send - t); + } + if (!(fl & SUB_START) && pattrylen(p, send, 0, 0, + &patstralloc, ioff)) { + *sp = get_match_ret(&imd, umltot, umltot); + return 1; + } + break; + + case SUB_SUBSTR: + /* Smallest at start, but matching substrings. */ + set_pat_start(p, l); + if (!(fl & SUB_GLOBAL) && + pattrylen(p, send, 0, 0, &patstralloc, 0) && + !--n) { + *sp = get_match_ret(&imd, 0, 0); + return 1; + } /* fall through */ + case (SUB_SUBSTR|SUB_LONG): + /* longest or smallest at start with substrings */ + t = s; + if (fl & SUB_GLOBAL) { + imd.repllist = (fl & SUB_LIST) ? znewlinklist() : newlinklist(); + if (repllistp) + *repllistp = imd.repllist; + } + ioff = 0; /* offset into string */ + umlen = umltot; + mb_charinit(); + do { + /* loop over all matches for global substitution */ + matched = 0; + for (; t <= send; ioff++) { + /* Find the longest match from this position. */ + set_pat_start(p, t-s); + if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) { + char *mpos = t + patmatchlen(); + if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { + char *ptr; + int umlen2; + /* + * If searching for the shortest match, + * start with a zero length and increase + * it until we reach the longest possible + * match, accepting the first successful + * match. + */ + for (ptr = t, umlen2 = 0; ptr < mpos;) { + set_pat_end(p, *ptr); + if (pattrylen(p, t, umlen2, 0, + &patstralloc, ioff)) { + mpos = t + patmatchlen(); + break; + } + umlen2 += iincchar(&ptr, mpos - ptr); + } + } + if (!--n || (n <= 0 && (fl & SUB_GLOBAL))) { + *sp = get_match_ret(&imd, t-s, mpos-s); + if (mpos == t) + mpos += mb_charlenconv(mpos, send - mpos, NULL); + } + if (!(fl & SUB_GLOBAL)) { + if (n) { + /* + * Looking for a later match: in this case, + * we can continue looking for matches from + * the next character, even if it overlaps + * with what we just found. + */ + umlen -= iincchar(&t, send - t); + continue; + } else { + return 1; + } + } + /* + * For a global match, we need to skip the stuff + * which is already marked for replacement. + */ + matched = 1; + if (t == send) + break; + while (t < mpos) { + ioff++; + umlen -= iincchar(&t, send - t); + } + break; + } + if (t == send) + break; + umlen -= iincchar(&t, send - t); + } + } while (matched && t < send); + /* + * check if we can match a blank string, if so do it + * at the start. Goodness knows if this is a good idea + * with global substitution, so it doesn't happen. + */ + set_pat_start(p, l); + if ((fl & (SUB_LONG|SUB_GLOBAL)) == SUB_LONG && + pattrylen(p, send, 0, 0, &patstralloc, 0) && !--n) { + *sp = get_match_ret(&imd, 0, 0); + return 1; + } + break; + + case (SUB_END|SUB_SUBSTR): + case (SUB_END|SUB_LONG|SUB_SUBSTR): + /* Longest/shortest at end, matching substrings. */ + if (!(fl & SUB_LONG)) { + set_pat_start(p, l); + if (pattrylen(p, send, 0, 0, &patstralloc, umltot) && + !--n) { + *sp = get_match_ret(&imd, umltot, umltot); + return 1; + } + } + /* + * If multibyte characters are present we need to start from the + * beginning. This is a bit unpleasant because we can't tell in + * advance how many times it will match and from where, so if n is + * greater then 1 we will need to count the number of times it + * matched and then go through again until we reach the right + * point. (Either that or record every single match in a list, + * which isn't stupid; it involves more memory management at this + * level but less use of the pattern matcher.) + */ + nmatches = 0; + tmatch = NULL; + mb_charinit(); + for (ioff = 0, t = s, umlen = umltot; t < send; ioff++) { + set_pat_start(p, t-s); + if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) { + nmatches++; + tmatch = t; + } + umlen -= iincchar(&t, send - t); + } + if (nmatches) { + char *mpos; + if (n > 1) { + /* + * We need to find the n'th last match. + */ + n = nmatches - n; + mb_charinit(); + for (ioff = 0, t = s, umlen = umltot; t < send; ioff++) { + set_pat_start(p, t-s); + if (pattrylen(p, t, umlen, 0, &patstralloc, ioff) && + !n--) { + tmatch = t; + break; + } + umlen -= iincchar(&t, send - t); + } + } + mpos = tmatch + patmatchlen(); + /* Look for the shortest match if necessary */ + if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { + for (t = tmatch, umlen = 0; t < mpos; ) { + set_pat_end(p, *t); + if (pattrylen(p, tmatch, umlen, 0, + &patstralloc, ioff)) { + mpos = tmatch + patmatchlen(); + break; + } + umlen += iincchar(&t, mpos - t); + } + } + *sp = get_match_ret(&imd, tmatch-s, mpos-s); + return 1; + } + set_pat_start(p, l); + if ((fl & SUB_LONG) && pattrylen(p, send, 0, 0, + &patstralloc, umltot) && + !--n) { + *sp = get_match_ret(&imd, umltot, umltot); + return 1; + } + break; + } + } + + if (imd.repllist && nonempty(imd.repllist)) { + /* Put all the bits of a global search and replace together. */ + LinkNode nd; + Repldata rd; + int lleft; + char *ptr, *start; + int i; + + /* + * Use metafied string again. + * Results from get_match_ret in repllist are all metafied. + */ + s = *sp; + if (!(fl & SUB_LIST)) { + lleft = 0; /* size of returned string */ + i = 0; /* start of last chunk we got from *sp */ + for (nd = firstnode(imd.repllist); nd; incnode(nd)) { + rd = (Repldata) getdata(nd); + lleft += rd->b - i; /* previous chunk of *sp */ + lleft += strlen(rd->replstr); /* the replaced bit */ + i = rd->e; /* start of next chunk of *sp */ + } + lleft += l - i; /* final chunk from *sp */ + start = t = zhalloc(lleft+1); + i = 0; + for (nd = firstnode(imd.repllist); nd; incnode(nd)) { + rd = (Repldata) getdata(nd); + memcpy(t, s + i, rd->b - i); + t += rd->b - i; + ptr = rd->replstr; + while (*ptr) + *t++ = *ptr++; + i = rd->e; + } + memcpy(t, s + i, l - i); + start[lleft] = '\0'; + *sp = (char *)start; + } + return 1; + } + if (fl & SUB_LIST) { /* safety: don't think this can happen */ + return 0; + } + + /* munge the whole string: no match, so no replstr */ + imd.replstr = NULL; + imd.repllist = NULL; + *sp = get_match_ret(&imd, 0, 0); + return (fl & SUB_RETFAIL) ? 0 : 1; +} + +/**/ +#else + +/* + * Increment pointer which may be on a Meta (x is a pointer variable), + * returning the incremented value (i.e. like pre-increment). + */ +#define METAINC(x) ((x) += (*(x) == Meta) ? 2 : 1) + +/**/ +static int +igetmatch(char **sp, Patprog p, int fl, int n, char *replstr, + LinkList *repllistp) +{ + char *s = *sp, *t, *send; + /* + * Note that ioff and uml count characters in the character + * set (Meta's are not included), while l counts characters in the + * metafied string. umlen is a counter for (unmetafied) character + * lengths. + */ + int ioff, l = strlen(*sp), uml = ztrlen(*sp), matched = 1, umlen; + struct patstralloc patstralloc; + struct imatchdata imd; + + (void)patallocstr(p, s, l, uml, 1, &patstralloc); + s = patstralloc.alloced; + DPUTS(!s, "forced patallocstr failed"); + send = s + uml; + + imd.mstr = *sp; + imd.mlen = l; + imd.ustr = s; + imd.ulen = uml; + imd.flags = fl; + imd.replstr = replstr; + imd.repllist = NULL; + + /* perform must-match test for complex closures */ + if (p->mustoff) + { + char *muststr = (char *)p + p->mustoff; + + matched = 0; + if (p->patmlen <= uml) + { + for (t = s; t <= send - p->patmlen; t++) + { + if (!memcmp(muststr, t, p->patmlen)) { + matched = 1; + break; + } + } + } + } + + /* in case we used the prog before... */ + p->flags &= ~(PAT_NOTSTART|PAT_NOTEND); + + if (fl & SUB_ALL) { + int i = matched && pattrylen(p, s, uml, 0, &patstralloc, 0); + if (!i) + imd.replstr = NULL; + *sp = get_match_ret(&imd, 0, i ? l : 0); + if (! **sp && (((fl & SUB_MATCH) && !i) || ((fl & SUB_REST) && i))) + return 0; + return 1; + } + if (matched) { + switch (fl & (SUB_END|SUB_LONG|SUB_SUBSTR)) { + case 0: + case SUB_LONG: + /* + * Largest/smallest possible match at head of string. + * First get the longest match... + */ + if (pattrylen(p, s, uml, 0, &patstralloc, 0)) { + /* patmatchlen returns metafied length, as we need */ + int mlen = patmatchlen(); + if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { + send = s + mlen; + /* + * ... now we know whether it's worth looking for the + * shortest, which we do by brute force. + */ + for (t = s, umlen = 0; t < s + mlen; METAINC(t), umlen++) { + set_pat_end(p, *t); + if (pattrylen(p, s, umlen, 0, &patstralloc, 0)) { + mlen = patmatchlen(); + break; + } + } + } + *sp = get_match_ret(&imd, 0, mlen); + return 1; + } + break; + + case SUB_END: + /* Smallest possible match at tail of string: * + * move back down string until we get a match. * + * There's no optimization here. */ + for (ioff = uml, t = send, umlen = 0; t >= s; + t--, ioff--, umlen++) { + set_pat_start(p, t-s); + if (pattrylen(p, t, umlen, 0, &patstralloc, ioff)) { + *sp = get_match_ret(&imd, t - s, uml); + return 1; + } + } + break; + + case (SUB_END|SUB_LONG): + /* Largest possible match at tail of string: * + * move forward along string until we get a match. * + * Again there's no optimisation. */ + for (ioff = 0, t = s, umlen = uml; t < send; + ioff++, t++, umlen--) { + set_pat_start(p, t-s); + if (pattrylen(p, t, send - t, umlen, &patstralloc, ioff)) { + *sp = get_match_ret(&imd, t-s, uml); + return 1; + } + } + break; + + case SUB_SUBSTR: + /* Smallest at start, but matching substrings. */ + set_pat_start(p, l); + if (!(fl & SUB_GLOBAL) && + pattrylen(p, send, 0, 0, &patstralloc, 0) && !--n) { + *sp = get_match_ret(&imd, 0, 0); + return 1; + } /* fall through */ + case (SUB_SUBSTR|SUB_LONG): + /* longest or smallest at start with substrings */ + t = s; + if (fl & SUB_GLOBAL) { + imd.repllist = newlinklist(); + if (repllistp) + *repllistp = imd.repllist; + } + ioff = 0; /* offset into string */ + umlen = uml; + do { + /* loop over all matches for global substitution */ + matched = 0; + for (; t < send; t++, ioff++, umlen--) { + /* Find the longest match from this position. */ + set_pat_start(p, t-s); + if (pattrylen(p, t, send - t, umlen, &patstralloc, ioff)) { + char *mpos = t + patmatchlen(); + if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { + char *ptr; + int umlen2; + for (ptr = t, umlen2 = 0; ptr < mpos; + ptr++, umlen2++) { + set_pat_end(p, *ptr); + if (pattrylen(p, t, ptr - t, umlen2, + &patstralloc, ioff)) { + mpos = t + patmatchlen(); + break; + } + } + } + if (!--n || (n <= 0 && (fl & SUB_GLOBAL))) { + *sp = get_match_ret(&imd, t-s, mpos-s); + if (mpos == t) + mpos++; + } + if (!(fl & SUB_GLOBAL)) { + if (n) { + /* + * Looking for a later match: in this case, + * we can continue looking for matches from + * the next character, even if it overlaps + * with what we just found. + */ + continue; + } else { + return 1; + } + } + /* + * For a global match, we need to skip the stuff + * which is already marked for replacement. + */ + matched = 1; + while (t < mpos) { + ioff++; + umlen--; + t++; + } + break; + } + } + } while (matched); + /* + * check if we can match a blank string, if so do it + * at the start. Goodness knows if this is a good idea + * with global substitution, so it doesn't happen. + */ + set_pat_start(p, l); + if ((fl & (SUB_LONG|SUB_GLOBAL)) == SUB_LONG && + pattrylen(p, send, 0, 0, &patstralloc, 0) && !--n) { + *sp = get_match_ret(&imd, 0, 0); + return 1; + } + break; + + case (SUB_END|SUB_SUBSTR): + case (SUB_END|SUB_LONG|SUB_SUBSTR): + /* Longest/shortest at end, matching substrings. */ + if (!(fl & SUB_LONG)) { + set_pat_start(p, l); + if (pattrylen(p, send, 0, 0, &patstralloc, uml) && !--n) { + *sp = get_match_ret(&imd, uml, uml); + return 1; + } + } + for (ioff = uml - 1, t = send - 1, umlen = 1; t >= s; + t--, ioff--, umlen++) { + set_pat_start(p, t-s); + if (pattrylen(p, t, send - t, umlen, &patstralloc, ioff) && + !--n) { + /* Found the longest match */ + char *mpos = t + patmatchlen(); + if (!(fl & SUB_LONG) && !(p->flags & PAT_PURES)) { + char *ptr; + int umlen2; + for (ptr = t, umlen2 = 0; ptr < mpos; + ptr++, umlen2++) { + set_pat_end(p, *ptr); + if (pattrylen(p, t, umlen2, 0, &patstralloc, + ioff)) { + mpos = t + patmatchlen(); + break; + } + } + } + *sp = get_match_ret(&imd, t-s, mpos-s); + return 1; + } + } + set_pat_start(p, l); + if ((fl & SUB_LONG) && pattrylen(p, send, 0, 0, + &patstralloc, uml) && + !--n) { + *sp = get_match_ret(&imd, uml, uml); + return 1; + } + break; + } + } + + if (imd.repllist && nonempty(imd.repllist)) { + /* Put all the bits of a global search and replace together. */ + LinkNode nd; + Repldata rd; + int lleft = 0; /* size of returned string */ + char *ptr, *start; + int i; + + /* + * Use metafied string again. + * Results from get_match_ret in repllist are all metafied. + */ + s = *sp; + i = 0; /* start of last chunk we got from *sp */ + for (nd = firstnode(imd.repllist); nd; incnode(nd)) { + rd = (Repldata) getdata(nd); + lleft += rd->b - i; /* previous chunk of *sp */ + lleft += strlen(rd->replstr); /* the replaced bit */ + i = rd->e; /* start of next chunk of *sp */ + } + lleft += l - i; /* final chunk from *sp */ + start = t = zhalloc(lleft+1); + i = 0; + for (nd = firstnode(imd.repllist); nd; incnode(nd)) { + rd = (Repldata) getdata(nd); + memcpy(t, s + i, rd->b - i); + t += rd->b - i; + ptr = rd->replstr; + while (*ptr) + *t++ = *ptr++; + i = rd->e; + } + memcpy(t, s + i, l - i); + start[lleft] = '\0'; + *sp = (char *)start; + return 1; + } + + /* munge the whole string: no match, so no replstr */ + imd.replstr = NULL; + imd.repllist = NULL; + *sp = get_match_ret(&imd, 0, 0); + return 1; +} + +/**/ +#endif /* MULTIBYTE_SUPPORT */ + +/* blindly turn a string into a tokenised expression without lexing */ + +/**/ +mod_export void +tokenize(char *s) +{ + zshtokenize(s, 0); +} + +/* + * shtokenize is used when we tokenize a string with GLOB_SUBST set. + * In that case we need to retain backslashes when we turn the + * pattern back into a string, so that the string is not + * modified if it failed to match a pattern. + * + * It may be modified by the effect of SH_GLOB which turns off + * various zsh-specific options. + */ + +/**/ +mod_export void +shtokenize(char *s) +{ + int flags = ZSHTOK_SUBST; + if (isset(SHGLOB)) + flags |= ZSHTOK_SHGLOB; + zshtokenize(s, flags); +} + +/**/ +static void +zshtokenize(char *s, int flags) +{ + char *t; + int bslash = 0; + + for (; *s; s++) { + cont: + switch (*s) { + case Meta: + /* skip both Meta and following character */ + s++; + break; + case Bnull: + case Bnullkeep: + case '\\': + if (bslash) { + s[-1] = (flags & ZSHTOK_SUBST) ? Bnullkeep : Bnull; + break; + } + bslash = 1; + continue; + case '<': + if (flags & ZSHTOK_SHGLOB) + break; + if (bslash) { + s[-1] = (flags & ZSHTOK_SUBST) ? Bnullkeep : Bnull; + break; + } + t = s; + while (idigit(*++s)); + if (!IS_DASH(*s)) + goto cont; + while (idigit(*++s)); + if (*s != '>') + goto cont; + *t = Inang; + *s = Outang; + break; + case '(': + case '|': + case ')': + if (flags & ZSHTOK_SHGLOB) + break; + /*FALLTHROUGH*/ + case '>': + case '^': + case '#': + case '~': + case '[': + case ']': + case '*': + case '?': + case '=': + case '-': + case '!': + for (t = ztokens; *t; t++) { + if (*t == *s) { + if (bslash) + s[-1] = (flags & ZSHTOK_SUBST) ? Bnullkeep : Bnull; + else + *s = (t - ztokens) + Pound; + break; + } + } + break; + } + bslash = 0; + } +} + +/* remove unnecessary Nulargs */ + +/**/ +mod_export void +remnulargs(char *s) +{ + if (*s) { + char *o = s, c; + + while ((c = *s++)) + if (c == Bnullkeep) { + /* + * An active backslash that needs to be turned back into + * a real backslash for output. However, we don't + * do that yet since we need to ignore it during + * pattern matching. + */ + continue; + } else if (inull(c)) { + char *t = s - 1; + + while ((c = *s++)) { + if (c == Bnullkeep) + *t++ = '\\'; + else if (!inull(c)) + *t++ = c; + } + *t = '\0'; + if (!*o) { + o[0] = Nularg; + o[1] = '\0'; + } + break; + } + } +} + +/* qualifier functions: mostly self-explanatory, see glob(). */ + +/* device number */ + +/**/ +static int +qualdev(UNUSED(char *name), struct stat *buf, off_t dv, UNUSED(char *dummy)) +{ + return (off_t)buf->st_dev == dv; +} + +/* number of hard links to file */ + +/**/ +static int +qualnlink(UNUSED(char *name), struct stat *buf, off_t ct, UNUSED(char *dummy)) +{ + return (g_range < 0 ? buf->st_nlink < ct : + g_range > 0 ? buf->st_nlink > ct : + buf->st_nlink == ct); +} + +/* user ID */ + +/**/ +static int +qualuid(UNUSED(char *name), struct stat *buf, off_t uid, UNUSED(char *dummy)) +{ + return buf->st_uid == uid; +} + +/* group ID */ + +/**/ +static int +qualgid(UNUSED(char *name), struct stat *buf, off_t gid, UNUSED(char *dummy)) +{ + return buf->st_gid == gid; +} + +/* device special file? */ + +/**/ +static int +qualisdev(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) +{ + return S_ISBLK(buf->st_mode) || S_ISCHR(buf->st_mode); +} + +/* block special file? */ + +/**/ +static int +qualisblk(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) +{ + return S_ISBLK(buf->st_mode); +} + +/* character special file? */ + +/**/ +static int +qualischr(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) +{ + return S_ISCHR(buf->st_mode); +} + +/* directory? */ + +/**/ +static int +qualisdir(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) +{ + return S_ISDIR(buf->st_mode); +} + +/* FIFO? */ + +/**/ +static int +qualisfifo(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) +{ + return S_ISFIFO(buf->st_mode); +} + +/* symbolic link? */ + +/**/ +static int +qualislnk(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) +{ + return S_ISLNK(buf->st_mode); +} + +/* regular file? */ + +/**/ +static int +qualisreg(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) +{ + return S_ISREG(buf->st_mode); +} + +/* socket? */ + +/**/ +static int +qualissock(UNUSED(char *name), struct stat *buf, UNUSED(off_t junk), UNUSED(char *dummy)) +{ + return S_ISSOCK(buf->st_mode); +} + +/* given flag is set in mode */ + +/**/ +static int +qualflags(UNUSED(char *name), struct stat *buf, off_t mod, UNUSED(char *dummy)) +{ + return mode_to_octal(buf->st_mode) & mod; +} + +/* mode matches specification */ + +/**/ +static int +qualmodeflags(UNUSED(char *name), struct stat *buf, off_t mod, UNUSED(char *dummy)) +{ + long v = mode_to_octal(buf->st_mode), y = mod & 07777, n = mod >> 12; + + return ((v & y) == y && !(v & n)); +} + +/* regular executable file? */ + +/**/ +static int +qualiscom(UNUSED(char *name), struct stat *buf, UNUSED(off_t mod), UNUSED(char *dummy)) +{ + return S_ISREG(buf->st_mode) && (buf->st_mode & S_IXUGO); +} + +/* size in required range? */ + +/**/ +static int +qualsize(UNUSED(char *name), struct stat *buf, off_t size, UNUSED(char *dummy)) +{ +#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT) +# define QS_CAST_SIZE() + zlong scaled = buf->st_size; +#else +# define QS_CAST_SIZE() (unsigned long) + unsigned long scaled = (unsigned long)buf->st_size; +#endif + + switch (g_units) { + case TT_POSIX_BLOCKS: + scaled += 511l; + scaled /= 512l; + break; + case TT_KILOBYTES: + scaled += 1023l; + scaled /= 1024l; + break; + case TT_MEGABYTES: + scaled += 1048575l; + scaled /= 1048576l; + break; +#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT) + case TT_GIGABYTES: + scaled += ZLONG_CONST(1073741823); + scaled /= ZLONG_CONST(1073741824); + break; + case TT_TERABYTES: + scaled += ZLONG_CONST(1099511627775); + scaled /= ZLONG_CONST(1099511627776); + break; +#endif + } + + return (g_range < 0 ? scaled < QS_CAST_SIZE() size : + g_range > 0 ? scaled > QS_CAST_SIZE() size : + scaled == QS_CAST_SIZE() size); +#undef QS_CAST_SIZE +} + +/* time in required range? */ + +/**/ +static int +qualtime(UNUSED(char *name), struct stat *buf, off_t days, UNUSED(char *dummy)) +{ + time_t now, diff; + + time(&now); + diff = now - (g_amc == 0 ? buf->st_atime : g_amc == 1 ? buf->st_mtime : + buf->st_ctime); + /* handle multipliers indicating units */ + switch (g_units) { + case TT_DAYS: + diff /= 86400l; + break; + case TT_HOURS: + diff /= 3600l; + break; + case TT_MINS: + diff /= 60l; + break; + case TT_WEEKS: + diff /= 604800l; + break; + case TT_MONTHS: + diff /= 2592000l; + break; + } + + return (g_range < 0 ? diff < days : + g_range > 0 ? diff > days : + diff == days); +} + +/* evaluate a string */ + +/**/ +static int +qualsheval(char *name, UNUSED(struct stat *buf), UNUSED(off_t days), char *str) +{ + Eprog prog; + + if ((prog = parse_string(str, 0))) { + int ef = errflag, lv = lastval, ret; + int cshglob = badcshglob; + + unsetparam("reply"); + setsparam("REPLY", ztrdup(name)); + badcshglob = 0; + + execode(prog, 1, 0, "globqual"); + + if ((ret = lastval)) + badcshglob |= cshglob; + /* Retain any user interrupt error status */ + errflag = ef | (errflag & ERRFLAG_INT); + lastval = lv; + + if (!(inserts = getaparam("reply")) && + !(inserts = gethparam("reply"))) { + char *tmp; + + if ((tmp = getsparam("reply")) || (tmp = getsparam("REPLY"))) { + static char *tmparr[2]; + + tmparr[0] = tmp; + tmparr[1] = NULL; + + inserts = tmparr; + } + } + + return !ret; + } + return 0; +} + +/**/ +static int +qualnonemptydir(char *name, struct stat *buf, UNUSED(off_t days), UNUSED(char *str)) +{ + DIR *dirh; + struct dirent *de; + int unamelen; + char *uname = unmetafy(dupstring(name), &unamelen); + + if (!S_ISDIR(buf->st_mode)) + return 0; + + if (buf->st_nlink > 2) + return 1; + + if (!(dirh = opendir(uname))) + return 0; + + while ((de = readdir(dirh))) { + if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) { + closedir(dirh); + return 1; + } + } + + closedir(dirh); + return 0; +} diff --git a/dotfiles/system/.zsh/modules/Src/hashtable.c b/dotfiles/system/.zsh/modules/Src/hashtable.c new file mode 100644 index 0000000..b7baa31 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/hashtable.c @@ -0,0 +1,1617 @@ +/* + * hashtable.c - hash tables + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "../config.h" + +#ifdef ZSH_HASH_DEBUG +# define HASHTABLE_DEBUG_MEMBERS \ + /* Members of struct hashtable used for debugging hash tables */ \ + HashTable next, last; /* linked list of all hash tables */ \ + char *tablename; /* string containing name of the hash table */ \ + PrintTableStats printinfo; /* pointer to function to print table stats */ +#else /* !ZSH_HASH_DEBUG */ +# define HASHTABLE_DEBUG_MEMBERS +#endif /* !ZSH_HASH_DEBUG */ + +#define HASHTABLE_INTERNAL_MEMBERS \ + ScanStatus scan; /* status of a scan over this hashtable */ \ + HASHTABLE_DEBUG_MEMBERS + +typedef struct scanstatus *ScanStatus; + +#include "zsh.mdh" +#include "hashtable.pro" + +/* Structure for recording status of a hashtable scan in progress. When a * + * scan starts, the .scan member of the hashtable structure points to one * + * of these. That member being non-NULL disables resizing of the * + * hashtable (when adding elements). When elements are deleted, the * + * contents of this structure is used to make sure the scan won't stumble * + * into the deleted element. */ + +struct scanstatus { + int sorted; + union { + struct { + HashNode *hashtab; + int ct; + } s; + HashNode u; + } u; +}; + +/********************************/ +/* Generic Hash Table functions */ +/********************************/ + +#ifdef ZSH_HASH_DEBUG +static HashTable firstht, lastht; +#endif /* ZSH_HASH_DEBUG */ + +/* Generic hash function */ + +/**/ +mod_export unsigned +hasher(const char *str) +{ + unsigned hashval = 0, c; + + while ((c = *((unsigned char *) str++))) + hashval += (hashval << 5) + c; + + return hashval; +} + +/* Get a new hash table */ + +/**/ +mod_export HashTable +newhashtable(int size, UNUSED(char const *name), UNUSED(PrintTableStats printinfo)) +{ + HashTable ht; + + ht = (HashTable) zshcalloc(sizeof *ht); +#ifdef ZSH_HASH_DEBUG + ht->next = NULL; + if(!firstht) + firstht = ht; + ht->last = lastht; + if(lastht) + lastht->next = ht; + lastht = ht; + ht->printinfo = printinfo ? printinfo : printhashtabinfo; + ht->tablename = ztrdup(name); +#endif /* ZSH_HASH_DEBUG */ + ht->nodes = (HashNode *) zshcalloc(size * sizeof(HashNode)); + ht->hsize = size; + ht->ct = 0; + ht->scan = NULL; + ht->scantab = NULL; + return ht; +} + +/* Delete a hash table. After this function has been used, any * + * existing pointers to the hash table are invalid. */ + +/**/ +mod_export void +deletehashtable(HashTable ht) +{ + ht->emptytable(ht); +#ifdef ZSH_HASH_DEBUG + if(ht->next) + ht->next->last = ht->last; + else + lastht = ht->last; + if(ht->last) + ht->last->next = ht->next; + else + firstht = ht->next; + zsfree(ht->tablename); +#endif /* ZSH_HASH_DEBUG */ + zfree(ht->nodes, ht->hsize * sizeof(HashNode)); + zfree(ht, sizeof(*ht)); +} + +/* Add a node to a hash table. * + * nam is the key to use in hashing. nodeptr points * + * to the node to add. If there is already a node in * + * the table with the same key, it is first freed, and * + * then the new node is added. If the number of nodes * + * is now greater than twice the number of hash values, * + * the table is then expanded. */ + +/**/ +mod_export void +addhashnode(HashTable ht, char *nam, void *nodeptr) +{ + HashNode oldnode = addhashnode2(ht, nam, nodeptr); + if (oldnode) + ht->freenode(oldnode); +} + +/* Add a node to a hash table, returning the old node on replacement. */ + +/**/ +HashNode +addhashnode2(HashTable ht, char *nam, void *nodeptr) +{ + unsigned hashval; + HashNode hn, hp, hq; + + hn = (HashNode) nodeptr; + hn->nam = nam; + + hashval = ht->hash(hn->nam) % ht->hsize; + hp = ht->nodes[hashval]; + + /* check if this is the first node for this hash value */ + if (!hp) { + hn->next = NULL; + ht->nodes[hashval] = hn; + if (++ht->ct >= ht->hsize * 2 && !ht->scan) + expandhashtable(ht); + return NULL; + } + + /* else check if the first node contains the same key */ + if (ht->cmpnodes(hp->nam, hn->nam) == 0) { + ht->nodes[hashval] = hn; + replacing: + hn->next = hp->next; + if(ht->scan) { + if(ht->scan->sorted) { + HashNode *hashtab = ht->scan->u.s.hashtab; + int i; + for(i = ht->scan->u.s.ct; i--; ) + if(hashtab[i] == hp) + hashtab[i] = hn; + } else if(ht->scan->u.u == hp) + ht->scan->u.u = hn; + } + return hp; + } + + /* else run through the list and check all the keys */ + hq = hp; + hp = hp->next; + for (; hp; hq = hp, hp = hp->next) { + if (ht->cmpnodes(hp->nam, hn->nam) == 0) { + hq->next = hn; + goto replacing; + } + } + + /* else just add it at the front of the list */ + hn->next = ht->nodes[hashval]; + ht->nodes[hashval] = hn; + if (++ht->ct >= ht->hsize * 2 && !ht->scan) + expandhashtable(ht); + return NULL; +} + +/* Get an enabled entry in a hash table. * + * If successful, it returns a pointer to * + * the hashnode. If the node is DISABLED * + * or isn't found, it returns NULL */ + +/**/ +mod_export HashNode +gethashnode(HashTable ht, const char *nam) +{ + unsigned hashval; + HashNode hp; + + hashval = ht->hash(nam) % ht->hsize; + for (hp = ht->nodes[hashval]; hp; hp = hp->next) { + if (ht->cmpnodes(hp->nam, nam) == 0) { + if (hp->flags & DISABLED) + return NULL; + else + return hp; + } + } + return NULL; +} + +/* Get an entry in a hash table. It will * + * ignore the DISABLED flag and return a * + * pointer to the hashnode if found, else * + * it returns NULL. */ + +/**/ +mod_export HashNode +gethashnode2(HashTable ht, const char *nam) +{ + unsigned hashval; + HashNode hp; + + hashval = ht->hash(nam) % ht->hsize; + for (hp = ht->nodes[hashval]; hp; hp = hp->next) { + if (ht->cmpnodes(hp->nam, nam) == 0) + return hp; + } + return NULL; +} + +/* Remove an entry from a hash table. * + * If successful, it removes the node from the * + * table and returns a pointer to it. If there * + * is no such node, then it returns NULL */ + +/**/ +mod_export HashNode +removehashnode(HashTable ht, const char *nam) +{ + unsigned hashval; + HashNode hp, hq; + + hashval = ht->hash(nam) % ht->hsize; + hp = ht->nodes[hashval]; + + /* if no nodes at this hash value, return NULL */ + if (!hp) + return NULL; + + /* else check if the key in the first one matches */ + if (ht->cmpnodes(hp->nam, nam) == 0) { + ht->nodes[hashval] = hp->next; + gotit: + ht->ct--; + if(ht->scan) { + if(ht->scan->sorted) { + HashNode *hashtab = ht->scan->u.s.hashtab; + int i; + for(i = ht->scan->u.s.ct; i--; ) + if(hashtab[i] == hp) + hashtab[i] = NULL; + } else if(ht->scan->u.u == hp) + ht->scan->u.u = hp->next; + } + return hp; + } + + /* else run through the list and check the rest of the keys */ + hq = hp; + hp = hp->next; + for (; hp; hq = hp, hp = hp->next) { + if (ht->cmpnodes(hp->nam, nam) == 0) { + hq->next = hp->next; + goto gotit; + } + } + + /* else it is not in the list, so return NULL */ + return NULL; +} + +/* Disable a node in a hash table */ + +/**/ +void +disablehashnode(HashNode hn, UNUSED(int flags)) +{ + hn->flags |= DISABLED; +} + +/* Enable a node in a hash table */ + +/**/ +void +enablehashnode(HashNode hn, UNUSED(int flags)) +{ + hn->flags &= ~DISABLED; +} + +/* Compare two hash table entries by name */ + +/**/ +static int +hnamcmp(const void *ap, const void *bp) +{ + HashNode a = *(HashNode *)ap; + HashNode b = *(HashNode *)bp; + return ztrcmp(a->nam, b->nam); +} + +/* Scan the nodes in a hash table and execute scanfunc on nodes based on + * the flags that are set/unset. scanflags is passed unchanged to + * scanfunc (if executed). + * + * If sorted != 0, then sort entries of hash table before scanning. + * If flags1 > 0, then execute scanfunc on a node only if at least one of + * these flags is set. + * If flags2 > 0, then execute scanfunc on a node only if all of + * these flags are NOT set. + * The conditions above for flags1/flags2 must both be true. + * + * It is safe to add, remove or replace hash table elements from within + * the scanfunc. Replaced elements will appear in the scan exactly once, + * the new version if it was not scanned before the replacement was made. + * Added elements might or might not appear in the scan. + * + * pprog, if non-NULL, is a pattern that must match the name + * of the node. + * + * The function returns the number of matches, as reduced by pprog, flags1 + * and flags2. + */ + +/**/ +mod_export int +scanmatchtable(HashTable ht, Patprog pprog, int sorted, + int flags1, int flags2, ScanFunc scanfunc, int scanflags) +{ + int match = 0; + struct scanstatus st; + + /* + * scantab is currently only used by modules to scan + * tables where the contents are generated on the fly from + * other objects. Note the fact that in this case pprog, + * sorted, flags1 and flags2 are ignore. + */ + if (!pprog && ht->scantab) { + ht->scantab(ht, scanfunc, scanflags); + return ht->ct; + } + if (sorted) { + int i, ct = ht->ct; + VARARR(HashNode, hnsorttab, ct); + HashNode *htp, hn; + + /* + * Because the structure might change under our feet, + * we can't apply the flags and the pattern before sorting, + * tempting though that is. + */ + for (htp = hnsorttab, i = 0; i < ht->hsize; i++) + for (hn = ht->nodes[i]; hn; hn = hn->next) + *htp++ = hn; + qsort((void *)hnsorttab, ct, sizeof(HashNode), hnamcmp); + + st.sorted = 1; + st.u.s.hashtab = hnsorttab; + st.u.s.ct = ct; + ht->scan = &st; + + for (htp = hnsorttab, i = 0; i < ct; i++, htp++) { + if ((!flags1 || ((*htp)->flags & flags1)) && + !((*htp)->flags & flags2) && + (!pprog || pattry(pprog, (*htp)->nam))) { + match++; + scanfunc(*htp, scanflags); + } + } + + ht->scan = NULL; + } else { + int i, hsize = ht->hsize; + HashNode *nodes = ht->nodes; + + st.sorted = 0; + ht->scan = &st; + + for (i = 0; i < hsize; i++) + for (st.u.u = nodes[i]; st.u.u; ) { + HashNode hn = st.u.u; + st.u.u = st.u.u->next; + if ((!flags1 || (hn->flags & flags1)) && !(hn->flags & flags2) + && (!pprog || pattry(pprog, hn->nam))) { + match++; + scanfunc(hn, scanflags); + } + } + + ht->scan = NULL; + } + + return match; +} + + +/**/ +mod_export int +scanhashtable(HashTable ht, int sorted, int flags1, int flags2, + ScanFunc scanfunc, int scanflags) +{ + return scanmatchtable(ht, NULL, sorted, flags1, flags2, + scanfunc, scanflags); +} + +/* Expand hash tables when they get too many entries. * + * The new size is 4 times the previous size. */ + +/**/ +static void +expandhashtable(HashTable ht) +{ + struct hashnode **onodes, **ha, *hn, *hp; + int i, osize; + + osize = ht->hsize; + onodes = ht->nodes; + + ht->hsize = osize * 4; + ht->nodes = (HashNode *) zshcalloc(ht->hsize * sizeof(HashNode)); + ht->ct = 0; + + /* scan through the old list of nodes, and * + * rehash them into the new list of nodes */ + for (i = 0, ha = onodes; i < osize; i++, ha++) { + for (hn = *ha; hn;) { + hp = hn->next; + ht->addnode(ht, hn->nam, hn); + hn = hp; + } + } + zfree(onodes, osize * sizeof(HashNode)); +} + +/* Empty the hash table and resize it if necessary */ + +/**/ +static void +resizehashtable(HashTable ht, int newsize) +{ + struct hashnode **ha, *hn, *hp; + int i; + + /* free all the hash nodes */ + ha = ht->nodes; + for (i = 0; i < ht->hsize; i++, ha++) { + for (hn = *ha; hn;) { + hp = hn->next; + ht->freenode(hn); + hn = hp; + } + } + + /* If new size desired is different from current size, * + * we free it and allocate a new nodes array. */ + if (ht->hsize != newsize) { + zfree(ht->nodes, ht->hsize * sizeof(HashNode)); + ht->nodes = (HashNode *) zshcalloc(newsize * sizeof(HashNode)); + ht->hsize = newsize; + } else { + /* else we just re-zero the current nodes array */ + memset(ht->nodes, 0, newsize * sizeof(HashNode)); + } + + ht->ct = 0; +} + +/* Generic method to empty a hash table */ + +/**/ +mod_export void +emptyhashtable(HashTable ht) +{ + resizehashtable(ht, ht->hsize); +} + +/**/ +#ifdef ZSH_HASH_DEBUG + +/* Print info about hash table */ + +#define MAXDEPTH 7 + +/**/ +static void +printhashtabinfo(HashTable ht) +{ + HashNode hn; + int chainlen[MAXDEPTH + 1]; + int i, tmpcount, total; + + printf("name of table : %s\n", ht->tablename); + printf("size of nodes[] : %d\n", ht->hsize); + printf("number of nodes : %d\n\n", ht->ct); + + memset(chainlen, 0, sizeof(chainlen)); + + /* count the number of nodes just to be sure */ + total = 0; + for (i = 0; i < ht->hsize; i++) { + tmpcount = 0; + for (hn = ht->nodes[i]; hn; hn = hn->next) + tmpcount++; + if (tmpcount >= MAXDEPTH) + chainlen[MAXDEPTH]++; + else + chainlen[tmpcount]++; + total += tmpcount; + } + + for (i = 0; i < MAXDEPTH; i++) + printf("number of hash values with chain of length %d : %4d\n", i, chainlen[i]); + printf("number of hash values with chain of length %d+ : %4d\n", MAXDEPTH, chainlen[MAXDEPTH]); + printf("total number of nodes : %4d\n", total); +} + +/**/ +int +bin_hashinfo(UNUSED(char *nam), UNUSED(char **args), UNUSED(Options ops), UNUSED(int func)) +{ + HashTable ht; + + printf("----------------------------------------------------\n"); + queue_signals(); + for(ht = firstht; ht; ht = ht->next) { + ht->printinfo(ht); + printf("----------------------------------------------------\n"); + } + unqueue_signals(); + return 0; +} + +/**/ +#endif /* ZSH_HASH_DEBUG */ + +/********************************/ +/* Command Hash Table Functions */ +/********************************/ + +/* hash table containing external commands */ + +/**/ +mod_export HashTable cmdnamtab; + +/* how far we've hashed the PATH so far */ + +/**/ +mod_export char **pathchecked; + +/* Create a new command hash table */ + +/**/ +void +createcmdnamtable(void) +{ + cmdnamtab = newhashtable(201, "cmdnamtab", NULL); + + cmdnamtab->hash = hasher; + cmdnamtab->emptytable = emptycmdnamtable; + cmdnamtab->filltable = fillcmdnamtable; + cmdnamtab->cmpnodes = strcmp; + cmdnamtab->addnode = addhashnode; + cmdnamtab->getnode = gethashnode2; + cmdnamtab->getnode2 = gethashnode2; + cmdnamtab->removenode = removehashnode; + cmdnamtab->disablenode = NULL; + cmdnamtab->enablenode = NULL; + cmdnamtab->freenode = freecmdnamnode; + cmdnamtab->printnode = printcmdnamnode; + + pathchecked = path; +} + +/**/ +static void +emptycmdnamtable(HashTable ht) +{ + emptyhashtable(ht); + pathchecked = path; +} + +/* Add all commands in a given directory * + * to the command hashtable. */ + +/**/ +void +hashdir(char **dirp) +{ + Cmdnam cn; + DIR *dir; + char *fn, *unmetadir, *pathbuf, *pathptr; + int dirlen; +#if defined(_WIN32) || defined(__CYGWIN__) + char *exe; +#endif /* _WIN32 || _CYGWIN__ */ + + if (isrelative(*dirp)) + return; + unmetadir = unmeta(*dirp); + if (!(dir = opendir(unmetadir))) + return; + + dirlen = strlen(unmetadir); + pathbuf = (char *)zalloc(dirlen + PATH_MAX + 2); + sprintf(pathbuf, "%s/", unmetadir); + pathptr = pathbuf + dirlen + 1; + + while ((fn = zreaddir(dir, 1))) { + if (!cmdnamtab->getnode(cmdnamtab, fn)) { + char *fname = ztrdup(fn); + struct stat statbuf; + int add = 0, dummylen; + + unmetafy(fn, &dummylen); + if (strlen(fn) > PATH_MAX) { + /* Too heavy to do all the allocation */ + add = 1; + } else { + strcpy(pathptr, fn); + /* + * This is the same test as for the glob qualifier for + * executable plain files. + */ + if (unset(HASHEXECUTABLESONLY) || + (access(pathbuf, X_OK) == 0 && + stat(pathbuf, &statbuf) == 0 && + S_ISREG(statbuf.st_mode) && (statbuf.st_mode & S_IXUGO))) + add = 1; + } + if (add) { + cn = (Cmdnam) zshcalloc(sizeof *cn); + cn->node.flags = 0; + cn->u.name = dirp; + cmdnamtab->addnode(cmdnamtab, fname, cn); + } else + zsfree(fname); + } +#if defined(_WIN32) || defined(__CYGWIN__) + /* Hash foo.exe as foo, since when no real foo exists, foo.exe + will get executed by DOS automatically. This quiets + spurious corrections when CORRECT or CORRECT_ALL is set. */ + if ((exe = strrchr(fn, '.')) && + (exe[1] == 'E' || exe[1] == 'e') && + (exe[2] == 'X' || exe[2] == 'x') && + (exe[3] == 'E' || exe[3] == 'e') && exe[4] == 0) { + *exe = 0; + if (!cmdnamtab->getnode(cmdnamtab, fn)) { + cn = (Cmdnam) zshcalloc(sizeof *cn); + cn->node.flags = 0; + cn->u.name = dirp; + cmdnamtab->addnode(cmdnamtab, ztrdup(fn), cn); + } + } +#endif /* _WIN32 || __CYGWIN__ */ + } + closedir(dir); + zfree(pathbuf, dirlen + PATH_MAX + 2); +} + +/* Go through user's PATH and add everything to * + * the command hashtable. */ + +/**/ +static void +fillcmdnamtable(UNUSED(HashTable ht)) +{ + char **pq; + + for (pq = pathchecked; *pq; pq++) + hashdir(pq); + + pathchecked = pq; +} + +/**/ +static void +freecmdnamnode(HashNode hn) +{ + Cmdnam cn = (Cmdnam) hn; + + zsfree(cn->node.nam); + if (cn->node.flags & HASHED) + zsfree(cn->u.cmd); + + zfree(cn, sizeof(struct cmdnam)); +} + +/* Print an element of the cmdnamtab hash table (external command) */ + +/**/ +static void +printcmdnamnode(HashNode hn, int printflags) +{ + Cmdnam cn = (Cmdnam) hn; + + if (printflags & PRINT_WHENCE_WORD) { + printf("%s: %s\n", cn->node.nam, (cn->node.flags & HASHED) ? + "hashed" : "command"); + return; + } + + if ((printflags & PRINT_WHENCE_CSH) || (printflags & PRINT_WHENCE_SIMPLE)) { + if (cn->node.flags & HASHED) { + zputs(cn->u.cmd, stdout); + putchar('\n'); + } else { + zputs(*(cn->u.name), stdout); + putchar('/'); + zputs(cn->node.nam, stdout); + putchar('\n'); + } + return; + } + + if (printflags & PRINT_WHENCE_VERBOSE) { + if (cn->node.flags & HASHED) { + nicezputs(cn->node.nam, stdout); + printf(" is hashed to "); + nicezputs(cn->u.cmd, stdout); + putchar('\n'); + } else { + nicezputs(cn->node.nam, stdout); + printf(" is "); + nicezputs(*(cn->u.name), stdout); + putchar('/'); + nicezputs(cn->node.nam, stdout); + putchar('\n'); + } + return; + } + + if (printflags & PRINT_LIST) { + printf("hash "); + + if(cn->node.nam[0] == '-') + printf("-- "); + } + + if (cn->node.flags & HASHED) { + quotedzputs(cn->node.nam, stdout); + putchar('='); + quotedzputs(cn->u.cmd, stdout); + putchar('\n'); + } else { + quotedzputs(cn->node.nam, stdout); + putchar('='); + quotedzputs(*(cn->u.name), stdout); + putchar('/'); + quotedzputs(cn->node.nam, stdout); + putchar('\n'); + } +} + +/***************************************/ +/* Shell Function Hash Table Functions */ +/***************************************/ + +/* hash table containing the shell functions */ + +/**/ +mod_export HashTable shfunctab; + +/**/ +void +createshfunctable(void) +{ + shfunctab = newhashtable(7, "shfunctab", NULL); + + shfunctab->hash = hasher; + shfunctab->emptytable = NULL; + shfunctab->filltable = NULL; + shfunctab->cmpnodes = strcmp; + shfunctab->addnode = addhashnode; + shfunctab->getnode = gethashnode; + shfunctab->getnode2 = gethashnode2; + shfunctab->removenode = removeshfuncnode; + shfunctab->disablenode = disableshfuncnode; + shfunctab->enablenode = enableshfuncnode; + shfunctab->freenode = freeshfuncnode; + shfunctab->printnode = printshfuncnode; +} + +/* Remove an entry from the shell function hash table. * + * It checks if the function is a signal trap and if so, * + * it will disable the trapping of that signal. */ + +/**/ +static HashNode +removeshfuncnode(UNUSED(HashTable ht), const char *nam) +{ + HashNode hn; + int signum; + + if (!strncmp(nam, "TRAP", 4) && (signum = getsignum(nam + 4)) != -1) + hn = removetrap(signum); + else + hn = removehashnode(shfunctab, nam); + + return hn; +} + +/* Disable an entry in the shell function hash table. * + * It checks if the function is a signal trap and if so, * + * it will disable the trapping of that signal. */ + +/**/ +static void +disableshfuncnode(HashNode hn, UNUSED(int flags)) +{ + hn->flags |= DISABLED; + if (!strncmp(hn->nam, "TRAP", 4)) { + int signum = getsignum(hn->nam + 4); + if (signum != -1) { + sigtrapped[signum] &= ~ZSIG_FUNC; + unsettrap(signum); + } + } +} + +/* Re-enable an entry in the shell function hash table. * + * It checks if the function is a signal trap and if so, * + * it will re-enable the trapping of that signal. */ + +/**/ +static void +enableshfuncnode(HashNode hn, UNUSED(int flags)) +{ + Shfunc shf = (Shfunc) hn; + + shf->node.flags &= ~DISABLED; + if (!strncmp(shf->node.nam, "TRAP", 4)) { + int signum = getsignum(shf->node.nam + 4); + if (signum != -1) { + settrap(signum, NULL, ZSIG_FUNC); + } + } +} + +/**/ +static void +freeshfuncnode(HashNode hn) +{ + Shfunc shf = (Shfunc) hn; + + zsfree(shf->node.nam); + if (shf->funcdef) + freeeprog(shf->funcdef); + if (shf->redir) + freeeprog(shf->redir); + dircache_set(&shf->filename, NULL); + if (shf->sticky) { + if (shf->sticky->n_on_opts) + zfree(shf->sticky->on_opts, + shf->sticky->n_on_opts * sizeof(*shf->sticky->on_opts)); + if (shf->sticky->n_off_opts) + zfree(shf->sticky->off_opts, + shf->sticky->n_off_opts * sizeof(*shf->sticky->off_opts)); + zfree(shf->sticky, sizeof(*shf->sticky)); + } + zfree(shf, sizeof(struct shfunc)); +} + +/* Print a shell function */ + +/**/ +static void +printshfuncnode(HashNode hn, int printflags) +{ + Shfunc f = (Shfunc) hn; + char *t = 0; + + if ((printflags & PRINT_NAMEONLY) || + ((printflags & PRINT_WHENCE_SIMPLE) && + !(printflags & PRINT_WHENCE_FUNCDEF))) { + zputs(f->node.nam, stdout); + putchar('\n'); + return; + } + + if ((printflags & (PRINT_WHENCE_VERBOSE|PRINT_WHENCE_WORD)) && + !(printflags & PRINT_WHENCE_FUNCDEF)) { + nicezputs(f->node.nam, stdout); + printf((printflags & PRINT_WHENCE_WORD) ? ": function" : + (f->node.flags & PM_UNDEFINED) ? + " is an autoload shell function" : + " is a shell function"); + if ((printflags & PRINT_WHENCE_VERBOSE) && f->filename) { + printf(" from "); + quotedzputs(f->filename, stdout); + if (f->node.flags & PM_LOADDIR) { + printf("/"); + quotedzputs(f->node.nam, stdout); + } + } + putchar('\n'); + return; + } + + quotedzputs(f->node.nam, stdout); + if (f->funcdef || f->node.flags & PM_UNDEFINED) { + 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", hashchar); + zoutputtab(stdout); + } + if (!t) { + char *fopt = "UtTkzc"; + int flgs[] = { + PM_UNALIASED, PM_TAGGED, PM_TAGGED_LOCAL, + PM_KSHSTORED, PM_ZSHSTORED, PM_CUR_FPATH, 0 + }; + int fl;; + + zputs("builtin autoload -X", stdout); + for (fl=0;fopt[fl];fl++) + if (f->node.flags & flgs[fl]) putchar(fopt[fl]); + if (f->filename && (f->node.flags & PM_LOADDIR)) { + putchar(' '); + zputs(f->filename, stdout); + } + } else { + zputs(t, stdout); + zsfree(t); + if (f->funcdef->flags & EF_RUN) { + printf("\n"); + zoutputtab(stdout); + quotedzputs(f->node.nam, stdout); + printf(" \"$@\""); + } + } + printf("\n}"); + } else { + printf(" () { }"); + } + if (f->redir) { + t = getpermtext(f->redir, NULL, 1); + if (t) { + zputs(t, stdout); + zsfree(t); + } + } + + 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; +} + +/* + * Get a heap-duplicated name of the shell function, for + * use in tracing. + */ + +/**/ +mod_export char * +getshfuncfile(Shfunc shf) +{ + if (shf->node.flags & PM_LOADDIR) { + return zhtricat(shf->filename, "/", shf->node.nam); + } else if (shf->filename) { + return dupstring(shf->filename); + } else { + return NULL; + } +} + +/**************************************/ +/* Reserved Word Hash Table Functions */ +/**************************************/ + +/* Nodes for reserved word hash table */ + +static struct reswd reswds[] = { + {{NULL, "!", 0}, BANG}, + {{NULL, "[[", 0}, DINBRACK}, + {{NULL, "{", 0}, INBRACE}, + {{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} +}; + +/* hash table containing the reserved words */ + +/**/ +mod_export HashTable reswdtab; + +/* Build the hash table containing zsh's reserved words. */ + +/**/ +void +createreswdtable(void) +{ + Reswd rw; + + reswdtab = newhashtable(23, "reswdtab", NULL); + + reswdtab->hash = hasher; + reswdtab->emptytable = NULL; + reswdtab->filltable = NULL; + reswdtab->cmpnodes = strcmp; + reswdtab->addnode = addhashnode; + reswdtab->getnode = gethashnode; + reswdtab->getnode2 = gethashnode2; + reswdtab->removenode = NULL; + reswdtab->disablenode = disablehashnode; + reswdtab->enablenode = enablehashnode; + reswdtab->freenode = NULL; + reswdtab->printnode = printreswdnode; + + for (rw = reswds; rw->node.nam; rw++) + reswdtab->addnode(reswdtab, rw->node.nam, rw); +} + +/* Print a reserved word */ + +/**/ +static void +printreswdnode(HashNode hn, int printflags) +{ + Reswd rw = (Reswd) hn; + + if (printflags & PRINT_WHENCE_WORD) { + printf("%s: reserved\n", rw->node.nam); + return; + } + + if (printflags & PRINT_WHENCE_CSH) { + printf("%s: shell reserved word\n", rw->node.nam); + return; + } + + if (printflags & PRINT_WHENCE_VERBOSE) { + printf("%s is a reserved word\n", rw->node.nam); + return; + } + + /* default is name only */ + printf("%s\n", rw->node.nam); +} + +/********************************/ +/* Aliases Hash Table Functions */ +/********************************/ + +/* hash table containing the aliases */ + +/**/ +mod_export HashTable aliastab; + +/* has table containing suffix aliases */ + +/**/ +mod_export HashTable sufaliastab; + +/* Create new hash tables for aliases */ + +/**/ +void +createaliastable(HashTable ht) +{ + ht->hash = hasher; + ht->emptytable = NULL; + ht->filltable = NULL; + ht->cmpnodes = strcmp; + ht->addnode = addhashnode; + ht->getnode = gethashnode; + ht->getnode2 = gethashnode2; + ht->removenode = removehashnode; + ht->disablenode = disablehashnode; + ht->enablenode = enablehashnode; + ht->freenode = freealiasnode; + ht->printnode = printaliasnode; +} + +/**/ +void +createaliastables(void) +{ + /* Table for regular and global aliases */ + + aliastab = newhashtable(23, "aliastab", NULL); + + createaliastable(aliastab); + + /* add the default aliases */ + aliastab->addnode(aliastab, ztrdup("run-help"), createaliasnode(ztrdup("man"), 0)); + aliastab->addnode(aliastab, ztrdup("which-command"), createaliasnode(ztrdup("whence"), 0)); + + + /* Table for suffix aliases --- make this smaller */ + + sufaliastab = newhashtable(11, "sufaliastab", NULL); + + createaliastable(sufaliastab); +} + +/* Create a new alias node */ + +/**/ +mod_export Alias +createaliasnode(char *txt, int flags) +{ + Alias al; + + al = (Alias) zshcalloc(sizeof *al); + al->node.flags = flags; + al->text = txt; + al->inuse = 0; + return al; +} + +/**/ +static void +freealiasnode(HashNode hn) +{ + Alias al = (Alias) hn; + + zsfree(al->node.nam); + zsfree(al->text); + zfree(al, sizeof(struct alias)); +} + +/* Print an alias */ + +/**/ +static void +printaliasnode(HashNode hn, int printflags) +{ + Alias a = (Alias) hn; + + if (printflags & PRINT_NAMEONLY) { + zputs(a->node.nam, stdout); + putchar('\n'); + return; + } + + if (printflags & PRINT_WHENCE_WORD) { + if (a->node.flags & ALIAS_SUFFIX) + printf("%s: suffix alias\n", a->node.nam); + else if (a->node.flags & ALIAS_GLOBAL) + printf("%s: global alias\n", a->node.nam); + else + printf("%s: alias\n", a->node.nam); + return; + } + + if (printflags & PRINT_WHENCE_SIMPLE) { + zputs(a->text, stdout); + putchar('\n'); + return; + } + + if (printflags & PRINT_WHENCE_CSH) { + nicezputs(a->node.nam, stdout); + printf(": "); + if (a->node.flags & ALIAS_SUFFIX) + printf("suffix "); + else if (a->node.flags & ALIAS_GLOBAL) + printf("globally "); + printf ("aliased to "); + nicezputs(a->text, stdout); + putchar('\n'); + return; + } + + if (printflags & PRINT_WHENCE_VERBOSE) { + nicezputs(a->node.nam, stdout); + printf(" is a"); + if (a->node.flags & ALIAS_SUFFIX) + printf(" suffix"); + else if (a->node.flags & ALIAS_GLOBAL) + printf(" global"); + else + printf("n"); + printf(" alias for "); + nicezputs(a->text, stdout); + putchar('\n'); + return; + } + + if (printflags & PRINT_LIST) { + /* Fast fail on unrepresentable values. */ + if (strchr(a->node.nam, '=')) { + zwarn("invalid alias '%s' encountered while printing aliases", + a->node.nam); + /* ### TODO: Return an error status to the C caller */ + return; + } + + /* Normal path. */ + printf("alias "); + if (a->node.flags & ALIAS_SUFFIX) + printf("-s "); + else if (a->node.flags & ALIAS_GLOBAL) + printf("-g "); + + /* If an alias begins with `-' or `+', then we must output `-- ' + * first, so that it is not interpreted as an option. */ + if(a->node.nam[0] == '-' || a->node.nam[0] == '+') + printf("-- "); + } + + quotedzputs(a->node.nam, stdout); + putchar('='); + quotedzputs(a->text, stdout); + + putchar('\n'); +} + +/*************************************/ +/* History Line Hash Table Functions */ +/*************************************/ + +/**/ +void +createhisttable(void) +{ + histtab = newhashtable(599, "histtab", NULL); + + histtab->hash = histhasher; + histtab->emptytable = emptyhisttable; + histtab->filltable = NULL; + histtab->cmpnodes = histstrcmp; + histtab->addnode = addhistnode; + histtab->getnode = gethashnode2; + histtab->getnode2 = gethashnode2; + histtab->removenode = removehashnode; + histtab->disablenode = NULL; + histtab->enablenode = NULL; + histtab->freenode = freehistnode; + histtab->printnode = NULL; +} + +/**/ +unsigned +histhasher(const char *str) +{ + unsigned hashval = 0; + + while (inblank(*str)) str++; + + while (*str) { + if (inblank(*str)) { + do str++; while (inblank(*str)); + if (*str) + hashval += (hashval << 5) + ' '; + } + else + hashval += (hashval << 5) + *(unsigned char *)str++; + } + return hashval; +} + +/**/ +void +emptyhisttable(HashTable ht) +{ + emptyhashtable(ht); + if (hist_ring) + histremovedups(); +} + +/* Compare two strings with normalized white-space */ + +/**/ +int +histstrcmp(const char *str1, const char *str2) +{ + while (inblank(*str1)) str1++; + while (inblank(*str2)) str2++; + while (*str1 && *str2) { + if (inblank(*str1)) { + if (!inblank(*str2)) + break; + do str1++; while (inblank(*str1)); + do str2++; while (inblank(*str2)); + } + else { + if (*str1 != *str2) + break; + str1++; + str2++; + } + } + return *str1 - *str2; +} + +/**/ +void +addhistnode(HashTable ht, char *nam, void *nodeptr) +{ + HashNode oldnode = addhashnode2(ht, nam, nodeptr); + Histent he = (Histent)nodeptr; + if (oldnode && oldnode != (HashNode)nodeptr) { + if (he->node.flags & HIST_MAKEUNIQUE + || (he->node.flags & HIST_FOREIGN && (Histent)oldnode == he->up)) { + (void) addhashnode2(ht, oldnode->nam, oldnode); /* restore hash */ + he->node.flags |= HIST_DUP; + he->node.flags &= ~HIST_MAKEUNIQUE; + } + else { + oldnode->flags |= HIST_DUP; + if (hist_ignore_all_dups) + freehistnode(oldnode); /* Remove the old dup */ + } + } + else + he->node.flags &= ~HIST_MAKEUNIQUE; +} + +/**/ +void +freehistnode(HashNode nodeptr) +{ + freehistdata((Histent)nodeptr, 1); + zfree(nodeptr, sizeof (struct histent)); +} + +/**/ +void +freehistdata(Histent he, int unlink) +{ + if (!he) + return; + + if (he == &curline) + return; + + if (!(he->node.flags & (HIST_DUP | HIST_TMPSTORE))) + removehashnode(histtab, he->node.nam); + + zsfree(he->node.nam); + if (he->nwords) + zfree(he->words, he->nwords*2*sizeof(short)); + + if (unlink) { + if (!--histlinect) + hist_ring = NULL; + else { + if (he == hist_ring) + hist_ring = hist_ring->up; + he->up->down = he->down; + he->down->up = he->up; + } + } +} + + +/*********************************************************************** + * Directory name cache mechanism + * + * The idea of this is that there are various shell structures, + * notably functions, that record the directories with which they + * are associated. Rather than store the full string each time, + * we store a pointer to the same location and count the references. + * This is optimised so that retrieval is quick at the expense of + * searching the list when setting up the structure, which is a much + * rarer operation. + * + * There is nothing special about the fact that the strings are + * directories, except for the assumptions for efficiency that many + * structures will point to the same one, and that there are not too + * many different directories associated with the shell. + **********************************************************************/ + +struct dircache_entry +{ + /* Name of directory in cache */ + char *name; + /* Number of references to it */ + int refs; +}; + +/* + * dircache is the cache, of length dircache_size. + * dircache_lastentry is the last entry used, an optimisation + * for multiple references to the same directory, e.g + * "autoload /blah/blah/\*". + */ +static struct dircache_entry *dircache, *dircache_lastentry; +static int dircache_size; + +/* + * Set *name to point to a cached version of value. + * value is copied so may come from any source. + * + * If value is NULL, look for the existing value of *name (safe if this + * too is NULL) and remove a reference to it from the cache. If it's + * not found in the cache, it's assumed to be an allocated string and + * freed --- this currently occurs for a shell function that's been + * loaded as the filename is now a full path, not just a directory, + * though we may one day optimise this to a cached directory plus a + * name, too. Note --- the function does *not* otherwise check + * if *name points to something already cached, so this is + * necessary any time *name may already be in the cache. + */ + +/**/ +mod_export void +dircache_set(char **name, char *value) +{ + struct dircache_entry *dcptr, *dcnew; + + if (!value) { + if (!*name) + return; + if (!dircache_size) { + zsfree(*name); + *name = NULL; + return; + } + + for (dcptr = dircache; dcptr < dircache + dircache_size; dcptr++) + { + /* Must be a pointer much, not a string match */ + if (*name == dcptr->name) + { + --dcptr->refs; + if (!dcptr->refs) { + ptrdiff_t ind = dcptr - dircache; + zsfree(dcptr->name); + --dircache_size; + + if (!dircache_size) { + zfree(dircache, sizeof(*dircache)); + dircache = NULL; + dircache_lastentry = NULL; + *name = NULL; + return; + } + dcnew = (struct dircache_entry *) + zalloc(dircache_size * sizeof(*dcnew)); + if (ind) + memcpy(dcnew, dircache, ind * sizeof(*dcnew)); + if (ind < dircache_size) + memcpy(dcnew + ind, dcptr + 1, + (dircache_size - ind) * sizeof(*dcnew)); + zfree(dircache, (dircache_size+1)*sizeof(*dcnew)); + dircache = dcnew; + dircache_lastentry = NULL; + } + *name = NULL; + return; + } + } + zsfree(*name); + *name = NULL; + } else { + /* + * As the function path has been resolved to a particular + * location, we'll store it as an absolute path. + */ + if (*value != '/') { + value = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), + "/", value); + value = xsymlink(value, 1); + } + /* + * We'll maintain the cache at exactly the right size rather + * than overallocating. The rationale here is that typically + * we'll get a lot of functions in a small number of directories + * so the complexity overhead of maintaining a separate count + * isn't really matched by the efficiency gain. + */ + if (dircache_lastentry && + !strcmp(value, dircache_lastentry->name)) { + *name = dircache_lastentry->name; + ++dircache_lastentry->refs; + return; + } else if (!dircache_size) { + dircache_size = 1; + dcptr = dircache = + (struct dircache_entry *)zalloc(sizeof(*dircache)); + } else { + for (dcptr = dircache; dcptr < dircache + dircache_size; dcptr++) + { + if (!strcmp(value, dcptr->name)) { + *name = dcptr->name; + ++dcptr->refs; + return; + } + } + ++dircache_size; + dircache = (struct dircache_entry *) + zrealloc(dircache, sizeof(*dircache) * dircache_size); + dcptr = dircache + dircache_size - 1; + } + dcptr->name = ztrdup(value); + *name = dcptr->name; + dcptr->refs = 1; + dircache_lastentry = dcptr; + } +} diff --git a/dotfiles/system/.zsh/modules/Src/hashtable.h b/dotfiles/system/.zsh/modules/Src/hashtable.h new file mode 100644 index 0000000..21398e1 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/hashtable.h @@ -0,0 +1,69 @@ +/* + * hashtable.h - header file for hash table handling code + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +/* Builtin function numbers; used by handler functions that handle more * + * than one builtin. Note that builtins such as compctl, that are not * + * overloaded, don't get a number. */ + +#define BIN_TYPESET 0 +#define BIN_BG 1 +#define BIN_FG 2 +#define BIN_JOBS 3 +#define BIN_WAIT 4 +#define BIN_DISOWN 5 +#define BIN_BREAK 6 +#define BIN_CONTINUE 7 +#define BIN_EXIT 8 +#define BIN_RETURN 9 +#define BIN_CD 10 +#define BIN_POPD 11 +#define BIN_PUSHD 12 +#define BIN_PRINT 13 +#define BIN_EVAL 14 +#define BIN_SCHED 15 +#define BIN_FC 16 +#define BIN_R 17 +#define BIN_PUSHLINE 18 +#define BIN_LOGOUT 19 +#define BIN_TEST 20 +#define BIN_BRACKET 21 +#define BIN_READONLY 22 +#define BIN_ECHO 23 +#define BIN_DISABLE 24 +#define BIN_ENABLE 25 +#define BIN_PRINTF 26 +#define BIN_COMMAND 27 +#define BIN_UNHASH 28 +#define BIN_UNALIAS 29 +#define BIN_UNFUNCTION 30 +#define BIN_UNSET 31 + +/* These currently depend on being 0 and 1. */ +#define BIN_SETOPT 0 +#define BIN_UNSETOPT 1 diff --git a/dotfiles/system/.zsh/modules/Src/init.c b/dotfiles/system/.zsh/modules/Src/init.c new file mode 100644 index 0000000..e9e6be9 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/init.c @@ -0,0 +1,1792 @@ +/* + * init.c - main loop and initialization routines + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" + +#include "zshpaths.h" +#include "zshxmods.h" + +#include "init.pro" + +#include "version.h" + +/**/ +int noexitct = 0; + +/* buffer for $_ and its length */ + +/**/ +char *zunderscore; + +/**/ +int underscorelen, underscoreused; + +/* what level of sourcing we are at */ + +/**/ +int sourcelevel; + +/* the shell tty fd */ + +/**/ +mod_export int SHTTY; + +/* the FILE attached to the shell tty */ + +/**/ +mod_export FILE *shout; + +/* termcap strings */ + +/**/ +mod_export char *tcstr[TC_COUNT]; + +/* lengths of each termcap string */ + +/**/ +mod_export int tclen[TC_COUNT]; + +/* Values of the li, co and am entries */ + +/**/ +int tclines, tccolumns; +/**/ +mod_export int hasam, hasbw, hasxn, hasye; + +/* Value of the Co (max_colors) entry: may not be set */ + +/**/ +mod_export int tccolours; + +/* SIGCHLD mask */ + +/**/ +mod_export sigset_t sigchld_mask; + +/**/ +mod_export struct hookdef zshhooks[] = { + HOOKDEF("exit", NULL, HOOKF_ALL), + HOOKDEF("before_trap", NULL, HOOKF_ALL), + HOOKDEF("after_trap", NULL, HOOKF_ALL), +}; + +/* keep executing lists until EOF found */ + +/**/ +enum loop_return +loop(int toplevel, int justonce) +{ + Eprog prog; + int err, non_empty = 0; + + queue_signals(); + pushheap(); + if (!toplevel) + zcontext_save(); + for (;;) { + freeheap(); + if (stophist == 3) /* re-entry via preprompt() */ + hend(NULL); + hbegin(1); /* init history mech */ + if (isset(SHINSTDIN)) { + setblock_stdin(); + if (interact && toplevel) { + int hstop = stophist; + stophist = 3; + /* + * Reset all errors including the interrupt error status + * immediately, so preprompt runs regardless of what + * just happened. We'll reset again below as a + * precaution to ensure we get back to the command line + * no matter what. + */ + errflag = 0; + preprompt(); + if (stophist != 3) + hbegin(1); + else + stophist = hstop; + /* + * Reset all errors, including user interupts. + * This is what allows ^C in an interactive shell + * to return us to the command line. + */ + errflag = 0; + } + } + use_exit_printed = 0; + intr(); /* interrupts on */ + lexinit(); /* initialize lexical state */ + if (!(prog = parse_event(ENDINPUT))) { + /* if we couldn't parse a list */ + hend(NULL); + if ((tok == ENDINPUT && !errflag) || + (tok == LEXERR && (!isset(SHINSTDIN) || !toplevel)) || + justonce) + break; + if (exit_pending) { + /* + * Something down there (a ZLE function?) decided + * to exit when there was stuff to clear up. + * Handle that now. + */ + stopmsg = 1; + zexit(exit_pending >> 1, 0); + } + if (tok == LEXERR && !lastval) + lastval = 1; + continue; + } + if (hend(prog)) { + enum lextok toksav = tok; + + non_empty = 1; + if (toplevel && + (getshfunc("preexec") || + paramtab->getnode(paramtab, "preexec" HOOK_SUFFIX))) { + LinkList args; + char *cmdstr; + + /* + * As we're about to freeheap() or popheap() + * anyway, there's no gain in using permanent + * storage here. + */ + args = newlinklist(); + addlinknode(args, "preexec"); + /* If curline got dumped from the history, we don't know + * what the user typed. */ + if (hist_ring && curline.histnum == curhist) + addlinknode(args, hist_ring->node.nam); + else + addlinknode(args, ""); + addlinknode(args, dupstring(getjobtext(prog, NULL))); + addlinknode(args, cmdstr = getpermtext(prog, NULL, 0)); + + callhookfunc("preexec", args, 1, NULL); + + /* The only permanent storage is from getpermtext() */ + zsfree(cmdstr); + /* + * Note this does *not* remove a user interrupt error + * condition, even though we're at the top level loop: + * that would be inconsistent with the case where + * we didn't execute a preexec function. This is + * an implementation detail that an interrupting user + * does't care about. + */ + errflag &= ~ERRFLAG_ERROR; + } + if (stopmsg) /* unset 'you have stopped jobs' flag */ + stopmsg--; + execode(prog, 0, 0, toplevel ? "toplevel" : "file"); + tok = toksav; + if (toplevel) + noexitct = 0; + } + if (ferror(stderr)) { + zerr("write error"); + clearerr(stderr); + } + if (subsh) /* how'd we get this far in a subshell? */ + exit(lastval); + if (((!interact || sourcelevel) && errflag) || retflag) + break; + if (isset(SINGLECOMMAND) && toplevel) { + dont_queue_signals(); + if (sigtrapped[SIGEXIT]) + dotrap(SIGEXIT); + exit(lastval); + } + if (justonce) + break; + } + err = errflag; + if (!toplevel) + zcontext_restore(); + popheap(); + unqueue_signals(); + + if (err) + return LOOP_ERROR; + if (!non_empty) + return LOOP_EMPTY; + return LOOP_OK; +} + +static int restricted; + +/**/ +static void +parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr) +{ + char **x; + LinkList paramlist; + int flags = PARSEARGS_TOPLEVEL; + if (**argv == '-') + flags |= PARSEARGS_LOGIN; + + argzero = posixzero = *argv++; + SHIN = 0; + + /* + * parseopts sets up some options after we deal with emulation in + * order to be consistent --- the code in parseopts_setemulate() is + * matched by code at the end of the present function. + */ + + if (parseopts(zsh_name, &argv, opts, cmdptr, NULL, flags)) + exit(1); + + /* + * USEZLE remains set if the shell has access to a terminal and + * is not reading from some other source as indicated by SHINSTDIN. + * SHINSTDIN becomes set below if there is no command argument, + * but it is the explicit setting (or not) that matters to USEZLE. + * USEZLE may also become unset in init_io() if the shell is not + * interactive or the terminal cannot be re-opened read/write. + */ + if (opts[SHINSTDIN]) + opts[USEZLE] = (opts[USEZLE] && isatty(0)); + + paramlist = znewlinklist(); + if (*argv) { + if (unset(SHINSTDIN)) { + posixzero = *argv; + if (*cmdptr) + argzero = *argv; + else + *runscript = *argv; + opts[INTERACTIVE] &= 1; + argv++; + } + while (*argv) + zaddlinknode(paramlist, ztrdup(*argv++)); + } else if (!*cmdptr) + opts[SHINSTDIN] = 1; + if(isset(SINGLECOMMAND)) + opts[INTERACTIVE] &= 1; + opts[INTERACTIVE] = !!opts[INTERACTIVE]; + if (opts[MONITOR] == 2) + opts[MONITOR] = opts[INTERACTIVE]; + if (opts[HASHDIRS] == 2) + opts[HASHDIRS] = opts[INTERACTIVE]; + pparams = x = (char **) zshcalloc((countlinknodes(paramlist) + 1) * sizeof(char *)); + + while ((*x++ = (char *)getlinknode(paramlist))); + free(paramlist); + argzero = ztrdup(argzero); + posixzero = ztrdup(posixzero); +} + +/* Insert into list in order of pointer value */ + +/**/ +static void +parseopts_insert(LinkList optlist, char *base, int optno) +{ + LinkNode node; + void *ptr = base + (optno < 0 ? -optno : optno); + + for (node = firstnode(optlist); node; incnode(node)) { + if (ptr < getdata(node)) { + insertlinknode(optlist, prevnode(node), ptr); + return; + } + } + + addlinknode(optlist, ptr); +} + +/* + * This sets the global emulation plus the options we traditionally + * set immediately after that. This is just for historical consistency + * --- I don't think those options actually need to be set here. + */ +static void parseopts_setemulate(char *nam, int flags) +{ + emulate(nam, 1, &emulation, opts); /* initialises most options */ + opts[LOGINSHELL] = ((flags & PARSEARGS_LOGIN) != 0); + opts[PRIVILEGED] = (getuid() != geteuid() || getgid() != getegid()); + + /* There's a bit of trickery with opts[INTERACTIVE] here. It starts * + * at a value of 2 (instead of 1) or 0. If it is explicitly set on * + * the command line, it goes to 1 or 0. If input is coming from * + * somewhere that normally makes the shell non-interactive, we do * + * "opts[INTERACTIVE] &= 1", so that only a *default* on state will * + * be changed. At the end of the function, a value of 2 gets * + * changed to 1. */ + opts[INTERACTIVE] = isatty(0) ? 2 : 0; + /* + * MONITOR is similar: we initialise it to 2, and if it's + * still 2 at the end, we set it to the value of INTERACTIVE. + */ + opts[MONITOR] = 2; /* may be unset in init_io() */ + opts[HASHDIRS] = 2; /* same relationship to INTERACTIVE */ + opts[USEZLE] = 1; /* see below, related to SHINSTDIN */ + opts[SHINSTDIN] = 0; + opts[SINGLECOMMAND] = 0; +} + +/* + * Parse shell options. + * + * If (flags & PARSEARGS_TOPLEVEL): + * - we are doing shell initilisation + * - nam is the name under which the shell was started + * - set up emulation and standard options based on that. + * Otherwise: + * - nam is a command name + * - don't exit on failure. + * + * If optlist is not NULL, it used to form a list of pointers + * into new_opts indicating which options have been changed. + */ + +/**/ +mod_export int +parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp, + LinkList optlist, int flags) +{ + int optionbreak = 0; + int action, optno; + char **argv = *argvp; + int toplevel = ((flags & PARSEARGS_TOPLEVEL) != 0u); + int emulate_required = toplevel; + char *top_emulation = nam; + + *cmdp = 0; +#define WARN_OPTION(F, S) \ + do { \ + if (!toplevel) \ + zwarnnam(nam, F, S); \ + else \ + zerr(F, S); \ + } while (0) +#define LAST_OPTION(N) \ + do { \ + if (!toplevel) { \ + if (*argv) \ + argv++; \ + goto doneargv; \ + } else exit(N); \ + } while(0) + + /* loop through command line options (begins with "-" or "+") */ + while (!optionbreak && *argv && (**argv == '-' || **argv == '+')) { + char *args = *argv; + action = (**argv == '-'); + if (!argv[0][1]) + *argv = "--"; + while (*++*argv) { + if (**argv == '-') { + if (!argv[0][1]) { + /* The pseudo-option `--' signifies the end of options. */ + argv++; + goto doneoptions; + } + if (!toplevel || *argv != args+1 || **argv != '-') + goto badoptionstring; + /* GNU-style long options */ + ++*argv; + if (!strcmp(*argv, "version")) { + printf("zsh %s (%s-%s-%s)\n", + ZSH_VERSION, MACHTYPE, VENDOR, OSTYPE); + LAST_OPTION(0); + } + if (!strcmp(*argv, "help")) { + printhelp(); + LAST_OPTION(0); + } + if (!strcmp(*argv, "emulate")) { + ++argv; + if (!*argv) { + zerr("--emulate: argument required"); + exit(1); + } + if (!emulate_required) { + zerr("--emulate: must precede other options"); + exit(1); + } + top_emulation = *argv; + break; + } + /* `-' characters are allowed in long options */ + for(args = *argv; *args; args++) + if(*args == '-') + *args = '_'; + goto longoptions; + } + + if (unset(SHOPTIONLETTERS) && **argv == 'b') { + if (emulate_required) { + parseopts_setemulate(top_emulation, flags); + emulate_required = 0; + } + /* -b ends options at the end of this argument */ + optionbreak = 1; + } else if (**argv == 'c') { + if (emulate_required) { + parseopts_setemulate(top_emulation, flags); + emulate_required = 0; + } + /* -c command */ + *cmdp = *argv; + new_opts[INTERACTIVE] &= 1; + if (toplevel) + scriptname = scriptfilename = ztrdup("zsh"); + } else if (**argv == 'o') { + if (!*++*argv) + argv++; + if (!*argv) { + WARN_OPTION("string expected after -o", NULL); + return 1; + } + longoptions: + if (emulate_required) { + parseopts_setemulate(top_emulation, flags); + emulate_required = 0; + } + if (!(optno = optlookup(*argv))) { + WARN_OPTION("no such option: %s", *argv); + return 1; + } else if (optno == RESTRICTED && toplevel) { + restricted = action; + } else if ((optno == EMACSMODE || optno == VIMODE) && !toplevel) { + WARN_OPTION("can't change option: %s", *argv); + } else { + if (dosetopt(optno, action, toplevel, new_opts) && + !toplevel) { + WARN_OPTION("can't change option: %s", *argv); + } else if (optlist) { + parseopts_insert(optlist, new_opts, optno); + } + } + break; + } else if (isspace(STOUC(**argv))) { + /* zsh's typtab not yet set, have to use ctype */ + while (*++*argv) + if (!isspace(STOUC(**argv))) { + badoptionstring: + WARN_OPTION("bad option string: '%s'", args); + return 1; + } + break; + } else { + if (emulate_required) { + parseopts_setemulate(top_emulation, flags); + emulate_required = 0; + } + if (!(optno = optlookupc(**argv))) { + WARN_OPTION("bad option: -%c", **argv); + return 1; + } else if (optno == RESTRICTED && toplevel) { + restricted = action; + } else if ((optno == EMACSMODE || optno == VIMODE) && + !toplevel) { + WARN_OPTION("can't change option: %s", *argv); + } else { + if (dosetopt(optno, action, toplevel, new_opts) && + !toplevel) { + WARN_OPTION("can't change option: -%c", **argv); + } else if (optlist) { + parseopts_insert(optlist, new_opts, optno); + } + } + } + } + argv++; + } + doneoptions: + if (*cmdp) { + if (!*argv) { + WARN_OPTION("string expected after -%s", *cmdp); + return 1; + } + *cmdp = *argv++; + } + doneargv: + *argvp = argv; + if (emulate_required) { + parseopts_setemulate(top_emulation, flags); + emulate_required = 0; + } + return 0; +} + +/**/ +static void +printhelp(void) +{ + printf("Usage: %s [] [ ...]\n", argzero); + printf("\nSpecial options:\n"); + printf(" --help show this message, then exit\n"); + printf(" --version show zsh version number, then exit\n"); + if(unset(SHOPTIONLETTERS)) + printf(" -b end option processing, like --\n"); + printf(" -c take first argument as a command to execute\n"); + printf(" -o OPTION set an option by name (see below)\n"); + printf("\nNormal options are named. An option may be turned on by\n"); + printf("`-o OPTION', `--OPTION', `+o no_OPTION' or `+-no-OPTION'. An\n"); + printf("option may be turned off by `-o no_OPTION', `--no-OPTION',\n"); + printf("`+o OPTION' or `+-OPTION'. Options are listed below only in\n"); + printf("`--OPTION' or `--no-OPTION' form.\n"); + printoptionlist(); +} + +/**/ +mod_export void +init_io(char *cmd) +{ + static char outbuf[BUFSIZ], errbuf[BUFSIZ]; + +#ifdef RSH_BUG_WORKAROUND + int i; +#endif + +/* stdout, stderr fully buffered */ +#ifdef _IOFBF + setvbuf(stdout, outbuf, _IOFBF, BUFSIZ); + setvbuf(stderr, errbuf, _IOFBF, BUFSIZ); +#else + setbuffer(stdout, outbuf, BUFSIZ); + setbuffer(stderr, errbuf, BUFSIZ); +#endif + +/* This works around a bug in some versions of in.rshd. * + * Currently this is not defined by default. */ +#ifdef RSH_BUG_WORKAROUND + if (cmd) { + for (i = 3; i < 10; i++) + close(i); + } +#else + (void)cmd; +#endif + + if (shout) { + /* + * Check if shout was set to stderr, if so don't close it. + * We do this if we are interactive but don't have a + * terminal. + */ + if (shout != stderr) + fclose(shout); + shout = 0; + } + if (SHTTY != -1) { + zclose(SHTTY); + SHTTY = -1; + } + + /* Send xtrace output to stderr -- see execcmd() */ + xtrerr = stderr; + + /* Make sure the tty is opened read/write. */ + if (isatty(0)) { + zsfree(ttystrname); + if ((ttystrname = ztrdup(ttyname(0)))) { + SHTTY = movefd(open(ttystrname, O_RDWR | O_NOCTTY)); +#ifdef TIOCNXCL + /* + * See if the terminal claims to be busy. If so, and fd 0 + * is a terminal, try and set non-exclusive use for that. + * This is something to do with Solaris over-cleverness. + */ + if (SHTTY == -1 && errno == EBUSY) + ioctl(0, TIOCNXCL, 0); +#endif + } + /* + * xterm, rxvt and probably all terminal emulators except + * dtterm on Solaris 2.6 & 7 have a bug. Applications are + * unable to open /dev/tty or /dev/pts/ + * because something in Sun's STREAMS modules doesn't like + * it. The open() call fails with EBUSY which is not even + * listed as a possibility in the open(2) man page. So we'll + * try to outsmart The Company. -- + * + * Presumably there's no harm trying this on any OS, given that + * isatty(0) worked but opening the tty didn't. Possibly we won't + * get the tty read/write, but it's the best we can do -- pws + * + * Try both stdin and stdout before trying /dev/tty. -- Bart + */ +#if defined(HAVE_FCNTL_H) && defined(F_GETFL) +#define rdwrtty(fd) ((fcntl(fd, F_GETFL, 0) & O_RDWR) == O_RDWR) +#else +#define rdwrtty(fd) 1 +#endif + if (SHTTY == -1 && rdwrtty(0)) { + SHTTY = movefd(dup(0)); + } + } + if (SHTTY == -1 && isatty(1) && rdwrtty(1) && + (SHTTY = movefd(dup(1))) != -1) { + zsfree(ttystrname); + ttystrname = ztrdup(ttyname(1)); + } + if (SHTTY == -1 && + (SHTTY = movefd(open("/dev/tty", O_RDWR | O_NOCTTY))) != -1) { + zsfree(ttystrname); + ttystrname = ztrdup(ttyname(SHTTY)); + } + if (SHTTY == -1) { + zsfree(ttystrname); + ttystrname = ztrdup(""); + } else { +#ifdef FD_CLOEXEC + long fdflags = fcntl(SHTTY, F_GETFD, 0); + if (fdflags != (long)-1) { + fdflags |= FD_CLOEXEC; + fcntl(SHTTY, F_SETFD, fdflags); + } +#endif + if (!ttystrname) + ttystrname = ztrdup("/dev/tty"); + } + + /* We will only use zle if shell is interactive, * + * SHTTY != -1, and shout != 0 */ + if (interact) { + init_shout(); + if(!SHTTY || !shout) + opts[USEZLE] = 0; + } else + opts[USEZLE] = 0; + +#ifdef JOB_CONTROL + /* If interactive, make sure the shell is in the foreground and is the + * process group leader. + */ + mypid = (zlong)getpid(); + if (opts[MONITOR] && (SHTTY != -1)) { + origpgrp = GETPGRP(); + acquire_pgrp(); /* might also clear opts[MONITOR] */ + } else + opts[MONITOR] = 0; +#else + opts[MONITOR] = 0; +#endif +} + +/**/ +mod_export void +init_shout(void) +{ + static char shoutbuf[BUFSIZ]; +#if defined(JOB_CONTROL) && defined(TIOCSETD) && defined(NTTYDISC) + int ldisc; +#endif + + if (SHTTY == -1) + { + /* Since we're interactive, it's nice to have somewhere to write. */ + shout = stderr; + return; + } + +#if defined(JOB_CONTROL) && defined(TIOCSETD) && defined(NTTYDISC) + ldisc = NTTYDISC; + ioctl(SHTTY, TIOCSETD, (char *)&ldisc); +#endif + + /* Associate terminal file descriptor with a FILE pointer */ + shout = fdopen(SHTTY, "w"); +#ifdef _IOFBF + if (shout) + setvbuf(shout, shoutbuf, _IOFBF, BUFSIZ); +#endif + + gettyinfo(&shttyinfo); /* get tty state */ +#if defined(__sgi) + if (shttyinfo.tio.c_cc[VSWTCH] <= 0) /* hack for irises */ + shttyinfo.tio.c_cc[VSWTCH] = CSWTCH; +#endif +} + +/* names of the termcap strings we want */ + +static char *tccapnams[TC_COUNT] = { + "cl", "le", "LE", "nd", "RI", "up", "UP", "do", + "DO", "dc", "DC", "ic", "IC", "cd", "ce", "al", "dl", "ta", + "md", "so", "us", "me", "se", "ue", "ch", + "ku", "kd", "kl", "kr", "sc", "rc", "bc", "AF", "AB" +}; + +/**/ +mod_export char * +tccap_get_name(int cap) +{ + if (cap >= TC_COUNT) { +#ifdef DEBUG + dputs("name of invalid capability %d requested", cap); +#endif + return ""; + } + return tccapnams[cap]; +} + +/* Initialise termcap */ + +/**/ +mod_export int +init_term(void) +{ +#ifndef TGETENT_ACCEPTS_NULL + static char termbuf[2048]; /* the termcap buffer */ +#endif + + if (!*term) { + termflags |= TERM_UNKNOWN; + return 0; + } + + /* unset zle if using zsh under emacs */ + if (!strcmp(term, "emacs")) + opts[USEZLE] = 0; + +#ifdef TGETENT_ACCEPTS_NULL + /* If possible, we let tgetent allocate its own termcap buffer */ + if (tgetent(NULL, term) != TGETENT_SUCCESS) +#else + if (tgetent(termbuf, term) != TGETENT_SUCCESS) +#endif + { + if (interact) + zerr("can't find terminal definition for %s", term); + errflag &= ~ERRFLAG_ERROR; + termflags |= TERM_BAD; + return 0; + } else { + char tbuf[1024], *pp; + int t0; + + termflags &= ~TERM_BAD; + termflags &= ~TERM_UNKNOWN; + for (t0 = 0; t0 != TC_COUNT; t0++) { + pp = tbuf; + zsfree(tcstr[t0]); + /* AIX tgetstr() ignores second argument */ + if (!(pp = tgetstr(tccapnams[t0], &pp))) + tcstr[t0] = NULL, tclen[t0] = 0; + else { + tclen[t0] = strlen(pp); + tcstr[t0] = (char *) zalloc(tclen[t0] + 1); + memcpy(tcstr[t0], pp, tclen[t0] + 1); + } + } + + /* check whether terminal has automargin (wraparound) capability */ + hasam = tgetflag("am"); + hasbw = tgetflag("bw"); + hasxn = tgetflag("xn"); /* also check for newline wraparound glitch */ + hasye = tgetflag("YE"); /* print in last column does carriage return */ + + tclines = tgetnum("li"); + tccolumns = tgetnum("co"); + tccolours = tgetnum("Co"); + + /* if there's no termcap entry for cursor up, use single line mode: * + * this is flagged by termflags which is examined in zle_refresh.c * + */ + if (tccan(TCUP)) + termflags &= ~TERM_NOUP; + else { + zsfree(tcstr[TCUP]); + tcstr[TCUP] = NULL; + termflags |= TERM_NOUP; + } + + /* most termcaps don't define "bc" because they use \b. */ + if (!tccan(TCBACKSPACE)) { + zsfree(tcstr[TCBACKSPACE]); + tcstr[TCBACKSPACE] = ztrdup("\b"); + tclen[TCBACKSPACE] = 1; + } + + /* if there's no termcap entry for cursor left, use backspace. */ + if (!tccan(TCLEFT)) { + zsfree(tcstr[TCLEFT]); + tcstr[TCLEFT] = ztrdup(tcstr[TCBACKSPACE]); + tclen[TCLEFT] = tclen[TCBACKSPACE]; + } + + if (tccan(TCSAVECURSOR) && !tccan(TCRESTRCURSOR)) { + tclen[TCSAVECURSOR] = 0; + zsfree(tcstr[TCSAVECURSOR]); + tcstr[TCSAVECURSOR] = NULL; + } + + /* if the termcap entry for down is \n, don't use it. */ + if (tccan(TCDOWN) && tcstr[TCDOWN][0] == '\n') { + tclen[TCDOWN] = 0; + zsfree(tcstr[TCDOWN]); + tcstr[TCDOWN] = NULL; + } + + /* if there's no termcap entry for clear, use ^L. */ + if (!tccan(TCCLEARSCREEN)) { + zsfree(tcstr[TCCLEARSCREEN]); + tcstr[TCCLEARSCREEN] = ztrdup("\14"); + tclen[TCCLEARSCREEN] = 1; + } + rprompt_indent = 1; /* If you change this, update rprompt_indent_unsetfn() */ + /* The following is an attempt at a heuristic, + * but it fails in some cases */ + /* rprompt_indent = ((hasam && !hasbw) || hasye || !tccan(TCLEFT)); */ + } + return 1; +} + +/* Initialize lots of global variables and hash tables */ + +/**/ +void +setupvals(char *cmd, char *runscript, char *zsh_name) +{ +#ifdef USE_GETPWUID + struct passwd *pswd; +#endif + struct timezone dummy_tz; + char *ptr; + int i, j; +#if defined(SITEFPATH_DIR) || defined(FPATH_DIR) || defined (ADDITIONAL_FPATH) || defined(FIXED_FPATH_DIR) +#define FPATH_NEEDS_INIT 1 + char **fpathptr; +# if defined(FPATH_DIR) && defined(FPATH_SUBDIRS) + char *fpath_subdirs[] = FPATH_SUBDIRS; +# endif +# if defined(ADDITIONAL_FPATH) + char *more_fndirs[] = ADDITIONAL_FPATH; + int more_fndirs_len; +# endif +# ifdef FIXED_FPATH_DIR +# define FIXED_FPATH_LEN 1 +# else +# define FIXED_FPATH_LEN 0 +# endif +# ifdef SITEFPATH_DIR +# define SITE_FPATH_LEN 1 +# else +# define SITE_FPATH_LEN 0 +# endif + int fpathlen = FIXED_FPATH_LEN + SITE_FPATH_LEN; +#endif + int close_fds[10], tmppipe[2]; + + /* + * Workaround a problem with NIS (in one guise or another) which + * grabs file descriptors and keeps them for future reference. + * We don't want these to be in the range where the user can + * open fd's, i.e. 0 to 9 inclusive. So we make sure all + * fd's in that range are in use. + */ + memset(close_fds, 0, 10*sizeof(int)); + if (pipe(tmppipe) == 0) { + /* + * Strategy: Make sure we have at least fd 0 open (hence + * the pipe). From then on, keep dup'ing until we are + * up to 9. If we go over the top, close immediately, else + * mark for later closure. + */ + i = -1; /* max fd we have checked */ + while (i < 9) { + /* j is current fd */ + if (i < tmppipe[0]) + j = tmppipe[0]; + else if (i < tmppipe[1]) + j = tmppipe[1]; + else { + j = dup(0); + if (j == -1) + break; + } + if (j < 10) + close_fds[j] = 1; + else + close(j); + if (i < j) + i = j; + } + if (i < tmppipe[0]) + close(tmppipe[0]); + if (i < tmppipe[1]) + close(tmppipe[1]); + } + + (void)addhookdefs(NULL, zshhooks, sizeof(zshhooks)/sizeof(*zshhooks)); + + init_eprog(); + + zero_mnumber.type = MN_INTEGER; + zero_mnumber.u.l = 0; + + noeval = 0; + curhist = 0; + histsiz = DEFAULT_HISTSIZE; + inithist(); + + cmdstack = (unsigned char *) zalloc(CMDSTACKSZ); + cmdsp = 0; + + bangchar = '!'; + hashchar = '#'; + hatchar = '^'; + termflags = TERM_UNKNOWN; + curjob = prevjob = coprocin = coprocout = -1; + gettimeofday(&shtimer, &dummy_tz); /* init $SECONDS */ + srand((unsigned int)(shtimer.tv_sec + shtimer.tv_usec)); /* seed $RANDOM */ + + /* Set default path */ + path = (char **) zalloc(sizeof(*path) * 5); + path[0] = ztrdup("/bin"); + path[1] = ztrdup("/usr/bin"); + path[2] = ztrdup("/usr/ucb"); + path[3] = ztrdup("/usr/local/bin"); + path[4] = NULL; + + cdpath = mkarray(NULL); + manpath = mkarray(NULL); + fignore = mkarray(NULL); + +#ifdef FPATH_NEEDS_INIT +# ifdef FPATH_DIR +# ifdef FPATH_SUBDIRS + fpathlen += sizeof(fpath_subdirs)/sizeof(char *); +# else /* FPATH_SUBDIRS */ + fpathlen++; +# endif /* FPATH_SUBDIRS */ +# endif /* FPATH_DIR */ +# if defined(ADDITIONAL_FPATH) + more_fndirs_len = sizeof(more_fndirs)/sizeof(char *); + fpathlen += more_fndirs_len; +# endif /* ADDITONAL_FPATH */ + fpath = fpathptr = (char **)zalloc((fpathlen+1)*sizeof(char *)); +# ifdef FIXED_FPATH_DIR + *fpathptr++ = ztrdup(FIXED_FPATH_DIR); + fpathlen--; +# endif +# ifdef SITEFPATH_DIR + *fpathptr++ = ztrdup(SITEFPATH_DIR); + fpathlen--; +# endif /* SITEFPATH_DIR */ +# if defined(ADDITIONAL_FPATH) + for (j = 0; j < more_fndirs_len; j++) + *fpathptr++ = ztrdup(more_fndirs[j]); +# endif +# ifdef FPATH_DIR +# ifdef FPATH_SUBDIRS +# ifdef ADDITIONAL_FPATH + for (j = more_fndirs_len; j < fpathlen; j++) + *fpathptr++ = tricat(FPATH_DIR, "/", fpath_subdirs[j - more_fndirs_len]); +# else + for (j = 0; j < fpathlen; j++) + *fpathptr++ = tricat(FPATH_DIR, "/", fpath_subdirs[j]); +#endif +# else + *fpathptr++ = ztrdup(FPATH_DIR); +# endif +# endif + *fpathptr = NULL; +#else /* FPATH_NEEDS_INIT */ + fpath = mkarray(NULL); +#endif /* FPATH_NEEDS_INIT */ + + mailpath = mkarray(NULL); + watch = mkarray(NULL); + psvar = mkarray(NULL); + module_path = mkarray(ztrdup(MODULE_DIR)); + modulestab = newmoduletable(17, "modules"); + linkedmodules = znewlinklist(); + + /* Set default prompts */ + if(unset(INTERACTIVE)) { + prompt = ztrdup(""); + prompt2 = ztrdup(""); + } else if (EMULATION(EMULATE_KSH|EMULATE_SH)) { + prompt = ztrdup(privasserted() ? "# " : "$ "); + prompt2 = ztrdup("> "); + } else { + prompt = ztrdup("%m%# "); + prompt2 = ztrdup("%_> "); + } + prompt3 = ztrdup("?# "); + prompt4 = EMULATION(EMULATE_KSH|EMULATE_SH) + ? ztrdup("+ ") : ztrdup("+%N:%i> "); + sprompt = ztrdup("zsh: correct '%R' to '%r' [nyae]? "); + + ifs = EMULATION(EMULATE_KSH|EMULATE_SH) ? + ztrdup(DEFAULT_IFS_SH) : ztrdup(DEFAULT_IFS); + wordchars = ztrdup(DEFAULT_WORDCHARS); + postedit = ztrdup(""); + zunderscore = (char *) zalloc(underscorelen = 32); + underscoreused = 1; + *zunderscore = '\0'; + + zoptarg = ztrdup(""); + zoptind = 1; + + ppid = (zlong) getppid(); + mypid = (zlong) getpid(); + term = ztrdup(""); + + nullcmd = ztrdup("cat"); + readnullcmd = ztrdup(DEFAULT_READNULLCMD); + + /* We cache the uid so we know when to * + * recheck the info for `USERNAME' */ + cached_uid = getuid(); + + /* Get password entry and set info for `USERNAME' */ +#ifdef USE_GETPWUID + if ((pswd = getpwuid(cached_uid))) { + if (EMULATION(EMULATE_ZSH)) + home = metafy(pswd->pw_dir, -1, META_DUP); + cached_username = ztrdup(pswd->pw_name); + } + else +#endif /* USE_GETPWUID */ + { + if (EMULATION(EMULATE_ZSH)) + home = ztrdup("/"); + cached_username = ztrdup(""); + } + + /* + * Try a cheap test to see if we can initialize `PWD' from `HOME'. + * In non-native emulations HOME must come from the environment; + * we're not allowed to set it locally. + */ + if (EMULATION(EMULATE_ZSH)) + ptr = home; + else + ptr = zgetenv("HOME"); + if (ptr && ispwd(ptr)) + pwd = ztrdup(ptr); + else if ((ptr = zgetenv("PWD")) && (strlen(ptr) < PATH_MAX) && + (ptr = metafy(ptr, -1, META_STATIC), ispwd(ptr))) + pwd = ztrdup(ptr); + else { + pwd = NULL; + pwd = metafy(zgetcwd(), -1, META_DUP); + } + + oldpwd = ztrdup(pwd); /* initialize `OLDPWD' = `PWD' */ + + inittyptab(); /* initialize the ztypes table */ + initlextabs(); /* initialize lexing tables */ + + createreswdtable(); /* create hash table for reserved words */ + createaliastables(); /* create hash tables for aliases */ + createcmdnamtable(); /* create hash table for external commands */ + createshfunctable(); /* create hash table for shell functions */ + createbuiltintable(); /* create hash table for builtin commands */ + createnameddirtable(); /* create hash table for named directories */ + createparamtable(); /* create parameter hash table */ + + condtab = NULL; + wrappers = NULL; + +#ifdef TIOCGWINSZ + adjustwinsize(0); +#else + /* columns and lines are normally zero, unless something different * + * was inhereted from the environment. If either of them are zero * + * the setiparam calls below set them to the defaults from termcap */ + setiparam("COLUMNS", zterm_columns); + setiparam("LINES", zterm_lines); +#endif + +#ifdef HAVE_GETRLIMIT + for (i = 0; i != RLIM_NLIMITS; i++) { + getrlimit(i, current_limits + i); + limits[i] = current_limits[i]; + } +#endif + + breaks = loops = 0; + lastmailcheck = time(NULL); + locallevel = sourcelevel = 0; + sfcontext = SFC_NONE; + trap_return = 0; + trap_state = TRAP_STATE_INACTIVE; + noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN | NOERREXIT_SIGNAL; + nohistsave = 1; + dirstack = znewlinklist(); + bufstack = znewlinklist(); + hsubl = hsubr = NULL; + lastpid = 0; + + get_usage(); + + /* Close the file descriptors we opened to block off 0 to 9 */ + for (i = 0; i < 10; i++) + if (close_fds[i]) + close(i); + + /* Colour sequences for outputting colours in prompts and zle */ + set_default_colour_sequences(); + + if (cmd) + setsparam("ZSH_EXECUTION_STRING", ztrdup(cmd)); + if (runscript) + setsparam("ZSH_SCRIPT", ztrdup(runscript)); + setsparam("ZSH_NAME", ztrdup(zsh_name)); /* NOTE: already metafied early in zsh_main() */ +} + +/* + * Setup shell input, opening any script file (runscript, may be NULL). + * This is deferred until we have a path to search, in case + * PATHSCRIPT is set for sh-compatible behaviour. + */ +static void +setupshin(char *runscript) +{ + if (runscript) { + char *funmeta, *sfname = NULL; + struct stat st; + + funmeta = unmeta(runscript); + /* + * Always search the current directory first. + */ + if (access(funmeta, F_OK) == 0 && + stat(funmeta, &st) >= 0 && + !S_ISDIR(st.st_mode)) + sfname = runscript; + else if (isset(PATHSCRIPT) && !strchr(runscript, '/')) { + /* + * With the PATHSCRIPT option, search the path if no + * path was given in the script name. + */ + funmeta = pathprog(runscript, &sfname); + } + if (!sfname || + (SHIN = movefd(open(funmeta, O_RDONLY | O_NOCTTY))) + == -1) { + zerr("can't open input file: %s", runscript); + exit(127); + } + scriptfilename = sfname; + 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 + * read commands from. + */ + lineno = 1; + /* + * Finish setting up SHIN and its relatives. + */ + bshin = SHIN ? fdopen(SHIN, "r") : stdin; + if (isset(SHINSTDIN) && !SHIN && unset(INTERACTIVE)) { +#ifdef _IONBF + setvbuf(stdin, NULL, _IONBF, 0); +#else + setlinebuf(stdin); +#endif + } +} + +/* Initialize signal handling */ + +/**/ +void +init_signals(void) +{ + if (interact) { + int i; + signal_setmask(signal_mask(0)); + for (i=0; i= 10) + fclose(bshin); + SHIN = movefd(open("/dev/null", O_RDONLY | O_NOCTTY)); + bshin = fdopen(SHIN, "r"); + execstring(cmd, 0, 1, "cmdarg"); + stopmsg = 1; + zexit(lastval, 0); + } + + if (interact && isset(RCS)) + readhistfile(NULL, 0, HFILE_USE_OPTIONS); +} + +/* + * source a file + * Returns one of the SOURCE_* enum values. + */ + +/**/ +mod_export enum source_return +source(char *s) +{ + Eprog prog; + int tempfd = -1, fd, cj; + zlong oldlineno; + int oldshst, osubsh, oloops; + FILE *obshin; + char *old_scriptname = scriptname, *us; + char *old_scriptfilename = scriptfilename; + unsigned char *ocs; + int ocsp; + int otrap_return = trap_return, otrap_state = trap_state; + struct funcstack fstack; + enum source_return ret = SOURCE_OK; + + if (!s || + (!(prog = try_source_file((us = unmeta(s)))) && + (tempfd = movefd(open(us, O_RDONLY | O_NOCTTY))) == -1)) { + return SOURCE_NOT_FOUND; + } + + /* save the current shell state */ + fd = SHIN; /* store the shell input fd */ + obshin = bshin; /* store file handle for buffered shell input */ + osubsh = subsh; /* store whether we are in a subshell */ + cj = thisjob; /* store our current job number */ + oldlineno = lineno; /* store our current lineno */ + oloops = loops; /* stored the # of nested loops we are in */ + oldshst = opts[SHINSTDIN]; /* store current value of this option */ + ocs = cmdstack; + ocsp = cmdsp; + cmdstack = (unsigned char *) zalloc(CMDSTACKSZ); + cmdsp = 0; + + if (!prog) { + SHIN = tempfd; + bshin = fdopen(SHIN, "r"); + } + subsh = 0; + lineno = 1; + loops = 0; + dosetopt(SHINSTDIN, 0, 1, opts); + scriptname = s; + scriptfilename = s; + + if (isset(SOURCETRACE)) { + printprompt4(); + fprintf(xtrerr ? xtrerr : stderr, "\n"); + } + + /* + * The special return behaviour of traps shouldn't + * trigger in files sourced from traps; the return + * is just a return from the file. + */ + trap_state = TRAP_STATE_INACTIVE; + + sourcelevel++; + + fstack.name = scriptfilename; + fstack.caller = funcstack ? funcstack->name : + dupstring(old_scriptfilename ? old_scriptfilename : "zsh"); + fstack.flineno = 0; + fstack.lineno = oldlineno; + fstack.filename = scriptfilename; + fstack.prev = funcstack; + fstack.tp = FS_SOURCE; + funcstack = &fstack; + + if (prog) { + pushheap(); + errflag &= ~ERRFLAG_ERROR; + execode(prog, 1, 0, "filecode"); + popheap(); + if (errflag) + ret = SOURCE_ERROR; + } else { + /* loop through the file to be sourced */ + switch (loop(0, 0)) + { + case LOOP_OK: + /* nothing to do but compilers like a complete enum */ + break; + + case LOOP_EMPTY: + /* Empty code resets status */ + lastval = 0; + break; + + case LOOP_ERROR: + ret = SOURCE_ERROR; + break; + } + } + funcstack = funcstack->prev; + sourcelevel--; + + trap_state = otrap_state; + trap_return = otrap_return; + + /* restore the current shell state */ + if (prog) + freeeprog(prog); + else { + fclose(bshin); + fdtable[SHIN] = FDT_UNUSED; + SHIN = fd; /* the shell input fd */ + bshin = obshin; /* file handle for buffered shell input */ + } + subsh = osubsh; /* whether we are in a subshell */ + thisjob = cj; /* current job number */ + lineno = oldlineno; /* our current lineno */ + loops = oloops; /* the # of nested loops we are in */ + dosetopt(SHINSTDIN, oldshst, 1, opts); /* SHINSTDIN option */ + errflag &= ~ERRFLAG_ERROR; + if (!exit_pending) + retflag = 0; + scriptname = old_scriptname; + scriptfilename = old_scriptfilename; + zfree(cmdstack, CMDSTACKSZ); + cmdstack = ocs; + cmdsp = ocsp; + + return ret; +} + +/* Try to source a file in the home directory */ + +/**/ +void +sourcehome(char *s) +{ + char *h; + + queue_signals(); + if (EMULATION(EMULATE_SH|EMULATE_KSH) || !(h = getsparam_u("ZDOTDIR"))) { + h = home; + if (!h) { + unqueue_signals(); + return; + } + } + + { + /* Let source() complain if path is too long */ + VARARR(char, buf, strlen(h) + strlen(s) + 2); + sprintf(buf, "%s/%s", h, s); + unqueue_signals(); + source(buf); + } +} + +/**/ +void +init_bltinmods(void) +{ + +#include "bltinmods.list" + + (void)load_module("zsh/main", NULL, 0); +} + +/**/ +mod_export void +noop_function(void) +{ + /* do nothing */ +} + +/**/ +mod_export void +noop_function_int(UNUSED(int nothing)) +{ + /* do nothing */ +} + +/* + * ZLE entry point pointer. + * No other source file needs to know which modules are linked in. + */ +/**/ +mod_export ZleEntryPoint zle_entry_ptr; + +/* + * State of loading of zle. + * 0 = Not loaded, not attempted. + * 1 = Loaded successfully + * 2 = Failed to load. + */ +/**/ +mod_export int zle_load_state; + +/**/ +mod_export char * +zleentry(VA_ALIST1(int cmd)) +VA_DCL +{ + char *ret = NULL; + va_list ap; + VA_DEF_ARG(int cmd); + + VA_START(ap, cmd); + VA_GET_ARG(ap, cmd, int); + +#if defined(LINKED_XMOD_zshQszle) || defined(UNLINKED_XMOD_zshQszle) + /* autoload */ + switch (zle_load_state) { + case 0: + /* + * Some commands don't require us to load ZLE. + * These also have no fallback. + */ + if (cmd != ZLE_CMD_TRASH && cmd != ZLE_CMD_RESET_PROMPT && + cmd != ZLE_CMD_REFRESH) + { + if (load_module("zsh/zle", NULL, 0) != 1) { + (void)load_module("zsh/compctl", NULL, 0); + ret = zle_entry_ptr(cmd, ap); + /* Don't execute fallback code */ + cmd = -1; + } else { + zle_load_state = 2; + /* Execute fallback code below */ + } + } + break; + + case 1: + ret = zle_entry_ptr(cmd, ap); + /* Don't execute fallback code */ + cmd = -1; + break; + + case 2: + /* Execute fallback code */ + break; + } +#endif + + switch (cmd) { + /* + * Only the read command really needs a fallback if zle + * is not available. ZLE_CMD_GET_LINE has traditionally + * had local code in bufferwords() to do this, but that' + * probably only because bufferwords() is part of completion + * and so everything to do with it is horribly complicated. + */ + case ZLE_CMD_READ: + { + char *pptbuf, **lp; + int pptlen; + + lp = va_arg(ap, char **); + + pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL, + NULL), + &pptlen); + write_loop(2, pptbuf, pptlen); + free(pptbuf); + + ret = shingetline(); + break; + } + + case ZLE_CMD_GET_LINE: + { + int *ll, *cs; + + ll = va_arg(ap, int *); + cs = va_arg(ap, int *); + *ll = *cs = 0; + ret = ztrdup(""); + break; + } + } + + va_end(ap); + return ret; +} + +/* compctl entry point pointers. Similar to the ZLE ones. */ + +/**/ +mod_export CompctlReadFn compctlreadptr = fallback_compctlread; + +/**/ +mod_export int +fallback_compctlread(char *name, UNUSED(char **args), UNUSED(Options ops), UNUSED(char *reply)) +{ + zwarnnam(name, "no loaded module provides read for completion context"); + return 1; +} + +/* + * Used by zle to indicate it has already printed a "use 'exit' to exit" + * message. + */ +/**/ +mod_export int use_exit_printed; + +/* + * This is real main entry point. This has to be mod_export'ed + * so zsh.exe can found it on Cygwin + */ + +/**/ +mod_export int +zsh_main(UNUSED(int argc), char **argv) +{ + char **t, *runscript = NULL, *zsh_name; + char *cmd; /* argument to -c */ + int t0; +#ifdef USE_LOCALE + setlocale(LC_ALL, ""); +#endif + + init_jobs(argv, environ); + + /* + * Provisionally set up the type table to allow metafication. + * This will be done properly when we have decided if we are + * interactive + */ + typtab['\0'] |= IMETA; + typtab[STOUC(Meta) ] |= IMETA; + typtab[STOUC(Marker)] |= IMETA; + for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(Nularg); t0++) + typtab[t0] |= ITOK | IMETA; + + for (t = argv; *t; *t = metafy(*t, -1, META_ALLOC), t++); + + zsh_name = argv[0]; + do { + char *arg0 = zsh_name; + if (!(zsh_name = strrchr(arg0, '/'))) + zsh_name = arg0; + else + zsh_name++; + if (*zsh_name == '-') + zsh_name++; + if (strcmp(zsh_name, "su") == 0) { + char *sh = zgetenv("SHELL"); + if (sh && *sh && arg0 != sh) + zsh_name = sh; + else + break; + } else + break; + } while (zsh_name); + + fdtable_size = zopenmax(); + fdtable = zshcalloc(fdtable_size*sizeof(*fdtable)); + fdtable[0] = fdtable[1] = fdtable[2] = FDT_EXTERNAL; + + createoptiontable(); + /* sets emulation, LOGINSHELL, PRIVILEGED, ZLE, INTERACTIVE, + * SHINSTDIN and SINGLECOMMAND */ + parseargs(zsh_name, argv, &runscript, &cmd); + + SHTTY = -1; + init_io(cmd); + setupvals(cmd, runscript, zsh_name); + + init_signals(); + init_bltinmods(); + init_builtins(); + run_init_scripts(); + setupshin(runscript); + init_misc(cmd, zsh_name); + + for (;;) { + /* + * See if we can free up some of jobtab. + * We only do this at top level, because if we are + * executing stuff we may refer to them by job pointer. + */ + int errexit = 0; + maybeshrinkjobtab(); + + do { + /* Reset return from top level which gets us back here */ + retflag = 0; + loop(1,0); + if (errflag && !interact && !isset(CONTINUEONERROR)) { + errexit = 1; + break; + } + } while (tok != ENDINPUT && (tok != LEXERR || isset(SHINSTDIN))); + if (tok == LEXERR || errexit) { + /* Make sure a fatal error exits with non-zero status */ + if (!lastval) + lastval = 1; + stopmsg = 1; + zexit(lastval, 0); + } + if (!(isset(IGNOREEOF) && interact)) { +#if 0 + if (interact) + fputs(islogin ? "logout\n" : "exit\n", shout); +#endif + zexit(lastval, 0); + continue; + } + noexitct++; + if (noexitct >= 10) { + stopmsg = 1; + zexit(lastval, 0); + } + /* + * Don't print the message if it was already handled by + * zle, since that makes special arrangements to keep + * the display tidy. + */ + if (!use_exit_printed) + zerrnam("zsh", (!islogin) ? "use 'exit' to exit." + : "use 'logout' to logout."); + } +} diff --git a/dotfiles/system/.zsh/modules/Src/input.c b/dotfiles/system/.zsh/modules/Src/input.c new file mode 100644 index 0000000..9787ded --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/input.c @@ -0,0 +1,701 @@ +/* + * input.c - read and store lines of input + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + + +/* + * This file deals with input buffering, supplying characters to the + * history expansion code a character at a time. Input is stored on a + * stack, which allows insertion of strings into the input, possibly with + * flags marking the end of alias expansion, with minimal copying of + * strings. The same stack is used to record the fact that the input + * is a history or alias expansion and to store the alias while it is in use. + * + * Input is taken either from zle, if appropriate, or read directly from + * the input file, or may be supplied by some other part of the shell (such + * as `eval' or $(...) substitution). In the last case, it should be + * supplied by pushing a new level onto the stack, via inpush(input_string, + * flag, alias); if the current input really needs to be altered, use + * inputsetline(input_string, flag). `Flag' can include or's of INP_FREE + * (if the input string is to be freed when used), INP_CONT (if the input + * is to continue onto what's already in the input queue), INP_ALIAS + * (push supplied alias onto stack) or INP_HIST (ditto, but used to + * mark history expansion). `alias' is ignored unless INP_ALIAS or + * INP_HIST is supplied. INP_ALIAS is always set if INP_HIST is. + * + * Note that the input string is itself used as the input buffer: it is not + * copied, nor is it every written back to, so using a constant string + * should work. Consequently, when passing areas of memory from the heap + * it is necessary that that heap last as long as the operation of reading + * the string. After the string is read, the stack should be popped with + * inpop(), which effectively flushes any unread input as well as restoring + * the previous input state. + * + * The internal flags INP_ALCONT and INP_HISTCONT show that the stack + * element was pushed by an alias or history expansion; they should not + * be needed elsewhere. + * + * The global variable inalmore is set to indicate aliases should + * continue to be expanded because the last alias expansion ended + * in a space. It is only reset after a complete word was read + * without expanding a new alias, in exalias(). + * + * PWS 1996/12/10 + */ + +#ifdef HAVE_STDIO_H +#include +#endif + +#include "zsh.mdh" +#include "input.pro" + +/* the shell input fd */ + +/**/ +int SHIN; + +/* buffered shell input for non-interactive shells */ + +/**/ +FILE *bshin; + +/* != 0 means we are reading input from a string */ + +/**/ +int strin; + +/* total # of characters waiting to be read. */ + +/**/ +mod_export int inbufct; + +/* the flags controlling the input routines in input.c: see INP_* in zsh.h */ + +/**/ +int inbufflags; + +static char *inbuf; /* Current input buffer */ +static char *inbufptr; /* Pointer into input buffer */ +static char *inbufpush; /* Character at which to re-push alias */ +static int inbufleft; /* Characters left in current input + stack element */ + + + /* Input must be stacked since the input queue is used by + * various different parts of the shell. + */ + +struct instacks { + char *buf, *bufptr; + Alias alias; + int bufleft, bufct, flags; +}; +static struct instacks *instack, *instacktop; +/* + * Input stack size. We need to push the stack for aliases, history + * expansion, and reading from internal strings: only if these operations + * are nested do we need more than one extra level. Thus we shouldn't need + * too much space as a rule. Initially, INSTACK_INITIAL is allocated; if + * more is required, an extra INSTACK_EXPAND is added each time. + */ +#define INSTACK_INITIAL 4 +#define INSTACK_EXPAND 4 + +static int instacksz = INSTACK_INITIAL; + +/* Read a line from bshin. Convert tokens and * + * null characters to Meta c^32 character pairs. */ + +/**/ +mod_export char * +shingetline(void) +{ + char *line = NULL; + int ll = 0; + int c; + char buf[BUFSIZ]; + char *p; + int q = queue_signal_level(); + + p = buf; + winch_unblock(); + dont_queue_signals(); + for (;;) { + /* Can't fgets() here because we need to accept '\0' bytes */ + 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) { + *p++ = '\0'; + line = zrealloc(line, ll + (p - buf)); + memcpy(line + ll, buf, p - buf); + } + return line; + } + if (imeta(c)) { + *p++ = Meta; + *p++ = c ^ 32; + } else + *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(); + dont_queue_signals(); + } + } +} + +/* Get the next character from the input. + * Will call inputline() to get a new line where necessary. + */ + +/**/ +int +ingetc(void) +{ + int lastc = ' '; + + if (lexstop) + return ' '; + for (;;) { + if (inbufleft) { + inbufleft--; + inbufct--; + if (itok(lastc = STOUC(*inbufptr++))) + continue; + if (((inbufflags & INP_LINENO) || !strin) && lastc == '\n') + lineno++; + break; + } + + /* + * See if we have reached the end of input + * (due to an error, or to reading from a single string). + * Check the remaining characters left, since if there aren't + * any we don't want to pop the stack---it'll mark any aliases + * as not in use before we've finished processing. + */ + if (!inbufct && (strin || errflag)) { + lexstop = 1; + break; + } + /* If the next element down the input stack is a continuation of + * this, use it. + */ + if (inbufflags & INP_CONT) { + inpoptop(); + continue; + } + /* As a last resort, get some more input */ + if (inputline()) + break; + } + if (!lexstop) + zshlex_raw_add(lastc); + return lastc; +} + +/* Read a line from the current command stream and store it as input */ + +/**/ +static int +inputline(void) +{ + char *ingetcline, **ingetcpmptl = NULL, **ingetcpmptr = NULL; + int context = ZLCON_LINE_START; + + /* If reading code interactively, work out the prompts. */ + if (interact && isset(SHINSTDIN)) { + if (!isfirstln) { + ingetcpmptl = &prompt2; + if (rprompt2) + ingetcpmptr = &rprompt2; + context = ZLCON_LINE_CONT; + } + else { + ingetcpmptl = &prompt; + if (rprompt) + ingetcpmptr = &rprompt; + } + } + if (!(interact && isset(SHINSTDIN) && SHTTY != -1 && isset(USEZLE))) { + /* + * If not using zle, read the line straight from the input file. + * Possibly we don't get the whole line at once: in that case, + * we get another chunk with the next call to inputline(). + */ + + if (interact && isset(SHINSTDIN)) { + /* + * We may still be interactive (e.g. running under emacs), + * so output a prompt if necessary. We don't know enough + * about the input device to be able to handle an rprompt, + * though. + */ + char *pptbuf; + int pptlen; + pptbuf = unmetafy(promptexpand(ingetcpmptl ? *ingetcpmptl : NULL, + 0, NULL, NULL, NULL), &pptlen); + write_loop(2, pptbuf, pptlen); + free(pptbuf); + } + ingetcline = shingetline(); + } else { + /* + * Since we may have to read multiple lines before getting + * a complete piece of input, we tell zle not to restore the + * original tty settings after reading each chunk. Instead, + * this is done when the history mechanism for the current input + * terminates, which is not until we have the whole input. + * This is supposed to minimise problems on systems that clobber + * typeahead when the terminal settings are altered. + * pws 1998/03/12 + */ + int flags = ZLRF_HISTORY|ZLRF_NOSETTY; + if (isset(IGNOREEOF)) + flags |= ZLRF_IGNOREEOF; + ingetcline = zleentry(ZLE_CMD_READ, ingetcpmptl, ingetcpmptr, + flags, context); + histdone |= HISTFLAG_SETTY; + } + if (!ingetcline) { + return lexstop = 1; + } + if (errflag) { + free(ingetcline); + errflag |= ERRFLAG_ERROR; + return lexstop = 1; + } + if (isset(VERBOSE)) { + /* Output the whole line read so far. */ + zputs(ingetcline, stderr); + fflush(stderr); + } + if (keyboardhackchar && *ingetcline && + ingetcline[strlen(ingetcline) - 1] == '\n' && + interact && isset(SHINSTDIN) && + SHTTY != -1 && ingetcline[1]) + { + char *stripptr = ingetcline + strlen(ingetcline) - 2; + if (*stripptr == keyboardhackchar) { + /* Junk an unwanted character at the end of the line. + (key too close to return key) */ + int ct = 1; /* force odd */ + char *ptr; + + if (keyboardhackchar == '\'' || keyboardhackchar == '"' || + keyboardhackchar == '`') { + /* + * for the chars above, also require an odd count before + * junking + */ + for (ct = 0, ptr = ingetcline; *ptr; ptr++) + if (*ptr == keyboardhackchar) + ct++; + } + if (ct & 1) { + stripptr[0] = '\n'; + stripptr[1] = '\0'; + } + } + } + isfirstch = 1; + if ((inbufflags & INP_APPEND) && inbuf) { + /* + * We need new input but need to be able to back up + * over the old input, so append this line. + * Pushing the line onto the stack doesn't have the right + * effect. + * + * This is quite a simple and inefficient fix, but currently + * we only need it when backing up over a multi-line $((... + * that turned out to be a command substitution rather than + * a math substitution, which is a very special case. + * So it's not worth rewriting. + */ + char *oinbuf = inbuf; + int newlen = strlen(ingetcline); + int oldlen = (int)(inbufptr - inbuf) + inbufleft; + if (inbufflags & INP_FREE) { + inbuf = realloc(inbuf, oldlen + newlen + 1); + } else { + inbuf = zalloc(oldlen + newlen + 1); + memcpy(inbuf, oinbuf, oldlen); + } + inbufptr += inbuf - oinbuf; + strcpy(inbuf + oldlen, ingetcline); + free(ingetcline); + inbufleft += newlen; + inbufct += newlen; + inbufflags |= INP_FREE; + } else { + /* Put this into the input channel. */ + inputsetline(ingetcline, INP_FREE); + } + + return 0; +} + +/* + * Put a string in the input queue: + * inbuf is only freeable if the flags include INP_FREE. + */ + +/**/ +static void +inputsetline(char *str, int flags) +{ + queue_signals(); + + if ((inbufflags & INP_FREE) && inbuf) { + free(inbuf); + } + inbuf = inbufptr = str; + inbufleft = strlen(inbuf); + + /* + * inbufct must reflect the total number of characters left, + * as it used by other parts of the shell, so we need to take account + * of whether the input stack continues, and whether there + * is an extra space to add on at the end. + */ + if (flags & INP_CONT) + inbufct += inbufleft; + else + inbufct = inbufleft; + inbufflags = flags; + + unqueue_signals(); +} + +/* + * Backup one character of the input. + * The last character can always be backed up, provided we didn't just + * expand an alias or a history reference. + * In fact, the character is ignored and the previous character is used. + * (If that's wrong, the bug is in the calling code. Use the #ifdef DEBUG + * code to check.) + */ + +/**/ +void +inungetc(int c) +{ + if (!lexstop) { + if (inbufptr != inbuf) { +#ifdef DEBUG + /* Just for debugging: enable only if foul play suspected. */ + if (inbufptr[-1] != (char) c) + fprintf(stderr, "Warning: backing up wrong character.\n"); +#endif + /* Just decrement the pointer: if it's not the same + * character being pushed back, we're in trouble anyway. + */ + inbufptr--; + inbufct++; + inbufleft++; + if (((inbufflags & INP_LINENO) || !strin) && c == '\n') + lineno--; + } + else if (!(inbufflags & INP_CONT)) { +#ifdef DEBUG + /* Just for debugging */ + fprintf(stderr, "Attempt to inungetc() at start of input.\n"); +#endif + zerr("Garbled input at %c (binary file as commands?)", c); + return; + } + else { + /* + * The character is being backed up from a previous input stack + * layer. However, there was an expansion in the middle, so we + * can't back up where we want to. Instead, we just push it + * onto the input stack as an extra character. + */ + char *cback = (char *)zshcalloc(2); + cback[0] = (char) c; + inpush(cback, INP_FREE|INP_CONT, NULL); + } + /* If we are back at the start of a segment, + * we may need to restore an alias popped from the stack. + * Note this may be a dummy (history expansion) entry. + */ + if (inbufptr == inbufpush && + (inbufflags & (INP_ALCONT|INP_HISTCONT))) { + /* + * Go back up the stack over all entries which were alias + * expansions and were pushed with nothing remaining to read. + */ + do { + if (instacktop->alias) + instacktop->alias->inuse = 1; + instacktop++; + } while ((instacktop->flags & (INP_ALCONT|INP_HISTCONT)) + && !instacktop->bufleft); + if (inbufflags & INP_HISTCONT) + inbufflags = INP_CONT|INP_ALIAS|INP_HIST; + else + inbufflags = INP_CONT|INP_ALIAS; + inbufleft = 0; + inbuf = inbufptr = ""; + } + zshlex_raw_back(); + } +} + +/* stuff a whole file into the input queue and print it */ + +/**/ +int +stuff(char *fn) +{ + FILE *in; + char *buf; + off_t len; + + if (!(in = fopen(unmeta(fn), "r"))) { + zerr("can't open %s", fn); + return 1; + } + fseek(in, 0, 2); + len = ftell(in); + fseek(in, 0, 0); + buf = (char *)zalloc(len + 1); + if (!(fread(buf, len, 1, in))) { + zerr("read error on %s", fn); + fclose(in); + zfree(buf, len + 1); + return 1; + } + fclose(in); + buf[len] = '\0'; + fwrite(buf, len, 1, stderr); + fflush(stderr); + inputsetline(metafy(buf, len, META_REALLOC), INP_FREE); + return 0; +} + +/* flush input queue */ + +/**/ +void +inerrflush(void) +{ + while (!lexstop && inbufct) + ingetc(); +} + +/* Set some new input onto a new element of the input stack */ + +/**/ +mod_export void +inpush(char *str, int flags, Alias inalias) +{ + if (!instack) { + /* Initial stack allocation */ + instack = (struct instacks *)zalloc(instacksz*sizeof(struct instacks)); + instacktop = instack; + } + + instacktop->buf = inbuf; + instacktop->bufptr = inbufptr; + instacktop->bufleft = inbufleft; + instacktop->bufct = inbufct; + inbufflags &= ~(INP_ALCONT|INP_HISTCONT); + if (flags & (INP_ALIAS|INP_HIST)) { + /* + * Text is expansion for history or alias, so continue + * back to old level when done. Also mark stack top + * as alias continuation so as to back up if necessary, + * and mark alias as in use. + */ + flags |= INP_CONT|INP_ALIAS; + if (flags & INP_HIST) + instacktop->flags = inbufflags | INP_HISTCONT; + else + instacktop->flags = inbufflags | INP_ALCONT; + if ((instacktop->alias = inalias)) + inalias->inuse = 1; + } else { + /* If we are continuing an alias expansion, record the alias + * expansion in new set of flags (do we need this?) + */ + if (((instacktop->flags = inbufflags) & INP_ALIAS) && + (flags & INP_CONT)) + flags |= INP_ALIAS; + } + + instacktop++; + if (instacktop == instack + instacksz) { + /* Expand the stack */ + instack = (struct instacks *) + realloc(instack, + (instacksz + INSTACK_EXPAND)*sizeof(struct instacks)); + instacktop = instack + instacksz; + instacksz += INSTACK_EXPAND; + } + /* + * We maintain the entry above the highest one with real + * text as a flag to inungetc() that it can stop re-pushing the stack. + */ + instacktop->flags = 0; + + inbufpush = inbuf = NULL; + + inputsetline(str, flags); +} + +/* Remove the top element of the stack */ + +/**/ +static void +inpoptop(void) +{ + if (!lexstop) { + inbufflags &= ~(INP_ALCONT|INP_HISTCONT); + while (inbufptr > inbuf) { + inbufptr--; + inbufct++; + inbufleft++; + /* + * As elsewhere in input and history mechanisms: + * unwinding aliases and unwinding history have different + * implications as aliases are after the lexer while + * history is before, but they're both pushed onto + * the input stack. + */ + if ((inbufflags & (INP_ALIAS|INP_HIST|INP_RAW_KEEP)) == INP_ALIAS) + zshlex_raw_back(); + } + } + + if (inbuf && (inbufflags & INP_FREE)) + free(inbuf); + + instacktop--; + + inbuf = instacktop->buf; + inbufptr = inbufpush = instacktop->bufptr; + inbufleft = instacktop->bufleft; + inbufct = instacktop->bufct; + inbufflags = instacktop->flags; + + if (!(inbufflags & (INP_ALCONT|INP_HISTCONT))) + return; + + if (instacktop->alias) { + char *t = instacktop->alias->text; + /* a real alias: mark it as unused. */ + instacktop->alias->inuse = 0; + if (*t && t[strlen(t) - 1] == ' ') { + inalmore = 1; + histbackword(); + } + } +} + +/* Remove the top element of the stack and all its continuations. */ + +/**/ +mod_export void +inpop(void) +{ + int remcont; + + do { + remcont = inbufflags & INP_CONT; + + inpoptop(); + } while (remcont); +} + +/* + * Expunge any aliases from the input stack; they shouldn't appear + * in the history and need to be flushed explicitly when we encounter + * an error. + */ + +/**/ +void +inpopalias(void) +{ + while (inbufflags & INP_ALIAS) + inpoptop(); +} + + +/* + * Get pointer to remaining string to read. + */ + +/**/ +char * +ingetptr(void) +{ + return inbufptr; +} + +/* + * Check if the current input line, including continuations, is + * expanding an alias. This does not detect alias expansions that + * have been fully processed and popped from the input stack. + * If there is an alias, the most recently expanded is returned, + * else NULL. + */ + +/**/ +char *input_hasalias(void) +{ + int flags = inbufflags; + struct instacks *instackptr = instacktop; + + for (;;) + { + if (!(flags & INP_CONT)) + break; + instackptr--; + if (instackptr->alias) + return instackptr->alias->node.nam; + flags = instackptr->flags; + } + + return NULL; +} diff --git a/dotfiles/system/.zsh/modules/Src/jobs.c b/dotfiles/system/.zsh/modules/Src/jobs.c new file mode 100644 index 0000000..38b3d89 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/jobs.c @@ -0,0 +1,2894 @@ +/* + * jobs.c - job control + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "jobs.pro" + +/* the process group of the shell at startup (equal to mypgprp, except + when we started without being process group leader */ + +/**/ +mod_export pid_t origpgrp; + +/* the process group of the shell */ + +/**/ +mod_export pid_t mypgrp; + +/* the job we are working on */ + +/**/ +mod_export int thisjob; + +/* the current job (+) */ + +/**/ +mod_export int curjob; + +/* the previous job (-) */ + +/**/ +mod_export int prevjob; + +/* the job table */ + +/**/ +mod_export struct job *jobtab; + +/* Size of the job table. */ + +/**/ +mod_export int jobtabsize; + +/* The highest numbered job in the jobtable */ + +/**/ +mod_export int maxjob; + +/* If we have entered a subshell, the original shell's job table. */ +static struct job *oldjobtab; + +/* The size of that. */ +static int oldmaxjob; + +/* shell timings */ + +/**/ +#ifdef HAVE_GETRUSAGE +/**/ +static struct rusage child_usage; +/**/ +#else +/**/ +static struct tms shtms; +/**/ +#endif + +/* 1 if ttyctl -f has been executed */ + +/**/ +mod_export int ttyfrozen; + +/* Previous values of errflag and breaks if the signal handler had to + * change them. And a flag saying if it did that. */ + +/**/ +int prev_errflag, prev_breaks, errbrk_saved; + +/**/ +int numpipestats, pipestats[MAX_PIPESTATS]; + +/* Diff two timevals for elapsed-time computations */ + +/**/ +static struct timeval * +dtime(struct timeval *dt, struct timeval *t1, struct timeval *t2) +{ + dt->tv_sec = t2->tv_sec - t1->tv_sec; + dt->tv_usec = t2->tv_usec - t1->tv_usec; + if (dt->tv_usec < 0) { + dt->tv_usec += 1000000.0; + dt->tv_sec -= 1.0; + } + return dt; +} + +/* change job table entry from stopped to running */ + +/**/ +void +makerunning(Job jn) +{ + Process pn; + + jn->stat &= ~STAT_STOPPED; + for (pn = jn->procs; pn; pn = pn->next) { +#if 0 + if (WIFSTOPPED(pn->status) && + (!(jn->stat & STAT_SUPERJOB) || pn->next)) + pn->status = SP_RUNNING; +#endif + if (WIFSTOPPED(pn->status)) + pn->status = SP_RUNNING; + } + + if (jn->stat & STAT_SUPERJOB) + makerunning(jobtab + jn->other); +} + +/* Find process and job associated with pid. * + * Return 1 if search was successful, else return 0. */ + +/**/ +int +findproc(pid_t pid, Job *jptr, Process *pptr, int aux) +{ + Process pn; + int i; + + *jptr = NULL; + *pptr = NULL; + for (i = 1; i <= maxjob; i++) + { + /* + * We are only interested in jobs with processes still + * marked as live. Careful in case there's an identical + * process number in a job we haven't quite got around + * to deleting. + */ + if (jobtab[i].stat & STAT_DONE) + continue; + + for (pn = aux ? jobtab[i].auxprocs : jobtab[i].procs; + pn; pn = pn->next) + { + /* + * Make sure we match a process that's still running. + * + * When a job contains two pids, one terminated pid and one + * running pid, then the condition (jobtab[i].stat & + * STAT_DONE) will not stop these pids from being candidates + * for the findproc result (which is supposed to be a + * RUNNING pid), and if the terminated pid is an identical + * process number for the pid identifying the running + * process we are trying to find (after pid number + * wrapping), then we need to avoid returning the terminated + * pid, otherwise the shell would block and wait forever for + * the termination of the process which pid we were supposed + * to return in a different job. + */ + if (pn->pid == pid) { + *pptr = pn; + *jptr = jobtab + i; + if (pn->status == SP_RUNNING) + return 1; + } + } + } + + return (*pptr && *jptr); +} + +/* Does the given job number have any processes? */ + +/**/ +int +hasprocs(int job) +{ + Job jn; + + if (job < 0) { + DPUTS(1, "job number invalid in hasprocs"); + return 0; + } + jn = jobtab + job; + + return jn->procs || jn->auxprocs; +} + +/* Find the super-job of a sub-job. */ + +/**/ +static int +super_job(int sub) +{ + int i; + + for (i = 1; i <= maxjob; i++) + if ((jobtab[i].stat & STAT_SUPERJOB) && + jobtab[i].other == sub && + jobtab[i].gleader) + return i; + return 0; +} + +/**/ +static int +handle_sub(int job, int fg) +{ + /* job: superjob; sj: subjob. */ + Job jn = jobtab + job, sj = jobtab + jn->other; + + if ((sj->stat & STAT_DONE) || (!sj->procs && !sj->auxprocs)) { + struct process *p; + + for (p = sj->procs; p; p = p->next) { + if (WIFSIGNALED(p->status)) { + if (jn->gleader != mypgrp && jn->procs->next) + killpg(jn->gleader, WTERMSIG(p->status)); + else + kill(jn->procs->pid, WTERMSIG(p->status)); + kill(sj->other, SIGCONT); + kill(sj->other, WTERMSIG(p->status)); + break; + } + } + if (!p) { + int cp; + + jn->stat &= ~STAT_SUPERJOB; + jn->stat |= STAT_WASSUPER; + + if ((cp = ((WIFEXITED(jn->procs->status) || + WIFSIGNALED(jn->procs->status)) && + killpg(jn->gleader, 0) == -1))) { + Process p; + for (p = jn->procs; p->next; p = p->next); + jn->gleader = p->pid; + } + /* This deleted the job too early if the parent + shell waited for a command in a list that will + be executed by the sub-shell (e.g.: if we have + `ls|if true;then sleep 20;cat;fi' and ^Z the + sleep, the rest will be executed by a sub-shell, + but the parent shell gets notified for the + sleep. + deletejob(sj, 0); */ + /* If this super-job contains only the sub-shell, + we have to attach the tty to its process group + now. */ + if ((fg || thisjob == job) && + (!jn->procs->next || cp || jn->procs->pid != jn->gleader)) + attachtty(jn->gleader); + kill(sj->other, SIGCONT); + if (jn->stat & STAT_DISOWN) + { + deletejob(jn, 1); + } + } + curjob = jn - jobtab; + } else if (sj->stat & STAT_STOPPED) { + struct process *p; + + jn->stat |= STAT_STOPPED; + for (p = jn->procs; p; p = p->next) + if (p->status == SP_RUNNING || + (!WIFEXITED(p->status) && !WIFSIGNALED(p->status))) + p->status = sj->procs->status; + curjob = jn - jobtab; + printjob(jn, !!isset(LONGLISTJOBS), 1); + return 1; + } + return 0; +} + + +/* Get the latest usage information */ + +/**/ +void +get_usage(void) +{ +#ifdef HAVE_GETRUSAGE + getrusage(RUSAGE_CHILDREN, &child_usage); +#else + times(&shtms); +#endif +} + + +#if !defined HAVE_WAIT3 || !defined HAVE_GETRUSAGE +/* Update status of process that we have just WAIT'ed for */ + +/**/ +void +update_process(Process pn, int status) +{ + struct timezone dummy_tz; +#ifdef HAVE_GETRUSAGE + struct timeval childs = child_usage.ru_stime; + struct timeval childu = child_usage.ru_utime; +#else + long childs = shtms.tms_cstime; + long childu = shtms.tms_cutime; +#endif + + /* get time-accounting info */ + get_usage(); + gettimeofday(&pn->endtime, &dummy_tz); /* record time process exited */ + + pn->status = status; /* save the status returned by WAIT */ +#ifdef HAVE_GETRUSAGE + dtime(&pn->ti.ru_stime, &childs, &child_usage.ru_stime); + dtime(&pn->ti.ru_utime, &childu, &child_usage.ru_utime); +#else + pn->ti.st = shtms.tms_cstime - childs; /* compute process system space time */ + pn->ti.ut = shtms.tms_cutime - childu; /* compute process user space time */ +#endif +} +#endif + +/* + * Called when the current shell is behaving as if it received + * a interactively generated signal (sig). + * + * As we got the signal or are pretending we did, we need to pretend + * anything attached to a CURSH process got it, too. + */ +/**/ +void +check_cursh_sig(int sig) +{ + int i, j; + + if (!errflag) + return; + for (i = 1; i <= maxjob; i++) { + if ((jobtab[i].stat & (STAT_CURSH|STAT_DONE)) == + STAT_CURSH) { + for (j = 0; j < 2; j++) { + Process pn = j ? jobtab[i].auxprocs : jobtab[i].procs; + for (; pn; pn = pn->next) { + if (pn->status == SP_RUNNING) { + kill(pn->pid, sig); + } + } + } + } + } +} + +/**/ +void +storepipestats(Job jn, int inforeground, int fixlastval) +{ + int i, pipefail = 0, jpipestats[MAX_PIPESTATS]; + Process p; + + for (p = jn->procs, i = 0; p && i < MAX_PIPESTATS; p = p->next, i++) { + jpipestats[i] = (WIFSIGNALED(p->status) ? + 0200 | WTERMSIG(p->status) : + (WIFSTOPPED(p->status) ? + 0200 | WEXITSTATUS(p->status) : + WEXITSTATUS(p->status))); + if (jpipestats[i]) + pipefail = jpipestats[i]; + } + if (inforeground) { + memcpy(pipestats, jpipestats, sizeof(int)*i); + if ((jn->stat & STAT_CURSH) && i < MAX_PIPESTATS) + pipestats[i++] = lastval; + numpipestats = i; + } + + if (fixlastval) { + if (jn->stat & STAT_CURSH) { + if (!lastval && isset(PIPEFAIL)) + lastval = pipefail; + } else if (isset(PIPEFAIL)) + lastval = pipefail; + } +} + +/* Update status of job, possibly printing it */ + +/**/ +void +update_job(Job jn) +{ + Process pn; + int job; + int val = 0, status = 0; + int somestopped = 0, inforeground = 0; + + for (pn = jn->auxprocs; pn; pn = pn->next) { +#ifdef WIFCONTINUED + if (WIFCONTINUED(pn->status)) + pn->status = SP_RUNNING; +#endif + if (pn->status == SP_RUNNING) + return; + } + + for (pn = jn->procs; pn; pn = pn->next) { +#ifdef WIFCONTINUED + if (WIFCONTINUED(pn->status)) { + jn->stat &= ~STAT_STOPPED; + pn->status = SP_RUNNING; + } +#endif + if (pn->status == SP_RUNNING) /* some processes in this job are running */ + return; /* so no need to update job table entry */ + if (WIFSTOPPED(pn->status)) /* some processes are stopped */ + somestopped = 1; /* so job is not done, but entry needs updating */ + if (!pn->next) /* last job in pipeline determines exit status */ + val = (WIFSIGNALED(pn->status) ? + 0200 | WTERMSIG(pn->status) : + (WIFSTOPPED(pn->status) ? + 0200 | WEXITSTATUS(pn->status) : + WEXITSTATUS(pn->status))); + if (pn->pid == jn->gleader) /* if this process is process group leader */ + status = pn->status; + } + + job = jn - jobtab; /* compute job number */ + + if (somestopped) { + if (jn->stty_in_env && !jn->ty) { + jn->ty = (struct ttyinfo *) zalloc(sizeof(struct ttyinfo)); + gettyinfo(jn->ty); + } + if (jn->stat & STAT_STOPPED) { + if (jn->stat & STAT_SUBJOB) { + /* If we have `cat foo|while read a; grep $a bar;done' + * and have hit ^Z, the sub-job is stopped, but the + * super-job may still be running, waiting to be stopped + * or to exit. So we have to send it a SIGTSTP. */ + int i; + + if ((i = super_job(job))) + killpg(jobtab[i].gleader, SIGTSTP); + } + return; + } + } + { /* job is done or stopped, remember return value */ + lastval2 = val; + /* If last process was run in the current shell, keep old status + * and let it handle its own traps, but always allow the test + * for the pgrp. + */ + if (jn->stat & STAT_CURSH) + inforeground = 1; + else if (job == thisjob) { + lastval = val; + inforeground = 2; + } + } + + if (shout && shout != stderr && !ttyfrozen && !jn->stty_in_env && + !zleactive && job == thisjob && !somestopped && + !(jn->stat & STAT_NOSTTY)) + gettyinfo(&shttyinfo); + + if (isset(MONITOR)) { + pid_t pgrp = gettygrp(); /* get process group of tty */ + + /* is this job in the foreground of an interactive shell? */ + if (mypgrp != pgrp && inforeground && + (jn->gleader == pgrp || (pgrp > 1 && kill(-pgrp, 0) == -1))) { + if (list_pipe) { + if (somestopped || (pgrp > 1 && kill(-pgrp, 0) == -1)) { + attachtty(mypgrp); + /* check window size and adjust if necessary */ + adjustwinsize(0); + } else { + /* + * Oh, dear, we're right in the middle of some confusion + * of shell jobs on the righthand side of a pipeline, so + * it's death to call attachtty() just yet. Mark the + * fact in the job, so that the attachtty() will be called + * when the job is finally deleted. + */ + jn->stat |= STAT_ATTACH; + } + /* If we have `foo|while true; (( x++ )); done', and hit + * ^C, we have to stop the loop, too. */ + if ((val & 0200) && inforeground == 1 && + ((val & ~0200) == SIGINT || (val & ~0200) == SIGQUIT)) { + if (!errbrk_saved) { + errbrk_saved = 1; + prev_breaks = breaks; + prev_errflag = errflag; + } + breaks = loops; + errflag |= ERRFLAG_INT; + inerrflush(); + } + } else { + attachtty(mypgrp); + /* check window size and adjust if necessary */ + adjustwinsize(0); + } + } + } else if (list_pipe && (val & 0200) && inforeground == 1 && + ((val & ~0200) == SIGINT || (val & ~0200) == SIGQUIT)) { + if (!errbrk_saved) { + errbrk_saved = 1; + prev_breaks = breaks; + prev_errflag = errflag; + } + breaks = loops; + errflag |= ERRFLAG_INT; + inerrflush(); + } + if (somestopped && jn->stat & STAT_SUPERJOB) + return; + jn->stat |= (somestopped) ? STAT_CHANGED | STAT_STOPPED : + STAT_CHANGED | STAT_DONE; + if (jn->stat & (STAT_DONE|STAT_STOPPED)) { + /* This may be redundant with printjob() but note that inforeground + * is true here for STAT_CURSH jobs even when job != thisjob, most + * likely because thisjob = -1 from exec.c:execsimple() trickery. + * However, if we reset lastval here we break it for printjob(). + */ + storepipestats(jn, inforeground, 0); + } + if (!inforeground && + (jn->stat & (STAT_SUBJOB | STAT_DONE)) == (STAT_SUBJOB | STAT_DONE)) { + int su; + + if ((su = super_job(jn - jobtab))) + handle_sub(su, 0); + } + if ((jn->stat & (STAT_DONE | STAT_STOPPED)) == STAT_STOPPED) { + prevjob = curjob; + curjob = job; + } + if ((isset(NOTIFY) || job == thisjob) && (jn->stat & STAT_LOCKED)) { + if (printjob(jn, !!isset(LONGLISTJOBS), 0) && + zleactive) + zleentry(ZLE_CMD_REFRESH); + } + if (sigtrapped[SIGCHLD] && job != thisjob) + dotrap(SIGCHLD); + + /* When MONITOR is set, the foreground process runs in a different * + * process group from the shell, so the shell will not receive * + * terminal signals, therefore we pretend that the shell got * + * the signal too. */ + if (inforeground == 2 && isset(MONITOR) && WIFSIGNALED(status)) { + int sig = WTERMSIG(status); + + if (sig == SIGINT || sig == SIGQUIT) { + if (sigtrapped[sig]) { + dotrap(sig); + /* We keep the errflag as set or not by dotrap. + * This is to fulfil the promise to carry on + * with the jobs if trap returns zero. + * Setting breaks = loops ensures a consistent return + * status if inside a loop. Maybe the code in loops + * should be changed. + */ + if (errflag) + breaks = loops; + } else { + breaks = loops; + errflag |= ERRFLAG_INT; + } + check_cursh_sig(sig); + } + } +} + +/* set the previous job to something reasonable */ + +/**/ +static void +setprevjob(void) +{ + int i; + + for (i = maxjob; i; i--) + if ((jobtab[i].stat & STAT_INUSE) && (jobtab[i].stat & STAT_STOPPED) && + !(jobtab[i].stat & STAT_SUBJOB) && i != curjob && i != thisjob) { + prevjob = i; + return; + } + + for (i = maxjob; i; i--) + if ((jobtab[i].stat & STAT_INUSE) && !(jobtab[i].stat & STAT_SUBJOB) && + i != curjob && i != thisjob) { + prevjob = i; + return; + } + + prevjob = -1; +} + +/**/ +long +get_clktck(void) +{ + static long clktck; + +#ifdef _SC_CLK_TCK + if (!clktck) + /* fetch clock ticks per second from * + * sysconf only the first time */ + clktck = sysconf(_SC_CLK_TCK); +#else +# ifdef __NeXT__ + /* NeXTStep 3.3 defines CLK_TCK wrongly */ + clktck = 60; +# else +# ifdef CLK_TCK + clktck = CLK_TCK; +# else +# ifdef HZ + clktck = HZ; +# else + clktck = 60; +# endif +# endif +# endif +#endif + + return clktck; +} + +/**/ +static void +printhhmmss(double secs) +{ + int mins = (int) secs / 60; + int hours = mins / 60; + + secs -= 60 * mins; + mins -= 60 * hours; + if (hours) + fprintf(stderr, "%d:%02d:%05.2f", hours, mins, secs); + else if (mins) + fprintf(stderr, "%d:%05.2f", mins, secs); + else + fprintf(stderr, "%.3f", secs); +} + +static void +printtime(struct timeval *real, child_times_t *ti, char *desc) +{ + char *s; + double elapsed_time, user_time, system_time; +#ifdef HAVE_GETRUSAGE + double total_time; +#endif + int percent, desclen; + + if (!desc) + { + desc = ""; + desclen = 0; + } + else + { + desc = dupstring(desc); + unmetafy(desc, &desclen); + } + + /* go ahead and compute these, since almost every TIMEFMT will have them */ + elapsed_time = real->tv_sec + real->tv_usec / 1000000.0; + +#ifdef HAVE_GETRUSAGE + user_time = ti->ru_utime.tv_sec + ti->ru_utime.tv_usec / 1000000.0; + system_time = ti->ru_stime.tv_sec + ti->ru_stime.tv_usec / 1000000.0; + total_time = user_time + system_time; + percent = 100.0 * total_time + / (real->tv_sec + real->tv_usec / 1000000.0); +#else + { + long clktck = get_clktck(); + user_time = ti->ut / (double) clktck; + system_time = ti->st / (double) clktck; + percent = 100.0 * (ti->ut + ti->st) + / (clktck * real->tv_sec + clktck * real->tv_usec / 1000000.0); + } +#endif + + queue_signals(); + if (!(s = getsparam("TIMEFMT"))) + s = DEFAULT_TIMEFMT; + else + s = unmetafy(s, NULL); + + for (; *s; s++) + if (*s == '%') + switch (*++s) { + case 'E': + fprintf(stderr, "%4.2fs", elapsed_time); + break; + case 'U': + fprintf(stderr, "%4.2fs", user_time); + break; + case 'S': + fprintf(stderr, "%4.2fs", system_time); + break; + case 'm': + switch (*++s) { + case 'E': + fprintf(stderr, "%0.fms", elapsed_time * 1000.0); + break; + case 'U': + fprintf(stderr, "%0.fms", user_time * 1000.0); + break; + case 'S': + fprintf(stderr, "%0.fms", system_time * 1000.0); + break; + default: + fprintf(stderr, "%%m"); + s--; + break; + } + break; + case 'u': + switch (*++s) { + case 'E': + fprintf(stderr, "%0.fus", elapsed_time * 1000000.0); + break; + case 'U': + fprintf(stderr, "%0.fus", user_time * 1000000.0); + break; + case 'S': + fprintf(stderr, "%0.fus", system_time * 1000000.0); + break; + default: + fprintf(stderr, "%%u"); + s--; + break; + } + break; + case '*': + switch (*++s) { + case 'E': + printhhmmss(elapsed_time); + break; + case 'U': + printhhmmss(user_time); + break; + case 'S': + printhhmmss(system_time); + break; + default: + fprintf(stderr, "%%*"); + s--; + break; + } + break; + case 'P': + fprintf(stderr, "%d%%", percent); + break; +#ifdef HAVE_STRUCT_RUSAGE_RU_NSWAP + case 'W': + fprintf(stderr, "%ld", ti->ru_nswap); + break; +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_IXRSS + case 'X': + fprintf(stderr, "%ld", + total_time ? + (long)(ti->ru_ixrss / total_time) : + (long)0); + break; +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_IDRSS + case 'D': + fprintf(stderr, "%ld", + total_time ? + (long) ((ti->ru_idrss +#ifdef HAVE_STRUCT_RUSAGE_RU_ISRSS + + ti->ru_isrss +#endif + ) / total_time) : + (long)0); + break; +#endif +#if defined(HAVE_STRUCT_RUSAGE_RU_IDRSS) || \ + defined(HAVE_STRUCT_RUSAGE_RU_ISRSS) || \ + defined(HAVE_STRUCT_RUSAGE_RU_IXRSS) + case 'K': + /* treat as D if X not available */ + fprintf(stderr, "%ld", + total_time ? + (long) (( +#ifdef HAVE_STRUCT_RUSAGE_RU_IXRSS + ti->ru_ixrss +#else + 0 +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_IDRSS + + ti->ru_idrss +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_ISRSS + + ti->ru_isrss +#endif + ) / total_time) : + (long)0); + break; +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_MAXRSS + case 'M': + fprintf(stderr, "%ld", ti->ru_maxrss / 1024); + break; +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_MAJFLT + case 'F': + fprintf(stderr, "%ld", ti->ru_majflt); + break; +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_MINFLT + case 'R': + fprintf(stderr, "%ld", ti->ru_minflt); + break; +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_INBLOCK + case 'I': + fprintf(stderr, "%ld", ti->ru_inblock); + break; +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_OUBLOCK + case 'O': + fprintf(stderr, "%ld", ti->ru_oublock); + break; +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_MSGRCV + case 'r': + fprintf(stderr, "%ld", ti->ru_msgrcv); + break; +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_MSGSND + case 's': + fprintf(stderr, "%ld", ti->ru_msgsnd); + break; +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_NSIGNALS + case 'k': + fprintf(stderr, "%ld", ti->ru_nsignals); + break; +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_NVCSW + case 'w': + fprintf(stderr, "%ld", ti->ru_nvcsw); + break; +#endif +#ifdef HAVE_STRUCT_RUSAGE_RU_NIVCSW + case 'c': + fprintf(stderr, "%ld", ti->ru_nivcsw); + break; +#endif + case 'J': + fwrite(desc, sizeof(char), desclen, stderr); + break; + case '%': + putc('%', stderr); + break; + case '\0': + s--; + break; + default: + fprintf(stderr, "%%%c", *s); + break; + } else + putc(*s, stderr); + unqueue_signals(); + putc('\n', stderr); + fflush(stderr); +} + +/**/ +static void +dumptime(Job jn) +{ + Process pn; + struct timeval dtimeval; + + if (!jn->procs) + return; + for (pn = jn->procs; pn; pn = pn->next) + printtime(dtime(&dtimeval, &pn->bgtime, &pn->endtime), &pn->ti, + pn->text); +} + +/* Check whether shell should report the amount of time consumed * + * by job. This will be the case if we have preceded the command * + * with the keyword time, or if REPORTTIME is non-negative and the * + * amount of time consumed by the job is greater than REPORTTIME */ + +/**/ +static int +should_report_time(Job j) +{ + struct value vbuf; + Value v; + char *s = "REPORTTIME"; + int save_errflag = errflag; + zlong reporttime = -1; +#ifdef HAVE_GETRUSAGE + char *sm = "REPORTMEMORY"; + zlong reportmemory = -1; +#endif + + /* if the time keyword was used */ + if (j->stat & STAT_TIMED) + return 1; + + queue_signals(); + errflag = 0; + if ((v = getvalue(&vbuf, &s, 0))) + reporttime = getintvalue(v); +#ifdef HAVE_GETRUSAGE + if ((v = getvalue(&vbuf, &sm, 0))) + reportmemory = getintvalue(v); +#endif + errflag = save_errflag; + unqueue_signals(); + if (reporttime < 0 +#ifdef HAVE_GETRUSAGE + && reportmemory < 0 +#endif + ) + return 0; + /* can this ever happen? */ + if (!j->procs) + return 0; + if (zleactive) + return 0; + + if (reporttime >= 0) + { +#ifdef HAVE_GETRUSAGE + reporttime -= j->procs->ti.ru_utime.tv_sec + + j->procs->ti.ru_stime.tv_sec; + if (j->procs->ti.ru_utime.tv_usec + + j->procs->ti.ru_stime.tv_usec >= 1000000) + reporttime--; + if (reporttime <= 0) + return 1; +#else + { + clktck = get_clktck(); + if ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime) + return 1; + } +#endif + } + +#ifdef HAVE_GETRUSAGE + if (reportmemory >= 0 && + j->procs->ti.ru_maxrss / 1024 > reportmemory) + return 1; +#endif + + return 0; +} + +/* !(lng & 3) means jobs * + * (lng & 1) means jobs -l * + * (lng & 2) means jobs -p + * (lng & 4) means jobs -d + * + * synch = 0 means asynchronous + * synch = 1 means synchronous + * synch = 2 means called synchronously from jobs + * synch = 3 means called synchronously from bg or fg + * + * Returns 1 if some output was done. + * + * The function also deletes the job if it was done, even it + * is not printed. + */ + +/**/ +int +printjob(Job jn, int lng, int synch) +{ + Process pn; + int job, len = 9, sig, sflag = 0, llen; + int conted = 0, lineleng = zterm_columns, skip = 0, doputnl = 0; + int doneprint = 0, skip_print = 0; + FILE *fout = (synch == 2 || !shout) ? stdout : shout; + + if (synch > 1 && oldjobtab != NULL) + job = jn - oldjobtab; + else + job = jn - jobtab; + DPUTS3(job < 0 || job > (oldjobtab && synch > 1 ? oldmaxjob : maxjob), + "bogus job number, jn = %L, jobtab = %L, oldjobtab = %L", + (long)jn, (long)jobtab, (long)oldjobtab); + + if (jn->stat & STAT_NOPRINT) { + skip_print = 1; + } + + if (lng < 0) { + conted = 1; + lng = !!isset(LONGLISTJOBS); + } + +/* find length of longest signame, check to see */ +/* if we really need to print this job */ + + for (pn = jn->procs; pn; pn = pn->next) { + if (jn->stat & STAT_SUPERJOB && + jn->procs->status == SP_RUNNING && !pn->next) + pn->status = SP_RUNNING; + if (pn->status != SP_RUNNING) { + if (WIFSIGNALED(pn->status)) { + sig = WTERMSIG(pn->status); + llen = strlen(sigmsg(sig)); + if (WCOREDUMP(pn->status)) + llen += 14; + if (llen > len) + len = llen; + if (sig != SIGINT && sig != SIGPIPE) + sflag = 1; + if (job == thisjob && sig == SIGINT) + doputnl = 1; + if (isset(PRINTEXITVALUE) && isset(SHINSTDIN)) { + sflag = 1; + skip_print = 0; + } + } else if (WIFSTOPPED(pn->status)) { + sig = WSTOPSIG(pn->status); + if ((int)strlen(sigmsg(sig)) > len) + len = strlen(sigmsg(sig)); + if (job == thisjob && sig == SIGTSTP) + doputnl = 1; + } else if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) && + WEXITSTATUS(pn->status)) { + sflag = 1; + skip_print = 0; + } + } + } + + if (skip_print) { + if (jn->stat & STAT_DONE) { + /* This looks silly, but see update_job() */ + if (synch <= 1) + storepipestats(jn, job == thisjob, job == thisjob); + if (should_report_time(jn)) + dumptime(jn); + deletejob(jn, 0); + if (job == curjob) { + curjob = prevjob; + prevjob = job; + } + if (job == prevjob) + setprevjob(); + } + return 0; + } + + /* + * - Always print if called from jobs + * - Otherwise, require MONITOR option ("jobbing") and some + * change of state + * - also either the shell is interactive or this is synchronous. + */ + if (synch == 2 || + ((interact || synch) && jobbing && + ((jn->stat & STAT_STOPPED) || sflag || job != thisjob))) { + int len2, fline = 1; + /* POSIX requires just the job text for bg and fg */ + int plainfmt = (synch == 3) && isset(POSIXJOBS); + /* use special format for current job, except in `jobs' */ + int thisfmt = job == thisjob && synch != 2; + Process qn; + + if (!synch) + zleentry(ZLE_CMD_TRASH); + if (doputnl && !synch) { + doneprint = 1; + putc('\n', fout); + } + for (pn = jn->procs; pn;) { + len2 = (thisfmt ? 5 : 10) + len; /* 2 spaces */ + if (lng & 3) + qn = pn->next; + else + for (qn = pn->next; qn; qn = qn->next) { + if (qn->status != pn->status) + break; + if ((int)strlen(qn->text) + len2 + ((qn->next) ? 3 : 0) + > lineleng) + break; + len2 += strlen(qn->text) + 2; + } + doneprint = 1; + if (!plainfmt) { + if (!thisfmt || lng) { + if (fline) + fprintf(fout, "[%ld] %c ", + (long)job, + (job == curjob) ? '+' + : (job == prevjob) ? '-' : ' '); + else + fprintf(fout, (job > 9) ? " " : " "); + } else + fprintf(fout, "zsh: "); + if (lng & 1) + fprintf(fout, "%ld ", (long) pn->pid); + else if (lng & 2) { + pid_t x = jn->gleader; + + fprintf(fout, "%ld ", (long) x); + do + skip++; + while ((x /= 10)); + skip++; + lng &= ~3; + } else + fprintf(fout, "%*s", skip, ""); + if (pn->status == SP_RUNNING) { + if (!conted) + fprintf(fout, "running%*s", len - 7 + 2, ""); + else + fprintf(fout, "continued%*s", len - 9 + 2, ""); + } + else if (WIFEXITED(pn->status)) { + if (WEXITSTATUS(pn->status)) + fprintf(fout, "exit %-4d%*s", WEXITSTATUS(pn->status), + len - 9 + 2, ""); + else + fprintf(fout, "done%*s", len - 4 + 2, ""); + } else if (WIFSTOPPED(pn->status)) + fprintf(fout, "%-*s", len + 2, + sigmsg(WSTOPSIG(pn->status))); + else if (WCOREDUMP(pn->status)) + fprintf(fout, "%s (core dumped)%*s", + sigmsg(WTERMSIG(pn->status)), + (int)(len - 14 + 2 - + strlen(sigmsg(WTERMSIG(pn->status)))), ""); + else + fprintf(fout, "%-*s", len + 2, + sigmsg(WTERMSIG(pn->status))); + } + for (; pn != qn; pn = pn->next) { + char *txt = dupstring(pn->text); + int txtlen; + unmetafy(txt, &txtlen); + fwrite(txt, sizeof(char), txtlen, fout); + if (pn->next) + fputs(" | ", fout); + } + putc('\n', fout); + fline = 0; + } + fflush(fout); + } else if (doputnl && interact && !synch) { + doneprint = 1; + putc('\n', fout); + fflush(fout); + } + + /* print "(pwd now: foo)" messages: with (lng & 4) we are printing + * the directory where the job is running, otherwise the current directory + */ + + if ((lng & 4) || (interact && job == thisjob && + jn->pwd && strcmp(jn->pwd, pwd))) { + doneprint = 1; + fprintf(fout, "(pwd %s: ", (lng & 4) ? "" : "now"); + fprintdir(((lng & 4) && jn->pwd) ? jn->pwd : pwd, fout); + fprintf(fout, ")\n"); + fflush(fout); + } + + /* delete job if done */ + + if (jn->stat & STAT_DONE) { + /* This looks silly, but see update_job() */ + if (synch <= 1) + storepipestats(jn, job == thisjob, job == thisjob); + if (should_report_time(jn)) + dumptime(jn); + deletejob(jn, 0); + if (job == curjob) { + curjob = prevjob; + prevjob = job; + } + if (job == prevjob) + setprevjob(); + } else + jn->stat &= ~STAT_CHANGED; + + return doneprint; +} + +/* Add a file to be deleted or fd to be closed to the current job */ + +/**/ +void +addfilelist(const char *name, int fd) +{ + Jobfile jf = (Jobfile)zalloc(sizeof(struct jobfile)); + LinkList ll = jobtab[thisjob].filelist; + + if (!ll) + ll = jobtab[thisjob].filelist = znewlinklist(); + if (name) + { + jf->u.name = ztrdup(name); + jf->is_fd = 0; + } + else + { + jf->u.fd = fd; + jf->is_fd = 1; + } + zaddlinknode(ll, jf); +} + +/* Clean up pipes no longer needed associated with a job */ + +/**/ +void +pipecleanfilelist(LinkList filelist, int proc_subst_only) +{ + LinkNode node; + + if (!filelist) + return; + node = firstnode(filelist); + while (node) { + Jobfile jf = (Jobfile)getdata(node); + if (jf->is_fd && + (!proc_subst_only || fdtable[jf->u.fd] == FDT_PROC_SUBST)) { + LinkNode next = nextnode(node); + zclose(jf->u.fd); + (void)remnode(filelist, node); + zfree(jf, sizeof(*jf)); + node = next; + } else + incnode(node); + } +} + +/* Finished with list of files for a job */ + +/**/ +void +deletefilelist(LinkList file_list, int disowning) +{ + Jobfile jf; + if (file_list) { + while ((jf = (Jobfile)getlinknode(file_list))) { + if (jf->is_fd) { + if (!disowning) + zclose(jf->u.fd); + } else { + if (!disowning) + unlink(jf->u.name); + zsfree(jf->u.name); + } + zfree(jf, sizeof(*jf)); + } + zfree(file_list, sizeof(struct linklist)); + } +} + +/**/ +void +freejob(Job jn, int deleting) +{ + struct process *pn, *nx; + + pn = jn->procs; + jn->procs = NULL; + for (; pn; pn = nx) { + nx = pn->next; + zfree(pn, sizeof(struct process)); + } + + pn = jn->auxprocs; + jn->auxprocs = NULL; + for (; pn; pn = nx) { + nx = pn->next; + zfree(pn, sizeof(struct process)); + } + + if (jn->ty) + zfree(jn->ty, sizeof(struct ttyinfo)); + if (jn->pwd) + zsfree(jn->pwd); + jn->pwd = NULL; + if (jn->stat & STAT_WASSUPER) { + /* careful in case we shrink and move the job table */ + int job = jn - jobtab; + if (deleting) + deletejob(jobtab + jn->other, 0); + else + freejob(jobtab + jn->other, 0); + jn = jobtab + job; + } + jn->gleader = jn->other = 0; + jn->stat = jn->stty_in_env = 0; + jn->filelist = NULL; + jn->ty = NULL; + + /* Find the new highest job number. */ + if (maxjob == jn - jobtab) { + while (maxjob && !(jobtab[maxjob].stat & STAT_INUSE)) + maxjob--; + } +} + +/* + * We are actually finished with this job, rather + * than freeing it to make space. + * + * If "disowning" is set, files associated with the job are not + * actually deleted --- and won't be as there is nothing left + * to clear up. + */ + +/**/ +void +deletejob(Job jn, int disowning) +{ + deletefilelist(jn->filelist, disowning); + if (jn->stat & STAT_ATTACH) { + attachtty(mypgrp); + adjustwinsize(0); + } + if (jn->stat & STAT_SUPERJOB) { + Job jno = jobtab + jn->other; + if (jno->stat & STAT_SUBJOB) + jno->stat |= STAT_SUBJOB_ORPHANED; + } + + freejob(jn, 1); +} + +/* + * Add a process to the current job. + * The third argument is 1 if we are adding a process which is not + * part of the main pipeline but an auxiliary process used for + * handling MULTIOS or process substitution. We will wait for it + * but not display job information about it. + */ + +/**/ +void +addproc(pid_t pid, char *text, int aux, struct timeval *bgtime) +{ + Process pn, *pnlist; + + DPUTS(thisjob == -1, "No valid job in addproc."); + pn = (Process) zshcalloc(sizeof *pn); + pn->pid = pid; + if (text) + strcpy(pn->text, text); + else + *pn->text = '\0'; + pn->status = SP_RUNNING; + pn->next = NULL; + + if (!aux) + { + pn->bgtime = *bgtime; + /* if this is the first process we are adding to * + * the job, then it's the group leader. */ + if (!jobtab[thisjob].gleader) + jobtab[thisjob].gleader = pid; + /* attach this process to end of process list of current job */ + pnlist = &jobtab[thisjob].procs; + } + else + pnlist = &jobtab[thisjob].auxprocs; + + if (*pnlist) { + Process n; + + for (n = *pnlist; n->next; n = n->next); + n->next = pn; + } else { + /* first process for this job */ + *pnlist = pn; + } + /* If the first process in the job finished before any others were * + * added, maybe STAT_DONE got set incorrectly. This can happen if * + * a $(...) was waited for and the last existing job in the * + * pipeline was already finished. We need to be very careful that * + * there was no call to printjob() between then and now, else * + * the job will already have been deleted from the table. */ + jobtab[thisjob].stat &= ~STAT_DONE; +} + +/* Check if we have files to delete. We need to check this to see * + * if it's all right to exec a command without forking in the last * + * component of subshells or after the `-c' option. */ + +/**/ +int +havefiles(void) +{ + int i; + + for (i = 1; i <= maxjob; i++) + if (jobtab[i].stat && jobtab[i].filelist) + return 1; + return 0; + +} + +/* + * Wait for a particular process. + * wait_cmd indicates this is from the interactive wait command, + * in which case the behaviour is a little different: the command + * itself can be interrupted by a trapped signal. + */ + +/**/ +int +waitforpid(pid_t pid, int wait_cmd) +{ + int first = 1, q = queue_signal_level(); + + /* child_block() around this loop in case #ifndef WNOHANG */ + 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 if (!wait_cmd) + kill(pid, SIGCONT); + + last_signal = -1; + signal_suspend(SIGCHLD, wait_cmd); + if (last_signal != SIGCHLD && wait_cmd && last_signal >= 0 && + (sigtrapped[last_signal] & ZSIG_TRAPPED)) { + /* wait command interrupted, but no error: return */ + restore_queue_signals(q); + return 128 + last_signal; + } + child_block(); + } + unqueue_traps(); + child_unblock(); + restore_queue_signals(q); + + return 0; +} + +/* + * Wait for a job to finish. + * wait_cmd indicates this is from the wait builtin; see + * wait_cmd in waitforpid(). + */ + +/**/ +static int +zwaitjob(int job, int wait_cmd) +{ + int q = queue_signal_level(); + Job jn = jobtab + job; + + 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) + printjob(jn, !!isset(LONGLISTJOBS), 1); + if (jn->filelist) { + /* + * The main shell is finished with any file descriptors used + * for process substitution associated with this job: close + * them to indicate to listeners there's no more input. + * + * Note we can't safely delete temporary files yet as these + * are directly visible to other processes. However, + * we can't deadlock on the fact that those still exist, so + * that's not a problem. + */ + pipecleanfilelist(jn->filelist, 0); + } + while (!(errflag & ERRFLAG_ERROR) && jn->stat && + !(jn->stat & STAT_DONE) && + !(interact && (jn->stat & STAT_STOPPED))) { + signal_suspend(SIGCHLD, wait_cmd); + if (last_signal != SIGCHLD && wait_cmd && last_signal >= 0 && + (sigtrapped[last_signal] & ZSIG_TRAPPED)) + { + /* builtin wait interrupted by trapped signal */ + restore_queue_signals(q); + return 128 + last_signal; + } + /* Commenting this out makes ^C-ing a job started by a function + stop the whole function again. But I guess it will stop + something else from working properly, we have to find out + what this might be. --oberon + + When attempting to separate errors and interrupts, we + assumed because of the previous comment it would be OK + to remove ERRFLAG_ERROR and leave ERRFLAG_INT set, since + that's the one related to ^C. But that doesn't work. + There's something more here we don't understand. --pws + + The change above to ignore ERRFLAG_INT in the loop test + solves a problem wherein child processes that ignore the + INT signal were never waited-for. Clearing the flag here + still seems the wrong thing, but perhaps ERRFLAG_INT + should be saved and restored around signal_suspend() to + prevent it being lost within a signal trap? --Bart + + errflag = 0; */ + + if (subsh) { + killjb(jn, SIGCONT); + jn->stat &= ~STAT_STOPPED; + } + if (jn->stat & STAT_SUPERJOB) + if (handle_sub(jn - jobtab, 1)) + break; + child_block(); + } + } else { + deletejob(jn, 0); + pipestats[0] = lastval; + numpipestats = 1; + } + restore_queue_signals(q); + unqueue_traps(); + child_unblock(); + + return 0; +} + +/* wait for running job to finish */ + +/**/ +void +waitjobs(void) +{ + Job jn = jobtab + thisjob; + DPUTS(thisjob == -1, "No valid job in waitjobs."); + + if (jn->procs || jn->auxprocs) + zwaitjob(thisjob, 0); + else { + deletejob(jn, 0); + pipestats[0] = lastval; + numpipestats = 1; + } + thisjob = -1; +} + +/* clear job table when entering subshells */ + +/**/ +mod_export void +clearjobtab(int monitor) +{ + int i; + + if (isset(POSIXJOBS)) + oldmaxjob = 0; + for (i = 1; i <= maxjob; i++) { + /* + * See if there is a jobtable worth saving. + * We never free the saved version; it only happens + * once for each subshell of a shell with job control, + * so doesn't create a leak. + */ + if (monitor && !isset(POSIXJOBS) && jobtab[i].stat) + oldmaxjob = i+1; + else if (jobtab[i].stat & STAT_INUSE) + freejob(jobtab + i, 0); + } + + if (monitor && oldmaxjob) { + int sz = oldmaxjob * sizeof(struct job); + if (oldjobtab) + free(oldjobtab); + oldjobtab = (struct job *)zalloc(sz); + memcpy(oldjobtab, jobtab, sz); + + /* Don't report any job we're part of */ + if (thisjob != -1 && thisjob < oldmaxjob) + memset(oldjobtab+thisjob, 0, sizeof(struct job)); + } + + memset(jobtab, 0, jobtabsize * sizeof(struct job)); /* zero out table */ + maxjob = 0; + + /* + * Although we don't have job control in subshells, we + * sometimes needs control structures for other purposes such + * as multios. Grab a job for this purpose; any will do + * since we've freed them all up (so there's no question + * of problems with the job table size here). + */ + thisjob = initjob(); +} + +static int initnewjob(int i) +{ + jobtab[i].stat = STAT_INUSE; + if (jobtab[i].pwd) { + zsfree(jobtab[i].pwd); + jobtab[i].pwd = NULL; + } + jobtab[i].gleader = 0; + + if (i > maxjob) + maxjob = i; + + return i; +} + +/* Get a free entry in the job table and initialize it. */ + +/**/ +int +initjob(void) +{ + int i; + + for (i = 1; i <= maxjob; i++) + if (!jobtab[i].stat) + return initnewjob(i); + if (maxjob + 1 < jobtabsize) + return initnewjob(maxjob+1); + + if (expandjobtab()) + return initnewjob(i); + + zerr("job table full or recursion limit exceeded"); + return -1; +} + +/**/ +void +setjobpwd(void) +{ + int i; + + for (i = 1; i <= maxjob; i++) + if (jobtab[i].stat && !jobtab[i].pwd) + jobtab[i].pwd = ztrdup(pwd); +} + +/* print pids for & */ + +/**/ +void +spawnjob(void) +{ + Process pn; + + DPUTS(thisjob == -1, "No valid job in spawnjob."); + /* if we are not in a subshell */ + if (!subsh) { + if (curjob == -1 || !(jobtab[curjob].stat & STAT_STOPPED)) { + curjob = thisjob; + setprevjob(); + } else if (prevjob == -1 || !(jobtab[prevjob].stat & STAT_STOPPED)) + prevjob = thisjob; + if (jobbing && jobtab[thisjob].procs) { + FILE *fout = shout ? shout : stdout; + fprintf(fout, "[%d]", thisjob); + for (pn = jobtab[thisjob].procs; pn; pn = pn->next) + fprintf(fout, " %ld", (long) pn->pid); + fprintf(fout, "\n"); + fflush(fout); + } + } + if (!hasprocs(thisjob)) + deletejob(jobtab + thisjob, 0); + else { + jobtab[thisjob].stat |= STAT_LOCKED; + pipecleanfilelist(jobtab[thisjob].filelist, 0); + } + thisjob = -1; +} + +/**/ +void +shelltime(void) +{ + struct timezone dummy_tz; + struct timeval dtimeval, now; + child_times_t ti; +#ifndef HAVE_GETRUSAGE + struct tms buf; +#endif + + gettimeofday(&now, &dummy_tz); + +#ifdef HAVE_GETRUSAGE + getrusage(RUSAGE_SELF, &ti); +#else + times(&buf); + + ti.ut = buf.tms_utime; + ti.st = buf.tms_stime; +#endif + printtime(dtime(&dtimeval, &shtimer, &now), &ti, "shell"); + +#ifdef HAVE_GETRUSAGE + getrusage(RUSAGE_CHILDREN, &ti); +#else + ti.ut = buf.tms_cutime; + ti.st = buf.tms_cstime; +#endif + printtime(&dtimeval, &ti, "children"); + +} + +/* see if jobs need printing */ + +/**/ +void +scanjobs(void) +{ + int i; + + for (i = 1; i <= maxjob; i++) + if (jobtab[i].stat & STAT_CHANGED) + printjob(jobtab + i, !!isset(LONGLISTJOBS), 1); +} + +/**** job control builtins ****/ + +/* This simple function indicates whether or not s may represent * + * a number. It returns true iff s consists purely of digits and * + * minuses. Note that minus may appear more than once, and the empty * + * string will produce a `true' response. */ + +/**/ +static int +isanum(char *s) +{ + while (*s == '-' || idigit(*s)) + s++; + return *s == '\0'; +} + +/* Make sure we have a suitable current and previous job set. */ + +/**/ +static void +setcurjob(void) +{ + if (curjob == thisjob || + (curjob != -1 && !(jobtab[curjob].stat & STAT_INUSE))) { + curjob = prevjob; + setprevjob(); + if (curjob == thisjob || + (curjob != -1 && !((jobtab[curjob].stat & STAT_INUSE) && + curjob != thisjob))) { + curjob = prevjob; + setprevjob(); + } + } +} + +/* Convert a job specifier ("%%", "%1", "%foo", "%?bar?", etc.) * + * to a job number. */ + +/**/ +mod_export int +getjob(const char *s, const char *prog) +{ + int jobnum, returnval, mymaxjob; + Job myjobtab; + + if (oldjobtab) { + myjobtab = oldjobtab; + mymaxjob = oldmaxjob; + } else { + myjobtab= jobtab; + mymaxjob = maxjob; + } + + /* if there is no %, treat as a name */ + if (*s != '%') + goto jump; + s++; + /* "%%", "%+" and "%" all represent the current job */ + if (*s == '%' || *s == '+' || !*s) { + if (curjob == -1) { + if (prog) + zwarnnam(prog, "no current job"); + returnval = -1; + goto done; + } + returnval = curjob; + goto done; + } + /* "%-" represents the previous job */ + if (*s == '-') { + if (prevjob == -1) { + if (prog) + zwarnnam(prog, "no previous job"); + returnval = -1; + goto done; + } + returnval = prevjob; + goto done; + } + /* a digit here means we have a job number */ + if (idigit(*s)) { + jobnum = atoi(s); + if (jobnum && jobnum <= mymaxjob && myjobtab[jobnum].stat && + !(myjobtab[jobnum].stat & STAT_SUBJOB) && + /* + * If running jobs in a subshell, we are allowed to + * refer to the "current" job (it's not really the + * current job in the subshell). It's possible we + * should reset thisjob to -1 on entering the subshell. + */ + (myjobtab == oldjobtab || jobnum != thisjob)) { + returnval = jobnum; + goto done; + } + if (prog) + zwarnnam(prog, "%%%s: no such job", s); + returnval = -1; + goto done; + } + /* "%?" introduces a search string */ + if (*s == '?') { + struct process *pn; + + for (jobnum = mymaxjob; jobnum >= 0; jobnum--) + if (myjobtab[jobnum].stat && + !(myjobtab[jobnum].stat & STAT_SUBJOB) && + jobnum != thisjob) + for (pn = myjobtab[jobnum].procs; pn; pn = pn->next) + if (strstr(pn->text, s + 1)) { + returnval = jobnum; + goto done; + } + if (prog) + zwarnnam(prog, "job not found: %s", s); + returnval = -1; + goto done; + } + jump: + /* anything else is a job name, specified as a string that begins the + job's command */ + if ((jobnum = findjobnam(s)) != -1) { + returnval = jobnum; + goto done; + } + /* if we get here, it is because none of the above succeeded and went + to done */ + zwarnnam(prog, "job not found: %s", s); + returnval = -1; + done: + return returnval; +} + +#ifndef HAVE_SETPROCTITLE +/* For jobs -Z (which modifies the shell's name as seen in ps listings). * + * hackzero is the start of the safely writable space, and hackspace is * + * its length, excluding a final NUL terminator that will always be left. */ + +static char *hackzero; +static int hackspace; +#endif + + +/* Initialise job handling. */ + +/**/ +void +init_jobs(char **argv, char **envp) +{ +#ifndef HAVE_SETPROCTITLE + char *p, *q; +#endif + size_t init_bytes = MAXJOBS_ALLOC*sizeof(struct job); + + /* + * Initialise the job table. If this fails, we're in trouble. + */ + jobtab = (struct job *)zalloc(init_bytes); + if (!jobtab) { + zerr("failed to allocate job table, aborting."); + exit(1); + } + jobtabsize = MAXJOBS_ALLOC; + memset(jobtab, 0, init_bytes); + +#ifndef HAVE_SETPROCTITLE + /* + * Initialise the jobs -Z system. The technique is borrowed from + * perl: check through the argument and environment space, to see + * how many of the strings are in contiguous space. This determines + * the value of hackspace. + */ + hackzero = *argv; + p = strchr(hackzero, 0); + while(*++argv) { + q = *argv; + if(q != p+1) + goto done; + p = strchr(q, 0); + } +#if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV) + for(; *envp; envp++) { + q = *envp; + if(q != p+1) + goto done; + p = strchr(q, 0); + } +#endif + done: + hackspace = p - hackzero; +#endif +} + + +/* + * We have run out of space in the job table. + * Expand it by an additional MAXJOBS_ALLOC slots. + */ + +/* + * An arbitrary limit on the absolute maximum size of the job table. + * This prevents us taking over the entire universe. + * Ought to be a multiple of MAXJOBS_ALLOC, but doesn't need to be. + */ +#define MAX_MAXJOBS 1000 + +/**/ +int +expandjobtab(void) +{ + int newsize = jobtabsize + MAXJOBS_ALLOC; + struct job *newjobtab; + + if (newsize > MAX_MAXJOBS) + return 0; + + newjobtab = (struct job *)zrealloc(jobtab, newsize * sizeof(struct job)); + if (!newjobtab) + return 0; + + /* + * Clear the new section of the table; this is necessary for + * the jobs to appear unused. + */ + memset(newjobtab + jobtabsize, 0, MAXJOBS_ALLOC * sizeof(struct job)); + + jobtab = newjobtab; + jobtabsize = newsize; + + return 1; +} + + +/* + * See if we can reduce the job table. We can if we go over + * a MAXJOBS_ALLOC boundary. However, we leave a boundary, + * currently 20 jobs, so that we have a place for immediate + * expansion and don't play ping pong with the job table size. + */ + +/**/ +void +maybeshrinkjobtab(void) +{ + int jobbound; + + queue_signals(); + jobbound = maxjob + MAXJOBS_ALLOC - (maxjob % MAXJOBS_ALLOC); + if (jobbound < jobtabsize && jobbound > maxjob + 20) { + struct job *newjobtab; + + /* Hope this can't fail, but anyway... */ + newjobtab = (struct job *)zrealloc(jobtab, + jobbound*sizeof(struct job)); + + if (newjobtab) { + jobtab = newjobtab; + jobtabsize = jobbound; + } + } + unqueue_signals(); +} + +/* + * Definitions for the background process stuff recorded below. + * This would be more efficient as a hash, but + * - that's quite heavyweight for something not needed very often + * - we need some kind of ordering as POSIX allows us to limit + * the size of the list to the value of _SC_CHILD_MAX and clearly + * we want to clear the oldest first + * - cases with a long list of background jobs where the user doesn't + * wait for a large number, and then does wait for one (the only + * inefficient case) are rare + * - in the context of waiting for an external process, looping + * over a list isn't so very inefficient. + * Enough excuses already. + */ + +/* Data in the link list, a key (process ID) / value (exit status) pair. */ +struct bgstatus { + pid_t pid; + int status; +}; +typedef struct bgstatus *Bgstatus; +/* The list of those entries */ +static LinkList bgstatus_list; +/* Count of entries. Reaches value of _SC_CHILD_MAX and stops. */ +static long bgstatus_count; + +/* + * Remove and free a bgstatus entry. + */ +static void rembgstatus(LinkNode node) +{ + zfree(remnode(bgstatus_list, node), sizeof(struct bgstatus)); + bgstatus_count--; +} + +/* + * Record the status of a background process that exited so we + * can execute the builtin wait for it. + * + * We can't execute the wait builtin for something that exited in the + * foreground as it's not visible to the user, so don't bother recording. + */ + +/**/ +void +addbgstatus(pid_t pid, int status) +{ + static long child_max; + Bgstatus bgstatus_entry; + + if (!child_max) { +#ifdef _SC_CHILD_MAX + child_max = sysconf(_SC_CHILD_MAX); + if (!child_max) /* paranoia */ +#endif + { + /* Be inventive */ + child_max = 1024L; + } + } + + if (!bgstatus_list) { + bgstatus_list = znewlinklist(); + /* + * We're not always robust about memory failures, but + * this is pretty deep in the shell basics to be failing owing + * to memory, and a failure to wait is reported loudly, so test + * and fail silently here. + */ + if (!bgstatus_list) + return; + } + if (bgstatus_count == child_max) { + /* Overflow. List is in order, remove first */ + rembgstatus(firstnode(bgstatus_list)); + } + bgstatus_entry = (Bgstatus)zalloc(sizeof(*bgstatus_entry)); + if (!bgstatus_entry) { + /* See note above */ + return; + } + bgstatus_entry->pid = pid; + bgstatus_entry->status = status; + if (!zaddlinknode(bgstatus_list, bgstatus_entry)) { + zfree(bgstatus_entry, sizeof(*bgstatus_entry)); + return; + } + bgstatus_count++; +} + +/* + * See if pid has a recorded exit status. + * Note we make no guarantee that the PIDs haven't wrapped, so this + * may not be the right process. + * + * This is only used by wait, which must only work on each + * pid once, so we need to remove the entry if we find it. + */ + +static int getbgstatus(pid_t pid) +{ + LinkNode node; + Bgstatus bgstatus_entry; + + if (!bgstatus_list) + return -1; + for (node = firstnode(bgstatus_list); node; incnode(node)) { + bgstatus_entry = (Bgstatus)getdata(node); + if (bgstatus_entry->pid == pid) { + int status = bgstatus_entry->status; + rembgstatus(node); + return status; + } + } + return -1; +} + +/* bg, disown, fg, jobs, wait: most of the job control commands are * + * here. They all take the same type of argument. Exception: wait can * + * take a pid or a job specifier, whereas the others only work on jobs. */ + +/**/ +int +bin_fg(char *name, char **argv, Options ops, int func) +{ + int job, lng, firstjob = -1, retval = 0, ofunc = func; + + if (OPT_ISSET(ops,'Z')) { + int len; + + if(isset(RESTRICTED)) { + zwarnnam(name, "-Z is restricted"); + return 1; + } + if(!argv[0] || argv[1]) { + zwarnnam(name, "-Z requires one argument"); + return 1; + } + queue_signals(); + unmetafy(*argv, &len); +#ifdef HAVE_SETPROCTITLE + setproctitle("%s", *argv); +#else + if(len > hackspace) + len = hackspace; + memcpy(hackzero, *argv, len); + memset(hackzero + len, 0, hackspace - len); +#endif + unqueue_signals(); + return 0; + } + + if (func == BIN_JOBS) { + lng = (OPT_ISSET(ops,'l')) ? 1 : (OPT_ISSET(ops,'p')) ? 2 : 0; + if (OPT_ISSET(ops,'d')) + lng |= 4; + } else { + lng = !!isset(LONGLISTJOBS); + } + + if ((func == BIN_FG || func == BIN_BG) && !jobbing) { + /* oops... maybe bg and fg should have been disabled? */ + zwarnnam(name, "no job control in this shell."); + return 1; + } + + queue_signals(); + /* + * In case any processes changed state recently, wait for them. + * This updates stopped processes (but we should have been + * signalled about those, up to inevitable races), and also + * continued processes if that feature is available. + */ + wait_for_processes(); + + /* If necessary, update job table. */ + if (unset(NOTIFY)) + scanjobs(); + + if (func != BIN_JOBS || isset(MONITOR) || !oldmaxjob) + setcurjob(); + + if (func == BIN_JOBS) + /* If you immediately type "exit" after "jobs", this * + * will prevent zexit from complaining about stopped jobs */ + stopmsg = 2; + if (!*argv) { + /* This block handles all of the default cases (no arguments). bg, + fg and disown act on the current job, and jobs and wait act on all the + jobs. */ + if (func == BIN_FG || func == BIN_BG || func == BIN_DISOWN) { + /* W.r.t. the above comment, we'd better have a current job at this + point or else. */ + if (curjob == -1 || (jobtab[curjob].stat & STAT_NOPRINT)) { + zwarnnam(name, "no current job"); + unqueue_signals(); + return 1; + } + firstjob = curjob; + } else if (func == BIN_JOBS) { + /* List jobs. */ + struct job *jobptr; + int curmaxjob, ignorejob; + if (unset(MONITOR) && oldmaxjob) { + jobptr = oldjobtab; + curmaxjob = oldmaxjob ? oldmaxjob - 1 : 0; + ignorejob = 0; + } else { + jobptr = jobtab; + curmaxjob = maxjob; + ignorejob = thisjob; + } + for (job = 0; job <= curmaxjob; job++, jobptr++) + if (job != ignorejob && jobptr->stat) { + if ((!OPT_ISSET(ops,'r') && !OPT_ISSET(ops,'s')) || + (OPT_ISSET(ops,'r') && OPT_ISSET(ops,'s')) || + (OPT_ISSET(ops,'r') && + !(jobptr->stat & STAT_STOPPED)) || + (OPT_ISSET(ops,'s') && jobptr->stat & STAT_STOPPED)) + printjob(jobptr, lng, 2); + } + unqueue_signals(); + return 0; + } else { /* Must be BIN_WAIT, so wait for all jobs */ + for (job = 0; job <= maxjob; job++) + if (job != thisjob && jobtab[job].stat && + !(jobtab[job].stat & STAT_NOPRINT)) + retval = zwaitjob(job, 1); + unqueue_signals(); + return retval; + } + } + + /* Defaults have been handled. We now have an argument or two, or three... + In the default case for bg, fg and disown, the argument will be provided by + the above routine. We now loop over the arguments. */ + for (; (firstjob != -1) || *argv; (void)(*argv && argv++)) { + int stopped, ocj = thisjob, jstat; + + func = ofunc; + + if (func == BIN_WAIT && isanum(*argv)) { + /* wait can take a pid; the others can't. */ + pid_t pid = (long)atoi(*argv); + Job j; + Process p; + + if (findproc(pid, &j, &p, 0)) { + if (j->stat & STAT_STOPPED) { + retval = (killjb(j, SIGCONT) != 0); + if (retval == 0) + makerunning(j); + } + if (retval == 0) { + /* + * returns 0 for normal exit, else signal+128 + * in which case we should return that status. + */ + retval = waitforpid(pid, 1); + } + if (retval == 0) { + if ((retval = getbgstatus(pid)) < 0) { + retval = lastval2; + } + } + } else if ((retval = getbgstatus(pid)) < 0) { + zwarnnam(name, "pid %d is not a child of this shell", pid); + /* presumably lastval2 doesn't tell us a heck of a lot? */ + retval = 1; + } + thisjob = ocj; + continue; + } + if (func != BIN_JOBS && oldjobtab != NULL) { + zwarnnam(name, "can't manipulate jobs in subshell"); + unqueue_signals(); + return 1; + } + /* The only type of argument allowed now is a job spec. Check it. */ + job = (*argv) ? getjob(*argv, name) : firstjob; + firstjob = -1; + if (job == -1) { + retval = 1; + break; + } + jstat = oldjobtab ? oldjobtab[job].stat : jobtab[job].stat; + if (!(jstat & STAT_INUSE) || + (jstat & STAT_NOPRINT)) { + zwarnnam(name, "%s: no such job", *argv); + unqueue_signals(); + return 1; + } + /* If AUTO_CONTINUE is set (automatically make stopped jobs running + * on disown), we actually do a bg and then delete the job table entry. */ + + if (isset(AUTOCONTINUE) && func == BIN_DISOWN && + jstat & STAT_STOPPED) + func = BIN_BG; + + /* We have a job number. Now decide what to do with it. */ + switch (func) { + case BIN_FG: + case BIN_BG: + case BIN_WAIT: + if (func == BIN_BG) { + jobtab[job].stat |= STAT_NOSTTY; + jobtab[job].stat &= ~STAT_CURSH; + } + if ((stopped = (jobtab[job].stat & STAT_STOPPED))) { + makerunning(jobtab + job); + if (func == BIN_BG) { + /* Set $! to indicate this was backgrounded */ + Process pn = jobtab[job].procs; + for (;;) { + Process next = pn->next; + if (!next) { + lastpid = (zlong) pn->pid; + break; + } + pn = next; + } + } + } else if (func == BIN_BG) { + /* Silly to bg a job already running. */ + zwarnnam(name, "job already in background"); + thisjob = ocj; + unqueue_signals(); + return 1; + } + /* It's time to shuffle the jobs around! Reset the current job, + and pick a sensible secondary job. */ + if (curjob == job) { + curjob = prevjob; + prevjob = (func == BIN_BG) ? -1 : job; + } + if (prevjob == job || prevjob == -1) + setprevjob(); + if (curjob == -1) { + curjob = prevjob; + setprevjob(); + } + if (func != BIN_WAIT) + /* for bg and fg -- show the job we are operating on */ + printjob(jobtab + job, (stopped) ? -1 : lng, 3); + if (func != BIN_BG) { /* fg or wait */ + if (jobtab[job].pwd && strcmp(jobtab[job].pwd, pwd)) { + FILE *fout = (func == BIN_JOBS || !shout) ? stdout : shout; + fprintf(fout, "(pwd : "); + fprintdir(jobtab[job].pwd, fout); + fprintf(fout, ")\n"); + fflush(fout); + } + if (func != BIN_WAIT) { /* fg */ + thisjob = job; + if ((jobtab[job].stat & STAT_SUPERJOB) && + ((!jobtab[job].procs->next || + (jobtab[job].stat & STAT_SUBLEADER) || + killpg(jobtab[job].gleader, 0) == -1)) && + jobtab[jobtab[job].other].gleader) + attachtty(jobtab[jobtab[job].other].gleader); + else + attachtty(jobtab[job].gleader); + } + } + if (stopped) { + if (func != BIN_BG && jobtab[job].ty) + settyinfo(jobtab[job].ty); + killjb(jobtab + job, SIGCONT); + } + if (func == BIN_WAIT) + { + retval = zwaitjob(job, 1); + if (!retval) + retval = lastval2; + } + else if (func != BIN_BG) { + /* + * HERE: there used not to be an "else" above. How + * could it be right to wait for the foreground job + * when we've just been told to wait for another + * job (and done it)? + */ + waitjobs(); + retval = lastval2; + } else if (ofunc == BIN_DISOWN) + deletejob(jobtab + job, 1); + break; + case BIN_JOBS: + printjob(job + (oldjobtab ? oldjobtab : jobtab), lng, 2); + break; + case BIN_DISOWN: + if (jobtab[job].stat & STAT_SUPERJOB) { + jobtab[job].stat |= STAT_DISOWN; + continue; + } + if (jobtab[job].stat & STAT_STOPPED) { + char buf[20], *pids = ""; + + if (jobtab[job].stat & STAT_SUPERJOB) { + Process pn; + + for (pn = jobtab[jobtab[job].other].procs; pn; pn = pn->next) { + sprintf(buf, " -%d", pn->pid); + pids = dyncat(pids, buf); + } + for (pn = jobtab[job].procs; pn->next; pn = pn->next) { + sprintf(buf, " %d", pn->pid); + pids = dyncat(pids, buf); + } + if (!jobtab[jobtab[job].other].procs && pn) { + sprintf(buf, " %d", pn->pid); + pids = dyncat(pids, buf); + } + } else { + sprintf(buf, " -%d", jobtab[job].gleader); + pids = buf; + } + zwarnnam(name, +#ifdef USE_SUSPENDED + "warning: job is suspended, use `kill -CONT%s' to resume", +#else + "warning: job is stopped, use `kill -CONT%s' to resume", +#endif + pids); + } + deletejob(jobtab + job, 1); + break; + } + thisjob = ocj; + } + unqueue_signals(); + return retval; +} + +static const struct { + const char *name; + int num; +} alt_sigs[] = { +#if defined(SIGCHLD) && defined(SIGCLD) +#if SIGCHLD == SIGCLD + { "CLD", SIGCLD }, +#endif +#endif +#if defined(SIGPOLL) && defined(SIGIO) +#if SIGPOLL == SIGIO + { "IO", SIGIO }, +#endif +#endif +#if !defined(SIGERR) + /* + * If SIGERR is not defined by the operating system, use it + * as an alias for SIGZERR. + */ + { "ERR", SIGZERR }, +#endif + { NULL, 0 } +}; + +/* kill: send a signal to a process. The process(es) may be specified * + * by job specifier (see above) or pid. A signal, defaulting to * + * SIGTERM, may be specified by name or number, preceded by a dash. */ + +/**/ +int +bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) +{ + int sig = SIGTERM; + int returnval = 0; + + /* check for, and interpret, a signal specifier */ + if (*argv && **argv == '-') { + if (idigit((*argv)[1])) { + char *endp; + /* signal specified by number */ + sig = zstrtol(*argv + 1, &endp, 10); + if (*endp) { + zwarnnam(nam, "invalid signal number: %s", *argv); + return 1; + } + } else if ((*argv)[1] != '-' || (*argv)[2]) { + char *signame; + + /* with argument "-l" display the list of signal names */ + if ((*argv)[1] == 'l' && (*argv)[2] == '\0') { + if (argv[1]) { + while (*++argv) { + sig = zstrtol(*argv, &signame, 10); + if (signame == *argv) { + if (!strncmp(signame, "SIG", 3)) + signame += 3; + for (sig = 1; sig <= SIGCOUNT; sig++) + if (!strcasecmp(sigs[sig], signame)) + break; + if (sig > SIGCOUNT) { + int i; + + for (i = 0; alt_sigs[i].name; i++) + if (!strcasecmp(alt_sigs[i].name, signame)) + { + sig = alt_sigs[i].num; + break; + } + } + if (sig > SIGCOUNT) { + zwarnnam(nam, "unknown signal: SIG%s", + signame); + returnval++; + } else + printf("%d\n", sig); + } else { + if (*signame) { + zwarnnam(nam, "unknown signal: SIG%s", + signame); + returnval++; + } else { + if (WIFSIGNALED(sig)) + sig = WTERMSIG(sig); + else if (WIFSTOPPED(sig)) + sig = WSTOPSIG(sig); + if (1 <= sig && sig <= SIGCOUNT) + printf("%s\n", sigs[sig]); + else + printf("%d\n", sig); + } + } + } + return returnval; + } + printf("%s", sigs[1]); + for (sig = 2; sig <= SIGCOUNT; sig++) + printf(" %s", sigs[sig]); + putchar('\n'); + return 0; + } + + if ((*argv)[1] == 'n' && (*argv)[2] == '\0') { + char *endp; + + if (!*++argv) { + zwarnnam(nam, "-n: argument expected"); + return 1; + } + sig = zstrtol(*argv, &endp, 10); + if (*endp) { + zwarnnam(nam, "invalid signal number: %s", *argv); + return 1; + } + } else { + if (!((*argv)[1] == 's' && (*argv)[2] == '\0')) + signame = *argv + 1; + else if (!(*++argv)) { + zwarnnam(nam, "-s: argument expected"); + return 1; + } else + signame = *argv; + if (!*signame) { + zwarnnam(nam, "-: signal name expected"); + return 1; + } + signame = casemodify(signame, CASMOD_UPPER); + if (!strncmp(signame, "SIG", 3)) + signame+=3; + + /* check for signal matching specified name */ + for (sig = 1; sig <= SIGCOUNT; sig++) + if (!strcmp(*(sigs + sig), signame)) + break; + if (*signame == '0' && !signame[1]) + sig = 0; + if (sig > SIGCOUNT) { + int i; + + for (i = 0; alt_sigs[i].name; i++) + if (!strcmp(alt_sigs[i].name, signame)) + { + sig = alt_sigs[i].num; + break; + } + } + if (sig > SIGCOUNT) { + zwarnnam(nam, "unknown signal: SIG%s", signame); + zwarnnam(nam, "type kill -l for a list of signals"); + return 1; + } + } + } + argv++; + } + + /* Discard the standard "-" and "--" option breaks */ + if (*argv && (*argv)[0] == '-' && (!(*argv)[1] || (*argv)[1] == '-')) + argv++; + + if (!*argv) { + zwarnnam(nam, "not enough arguments"); + return 1; + } + + queue_signals(); + setcurjob(); + + /* Remaining arguments specify processes. Loop over them, and send the + signal (number sig) to each process. */ + for (; *argv; argv++) { + if (**argv == '%') { + /* job specifier introduced by '%' */ + int p; + + if ((p = getjob(*argv, nam)) == -1) { + returnval++; + continue; + } + if (killjb(jobtab + p, sig) == -1) { + zwarnnam("kill", "kill %s failed: %e", *argv, errno); + returnval++; + continue; + } + /* automatically update the job table if sending a SIGCONT to a + job, and send the job a SIGCONT if sending it a non-stopping + signal. */ + if (jobtab[p].stat & STAT_STOPPED) { +#ifndef WIFCONTINUED + /* With WIFCONTINUED we find this out properly */ + if (sig == SIGCONT) + makerunning(jobtab + p); +#endif + if (sig != SIGKILL && sig != SIGCONT && sig != SIGTSTP + && sig != SIGTTOU && sig != SIGTTIN && sig != SIGSTOP) + killjb(jobtab + p, SIGCONT); + } + } else if (!isanum(*argv)) { + zwarnnam("kill", "illegal pid: %s", *argv); + returnval++; + } else { + int pid = atoi(*argv); + if (kill(pid, sig) == -1) { + zwarnnam("kill", "kill %s failed: %e", *argv, errno); + returnval++; + } +#ifndef WIFCONTINUED + else if (sig == SIGCONT) { + Job jn; + Process pn; + /* With WIFCONTINUED we find this out properly */ + if (findproc(pid, &jn, &pn, 0)) { + if (WIFSTOPPED(pn->status)) + pn->status = SP_RUNNING; + } + } +#endif + } + } + unqueue_signals(); + + return returnval < 126 ? returnval : 1; +} +/* Get a signal number from a string */ + +/**/ +mod_export int +getsignum(const char *s) +{ + int x, i; + + /* check for a signal specified by number */ + x = atoi(s); + if (idigit(*s) && x >= 0 && x < VSIGCOUNT) + return x; + + /* search for signal by name */ + if (!strncmp(s, "SIG", 3)) + s += 3; + + for (i = 0; i < VSIGCOUNT; i++) + if (!strcmp(s, sigs[i])) + return i; + + for (i = 0; alt_sigs[i].name; i++) + { + if (!strcmp(s, alt_sigs[i].name)) + return alt_sigs[i].num; + } + + /* no matching signal */ + return -1; +} + +/* Get the name for a signal. */ + +/**/ +mod_export const char * +getsigname(int sig) +{ + if (sigtrapped[sig] & ZSIG_ALIAS) + { + int i; + for (i = 0; alt_sigs[i].name; i++) + if (sig == alt_sigs[i].num) + return alt_sigs[i].name; + } + else + return sigs[sig]; + + /* shouldn't reach here */ +#ifdef DEBUG + dputs("Bad alias flag for signal"); +#endif + return ""; +} + + +/* Get the function node for a trap, taking care about alternative names */ +/**/ +HashNode +gettrapnode(int sig, int ignoredisable) +{ + char fname[20]; + HashNode hn; + HashNode (*getptr)(HashTable ht, const char *name); + int i; + if (ignoredisable) + getptr = shfunctab->getnode2; + else + getptr = shfunctab->getnode; + + sprintf(fname, "TRAP%s", sigs[sig]); + if ((hn = getptr(shfunctab, fname))) + return hn; + + for (i = 0; alt_sigs[i].name; i++) { + if (alt_sigs[i].num == sig) { + sprintf(fname, "TRAP%s", alt_sigs[i].name); + if ((hn = getptr(shfunctab, fname))) + return hn; + } + } + + return NULL; +} + +/* Remove a TRAP function under any name for the signal */ + +/**/ +void +removetrapnode(int sig) +{ + HashNode hn = gettrapnode(sig, 1); + if (hn) { + shfunctab->removenode(shfunctab, hn->nam); + shfunctab->freenode(hn); + } +} + +/* Suspend this shell */ + +/**/ +int +bin_suspend(char *name, UNUSED(char **argv), Options ops, UNUSED(int func)) +{ + /* won't suspend a login shell, unless forced */ + if (islogin && !OPT_ISSET(ops,'f')) { + zwarnnam(name, "can't suspend login shell"); + return 1; + } + if (jobbing) { + /* stop ignoring signals */ + signal_default(SIGTTIN); + signal_default(SIGTSTP); + signal_default(SIGTTOU); + + /* Move ourselves back to the process group we came from */ + release_pgrp(); + } + + /* suspend ourselves with a SIGTSTP */ + killpg(origpgrp, SIGTSTP); + + if (jobbing) { + acquire_pgrp(); + /* restore signal handling */ + signal_ignore(SIGTTOU); + signal_ignore(SIGTSTP); + signal_ignore(SIGTTIN); + } + return 0; +} + +/* find a job named s */ + +/**/ +int +findjobnam(const char *s) +{ + int jobnum; + + for (jobnum = maxjob; jobnum >= 0; jobnum--) + if (!(jobtab[jobnum].stat & (STAT_SUBJOB | STAT_NOPRINT)) && + jobtab[jobnum].stat && jobtab[jobnum].procs && jobnum != thisjob && + jobtab[jobnum].procs->text[0] && strpfx(s, jobtab[jobnum].procs->text)) + return jobnum; + return -1; +} + + +/* make sure we are a process group leader by creating a new process + group if necessary */ + +/**/ +void +acquire_pgrp(void) +{ + long ttpgrp; + sigset_t blockset, oldset; + + if ((mypgrp = GETPGRP()) >= 0) { + long lastpgrp = mypgrp; + sigemptyset(&blockset); + sigaddset(&blockset, SIGTTIN); + sigaddset(&blockset, SIGTTOU); + sigaddset(&blockset, SIGTSTP); + oldset = signal_block(blockset); + while ((ttpgrp = gettygrp()) != -1 && ttpgrp != mypgrp) { + mypgrp = GETPGRP(); + if (mypgrp == mypid) { + if (!interact) + break; /* attachtty() will be a no-op, give up */ + signal_setmask(oldset); + attachtty(mypgrp); /* Might generate SIGT* */ + signal_block(blockset); + } + if (mypgrp == gettygrp()) + break; + signal_setmask(oldset); + if (read(0, NULL, 0) != 0) {} /* Might generate SIGT* */ + signal_block(blockset); + mypgrp = GETPGRP(); + if (mypgrp == lastpgrp && !interact) + break; /* Unlikely that pgrp will ever change */ + lastpgrp = mypgrp; + } + if (mypgrp != mypid) { + if (setpgrp(0, 0) == 0) { + mypgrp = mypid; + attachtty(mypgrp); + } else + opts[MONITOR] = 0; + } + signal_setmask(oldset); + } else + opts[MONITOR] = 0; +} + +/* revert back to the process group we came from (before acquire_pgrp) */ + +/**/ +void +release_pgrp(void) +{ + if (origpgrp != mypgrp) { + /* in linux pid namespaces, origpgrp may never have been set */ + if (origpgrp) { + attachtty(origpgrp); + setpgrp(0, origpgrp); + } + mypgrp = origpgrp; + } +} diff --git a/dotfiles/system/.zsh/modules/Src/lex.c b/dotfiles/system/.zsh/modules/Src/lex.c new file mode 100644 index 0000000..44ad880 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/lex.c @@ -0,0 +1,2203 @@ +/* + * lex.c - lexical analysis + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "lex.pro" + +#define LEX_HEAP_SIZE (32) + +/* tokens */ + +/**/ +mod_export char ztokens[] = "#$^*(())$=|{}[]`<>>?~`,-!'\"\\\\"; + +/* parts of the current token */ + +/**/ +char *zshlextext; +/**/ +mod_export char *tokstr; +/**/ +mod_export enum lextok tok; +/**/ +mod_export int tokfd; + +/* + * Line number at which the first character of a token was found. + * We always set this in gettok(), which is always called from + * zshlex() unless we have reached an error. So it is always + * valid when parsing. It is not useful during execution + * of the parsed structure. + */ + +/**/ +zlong toklineno; + +/* lexical analyzer error flag */ + +/**/ +mod_export int lexstop; + +/* if != 0, this is the first line of the command */ + +/**/ +mod_export int isfirstln; + +/* if != 0, this is the first char of the command (not including white space) */ + +/**/ +int isfirstch; + +/* flag that an alias should be expanded after expansion ending in space */ + +/**/ +int inalmore; + +/* + * Don't do spelling correction. + * Bit 1 is only valid for the current word. It's + * set when we detect a lookahead that stops the word from + * needing correction. + */ + +/**/ +int nocorrect; + +/* + * TBD: the following exported variables are part of the non-interface + * with ZLE for completion. They are poorly named and the whole + * scheme is incredibly brittle. One piece of robustness is applied: + * the variables are only set if LEXFLAGS_ZLE is set. Improvements + * should therefore concentrate on areas with this flag set. + * + * Cursor position and line length in zle when the line is + * metafied for access from the main shell. + */ + +/**/ +mod_export int zlemetacs, zlemetall; + +/* inwhat says what exactly we are in * + * (its value is one of the IN_* things). */ + +/**/ +mod_export int inwhat; + +/* 1 if x added to complete in a blank between words */ + +/**/ +mod_export int addedx; + +/* wb and we hold the beginning/end position of the word we are completing. */ + +/**/ +mod_export int wb, we; + +/**/ +mod_export int wordbeg; + +/**/ +mod_export int parbegin; + +/**/ +mod_export int parend; + + +/* 1 if aliases should not be expanded */ + +/**/ +mod_export int noaliases; + +/* + * If non-zero, we are parsing a line sent to use by the editor, or some + * other string that's not part of standard command input (e.g. eval is + * part of normal command input). + * + * Set of bits from LEXFLAGS_*. + * + * Note that although it is passed into the lexer as an input, the + * lexer can set it to zero after finding the word it's searching for. + * This only happens if the line being parsed actually does come from + * ZLE, and hence the bit LEXFLAGS_ZLE is set. + */ + +/**/ +mod_export int lexflags; + +/* don't recognize comments */ + +/**/ +mod_export int nocomments; + +/* add raw input characters while parsing command substitution */ + +/**/ +int lex_add_raw; + +/* variables associated with the above */ + +static char *tokstr_raw; +static struct lexbufstate lexbuf_raw; + +/* text of punctuation tokens */ + +/**/ +mod_export char *tokstrings[WHILE + 1] = { + NULL, /* NULLTOK 0 */ + ";", /* SEPER */ + "\\n", /* NEWLIN */ + ";", /* SEMI */ + ";;", /* DSEMI */ + "&", /* AMPER 5 */ + "(", /* INPAR */ + ")", /* OUTPAR */ + "||", /* DBAR */ + "&&", /* DAMPER */ + ">", /* OUTANG 10 */ + ">|", /* OUTANGBANG */ + ">>", /* DOUTANG */ + ">>|", /* DOUTANGBANG */ + "<", /* INANG */ + "<>", /* INOUTANG 15 */ + "<<", /* DINANG */ + "<<-", /* DINANGDASH */ + "<&", /* INANGAMP */ + ">&", /* OUTANGAMP */ + "&>", /* AMPOUTANG 20 */ + "&>|", /* OUTANGAMPBANG */ + ">>&", /* DOUTANGAMP */ + ">>&|", /* DOUTANGAMPBANG */ + "<<<", /* TRINANG */ + "|", /* BAR 25 */ + "|&", /* BARAMP */ + "()", /* INOUTPAR */ + "((", /* DINPAR */ + "))", /* DOUTPAR */ + "&|", /* AMPERBANG 30 */ + ";&", /* SEMIAMP */ + ";|", /* SEMIBAR */ +}; + +/* lexical state */ + +static int dbparens; +static struct lexbufstate lexbuf = { NULL, 256, 0 }; + +/* save lexical context */ + +/**/ +void +lex_context_save(struct lex_stack *ls, int toplevel) +{ + (void)toplevel; + + ls->dbparens = dbparens; + ls->isfirstln = isfirstln; + ls->isfirstch = isfirstch; + ls->lexflags = lexflags; + + ls->tok = tok; + ls->tokstr = tokstr; + ls->zshlextext = zshlextext; + ls->lexbuf = lexbuf; + ls->lex_add_raw = lex_add_raw; + ls->tokstr_raw = tokstr_raw; + ls->lexbuf_raw = lexbuf_raw; + ls->lexstop = lexstop; + ls->toklineno = toklineno; + + tokstr = zshlextext = lexbuf.ptr = NULL; + lexbuf.siz = 256; + tokstr_raw = lexbuf_raw.ptr = NULL; + lexbuf_raw.siz = lexbuf_raw.len = lex_add_raw = 0; +} + +/* restore lexical context */ + +/**/ +mod_export void +lex_context_restore(const struct lex_stack *ls, int toplevel) +{ + (void)toplevel; + + dbparens = ls->dbparens; + isfirstln = ls->isfirstln; + isfirstch = ls->isfirstch; + lexflags = ls->lexflags; + tok = ls->tok; + tokstr = ls->tokstr; + zshlextext = ls->zshlextext; + lexbuf = ls->lexbuf; + lex_add_raw = ls->lex_add_raw; + tokstr_raw = ls->tokstr_raw; + lexbuf_raw = ls->lexbuf_raw; + lexstop = ls->lexstop; + toklineno = ls->toklineno; +} + +/**/ +void +zshlex(void) +{ + if (tok == LEXERR) + return; + do { + if (inrepeat_) + ++inrepeat_; + if (inrepeat_ == 3 && isset(SHORTLOOPS)) + incmdpos = 1; + tok = gettok(); + } while (tok != ENDINPUT && exalias()); + nocorrect &= 1; + if (tok == NEWLIN || tok == ENDINPUT) { + while (hdocs) { + struct heredocs *next = hdocs->next; + char *doc, *munged_term; + + hwbegin(0); + cmdpush(hdocs->type == REDIR_HEREDOC ? CS_HEREDOC : CS_HEREDOCD); + munged_term = dupstring(hdocs->str); + STOPHIST + doc = gethere(&munged_term, hdocs->type); + ALLOWHIST + cmdpop(); + hwend(); + if (!doc) { + zerr("here document too large"); + while (hdocs) { + next = hdocs->next; + zfree(hdocs, sizeof(struct heredocs)); + hdocs = next; + } + tok = LEXERR; + break; + } + setheredoc(hdocs->pc, REDIR_HERESTR, doc, hdocs->str, + munged_term); + zfree(hdocs, sizeof(struct heredocs)); + hdocs = next; + } + } + if (tok != NEWLIN) + isnewlin = 0; + else + isnewlin = (inbufct) ? -1 : 1; + if (tok == SEMI || (tok == NEWLIN && !(lexflags & LEXFLAGS_NEWLINE))) + tok = SEPER; +} + +/**/ +mod_export void +ctxtlex(void) +{ + static int oldpos; + + zshlex(); + switch (tok) { + case SEPER: + case NEWLIN: + case SEMI: + case DSEMI: + case SEMIAMP: + case SEMIBAR: + case AMPER: + case AMPERBANG: + case INPAR: + case INBRACE: + case DBAR: + case DAMPER: + case BAR: + case BARAMP: + case INOUTPAR: + case DOLOOP: + case THEN: + case ELIF: + case ELSE: + case DOUTBRACK: + incmdpos = 1; + break; + case STRING: + case TYPESET: + /* case ENVSTRING: */ + case ENVARRAY: + case OUTPAR: + case CASE: + case DINBRACK: + incmdpos = 0; + break; + + default: + /* nothing to do, keep compiler happy */ + break; + } + if (tok != DINPAR) + infor = tok == FOR ? 2 : 0; + if (IS_REDIROP(tok) || tok == FOR || tok == FOREACH || tok == SELECT) { + inredir = 1; + oldpos = incmdpos; + incmdpos = 0; + } else if (inredir) { + incmdpos = oldpos; + inredir = 0; + } +} + +#define LX1_BKSLASH 0 +#define LX1_COMMENT 1 +#define LX1_NEWLIN 2 +#define LX1_SEMI 3 +#define LX1_AMPER 5 +#define LX1_BAR 6 +#define LX1_INPAR 7 +#define LX1_OUTPAR 8 +#define LX1_INANG 13 +#define LX1_OUTANG 14 +#define LX1_OTHER 15 + +#define LX2_BREAK 0 +#define LX2_OUTPAR 1 +#define LX2_BAR 2 +#define LX2_STRING 3 +#define LX2_INBRACK 4 +#define LX2_OUTBRACK 5 +#define LX2_TILDE 6 +#define LX2_INPAR 7 +#define LX2_INBRACE 8 +#define LX2_OUTBRACE 9 +#define LX2_OUTANG 10 +#define LX2_INANG 11 +#define LX2_EQUALS 12 +#define LX2_BKSLASH 13 +#define LX2_QUOTE 14 +#define LX2_DQUOTE 15 +#define LX2_BQUOTE 16 +#define LX2_COMMA 17 +#define LX2_DASH 18 +#define LX2_BANG 19 +#define LX2_OTHER 20 +#define LX2_META 21 + +static unsigned char lexact1[256], lexact2[256], lextok2[256]; + +/**/ +void +initlextabs(void) +{ + int t0; + static char *lx1 = "\\q\n;!&|(){}[]<>"; + static char *lx2 = ";)|$[]~({}><=\\\'\"`,-!"; + + for (t0 = 0; t0 != 256; t0++) { + lexact1[t0] = LX1_OTHER; + lexact2[t0] = LX2_OTHER; + lextok2[t0] = t0; + } + for (t0 = 0; lx1[t0]; t0++) + lexact1[(int)lx1[t0]] = t0; + for (t0 = 0; lx2[t0]; t0++) + lexact2[(int)lx2[t0]] = t0; + lexact2['&'] = LX2_BREAK; + lexact2[STOUC(Meta)] = LX2_META; + lextok2['*'] = Star; + lextok2['?'] = Quest; + lextok2['{'] = Inbrace; + lextok2['['] = Inbrack; + lextok2['$'] = String; + lextok2['~'] = Tilde; + lextok2['#'] = Pound; + lextok2['^'] = Hat; +} + +/* initialize lexical state */ + +/**/ +void +lexinit(void) +{ + nocorrect = dbparens = lexstop = 0; + tok = ENDINPUT; +} + +/* add a char to the string buffer */ + +/**/ +void +add(int c) +{ + *lexbuf.ptr++ = c; + if (lexbuf.siz == ++lexbuf.len) { + int newbsiz = lexbuf.siz * 2; + + if (newbsiz > inbufct && inbufct > lexbuf.siz) + newbsiz = inbufct; + + tokstr = (char *)hrealloc(tokstr, lexbuf.siz, newbsiz); + lexbuf.ptr = tokstr + lexbuf.len; + /* len == bsiz, so bptr is at the start of newly allocated memory */ + memset(lexbuf.ptr, 0, newbsiz - lexbuf.siz); + lexbuf.siz = newbsiz; + } +} + +#define SETPARBEGIN { \ + if ((lexflags & LEXFLAGS_ZLE) && !(inbufflags & INP_ALIAS) && \ + zlemetacs >= zlemetall+1-inbufct) \ + parbegin = inbufct; \ + } +#define SETPAREND { \ + if ((lexflags & LEXFLAGS_ZLE) && !(inbufflags & INP_ALIAS) && \ + parbegin != -1 && parend == -1) { \ + if (zlemetacs >= zlemetall + 1 - inbufct) \ + parbegin = -1; \ + else \ + parend = inbufct; \ + } \ + } + +enum { + CMD_OR_MATH_CMD, + CMD_OR_MATH_MATH, + CMD_OR_MATH_ERR +}; + +/* + * Return one of the above. If it couldn't be + * parsed as math, but there was no gross error, it's a command. + */ + +static int +cmd_or_math(int cs_type) +{ + int oldlen = lexbuf.len; + int c; + int oinflags = inbufflags; + + cmdpush(cs_type); + inbufflags |= INP_APPEND; + c = dquote_parse(')', 0); + if (!(oinflags & INP_APPEND)) + inbufflags &= ~INP_APPEND; + cmdpop(); + *lexbuf.ptr = '\0'; + if (!c) { + /* Successfully parsed, see if it was math */ + c = hgetc(); + if (c == ')') + return CMD_OR_MATH_MATH; /* yes */ + hungetc(c); + lexstop = 0; + c = ')'; + } else if (lexstop) { + /* we haven't got anything to unget */ + return CMD_OR_MATH_ERR; + } + /* else unsuccessful: unget the whole thing */ + hungetc(c); + lexstop = 0; + while (lexbuf.len > oldlen && !(errflag & ERRFLAG_ERROR)) { + lexbuf.len--; + hungetc(itok(*--lexbuf.ptr) ? + ztokens[*lexbuf.ptr - Pound] : *lexbuf.ptr); + } + if (errflag) + return CMD_OR_MATH_ERR; + hungetc('('); + return errflag ? CMD_OR_MATH_ERR : CMD_OR_MATH_CMD; +} + + +/* + * Parse either a $(( ... )) or a $(...) + * Return the same as cmd_or_math(). + */ +static int +cmd_or_math_sub(void) +{ + int c = hgetc(), ret; + + if (c == '(') { + int lexpos = (int)(lexbuf.ptr - tokstr); + add(Inpar); + add('('); + if ((ret = cmd_or_math(CS_MATHSUBST)) == CMD_OR_MATH_MATH) { + tokstr[lexpos] = Inparmath; + add(')'); + return CMD_OR_MATH_MATH; + } + if (ret == CMD_OR_MATH_ERR) + return CMD_OR_MATH_ERR; + lexbuf.ptr -= 2; + lexbuf.len -= 2; + } else { + hungetc(c); + lexstop = 0; + } + return skipcomm() ? CMD_OR_MATH_ERR : CMD_OR_MATH_CMD; +} + +/* Check whether we're looking at valid numeric globbing syntax * + * (/\<[0-9]*-[0-9]*\>/). Call pointing just after the opening "<". * + * Leaves the input in the same place, returning 0 or 1. */ + +/**/ +static int +isnumglob(void) +{ + int c, ec = '-', ret = 0; + int tbs = 256, n = 0; + char *tbuf = (char *)zalloc(tbs); + + while(1) { + c = hgetc(); + if(lexstop) { + lexstop = 0; + break; + } + tbuf[n++] = c; + if(!idigit(c)) { + if(c != ec) + break; + if(ec == '>') { + ret = 1; + break; + } + ec = '>'; + } + if(n == tbs) + tbuf = (char *)realloc(tbuf, tbs *= 2); + } + while(n--) + hungetc(tbuf[n]); + zfree(tbuf, tbs); + return ret; +} + +/**/ +static enum lextok +gettok(void) +{ + int c, d; + int peekfd = -1; + enum lextok peek; + + beginning: + tokstr = NULL; + while (iblank(c = hgetc()) && !lexstop); + toklineno = lineno; + if (lexstop) + return (errflag) ? LEXERR : ENDINPUT; + isfirstln = 0; + if ((lexflags & LEXFLAGS_ZLE) && !(inbufflags & INP_ALIAS)) + wordbeg = inbufct - (qbang && c == bangchar); + hwbegin(-1-(qbang && c == bangchar)); + /* word includes the last character read and possibly \ before ! */ + if (dbparens) { + lexbuf.len = 0; + lexbuf.ptr = tokstr = (char *) hcalloc(lexbuf.siz = LEX_HEAP_SIZE); + hungetc(c); + cmdpush(CS_MATH); + c = dquote_parse(infor ? ';' : ')', 0); + cmdpop(); + *lexbuf.ptr = '\0'; + if (!c && infor) { + infor--; + return DINPAR; + } + if (c || (c = hgetc()) != ')') { + hungetc(c); + return LEXERR; + } + dbparens = 0; + return DOUTPAR; + } else if (idigit(c)) { /* handle 1< foo */ + d = hgetc(); + if(d == '&') { + d = hgetc(); + if(d == '>') { + peekfd = c - '0'; + hungetc('>'); + c = '&'; + } else { + hungetc(d); + lexstop = 0; + hungetc('&'); + } + } else if (d == '>' || d == '<') { + peekfd = c - '0'; + c = d; + } else { + hungetc(d); + lexstop = 0; + } + } + + /* chars in initial position in word */ + + /* + * Handle comments. There are some special cases when this + * is not normal command input: lexflags implies we are examining + * a line lexically without it being used for normal command input. + */ + if (c == hashchar && !nocomments && + (isset(INTERACTIVECOMMENTS) || + ((!lexflags || (lexflags & LEXFLAGS_COMMENTS)) && !expanding && + (!interact || unset(SHINSTDIN) || strin)))) { + /* History is handled here to prevent extra * + * newlines being inserted into the history. */ + + if (lexflags & LEXFLAGS_COMMENTS_KEEP) { + lexbuf.len = 0; + lexbuf.ptr = tokstr = + (char *)hcalloc(lexbuf.siz = LEX_HEAP_SIZE); + add(c); + } + hwabort(); + while ((c = ingetc()) != '\n' && !lexstop) { + hwaddc(c); + addtoline(c); + if (lexflags & LEXFLAGS_COMMENTS_KEEP) + add(c); + } + + if (errflag) + peek = LEXERR; + else { + if (lexflags & LEXFLAGS_COMMENTS_KEEP) { + *lexbuf.ptr = '\0'; + if (!lexstop) + hungetc(c); + peek = STRING; + } else { + hwend(); + hwbegin(0); + hwaddc('\n'); + addtoline('\n'); + /* + * If splitting a line and removing comments, + * we don't want a newline token since it's + * treated specially. + */ + if ((lexflags & LEXFLAGS_COMMENTS_STRIP) && lexstop) + peek = ENDINPUT; + else + peek = NEWLIN; + } + } + return peek; + } + switch (lexact1[STOUC(c)]) { + case LX1_BKSLASH: + d = hgetc(); + if (d == '\n') + goto beginning; + hungetc(d); + lexstop = 0; + break; + case LX1_NEWLIN: + return NEWLIN; + case LX1_SEMI: + d = hgetc(); + if(d == ';') + return DSEMI; + else if(d == '&') + return SEMIAMP; + else if (d == '|') + return SEMIBAR; + hungetc(d); + lexstop = 0; + return SEMI; + case LX1_AMPER: + d = hgetc(); + if (d == '&') + return DAMPER; + else if (d == '!' || d == '|') + return AMPERBANG; + else if (d == '>') { + tokfd = peekfd; + d = hgetc(); + if (d == '!' || d == '|') + return OUTANGAMPBANG; + else if (d == '>') { + d = hgetc(); + if (d == '!' || d == '|') + return DOUTANGAMPBANG; + hungetc(d); + lexstop = 0; + return DOUTANGAMP; + } + hungetc(d); + lexstop = 0; + return AMPOUTANG; + } + hungetc(d); + lexstop = 0; + return AMPER; + case LX1_BAR: + d = hgetc(); + if (d == '|' && !incasepat) + return DBAR; + else if (d == '&') + return BARAMP; + hungetc(d); + lexstop = 0; + return BAR; + case LX1_INPAR: + d = hgetc(); + if (d == '(') { + if (infor) { + dbparens = 1; + return DINPAR; + } + if (incmdpos || (isset(SHGLOB) && !isset(KSHGLOB))) { + lexbuf.len = 0; + lexbuf.ptr = tokstr = (char *) + hcalloc(lexbuf.siz = LEX_HEAP_SIZE); + switch (cmd_or_math(CS_MATH)) { + case CMD_OR_MATH_MATH: + return DINPAR; + + case CMD_OR_MATH_CMD: + /* + * Not math, so we don't return the contents + * as a string in this case. + */ + tokstr = NULL; + return INPAR; + + case CMD_OR_MATH_ERR: + /* + * LEXFLAGS_ACTIVE means we came from bufferwords(), + * so we treat as an incomplete math expression + */ + if (lexflags & LEXFLAGS_ACTIVE) + tokstr = dyncat("((", tokstr ? tokstr : ""); + /* fall through */ + + default: + return LEXERR; + } + } + } else if (d == ')') + return INOUTPAR; + hungetc(d); + lexstop = 0; + if (!(isset(SHGLOB) || incond == 1 || incmdpos)) + break; + return INPAR; + case LX1_OUTPAR: + return OUTPAR; + case LX1_INANG: + d = hgetc(); + if (d == '(') { + hungetc(d); + lexstop = 0; + unpeekfd: + if(peekfd != -1) { + hungetc(c); + c = '0' + peekfd; + } + break; + } + if (d == '>') { + peek = INOUTANG; + } else if (d == '<') { + int e = hgetc(); + + if (e == '(') { + hungetc(e); + hungetc(d); + peek = INANG; + } else if (e == '<') + peek = TRINANG; + else if (e == '-') + peek = DINANGDASH; + else { + hungetc(e); + lexstop = 0; + peek = DINANG; + } + } else if (d == '&') { + peek = INANGAMP; + } else { + hungetc(d); + if(isnumglob()) + goto unpeekfd; + peek = INANG; + } + tokfd = peekfd; + return peek; + case LX1_OUTANG: + d = hgetc(); + if (d == '(') { + hungetc(d); + goto unpeekfd; + } else if (d == '&') { + d = hgetc(); + if (d == '!' || d == '|') + peek = OUTANGAMPBANG; + else { + hungetc(d); + lexstop = 0; + peek = OUTANGAMP; + } + } else if (d == '!' || d == '|') + peek = OUTANGBANG; + else if (d == '>') { + d = hgetc(); + if (d == '&') { + d = hgetc(); + if (d == '!' || d == '|') + peek = DOUTANGAMPBANG; + else { + hungetc(d); + lexstop = 0; + peek = DOUTANGAMP; + } + } else if (d == '!' || d == '|') + peek = DOUTANGBANG; + else if (d == '(') { + hungetc(d); + hungetc('>'); + peek = OUTANG; + } else { + hungetc(d); + lexstop = 0; + peek = DOUTANG; + if (isset(HISTALLOWCLOBBER)) + hwaddc('|'); + } + } else { + hungetc(d); + lexstop = 0; + peek = OUTANG; + if (!incond && isset(HISTALLOWCLOBBER)) + hwaddc('|'); + } + tokfd = peekfd; + return peek; + } + + /* we've started a string, now get the * + * rest of it, performing tokenization */ + return gettokstr(c, 0); +} + +/* + * Get the remains of a token string. This has two uses. + * When called from gettok(), with sub = 0, we have already identified + * any interesting initial character and want to get the rest of + * what we now know is a string. However, the string may still include + * metacharacters and potentially substitutions. + * + * When called from parse_subst_string() with sub = 1, we are not + * fully parsing a command line, merely tokenizing a string. + * In this case we always add characters to the parsed string + * unless there is a parse error. + */ + +/**/ +static enum lextok +gettokstr(int c, int sub) +{ + int bct = 0, pct = 0, brct = 0, seen_brct = 0, fdpar = 0; + int intpos = 1, in_brace_param = 0; + int inquote, unmatched = 0; + enum lextok peek; +#ifdef DEBUG + int ocmdsp = cmdsp; +#endif + + peek = STRING; + if (!sub) { + lexbuf.len = 0; + lexbuf.ptr = tokstr = (char *) hcalloc(lexbuf.siz = LEX_HEAP_SIZE); + } + for (;;) { + int act; + int e; + int inbl = inblank(c); + + if (fdpar && !inbl && c != ')') + fdpar = 0; + + if (inbl && !in_brace_param && !pct) + act = LX2_BREAK; + else { + act = lexact2[STOUC(c)]; + c = lextok2[STOUC(c)]; + } + switch (act) { + case LX2_BREAK: + if (!in_brace_param && !sub) + goto brk; + break; + case LX2_META: + c = hgetc(); +#ifdef DEBUG + if (lexstop) { + fputs("BUG: input terminated by Meta\n", stderr); + fflush(stderr); + goto brk; + } +#endif + add(Meta); + break; + case LX2_OUTPAR: + if (fdpar) { + /* this is a single word `( )', treat as INOUTPAR */ + add(c); + *lexbuf.ptr = '\0'; + return INOUTPAR; + } + if ((sub || in_brace_param) && isset(SHGLOB)) + break; + if (!in_brace_param && !pct--) { + if (sub) { + pct = 0; + break; + } else + goto brk; + } + c = Outpar; + break; + case LX2_BAR: + if (!pct && !in_brace_param) { + if (sub) + break; + else + goto brk; + } + if (unset(SHGLOB) || (!sub && !in_brace_param)) + c = Bar; + break; + case LX2_STRING: + e = hgetc(); + if (e == '[') { + cmdpush(CS_MATHSUBST); + add(String); + add(Inbrack); + c = dquote_parse(']', sub); + cmdpop(); + if (c) { + peek = LEXERR; + goto brk; + } + c = Outbrack; + } else if (e == '(') { + add(String); + switch (cmd_or_math_sub()) { + case CMD_OR_MATH_CMD: + c = Outpar; + break; + + case CMD_OR_MATH_MATH: + c = Outparmath; + break; + + default: + peek = LEXERR; + goto brk; + } + } else { + if (e == '{') { + add(c); + c = Inbrace; + ++bct; + cmdpush(CS_BRACEPAR); + if (!in_brace_param) { + if ((in_brace_param = bct)) + seen_brct = 0; + } + } else { + hungetc(e); + lexstop = 0; + } + } + break; + case LX2_INBRACK: + if (!in_brace_param) { + brct++; + seen_brct = 1; + } + c = Inbrack; + break; + case LX2_OUTBRACK: + if (!in_brace_param) + brct--; + if (brct < 0) + brct = 0; + c = Outbrack; + break; + case LX2_INPAR: + if (isset(SHGLOB)) { + if (sub || in_brace_param) + break; + if (incasepat > 0 && !lexbuf.len) + return INPAR; + if (!isset(KSHGLOB) && lexbuf.len) + goto brk; + } + if (!in_brace_param) { + if (!sub) { + e = hgetc(); + hungetc(e); + lexstop = 0; + /* For command words, parentheses are only + * special at the start. But now we're tokenising + * the remaining string. So I don't see what + * the old incmdpos test here is for. + * pws 1999/6/8 + * + * Oh, no. + * func1( ) + * is a valid function definition in [k]sh. The best + * thing we can do, without really nasty lookahead tricks, + * is break if we find a blank after a parenthesis. At + * least this can't happen inside braces or brackets. We + * only allow this with SHGLOB (set for both sh and ksh). + * + * Things like `print @( |foo)' should still + * work, because [k]sh don't allow multiple words + * in a function definition, so we only do this + * in command position. + * pws 1999/6/14 + */ + if (e == ')' || (isset(SHGLOB) && inblank(e) && !bct && + !brct && !intpos && incmdpos)) { + /* + * Either a () token, or a command word with + * something suspiciously like a ksh function + * definition. + * The current word isn't spellcheckable. + */ + nocorrect |= 2; + goto brk; + } + } + /* + * This also handles the [k]sh `foo( )' function definition. + * Maintain a variable fdpar, set as long as a single set of + * parentheses contains only space. Then if we get to the + * closing parenthesis and it is still set, we can assume we + * have a function definition. Only do this at the start of + * the word, since the (...) must be a separate token. + */ + if (!pct++ && isset(SHGLOB) && intpos && !bct && !brct) + fdpar = 1; + } + c = Inpar; + break; + case LX2_INBRACE: + if (isset(IGNOREBRACES) || sub) + c = '{'; + else { + if (!lexbuf.len && incmdpos) { + add('{'); + *lexbuf.ptr = '\0'; + return STRING; + } + if (in_brace_param) { + cmdpush(CS_BRACE); + } + bct++; + } + break; + case LX2_OUTBRACE: + if ((isset(IGNOREBRACES) || sub) && !in_brace_param) + break; + if (!bct) + break; + if (in_brace_param) { + cmdpop(); + } + if (bct-- == in_brace_param) + in_brace_param = 0; + c = Outbrace; + break; + case LX2_COMMA: + if (unset(IGNOREBRACES) && !sub && bct > in_brace_param) + c = Comma; + break; + case LX2_OUTANG: + if (in_brace_param || sub) + break; + e = hgetc(); + if (e != '(') { + hungetc(e); + lexstop = 0; + goto brk; + } + add(OutangProc); + if (skipcomm()) { + peek = LEXERR; + goto brk; + } + c = Outpar; + break; + case LX2_INANG: + if (isset(SHGLOB) && sub) + break; + e = hgetc(); + if (!(in_brace_param || sub) && e == '(') { + add(Inang); + if (skipcomm()) { + peek = LEXERR; + goto brk; + } + c = Outpar; + break; + } + hungetc(e); + if(isnumglob()) { + add(Inang); + while ((c = hgetc()) != '>') + add(c); + c = Outang; + break; + } + lexstop = 0; + if (in_brace_param || sub) + break; + goto brk; + case LX2_EQUALS: + if (!sub) { + if (intpos) { + e = hgetc(); + if (e != '(') { + hungetc(e); + lexstop = 0; + c = Equals; + } else { + add(Equals); + if (skipcomm()) { + peek = LEXERR; + goto brk; + } + c = Outpar; + } + } else if (peek != ENVSTRING && + (incmdpos || intypeset) && !bct && !brct) { + char *t = tokstr; + if (idigit(*t)) + while (++t < lexbuf.ptr && idigit(*t)); + else { + int sav = *lexbuf.ptr; + *lexbuf.ptr = '\0'; + t = itype_end(t, IIDENT, 0); + if (t < lexbuf.ptr) { + skipparens(Inbrack, Outbrack, &t); + } else { + *lexbuf.ptr = sav; + } + } + if (*t == '+') + t++; + if (t == lexbuf.ptr) { + e = hgetc(); + if (e == '(') { + *lexbuf.ptr = '\0'; + return ENVARRAY; + } + hungetc(e); + lexstop = 0; + peek = ENVSTRING; + intpos = 2; + } else + c = Equals; + } else + c = Equals; + } + break; + case LX2_BKSLASH: + c = hgetc(); + if (c == '\n') { + c = hgetc(); + if (!lexstop) + continue; + } else { + add(Bnull); + if (c == STOUC(Meta)) { + c = hgetc(); +#ifdef DEBUG + if (lexstop) { + fputs("BUG: input terminated by Meta\n", stderr); + fflush(stderr); + goto brk; + } +#endif + add(Meta); + } + } + if (lexstop) + goto brk; + break; + case LX2_QUOTE: { + int strquote = (lexbuf.len && lexbuf.ptr[-1] == String); + + add(Snull); + cmdpush(CS_QUOTE); + for (;;) { + STOPHIST + while ((c = hgetc()) != '\'' && !lexstop) { + if (strquote && c == '\\') { + c = hgetc(); + if (lexstop) + break; + /* + * Mostly we don't need to do anything special + * with escape backslashes or closing quotes + * inside $'...'; however in completion we + * need to be able to strip multiple backslashes + * neatly. + */ + if (c == '\\' || c == '\'') + add(Bnull); + else + add('\\'); + } else if (!sub && isset(CSHJUNKIEQUOTES) && c == '\n') { + if (lexbuf.ptr[-1] == '\\') + lexbuf.ptr--, lexbuf.len--; + else + break; + } + add(c); + } + ALLOWHIST + if (c != '\'') { + unmatched = '\''; + /* Not an error when called from bufferwords() */ + if (!(lexflags & LEXFLAGS_ACTIVE)) + peek = LEXERR; + cmdpop(); + goto brk; + } + e = hgetc(); + if (e != '\'' || unset(RCQUOTES) || strquote) + break; + add(c); + } + cmdpop(); + hungetc(e); + lexstop = 0; + c = Snull; + break; + } + case LX2_DQUOTE: + add(Dnull); + cmdpush(CS_DQUOTE); + c = dquote_parse('"', sub); + cmdpop(); + if (c) { + unmatched = '"'; + /* Not an error when called from bufferwords() */ + if (!(lexflags & LEXFLAGS_ACTIVE)) + peek = LEXERR; + goto brk; + } + c = Dnull; + break; + case LX2_BQUOTE: + add(Tick); + cmdpush(CS_BQUOTE); + SETPARBEGIN + inquote = 0; + while ((c = hgetc()) != '`' && !lexstop) { + if (c == '\\') { + c = hgetc(); + if (c != '\n') { + add(c == '`' || c == '\\' || c == '$' ? Bnull : '\\'); + add(c); + } + else if (!sub && isset(CSHJUNKIEQUOTES)) + add(c); + } else { + if (!sub && isset(CSHJUNKIEQUOTES) && c == '\n') { + break; + } + add(c); + if (c == '\'') { + if ((inquote = !inquote)) + STOPHIST + else + ALLOWHIST + } + } + } + if (inquote) + ALLOWHIST + cmdpop(); + if (c != '`') { + unmatched = '`'; + /* Not an error when called from bufferwords() */ + if (!(lexflags & LEXFLAGS_ACTIVE)) + peek = LEXERR; + goto brk; + } + c = Tick; + SETPAREND + break; + case LX2_DASH: + /* + * - shouldn't be treated as a special character unless + * we're in a pattern. Unfortunately, working out for + * sure in complicated expressions whether we're in a + * pattern is tricky. So we'll make it special and + * turn it back any time we don't need it special. + * This is not ideal as it's a lot of work. + */ + c = Dash; + break; + case LX2_BANG: + /* + * Same logic as Dash, for ! to perform negation in range. + */ + if (seen_brct) + c = Bang; + else + c = '!'; + } + add(c); + c = hgetc(); + if (intpos) + intpos--; + if (lexstop) + break; + } + brk: + if (errflag) { + if (in_brace_param) { + while(bct-- >= in_brace_param) + cmdpop(); + } + return LEXERR; + } + hungetc(c); + if (unmatched && !(lexflags & LEXFLAGS_ACTIVE)) + zerr("unmatched %c", unmatched); + if (in_brace_param) { + while(bct-- >= in_brace_param) + cmdpop(); + zerr("closing brace expected"); + } else if (unset(IGNOREBRACES) && !sub && lexbuf.len > 1 && + peek == STRING && lexbuf.ptr[-1] == '}' && + lexbuf.ptr[-2] != Bnull) { + /* hack to get {foo} command syntax work */ + lexbuf.ptr--; + lexbuf.len--; + lexstop = 0; + hungetc('}'); + } + *lexbuf.ptr = '\0'; + DPUTS(cmdsp != ocmdsp, "BUG: gettok: cmdstack changed."); + return peek; +} + + +/* + * Parse input as if in double quotes. + * endchar is the end character to expect. + * sub has got something to do with whether we are doing quoted substitution. + * Return non-zero for error (character to unget), else zero + */ + +/**/ +static int +dquote_parse(char endchar, int sub) +{ + int pct = 0, brct = 0, bct = 0, intick = 0, err = 0; + int c; + int math = endchar == ')' || endchar == ']' || infor; + int zlemath = math && zlemetacs > zlemetall + addedx - inbufct; + + while (((c = hgetc()) != endchar || bct || + (math && ((pct > 0) || (brct > 0))) || + intick) && !lexstop) { + cont: + switch (c) { + case '\\': + c = hgetc(); + if (c != '\n') { + if (c == '$' || c == '\\' || (c == '}' && !intick && bct) || + c == endchar || c == '`' || + (endchar == ']' && (c == '[' || c == ']' || + c == '(' || c == ')' || + c == '{' || c == '}' || + (c == '"' && sub)))) + add(Bnull); + else { + /* lexstop is implicitly handled here */ + add('\\'); + goto cont; + } + } else if (sub || unset(CSHJUNKIEQUOTES) || endchar != '"') + continue; + break; + case '\n': + err = !sub && isset(CSHJUNKIEQUOTES) && endchar == '"'; + break; + case '$': + if (intick) + break; + c = hgetc(); + if (c == '(') { + add(Qstring); + switch (cmd_or_math_sub()) { + case CMD_OR_MATH_CMD: + c = Outpar; + break; + + case CMD_OR_MATH_MATH: + c = Outparmath; + break; + + default: + err = 1; + break; + } + } else if (c == '[') { + add(String); + add(Inbrack); + cmdpush(CS_MATHSUBST); + err = dquote_parse(']', sub); + cmdpop(); + c = Outbrack; + } else if (c == '{') { + add(Qstring); + c = Inbrace; + cmdpush(CS_BRACEPAR); + bct++; + } else if (c == '$') + add(Qstring); + else { + hungetc(c); + lexstop = 0; + c = Qstring; + } + break; + case '}': + if (intick || !bct) + break; + c = Outbrace; + bct--; + cmdpop(); + break; + case '`': + c = Qtick; + if (intick == 2) + ALLOWHIST + if ((intick = !intick)) { + SETPARBEGIN + cmdpush(CS_BQUOTE); + } else { + SETPAREND + cmdpop(); + } + break; + case '\'': + if (!intick) + break; + if (intick == 1) + intick = 2, STOPHIST + else + intick = 1, ALLOWHIST + break; + case '(': + if (!math || !bct) + pct++; + break; + case ')': + if (!math || !bct) + err = (!pct-- && math); + break; + case '[': + if (!math || !bct) + brct++; + break; + case ']': + if (!math || !bct) + err = (!brct-- && math); + break; + case '"': + if (intick || (endchar != '"' && !bct)) + break; + if (bct) { + add(Dnull); + cmdpush(CS_DQUOTE); + err = dquote_parse('"', sub); + cmdpop(); + c = Dnull; + } else + err = 1; + break; + } + if (err || lexstop) + break; + add(c); + } + if (intick == 2) + ALLOWHIST + if (intick) { + cmdpop(); + } + while (bct--) + cmdpop(); + if (lexstop) + err = intick || endchar || err; + else if (err == 1) { + /* + * TODO: as far as I can see, this hack is used in gettokstr() + * to hungetc() a character on an error. However, I don't + * understand what that actually gets us, and we can't guarantee + * it's a character anyway, because of the previous test. + * + * We use the same feature in cmd_or_math where we actually do + * need to unget if we decide it's really a command substitution. + * We try to handle the other case by testing for lexstop. + */ + err = c; + } + if (zlemath && zlemetacs <= zlemetall + 1 - inbufct) + inwhat = IN_MATH; + return err; +} + +/* + * Tokenize a string given in s. Parsing is done as in double + * quotes. This is usually called before singsub(). + * + * parsestr() is noisier, reporting an error if the parse failed. + * + * On entry, *s must point to a string allocated from the stack of + * exactly the right length, i.e. strlen(*s) + 1, as the string + * is used as the lexical token string whose memory management + * demands this. Usually the input string will therefore be + * the result of an immediately preceding dupstring(). + */ + +/**/ +mod_export int +parsestr(char **s) +{ + int err; + + if ((err = parsestrnoerr(s))) { + untokenize(*s); + if (!(errflag & ERRFLAG_INT)) { + if (err > 32 && err < 127) + zerr("parse error near `%c'", err); + else + zerr("parse error"); + } + } + return err; +} + +/**/ +mod_export int +parsestrnoerr(char **s) +{ + int l = strlen(*s), err; + + zcontext_save(); + untokenize(*s); + inpush(dupstring(*s), 0, NULL); + strinbeg(0); + lexbuf.len = 0; + lexbuf.ptr = tokstr = *s; + lexbuf.siz = l + 1; + err = dquote_parse('\0', 1); + if (tokstr) + *s = tokstr; + *lexbuf.ptr = '\0'; + strinend(); + inpop(); + DPUTS(cmdsp, "BUG: parsestr: cmdstack not empty."); + zcontext_restore(); + return err; +} + +/* + * Parse a subscript in string s. + * sub is passed down to dquote_parse(). + * endchar is the final character. + * Return the next character, or NULL. + */ +/**/ +mod_export char * +parse_subscript(char *s, int sub, int endchar) +{ + int l = strlen(s), err, toklen; + char *t; + + if (!*s || *s == endchar) + return 0; + zcontext_save(); + untokenize(t = dupstring(s)); + inpush(t, 0, NULL); + strinbeg(0); + /* + * Warning to Future Generations: + * + * This way of passing the subscript through the lexer is brittle. + * Code above this for several layers assumes that when we tokenise + * the input it goes into the same place as the original string. + * However, the lexer may overwrite later bits of the string or + * reallocate it, in particular when expanding aliaes. To get + * around this, we copy the string and then copy it back. This is a + * bit more robust but still relies on the underlying assumption of + * length preservation. + */ + lexbuf.len = 0; + lexbuf.ptr = tokstr = dupstring(s); + lexbuf.siz = l + 1; + err = dquote_parse(endchar, sub); + toklen = (int)(lexbuf.ptr - tokstr); + DPUTS(toklen > l, "Bad length for parsed subscript"); + memcpy(s, tokstr, toklen); + if (err) { + char *strend = s + toklen; + err = *strend; + *strend = '\0'; + untokenize(s); + *strend = err; + s = NULL; + } else { + s += toklen; + } + strinend(); + inpop(); + DPUTS(cmdsp, "BUG: parse_subscript: cmdstack not empty."); + zcontext_restore(); + return s; +} + +/* Tokenize a string given in s. Parsing is done as if s were a normal * + * command-line argument but it may contain separators. This is used * + * to parse the right-hand side of ${...%...} substitutions. */ + +/**/ +mod_export int +parse_subst_string(char *s) +{ + int c, l = strlen(s), err; + char *ptr; + enum lextok ctok; + + if (!*s || !strcmp(s, nulstring)) + return 0; + zcontext_save(); + untokenize(s); + inpush(dupstring(s), 0, NULL); + strinbeg(0); + lexbuf.len = 0; + lexbuf.ptr = tokstr = s; + lexbuf.siz = l + 1; + c = hgetc(); + ctok = gettokstr(c, 1); + err = errflag; + strinend(); + inpop(); + DPUTS(cmdsp, "BUG: parse_subst_string: cmdstack not empty."); + zcontext_restore(); + /* Keep any interrupt error status */ + errflag = err | (errflag & ERRFLAG_INT); + if (ctok == LEXERR) { + untokenize(s); + return 1; + } +#ifdef DEBUG + /* + * Historical note: we used to check here for olen (the value of lexbuf.len + * before zcontext_restore()) == l, but that's not necessarily the case if + * we stripped an RCQUOTE. + */ + if (ctok != STRING || (errflag && !noerrs)) { + fprintf(stderr, "Oops. Bug in parse_subst_string: %s\n", + errflag ? "errflag" : "ctok != STRING"); + fflush(stderr); + untokenize(s); + return 1; + } +#endif + /* Check for $'...' quoting. This needs special handling. */ + for (ptr = s; *ptr; ) + { + if (*ptr == String && ptr[1] == Snull) + { + char *t; + int len, tlen, diff; + t = getkeystring(ptr + 2, &len, GETKEYS_DOLLARS_QUOTE, NULL); + len += 2; + tlen = strlen(t); + diff = len - tlen; + /* + * Yuk. + * parse_subst_string() currently handles strings in-place. + * That's not so easy to fix without knowing whether + * additional memory should come off the heap or + * otherwise. So we cheat by copying the unquoted string + * into place, unless it's too long. That's not the + * normal case, but I'm worried there are pathological + * cases with converting metafied multibyte strings. + * If someone can prove there aren't I will be very happy. + */ + if (diff < 0) { + DPUTS(1, "$'...' subst too long: fix get_parse_string()"); + return 1; + } + memcpy(ptr, t, tlen); + ptr += tlen; + if (diff > 0) { + char *dptr = ptr; + char *sptr = ptr + diff; + while ((*dptr++ = *sptr++)) + ; + } + } else + ptr++; + } + return 0; +} + +/* Called below to report word positions. */ + +/**/ +static void +gotword(void) +{ + int nwe = zlemetall + 1 - inbufct + (addedx == 2 ? 1 : 0); + if (zlemetacs <= nwe) { + int nwb = zlemetall - wordbeg + addedx; + if (zlemetacs >= nwb) { + wb = nwb; + we = nwe; + } else { + wb = zlemetacs + addedx; + if (we < wb) + we = wb; + } + lexflags = 0; + } +} + +/* Check if current lex text matches an alias: 1 if so, else 0 */ + +static int +checkalias(void) +{ + Alias an; + + if (!zshlextext) + return 0; + + if (!noaliases && isset(ALIASESOPT) && + (!isset(POSIXALIASES) || + (tok == STRING && !reswdtab->getnode(reswdtab, zshlextext)))) { + char *suf; + + an = (Alias) aliastab->getnode(aliastab, zshlextext); + if (an && !an->inuse && + ((an->node.flags & ALIAS_GLOBAL) || + (incmdpos && tok == STRING) || inalmore)) { + if (!lexstop) { + /* + * Tokens that don't require a space after, get one, + * because they are treated as if preceded by one. + */ + int c = hgetc(); + hungetc(c); + if (!iblank(c)) + inpush(" ", INP_ALIAS, 0); + } + inpush(an->text, INP_ALIAS, an); + if (an->text[0] == ' ' && !(an->node.flags & ALIAS_GLOBAL)) + aliasspaceflag = 1; + lexstop = 0; + return 1; + } + if ((suf = strrchr(zshlextext, '.')) && suf[1] && + suf > zshlextext && suf[-1] != Meta && + (an = (Alias)sufaliastab->getnode(sufaliastab, suf+1)) && + !an->inuse && incmdpos) { + inpush(dupstring(zshlextext), INP_ALIAS, an); + inpush(" ", INP_ALIAS, NULL); + inpush(an->text, INP_ALIAS, NULL); + lexstop = 0; + return 1; + } + } + + return 0; +} + +/* expand aliases and reserved words */ + +/**/ +int +exalias(void) +{ + Reswd rw; + + hwend(); + if (interact && isset(SHINSTDIN) && !strin && incasepat <= 0 && + tok == STRING && !nocorrect && !(inbufflags & INP_ALIAS) && + (isset(CORRECTALL) || (isset(CORRECT) && incmdpos))) + spckword(&tokstr, 1, incmdpos, 1); + + if (!tokstr) { + zshlextext = tokstrings[tok]; + + if (tok == NEWLIN) + return 0; + return checkalias(); + } else { + VARARR(char, copy, (strlen(tokstr) + 1)); + + if (has_token(tokstr)) { + char *p, *t; + + zshlextext = p = copy; + for (t = tokstr; + (*p++ = itok(*t) ? ztokens[*t++ - Pound] : *t++);); + } else + zshlextext = tokstr; + + if ((lexflags & LEXFLAGS_ZLE) && !(inbufflags & INP_ALIAS)) { + int zp = lexflags; + + gotword(); + if ((zp & LEXFLAGS_ZLE) && !lexflags) { + if (zshlextext == copy) + zshlextext = tokstr; + return 0; + } + } + + if (tok == STRING) { + /* Check for an alias */ + if ((zshlextext != copy || !isset(POSIXALIASES)) && checkalias()) { + if (zshlextext == copy) + zshlextext = tokstr; + return 1; + } + + /* Then check for a reserved word */ + if ((incmdpos || + (unset(IGNOREBRACES) && unset(IGNORECLOSEBRACES) && + zshlextext[0] == '}' && !zshlextext[1])) && + (rw = (Reswd) reswdtab->getnode(reswdtab, zshlextext))) { + tok = rw->token; + inrepeat_ = (tok == REPEAT); + if (tok == DINBRACK) + incond = 1; + } else if (incond && !strcmp(zshlextext, "]]")) { + tok = DOUTBRACK; + incond = 0; + } else if (incond == 1 && zshlextext[0] == '!' && !zshlextext[1]) + tok = BANG; + } + inalmore = 0; + if (zshlextext == copy) + zshlextext = tokstr; + } + return 0; +} + +/**/ +void +zshlex_raw_add(int c) +{ + if (!lex_add_raw) + return; + + *lexbuf_raw.ptr++ = c; + if (lexbuf_raw.siz == ++lexbuf_raw.len) { + int newbsiz = lexbuf_raw.siz * 2; + + tokstr_raw = (char *)hrealloc(tokstr_raw, lexbuf_raw.siz, newbsiz); + lexbuf_raw.ptr = tokstr_raw + lexbuf_raw.len; + memset(lexbuf_raw.ptr, 0, newbsiz - lexbuf_raw.siz); + lexbuf_raw.siz = newbsiz; + } +} + +/**/ +void +zshlex_raw_back(void) +{ + if (!lex_add_raw) + return; + lexbuf_raw.ptr--; + lexbuf_raw.len--; +} + +/**/ +int +zshlex_raw_mark(int offset) +{ + if (!lex_add_raw) + return 0; + return lexbuf_raw.len + offset; +} + +/**/ +void +zshlex_raw_back_to_mark(int mark) +{ + if (!lex_add_raw) + return; + lexbuf_raw.ptr = tokstr_raw + mark; + lexbuf_raw.len = mark; +} + +/* + * Skip (...) for command-style substitutions: $(...), <(...), >(...) + * + * In order to ensure we don't stop at closing parentheses with + * some other syntactic significance, we'll parse the input until + * we find an unmatched closing parenthesis. However, we'll throw + * away the result of the parsing and just keep the string we've built + * up on the way. + */ + +/**/ +static int +skipcomm(void) +{ +#ifdef ZSH_OLD_SKIPCOMM + int pct = 1, c, start = 1; + + cmdpush(CS_CMDSUBST); + SETPARBEGIN + c = Inpar; + do { + int iswhite; + add(c); + c = hgetc(); + if (itok(c) || lexstop) + break; + iswhite = inblank(c); + switch (c) { + case '(': + pct++; + break; + case ')': + pct--; + break; + case '\\': + add(c); + c = hgetc(); + break; + case '\'': { + int strquote = lexbuf.ptr[-1] == '$'; + add(c); + STOPHIST + while ((c = hgetc()) != '\'' && !lexstop) { + if (c == '\\' && strquote) { + add(c); + c = hgetc(); + } + add(c); + } + ALLOWHIST + break; + } + case '\"': + add(c); + while ((c = hgetc()) != '\"' && !lexstop) + if (c == '\\') { + add(c); + add(hgetc()); + } else + add(c); + break; + case '`': + add(c); + while ((c = hgetc()) != '`' && !lexstop) + if (c == '\\') + add(c), add(hgetc()); + else + add(c); + break; + case '#': + if (start) { + add(c); + while ((c = hgetc()) != '\n' && !lexstop) + add(c); + iswhite = 1; + } + break; + } + start = iswhite; + } + while (pct); + if (!lexstop) + SETPAREND + cmdpop(); + return lexstop; +#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); + + new_lex_add_raw = lex_add_raw + 1; + if (!lex_add_raw) { + /* + * We'll combine the string so far with the input + * read in for the command substitution. To do this + * we'll just propagate the current tokstr etc. as the + * variables used for adding raw input, and + * ensure we swap those for the real tokstr etc. at the end. + * + * However, we need to save and restore the rest of the + * lexical and parse state as we're effectively parsing + * an internal string. Because we're still parsing it from + * the original input source (we have to --- we don't know + * when to stop inputting it otherwise and can't rely on + * the input being recoverable until we've read it) we need + * to keep the same history context. + */ + 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 { + /* + * Set up for nested command subsitution, however + * we don't actually need the string until we get + * back to the top level and recover the lot. + * The $() body just appears empty. + * + * We do need to propagate the raw variables which would + * otherwise by cleared, though. + */ + new_tokstr = tokstr_raw; + new_lexbuf = lexbuf_raw; + + zcontext_save_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE); + } + tokstr_raw = new_tokstr; + lexbuf_raw = new_lexbuf; + lex_add_raw = new_lex_add_raw; + /* + * Don't do any ZLE specials down here: they're only needed + * when we return the string from the recursive parse. + * (TBD: this probably means we should be initialising lexflags + * more consistently.) + * + * Note that in that case we're still using the ZLE line reading + * function at the history layer --- this is consistent with the + * intention of maintaining the history and input layers across + * the recursive parsing. + * + * Also turn off LEXFLAGS_NEWLINE because this is already skipping + * across the entire construct, and parse_event() needs embedded + * newlines to be "real" when looking for the OUTPAR token. + */ + lexflags &= ~(LEXFLAGS_ZLE|LEXFLAGS_NEWLINE); + dbparens = 0; /* restored by zcontext_restore_partial() */ + + if (!parse_event(OUTPAR) || tok != OUTPAR) { + if (strin) { + /* + * Get the rest of the string raw since we don't + * know where this token ends. + */ + while (!lexstop) + (void)ingetc(); + } else + lexstop = 1; + } + /* Outpar lexical token gets added in caller if present */ + + /* + * We're going to keep the full raw input string + * as the current token string after popping the stack. + */ + new_tokstr = tokstr_raw; + new_lexbuf = lexbuf_raw; + /* + * We're also going to propagate the lexical state: + * if we couldn't parse the command substitution we + * can't continue. + */ + new_lexstop = lexstop; + + zcontext_restore_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE); + + if (lex_add_raw) { + /* + * Keep going, so retain the raw variables. + */ + tokstr_raw = new_tokstr; + lexbuf_raw = new_lexbuf; + } else { + if (!new_lexstop) { + /* Ignore the ')' added on input */ + new_lexbuf.len--; + *--new_lexbuf.ptr = '\0'; + } + + /* + * Convince the rest of lex.c we were examining a string + * all along. + */ + tokstr = new_tokstr; + lexbuf = new_lexbuf; + lexstop = new_lexstop; + hist_in_word(0); + } + + if (!lexstop) + SETPAREND + cmdpop(); + infor = save_infor; + + return lexstop; +#endif +} diff --git a/dotfiles/system/.zsh/modules/Src/loop.c b/dotfiles/system/.zsh/modules/Src/loop.c new file mode 100644 index 0000000..1013aeb --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/loop.c @@ -0,0 +1,795 @@ +/* + * loop.c - loop execution + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "loop.pro" + +/* # of nested loops we are in */ + +/**/ +int loops; + +/* # of continue levels */ + +/**/ +mod_export int contflag; + +/* # of break levels */ + +/**/ +mod_export int breaks; + +/**/ +int +execfor(Estate state, int do_exec) +{ + Wordcode end, loop; + wordcode code = state->pc[-1]; + int iscond = (WC_FOR_TYPE(code) == WC_FOR_COND), ctok = 0, atok = 0; + int last = 0; + 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); + + if (iscond) { + str = dupstring(ecgetstr(state, EC_NODUP, NULL)); + singsub(&str); + if (isset(XTRACE)) { + char *str2 = dupstring(str); + untokenize(str2); + printprompt4(); + fprintf(xtrerr, "%s\n", str2); + fflush(xtrerr); + } + if (!errflag) { + matheval(str); + } + if (errflag) { + state->pc = end; + simple_pline = old_simple_pline; + return 1; + } + cond = ecgetstr(state, EC_NODUP, &ctok); + advance = ecgetstr(state, EC_NODUP, &atok); + } else { + vars = ecgetlist(state, *state->pc++, EC_NODUP, NULL); + + if (WC_FOR_TYPE(code) == WC_FOR_LIST) { + int htok = 0; + + 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; + } + } + } else { + char **x; + + args = newlinklist(); + for (x = pparams; *x; x++) + addlinknode(args, dupstring(*x)); + } + } + + if (!args || empty(args)) + lastval = 0; + + loops++; + pushheap(); + cmdpush(CS_FOR); + loop = state->pc; + while (!last) { + if (iscond) { + if (ctok) { + str = dupstring(cond); + singsub(&str); + } else + str = cond; + if (!errflag) { + while (iblank(*str)) + str++; + if (*str) { + if (isset(XTRACE)) { + printprompt4(); + fprintf(xtrerr, "%s\n", str); + fflush(xtrerr); + } + val = mathevali(str); + } else + val = 1; + } + if (errflag) { + if (breaks) + breaks--; + lastval = 1; + break; + } + if (!val) + break; + } else { + LinkNode node; + int count = 0; + for (node = firstnode(vars); node; incnode(node)) + { + name = (char *)getdata(node); + if (!args || !(str = (char *) ugetnode(args))) + { + if (count) { + str = ""; + last = 1; + } else + break; + } + if (isset(XTRACE)) { + printprompt4(); + fprintf(xtrerr, "%s=%s\n", name, str); + fflush(xtrerr); + } + setsparam(name, ztrdup(str)); + count++; + } + if (!count) + break; + } + state->pc = loop; + execlist(state, 1, do_exec && args && empty(args)); + if (breaks) { + breaks--; + if (breaks || !contflag) + break; + contflag = 0; + } + if (retflag) + break; + if (iscond && !errflag) { + if (atok) { + str = dupstring(advance); + singsub(&str); + } else + str = advance; + if (isset(XTRACE)) { + printprompt4(); + fprintf(xtrerr, "%s\n", str); + fflush(xtrerr); + } + if (!errflag) + matheval(str); + } + if (errflag) { + if (breaks) + breaks--; + lastval = 1; + break; + } + freeheap(); + } + popheap(); + cmdpop(); + loops--; + simple_pline = old_simple_pline; + state->pc = end; + this_noerrexit = 1; + return lastval; +} + +/**/ +int +execselect(Estate state, UNUSED(int do_exec)) +{ + Wordcode end, loop; + wordcode code = state->pc[-1]; + char *str, *s, *name; + LinkNode n; + int i, usezle; + 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); + + if (WC_SELECT_TYPE(code) == WC_SELECT_PPARAM) { + char **x; + + args = newlinklist(); + for (x = pparams; *x; x++) + addlinknode(args, dupstring(*x)); + } else { + int htok = 0; + + 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++; + + pushheap(); + cmdpush(CS_SELECT); + usezle = interact && SHTTY != -1 && isset(USEZLE); + inp = fdopen(dup(usezle ? SHTTY : 0), "r"); + more = selectlist(args, 0); + loop = state->pc; + for (;;) { + for (;;) { + if (empty(bufstack)) { + if (usezle) { + int oef = errflag; + + isfirstln = 1; + str = zleentry(ZLE_CMD_READ, &prompt3, NULL, + 0, ZLCON_SELECT); + if (errflag) + str = NULL; + /* Keep any user interrupt error status */ + errflag = oef | (errflag & ERRFLAG_INT); + } else { + str = promptexpand(prompt3, 0, NULL, NULL, NULL); + zputs(str, stderr); + free(str); + fflush(stderr); + str = fgets(zhalloc(256), 256, inp); + } + } else + str = (char *)getlinknode(bufstack); + if (!str && !errflag) + setsparam("REPLY", ztrdup("")); /* EOF (user pressed Ctrl+D) */ + if (!str || errflag) { + if (breaks) + breaks--; + fprintf(stderr, "\n"); + fflush(stderr); + goto done; + } + if ((s = strchr(str, '\n'))) + *s = '\0'; + if (*str) + break; + more = selectlist(args, more); + } + setsparam("REPLY", ztrdup(str)); + i = atoi(str); + if (!i) + str = ""; + else { + for (i--, n = firstnode(args); n && i; incnode(n), i--); + if (n) + str = (char *) getdata(n); + else + str = ""; + } + setsparam(name, ztrdup(str)); + state->pc = loop; + execlist(state, 1, 0); + freeheap(); + if (breaks) { + breaks--; + if (breaks || !contflag) + break; + contflag = 0; + } + if (retflag || errflag) + break; + } + done: + cmdpop(); + popheap(); + fclose(inp); + loops--; + simple_pline = old_simple_pline; + state->pc = end; + this_noerrexit = 1; + return lastval; +} + +/* And this is used to print select lists. */ + +/**/ +size_t +selectlist(LinkList l, size_t start) +{ + size_t longest = 1, fct, fw = 0, colsz, t0, t1, ct; + char **arr, **ap; + + zleentry(ZLE_CMD_TRASH); + arr = hlinklist2array(l, 0); + for (ap = arr; *ap; ap++) + if (strlen(*ap) > longest) + longest = strlen(*ap); + t0 = ct = ap - arr; + longest++; + while (t0) + t0 /= 10, longest++; + /* to compensate for added ')' */ + fct = (zterm_columns - 1) / (longest + 3); + if (fct == 0) + fct = 1; + else + fw = (zterm_columns - 1) / fct; + colsz = (ct + fct - 1) / fct; + for (t1 = start; t1 != colsz && t1 - start < zterm_lines - 2; t1++) { + ap = arr + t1; + do { + size_t t2 = strlen(*ap) + 2; + int t3; + + fprintf(stderr, "%d) %s", t3 = ap - arr + 1, *ap); + while (t3) + t2++, t3 /= 10; + for (; t2 < fw; t2++) + fputc(' ', stderr); + for (t0 = colsz; t0 && *ap; t0--, ap++); + } + while (*ap); + fputc('\n', stderr); + } + + /* Below is a simple attempt at doing it the Korn Way.. + ap = arr; + t0 = 0; + do { + t0++; + fprintf(stderr,"%d) %s\n",t0,*ap); + ap++; + } + while (*ap);*/ + fflush(stderr); + + return t1 < colsz ? t1 : 0; +} + +/**/ +int +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; + oldval = 0; + pushheap(); + cmdpush(isuntil ? CS_UNTIL : CS_WHILE); + loops++; + loop = state->pc; + + if (loop[0] == WC_END && loop[1] == WC_END) { + + /* This is an empty loop. Make sure the signal handler sets the + * flags and then just wait for someone hitting ^C. */ + + simple_pline = 1; + + while (!breaks) + ; + breaks--; + + simple_pline = old_simple_pline; + } else + for (;;) { + state->pc = loop; + noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; + + /* 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) + breaks--; + if (!retflag) + lastval = oldval; + break; + } + if (retflag) + 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) + break; + contflag = 0; + } + if (errflag) { + lastval = 1; + break; + } + if (retflag) + break; + freeheap(); + oldval = lastval; + } + cmdpop(); + popheap(); + loops--; + state->pc = end; + this_noerrexit = 1; + return lastval; +} + +/**/ +int +execrepeat(Estate state, UNUSED(int do_exec)) +{ + Wordcode end, loop; + 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); + + lastval = 0; + tmp = ecgetstr(state, EC_DUPTOK, &htok); + if (htok) + singsub(&tmp); + count = mathevali(tmp); + if (errflag) + return 1; + pushheap(); + cmdpush(CS_REPEAT); + loops++; + loop = state->pc; + while (count-- > 0) { + state->pc = loop; + execlist(state, 1, 0); + freeheap(); + if (breaks) { + breaks--; + if (breaks || !contflag) + break; + contflag = 0; + } + if (errflag) { + lastval = 1; + break; + } + if (retflag) + break; + } + cmdpop(); + popheap(); + loops--; + simple_pline = old_simple_pline; + state->pc = end; + this_noerrexit = 1; + return lastval; +} + +/**/ +int +execif(Estate state, int do_exec) +{ + Wordcode end, next; + wordcode code = state->pc[-1]; + int olderrexit, s = 0, run = 0; + + olderrexit = noerrexit; + end = state->pc + WC_IF_SKIP(code); + + noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN; + while (state->pc < end) { + code = *state->pc++; + if (wc_code(code) != WC_IF || + (run = (WC_IF_TYPE(code) == WC_IF_ELSE))) { + if (run) + run = 2; + break; + } + next = state->pc + WC_IF_SKIP(code); + cmdpush(s ? CS_ELIF : CS_IF); + execlist(state, 1, 0); + cmdpop(); + if (!lastval) { + run = 1; + break; + } + if (retflag) + break; + s = 1; + state->pc = next; + } + + if (run) { + /* we need to ignore lastval until we reach execcmd() */ + if (olderrexit) + noerrexit = olderrexit; + else if (lastval) + noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN | NOERREXIT_UNTIL_EXEC; + else + noerrexit &= ~ (NOERREXIT_EXIT | NOERREXIT_RETURN); + cmdpush(run == 2 ? CS_ELSE : (s ? CS_ELIFTHEN : CS_IFTHEN)); + execlist(state, 1, do_exec); + cmdpop(); + } else { + noerrexit = olderrexit; + if (!retflag) + lastval = 0; + } + state->pc = end; + this_noerrexit = 1; + + return lastval; +} + +/**/ +int +execcase(Estate state, int do_exec) +{ + Wordcode end, next; + wordcode code = state->pc[-1]; + char *word, *pat; + int npat, save, nalts, ialt, patok, anypatok; + Patprog *spprog, pprog; + + end = state->pc + WC_CASE_SKIP(code); + + word = ecgetstr(state, EC_DUP, NULL); + singsub(&word); + untokenize(word); + anypatok = 0; + + cmdpush(CS_CASE); + while (state->pc < end) { + code = *state->pc++; + if (wc_code(code) != WC_CASE) + break; + + save = 0; + next = state->pc + WC_CASE_SKIP(code); + nalts = *state->pc++; + ialt = patok = 0; + + if (isset(XTRACE)) { + printprompt4(); + fprintf(xtrerr, "case %s (", word); + } + + while (!patok && nalts) { + npat = state->pc[1]; + spprog = state->prog->pats + npat; + pprog = NULL; + pat = NULL; + + queue_signals(); + + if (isset(XTRACE)) { + int htok = 0; + pat = dupstring(ecrawstr(state->prog, state->pc, &htok)); + if (htok) + singsub(&pat); + + if (ialt++) + fprintf(stderr, " | "); + quote_tokenized_output(pat, xtrerr); + } + + if (*spprog != dummy_patprog1 && *spprog != dummy_patprog2) + pprog = *spprog; + + if (!pprog) { + if (!pat) { + char *opat; + int htok = 0; + + pat = dupstring(opat = ecrawstr(state->prog, + state->pc, &htok)); + if (htok) + singsub(&pat); + save = (!(state->prog->flags & EF_HEAP) && + !strcmp(pat, opat) && *spprog != dummy_patprog2); + } + if (!(pprog = patcompile(pat, (save ? PAT_ZDUP : PAT_STATIC), + NULL))) + zerr("bad pattern: %s", pat); + else if (save) + *spprog = pprog; + } + if (pprog && pattry(pprog, word)) + patok = anypatok = 1; + state->pc += 2; + nalts--; + + unqueue_signals(); + } + state->pc += 2 * nalts; + if (isset(XTRACE)) { + fprintf(xtrerr, ")\n"); + fflush(xtrerr); + } + if (patok) { + execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) && + do_exec)); + while (!retflag && wc_code(code) == WC_CASE && + WC_CASE_TYPE(code) == WC_CASE_AND && state->pc < end) { + state->pc = next; + code = *state->pc++; + next = state->pc + WC_CASE_SKIP(code); + nalts = *state->pc++; + state->pc += 2 * nalts; + execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) && + do_exec)); + } + if (WC_CASE_TYPE(code) != WC_CASE_TESTAND) + break; + } + state->pc = next; + } + cmdpop(); + + state->pc = end; + + if (!anypatok) + lastval = 0; + this_noerrexit = 1; + + return lastval; +} + +/* + * Errflag from `try' block, may be reset in `always' block. + * Accessible from an integer parameter, so needs to be a zlong. + */ + +/**/ +zlong +try_errflag = -1; + +/** + * Corresponding interrupt error status form `try' block. + */ + +/**/ +zlong +try_interrupt = -1; + +/**/ +zlong +try_tryflag = 0; + +/**/ +int +exectry(Estate state, int do_exec) +{ + Wordcode end, always; + int endval; + int save_retflag, save_breaks, save_contflag; + zlong save_try_errflag, save_try_tryflag, save_try_interrupt; + + end = state->pc + WC_TRY_SKIP(state->pc[-1]); + always = state->pc + 1 + WC_TRY_SKIP(*state->pc); + state->pc++; + pushheap(); + cmdpush(CS_CURSH); + + /* The :try clause */ + save_try_tryflag = try_tryflag; + try_tryflag = 1; + + execlist(state, 1, do_exec); + + try_tryflag = save_try_tryflag; + + /* Don't record errflag here, may be reset. However, */ + /* endval should show failure when there is an error. */ + endval = lastval ? lastval : errflag; + + freeheap(); + + cmdpop(); + cmdpush(CS_ALWAYS); + + /* The always clause. */ + save_try_errflag = try_errflag; + save_try_interrupt = try_interrupt; + try_errflag = (zlong)(errflag & ERRFLAG_ERROR); + try_interrupt = (zlong)((errflag & ERRFLAG_INT) ? 1 : 0); + /* We need to reset all errors to allow the block to execute */ + errflag = 0; + save_retflag = retflag; + retflag = 0; + save_breaks = breaks; + breaks = 0; + save_contflag = contflag; + contflag = 0; + + state->pc = always; + execlist(state, 1, do_exec); + + if (try_errflag) + errflag |= ERRFLAG_ERROR; + else + errflag &= ~ERRFLAG_ERROR; + if (try_interrupt) + errflag |= ERRFLAG_INT; + else + errflag &= ~ERRFLAG_INT; + try_errflag = save_try_errflag; + try_interrupt = save_try_interrupt; + if (!retflag) + retflag = save_retflag; + if (!breaks) + breaks = save_breaks; + if (!contflag) + contflag = save_contflag; + + cmdpop(); + popheap(); + state->pc = end; + + return endval; +} diff --git a/dotfiles/system/.zsh/modules/Src/makepro.awk b/dotfiles/system/.zsh/modules/Src/makepro.awk new file mode 100644 index 0000000..0498c15 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/makepro.awk @@ -0,0 +1,166 @@ +# +# makepro.awk - generate prototype lists +# + +BEGIN { + aborting = 0 + + # arg 1 is the name of the file to process + # arg 2 is the name of the subdirectory it is in + if(ARGC != 3) { + aborting = 1 + exit 1 + } + name = ARGV[1] + gsub(/^.*\//, "", name) + gsub(/\.c$/, "", name) + name = ARGV[2] "_" name + gsub(/\//, "_", name) + ARGC-- + + printf "E#ifndef have_%s_globals\n", name + printf "E#define have_%s_globals\n", name + printf "E\n" +} + +# all relevant declarations are preceded by "/**/" on a line by itself + +/^\/\*\*\/$/ { + # The declaration is on following lines. The interesting part might + # be terminated by a `{' (`int foo(void) { }' or `int bar[] = {') + # or `;' (`int x;'). + line = "" + isfunc = 0 + while(1) { + if(getline <= 0) { + aborting = 1 + exit 1 + } + if (line == "" && $0 ~ /^[ \t]*#/) { + # Directly after the /**/ was a preprocessor line. + # Spit it out and re-start the outer loop. + printf "E%s\n", $0 + printf "L%s\n", $0 + next + } + gsub(/\t/, " ") + line = line " " $0 + gsub(/\/\*([^*]|\*+[^*\/])*\*+\//, " ", line) + if(line ~ /\/\*/) + continue + # If it is a function definition, note so. + if(line ~ /\) *(VA_DCL )*[{].*$/) #} + isfunc = 1 + if(sub(/ *[{;].*$/, "", line)) #} + break + } + if (!match(line, /VA_ALIST/)) { + # Put spaces around each identifier. + while(match(line, /[^_0-9A-Za-z ][_0-9A-Za-z]/) || + match(line, /[_0-9A-Za-z][^_0-9A-Za-z ]/)) + line = substr(line, 1, RSTART) " " substr(line, RSTART+1) + } + # Separate declarations into a type and a list of declarators. + # In each declarator, "@{" and "@}" are used in place of parens to + # mark function parameter lists, and "@!" is used in place of commas + # in parameter lists. "@<" and "@>" are used in place of + # non-parameter list parens. + gsub(/ _ +/, " _ ", line) + while(1) { + if(isfunc && match(line, /\([^()]*\)$/)) + line = substr(line, 1, RSTART-1) " _ (" substr(line, RSTART) ")" + else if(match(line, / _ \(\([^,()]*,/)) + line = substr(line, 1, RSTART+RLENGTH-2) "@!" substr(line, RSTART+RLENGTH) + else if(match(line, / _ \(\([^,()]*\)\)/)) + line = substr(line, 1, RSTART-1) "@{" substr(line, RSTART+5, RLENGTH-7) "@}" substr(line, RSTART+RLENGTH) + else if(match(line, /\([^,()]*\)/)) + line = substr(line, 1, RSTART-1) "@<" substr(line, RSTART+1, RLENGTH-2) "@>" substr(line, RSTART+RLENGTH) + else + break + } + sub(/^ */, "", line) + match(line, /^((const|enum|mod_export|static|struct|union) +)*([_0-9A-Za-z]+ +|((char|double|float|int|long|short|unsigned|void) +)+)((const|static) +)*/) + dtype = substr(line, 1, RLENGTH) + sub(/ *$/, "", dtype) + if(" " dtype " " ~ / static /) + locality = "L" + else + locality = "E" + exported = " " dtype " " ~ / mod_export / + line = substr(line, RLENGTH+1) "," + # Handle each declarator. + if (match(line, /VA_ALIST/)) { + # Already has VARARGS handling. + + # Put parens etc. back + gsub(/@[{]/, "((", line) + gsub(/@}/, "))", line) + gsub(/@/, ")", line) + gsub(/@!/, ",", line) + sub(/,$/, ";", line) + gsub(/mod_export/, "mod_import_function", dtype) + gsub(/VA_ALIST/, "VA_ALIST_PROTO", line) + sub(/ VA_DCL/, "", line) + + if(locality ~ /E/) + dtype = "extern " dtype + + if (match(line, /[_0-9A-Za-z]+\(VA_ALIST/)) + dnam = substr(line, RSTART, RLENGTH-9) + + # If this is exported, add it to the exported symbol list. + if (exported) + printf "X%s\n", dnam + + printf "%s%s %s\n", locality, dtype, line + } else { + while(match(line, /^[^,]*,/)) { + # Separate out the name from the declarator. Use "@+" and "@-" + # to bracket the name within the declarator. Strip off any + # initialiser. + dcltor = substr(line, 1, RLENGTH-1) + line = substr(line, RLENGTH+1) + sub(/\=.*$/, "", dcltor) + match(dcltor, /^([^_0-9A-Za-z]| const )*/) + dcltor = substr(dcltor, 1, RLENGTH) "@+" substr(dcltor, RLENGTH+1) + match(dcltor, /^.*@\+[_0-9A-Za-z]+/) + dcltor = substr(dcltor, 1, RLENGTH) "@-" substr(dcltor, RLENGTH+1) + dnam = dcltor + sub(/^.*@\+/, "", dnam) + sub(/@-.*$/, "", dnam) + + # Put parens etc. back + gsub(/@[{]/, " _((", dcltor) + gsub(/@}/, "))", dcltor) + gsub(/@/, ")", dcltor) + gsub(/@!/, ",", dcltor) + + # If this is exported, add it to the exported symbol list. + if(exported) + printf "X%s\n", dnam + + # Format the declaration for output + dcl = dtype " " dcltor ";" + if(locality ~ /E/) + dcl = "extern " dcl + if(isfunc) + gsub(/ mod_export /, " mod_import_function ", dcl) + else + gsub(/ mod_export /, " mod_import_variable ", dcl) + gsub(/@[+-]/, "", dcl) + gsub(/ +/, " ", dcl) + while(match(dcl, /[^_0-9A-Za-z] ./) || match(dcl, /. [^_0-9A-Za-z]/)) + dcl = substr(dcl, 1, RSTART) substr(dcl, RSTART+2) + printf "%s%s\n", locality, dcl + } + } +} + +END { + if(aborting) + exit 1 + printf "E\n" + printf "E#endif /* !have_%s_globals */\n", name +} diff --git a/dotfiles/system/.zsh/modules/Src/mem.c b/dotfiles/system/.zsh/modules/Src/mem.c new file mode 100644 index 0000000..77e4375 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/mem.c @@ -0,0 +1,1899 @@ +/* + * mem.c - memory management + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "mem.pro" + +/* + There are two ways to allocate memory in zsh. The first way is + to call zalloc/zshcalloc, which call malloc/calloc directly. It + is legal to call realloc() or free() on memory allocated this way. + The second way is to call zhalloc/hcalloc, which allocates memory + from one of the memory pools on the heap stack. Such memory pools + will automatically created when the heap allocation routines are + called. To be sure that they are freed at appropriate times + one should call pushheap() before one starts using heaps and + popheap() after that (when the memory allocated on the heaps since + the last pushheap() isn't needed anymore). + pushheap() saves the states of all currently allocated heaps and + popheap() resets them to the last state saved and destroys the + information about that state. If you called pushheap() and + allocated some memory on the heaps and then come to a place where + you don't need the allocated memory anymore but you still want + to allocate memory on the heap, you should call freeheap(). This + works like popheap(), only that it doesn't free the information + about the heap states (i.e. the heaps are like after the call to + pushheap() and you have to call popheap some time later). + + Memory allocated in this way does not have to be freed explicitly; + it will all be freed when the pool is destroyed. In fact, + attempting to free this memory may result in a core dump. + + If possible, the heaps are allocated using mmap() so that the + (*real*) heap isn't filled up with empty zsh heaps. If mmap() + is not available and zsh's own allocator is used, we use a simple trick + to avoid that: we allocate a large block of memory before allocating + a heap pool, this memory is freed again immediately after the pool + is allocated. If there are only small blocks on the free list this + guarantees that the memory for the pool is at the end of the memory + which means that we can give it back to the system when the pool is + freed. + + hrealloc(char *p, size_t old, size_t new) is an optimisation + with a similar interface to realloc(). Typically the new size + will be larger than the old one, since there is no gain in + shrinking the allocation (indeed, that will confused hrealloc() + since it will forget that the unused space once belonged to this + pointer). However, new == 0 is a special case; then if we + had to allocate a special heap for this memory it is freed at + that point. +*/ + +#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MMAP) && defined(HAVE_MUNMAP) + +#include + +/* + * This definition is designed to enable use of memory mapping on MacOS. + * However, performance tests indicate that MacOS mapped regions are + * somewhat slower to allocate than memory from malloc(), so whether + * using this improves performance depends on details of zhalloc(). + */ +#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) +#define MAP_ANONYMOUS MAP_ANON +#endif + +#if defined(MAP_ANONYMOUS) && defined(MAP_PRIVATE) + +#define USE_MMAP 1 +#define MMAP_FLAGS (MAP_ANONYMOUS | MAP_PRIVATE) + +#endif +#endif + +#ifdef ZSH_MEM_WARNING +# ifndef DEBUG +# define DEBUG 1 +# endif +#endif + +#if defined(ZSH_MEM) && defined(ZSH_MEM_DEBUG) + +static int h_m[1025], h_push, h_pop, h_free; + +#endif + +/* Make sure we align to the longest fundamental type. */ +union mem_align { + zlong l; + double d; +}; + +#define H_ISIZE sizeof(union mem_align) +#define HEAPSIZE (16384 - H_ISIZE) +/* Memory available for user data in default arena size */ +#define HEAP_ARENA_SIZE (HEAPSIZE - sizeof(struct heap)) +#define HEAPFREE (16384 - H_ISIZE) + +/* Memory available for user data in heap h */ +#define ARENA_SIZEOF(h) ((h)->size - sizeof(struct heap)) + +/* list of zsh heaps */ + +static Heap heaps; + +/* a heap with free space, not always correct (it will be the last heap + * if that was newly allocated but it may also be another one) */ + +static Heap fheap; + +/**/ +#ifdef ZSH_HEAP_DEBUG +/* + * The heap ID we'll allocate next. + * + * We'll avoid using 0 as that means zero-initialised memory + * containing a heap ID is (correctly) marked as invalid. + */ +static Heapid next_heap_id = (Heapid)1; + +/* + * The ID of the heap from which we last allocated heap memory. + * In theory, since we carefully avoid allocating heap memory during + * interrupts, after any call to zhalloc() or wrappers this should + * be the ID of the heap containing the memory just returned. + */ +/**/ +mod_export Heapid last_heap_id; + +/* + * Stack of heaps saved by new_heaps(). + * Assumes old_heaps() will come along and restore it later + * (outputs an error if old_heaps() is called out of sequence). + */ +static LinkList heaps_saved; + +/* + * Debugging verbosity. This must be set from a debugger. + * An 'or' of bits from the enum heap_debug_verbosity. + */ +static volatile int heap_debug_verbosity; + +/* + * Generate a heap identifier that's unique up to unsigned integer wrap. + * + * For the purposes of debugging we won't bother trying to make a + * heap_id globally unique, which would require checking all existing + * heaps every time we create an ID and still wouldn't do what we + * ideally want, which is to make sure the IDs of valid heaps are + * different from the IDs of no-longer-valid heaps. Given that, + * we'll just assume that if we haven't tracked the problem when the + * ID wraps we're out of luck. We could change the type to a long long + * if we wanted more room + */ + +static Heapid +new_heap_id(void) +{ + return next_heap_id++; +} + +/**/ +#endif + +/* Use new heaps from now on. This returns the old heap-list. */ + +/**/ +mod_export Heap +new_heaps(void) +{ + Heap h; + + queue_signals(); + h = heaps; + + fheap = heaps = NULL; + unqueue_signals(); + +#ifdef ZSH_HEAP_DEBUG + if (heap_debug_verbosity & HDV_NEW) { + fprintf(stderr, "HEAP DEBUG: heap " HEAPID_FMT + " saved, new heaps created.\n", h->heap_id); + } + if (!heaps_saved) + heaps_saved = znewlinklist(); + zpushnode(heaps_saved, h); +#endif + return h; +} + +/* Re-install the old heaps again, freeing the new ones. */ + +/**/ +mod_export void +old_heaps(Heap old) +{ + Heap h, n; + + queue_signals(); + for (h = heaps; h; h = n) { + n = h->next; + DPUTS(h->sp, "BUG: old_heaps() with pushed heaps"); +#ifdef ZSH_HEAP_DEBUG + if (heap_debug_verbosity & HDV_FREE) { + fprintf(stderr, "HEAP DEBUG: heap " HEAPID_FMT + "freed in old_heaps().\n", h->heap_id); + } +#endif +#ifdef USE_MMAP + munmap((void *) h, h->size); +#else + zfree(h, HEAPSIZE); +#endif +#ifdef ZSH_VALGRIND + VALGRIND_DESTROY_MEMPOOL((char *)h); +#endif + } + heaps = old; +#ifdef ZSH_HEAP_DEBUG + if (heap_debug_verbosity & HDV_OLD) { + fprintf(stderr, "HEAP DEBUG: heap " HEAPID_FMT + "restored.\n", heaps->heap_id); + } + { + Heap myold = heaps_saved ? getlinknode(heaps_saved) : NULL; + if (old != myold) + { + fprintf(stderr, "HEAP DEBUG: invalid old heap " HEAPID_FMT + ", expecting " HEAPID_FMT ".\n", old->heap_id, + myold->heap_id); + } + } +#endif + fheap = NULL; + unqueue_signals(); +} + +/* Temporarily switch to other heaps (or back again). */ + +/**/ +mod_export Heap +switch_heaps(Heap new) +{ + Heap h; + + queue_signals(); + h = heaps; + +#ifdef ZSH_HEAP_DEBUG + if (heap_debug_verbosity & HDV_SWITCH) { + fprintf(stderr, "HEAP DEBUG: heap temporarily switched from " + HEAPID_FMT " to " HEAPID_FMT ".\n", h->heap_id, new->heap_id); + } +#endif + heaps = new; + fheap = NULL; + unqueue_signals(); + + return h; +} + +/* save states of zsh heaps */ + +/**/ +mod_export void +pushheap(void) +{ + Heap h; + Heapstack hs; + + queue_signals(); + +#if defined(ZSH_MEM) && defined(ZSH_MEM_DEBUG) + h_push++; +#endif + + for (h = heaps; h; h = h->next) { + DPUTS(!h->used && h->next, "BUG: empty heap"); + hs = (Heapstack) zalloc(sizeof(*hs)); + hs->next = h->sp; + h->sp = hs; + hs->used = h->used; +#ifdef ZSH_HEAP_DEBUG + hs->heap_id = h->heap_id; + h->heap_id = new_heap_id(); + if (heap_debug_verbosity & HDV_PUSH) { + fprintf(stderr, "HEAP DEBUG: heap " HEAPID_FMT " pushed, new id is " + HEAPID_FMT ".\n", + hs->heap_id, h->heap_id); + } +#endif + } + unqueue_signals(); +} + +/* reset heaps to previous state */ + +/**/ +mod_export void +freeheap(void) +{ + Heap h, hn, hl = NULL; + + queue_signals(); + +#if defined(ZSH_MEM) && defined(ZSH_MEM_DEBUG) + h_free++; +#endif + + /* + * When pushheap() is called, it sweeps over the entire heaps list of + * arenas and marks every one of them with the amount of free space in + * that arena at that moment. zhalloc() is then allowed to grab bits + * out of any of those arenas that have free space. + * + * Whenever fheap is NULL here, the loop below sweeps back over the + * entire heap list again, resetting the free space in every arena to + * the amount stashed by pushheap() and finding the arena with the most + * free space to optimize zhalloc()'s next search. When there's a lot + * of stuff already on the heap, this is an enormous amount of work, + * and performance goes to hell. + * + * Therefore, we defer freeing the most recently allocated arena until + * we reach popheap(). + * + * However, if the arena to which fheap points is unused, we want to + * reclaim space in earlier arenas, so we have no choice but to do the + * sweep for a new fheap. + */ + if (fheap && !fheap->sp) + fheap = NULL; /* We used to do this unconditionally */ + /* + * In other cases, either fheap is already correct, or it has never + * been set and this loop will do it, or it'll be reset from scratch + * on the next popheap(). So all that's needed here is to pick up + * the scan wherever the last pass [or the last popheap()] left off. + */ + for (h = (fheap ? fheap : heaps); h; h = hn) { + hn = h->next; + if (h->sp) { +#ifdef ZSH_MEM_DEBUG +#ifdef ZSH_VALGRIND + VALGRIND_MAKE_MEM_UNDEFINED((char *)arena(h) + h->sp->used, + h->used - h->sp->used); +#endif + memset(arena(h) + h->sp->used, 0xff, h->used - h->sp->used); +#endif + h->used = h->sp->used; + if (!fheap) { + if (h->used < ARENA_SIZEOF(h)) + fheap = h; + } else if (ARENA_SIZEOF(h) - h->used > + ARENA_SIZEOF(fheap) - fheap->used) + fheap = h; + hl = h; +#ifdef ZSH_HEAP_DEBUG + /* + * As the free makes the heap invalid, give it a new + * identifier. We're not popping it, so don't use + * the one in the heap stack. + */ + { + Heapid new_id = new_heap_id(); + if (heap_debug_verbosity & HDV_FREE) { + fprintf(stderr, "HEAP DEBUG: heap " HEAPID_FMT + " freed, new id is " HEAPID_FMT ".\n", + h->heap_id, new_id); + } + h->heap_id = new_id; + } +#endif +#ifdef ZSH_VALGRIND + VALGRIND_MEMPOOL_TRIM((char *)h, (char *)arena(h), h->used); +#endif + } else { + if (fheap == h) + fheap = NULL; + if (h->next) { + /* We want to cut this out of the arena list if we can */ + if (h == heaps) + hl = heaps = h->next; + else if (hl && hl->next == h) + hl->next = h->next; + else { + DPUTS(hl, "hl->next != h when freeing"); + hl = h; + continue; + } + h->next = NULL; + } else { + /* Leave an empty arena at the end until popped */ + h->used = 0; + fheap = hl = h; + break; + } +#ifdef USE_MMAP + munmap((void *) h, h->size); +#else + zfree(h, HEAPSIZE); +#endif +#ifdef ZSH_VALGRIND + VALGRIND_DESTROY_MEMPOOL((char *)h); +#endif + } + } + if (hl) + hl->next = NULL; + else + heaps = fheap = NULL; + + unqueue_signals(); +} + +/* reset heap to previous state and destroy state information */ + +/**/ +mod_export void +popheap(void) +{ + Heap h, hn, hl = NULL; + Heapstack hs; + + queue_signals(); + +#if defined(ZSH_MEM) && defined(ZSH_MEM_DEBUG) + h_pop++; +#endif + + fheap = NULL; + for (h = heaps; h; h = hn) { + hn = h->next; + if ((hs = h->sp)) { + h->sp = hs->next; +#ifdef ZSH_MEM_DEBUG +#ifdef ZSH_VALGRIND + VALGRIND_MAKE_MEM_UNDEFINED((char *)arena(h) + hs->used, + h->used - hs->used); +#endif + memset(arena(h) + hs->used, 0xff, h->used - hs->used); +#endif + h->used = hs->used; +#ifdef ZSH_HEAP_DEBUG + if (heap_debug_verbosity & HDV_POP) { + fprintf(stderr, "HEAP DEBUG: heap " HEAPID_FMT + " popped, old heap was " HEAPID_FMT ".\n", + h->heap_id, hs->heap_id); + } + h->heap_id = hs->heap_id; +#endif +#ifdef ZSH_VALGRIND + VALGRIND_MEMPOOL_TRIM((char *)h, (char *)arena(h), h->used); +#endif + if (!fheap) { + if (h->used < ARENA_SIZEOF(h)) + fheap = h; + } else if (ARENA_SIZEOF(h) - h->used > + ARENA_SIZEOF(fheap) - fheap->used) + fheap = h; + zfree(hs, sizeof(*hs)); + + hl = h; + } else { + if (h->next) { + /* We want to cut this out of the arena list if we can */ + if (h == heaps) + hl = heaps = h->next; + else if (hl && hl->next == h) + hl->next = h->next; + else { + DPUTS(hl, "hl->next != h when popping"); + hl = h; + continue; + } + h->next = NULL; + } else if (hl == h) /* This is the last arena of all */ + hl = NULL; +#ifdef USE_MMAP + munmap((void *) h, h->size); +#else + zfree(h, HEAPSIZE); +#endif +#ifdef ZSH_VALGRIND + VALGRIND_DESTROY_MEMPOOL((char *)h); +#endif + } + } + if (hl) + hl->next = NULL; + else + heaps = NULL; + + unqueue_signals(); +} + +#ifdef USE_MMAP +/* + * Utility function to allocate a heap area of at least *n bytes. + * *n will be rounded up to the next page boundary. + */ +static Heap +mmap_heap_alloc(size_t *n) +{ + Heap h; + static size_t pgsz = 0; + + if (!pgsz) { + +#ifdef _SC_PAGESIZE + pgsz = sysconf(_SC_PAGESIZE); /* SVR4 */ +#else +# ifdef _SC_PAGE_SIZE + pgsz = sysconf(_SC_PAGE_SIZE); /* HPUX */ +# else + pgsz = getpagesize(); +# endif +#endif + + pgsz--; + } + *n = (*n + pgsz) & ~pgsz; + h = (Heap) mmap(NULL, *n, PROT_READ | PROT_WRITE, + MMAP_FLAGS, -1, 0); + if (h == ((Heap) -1)) { + zerr("fatal error: out of heap memory"); + exit(1); + } + + return h; +} +#endif + +/* check whether a pointer is within a memory pool */ + +/**/ +mod_export void * +zheapptr(void *p) +{ + Heap h; + queue_signals(); + for (h = heaps; h; h = h->next) + if ((char *)p >= arena(h) && + (char *)p + H_ISIZE < arena(h) + ARENA_SIZEOF(h)) + break; + unqueue_signals(); + return (h ? p : 0); +} + +/* allocate memory from the current memory pool */ + +/**/ +mod_export void * +zhalloc(size_t size) +{ + Heap h, hp = NULL; + size_t n; +#ifdef ZSH_VALGRIND + size_t req_size = size; + + if (size == 0) + return NULL; +#endif + + size = (size + H_ISIZE - 1) & ~(H_ISIZE - 1); + + queue_signals(); + +#if defined(ZSH_MEM) && defined(ZSH_MEM_DEBUG) + h_m[size < (1024 * H_ISIZE) ? (size / H_ISIZE) : 1024]++; +#endif + + /* find a heap with enough free space */ + + /* + * This previously assigned: + * h = ((fheap && ARENA_SIZEOF(fheap) >= (size + fheap->used)) + * ? fheap : heaps); + * but we think that nothing upstream of fheap has more free space, + * so why start over at heaps just because fheap has too little? + */ + for (h = (fheap ? fheap : heaps); h; h = h->next) { + hp = h; + if (ARENA_SIZEOF(h) >= (n = size + h->used)) { + void *ret; + + h->used = n; + ret = arena(h) + n - size; + unqueue_signals(); +#ifdef ZSH_HEAP_DEBUG + last_heap_id = h->heap_id; + if (heap_debug_verbosity & HDV_ALLOC) { + fprintf(stderr, "HEAP DEBUG: allocated memory from heap " + HEAPID_FMT ".\n", h->heap_id); + } +#endif +#ifdef ZSH_VALGRIND + VALGRIND_MEMPOOL_ALLOC((char *)h, (char *)ret, req_size); +#endif + return ret; + } + } + { + /* not found, allocate new heap */ +#if defined(ZSH_MEM) && !defined(USE_MMAP) + static int called = 0; + void *foo = called ? (void *)malloc(HEAPFREE) : NULL; + /* tricky, see above */ +#endif + + n = HEAP_ARENA_SIZE > size ? HEAPSIZE : size + sizeof(*h); + +#ifdef USE_MMAP + h = mmap_heap_alloc(&n); +#else + h = (Heap) zalloc(n); +#endif + +#if defined(ZSH_MEM) && !defined(USE_MMAP) + if (called) + zfree(foo, HEAPFREE); + called = 1; +#endif + + h->size = n; + h->used = size; + h->next = NULL; + h->sp = NULL; +#ifdef ZSH_HEAP_DEBUG + h->heap_id = new_heap_id(); + if (heap_debug_verbosity & HDV_CREATE) { + fprintf(stderr, "HEAP DEBUG: create new heap " HEAPID_FMT ".\n", + h->heap_id); + } +#endif +#ifdef ZSH_VALGRIND + VALGRIND_CREATE_MEMPOOL((char *)h, 0, 0); + VALGRIND_MAKE_MEM_NOACCESS((char *)arena(h), + n - ((char *)arena(h)-(char *)h)); + VALGRIND_MEMPOOL_ALLOC((char *)h, (char *)arena(h), req_size); +#endif + + DPUTS(hp && hp->next, "failed to find end of chain in zhalloc"); + if (hp) + hp->next = h; + else + heaps = h; + fheap = h; + + unqueue_signals(); +#ifdef ZSH_HEAP_DEBUG + last_heap_id = h->heap_id; + if (heap_debug_verbosity & HDV_ALLOC) { + fprintf(stderr, "HEAP DEBUG: allocated memory from heap " + HEAPID_FMT ".\n", h->heap_id); + } +#endif + return arena(h); + } +} + +/**/ +mod_export void * +hrealloc(char *p, size_t old, size_t new) +{ + Heap h, ph; + +#ifdef ZSH_VALGRIND + size_t new_req = new; +#endif + + old = (old + H_ISIZE - 1) & ~(H_ISIZE - 1); + new = (new + H_ISIZE - 1) & ~(H_ISIZE - 1); + + if (old == new) + return p; + if (!old && !p) +#ifdef ZSH_VALGRIND + return zhalloc(new_req); +#else + return zhalloc(new); +#endif + + /* find the heap with p */ + + queue_signals(); + for (h = heaps, ph = NULL; h; ph = h, h = h->next) + if (p >= arena(h) && p < arena(h) + ARENA_SIZEOF(h)) + break; + + DPUTS(!h, "BUG: hrealloc() called for non-heap memory."); + DPUTS(h->sp && arena(h) + h->sp->used > p, + "BUG: hrealloc() wants to realloc pushed memory"); + + /* + * If the end of the old chunk is before the used pointer, + * more memory has been zhalloc'ed afterwards. + * We can't tell if that's still in use, obviously, since + * that's the whole point of heap memory. + * We have no choice other than to grab some more memory + * somewhere else and copy in the old stuff. + */ + if (p + old < arena(h) + h->used) { + if (new > old) { +#ifdef ZSH_VALGRIND + char *ptr = (char *) zhalloc(new_req); +#else + char *ptr = (char *) zhalloc(new); +#endif + memcpy(ptr, p, old); +#ifdef ZSH_MEM_DEBUG + memset(p, 0xff, old); +#endif +#ifdef ZSH_VALGRIND + VALGRIND_MEMPOOL_FREE((char *)h, (char *)p); + /* + * zhalloc() marked h,ptr,new as an allocation so we don't + * need to do that here. + */ +#endif + unqueue_signals(); + return ptr; + } else { +#ifdef ZSH_VALGRIND + VALGRIND_MEMPOOL_FREE((char *)h, (char *)p); + if (p) { + VALGRIND_MEMPOOL_ALLOC((char *)h, (char *)p, + new_req); + VALGRIND_MAKE_MEM_DEFINED((char *)h, (char *)p); + } +#endif + unqueue_signals(); + return new ? p : NULL; + } + } + + DPUTS(p + old != arena(h) + h->used, "BUG: hrealloc more than allocated"); + + /* + * We now know there's nothing afterwards in the heap, now see if + * there's nothing before. Then we can reallocate the whole thing. + * Otherwise, we need to keep the stuff at the start of the heap, + * then allocate a new one too; this is handled below. (This will + * guarantee we occupy a full heap next time round, provided we + * don't use the heap for anything else.) + */ + if (p == arena(h)) { +#ifdef ZSH_HEAP_DEBUG + Heapid heap_id = h->heap_id; +#endif + /* + * Zero new seems to be a special case saying we've finished + * with the specially reallocated memory, see scanner() in glob.c. + */ + if (!new) { + if (ph) + ph->next = h->next; + else + heaps = h->next; + fheap = NULL; +#ifdef USE_MMAP + munmap((void *) h, h->size); +#else + zfree(h, HEAPSIZE); +#endif +#ifdef ZSH_VALGRIND + VALGRIND_DESTROY_MEMPOOL((char *)h); +#endif + unqueue_signals(); + return NULL; + } + if (new > ARENA_SIZEOF(h)) { + Heap hnew; + /* + * Not enough memory in this heap. Allocate a new + * one of sufficient size. + * + * To avoid this happening too often, allocate + * chunks in multiples of HEAPSIZE. + * (Historical note: there didn't used to be any + * point in this since we didn't consistently record + * the allocated size of the heap, but now we do.) + */ + size_t n = (new + sizeof(*h) + HEAPSIZE); + n -= n % HEAPSIZE; + fheap = NULL; + +#ifdef USE_MMAP + { + /* + * I don't know any easy portable way of requesting + * a mmap'd segment be extended, so simply allocate + * a new one and copy. + */ + hnew = mmap_heap_alloc(&n); + /* Copy the entire heap, header (with next pointer) included */ + memcpy(hnew, h, h->size); + munmap((void *)h, h->size); + } +#else + hnew = (Heap) realloc(h, n); +#endif +#ifdef ZSH_VALGRIND + VALGRIND_MEMPOOL_FREE((char *)h, p); + VALGRIND_DESTROY_MEMPOOL((char *)h); + VALGRIND_CREATE_MEMPOOL((char *)hnew, 0, 0); + VALGRIND_MEMPOOL_ALLOC((char *)hnew, (char *)arena(hnew), + new_req); + VALGRIND_MAKE_MEM_DEFINED((char *)hnew, (char *)arena(hnew)); +#endif + h = hnew; + + h->size = n; + if (ph) + ph->next = h; + else + heaps = h; + } +#ifdef ZSH_VALGRIND + else { + VALGRIND_MEMPOOL_FREE((char *)h, (char *)p); + VALGRIND_MEMPOOL_ALLOC((char *)h, (char *)p, new_req); + VALGRIND_MAKE_MEM_DEFINED((char *)h, (char *)p); + } +#endif + h->used = new; +#ifdef ZSH_HEAP_DEBUG + h->heap_id = heap_id; +#endif + unqueue_signals(); + return arena(h); + } +#ifndef USE_MMAP + DPUTS(h->used > ARENA_SIZEOF(h), "BUG: hrealloc at invalid address"); +#endif + if (h->used + (new - old) <= ARENA_SIZEOF(h)) { + h->used += new - old; + unqueue_signals(); +#ifdef ZSH_VALGRIND + VALGRIND_MEMPOOL_FREE((char *)h, (char *)p); + VALGRIND_MEMPOOL_ALLOC((char *)h, (char *)p, new_req); + VALGRIND_MAKE_MEM_DEFINED((char *)h, (char *)p); +#endif + return p; + } else { + char *t = zhalloc(new); + memcpy(t, p, old > new ? new : old); + h->used -= old; +#ifdef ZSH_MEM_DEBUG + memset(p, 0xff, old); +#endif +#ifdef ZSH_VALGRIND + VALGRIND_MEMPOOL_FREE((char *)h, (char *)p); + /* t already marked as allocated by zhalloc() */ +#endif + unqueue_signals(); + return t; + } +} + +/**/ +#ifdef ZSH_HEAP_DEBUG +/* + * Check if heap_id is the identifier of a currently valid heap, + * including any heap buried on the stack, or of permanent memory. + * Return 0 if so, else 1. + * + * This gets confused by use of switch_heaps(). That's because so do I. + */ + +/**/ +mod_export int +memory_validate(Heapid heap_id) +{ + Heap h; + Heapstack hs; + LinkNode node; + + if (heap_id == HEAPID_PERMANENT) + return 0; + + queue_signals(); + for (h = heaps; h; h = h->next) { + if (h->heap_id == heap_id) { + unqueue_signals(); + return 0; + } + for (hs = heaps->sp; hs; hs = hs->next) { + if (hs->heap_id == heap_id) { + unqueue_signals(); + return 0; + } + } + } + + if (heaps_saved) { + for (node = firstnode(heaps_saved); node; incnode(node)) { + for (h = (Heap)getdata(node); h; h = h->next) { + if (h->heap_id == heap_id) { + unqueue_signals(); + return 0; + } + for (hs = heaps->sp; hs; hs = hs->next) { + if (hs->heap_id == heap_id) { + unqueue_signals(); + return 0; + } + } + } + } + } + + unqueue_signals(); + return 1; +} +/**/ +#endif + +/* allocate memory from the current memory pool and clear it */ + +/**/ +mod_export void * +hcalloc(size_t size) +{ + void *ptr; + + ptr = zhalloc(size); + memset(ptr, 0, size); + return ptr; +} + +/* allocate permanent memory */ + +/**/ +mod_export void * +zalloc(size_t size) +{ + void *ptr; + + if (!size) + size = 1; + queue_signals(); + if (!(ptr = (void *) malloc(size))) { + zerr("fatal error: out of memory"); + exit(1); + } + unqueue_signals(); + + return ptr; +} + +/**/ +mod_export void * +zshcalloc(size_t size) +{ + void *ptr = zalloc(size); + if (!size) + size = 1; + memset(ptr, 0, size); + return ptr; +} + +/* This front-end to realloc is used to make sure we have a realloc * + * that conforms to POSIX realloc. Older realloc's can fail if * + * passed a NULL pointer, but POSIX realloc should handle this. A * + * better solution would be for configure to check if realloc is * + * POSIX compliant, but I'm not sure how to do that. */ + +/**/ +mod_export void * +zrealloc(void *ptr, size_t size) +{ + queue_signals(); + if (ptr) { + if (size) { + /* Do normal realloc */ + if (!(ptr = (void *) realloc(ptr, size))) { + zerr("fatal error: out of memory"); + exit(1); + } + unqueue_signals(); + return ptr; + } + else + /* If ptr is not NULL, but size is zero, * + * then object pointed to is freed. */ + free(ptr); + + ptr = NULL; + } else { + /* If ptr is NULL, then behave like malloc */ + if (!(ptr = (void *) malloc(size))) { + zerr("fatal error: out of memory"); + exit(1); + } + } + unqueue_signals(); + + return ptr; +} + +/**/ +#ifdef ZSH_MEM + +/* + Below is a simple segment oriented memory allocator for systems on + which it is better than the system's one. Memory is given in blocks + aligned to an integer multiple of sizeof(union mem_align), which will + probably be 64-bit as it is the longer of zlong or double. Each block is + preceded by a header which contains the length of the data part (in + bytes). In allocated blocks only this field of the structure m_hdr is + senseful. In free blocks the second field (next) is a pointer to the next + free segment on the free list. + + On top of this simple allocator there is a second allocator for small + chunks of data. It should be both faster and less space-consuming than + using the normal segment mechanism for such blocks. + For the first M_NSMALL-1 possible sizes memory is allocated in arrays + that can hold M_SNUM blocks. Each array is stored in one segment of the + main allocator. In these segments the third field of the header structure + (free) contains a pointer to the first free block in the array. The + last field (used) gives the number of already used blocks in the array. + + If the macro name ZSH_MEM_DEBUG is defined, some information about the memory + usage is stored. This information can than be viewed by calling the + builtin `mem' (which is only available if ZSH_MEM_DEBUG is set). + + If ZSH_MEM_WARNING is defined, error messages are printed in case of errors. + + If ZSH_SECURE_FREE is defined, free() checks if the given address is really + one that was returned by malloc(), it ignores it if it wasn't (printing + an error message if ZSH_MEM_WARNING is also defined). +*/ +#if !defined(__hpux) && !defined(DGUX) && !defined(__osf__) +# if defined(_BSD) +# ifndef HAVE_BRK_PROTO + extern int brk _((caddr_t)); +# endif +# ifndef HAVE_SBRK_PROTO + extern caddr_t sbrk _((int)); +# endif +# else +# ifndef HAVE_BRK_PROTO + extern int brk _((void *)); +# endif +# ifndef HAVE_SBRK_PROTO + extern void *sbrk _((int)); +# endif +# endif +#endif + +#if defined(_BSD) && !defined(STDC_HEADERS) +# define FREE_RET_T int +# define FREE_ARG_T char * +# define FREE_DO_RET +# define MALLOC_RET_T char * +# define MALLOC_ARG_T size_t +#else +# define FREE_RET_T void +# define FREE_ARG_T void * +# define MALLOC_RET_T void * +# define MALLOC_ARG_T size_t +#endif + +/* structure for building free list in blocks holding small blocks */ + +struct m_shdr { + struct m_shdr *next; /* next one on free list */ +#ifdef PAD_64_BIT + /* dummy to make this 64-bit aligned */ + struct m_shdr *dummy; +#endif +}; + +struct m_hdr { + zlong len; /* length of memory block */ +#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE) + /* either 1 or 2 zlong's, whichever makes up 64 bits. */ + zlong dummy1; +#endif + struct m_hdr *next; /* if free: next on free list + if block of small blocks: next one with + small blocks of same size*/ + struct m_shdr *free; /* if block of small blocks: free list */ + zlong used; /* if block of small blocks: number of used + blocks */ +#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE) + zlong dummy2; +#endif +}; + + +/* alignment for memory blocks */ + +#define M_ALIGN (sizeof(union mem_align)) + +/* length of memory header, length of first field of memory header and + minimal size of a block left free (if we allocate memory and take a + block from the free list that is larger than needed, it must have at + least M_MIN extra bytes to be splitted; if it has, the rest is put on + the free list) */ + +#define M_HSIZE (sizeof(struct m_hdr)) +#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE) +# define M_ISIZE (2*sizeof(zlong)) +#else +# define M_ISIZE (sizeof(zlong)) +#endif +#define M_MIN (2 * M_ISIZE) + +/* M_FREE is the number of bytes that have to be free before memory is + * given back to the system + * M_KEEP is the number of bytes that will be kept when memory is given + * back; note that this has to be less than M_FREE + * M_ALLOC is the number of extra bytes to request from the system */ + +#define M_FREE 32768 +#define M_KEEP 16384 +#define M_ALLOC M_KEEP + +/* a pointer to the last free block, a pointer to the free list (the blocks + on this list are kept in order - lowest address first) */ + +static struct m_hdr *m_lfree, *m_free; + +/* system's pagesize */ + +static long m_pgsz = 0; + +/* the highest and the lowest valid memory addresses, kept for fast validity + checks in free() and to find out if and when we can give memory back to + the system */ + +static char *m_high, *m_low; + +/* Management of blocks for small blocks: + Such blocks are kept in lists (one list for each of the sizes that are + allocated in such blocks). The lists are stored in the m_small array. + M_SIDX() calculates the index into this array for a given size. M_SNUM + is the size (in small blocks) of such blocks. M_SLEN() calculates the + size of the small blocks held in a memory block, given a pointer to the + header of it. M_SBLEN() gives the size of a memory block that can hold + an array of small blocks, given the size of these small blocks. M_BSLEN() + calculates the size of the small blocks held in a memory block, given the + length of that block (including the header of the memory block. M_NSMALL + is the number of possible block sizes that small blocks should be used + for. */ + + +#define M_SIDX(S) ((S) / M_ISIZE) +#define M_SNUM 128 +#define M_SLEN(M) ((M)->len / M_SNUM) +#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE) +/* Include the dummy in the alignment */ +#define M_SBLEN(S) ((S) * M_SNUM + sizeof(struct m_shdr *) + \ + 2*sizeof(zlong) + sizeof(struct m_hdr *)) +#define M_BSLEN(S) (((S) - sizeof(struct m_shdr *) - \ + 2*sizeof(zlong) - sizeof(struct m_hdr *)) / M_SNUM) +#else +#define M_SBLEN(S) ((S) * M_SNUM + sizeof(struct m_shdr *) + \ + sizeof(zlong) + sizeof(struct m_hdr *)) +#define M_BSLEN(S) (((S) - sizeof(struct m_shdr *) - \ + sizeof(zlong) - sizeof(struct m_hdr *)) / M_SNUM) +#endif +#define M_NSMALL 8 + +static struct m_hdr *m_small[M_NSMALL]; + +#ifdef ZSH_MEM_DEBUG + +static int m_s = 0, m_b = 0; +static int m_m[1025], m_f[1025]; + +static struct m_hdr *m_l; + +#endif /* ZSH_MEM_DEBUG */ + +MALLOC_RET_T +malloc(MALLOC_ARG_T size) +{ + struct m_hdr *m, *mp, *mt; + long n, s, os = 0; +#ifndef USE_MMAP + struct heap *h, *hp, *hf = NULL, *hfp = NULL; +#endif + + /* some systems want malloc to return the highest valid address plus one + if it is called with an argument of zero. + + TODO: really? Suppose we allocate more memory, so + that this is now in bounds, then a more rational application + that thinks it can free() anything it malloc'ed, even + of zero length, calls free for it? Aren't we in big + trouble? Wouldn't it be safer just to allocate some + memory anyway? + + If the above comment is really correct, then at least + we need to check in free() if we're freeing memory + at m_high. + */ + + if (!size) +#if 1 + size = 1; +#else + return (MALLOC_RET_T) m_high; +#endif + + queue_signals(); /* just queue signals rather than handling them */ + + /* first call, get page size */ + + if (!m_pgsz) { + +#ifdef _SC_PAGESIZE + m_pgsz = sysconf(_SC_PAGESIZE); /* SVR4 */ +#else +# ifdef _SC_PAGE_SIZE + m_pgsz = sysconf(_SC_PAGE_SIZE); /* HPUX */ +# else + m_pgsz = getpagesize(); +# endif +#endif + + m_free = m_lfree = NULL; + } + size = (size + M_ALIGN - 1) & ~(M_ALIGN - 1); + + /* Do we need a small block? */ + + if ((s = M_SIDX(size)) && s < M_NSMALL) { + /* yep, find a memory block with free small blocks of the + appropriate size (if we find it in this list, this means that + it has room for at least one more small block) */ + for (mp = NULL, m = m_small[s]; m && !m->free; mp = m, m = m->next); + + if (m) { + /* we found one */ + struct m_shdr *sh = m->free; + + m->free = sh->next; + m->used++; + + /* if all small blocks in this block are allocated, the block is + put at the end of the list blocks with small blocks of this + size (i.e., we try to keep blocks with free blocks at the + beginning of the list, to make the search faster) */ + + if (m->used == M_SNUM && m->next) { + for (mt = m; mt->next; mt = mt->next); + + mt->next = m; + if (mp) + mp->next = m->next; + else + m_small[s] = m->next; + m->next = NULL; + } +#ifdef ZSH_MEM_DEBUG + m_m[size / M_ISIZE]++; +#endif + + unqueue_signals(); + return (MALLOC_RET_T) sh; + } + /* we still want a small block but there were no block with a free + small block of the requested size; so we use the real allocation + routine to allocate a block for small blocks of this size */ + os = size; + size = M_SBLEN(size); + } else + s = 0; + + /* search the free list for an block of at least the requested size */ + for (mp = NULL, m = m_free; m && m->len < size; mp = m, m = m->next); + +#ifndef USE_MMAP + + /* if there is an empty zsh heap at a lower address we steal it and take + the memory from it, putting the rest on the free list (remember + that the blocks on the free list are ordered) */ + + for (hp = NULL, h = heaps; h; hp = h, h = h->next) + if (!h->used && + (!hf || h < hf) && + (!m || ((char *)m) > ((char *)h))) + hf = h, hfp = hp; + + if (hf) { + /* we found such a heap */ + Heapstack hso, hsn; + + /* delete structures on the list holding the heap states */ + for (hso = hf->sp; hso; hso = hsn) { + hsn = hso->next; + zfree(hso, sizeof(*hso)); + } + /* take it from the list of heaps */ + if (hfp) + hfp->next = hf->next; + else + heaps = hf->next; + /* now we simply free it and than search the free list again */ + zfree(hf, HEAPSIZE); + + for (mp = NULL, m = m_free; m && m->len < size; mp = m, m = m->next); + } +#endif + if (!m) { + long nal; + /* no matching free block was found, we have to request new + memory from the system */ + n = (size + M_HSIZE + M_ALLOC + m_pgsz - 1) & ~(m_pgsz - 1); + + if (((char *)(m = (struct m_hdr *)sbrk(n))) == ((char *)-1)) { + DPUTS1(1, "MEM: allocation error at sbrk, size %L.", n); + unqueue_signals(); + return NULL; + } + if ((nal = ((long)(char *)m) & (M_ALIGN-1))) { + if ((char *)sbrk(M_ALIGN - nal) == (char *)-1) { + DPUTS(1, "MEM: allocation error at sbrk."); + unqueue_signals(); + return NULL; + } + m = (struct m_hdr *) ((char *)m + (M_ALIGN - nal)); + } + /* set m_low, for the check in free() */ + if (!m_low) + m_low = (char *)m; + +#ifdef ZSH_MEM_DEBUG + m_s += n; + + if (!m_l) + m_l = m; +#endif + + /* save new highest address */ + m_high = ((char *)m) + n; + + /* initialize header */ + m->len = n - M_ISIZE; + m->next = NULL; + + /* put it on the free list and set m_lfree pointing to it */ + if ((mp = m_lfree)) + m_lfree->next = m; + m_lfree = m; + } + if ((n = m->len - size) > M_MIN) { + /* the block we want to use has more than M_MIN bytes plus the + number of bytes that were requested; we split it in two and + leave the rest on the free list */ + struct m_hdr *mtt = (struct m_hdr *)(((char *)m) + M_ISIZE + size); + + mtt->len = n - M_ISIZE; + mtt->next = m->next; + + m->len = size; + + /* put the rest on the list */ + if (m_lfree == m) + m_lfree = mtt; + + if (mp) + mp->next = mtt; + else + m_free = mtt; + } else if (mp) { + /* the block we found wasn't the first one on the free list */ + if (m == m_lfree) + m_lfree = mp; + mp->next = m->next; + } else { + /* it was the first one */ + m_free = m->next; + if (m == m_lfree) + m_lfree = m_free; + } + + if (s) { + /* we are allocating a block that should hold small blocks */ + struct m_shdr *sh, *shn; + + /* build the free list in this block and set `used' filed */ + m->free = sh = (struct m_shdr *)(((char *)m) + + sizeof(struct m_hdr) + os); + + for (n = M_SNUM - 2; n--; sh = shn) + shn = sh->next = sh + s; + sh->next = NULL; + + m->used = 1; + + /* put the block on the list of blocks holding small blocks if + this size */ + m->next = m_small[s]; + m_small[s] = m; + +#ifdef ZSH_MEM_DEBUG + m_m[os / M_ISIZE]++; +#endif + + unqueue_signals(); + return (MALLOC_RET_T) (((char *)m) + sizeof(struct m_hdr)); + } +#ifdef ZSH_MEM_DEBUG + m_m[m->len < (1024 * M_ISIZE) ? (m->len / M_ISIZE) : 1024]++; +#endif + + unqueue_signals(); + return (MALLOC_RET_T) & m->next; +} + +/* this is an internal free(); the second argument may, but need not hold + the size of the block the first argument is pointing to; if it is the + right size of this block, freeing it will be faster, though; the value + 0 for this parameter means: `don't know' */ + +/**/ +mod_export void +zfree(void *p, int sz) +{ + struct m_hdr *m = (struct m_hdr *)(((char *)p) - M_ISIZE), *mp, *mt = NULL; + int i; +# ifdef DEBUG + int osz = sz; +# endif + +#ifdef ZSH_SECURE_FREE + sz = 0; +#else + sz = (sz + M_ALIGN - 1) & ~(M_ALIGN - 1); +#endif + + if (!p) + return; + + /* first a simple check if the given address is valid */ + if (((char *)p) < m_low || ((char *)p) > m_high || + ((long)p) & (M_ALIGN - 1)) { + DPUTS(1, "BUG: attempt to free storage at invalid address"); + return; + } + + queue_signals(); + + fr_rec: + + if ((i = sz / M_ISIZE) < M_NSMALL || !sz) + /* if the given sizes says that it is a small block, find the + memory block holding it; we search all blocks with blocks + of at least the given size; if the size parameter is zero, + this means, that all blocks are searched */ + for (; i < M_NSMALL; i++) { + for (mp = NULL, mt = m_small[i]; + mt && (((char *)mt) > ((char *)p) || + (((char *)mt) + mt->len) < ((char *)p)); + mp = mt, mt = mt->next); + + if (mt) { + /* we found the block holding the small block */ + struct m_shdr *sh = (struct m_shdr *)p; + +#ifdef ZSH_SECURE_FREE + struct m_shdr *sh2; + + /* check if the given address is equal to the address of + the first small block plus an integer multiple of the + block size */ + if ((((char *)p) - (((char *)mt) + sizeof(struct m_hdr))) % + M_BSLEN(mt->len)) { + + DPUTS(1, "BUG: attempt to free storage at invalid address"); + unqueue_signals(); + return; + } + /* check, if the address is on the (block-intern) free list */ + for (sh2 = mt->free; sh2; sh2 = sh2->next) + if (((char *)p) == ((char *)sh2)) { + + DPUTS(1, "BUG: attempt to free already free storage"); + unqueue_signals(); + return; + } +#endif + DPUTS(M_BSLEN(mt->len) < osz, + "BUG: attempt to free more than allocated."); + +#ifdef ZSH_MEM_DEBUG + m_f[M_BSLEN(mt->len) / M_ISIZE]++; + memset(sh, 0xff, M_BSLEN(mt->len)); +#endif + + /* put the block onto the free list */ + sh->next = mt->free; + mt->free = sh; + + if (--mt->used) { + /* if there are still used blocks in this block, we + put it at the beginning of the list with blocks + holding small blocks of the same size (since we + know that there is at least one free block in it, + this will make allocation of small blocks faster; + it also guarantees that long living memory blocks + are preferred over younger ones */ + if (mp) { + mp->next = mt->next; + mt->next = m_small[i]; + m_small[i] = mt; + } + unqueue_signals(); + return; + } + /* if there are no more used small blocks in this + block, we free the whole block */ + if (mp) + mp->next = mt->next; + else + m_small[i] = mt->next; + + m = mt; + p = (void *) & m->next; + + break; + } else if (sz) { + /* if we didn't find a block and a size was given, try it + again as if no size were given */ + sz = 0; + goto fr_rec; + } + } +#ifdef ZSH_MEM_DEBUG + if (!mt) + m_f[m->len < (1024 * M_ISIZE) ? (m->len / M_ISIZE) : 1024]++; +#endif + +#ifdef ZSH_SECURE_FREE + /* search all memory blocks, if one of them is at the given address */ + for (mt = (struct m_hdr *)m_low; + ((char *)mt) < m_high; + mt = (struct m_hdr *)(((char *)mt) + M_ISIZE + mt->len)) + if (((char *)p) == ((char *)&mt->next)) + break; + + /* no block was found at the given address */ + if (((char *)mt) >= m_high) { + DPUTS(1, "BUG: attempt to free storage at invalid address"); + unqueue_signals(); + return; + } +#endif + + /* see if the block is on the free list */ + for (mp = NULL, mt = m_free; mt && mt < m; mp = mt, mt = mt->next); + + if (m == mt) { + /* it is, ouch! */ + DPUTS(1, "BUG: attempt to free already free storage"); + unqueue_signals(); + return; + } + DPUTS(m->len < osz, "BUG: attempt to free more than allocated"); +#ifdef ZSH_MEM_DEBUG + memset(p, 0xff, m->len); +#endif + if (mt && ((char *)mt) == (((char *)m) + M_ISIZE + m->len)) { + /* the block after the one we are freeing is free, we put them + together */ + m->len += mt->len + M_ISIZE; + m->next = mt->next; + + if (mt == m_lfree) + m_lfree = m; + } else + m->next = mt; + + if (mp && ((char *)m) == (((char *)mp) + M_ISIZE + mp->len)) { + /* the block before the one we are freeing is free, we put them + together */ + mp->len += m->len + M_ISIZE; + mp->next = m->next; + + if (m == m_lfree) + m_lfree = mp; + } else if (mp) + /* otherwise, we just put it on the free list */ + mp->next = m; + else { + m_free = m; + if (!m_lfree) + m_lfree = m_free; + } + + /* if the block we have just freed was at the end of the process heap + and now there is more than one page size of memory, we can give + it back to the system (and we do it ;-) */ + if ((((char *)m_lfree) + M_ISIZE + m_lfree->len) == m_high && + m_lfree->len >= m_pgsz + M_MIN + M_FREE) { + long n = (m_lfree->len - M_MIN - M_KEEP) & ~(m_pgsz - 1); + + m_lfree->len -= n; +#ifdef HAVE_BRK + if (brk(m_high -= n) == -1) { +#else + m_high -= n; + if (sbrk(-n) == (void *)-1) { +#endif /* HAVE_BRK */ + DPUTS(1, "MEM: allocation error at brk."); + } + +#ifdef ZSH_MEM_DEBUG + m_b += n; +#endif + } + unqueue_signals(); +} + +FREE_RET_T +free(FREE_ARG_T p) +{ + zfree(p, 0); /* 0 means: size is unknown */ + +#ifdef FREE_DO_RET + return 0; +#endif +} + +/* this one is for strings (and only strings, real strings, real C strings, + those that have a zero byte at the end) */ + +/**/ +mod_export void +zsfree(char *p) +{ + if (p) + zfree(p, strlen(p) + 1); +} + +MALLOC_RET_T +realloc(MALLOC_RET_T p, MALLOC_ARG_T size) +{ + struct m_hdr *m = (struct m_hdr *)(((char *)p) - M_ISIZE), *mt; + char *r; + int i, l = 0; + + /* some system..., see above */ + if (!p && size) { + queue_signals(); + r = malloc(size); + unqueue_signals(); + return (MALLOC_RET_T) r; + } + + /* and some systems even do this... */ + if (!p || !size) + return (MALLOC_RET_T) p; + + queue_signals(); /* just queue signals caught rather than handling them */ + + /* check if we are reallocating a small block, if we do, we have + to compute the size of the block from the sort of block it is in */ + for (i = 0; i < M_NSMALL; i++) { + for (mt = m_small[i]; + mt && (((char *)mt) > ((char *)p) || + (((char *)mt) + mt->len) < ((char *)p)); + mt = mt->next); + + if (mt) { + l = M_BSLEN(mt->len); + break; + } + } + if (!l) + /* otherwise the size of the block is in the memory just before + the given address */ + l = m->len; + + /* now allocate the new block, copy the old contents, and free the + old block */ + r = malloc(size); + memcpy(r, (char *)p, (size > l) ? l : size); + free(p); + + unqueue_signals(); + return (MALLOC_RET_T) r; +} + +MALLOC_RET_T +calloc(MALLOC_ARG_T n, MALLOC_ARG_T size) +{ + long l; + char *r; + + if (!(l = n * size)) + return (MALLOC_RET_T) m_high; + + /* + * use realloc() (with a NULL `p` argument it behaves exactly the same + * as malloc() does) to prevent an infinite loop caused by sibling-call + * optimizations (the malloc() call would otherwise be replaced by an + * unconditional branch back to line 1719 ad infinitum). + */ + r = realloc(NULL, l); + + memset(r, 0, l); + + return (MALLOC_RET_T) r; +} + +#ifdef ZSH_MEM_DEBUG + +/**/ +int +bin_mem(char *name, char **argv, Options ops, int func) +{ + int i, ii, fi, ui, j; + struct m_hdr *m, *mf, *ms; + char *b, *c, buf[40]; + long u = 0, f = 0, to, cu; + + queue_signals(); + if (OPT_ISSET(ops,'v')) { + printf("The lower and the upper addresses of the heap. Diff gives\n"); + printf("the difference between them, i.e. the size of the heap.\n\n"); + } + printf("low mem %ld\t high mem %ld\t diff %ld\n", + (long)m_l, (long)m_high, (long)(m_high - ((char *)m_l))); + + if (OPT_ISSET(ops,'v')) { + printf("\nThe number of bytes that were allocated using sbrk() and\n"); + printf("the number of bytes that were given back to the system\n"); + printf("via brk().\n"); + } + printf("\nsbrk %d\tbrk %d\n", m_s, m_b); + + if (OPT_ISSET(ops,'v')) { + printf("\nInformation about the sizes that were allocated or freed.\n"); + printf("For each size that were used the number of mallocs and\n"); + printf("frees is shown. Diff gives the difference between these\n"); + printf("values, i.e. the number of blocks of that size that is\n"); + printf("currently allocated. Total is the product of size and diff,\n"); + printf("i.e. the number of bytes that are allocated for blocks of\n"); + printf("this size. The last field gives the accumulated number of\n"); + printf("bytes for all sizes.\n"); + } + printf("\nsize\tmalloc\tfree\tdiff\ttotal\tcum\n"); + for (i = 0, cu = 0; i < 1024; i++) + if (m_m[i] || m_f[i]) { + to = (long) i * M_ISIZE * (m_m[i] - m_f[i]); + printf("%ld\t%d\t%d\t%d\t%ld\t%ld\n", + (long)i * M_ISIZE, m_m[i], m_f[i], m_m[i] - m_f[i], + to, (cu += to)); + } + + if (m_m[i] || m_f[i]) + printf("big\t%d\t%d\t%d\n", m_m[i], m_f[i], m_m[i] - m_f[i]); + + if (OPT_ISSET(ops,'v')) { + printf("\nThe list of memory blocks. For each block the following\n"); + printf("information is shown:\n\n"); + printf("num\tthe number of this block\n"); + printf("tnum\tlike num but counted separately for used and free\n"); + printf("\tblocks\n"); + printf("addr\tthe address of this block\n"); + printf("len\tthe length of the block\n"); + printf("state\tthe state of this block, this can be:\n"); + printf("\t used\tthis block is used for one big block\n"); + printf("\t free\tthis block is free\n"); + printf("\t small\tthis block is used for an array of small blocks\n"); + printf("cum\tthe accumulated sizes of the blocks, counted\n"); + printf("\tseparately for used and free blocks\n"); + printf("\nFor blocks holding small blocks the number of free\n"); + printf("blocks, the number of used blocks and the size of the\n"); + printf("blocks is shown. For otherwise used blocks the first few\n"); + printf("bytes are shown as an ASCII dump.\n"); + } + printf("\nblock list:\nnum\ttnum\taddr\t\tlen\tstate\tcum\n"); + for (m = m_l, mf = m_free, ii = fi = ui = 1; ((char *)m) < m_high; + m = (struct m_hdr *)(((char *)m) + M_ISIZE + m->len), ii++) { + for (j = 0, ms = NULL; j < M_NSMALL && !ms; j++) + for (ms = m_small[j]; ms; ms = ms->next) + if (ms == m) + break; + + if (m == mf) + buf[0] = '\0'; + else if (m == ms) + sprintf(buf, "%ld %ld %ld", (long)(M_SNUM - ms->used), + (long)ms->used, + (long)(m->len - sizeof(struct m_hdr)) / M_SNUM + 1); + + else { + for (i = 0, b = buf, c = (char *)&m->next; i < 20 && i < m->len; + i++, c++) + *b++ = (*c >= ' ' && *c < 127) ? *c : '.'; + *b = '\0'; + } + + printf("%d\t%d\t%ld\t%ld\t%s\t%ld\t%s\n", ii, + (m == mf) ? fi++ : ui++, + (long)m, (long)m->len, + (m == mf) ? "free" : ((m == ms) ? "small" : "used"), + (m == mf) ? (f += m->len) : (u += m->len), + buf); + + if (m == mf) + mf = mf->next; + } + + if (OPT_ISSET(ops,'v')) { + printf("\nHere is some information about the small blocks used.\n"); + printf("For each size the arrays with the number of free and the\n"); + printf("number of used blocks are shown.\n"); + } + printf("\nsmall blocks:\nsize\tblocks (free/used)\n"); + + for (i = 0; i < M_NSMALL; i++) + if (m_small[i]) { + printf("%ld\t", (long)i * M_ISIZE); + + for (ii = 0, m = m_small[i]; m; m = m->next) { + printf("(%ld/%ld) ", (long)(M_SNUM - m->used), + (long)m->used); + if (!((++ii) & 7)) + printf("\n\t"); + } + putchar('\n'); + } + if (OPT_ISSET(ops,'v')) { + printf("\n\nBelow is some information about the allocation\n"); + printf("behaviour of the zsh heaps. First the number of times\n"); + printf("pushheap(), popheap(), and freeheap() were called.\n"); + } + printf("\nzsh heaps:\n\n"); + + printf("push %d\tpop %d\tfree %d\n\n", h_push, h_pop, h_free); + + if (OPT_ISSET(ops,'v')) { + printf("\nThe next list shows for several sizes the number of times\n"); + printf("memory of this size were taken from heaps.\n\n"); + } + printf("size\tmalloc\ttotal\n"); + for (i = 0; i < 1024; i++) + if (h_m[i]) + printf("%ld\t%d\t%ld\n", (long)i * H_ISIZE, h_m[i], + (long)i * H_ISIZE * h_m[i]); + if (h_m[1024]) + printf("big\t%d\n", h_m[1024]); + + unqueue_signals(); + return 0; +} + +#endif + +/**/ +#else /* not ZSH_MEM */ + +/**/ +mod_export void +zfree(void *p, UNUSED(int sz)) +{ + free(p); +} + +/**/ +mod_export void +zsfree(char *p) +{ + free(p); +} + +/**/ +#endif diff --git a/dotfiles/system/.zsh/modules/Src/mkbltnmlst.sh b/dotfiles/system/.zsh/modules/Src/mkbltnmlst.sh new file mode 100644 index 0000000..c4611d8 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/mkbltnmlst.sh @@ -0,0 +1,116 @@ +#! /bin/sh +# +# mkbltnmlst.sh: generate boot code for linked-in modules +# +# Written by Andrew Main +# + +srcdir=${srcdir-`echo $0|sed 's%/[^/][^/]*$%%'`} +test "x$srcdir" = "x$0" && srcdir=. +test "x$srcdir" = "x" && srcdir=. +CFMOD=${CFMOD-$srcdir/../config.modules} + +bin_mods="`grep ' link=static' $CFMOD | sed -e '/^#/d' \ +-e 's/ .*/ /' -e 's/^name=/ /'`" + +x_mods="`grep ' load=yes' $CFMOD | sed -e '/^#/d' -e '/ link=no/d' \ +-e 's/ .*/ /' -e 's/^name=/ /'`" + +trap "rm -f $1; exit 1" 1 2 15 + +exec > $1 + +for x_mod in $x_mods; do + modfile="`grep '^name='$x_mod' ' $CFMOD | sed -e 's/^.* modfile=//' \ + -e 's/ .*//'`" + if test "x$modfile" = x; then + echo >&2 "WARNING: no name for \`$x_mod' in $CFMOD (ignored)" + continue + fi + case "$bin_mods" in + *" $x_mod "*) + echo "/* linked-in known module \`$x_mod' */" + linked=yes + ;; + *) + echo "#ifdef DYNAMIC" + echo "/* non-linked-in known module \`$x_mod' */" + linked=no + esac + unset moddeps autofeatures autofeatures_emu + . $srcdir/../$modfile + if test "x$autofeatures" != x; then + if test "x$autofeatures_emu" != x; then + echo " {" + echo " char *zsh_features[] = { " + for feature in $autofeatures; do + echo " \"$feature\"," + done + echo " NULL" + echo " }; " + echo " char *emu_features[] = { " + for feature in $autofeatures_emu; do + echo " \"$feature\"," + done + echo " NULL" + echo " }; " + echo " autofeatures(\"zsh\", \"$x_mod\"," + echo " EMULATION(EMULATE_ZSH) ? zsh_features : emu_features," + echo " 0, 1);" + echo " }" + else + echo " if (EMULATION(EMULATE_ZSH)) {" + echo " char *features[] = { " + for feature in $autofeatures; do + echo " \"$feature\"," + done + echo " NULL" + echo " }; " + echo " autofeatures(\"zsh\", \"$x_mod\", features, 0, 1);" + echo " }" + fi + fi + for dep in $moddeps; do + echo " add_dep(\"$x_mod\", \"$dep\");" + done + test "x$linked" = xno && echo "#endif" +done + +echo +done_mods=" " +for bin_mod in $bin_mods; do + q_bin_mod=`echo $bin_mod | sed 's,Q,Qq,g;s,_,Qu,g;s,/,Qs,g'` + modfile="`grep '^name='$bin_mod' ' $CFMOD | sed -e 's/^.* modfile=//' \ + -e 's/ .*//'`" + echo "/* linked-in module \`$bin_mod' */" + unset moddeps + . $srcdir/../$modfile + for dep in $moddeps; do + # This assumes there are no circular dependencies in the builtin + # modules. Better ordering of config.modules would be necessary + # to enforce stricter dependency checking. + case $bin_mods in + *" $dep "*) + echo " /* depends on \`$dep' */" ;; + *) echo >&2 "ERROR: linked-in module \`$bin_mod' depends on \`$dep'" + rm -f $1 + exit 1 ;; + esac + done + echo " {" + echo " extern int setup_${q_bin_mod} _((Module));" + echo " extern int boot_${q_bin_mod} _((Module));" + echo " extern int features_${q_bin_mod} _((Module,char***));" + echo " extern int enables_${q_bin_mod} _((Module,int**));" + echo " extern int cleanup_${q_bin_mod} _((Module));" + echo " extern int finish_${q_bin_mod} _((Module));" + echo + echo " register_module(\"$bin_mod\"," + echo " setup_${q_bin_mod}," + echo " features_${q_bin_mod}," + echo " enables_${q_bin_mod}," + echo " boot_${q_bin_mod}," + echo " cleanup_${q_bin_mod}, finish_${q_bin_mod});" + echo " }" + done_mods="$done_mods$bin_mod " +done diff --git a/dotfiles/system/.zsh/modules/Src/mkmakemod.sh b/dotfiles/system/.zsh/modules/Src/mkmakemod.sh new file mode 100644 index 0000000..140bf70 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/mkmakemod.sh @@ -0,0 +1,468 @@ +#!/bin/sh +# +# mkmakemod.sh: generate Makefile.in files for module building +# +# Options: +# -m = file is already generated; only build the second stage +# -i = do not build second stage +# +# Args: +# $1 = subdirectory to look in, relative to $top_srcdir +# $2 = final output filename, within the $1 directory +# +# This script must be run from the top-level build directory, and $top_srcdir +# must be set correctly in the environment. +# +# This looks in $1, and uses all the *.mdd files there. Each .mdd file +# defines one module. The .mdd file is actually a shell script, which will +# be sourced. It may define the following shell variables: +# +# name name of this module +# moddeps modules on which this module depends (default none) +# nozshdep non-empty indicates no dependence on the `zsh/main' pseudo-module +# alwayslink if non-empty, always link the module into the executable +# autofeatures features defined by the module, for autoloading +# autofeatures_emu As autofeatures, but for non-zsh emulation modes +# objects .o files making up this module (*must* be defined) +# proto .syms files for this module (default generated from $objects) +# headers extra headers for this module (default none) +# hdrdeps extra headers on which the .mdh depends (default none) +# otherincs extra headers that are included indirectly (default none) +# +# The .mdd file may also include a Makefile.in fragment between lines +# `:<<\Make' and `Make' -- this will be copied into Makemod.in. +# +# The resulting Makemod.in knows how to build each module that is defined. +# For each module it also knows how to build a .mdh file. Each source file +# should #include the .mdh file for the module it is a part of. The .mdh +# file #includes the .mdh files for any module dependencies, then each of +# $headers, and then each .epro (for global declarations). It will +# be recreated if any of the dependency .mdh files changes, or if any of +# $headers or $hdrdeps changes. When anything depends on it, all the .epros +# and $otherincs will be made up to date, but the .mdh file won't actually +# be rebuilt if those files change. +# +# The order of sections of the output file is thus: +# simple generated macros +# macros generated from *.mdd +# included Makemod.in.in +# rules generated from *.mdd +# The order dependencies are basically that the generated macros are required +# in Makemod.in.in, but some of the macros that it creates are needed in the +# later rules. +# + +# sed script to normalise a pathname +sed_normalise=' + s,^,/, + s,$,/, + :1 + s,/\./,/, + t1 + :2 + s,/[^/.][^/]*/\.\./,/, + s,/\.[^/.][^/]*/\.\./,/, + s,/\.\.[^/][^/]*/\.\./,/, + t2 + s,^/$,., + s,^/,, + s,\(.\)/$,\1, +' + +# decide which stages to process +first_stage=true +second_stage=true +if test ."$1" = .-m; then + shift + first_stage=false +elif test ."$1" = .-i; then + shift + second_stage=false +fi + +top_srcdir=`echo $top_srcdir | sed "$sed_normalise"` +the_subdir=$1 +the_makefile=$2 + +if $first_stage; then + + dir_top=`echo $the_subdir | sed 's,[^/][^/]*,..,g'` + + trap "rm -f $the_subdir/${the_makefile}.in; exit 1" 1 2 15 + echo "creating $the_subdir/${the_makefile}.in" + exec 3>&1 >$the_subdir/${the_makefile}.in + echo "##### ${the_makefile}.in generated automatically by mkmakemod.sh" + echo "##### DO NOT EDIT!" + echo + echo "##### ===== DEFINITIONS ===== #####" + echo + echo "makefile = ${the_makefile}" + echo "dir_top = ${dir_top}" + echo "subdir = ${the_subdir}" + echo + + bin_mods=`grep link=static ./config.modules | \ + sed -e '/^#/d' -e 's/ .*/ /' -e 's/^name=/ /'` + dyn_mods="`grep link=dynamic ./config.modules | \ + sed -e '/^#/d' -e 's/ .*/ /' -e 's/^name=/ /'`" + module_list="${bin_mods}${dyn_mods}" + + if grep '^#define DYNAMIC ' config.h >/dev/null; then + is_dynamic=true + else + is_dynamic=false + fi + + here_mddnames= + all_subdirs= + all_modobjs= + all_modules= + all_mdds= + all_mdhs= + all_proto= + lastsub=// + for module in $module_list; do + modfile="`grep '^name='$module' ' ./config.modules | \ + sed -e 's/^.* modfile=//' -e 's/ .*//'`" + case $modfile in + $the_subdir/$lastsub/*) ;; + $the_subdir/*/*) + lastsub=`echo $modfile | sed 's,^'$the_subdir'/,,;s,/[^/]*$,,'` + case "$all_subdirs " in + *" $lastsub "* ) ;; + * ) + all_subdirs="$all_subdirs $lastsub" + ;; + esac + ;; + $the_subdir/*) + mddname=`echo $modfile | sed 's,^.*/,,;s,\.mdd$,,'` + here_mddnames="$here_mddnames $mddname" + build=$is_dynamic + case $is_dynamic@$bin_mods in + *" $module "*) + build=true + all_modobjs="$all_modobjs modobjs.${mddname}" ;; + true@*) + all_modules="$all_modules ${mddname}.\$(DL_EXT)" ;; + esac + all_mdds="$all_mdds ${mddname}.mdd" + $build && all_mdhs="$all_mdhs ${mddname}.mdh" + $build && all_proto="$all_proto proto.${mddname}" + ;; + esac + done + echo "MODOBJS =$all_modobjs" + echo "MODULES =$all_modules" + echo "MDDS =$all_mdds" + echo "MDHS =$all_mdhs" + echo "PROTOS =$all_proto" + echo "SUBDIRS =$all_subdirs" + echo + echo "ENTRYOBJ = \$(dir_src)/modentry..o" + echo "NNTRYOBJ =" + echo "ENTRYOPT = -emodentry" + echo "NNTRYOPT =" + echo + + echo "##### ===== INCLUDING Makemod.in.in ===== #####" + echo + cat $top_srcdir/Src/Makemod.in.in + echo + + case $the_subdir in + Src) modobjs_sed= ;; + Src/*) modobjs_sed="| sed 's\" \" "`echo $the_subdir | sed 's,^Src/,,'`"/\"g' " ;; + *) modobjs_sed="| sed 's\" \" ../$the_subdir/\"g' " ;; + esac + + other_mdhs= + remote_mdhs= + other_exports= + remote_exports= + other_modules= + remote_modules= + for mddname in $here_mddnames; do + + unset name moddeps nozshdep alwayslink hasexport + unset autofeatures autofeatures_emu + unset objects proto headers hdrdeps otherincs + . $top_srcdir/$the_subdir/${mddname}.mdd + q_name=`echo $name | sed 's,Q,Qq,g;s,_,Qu,g;s,/,Qs,g'` + test -n "${moddeps+set}" || moddeps= + test -n "$nozshdep" || moddeps="$moddeps zsh/main" + test -n "${proto+set}" || + proto=`echo $objects '' | sed 's,\.o ,.syms ,g'` + + dobjects=`echo $objects '' | sed 's,\.o ,..o ,g'` + modhdeps= + mododeps= + exportdeps= + imports= + q_moddeps= + for dep in $moddeps; do + depfile="`grep '^name='$dep' ' ./config.modules | \ + sed -e 's/^.* modfile=//' -e 's/ .*//'`" + q_dep=`echo $dep | sed 's,Q,Qq,g;s,_,Qu,g;s,/,Qs,g'` + q_moddeps="$q_moddeps $q_dep" + eval `echo $depfile | sed 's,/\([^/]*\)\.mdd$,;depbase=\1,;s,^,loc=,'` + case "$binmod" in + *" $dep "* ) + dep=zsh/main + ;; + esac + + case $the_subdir in + $loc) + mdh="${depbase}.mdh" + export="${depbase}.export" + case "$dep" in + zsh/main ) + mdll="\$(dir_top)/Src/libzsh-\$(VERSION).\$(DL_EXT) " + ;; + * ) + mdll="${depbase}.\$(DL_EXT) " + ;; + esac + ;; + $loc/*) + mdh="\$(dir_top)/$loc/${depbase}.mdh" + case "$other_mdhs " in + *" $mdh "*) ;; + *) other_mdhs="$other_mdhs $mdh" ;; + esac + export="\$(dir_top)/$loc/${depbase}.export" + case "$other_exports " in + *" $export "*) ;; + *) other_exports="$other_exports $export" ;; + esac + case "$dep" in + zsh/main ) + mdll="\$(dir_top)/Src/libzsh-\$(VERSION).\$(DL_EXT) " + ;; + * ) + mdll="\$(dir_top)/$loc/${depbase}.\$(DL_EXT) " + ;; + esac + case "$other_modules " in + *" $mdll "*) ;; + *) other_modules="$other_modules $mdll" ;; + esac + ;; + *) + mdh="\$(dir_top)/$loc/${depbase}.mdh" + case "$remote_mdhs " in + *" $mdh "*) ;; + *) remote_mdhs="$remote_mdhs $mdh" ;; + esac + export="\$(dir_top)/$loc/${depbase}.export" + case "$remote_exports " in + *" $export "*) ;; + *) remote_exports="$remote_exports $export" ;; + esac + case "$dep" in + zsh/main ) + mdll="\$(dir_top)/Src/libzsh-\$(VERSION).\$(DL_EXT) " + ;; + * ) + mdll="\$(dir_top)/$loc/${depbase}.\$(DL_EXT) " + ;; + esac + case "$remote_modules " in + *" $mdll "*) ;; + *) remote_modules="$remote_modules $mdll" ;; + esac + ;; + esac + modhdeps="$modhdeps $mdh" + exportdeps="$exportdeps $export" + imports="$imports \$(IMPOPT)$export" + case "$mododeps " in + *" $mdll "* ) + : + ;; + * ) + mododeps="$mododeps $mdll" + ;; + esac + done + + echo "##### ===== DEPENDENCIES GENERATED FROM ${mddname}.mdd ===== #####" + echo + echo "MODOBJS_${mddname} = $objects" + echo "MODDOBJS_${mddname} = $dobjects \$(@E@NTRYOBJ)" + echo "SYMS_${mddname} = $proto" + echo "EPRO_${mddname} = "`echo $proto '' | sed 's,\.syms ,.epro ,g'` + echo "INCS_${mddname} = \$(EPRO_${mddname}) $otherincs" + echo "EXPIMP_${mddname} = $imports \$(EXPOPT)$mddname.export" + echo "NXPIMP_${mddname} =" + echo "LINKMODS_${mddname} = $mododeps" + echo "NOLINKMODS_${mddname} = " + echo + echo "proto.${mddname}: \$(EPRO_${mddname})" + echo "\$(SYMS_${mddname}): \$(PROTODEPS)" + echo + echo "${mddname}.export: \$(SYMS_${mddname})" + echo " @( echo '#!'; cat \$(SYMS_${mddname}) | sed -n '/^X/{s/^X//;p;}' | sort -u ) > \$@" + echo + echo "modobjs.${mddname}: \$(MODOBJS_${mddname})" + echo " @echo '' \$(MODOBJS_${mddname}) $modobjs_sed>> \$(dir_src)/stamp-modobjs.tmp" + echo + if test -z "$alwayslink"; then + case " $all_modules" in *" ${mddname}."*) + echo "install.modules-here: install.modules.${mddname}" + echo "uninstall.modules-here: uninstall.modules.${mddname}" + echo + ;; esac + instsubdir=`echo $name | sed 's,^,/,;s,/[^/]*$,,'` + echo "install.modules.${mddname}: ${mddname}.\$(DL_EXT)" + echo " \$(SHELL) \$(sdir_top)/mkinstalldirs \$(DESTDIR)\$(MODDIR)${instsubdir}" + echo " \$(INSTALL_PROGRAM) \$(STRIPFLAGS) ${mddname}.\$(DL_EXT) \$(DESTDIR)\$(MODDIR)/${name}.\$(DL_EXT)" + echo + echo "uninstall.modules.${mddname}:" + echo " rm -f \$(DESTDIR)\$(MODDIR)/${name}.\$(DL_EXT)" + echo + echo "${mddname}.\$(DL_EXT): \$(MODDOBJS_${mddname}) ${mddname}.export $exportdeps \$(@LINKMODS@_${mddname})" + echo ' rm -f $@' + echo " \$(DLLINK) \$(@E@XPIMP_$mddname) \$(@E@NTRYOPT) \$(MODDOBJS_${mddname}) \$(@LINKMODS@_${mddname}) \$(LIBS) " + echo + fi + echo "${mddname}.mdhi: ${mddname}.mdhs \$(INCS_${mddname})" + echo " @test -f \$@ || echo 'do not delete this file' > \$@" + echo + echo "${mddname}.mdhs: ${mddname}.mdd" + echo " @\$(MAKE) -f \$(makefile) \$(MAKEDEFS) ${mddname}.mdh.tmp" + echo " @if cmp -s ${mddname}.mdh ${mddname}.mdh.tmp; then \\" + echo " rm -f ${mddname}.mdh.tmp; \\" + echo " echo \"\\\`${mddname}.mdh' is up to date.\"; \\" + echo " else \\" + echo " mv -f ${mddname}.mdh.tmp ${mddname}.mdh; \\" + echo " echo \"Updated \\\`${mddname}.mdh'.\"; \\" + echo " fi" + echo " echo 'timestamp for ${mddname}.mdh against ${mddname}.mdd' > \$@" + echo + echo "${mddname}.mdh: ${modhdeps} ${headers} ${hdrdeps} ${mddname}.mdhi" + echo " @\$(MAKE) -f \$(makefile) \$(MAKEDEFS) ${mddname}.mdh.tmp" + echo " @mv -f ${mddname}.mdh.tmp ${mddname}.mdh" + echo " @echo \"Updated \\\`${mddname}.mdh'.\"" + echo + echo "${mddname}.mdh.tmp:" + echo " @( \\" + echo " echo '#ifndef have_${q_name}_module'; \\" + echo " echo '#define have_${q_name}_module'; \\" + echo " echo; \\" + echo " echo '# ifndef IMPORTING_MODULE_${q_name}'; \\" + echo " if test @SHORTBOOTNAMES@ = yes; then \\" + echo " echo '# ifndef MODULE'; \\" + echo " fi; \\" + echo " echo '# define boot_ boot_${q_name}'; \\" + echo " echo '# define cleanup_ cleanup_${q_name}'; \\" + echo " echo '# define features_ features_${q_name}'; \\" + echo " echo '# define enables_ enables_${q_name}'; \\" + echo " echo '# define setup_ setup_${q_name}'; \\" + echo " echo '# define finish_ finish_${q_name}'; \\" + echo " if test @SHORTBOOTNAMES@ = yes; then \\" + echo " echo '# endif /* !MODULE */'; \\" + echo " fi; \\" + echo " echo '# endif /* !IMPORTING_MODULE_${q_name} */'; \\" + echo " echo; \\" + if test -n "$moddeps"; then ( + set x $q_moddeps + echo " echo '/* Module dependencies */'; \\" + for hdep in $modhdeps; do + shift + echo " echo '# define IMPORTING_MODULE_${1} 1'; \\" + echo " echo '# include \"${hdep}\"'; \\" + done + echo " echo; \\" + ) fi + if test -n "$headers"; then + echo " echo '/* Extra headers for this module */'; \\" + echo " for hdr in $headers; do \\" + echo " echo '# include \"'\$\$hdr'\"'; \\" + echo " done; \\" + echo " echo; \\" + fi + if test -n "$proto"; then + echo " echo '# undef mod_import_variable'; \\" + echo " echo '# undef mod_import_function'; \\" + echo " echo '# if defined(IMPORTING_MODULE_${q_name}) && defined(MODULE)'; \\" + echo " echo '# define mod_import_variable @MOD_IMPORT_VARIABLE@'; \\" + echo " echo '# define mod_import_function @MOD_IMPORT_FUNCTION@'; \\" + echo " echo '# else'; \\" + echo " echo '# define mod_import_function'; \\" + echo " echo '# define mod_import_variable'; \\" + echo " echo '# endif /* IMPORTING_MODULE_${q_name} && MODULE */'; \\" + echo " for epro in \$(EPRO_${mddname}); do \\" + echo " echo '# include \"'\$\$epro'\"'; \\" + echo " done; \\" + echo " echo '# undef mod_import_variable'; \\" + echo " echo '# define mod_import_variable'; \\" + echo " echo '# undef mod_import_variable'; \\" + echo " echo '# define mod_import_variable'; \\" + echo " echo '# ifndef mod_export'; \\" + echo " echo '# define mod_export @MOD_EXPORT@'; \\" + echo " echo '# endif /* mod_export */'; \\" + echo " echo; \\" + fi + echo " echo '#endif /* !have_${q_name}_module */'; \\" + echo " ) > \$@" + echo + echo "\$(MODOBJS_${mddname}) \$(MODDOBJS_${mddname}): ${mddname}.mdh" + sed -e '/^ *: *<< *\\Make *$/,/^Make$/!d' \ + -e 's/^ *: *<< *\\Make *$//; /^Make$/d' \ + < $top_srcdir/$the_subdir/${mddname}.mdd + echo + + done + + if test -n "$remote_mdhs$other_mdhs$remote_exports$other_exports$remote_modules$other_modules"; then + echo "##### ===== DEPENDENCIES FOR REMOTE MODULES ===== #####" + echo + for mdh in $remote_mdhs; do + echo "$mdh: FORCE" + echo " @cd @%@ && \$(MAKE) \$(MAKEDEFS) @%@$mdh" + echo + done | sed 's,^\(.*\)@%@\(.*\)@%@\(.*\)/\([^/]*\)$,\1\3\2\4,' + if test -n "$other_mdhs"; then + echo "${other_mdhs}:" | sed 's,^ ,,' + echo " false # A. should only happen with make -n" + echo + fi + for export in $remote_exports; do + echo "$export: FORCE" + echo " @cd @%@ && \$(MAKE) \$(MAKEDEFS) @%@$export" + echo + done | sed 's,^\(.*\)@%@\(.*\)@%@\(.*\)/\([^/]*\)$,\1\3\2\4,' + if test -n "$other_exports"; then + echo "${other_exports}:" | sed 's,^ ,,' + echo " false # B. should only happen with make -n" + echo + fi + for mdll in $remote_modules; do + echo "$mdll: FORCE" + echo " @cd @%@ && \$(MAKE) \$(MAKEDEFS) @%@$mdll" + echo + done | sed 's,^\(.*\)@%@\(.*\)@%@\(.*\)/\([^/]*\)$,\1\3\2\4,' + if test -n "$other_modules"; then + echo "${other_modules}:" | sed 's,^ ,,' + echo " false # C. should only happen with make -n" + echo + fi + fi + + echo "##### End of ${the_makefile}.in" + + exec >&3 3>&- + +fi + +if $second_stage ; then + trap "rm -f $the_subdir/${the_makefile}; exit 1" 1 2 15 + + ${CONFIG_SHELL-/bin/sh} ./config.status \ + --file=$the_subdir/${the_makefile}:$the_subdir/${the_makefile}.in || + exit 1 +fi + +exit 0 diff --git a/dotfiles/system/.zsh/modules/Src/module.c b/dotfiles/system/.zsh/modules/Src/module.c new file mode 100644 index 0000000..4ae7831 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/module.c @@ -0,0 +1,3641 @@ +/* + * module.c - deal with dynamic modules + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1996-1997 Zoltรกn Hidvรฉgi + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Zoltรกn Hidvรฉgi or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Zoltรกn Hidvรฉgi and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Zoltรกn Hidvรฉgi and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Zoltรกn Hidvรฉgi and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + */ + +#include "zsh.mdh" +#include "module.pro" + +/* + * List of linked-in modules. + * This is set up at boot and remains for the life of the shell; + * entries do not appear in "zmodload" listings. + */ + +/**/ +LinkList linkedmodules; + +/* $module_path ($MODULE_PATH) */ + +/**/ +char **module_path; + +/* Hash of modules */ + +/**/ +mod_export HashTable modulestab; + +/* + * Bit flags passed as the "flags" argument of a autofeaturefn_t. + * Used in other places, such as the final argument to + * do_module_features(). + */ +enum { + /* + * `-i' option: ignore errors pertaining to redefinitions, + * or indicate to do_module_features() that it should be + * silent. + */ + FEAT_IGNORE = 0x0001, + /* If a condition, condition is infix rather than prefix */ + FEAT_INFIX = 0x0002, + /* + * Enable all features in the module when autoloading. + * This is the traditional zmodload -a behaviour; + * zmodload -Fa only enables features explicitly marked for + * autoloading. + */ + FEAT_AUTOALL = 0x0004, + /* + * Remove feature: alternative to "-X:NAME" used if + * X is passed separately from NAME. + */ + FEAT_REMOVE = 0x0008, + /* + * For do_module_features(). Check that any autoloads + * for the module are actually provided. + */ + FEAT_CHECKAUTO = 0x0010 +}; + +/* + * All functions to add or remove autoloadable features fit + * the following prototype. + * + * "module" is the name of the module. + * + * "feature" is the name of the feature, minus any type prefix. + * + * "flags" is a set of the bits above. + * + * The return value is 0 for success, -1 for failure with no + * message needed, and one of the following to indicate the calling + * function should print a message: + * + * 1: failed to add [type] `[feature]' + * 2: [feature]: no such [type] + * 3: [feature]: [type] is already defined + */ +typedef int (*autofeaturefn_t)(const char *module, const char *feature, + int flags); + +/* Bits in the second argument to find_module. */ +enum { + /* + * Resolve any aliases to the underlying module. + */ + FINDMOD_ALIASP = 0x0001, + /* + * Create an element for the module in the list if + * it is not found. + */ + FINDMOD_CREATE = 0x0002, +}; + +static void +freemodulenode(HashNode hn) +{ + Module m = (Module) hn; + + if (m->node.flags & MOD_ALIAS) + zsfree(m->u.alias); + zsfree(m->node.nam); + if (m->autoloads) + freelinklist(m->autoloads, freestr); + if (m->deps) + freelinklist(m->deps, freestr); + zfree(m, sizeof(*m)); +} + +/* flags argument to printmodulenode */ +enum { + /* -L flag, output zmodload commands */ + PRINTMOD_LIST = 0x0001, + /* -e flag */ + PRINTMOD_EXIST = 0x0002, + /* -A flag */ + PRINTMOD_ALIAS = 0x0004, + /* -d flag */ + PRINTMOD_DEPS = 0x0008, + /* -F flag */ + PRINTMOD_FEATURES = 0x0010, + /* -l flag in combination with -L flag */ + PRINTMOD_LISTALL = 0x0020, + /* -a flag */ + PRINTMOD_AUTO = 0x0040 +}; + +/* Scan function for printing module details */ + +static void +printmodulenode(HashNode hn, int flags) +{ + Module m = (Module)hn; + /* + * If we check for a module loaded under an alias, we + * need the name of the alias. We can use it in other + * cases, too. + */ + const char *modname = m->node.nam; + + if (flags & PRINTMOD_DEPS) { + /* + * Print the module's dependencies. + */ + LinkNode n; + + if (!m->deps) + return; + + if (flags & PRINTMOD_LIST) { + printf("zmodload -d "); + if (modname[0] == '-') + fputs("-- ", stdout); + quotedzputs(modname, stdout); + } else { + nicezputs(modname, stdout); + putchar(':'); + } + for (n = firstnode(m->deps); n; incnode(n)) { + putchar(' '); + if (flags & PRINTMOD_LIST) + quotedzputs((char *) getdata(n), stdout); + else + nicezputs((char *) getdata(n), stdout); + } + } else if (flags & PRINTMOD_EXIST) { + /* + * Just print the module name, provided the module is + * present under an alias or otherwise. + */ + if (m->node.flags & MOD_ALIAS) { + if (!(flags & PRINTMOD_ALIAS) || + !(m = find_module(m->u.alias, FINDMOD_ALIASP, NULL))) + return; + } + if (!m->u.handle || (m->node.flags & MOD_UNLOAD)) + return; + nicezputs(modname, stdout); + } else if (m->node.flags & MOD_ALIAS) { + /* + * Normal listing, but for aliases. + */ + if (flags & PRINTMOD_LIST) { + printf("zmodload -A "); + if (modname[0] == '-') + fputs("-- ", stdout); + quotedzputs(modname, stdout); + putchar('='); + quotedzputs(m->u.alias, stdout); + } else { + nicezputs(modname, stdout); + fputs(" -> ", stdout); + nicezputs(m->u.alias, stdout); + } + } else if (m->u.handle || (flags & PRINTMOD_AUTO)) { + /* + * Loaded module. + */ + if (flags & PRINTMOD_LIST) { + /* + * List with -L format. Possibly we are printing + * features, either enables or autoloads. + */ + char **features = NULL; + int *enables = NULL; + if (flags & PRINTMOD_AUTO) { + if (!m->autoloads || !firstnode(m->autoloads)) + return; + } else if (flags & PRINTMOD_FEATURES) { + if (features_module(m, &features) || + enables_module(m, &enables) || + !*features) + return; + } + printf("zmodload "); + if (flags & PRINTMOD_AUTO) { + fputs("-Fa ", stdout); + } else if (features) + fputs("-F ", stdout); + if(modname[0] == '-') + fputs("-- ", stdout); + quotedzputs(modname, stdout); + if (flags & PRINTMOD_AUTO) { + LinkNode an; + for (an = firstnode(m->autoloads); an; incnode(an)) { + putchar(' '); + quotedzputs((char *)getdata(an), stdout); + } + } else if (features) { + const char *f; + while ((f = *features++)) { + int on = *enables++; + if (flags & PRINTMOD_LISTALL) + printf(" %s", on ? "+" : "-"); + else if (!on) + continue; + else + putchar(' '); + quotedzputs(f, stdout); + } + } + } else /* -l */ + nicezputs(modname, stdout); + } else + return; + putchar('\n'); +} + +/**/ +HashTable +newmoduletable(int size, char const *name) +{ + HashTable ht; + ht = newhashtable(size, name, NULL); + + ht->hash = hasher; + ht->emptytable = emptyhashtable; + ht->filltable = NULL; + ht->cmpnodes = strcmp; + ht->addnode = addhashnode; + /* DISABLED is not supported */ + ht->getnode = gethashnode2; + ht->getnode2 = gethashnode2; + ht->removenode = removehashnode; + ht->disablenode = NULL; + ht->enablenode = NULL; + ht->freenode = freemodulenode; + ht->printnode = printmodulenode; + + return ht; +} + +/************************************************************************ + * zsh/main standard module functions + ************************************************************************/ + +/* The `zsh/main' module contains all the base code that can't actually be * + * built as a separate module. It is initialised by main(), so there's * + * nothing for the boot function to do. */ + +/**/ +int +setup_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +features_(UNUSED(Module m), UNUSED(char ***features)) +{ + /* + * There are lots and lots of features, but they're not + * handled here. + */ + return 1; +} + +/**/ +int +enables_(UNUSED(Module m), UNUSED(int **enables)) +{ + return 1; +} + +/**/ +int +boot_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +cleanup_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + return 0; +} + + +/************************************************************************ + * Module utility functions + ************************************************************************/ + +/* This registers a builtin module. */ + +/**/ +void +register_module(char *n, Module_void_func setup, + Module_features_func features, + Module_enables_func enables, + Module_void_func boot, + Module_void_func cleanup, + Module_void_func finish) +{ + Linkedmod m; + + m = (Linkedmod) zalloc(sizeof(*m)); + + m->name = ztrdup(n); + m->setup = setup; + m->features = features; + m->enables = enables; + m->boot = boot; + m->cleanup = cleanup; + m->finish = finish; + + zaddlinknode(linkedmodules, m); +} + +/* Check if a module is linked in. */ + +/**/ +Linkedmod +module_linked(char const *name) +{ + LinkNode node; + + for (node = firstnode(linkedmodules); node; incnode(node)) + if (!strcmp(((Linkedmod) getdata(node))->name, name)) + return (Linkedmod) getdata(node); + + return NULL; +} + + +/************************************************************************ + * Support for the various feature types. + * First, builtins. + ************************************************************************/ + +/* addbuiltin() can be used to add a new builtin. It returns zero on * + * success, 1 on failure. The only possible type of failure is that * + * a builtin with the specified name already exists. An autoloaded * + * builtin can be replaced using this function. */ + +/**/ +static int +addbuiltin(Builtin b) +{ + Builtin bn = (Builtin) builtintab->getnode2(builtintab, b->node.nam); + if (bn && (bn->node.flags & BINF_ADDED)) + return 1; + if (bn) + builtintab->freenode(builtintab->removenode(builtintab, b->node.nam)); + builtintab->addnode(builtintab, b->node.nam, b); + return 0; +} + +/* Define an autoloadable builtin. It returns 0 on success, or 1 on * + * failure. The only possible cause of failure is that a builtin * + * with the specified name already exists. */ + +/**/ +static int +add_autobin(const char *module, const char *bnam, int flags) +{ + Builtin bn; + int ret; + + bn = zshcalloc(sizeof(*bn)); + bn->node.nam = ztrdup(bnam); + bn->optstr = ztrdup(module); + if (flags & FEAT_AUTOALL) + bn->node.flags |= BINF_AUTOALL; + if ((ret = addbuiltin(bn))) { + builtintab->freenode(&bn->node); + if (!(flags & FEAT_IGNORE)) + return 1; + } + return 0; +} + +/* Remove the builtin added previously by addbuiltin(). Returns * + * zero on succes and -1 if there is no builtin with that name. */ + +/**/ +int +deletebuiltin(const char *nam) +{ + Builtin bn; + + bn = (Builtin) builtintab->removenode(builtintab, nam); + if (!bn) + return -1; + builtintab->freenode(&bn->node); + return 0; +} + +/* Remove an autoloaded added by add_autobin */ + +/**/ +static int +del_autobin(UNUSED(const char *module), const char *bnam, int flags) +{ + Builtin bn = (Builtin) builtintab->getnode2(builtintab, bnam); + if (!bn) { + if(!(flags & FEAT_IGNORE)) + return 2; + } else if (bn->node.flags & BINF_ADDED) { + if (!(flags & FEAT_IGNORE)) + return 3; + } else + deletebuiltin(bnam); + + return 0; +} + +/* + * Manipulate a set of builtins. This should be called + * via setfeatureenables() (or, usually, via the next level up, + * handlefeatures()). + * + * "nam" is the name of the calling code builtin, probably "zmodload". + * + * "binl" is the builtin table containing an array of "size" builtins. + * + * "e" is either NULL, in which case all builtins in the + * table are removed, or else an array corresponding to "binl" + * with a 1 for builtins that are to be added and a 0 for builtins + * that are to be removed. Any builtin already in the appropriate + * state is left alone. + * + * Returns 1 on any error, 0 for success. The recommended way + * of handling errors is to compare the enables passed down + * with the set retrieved after the error to find what failed. + */ + +/**/ +static int +setbuiltins(char const *nam, Builtin binl, int size, int *e) +{ + int ret = 0, n; + + for(n = 0; n < size; n++) { + Builtin b = &binl[n]; + if (e && *e++) { + if (b->node.flags & BINF_ADDED) + continue; + if (addbuiltin(b)) { + zwarnnam(nam, + "name clash when adding builtin `%s'", b->node.nam); + ret = 1; + } else { + b->node.flags |= BINF_ADDED; + } + } else { + if (!(b->node.flags & BINF_ADDED)) + continue; + if (deletebuiltin(b->node.nam)) { + zwarnnam(nam, "builtin `%s' already deleted", b->node.nam); + ret = 1; + } else { + b->node.flags &= ~BINF_ADDED; + } + } + } + return ret; +} + +/* + * Add multiple builtins. binl points to a table of `size' builtin + * structures. Those for which (.flags & BINF_ADDED) is false are to be + * added; that flag is set if they succeed. + * + * If any fail, an error message is printed, using nam as the leading name. + * Returns 0 on success, 1 for any failure. + * + * This should not be used from a module; instead, use handlefeatures(). + */ + +/**/ +mod_export int +addbuiltins(char const *nam, Builtin binl, int size) +{ + int ret = 0, n; + + for(n = 0; n < size; n++) { + Builtin b = &binl[n]; + if(b->node.flags & BINF_ADDED) + continue; + if(addbuiltin(b)) { + zwarnnam(nam, "name clash when adding builtin `%s'", b->node.nam); + ret = 1; + } else { + b->node.flags |= BINF_ADDED; + } + } + return ret; +} + + +/************************************************************************ + * Function wrappers. + ************************************************************************/ + +/* The list of function wrappers defined. */ + +/**/ +FuncWrap wrappers; + +/* This adds a definition for a wrapper. Return value is one in case of * + * error and zero if all went fine. */ + +/**/ +mod_export int +addwrapper(Module m, FuncWrap w) +{ + FuncWrap p, q; + + /* + * We can't add a wrapper to an alias, since it's supposed + * to behave identically to the resolved module. This shouldn't + * happen since we usually add wrappers when a real module is + * loaded. + */ + if (m->node.flags & MOD_ALIAS) + return 1; + + if (w->flags & WRAPF_ADDED) + return 1; + for (p = wrappers, q = NULL; p; q = p, p = p->next); + if (q) + q->next = w; + else + wrappers = w; + w->next = NULL; + w->flags |= WRAPF_ADDED; + w->module = m; + + return 0; +} + +/* This removes the given wrapper definition from the list. Returned is * + * one in case of error and zero otherwise. */ + +/**/ +mod_export int +deletewrapper(Module m, FuncWrap w) +{ + FuncWrap p, q; + + if (m->node.flags & MOD_ALIAS) + return 1; + + if (w->flags & WRAPF_ADDED) { + for (p = wrappers, q = NULL; p && p != w; q = p, p = p->next); + + if (p) { + if (q) + q->next = p->next; + else + wrappers = p->next; + p->flags &= ~WRAPF_ADDED; + + return 0; + } + } + return 1; +} + + +/************************************************************************ + * Conditions. + ************************************************************************/ + +/* The list of module-defined conditions. */ + +/**/ +mod_export Conddef condtab; + +/* This gets a condition definition with the given name. The first * + * argument says if we have to look for an infix condition. The last * + * argument is non-zero if we should autoload modules if needed. */ + +/**/ +Conddef +getconddef(int inf, const char *name, int autol) +{ + Conddef p; + int f = 1; + char *lookup, *s; + + /* detokenize the Dash to the form encoded in lookup tables */ + lookup = dupstring(name); + if (!lookup) + return NULL; + for (s = lookup; *s != '\0'; s++) { + if (*s == Dash) + *s = '-'; + } + + do { + for (p = condtab; p; p = p->next) { + if ((!!inf == !!(p->flags & CONDF_INFIX)) && + !strcmp(lookup, p->name)) + break; + } + if (autol && p && p->module) { + /* + * This is a definition for an autoloaded condition; load the + * module if we haven't tried that already. + */ + if (f) { + (void)ensurefeature(p->module, + (p->flags & CONDF_INFIX) ? "C:" : "c:", + (p->flags & CONDF_AUTOALL) ? NULL : lookup); + f = 0; + p = NULL; + } else { + deleteconddef(p); + return NULL; + } + } else + break; + } while (!p); + + return p; +} + +/* + * This adds the given condition definition. The return value is zero on * + * success and 1 on failure. If there is a matching definition for an * + * autoloaded condition, it is removed. + * + * This is used for adding both an autoload definition or + * a real condition. In the latter case the caller is responsible + * for setting the CONDF_ADDED flag. + */ + +/**/ +static int +addconddef(Conddef c) +{ + Conddef p = getconddef((c->flags & CONDF_INFIX), c->name, 0); + + if (p) { + if (!p->module || (p->flags & CONDF_ADDED)) + return 1; + /* There is an autoload definition. */ + + deleteconddef(p); + } + c->next = condtab; + condtab = c; + return 0; +} + +/* This removes the given condition definition from the list(s). If this * + * is a definition for a autoloaded condition, the memory is freed. */ + +/**/ +int +deleteconddef(Conddef c) +{ + Conddef p, q; + + for (p = condtab, q = NULL; p && p != c; q = p, p = p->next); + + if (p) { + if (q) + q->next = p->next; + else + condtab = p->next; + + if (p->module) { + /* autoloaded, free it */ + zsfree(p->name); + zsfree(p->module); + zfree(p, sizeof(*p)); + } + return 0; + } + return -1; +} + +/* + * Add or remove sets of conditions. The interface is + * identical to setbuiltins(). + */ + +/**/ +static int +setconddefs(char const *nam, Conddef c, int size, int *e) +{ + int ret = 0; + + while (size--) { + if (e && *e++) { + if (c->flags & CONDF_ADDED) { + c++; + continue; + } + if (addconddef(c)) { + zwarnnam(nam, "name clash when adding condition `%s'", + c->name); + ret = 1; + } else { + c->flags |= CONDF_ADDED; + } + } else { + if (!(c->flags & CONDF_ADDED)) { + c++; + continue; + } + if (deleteconddef(c)) { + zwarnnam(nam, "condition `%s' already deleted", c->name); + ret = 1; + } else { + c->flags &= ~CONDF_ADDED; + } + } + c++; + } + return ret; +} + +/* This adds a definition for autoloading a module for a condition. */ + +/**/ +static int +add_autocond(const char *module, const char *cnam, int flags) +{ + Conddef c; + + c = (Conddef) zalloc(sizeof(*c)); + + c->name = ztrdup(cnam); + c->flags = ((flags & FEAT_INFIX) ? CONDF_INFIX : 0); + if (flags & FEAT_AUTOALL) + c->flags |= CONDF_AUTOALL; + c->module = ztrdup(module); + + if (addconddef(c)) { + zsfree(c->name); + zsfree(c->module); + zfree(c, sizeof(*c)); + + if (!(flags & FEAT_IGNORE)) + return 1; + } + return 0; +} + +/* Remove a condition added with add_autocond */ + +/**/ +static int +del_autocond(UNUSED(const char *modnam), const char *cnam, int flags) +{ + Conddef cd = getconddef((flags & FEAT_INFIX) ? 1 : 0, cnam, 0); + + if (!cd) { + if (!(flags & FEAT_IGNORE)) { + return 2; + } + } else if (cd->flags & CONDF_ADDED) { + if (!(flags & FEAT_IGNORE)) + return 3; + } else + deleteconddef(cd); + + return 0; +} + +/************************************************************************ + * Hook functions. + ************************************************************************/ + +/* This list of hook functions defined. */ + +/**/ +Hookdef hooktab; + +/* Find a hook definition given the name. */ + +/**/ +Hookdef +gethookdef(char *n) +{ + Hookdef p; + + for (p = hooktab; p; p = p->next) + if (!strcmp(n, p->name)) + return p; + return NULL; +} + +/* This adds the given hook definition. The return value is zero on * + * success and 1 on failure. */ + +/**/ +int +addhookdef(Hookdef h) +{ + if (gethookdef(h->name)) + return 1; + + h->next = hooktab; + hooktab = h; + h->funcs = znewlinklist(); + + return 0; +} + +/* + * This adds multiple hook definitions. This is like addbuiltins(). + * This allows a NULL module because we call it from init.c. + */ + +/**/ +mod_export int +addhookdefs(Module m, Hookdef h, int size) +{ + int ret = 0; + + while (size--) { + if (addhookdef(h)) { + zwarnnam(m ? m->node.nam : NULL, + "name clash when adding hook `%s'", h->name); + ret = 1; + } + h++; + } + return ret; +} + +/* Delete hook definitions. */ + +/**/ +int +deletehookdef(Hookdef h) +{ + Hookdef p, q; + + for (p = hooktab, q = NULL; p && p != h; q = p, p = p->next); + + if (!p) + return 1; + + if (q) + q->next = p->next; + else + hooktab = p->next; + freelinklist(p->funcs, NULL); + return 0; +} + +/* Remove multiple hook definitions. */ + +/**/ +mod_export int +deletehookdefs(UNUSED(Module m), Hookdef h, int size) +{ + int ret = 0; + + while (size--) { + if (deletehookdef(h)) + ret = 1; + h++; + } + return ret; +} + +/* Add a function to a hook. */ + +/**/ +int +addhookdeffunc(Hookdef h, Hookfn f) +{ + zaddlinknode(h->funcs, (void *) f); + + return 0; +} + +/**/ +mod_export int +addhookfunc(char *n, Hookfn f) +{ + Hookdef h = gethookdef(n); + + if (h) + return addhookdeffunc(h, f); + return 1; +} + +/* Delete a function from a hook. */ + +/**/ +int +deletehookdeffunc(Hookdef h, Hookfn f) +{ + LinkNode p; + + for (p = firstnode(h->funcs); p; incnode(p)) + if (f == (Hookfn) getdata(p)) { + remnode(h->funcs, p); + return 0; + } + return 1; +} + +/* Delete a hook. */ + +/**/ +mod_export int +deletehookfunc(char *n, Hookfn f) +{ + Hookdef h = gethookdef(n); + + if (h) + return deletehookdeffunc(h, f); + return 1; +} + +/* Run the function(s) for a hook. */ + +/**/ +mod_export int +runhookdef(Hookdef h, void *d) +{ + if (empty(h->funcs)) { + if (h->def) + return h->def(h, d); + return 0; + } else if (h->flags & HOOKF_ALL) { + LinkNode p; + int r; + + for (p = firstnode(h->funcs); p; incnode(p)) + if ((r = ((Hookfn) getdata(p))(h, d))) + return r; + if (h->def) + return h->def(h, d); + return 0; + } else + return ((Hookfn) getdata(lastnode(h->funcs)))(h, d); +} + + + +/************************************************************************ + * Shell parameters. + ************************************************************************/ + +/* + * Check that it's possible to add a parameter. This + * requires that either there's no parameter already present, + * or it's a global parameter marked for autoloading. + * + * The special status 2 is to indicate it didn't work but + * -i was in use so we didn't print a warning. + */ + +static int +checkaddparam(const char *nam, int opt_i) +{ + Param pm; + + if (!(pm = (Param) gethashnode2(paramtab, nam))) + return 0; + + if (pm->level || !(pm->node.flags & PM_AUTOLOAD)) { + /* + * -i suppresses "it's already that way" warnings, + * but not "this can't possibly work" warnings, so we print + * the message anyway if there's a local parameter blocking + * the parameter we want to add, not if there's a + * non-autoloadable parameter already there. This + * is consistent with the way add_auto* functions work. + */ + if (!opt_i || !pm->level) { + zwarn("Can't add module parameter `%s': %s", + nam, pm->level ? + "local parameter exists" : + "parameter already exists"); + return 1; + } + return 2; + } + + unsetparam_pm(pm, 0, 1); + return 0; +} + +/* This adds the given parameter definition. The return value is zero on * + * success and 1 on failure. */ + +/**/ +int +addparamdef(Paramdef d) +{ + Param pm; + + if (checkaddparam(d->name, 0)) + return 1; + + if (d->getnfn) { + if (!(pm = createspecialhash(d->name, d->getnfn, + d->scantfn, d->flags))) + return 1; + } + else if (!(pm = createparam(d->name, d->flags)) && + !(pm = (Param) paramtab->getnode(paramtab, d->name))) + return 1; + + d->pm = pm; + pm->level = 0; + if (d->var) + pm->u.data = d->var; + if (d->var || d->gsu) { + /* + * If no get/set/unset class, use the appropriate + * variable type, else use the one supplied. + */ + switch (PM_TYPE(pm->node.flags)) { + case PM_SCALAR: + pm->gsu.s = d->gsu ? (GsuScalar)d->gsu : &varscalar_gsu; + break; + + case PM_INTEGER: + pm->gsu.i = d->gsu ? (GsuInteger)d->gsu : &varinteger_gsu; + break; + + case PM_FFLOAT: + case PM_EFLOAT: + pm->gsu.f = d->gsu; + break; + + case PM_ARRAY: + pm->gsu.a = d->gsu ? (GsuArray)d->gsu : &vararray_gsu; + break; + + case PM_HASHED: + /* hashes may behave like standard hashes */ + if (d->gsu) + pm->gsu.h = (GsuHash)d->gsu; + break; + + default: + unsetparam_pm(pm, 0, 1); + return 1; + } + } + + return 0; +} + +/* Delete parameters defined. No error checking yet. */ + +/**/ +int +deleteparamdef(Paramdef d) +{ + Param pm = (Param) paramtab->getnode(paramtab, d->name); + + if (!pm) + return 1; + if (pm != d->pm) { + /* + * See if the parameter has been hidden. If so, + * bring it to the front to unset it. + */ + Param prevpm, searchpm; + for (prevpm = pm, searchpm = pm->old; + searchpm; + prevpm = searchpm, searchpm = searchpm->old) + if (searchpm == d->pm) + break; + + if (!searchpm) + return 1; + + paramtab->removenode(paramtab, pm->node.nam); + prevpm->old = searchpm->old; + searchpm->old = pm; + paramtab->addnode(paramtab, searchpm->node.nam, searchpm); + + pm = searchpm; + } + pm->node.flags = (pm->node.flags & ~PM_READONLY) | PM_REMOVABLE; + unsetparam_pm(pm, 0, 1); + d->pm = NULL; + return 0; +} + +/* + * Add or remove sets of parameters. The interface is + * identical to setbuiltins(). + */ + +/**/ +static int +setparamdefs(char const *nam, Paramdef d, int size, int *e) +{ + int ret = 0; + + while (size--) { + if (e && *e++) { + if (d->pm) { + d++; + continue; + } + if (addparamdef(d)) { + zwarnnam(nam, "error when adding parameter `%s'", d->name); + ret = 1; + } + } else { + if (!d->pm) { + d++; + continue; + } + if (deleteparamdef(d)) { + zwarnnam(nam, "parameter `%s' already deleted", d->name); + ret = 1; + } + } + d++; + } + return ret; +} + +/* This adds a definition for autoloading a module for a parameter. */ + +/**/ +static int +add_autoparam(const char *module, const char *pnam, int flags) +{ + Param pm; + int ret; + + queue_signals(); + if ((ret = checkaddparam(pnam, (flags & FEAT_IGNORE)))) { + unqueue_signals(); + /* + * checkaddparam() has already printed a message if one was + * needed. If it wasn't owing to the presence of -i, ret is 2; + * for consistency with other add_auto* functions we return + * status 0 to indicate there's already such a parameter and + * we've been told not to worry if so. + */ + return ret == 2 ? 0 : -1; + } + + pm = setsparam(dupstring(pnam), ztrdup(module)); + + pm->node.flags |= PM_AUTOLOAD; + if (flags & FEAT_AUTOALL) + pm->node.flags |= PM_AUTOALL; + unqueue_signals(); + + return 0; +} + +/* Remove a parameter added with add_autoparam() */ + +/**/ +static int +del_autoparam(UNUSED(const char *modnam), const char *pnam, int flags) +{ + Param pm = (Param) gethashnode2(paramtab, pnam); + + if (!pm) { + if (!(flags & FEAT_IGNORE)) + return 2; + } else if (!(pm->node.flags & PM_AUTOLOAD)) { + if (!(flags & FEAT_IGNORE)) + return 3; + } else + unsetparam_pm(pm, 0, 1); + + return 0; +} + +/************************************************************************ + * Math functions. + ************************************************************************/ + +/* List of math functions. */ + +/**/ +MathFunc mathfuncs; + +/* + * Remove a single math function form the list (utility function). + * This does not delete a module math function, that's deletemathfunc(). + */ + +/**/ +void +removemathfunc(MathFunc previous, MathFunc current) +{ + if (previous) + previous->next = current->next; + else + mathfuncs = current->next; + + zsfree(current->name); + zsfree(current->module); + zfree(current, sizeof(*current)); +} + +/* Find a math function in the list, handling autoload if necessary. */ + +/**/ +MathFunc +getmathfunc(const char *name, int autol) +{ + MathFunc p, q = NULL; + + for (p = mathfuncs; p; q = p, p = p->next) + if (!strcmp(name, p->name)) { + if (autol && p->module && !(p->flags & MFF_USERFUNC)) { + char *n = dupstring(p->module); + int flags = p->flags; + + removemathfunc(q, p); + + (void)ensurefeature(n, "f:", (flags & MFF_AUTOALL) ? NULL : + name); + + p = getmathfunc(name, 0); + if (!p) { + zerr("autoloading module %s failed to define math function: %s", n, name); + } + } + return p; + } + + return NULL; +} + +/* Add a single math function */ + +/**/ +static int +addmathfunc(MathFunc f) +{ + MathFunc p, q = NULL; + + if (f->flags & MFF_ADDED) + return 1; + + for (p = mathfuncs; p; q = p, p = p->next) + if (!strcmp(f->name, p->name)) { + if (p->module && !(p->flags & MFF_USERFUNC)) { + /* + * Autoloadable, replace. + */ + removemathfunc(q, p); + break; + } + return 1; + } + + f->next = mathfuncs; + mathfuncs = f; + + return 0; +} + +/* Delete a single math function */ + +/**/ +mod_export int +deletemathfunc(MathFunc f) +{ + MathFunc p, q; + + for (p = mathfuncs, q = NULL; p && p != f; q = p, p = p->next); + + if (p) { + if (q) + q->next = f->next; + else + mathfuncs = f->next; + + /* the following applies to both unloaded and user-defined functions */ + if (f->module) { + zsfree(f->name); + zsfree(f->module); + zfree(f, sizeof(*f)); + } else + f->flags &= ~MFF_ADDED; + + return 0; + } + return -1; +} + +/* + * Add or remove sets of math functions. The interface is + * identical to setbuiltins(). + */ + +/**/ +static int +setmathfuncs(char const *nam, MathFunc f, int size, int *e) +{ + int ret = 0; + + while (size--) { + if (e && *e++) { + if (f->flags & MFF_ADDED) { + f++; + continue; + } + if (addmathfunc(f)) { + zwarnnam(nam, "name clash when adding math function `%s'", + f->name); + ret = 1; + } else { + f->flags |= MFF_ADDED; + } + } else { + if (!(f->flags & MFF_ADDED)) { + f++; + continue; + } + if (deletemathfunc(f)) { + zwarnnam(nam, "math function `%s' already deleted", f->name); + ret = 1; + } else { + f->flags &= ~MFF_ADDED; + } + } + f++; + } + return ret; +} + +/* Add an autoload definition for a math function. */ + +/**/ +static int +add_automathfunc(const char *module, const char *fnam, int flags) +{ + MathFunc f; + + f = (MathFunc) zalloc(sizeof(*f)); + + f->name = ztrdup(fnam); + f->module = ztrdup(module); + f->flags = 0; + + if (addmathfunc(f)) { + zsfree(f->name); + zsfree(f->module); + zfree(f, sizeof(*f)); + + if (!(flags & FEAT_IGNORE)) + return 1; + } + + return 0; +} + +/* Remove a math function added with add_automathfunc() */ + +/**/ +static int +del_automathfunc(UNUSED(const char *modnam), const char *fnam, int flags) +{ + MathFunc f = getmathfunc(fnam, 0); + + if (!f) { + if (!(flags & FEAT_IGNORE)) + return 2; + } else if (f->flags & MFF_ADDED) { + if (!(flags & FEAT_IGNORE)) + return 3; + } else + deletemathfunc(f); + + return 0; +} + +/************************************************************************ + * Now support for dynamical loading and the fallback functions + * we use for loading if dynamical loading is not available. + ************************************************************************/ + +/**/ +#ifdef DYNAMIC + +/**/ +#ifdef AIXDYNAMIC + +#include + +static char *dlerrstr[256]; + +static void * +load_and_bind(const char *fn) +{ + void *ret = (void *) load((char *) fn, L_NOAUTODEFER, NULL); + + if (ret) { + Module m; + int i, err = loadbind(0, (void *) addbuiltin, ret); + for (i = 0; i < modulestab->hsize && !err; i++) { + for (m = (Module)modulestab->nodes[i]; m && !err; + m = (Module)m->node.next) { + if (!(m->node.flags & MOD_ALIAS) && + m->u.handle && !(m->node.flags & MOD_LINKED)) + err |= loadbind(0, m->u.handle, ret); + } + } + + if (err) { + loadquery(L_GETMESSAGES, dlerrstr, sizeof(dlerrstr)); + unload(ret); + ret = NULL; + } + } else + loadquery(L_GETMESSAGES, dlerrstr, sizeof(dlerrstr)); + + return ret; +} + +#define dlopen(X,Y) load_and_bind(X) +#define dlclose(X) unload(X) +#define dlerror() (dlerrstr[0]) +#ifndef HAVE_DLERROR +# define HAVE_DLERROR 1 +#endif + +/**/ +#else + +#ifdef HAVE_DLFCN_H +# if defined(HAVE_DL_H) && defined(HPUX10DYNAMIC) +# include +# else +# include +# endif +#else +# ifdef HAVE_DL_H +# include +# define RTLD_LAZY BIND_DEFERRED +# define RTLD_GLOBAL DYNAMIC_PATH +# else +# include +# include +# include +# endif +#endif + +/**/ +#ifdef HPUX10DYNAMIC +# define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +# define dlclose(handle) shl_unload((shl_t)(handle)) + +static +void * +hpux_dlsym(void *handle, char *name) +{ + void *sym_addr; + if (!shl_findsym((shl_t *)&handle, name, TYPE_UNDEFINED, &sym_addr)) + return sym_addr; + return NULL; +} + +# define dlsym(handle,name) hpux_dlsym(handle,name) +# ifdef HAVE_DLERROR /* paranoia */ +# undef HAVE_DLERROR +# endif +#else +# ifndef HAVE_DLCLOSE +# define dlclose(X) ((X), 0) +# endif +/**/ +#endif + +#ifdef DLSYM_NEEDS_UNDERSCORE +# define STR_SETUP "_setup_" +# define STR_FEATURES "_features_" +# define STR_ENABLES "_enables_" +# define STR_BOOT "_boot_" +# define STR_CLEANUP "_cleanup_" +# define STR_FINISH "_finish_" +#else /* !DLSYM_NEEDS_UNDERSCORE */ +# define STR_SETUP "setup_" +# define STR_FEATURES "features_" +# define STR_ENABLES "enables_" +# define STR_BOOT "boot_" +# define STR_CLEANUP "cleanup_" +# define STR_FINISH "finish_" +#endif /* !DLSYM_NEEDS_UNDERSCORE */ + +/**/ +#endif /* !AIXDYNAMIC */ + +#ifndef RTLD_LAZY +# define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +# define RTLD_GLOBAL 0 +#endif + +/* + * Attempt to load a module. This is the lowest level of + * zsh function for dynamical modules. Returns the handle + * from the dynamic loader. + */ + +/**/ +static void * +try_load_module(char const *name) +{ + char buf[PATH_MAX + 1]; + char **pp; + void *ret = NULL; + int l; + + l = 1 + strlen(name) + 1 + strlen(DL_EXT); + for (pp = module_path; !ret && *pp; pp++) { + if (l + (**pp ? strlen(*pp) : 1) > PATH_MAX) + continue; + sprintf(buf, "%s/%s.%s", **pp ? *pp : ".", name, DL_EXT); + unmetafy(buf, NULL); + if (*buf) /* dlopen(NULL) returns a handle to the main binary */ + ret = dlopen(buf, RTLD_LAZY | RTLD_GLOBAL); + } + + return ret; +} + +/* + * Load a module, with option to complain or not. + * Returns the handle from the dynamic loader. + */ + +/**/ +static void * +do_load_module(char const *name, int silent) +{ + void *ret; + + ret = try_load_module(name); + if (!ret && !silent) { +#ifdef HAVE_DLERROR + char *errstr = dlerror(); + zwarn("failed to load module `%s': %s", name, + errstr ? metafy(errstr, -1, META_HEAPDUP) : "empty module path"); +#else + zwarn("failed to load module: %s", name); +#endif + } + return ret; +} + +/**/ +#else /* !DYNAMIC */ + +/* + * Dummy loader when no dynamic loading available; always fails. + */ + +/**/ +static void * +do_load_module(char const *name, int silent) +{ + if (!silent) + zwarn("failed to load module: %s", name); + + return NULL; +} + +/**/ +#endif /* !DYNAMIC */ + +/* + * Find a module in the list. + * flags is a set of bits defined in the enum above. + * If namep is set, this is set to point to the last alias value resolved, + * even if that module was not loaded. or the module name if no aliases. + * Hence this is always the physical module to load in a chain of aliases. + * Return NULL if the module named is not stored as a structure, or if we were + * resolving aliases and the final module named is not stored as a + * structure. + */ +/**/ +static Module +find_module(const char *name, int flags, const char **namep) +{ + Module m; + + m = (Module)modulestab->getnode2(modulestab, name); + if (m) { + if ((flags & FINDMOD_ALIASP) && (m->node.flags & MOD_ALIAS)) { + if (namep) + *namep = m->u.alias; + return find_module(m->u.alias, flags, namep); + } + if (namep) + *namep = m->node.nam; + return m; + } + if (!(flags & FINDMOD_CREATE)) + return NULL; + m = zshcalloc(sizeof(*m)); + modulestab->addnode(modulestab, ztrdup(name), m); + return m; +} + +/* + * Unlink and free a module node from the linked list. + */ + +/**/ +static void +delete_module(Module m) +{ + modulestab->removenode(modulestab, m->node.nam); + + modulestab->freenode(&m->node); +} + +/* + * Return 1 if a module is fully loaded else zero. + * A linked module may be marked as unloaded even though + * we can't fully unload it; this returns 0 to try to + * make that state transparently like an unloaded module. + */ + +/**/ +mod_export int +module_loaded(const char *name) +{ + Module m; + + return ((m = find_module(name, FINDMOD_ALIASP, NULL)) && + m->u.handle && + !(m->node.flags & MOD_UNLOAD)); +} + +/* + * Setup and cleanup functions: we don't search for aliases here, + * since they should have been resolved before we try to load or unload + * the module. + */ + +/**/ +#ifdef DYNAMIC + +/**/ +#ifdef AIXDYNAMIC + +/**/ +static int +dyn_setup_module(Module m) +{ + return ((int (*)_((int,Module, void*))) m->u.handle)(0, m, NULL); +} + +/**/ +static int +dyn_features_module(Module m, char ***features) +{ + return ((int (*)_((int,Module, void*))) m->u.handle)(4, m, features); +} + +/**/ +static int +dyn_enables_module(Module m, int **enables) +{ + return ((int (*)_((int,Module, void*))) m->u.handle)(5, m, enables); +} + +/**/ +static int +dyn_boot_module(Module m) +{ + return ((int (*)_((int,Module, void*))) m->u.handle)(1, m, NULL); +} + +/**/ +static int +dyn_cleanup_module(Module m) +{ + return ((int (*)_((int,Module, void*))) m->u.handle)(2, m, NULL); +} + +/**/ +static int +dyn_finish_module(Module m) +{ + return ((int (*)_((int,Module,void *))) m->u.handle)(3, m, NULL); +} + +/**/ +#else + +static Module_generic_func +module_func(Module m, char *name) +{ +#ifdef DYNAMIC_NAME_CLASH_OK + return (Module_generic_func) dlsym(m->u.handle, name); +#else /* !DYNAMIC_NAME_CLASH_OK */ + VARARR(char, buf, strlen(name) + strlen(m->node.nam)*2 + 1); + char const *p; + char *q; + strcpy(buf, name); + q = strchr(buf, 0); + for(p = m->node.nam; *p; p++) { + if(*p == '/') { + *q++ = 'Q'; + *q++ = 's'; + } else if(*p == '_') { + *q++ = 'Q'; + *q++ = 'u'; + } else if(*p == 'Q') { + *q++ = 'Q'; + *q++ = 'q'; + } else + *q++ = *p; + } + *q = 0; + return (Module_generic_func) dlsym(m->u.handle, buf); +#endif /* !DYNAMIC_NAME_CLASH_OK */ +} + +/**/ +static int +dyn_setup_module(Module m) +{ + Module_void_func fn = (Module_void_func)module_func(m, STR_SETUP); + + if (fn) + return fn(m); + zwarnnam(m->node.nam, "no setup function"); + return 1; +} + +/**/ +static int +dyn_features_module(Module m, char ***features) +{ + Module_features_func fn = + (Module_features_func)module_func(m, STR_FEATURES); + + if (fn) + return fn(m, features); + /* not a user-visible error if no features function */ + return 1; +} + +/**/ +static int +dyn_enables_module(Module m, int **enables) +{ + Module_enables_func fn = (Module_enables_func)module_func(m, STR_ENABLES); + + if (fn) + return fn(m, enables); + /* not a user-visible error if no enables function */ + return 1; +} + +/**/ +static int +dyn_boot_module(Module m) +{ + Module_void_func fn = (Module_void_func)module_func(m, STR_BOOT); + + if(fn) + return fn(m); + zwarnnam(m->node.nam, "no boot function"); + return 1; +} + +/**/ +static int +dyn_cleanup_module(Module m) +{ + Module_void_func fn = (Module_void_func)module_func(m, STR_CLEANUP); + + if(fn) + return fn(m); + zwarnnam(m->node.nam, "no cleanup function"); + return 1; +} + +/* Note that this function does more than just calling finish_foo(), * + * it really unloads the module. */ + +/**/ +static int +dyn_finish_module(Module m) +{ + Module_void_func fn = (Module_void_func)module_func(m, STR_FINISH); + int r; + + if (fn) + r = fn(m); + else { + zwarnnam(m->node.nam, "no finish function"); + r = 1; + } + dlclose(m->u.handle); + return r; +} + +/**/ +#endif /* !AIXDYNAMIC */ + +/**/ +static int +setup_module(Module m) +{ + return ((m->node.flags & MOD_LINKED) ? + (m->u.linked->setup)(m) : dyn_setup_module(m)); +} + +/**/ +static int +features_module(Module m, char ***features) +{ + return ((m->node.flags & MOD_LINKED) ? + (m->u.linked->features)(m, features) : + dyn_features_module(m, features)); +} + +/**/ +static int +enables_module(Module m, int **enables) +{ + return ((m->node.flags & MOD_LINKED) ? + (m->u.linked->enables)(m, enables) : + dyn_enables_module(m, enables)); +} + +/**/ +static int +boot_module(Module m) +{ + return ((m->node.flags & MOD_LINKED) ? + (m->u.linked->boot)(m) : dyn_boot_module(m)); +} + +/**/ +static int +cleanup_module(Module m) +{ + return ((m->node.flags & MOD_LINKED) ? + (m->u.linked->cleanup)(m) : dyn_cleanup_module(m)); +} + +/**/ +static int +finish_module(Module m) +{ + return ((m->node.flags & MOD_LINKED) ? + (m->u.linked->finish)(m) : dyn_finish_module(m)); +} + +/**/ +#else /* !DYNAMIC */ + +/**/ +static int +setup_module(Module m) +{ + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->setup)(m) : 1); +} + +/**/ +static int +features_module(Module m, char ***features) +{ + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->features)(m, features) + : 1); +} + +/**/ +static int +enables_module(Module m, int **enables) +{ + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->enables)(m, enables) + : 1); +} + +/**/ +static int +boot_module(Module m) +{ + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->boot)(m) : 1); +} + +/**/ +static int +cleanup_module(Module m) +{ + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->cleanup)(m) : 1); +} + +/**/ +static int +finish_module(Module m) +{ + return ((m->node.flags & MOD_LINKED) ? (m->u.linked->finish)(m) : 1); +} + +/**/ +#endif /* !DYNAMIC */ + + +/************************************************************************ + * Functions called when manipulating modules + ************************************************************************/ + +/* + * Set the features for the module, which must be loaded + * by now (though may not be fully set up). + * + * Return 0 for success, 1 for failure, 2 if some features + * couldn't be set by the module itself (non-existent features + * are tested here and cause 1 to be returned). + */ + +/**/ +static int +do_module_features(Module m, Feature_enables enablesarr, int flags) +{ + char **features; + int ret = 0; + + if (features_module(m, &features) == 0) { + /* + * Features are supported. If we were passed + * a NULL array, enable all features, else + * enable only the features listed. + * (This may in principle be an empty array, + * although that's not very pointful.) + */ + int *enables = NULL; + if (enables_module(m, &enables)) { + /* If features are supported, enables should be, too */ + if (!(flags & FEAT_IGNORE)) + zwarn("error getting enabled features for module `%s'", + m->node.nam); + return 1; + } + + if ((flags & FEAT_CHECKAUTO) && m->autoloads) { + /* + * Check autoloads are available. Since these + * have been requested at some other point, they + * don't affect the return status unless something + * in enablesstr doesn't work. + */ + LinkNode an, nextn; + for (an = firstnode(m->autoloads); an; an = nextn) { + char *al = (char *)getdata(an), **ptr; + /* careful, we can delete the current node */ + nextn = nextnode(an); + for (ptr = features; *ptr; ptr++) + if (!strcmp(al, *ptr)) + break; + if (!*ptr) { + char *arg[2]; + if (!(flags & FEAT_IGNORE)) + zwarn( + "module `%s' has no such feature: `%s': autoload cancelled", + m->node.nam, al); + /* + * This shouldn't happen, so it's not worth optimising + * the call to autofeatures... + */ + arg[0] = al = dupstring(al); + arg[1] = NULL; + (void)autofeatures(NULL, m->node.nam, arg, 0, + FEAT_IGNORE|FEAT_REMOVE); + /* + * don't want to try to enable *that*... + * expunge it from the enable string. + */ + if (enablesarr) { + Feature_enables fep; + for (fep = enablesarr; fep->str; fep++) { + char *str = fep->str; + if (*str == '+' || *str == '-') + str++; + if (fep->pat ? pattry(fep->pat, al) : + !strcmp(al, str)) { + /* can't enable it after all, so return 1 */ + ret = 1; + while (fep->str) { + fep->str = fep[1].str; + fep->pat = fep[1].pat; + fep++; + } + if (!fep->pat) + break; + } + } + } + } + } + } + + if (enablesarr) { + Feature_enables fep; + for (fep = enablesarr; fep->str; fep++) { + char **fp, *esp = fep->str; + int on = 1, found = 0; + if (*esp == '+') + esp++; + else if (*esp == '-') { + on = 0; + esp++; + } + for (fp = features; *fp; fp++) + if (fep->pat ? pattry(fep->pat, *fp) : !strcmp(*fp, esp)) { + enables[fp - features] = on; + found++; + if (!fep->pat) + break; + } + if (!found) { + if (!(flags & FEAT_IGNORE)) + zwarn(fep->pat ? + "module `%s' has no feature matching: `%s'" : + "module `%s' has no such feature: `%s'", + m->node.nam, esp); + return 1; + } + } + } else { + /* + * Enable all features. This is used when loading + * without using zmodload -F. + */ + int n_features = arrlen(features); + int *ep; + for (ep = enables; n_features--; ep++) + *ep = 1; + } + + if (enables_module(m, &enables)) + return 2; + } else if (enablesarr) { + if (!(flags & FEAT_IGNORE)) + zwarn("module `%s' does not support features", m->node.nam); + return 1; + } + /* Else it doesn't support features but we don't care. */ + + return ret; +} + +/* + * Boot the module, including setting up features. + * As we've only just loaded the module, we don't yet + * know what features it supports, so we get them passed + * as a string. + * + * Returns 0 if OK, 1 if completely failed, 2 if some features + * couldn't be set up. + */ + +/**/ +static int +do_boot_module(Module m, Feature_enables enablesarr, int silent) +{ + int ret = do_module_features(m, enablesarr, + silent ? FEAT_IGNORE|FEAT_CHECKAUTO : + FEAT_CHECKAUTO); + + if (ret == 1) + return 1; + + if (boot_module(m)) + return 1; + return ret; +} + +/* + * Cleanup the module. + */ + +/**/ +static int +do_cleanup_module(Module m) +{ + return (m->node.flags & MOD_LINKED) ? + (m->u.linked && m->u.linked->cleanup(m)) : + (m->u.handle && cleanup_module(m)); +} + +/* + * Test a module name contains only valid characters: those + * allowed in a shell identifier plus slash. Return 1 if so. + */ + +/**/ +static int +modname_ok(char const *p) +{ + do { + p = itype_end(p, IIDENT, 0); + if (!*p) + return 1; + } while(*p++ == '/'); + return 0; +} + +/* + * High level function to load a module, encapsulating + * all the handling of module functions. + * + * "*enablesstr" is NULL if the caller is not feature-aware; + * then the module should turn on all features. If it + * is not NULL it points to an array of features to be + * turned on. This function is responsible for testing whether + * the module supports those features. + * + * If "silent" is 1, don't issue warnings for errors. + * + * Now returns 0 for success (changed post-4.3.4), + * 1 for complete failure, 2 if some features couldn't be set. + */ + +/**/ +mod_export int +load_module(char const *name, Feature_enables enablesarr, int silent) +{ + Module m; + void *handle = NULL; + Linkedmod linked; + int set, bootret; + + if (!modname_ok(name)) { + if (!silent) + zerr("invalid module name `%s'", name); + return 1; + } + /* + * The following function call may alter name to the final name in a + * chain of aliases. This makes sure the actual module loaded + * is the right one. + */ + queue_signals(); + if (!(m = find_module(name, FINDMOD_ALIASP, &name))) { + if (!(linked = module_linked(name)) && + !(handle = do_load_module(name, silent))) { + unqueue_signals(); + return 1; + } + m = zshcalloc(sizeof(*m)); + if (handle) { + m->u.handle = handle; + m->node.flags |= MOD_SETUP; + } else { + m->u.linked = linked; + m->node.flags |= MOD_SETUP | MOD_LINKED; + } + modulestab->addnode(modulestab, ztrdup(name), m); + + if ((set = setup_module(m)) || + (bootret = do_boot_module(m, enablesarr, silent)) == 1) { + if (!set) + do_cleanup_module(m); + finish_module(m); + delete_module(m); + unqueue_signals(); + return 1; + } + m->node.flags |= MOD_INIT_S | MOD_INIT_B; + m->node.flags &= ~MOD_SETUP; + unqueue_signals(); + return bootret; + } + if (m->node.flags & MOD_SETUP) { + unqueue_signals(); + return 0; + } + if (m->node.flags & MOD_UNLOAD) + m->node.flags &= ~MOD_UNLOAD; + else if ((m->node.flags & MOD_LINKED) ? m->u.linked : m->u.handle) { + unqueue_signals(); + return 0; + } + if (m->node.flags & MOD_BUSY) { + unqueue_signals(); + zerr("circular dependencies for module ;%s", name); + return 1; + } + m->node.flags |= MOD_BUSY; + /* + * TODO: shouldn't we unload the module if one of + * its dependencies fails? + */ + if (m->deps) { + LinkNode n; + for (n = firstnode(m->deps); n; incnode(n)) + if (load_module((char *) getdata(n), NULL, silent) == 1) { + m->node.flags &= ~MOD_BUSY; + unqueue_signals(); + return 1; + } + } + m->node.flags &= ~MOD_BUSY; + if (!m->u.handle) { + handle = NULL; + if (!(linked = module_linked(name)) && + !(handle = do_load_module(name, silent))) { + unqueue_signals(); + return 1; + } + if (handle) { + m->u.handle = handle; + m->node.flags |= MOD_SETUP; + } else { + m->u.linked = linked; + m->node.flags |= MOD_SETUP | MOD_LINKED; + } + if (setup_module(m)) { + finish_module(m); + if (handle) + m->u.handle = NULL; + else + m->u.linked = NULL; + m->node.flags &= ~MOD_SETUP; + unqueue_signals(); + return 1; + } + m->node.flags |= MOD_INIT_S; + } + m->node.flags |= MOD_SETUP; + if ((bootret = do_boot_module(m, enablesarr, silent)) == 1) { + do_cleanup_module(m); + finish_module(m); + if (m->node.flags & MOD_LINKED) + m->u.linked = NULL; + else + m->u.handle = NULL; + m->node.flags &= ~MOD_SETUP; + unqueue_signals(); + return 1; + } + m->node.flags |= MOD_INIT_B; + m->node.flags &= ~MOD_SETUP; + unqueue_signals(); + return bootret; +} + +/* This ensures that the module with the name given as the first argument + * is loaded. + * The other argument is the array of features to set. If this is NULL + * all features are enabled (even if the module was already loaded). + * + * If this is non-NULL the module features are set accordingly + * whether or not the module is loaded; it is an error if the + * module does not support the features passed (even if the feature + * is to be turned off) or if the module does not support features + * at all. + * The return value is 0 if the module was found or loaded + * (this changed post-4.3.4, because I got so confused---pws), + * 1 if loading failed completely, 2 if some features couldn't be set. + * + * This function behaves like load_module() except that it + * handles the case where the module was already loaded, and + * sets features accordingly. + */ + +/**/ +mod_export int +require_module(const char *module, Feature_enables features, int silent) +{ + Module m = NULL; + int ret = 0; + + /* Resolve aliases and actual loadable module as for load_module */ + queue_signals(); + m = find_module(module, FINDMOD_ALIASP, &module); + if (!m || !m->u.handle || + (m->node.flags & MOD_UNLOAD)) + ret = load_module(module, features, silent); + else + ret = do_module_features(m, features, 0); + unqueue_signals(); + + return ret; +} + +/* + * Indicate that the module named "name" depends on the module + * named "from". + */ + +/**/ +void +add_dep(const char *name, char *from) +{ + LinkNode node; + Module m; + + /* + * If we were passed an alias, we must resolve it to a final + * module name (and maybe add the corresponding struct), since otherwise + * we would need to check all modules to see if they happen + * to be aliased to the same thing to implement dependencies properly. + * + * This should mean that an attempt to add an alias which would + * have the same name as a module which has dependencies is correctly + * rejected, because then the module named already exists as a non-alias. + * Better make sure. (There's no problem making a an alias which + * *points* to a module with dependencies, of course.) + */ + m = find_module(name, FINDMOD_ALIASP|FINDMOD_CREATE, &name); + if (!m->deps) + m->deps = znewlinklist(); + for (node = firstnode(m->deps); + node && strcmp((char *) getdata(node), from); + incnode(node)); + if (!node) + zaddlinknode(m->deps, ztrdup(from)); +} + +/* + * Function to be used when scanning the builtins table to + * find and print autoloadable builtins. + */ + +/**/ +static void +autoloadscan(HashNode hn, int printflags) +{ + Builtin bn = (Builtin) hn; + + if(bn->node.flags & BINF_ADDED) + return; + if(printflags & PRINT_LIST) { + fputs("zmodload -ab ", stdout); + if(bn->optstr[0] == '-') + fputs("-- ", stdout); + quotedzputs(bn->optstr, stdout); + if(strcmp(bn->node.nam, bn->optstr)) { + putchar(' '); + quotedzputs(bn->node.nam, stdout); + } + } else { + nicezputs(bn->node.nam, stdout); + if(strcmp(bn->node.nam, bn->optstr)) { + fputs(" (", stdout); + nicezputs(bn->optstr, stdout); + putchar(')'); + } + } + putchar('\n'); +} + + +/************************************************************************ + * Handling for the zmodload builtin and its various options. + ************************************************************************/ + +/* + * Main builtin entry point for zmodload. + */ + +/**/ +int +bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func)) +{ + int ops_bcpf = OPT_ISSET(ops,'b') || OPT_ISSET(ops,'c') || + OPT_ISSET(ops,'p') || OPT_ISSET(ops,'f'); + int ops_au = OPT_ISSET(ops,'a') || OPT_ISSET(ops,'u'); + int ret = 1, autoopts; + /* options only allowed with -F */ + char *fonly = "lP", *fp; + + if (ops_bcpf && !ops_au) { + zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u"); + return 1; + } + if (OPT_ISSET(ops,'F') && (ops_bcpf || OPT_ISSET(ops,'u'))) { + zwarnnam(nam, "-b, -c, -f, -p and -u cannot be combined with -F"); + return 1; + } + if (OPT_ISSET(ops,'A') || OPT_ISSET(ops,'R')) { + if (ops_bcpf || ops_au || OPT_ISSET(ops,'d') || + (OPT_ISSET(ops,'R') && OPT_ISSET(ops,'e'))) { + zwarnnam(nam, "illegal flags combined with -A or -R"); + return 1; + } + if (!OPT_ISSET(ops,'e')) + return bin_zmodload_alias(nam, args, ops); + } + if (OPT_ISSET(ops,'d') && OPT_ISSET(ops,'a')) { + zwarnnam(nam, "-d cannot be combined with -a"); + return 1; + } + if (OPT_ISSET(ops,'u') && !*args) { + zwarnnam(nam, "what do you want to unload?"); + return 1; + } + if (OPT_ISSET(ops,'e') && (OPT_ISSET(ops,'I') || OPT_ISSET(ops,'L') || + (OPT_ISSET(ops,'a') && !OPT_ISSET(ops,'F')) + || OPT_ISSET(ops,'d') || + OPT_ISSET(ops,'i') || OPT_ISSET(ops,'u'))) { + zwarnnam(nam, "-e cannot be combined with other options"); + /* except -F ... */ + return 1; + } + for (fp = fonly; *fp; fp++) { + if (OPT_ISSET(ops,STOUC(*fp)) && !OPT_ISSET(ops,'F')) { + zwarnnam(nam, "-%c is only allowed with -F", *fp); + return 1; + } + } + queue_signals(); + if (OPT_ISSET(ops, 'F')) + ret = bin_zmodload_features(nam, args, ops); + else if (OPT_ISSET(ops,'e')) + ret = bin_zmodload_exist(nam, args, ops); + else if (OPT_ISSET(ops,'d')) + ret = bin_zmodload_dep(nam, args, ops); + else if ((autoopts = OPT_ISSET(ops, 'b') + OPT_ISSET(ops, 'c') + + OPT_ISSET(ops, 'p') + OPT_ISSET(ops, 'f')) || + /* zmodload -a is equivalent to zmodload -ab, annoyingly */ + OPT_ISSET(ops, 'a')) { + if (autoopts > 1) { + zwarnnam(nam, "use only one of -b, -c, or -p"); + ret = 1; + } else + ret = bin_zmodload_auto(nam, args, ops); + } else + ret = bin_zmodload_load(nam, args, ops); + unqueue_signals(); + + return ret; +} + +/* zmodload -A */ + +/**/ +static int +bin_zmodload_alias(char *nam, char **args, Options ops) +{ + /* + * TODO: while it would be too nasty to have aliases, as opposed + * to real loadable modules, with dependencies --- just what would + * we need to load when, exactly? --- there is in principle no objection + * to making it possible to force an alias onto an existing unloaded + * module which has dependencies. This would simply transfer + * the dependencies down the line to the aliased-to module name. + * This is actually useful, since then you can alias zsh/zle=mytestzle + * to load another version of zle. But then what happens when the + * alias is removed? Do you transfer the dependencies back? And + * suppose other names are aliased to the same file? It might be + * kettle of fish best left unwormed. + */ + Module m; + + if (!*args) { + if (OPT_ISSET(ops,'R')) { + zwarnnam(nam, "no module alias to remove"); + return 1; + } + scanhashtable(modulestab, 1, MOD_ALIAS, 0, + modulestab->printnode, + OPT_ISSET(ops,'L') ? PRINTMOD_LIST : 0); + return 0; + } + + for (; *args; args++) { + char *eqpos = strchr(*args, '='); + char *aliasname = eqpos ? eqpos+1 : NULL; + if (eqpos) + *eqpos = '\0'; + if (!modname_ok(*args)) { + zwarnnam(nam, "invalid module name `%s'", *args); + return 1; + } + if (OPT_ISSET(ops,'R')) { + if (aliasname) { + zwarnnam(nam, "bad syntax for removing module alias: %s", + *args); + return 1; + } + m = find_module(*args, 0, NULL); + if (m) { + if (!(m->node.flags & MOD_ALIAS)) { + zwarnnam(nam, "module is not an alias: %s", *args); + return 1; + } + delete_module(m); + } else { + zwarnnam(nam, "no such module alias: %s", *args); + return 1; + } + } else { + if (aliasname) { + const char *mname = aliasname; + if (!modname_ok(aliasname)) { + zwarnnam(nam, "invalid module name `%s'", aliasname); + return 1; + } + do { + if (!strcmp(mname, *args)) { + zwarnnam(nam, "module alias would refer to itself: %s", + *args); + return 1; + } + } while ((m = find_module(mname, 0, NULL)) + && (m->node.flags & MOD_ALIAS) + && (mname = m->u.alias)); + m = find_module(*args, 0, NULL); + if (m) { + if (!(m->node.flags & MOD_ALIAS)) { + zwarnnam(nam, "module is not an alias: %s", *args); + return 1; + } + zsfree(m->u.alias); + } else { + m = (Module) zshcalloc(sizeof(*m)); + m->node.flags = MOD_ALIAS; + modulestab->addnode(modulestab, ztrdup(*args), m); + } + m->u.alias = ztrdup(aliasname); + } else { + if ((m = find_module(*args, 0, NULL))) { + if (m->node.flags & MOD_ALIAS) + modulestab->printnode(&m->node, + OPT_ISSET(ops,'L') ? + PRINTMOD_LIST : 0); + else { + zwarnnam(nam, "module is not an alias: %s", *args); + return 1; + } + } else { + zwarnnam(nam, "no such module alias: %s", *args); + return 1; + } + } + } + } + + return 0; +} + +/* zmodload -e (without -F) */ + +/**/ +static int +bin_zmodload_exist(UNUSED(char *nam), char **args, Options ops) +{ + Module m; + + if (!*args) { + scanhashtable(modulestab, 1, 0, 0, modulestab->printnode, + OPT_ISSET(ops,'A') ? PRINTMOD_EXIST|PRINTMOD_ALIAS : + PRINTMOD_EXIST); + return 0; + } else { + int ret = 0; + + for (; !ret && *args; args++) { + if (!(m = find_module(*args, FINDMOD_ALIASP, NULL)) + || !m->u.handle + || (m->node.flags & MOD_UNLOAD)) + ret = 1; + } + return ret; + } +} + +/* zmodload -d */ + +/**/ +static int +bin_zmodload_dep(UNUSED(char *nam), char **args, Options ops) +{ + Module m; + if (OPT_ISSET(ops,'u')) { + /* remove dependencies, which can't pertain to aliases */ + const char *tnam = *args++; + m = find_module(tnam, FINDMOD_ALIASP, &tnam); + if (!m) + return 0; + if (*args && m->deps) { + do { + LinkNode dnode; + for (dnode = firstnode(m->deps); dnode; incnode(dnode)) + if (!strcmp(*args, getdata(dnode))) { + zsfree(getdata(dnode)); + remnode(m->deps, dnode); + break; + } + } while(*++args); + if (empty(m->deps)) { + freelinklist(m->deps, freestr); + m->deps = NULL; + } + } else { + if (m->deps) { + freelinklist(m->deps, freestr); + m->deps = NULL; + } + } + if (!m->deps && !m->u.handle) + delete_module(m); + return 0; + } else if (!args[0] || !args[1]) { + /* list dependencies */ + int depflags = OPT_ISSET(ops,'L') ? + PRINTMOD_DEPS|PRINTMOD_LIST : PRINTMOD_DEPS; + if (args[0]) { + if ((m = (Module)modulestab->getnode2(modulestab, args[0]))) + modulestab->printnode(&m->node, depflags); + } else { + scanhashtable(modulestab, 1, 0, 0, modulestab->printnode, + depflags); + } + return 0; + } else { + /* add dependencies */ + int ret = 0; + char *tnam = *args++; + + for (; *args; args++) + add_dep(tnam, *args); + return ret; + } +} + +/* + * Function for scanning the parameter table to find and print + * out autoloadable parameters. + */ + +static void +printautoparams(HashNode hn, int lon) +{ + Param pm = (Param) hn; + + if (pm->node.flags & PM_AUTOLOAD) { + if (lon) + printf("zmodload -ap %s %s\n", pm->u.str, pm->node.nam); + else + printf("%s (%s)\n", pm->node.nam, pm->u.str); + } +} + +/* zmodload -a/u [bcpf] */ + +/**/ +static int +bin_zmodload_auto(char *nam, char **args, Options ops) +{ + int fchar, flags; + char *modnam; + + if (OPT_ISSET(ops,'c')) { + if (!*args) { + /* list autoloaded conditions */ + Conddef p; + + for (p = condtab; p; p = p->next) { + if (p->module) { + if (OPT_ISSET(ops,'L')) { + fputs("zmodload -ac", stdout); + if (p->flags & CONDF_INFIX) + putchar('I'); + printf(" %s %s\n", p->module, p->name); + } else { + if (p->flags & CONDF_INFIX) + fputs("infix ", stdout); + else + fputs("post ", stdout); + printf("%s (%s)\n",p->name, p->module); + } + } + } + return 0; + } + fchar = OPT_ISSET(ops,'I') ? 'C' : 'c'; + } else if (OPT_ISSET(ops,'p')) { + if (!*args) { + /* list autoloaded parameters */ + scanhashtable(paramtab, 1, 0, 0, printautoparams, + OPT_ISSET(ops,'L')); + return 0; + } + fchar = 'p'; + } else if (OPT_ISSET(ops,'f')) { + if (!*args) { + /* list autoloaded math functions */ + MathFunc p; + + for (p = mathfuncs; p; p = p->next) { + if (!(p->flags & MFF_USERFUNC) && p->module) { + if (OPT_ISSET(ops,'L')) { + fputs("zmodload -af", stdout); + printf(" %s %s\n", p->module, p->name); + } else + printf("%s (%s)\n",p->name, p->module); + } + } + return 0; + } + fchar = 'f'; + } else { + /* builtins are the default; zmodload -ab or just zmodload -a */ + if (!*args) { + /* list autoloaded builtins */ + scanhashtable(builtintab, 1, 0, 0, + autoloadscan, OPT_ISSET(ops,'L') ? PRINT_LIST : 0); + return 0; + } + fchar = 'b'; + } + + flags = FEAT_AUTOALL; + if (OPT_ISSET(ops,'i')) + flags |= FEAT_IGNORE; + if (OPT_ISSET(ops,'u')) { + /* remove autoload */ + flags |= FEAT_REMOVE; + modnam = NULL; + } else { + /* add autoload */ + modnam = *args; + + if (args[1]) + args++; + } + return autofeatures(nam, modnam, args, fchar, flags); +} + +/* Backend handler for zmodload -u */ + +/**/ +int +unload_module(Module m) +{ + int del; + + /* + * Only unload the real module, so resolve aliases. + */ + if (m->node.flags & MOD_ALIAS) { + m = find_module(m->u.alias, FINDMOD_ALIASP, NULL); + if (!m) + return 1; + } + /* + * We may need to clean up the module any time setup_ has been + * called. After cleanup_ is successful we are no longer in the + * booted state (because features etc. are deregistered), so remove + * MOD_INIT_B, and also MOD_INIT_S since we won't need to cleanup + * again if this succeeded. + */ + if ((m->node.flags & MOD_INIT_S) && + !(m->node.flags & MOD_UNLOAD) && + do_cleanup_module(m)) + return 1; + m->node.flags &= ~(MOD_INIT_B|MOD_INIT_S); + + del = (m->node.flags & MOD_UNLOAD); + + if (m->wrapper) { + m->node.flags |= MOD_UNLOAD; + return 0; + } + m->node.flags &= ~MOD_UNLOAD; + + /* + * We always need to finish the module (and unload it) + * if it is present. + */ + if (m->node.flags & MOD_LINKED) { + if (m->u.linked) { + m->u.linked->finish(m); + m->u.linked = NULL; + } + } else { + if (m->u.handle) { + finish_module(m); + m->u.handle = NULL; + } + } + + if (del && m->deps) { + /* The module was unloaded delayed, unload all modules * + * on which it depended. */ + LinkNode n; + + for (n = firstnode(m->deps); n; incnode(n)) { + Module dm = find_module((char *) getdata(n), + FINDMOD_ALIASP, NULL); + + if (dm && + (dm->node.flags & MOD_UNLOAD)) { + /* See if this is the only module depending on it. */ + Module am; + int du = 1, i; + /* Scan hash table the hard way */ + for (i = 0; du && i < modulestab->hsize; i++) { + for (am = (Module)modulestab->nodes[i]; du && am; + am = (Module)am->node.next) { + LinkNode sn; + /* + * Don't scan the module we're unloading; + * ignore if no dependencies. + */ + if (am == m || !am->deps) + continue; + /* Don't scan if not loaded nor linked */ + if ((am->node.flags & MOD_LINKED) ? + !am->u.linked : !am->u.handle) + continue; + for (sn = firstnode(am->deps); du && sn; + incnode(sn)) { + if (!strcmp((char *) getdata(sn), + dm->node.nam)) + du = 0; + } + } + } + if (du) + unload_module(dm); + } + } + } + if (m->autoloads && firstnode(m->autoloads)) { + /* + * Module has autoloadable features. Restore them + * so that the module will be reloaded when needed. + */ + autofeatures("zsh", m->node.nam, + hlinklist2array(m->autoloads, 0), 0, FEAT_IGNORE); + } else if (!m->deps) { + delete_module(m); + } + return 0; +} + +/* + * Unload a module by name (modname); nam is the command name. + * Optionally don't print some error messages (always print + * dependency errors). + */ + +/**/ +int +unload_named_module(char *modname, char *nam, int silent) +{ + const char *mname; + Module m; + int ret = 0; + + m = find_module(modname, FINDMOD_ALIASP, &mname); + if (m) { + int i, del = 0; + Module dm; + + for (i = 0; i < modulestab->hsize; i++) { + for (dm = (Module)modulestab->nodes[i]; dm; + dm = (Module)dm->node.next) { + LinkNode dn; + if (!dm->deps || !dm->u.handle) + continue; + for (dn = firstnode(dm->deps); dn; incnode(dn)) { + if (!strcmp((char *) getdata(dn), mname)) { + if (dm->node.flags & MOD_UNLOAD) + del = 1; + else { + zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", mname); + return 1; + } + } + } + } + } + if (del) + m->wrapper++; + if (unload_module(m)) + ret = 1; + if (del) + m->wrapper--; + } else if (!silent) { + zwarnnam(nam, "no such module %s", modname); + ret = 1; + } + + return ret; +} + +/* zmodload -u without -d */ + +/**/ +static int +bin_zmodload_load(char *nam, char **args, Options ops) +{ + int ret = 0; + if(OPT_ISSET(ops,'u')) { + /* unload modules */ + for(; *args; args++) { + if (unload_named_module(*args, nam, OPT_ISSET(ops,'i'))) + ret = 1; + } + return ret; + } else if(!*args) { + /* list modules */ + scanhashtable(modulestab, 1, 0, MOD_UNLOAD|MOD_ALIAS, + modulestab->printnode, + OPT_ISSET(ops,'L') ? PRINTMOD_LIST : 0); + return 0; + } else { + /* load modules */ + for (; *args; args++) { + int tmpret = require_module(*args, NULL, OPT_ISSET(ops,'s')); + if (tmpret && ret != 1) + ret = tmpret; + } + + return ret; + } +} + +/* zmodload -F */ + +/**/ +static int +bin_zmodload_features(const char *nam, char **args, Options ops) +{ + int iarg; + char *modname = *args; + Patprog *patprogs; + Feature_enables features, fep; + + if (modname) + args++; + else if (OPT_ISSET(ops,'L')) { + int printflags = PRINTMOD_LIST|PRINTMOD_FEATURES; + if (OPT_ISSET(ops,'P')) { + zwarnnam(nam, "-P is only allowed with a module name"); + return 1; + } + if (OPT_ISSET(ops,'l')) + printflags |= PRINTMOD_LISTALL; + if (OPT_ISSET(ops,'a')) + printflags |= PRINTMOD_AUTO; + scanhashtable(modulestab, 1, 0, MOD_ALIAS, + modulestab->printnode, printflags); + return 0; + } + + if (!modname) { + zwarnnam(nam, "-F requires a module name"); + return 1; + } + + if (OPT_ISSET(ops,'m')) { + char **argp; + Patprog *patprogp; + + /* not NULL terminated */ + patprogp = patprogs = + (Patprog *)zhalloc(arrlen(args)*sizeof(Patprog)); + for (argp = args; *argp; argp++, patprogp++) { + char *arg = *argp; + if (*arg == '+' || *arg == '-') + arg++; + tokenize(arg); + *patprogp = patcompile(arg, 0, 0); + } + } else + patprogs = NULL; + + if (OPT_ISSET(ops,'l') || OPT_ISSET(ops,'L') || OPT_ISSET(ops,'e')) { + /* + * With option 'l', list all features one per line with + or -. + * With option 'L', list as zmodload statement showing + * only options turned on. + * With both options, list as zmodload showing options + * to be turned both on and off. + */ + Module m; + char **features, **fp, **arrset = NULL, **arrp = NULL; + int *enables = NULL, *ep; + char *param = OPT_ARG_SAFE(ops,'P'); + + m = find_module(modname, FINDMOD_ALIASP, NULL); + if (OPT_ISSET(ops,'a')) { + LinkNode ln; + /* + * If there are no autoloads defined, return status 1. + */ + if (!m || !m->autoloads) + return 1; + if (OPT_ISSET(ops,'e')) { + for (fp = args; *fp; fp++) { + char *fstr = *fp; + int sense = 1; + if (*fstr == '+') + fstr++; + else if (*fstr == '-') { + fstr++; + sense = 0; + } + if ((linknodebystring(m->autoloads, fstr) != NULL) != + sense) + return 1; + } + return 0; + } + if (param) { + arrp = arrset = (char **)zalloc(sizeof(char*) * + (countlinknodes(m->autoloads)+1)); + } else if (OPT_ISSET(ops,'L')) { + printf("zmodload -aF %s%c", m->node.nam, + m->autoloads && firstnode(m->autoloads) ? ' ' : '\n'); + arrp = NULL; + } + for (ln = firstnode(m->autoloads); ln; incnode(ln)) { + char *al = (char *)getdata(ln); + if (param) + *arrp++ = ztrdup(al); + else + printf("%s%c", al, + OPT_ISSET(ops,'L') && nextnode(ln) ? ' ' : '\n'); + } + if (param) { + *arrp = NULL; + if (!setaparam(param, arrset)) + return 1; + } + return 0; + } + if (!m || !m->u.handle || (m->node.flags & MOD_UNLOAD)) { + if (!OPT_ISSET(ops,'e')) + zwarnnam(nam, "module `%s' is not yet loaded", modname); + return 1; + } + if (features_module(m, &features)) { + if (!OPT_ISSET(ops,'e')) + zwarnnam(nam, "module `%s' does not support features", + m->node.nam); + return 1; + } + if (enables_module(m, &enables)) { + /* this shouldn't ever happen, so don't silence this error */ + zwarnnam(nam, "error getting enabled features for module `%s'", + m->node.nam); + return 1; + } + for (arrp = args, iarg = 0; *arrp; arrp++, iarg++) { + char *arg = *arrp; + int on, found = 0; + if (*arg == '-') { + on = 0; + arg++; + } else if (*arg == '+') { + on = 1; + arg++; + } else + on = -1; + for (fp = features, ep = enables; *fp; fp++, ep++) { + if (patprogs ? pattry(patprogs[iarg], *fp) : + !strcmp(arg, *fp)) { + /* for -e, check given state, if any */ + if (OPT_ISSET(ops,'e') && on != -1 && + on != (*ep & 1)) + return 1; + found++; + if (!patprogs) + break; + } + } + if (!found) { + if (!OPT_ISSET(ops,'e')) + zwarnnam(nam, patprogs ? + "module `%s' has no feature matching: `%s'" : + "module `%s' has no such feature: `%s'", + modname, *arrp); + return 1; + } + } + if (OPT_ISSET(ops,'e')) /* yep, everything we want exists */ + return 0; + if (param) { + int arrlen = 0; + for (fp = features, ep = enables; *fp; fp++, ep++) { + if (OPT_ISSET(ops, 'L') && !OPT_ISSET(ops, 'l') && + !*ep) + continue; + if (*args) { + char **argp; + for (argp = args, iarg = 0; *argp; argp++, iarg++) { + char *arg = *argp; + /* ignore +/- for consistency */ + if (*arg == '+' || *arg == '-') + arg++; + if (patprogs ? pattry(patprogs[iarg], *fp) : + !strcmp(*fp, arg)) + break; + } + if (!*argp) + continue; + } + arrlen++; + } + arrp = arrset = zalloc(sizeof(char *) * (arrlen+1)); + } else if (OPT_ISSET(ops, 'L')) + printf("zmodload -F %s ", m->node.nam); + for (fp = features, ep = enables; *fp; fp++, ep++) { + char *onoff; + int term; + if (*args) { + char **argp; + for (argp = args, iarg = 0; *argp; argp++, iarg++) { + char *arg = *argp; + if (*arg == '+' || *arg == '-') + arg++; + if (patprogs ? pattry(patprogs[iarg], *fp) : + !strcmp(*fp, *argp)) + break; + } + if (!*argp) + continue; + } + if (OPT_ISSET(ops, 'L') && !OPT_ISSET(ops, 'l')) { + if (!*ep) + continue; + onoff = ""; + } else if (*ep) { + onoff = "+"; + } else { + onoff = "-"; + } + if (param) { + *arrp++ = bicat(onoff, *fp); + } else { + if (OPT_ISSET(ops, 'L') && fp[1]) { + term = ' '; + } else { + term = '\n'; + } + printf("%s%s%c", onoff, *fp, term); + } + } + if (param) { + *arrp = NULL; + if (!setaparam(param, arrset)) + return 1; + } + return 0; + } else if (OPT_ISSET(ops,'P')) { + zwarnnam(nam, "-P can only be used with -l or -L"); + return 1; + } else if (OPT_ISSET(ops,'a')) { + if (OPT_ISSET(ops,'m')) { + zwarnnam(nam, "-m cannot be used with -a"); + return 1; + } + /* + * With zmodload -aF, we always use the effect of -i. + * The thinking is that marking a feature for + * autoload is separate from enabling or disabling it. + * Arguably we could do this with the zmodload -ab method + * but I've kept it there for old time's sake. + * The decoupling has meant FEAT_IGNORE/-i also + * suppresses an error for attempting to remove an + * autoload when the feature is enabled, which used + * to be a hard error before. + */ + return autofeatures(nam, modname, args, 0, FEAT_IGNORE); + } + + fep = features = + (Feature_enables)zhalloc((arrlen(args)+1)*sizeof(*fep)); + + while (*args) { + fep->str = *args++; + fep->pat = patprogs ? *patprogs++ : NULL; + fep++; + } + fep->str = NULL; + fep->pat = NULL; + + return require_module(modname, features, OPT_ISSET(ops,'s')); +} + + +/************************************************************************ + * Generic feature support. + * These functions are designed to be called by modules. + ************************************************************************/ + +/* + * Construct a features array out of the list of concrete + * features given, leaving space for any abstract features + * to be added by the module itself. + * + * Note the memory is from the heap. + */ + +/**/ +mod_export char ** +featuresarray(UNUSED(Module m), Features f) +{ + int bn_size = f->bn_size, cd_size = f->cd_size; + int mf_size = f->mf_size, pd_size = f->pd_size; + int features_size = bn_size + cd_size + pd_size + mf_size + f->n_abstract; + Builtin bnp = f->bn_list; + Conddef cdp = f->cd_list; + MathFunc mfp = f->mf_list; + Paramdef pdp = f->pd_list; + char **features = (char **)zhalloc((features_size + 1) * sizeof(char *)); + char **featurep = features; + + while (bn_size--) + *featurep++ = dyncat("b:", (bnp++)->node.nam); + while (cd_size--) { + *featurep++ = dyncat((cdp->flags & CONDF_INFIX) ? "C:" : "c:", + cdp->name); + cdp++; + } + while (mf_size--) + *featurep++ = dyncat("f:", (mfp++)->name); + while (pd_size--) + *featurep++ = dyncat("p:", (pdp++)->name); + + features[features_size] = NULL; + return features; +} + +/* + * Return the current set of enables for the features in a + * module using heap memory. Leave space for abstract + * features. The array is not zero terminated. + */ +/**/ +mod_export int * +getfeatureenables(UNUSED(Module m), Features f) +{ + int bn_size = f->bn_size, cd_size = f->cd_size; + int mf_size = f->mf_size, pd_size = f->pd_size; + int features_size = bn_size + cd_size + mf_size + pd_size + f->n_abstract; + Builtin bnp = f->bn_list; + Conddef cdp = f->cd_list; + MathFunc mfp = f->mf_list; + Paramdef pdp = f->pd_list; + int *enables = zhalloc(sizeof(int) * features_size); + int *enablep = enables; + + while (bn_size--) + *enablep++ = ((bnp++)->node.flags & BINF_ADDED) ? 1 : 0; + while (cd_size--) + *enablep++ = ((cdp++)->flags & CONDF_ADDED) ? 1 : 0; + while (mf_size--) + *enablep++ = ((mfp++)->flags & MFF_ADDED) ? 1 : 0; + while (pd_size--) + *enablep++ = (pdp++)->pm ? 1 : 0; + + return enables; +} + +/* + * Add or remove the concrete features passed in arguments, + * depending on the corresponding element of the array e. + * If e is NULL, disable everything. + * Return 0 for success, 1 for failure; does not attempt + * to imitate the return values of addbuiltins() etc. + * Any failure in adding a requested feature is an + * error. + */ + +/**/ +mod_export int +setfeatureenables(Module m, Features f, int *e) +{ + int ret = 0; + + if (f->bn_size) { + if (setbuiltins(m->node.nam, f->bn_list, f->bn_size, e)) + ret = 1; + if (e) + e += f->bn_size; + } + if (f->cd_size) { + if (setconddefs(m->node.nam, f->cd_list, f->cd_size, e)) + ret = 1; + if (e) + e += f->cd_size; + } + if (f->mf_size) { + if (setmathfuncs(m->node.nam, f->mf_list, f->mf_size, e)) + ret = 1; + if (e) + e += f->mf_size; + } + if (f->pd_size) { + if (setparamdefs(m->node.nam, f->pd_list, f->pd_size, e)) + ret = 1; + if (e) + e += f->pd_size; + } + return ret; +} + +/* + * Convenient front-end to get or set features which + * can be used in a module enables_() function. + */ + +/**/ +mod_export int +handlefeatures(Module m, Features f, int **enables) +{ + if (!enables || *enables) + return setfeatureenables(m, f, enables ? *enables : NULL); + *enables = getfeatureenables(m, f); + return 0; +} + +/* + * Ensure module "modname" is providing feature with "prefix" + * and "feature" (e.g. "b:", "limit"). If feature is NULL, + * ensure all features are loaded (used for compatibility + * with the pre-feature autoloading behaviour). + * + * This will usually be called from the main shell to handle + * loading of an autoloadable feature. + * + * Returns 0 on success, 1 for error in module, 2 for error + * setting the feature. However, this isn't actually all + * that useful for testing immediately on an autoload since + * it could be a failure to autoload a different feature + * from the one we want. We could fix this but it's + * possible to test other ways. + */ + +/**/ +mod_export int +ensurefeature(const char *modname, const char *prefix, const char *feature) +{ + char *f; + struct feature_enables features[2]; + + if (!feature) + return require_module(modname, NULL, 0); + f = dyncat(prefix, feature); + + features[0].str = f; + features[0].pat = NULL; + features[1].str = NULL; + features[1].pat = NULL; + return require_module(modname, features, 0); +} + +/* + * Add autoloadable features for a given module. + */ + +/**/ +int +autofeatures(const char *cmdnam, const char *module, char **features, + int prefchar, int defflags) +{ + int ret = 0, subret; + Module defm, m; + char **modfeatures = NULL; + int *modenables = NULL; + if (module) { + defm = (Module)find_module(module, + FINDMOD_ALIASP|FINDMOD_CREATE, NULL); + if ((defm->node.flags & MOD_LINKED) ? defm->u.linked : + defm->u.handle) { + (void)features_module(defm, &modfeatures); + (void)enables_module(defm, &modenables); + } + } else + defm = NULL; + + for (; *features; features++) { + char *fnam, *typnam, *feature; + int add, fchar, flags = defflags; + autofeaturefn_t fn; + + if (prefchar) { + /* + * "features" is list of bare features with no + * type prefix; prefchar gives type character. + */ + add = 1; /* unless overridden by flag */ + fchar = prefchar; + fnam = *features; + feature = zhalloc(strlen(fnam) + 3); + sprintf(feature, "%c:%s", fchar, fnam); + } else { + feature = *features; + if (*feature == '-') { + add = 0; + feature++; + } else { + add = 1; + if (*feature == '+') + feature++; + } + + if (!*feature || feature[1] != ':') { + zwarnnam(cmdnam, "bad format for autoloadable feature: `%s'", + feature); + ret = 1; + continue; + } + fnam = feature + 2; + fchar = feature[0]; + } + if (flags & FEAT_REMOVE) + add = 0; + + switch (fchar) { + case 'b': + fn = add ? add_autobin : del_autobin; + typnam = "builtin"; + break; + + case 'C': + flags |= FEAT_INFIX; + /* FALLTHROUGH */ + case 'c': + fn = add ? add_autocond : del_autocond; + typnam = "condition"; + break; + + case 'f': + fn = add ? add_automathfunc : del_automathfunc; + typnam = "math function"; + break; + + case 'p': + fn = add ? add_autoparam : del_autoparam; + typnam = "parameter"; + break; + + default: + zwarnnam(cmdnam, "bad autoloadable feature type: `%c'", + fchar); + ret = 1; + continue; + } + + if (strchr(fnam, '/')) { + zwarnnam(cmdnam, "%s: `/' is illegal in a %s", fnam, typnam); + ret = 1; + continue; + } + + if (!module) { + /* + * Traditional un-autoload syntax doesn't tell us + * which module this came from. + */ + int i; + for (i = 0, m = NULL; !m && i < modulestab->hsize; i++) { + for (m = (Module)modulestab->nodes[i]; m; + m = (Module)m->node.next) { + if (m->autoloads && + linknodebystring(m->autoloads, feature)) + break; + } + } + if (!m) { + if (!(flags & FEAT_IGNORE)) { + ret = 1; + zwarnnam(cmdnam, "%s: no such %s", fnam, typnam); + } + continue; + } + } else + m = defm; + + subret = 0; + if (add) { + char **ptr; + if (modfeatures) { + /* + * If the module is already available, check that + * it does in fact provide the necessary feature. + */ + for (ptr = modfeatures; *ptr; ptr++) + if (!strcmp(*ptr, feature)) + break; + if (!*ptr) { + zwarnnam(cmdnam, "module `%s' has no such feature: `%s'", + m->node.nam, feature); + ret = 1; + continue; + } + /* + * If the feature is already provided by the module, there's + * nothing more to do. + */ + if (modenables[ptr-modfeatures]) + continue; + /* + * Otherwise, marking it for autoload will do the + * right thing when the feature is eventually used. + */ + } + if (!m->autoloads) { + m->autoloads = znewlinklist(); + zaddlinknode(m->autoloads, ztrdup(feature)); + } else { + /* Insert in lexical order */ + LinkNode ln, prev = (LinkNode)m->autoloads; + while ((ln = nextnode(prev))) { + int cmp = strcmp(feature, (char *)getdata(ln)); + if (cmp == 0) { + /* Already there. Never an error. */ + break; + } + if (cmp < 0) { + zinsertlinknode(m->autoloads, prev, + ztrdup(feature)); + break; + } + prev = ln; + } + if (!ln) + zaddlinknode(m->autoloads, ztrdup(feature)); + } + } else if (m->autoloads) { + LinkNode ln; + if ((ln = linknodebystring(m->autoloads, feature))) + zsfree((char *)remnode(m->autoloads, ln)); + else { + /* + * With -i (or zmodload -Fa), removing an autoload + * that's not there is not an error. + */ + subret = (flags & FEAT_IGNORE) ? -2 : 2; + } + } + + if (subret == 0) + subret = fn(module, fnam, flags); + + if (subret != 0) { + /* -2 indicates not an error, just skip running fn() */ + if (subret != -2) + ret = 1; + switch (subret) { + case 1: + zwarnnam(cmdnam, "failed to add %s `%s'", typnam, fnam); + break; + + case 2: + zwarnnam(cmdnam, "%s: no such %s", fnam, typnam); + break; + + case 3: + zwarnnam(cmdnam, "%s: %s is already defined", fnam, typnam); + break; + + default: + /* no (further) message needed */ + break; + } + } + } + + return ret; +} diff --git a/dotfiles/system/.zsh/modules/Src/options.c b/dotfiles/system/.zsh/modules/Src/options.c new file mode 100644 index 0000000..600b649 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/options.c @@ -0,0 +1,955 @@ +/* + * options.c - shell options + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "options.pro" + +/* current emulation (used to decide which set of option letters is used) */ + +/**/ +mod_export int emulation; + +/* current sticky emulation: sticky = NULL means none */ + +/**/ +mod_export Emulation_options sticky; + +/* the options; e.g. if opts[SHGLOB] != 0, SH_GLOB is turned on */ + +/**/ +mod_export char opts[OPT_SIZE]; + +/* Option name hash table */ + +/**/ +mod_export HashTable optiontab; + +/* The canonical option name table */ + +#define OPT_CSH EMULATE_CSH +#define OPT_KSH EMULATE_KSH +#define OPT_SH EMULATE_SH +#define OPT_ZSH EMULATE_ZSH + +#define OPT_ALL (OPT_CSH|OPT_KSH|OPT_SH|OPT_ZSH) +#define OPT_BOURNE (OPT_KSH|OPT_SH) +#define OPT_BSHELL (OPT_KSH|OPT_SH|OPT_ZSH) +#define OPT_NONBOURNE (OPT_ALL & ~OPT_BOURNE) +#define OPT_NONZSH (OPT_ALL & ~OPT_ZSH) + +/* option is relevant to emulation */ +#define OPT_EMULATE (EMULATE_UNUSED) +/* option should never be set by emulate() */ +#define OPT_SPECIAL (EMULATE_UNUSED<<1) +/* option is an alias to an other option */ +#define OPT_ALIAS (EMULATE_UNUSED<<2) + +#define defset(X, my_emulation) (!!((X)->node.flags & my_emulation)) + +/* + * Note that option names should usually be fewer than 20 characters long + * to avoid formatting problems. + */ +static struct optname optns[] = { +{{NULL, "aliases", OPT_EMULATE|OPT_ALL}, ALIASESOPT}, +{{NULL, "aliasfuncdef", OPT_EMULATE|OPT_BOURNE}, ALIASFUNCDEF}, +{{NULL, "allexport", OPT_EMULATE}, ALLEXPORT}, +{{NULL, "alwayslastprompt", OPT_ALL}, ALWAYSLASTPROMPT}, +{{NULL, "alwaystoend", 0}, ALWAYSTOEND}, +{{NULL, "appendcreate", OPT_EMULATE|OPT_BOURNE}, APPENDCREATE}, +{{NULL, "appendhistory", OPT_ALL}, APPENDHISTORY}, +{{NULL, "autocd", OPT_EMULATE}, AUTOCD}, +{{NULL, "autocontinue", 0}, AUTOCONTINUE}, +{{NULL, "autolist", OPT_ALL}, AUTOLIST}, +{{NULL, "automenu", OPT_ALL}, AUTOMENU}, +{{NULL, "autonamedirs", 0}, AUTONAMEDIRS}, +{{NULL, "autoparamkeys", OPT_ALL}, AUTOPARAMKEYS}, +{{NULL, "autoparamslash", OPT_ALL}, AUTOPARAMSLASH}, +{{NULL, "autopushd", 0}, AUTOPUSHD}, +{{NULL, "autoremoveslash", OPT_ALL}, AUTOREMOVESLASH}, +{{NULL, "autoresume", 0}, AUTORESUME}, +{{NULL, "badpattern", OPT_EMULATE|OPT_NONBOURNE},BADPATTERN}, +{{NULL, "banghist", OPT_NONBOURNE}, BANGHIST}, +{{NULL, "bareglobqual", OPT_EMULATE|OPT_ZSH}, BAREGLOBQUAL}, +{{NULL, "bashautolist", 0}, BASHAUTOLIST}, +{{NULL, "bashrematch", 0}, BASHREMATCH}, +{{NULL, "beep", OPT_ALL}, BEEP}, +{{NULL, "bgnice", OPT_EMULATE|OPT_NONBOURNE},BGNICE}, +{{NULL, "braceccl", OPT_EMULATE}, BRACECCL}, +{{NULL, "bsdecho", OPT_EMULATE|OPT_SH}, BSDECHO}, +{{NULL, "caseglob", OPT_ALL}, CASEGLOB}, +{{NULL, "casematch", OPT_ALL}, CASEMATCH}, +{{NULL, "cbases", 0}, CBASES}, +{{NULL, "cprecedences", OPT_EMULATE|OPT_NONZSH}, CPRECEDENCES}, +{{NULL, "cdablevars", OPT_EMULATE}, CDABLEVARS}, +{{NULL, "chasedots", OPT_EMULATE}, CHASEDOTS}, +{{NULL, "chaselinks", OPT_EMULATE}, CHASELINKS}, +{{NULL, "checkjobs", OPT_EMULATE|OPT_ZSH}, CHECKJOBS}, +{{NULL, "checkrunningjobs", OPT_EMULATE|OPT_ZSH}, CHECKRUNNINGJOBS}, +{{NULL, "clobber", OPT_EMULATE|OPT_ALL}, CLOBBER}, +{{NULL, "combiningchars", 0}, COMBININGCHARS}, +{{NULL, "completealiases", 0}, COMPLETEALIASES}, +{{NULL, "completeinword", 0}, COMPLETEINWORD}, +{{NULL, "continueonerror", 0}, CONTINUEONERROR}, +{{NULL, "correct", 0}, CORRECT}, +{{NULL, "correctall", 0}, CORRECTALL}, +{{NULL, "cshjunkiehistory", OPT_EMULATE|OPT_CSH}, CSHJUNKIEHISTORY}, +{{NULL, "cshjunkieloops", OPT_EMULATE|OPT_CSH}, CSHJUNKIELOOPS}, +{{NULL, "cshjunkiequotes", OPT_EMULATE|OPT_CSH}, CSHJUNKIEQUOTES}, +{{NULL, "cshnullcmd", OPT_EMULATE|OPT_CSH}, CSHNULLCMD}, +{{NULL, "cshnullglob", OPT_EMULATE|OPT_CSH}, CSHNULLGLOB}, +{{NULL, "debugbeforecmd", OPT_ALL}, DEBUGBEFORECMD}, +{{NULL, "emacs", 0}, EMACSMODE}, +{{NULL, "equals", OPT_EMULATE|OPT_ZSH}, EQUALS}, +{{NULL, "errexit", OPT_EMULATE}, ERREXIT}, +{{NULL, "errreturn", OPT_EMULATE}, ERRRETURN}, +{{NULL, "exec", OPT_ALL}, EXECOPT}, +{{NULL, "extendedglob", OPT_EMULATE}, EXTENDEDGLOB}, +{{NULL, "extendedhistory", OPT_CSH}, EXTENDEDHISTORY}, +{{NULL, "evallineno", OPT_EMULATE|OPT_ZSH}, EVALLINENO}, +{{NULL, "flowcontrol", OPT_ALL}, FLOWCONTROL}, +{{NULL, "forcefloat", 0}, FORCEFLOAT}, +{{NULL, "functionargzero", OPT_EMULATE|OPT_NONBOURNE},FUNCTIONARGZERO}, +{{NULL, "glob", OPT_EMULATE|OPT_ALL}, GLOBOPT}, +{{NULL, "globalexport", OPT_EMULATE|OPT_ZSH}, GLOBALEXPORT}, +{{NULL, "globalrcs", OPT_ALL}, GLOBALRCS}, +{{NULL, "globassign", OPT_EMULATE|OPT_CSH}, GLOBASSIGN}, +{{NULL, "globcomplete", 0}, GLOBCOMPLETE}, +{{NULL, "globdots", OPT_EMULATE}, GLOBDOTS}, +{{NULL, "globstarshort", OPT_EMULATE}, GLOBSTARSHORT}, +{{NULL, "globsubst", OPT_EMULATE|OPT_NONZSH}, GLOBSUBST}, +{{NULL, "hashcmds", OPT_ALL}, HASHCMDS}, +{{NULL, "hashdirs", OPT_ALL}, HASHDIRS}, +{{NULL, "hashexecutablesonly", 0}, HASHEXECUTABLESONLY}, +{{NULL, "hashlistall", OPT_ALL}, HASHLISTALL}, +{{NULL, "histallowclobber", 0}, HISTALLOWCLOBBER}, +{{NULL, "histbeep", OPT_ALL}, HISTBEEP}, +{{NULL, "histexpiredupsfirst",0}, HISTEXPIREDUPSFIRST}, +{{NULL, "histfcntllock", 0}, HISTFCNTLLOCK}, +{{NULL, "histfindnodups", 0}, HISTFINDNODUPS}, +{{NULL, "histignorealldups", 0}, HISTIGNOREALLDUPS}, +{{NULL, "histignoredups", 0}, HISTIGNOREDUPS}, +{{NULL, "histignorespace", 0}, HISTIGNORESPACE}, +{{NULL, "histlexwords", 0}, HISTLEXWORDS}, +{{NULL, "histnofunctions", 0}, HISTNOFUNCTIONS}, +{{NULL, "histnostore", 0}, HISTNOSTORE}, +{{NULL, "histsubstpattern", OPT_EMULATE}, HISTSUBSTPATTERN}, +{{NULL, "histreduceblanks", 0}, HISTREDUCEBLANKS}, +{{NULL, "histsavebycopy", OPT_ALL}, HISTSAVEBYCOPY}, +{{NULL, "histsavenodups", 0}, HISTSAVENODUPS}, +{{NULL, "histverify", 0}, HISTVERIFY}, +{{NULL, "hup", OPT_EMULATE|OPT_ZSH}, HUP}, +{{NULL, "ignorebraces", OPT_EMULATE|OPT_SH}, IGNOREBRACES}, +{{NULL, "ignoreclosebraces", OPT_EMULATE}, IGNORECLOSEBRACES}, +{{NULL, "ignoreeof", 0}, IGNOREEOF}, +{{NULL, "incappendhistory", 0}, INCAPPENDHISTORY}, +{{NULL, "incappendhistorytime", 0}, INCAPPENDHISTORYTIME}, +{{NULL, "interactive", OPT_SPECIAL}, INTERACTIVE}, +{{NULL, "interactivecomments",OPT_BOURNE}, INTERACTIVECOMMENTS}, +{{NULL, "ksharrays", OPT_EMULATE|OPT_BOURNE}, KSHARRAYS}, +{{NULL, "kshautoload", OPT_EMULATE|OPT_BOURNE}, KSHAUTOLOAD}, +{{NULL, "kshglob", OPT_EMULATE|OPT_KSH}, KSHGLOB}, +{{NULL, "kshoptionprint", OPT_EMULATE|OPT_KSH}, KSHOPTIONPRINT}, +{{NULL, "kshtypeset", 0}, KSHTYPESET}, +{{NULL, "kshzerosubscript", 0}, KSHZEROSUBSCRIPT}, +{{NULL, "listambiguous", OPT_ALL}, LISTAMBIGUOUS}, +{{NULL, "listbeep", OPT_ALL}, LISTBEEP}, +{{NULL, "listpacked", 0}, LISTPACKED}, +{{NULL, "listrowsfirst", 0}, LISTROWSFIRST}, +{{NULL, "listtypes", OPT_ALL}, LISTTYPES}, +{{NULL, "localoptions", OPT_EMULATE|OPT_KSH}, LOCALOPTIONS}, +{{NULL, "localloops", OPT_EMULATE}, LOCALLOOPS}, +{{NULL, "localpatterns", OPT_EMULATE}, LOCALPATTERNS}, +{{NULL, "localtraps", OPT_EMULATE|OPT_KSH}, LOCALTRAPS}, +{{NULL, "login", OPT_SPECIAL}, LOGINSHELL}, +{{NULL, "longlistjobs", 0}, LONGLISTJOBS}, +{{NULL, "magicequalsubst", OPT_EMULATE}, MAGICEQUALSUBST}, +{{NULL, "mailwarning", 0}, MAILWARNING}, +{{NULL, "markdirs", 0}, MARKDIRS}, +{{NULL, "menucomplete", 0}, MENUCOMPLETE}, +{{NULL, "monitor", OPT_SPECIAL}, MONITOR}, +{{NULL, "multibyte", +#ifdef MULTIBYTE_SUPPORT + OPT_ALL +#else + 0 +#endif + }, MULTIBYTE}, +{{NULL, "multifuncdef", OPT_EMULATE|OPT_ZSH}, MULTIFUNCDEF}, +{{NULL, "multios", OPT_EMULATE|OPT_ZSH}, MULTIOS}, +{{NULL, "nomatch", OPT_EMULATE|OPT_NONBOURNE},NOMATCH}, +{{NULL, "notify", OPT_ZSH}, NOTIFY}, +{{NULL, "nullglob", OPT_EMULATE}, NULLGLOB}, +{{NULL, "numericglobsort", OPT_EMULATE}, NUMERICGLOBSORT}, +{{NULL, "octalzeroes", OPT_EMULATE|OPT_SH}, OCTALZEROES}, +{{NULL, "overstrike", 0}, OVERSTRIKE}, +{{NULL, "pathdirs", OPT_EMULATE}, PATHDIRS}, +{{NULL, "pathscript", OPT_EMULATE|OPT_BOURNE}, PATHSCRIPT}, +{{NULL, "pipefail", OPT_EMULATE}, PIPEFAIL}, +{{NULL, "posixaliases", OPT_EMULATE|OPT_BOURNE}, POSIXALIASES}, +{{NULL, "posixargzero", OPT_EMULATE}, POSIXARGZERO}, +{{NULL, "posixbuiltins", OPT_EMULATE|OPT_BOURNE}, POSIXBUILTINS}, +{{NULL, "posixcd", OPT_EMULATE|OPT_BOURNE}, POSIXCD}, +{{NULL, "posixidentifiers", OPT_EMULATE|OPT_BOURNE}, POSIXIDENTIFIERS}, +{{NULL, "posixjobs", OPT_EMULATE|OPT_BOURNE}, POSIXJOBS}, +{{NULL, "posixstrings", OPT_EMULATE|OPT_BOURNE}, POSIXSTRINGS}, +{{NULL, "posixtraps", OPT_EMULATE|OPT_BOURNE}, POSIXTRAPS}, +{{NULL, "printeightbit", 0}, PRINTEIGHTBIT}, +{{NULL, "printexitvalue", 0}, PRINTEXITVALUE}, +{{NULL, "privileged", OPT_SPECIAL}, PRIVILEGED}, +{{NULL, "promptbang", OPT_KSH}, PROMPTBANG}, +{{NULL, "promptcr", OPT_ALL}, PROMPTCR}, +{{NULL, "promptpercent", OPT_NONBOURNE}, PROMPTPERCENT}, +{{NULL, "promptsp", OPT_ALL}, PROMPTSP}, +{{NULL, "promptsubst", OPT_BOURNE}, PROMPTSUBST}, +{{NULL, "pushdignoredups", OPT_EMULATE}, PUSHDIGNOREDUPS}, +{{NULL, "pushdminus", OPT_EMULATE}, PUSHDMINUS}, +{{NULL, "pushdsilent", 0}, PUSHDSILENT}, +{{NULL, "pushdtohome", OPT_EMULATE}, PUSHDTOHOME}, +{{NULL, "rcexpandparam", OPT_EMULATE}, RCEXPANDPARAM}, +{{NULL, "rcquotes", OPT_EMULATE}, RCQUOTES}, +{{NULL, "rcs", OPT_ALL}, RCS}, +{{NULL, "recexact", 0}, RECEXACT}, +{{NULL, "rematchpcre", 0}, REMATCHPCRE}, +{{NULL, "restricted", OPT_SPECIAL}, RESTRICTED}, +{{NULL, "rmstarsilent", OPT_BOURNE}, RMSTARSILENT}, +{{NULL, "rmstarwait", 0}, RMSTARWAIT}, +{{NULL, "sharehistory", OPT_KSH}, SHAREHISTORY}, +{{NULL, "shfileexpansion", OPT_EMULATE|OPT_BOURNE}, SHFILEEXPANSION}, +{{NULL, "shglob", OPT_EMULATE|OPT_BOURNE}, SHGLOB}, +{{NULL, "shinstdin", OPT_SPECIAL}, SHINSTDIN}, +{{NULL, "shnullcmd", OPT_EMULATE|OPT_BOURNE}, SHNULLCMD}, +{{NULL, "shoptionletters", OPT_EMULATE|OPT_BOURNE}, SHOPTIONLETTERS}, +{{NULL, "shortloops", OPT_EMULATE|OPT_NONBOURNE},SHORTLOOPS}, +{{NULL, "shwordsplit", OPT_EMULATE|OPT_BOURNE}, SHWORDSPLIT}, +{{NULL, "singlecommand", OPT_SPECIAL}, SINGLECOMMAND}, +{{NULL, "singlelinezle", OPT_KSH}, SINGLELINEZLE}, +{{NULL, "sourcetrace", 0}, SOURCETRACE}, +{{NULL, "sunkeyboardhack", 0}, SUNKEYBOARDHACK}, +{{NULL, "transientrprompt", 0}, TRANSIENTRPROMPT}, +{{NULL, "trapsasync", 0}, TRAPSASYNC}, +{{NULL, "typesetsilent", OPT_EMULATE|OPT_BOURNE}, TYPESETSILENT}, +{{NULL, "unset", OPT_EMULATE|OPT_BSHELL}, UNSET}, +{{NULL, "verbose", 0}, VERBOSE}, +{{NULL, "vi", 0}, VIMODE}, +{{NULL, "warncreateglobal", OPT_EMULATE}, WARNCREATEGLOBAL}, +{{NULL, "warnnestedvar", OPT_EMULATE}, WARNNESTEDVAR}, +{{NULL, "xtrace", 0}, XTRACE}, +{{NULL, "zle", OPT_SPECIAL}, USEZLE}, +{{NULL, "braceexpand", OPT_ALIAS}, /* ksh/bash */ -IGNOREBRACES}, +{{NULL, "dotglob", OPT_ALIAS}, /* bash */ GLOBDOTS}, +{{NULL, "hashall", OPT_ALIAS}, /* bash */ HASHCMDS}, +{{NULL, "histappend", OPT_ALIAS}, /* bash */ APPENDHISTORY}, +{{NULL, "histexpand", OPT_ALIAS}, /* bash */ BANGHIST}, +{{NULL, "log", OPT_ALIAS}, /* ksh */ -HISTNOFUNCTIONS}, +{{NULL, "mailwarn", OPT_ALIAS}, /* bash */ MAILWARNING}, +{{NULL, "onecmd", OPT_ALIAS}, /* bash */ SINGLECOMMAND}, +{{NULL, "physical", OPT_ALIAS}, /* ksh/bash */ CHASELINKS}, +{{NULL, "promptvars", OPT_ALIAS}, /* bash */ PROMPTSUBST}, +{{NULL, "stdin", OPT_ALIAS}, /* ksh */ SHINSTDIN}, +{{NULL, "trackall", OPT_ALIAS}, /* ksh */ HASHCMDS}, +{{NULL, "dvorak", 0}, DVORAK}, +{{NULL, NULL, 0}, 0} +}; + +/* Option letters */ + +#define optletters (isset(SHOPTIONLETTERS) ? kshletters : zshletters) + +#define FIRST_OPT '0' +#define LAST_OPT 'y' + +static short zshletters[LAST_OPT - FIRST_OPT + 1] = { + /* 0 */ CORRECT, + /* 1 */ PRINTEXITVALUE, + /* 2 */ -BADPATTERN, + /* 3 */ -NOMATCH, + /* 4 */ GLOBDOTS, + /* 5 */ NOTIFY, + /* 6 */ BGNICE, + /* 7 */ IGNOREEOF, + /* 8 */ MARKDIRS, + /* 9 */ AUTOLIST, + /* : */ 0, + /* ; */ 0, + /* < */ 0, + /* = */ 0, + /* > */ 0, + /* ? */ 0, + /* @ */ 0, + /* A */ 0, /* use with set for arrays */ + /* B */ -BEEP, + /* C */ -CLOBBER, + /* D */ PUSHDTOHOME, + /* E */ PUSHDSILENT, + /* F */ -GLOBOPT, + /* G */ NULLGLOB, + /* H */ RMSTARSILENT, + /* I */ IGNOREBRACES, + /* J */ AUTOCD, + /* K */ -BANGHIST, + /* L */ SUNKEYBOARDHACK, + /* M */ SINGLELINEZLE, + /* N */ AUTOPUSHD, + /* O */ CORRECTALL, + /* P */ RCEXPANDPARAM, + /* Q */ PATHDIRS, + /* R */ LONGLISTJOBS, + /* S */ RECEXACT, + /* T */ CDABLEVARS, + /* U */ MAILWARNING, + /* V */ -PROMPTCR, + /* W */ AUTORESUME, + /* X */ LISTTYPES, + /* Y */ MENUCOMPLETE, + /* Z */ USEZLE, + /* [ */ 0, + /* \ */ 0, + /* ] */ 0, + /* ^ */ 0, + /* _ */ 0, + /* ` */ 0, + /* a */ ALLEXPORT, + /* b */ 0, /* in non-Bourne shells, end of options */ + /* c */ 0, /* command follows */ + /* d */ -GLOBALRCS, + /* e */ ERREXIT, + /* f */ -RCS, + /* g */ HISTIGNORESPACE, + /* h */ HISTIGNOREDUPS, + /* i */ INTERACTIVE, + /* j */ 0, + /* k */ INTERACTIVECOMMENTS, + /* l */ LOGINSHELL, + /* m */ MONITOR, + /* n */ -EXECOPT, + /* o */ 0, /* long option name follows */ + /* p */ PRIVILEGED, + /* q */ 0, + /* r */ RESTRICTED, + /* s */ SHINSTDIN, + /* t */ SINGLECOMMAND, + /* u */ -UNSET, + /* v */ VERBOSE, + /* w */ CHASELINKS, + /* x */ XTRACE, + /* y */ SHWORDSPLIT, +}; + +static short kshletters[LAST_OPT - FIRST_OPT + 1] = { + /* 0 */ 0, + /* 1 */ 0, + /* 2 */ 0, + /* 3 */ 0, + /* 4 */ 0, + /* 5 */ 0, + /* 6 */ 0, + /* 7 */ 0, + /* 8 */ 0, + /* 9 */ 0, + /* : */ 0, + /* ; */ 0, + /* < */ 0, + /* = */ 0, + /* > */ 0, + /* ? */ 0, + /* @ */ 0, + /* A */ 0, + /* B */ 0, + /* C */ -CLOBBER, + /* D */ 0, + /* E */ 0, + /* F */ 0, + /* G */ 0, + /* H */ 0, + /* I */ 0, + /* J */ 0, + /* K */ 0, + /* L */ 0, + /* M */ 0, + /* N */ 0, + /* O */ 0, + /* P */ 0, + /* Q */ 0, + /* R */ 0, + /* S */ 0, + /* T */ TRAPSASYNC, + /* U */ 0, + /* V */ 0, + /* W */ 0, + /* X */ MARKDIRS, + /* Y */ 0, + /* Z */ 0, + /* [ */ 0, + /* \ */ 0, + /* ] */ 0, + /* ^ */ 0, + /* _ */ 0, + /* ` */ 0, + /* a */ ALLEXPORT, + /* b */ NOTIFY, + /* c */ 0, + /* d */ 0, + /* e */ ERREXIT, + /* f */ -GLOBOPT, + /* g */ 0, + /* h */ 0, + /* i */ INTERACTIVE, + /* j */ 0, + /* k */ 0, + /* l */ LOGINSHELL, + /* m */ MONITOR, + /* n */ -EXECOPT, + /* o */ 0, + /* p */ PRIVILEGED, + /* q */ 0, + /* r */ RESTRICTED, + /* s */ SHINSTDIN, + /* t */ SINGLECOMMAND, + /* u */ -UNSET, + /* v */ VERBOSE, + /* w */ 0, + /* x */ XTRACE, + /* y */ 0, +}; + +/* Initialisation of the option name hash table */ + +/**/ +static void +printoptionnode(HashNode hn, int set) +{ + Optname on = (Optname) hn; + int optno = on->optno; + + if (optno < 0) + optno = -optno; + if (isset(KSHOPTIONPRINT)) { + if (defset(on, emulation)) + printf("no%-19s %s\n", on->node.nam, isset(optno) ? "off" : "on"); + else + printf("%-21s %s\n", on->node.nam, isset(optno) ? "on" : "off"); + } else if (set == (isset(optno) ^ defset(on, emulation))) { + if (set ^ isset(optno)) + fputs("no", stdout); + puts(on->node.nam); + } +} + +/**/ +void +createoptiontable(void) +{ + Optname on; + + optiontab = newhashtable(101, "optiontab", NULL); + + optiontab->hash = hasher; + optiontab->emptytable = NULL; + optiontab->filltable = NULL; + optiontab->cmpnodes = strcmp; + optiontab->addnode = addhashnode; + optiontab->getnode = gethashnode; + optiontab->getnode2 = gethashnode2; + optiontab->removenode = NULL; + optiontab->disablenode = disablehashnode; + optiontab->enablenode = enablehashnode; + optiontab->freenode = NULL; + optiontab->printnode = printoptionnode; + + for (on = optns; on->node.nam; on++) + optiontab->addnode(optiontab, on->node.nam, on); +} + +/* Emulation appropriate to the setemulate function */ + +static int setemulate_emulation; + +/* Option array manipulated within the setemulate function */ + +/**/ +static char *setemulate_opts; + +/* Setting of default options */ + +/**/ +static void +setemulate(HashNode hn, int fully) +{ + Optname on = (Optname) hn; + + /* Set options: each non-special option is set according to the * + * current emulation mode if either it is considered relevant * + * to emulation or we are doing a full emulation (as indicated * + * by the `fully' parameter). */ + if (!(on->node.flags & OPT_ALIAS) && + ((fully && !(on->node.flags & OPT_SPECIAL)) || + (on->node.flags & OPT_EMULATE))) + setemulate_opts[on->optno] = defset(on, setemulate_emulation); +} + +/**/ +void +installemulation(int new_emulation, char *new_opts) +{ + setemulate_emulation = new_emulation; + setemulate_opts = new_opts; + scanhashtable(optiontab, 0, 0, 0, setemulate, + !!(new_emulation & EMULATE_FULLY)); +} + +/**/ +void +emulate(const char *zsh_name, int fully, int *new_emulation, char *new_opts) +{ + char ch = *zsh_name; + + if (ch == 'r') + ch = zsh_name[1]; + + /* Work out the new emulation mode */ + if (ch == 'c') + *new_emulation = EMULATE_CSH; + else if (ch == 'k') + *new_emulation = EMULATE_KSH; + else if (ch == 's' || ch == 'b') + *new_emulation = EMULATE_SH; + else + *new_emulation = EMULATE_ZSH; + + if (fully) + *new_emulation |= EMULATE_FULLY; + installemulation(*new_emulation, new_opts); + + if (funcstack && funcstack->tp == FS_FUNC) { + /* + * We are inside a function. Decide if it's traced. + * Pedantic note: the function in the function table isn't + * guaranteed to be what we're executing, but it's + * close enough. + */ + Shfunc shf = (Shfunc)shfunctab->getnode(shfunctab, funcstack->name); + if (shf && (shf->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL))) { + /* Tracing is on, so set xtrace */ + new_opts[XTRACE] = 1; + } + } +} + +/* setopt, unsetopt */ + +/**/ +static void +setoption(HashNode hn, int value) +{ + dosetopt(((Optname) hn)->optno, value, 0, opts); +} + +/**/ +int +bin_setopt(char *nam, char **args, UNUSED(Options ops), int isun) +{ + int action, optno, match = 0; + + /* With no arguments or options, display options. */ + if (!*args) { + scanhashtable(optiontab, 1, 0, OPT_ALIAS, optiontab->printnode, !isun); + return 0; + } + + /* loop through command line options (begins with "-" or "+") */ + while (*args && (**args == '-' || **args == '+')) { + action = (**args == '-') ^ isun; + if(!args[0][1]) + *args = "--"; + while (*++*args) { + if(**args == Meta) + *++*args ^= 32; + /* The pseudo-option `--' signifies the end of options. */ + if (**args == '-') { + args++; + goto doneoptions; + } else if (**args == 'o') { + if (!*++*args) + args++; + if (!*args) { + zwarnnam(nam, "string expected after -o"); + inittyptab(); + return 1; + } + if(!(optno = optlookup(*args))) + zwarnnam(nam, "no such option: %s", *args); + else if(dosetopt(optno, action, 0, opts)) + zwarnnam(nam, "can't change option: %s", *args); + break; + } else if(**args == 'm') { + match = 1; + } else { + if (!(optno = optlookupc(**args))) + zwarnnam(nam, "bad option: -%c", **args); + else if(dosetopt(optno, action, 0, opts)) + zwarnnam(nam, "can't change option: -%c", **args); + } + } + args++; + } + doneoptions: + + if (!match) { + /* Not globbing the arguments -- arguments are simply option names. */ + while (*args) { + if(!(optno = optlookup(*args++))) + zwarnnam(nam, "no such option: %s", args[-1]); + else if(dosetopt(optno, !isun, 0, opts)) + zwarnnam(nam, "can't change option: %s", args[-1]); + } + } else { + /* Globbing option (-m) set. */ + while (*args) { + Patprog pprog; + char *s, *t; + + t = s = dupstring(*args); + while (*t) + if (*t == '_') + chuck(t); + else { + /* See comment in optlookup() */ + if (*t >= 'A' && *t <= 'Z') + *t = (*t - 'A') + 'a'; + t++; + } + + /* Expand the current arg. */ + tokenize(s); + if (!(pprog = patcompile(s, PAT_HEAPDUP, NULL))) { + zwarnnam(nam, "bad pattern: %s", *args); + continue; + } + /* Loop over expansions. */ + scanmatchtable(optiontab, pprog, 0, 0, OPT_ALIAS, + setoption, !isun); + args++; + } + } + inittyptab(); + return 0; +} + +/* Identify an option name */ + +/**/ +mod_export int +optlookup(char const *name) +{ + char *s, *t; + Optname n; + + s = t = dupstring(name); + + /* exorcise underscores, and change to lowercase */ + while (*t) + if (*t == '_') + chuck(t); + else { + /* + * Some locales (in particular tr_TR.UTF-8) may + * have non-standard mappings of ASCII characters, + * so be careful. Option names must be ASCII so + * we don't need to be too clever. + */ + if (*t >= 'A' && *t <= 'Z') + *t = (*t - 'A') + 'a'; + t++; + } + + /* look up name in the table */ + if (s[0] == 'n' && s[1] == 'o' && + (n = (Optname) optiontab->getnode(optiontab, s + 2))) { + return -n->optno; + } else if ((n = (Optname) optiontab->getnode(optiontab, s))) + return n->optno; + else + return OPT_INVALID; +} + +/* Identify an option letter */ + +/**/ +int +optlookupc(char c) +{ + if(c < FIRST_OPT || c > LAST_OPT) + return 0; + + return optletters[c - FIRST_OPT]; +} + +/**/ +static void +restrictparam(char *nam) +{ + Param pm = (Param) paramtab->getnode(paramtab, nam); + + if (pm) { + pm->node.flags |= PM_SPECIAL | PM_RESTRICTED; + return; + } + createparam(nam, PM_SCALAR | PM_UNSET | PM_SPECIAL | PM_RESTRICTED); +} + +/* list of restricted parameters which are not otherwise special */ +static char *rparams[] = { + "SHELL", "HISTFILE", "LD_LIBRARY_PATH", "LD_AOUT_LIBRARY_PATH", + "LD_PRELOAD", "LD_AOUT_PRELOAD", NULL +}; + +/* Set or unset an option, as a result of user request. The option * + * number may be negative, indicating that the sense is reversed * + * from the usual meaning of the option. */ + +/**/ +mod_export int +dosetopt(int optno, int value, int force, char *new_opts) +{ + if(!optno) + return -1; + if(optno < 0) { + optno = -optno; + value = !value; + } + if (optno == RESTRICTED) { + if (isset(RESTRICTED)) + return value ? 0 : -1; + if (value) { + char **s; + + for (s = rparams; *s; s++) + restrictparam(*s); + } + } else if(!force && optno == EXECOPT && !value && interact) { + /* cannot set noexec when interactive */ + return -1; + } else if(!force && (optno == INTERACTIVE || optno == SHINSTDIN || + optno == SINGLECOMMAND)) { + if (new_opts[optno] == value) + return 0; + /* it is not permitted to change the value of these options */ + return -1; + } else if(!force && optno == USEZLE && value) { + /* we require a terminal in order to use ZLE */ + if(!interact || SHTTY == -1 || !shout) + return -1; + } else if(optno == PRIVILEGED && !value) { + /* unsetting PRIVILEGED causes the shell to make itself unprivileged */ +#ifdef HAVE_SETUID + int ignore_err; + errno = 0; + /* + * Set the GID first as if we set the UID to non-privileged it + * might be impossible to restore the GID. + * + * Some OSes (possibly no longer around) have been known to + * fail silently the first time, so we attempt the change twice. + * If it fails we are guaranteed to pick this up the second + * time, so ignore the first time. + * + * Some versions of gcc make it hard to ignore the results the + * first time, hence the following. (These are probably not + * systems that require the doubled calls.) + */ + ignore_err = setgid(getgid()); + (void)ignore_err; + ignore_err = setuid(getuid()); + (void)ignore_err; + if (setgid(getgid())) { + zwarn("failed to change group ID: %e", errno); + return -1; + } else if (setuid(getuid())) { + zwarn("failed to change user ID: %e", errno); + return -1; + } +#else + zwarn("setuid not available"); + return -1; +#endif /* not HAVE_SETUID */ +#ifdef JOB_CONTROL + } else if (!force && optno == MONITOR && value) { + if (new_opts[optno] == value) + return 0; + if (SHTTY != -1) { + origpgrp = GETPGRP(); + acquire_pgrp(); + } else + return -1; +#else + } else if(optno == MONITOR && value) { + return -1; +#endif /* not JOB_CONTROL */ +#ifdef GETPWNAM_FAKED + } else if(optno == CDABLEVARS && value) { + return -1; +#endif /* GETPWNAM_FAKED */ + } else if ((optno == EMACSMODE || optno == VIMODE) && value) { + if (sticky && sticky->emulation) + return -1; + zleentry(ZLE_CMD_SET_KEYMAP, optno); + new_opts[(optno == EMACSMODE) ? VIMODE : EMACSMODE] = 0; + } else if (optno == SUNKEYBOARDHACK) { + /* for backward compatibility */ + keyboardhackchar = (value ? '`' : '\0'); + } + new_opts[optno] = value; + if (optno == BANGHIST || optno == SHINSTDIN) + inittyptab(); + return 0; +} + +/* Function to get value for special parameter `-' */ + +/**/ +char * +dashgetfn(UNUSED(Param pm)) +{ + static char buf[LAST_OPT - FIRST_OPT + 2]; + char *val = buf; + int i; + + for(i = 0; i <= LAST_OPT - FIRST_OPT; i++) { + int optno = optletters[i]; + if(optno && ((optno > 0) ? isset(optno) : unset(-optno))) + *val++ = FIRST_OPT + i; + } + *val = '\0'; + return buf; +} + +/* print options for set -o/+o */ + +/**/ +void +printoptionstates(int hadplus) +{ + scanhashtable(optiontab, 1, 0, OPT_ALIAS, printoptionnodestate, hadplus); +} + +/**/ +static void +printoptionnodestate(HashNode hn, int hadplus) +{ + Optname on = (Optname) hn; + int optno = on->optno; + + if (hadplus) { + printf("set %co %s%s\n", + defset(on, emulation) != isset(optno) ? '-' : '+', + defset(on, emulation) ? "no" : "", + on->node.nam); + } else { + if (defset(on, emulation)) + printf("no%-19s %s\n", on->node.nam, isset(optno) ? "off" : "on"); + else + printf("%-21s %s\n", on->node.nam, isset(optno) ? "on" : "off"); + } +} + +/* Print option list for --help */ + +/**/ +void +printoptionlist(void) +{ + short *lp; + char c; + + printf("\nNamed options:\n"); + scanhashtable(optiontab, 1, 0, OPT_ALIAS, printoptionlist_printoption, 0); + printf("\nOption aliases:\n"); + scanhashtable(optiontab, 1, OPT_ALIAS, 0, printoptionlist_printoption, 0); + printf("\nOption letters:\n"); + for(lp = optletters, c = FIRST_OPT; c <= LAST_OPT; lp++, c++) { + if(!*lp) + continue; + printf(" -%c ", c); + printoptionlist_printequiv(*lp); + } +} + +/**/ +static void +printoptionlist_printoption(HashNode hn, UNUSED(int ignored)) +{ + Optname on = (Optname) hn; + + if(on->node.flags & OPT_ALIAS) { + printf(" --%-19s ", on->node.nam); + printoptionlist_printequiv(on->optno); + } else + printf(" --%s\n", on->node.nam); +} + +/**/ +static void +printoptionlist_printequiv(int optno) +{ + int isneg = optno < 0; + + optno *= (isneg ? -1 : 1); + printf(" equivalent to --%s%s\n", isneg ? "no-" : "", optns[optno-1].node.nam); +} + +/**/ +static char *print_emulate_opts; + +/**/ +static void +print_emulate_option(HashNode hn, int fully) +{ + Optname on = (Optname) hn; + + if (!(on->node.flags & OPT_ALIAS) && + ((fully && !(on->node.flags & OPT_SPECIAL)) || + (on->node.flags & OPT_EMULATE))) + { + if (!print_emulate_opts[on->optno]) + fputs("no", stdout); + puts(on->node.nam); + } +} + +/* + * List the settings of options associated with an emulation + */ + +/**/ +void list_emulate_options(char *cmdopts, int fully) +{ + print_emulate_opts = cmdopts; + scanhashtable(optiontab, 1, 0, 0, print_emulate_option, fully); +} diff --git a/dotfiles/system/.zsh/modules/Src/params.c b/dotfiles/system/.zsh/modules/Src/params.c new file mode 100644 index 0000000..a1c299f --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/params.c @@ -0,0 +1,5884 @@ +/* + * params.c - parameters + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "params.pro" + +#include "version.h" +#ifdef CUSTOM_PATCHLEVEL +#define ZSH_PATCHLEVEL CUSTOM_PATCHLEVEL +#else +#include "patchlevel.h" + +#include + +/* If removed from the ChangeLog for some reason */ +#ifndef ZSH_PATCHLEVEL +#define ZSH_PATCHLEVEL "unknown" +#endif +#endif + +/* what level of localness we are at */ + +/**/ +mod_export int locallevel; + +/* Variables holding values of special parameters */ + +/**/ +mod_export +char **pparams, /* $argv */ + **cdpath, /* $cdpath */ + **fpath, /* $fpath */ + **mailpath, /* $mailpath */ + **manpath, /* $manpath */ + **psvar, /* $psvar */ + **watch, /* $watch */ + **zsh_eval_context; /* $zsh_eval_context */ +/**/ +mod_export +char **path, /* $path */ + **fignore; /* $fignore */ + +/**/ +mod_export +char *argzero, /* $0 */ + *posixzero, /* $0 */ + *home, /* $HOME */ + *nullcmd, /* $NULLCMD */ + *oldpwd, /* $OLDPWD */ + *zoptarg, /* $OPTARG */ + *prompt, /* $PROMPT */ + *prompt2, /* $PROMPT2 */ + *prompt3, /* $PROMPT3 */ + *prompt4, /* $PROMPT4 */ + *readnullcmd, /* $READNULLCMD */ + *rprompt, /* $RPROMPT */ + *rprompt2, /* $RPROMPT2 */ + *sprompt, /* $SPROMPT */ + *wordchars; /* $WORDCHARS */ +/**/ +mod_export +char *ifs, /* $IFS */ + *postedit, /* $POSTEDIT */ + *term, /* $TERM */ + *zsh_terminfo, /* $TERMINFO */ + *zsh_terminfodirs, /* $TERMINFO_DIRS */ + *ttystrname, /* $TTY */ + *pwd; /* $PWD */ + +/**/ +mod_export +zlong lastval, /* $? */ + mypid, /* $$ */ + lastpid, /* $! */ + zterm_columns, /* $COLUMNS */ + zterm_lines, /* $LINES */ + rprompt_indent, /* $ZLE_RPROMPT_INDENT */ + ppid, /* $PPID */ + zsh_subshell; /* $ZSH_SUBSHELL */ + +/* $FUNCNEST */ +/**/ +mod_export +zlong zsh_funcnest = +#ifdef MAX_FUNCTION_DEPTH + MAX_FUNCTION_DEPTH +#else + /* Disabled by default but can be enabled at run time */ + -1 +#endif + ; + +/**/ +zlong lineno, /* $LINENO */ + zoptind, /* $OPTIND */ + shlvl; /* $SHLVL */ + +/* $histchars */ + +/**/ +mod_export unsigned char bangchar; +/**/ +unsigned char hatchar, hashchar; + +/**/ +unsigned char keyboardhackchar = '\0'; + +/* $SECONDS = now.tv_sec - shtimer.tv_sec + * + (now.tv_usec - shtimer.tv_usec) / 1000000.0 + * (rounded to an integer if the parameter is not set to float) */ + +/**/ +struct timeval shtimer; + +/* 0 if this $TERM setup is usable, otherwise it contains TERM_* flags */ + +/**/ +mod_export int termflags; + +/* Forward declaration */ + +static void +rprompt_indent_unsetfn(Param pm, int exp); + +/* Standard methods for get/set/unset pointers in parameters */ + +/**/ +mod_export const struct gsu_scalar stdscalar_gsu = +{ strgetfn, strsetfn, stdunsetfn }; +/**/ +mod_export const struct gsu_scalar varscalar_gsu = +{ strvargetfn, strvarsetfn, stdunsetfn }; +/**/ +mod_export const struct gsu_scalar nullsetscalar_gsu = +{ strgetfn, nullstrsetfn, NULL }; + +/**/ +mod_export const struct gsu_integer stdinteger_gsu = +{ intgetfn, intsetfn, stdunsetfn }; +/**/ +mod_export const struct gsu_integer varinteger_gsu = +{ intvargetfn, intvarsetfn, stdunsetfn }; +/**/ +mod_export const struct gsu_integer nullsetinteger_gsu = +{ intgetfn, NULL, NULL }; + +/**/ +mod_export const struct gsu_float stdfloat_gsu = +{ floatgetfn, floatsetfn, stdunsetfn }; + +/**/ +mod_export const struct gsu_array stdarray_gsu = +{ arrgetfn, arrsetfn, stdunsetfn }; +/**/ +mod_export const struct gsu_array vararray_gsu = +{ arrvargetfn, arrvarsetfn, stdunsetfn }; + +/**/ +mod_export const struct gsu_hash stdhash_gsu = +{ hashgetfn, hashsetfn, stdunsetfn }; +/**/ +mod_export const struct gsu_hash nullsethash_gsu = +{ hashgetfn, nullsethashfn, nullunsetfn }; + + +/* Non standard methods (not exported) */ +static const struct gsu_integer pound_gsu = +{ poundgetfn, nullintsetfn, stdunsetfn }; +static const struct gsu_integer errno_gsu = +{ errnogetfn, errnosetfn, stdunsetfn }; +static const struct gsu_integer gid_gsu = +{ gidgetfn, gidsetfn, stdunsetfn }; +static const struct gsu_integer egid_gsu = +{ egidgetfn, egidsetfn, stdunsetfn }; +static const struct gsu_integer histsize_gsu = +{ histsizegetfn, histsizesetfn, stdunsetfn }; +static const struct gsu_integer random_gsu = +{ randomgetfn, randomsetfn, stdunsetfn }; +static const struct gsu_integer savehist_gsu = +{ savehistsizegetfn, savehistsizesetfn, stdunsetfn }; +static const struct gsu_integer intseconds_gsu = +{ intsecondsgetfn, intsecondssetfn, stdunsetfn }; +static const struct gsu_float floatseconds_gsu = +{ floatsecondsgetfn, floatsecondssetfn, stdunsetfn }; +static const struct gsu_integer uid_gsu = +{ uidgetfn, uidsetfn, stdunsetfn }; +static const struct gsu_integer euid_gsu = +{ euidgetfn, euidsetfn, stdunsetfn }; +static const struct gsu_integer ttyidle_gsu = +{ ttyidlegetfn, nullintsetfn, stdunsetfn }; + +static const struct gsu_scalar argzero_gsu = +{ argzerogetfn, argzerosetfn, nullunsetfn }; +static const struct gsu_scalar username_gsu = +{ usernamegetfn, usernamesetfn, stdunsetfn }; +static const struct gsu_scalar dash_gsu = +{ dashgetfn, nullstrsetfn, stdunsetfn }; +static const struct gsu_scalar histchars_gsu = +{ histcharsgetfn, histcharssetfn, stdunsetfn }; +static const struct gsu_scalar home_gsu = +{ homegetfn, homesetfn, stdunsetfn }; +static const struct gsu_scalar term_gsu = +{ termgetfn, termsetfn, stdunsetfn }; +static const struct gsu_scalar terminfo_gsu = +{ terminfogetfn, terminfosetfn, stdunsetfn }; +static const struct gsu_scalar terminfodirs_gsu = +{ terminfodirsgetfn, terminfodirssetfn, stdunsetfn }; +static const struct gsu_scalar wordchars_gsu = +{ wordcharsgetfn, wordcharssetfn, stdunsetfn }; +static const struct gsu_scalar ifs_gsu = +{ ifsgetfn, ifssetfn, stdunsetfn }; +static const struct gsu_scalar underscore_gsu = +{ underscoregetfn, nullstrsetfn, stdunsetfn }; +static const struct gsu_scalar keyboard_hack_gsu = +{ keyboardhackgetfn, keyboardhacksetfn, stdunsetfn }; +#ifdef USE_LOCALE +static const struct gsu_scalar lc_blah_gsu = +{ strgetfn, lcsetfn, stdunsetfn }; +static const struct gsu_scalar lang_gsu = +{ strgetfn, langsetfn, stdunsetfn }; +static const struct gsu_scalar lc_all_gsu = +{ strgetfn, lc_allsetfn, stdunsetfn }; +#endif + +static const struct gsu_integer varint_readonly_gsu = +{ intvargetfn, nullintsetfn, stdunsetfn }; +static const struct gsu_integer zlevar_gsu = +{ intvargetfn, zlevarsetfn, stdunsetfn }; + +static const struct gsu_scalar colonarr_gsu = +{ colonarrgetfn, colonarrsetfn, stdunsetfn }; + +static const struct gsu_integer argc_gsu = +{ poundgetfn, nullintsetfn, stdunsetfn }; +static const struct gsu_array pipestatus_gsu = +{ pipestatgetfn, pipestatsetfn, stdunsetfn }; + +static const struct gsu_integer rprompt_indent_gsu = +{ intvargetfn, zlevarsetfn, rprompt_indent_unsetfn }; + +/* Nodes for special parameters for parameter hash table */ + +#ifdef HAVE_UNION_INIT +# define BR(X) {X} +typedef struct param initparam; +#else +# define BR(X) X +typedef struct iparam { + struct hashnode *next; + char *nam; /* hash data */ + int flags; /* PM_* flags (defined in zsh.h) */ + void *value; + void *gsu; /* get/set/unset methods */ + int base; /* output base */ + int width; /* output field width */ + char *env; /* location in environment, if exported */ + char *ename; /* name of corresponding environment var */ + Param old; /* old struct for use with local */ + int level; /* if (old != NULL), level of localness */ +} initparam; +#endif + +static initparam special_params[] ={ +#define GSU(X) BR((GsuScalar)(void *)(&(X))) +#define NULL_GSU BR((GsuScalar)(void *)NULL) +#define IPDEF1(A,B,C) {{NULL,A,PM_INTEGER|PM_SPECIAL|C},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0} +IPDEF1("#", pound_gsu, PM_READONLY), +IPDEF1("ERRNO", errno_gsu, PM_UNSET), +IPDEF1("GID", gid_gsu, PM_DONTIMPORT | PM_RESTRICTED), +IPDEF1("EGID", egid_gsu, PM_DONTIMPORT | PM_RESTRICTED), +IPDEF1("HISTSIZE", histsize_gsu, PM_RESTRICTED), +IPDEF1("RANDOM", random_gsu, 0), +IPDEF1("SAVEHIST", savehist_gsu, PM_RESTRICTED), +IPDEF1("SECONDS", intseconds_gsu, 0), +IPDEF1("UID", uid_gsu, PM_DONTIMPORT | PM_RESTRICTED), +IPDEF1("EUID", euid_gsu, PM_DONTIMPORT | PM_RESTRICTED), +IPDEF1("TTYIDLE", ttyidle_gsu, PM_READONLY), + +#define IPDEF2(A,B,C) {{NULL,A,PM_SCALAR|PM_SPECIAL|C},BR(NULL),GSU(B),0,0,NULL,NULL,NULL,0} +IPDEF2("USERNAME", username_gsu, PM_DONTIMPORT|PM_RESTRICTED), +IPDEF2("-", dash_gsu, PM_READONLY), +IPDEF2("histchars", histchars_gsu, PM_DONTIMPORT), +IPDEF2("HOME", home_gsu, PM_UNSET), +IPDEF2("TERM", term_gsu, PM_UNSET), +IPDEF2("TERMINFO", terminfo_gsu, PM_UNSET), +IPDEF2("TERMINFO_DIRS", terminfodirs_gsu, PM_UNSET), +IPDEF2("WORDCHARS", wordchars_gsu, 0), +IPDEF2("IFS", ifs_gsu, PM_DONTIMPORT | PM_RESTRICTED), +IPDEF2("_", underscore_gsu, PM_DONTIMPORT), +IPDEF2("KEYBOARD_HACK", keyboard_hack_gsu, PM_DONTIMPORT), +IPDEF2("0", argzero_gsu, 0), + +#ifdef USE_LOCALE +# define LCIPDEF(name) IPDEF2(name, lc_blah_gsu, PM_UNSET) +IPDEF2("LANG", lang_gsu, PM_UNSET), +IPDEF2("LC_ALL", lc_all_gsu, PM_UNSET), +# ifdef LC_COLLATE +LCIPDEF("LC_COLLATE"), +# endif +# ifdef LC_CTYPE +LCIPDEF("LC_CTYPE"), +# endif +# ifdef LC_MESSAGES +LCIPDEF("LC_MESSAGES"), +# endif +# ifdef LC_NUMERIC +LCIPDEF("LC_NUMERIC"), +# endif +# ifdef LC_TIME +LCIPDEF("LC_TIME"), +# endif +#endif /* USE_LOCALE */ + +#define IPDEF4(A,B) {{NULL,A,PM_INTEGER|PM_READONLY|PM_SPECIAL},BR((void *)B),GSU(varint_readonly_gsu),10,0,NULL,NULL,NULL,0} +IPDEF4("!", &lastpid), +IPDEF4("$", &mypid), +IPDEF4("?", &lastval), +IPDEF4("HISTCMD", &curhist), +IPDEF4("LINENO", &lineno), +IPDEF4("PPID", &ppid), +IPDEF4("ZSH_SUBSHELL", &zsh_subshell), + +#define IPDEF5(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0} +#define IPDEF5U(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0} +IPDEF5("COLUMNS", &zterm_columns, zlevar_gsu), +IPDEF5("LINES", &zterm_lines, zlevar_gsu), +IPDEF5U("ZLE_RPROMPT_INDENT", &rprompt_indent, rprompt_indent_gsu), +IPDEF5("SHLVL", &shlvl, varinteger_gsu), +IPDEF5("FUNCNEST", &zsh_funcnest, varinteger_gsu), + +/* Don't import internal integer status variables. */ +#define IPDEF6(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0} +IPDEF6("OPTIND", &zoptind, varinteger_gsu), +IPDEF6("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu), +IPDEF6("TRY_BLOCK_INTERRUPT", &try_interrupt, varinteger_gsu), + +#define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0} +#define IPDEF7R(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_DONTIMPORT_SUID},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0} +#define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0} +IPDEF7("OPTARG", &zoptarg), +IPDEF7("NULLCMD", &nullcmd), +IPDEF7U("POSTEDIT", &postedit), +IPDEF7("READNULLCMD", &readnullcmd), +IPDEF7("PS1", &prompt), +IPDEF7U("RPS1", &rprompt), +IPDEF7U("RPROMPT", &rprompt), +IPDEF7("PS2", &prompt2), +IPDEF7U("RPS2", &rprompt2), +IPDEF7U("RPROMPT2", &rprompt2), +IPDEF7("PS3", &prompt3), +IPDEF7R("PS4", &prompt4), +IPDEF7("SPROMPT", &sprompt), + +#define IPDEF9F(A,B,C,D) {{NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(vararray_gsu),0,0,NULL,C,NULL,0} +#define IPDEF9(A,B,C) IPDEF9F(A,B,C,0) +IPDEF9F("*", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY), +IPDEF9F("@", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY), + +/* + * This empty row indicates the end of parameters available in + * all emulations. + */ +{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0}, + +#define IPDEF8(A,B,C,D) {{NULL,A,D|PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(colonarr_gsu),0,0,NULL,C,NULL,0} +IPDEF8("CDPATH", &cdpath, "cdpath", 0), +IPDEF8("FIGNORE", &fignore, "fignore", 0), +IPDEF8("FPATH", &fpath, "fpath", 0), +IPDEF8("MAILPATH", &mailpath, "mailpath", 0), +IPDEF8("WATCH", &watch, "watch", 0), +IPDEF8("PATH", &path, "path", PM_RESTRICTED), +IPDEF8("PSVAR", &psvar, "psvar", 0), +IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY), + +/* MODULE_PATH is not imported for security reasons */ +IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED), + +#define IPDEF10(A,B) {{NULL,A,PM_ARRAY|PM_SPECIAL},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0} + +/* + * The following parameters are not available in sh/ksh compatibility * + * mode. + */ + +/* All of these have sh compatible equivalents. */ +IPDEF1("ARGC", argc_gsu, PM_READONLY), +IPDEF2("HISTCHARS", histchars_gsu, PM_DONTIMPORT), +IPDEF4("status", &lastval), +IPDEF7("prompt", &prompt), +IPDEF7("PROMPT", &prompt), +IPDEF7("PROMPT2", &prompt2), +IPDEF7("PROMPT3", &prompt3), +IPDEF7("PROMPT4", &prompt4), +IPDEF8("MANPATH", &manpath, "manpath", 0), +IPDEF9("argv", &pparams, NULL), +IPDEF9("fignore", &fignore, "FIGNORE"), +IPDEF9("cdpath", &cdpath, "CDPATH"), +IPDEF9("fpath", &fpath, "FPATH"), +IPDEF9("mailpath", &mailpath, "MAILPATH"), +IPDEF9("manpath", &manpath, "MANPATH"), +IPDEF9("psvar", &psvar, "PSVAR"), +IPDEF9("watch", &watch, "WATCH"), + +IPDEF9F("zsh_eval_context", &zsh_eval_context, "ZSH_EVAL_CONTEXT", PM_READONLY), + +IPDEF9F("module_path", &module_path, "MODULE_PATH", PM_RESTRICTED), +IPDEF9F("path", &path, "PATH", PM_RESTRICTED), + +/* These are known to zsh alone. */ + +IPDEF10("pipestatus", pipestatus_gsu), + +{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0}, +}; + +/* + * Alternative versions of colon-separated path parameters for + * sh emulation. These don't link to the array versions. + */ +static initparam special_params_sh[] = { +IPDEF8("CDPATH", &cdpath, NULL, 0), +IPDEF8("FIGNORE", &fignore, NULL, 0), +IPDEF8("FPATH", &fpath, NULL, 0), +IPDEF8("MAILPATH", &mailpath, NULL, 0), +IPDEF8("WATCH", &watch, NULL, 0), +IPDEF8("PATH", &path, NULL, PM_RESTRICTED), +IPDEF8("PSVAR", &psvar, NULL, 0), +IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, NULL, PM_READONLY), + +/* MODULE_PATH is not imported for security reasons */ +IPDEF8("MODULE_PATH", &module_path, NULL, PM_DONTIMPORT|PM_RESTRICTED), + +{{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0}, +}; + +/* + * Special way of referring to the positional parameters. Unlike $* + * and $@, this is not readonly. This parameter is not directly + * visible in user space. + */ +static initparam argvparam_pm = IPDEF9F("", &pparams, NULL, \ + PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT); + +#undef BR + +#define IS_UNSET_VALUE(V) \ + ((V) && (!(V)->pm || ((V)->pm->node.flags & PM_UNSET) || \ + !(V)->pm->node.nam || !*(V)->pm->node.nam)) + +static Param argvparam; + +/* hash table containing the parameters */ + +/**/ +mod_export HashTable paramtab, realparamtab; + +/**/ +mod_export HashTable +newparamtable(int size, char const *name) +{ + HashTable ht; + if (!size) + size = 17; + ht = newhashtable(size, name, NULL); + + ht->hash = hasher; + ht->emptytable = emptyhashtable; + ht->filltable = NULL; + ht->cmpnodes = strcmp; + ht->addnode = addhashnode; + ht->getnode = getparamnode; + ht->getnode2 = gethashnode2; + ht->removenode = removehashnode; + ht->disablenode = NULL; + ht->enablenode = NULL; + ht->freenode = freeparamnode; + ht->printnode = printparamnode; + + return ht; +} + +/**/ +static HashNode +getparamnode(HashTable ht, const char *nam) +{ + HashNode hn = gethashnode2(ht, nam); + Param pm = (Param) hn; + + if (pm && pm->u.str && (pm->node.flags & PM_AUTOLOAD)) { + char *mn = dupstring(pm->u.str); + + (void)ensurefeature(mn, "p:", (pm->node.flags & PM_AUTOALL) ? NULL : + nam); + hn = gethashnode2(ht, nam); + if (!hn) { + /* + * This used to be a warning, but surely if we allow + * stuff to go ahead with the autoload stub with + * no error status we're in for all sorts of mayhem? + */ + zerr("autoloading module %s failed to define parameter: %s", mn, + nam); + } + } + return hn; +} + +/* Copy a parameter hash table */ + +static HashTable outtable; + +/**/ +static void +scancopyparams(HashNode hn, UNUSED(int flags)) +{ + /* Going into a real parameter, so always use permanent storage */ + Param pm = (Param)hn; + Param tpm = (Param) zshcalloc(sizeof *tpm); + tpm->node.nam = ztrdup(pm->node.nam); + copyparam(tpm, pm, 0); + addhashnode(outtable, tpm->node.nam, tpm); +} + +/**/ +HashTable +copyparamtable(HashTable ht, char *name) +{ + HashTable nht = 0; + if (ht) { + nht = newparamtable(ht->hsize, name); + outtable = nht; + scanhashtable(ht, 0, 0, 0, scancopyparams, 0); + outtable = NULL; + } + return nht; +} + +/* Flag to freeparamnode to unset the struct */ + +static int delunset; + +/* Function to delete a parameter table. */ + +/**/ +mod_export void +deleteparamtable(HashTable t) +{ + /* The parameters in the hash table need to be unset * + * before being deleted. */ + int odelunset = delunset; + delunset = 1; + deletehashtable(t); + delunset = odelunset; +} + +static unsigned numparamvals; + +/**/ +mod_export void +scancountparams(UNUSED(HashNode hn), int flags) +{ + ++numparamvals; + if ((flags & SCANPM_WANTKEYS) && (flags & SCANPM_WANTVALS)) + ++numparamvals; +} + +static Patprog scanprog; +static char *scanstr; +static char **paramvals; +static Param foundparam; + +/**/ +static void +scanparamvals(HashNode hn, int flags) +{ + struct value v; + Patprog prog; + + if (numparamvals && !(flags & SCANPM_MATCHMANY) && + (flags & (SCANPM_MATCHVAL|SCANPM_MATCHKEY|SCANPM_KEYMATCH))) + return; + v.pm = (Param)hn; + if ((flags & SCANPM_KEYMATCH)) { + char *tmp = dupstring(v.pm->node.nam); + + tokenize(tmp); + remnulargs(tmp); + + if (!(prog = patcompile(tmp, 0, NULL)) || !pattry(prog, scanstr)) + return; + } else if ((flags & SCANPM_MATCHKEY) && !pattry(scanprog, v.pm->node.nam)) { + return; + } + foundparam = v.pm; + if (flags & SCANPM_WANTKEYS) { + paramvals[numparamvals++] = v.pm->node.nam; + if (!(flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL))) + return; + } + v.isarr = (PM_TYPE(v.pm->node.flags) & (PM_ARRAY|PM_HASHED)); + v.flags = 0; + v.start = 0; + v.end = -1; + paramvals[numparamvals] = getstrvalue(&v); + if (flags & SCANPM_MATCHVAL) { + if (pattry(scanprog, paramvals[numparamvals])) { + numparamvals += ((flags & SCANPM_WANTVALS) ? 1 : + !(flags & SCANPM_WANTKEYS)); + } else if (flags & SCANPM_WANTKEYS) + --numparamvals; /* Value didn't match, discard key */ + } else + ++numparamvals; + foundparam = NULL; +} + +/**/ +char ** +paramvalarr(HashTable ht, int flags) +{ + DPUTS((flags & (SCANPM_MATCHKEY|SCANPM_MATCHVAL)) && !scanprog, + "BUG: scanning hash without scanprog set"); + numparamvals = 0; + if (ht) + scanhashtable(ht, 0, 0, PM_UNSET, scancountparams, flags); + paramvals = (char **) zhalloc((numparamvals + 1) * sizeof(char *)); + if (ht) { + numparamvals = 0; + scanhashtable(ht, 0, 0, PM_UNSET, scanparamvals, flags); + } + paramvals[numparamvals] = 0; + return paramvals; +} + +/* Return the full array (no indexing) referred to by a Value. * + * The array value is cached for the lifetime of the Value. */ + +/**/ +static char ** +getvaluearr(Value v) +{ + if (v->arr) + return v->arr; + else if (PM_TYPE(v->pm->node.flags) == PM_ARRAY) + return v->arr = v->pm->gsu.a->getfn(v->pm); + else if (PM_TYPE(v->pm->node.flags) == PM_HASHED) { + v->arr = paramvalarr(v->pm->gsu.h->getfn(v->pm), v->isarr); + /* Can't take numeric slices of associative arrays */ + v->start = 0; + v->end = numparamvals + 1; + return v->arr; + } else + return NULL; +} + +/* Return whether the variable is set * + * checks that array slices are within range * + * used for [[ -v ... ]] condition test */ + +/**/ +int +issetvar(char *name) +{ + struct value vbuf; + Value v; + int slice; + char **arr; + + if (!(v = getvalue(&vbuf, &name, 1)) || *name) + return 0; /* no value or more chars after the variable name */ + if (v->isarr & ~SCANPM_ARRONLY) + return v->end > 1; /* for extracted elements, end gives us a count */ + + slice = v->start != 0 || v->end != -1; + if (PM_TYPE(v->pm->node.flags) != PM_ARRAY || !slice) + return !slice && !(v->pm->node.flags & PM_UNSET); + + if (!v->end) /* empty array slice */ + return 0; + /* get the array and check end is within range */ + if (!(arr = getvaluearr(v))) + return 0; + return arrlen_ge(arr, v->end < 0 ? - v->end : v->end); +} + +/* + * Split environment string into (name, value) pair. + * this is used to avoid in-place editing of environment table + * that results in core dump on some systems + */ + +static int +split_env_string(char *env, char **name, char **value) +{ + char *str, *tenv; + + if (!env || !name || !value) + return 0; + + tenv = strcpy(zhalloc(strlen(env) + 1), env); + for (str = tenv; *str && *str != '='; str++) { + if (STOUC(*str) >= 128) { + /* + * We'll ignore environment variables with names not + * from the portable character set since we don't + * know of a good reason to accept them. + */ + return 0; + } + } + if (str != tenv && *str == '=') { + *str = '\0'; + *name = tenv; + *value = str + 1; + return 1; + } else + return 0; +} + +/** + * Check parameter flags to see if parameter shouldn't be imported + * from environment at start. + * + * return 1: don't import: 0: ok to import. + */ +static int dontimport(int flags) +{ + /* If explicitly marked as don't export */ + if (flags & PM_DONTIMPORT) + return 1; + /* If value already exported */ + if (flags & PM_EXPORTED) + return 1; + /* If security issue when importing and running with some privilege */ + if ((flags & PM_DONTIMPORT_SUID) && isset(PRIVILEGED)) + return 1; + /* OK to import */ + return 0; +} + +/* Set up parameter hash table. This will add predefined * + * parameter entries as well as setting up parameter table * + * entries for environment variables we inherit. */ + +/**/ +void +createparamtable(void) +{ + Param ip, pm; +#if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV) + char **new_environ; + int envsize; +#endif +#ifndef USE_SET_UNSET_ENV + char **envp; +#endif + char **envp2, **sigptr, **t; + char buf[50], *str, *iname, *ivalue, *hostnam; + int oae = opts[ALLEXPORT]; +#ifdef HAVE_UNAME + struct utsname unamebuf; + char *machinebuf; +#endif + + paramtab = realparamtab = newparamtable(151, "paramtab"); + + /* Add the special parameters to the hash table */ + for (ip = special_params; ip->node.nam; ip++) + paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip); + if (EMULATION(EMULATE_SH|EMULATE_KSH)) { + for (ip = special_params_sh; ip->node.nam; ip++) + paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip); + } else { + while ((++ip)->node.nam) + paramtab->addnode(paramtab, ztrdup(ip->node.nam), ip); + } + + argvparam = (Param) &argvparam_pm; + + noerrs = 2; + + /* Add the standard non-special parameters which have to * + * be initialized before we copy the environment variables. * + * We don't want to override whatever values the user has * + * given them in the environment. */ + opts[ALLEXPORT] = 0; + setiparam("MAILCHECK", 60); + setiparam("LOGCHECK", 60); + setiparam("KEYTIMEOUT", 40); + setiparam("LISTMAX", 100); + /* + * We used to get the output baud rate here. However, that's + * pretty irrelevant to a terminal on an X display and can lead + * to unnecessary delays if it's wrong (which it probably is). + * Furthermore, even if the output is slow it's very likely + * to be because of WAN delays, not covered by the output + * baud rate. + * So allow the user to set it in the special cases where it's + * useful. + */ + setsparam("TMPPREFIX", ztrdup_metafy(DEFAULT_TMPPREFIX)); + setsparam("TIMEFMT", ztrdup_metafy(DEFAULT_TIMEFMT)); + setsparam("WATCHFMT", ztrdup_metafy(default_watchfmt)); + + hostnam = (char *)zalloc(256); + gethostname(hostnam, 256); + setsparam("HOST", ztrdup_metafy(hostnam)); + zfree(hostnam, 256); + + setsparam("LOGNAME", + ztrdup_metafy((str = getlogin()) && *str ? + str : cached_username)); + +#if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV) + /* Copy the environment variables we are inheriting to dynamic * + * memory, so we can do mallocs and frees on it. */ + envsize = sizeof(char *)*(1 + arrlen(environ)); + new_environ = (char **) zalloc(envsize); + memcpy(new_environ, environ, envsize); + environ = new_environ; +#endif + + /* Use heap allocation to avoid many small alloc/free calls */ + pushheap(); + + /* Now incorporate environment variables we are inheriting * + * into the parameter hash table. Copy them into dynamic * + * memory so that we can free them if needed */ + for ( +#ifndef USE_SET_UNSET_ENV + envp = +#endif + envp2 = environ; *envp2; envp2++) { + if (split_env_string(*envp2, &iname, &ivalue)) { + if (!idigit(*iname) && isident(iname) && !strchr(iname, '[')) { + /* + * Parameters that aren't already in the parameter table + * aren't special to the shell, so it's always OK to + * import. Otherwise, check parameter flags. + */ + if ((!(pm = (Param) paramtab->getnode(paramtab, iname)) || + !dontimport(pm->node.flags)) && + (pm = assignsparam(iname, metafy(ivalue, -1, META_DUP), + ASSPM_ENV_IMPORT))) { + pm->node.flags |= PM_EXPORTED; + if (pm->node.flags & PM_SPECIAL) + pm->env = mkenvstr (pm->node.nam, + getsparam(pm->node.nam), pm->node.flags); + else + pm->env = ztrdup(*envp2); +#ifndef USE_SET_UNSET_ENV + *envp++ = pm->env; +#endif + } + } + } + } + popheap(); +#ifndef USE_SET_UNSET_ENV + *envp = NULL; +#endif + opts[ALLEXPORT] = oae; + + /* + * For native emulation we always set the variable home + * (see setupvals()). + */ + pm = (Param) paramtab->getnode(paramtab, "HOME"); + if (EMULATION(EMULATE_ZSH)) + { + pm->node.flags &= ~PM_UNSET; + if (!(pm->node.flags & PM_EXPORTED)) + addenv(pm, home); + } else if (!home) + pm->node.flags |= PM_UNSET; + pm = (Param) paramtab->getnode(paramtab, "LOGNAME"); + if (!(pm->node.flags & PM_EXPORTED)) + addenv(pm, pm->u.str); + pm = (Param) paramtab->getnode(paramtab, "SHLVL"); + sprintf(buf, "%d", (int)++shlvl); + /* shlvl value in environment needs updating unconditionally */ + addenv(pm, buf); + + /* Add the standard non-special parameters */ + set_pwd_env(); +#ifdef HAVE_UNAME + if(uname(&unamebuf)) setsparam("CPUTYPE", ztrdup("unknown")); + else + { + machinebuf = ztrdup_metafy(unamebuf.machine); + setsparam("CPUTYPE", machinebuf); + } + +#else + setsparam("CPUTYPE", ztrdup_metafy("unknown")); +#endif + setsparam("MACHTYPE", ztrdup_metafy(MACHTYPE)); + setsparam("OSTYPE", ztrdup_metafy(OSTYPE)); + setsparam("TTY", ztrdup_metafy(ttystrname)); + setsparam("VENDOR", ztrdup_metafy(VENDOR)); + setsparam("ZSH_ARGZERO", ztrdup(posixzero)); + setsparam("ZSH_VERSION", ztrdup_metafy(ZSH_VERSION)); + setsparam("ZSH_PATCHLEVEL", ztrdup_metafy(ZSH_PATCHLEVEL)); + setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *))); + for (t = sigs; (*sigptr++ = ztrdup_metafy(*t++)); ); + + noerrs = 0; +} + +/* assign various functions used for non-special parameters */ + +/**/ +mod_export void +assigngetset(Param pm) +{ + switch (PM_TYPE(pm->node.flags)) { + case PM_SCALAR: + pm->gsu.s = &stdscalar_gsu; + break; + case PM_INTEGER: + pm->gsu.i = &stdinteger_gsu; + break; + case PM_EFLOAT: + case PM_FFLOAT: + pm->gsu.f = &stdfloat_gsu; + break; + case PM_ARRAY: + pm->gsu.a = &stdarray_gsu; + break; + case PM_HASHED: + pm->gsu.h = &stdhash_gsu; + break; + default: + DPUTS(1, "BUG: tried to create param node without valid flag"); + break; + } +} + +/* Create a parameter, so that it can be assigned to. Returns NULL if the * + * parameter already exists or can't be created, otherwise returns the * + * parameter node. If a parameter of the same name exists in an outer * + * scope, it is hidden by a newly created parameter. An already existing * + * parameter node at the current level may be `created' and returned * + * provided it is unset and not special. If the parameter can't be * + * created because it already exists, the PM_UNSET flag is cleared. */ + +/**/ +mod_export Param +createparam(char *name, int flags) +{ + Param pm, oldpm; + + if (paramtab != realparamtab) + flags = (flags & ~PM_EXPORTED) | PM_HASHELEM; + + if (name != nulstring) { + oldpm = (Param) (paramtab == realparamtab ? + /* gethashnode2() for direct table read */ + gethashnode2(paramtab, name) : + paramtab->getnode(paramtab, name)); + + DPUTS(oldpm && oldpm->level > locallevel, + "BUG: old local parameter not deleted"); + if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) { + if (isset(POSIXBUILTINS) && (oldpm->node.flags & PM_READONLY)) { + zerr("read-only variable: %s", name); + return NULL; + } + if ((oldpm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) { + zerr("%s: restricted", name); + return NULL; + } + if (!(oldpm->node.flags & PM_UNSET) || + (oldpm->node.flags & PM_SPECIAL) || + /* POSIXBUILTINS horror: we need to retain 'export' flags */ + (isset(POSIXBUILTINS) && (oldpm->node.flags & PM_EXPORTED))) { + oldpm->node.flags &= ~PM_UNSET; + if ((oldpm->node.flags & PM_SPECIAL) && oldpm->ename) { + Param altpm = + (Param) paramtab->getnode(paramtab, oldpm->ename); + if (altpm) + altpm->node.flags &= ~PM_UNSET; + } + return NULL; + } + + pm = oldpm; + pm->base = pm->width = 0; + oldpm = pm->old; + } else { + pm = (Param) zshcalloc(sizeof *pm); + if ((pm->old = oldpm)) { + /* + * needed to avoid freeing oldpm, but we do take it + * out of the environment when it's hidden. + */ + if (oldpm->env) + delenv(oldpm); + paramtab->removenode(paramtab, name); + } + paramtab->addnode(paramtab, ztrdup(name), pm); + } + + if (isset(ALLEXPORT) && !(flags & PM_HASHELEM)) + flags |= PM_EXPORTED; + } else { + pm = (Param) hcalloc(sizeof *pm); + pm->node.nam = nulstring; + } + pm->node.flags = flags & ~PM_LOCAL; + + if(!(pm->node.flags & PM_SPECIAL)) + assigngetset(pm); + return pm; +} + +/* Empty dummy function for special hash parameters. */ + +/**/ +static void +shempty(void) +{ +} + +/* + * Create a simple special hash parameter. + * + * This is for hashes added internally --- it's not possible to add + * special hashes from shell commands. It's currently used + * - by addparamdef() for special parameters in the zsh/parameter + * module + * - by ztie for special parameters tied to databases. + */ + +/**/ +mod_export Param +createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan, int flags) +{ + Param pm; + HashTable ht; + + if (!(pm = createparam(name, PM_SPECIAL|PM_HASHED|flags))) + return NULL; + + /* + * If there's an old parameter, we'll put the new one at + * the current locallevel, so that the old parameter is + * exposed again after leaving the function. Otherwise, + * we'll leave it alone. Usually this means the parameter + * will stay in place until explicitly unloaded, however + * if the parameter was previously unset within a function + * we'll inherit the level of that function and follow the + * standard convention that the parameter remains local + * even if unset. + * + * These semantics are similar to those of a normal parameter set + * within a function without a local definition. + */ + if (pm->old) + pm->level = locallevel; + pm->gsu.h = (flags & PM_READONLY) ? &stdhash_gsu : + &nullsethash_gsu; + pm->u.hash = ht = newhashtable(0, name, NULL); + + ht->hash = hasher; + ht->emptytable = (TableFunc) shempty; + ht->filltable = NULL; + ht->addnode = (AddNodeFunc) shempty; + ht->getnode = ht->getnode2 = get; + ht->removenode = (RemoveNodeFunc) shempty; + ht->disablenode = NULL; + ht->enablenode = NULL; + ht->freenode = (FreeNodeFunc) shempty; + ht->printnode = printparamnode; + ht->scantab = scan; + + return pm; +} + + +/* + * Copy a parameter + * + * If fakecopy is set, we are just saving the details of a special + * parameter. Otherwise, the result will be used as a real parameter + * and we need to do more work. + */ + +/**/ +void +copyparam(Param tpm, Param pm, int fakecopy) +{ + /* + * Note that tpm, into which we're copying, may not be in permanent + * storage. However, the values themselves are later used directly + * to set the parameter, so must be permanently allocated (in accordance + * with sets.?fn() usage). + */ + tpm->node.flags = pm->node.flags; + tpm->base = pm->base; + tpm->width = pm->width; + tpm->level = pm->level; + if (!fakecopy) + tpm->node.flags &= ~PM_SPECIAL; + switch (PM_TYPE(pm->node.flags)) { + case PM_SCALAR: + tpm->u.str = ztrdup(pm->gsu.s->getfn(pm)); + break; + case PM_INTEGER: + tpm->u.val = pm->gsu.i->getfn(pm); + break; + case PM_EFLOAT: + case PM_FFLOAT: + tpm->u.dval = pm->gsu.f->getfn(pm); + break; + case PM_ARRAY: + tpm->u.arr = zarrdup(pm->gsu.a->getfn(pm)); + break; + case PM_HASHED: + tpm->u.hash = copyparamtable(pm->gsu.h->getfn(pm), pm->node.nam); + break; + } + /* + * If the value is going to be passed as a real parameter (e.g. this is + * called from inside an associative array), we need the gets and sets + * functions to be useful. + * + * In this case we assume the saved parameter is not itself special, + * so we just use the standard functions. This is also why we switch off + * PM_SPECIAL. + */ + if (!fakecopy) + assigngetset(tpm); +} + +/* Return 1 if the string s is a valid identifier, else return 0. */ + +/**/ +mod_export int +isident(char *s) +{ + char *ss; + + if (!*s) /* empty string is definitely not valid */ + return 0; + + if (idigit(*s)) { + /* If the first character is `s' is a digit, then all must be */ + for (ss = ++s; *ss; ss++) + if (!idigit(*ss)) + break; + } else { + /* Find the first character in `s' not in the iident type table */ + ss = itype_end(s, IIDENT, 0); + } + + /* If the next character is not [, then it is * + * definitely not a valid identifier. */ + if (!*ss) + return 1; + if (s == ss) + return 0; + if (*ss != '[') + return 0; + + /* Require balanced [ ] pairs with something between */ + if (!(ss = parse_subscript(++ss, 1, ']'))) + return 0; + untokenize(s); + return !ss[1]; +} + +/* + * Parse a single argument to a parameter subscript. + * The subscripts starts at *str; *str is updated (input/output) + * + * *inv is set to indicate if the subscript is reversed (output) + * v is the Value for the parameter being accessed (input; note + * v->isarr may be modified, and if v is a hash the parameter will + * be updated to the element of the hash) + * a2 is 1 if this is the second subscript of a range (input) + * *w is only set if we need to find the end of a word (input; should + * be set to 0 by the caller). + * + * The final two arguments are to support multibyte characters. + * If supplied they are set to the length of the character before + * the index position and the one at the index position. If + * multibyte characters are not in use they are set to 1 for + * consistency. Note they aren't fully handled if a2 is non-zero, + * since they aren't needed. + * + * Returns a raw offset into the value from the start or end (i.e. + * after the arithmetic for Meta and possible multibyte characters has + * been taken into account). This actually gives the offset *after* + * the character in question; subtract *prevcharlen if necessary. + */ + +/**/ +static zlong +getarg(char **str, int *inv, Value v, int a2, zlong *w, + int *prevcharlen, int *nextcharlen, int flags) +{ + int hasbeg = 0, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash; + int keymatch = 0, needtok = 0, arglen, len, inpar = 0; + char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt, c; + zlong num = 1, beg = 0, r = 0, quote_arg = 0; + Patprog pprog = NULL; + + /* + * If in NO_EXEC mode, the parameters won't be set up properly, + * so just pretend everything is a hash for subscript parsing + */ + + ishash = (unset(EXECOPT) || + (v->pm && PM_TYPE(v->pm->node.flags) == PM_HASHED)); + if (prevcharlen) + *prevcharlen = 1; + if (nextcharlen) + *nextcharlen = 1; + + /* first parse any subscription flags */ + if (v->pm && (*s == '(' || *s == Inpar)) { + int escapes = 0; + int waste; + for (s++; *s != ')' && *s != Outpar && s != *str; s++) { + switch (*s) { + case 'r': + rev = 1; + keymatch = down = ind = 0; + break; + case 'R': + rev = down = 1; + keymatch = ind = 0; + break; + case 'k': + keymatch = ishash; + rev = 1; + down = ind = 0; + break; + case 'K': + keymatch = ishash; + rev = down = 1; + ind = 0; + break; + case 'i': + rev = ind = 1; + down = keymatch = 0; + break; + case 'I': + rev = ind = down = 1; + keymatch = 0; + break; + case 'w': + /* If the parameter is a scalar, then make subscription * + * work on a per-word basis instead of characters. */ + word = 1; + break; + case 'f': + word = 1; + sep = "\n"; + break; + case 'e': + quote_arg = 1; + break; + case 'n': + t = get_strarg(++s, &arglen); + if (!*t) + goto flagerr; + sav = *t; + *t = '\0'; + num = mathevalarg(s + arglen, &d); + if (!num) + num = 1; + *t = sav; + s = t + arglen - 1; + break; + case 'b': + hasbeg = 1; + t = get_strarg(++s, &arglen); + if (!*t) + goto flagerr; + sav = *t; + *t = '\0'; + if ((beg = mathevalarg(s + arglen, &d)) > 0) + beg--; + *t = sav; + s = t + arglen - 1; + break; + case 'p': + escapes = 1; + break; + case 's': + /* This gives the string that separates words * + * (for use with the `w' flag). */ + t = get_strarg(++s, &arglen); + if (!*t) + goto flagerr; + sav = *t; + *t = '\0'; + s += arglen; + sep = escapes ? getkeystring(s, &waste, GETKEYS_SEP, NULL) + : dupstring(s); + *t = sav; + s = t + arglen - 1; + break; + default: + flagerr: + num = 1; + word = rev = ind = down = keymatch = 0; + sep = NULL; + s = *str - 1; + } + } + if (s != *str) + s++; + } + if (num < 0) { + down = !down; + num = -num; + } + if (v->isarr & SCANPM_WANTKEYS) + *inv = (ind || !(v->isarr & SCANPM_WANTVALS)); + else if (v->isarr & SCANPM_WANTVALS) + *inv = 0; + else { + if (v->isarr) { + if (ind) { + v->isarr |= SCANPM_WANTKEYS; + v->isarr &= ~SCANPM_WANTVALS; + } else if (rev) + v->isarr |= SCANPM_WANTVALS; + /* + * This catches the case where we are using "k" (rather + * than "K") on a hash. + */ + if (!down && keymatch && ishash) + v->isarr &= ~SCANPM_MATCHMANY; + } + *inv = ind; + } + + for (t = s, i = 0; + (c = *t) && + ((c != Outbrack && (ishash || c != ',')) || i || inpar); + t++) { + /* Untokenize inull() except before brackets and double-quotes */ + if (inull(c)) { + c = t[1]; + if (c == '[' || c == ']' || + c == '(' || c == ')' || + c == '{' || c == '}') { + /* This test handles nested subscripts in hash keys */ + if (ishash && i) + *t = ztokens[*t - Pound]; + needtok = 1; + ++t; + } else if (c != '"') + *t = ztokens[*t - Pound]; + continue; + } + /* Inbrack and Outbrack are probably never found here ... */ + if (c == '[' || c == Inbrack) + i++; + else if (c == ']' || c == Outbrack) + i--; + if (c == '(' || c == Inpar) + inpar++; + else if (c == ')' || c == Outpar) + inpar--; + if (ispecial(c)) + needtok = 1; + } + if (!c) + return 0; + *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 * + * subscript arithmetic is performed (for nested subscripts). */ + if (ishash && (keymatch || !rev)) + remnulargs(s); + if (needtok) { + s = dupstring(s); + if (parsestr(&s)) + return 0; + singsub(&s); + } else if (rev) + remnulargs(s); /* This is probably always a no-op, but ... */ + if (!rev) { + if (ishash) { + HashTable ht = v->pm->gsu.h->getfn(v->pm); + if (!ht) { + if (flags & SCANPM_CHECKING) + return 0; + ht = newparamtable(17, v->pm->node.nam); + v->pm->gsu.h->setfn(v->pm, ht); + } + untokenize(s); + if (!(v->pm = (Param) ht->getnode(ht, s))) { + HashTable tht = paramtab; + paramtab = ht; + v->pm = createparam(s, PM_SCALAR|PM_UNSET); + paramtab = tht; + } + v->isarr = (*inv ? SCANPM_WANTINDEX : 0); + v->start = 0; + *inv = 0; /* We've already obtained the "index" (key) */ + *w = v->end = -1; + r = isset(KSHARRAYS) ? 1 : 0; + } else { + r = mathevalarg(s, &s); + if (isset(KSHARRAYS) && r >= 0) + r++; + } + if (word && !v->isarr) { + s = t = getstrvalue(v); + i = wordcount(s, sep, 0); + if (r < 0) + r += i + 1; + if (r < 1) + r = 1; + if (r > i) + r = i; + if (!s || !*s) + return 0; + while ((d = findword(&s, sep)) && --r); + if (!d) + return 0; + + if (!a2 && *tt != ',') + *w = (zlong)(s - t); + + return (a2 ? s : d + 1) - t; + } else if (!v->isarr && !word) { + int lastcharlen = 1; + s = getstrvalue(v); + /* + * Note for the confused (= pws): the index r we + * have so far is that specified by the user. The value + * passed back is an offset from the start or end of + * the string. Hence it needs correcting at least + * for Meta characters and maybe for multibyte characters. + */ + if (r > 0) { + zlong nchars = r; + + MB_METACHARINIT(); + for (t = s; nchars && *t; nchars--) + t += (lastcharlen = MB_METACHARLEN(t)); + /* for consistency, keep any remainder off the end */ + r = (zlong)(t - s) + nchars; + if (prevcharlen && !nchars /* ignore if off the end */) + *prevcharlen = lastcharlen; + if (nextcharlen && *t) + *nextcharlen = MB_METACHARLEN(t); + } else if (r == 0) { + if (prevcharlen) + *prevcharlen = 0; + if (nextcharlen && *s) { + MB_METACHARINIT(); + *nextcharlen = MB_METACHARLEN(s); + } + } else { + zlong nchars = (zlong)MB_METASTRLEN(s) + r; + + if (nchars < 0) { + /* make sure this isn't valid as a raw pointer */ + r -= (zlong)strlen(s); + } else { + MB_METACHARINIT(); + for (t = s; nchars && *t; nchars--) + t += (lastcharlen = MB_METACHARLEN(t)); + r = - (zlong)strlen(t); /* keep negative */ + if (prevcharlen) + *prevcharlen = lastcharlen; + if (nextcharlen && *t) + *nextcharlen = MB_METACHARLEN(t); + } + } + } + } else { + if (!v->isarr && !word && !quote_arg) { + l = strlen(s); + if (a2) { + if (!l || *s != '*') { + d = (char *) hcalloc(l + 2); + *d = '*'; + strcpy(d + 1, s); + s = d; + } + } else { + if (!l || s[l - 1] != '*' || (l > 1 && s[l - 2] == '\\')) { + d = (char *) hcalloc(l + 2); + strcpy(d, s); + strcat(d, "*"); + s = d; + } + } + } + if (!keymatch) { + if (quote_arg) { + untokenize(s); + /* Scalar (e) needs implicit asterisk tokens */ + if (!v->isarr && !word) { + l = strlen(s); + d = (char *) hcalloc(l + 2); + if (a2) { + *d = Star; + strcpy(d + 1, s); + } else { + strcpy(d, s); + d[l] = Star; + d[l + 1] = '\0'; + } + s = d; + } + } else + tokenize(s); + remnulargs(s); + pprog = patcompile(s, 0, NULL); + } else + pprog = NULL; + + if (v->isarr) { + if (ishash) { + scanprog = pprog; + scanstr = s; + if (keymatch) + v->isarr |= SCANPM_KEYMATCH; + else { + if (!pprog) + return 1; + if (ind) + v->isarr |= SCANPM_MATCHKEY; + else + v->isarr |= SCANPM_MATCHVAL; + } + if (down) + v->isarr |= SCANPM_MATCHMANY; + if ((ta = getvaluearr(v)) && + (*ta || ((v->isarr & SCANPM_MATCHMANY) && + (v->isarr & (SCANPM_MATCHKEY | SCANPM_MATCHVAL | + SCANPM_KEYMATCH))))) { + *inv = (v->flags & VALFLAG_INV) ? 1 : 0; + *w = v->end; + scanprog = NULL; + return 1; + } + scanprog = NULL; + } else + ta = getarrvalue(v); + if (!ta || !*ta) + return !down; + len = arrlen(ta); + if (beg < 0) + beg += len; + if (down) { + if (beg < 0) + return 0; + } else if (beg >= len) + return len + 1; + if (beg >= 0 && beg < len) { + if (down) { + if (!hasbeg) + beg = len - 1; + for (r = 1 + beg, p = ta + beg; p >= ta; r--, p--) { + if (pprog && pattry(pprog, *p) && !--num) + return r; + } + } else + for (r = 1 + beg, p = ta + beg; *p; r++, p++) + if (pprog && pattry(pprog, *p) && !--num) + return r; + } + } else if (word) { + ta = sepsplit(d = s = getstrvalue(v), sep, 1, 1); + len = arrlen(ta); + if (beg < 0) + beg += len; + if (down) { + if (beg < 0) + return 0; + } else if (beg >= len) + return len + 1; + if (beg >= 0 && beg < len) { + if (down) { + if (!hasbeg) + beg = len - 1; + for (r = 1 + beg, p = ta + beg; p >= ta; p--, r--) + if (pprog && pattry(pprog, *p) && !--num) + break; + if (p < ta) + return 0; + } else { + for (r = 1 + beg, p = ta + beg; *p; r++, p++) + if (pprog && pattry(pprog, *p) && !--num) + break; + if (!*p) + return 0; + } + } + if (a2) + r++; + for (i = 0; (t = findword(&d, sep)) && *t; i++) + if (!--r) { + r = (zlong)(t - s + (a2 ? -1 : 1)); + if (!a2 && *tt != ',') + *w = r + strlen(ta[i]) - 1; + return r; + } + return a2 ? -1 : 0; + } else { + /* Searching characters */ + int slen; + d = getstrvalue(v); + if (!d || !*d) + return 0; + /* + * beg and len are character counts, not raw offsets. + * Remember we need to return a raw offset. + */ + len = MB_METASTRLEN(d); + slen = strlen(d); + if (beg < 0) + beg += len; + MB_METACHARINIT(); + if (beg >= 0 && beg < len) { + char *de = d + slen; + + if (a2) { + /* + * Second argument: we don't need to + * handle prevcharlen or nextcharlen, but + * we do need to handle characters appropriately. + */ + if (down) { + int nmatches = 0; + char *lastpos = NULL; + + if (!hasbeg) + beg = len; + + /* + * See below: we have to move forward, + * but need to count from the end. + */ + for (t = d, r = 0; r <= beg; r++) { + sav = *t; + *t = '\0'; + if (pprog && pattry(pprog, d)) { + nmatches++; + lastpos = t; + } + *t = sav; + if (t == de) + break; + t += MB_METACHARLEN(t); + } + + if (nmatches >= num) { + if (num > 1) { + nmatches -= num; + MB_METACHARINIT(); + for (t = d, r = 0; ; r++) { + sav = *t; + *t = '\0'; + if (pprog && pattry(pprog, d) && + nmatches-- == 0) { + lastpos = t; + *t = sav; + break; + } + *t = sav; + t += MB_METACHARLEN(t); + } + } + /* else lastpos is already OK */ + + return lastpos - d; + } + } else { + /* + * This handling of the b flag + * gives odd results, but this is the + * way it's always worked. + */ + for (t = d; beg && t <= de; beg--) + t += MB_METACHARLEN(t); + for (;;) { + sav = *t; + *t = '\0'; + if (pprog && pattry(pprog, d) && !--num) { + *t = sav; + /* + * This time, don't increment + * pointer, since it's already + * after everything we matched. + */ + return t - d; + } + *t = sav; + if (t == de) + break; + t += MB_METACHARLEN(t); + } + } + } else { + /* + * First argument: this is the only case + * where we need prevcharlen and nextcharlen. + */ + int lastcharlen; + + if (down) { + int nmatches = 0; + char *lastpos = NULL; + + if (!hasbeg) + beg = len; + + /* + * We can only move forward through + * multibyte strings, so record the + * matches. + * Unfortunately the count num works + * from the end, so it's easy to get the + * last one but we need to repeat if + * we want another one. + */ + for (t = d, r = 0; r <= beg; r++) { + if (pprog && pattry(pprog, t)) { + nmatches++; + lastpos = t; + } + if (t == de) + break; + t += MB_METACHARLEN(t); + } + + if (nmatches >= num) { + if (num > 1) { + /* + * Need to start again and repeat + * to get the right match. + */ + nmatches -= num; + MB_METACHARINIT(); + for (t = d, r = 0; ; r++) { + if (pprog && pattry(pprog, t) && + nmatches-- == 0) { + lastpos = t; + break; + } + t += MB_METACHARLEN(t); + } + } + /* else lastpos is already OK */ + + /* return pointer after matched char */ + lastpos += + (lastcharlen = MB_METACHARLEN(lastpos)); + if (prevcharlen) + *prevcharlen = lastcharlen; + if (nextcharlen) + *nextcharlen = MB_METACHARLEN(lastpos); + return lastpos - d; + } + + for (r = beg + 1, t = d + beg; t >= d; r--, t--) { + if (pprog && pattry(pprog, t) && + !--num) + return r; + } + } else { + for (t = d; beg && t <= de; beg--) + t += MB_METACHARLEN(t); + for (;;) { + if (pprog && pattry(pprog, t) && !--num) { + /* return pointer after matched char */ + t += (lastcharlen = MB_METACHARLEN(t)); + if (prevcharlen) + *prevcharlen = lastcharlen; + if (nextcharlen) + *nextcharlen = MB_METACHARLEN(t); + return t - d; + } + if (t == de) + break; + t += MB_METACHARLEN(t); + } + } + } + } + return down ? 0 : slen + 1; + } + } + return r; +} + +/* + * Parse a subscript. + * + * pptr: In/Out parameter. On entry, *ptr points to a "[foo]" string. On exit + * it will point one past the closing bracket. + * + * v: In/Out parameter. Its .start and .end members (at least) will be updated + * with the parsed indices. + * + * flags: can be either SCANPM_DQUOTED or zero. Other bits are not used. + */ + +/**/ +int +getindex(char **pptr, Value v, int flags) +{ + int start, end, inv = 0; + char *s = *pptr, *tbrack; + + *s++ = '['; + /* Error handled after untokenizing */ + s = parse_subscript(s, flags & SCANPM_DQUOTED, ']'); + /* Now we untokenize everything except inull() markers so we can check * + * for the '*' and '@' special subscripts. The inull()s are removed * + * in getarg() after we know whether we're doing reverse indexing. */ + for (tbrack = *pptr + 1; *tbrack && tbrack != s; tbrack++) { + if (inull(*tbrack) && !*++tbrack) + break; + if (itok(*tbrack)) /* Need to check for Nularg here? */ + *tbrack = ztokens[*tbrack - Pound]; + } + /* If we reached the end of the string (s == NULL) we have an error */ + if (*tbrack) + *tbrack = Outbrack; + else { + zerr("invalid subscript"); + *pptr = tbrack; + return 1; + } + s = *pptr + 1; + if ((s[0] == '*' || s[0] == '@') && s + 1 == tbrack) { + if ((v->isarr || IS_UNSET_VALUE(v)) && s[0] == '@') + v->isarr |= SCANPM_ISVAR_AT; + v->start = 0; + v->end = -1; + s += 2; + } else { + zlong we = 0, dummy; + int startprevlen, startnextlen; + + start = getarg(&s, &inv, v, 0, &we, &startprevlen, &startnextlen, + flags); + + if (inv) { + if (!v->isarr && start != 0) { + char *t, *p; + t = getstrvalue(v); + /* + * Note for the confused (= pws): this is an inverse + * offset so at this stage we need to convert from + * the immediate offset into the value that we have + * into a logical character position. + */ + if (start > 0) { + int nstart = 0; + char *target = t + start - startprevlen; + + p = t; + MB_METACHARINIT(); + while (*p) { + /* + * move up characters, counting how many we + * found + */ + p += MB_METACHARLEN(p); + if (p < target) + nstart++; + else { + if (p == target) + nstart++; + else + p = target; /* pretend we hit exactly */ + break; + } + } + /* if start was too big, keep the difference */ + start = nstart + (target - p) + 1; + } else { + zlong startoff = start + strlen(t); +#ifdef DEBUG + dputs("BUG: can't have negative inverse offsets???"); +#endif + if (startoff < 0) { + /* invalid: keep index but don't dereference */ + start = startoff; + } else { + /* find start in full characters */ + MB_METACHARINIT(); + for (p = t; p < t + startoff;) + p += MB_METACHARLEN(p); + start = - MB_METASTRLEN(p); + } + } + } + if (start > 0 && (isset(KSHARRAYS) || (v->pm->node.flags & PM_HASHED))) + start--; + if (v->isarr != SCANPM_WANTINDEX) { + v->flags |= VALFLAG_INV; + v->isarr = 0; + v->start = start; + v->end = start + 1; + } + if (*s == ',') { + zerr("invalid subscript"); + *tbrack = ']'; + *pptr = tbrack+1; + return 1; + } + if (s == tbrack) + s++; + } else { + int com; + + if ((com = (*s == ','))) { + s++; + end = getarg(&s, &inv, v, 1, &dummy, NULL, NULL, flags); + } else { + end = we ? we : start; + } + if (start != end) + com = 1; + /* + * Somehow the logic sometimes forces us to use the previous + * or next character to what we would expect, which is + * why we had to calculate them in getarg(). + */ + if (start > 0) + start -= startprevlen; + else if (start == 0 && end == 0) + { + /* + * Strictly, this range is entirely off the + * start of the available index range. + * This can't happen with KSH_ARRAYS; we already + * altered the start index in getarg(). + * Are we being strict? + */ + if (isset(KSHZEROSUBSCRIPT)) { + /* + * We're not. + * Treat this as accessing the first element of the + * array. + */ + end = startnextlen; + } else { + /* + * We are. Flag that this range is invalid + * for setting elements. Set the indexes + * to a range that returns empty for other accesses. + */ + v->flags |= VALFLAG_EMPTY; + start = -1; + com = 1; + } + } + if (s == tbrack) { + s++; + if (v->isarr && !com && + (!(v->isarr & SCANPM_MATCHMANY) || + !(v->isarr & (SCANPM_MATCHKEY | SCANPM_MATCHVAL | + SCANPM_KEYMATCH)))) + v->isarr = 0; + v->start = start; + v->end = end; + } else + s = *pptr; + } + } + *tbrack = ']'; + *pptr = s; + return 0; +} + + +/**/ +mod_export Value +getvalue(Value v, char **pptr, int bracks) +{ + return fetchvalue(v, pptr, bracks, 0); +} + +/**/ +mod_export Value +fetchvalue(Value v, char **pptr, int bracks, int flags) +{ + char *s, *t, *ie; + char sav, c; + int ppar = 0; + + s = t = *pptr; + + if (idigit(c = *s)) { + if (bracks >= 0) + ppar = zstrtol(s, &s, 10); + else + ppar = *s++ - '0'; + } + else if ((ie = itype_end(s, IIDENT, 0)) != s) + s = ie; + else if (c == Quest) + *s++ = '?'; + else if (c == Pound) + *s++ = '#'; + else if (c == String) + *s++ = '$'; + else if (c == Qstring) + *s++ = '$'; + else if (c == Star) + *s++ = '*'; + else if (IS_DASH(c)) + *s++ = '-'; + else if (c == '#' || c == '?' || c == '$' || + c == '!' || c == '@' || c == '*') + s++; + else + return NULL; + + if ((sav = *s)) + *s = '\0'; + if (ppar) { + if (v) + memset(v, 0, sizeof(*v)); + else + v = (Value) hcalloc(sizeof *v); + v->pm = argvparam; + v->flags = 0; + v->start = ppar - 1; + v->end = ppar; + if (sav) + *s = sav; + } else { + Param pm; + int isvarat; + + isvarat = (t[0] == '@' && !t[1]); + pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t); + if (sav) + *s = sav; + *pptr = s; + if (!pm || (pm->node.flags & PM_UNSET)) + return NULL; + if (v) + memset(v, 0, sizeof(*v)); + else + v = (Value) hcalloc(sizeof *v); + if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) { + /* Overload v->isarr as the flag bits for hashed arrays. */ + v->isarr = flags | (isvarat ? SCANPM_ISVAR_AT : 0); + /* If no flags were passed, we need something to represent * + * `true' yet differ from an explicit WANTVALS. Use a * + * special flag for this case. */ + if (!v->isarr) + v->isarr = SCANPM_ARRONLY; + } + v->pm = pm; + v->flags = 0; + v->start = 0; + v->end = -1; + if (bracks > 0 && (*s == '[' || *s == Inbrack)) { + if (getindex(&s, v, flags)) { + *pptr = s; + return v; + } + } else if (!(flags & SCANPM_ASSIGNING) && v->isarr && + itype_end(t, IIDENT, 1) != t && isset(KSHARRAYS)) + v->end = 1, v->isarr = 0; + } + if (!bracks && *s) + return NULL; + *pptr = s; +#if 0 + /* + * Check for large subscripts that might be erroneous. + * This code is too gross in several ways: + * - the limit is completely arbitrary + * - the test vetoes operations on existing arrays + * - it's not at all clear a general test on large arrays of + * this kind is any use. + * + * Until someone comes up with workable replacement code it's + * therefore commented out. + */ + if (v->start > MAX_ARRLEN) { + zerr("subscript too %s: %d", "big", v->start + !isset(KSHARRAYS)); + return NULL; + } + if (v->start < -MAX_ARRLEN) { + zerr("subscript too %s: %d", "small", v->start); + return NULL; + } + if (v->end > MAX_ARRLEN+1) { + zerr("subscript too %s: %d", "big", v->end - !!isset(KSHARRAYS)); + return NULL; + } + if (v->end < -MAX_ARRLEN) { + zerr("subscript too %s: %d", "small", v->end); + return NULL; + } +#endif + return v; +} + +/**/ +mod_export char * +getstrvalue(Value v) +{ + char *s, **ss; + char buf[BDIGBUFSIZE]; + int len; + + if (!v) + return hcalloc(1); + + if ((v->flags & VALFLAG_INV) && !(v->pm->node.flags & PM_HASHED)) { + sprintf(buf, "%d", v->start); + s = dupstring(buf); + return s; + } + + switch(PM_TYPE(v->pm->node.flags)) { + case PM_HASHED: + /* (!v->isarr) should be impossible unless emulating ksh */ + if (!v->isarr && EMULATION(EMULATE_KSH)) { + s = dupstring("[0]"); + if (getindex(&s, v, 0) == 0) + s = getstrvalue(v); + return s; + } /* else fall through */ + case PM_ARRAY: + ss = getvaluearr(v); + if (v->isarr) + s = sepjoin(ss, NULL, 1); + else { + if (v->start < 0) + v->start += arrlen(ss); + s = (arrlen_le(ss, v->start) || v->start < 0) ? + (char *) hcalloc(1) : ss[v->start]; + } + return s; + case PM_INTEGER: + convbase(buf, v->pm->gsu.i->getfn(v->pm), v->pm->base); + s = dupstring(buf); + break; + case PM_EFLOAT: + case PM_FFLOAT: + s = convfloat(v->pm->gsu.f->getfn(v->pm), + v->pm->base, v->pm->node.flags, NULL); + break; + case PM_SCALAR: + s = v->pm->gsu.s->getfn(v->pm); + break; + default: + s = ""; + DPUTS(1, "BUG: param node without valid type"); + break; + } + + if (v->flags & VALFLAG_SUBST) { + if (v->pm->node.flags & (PM_LEFT|PM_RIGHT_B|PM_RIGHT_Z)) { + unsigned int fwidth = v->pm->width ? v->pm->width : MB_METASTRLEN(s); + switch (v->pm->node.flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) { + char *t, *tend; + unsigned int t0; + + case PM_LEFT: + case PM_LEFT | PM_RIGHT_Z: + t = s; + if (v->pm->node.flags & PM_RIGHT_Z) + while (*t == '0') + t++; + else + while (iblank(*t)) + t++; + MB_METACHARINIT(); + for (tend = t, t0 = 0; t0 < fwidth && *tend; t0++) + tend += MB_METACHARLEN(tend); + /* + * t0 is the number of characters from t used, + * hence (fwidth - t0) is the number of padding + * characters. fwidth is a misnomer: we use + * character counts, not character widths. + * + * (tend - t) is the number of bytes we need + * to get fwidth characters or the entire string; + * the characters may be multiple bytes. + */ + fwidth -= t0; /* padding chars remaining */ + t0 = tend - t; /* bytes to copy from string */ + s = (char *) hcalloc(t0 + fwidth + 1); + memcpy(s, t, t0); + if (fwidth) + memset(s + t0, ' ', fwidth); + s[t0 + fwidth] = '\0'; + break; + case PM_RIGHT_B: + case PM_RIGHT_Z: + case PM_RIGHT_Z | PM_RIGHT_B: + { + int zero = 1; + /* Calculate length in possibly multibyte chars */ + unsigned int charlen = MB_METASTRLEN(s); + + if (charlen < fwidth) { + char *valprefend = s; + int preflen; + if (v->pm->node.flags & PM_RIGHT_Z) { + /* + * This is a documented feature: when deciding + * whether to pad with zeroes, ignore + * leading blanks already in the value; + * only look for numbers after that. + * Not sure how useful this really is. + * It's certainly confusing to code around. + */ + for (t = s; iblank(*t); t++) + ; + /* + * Allow padding after initial minus + * for numeric variables. + */ + if ((v->pm->node.flags & + (PM_INTEGER|PM_EFLOAT|PM_FFLOAT)) && + *t == '-') + t++; + /* + * Allow padding after initial 0x or + * base# for integer variables. + */ + if (v->pm->node.flags & PM_INTEGER) { + if (isset(CBASES) && + t[0] == '0' && t[1] == 'x') + t += 2; + else if ((valprefend = strchr(t, '#'))) + t = valprefend + 1; + } + valprefend = t; + if (!*t) + zero = 0; + else if (v->pm->node.flags & + (PM_INTEGER|PM_EFLOAT|PM_FFLOAT)) { + /* zero always OK */ + } else if (!idigit(*t)) + zero = 0; + } + /* number of characters needed for padding */ + fwidth -= charlen; + /* bytes from original string */ + t0 = strlen(s); + t = (char *) hcalloc(fwidth + t0 + 1); + /* prefix guaranteed to be single byte chars */ + preflen = valprefend - s; + memset(t + preflen, + (((v->pm->node.flags & PM_RIGHT_B) + || !zero) ? ' ' : '0'), fwidth); + /* + * Copy - or 0x or base# before any padding + * zeroes. + */ + if (preflen) + memcpy(t, s, preflen); + memcpy(t + preflen + fwidth, + valprefend, t0 - preflen); + t[fwidth + t0] = '\0'; + s = t; + } else { + /* Need to skip (charlen - fwidth) chars */ + for (t0 = charlen - fwidth; t0; t0--) + s += MB_METACHARLEN(s); + } + } + break; + } + } + switch (v->pm->node.flags & (PM_LOWER | PM_UPPER)) { + case PM_LOWER: + s = casemodify(s, CASMOD_LOWER); + break; + case PM_UPPER: + s = casemodify(s, CASMOD_UPPER); + break; + } + } + if (v->start == 0 && v->end == -1) + return s; + + len = strlen(s); + if (v->start < 0) { + v->start += len; + if (v->start < 0) + v->start = 0; + } + if (v->end < 0) { + v->end += len; + if (v->end >= 0) { + char *eptr = s + v->end; + if (*eptr) + v->end += MB_METACHARLEN(eptr); + } + } + + s = (v->start > len) ? dupstring("") : + dupstring_wlen(s + v->start, len - v->start); + + if (v->end <= v->start) + s[0] = '\0'; + else if (v->end - v->start <= len - v->start) + s[v->end - v->start] = '\0'; + + return s; +} + +static char *nular[] = {"", NULL}; + +/**/ +mod_export char ** +getarrvalue(Value v) +{ + char **s; + + if (!v) + return arrdup(nular); + else if (IS_UNSET_VALUE(v)) + return arrdup(&nular[1]); + if (v->flags & VALFLAG_INV) { + char buf[DIGBUFSIZE]; + + s = arrdup(nular); + sprintf(buf, "%d", v->start); + s[0] = dupstring(buf); + return s; + } + s = getvaluearr(v); + if (v->start == 0 && v->end == -1) + return s; + if (v->start < 0) + v->start += arrlen(s); + if (v->end < 0) + v->end += arrlen(s) + 1; + + /* Null if 1) array too short, 2) index still negative */ + if (v->end <= v->start) { + s = arrdup_max(nular, 0); + } + else if (v->start < 0) { + s = arrdup_max(nular, 1); + } + else if (arrlen_le(s, v->start)) { + /* Handle $ary[i,i] consistently for any $i > $#ary + * and $ary[i,j] consistently for any $j > $i > $#ary + */ + s = arrdup_max(nular, v->end - (v->start + 1)); + } + else { + /* Copy to a point before the end of the source array: + * arrdup_max will copy at most v->end - v->start elements, + * starting from v->start element. Original code said: + * s[v->end - v->start] = NULL + * which means that there are exactly the same number of + * elements as the value of the above *0-based* index. + */ + s = arrdup_max(s + v->start, v->end - v->start); + } + + return s; +} + +/**/ +mod_export zlong +getintvalue(Value v) +{ + if (!v) + return 0; + if (v->flags & VALFLAG_INV) + return v->start; + if (v->isarr) { + char **arr = getarrvalue(v); + if (arr) { + char *scal = sepjoin(arr, NULL, 1); + return mathevali(scal); + } else + return 0; + } + if (PM_TYPE(v->pm->node.flags) == PM_INTEGER) + return v->pm->gsu.i->getfn(v->pm); + if (v->pm->node.flags & (PM_EFLOAT|PM_FFLOAT)) + return (zlong)v->pm->gsu.f->getfn(v->pm); + return mathevali(getstrvalue(v)); +} + +/**/ +mnumber +getnumvalue(Value v) +{ + mnumber mn; + mn.type = MN_INTEGER; + + + if (!v) { + mn.u.l = 0; + } else if (v->flags & VALFLAG_INV) { + mn.u.l = v->start; + } else if (v->isarr) { + char **arr = getarrvalue(v); + if (arr) { + char *scal = sepjoin(arr, NULL, 1); + return matheval(scal); + } else + mn.u.l = 0; + } else if (PM_TYPE(v->pm->node.flags) == PM_INTEGER) { + mn.u.l = v->pm->gsu.i->getfn(v->pm); + } else if (v->pm->node.flags & (PM_EFLOAT|PM_FFLOAT)) { + mn.type = MN_FLOAT; + mn.u.d = v->pm->gsu.f->getfn(v->pm); + } else + return matheval(getstrvalue(v)); + return mn; +} + +/**/ +void +export_param(Param pm) +{ + char buf[BDIGBUFSIZE], *val; + + if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) { +#if 0 /* Requires changes elsewhere in params.c and builtin.c */ + if (EMULATION(EMULATE_KSH) /* isset(KSHARRAYS) */) { + struct value v; + v.isarr = 1; + v.flags = 0; + v.start = 0; + v.end = -1; + val = getstrvalue(&v); + } else +#endif + return; + } else if (PM_TYPE(pm->node.flags) == PM_INTEGER) + convbase(val = buf, pm->gsu.i->getfn(pm), pm->base); + else if (pm->node.flags & (PM_EFLOAT|PM_FFLOAT)) + val = convfloat(pm->gsu.f->getfn(pm), pm->base, + pm->node.flags, NULL); + else + val = pm->gsu.s->getfn(pm); + + addenv(pm, val); +} + +/**/ +mod_export void +setstrvalue(Value v, char *val) +{ + assignstrvalue(v, val, 0); +} + +/**/ +mod_export void +assignstrvalue(Value v, char *val, int flags) +{ + if (unset(EXECOPT)) + return; + if (v->pm->node.flags & PM_READONLY) { + zerr("read-only variable: %s", v->pm->node.nam); + zsfree(val); + return; + } + if ((v->pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) { + zerr("%s: restricted", v->pm->node.nam); + zsfree(val); + return; + } + if ((v->pm->node.flags & PM_HASHED) && + (v->isarr & (SCANPM_MATCHMANY|SCANPM_ARRONLY))) { + zerr("%s: attempt to set slice of associative array", v->pm->node.nam); + zsfree(val); + return; + } + if (v->flags & VALFLAG_EMPTY) { + zerr("%s: assignment to invalid subscript range", v->pm->node.nam); + zsfree(val); + return; + } + v->pm->node.flags &= ~PM_UNSET; + switch (PM_TYPE(v->pm->node.flags)) { + case PM_SCALAR: + if (v->start == 0 && v->end == -1) { + v->pm->gsu.s->setfn(v->pm, val); + if ((v->pm->node.flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) && + !v->pm->width) + v->pm->width = strlen(val); + } else { + char *z, *x; + int zlen, vlen, newsize; + + z = v->pm->gsu.s->getfn(v->pm); + zlen = strlen(z); + + if ((v->flags & VALFLAG_INV) && unset(KSHARRAYS)) + v->start--, v->end--; + if (v->start < 0) { + v->start += zlen; + if (v->start < 0) + v->start = 0; + } + if (v->start > zlen) + v->start = zlen; + if (v->end < 0) { + v->end += zlen; + if (v->end < 0) { + v->end = 0; + } else if (v->end >= zlen) { + v->end = zlen; + } else { +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) { + v->end += MB_METACHARLEN(z + v->end); + } else { + v->end++; + } +#else + v->end++; +#endif + } + } + else if (v->end > zlen) + v->end = zlen; + + vlen = strlen(val); + /* Characters preceding start index + + characters of what is assigned + + characters following end index */ + newsize = v->start + vlen + (zlen - v->end); + + /* Does new size differ? */ + if (newsize != zlen || v->pm->gsu.s->setfn != strsetfn) { + x = (char *) zalloc(newsize + 1); + strncpy(x, z, v->start); + strcpy(x + v->start, val); + strcat(x + v->start, z + v->end); + v->pm->gsu.s->setfn(v->pm, x); + } else { + Param pm = v->pm; + /* Size doesn't change, can limit actions to only + * overwriting bytes in already allocated string */ + strncpy(z + v->start, val, vlen); + /* Implement remainder of strsetfn */ + if (!(pm->node.flags & PM_HASHELEM) && + ((pm->node.flags & PM_NAMEDDIR) || + isset(AUTONAMEDIRS))) { + pm->node.flags |= PM_NAMEDDIR; + adduserdir(pm->node.nam, z, 0, 0); + } + } + zsfree(val); + } + break; + case PM_INTEGER: + if (val) { + zlong ival; + if (flags & ASSPM_ENV_IMPORT) { + char *ptr; + ival = zstrtol_underscore(val, &ptr, 0, 1); + } else + ival = mathevali(val); + v->pm->gsu.i->setfn(v->pm, ival); + if ((v->pm->node.flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) && + !v->pm->width) + v->pm->width = strlen(val); + zsfree(val); + } + if (!v->pm->base && lastbase != -1) + v->pm->base = lastbase; + break; + case PM_EFLOAT: + case PM_FFLOAT: + if (val) { + mnumber mn; + if (flags & ASSPM_ENV_IMPORT) { + char *ptr; + mn.type = MN_FLOAT; + mn.u.d = strtod(val, &ptr); + } else + mn = matheval(val); + v->pm->gsu.f->setfn(v->pm, (mn.type & MN_FLOAT) ? mn.u.d : + (double)mn.u.l); + if ((v->pm->node.flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) && + !v->pm->width) + v->pm->width = strlen(val); + zsfree(val); + } + break; + case PM_ARRAY: + { + char **ss = (char **) zalloc(2 * sizeof(char *)); + + ss[0] = val; + ss[1] = NULL; + setarrvalue(v, ss); + } + break; + case PM_HASHED: + { + if (foundparam == NULL) + { + zerr("%s: attempt to set associative array to scalar", + v->pm->node.nam); + zsfree(val); + return; + } + else + foundparam->gsu.s->setfn(foundparam, val); + } + break; + } + if ((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) && + !(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) || + (v->pm->node.flags & PM_ARRAY) || v->pm->ename) + return; + export_param(v->pm); +} + +/**/ +void +setnumvalue(Value v, mnumber val) +{ + char buf[BDIGBUFSIZE], *p; + + if (unset(EXECOPT)) + return; + if (v->pm->node.flags & PM_READONLY) { + zerr("read-only variable: %s", v->pm->node.nam); + return; + } + if ((v->pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) { + zerr("%s: restricted", v->pm->node.nam); + return; + } + switch (PM_TYPE(v->pm->node.flags)) { + case PM_SCALAR: + case PM_ARRAY: + if ((val.type & MN_INTEGER) || outputradix) { + if (!(val.type & MN_INTEGER)) + val.u.l = (zlong) val.u.d; + p = convbase_underscore(buf, val.u.l, outputradix, + outputunderscore); + } else + p = convfloat_underscore(val.u.d, outputunderscore); + setstrvalue(v, ztrdup(p)); + break; + case PM_INTEGER: + v->pm->gsu.i->setfn(v->pm, (val.type & MN_INTEGER) ? val.u.l : + (zlong) val.u.d); + setstrvalue(v, NULL); + break; + case PM_EFLOAT: + case PM_FFLOAT: + v->pm->gsu.f->setfn(v->pm, (val.type & MN_INTEGER) ? + (double)val.u.l : val.u.d); + setstrvalue(v, NULL); + break; + } +} + +/**/ +mod_export void +setarrvalue(Value v, char **val) +{ + if (unset(EXECOPT)) + return; + if (v->pm->node.flags & PM_READONLY) { + zerr("read-only variable: %s", v->pm->node.nam); + freearray(val); + return; + } + if ((v->pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) { + zerr("%s: restricted", v->pm->node.nam); + freearray(val); + return; + } + if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED))) { + freearray(val); + zerr("%s: attempt to assign array value to non-array", + v->pm->node.nam); + return; + } + if (v->flags & VALFLAG_EMPTY) { + zerr("%s: assignment to invalid subscript range", v->pm->node.nam); + freearray(val); + return; + } + + if (v->start == 0 && v->end == -1) { + if (PM_TYPE(v->pm->node.flags) == PM_HASHED) + arrhashsetfn(v->pm, val, 0); + else + v->pm->gsu.a->setfn(v->pm, val); + } else if (v->start == -1 && v->end == 0 && + PM_TYPE(v->pm->node.flags) == PM_HASHED) { + arrhashsetfn(v->pm, val, ASSPM_AUGMENT); + } else if ((PM_TYPE(v->pm->node.flags) == PM_HASHED)) { + freearray(val); + zerr("%s: attempt to set slice of associative array", + v->pm->node.nam); + return; + } else { + char **const old = v->pm->gsu.a->getfn(v->pm); + char **new; + char **p, **q, **r; /* index variables */ + const int pre_assignment_length = arrlen(old); + int post_assignment_length; + int i; + + q = old; + + if ((v->flags & VALFLAG_INV) && unset(KSHARRAYS)) { + if (v->start > 0) + v->start--; + v->end--; + } + if (v->start < 0) { + v->start += pre_assignment_length; + if (v->start < 0) + v->start = 0; + } + if (v->end < 0) { + v->end += pre_assignment_length + 1; + if (v->end < 0) + v->end = 0; + } + if (v->end < v->start) + v->end = v->start; + + post_assignment_length = v->start + arrlen(val); + if (v->end < pre_assignment_length) { + /* + * Allocate room for array elements between the end of the slice `v' + * and the original array's end. + */ + post_assignment_length += pre_assignment_length - v->end; + } + + if (pre_assignment_length == post_assignment_length + && v->pm->gsu.a->setfn == arrsetfn + /* ... and isn't something that arrsetfn() treats specially */ + && 0 == (v->pm->node.flags & (PM_SPECIAL|PM_UNIQUE)) + && NULL == v->pm->ename) + { + /* v->start is 0-based */ + p = old + v->start; + for (r = val; *r;) { + /* Free previous string */ + zsfree(*p); + /* Give away ownership of the string */ + *p++ = *r++; + } + } else { + /* arr+=( ... ) + * arr[${#arr}+x,...]=( ... ) */ + if (post_assignment_length > pre_assignment_length && + pre_assignment_length <= v->start && + pre_assignment_length > 0 && + v->pm->gsu.a->setfn == arrsetfn) + { + p = new = (char **) zrealloc(old, sizeof(char *) + * (post_assignment_length + 1)); + + p += pre_assignment_length; /* after old elements */ + + /* Consider 1 < 0, case for a=( 1 ); a[1,..] = + * 1 < 1, case for a=( 1 ); a[2,..] = */ + if (pre_assignment_length < v->start) { + for (i = pre_assignment_length; i < v->start; i++) { + *p++ = ztrdup(""); + } + } + + for (r = val; *r;) { + /* Give away ownership of the string */ + *p++ = *r++; + } + + /* v->end doesn't matter: + * a=( 1 2 ); a[4,100]=( a b ); echo "${(q@)a}" + * 1 2 '' a b */ + *p = NULL; + + v->pm->u.arr = NULL; + v->pm->gsu.a->setfn(v->pm, new); + } else { + p = new = (char **) zalloc(sizeof(char *) + * (post_assignment_length + 1)); + for (i = 0; i < v->start; i++) + *p++ = i < pre_assignment_length ? ztrdup(*q++) : ztrdup(""); + for (r = val; *r;) { + /* Give away ownership of the string */ + *p++ = *r++; + } + if (v->end < pre_assignment_length) + for (q = old + v->end; *q;) + *p++ = ztrdup(*q++); + *p = NULL; + + v->pm->gsu.a->setfn(v->pm, new); + } + + DPUTS2(p - new != post_assignment_length, "setarrvalue: wrong allocation: %d 1= %lu", + post_assignment_length, (unsigned long)(p - new)); + } + + /* Ownership of all strings has been + * given away, can plainly free */ + free(val); + } +} + +/* Retrieve an integer parameter */ + +/**/ +mod_export zlong +getiparam(char *s) +{ + struct value vbuf; + Value v; + + if (!(v = getvalue(&vbuf, &s, 1))) + return 0; + return getintvalue(v); +} + +/* Retrieve a numerical parameter, either integer or floating */ + +/**/ +mnumber +getnparam(char *s) +{ + struct value vbuf; + Value v; + + if (!(v = getvalue(&vbuf, &s, 1))) { + mnumber mn; + mn.type = MN_INTEGER; + mn.u.l = 0; + return mn; + } + return getnumvalue(v); +} + +/* Retrieve a scalar (string) parameter */ + +/**/ +mod_export char * +getsparam(char *s) +{ + struct value vbuf; + Value v; + + if (!(v = getvalue(&vbuf, &s, 0))) + return NULL; + return getstrvalue(v); +} + +/**/ +mod_export char * +getsparam_u(char *s) +{ + if ((s = getsparam(s))) + return unmetafy(s, NULL); + return s; +} + +/* Retrieve an array parameter */ + +/**/ +mod_export char ** +getaparam(char *s) +{ + struct value vbuf; + Value v; + + if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) && + PM_TYPE(v->pm->node.flags) == PM_ARRAY) + return v->pm->gsu.a->getfn(v->pm); + return NULL; +} + +/* Retrieve an assoc array parameter as an array */ + +/**/ +mod_export char ** +gethparam(char *s) +{ + struct value vbuf; + Value v; + + if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) && + PM_TYPE(v->pm->node.flags) == PM_HASHED) + return paramvalarr(v->pm->gsu.h->getfn(v->pm), SCANPM_WANTVALS); + return NULL; +} + +/* Retrieve the keys of an assoc array parameter as an array */ + +/**/ +mod_export char ** +gethkparam(char *s) +{ + struct value vbuf; + Value v; + + if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) && + PM_TYPE(v->pm->node.flags) == PM_HASHED) + return paramvalarr(v->pm->gsu.h->getfn(v->pm), SCANPM_WANTKEYS); + return NULL; +} + +/* + * Function behind WARNCREATEGLOBAL and WARNNESTEDVAR option. + * + * For WARNNESTEDVAR: + * Called when the variable is created. + * Apply heuristics to see if this variable was just created + * globally but in a local context. + * + * For WARNNESTEDVAR: + * Called when the variable already exists and is set. + * Apply heuristics to see if this variable is setting + * a variable that was created in a less nested function + * or globally. + */ + +/**/ +static void +check_warn_pm(Param pm, const char *pmtype, int created, + int may_warn_about_nested_vars) +{ + Funcstack i; + + if (!may_warn_about_nested_vars && !created) + return; + + if (created && isset(WARNCREATEGLOBAL)) { + if (locallevel <= forklevel || pm->level != 0) + return; + } else if (!created && isset(WARNNESTEDVAR)) { + if (pm->level >= locallevel) + return; + } else + return; + + if (pm->node.flags & PM_SPECIAL) + return; + + for (i = funcstack; i; i = i->prev) { + if (i->tp == FS_FUNC) { + char *msg; + DPUTS(!i->name, "funcstack entry with no name"); + msg = created ? + "%s parameter %s created globally in function %s" : + "%s parameter %s set in enclosing scope in function %s"; + zwarn(msg, pmtype, pm->node.nam, i->name); + break; + } + } +} + +/**/ +mod_export Param +assignsparam(char *s, char *val, int flags) +{ + struct value vbuf; + Value v; + char *t = s; + char *ss, *copy, *var; + size_t lvar; + mnumber lhs, rhs; + int sstart, created = 0; + + if (!isident(s)) { + zerr("not an identifier: %s", s); + zsfree(val); + errflag |= ERRFLAG_ERROR; + return NULL; + } + queue_signals(); + if ((ss = strchr(s, '['))) { + *ss = '\0'; + if (!(v = getvalue(&vbuf, &s, 1))) { + createparam(t, PM_ARRAY); + created = 1; + } else { + if (v->pm->node.flags & PM_READONLY) { + zerr("read-only variable: %s", v->pm->node.nam); + *ss = '['; + zsfree(val); + unqueue_signals(); + return NULL; + } + /* + * Parameter defined here is a temporary bogus one. + * Don't warn about anything. + */ + flags &= ~ASSPM_WARN; + } + *ss = '['; + v = NULL; + } else { + if (!(v = getvalue(&vbuf, &s, 1))) { + createparam(t, PM_SCALAR); + created = 1; + } else if ((((v->pm->node.flags & PM_ARRAY) && !(flags & ASSPM_AUGMENT)) || + (v->pm->node.flags & PM_HASHED)) && + !(v->pm->node.flags & (PM_SPECIAL|PM_TIED)) && + unset(KSHARRAYS)) { + unsetparam(t); + createparam(t, PM_SCALAR); + /* not regarded as a new creation */ + v = NULL; + } + } + if (!v && !(v = getvalue(&vbuf, &t, 1))) { + unqueue_signals(); + zsfree(val); + /* errflag |= ERRFLAG_ERROR; */ + return NULL; + } + if (flags & ASSPM_WARN) + check_warn_pm(v->pm, "scalar", created, 1); + if (flags & ASSPM_AUGMENT) { + if (v->start == 0 && v->end == -1) { + switch (PM_TYPE(v->pm->node.flags)) { + case PM_SCALAR: + v->start = INT_MAX; /* just append to scalar value */ + break; + case PM_INTEGER: + case PM_EFLOAT: + case PM_FFLOAT: + rhs = matheval(val); + lhs = getnumvalue(v); + if (lhs.type == MN_FLOAT) { + if ((rhs.type) == MN_FLOAT) + lhs.u.d = lhs.u.d + rhs.u.d; + else + lhs.u.d = lhs.u.d + (double)rhs.u.l; + } else { + if ((rhs.type) == MN_INTEGER) + lhs.u.l = lhs.u.l + rhs.u.l; + else + lhs.u.l = lhs.u.l + (zlong)rhs.u.d; + } + setnumvalue(v, lhs); + unqueue_signals(); + zsfree(val); + return v->pm; /* avoid later setstrvalue() call */ + case PM_ARRAY: + if (unset(KSHARRAYS)) { + v->start = arrlen(v->pm->gsu.a->getfn(v->pm)); + v->end = v->start + 1; + } else { + /* ksh appends scalar to first element */ + v->end = 1; + goto kshappend; + } + break; + } + } else { + switch (PM_TYPE(v->pm->node.flags)) { + case PM_SCALAR: + if (v->end > 0) + v->start = v->end; + else + v->start = v->end = strlen(v->pm->gsu.s->getfn(v->pm)) + + v->end + 1; + break; + case PM_INTEGER: + case PM_EFLOAT: + case PM_FFLOAT: + unqueue_signals(); + zerr("attempt to add to slice of a numeric variable"); + zsfree(val); + return NULL; + case PM_ARRAY: + kshappend: + /* treat slice as the end element */ + v->start = sstart = v->end > 0 ? v->end - 1 : v->end; + v->isarr = 0; + var = getstrvalue(v); + v->start = sstart; + copy = val; + lvar = strlen(var); + val = (char *)zalloc(lvar + strlen(val) + 1); + strcpy(val, var); + strcpy(val + lvar, copy); + zsfree(copy); + break; + } + } + } + + assignstrvalue(v, val, flags); + unqueue_signals(); + return v->pm; +} + +/**/ +mod_export Param +setsparam(char *s, char *val) +{ + return assignsparam(s, val, ASSPM_WARN); +} + +/**/ +mod_export Param +assignaparam(char *s, char **val, int flags) +{ + struct value vbuf; + Value v; + char *t = s; + char *ss; + int created = 0; + int may_warn_about_nested_vars = 1; + + if (!isident(s)) { + zerr("not an identifier: %s", s); + freearray(val); + errflag |= ERRFLAG_ERROR; + return NULL; + } + queue_signals(); + if ((ss = strchr(s, '['))) { + *ss = '\0'; + if (!(v = getvalue(&vbuf, &s, 1))) { + createparam(t, PM_ARRAY); + created = 1; + } else { + may_warn_about_nested_vars = 0; + } + *ss = '['; + if (v && PM_TYPE(v->pm->node.flags) == PM_HASHED) { + unqueue_signals(); + zerr("%s: attempt to set slice of associative array", + v->pm->node.nam); + freearray(val); + errflag |= ERRFLAG_ERROR; + return NULL; + } + v = NULL; + } else { + if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) { + createparam(t, PM_ARRAY); + created = 1; + } else if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED)) && + !(v->pm->node.flags & (PM_SPECIAL|PM_TIED))) { + int uniq = v->pm->node.flags & PM_UNIQUE; + if (flags & ASSPM_AUGMENT) { + /* insert old value at the beginning of the val array */ + char **new; + int lv = arrlen(val); + + new = (char **) zalloc(sizeof(char *) * (lv + 2)); + *new = ztrdup(getstrvalue(v)); + memcpy(new+1, val, sizeof(char *) * (lv + 1)); + free(val); + val = new; + } + unsetparam(t); + createparam(t, PM_ARRAY | uniq); + v = NULL; + } + } + if (!v) + if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) { + unqueue_signals(); + freearray(val); + /* errflag |= ERRFLAG_ERROR; */ + return NULL; + } + + if (flags & ASSPM_WARN) + check_warn_pm(v->pm, "array", created, may_warn_about_nested_vars); + + /* + * At this point, we may have array entries consisting of + * - a Marker element --- normally allocated array entry but + * with just Marker char and null + * - an array index element --- as normal for associative array, + * but non-standard for normal array which we handle now. + * - a value for the indexed element. + * This only applies if the flag ASSPM_KEY_VALUE is passed in, + * indicating prefork() detected this syntax. + * + * For associative arrays we just junk the Marker elements. + */ + if (flags & ASSPM_KEY_VALUE) { + char **aptr; + if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) { + /* + * This is an ordinary array with key / value pairs. + */ + int maxlen, origlen, nextind; + char **fullval, **origptr; + zlong *subscripts = (zlong *)zhalloc(arrlen(val) * sizeof(zlong)); + zlong *iptr = subscripts; + if (flags & ASSPM_AUGMENT) { + origptr = v->pm->gsu.a->getfn(v->pm); + maxlen = origlen = arrlen(origptr); + } else { + maxlen = origlen = 0; + origptr = NULL; + } + nextind = 0; + for (aptr = val; *aptr; ) { + if (**aptr == Marker) { + *iptr = mathevali(*++aptr); + if (*iptr < 0 || + (!isset(KSHARRAYS) && *iptr == 0)) { + unqueue_signals(); + zerr("bad subscript for direct array assignment: %s", *aptr); + freearray(val); + return NULL; + } + if (!isset(KSHARRAYS)) + --*iptr; + nextind = *iptr + 1; + ++iptr; + aptr += 2; + } else { + ++nextind; + ++aptr; + } + if (nextind > maxlen) + maxlen = nextind; + } + fullval = zshcalloc((maxlen+1) * sizeof(char *)); + if (!fullval) { + zerr("array too large"); + freearray(val); + return NULL; + } + fullval[maxlen] = NULL; + if (flags & ASSPM_AUGMENT) { + char **srcptr = origptr; + for (aptr = fullval; aptr <= fullval + origlen; aptr++) { + *aptr = ztrdup(*srcptr); + srcptr++; + } + } + iptr = subscripts; + nextind = 0; + for (aptr = val; *aptr; ++aptr) { + char *old; + if (**aptr == Marker) { + int augment = ((*aptr)[1] == '+'); + zsfree(*aptr); + zsfree(*++aptr); /* Index, no longer needed */ + old = fullval[*iptr]; + if (augment && old) { + fullval[*iptr] = bicat(old, *++aptr); + zsfree(*aptr); + } else { + fullval[*iptr] = *++aptr; + } + nextind = *iptr + 1; + ++iptr; + } else { + old = fullval[nextind]; + fullval[nextind] = *aptr; + ++nextind; + } + if (old) + zsfree(old); + /* aptr now on value in both cases */ + } + if (*aptr) { /* Shouldn't be possible */ + DPUTS(1, "Extra element in key / value array"); + zsfree(*aptr); + } + free(val); + for (aptr = fullval; aptr < fullval + maxlen; aptr++) { + /* + * Remember we don't have sparse arrays but and they're null + * terminated --- so any value we don't set has to be an + * empty string. + */ + if (!*aptr) + *aptr = ztrdup(""); + } + setarrvalue(v, fullval); + unqueue_signals(); + return v->pm; + } else if (PM_TYPE(v->pm->node.flags & PM_HASHED)) { + /* + * We strictly enforce [key]=value syntax for associative + * arrays. Marker can only indicate a Marker / key / value + * triad; it cannot be there by accident. + * + * It's too inefficient to strip Markers here, and they + * can't be there in the other form --- so just ignore + * them willy nilly lower down. + */ + for (aptr = val; *aptr; aptr += 3) { + if (**aptr != Marker) { + unqueue_signals(); + freearray(val); + zerr("bad [key]=value syntax for associative array"); + return NULL; + } + } + } else { + unqueue_signals(); + freearray(val); + zerr("invalid use of [key]=value assignment syntax"); + return NULL; + } + } + + if (flags & ASSPM_AUGMENT) { + if (v->start == 0 && v->end == -1) { + if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) { + v->start = arrlen(v->pm->gsu.a->getfn(v->pm)); + v->end = v->start + 1; + } else if (PM_TYPE(v->pm->node.flags) & PM_HASHED) + v->start = -1, v->end = 0; + } else { + if (v->end > 0) + v->start = v->end--; + else if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) { + v->end = arrlen(v->pm->gsu.a->getfn(v->pm)) + v->end; + v->start = v->end + 1; + } + } + } + + setarrvalue(v, val); + unqueue_signals(); + return v->pm; +} + + +/**/ +mod_export Param +setaparam(char *s, char **aval) +{ + return assignaparam(s, aval, ASSPM_WARN); +} + +/**/ +mod_export Param +sethparam(char *s, char **val) +{ + struct value vbuf; + Value v; + char *t = s; + int checkcreate = 0; + + if (!isident(s)) { + zerr("not an identifier: %s", s); + freearray(val); + errflag |= ERRFLAG_ERROR; + return NULL; + } + if (strchr(s, '[')) { + freearray(val); + zerr("nested associative arrays not yet supported"); + errflag |= ERRFLAG_ERROR; + return NULL; + } + if (unset(EXECOPT)) + return NULL; + queue_signals(); + if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) { + createparam(t, PM_HASHED); + checkcreate = 1; + } else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED)) { + if (!(v->pm->node.flags & PM_SPECIAL)) { + unsetparam(t); + /* no WARNCREATEGLOBAL check here as parameter already existed */ + createparam(t, PM_HASHED); + v = NULL; + } else { + zerr("%s: can't change type of a special parameter", t); + unqueue_signals(); + return NULL; + } + } + if (!v) + if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) { + unqueue_signals(); + /* errflag |= ERRFLAG_ERROR; */ + return NULL; + } + check_warn_pm(v->pm, "associative array", checkcreate, 1); + setarrvalue(v, val); + unqueue_signals(); + return v->pm; +} + + +/* + * Set a generic shell number, floating point or integer. + * Option to warn on setting. + */ + +/**/ +mod_export Param +assignnparam(char *s, mnumber val, int flags) +{ + struct value vbuf; + Value v; + char *t = s, *ss; + Param pm; + int was_unset = 0; + + if (!isident(s)) { + zerr("not an identifier: %s", s); + errflag |= ERRFLAG_ERROR; + return NULL; + } + if (unset(EXECOPT)) + return NULL; + queue_signals(); + ss = strchr(s, '['); + v = getvalue(&vbuf, &s, 1); + if (v && (v->pm->node.flags & (PM_ARRAY|PM_HASHED)) && + !(v->pm->node.flags & (PM_SPECIAL|PM_TIED)) && + /* + * not sure what KSHARRAYS has got to do with this... + * copied this from assignsparam(). + */ + unset(KSHARRAYS) && !ss) { + unsetparam_pm(v->pm, 0, 1); + was_unset = 1; + s = t; + v = NULL; + } + if (!v) { + /* s has been updated by getvalue, so check again */ + ss = strchr(s, '['); + if (ss) + *ss = '\0'; + pm = createparam(t, ss ? PM_ARRAY : + isset(POSIXIDENTIFIERS) ? PM_SCALAR : + (val.type & MN_INTEGER) ? PM_INTEGER : PM_FFLOAT); + if (!pm) + pm = (Param) paramtab->getnode(paramtab, t); + DPUTS(!pm, "BUG: parameter not created"); + if (ss) { + *ss = '['; + } else if (val.type & MN_INTEGER) { + pm->base = outputradix; + } + if (!(v = getvalue(&vbuf, &t, 1))) { + DPUTS(!v, "BUG: value not found for new parameter"); + /* errflag |= ERRFLAG_ERROR; */ + unqueue_signals(); + return NULL; + } + if (flags & ASSPM_WARN) + check_warn_pm(v->pm, "numeric", !was_unset, 1); + } else { + if (flags & ASSPM_WARN) + check_warn_pm(v->pm, "numeric", 0, 1); + } + setnumvalue(v, val); + unqueue_signals(); + return v->pm; +} + +/* + * Set a generic shell number, floating point or integer. + * Warn on setting based on option. + */ + +/**/ +mod_export Param +setnparam(char *s, mnumber val) +{ + return assignnparam(s, val, ASSPM_WARN); +} + +/* Simplified interface to assignnparam */ + +/**/ +mod_export Param +assigniparam(char *s, zlong val, int flags) +{ + mnumber mnval; + mnval.type = MN_INTEGER; + mnval.u.l = val; + return assignnparam(s, mnval, flags); +} + +/* Simplified interface to setnparam */ + +/**/ +mod_export Param +setiparam(char *s, zlong val) +{ + mnumber mnval; + mnval.type = MN_INTEGER; + mnval.u.l = val; + return assignnparam(s, mnval, ASSPM_WARN); +} + +/* + * Set an integer parameter without forcing creation of an integer type. + * This is useful if the integer is going to be set to a parmaeter which + * would usually be scalar but may not exist. + */ + +/**/ +mod_export Param +setiparam_no_convert(char *s, zlong val) +{ + /* + * If the target is already an integer, thisgets converted + * back. Low technology rules. + */ + char buf[BDIGBUFSIZE]; + convbase(buf, val, 10); + return assignsparam(s, ztrdup(buf), ASSPM_WARN); +} + +/* Unset a parameter */ + +/**/ +mod_export void +unsetparam(char *s) +{ + Param pm; + + queue_signals(); + if ((pm = (Param) (paramtab == realparamtab ? + /* getnode2() to avoid autoloading */ + paramtab->getnode2(paramtab, s) : + paramtab->getnode(paramtab, s)))) + unsetparam_pm(pm, 0, 1); + unqueue_signals(); +} + +/* Unset a parameter + * + * altflag: if true, don't remove pm->ename from the environment + * exp: See stdunsetfn() + */ + +/**/ +mod_export int +unsetparam_pm(Param pm, int altflag, int exp) +{ + Param oldpm, altpm; + char *altremove; + + if ((pm->node.flags & PM_READONLY) && pm->level <= locallevel) { + zerr("read-only variable: %s", pm->node.nam); + return 1; + } + if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) { + zerr("%s: restricted", pm->node.nam); + return 1; + } + + if (pm->ename && !altflag) + altremove = ztrdup(pm->ename); + else + altremove = NULL; + + if (!(pm->node.flags & PM_UNSET)) + pm->gsu.s->unsetfn(pm, exp); + if (pm->env) + delenv(pm); + + /* remove it under its alternate name if necessary */ + if (altremove) { + altpm = (Param) paramtab->getnode(paramtab, altremove); + /* tied parameters are at the same local level as each other */ + oldpm = NULL; + while (altpm && altpm->level > pm->level) { + /* param under alternate name hidden by a local */ + oldpm = altpm; + altpm = altpm->old; + } + if (altpm) { + if (oldpm && !altpm->level) { + oldpm->old = NULL; + /* fudge things so removenode isn't called */ + altpm->level = 1; + } + unsetparam_pm(altpm, 1, exp); + } + + zsfree(altremove); + } + + /* + * If this was a local variable, we need to keep the old + * struct so that it is resurrected at the right level. + * This is partly because when an array/scalar value is set + * and the parameter used to be the other sort, unsetparam() + * is called. Beyond that, there is an ambiguity: should + * foo() { local bar; unset bar; } make the global bar + * available or not? The following makes the answer "no". + * + * Some specials, such as those used in zle, still need removing + * from the parameter table; they have the PM_REMOVABLE flag. + */ + if ((pm->level && locallevel >= pm->level) || + (pm->node.flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL) + return 0; + + /* remove parameter node from table */ + paramtab->removenode(paramtab, pm->node.nam); + + if (pm->old) { + oldpm = pm->old; + paramtab->addnode(paramtab, oldpm->node.nam, oldpm); + if ((PM_TYPE(oldpm->node.flags) == PM_SCALAR) && + !(pm->node.flags & PM_HASHELEM) && + (oldpm->node.flags & PM_NAMEDDIR) && + oldpm->gsu.s == &stdscalar_gsu) + adduserdir(oldpm->node.nam, oldpm->u.str, 0, 0); + if (oldpm->node.flags & PM_EXPORTED) { + /* + * Re-export the old value which we removed in typeset_single(). + * I don't think we need to test for ALL_EXPORT here, since if + * it was used to export the parameter originally the parameter + * should still have the PM_EXPORTED flag. + */ + export_param(oldpm); + } + } + + paramtab->freenode(&pm->node); /* free parameter node */ + + return 0; +} + +/* Standard function to unset a parameter. This is mostly delegated to * + * the specific set function. + * + * This could usefully be made type-specific, but then we need + * to be more careful when calling the unset method directly. + * + * The "exp"licit parameter should be nonzero for assignments and the + * unset command, and zero for implicit unset (e.g., end of scope). + * Currently this is used only by some modules. + */ + +/**/ +mod_export void +stdunsetfn(Param pm, UNUSED(int exp)) +{ + switch (PM_TYPE(pm->node.flags)) { + case PM_SCALAR: + if (pm->gsu.s->setfn) + pm->gsu.s->setfn(pm, NULL); + break; + + case PM_ARRAY: + if (pm->gsu.a->setfn) + pm->gsu.a->setfn(pm, NULL); + break; + + case PM_HASHED: + if (pm->gsu.h->setfn) + pm->gsu.h->setfn(pm, NULL); + break; + + default: + if (!(pm->node.flags & PM_SPECIAL)) + pm->u.str = NULL; + break; + } + if ((pm->node.flags & (PM_SPECIAL|PM_TIED)) == PM_TIED) { + if (pm->ename) { + zsfree(pm->ename); + pm->ename = NULL; + } + pm->node.flags &= ~PM_TIED; + } + pm->node.flags |= PM_UNSET; +} + +/* Function to get value of an integer parameter */ + +/**/ +mod_export zlong +intgetfn(Param pm) +{ + return pm->u.val; +} + +/* Function to set value of an integer parameter */ + +/**/ +static void +intsetfn(Param pm, zlong x) +{ + pm->u.val = x; +} + +/* Function to get value of a floating point parameter */ + +/**/ +static double +floatgetfn(Param pm) +{ + return pm->u.dval; +} + +/* Function to set value of an integer parameter */ + +/**/ +static void +floatsetfn(Param pm, double x) +{ + pm->u.dval = x; +} + +/* Function to get value of a scalar (string) parameter */ + +/**/ +mod_export char * +strgetfn(Param pm) +{ + return pm->u.str ? pm->u.str : (char *) hcalloc(1); +} + +/* Function to set value of a scalar (string) parameter */ + +/**/ +mod_export void +strsetfn(Param pm, char *x) +{ + zsfree(pm->u.str); + pm->u.str = x; + if (!(pm->node.flags & PM_HASHELEM) && + ((pm->node.flags & PM_NAMEDDIR) || isset(AUTONAMEDIRS))) { + pm->node.flags |= PM_NAMEDDIR; + adduserdir(pm->node.nam, x, 0, 0); + } + /* If you update this function, you may need to update the + * `Implement remainder of strsetfn' block in assignstrvalue(). */ +} + +/* Function to get value of an array parameter */ + +static char *nullarray = NULL; + +/**/ +char ** +arrgetfn(Param pm) +{ + return pm->u.arr ? pm->u.arr : &nullarray; +} + +/* Function to set value of an array parameter */ + +/**/ +mod_export void +arrsetfn(Param pm, char **x) +{ + if (pm->u.arr && pm->u.arr != x) + freearray(pm->u.arr); + if (pm->node.flags & PM_UNIQUE) + uniqarray(x); + pm->u.arr = x; + /* Arrays tied to colon-arrays may need to fix the environment */ + if (pm->ename && x) + arrfixenv(pm->ename, x); + /* If you extend this function, update the list of conditions in + * setarrvalue(). */ +} + +/* Function to get value of an association parameter */ + +/**/ +mod_export HashTable +hashgetfn(Param pm) +{ + return pm->u.hash; +} + +/* Function to set value of an association parameter */ + +/**/ +mod_export void +hashsetfn(Param pm, HashTable x) +{ + if (pm->u.hash && pm->u.hash != x) + deleteparamtable(pm->u.hash); + pm->u.hash = x; +} + +/* Function to dispose of setting of an unsettable hash */ + +/**/ +mod_export void +nullsethashfn(UNUSED(Param pm), HashTable x) +{ + deleteparamtable(x); +} + +/* Function to set value of an association parameter using key/value pairs */ + +/**/ +static void +arrhashsetfn(Param pm, char **val, int flags) +{ + /* Best not to shortcut this by using the existing hash table, * + * since that could cause trouble for special hashes. This way, * + * it's up to pm->gsu.h->setfn() what to do. */ + int alen = 0; + HashTable opmtab = paramtab, ht = 0; + char **aptr; + Value v = (Value) hcalloc(sizeof *v); + v->end = -1; + + for (aptr = val; *aptr; ++aptr) { + if (**aptr != Marker) + ++alen; + } + + if (alen % 2) { + freearray(val); + zerr("bad set of key/value pairs for associative array"); + return; + } + if (flags & ASSPM_AUGMENT) { + ht = paramtab = pm->gsu.h->getfn(pm); + } + if (alen && (!(flags & ASSPM_AUGMENT) || !paramtab)) { + ht = paramtab = newparamtable(17, pm->node.nam); + } + for (aptr = val; *aptr; ) { + int eltflags = 0; + if (**aptr == Marker) { + /* Either all elements have Marker or none. Checked in caller. */ + if ((*aptr)[1] == '+') { + /* Actually, assignstrvalue currently doesn't handle this... */ + eltflags = ASSPM_AUGMENT; + /* ...so we'll use the trick from setsparam(). */ + v->start = INT_MAX; + } else { + v->start = 0; + } + v->end = -1; + zsfree(*aptr++); + } + /* The parameter name is ztrdup'd... */ + v->pm = createparam(*aptr, PM_SCALAR|PM_UNSET); + /* + * createparam() doesn't return anything if the parameter + * already existed. + */ + if (!v->pm) + v->pm = (Param) paramtab->getnode(paramtab, *aptr); + zsfree(*aptr++); + /* ...but we can use the value without copying. */ + assignstrvalue(v, *aptr++, eltflags); + } + paramtab = opmtab; + pm->gsu.h->setfn(pm, ht); + free(val); /* not freearray() */ +} + +/* + * These functions are used as the set function for special parameters that + * cannot be set by the user. The set is incomplete as the only such + * parameters are scalar and integer. + */ + +/**/ +mod_export void +nullstrsetfn(UNUSED(Param pm), char *x) +{ + zsfree(x); +} + +/**/ +mod_export void +nullintsetfn(UNUSED(Param pm), UNUSED(zlong x)) +{} + +/**/ +mod_export void +nullunsetfn(UNUSED(Param pm), UNUSED(int exp)) +{} + + +/* Function to get value of generic special integer * + * parameter. data is pointer to global variable * + * containing the integer value. */ + +/**/ +mod_export zlong +intvargetfn(Param pm) +{ + return *pm->u.valptr; +} + +/* Function to set value of generic special integer * + * parameter. data is pointer to global variable * + * where the value is to be stored. */ + +/**/ +mod_export void +intvarsetfn(Param pm, zlong x) +{ + *pm->u.valptr = x; +} + +/* Function to set value of any ZLE-related integer * + * parameter. data is pointer to global variable * + * where the value is to be stored. */ + +/**/ +void +zlevarsetfn(Param pm, zlong x) +{ + zlong *p = pm->u.valptr; + + *p = x; + if (p == &zterm_lines || p == &zterm_columns) + adjustwinsize(2 + (p == &zterm_columns)); +} + + +/* Implements gsu_integer.unsetfn for ZLE_RPROMPT_INDENT; see stdunsetfn() */ + +static void +rprompt_indent_unsetfn(Param pm, int exp) +{ + stdunsetfn(pm, exp); + rprompt_indent = 1; /* Keep this in sync with init_term() */ +} + +/* Function to set value of generic special scalar * + * parameter. data is pointer to a character pointer * + * representing the scalar (string). */ + +/**/ +mod_export void +strvarsetfn(Param pm, char *x) +{ + char **q = ((char **)pm->u.data); + + zsfree(*q); + *q = x; +} + +/* Function to get value of generic special scalar * + * parameter. data is pointer to a character pointer * + * representing the scalar (string). */ + +/**/ +mod_export char * +strvargetfn(Param pm) +{ + char *s = *((char **)pm->u.data); + + if (!s) + return hcalloc(1); + return s; +} + +/* Function to get value of generic special array * + * parameter. data is a pointer to the pointer to * + * a pointer (a pointer to a variable length array * + * of pointers). */ + +/**/ +mod_export char ** +arrvargetfn(Param pm) +{ + char **arrptr = *((char ***)pm->u.data); + + return arrptr ? arrptr : &nullarray; +} + +/* Function to set value of generic special array parameter. * + * data is pointer to a variable length array of pointers which * + * represents this array of scalars (strings). If pm->ename is * + * non NULL, then it is a colon separated environment variable * + * version of this array which will need to be updated. */ + +/**/ +mod_export void +arrvarsetfn(Param pm, char **x) +{ + char ***dptr = (char ***)pm->u.data; + + if (*dptr != x) + freearray(*dptr); + if (pm->node.flags & PM_UNIQUE) + uniqarray(x); + /* + * Special tied arrays point to variables accessible in other + * ways which need to be set to NULL. We can't do this + * with user tied variables since we can leak memory. + */ + if ((pm->node.flags & PM_SPECIAL) && !x) + *dptr = mkarray(NULL); + else + *dptr = x; + if (pm->ename) { + if (x) + arrfixenv(pm->ename, x); + else if (*dptr == path) + pathchecked = path; + } +} + +/**/ +char * +colonarrgetfn(Param pm) +{ + char ***dptr = (char ***)pm->u.data; + return *dptr ? zjoin(*dptr, ':', 1) : ""; +} + +/**/ +void +colonarrsetfn(Param pm, char *x) +{ + char ***dptr = (char ***)pm->u.data; + /* + * We have to make sure this is never NULL, since that + * can cause problems. + */ + if (*dptr) + freearray(*dptr); + if (x) + *dptr = colonsplit(x, pm->node.flags & PM_UNIQUE); + else + *dptr = mkarray(NULL); + arrfixenv(pm->node.nam, *dptr); + zsfree(x); +} + +/**/ +char * +tiedarrgetfn(Param pm) +{ + struct tieddata *dptr = (struct tieddata *)pm->u.data; + return *dptr->arrptr ? zjoin(*dptr->arrptr, STOUC(dptr->joinchar), 1) : ""; +} + +/**/ +void +tiedarrsetfn(Param pm, char *x) +{ + struct tieddata *dptr = (struct tieddata *)pm->u.data; + + if (*dptr->arrptr) + freearray(*dptr->arrptr); + if (x) { + char sepbuf[3]; + if (imeta(dptr->joinchar)) + { + sepbuf[0] = Meta; + sepbuf[1] = dptr->joinchar ^ 32; + sepbuf[2] = '\0'; + } + else + { + sepbuf[0] = dptr->joinchar; + sepbuf[1] = '\0'; + } + *dptr->arrptr = sepsplit(x, sepbuf, 0, 0); + if (pm->node.flags & PM_UNIQUE) + uniqarray(*dptr->arrptr); + zsfree(x); + } else + *dptr->arrptr = NULL; + if (pm->ename) + arrfixenv(pm->node.nam, *dptr->arrptr); +} + +/**/ +void +tiedarrunsetfn(Param pm, UNUSED(int exp)) +{ + /* + * Special unset function because we allocated a struct tieddata + * in typeset_single to hold the special data which we now + * need to delete. + */ + pm->gsu.s->setfn(pm, NULL); + zfree(pm->u.data, sizeof(struct tieddata)); + /* paranoia -- shouldn't need these, but in case we reuse the struct... */ + pm->u.data = NULL; + zsfree(pm->ename); + pm->ename = NULL; + pm->node.flags &= ~PM_TIED; + pm->node.flags |= PM_UNSET; +} + +/**/ +static void +simple_arrayuniq(char **x, int freeok) +{ + char **t, **p = x; + char *hole = ""; + + /* Find duplicates and replace them with holes */ + while (*++p) + for (t = x; t < p; t++) + if (*t != hole && !strcmp(*p, *t)) { + if (freeok) + zsfree(*p); + *p = hole; + break; + } + /* Swap non-holes into holes in optimal jumps */ + for (p = t = x; *t != NULL; t++) { + if (*t == hole) { + while (*p == hole) + ++p; + if ((*t = *p) != NULL) + *p++ = hole; + } else if (p == t) + p++; + } + /* Erase all the remaining holes, just in case */ + while (++t < p) + *t = NULL; +} + +/**/ +static void +arrayuniq_freenode(HashNode hn) +{ + (void)hn; +} + +/**/ +HashTable +newuniqtable(zlong size) +{ + HashTable ht = newhashtable((int)size, "arrayuniq", NULL); + /* ??? error checking */ + + ht->hash = hasher; + ht->emptytable = emptyhashtable; + ht->filltable = NULL; + ht->cmpnodes = strcmp; + ht->addnode = addhashnode; + ht->getnode = gethashnode2; + ht->getnode2 = gethashnode2; + ht->removenode = removehashnode; + ht->disablenode = disablehashnode; + ht->enablenode = enablehashnode; + ht->freenode = arrayuniq_freenode; + ht->printnode = NULL; + + return ht; +} + +/**/ +static void +arrayuniq(char **x, int freeok) +{ + char **it, **write_it; + zlong array_size = arrlen(x); + HashTable ht; + + if (array_size == 0) + return; + if (array_size < 10 || !(ht = newuniqtable(array_size + 1))) { + /* fallback to simpler routine */ + simple_arrayuniq(x, freeok); + return; + } + + for (it = x, write_it = x; *it;) { + if (! gethashnode2(ht, *it)) { + HashNode new_node = zhalloc(sizeof(struct hashnode)); + if (!new_node) { + /* Oops, out of heap memory, no way to recover */ + zerr("out of memory in arrayuniq"); + break; + } + (void) addhashnode2(ht, *it, new_node); + *write_it = *it; + if (it != write_it) + *it = NULL; + ++write_it; + } + else { + if (freeok) + zsfree(*it); + *it = NULL; + } + ++it; + } + + deletehashtable(ht); +} + +/**/ +void +uniqarray(char **x) +{ + if (!x || !*x) + return; + arrayuniq(x, !zheapptr(*x)); +} + +/**/ +void +zhuniqarray(char **x) +{ + if (!x || !*x) + return; + arrayuniq(x, 0); +} + +/* Function to get value of special parameter `#' and `ARGC' */ + +/**/ +zlong +poundgetfn(UNUSED(Param pm)) +{ + return arrlen(pparams); +} + +/* Function to get value for special parameter `RANDOM' */ + +/**/ +zlong +randomgetfn(UNUSED(Param pm)) +{ + return rand() & 0x7fff; +} + +/* Function to set value of special parameter `RANDOM' */ + +/**/ +void +randomsetfn(UNUSED(Param pm), zlong v) +{ + srand((unsigned int)v); +} + +/* Function to get value for special parameter `SECONDS' */ + +/**/ +zlong +intsecondsgetfn(UNUSED(Param pm)) +{ + struct timeval now; + struct timezone dummy_tz; + + gettimeofday(&now, &dummy_tz); + + return (zlong)(now.tv_sec - shtimer.tv_sec - + (now.tv_usec < shtimer.tv_usec ? 1 : 0)); +} + +/* Function to set value of special parameter `SECONDS' */ + +/**/ +void +intsecondssetfn(UNUSED(Param pm), zlong x) +{ + struct timeval now; + struct timezone dummy_tz; + zlong diff; + + gettimeofday(&now, &dummy_tz); + diff = (zlong)now.tv_sec - x; + shtimer.tv_sec = diff; + if ((zlong)shtimer.tv_sec != diff) + zwarn("SECONDS truncated on assignment"); + shtimer.tv_usec = now.tv_usec; +} + +/**/ +double +floatsecondsgetfn(UNUSED(Param pm)) +{ + struct timeval now; + struct timezone dummy_tz; + + gettimeofday(&now, &dummy_tz); + + return (double)(now.tv_sec - shtimer.tv_sec) + + (double)(now.tv_usec - shtimer.tv_usec) / 1000000.0; +} + +/**/ +void +floatsecondssetfn(UNUSED(Param pm), double x) +{ + struct timeval now; + struct timezone dummy_tz; + + gettimeofday(&now, &dummy_tz); + shtimer.tv_sec = now.tv_sec - (zlong)x; + shtimer.tv_usec = now.tv_usec - (zlong)((x - (zlong)x) * 1000000.0); +} + +/**/ +double +getrawseconds(void) +{ + return (double)shtimer.tv_sec + (double)shtimer.tv_usec / 1000000.0; +} + +/**/ +void +setrawseconds(double x) +{ + shtimer.tv_sec = (zlong)x; + shtimer.tv_usec = (zlong)((x - (zlong)x) * 1000000.0); +} + +/**/ +int +setsecondstype(Param pm, int on, int off) +{ + int newflags = (pm->node.flags | on) & ~off; + int tp = PM_TYPE(newflags); + /* Only one of the numeric types is allowed. */ + if (tp == PM_EFLOAT || tp == PM_FFLOAT) + { + pm->gsu.f = &floatseconds_gsu; + } + else if (tp == PM_INTEGER) + { + pm->gsu.i = &intseconds_gsu; + } + else + return 1; + pm->node.flags = newflags; + return 0; +} + +/* Function to get value for special parameter `USERNAME' */ + +/**/ +char * +usernamegetfn(UNUSED(Param pm)) +{ + return get_username(); +} + +/* Function to set value of special parameter `USERNAME' */ + +/**/ +void +usernamesetfn(UNUSED(Param pm), char *x) +{ +#if defined(HAVE_SETUID) && defined(HAVE_GETPWNAM) + struct passwd *pswd; + + if (x && (pswd = getpwnam(x)) && (pswd->pw_uid != cached_uid)) { +# ifdef USE_INITGROUPS + initgroups(x, pswd->pw_gid); +# endif + if (setgid(pswd->pw_gid)) + zwarn("failed to change group ID: %e", errno); + else if (setuid(pswd->pw_uid)) + zwarn("failed to change user ID: %e", errno); + else { + zsfree(cached_username); + cached_username = ztrdup(pswd->pw_name); + cached_uid = pswd->pw_uid; + } + } +#endif /* HAVE_SETUID && HAVE_GETPWNAM */ + zsfree(x); +} + +/* Function to get value for special parameter `UID' */ + +/**/ +zlong +uidgetfn(UNUSED(Param pm)) +{ + return getuid(); +} + +/* Function to set value of special parameter `UID' */ + +/**/ +void +uidsetfn(UNUSED(Param pm), zlong x) +{ +#ifdef HAVE_SETUID + if (setuid((uid_t)x)) + zerr("failed to change user ID: %e", errno); +#endif +} + +/* Function to get value for special parameter `EUID' */ + +/**/ +zlong +euidgetfn(UNUSED(Param pm)) +{ + return geteuid(); +} + +/* Function to set value of special parameter `EUID' */ + +/**/ +void +euidsetfn(UNUSED(Param pm), zlong x) +{ +#ifdef HAVE_SETEUID + if (seteuid((uid_t)x)) + zerr("failed to change effective user ID: %e", errno); +#endif +} + +/* Function to get value for special parameter `GID' */ + +/**/ +zlong +gidgetfn(UNUSED(Param pm)) +{ + return getgid(); +} + +/* Function to set value of special parameter `GID' */ + +/**/ +void +gidsetfn(UNUSED(Param pm), zlong x) +{ +#ifdef HAVE_SETUID + if (setgid((gid_t)x)) + zerr("failed to change group ID: %e", errno); +#endif +} + +/* Function to get value for special parameter `EGID' */ + +/**/ +zlong +egidgetfn(UNUSED(Param pm)) +{ + return getegid(); +} + +/* Function to set value of special parameter `EGID' */ + +/**/ +void +egidsetfn(UNUSED(Param pm), zlong x) +{ +#ifdef HAVE_SETEUID + if (setegid((gid_t)x)) + zerr("failed to change effective group ID: %e", errno); +#endif +} + +/**/ +zlong +ttyidlegetfn(UNUSED(Param pm)) +{ + struct stat ttystat; + + if (SHTTY == -1 || fstat(SHTTY, &ttystat)) + return -1; + return time(NULL) - ttystat.st_atime; +} + +/* Function to get value for special parameter `IFS' */ + +/**/ +char * +ifsgetfn(UNUSED(Param pm)) +{ + return ifs; +} + +/* Function to set value of special parameter `IFS' */ + +/**/ +void +ifssetfn(UNUSED(Param pm), char *x) +{ + zsfree(ifs); + ifs = x; + inittyptab(); +} + +/* Functions to set value of special parameters `LANG' and `LC_*' */ + +#ifdef USE_LOCALE +static struct localename { + char *name; + int category; +} lc_names[] = { +#ifdef LC_COLLATE + {"LC_COLLATE", LC_COLLATE}, +#endif +#ifdef LC_CTYPE + {"LC_CTYPE", LC_CTYPE}, +#endif +#ifdef LC_MESSAGES + {"LC_MESSAGES", LC_MESSAGES}, +#endif +#ifdef LC_NUMERIC + {"LC_NUMERIC", LC_NUMERIC}, +#endif +#ifdef LC_TIME + {"LC_TIME", LC_TIME}, +#endif + {NULL, 0} +}; + +/**/ +static void +setlang(char *x) +{ + struct localename *ln; + char *x2; + + if ((x2 = getsparam_u("LC_ALL")) && *x2) + return; + + /* + * Set the global locale to the value passed, but override + * this with any non-empty definitions for specific + * categories. + * + * We only use non-empty definitions because empty values aren't + * valid as locales; when passed to setlocale() they mean "use the + * environment variable", but if that's what we're setting the value + * from this is meaningless. So just all $LANG to show through in + * that case. + */ + setlocale(LC_ALL, x ? unmeta(x) : ""); + queue_signals(); + for (ln = lc_names; ln->name; ln++) + if ((x = getsparam_u(ln->name)) && *x) + setlocale(ln->category, x); + unqueue_signals(); +} + +/**/ +void +lc_allsetfn(Param pm, char *x) +{ + strsetfn(pm, x); + /* + * Treat an empty LC_ALL the same as an unset one, + * namely by using LANG as the default locale but overriding + * that with any LC_* that are set. + */ + if (!x || !*x) { + x = getsparam_u("LANG"); + if (x && *x) { + queue_signals(); + setlang(x); + unqueue_signals(); + } + } + else + setlocale(LC_ALL, unmeta(x)); +} + +/**/ +void +langsetfn(Param pm, char *x) +{ + strsetfn(pm, x); + setlang(unmeta(x)); +} + +/**/ +void +lcsetfn(Param pm, char *x) +{ + char *x2; + struct localename *ln; + + strsetfn(pm, x); + if ((x2 = getsparam("LC_ALL")) && *x2) + return; + queue_signals(); + /* Treat empty LC_* the same as unset. */ + if (!x || !*x) + x = getsparam("LANG"); + + /* + * If we've got no non-empty string at this + * point (after checking $LANG, too), + * we shouldn't bother setting anything. + */ + if (x && *x) { + for (ln = lc_names; ln->name; ln++) + if (!strcmp(ln->name, pm->node.nam)) + 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)) + zerr("read-only variable: 0"); + else { + zsfree(argzero); + argzero = ztrdup(x); + } + zsfree(x); + } +} + +/* Function to get value for special parameter `0' */ + +/**/ +static char * +argzerogetfn(UNUSED(Param pm)) +{ + if (isset(POSIXARGZERO)) + return posixzero; + return argzero; +} + +/* Function to get value for special parameter `HISTSIZE' */ + +/**/ +zlong +histsizegetfn(UNUSED(Param pm)) +{ + return histsiz; +} + +/* Function to set value of special parameter `HISTSIZE' */ + +/**/ +void +histsizesetfn(UNUSED(Param pm), zlong v) +{ + if ((histsiz = v) < 1) + histsiz = 1; + resizehistents(); +} + +/* Function to get value for special parameter `SAVEHIST' */ + +/**/ +zlong +savehistsizegetfn(UNUSED(Param pm)) +{ + return savehistsiz; +} + +/* Function to set value of special parameter `SAVEHIST' */ + +/**/ +void +savehistsizesetfn(UNUSED(Param pm), zlong v) +{ + if ((savehistsiz = v) < 0) + savehistsiz = 0; +} + +/* Function to set value for special parameter `ERRNO' */ + +/**/ +void +errnosetfn(UNUSED(Param pm), zlong x) +{ + errno = (int)x; + if ((zlong)errno != x) + zwarn("errno truncated on assignment"); +} + +/* Function to get value for special parameter `ERRNO' */ + +/**/ +zlong +errnogetfn(UNUSED(Param pm)) +{ + return errno; +} + +/* Function to get value for special parameter `KEYBOARD_HACK' */ + +/**/ +char * +keyboardhackgetfn(UNUSED(Param pm)) +{ + static char buf[2]; + + buf[0] = keyboardhackchar; + buf[1] = '\0'; + return buf; +} + + +/* Function to set value of special parameter `KEYBOARD_HACK' */ + +/**/ +void +keyboardhacksetfn(UNUSED(Param pm), char *x) +{ + if (x) { + int len, i; + + unmetafy(x, &len); + if (len > 1) { + len = 1; + zwarn("Only one KEYBOARD_HACK character can be defined"); /* could be changed if needed */ + } + for (i = 0; i < len; i++) { + if (!isascii(STOUC(x[i]))) { + zwarn("KEYBOARD_HACK can only contain ASCII characters"); + return; + } + } + keyboardhackchar = len ? STOUC(x[0]) : '\0'; + free(x); + } else + keyboardhackchar = '\0'; +} + +/* Function to get value for special parameter `histchar' */ + +/**/ +char * +histcharsgetfn(UNUSED(Param pm)) +{ + static char buf[4]; + + buf[0] = bangchar; + buf[1] = hatchar; + buf[2] = hashchar; + buf[3] = '\0'; + return buf; +} + +/* Function to set value of special parameter `histchar' */ + +/**/ +void +histcharssetfn(UNUSED(Param pm), char *x) +{ + if (x) { + int len, i; + + unmetafy(x, &len); + if (len > 3) + len = 3; + for (i = 0; i < len; i++) { + if (!isascii(STOUC(x[i]))) { + zwarn("HISTCHARS can only contain ASCII characters"); + return; + } + } + bangchar = len ? STOUC(x[0]) : '\0'; + hatchar = len > 1 ? STOUC(x[1]) : '\0'; + hashchar = len > 2 ? STOUC(x[2]) : '\0'; + free(x); + } else { + bangchar = '!'; + hashchar = '#'; + hatchar = '^'; + } + inittyptab(); +} + +/* Function to get value for special parameter `HOME' */ + +/**/ +char * +homegetfn(UNUSED(Param pm)) +{ + return home; +} + +/* Function to set value of special parameter `HOME' */ + +/**/ +void +homesetfn(UNUSED(Param pm), char *x) +{ + zsfree(home); + if (x && isset(CHASELINKS) && (home = xsymlink(x, 0))) + zsfree(x); + else + home = x ? x : ztrdup(""); + finddir(NULL); +} + +/* Function to get value for special parameter `WORDCHARS' */ + +/**/ +char * +wordcharsgetfn(UNUSED(Param pm)) +{ + return wordchars; +} + +/* Function to set value of special parameter `WORDCHARS' */ + +/**/ +void +wordcharssetfn(UNUSED(Param pm), char *x) +{ + zsfree(wordchars); + wordchars = x; + inittyptab(); +} + +/* Function to get value for special parameter `_' */ + +/**/ +char * +underscoregetfn(UNUSED(Param pm)) +{ + char *u = dupstring(zunderscore); + + untokenize(u); + return u; +} + +/* Function used when we need to reinitialise the terminal */ + +static void +term_reinit_from_pm(void) +{ + /* If non-interactive, delay setting up term till we need it. */ + if (unset(INTERACTIVE) || !*term) + termflags |= TERM_UNKNOWN; + else + init_term(); +} + +/* Function to get value for special parameter `TERM' */ + +/**/ +char * +termgetfn(UNUSED(Param pm)) +{ + return term; +} + +/* Function to set value of special parameter `TERM' */ + +/**/ +void +termsetfn(UNUSED(Param pm), char *x) +{ + zsfree(term); + term = x ? x : ztrdup(""); + term_reinit_from_pm(); +} + +/* Function to get value of special parameter `TERMINFO' */ + +/**/ +char * +terminfogetfn(UNUSED(Param pm)) +{ + return zsh_terminfo ? zsh_terminfo : dupstring(""); +} + +/* Function to set value of special parameter `TERMINFO' */ + +/**/ +void +terminfosetfn(Param pm, char *x) +{ + zsfree(zsh_terminfo); + zsh_terminfo = x; + + /* + * terminfo relies on the value being exported before + * we reinitialise the terminal. This is a bit inefficient. + */ + if ((pm->node.flags & PM_EXPORTED) && x) + addenv(pm, x); + + term_reinit_from_pm(); +} + +/* Function to get value of special parameter `TERMINFO_DIRS' */ + +/**/ +char * +terminfodirsgetfn(UNUSED(Param pm)) +{ + return zsh_terminfodirs ? zsh_terminfodirs : dupstring(""); +} + +/* Function to set value of special parameter `TERMINFO_DIRS' */ + +/**/ +void +terminfodirssetfn(Param pm, char *x) +{ + zsfree(zsh_terminfodirs); + zsh_terminfodirs = x; + + /* + * terminfo relies on the value being exported before + * we reinitialise the terminal. This is a bit inefficient. + */ + if ((pm->node.flags & PM_EXPORTED) && x) + addenv(pm, x); + + term_reinit_from_pm(); +} +/* Function to get value for special parameter `pipestatus' */ + +/**/ +static char ** +pipestatgetfn(UNUSED(Param pm)) +{ + char **x = (char **) zhalloc((numpipestats + 1) * sizeof(char *)); + char buf[DIGBUFSIZE], **p; + int *q, i; + + for (p = x, q = pipestats, i = numpipestats; i--; p++, q++) { + sprintf(buf, "%d", *q); + *p = dupstring(buf); + } + *p = NULL; + + return x; +} + +/* Function to get value for special parameter `pipestatus' */ + +/**/ +static void +pipestatsetfn(UNUSED(Param pm), char **x) +{ + if (x) { + int i; + + for (i = 0; *x && i < MAX_PIPESTATS; i++, x++) + pipestats[i] = atoi(*x); + numpipestats = i; + } + else + numpipestats = 0; +} + +/**/ +void +arrfixenv(char *s, char **t) +{ + Param pm; + int joinchar; + + if (t == path) + cmdnamtab->emptytable(cmdnamtab); + + pm = (Param) paramtab->getnode(paramtab, s); + + /* + * Only one level of a parameter can be exported. Unless + * ALLEXPORT is set, this must be global. + */ + + if (pm->node.flags & PM_HASHELEM) + return; + + if (isset(ALLEXPORT)) + pm->node.flags |= PM_EXPORTED; + + /* + * Do not "fix" parameters that were not exported + */ + + if (!(pm->node.flags & PM_EXPORTED)) + return; + + if (pm->node.flags & PM_TIED) + joinchar = STOUC(((struct tieddata *)pm->u.data)->joinchar); + else + joinchar = ':'; + + addenv(pm, t ? zjoin(t, joinchar, 1) : ""); +} + + +/**/ +int +zputenv(char *str) +{ + DPUTS(!str, "Attempt to put null string into environment."); +#ifdef USE_SET_UNSET_ENV + /* + * If we are using unsetenv() to remove values from the + * environment, which is the safe thing to do, we + * need to use setenv() to put them there in the first place. + * Unfortunately this is a slightly different interface + * from what zputenv() assumes. + */ + char *ptr; + int ret; + + for (ptr = str; *ptr && STOUC(*ptr) < 128 && *ptr != '='; ptr++) + ; + if (STOUC(*ptr) >= 128) { + /* + * Environment variables not in the portable character + * set are non-standard and we don't really know of + * a use for them. + * + * We'll disable until someone complains. + */ + return 1; + } else if (*ptr) { + *ptr = '\0'; + ret = setenv(str, ptr+1, 1); + *ptr = '='; + } else { + /* safety first */ + DPUTS(1, "bad environment string"); + ret = setenv(str, ptr, 1); + } + return ret; +#else +#ifdef HAVE_PUTENV + return putenv(str); +#else + char **ep; + int num_env; + + + /* First check if there is already an environment * + * variable matching string `name'. */ + if (findenv(str, &num_env)) { + environ[num_env] = str; + } else { + /* Else we have to make room and add it */ + num_env = arrlen(environ); + environ = (char **) zrealloc(environ, (sizeof(char *)) * (num_env + 2)); + + /* Now add it at the end */ + ep = environ + num_env; + *ep = str; + *(ep + 1) = NULL; + } + return 0; +#endif +#endif +} + +/**/ +#ifndef USE_SET_UNSET_ENV +/**/ +static int +findenv(char *name, int *pos) +{ + char **ep, *eq; + int nlen; + + + eq = strchr(name, '='); + nlen = eq ? eq - name : (int)strlen(name); + for (ep = environ; *ep; ep++) + if (!strncmp (*ep, name, nlen) && *((*ep)+nlen) == '=') { + if (pos) + *pos = ep - environ; + return 1; + } + + return 0; +} +/**/ +#endif + +/* Given *name = "foo", it searches the environment for string * + * "foo=bar", and returns a pointer to the beginning of "bar" */ + +/**/ +mod_export char * +zgetenv(char *name) +{ +#ifdef HAVE_GETENV + return getenv(name); +#else + char **ep, *s, *t; + + for (ep = environ; *ep; ep++) { + for (s = *ep, t = name; *s && *s == *t; s++, t++); + if (*s == '=' && !*t) + return s + 1; + } + return NULL; +#endif +} + +/**/ +static void +copyenvstr(char *s, char *value, int flags) +{ + while (*s++) { + if ((*s = *value++) == Meta) + *s = *value++ ^ 32; + if (flags & PM_LOWER) + *s = tulower(*s); + else if (flags & PM_UPPER) + *s = tuupper(*s); + } +} + +/**/ +void +addenv(Param pm, char *value) +{ + char *newenv = 0; +#ifndef USE_SET_UNSET_ENV + char *oldenv = 0, *env = 0; + int pos; + + /* + * First check if there is already an environment + * variable matching string `name'. + */ + if (findenv(pm->node.nam, &pos)) + oldenv = environ[pos]; +#endif + + newenv = mkenvstr(pm->node.nam, value, pm->node.flags); + if (zputenv(newenv)) { + zsfree(newenv); + pm->env = NULL; + return; + } +#ifdef USE_SET_UNSET_ENV + /* + * If we are using setenv/unsetenv to manage the environment, + * we simply store the string we created in pm->env since + * memory management of the environment is handled entirely + * by the system. + * + * TODO: is this good enough to fix problem cases from + * the other branch? If so, we don't actually need to + * store pm->env at all, just a flag that the value was set. + */ + if (pm->env) + zsfree(pm->env); + pm->env = newenv; + pm->node.flags |= PM_EXPORTED; +#else + /* + * Under Cygwin we must use putenv() to maintain consistency. + * Unfortunately, current version (1.1.2) copies argument and may + * silently reuse existing environment string. This tries to + * check for both cases + */ + if (findenv(pm->node.nam, &pos)) { + env = environ[pos]; + if (env != oldenv) + zsfree(oldenv); + if (env != newenv) + zsfree(newenv); + pm->node.flags |= PM_EXPORTED; + pm->env = env; + return; + } + + DPUTS(1, "addenv should never reach the end"); + pm->env = NULL; +#endif +} + + +/* Given strings *name = "foo", *value = "bar", * + * return a new string *str = "foo=bar". */ + +/**/ +static char * +mkenvstr(char *name, char *value, int flags) +{ + char *str, *s = value; + int len_name, len_value = 0; + + len_name = strlen(name); + if (s) + while (*s && (*s++ != Meta || *s++ != 32)) + len_value++; + s = str = (char *) zalloc(len_name + len_value + 2); + strcpy(s, name); + s += len_name; + *s = '='; + if (value) + copyenvstr(s, value, flags); + else + *++s = '\0'; + return str; +} + +/* Given *name = "foo", *value = "bar", add the * + * string "foo=bar" to the environment. Return a * + * pointer to the location of this new environment * + * string. */ + + +#ifndef USE_SET_UNSET_ENV +/**/ +void +delenvvalue(char *x) +{ + char **ep; + + for (ep = environ; *ep; ep++) { + if (*ep == x) + break; + } + if (*ep) { + for (; (ep[0] = ep[1]); ep++); + } + zsfree(x); +} +#endif + + +/* Delete a pointer from the list of pointers to environment * + * variables by shifting all the other pointers up one slot. */ + +/**/ +void +delenv(Param pm) +{ +#ifdef USE_SET_UNSET_ENV + unsetenv(pm->node.nam); + zsfree(pm->env); +#else + delenvvalue(pm->env); +#endif + pm->env = NULL; + /* + * Note we don't remove PM_EXPORT from the flags. This + * may be asking for trouble but we need to know later + * if we restore this parameter to its old value. + */ +} + +/* + * Guts of convbase: this version can return the number of digits + * sans any base discriminator. + */ + +/**/ +void +convbase_ptr(char *s, zlong v, int base, int *ndigits) +{ + int digs = 0; + zulong x; + + if (v < 0) + *s++ = '-', v = -v; + if (base >= -1 && base <= 1) + base = -10; + + if (base > 0) { + if (isset(CBASES) && base == 16) + sprintf(s, "0x"); + else if (isset(CBASES) && base == 8 && isset(OCTALZEROES)) + sprintf(s, "0"); + else if (base != 10) + sprintf(s, "%d#", base); + else + *s = 0; + s += strlen(s); + } else + base = -base; + for (x = v; x; digs++) + x /= base; + if (!digs) + digs = 1; + if (ndigits) + *ndigits = digs; + s[digs--] = '\0'; + x = v; + while (digs >= 0) { + int dig = x % base; + + s[digs--] = (dig < 10) ? '0' + dig : dig - 10 + 'A'; + x /= base; + } +} + +/* + * Basic conversion of integer to a string given a base. + * If 0 base is 10. + * If negative no base discriminator is output. + */ + +/**/ +mod_export void +convbase(char *s, zlong v, int base) +{ + convbase_ptr(s, v, base, NULL); +} + +/* + * Add underscores to converted integer for readability with given spacing. + * s is as for convbase: at least BDIGBUFSIZE. + * If underscores were added, returned value with underscores comes from + * heap, else the returned value is s. + */ + +/**/ +char * +convbase_underscore(char *s, zlong v, int base, int underscore) +{ + char *retptr, *sptr, *dptr; + int ndigits, nunderscore, mod, len; + + convbase_ptr(s, v, base, &ndigits); + + if (underscore <= 0) + return s; + + nunderscore = (ndigits - 1) / underscore; + if (!nunderscore) + return s; + len = strlen(s); + retptr = zhalloc(len + nunderscore + 1); + mod = 0; + memcpy(retptr, s, len - ndigits); + sptr = s + len; + dptr = retptr + len + nunderscore; + /* copy the null */ + *dptr-- = *sptr--; + for (;;) { + *dptr = *sptr; + if (!--ndigits) + break; + dptr--; + sptr--; + if (++mod == underscore) { + mod = 0; + *dptr-- = '_'; + } + } + + return retptr; +} + +/* + * Convert a floating point value for output. + * Unlike convbase(), this has its own internal storage and returns + * a value from the heap. + */ + +/**/ +char * +convfloat(double dval, int digits, int flags, FILE *fout) +{ + char fmt[] = "%.*e"; + char *prev_locale, *ret; + + /* + * The difficulty with the buffer size is that a %f conversion + * prints all digits before the decimal point: with 64 bit doubles, + * that's around 310. We can't check without doing some quite + * serious floating point operations we'd like to avoid. + * Then we are liable to get all the digits + * we asked for after the decimal point, or we should at least + * bargain for it. So we just allocate 512 + digits. This + * should work until somebody decides on 128-bit doubles. + */ + if (!(flags & (PM_EFLOAT|PM_FFLOAT))) { + /* + * Conversion from a floating point expression without using + * a variable. The best bet in this case just seems to be + * to use the general %g format with something like the maximum + * double precision. + */ + fmt[3] = 'g'; + if (!digits) + digits = 17; + } else { + if (flags & PM_FFLOAT) + fmt[3] = 'f'; + if (digits <= 0) + digits = 10; + if (flags & PM_EFLOAT) { + /* + * Here, we are given the number of significant figures, but + * %e wants the number of decimal places (unlike %g) + */ + digits--; + } + } +#ifdef USE_LOCALE + prev_locale = dupstring(setlocale(LC_NUMERIC, NULL)); + setlocale(LC_NUMERIC, "POSIX"); +#endif + if (fout) { + fprintf(fout, fmt, digits, dval); + ret = NULL; + } else { + VARARR(char, buf, 512 + digits); + if (isinf(dval)) + ret = dupstring((dval < 0.0) ? "-Inf" : "Inf"); + else if (isnan(dval)) + ret = dupstring("NaN"); + else { + sprintf(buf, fmt, digits, dval); + if (!strchr(buf, 'e') && !strchr(buf, '.')) + strcat(buf, "."); + ret = dupstring(buf); + } + } +#ifdef USE_LOCALE + if (prev_locale) setlocale(LC_NUMERIC, prev_locale); +#endif + return ret; +} + +/* + * convert float to string with basic options but inserting underscores + * for readability. + */ + +/**/ +char *convfloat_underscore(double dval, int underscore) +{ + int ndigits_int = 0, ndigits_frac = 0, nunderscore, len; + char *s, *retptr, *sptr, *dptr; + + s = convfloat(dval, 0, 0, NULL); + if (underscore <= 0) + return s; + + /* + * Count the number of digits before and after the decimal point, if any. + */ + sptr = s; + if (*sptr == '-') + sptr++; + while (idigit(*sptr)) { + ndigits_int++; + sptr++; + } + if (*sptr == '.') { + sptr++; + while (idigit(*sptr)) { + ndigits_frac++; + sptr++; + } + } + + /* + * Work out how many underscores to insert --- remember we + * put them in integer and fractional parts separately. + */ + nunderscore = (ndigits_int-1) / underscore + (ndigits_frac-1) / underscore; + if (!nunderscore) + return s; + len = strlen(s); + dptr = retptr = zhalloc(len + nunderscore + 1); + + /* + * Insert underscores in integer part. + * Grouping starts from the point in both directions. + */ + sptr = s; + if (*sptr == '-') + *dptr++ = *sptr++; + while (ndigits_int) { + *dptr++ = *sptr++; + if (--ndigits_int && !(ndigits_int % underscore)) + *dptr++ = '_'; + } + if (ndigits_frac) { + /* + * Insert underscores in the fractional part. + */ + int mod = 0; + /* decimal point, we already checked */ + *dptr++ = *sptr++; + while (ndigits_frac) { + *dptr++ = *sptr++; + mod++; + if (--ndigits_frac && mod == underscore) { + *dptr++ = '_'; + mod = 0; + } + } + } + /* Copy exponent and anything else up to null */ + while ((*dptr++ = *sptr++)) + ; + return retptr; +} + +/* Start a parameter scope */ + +/**/ +mod_export void +startparamscope(void) +{ + locallevel++; +} + +/* End a parameter scope: delete the parameters local to the scope. */ + +/**/ +mod_export void +endparamscope(void) +{ + queue_signals(); + locallevel--; + /* This pops anything from a higher locallevel */ + saveandpophiststack(0, HFILE_USE_OPTIONS); + scanhashtable(paramtab, 0, 0, 0, scanendscope, 0); + unqueue_signals(); +} + +/**/ +static void +scanendscope(HashNode hn, UNUSED(int flags)) +{ + Param pm = (Param)hn; + if (pm->level > locallevel) { + if ((pm->node.flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL) { + /* + * Removable specials are normal in that they can be removed + * to reveal an ordinary parameter beneath. Here we handle + * non-removable specials, which were made local by stealth + * (see newspecial code in typeset_single()). In fact the + * visible pm is always the same struct; the pm->old is + * just a place holder for old data and flags. + */ + Param tpm = pm->old; + + if (!strcmp(pm->node.nam, "SECONDS")) + { + setsecondstype(pm, PM_TYPE(tpm->node.flags), PM_TYPE(pm->node.flags)); + /* + * We restore SECONDS by restoring its raw internal value + * that we cached off into tpm->u.dval. + */ + setrawseconds(tpm->u.dval); + tpm->node.flags |= PM_NORESTORE; + } + DPUTS(!tpm || PM_TYPE(pm->node.flags) != PM_TYPE(tpm->node.flags) || + !(tpm->node.flags & PM_SPECIAL), + "BUG: in restoring scope of special parameter"); + pm->old = tpm->old; + pm->node.flags = (tpm->node.flags & ~PM_NORESTORE); + pm->level = tpm->level; + pm->base = tpm->base; + pm->width = tpm->width; + if (pm->env) + delenv(pm); + + if (!(tpm->node.flags & (PM_NORESTORE|PM_READONLY))) + switch (PM_TYPE(pm->node.flags)) { + case PM_SCALAR: + pm->gsu.s->setfn(pm, tpm->u.str); + break; + case PM_INTEGER: + pm->gsu.i->setfn(pm, tpm->u.val); + break; + case PM_EFLOAT: + case PM_FFLOAT: + pm->gsu.f->setfn(pm, tpm->u.dval); + break; + case PM_ARRAY: + pm->gsu.a->setfn(pm, tpm->u.arr); + break; + case PM_HASHED: + pm->gsu.h->setfn(pm, tpm->u.hash); + break; + } + zfree(tpm, sizeof(*tpm)); + + if (pm->node.flags & PM_EXPORTED) + export_param(pm); + } else + unsetparam_pm(pm, 0, 0); + } +} + + +/**********************************/ +/* Parameter Hash Table Functions */ +/**********************************/ + +/**/ +void +freeparamnode(HashNode hn) +{ + Param pm = (Param) hn; + + /* The second argument of unsetfn() is used by modules to + * differentiate "exp"licit unset from implicit unset, as when + * a parameter is going out of scope. It's not clear which + * of these applies here, but passing 1 has always worked. + */ + if (delunset) + pm->gsu.s->unsetfn(pm, 1); + zsfree(pm->node.nam); + /* If this variable was tied by the user, ename was ztrdup'd */ + if (pm->node.flags & PM_TIED) + zsfree(pm->ename); + zfree(pm, sizeof(struct param)); +} + +/* Print a parameter */ + +enum paramtypes_flags { + PMTF_USE_BASE = (1<<0), + PMTF_USE_WIDTH = (1<<1), + PMTF_TEST_LEVEL = (1<<2) +}; + +struct paramtypes { + int binflag; /* The relevant PM_FLAG(S) */ + const char *string; /* String for verbose output */ + int typeflag; /* Flag for typeset -? */ + int flags; /* The enum above */ +}; + +static const struct paramtypes pmtypes[] = { + { PM_AUTOLOAD, "undefined", 0, 0}, + { PM_INTEGER, "integer", 'i', PMTF_USE_BASE}, + { PM_EFLOAT, "float", 'E', 0}, + { PM_FFLOAT, "float", 'F', 0}, + { PM_ARRAY, "array", 'a', 0}, + { PM_HASHED, "association", 'A', 0}, + { 0, "local", 0, PMTF_TEST_LEVEL}, + { PM_LEFT, "left justified", 'L', PMTF_USE_WIDTH}, + { PM_RIGHT_B, "right justified", 'R', PMTF_USE_WIDTH}, + { PM_RIGHT_Z, "zero filled", 'Z', PMTF_USE_WIDTH}, + { PM_LOWER, "lowercase", 'l', 0}, + { PM_UPPER, "uppercase", 'u', 0}, + { PM_READONLY, "readonly", 'r', 0}, + { PM_TAGGED, "tagged", 't', 0}, + { PM_EXPORTED, "exported", 'x', 0} +}; + +#define PMTYPES_SIZE ((int)(sizeof(pmtypes)/sizeof(struct paramtypes))) + +static void +printparamvalue(Param p, int printflags) +{ + char *t, **u; + + if (!(printflags & PRINT_KV_PAIR)) + putchar('='); + + /* How the value is displayed depends * + * on the type of the parameter */ + switch (PM_TYPE(p->node.flags)) { + case PM_SCALAR: + /* string: simple output */ + if (p->gsu.s->getfn && (t = p->gsu.s->getfn(p))) + quotedzputs(t, stdout); + break; + case PM_INTEGER: + /* integer */ +#ifdef ZSH_64_BIT_TYPE + fputs(output64(p->gsu.i->getfn(p)), stdout); +#else + printf("%ld", p->gsu.i->getfn(p)); +#endif + break; + case PM_EFLOAT: + case PM_FFLOAT: + /* float */ + convfloat(p->gsu.f->getfn(p), p->base, p->node.flags, stdout); + break; + case PM_ARRAY: + /* array */ + if (!(printflags & PRINT_KV_PAIR)) { + putchar('('); + if (!(printflags & PRINT_LINE)) + putchar(' '); + } + u = p->gsu.a->getfn(p); + if(*u) { + if (printflags & PRINT_LINE) { + if (printflags & PRINT_KV_PAIR) + printf(" "); + else + printf("\n "); + } + quotedzputs(*u++, stdout); + while (*u) { + if (printflags & PRINT_LINE) + printf("\n "); + else + putchar(' '); + quotedzputs(*u++, stdout); + } + if ((printflags & (PRINT_LINE|PRINT_KV_PAIR)) == PRINT_LINE) + putchar('\n'); + } + if (!(printflags & PRINT_KV_PAIR)) { + if (!(printflags & PRINT_LINE)) + putchar(' '); + putchar(')'); + } + break; + case PM_HASHED: + /* association */ + { + HashTable ht; + int found = 0; + if (!(printflags & PRINT_KV_PAIR)) { + putchar('('); + if (!(printflags & PRINT_LINE)) + putchar(' '); + } + ht = p->gsu.h->getfn(p); + if (ht) + found = scanhashtable(ht, 1, 0, PM_UNSET, + ht->printnode, PRINT_KV_PAIR | + (printflags & PRINT_LINE)); + if (!(printflags & PRINT_KV_PAIR)) { + if (found && (printflags & PRINT_LINE)) + putchar('\n'); + putchar(')'); + } + } + break; + } + if ((printflags & (PRINT_KV_PAIR|PRINT_LINE)) == PRINT_KV_PAIR) + putchar(' '); + else if (!(printflags & PRINT_KV_PAIR)) + putchar('\n'); +} + +/**/ +mod_export void +printparamnode(HashNode hn, int printflags) +{ + Param p = (Param) hn; + + if (p->node.flags & PM_UNSET) { + if (isset(POSIXBUILTINS) && (p->node.flags & PM_READONLY) && + (printflags & PRINT_TYPESET)) + { + /* + * Special POSIX rules: show the parameter as readonly + * even though it's unset, but with no value. + */ + printflags |= PRINT_NAMEONLY; + } + else if (p->node.flags & PM_EXPORTED) + printflags |= PRINT_NAMEONLY; + else + return; + } + if (p->node.flags & PM_AUTOLOAD) + printflags |= PRINT_NAMEONLY; + + if (printflags & PRINT_TYPESET) { + if ((p->node.flags & (PM_READONLY|PM_SPECIAL)) == + (PM_READONLY|PM_SPECIAL) || + (p->node.flags & PM_AUTOLOAD)) { + /* + * It's not possible to restore the state of + * these, so don't output. + */ + return; + } + if (locallevel && p->level >= locallevel) { + printf("typeset "); /* printf("local "); */ + } else if ((p->node.flags & PM_EXPORTED) && + !(p->node.flags & (PM_ARRAY|PM_HASHED))) { + printf("export "); + } else if (locallevel) { + printf("typeset -g "); + } else + printf("typeset "); + } + + /* Print the attributes of the parameter */ + if (printflags & (PRINT_TYPE|PRINT_TYPESET)) { + int doneminus = 0, i; + const struct paramtypes *pmptr; + + for (pmptr = pmtypes, i = 0; i < PMTYPES_SIZE; i++, pmptr++) { + int doprint = 0; + if (pmptr->flags & PMTF_TEST_LEVEL) { + if (p->level) + doprint = 1; + } else if ((pmptr->binflag != PM_EXPORTED || p->level || + (p->node.flags & (PM_LOCAL|PM_ARRAY|PM_HASHED))) && + (p->node.flags & pmptr->binflag)) + doprint = 1; + + if (doprint) { + if (printflags & PRINT_TYPESET) { + if (pmptr->typeflag) { + if (!doneminus) { + putchar('-'); + doneminus = 1; + } + putchar(pmptr->typeflag); + } + } else + printf("%s ", pmptr->string); + if ((pmptr->flags & PMTF_USE_BASE) && p->base) { + printf("%d ", p->base); + doneminus = 0; + } + if ((pmptr->flags & PMTF_USE_WIDTH) && p->width) { + printf("%d ", p->width); + doneminus = 0; + } + } + } + if (doneminus) + putchar(' '); + } + + if ((printflags & PRINT_NAMEONLY) || + ((p->node.flags & PM_HIDEVAL) && !(printflags & PRINT_INCLUDEVALUE))) { + zputs(p->node.nam, stdout); + putchar('\n'); + } else { + if (printflags & PRINT_KV_PAIR) { + if (printflags & PRINT_LINE) + printf("\n "); + putchar('['); + } + quotedzputs(p->node.nam, stdout); + if (printflags & PRINT_KV_PAIR) + printf("]="); + + printparamvalue(p, printflags); + } +} diff --git a/dotfiles/system/.zsh/modules/Src/parse.c b/dotfiles/system/.zsh/modules/Src/parse.c new file mode 100644 index 0000000..83383f1 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/parse.c @@ -0,0 +1,3977 @@ +/* + * parse.c - parser + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "parse.pro" + +/* != 0 if we are about to read a command word */ + +/**/ +mod_export int incmdpos; + +/**/ +int aliasspaceflag; + +/* != 0 if we are in the middle of a [[ ... ]] */ + +/**/ +mod_export int incond; + +/* != 0 if we are after a redirection (for ctxtlex only) */ + +/**/ +mod_export int inredir; + +/* + * 1 if we are about to read a case pattern + * -1 if we are not quite sure + * 0 otherwise + */ + +/**/ +int incasepat; + +/* != 0 if we just read a newline */ + +/**/ +int isnewlin; + +/* != 0 if we are after a for keyword */ + +/**/ +int infor; + +/* != 0 if we are after a repeat keyword; if it's nonzero it's a 1-based index + * of the current token from the last-seen command position */ + +/**/ +int inrepeat_; /* trailing underscore because of name clash with Zle/zle_vi.c */ + +/* != 0 if parsing arguments of typeset etc. */ + +/**/ +mod_export int intypeset; + +/* list of here-documents */ + +/**/ +struct heredocs *hdocs; + + +#define YYERROR(O) { tok = LEXERR; ecused = (O); return 0; } +#define YYERRORV(O) { tok = LEXERR; ecused = (O); return; } +#define COND_ERROR(X,Y) \ + do { \ + zwarn(X,Y); \ + herrflush(); \ + if (noerrs != 2) \ + errflag |= ERRFLAG_ERROR; \ + YYERROR(ecused) \ + } while(0) + + +/* + * Word code. + * + * The parser now produces word code, reducing memory consumption compared + * to the nested structs we had before. + * + * Word code layout: + * + * WC_END + * - end of program code + * + * WC_LIST + * - data contains type (sync, ...) + * - followed by code for this list + * - if not (type & Z_END), followed by next WC_LIST + * + * WC_SUBLIST + * - data contains type (&&, ||, END) and flags (coprog, not) + * - followed by code for sublist + * - if not (type == END), followed by next WC_SUBLIST + * + * WC_PIPE + * - data contains type (end, mid) and LINENO + * - if not (type == END), followed by offset to next WC_PIPE + * - followed by command + * - if not (type == END), followed by next WC_PIPE + * + * WC_REDIR + * - must precede command-code (or WC_ASSIGN) + * - data contains type (<, >, ...) + * - followed by fd1 and name from struct redir + * - for the extended form {var}>... where the fd is assigned + * to var, there is an extra item to contain var + * + * 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 + * + * WC_CURSH + * - data unused + * - followed by list + * + * WC_TIMED + * - data contains type (followed by pipe or not) + * - if (type == PIPE), followed by pipe + * + * WC_FUNCDEF + * - data contains offset to after body + * - followed by number of names + * - followed by names + * - followed by offset to first string + * - followed by length of string table + * - followed by number of patterns for body + * - followed by codes for body + * - followed by strings for body + * + * WC_FOR + * - data contains type (list, ...) and offset to after body + * - if (type == COND), followed by init, cond, advance expressions + * - else if (type == PPARAM), followed by param name + * - else if (type == LIST), followed by param name, num strings, strings + * - followed by body + * + * WC_SELECT + * - data contains type (list, ...) and offset to after body + * - if (type == PPARAM), followed by param name + * - else if (type == LIST), followed by param name, num strings, strings + * - followed by body + * + * WC_WHILE + * - data contains type (while, until) and offset to after body + * - followed by condition + * - followed by body + * + * WC_REPEAT + * - data contains offset to after body + * - followed by number-string + * - followed by body + * + * WC_CASE + * - first CASE is always of type HEAD, data contains offset to esac + * - after that CASEs of type OR (;;), AND (;&) and TESTAND (;|), + * data is offset to next case + * - each OR/AND/TESTAND case is followed by pattern, pattern-number, list + * + * WC_IF + * - first IF is of type HEAD, data contains offset to fi + * - after that IFs of type IF, ELIF, ELSE, data is offset to next + * - each non-HEAD is followed by condition (only IF, ELIF) and body + * + * WC_COND + * - data contains type + * - if (type == AND/OR), data contains offset to after this one, + * followed by two CONDs + * - else if (type == NOT), followed by COND + * - else if (type == MOD), followed by name and strings + * - else if (type == MODI), followed by name, left, right + * - else if (type == STR[N]EQ), followed by left, right, pattern-number + * - else if (has two args) followed by left, right + * - else followed by string + * + * WC_ARITH + * - followed by string (there's only one) + * + * WC_AUTOFN + * - only used by the autoload builtin + * + * Lists and sublists may also be simplified, indicated by the presence + * of the Z_SIMPLE or WC_SUBLIST_SIMPLE flags. In this case they are only + * followed by a slot containing the line number, not by a WC_SUBLIST or + * WC_PIPE, respectively. The real advantage of simplified lists and + * sublists is that they can be executed faster, see exec.c. In the + * parser, the test if a list can be simplified is done quite simply + * by passing a int* around which gets set to non-zero if the thing + * just parsed is `cmplx', i.e. may need to be run by forking or + * some such. + * + * In each of the above, strings are encoded as one word code. For empty + * strings this is the bit pattern 11x, the lowest bit is non-zero if the + * string contains tokens and zero otherwise (this is true for the other + * ways to encode strings, too). For short strings (one to three + * characters), this is the marker 01x with the 24 bits above that + * containing the characters. Longer strings are encoded as the offset + * into the strs character array stored in the eprog struct shifted by + * two and ored with the bit pattern 0x. + * The ecstrcode() function that adds the code for a string uses a simple + * binary tree of strings already added so that long strings are encoded + * only once. + * + * Note also that in the eprog struct the pattern, code, and string + * arrays all point to the same memory block. + * + * + * To make things even faster in future versions, we could not only + * test if the strings contain tokens, but instead what kind of + * expansions need to be done on strings. In the execution code we + * could then use these flags for a specialized version of prefork() + * to avoid a lot of string parsing and some more string duplication. + */ + +/**/ +int eclen, ecused, ecnpats; +/**/ +Wordcode ecbuf; +/**/ +Eccstr ecstrs; +/**/ +int ecsoffs, ecssub, ecnfunc; + +#define EC_INIT_SIZE 256 +#define EC_DOUBLE_THRESHOLD 32768 +#define EC_INCREMENT 1024 + +/* save parse context */ + +/**/ +void +parse_context_save(struct parse_stack *ps, int toplevel) +{ + (void)toplevel; + + ps->incmdpos = incmdpos; + ps->aliasspaceflag = aliasspaceflag; + ps->incond = incond; + ps->inredir = inredir; + ps->incasepat = incasepat; + ps->isnewlin = isnewlin; + ps->infor = infor; + ps->inrepeat_ = inrepeat_; + ps->intypeset = intypeset; + + ps->hdocs = hdocs; + ps->eclen = eclen; + ps->ecused = ecused; + ps->ecnpats = ecnpats; + ps->ecbuf = ecbuf; + ps->ecstrs = ecstrs; + ps->ecsoffs = ecsoffs; + ps->ecssub = ecssub; + ps->ecnfunc = ecnfunc; + ecbuf = NULL; + hdocs = NULL; +} + +/* restore parse context */ + +/**/ +void +parse_context_restore(const struct parse_stack *ps, int toplevel) +{ + (void)toplevel; + + if (ecbuf) + zfree(ecbuf, eclen); + + incmdpos = ps->incmdpos; + aliasspaceflag = ps->aliasspaceflag; + incond = ps->incond; + inredir = ps->inredir; + incasepat = ps->incasepat; + isnewlin = ps->isnewlin; + infor = ps->infor; + inrepeat_ = ps->inrepeat_; + intypeset = ps->intypeset; + + hdocs = ps->hdocs; + eclen = ps->eclen; + ecused = ps->ecused; + ecnpats = ps->ecnpats; + ecbuf = ps->ecbuf; + ecstrs = ps->ecstrs; + ecsoffs = ps->ecsoffs; + ecssub = ps->ecssub; + ecnfunc = ps->ecnfunc; + + errflag &= ~ERRFLAG_ERROR; +} + +/* Adjust pointers in here-doc structs. */ + +static void +ecadjusthere(int p, int d) +{ + struct heredocs *h; + + for (h = hdocs; h; h = h->next) + if (h->pc >= p) + h->pc += d; +} + +/* Insert n free code-slots at position p. */ + +static void +ecispace(int p, int n) +{ + int m; + + if ((eclen - ecused) < n) { + int a = (eclen < EC_DOUBLE_THRESHOLD ? eclen : EC_INCREMENT); + + if (n > a) a = n; + + ecbuf = (Wordcode) zrealloc((char *) ecbuf, (eclen + a) * sizeof(wordcode)); + eclen += a; + } + if ((m = ecused - p) > 0) + memmove(ecbuf + p + n, ecbuf + p, m * sizeof(wordcode)); + ecused += n; + ecadjusthere(p, n); +} + +/* Add one wordcode. */ + +static int +ecadd(wordcode c) +{ + if ((eclen - ecused) < 1) { + int a = (eclen < EC_DOUBLE_THRESHOLD ? eclen : EC_INCREMENT); + + ecbuf = (Wordcode) zrealloc((char *) ecbuf, (eclen + a) * sizeof(wordcode)); + eclen += a; + } + ecbuf[ecused] = c; + + return ecused++; +} + +/* Delete a wordcode. */ + +static void +ecdel(int p) +{ + int n = ecused - p - 1; + + if (n > 0) + memmove(ecbuf + p, ecbuf + p + 1, n * sizeof(wordcode)); + ecused--; + ecadjusthere(p, -1); +} + +/* Build the wordcode for a string. */ + +static wordcode +ecstrcode(char *s) +{ + int l, t; + + unsigned val = hasher(s); + + if ((l = strlen(s) + 1) && l <= 4) { + t = has_token(s); + wordcode c = (t ? 3 : 2); + switch (l) { + case 4: c |= ((wordcode) STOUC(s[2])) << 19; + case 3: c |= ((wordcode) STOUC(s[1])) << 11; + case 2: c |= ((wordcode) STOUC(s[0])) << 3; break; + case 1: c = (t ? 7 : 6); break; + } + return c; + } else { + Eccstr p, *pp; + int cmp; + + for (pp = &ecstrs; (p = *pp); ) { + if (!(cmp = p->nfunc - ecnfunc) && !(cmp = (((signed)p->hashval) - ((signed)val))) && !(cmp = strcmp(p->str, s))) { + return p->offs; + } + pp = (cmp < 0 ? &(p->left) : &(p->right)); + } + + t = has_token(s); + + p = *pp = (Eccstr) zhalloc(sizeof(*p)); + p->left = p->right = 0; + p->offs = ((ecsoffs - ecssub) << 2) | (t ? 1 : 0); + p->aoffs = ecsoffs; + p->str = s; + p->nfunc = ecnfunc; + p->hashval = val; + ecsoffs += l; + + return p->offs; + } +} + +#define ecstr(S) ecadd(ecstrcode(S)) + +#define par_save_list(C) \ + do { \ + int eu = ecused; \ + par_list(C); \ + if (eu == ecused) ecadd(WCB_END()); \ + } while (0) +#define par_save_list1(C) \ + do { \ + int eu = ecused; \ + par_list1(C); \ + if (eu == ecused) ecadd(WCB_END()); \ + } while (0) + + +/**/ +mod_export void +init_parse_status(void) +{ + /* + * These variables are currently declared by the parser, so we + * initialise them here. Possibly they are more naturally declared + * by the lexical anaylser; however, as they are used for signalling + * 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 = intypeset = 0; + inrepeat_ = 0; + incmdpos = 1; +} + +/* Initialise wordcode buffer. */ + +/**/ +void +init_parse(void) +{ + queue_signals(); + + if (ecbuf) zfree(ecbuf, eclen); + + ecbuf = (Wordcode) zalloc((eclen = EC_INIT_SIZE) * sizeof(wordcode)); + ecused = 0; + ecstrs = NULL; + ecsoffs = ecnpats = 0; + ecssub = 0; + ecnfunc = 0; + + init_parse_status(); + + unqueue_signals(); +} + +/* Build eprog. */ + +/* careful: copy_ecstr is from arg1 to arg2, unlike memcpy */ + +static void +copy_ecstr(Eccstr s, char *p) +{ + while (s) { + memcpy(p + s->aoffs, s->str, strlen(s->str) + 1); + copy_ecstr(s->left, p); + s = s->right; + } +} + +static Eprog +bld_eprog(int heap) +{ + Eprog ret; + int l; + + queue_signals(); + + ecadd(WCB_END()); + + ret = heap ? (Eprog) zhalloc(sizeof(*ret)) : (Eprog) zalloc(sizeof(*ret)); + ret->len = ((ecnpats * sizeof(Patprog)) + + (ecused * sizeof(wordcode)) + + ecsoffs); + ret->npats = ecnpats; + ret->nref = heap ? -1 : 1; + ret->pats = heap ? (Patprog *) zhalloc(ret->len) : + (Patprog *) zshcalloc(ret->len); + ret->prog = (Wordcode) (ret->pats + ecnpats); + ret->strs = (char *) (ret->prog + ecused); + ret->shf = NULL; + ret->flags = heap ? EF_HEAP : EF_REAL; + ret->dump = NULL; + for (l = 0; l < ecnpats; l++) + ret->pats[l] = dummy_patprog1; + memcpy(ret->prog, ecbuf, ecused * sizeof(wordcode)); + copy_ecstr(ecstrs, ret->strs); + + zfree(ecbuf, eclen); + ecbuf = NULL; + + unqueue_signals(); + + return ret; +} + +/**/ +mod_export int +empty_eprog(Eprog p) +{ + return (!p || !p->prog || *p->prog == WCB_END()); +} + +static void +clear_hdocs(void) +{ + struct heredocs *p, *n; + + for (p = hdocs; p; p = n) { + n = p->next; + zfree(p, sizeof(struct heredocs)); + } + hdocs = NULL; +} + +/* + * event : ENDINPUT + * | SEPER + * | sublist [ SEPER | AMPER | AMPERBANG ] + * + * cmdsubst indicates our event is part of a command-style + * substitution terminated by the token indicationg, usual closing + * parenthesis. In other cases endtok is ENDINPUT. + */ + +/**/ +Eprog +parse_event(int endtok) +{ + tok = ENDINPUT; + incmdpos = 1; + aliasspaceflag = 0; + zshlex(); + init_parse(); + + if (!par_event(endtok)) { + clear_hdocs(); + return NULL; + } + if (endtok != ENDINPUT) { + /* don't need to build an eprog for this */ + return &dummy_eprog; + } + return bld_eprog(1); +} + +/**/ +int +par_event(int endtok) +{ + int r = 0, p, c = 0; + + while (tok == SEPER) { + if (isnewlin > 0 && endtok == ENDINPUT) + return 0; + zshlex(); + } + if (tok == ENDINPUT) + return 0; + if (tok == endtok) + return 1; + + p = ecadd(0); + + if (par_sublist(&c)) { + if (tok == ENDINPUT || tok == endtok) { + set_list_code(p, Z_SYNC, c); + r = 1; + } else if (tok == SEPER) { + set_list_code(p, Z_SYNC, c); + if (isnewlin <= 0 || endtok != ENDINPUT) + zshlex(); + r = 1; + } else if (tok == AMPER) { + set_list_code(p, Z_ASYNC, c); + zshlex(); + r = 1; + } else if (tok == AMPERBANG) { + set_list_code(p, (Z_ASYNC | Z_DISOWN), c); + zshlex(); + r = 1; + } + } + if (!r) { + tok = LEXERR; + if (errflag) { + yyerror(0); + ecused--; + return 0; + } + yyerror(1); + herrflush(); + if (noerrs != 2) + errflag |= ERRFLAG_ERROR; + ecused--; + return 0; + } else { + int oec = ecused; + + if (!par_event(endtok)) { + ecused = oec; + ecbuf[p] |= wc_bdata(Z_END); + return errflag ? 0 : 1; + } + } + return 1; +} + +/**/ +mod_export Eprog +parse_list(void) +{ + int c = 0; + + tok = ENDINPUT; + init_parse(); + zshlex(); + par_list(&c); + if (tok != ENDINPUT) { + clear_hdocs(); + tok = LEXERR; + yyerror(0); + return NULL; + } + return bld_eprog(1); +} + +/* + * This entry point is only used for bin_test, our attempt to + * provide compatibility with /bin/[ and /bin/test. Hence + * at this point condlex should always be set to testlex. + */ + +/**/ +mod_export Eprog +parse_cond(void) +{ + init_parse(); + + if (!par_cond()) { + clear_hdocs(); + return NULL; + } + return bld_eprog(1); +} + +/* This adds a list wordcode. The important bit about this is that it also + * tries to optimise this to a Z_SIMPLE list code. */ + +/**/ +static void +set_list_code(int p, int type, int cmplx) +{ + if (!cmplx && (type == Z_SYNC || type == (Z_SYNC | Z_END)) && + WC_SUBLIST_TYPE(ecbuf[p + 1]) == WC_SUBLIST_END) { + int ispipe = !(WC_SUBLIST_FLAGS(ecbuf[p + 1]) & WC_SUBLIST_SIMPLE); + ecbuf[p] = WCB_LIST((type | Z_SIMPLE), ecused - 2 - p); + ecdel(p + 1); + if (ispipe) + ecbuf[p + 1] = WC_PIPE_LINENO(ecbuf[p + 1]); + } else + ecbuf[p] = WCB_LIST(type, 0); +} + +/* The same for sublists. */ + +/**/ +static void +set_sublist_code(int p, int type, int flags, int skip, int cmplx) +{ + if (cmplx) + ecbuf[p] = WCB_SUBLIST(type, flags, skip); + else { + ecbuf[p] = WCB_SUBLIST(type, (flags | WC_SUBLIST_SIMPLE), skip); + ecbuf[p + 1] = WC_PIPE_LINENO(ecbuf[p + 1]); + } +} + +/* + * list : { SEPER } [ sublist [ { SEPER | AMPER | AMPERBANG } list ] ] + */ + +/**/ +static void +par_list(int *cmplx) +{ + int p, lp = -1, c; + + rec: + + while (tok == SEPER) + zshlex(); + + p = ecadd(0); + c = 0; + + if (par_sublist(&c)) { + *cmplx |= c; + if (tok == SEPER || tok == AMPER || tok == AMPERBANG) { + if (tok != SEPER) + *cmplx = 1; + set_list_code(p, ((tok == SEPER) ? Z_SYNC : + (tok == AMPER) ? Z_ASYNC : + (Z_ASYNC | Z_DISOWN)), c); + incmdpos = 1; + do { + zshlex(); + } while (tok == SEPER); + lp = p; + goto rec; + } else + set_list_code(p, (Z_SYNC | Z_END), c); + } else { + ecused--; + if (lp >= 0) + ecbuf[lp] |= wc_bdata(Z_END); + } +} + +/**/ +static void +par_list1(int *cmplx) +{ + int p = ecadd(0), c = 0; + + if (par_sublist(&c)) { + set_list_code(p, (Z_SYNC | Z_END), c); + *cmplx |= c; + } else + ecused--; +} + +/* + * sublist : sublist2 [ ( DBAR | DAMPER ) { SEPER } sublist ] + */ + +/**/ +static int +par_sublist(int *cmplx) +{ + int f, p, c = 0; + + p = ecadd(0); + + if ((f = par_sublist2(&c)) != -1) { + int e = ecused; + + *cmplx |= c; + if (tok == DBAR || tok == DAMPER) { + enum lextok qtok = tok; + int sl; + + cmdpush(tok == DBAR ? CS_CMDOR : CS_CMDAND); + zshlex(); + while (tok == SEPER) + zshlex(); + sl = par_sublist(cmplx); + set_sublist_code(p, (sl ? (qtok == DBAR ? + WC_SUBLIST_OR : WC_SUBLIST_AND) : + WC_SUBLIST_END), + f, (e - 1 - p), c); + cmdpop(); + } else { + if (tok == AMPER || tok == AMPERBANG) { + c = 1; + *cmplx |= c; + } + set_sublist_code(p, WC_SUBLIST_END, f, (e - 1 - p), c); + } + return 1; + } else { + ecused--; + return 0; + } +} + +/* + * sublist2 : [ COPROC | BANG ] pline + */ + +/**/ +static int +par_sublist2(int *cmplx) +{ + int f = 0; + + if (tok == COPROC) { + *cmplx = 1; + f |= WC_SUBLIST_COPROC; + zshlex(); + } else if (tok == BANG) { + *cmplx = 1; + f |= WC_SUBLIST_NOT; + zshlex(); + } + if (!par_pline(cmplx) && !f) + return -1; + + return f; +} + +/* + * pline : cmd [ ( BAR | BARAMP ) { SEPER } pline ] + */ + +/**/ +static int +par_pline(int *cmplx) +{ + int p; + zlong line = toklineno; + + p = ecadd(0); + + if (!par_cmd(cmplx, 0)) { + ecused--; + return 0; + } + if (tok == BAR) { + *cmplx = 1; + cmdpush(CS_PIPE); + zshlex(); + while (tok == SEPER) + zshlex(); + ecbuf[p] = WCB_PIPE(WC_PIPE_MID, (line >= 0 ? line + 1 : 0)); + ecispace(p + 1, 1); + ecbuf[p + 1] = ecused - 1 - p; + if (!par_pline(cmplx)) { + tok = LEXERR; + } + cmdpop(); + return 1; + } else if (tok == BARAMP) { + int r; + + for (r = p + 1; wc_code(ecbuf[r]) == WC_REDIR; + r += WC_REDIR_WORDS(ecbuf[r])); + + ecispace(r, 3); + ecbuf[r] = WCB_REDIR(REDIR_MERGEOUT); + ecbuf[r + 1] = 2; + ecbuf[r + 2] = ecstrcode("1"); + + *cmplx = 1; + cmdpush(CS_ERRPIPE); + zshlex(); + while (tok == SEPER) + zshlex(); + ecbuf[p] = WCB_PIPE(WC_PIPE_MID, (line >= 0 ? line + 1 : 0)); + ecispace(p + 1, 1); + ecbuf[p + 1] = ecused - 1 - p; + if (!par_pline(cmplx)) { + tok = LEXERR; + } + cmdpop(); + return 1; + } else { + ecbuf[p] = WCB_PIPE(WC_PIPE_END, (line >= 0 ? line + 1 : 0)); + return 1; + } +} + +/* + * cmd : { redir } ( for | case | if | while | repeat | + * subsh | funcdef | time | dinbrack | dinpar | simple ) { redir } + * + * zsh_construct is passed through to par_subsh(), q.v. + */ + +/**/ +static int +par_cmd(int *cmplx, int zsh_construct) +{ + int r, nr = 0; + + r = ecused; + + if (IS_REDIROP(tok)) { + *cmplx = 1; + while (IS_REDIROP(tok)) { + nr += par_redir(&r, NULL); + } + } + switch (tok) { + case FOR: + cmdpush(CS_FOR); + par_for(cmplx); + cmdpop(); + break; + case FOREACH: + cmdpush(CS_FOREACH); + par_for(cmplx); + cmdpop(); + break; + case SELECT: + *cmplx = 1; + cmdpush(CS_SELECT); + par_for(cmplx); + cmdpop(); + break; + case CASE: + cmdpush(CS_CASE); + par_case(cmplx); + cmdpop(); + break; + case IF: + par_if(cmplx); + break; + case WHILE: + cmdpush(CS_WHILE); + par_while(cmplx); + cmdpop(); + break; + case UNTIL: + cmdpush(CS_UNTIL); + par_while(cmplx); + cmdpop(); + break; + case REPEAT: + cmdpush(CS_REPEAT); + par_repeat(cmplx); + cmdpop(); + break; + case INPAR: + *cmplx = 1; + cmdpush(CS_SUBSH); + par_subsh(cmplx, zsh_construct); + cmdpop(); + break; + case INBRACE: + cmdpush(CS_CURSH); + par_subsh(cmplx, zsh_construct); + cmdpop(); + break; + case FUNC: + cmdpush(CS_FUNCDEF); + par_funcdef(cmplx); + cmdpop(); + break; + case DINBRACK: + cmdpush(CS_COND); + par_dinbrack(); + cmdpop(); + break; + case DINPAR: + ecadd(WCB_ARITH()); + ecstr(tokstr); + zshlex(); + break; + case TIME: + { + static int inpartime = 0; + + if (!inpartime) { + *cmplx = 1; + inpartime = 1; + par_time(); + inpartime = 0; + break; + } + } + tok = STRING; + /* fall through */ + default: + { + int sr; + + if (!(sr = par_simple(cmplx, nr))) { + if (!nr) + return 0; + } else { + /* Take account of redirections */ + if (sr > 1) { + *cmplx = 1; + r += sr - 1; + } + } + } + break; + } + if (IS_REDIROP(tok)) { + *cmplx = 1; + while (IS_REDIROP(tok)) + (void)par_redir(&r, NULL); + } + incmdpos = 1; + incasepat = 0; + incond = 0; + intypeset = 0; + return 1; +} + +/* + * for : ( FOR DINPAR expr SEMI expr SEMI expr DOUTPAR | + * ( FOR[EACH] | SELECT ) name ( "in" wordlist | INPAR wordlist OUTPAR ) ) + * { SEPER } ( DO list DONE | INBRACE list OUTBRACE | list ZEND | list1 ) + */ + +/**/ +static void +par_for(int *cmplx) +{ + int oecused = ecused, csh = (tok == FOREACH), p, sel = (tok == SELECT); + int type; + + p = ecadd(0); + + incmdpos = 0; + infor = tok == FOR ? 2 : 0; + zshlex(); + if (tok == DINPAR) { + zshlex(); + if (tok != DINPAR) + YYERRORV(oecused); + ecstr(tokstr); + zshlex(); + if (tok != DINPAR) + YYERRORV(oecused); + ecstr(tokstr); + zshlex(); + if (tok != DOUTPAR) + YYERRORV(oecused); + ecstr(tokstr); + infor = 0; + incmdpos = 1; + zshlex(); + type = WC_FOR_COND; + } else { + int np = 0, n, posix_in, ona = noaliases, onc = nocorrect; + infor = 0; + if (tok != STRING || !isident(tokstr)) + YYERRORV(oecused); + if (!sel) + np = ecadd(0); + n = 0; + incmdpos = 1; + noaliases = nocorrect = 1; + for (;;) { + n++; + ecstr(tokstr); + zshlex(); + if (tok != STRING || !strcmp(tokstr, "in") || sel) + break; + if (!isident(tokstr) || errflag) + { + noaliases = ona; + nocorrect = onc; + YYERRORV(oecused); + } + } + noaliases = ona; + nocorrect = onc; + if (!sel) + ecbuf[np] = n; + posix_in = isnewlin; + while (isnewlin) + zshlex(); + if (tok == STRING && !strcmp(tokstr, "in")) { + incmdpos = 0; + zshlex(); + np = ecadd(0); + n = par_wordlist(); + if (tok != SEPER) + YYERRORV(oecused); + ecbuf[np] = n; + type = (sel ? WC_SELECT_LIST : WC_FOR_LIST); + } else if (!posix_in && tok == INPAR) { + incmdpos = 0; + zshlex(); + np = ecadd(0); + n = par_nl_wordlist(); + if (tok != OUTPAR) + YYERRORV(oecused); + ecbuf[np] = n; + incmdpos = 1; + zshlex(); + type = (sel ? WC_SELECT_LIST : WC_FOR_LIST); + } else + type = (sel ? WC_SELECT_PPARAM : WC_FOR_PPARAM); + } + incmdpos = 1; + while (tok == SEPER) + zshlex(); + if (tok == DOLOOP) { + zshlex(); + par_save_list(cmplx); + if (tok != DONE) + YYERRORV(oecused); + incmdpos = 0; + zshlex(); + } else if (tok == INBRACE) { + zshlex(); + par_save_list(cmplx); + if (tok != OUTBRACE) + YYERRORV(oecused); + incmdpos = 0; + zshlex(); + } else if (csh || isset(CSHJUNKIELOOPS)) { + par_save_list(cmplx); + if (tok != ZEND) + YYERRORV(oecused); + incmdpos = 0; + zshlex(); + } else if (unset(SHORTLOOPS)) { + YYERRORV(oecused); + } else + par_save_list1(cmplx); + + ecbuf[p] = (sel ? + WCB_SELECT(type, ecused - 1 - p) : + WCB_FOR(type, ecused - 1 - p)); +} + +/* + * case : CASE STRING { SEPER } ( "in" | INBRACE ) + { { SEPER } STRING { BAR STRING } OUTPAR + list [ DSEMI | SEMIAMP | SEMIBAR ] } + { SEPER } ( "esac" | OUTBRACE ) + */ + +/**/ +static void +par_case(int *cmplx) +{ + int oecused = ecused, brflag, p, pp, palts, type, nalts; + int ona, onc; + + p = ecadd(0); + + incmdpos = 0; + zshlex(); + if (tok != STRING) + YYERRORV(oecused); + ecstr(tokstr); + + incmdpos = 1; + ona = noaliases; + onc = nocorrect; + noaliases = nocorrect = 1; + zshlex(); + while (tok == SEPER) + zshlex(); + if (!(tok == STRING && !strcmp(tokstr, "in")) && tok != INBRACE) + { + noaliases = ona; + nocorrect = onc; + YYERRORV(oecused); + } + brflag = (tok == INBRACE); + incasepat = 1; + incmdpos = 0; + noaliases = ona; + nocorrect = onc; + zshlex(); + + for (;;) { + char *str; + int skip_zshlex; + + while (tok == SEPER) + zshlex(); + if (tok == OUTBRACE) + break; + if (tok == INPAR) + zshlex(); + if (tok == BAR) { + str = dupstring(""); + skip_zshlex = 1; + } else { + if (tok != STRING) + YYERRORV(oecused); + if (!strcmp(tokstr, "esac")) + break; + str = dupstring(tokstr); + skip_zshlex = 0; + } + type = WC_CASE_OR; + pp = ecadd(0); + palts = ecadd(0); + nalts = 0; + /* + * Hack here. + * + * [Pause for astonished hubbub to subside.] + * + * The next token we get may be + * - ")" or "|" if we're looking at an honest-to-god + * "case" pattern, either because there's no opening + * parenthesis, or because SH_GLOB is set and we + * managed to grab an initial "(" to mark the start + * of the case pattern. + * - Something else --- we don't care what --- because + * we're parsing a complete "(...)" as a complete + * zsh pattern. In that case, we treat this as a + * single instance of a case pattern but we pretend + * we're doing proper case parsing --- in which the + * parentheses and bar are in different words from + * the string, so may be separated by whitespace. + * So we quietly massage the whitespace and hope + * no one noticed. This is horrible, but it's + * unfortunately too difficult to combine traditional + * zsh patterns with a properly parsed case pattern + * without generating incompatibilities which aren't + * all that popular (I've discovered). + * - We can also end up with something other than ")" or "|" + * just because we're looking at garbage. + * + * Because of the second case, what happens next might + * be the start of the command after the pattern, so we + * need to treat it as in command position. Luckily + * this doesn't affect our ability to match a | or ) as + * these are valid on command lines. + */ + incasepat = -1; + incmdpos = 1; + if (!skip_zshlex) + zshlex(); + for (;;) { + if (tok == OUTPAR) { + ecstr(str); + ecadd(ecnpats++); + nalts++; + + incasepat = 0; + incmdpos = 1; + zshlex(); + break; + } else if (tok == BAR) { + ecstr(str); + ecadd(ecnpats++); + nalts++; + + incasepat = 1; + incmdpos = 0; + } else { + if (!nalts && str[0] == Inpar) { + int pct = 0, sl; + char *s; + + for (s = str; *s; s++) { + if (*s == Inpar) + pct++; + if (!pct) + break; + if (pct == 1) { + if (*s == Bar || *s == Inpar) + while (iblank(s[1])) + chuck(s+1); + if (*s == Bar || *s == Outpar) + while (iblank(s[-1]) && + (s < str + 1 || s[-2] != Meta)) + chuck(--s); + } + if (*s == Outpar) + pct--; + } + if (*s || pct || s == str) + YYERRORV(oecused); + /* Simplify pattern by removing surrounding (...) */ + sl = strlen(str); + DPUTS(*str != Inpar || str[sl - 1] != Outpar, + "BUG: strange case pattern"); + str[sl - 1] = '\0'; + chuck(str); + ecstr(str); + ecadd(ecnpats++); + nalts++; + break; + } + YYERRORV(oecused); + } + + zshlex(); + switch (tok) { + case STRING: + /* Normal case */ + str = dupstring(tokstr); + zshlex(); + break; + + case OUTPAR: + case BAR: + /* Empty string */ + str = dupstring(""); + break; + + default: + /* Oops. */ + YYERRORV(oecused); + break; + } + } + incasepat = 0; + par_save_list(cmplx); + if (tok == SEMIAMP) + type = WC_CASE_AND; + else if (tok == SEMIBAR) + type = WC_CASE_TESTAND; + ecbuf[pp] = WCB_CASE(type, ecused - 1 - pp); + ecbuf[palts] = nalts; + if ((tok == ESAC && !brflag) || (tok == OUTBRACE && brflag)) + break; + if (tok != DSEMI && tok != SEMIAMP && tok != SEMIBAR) + YYERRORV(oecused); + incasepat = 1; + incmdpos = 0; + zshlex(); + } + incmdpos = 1; + incasepat = 0; + zshlex(); + + ecbuf[p] = WCB_CASE(WC_CASE_HEAD, ecused - 1 - p); +} + +/* + * if : { ( IF | ELIF ) { SEPER } ( INPAR list OUTPAR | list ) + { SEPER } ( THEN list | INBRACE list OUTBRACE | list1 ) } + [ FI | ELSE list FI | ELSE { SEPER } INBRACE list OUTBRACE ] + (you get the idea...?) + */ + +/**/ +static void +par_if(int *cmplx) +{ + int oecused = ecused, p, pp, type, usebrace = 0; + enum lextok xtok; + unsigned char nc; + + p = ecadd(0); + + for (;;) { + xtok = tok; + cmdpush(xtok == IF ? CS_IF : CS_ELIF); + if (xtok == FI) { + incmdpos = 0; + zshlex(); + break; + } + zshlex(); + if (xtok == ELSE) + break; + while (tok == SEPER) + zshlex(); + if (!(xtok == IF || xtok == ELIF)) { + cmdpop(); + YYERRORV(oecused); + } + pp = ecadd(0); + type = (xtok == IF ? WC_IF_IF : WC_IF_ELIF); + par_save_list(cmplx); + incmdpos = 1; + if (tok == ENDINPUT) { + cmdpop(); + YYERRORV(oecused); + } + while (tok == SEPER) + zshlex(); + xtok = FI; + nc = cmdstack[cmdsp - 1] == CS_IF ? CS_IFTHEN : CS_ELIFTHEN; + if (tok == THEN) { + usebrace = 0; + cmdpop(); + cmdpush(nc); + zshlex(); + par_save_list(cmplx); + ecbuf[pp] = WCB_IF(type, ecused - 1 - pp); + incmdpos = 1; + cmdpop(); + } else if (tok == INBRACE) { + usebrace = 1; + cmdpop(); + cmdpush(nc); + zshlex(); + par_save_list(cmplx); + if (tok != OUTBRACE) { + cmdpop(); + YYERRORV(oecused); + } + ecbuf[pp] = WCB_IF(type, ecused - 1 - pp); + /* command word (else) allowed to follow immediately */ + zshlex(); + incmdpos = 1; + if (tok == SEPER) + break; + cmdpop(); + } else if (unset(SHORTLOOPS)) { + cmdpop(); + YYERRORV(oecused); + } else { + cmdpop(); + cmdpush(nc); + par_save_list1(cmplx); + ecbuf[pp] = WCB_IF(type, ecused - 1 - pp); + incmdpos = 1; + break; + } + } + cmdpop(); + if (xtok == ELSE || tok == ELSE) { + pp = ecadd(0); + cmdpush(CS_ELSE); + while (tok == SEPER) + zshlex(); + if (tok == INBRACE && usebrace) { + zshlex(); + par_save_list(cmplx); + if (tok != OUTBRACE) { + cmdpop(); + YYERRORV(oecused); + } + } else { + par_save_list(cmplx); + if (tok != FI) { + cmdpop(); + YYERRORV(oecused); + } + } + incmdpos = 0; + ecbuf[pp] = WCB_IF(WC_IF_ELSE, ecused - 1 - pp); + zshlex(); + cmdpop(); + } + ecbuf[p] = WCB_IF(WC_IF_HEAD, ecused - 1 - p); +} + +/* + * while : ( WHILE | UNTIL ) ( INPAR list OUTPAR | list ) { SEPER } + ( DO list DONE | INBRACE list OUTBRACE | list ZEND ) + */ + +/**/ +static void +par_while(int *cmplx) +{ + int oecused = ecused, p; + int type = (tok == UNTIL ? WC_WHILE_UNTIL : WC_WHILE_WHILE); + + p = ecadd(0); + zshlex(); + par_save_list(cmplx); + incmdpos = 1; + while (tok == SEPER) + zshlex(); + if (tok == DOLOOP) { + zshlex(); + par_save_list(cmplx); + if (tok != DONE) + YYERRORV(oecused); + incmdpos = 0; + zshlex(); + } else if (tok == INBRACE) { + zshlex(); + par_save_list(cmplx); + if (tok != OUTBRACE) + YYERRORV(oecused); + incmdpos = 0; + zshlex(); + } else if (isset(CSHJUNKIELOOPS)) { + par_save_list(cmplx); + if (tok != ZEND) + YYERRORV(oecused); + zshlex(); + } else if (unset(SHORTLOOPS)) { + YYERRORV(oecused); + } else + par_save_list1(cmplx); + + ecbuf[p] = WCB_WHILE(type, ecused - 1 - p); +} + +/* + * repeat : REPEAT STRING { SEPER } ( DO list DONE | list1 ) + */ + +/**/ +static void +par_repeat(int *cmplx) +{ + /* ### what to do about inrepeat_ here? */ + int oecused = ecused, p; + + p = ecadd(0); + + incmdpos = 0; + zshlex(); + if (tok != STRING) + YYERRORV(oecused); + ecstr(tokstr); + incmdpos = 1; + zshlex(); + while (tok == SEPER) + zshlex(); + if (tok == DOLOOP) { + zshlex(); + par_save_list(cmplx); + if (tok != DONE) + YYERRORV(oecused); + incmdpos = 0; + zshlex(); + } else if (tok == INBRACE) { + zshlex(); + par_save_list(cmplx); + if (tok != OUTBRACE) + YYERRORV(oecused); + incmdpos = 0; + zshlex(); + } else if (isset(CSHJUNKIELOOPS)) { + par_save_list(cmplx); + if (tok != ZEND) + YYERRORV(oecused); + zshlex(); + } else if (unset(SHORTLOOPS)) { + YYERRORV(oecused); + } else + par_save_list1(cmplx); + + ecbuf[p] = WCB_REPEAT(ecused - 1 - p); +} + +/* + * subsh : INPAR list OUTPAR | + * INBRACE list OUTBRACE [ "always" INBRACE list OUTBRACE ] + * + * With zsh_construct non-zero, we're doing a zsh special in which + * the following token is not considered in command position. This + * is used for arguments of anonymous functions. + */ + +/**/ +static void +par_subsh(int *cmplx, int zsh_construct) +{ + enum lextok otok = tok; + int oecused = ecused, p, pp; + + p = ecadd(0); + /* Extra word only needed for always block */ + pp = ecadd(0); + zshlex(); + par_list(cmplx); + ecadd(WCB_END()); + if (tok != ((otok == INPAR) ? OUTPAR : OUTBRACE)) + YYERRORV(oecused); + incmdpos = !zsh_construct; + zshlex(); + + /* Optional always block. No intervening SEPERs allowed. */ + if (otok == INBRACE && tok == STRING && !strcmp(tokstr, "always")) { + ecbuf[pp] = WCB_TRY(ecused - 1 - pp); + incmdpos = 1; + do { + zshlex(); + } while (tok == SEPER); + + if (tok != INBRACE) + YYERRORV(oecused); + cmdpop(); + cmdpush(CS_ALWAYS); + + zshlex(); + par_save_list(cmplx); + while (tok == SEPER) + zshlex(); + + incmdpos = 1; + + if (tok != OUTBRACE) + YYERRORV(oecused); + zshlex(); + ecbuf[p] = WCB_TRY(ecused - 1 - p); + } else { + ecbuf[p] = (otok == INPAR ? WCB_SUBSH(ecused - 1 - p) : + WCB_CURSH(ecused - 1 - p)); + } +} + +/* + * funcdef : FUNCTION wordlist [ INOUTPAR ] { SEPER } + * ( list1 | INBRACE list OUTBRACE ) + */ + +/**/ +static void +par_funcdef(int *cmplx) +{ + int oecused = ecused, num = 0, onp, p, c = 0; + int so, oecssub = ecssub; + zlong oldlineno = lineno; + + lineno = 0; + nocorrect = 1; + incmdpos = 0; + zshlex(); + + p = ecadd(0); + ecadd(0); + + while (tok == STRING) { + if ((*tokstr == Inbrace || *tokstr == '{') && + !tokstr[1]) { + tok = INBRACE; + break; + } + ecstr(tokstr); + num++; + zshlex(); + } + ecadd(0); + ecadd(0); + ecadd(0); + + nocorrect = 0; + incmdpos = 1; + if (tok == INOUTPAR) + zshlex(); + while (tok == SEPER) + zshlex(); + + ecnfunc++; + ecssub = so = ecsoffs; + onp = ecnpats; + ecnpats = 0; + + if (tok == INBRACE) { + zshlex(); + par_list(&c); + if (tok != OUTBRACE) { + lineno += oldlineno; + ecnpats = onp; + ecssub = oecssub; + YYERRORV(oecused); + } + if (num == 0) { + /* Anonymous function, possibly with arguments */ + incmdpos = 0; + } + zshlex(); + } else if (unset(SHORTLOOPS)) { + lineno += oldlineno; + ecnpats = onp; + ecssub = oecssub; + YYERRORV(oecused); + } else + par_list1(&c); + + ecadd(WCB_END()); + ecbuf[p + num + 2] = so - oecssub; + ecbuf[p + num + 3] = ecsoffs - so; + ecbuf[p + num + 4] = ecnpats; + ecbuf[p + 1] = num; + + ecnpats = onp; + ecssub = oecssub; + ecnfunc++; + + ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); + + if (num == 0) { + /* Unnamed function */ + int parg = ecadd(0); + ecadd(0); + while (tok == STRING) { + ecstr(tokstr); + num++; + zshlex(); + } + if (num > 0) + *cmplx = 1; + ecbuf[parg] = ecused - parg; /*?*/ + ecbuf[parg+1] = num; + } + lineno += oldlineno; +} + +/* + * time : TIME sublist2 + */ + +/**/ +static void +par_time(void) +{ + int p, f, c = 0; + + zshlex(); + + p = ecadd(0); + ecadd(0); + if ((f = par_sublist2(&c)) < 0) { + ecused--; + ecbuf[p] = WCB_TIMED(WC_TIMED_EMPTY); + } else { + ecbuf[p] = WCB_TIMED(WC_TIMED_PIPE); + set_sublist_code(p + 1, WC_SUBLIST_END, f, ecused - 2 - p, c); + } +} + +/* + * dinbrack : DINBRACK cond DOUTBRACK + */ + +/**/ +static void +par_dinbrack(void) +{ + int oecused = ecused; + + incond = 1; + incmdpos = 0; + zshlex(); + par_cond(); + if (tok != DOUTBRACK) + YYERRORV(oecused); + incond = 0; + incmdpos = 1; + zshlex(); +} + +/* + * simple : { COMMAND | EXEC | NOGLOB | NOCORRECT | DASH } + { STRING | ENVSTRING | ENVARRAY wordlist OUTPAR | redir } + [ INOUTPAR { SEPER } ( list1 | INBRACE list OUTBRACE ) ] + * + * Returns 0 if no code, else 1 plus the number of code words + * used up by redirections. + */ + +/**/ +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, ppost = 0, is_typeset = 0; + char *hasalias = input_hasalias(); + wordcode postassigns = 0; + + r = ecused; + for (;;) { + if (tok == NOCORRECT) { + *cmplx = c = 1; + nocorrect = 1; + } else if (tok == ENVSTRING) { + char *ptr, *name, *str; + + name = tokstr; + 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 (*ptr == '=') { + *ptr = '\0'; + str = ptr + 1; + } else + equalsplit(tokstr, &str); + 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 (ptr[1] == Inpar && + (*ptr == Equals || *ptr == Inang || *ptr == OutangProc)) { + *cmplx = 1; + break; + } + } + ecstr(name); + ecstr(str); + isnull = 0; + assignments = 1; + } else if (tok == ENVARRAY) { + int oldcmdpos = incmdpos, n, type2; + + /* + * We consider array setting cmplx because it can + * contain process substitutions, which need a valid job. + */ + *cmplx = c = 1; + p = ecadd(0); + incmdpos = 0; + if ((type2 = strlen(tokstr) - 1) && tokstr[type2] == '+') { + tokstr[type2] = '\0'; + type2 = WC_ASSIGN_INC; + } else + type2 = WC_ASSIGN_NEW; + ecstr(tokstr); + cmdpush(CS_ARRAY); + zshlex(); + n = par_nl_wordlist(); + ecbuf[p] = WCB_ASSIGN(WC_ASSIGN_ARRAY, type2, n); + cmdpop(); + if (tok != OUTPAR) + YYERROR(oecused); + incmdpos = oldcmdpos; + isnull = 0; + assignments = 1; + } else if (IS_REDIROP(tok)) { + *cmplx = c = 1; + nr += par_redir(&r, NULL); + continue; + } else + break; + zshlex(); + if (!hasalias) + hasalias = input_hasalias(); + } + if (tok == AMPER || tok == AMPERBANG) + YYERROR(oecused); + + p = ecadd(WCB_SIMPLE(0)); + + for (;;) { + 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; + + if (*ptr == Outbrace && ptr > tokstr + 1) + { + if (itype_end(tokstr+1, IIDENT, 0) >= ptr) + { + char *toksave = tokstr; + char *idstring = dupstrpfx(tokstr+1, eptr-tokstr-1); + redir_var = 1; + zshlex(); + if (!hasalias) + hasalias = input_hasalias(); + + if (IS_REDIROP(tok) && tokfd == -1) + { + *cmplx = c = 1; + nrediradd = par_redir(&r, idstring); + p += nrediradd; + sr += nrediradd; + } + else + { + ecstr(toksave); + argc++; + } + } + } + } + + if (!redir_var) + { + 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(); + if (!hasalias) + hasalias = input_hasalias(); + } + } 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(); + if (!hasalias) + hasalias = input_hasalias(); + } 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; + + /* Error if too many function definitions at once */ + if (!isset(MULTIFUNCDEF) && argc > 1) + YYERROR(oecused); + /* Error if preceding assignments */ + if (assignments || postassigns) + YYERROR(oecused); + if (hasalias && !isset(ALIASFUNCDEF) && argc && + hasalias != input_hasalias()) { + zwarn("defining function based on alias `%s'", hasalias); + YYERROR(oecused); + } + + *cmplx = c; + lineno = 0; + incmdpos = 1; + cmdpush(CS_FUNCDEF); + zshlex(); + while (tok == SEPER) + zshlex(); + + ecispace(p + 1, 1); + ecbuf[p + 1] = argc; + ecadd(0); + ecadd(0); + ecadd(0); + + ecnfunc++; + ecssub = so = ecsoffs; + onp = ecnpats; + ecnpats = 0; + + if (tok == INBRACE) { + int c = 0; + + zshlex(); + par_list(&c); + if (tok != OUTBRACE) { + cmdpop(); + lineno += oldlineno; + ecnpats = onp; + ecssub = oecssub; + YYERROR(oecused); + } + if (argc == 0) { + /* Anonymous function, possibly with arguments */ + incmdpos = 0; + } + zshlex(); + } else { + int ll, sl, c = 0; + + ll = ecadd(0); + sl = ecadd(0); + (void)ecadd(WCB_PIPE(WC_PIPE_END, 0)); + + if (!par_cmd(&c, argc == 0)) { + cmdpop(); + YYERROR(oecused); + } + if (argc == 0) { + /* + * Anonymous function, possibly with arguments. + * N.B. for cmplx structures in particular + * ( ... ) we rely on lower level code doing this + * to get the immediately following word (the + * first token after the ")" has already been + * read). + */ + incmdpos = 0; + } + + set_sublist_code(sl, WC_SUBLIST_END, 0, ecused - 1 - sl, c); + set_list_code(ll, (Z_SYNC | Z_END), c); + } + cmdpop(); + + ecadd(WCB_END()); + ecbuf[p + argc + 2] = so - oecssub; + ecbuf[p + argc + 3] = ecsoffs - so; + ecbuf[p + argc + 4] = ecnpats; + + ecnpats = onp; + ecssub = oecssub; + ecnfunc++; + + ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); + + if (argc == 0) { + /* Unnamed function */ + int parg = ecadd(0); + ecadd(0); + while (tok == STRING || IS_REDIROP(tok)) { + if (tok == STRING) + { + ecstr(tokstr); + argc++; + zshlex(); + } else { + *cmplx = c = 1; + nrediradd = par_redir(&r, NULL); + p += nrediradd; + if (ppost) + ppost += nrediradd; + sr += nrediradd; + parg += nrediradd; + } + } + if (argc > 0) + *cmplx = 1; + ecbuf[parg] = ecused - parg; /*?*/ + ecbuf[parg+1] = argc; + } + lineno += oldlineno; + + isfunc = 1; + isnull = 0; + break; + } else + break; + isnull = 0; + } + if (isnull && !(sr + nr)) { + ecused = p; + return 0; + } + incmdpos = 1; + intypeset = 0; + + 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; +} + +/* + * redir : ( OUTANG | ... | TRINANG ) STRING + * + * Return number of code words required for redirection + */ + +static int redirtab[TRINANG - OUTANG + 1] = { + REDIR_WRITE, + REDIR_WRITENOW, + REDIR_APP, + REDIR_APPNOW, + REDIR_READ, + REDIR_READWRITE, + REDIR_HEREDOC, + REDIR_HEREDOCDASH, + REDIR_MERGEIN, + REDIR_MERGEOUT, + REDIR_ERRWRITE, + REDIR_ERRWRITENOW, + REDIR_ERRAPP, + REDIR_ERRAPPNOW, + REDIR_HERESTR, +}; + +/**/ +static int +par_redir(int *rp, char *idstring) +{ + int r = *rp, type, fd1, oldcmdpos, oldnc, ncodes; + char *name; + + oldcmdpos = incmdpos; + incmdpos = 0; + oldnc = nocorrect; + if (tok != INANG && tok != INOUTANG) + nocorrect = 1; + type = redirtab[tok - OUTANG]; + fd1 = tokfd; + zshlex(); + if (tok != STRING && tok != ENVSTRING) + YYERROR(ecused); + incmdpos = oldcmdpos; + nocorrect = oldnc; + + /* assign default fd */ + if (fd1 == -1) + fd1 = IS_READFD(type) ? 0 : 1; + + name = tokstr; + + switch (type) { + case REDIR_HEREDOC: + case REDIR_HEREDOCDASH: { + /* <<[-] name */ + struct heredocs **hd; + int htype = type; + + /* + * Add two here for the string to remember the HERE + * terminator in raw and munged form. + */ + if (idstring) + { + type |= REDIR_VARID_MASK; + ncodes = 6; + } + else + ncodes = 5; + + /* If we ever to change the number of codes, we have to change + * the definition of WC_REDIR_WORDS. */ + ecispace(r, ncodes); + *rp = r + ncodes; + ecbuf[r] = WCB_REDIR(type | REDIR_FROM_HEREDOC_MASK); + ecbuf[r + 1] = fd1; + + /* + * r + 2: the HERE string we recover + * r + 3: the HERE document terminator, raw + * r + 4: the HERE document terminator, munged + */ + if (idstring) + ecbuf[r + 5] = ecstrcode(idstring); + + for (hd = &hdocs; *hd; hd = &(*hd)->next) + ; + *hd = zalloc(sizeof(struct heredocs)); + (*hd)->next = NULL; + (*hd)->type = htype; + (*hd)->pc = r; + (*hd)->str = tokstr; + + zshlex(); + return ncodes; + } + case REDIR_WRITE: + case REDIR_WRITENOW: + if (tokstr[0] == OutangProc && tokstr[1] == Inpar) + /* > >(...) */ + type = REDIR_OUTPIPE; + else if (tokstr[0] == Inang && tokstr[1] == Inpar) + YYERROR(ecused); + break; + case REDIR_READ: + if (tokstr[0] == Inang && tokstr[1] == Inpar) + /* < <(...) */ + type = REDIR_INPIPE; + else if (tokstr[0] == OutangProc && tokstr[1] == Inpar) + YYERROR(ecused); + break; + case REDIR_READWRITE: + if ((tokstr[0] == Inang || tokstr[0] == OutangProc) && + tokstr[1] == Inpar) + type = tokstr[0] == Inang ? REDIR_INPIPE : REDIR_OUTPIPE; + break; + } + zshlex(); + + /* If we ever to change the number of codes, we have to change + * the definition of WC_REDIR_WORDS. */ + if (idstring) + { + type |= REDIR_VARID_MASK; + ncodes = 4; + } + else + ncodes = 3; + + ecispace(r, ncodes); + *rp = r + ncodes; + ecbuf[r] = WCB_REDIR(type); + ecbuf[r + 1] = fd1; + ecbuf[r + 2] = ecstrcode(name); + if (idstring) + ecbuf[r + 3] = ecstrcode(idstring); + + return ncodes; +} + +/**/ +void +setheredoc(int pc, int type, char *str, char *termstr, char *munged_termstr) +{ + ecbuf[pc] = WCB_REDIR(type | REDIR_FROM_HEREDOC_MASK); + ecbuf[pc + 2] = ecstrcode(str); + ecbuf[pc + 3] = ecstrcode(termstr); + ecbuf[pc + 4] = ecstrcode(munged_termstr); +} + +/* + * wordlist : { STRING } + */ + +/**/ +static int +par_wordlist(void) +{ + int num = 0; + while (tok == STRING) { + ecstr(tokstr); + num++; + zshlex(); + } + return num; +} + +/* + * nl_wordlist : { STRING | SEPER } + */ + +/**/ +static int +par_nl_wordlist(void) +{ + int num = 0; + + while (tok == STRING || tok == SEPER) { + if (tok != SEPER) { + ecstr(tokstr); + num++; + } + zshlex(); + } + return num; +} + +/* + * condlex is zshlex for normal parsing, but is altered to allow + * the test builtin to use par_cond. + */ + +/**/ +void (*condlex) _((void)) = zshlex; + +/* + * cond : cond_1 { SEPER } [ DBAR { SEPER } cond ] + */ + +#define COND_SEP() (tok == SEPER && condlex != testlex && *zshlextext != ';') + +/**/ +static int +par_cond(void) +{ + int p = ecused, r; + + r = par_cond_1(); + while (COND_SEP()) + condlex(); + if (tok == DBAR) { + condlex(); + while (COND_SEP()) + condlex(); + ecispace(p, 1); + par_cond(); + ecbuf[p] = WCB_COND(COND_OR, ecused - 1 - p); + return 1; + } + return r; +} + +/* + * cond_1 : cond_2 { SEPER } [ DAMPER { SEPER } cond_1 ] + */ + +/**/ +static int +par_cond_1(void) +{ + int r, p = ecused; + + r = par_cond_2(); + while (COND_SEP()) + condlex(); + if (tok == DAMPER) { + condlex(); + while (COND_SEP()) + condlex(); + ecispace(p, 1); + par_cond_1(); + ecbuf[p] = WCB_COND(COND_AND, ecused - 1 - p); + return 1; + } + return r; +} + +/* + * Return 1 if condition matches. This also works for non-elided options. + * + * input is test string, may begin - or Dash. + * cond is condition following the -. + */ +static int check_cond(const char *input, const char *cond) +{ + if (!IS_DASH(input[0])) + return 0; + return !strcmp(input + 1, cond); +} + +/* + * cond_2 : BANG cond_2 + | INPAR { SEPER } cond_2 { SEPER } OUTPAR + | STRING STRING STRING + | STRING STRING + | STRING ( INANG | OUTANG ) STRING + */ + +/**/ +static int +par_cond_2(void) +{ + char *s1, *s2, *s3; + int dble = 0; + int n_testargs = (condlex == testlex) ? arrlen(testargs) + 1 : 0; + + if (n_testargs) { + /* See the description of test in POSIX 1003.2 */ + if (tok == NULLTOK) + /* no arguments: false */ + return par_cond_double(dupstring("-n"), dupstring("")); + if (n_testargs == 1) { + /* one argument: [ foo ] is equivalent to [ -n foo ] */ + s1 = tokstr; + condlex(); + /* ksh behavior: [ -t ] means [ -t 1 ]; bash disagrees */ + if (unset(POSIXBUILTINS) && check_cond(s1, "t")) + return par_cond_double(s1, dupstring("1")); + return par_cond_double(dupstring("-n"), s1); + } + if (n_testargs > 2) { + /* three arguments: if the second argument is a binary operator, * + * perform that binary test on the first and the third argument */ + if (!strcmp(*testargs, "=") || + !strcmp(*testargs, "==") || + !strcmp(*testargs, "!=") || + (IS_DASH(**testargs) && get_cond_num(*testargs + 1) >= 0)) { + s1 = tokstr; + condlex(); + s2 = tokstr; + condlex(); + s3 = tokstr; + condlex(); + return par_cond_triple(s1, s2, s3); + } + } + /* + * We fall through here on any non-numeric infix operator + * or any other time there are at least two arguments. + */ + } else + while (COND_SEP()) + condlex(); + if (tok == BANG) { + /* + * In "test" compatibility mode, "! -a ..." and "! -o ..." + * are treated as "[string] [and] ..." and "[string] [or] ...". + */ + if (!(n_testargs > 1 && (check_cond(*testargs, "a") || + check_cond(*testargs, "o")))) + { + condlex(); + ecadd(WCB_COND(COND_NOT, 0)); + return par_cond_2(); + } + } + if (tok == INPAR) { + int r; + + condlex(); + while (COND_SEP()) + condlex(); + r = par_cond(); + while (COND_SEP()) + condlex(); + if (tok != OUTPAR) + YYERROR(ecused); + condlex(); + return r; + } + s1 = tokstr; + dble = (s1 && IS_DASH(*s1) + && (!n_testargs + || strspn(s1+1, "abcdefghknoprstuvwxzLONGS") == 1) + && !s1[2]); + if (tok != STRING) { + /* Check first argument for [[ STRING ]] re-interpretation */ + if (s1 /* tok != DOUTBRACK && tok != DAMPER && tok != DBAR */ + && tok != LEXERR && (!dble || n_testargs)) { + do condlex(); while (COND_SEP()); + return par_cond_double(dupstring("-n"), s1); + } else + YYERROR(ecused); + } + condlex(); + if (n_testargs == 2 && tok != STRING && tokstr && IS_DASH(s1[0])) { + /* + * Something like "test -z" followed by a token. + * We'll turn the token into a string (we've also + * checked it does have a string representation). + */ + tok = STRING; + } else + while (COND_SEP()) + condlex(); + if (tok == INANG || tok == OUTANG) { + enum lextok xtok = tok; + do condlex(); while (COND_SEP()); + if (tok != STRING) + YYERROR(ecused); + s3 = tokstr; + do condlex(); while (COND_SEP()); + ecadd(WCB_COND((xtok == INANG ? COND_STRLT : COND_STRGTR), 0)); + ecstr(s1); + ecstr(s3); + return 1; + } + if (tok != STRING) { + /* + * Check second argument in case semantics e.g. [ = -a = ] + * mean we have to go back and fix up the first one + */ + if (tok != LEXERR) { + if (!dble || n_testargs) + return par_cond_double(dupstring("-n"), s1); + else + return par_cond_multi(s1, newlinklist()); + } else + YYERROR(ecused); + } + s2 = tokstr; + if (!n_testargs) + dble = (s2 && IS_DASH(*s2) && !s2[2]); + incond++; /* parentheses do globbing */ + do condlex(); while (COND_SEP()); + incond--; /* parentheses do grouping */ + if (tok == STRING && !dble) { + s3 = tokstr; + do condlex(); while (COND_SEP()); + if (tok == STRING) { + LinkList l = newlinklist(); + + addlinknode(l, s2); + addlinknode(l, s3); + + while (tok == STRING) { + addlinknode(l, tokstr); + do condlex(); while (COND_SEP()); + } + return par_cond_multi(s1, l); + } else + return par_cond_triple(s1, s2, s3); + } else + return par_cond_double(s1, s2); +} + +/**/ +static int +par_cond_double(char *a, char *b) +{ + if (!IS_DASH(a[0]) || !a[1]) + COND_ERROR("parse error: condition expected: %s", a); + else if (!a[2] && strspn(a+1, "abcdefgknoprstuvwxzhLONGS") == 1) { + ecadd(WCB_COND(a[1], 0)); + ecstr(b); + } else { + ecadd(WCB_COND(COND_MOD, 1)); + ecstr(a); + ecstr(b); + } + return 1; +} + +/**/ +static int +get_cond_num(char *tst) +{ + static char *condstrs[] = + { + "nt", "ot", "ef", "eq", "ne", "lt", "gt", "le", "ge", NULL + }; + int t0; + + for (t0 = 0; condstrs[t0]; t0++) + if (!strcmp(condstrs[t0], tst)) + return t0; + return -1; +} + +/**/ +static int +par_cond_triple(char *a, char *b, char *c) +{ + int t0; + + if ((b[0] == Equals || b[0] == '=') && !b[1]) { + ecadd(WCB_COND(COND_STREQ, 0)); + ecstr(a); + ecstr(c); + ecadd(ecnpats++); + } else if ((b[0] == Equals || b[0] == '=') && + (b[1] == Equals || b[1] == '=') && !b[2]) { + ecadd(WCB_COND(COND_STRDEQ, 0)); + ecstr(a); + ecstr(c); + ecadd(ecnpats++); + } else if (b[0] == '!' && (b[1] == Equals || b[1] == '=') && !b[2]) { + ecadd(WCB_COND(COND_STRNEQ, 0)); + ecstr(a); + ecstr(c); + ecadd(ecnpats++); + } else if ((b[0] == Equals || b[0] == '=') && + (b[1] == '~' || b[1] == Tilde) && !b[2]) { + /* We become an implicit COND_MODI but do not provide the first + * item, it's skipped */ + ecadd(WCB_COND(COND_REGEX, 0)); + ecstr(a); + ecstr(c); + } else if (IS_DASH(b[0])) { + if ((t0 = get_cond_num(b + 1)) > -1) { + ecadd(WCB_COND(t0 + COND_NT, 0)); + ecstr(a); + ecstr(c); + } else { + ecadd(WCB_COND(COND_MODI, 0)); + ecstr(b); + ecstr(a); + ecstr(c); + } + } else if (IS_DASH(a[0]) && a[1]) { + ecadd(WCB_COND(COND_MOD, 2)); + ecstr(a); + ecstr(b); + ecstr(c); + } else + COND_ERROR("condition expected: %s", b); + + return 1; +} + +/**/ +static int +par_cond_multi(char *a, LinkList l) +{ + if (!IS_DASH(a[0]) || !a[1]) + COND_ERROR("condition expected: %s", a); + else { + LinkNode n; + + ecadd(WCB_COND(COND_MOD, countlinknodes(l))); + ecstr(a); + for (n = firstnode(l); n; incnode(n)) + ecstr((char *) getdata(n)); + } + return 1; +} + +/**/ +static void +yyerror(int noerr) +{ + int t0; + char *t; + + if ((t = dupstring(zshlextext))) + untokenize(t); + + for (t0 = 0; t0 != 20; t0++) + if (!t || !t[t0] || t[t0] == '\n') + break; + if (!(histdone & HISTFLAG_NOEXEC) && !(errflag & ERRFLAG_INT)) { + if (t0 == 20) + zwarn("parse error near `%l...'", t, 20); + else if (t0) + zwarn("parse error near `%l'", t, t0); + else + zwarn("parse error"); + } + if (!noerr && noerrs != 2) + errflag |= ERRFLAG_ERROR; +} + +/* + * Duplicate a programme list, on the heap if heap is 1, else + * in permanent storage. + * + * Be careful in case p is the Eprog for a function which will + * later be autoloaded. The shf element of the returned Eprog + * must be set appropriately by the caller. (Normally we create + * the Eprog in this case by using mkautofn.) + */ + +/**/ +mod_export Eprog +dupeprog(Eprog p, int heap) +{ + Eprog r; + int i; + Patprog *pp; + + if (p == &dummy_eprog) + return p; + + r = (heap ? (Eprog) zhalloc(sizeof(*r)) : (Eprog) zalloc(sizeof(*r))); + r->flags = (heap ? EF_HEAP : EF_REAL) | (p->flags & EF_RUN); + r->dump = NULL; + r->len = p->len; + r->npats = p->npats; + /* + * If Eprog is on the heap, reference count is not valid. + * Otherwise, initialise reference count to 1 so that a freeeprog() + * will delete it if it is not in use. + */ + r->nref = heap ? -1 : 1; + pp = r->pats = (heap ? (Patprog *) hcalloc(r->len) : + (Patprog *) zshcalloc(r->len)); + r->prog = (Wordcode) (r->pats + r->npats); + r->strs = ((char *) r->prog) + (p->strs - ((char *) p->prog)); + memcpy(r->prog, p->prog, r->len - (p->npats * sizeof(Patprog))); + r->shf = NULL; + + for (i = r->npats; i--; pp++) + *pp = dummy_patprog1; + + return r; +} + + +/* + * Pair of functions to mark an Eprog as in use, and to delete it + * when it is no longer in use, by means of the reference count in + * then nref element. + * + * If nref is negative, the Eprog is on the heap and is never freed. + */ + +/* Increase the reference count of an Eprog so it won't be deleted. */ + +/**/ +mod_export void +useeprog(Eprog p) +{ + if (p && p != &dummy_eprog && p->nref >= 0) + p->nref++; +} + +/* Free an Eprog if we have finished with it */ + +/**/ +mod_export void +freeeprog(Eprog p) +{ + int i; + Patprog *pp; + + if (p && p != &dummy_eprog) { + /* paranoia */ + DPUTS(p->nref > 0 && (p->flags & EF_HEAP), "Heap EPROG has nref > 0"); + DPUTS(p->nref < 0 && !(p->flags & EF_HEAP), "Real EPROG has nref < 0"); + DPUTS(p->nref < -1, "Uninitialised EPROG nref"); +#ifdef MAX_FUNCTION_DEPTH + DPUTS(zsh_funcnest >=0 && p->nref > zsh_funcnest + 10, + "Overlarge EPROG nref"); +#endif + if (p->nref > 0 && !--p->nref) { + for (i = p->npats, pp = p->pats; i--; pp++) + freepatprog(*pp); + if (p->dump) { + decrdumpcount(p->dump); + zfree(p->pats, p->npats * sizeof(Patprog)); + } else + zfree(p->pats, p->len); + zfree(p, sizeof(*p)); + } + } +} + +/**/ +char * +ecgetstr(Estate s, int dup, int *tokflag) +{ + static char buf[4]; + wordcode c = *s->pc++; + char *r; + + if (c == 6 || c == 7) + r = ""; + else if (c & 2) { + buf[0] = (char) ((c >> 3) & 0xff); + buf[1] = (char) ((c >> 11) & 0xff); + buf[2] = (char) ((c >> 19) & 0xff); + buf[3] = '\0'; + r = dupstring(buf); + dup = EC_NODUP; + } else { + r = s->strs + (c >> 2); + } + if (tokflag) + *tokflag = (c & 1); + + /*** Since function dump files are mapped read-only, avoiding to + * to duplicate strings when they don't contain tokens may fail + * when one of the many utility functions happens to write to + * one of the strings (without really modifying it). + * If that happens to you and you don't feel like debugging it, + * just change the line below to: + * + * return (dup ? dupstring(r) : r); + */ + + return ((dup == EC_DUP || (dup && (c & 1))) ? dupstring(r) : r); +} + +/**/ +char * +ecrawstr(Eprog p, Wordcode pc, int *tokflag) +{ + static char buf[4]; + wordcode c = *pc; + + if (c == 6 || c == 7) { + if (tokflag) + *tokflag = (c & 1); + return ""; + } else if (c & 2) { + buf[0] = (char) ((c >> 3) & 0xff); + buf[1] = (char) ((c >> 11) & 0xff); + buf[2] = (char) ((c >> 19) & 0xff); + buf[3] = '\0'; + if (tokflag) + *tokflag = (c & 1); + return buf; + } else { + if (tokflag) + *tokflag = (c & 1); + return p->strs + (c >> 2); + } +} + +/**/ +char ** +ecgetarr(Estate s, int num, int dup, int *tokflag) +{ + char **ret, **rp; + int tf = 0, tmp = 0; + + ret = rp = (char **) zhalloc((num + 1) * sizeof(char *)); + + while (num--) { + *rp++ = ecgetstr(s, dup, &tmp); + tf |= tmp; + } + *rp = NULL; + if (tokflag) + *tokflag = tf; + + return ret; +} + +/**/ +LinkList +ecgetlist(Estate s, int num, int dup, int *tokflag) +{ + if (num) { + LinkList ret; + int i, tf = 0, tmp = 0; + + ret = newsizedlist(num); + for (i = 0; i < num; i++) { + setsizednode(ret, i, ecgetstr(s, dup, &tmp)); + tf |= tmp; + } + if (tokflag) + *tokflag = tf; + return ret; + } + if (tokflag) + *tokflag = 0; + return NULL; +} + +/**/ +LinkList +ecgetredirs(Estate s) +{ + LinkList ret = newlinklist(); + wordcode code = *s->pc++; + + while (wc_code(code) == WC_REDIR) { + Redir r = (Redir) zhalloc(sizeof(*r)); + + r->type = WC_REDIR_TYPE(code); + r->fd1 = *s->pc++; + r->name = ecgetstr(s, EC_DUP, NULL); + if (WC_REDIR_FROM_HEREDOC(code)) { + r->flags = REDIRF_FROM_HEREDOC; + r->here_terminator = ecgetstr(s, EC_DUP, NULL); + r->munged_here_terminator = ecgetstr(s, EC_DUP, NULL); + } else { + r->flags = 0; + r->here_terminator = NULL; + r->munged_here_terminator = NULL; + } + if (WC_REDIR_VARID(code)) + r->varid = ecgetstr(s, EC_DUP, NULL); + else + r->varid = NULL; + + addlinknode(ret, r); + + code = *s->pc++; + } + s->pc--; + + return ret; +} + +/* + * Copy the consecutive set of redirections in the state at s. + * Return NULL if none, else an Eprog consisting only of the + * redirections from permanently allocated memory. + * + * s is left in the state ready for whatever follows the redirections. + */ + +/**/ +Eprog +eccopyredirs(Estate s) +{ + Wordcode pc = s->pc; + wordcode code = *pc; + int ncode, ncodes = 0, r; + + if (wc_code(code) != WC_REDIR) + return NULL; + + init_parse(); + + while (wc_code(code) == WC_REDIR) { +#ifdef DEBUG + int type = WC_REDIR_TYPE(code); +#endif + + DPUTS(type == REDIR_HEREDOC || type == REDIR_HEREDOCDASH, + "unexpanded here document"); + + if (WC_REDIR_FROM_HEREDOC(code)) + ncode = 5; + else + ncode = 3; + if (WC_REDIR_VARID(code)) + ncode++; + pc += ncode; + ncodes += ncode; + code = *pc; + } + r = ecused; + ecispace(r, ncodes); + + code = *s->pc; + while (wc_code(code) == WC_REDIR) { + s->pc++; + + ecbuf[r++] = code; + /* fd1 */ + ecbuf[r++] = *s->pc++; + /* name or HERE string */ + /* No DUP needed as we'll copy into Eprog immediately below */ + ecbuf[r++] = ecstrcode(ecgetstr(s, EC_NODUP, NULL)); + if (WC_REDIR_FROM_HEREDOC(code)) + { + /* terminator, raw */ + ecbuf[r++] = ecstrcode(ecgetstr(s, EC_NODUP, NULL)); + /* terminator, munged */ + ecbuf[r++] = ecstrcode(ecgetstr(s, EC_NODUP, NULL)); + } + if (WC_REDIR_VARID(code)) + ecbuf[r++] = ecstrcode(ecgetstr(s, EC_NODUP, NULL)); + + code = *s->pc; + } + + /* bld_eprog() appends a useful WC_END marker */ + return bld_eprog(0); +} + +/**/ +mod_export struct eprog dummy_eprog; + +static wordcode dummy_eprog_code; + +/**/ +void +init_eprog(void) +{ + dummy_eprog_code = WCB_END(); + dummy_eprog.len = sizeof(wordcode); + dummy_eprog.prog = &dummy_eprog_code; + dummy_eprog.strs = NULL; +} + +/* Code for function dump files. + * + * Dump files consist of a header and the function bodies (the wordcode + * plus the string table) and that twice: once for the byte-order of the + * host the file was created on and once for the other byte-order. The + * header describes where the beginning of the `other' version is and it + * is up to the shell reading the file to decide which version it needs. + * This is done by checking if the first word is FD_MAGIC (then the + * shell reading the file has the same byte order as the one that created + * the file) or if it is FD_OMAGIC, then the `other' version has to be + * read. + * The header is the magic number, a word containing the flags (if the + * file should be mapped or read and if this header is the `other' one), + * the version string in a field of 40 characters and the descriptions + * for the functions in the dump file. + * + * NOTES: + * - This layout has to be kept; everything after it may be changed. + * - When incompatible changes are made, the FD_MAGIC and FD_OMAGIC + * numbers have to be changed. + * + * Each description consists of a struct fdhead followed by the name, + * aligned to sizeof(wordcode) (i.e. 4 bytes). + */ + +#include "version.h" + +#define FD_EXT ".zwc" +#define FD_MINMAP 4096 + +#define FD_PRELEN 12 +#define FD_MAGIC 0x04050607 +#define FD_OMAGIC 0x07060504 + +#define FDF_MAP 1 +#define FDF_OTHER 2 + +typedef struct fdhead *FDHead; + +struct fdhead { + wordcode start; /* offset to function definition */ + wordcode len; /* length of wordcode/strings */ + wordcode npats; /* number of patterns needed */ + wordcode strs; /* offset to strings */ + wordcode hlen; /* header length (incl. name) */ + wordcode flags; /* flags and offset to name tail */ +}; + +#define fdheaderlen(f) (((Wordcode) (f))[FD_PRELEN]) + +#define fdmagic(f) (((Wordcode) (f))[0]) +#define fdsetbyte(f,i,v) \ + ((((unsigned char *) (((Wordcode) (f)) + 1))[i]) = ((unsigned char) (v))) +#define fdbyte(f,i) ((wordcode) (((unsigned char *) (((Wordcode) (f)) + 1))[i])) +#define fdflags(f) fdbyte(f, 0) +#define fdsetflags(f,v) fdsetbyte(f, 0, v) +#define fdother(f) (fdbyte(f, 1) + (fdbyte(f, 2) << 8) + (fdbyte(f, 3) << 16)) +#define fdsetother(f, o) \ + do { \ + fdsetbyte(f, 1, ((o) & 0xff)); \ + fdsetbyte(f, 2, (((o) >> 8) & 0xff)); \ + fdsetbyte(f, 3, (((o) >> 16) & 0xff)); \ + } while (0) +#define fdversion(f) ((char *) ((f) + 2)) + +#define firstfdhead(f) ((FDHead) (((Wordcode) (f)) + FD_PRELEN)) +#define nextfdhead(f) ((FDHead) (((Wordcode) (f)) + (f)->hlen)) + +#define fdhflags(f) (((FDHead) (f))->flags) +#define fdhtail(f) (((FDHead) (f))->flags >> 2) +#define fdhbldflags(f,t) ((f) | ((t) << 2)) + +#define FDHF_KSHLOAD 1 +#define FDHF_ZSHLOAD 2 + +#define fdname(f) ((char *) (((FDHead) (f)) + 1)) + +/* This is used when building wordcode files. */ + +typedef struct wcfunc *WCFunc; + +struct wcfunc { + char *name; + Eprog prog; + int flags; +}; + +/* Try to find the description for the given function name. */ + +static FDHead +dump_find_func(Wordcode h, char *name) +{ + FDHead n, e = (FDHead) (h + fdheaderlen(h)); + + for (n = firstfdhead(h); n < e; n = nextfdhead(n)) + if (!strcmp(name, fdname(n) + fdhtail(n))) + return n; + + return NULL; +} + +/**/ +int +bin_zcompile(char *nam, char **args, Options ops, UNUSED(int func)) +{ + int map, flags, ret; + char *dump; + + if ((OPT_ISSET(ops,'k') && OPT_ISSET(ops,'z')) || + (OPT_ISSET(ops,'R') && OPT_ISSET(ops,'M')) || + (OPT_ISSET(ops,'c') && + (OPT_ISSET(ops,'U') || OPT_ISSET(ops,'k') || OPT_ISSET(ops,'z'))) || + (!(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) && OPT_ISSET(ops,'m'))) { + zwarnnam(nam, "illegal combination of options"); + return 1; + } + if ((OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) && isset(KSHAUTOLOAD)) + zwarnnam(nam, "functions will use zsh style autoloading"); + + flags = (OPT_ISSET(ops,'k') ? FDHF_KSHLOAD : + (OPT_ISSET(ops,'z') ? FDHF_ZSHLOAD : 0)); + + if (OPT_ISSET(ops,'t')) { + Wordcode f; + + if (!*args) { + zwarnnam(nam, "too few arguments"); + return 1; + } + if (!(f = load_dump_header(nam, (strsfx(FD_EXT, *args) ? *args : + dyncat(*args, FD_EXT)), 1))) + return 1; + + if (args[1]) { + for (args++; *args; args++) + if (!dump_find_func(f, *args)) + return 1; + return 0; + } else { + FDHead h, e = (FDHead) (f + fdheaderlen(f)); + + printf("zwc file (%s) for zsh-%s\n", + ((fdflags(f) & FDF_MAP) ? "mapped" : "read"), fdversion(f)); + for (h = firstfdhead(f); h < e; h = nextfdhead(h)) + printf("%s\n", fdname(h)); + return 0; + } + } + if (!*args) { + zwarnnam(nam, "too few arguments"); + return 1; + } + map = (OPT_ISSET(ops,'M') ? 2 : (OPT_ISSET(ops,'R') ? 0 : 1)); + + if (!args[1] && !(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a'))) { + queue_signals(); + ret = build_dump(nam, dyncat(*args, FD_EXT), args, OPT_ISSET(ops,'U'), + map, flags); + unqueue_signals(); + return ret; + } + dump = (strsfx(FD_EXT, *args) ? *args : dyncat(*args, FD_EXT)); + + queue_signals(); + ret = ((OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) ? + build_cur_dump(nam, dump, args + 1, OPT_ISSET(ops,'m'), map, + (OPT_ISSET(ops,'c') ? 1 : 0) | + (OPT_ISSET(ops,'a') ? 2 : 0)) : + build_dump(nam, dump, args + 1, OPT_ISSET(ops,'U'), map, flags)); + unqueue_signals(); + + return ret; +} + +/* Load the header of a dump file. Returns NULL if the file isn't a + * valid dump file. */ + +/**/ +static Wordcode +load_dump_header(char *nam, char *name, int err) +{ + int fd, v = 1; + wordcode buf[FD_PRELEN + 1]; + + if ((fd = open(name, O_RDONLY)) < 0) { + if (err) + zwarnnam(nam, "can't open zwc file: %s", name); + return NULL; + } + if (read(fd, buf, (FD_PRELEN + 1) * sizeof(wordcode)) != + ((FD_PRELEN + 1) * sizeof(wordcode)) || + (v = (fdmagic(buf) != FD_MAGIC && fdmagic(buf) != FD_OMAGIC)) || + strcmp(fdversion(buf), ZSH_VERSION)) { + if (err) { + if (!v) { + zwarnnam(nam, "zwc file has wrong version (zsh-%s): %s", + fdversion(buf), name); + } else + zwarnnam(nam, "invalid zwc file: %s" , name); + } + close(fd); + return NULL; + } else { + int len; + Wordcode head; + + if (fdmagic(buf) == FD_MAGIC) { + len = fdheaderlen(buf) * sizeof(wordcode); + head = (Wordcode) zhalloc(len); + } + else { + int o = fdother(buf); + + if (lseek(fd, o, 0) == -1 || + read(fd, buf, (FD_PRELEN + 1) * sizeof(wordcode)) != + ((FD_PRELEN + 1) * sizeof(wordcode))) { + zwarnnam(nam, "invalid zwc file: %s" , name); + close(fd); + return NULL; + } + len = fdheaderlen(buf) * sizeof(wordcode); + head = (Wordcode) zhalloc(len); + } + memcpy(head, buf, (FD_PRELEN + 1) * sizeof(wordcode)); + + len -= (FD_PRELEN + 1) * sizeof(wordcode); + if (read(fd, head + (FD_PRELEN + 1), len) != len) { + close(fd); + zwarnnam(nam, "invalid zwc file: %s" , name); + return NULL; + } + close(fd); + return head; + } +} + +/* Swap the bytes in a wordcode. */ + +static void +fdswap(Wordcode p, int n) +{ + wordcode c; + + for (; n--; p++) { + c = *p; + *p = (((c & 0xff) << 24) | + ((c & 0xff00) << 8) | + ((c & 0xff0000) >> 8) | + ((c & 0xff000000) >> 24)); + } +} + +/* Write a dump file. */ + +static void +write_dump(int dfd, LinkList progs, int map, int hlen, int tlen) +{ + LinkNode node; + WCFunc wcf; + int other = 0, ohlen, tmp; + wordcode pre[FD_PRELEN]; + char *tail, *n; + struct fdhead head; + Eprog prog; + + if (map == 1) + map = (tlen >= FD_MINMAP); + + memset(pre, 0, sizeof(wordcode) * FD_PRELEN); + + for (ohlen = hlen; ; hlen = ohlen) { + fdmagic(pre) = (other ? FD_OMAGIC : FD_MAGIC); + fdsetflags(pre, ((map ? FDF_MAP : 0) | other)); + fdsetother(pre, tlen); + strcpy(fdversion(pre), ZSH_VERSION); + write_loop(dfd, (char *)pre, FD_PRELEN * sizeof(wordcode)); + + for (node = firstnode(progs); node; incnode(node)) { + wcf = (WCFunc) getdata(node); + n = wcf->name; + prog = wcf->prog; + head.start = hlen; + hlen += (prog->len - (prog->npats * sizeof(Patprog)) + + sizeof(wordcode) - 1) / sizeof(wordcode); + head.len = prog->len - (prog->npats * sizeof(Patprog)); + head.npats = prog->npats; + head.strs = prog->strs - ((char *) prog->prog); + head.hlen = (sizeof(struct fdhead) / sizeof(wordcode)) + + (strlen(n) + sizeof(wordcode)) / sizeof(wordcode); + if ((tail = strrchr(n, '/'))) + tail++; + else + tail = n; + head.flags = fdhbldflags(wcf->flags, (tail - n)); + if (other) + fdswap((Wordcode) &head, sizeof(head) / sizeof(wordcode)); + write_loop(dfd, (char *)&head, sizeof(head)); + tmp = strlen(n) + 1; + write_loop(dfd, n, tmp); + if ((tmp &= (sizeof(wordcode) - 1))) + write_loop(dfd, (char *)&head, sizeof(wordcode) - tmp); + } + for (node = firstnode(progs); node; incnode(node)) { + prog = ((WCFunc) getdata(node))->prog; + tmp = (prog->len - (prog->npats * sizeof(Patprog)) + + sizeof(wordcode) - 1) / sizeof(wordcode); + if (other) + fdswap(prog->prog, (((Wordcode) prog->strs) - prog->prog)); + write_loop(dfd, (char *)prog->prog, tmp * sizeof(wordcode)); + } + if (other) + break; + other = FDF_OTHER; + } +} + +/**/ +static int +build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) +{ + int dfd, fd, hlen, tlen, flen, ona = noaliases; + LinkList progs; + char *file; + Eprog prog; + WCFunc wcf; + + if (!strsfx(FD_EXT, dump)) + dump = dyncat(dump, FD_EXT); + + unlink(dump); + if ((dfd = open(dump, O_WRONLY|O_CREAT, 0444)) < 0) { + zwarnnam(nam, "can't write zwc file: %s", dump); + return 1; + } + progs = newlinklist(); + noaliases = ali; + + for (hlen = FD_PRELEN, tlen = 0; *files; files++) { + struct stat st; + + if (check_cond(*files, "k")) { + flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_KSHLOAD; + continue; + } else if (check_cond(*files, "z")) { + flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_ZSHLOAD; + continue; + } + if ((fd = open(*files, O_RDONLY)) < 0 || + fstat(fd, &st) != 0 || !S_ISREG(st.st_mode) || + (flen = lseek(fd, 0, 2)) == -1) { + if (fd >= 0) + close(fd); + close(dfd); + zwarnnam(nam, "can't open file: %s", *files); + noaliases = ona; + unlink(dump); + return 1; + } + file = (char *) zalloc(flen + 1); + file[flen] = '\0'; + lseek(fd, 0, 0); + if (read(fd, file, flen) != flen) { + close(fd); + close(dfd); + zfree(file, flen); + zwarnnam(nam, "can't read file: %s", *files); + noaliases = ona; + unlink(dump); + return 1; + } + close(fd); + file = metafy(file, flen, META_REALLOC); + + if (!(prog = parse_string(file, 1)) || errflag) { + errflag &= ~ERRFLAG_ERROR; + close(dfd); + zfree(file, flen); + zwarnnam(nam, "can't read file: %s", *files); + noaliases = ona; + unlink(dump); + return 1; + } + zfree(file, flen); + + wcf = (WCFunc) zhalloc(sizeof(*wcf)); + wcf->name = *files; + wcf->prog = prog; + wcf->flags = ((prog->flags & EF_RUN) ? FDHF_KSHLOAD : flags); + addlinknode(progs, wcf); + + flen = (strlen(*files) + sizeof(wordcode)) / sizeof(wordcode); + hlen += (sizeof(struct fdhead) / sizeof(wordcode)) + flen; + + tlen += (prog->len - (prog->npats * sizeof(Patprog)) + + sizeof(wordcode) - 1) / sizeof(wordcode); + } + noaliases = ona; + + tlen = (tlen + hlen) * sizeof(wordcode); + + write_dump(dfd, progs, map, hlen, tlen); + + close(dfd); + + return 0; +} + +static int +cur_add_func(char *nam, Shfunc shf, LinkList names, LinkList progs, + int *hlen, int *tlen, int what) +{ + Eprog prog; + WCFunc wcf; + + if (shf->node.flags & PM_UNDEFINED) { + int ona = noaliases; + + if (!(what & 2)) { + zwarnnam(nam, "function is not loaded: %s", shf->node.nam); + return 1; + } + noaliases = (shf->node.flags & PM_UNALIASED); + if (!(prog = getfpfunc(shf->node.nam, NULL, NULL, NULL, 0)) || + prog == &dummy_eprog) { + noaliases = ona; + zwarnnam(nam, "can't load function: %s", shf->node.nam); + return 1; + } + if (prog->dump) + prog = dupeprog(prog, 1); + noaliases = ona; + } else { + if (!(what & 1)) { + zwarnnam(nam, "function is already loaded: %s", shf->node.nam); + return 1; + } + prog = dupeprog(shf->funcdef, 1); + } + wcf = (WCFunc) zhalloc(sizeof(*wcf)); + wcf->name = shf->node.nam; + wcf->prog = prog; + wcf->flags = ((prog->flags & EF_RUN) ? FDHF_KSHLOAD : FDHF_ZSHLOAD); + addlinknode(progs, wcf); + addlinknode(names, shf->node.nam); + + *hlen += ((sizeof(struct fdhead) / sizeof(wordcode)) + + ((strlen(shf->node.nam) + sizeof(wordcode)) / sizeof(wordcode))); + *tlen += (prog->len - (prog->npats * sizeof(Patprog)) + + sizeof(wordcode) - 1) / sizeof(wordcode); + + return 0; +} + +/**/ +static int +build_cur_dump(char *nam, char *dump, char **names, int match, int map, + int what) +{ + int dfd, hlen, tlen; + LinkList progs, lnames; + Shfunc shf = NULL; + + if (!strsfx(FD_EXT, dump)) + dump = dyncat(dump, FD_EXT); + + unlink(dump); + if ((dfd = open(dump, O_WRONLY|O_CREAT, 0444)) < 0) { + zwarnnam(nam, "can't write zwc file: %s", dump); + return 1; + } + progs = newlinklist(); + lnames = newlinklist(); + + hlen = FD_PRELEN; + tlen = 0; + + if (!*names) { + int i; + HashNode hn; + + for (i = 0; i < shfunctab->hsize; i++) + for (hn = shfunctab->nodes[i]; hn; hn = hn->next) + if (cur_add_func(nam, (Shfunc) hn, lnames, progs, + &hlen, &tlen, what)) { + errflag &= ~ERRFLAG_ERROR; + close(dfd); + unlink(dump); + return 1; + } + } else if (match) { + char *pat; + Patprog pprog; + int i; + HashNode hn; + + for (; *names; names++) { + tokenize(pat = dupstring(*names)); + /* Signal-safe here, caller queues signals */ + if (!(pprog = patcompile(pat, PAT_STATIC, NULL))) { + zwarnnam(nam, "bad pattern: %s", *names); + close(dfd); + unlink(dump); + return 1; + } + for (i = 0; i < shfunctab->hsize; i++) + for (hn = shfunctab->nodes[i]; hn; hn = hn->next) + if (!linknodebydatum(lnames, hn->nam) && + pattry(pprog, hn->nam) && + cur_add_func(nam, (Shfunc) hn, lnames, progs, + &hlen, &tlen, what)) { + errflag &= ~ERRFLAG_ERROR; + close(dfd); + unlink(dump); + return 1; + } + } + } else { + for (; *names; names++) { + if (errflag || + !(shf = (Shfunc) shfunctab->getnode(shfunctab, *names))) { + zwarnnam(nam, "unknown function: %s", *names); + errflag &= ~ERRFLAG_ERROR; + close(dfd); + unlink(dump); + return 1; + } + if (cur_add_func(nam, shf, lnames, progs, &hlen, &tlen, what)) { + errflag &= ~ERRFLAG_ERROR; + close(dfd); + unlink(dump); + return 1; + } + } + } + if (empty(progs)) { + zwarnnam(nam, "no functions"); + errflag &= ~ERRFLAG_ERROR; + close(dfd); + unlink(dump); + return 1; + } + tlen = (tlen + hlen) * sizeof(wordcode); + + write_dump(dfd, progs, map, hlen, tlen); + + close(dfd); + + return 0; +} + +/**/ +#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MMAP) && defined(HAVE_MUNMAP) + +#include + +/**/ +#if defined(MAP_SHARED) && defined(PROT_READ) + +/**/ +#define USE_MMAP 1 + +/**/ +#endif +/**/ +#endif + +/**/ +#ifdef USE_MMAP + +/* List of dump files mapped. */ + +static FuncDump dumps; + +/**/ +static int +zwcstat(char *filename, struct stat *buf) +{ + if (stat(filename, buf)) { +#ifdef HAVE_FSTAT + FuncDump f; + + for (f = dumps; f; f = f->next) { + if (!strncmp(filename, f->filename, strlen(f->filename)) && + !fstat(f->fd, buf)) + return 0; + } +#endif + return 1; + } else return 0; +} + +/* Load a dump file (i.e. map it). */ + +static void +load_dump_file(char *dump, struct stat *sbuf, int other, int len) +{ + FuncDump d; + Wordcode addr; + int fd, off, mlen; + + if (other) { + static size_t pgsz = 0; + + if (!pgsz) { + +#ifdef _SC_PAGESIZE + pgsz = sysconf(_SC_PAGESIZE); /* SVR4 */ +#else +# ifdef _SC_PAGE_SIZE + pgsz = sysconf(_SC_PAGE_SIZE); /* HPUX */ +# else + pgsz = getpagesize(); +# endif +#endif + + pgsz--; + } + off = len & ~pgsz; + mlen = len + (len - off); + } else { + off = 0; + mlen = len; + } + if ((fd = open(dump, O_RDONLY)) < 0) + return; + + fd = movefd(fd); + if (fd == -1) + return; + + if ((addr = (Wordcode) mmap(NULL, mlen, PROT_READ, MAP_SHARED, fd, off)) == + ((Wordcode) -1)) { + close(fd); + return; + } + d = (FuncDump) zalloc(sizeof(*d)); + d->next = dumps; + dumps = d; + d->dev = sbuf->st_dev; + d->ino = sbuf->st_ino; + d->fd = fd; +#ifdef FD_CLOEXEC + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + d->map = addr + (other ? (len - off) / sizeof(wordcode) : 0); + d->addr = addr; + d->len = len; + d->count = 0; + d->filename = ztrdup(dump); +} + +#else + +#define zwcstat(f, b) (!!stat(f, b)) + +/**/ +#endif + +/* Try to load a function from one of the possible wordcode files for it. + * The first argument is a element of $fpath, the second one is the name + * of the function searched and the last one is the possible name for the + * uncompiled function file (/). */ + +/**/ +Eprog +try_dump_file(char *path, char *name, char *file, int *ksh, int test_only) +{ + Eprog prog; + struct stat std, stc, stn; + int rd, rc, rn; + char *dig, *wc; + + if (strsfx(FD_EXT, path)) { + queue_signals(); + prog = check_dump_file(path, NULL, name, ksh, test_only); + unqueue_signals(); + return prog; + } + dig = dyncat(path, FD_EXT); + wc = dyncat(file, FD_EXT); + + rd = zwcstat(dig, &std); + rc = stat(wc, &stc); + rn = stat(file, &stn); + + /* See if there is a digest file for the directory, it is younger than + * both the uncompiled function file and its compiled version (or they + * don't exist) and the digest file contains the definition for the + * function. */ + queue_signals(); + if (!rd && + (rc || std.st_mtime >= stc.st_mtime) && + (rn || std.st_mtime >= stn.st_mtime) && + (prog = check_dump_file(dig, &std, name, ksh, test_only))) { + unqueue_signals(); + return prog; + } + /* No digest file. Now look for the per-function compiled file. */ + if (!rc && + (rn || stc.st_mtime >= stn.st_mtime) && + (prog = check_dump_file(wc, &stc, name, ksh, test_only))) { + unqueue_signals(); + return prog; + } + /* No compiled file for the function. The caller (getfpfunc() will + * check if the directory contains the uncompiled file for it. */ + unqueue_signals(); + return NULL; +} + +/* Almost the same, but for sourced files. */ + +/**/ +Eprog +try_source_file(char *file) +{ + Eprog prog; + struct stat stc, stn; + int rc, rn; + char *wc, *tail; + + if ((tail = strrchr(file, '/'))) + tail++; + else + tail = file; + + if (strsfx(FD_EXT, file)) { + queue_signals(); + prog = check_dump_file(file, NULL, tail, NULL, 0); + unqueue_signals(); + return prog; + } + wc = dyncat(file, FD_EXT); + + rc = stat(wc, &stc); + rn = stat(file, &stn); + + queue_signals(); + if (!rc && (rn || stc.st_mtime >= stn.st_mtime) && + (prog = check_dump_file(wc, &stc, tail, NULL, 0))) { + unqueue_signals(); + return prog; + } + unqueue_signals(); + return NULL; +} + +/* See if `file' names a wordcode dump file and that contains the + * definition for the function `name'. If so, return an eprog for it. */ + +/**/ +static Eprog +check_dump_file(char *file, struct stat *sbuf, char *name, int *ksh, + int test_only) +{ + int isrec = 0; + Wordcode d; + FDHead h; + FuncDump f; + struct stat lsbuf; + + if (!sbuf) { + if (zwcstat(file, &lsbuf)) + return NULL; + sbuf = &lsbuf; + } + +#ifdef USE_MMAP + + rec: + +#endif + + d = NULL; + +#ifdef USE_MMAP + + for (f = dumps; f; f = f->next) + if (f->dev == sbuf->st_dev && f->ino == sbuf->st_ino) { + d = f->map; + break; + } + +#else + + f = NULL; + +#endif + + if (!f && (isrec || !(d = load_dump_header(NULL, file, 0)))) + return NULL; + + if ((h = dump_find_func(d, name))) { + /* Found the name. If the file is already mapped, return the eprog, + * otherwise map it and just go up. */ + if (test_only) + { + /* This is all we need. Just return dummy. */ + return &dummy_eprog; + } + +#ifdef USE_MMAP + + if (f) { + Eprog prog = (Eprog) zalloc(sizeof(*prog)); + Patprog *pp; + int np; + + prog->flags = EF_MAP; + prog->len = h->len; + prog->npats = np = h->npats; + prog->nref = 1; /* allocated from permanent storage */ + prog->pats = pp = (Patprog *) zalloc(np * sizeof(Patprog)); + prog->prog = f->map + h->start; + prog->strs = ((char *) prog->prog) + h->strs; + prog->shf = NULL; + prog->dump = f; + + incrdumpcount(f); + + while (np--) + *pp++ = dummy_patprog1; + + if (ksh) + *ksh = ((fdhflags(h) & FDHF_KSHLOAD) ? 2 : + ((fdhflags(h) & FDHF_ZSHLOAD) ? 0 : 1)); + + return prog; + } else if (fdflags(d) & FDF_MAP) { + load_dump_file(file, sbuf, (fdflags(d) & FDF_OTHER), fdother(d)); + isrec = 1; + goto rec; + } else + +#endif + + { + Eprog prog; + Patprog *pp; + int np, fd, po = h->npats * sizeof(Patprog); + + if ((fd = open(file, O_RDONLY)) < 0 || + lseek(fd, ((h->start * sizeof(wordcode)) + + ((fdflags(d) & FDF_OTHER) ? fdother(d) : 0)), 0) < 0) { + if (fd >= 0) + close(fd); + return NULL; + } + d = (Wordcode) zalloc(h->len + po); + + if (read(fd, ((char *) d) + po, h->len) != (int)h->len) { + close(fd); + zfree(d, h->len); + + return NULL; + } + close(fd); + + prog = (Eprog) zalloc(sizeof(*prog)); + + prog->flags = EF_REAL; + prog->len = h->len + po; + prog->npats = np = h->npats; + prog->nref = 1; /* allocated from permanent storage */ + prog->pats = pp = (Patprog *) d; + prog->prog = (Wordcode) (((char *) d) + po); + prog->strs = ((char *) prog->prog) + h->strs; + prog->shf = NULL; + prog->dump = f; + + while (np--) + *pp++ = dummy_patprog1; + + if (ksh) + *ksh = ((fdhflags(h) & FDHF_KSHLOAD) ? 2 : + ((fdhflags(h) & FDHF_ZSHLOAD) ? 0 : 1)); + + return prog; + } + } + return NULL; +} + +#ifdef USE_MMAP + +/* Increment the reference counter for a dump file. */ + +/**/ +void +incrdumpcount(FuncDump f) +{ + f->count++; +} + +/**/ +static void +freedump(FuncDump f) +{ + munmap((void *) f->addr, f->len); + zclose(f->fd); + zsfree(f->filename); + zfree(f, sizeof(*f)); +} + +/* Decrement the reference counter for a dump file. If zero, unmap the file. */ + +/**/ +void +decrdumpcount(FuncDump f) +{ + f->count--; + if (!f->count) { + FuncDump p, q; + + for (q = NULL, p = dumps; p && p != f; q = p, p = p->next); + if (p) { + if (q) + q->next = p->next; + else + dumps = p->next; + freedump(f); + } + } +} + +#ifndef FD_CLOEXEC +/**/ +mod_export void +closedumps(void) +{ + while (dumps) { + FuncDump p = dumps->next; + freedump(dumps); + dumps = p; + } +} +#endif + +#else + +void +incrdumpcount(FuncDump f) +{ +} + +void +decrdumpcount(FuncDump f) +{ +} + +#ifndef FD_CLOEXEC +/**/ +mod_export void +closedumps(void) +{ +} +#endif + +#endif + +/**/ +int +dump_autoload(char *nam, char *file, int on, Options ops, int func) +{ + Wordcode h; + FDHead n, e; + Shfunc shf; + int ret = 0; + + if (!strsfx(FD_EXT, file)) + file = dyncat(file, FD_EXT); + + if (!(h = load_dump_header(nam, file, 1))) + return 1; + + for (n = firstfdhead(h), e = (FDHead) (h + fdheaderlen(h)); n < e; + n = nextfdhead(n)) { + shf = (Shfunc) zshcalloc(sizeof *shf); + shf->node.flags = on; + shf->funcdef = mkautofn(shf); + shf->sticky = NULL; + shfunctab->addnode(shfunctab, ztrdup(fdname(n) + fdhtail(n)), shf); + if (OPT_ISSET(ops,'X') && eval_autoload(shf, shf->node.nam, ops, func)) + ret = 1; + } + return ret; +} diff --git a/dotfiles/system/.zsh/modules/Src/pattern.c b/dotfiles/system/.zsh/modules/Src/pattern.c new file mode 100644 index 0000000..737f5cd --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/pattern.c @@ -0,0 +1,4336 @@ +/* + * pattern.c - pattern matching + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1999 Peter Stephenson + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Peter Stephenson or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Peter Stephenson and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Peter Stephenson and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Peter Stephenson and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + * Pattern matching code derived from the regexp library by Henry + * Spencer, which has the following copyright. + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * Eagle-eyed readers will notice this is an altered version. Incredibly + * sharp-eyed readers might even find bits that weren't altered. + * + * + * And I experienced a sense that, like certain regular + * expressions, seemed to match the day from beginning to end, so + * that I did not need to identify the parenthesised subexpression + * that told of dawn, nor the group of characters that indicated + * the moment when my grandfather returned home with news of + * Swann's departure for Paris; and the whole length of the month + * of May, as if matched by a closure, fitted into the buffer of my + * life with no sign of overflowing, turning the days, like a + * procession of insects that could consist of this or that + * species, into a random and unstructured repetition of different + * sequences, anchored from the first day of the month to the last + * in the same fashion as the weeks when I knew I would not see + * Gilberte and would search in vain for any occurrences of the + * string in the avenue of hawthorns by Tansonville, without my + * having to delimit explicitly the start or finish of the pattern. + * + * M. Proust, "In Search of Lost Files", + * bk I, "The Walk by Bourne's Place". + */ + +#include "zsh.mdh" + +/* + * The following union is used mostly for alignment purposes. + * Normal nodes are longs, while certain nodes take a char * as an argument; + * here we make sure that they both work out to the same length. + * The compiled regexp we construct consists of upats stuck together; + * anything else to be added (strings, numbers) is stuck after and + * then aligned to a whole number of upat units. + * + * Note also that offsets are in terms of the sizes of these things. + */ +union upat { + long l; + unsigned char *p; +}; + +typedef union upat *Upat; + +#include "pattern.pro" + +/* Number of active parenthesized expressions allowed in backreferencing */ +#define NSUBEXP 9 + +/* definition number opnd? meaning */ +#define P_END 0x00 /* no End of program. */ +#define P_EXCSYNC 0x01 /* no Test if following exclude already failed */ +#define P_EXCEND 0x02 /* no Test if exclude matched orig branch */ +#define P_BACK 0x03 /* no Match "", "next" ptr points backward. */ +#define P_EXACTLY 0x04 /* lstr Match this string. */ +#define P_NOTHING 0x05 /* no Match empty string. */ +#define P_ONEHASH 0x06 /* node Match this (simple) thing 0 or more times. */ +#define P_TWOHASH 0x07 /* node Match this (simple) thing 1 or more times. */ +#define P_GFLAGS 0x08 /* long Match nothing and set globbing flags */ +#define P_ISSTART 0x09 /* no Match start of string. */ +#define P_ISEND 0x0a /* no Match end of string. */ +#define P_COUNTSTART 0x0b /* no Initialise P_COUNT */ +#define P_COUNT 0x0c /* 3*long uc* node Match a number of repetitions */ +/* numbered so we can test bit 5 for a branch */ +#define P_BRANCH 0x20 /* node Match this alternative, or the next... */ +#define P_WBRANCH 0x21 /* uc* node P_BRANCH, but match at least 1 char */ +/* excludes are also branches, but have bit 4 set, too */ +#define P_EXCLUDE 0x30 /* uc* node Exclude this from previous branch */ +#define P_EXCLUDP 0x31 /* uc* node Exclude, using full file path so far */ +/* numbered so we can test bit 6 so as not to match initial '.' */ +#define P_ANY 0x40 /* no Match any one character. */ +#define P_ANYOF 0x41 /* str Match any character in this string. */ +#define P_ANYBUT 0x42 /* str Match any character not in this string. */ +#define P_STAR 0x43 /* no Match any set of characters. */ +#define P_NUMRNG 0x44 /* zr, zr Match a numeric range. */ +#define P_NUMFROM 0x45 /* zr Match a number >= X */ +#define P_NUMTO 0x46 /* zr Match a number <= X */ +#define P_NUMANY 0x47 /* no Match any set of decimal digits */ +/* spaces left for P_OPEN+n,... for backreferences */ +#define P_OPEN 0x80 /* no Mark this point in input as start of n. */ +#define P_CLOSE 0x90 /* no Analogous to OPEN. */ +/* + * no no argument + * zr the range type zrange_t: may be zlong or unsigned long + * char a single char + * uc* a pointer to unsigned char, used at run time and initialised + * to NULL. + * str null-terminated, metafied string + * lstr length as long then string, not null-terminated, unmetafied. + */ + +/* + * Notes on usage: + * P_WBRANCH: This works like a branch and is used in complex closures, + * to ensure we don't succeed on a zero-length match of the pattern, + * since that would cause an infinite loop. We do this by recording + * the positions where we have already tried to match. See the + * P_WBRANCH test in patmatch(). + * + * P_ANY, P_ANYOF: the operand is a null terminated + * string. Normal characters match as expected. Characters + * in the range Meta+PP_ALPHA..Meta+PP_UNKWN do the appropriate + * Posix range tests. This relies on imeta returning true for these + * characters. We treat unknown POSIX ranges as never matching. + * PP_RANGE means the next two (possibly metafied) characters form + * the limits of a range to test; it's too much like hard work to + * expand the range. + * + * P_EXCLUDE, P_EXCSYNC, PEXCEND: P_EXCLUDE appears in the pattern like + * P_BRANCH, but applies to the immediately preceding branch. The code in + * the corresponding branch is followed by a P_EXCSYNC, which simply + * acts as a marker that a P_EXCLUDE comes next. The P_EXCLUDE + * has a pointer to char embeded in it, which works + * like P_WBRANCH: if we get to the P_EXCSYNC, and we already matched + * up to the same position, fail. Thus we are forced to backtrack + * on closures in the P_BRANCH if the first attempt was excluded. + * Corresponding to P_EXCSYNC in the original branch, there is a + * P_EXCEND in the exclusion. If we get to this point, and we did + * *not* match in the original branch, the exclusion itself fails, + * otherwise it succeeds since we know the tail already matches, + * so P_EXCEND is the end of the exclusion test. + * The whole sorry mess looks like this, where the upper lines + * show the linkage of the branches, and the lower shows the linkage + * of their pattern arguments. + * + * --------------------- ---------------------- + * ^ v ^ v + * ( :apat-> :excpat-> ) tail + * ^ + * | | + * -------------------------------------- + * + * P_EXCLUDP: this behaves exactly like P_EXCLUDE, with the sole exception + * that we prepend the path so far to the exclude pattern. This is + * for top level file globs, e.g. ** / *.c~*foo.c + * ^ I had to leave this space + * P_NUM*: zl is a zlong if that is 64-bit, else an unsigned long. + * + * P_COUNTSTART, P_COUNT: a P_COUNTSTART flags the start of a quantified + * closure (#cN,M) and is used to initialise the count. Executing + * the pattern leads back to the P_COUNT, while the next links of the + * P_COUNTSTART and P_COUNT lead to the tail of the pattern: + * + * ---------------- + * v ^ + * pattern tail + * v v ^ + * ------------------------ + */ + +#define P_OP(p) ((p)->l & 0xff) +#define P_NEXT(p) ((p)->l >> 8) +#define P_OPERAND(p) ((p) + 1) +#define P_ISBRANCH(p) ((p)->l & 0x20) +#define P_ISEXCLUDE(p) (((p)->l & 0x30) == 0x30) +#define P_NOTDOT(p) ((p)->l & 0x40) + +/* Specific to lstr type, i.e. P_EXACTLY. */ +#define P_LS_LEN(p) ((p)[1].l) /* can be used as lvalue */ +#define P_LS_STR(p) ((char *)((p) + 2)) + +/* Specific to P_COUNT: arguments as offset in nodes from operator */ +#define P_CT_CURRENT (1) /* Current count */ +#define P_CT_MIN (2) /* Minimum count */ +#define P_CT_MAX (3) /* Maximum count, -1 for none */ +#define P_CT_PTR (4) /* Pointer to last match start */ +#define P_CT_OPERAND (5) /* Operand of P_COUNT */ + +/* Flags needed when pattern is executed */ +#define P_SIMPLE 0x01 /* Simple enough to be #/## operand. */ +#define P_HSTART 0x02 /* Starts with # or ##'d pattern. */ +#define P_PURESTR 0x04 /* Can be matched with a strcmp */ + +#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT) +typedef zlong zrange_t; +#define ZRANGE_T_IS_SIGNED (1) +#define ZRANGE_MAX ZLONG_MAX +#else +typedef unsigned long zrange_t; +#define ZRANGE_MAX ULONG_MAX +#endif + +#ifdef MULTIBYTE_SUPPORT +/* + * Handle a byte that's not part of a valid character. + * + * This range in Unicode is recommended for purposes of this + * kind as it corresponds to invalid characters. + * + * Note that this strictly only works if wchar_t represents + * Unicode code points, which isn't necessarily true; however, + * converting an invalid character into an unknown format is + * a bit tricky... + */ +#define WCHAR_INVALID(ch) \ + ((wchar_t) (0xDC00 + STOUC(ch))) +#endif /* MULTIBYTE_SUPPORT */ + +/* + * Array of characters corresponding to zpc_chars enum, which it must match. + */ +static const char zpc_chars[ZPC_COUNT] = { + '/', '\0', Bar, Outpar, Tilde, Inpar, Quest, Star, Inbrack, Inang, + Hat, Pound, Bnullkeep, Quest, Star, '+', Bang, '!', '@' +}; + +/* + * Corresponding strings used in enable/disable -p. + * NULL means no way of turning this on or off. + */ +/**/ +mod_export const char *zpc_strings[ZPC_COUNT] = { + NULL, NULL, "|", NULL, "~", "(", "?", "*", "[", "<", + "^", "#", NULL, "?(", "*(", "+(", "!(", "\\!(", "@(" +}; + +/* + * Corresponding array of pattern disables as set by the user + * using "disable -p". + */ +/**/ +mod_export char zpc_disables[ZPC_COUNT]; + +/* + * Stack of saved (compressed) zpc_disables for function scope. + */ + +static struct zpc_disables_save *zpc_disables_stack; + +/* + * Characters which terminate a simple string (ZPC_COUNT) or + * an entire pattern segment (the first ZPC_SEG_COUNT). + * Each entry is either the corresponding character in zpc_chars + * or Marker which is guaranteed not to match a character in a + * pattern we are compiling. + * + * The complete list indicates characters that are special, so e.g. + * (testchar == special[ZPC_TILDE]) succeeds only if testchar is a Tilde + * *and* Tilde is currently special. + */ + +/**/ +char zpc_special[ZPC_COUNT]; + +/* Default size for pattern buffer */ +#define P_DEF_ALLOC 256 + +/* Flags used in compilation */ +static char *patstart, *patparse; /* input pointers */ +static int patnpar; /* () count */ +static char *patcode; /* point of code emission */ +static long patsize; /* size of code */ +static char *patout; /* start of code emission string */ +static long patalloc; /* size allocated for same */ + +/* Flags used in both compilation and execution */ +static int patflags; /* flags passed down to patcompile */ +static int patglobflags; /* globbing flags & approx */ + +/* + * Increment pointer to metafied multibyte string. + */ +#ifdef MULTIBYTE_SUPPORT +typedef wint_t patint_t; + +#define PEOF WEOF + +#define METACHARINC(x) ((void)metacharinc(&x)) + +/* + * TODO: the shiftstate isn't well handled; we don't guarantee + * to maintain it properly between characters. If we don't + * need it we should use mbtowc() instead. + */ +static mbstate_t shiftstate; + +/* + * Multibyte version: it's (almost) as easy to return the + * value as not, so do so since we sometimes need it.. + */ +static wchar_t +metacharinc(char **x) +{ + char *inptr = *x; + char inchar; + size_t ret = MB_INVALID; + wchar_t wc; + + /* + * Cheat if the top bit isn't set. This is second-guessing + * the library, but we know for sure that if the character + * set doesn't have the property that all bytes with the 8th + * bit clear are single characters then we are stuffed. + */ + if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*inptr) & 0x80)) + { + if (itok(*inptr)) + inchar = ztokens[*inptr++ - Pound]; + else if (*inptr == Meta) { + inptr++; + inchar = *inptr++ ^ 32; + } else { + inchar = *inptr++; + } + *x = inptr; + return (wchar_t)STOUC(inchar); + } + + while (*inptr) { + if (itok(*inptr)) + inchar = ztokens[*inptr++ - Pound]; + else if (*inptr == Meta) { + inptr++; + inchar = *inptr++ ^ 32; + } else { + inchar = *inptr++; + } + ret = mbrtowc(&wc, &inchar, 1, &shiftstate); + + if (ret == MB_INVALID) + break; + if (ret == MB_INCOMPLETE) + continue; + *x = inptr; + return wc; + } + + /* Error. */ + /* Reset the shift state for next time. */ + memset(&shiftstate, 0, sizeof(shiftstate)); + return WCHAR_INVALID(*(*x)++); +} + +#else +typedef int patint_t; + +#define PEOF EOF + +#define METACHARINC(x) ((void)((x) += (*(x) == Meta) ? 2 : 1)) +#endif + +/* + * Return unmetafied char from string (x is any char *). + * Used with MULTIBYTE_SUPPORT if the GF_MULTIBYTE is not + * in effect. + */ +#define UNMETA(x) (*(x) == Meta ? (x)[1] ^ 32 : *(x)) + +/* Add n more characters, ensuring there is enough space. */ + +enum { + PA_NOALIGN = 1, + PA_UNMETA = 2 +}; + +/**/ +static void +patadd(char *add, int ch, long n, int paflags) +{ + /* Make sure everything gets aligned unless we get PA_NOALIGN. */ + long newpatsize = patsize + n; + if (!(paflags & PA_NOALIGN)) + newpatsize = (newpatsize + sizeof(union upat) - 1) & + ~(sizeof(union upat) - 1); + if (patalloc < newpatsize) { + long newpatalloc = + 2*(newpatsize > patalloc ? newpatsize : patalloc); + patout = (char *)zrealloc((char *)patout, newpatalloc); + patcode = patout + patsize; + patalloc = newpatalloc; + } + patsize = newpatsize; + if (add) { + if (paflags & PA_UNMETA) { + /* + * Unmetafy and untokenize the string as we go. + * The Meta characters in add aren't counted in n. + */ + while (n--) { + if (itok(*add)) + *patcode++ = ztokens[*add++ - Pound]; + else if (*add == Meta) { + add++; + *patcode++ = *add++ ^ 32; + } else { + *patcode++ = *add++; + } + } + } else { + while (n--) + *patcode++ = *add++; + } + } else + *patcode++ = ch; + patcode = patout + patsize; +} + +static long rn_offs; +/* operates on pointers to union upat, returns a pointer */ +#define PATNEXT(p) ((rn_offs = P_NEXT(p)) ? \ + (P_OP(p) == P_BACK) ? \ + ((p)-rn_offs) : ((p)+rn_offs) : NULL) + +/* + * Set up zpc_special with characters that end a string segment. + * "Marker" cannot occur in the pattern we are compiling so + * is used to mark "invalid". + */ +static void +patcompcharsset(void) +{ + char *spp, *disp; + int i; + + /* Initialise enabled special characters */ + memcpy(zpc_special, zpc_chars, ZPC_COUNT); + /* Apply user disables from disable -p */ + for (i = 0, spp = zpc_special, disp = zpc_disables; + i < ZPC_COUNT; + i++, spp++, disp++) { + if (*disp) + *spp = Marker; + } + + if (!isset(EXTENDEDGLOB)) { + /* Extended glob characters are not active */ + zpc_special[ZPC_TILDE] = zpc_special[ZPC_HAT] = + zpc_special[ZPC_HASH] = Marker; + } + if (!isset(KSHGLOB)) { + /* + * Ksh glob characters are not active. + * * and ? are shared with normal globbing, but for their + * use here we are looking for a following Inpar. + */ + zpc_special[ZPC_KSH_QUEST] = zpc_special[ZPC_KSH_STAR] = + zpc_special[ZPC_KSH_PLUS] = zpc_special[ZPC_KSH_BANG] = + zpc_special[ZPC_KSH_BANG2] = zpc_special[ZPC_KSH_AT] = Marker; + } + /* + * Note that if we are using KSHGLOB, then we test for a following + * Inpar, not zpc_special[ZPC_INPAR]: the latter makes an Inpar on + * its own active. The zpc_special[ZPC_KSH_*] followed by any old Inpar + * discriminate ksh globbing. + */ + if (isset(SHGLOB)) { + /* + * Grouping and numeric ranges are not valid. + * We do allow alternation, however; it's needed for + * "case". This may not be entirely consistent. + * + * Don't disable Outpar: we may need to match the end of KSHGLOB + * parentheses and it would be difficult to tell them apart. + */ + zpc_special[ZPC_INPAR] = zpc_special[ZPC_INANG] = Marker; + } +} + +/* Called before parsing a set of file matchs to initialize flags */ + +/**/ +void +patcompstart(void) +{ + patcompcharsset(); + if (isset(CASEGLOB)) + patglobflags = 0; + else + patglobflags = GF_IGNCASE; + if (isset(MULTIBYTE)) + patglobflags |= GF_MULTIBYTE; +} + +/* + * Top level pattern compilation subroutine + * exp is a null-terminated, metafied string. + * inflags is an or of some PAT_* flags. + * endexp, if non-null, is set to a pointer to the end of the + * part of exp which was compiled. This is used when + * compiling patterns for directories which must be + * matched recursively. + */ + +/**/ +mod_export Patprog +patcompile(char *exp, int inflags, char **endexp) +{ + int flags = 0; + long len = 0; + long startoff; + Upat pscan; + 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); + + /* Allocate reasonable sized chunk if none, reduce size if too big */ + if (patalloc != P_DEF_ALLOC) + patout = (char *)zrealloc(patout, patalloc = P_DEF_ALLOC); + patcode = patout + startoff; + patsize = patcode - patout; + patstart = patparse = exp; + /* + * Note global patnpar numbers parentheses 1..9, while patnpar + * in struct is actual count of parentheses. + */ + patnpar = 1; + patflags = inflags & ~(PAT_PURES|PAT_HAS_EXCLUDP); + + if (!(patflags & PAT_FILE)) { + patcompcharsset(); + zpc_special[ZPC_SLASH] = Marker; + remnulargs(patparse); + if (isset(MULTIBYTE)) + patglobflags = GF_MULTIBYTE; + else + patglobflags = 0; + } + if (patflags & PAT_LCMATCHUC) + patglobflags |= GF_LCMATCHUC; + /* + * Have to be set now, since they get updated during compilation. + */ + ((Patprog)patout)->globflags = patglobflags; + + if (!(patflags & PAT_ANY)) { + /* Look for a really pure string, with no tokens at all. */ + if (!(patglobflags & ~GF_MULTIBYTE) +#ifdef __CYGWIN__ + /* + * If the OS treats files case-insensitively and we + * are looking at files, we don't need to use pattern + * matching to find the file. + */ + || (!(patglobflags & ~GF_IGNCASE) && (patflags & PAT_FILE)) +#endif + ) + { + /* + * Waah! I wish I understood this. + * Empty metafied strings have an initial Nularg. + * This never corresponds to a real character in + * a glob pattern or string, so skip it. + */ + if (*exp == Nularg) + exp++; + for (strp = exp; *strp && + (!(patflags & PAT_FILE) || *strp != '/') && !itok(*strp); + strp++) + ; + } + if (!strp || (*strp && *strp != '/')) { + /* No, do normal compilation. */ + strp = NULL; + if (patcompswitch(0, &flags) == 0) { + unqueue_signals(); + return NULL; + } + } else { + /* + * Yes, copy the string, and skip compilation altogether. + * Null terminate for the benefit of globbing. + * Leave metafied both for globbing and for our own + * efficiency. + */ + patparse = strp; + len = strp - exp; + patadd(exp, 0, len + 1, 0); + patout[startoff + len] = '\0'; + patflags |= PAT_PURES; + } + } + + /* end of compilation: safe to use pointers */ + p = (Patprog)patout; + p->startoff = startoff; + p->patstartch = '\0'; + p->globend = patglobflags; + p->flags = patflags; + p->mustoff = 0; + p->size = patsize; + p->patmlen = len; + p->patnpar = patnpar-1; + + if (!strp) { + pscan = (Upat)(patout + startoff); + + if (!(patflags & PAT_ANY) && P_OP(PATNEXT(pscan)) == P_END) { + /* only one top level choice */ + pscan = P_OPERAND(pscan); + + if (flags & P_PURESTR) { + /* + * The pattern can be matched with a simple strncmp/strcmp. + * Careful in case we've overwritten the node for the next ptr. + */ + char *dst = patout + startoff; + Upat next; + p->flags |= PAT_PURES; + for (; pscan; pscan = next) { + next = PATNEXT(pscan); + if (P_OP(pscan) == P_EXACTLY) { + char *opnd = P_LS_STR(pscan), *mtest; + long oplen = P_LS_LEN(pscan), ilen; + int nmeta = 0; + /* + * Unfortunately we unmetafied the string + * and we need to put any metacharacters + * back now we know it's a pure string. + * This shouldn't happen too often, it's + * just that there are some cases such + * as . and .. in files where we really + * need a pure string even if there are + * pattern characters flying around. + */ + for (mtest = opnd, ilen = oplen; ilen; + mtest++, ilen--) + if (imeta(*mtest)) + nmeta++; + if (nmeta) { + patadd(NULL, 0, nmeta, 0); + p = (Patprog)patout; + opnd = dupstring_wlen(opnd, oplen); + dst = patout + startoff; + } + + while (oplen--) { + if (imeta(*opnd)) { + *dst++ = Meta; + *dst++ = *opnd++ ^ 32; + } else { + *dst++ = *opnd++; + } + } + /* Only one string in a PAT_PURES, so now done. */ + break; + } + } + p->size = dst - patout; + /* patmlen is really strlen. We don't need a null. */ + p->patmlen = p->size - startoff; + } else { + /* starting point info */ + if (P_OP(pscan) == P_EXACTLY && !p->globflags && + P_LS_LEN(pscan)) + p->patstartch = *P_LS_STR(pscan); + /* + * Find the longest literal string in something expensive. + * This is itself not all that cheap if we have + * case-insensitive matching or approximation, so don't. + */ + if ((flags & P_HSTART) && !p->globflags) { + lng = NULL; + len = 0; + for (; pscan; pscan = PATNEXT(pscan)) + if (P_OP(pscan) == P_EXACTLY && + P_LS_LEN(pscan) >= len) { + lng = P_LS_STR(pscan); + len = P_LS_LEN(pscan); + } + if (lng) { + p->mustoff = lng - patout; + p->patmlen = len; + } + } + } + } + } + + /* + * The pattern was compiled in a fixed buffer: unless told otherwise, + * we stick the compiled pattern on the heap. This is necessary + * for files where we will often be compiling multiple segments at once. + * But if we get the ZDUP flag we always put it in zalloc()ed memory. + */ + if (patflags & PAT_ZDUP) { + Patprog newp = (Patprog)zalloc(patsize); + memcpy((char *)newp, (char *)p, patsize); + p = newp; + } else if (!(patflags & PAT_STATIC)) { + Patprog newp = (Patprog)zhalloc(patsize); + memcpy((char *)newp, (char *)p, patsize); + p = newp; + } + + if (endexp) + *endexp = patparse; + + unqueue_signals(); + return p; +} + +/* + * Main body or parenthesized subexpression in pattern + * Parenthesis (and any ksh_glob gubbins) will have been removed. + */ + +/**/ +static long +patcompswitch(int paren, int *flagp) +{ + long starter, br, ender, excsync = 0; + int parno = 0; + int flags, gfchanged = 0; + long savglobflags = (long)patglobflags; + Upat ptr; + + *flagp = 0; + + if (paren && (patglobflags & GF_BACKREF) && patnpar <= NSUBEXP) { + /* + * parenthesized: make an open node. + * We can only refer to the first nine parentheses. + * For any others, we just use P_OPEN on its own; there's + * no gain in arbitrarily limiting the number of parentheses. + */ + parno = patnpar++; + starter = patnode(P_OPEN + parno); + } else + starter = 0; + + br = patnode(P_BRANCH); + if (!patcompbranch(&flags, paren)) + return 0; + if (patglobflags != (int)savglobflags) + gfchanged++; + if (starter) + pattail(starter, br); + else + starter = br; + + *flagp |= flags & (P_HSTART|P_PURESTR); + + while (*patparse == zpc_chars[ZPC_BAR] || + (*patparse == zpc_special[ZPC_TILDE] && + (patparse[1] == '/' || + !memchr(zpc_special, patparse[1], ZPC_SEG_COUNT)))) { + int tilde = *patparse++ == zpc_special[ZPC_TILDE]; + long gfnode = 0, newbr; + + *flagp &= ~P_PURESTR; + + if (tilde) { + union upat up; + /* excsync remembers the P_EXCSYNC node before a chain of + * exclusions: all point back to this. only the + * original (non-excluded) branch gets a trailing P_EXCSYNC. + */ + if (!excsync) { + excsync = patnode(P_EXCSYNC); + patoptail(br, excsync); + } + /* + * By default, approximations are turned off in exclusions: + * we need to do this here as otherwise the code compiling + * the exclusion doesn't know if the flags have really + * changed if the error count gets restored. + */ + patglobflags &= ~0xff; + if (!(patflags & PAT_FILET) || paren) { + br = patnode(P_EXCLUDE); + } else { + /* + * At top level (paren == 0) in a file glob !(patflags + * &PAT_FILET) do the exclusion prepending the file path + * so far. We need to flag this to avoid unnecessarily + * copying the path. + */ + br = patnode(P_EXCLUDP); + patflags |= PAT_HAS_EXCLUDP; + } + up.p = NULL; + patadd((char *)&up, 0, sizeof(up), 0); + /* / is not treated as special if we are at top level */ + if (!paren && zpc_special[ZPC_SLASH] == '/') { + tilde++; + zpc_special[ZPC_SLASH] = Marker; + } + } else { + excsync = 0; + br = patnode(P_BRANCH); + /* + * The position of the following statements means globflags + * set in the main branch carry over to the exclusion. + */ + if (!paren) { + patglobflags = 0; + if (((Patprog)patout)->globflags) { + /* + * If at top level, we need to reinitialize flags to zero, + * since (#i)foo|bar only applies to foo and we stuck + * the #i into the global flags. + * We could have done it so that they only got set in the + * first branch, but it's quite convenient having any + * global flags set in the header and not buried in the + * pattern. (Or maybe it isn't and we should + * forget this bit and always stick in an explicit GFLAGS + * statement instead of using the header.) + * Also, this can't happen for file globs where there are + * no top-level |'s. + * + * No gfchanged, as nothing to follow branch at top + * level. + */ + union upat up; + gfnode = patnode(P_GFLAGS); + up.l = patglobflags; + patadd((char *)&up, 0, sizeof(union upat), 0); + } + } else { + patglobflags = (int)savglobflags; + } + } + newbr = patcompbranch(&flags, paren); + if (tilde == 2) { + /* restore special treatment of / */ + zpc_special[ZPC_SLASH] = '/'; + } + if (!newbr) + return 0; + if (gfnode) + pattail(gfnode, newbr); + if (!tilde && patglobflags != (int)savglobflags) + gfchanged++; + pattail(starter, br); + if (excsync) + patoptail(br, patnode(P_EXCEND)); + *flagp |= flags & P_HSTART; + } + + /* + * Make a closing node, hooking it to the end. + * Note that we can't optimize P_NOTHING out here, since another + * branch at that point would indicate the current choices continue, + * which they don't. + */ + ender = patnode(paren ? parno ? P_CLOSE+parno : P_NOTHING : P_END); + pattail(starter, ender); + + /* + * Hook the tails of the branches to the closing node, + * except for exclusions which terminate where they are. + */ + for (ptr = (Upat)patout + starter; ptr; ptr = PATNEXT(ptr)) + if (!P_ISEXCLUDE(ptr)) + patoptail(ptr-(Upat)patout, ender); + + /* check for proper termination */ + if ((paren && *patparse++ != Outpar) || + (!paren && *patparse && + !((patflags & PAT_FILE) && *patparse == '/'))) + return 0; + + if (paren && gfchanged) { + /* + * Restore old values of flags when leaving parentheses. + * gfchanged detects a change in any branch (except exclusions + * which are separate), since we need to emit this even if + * a later branch happened to put the flags back. + */ + pattail(ender, patnode(P_GFLAGS)); + patglobflags = (int)savglobflags; + patadd((char *)&savglobflags, 0, sizeof(long), 0); + } + + return starter; +} + +/* + * Compile something ended by Bar, Outpar, Tilde, or end of string. + * Note the BRANCH or EXCLUDE tag must already have been omitted: + * this returns the position of the operand of that. + */ + +/**/ +static long +patcompbranch(int *flagp, int paren) +{ + long chain, latest = 0, starter; + int flags = 0; + + *flagp = P_PURESTR; + + starter = chain = 0; + while (!memchr(zpc_special, *patparse, ZPC_SEG_COUNT) || + (*patparse == zpc_special[ZPC_TILDE] && patparse[1] != '/' && + memchr(zpc_special, patparse[1], ZPC_SEG_COUNT))) { + if ((*patparse == zpc_special[ZPC_INPAR] && + patparse[1] == zpc_special[ZPC_HASH]) || + (*patparse == zpc_special[ZPC_KSH_AT] && patparse[1] == Inpar && + patparse[2] == zpc_special[ZPC_HASH])) { + /* Globbing flags. */ + char *pp1 = patparse; + int oldglobflags = patglobflags, ignore; + long assert; + patparse += (*patparse == '@') ? 3 : 2; + if (!patgetglobflags(&patparse, &assert, &ignore)) + return 0; + if (!ignore) { + if (assert) { + /* + * Start/end assertion looking like flags, but + * actually handled as a normal node + */ + latest = patnode(assert); + flags = 0; + } else { + if (pp1 == patstart) { + /* Right at start of pattern, the simplest case. + * Put them into the flags and don't emit anything. + */ + ((Patprog)patout)->globflags = patglobflags; + continue; + } else if (!*patparse) { + /* Right at the end, so just leave the flags for + * the next Patprog in the chain to pick up. + */ + break; + } + /* + * Otherwise, we have to stick them in as a pattern + * matching nothing. + */ + if (oldglobflags != patglobflags) { + /* Flags changed */ + union upat up; + latest = patnode(P_GFLAGS); + up.l = patglobflags; + patadd((char *)&up, 0, sizeof(union upat), 0); + } else { + /* No effect. */ + continue; + } + } + } else if (!*patparse) + break; + else + continue; + } else if (*patparse == zpc_special[ZPC_HAT]) { + /* + * ^pat: anything but pat. For proper backtracking, + * etc., we turn this into (*~pat), except without the + * parentheses. + */ + patparse++; + latest = patcompnot(0, &flags); + } else + latest = patcomppiece(&flags, paren); + if (!latest) + return 0; + if (!starter) + starter = latest; + if (!(flags & P_PURESTR)) + *flagp &= ~P_PURESTR; + if (!chain) + *flagp |= flags & P_HSTART; + else + pattail(chain, latest); + chain = latest; + } + /* check if there was nothing in the loop, i.e. () */ + if (!chain) + starter = patnode(P_NOTHING); + + return starter; +} + +/* get glob flags, return 1 for success, 0 for failure */ + +/**/ +int +patgetglobflags(char **strp, long *assertp, int *ignore) +{ + char *nptr, *ptr = *strp; + zlong ret; + + *assertp = 0; + *ignore = 1; + /* (#X): assumes we are still positioned on the first X */ + for (; *ptr && *ptr != Outpar; ptr++) { + if (*ptr == 'q') { + /* Glob qualifiers, ignored in pattern code */ + while (*ptr && *ptr != Outpar) + ptr++; + break; + } else { + *ignore = 0; + switch (*ptr) { + case 'a': + /* Approximate matching, max no. of errors follows */ + ret = zstrtol(++ptr, &nptr, 10); + /* + * We can't have more than 254, because we need 255 to + * mark 254 errors in wbranch and exclude sync strings + * (hypothetically --- hope no-one tries it). + */ + if (ret < 0 || ret > 254 || ptr == nptr) + return 0; + patglobflags = (patglobflags & ~0xff) | (ret & 0xff); + ptr = nptr-1; + break; + + case 'l': + /* Lowercase in pattern matches lower or upper in target */ + patglobflags = (patglobflags & ~GF_IGNCASE) | GF_LCMATCHUC; + break; + + case 'i': + /* Fully case insensitive */ + patglobflags = (patglobflags & ~GF_LCMATCHUC) | GF_IGNCASE; + break; + + case 'I': + /* Restore case sensitivity */ + patglobflags &= ~(GF_LCMATCHUC|GF_IGNCASE); + break; + + case 'b': + /* Make backreferences */ + patglobflags |= GF_BACKREF; + break; + + case 'B': + /* Don't make backreferences */ + patglobflags &= ~GF_BACKREF; + break; + + case 'm': + /* Make references to complete match */ + patglobflags |= GF_MATCHREF; + break; + + case 'M': + /* Don't */ + patglobflags &= ~GF_MATCHREF; + break; + + case 's': + *assertp = P_ISSTART; + break; + + case 'e': + *assertp = P_ISEND; + break; + + case 'u': + patglobflags |= GF_MULTIBYTE; + break; + + case 'U': + patglobflags &= ~GF_MULTIBYTE; + break; + + default: + return 0; + } + } + } + if (*ptr != Outpar) + return 0; + /* Start/end assertions must appear on their own. */ + if (*assertp && (*strp)[1] != Outpar) + return 0; + *strp = ptr + 1; + return 1; +} + + +static const char *colon_stuffs[] = { + "alpha", "alnum", "ascii", "blank", "cntrl", "digit", "graph", + "lower", "print", "punct", "space", "upper", "xdigit", "IDENT", + "IFS", "IFSSPACE", "WORD", "INCOMPLETE", "INVALID", NULL +}; + +/* + * Handle the guts of a [:stuff:] character class element. + * start is the beginning of "stuff" and len is its length. + * This code is exported for the benefit of completion matching. + */ + +/**/ +mod_export int +range_type(char *start, int len) +{ + const char **csp; + + for (csp = colon_stuffs; *csp; csp++) { + if (strlen(*csp) == len && !strncmp(start, *csp, len)) + return (csp - colon_stuffs) + PP_FIRST; + } + + return PP_UNKWN; +} + + +/* + * Convert the contents of a [...] or [^...] expression (just the + * ... part) back into a string. This is used by compfiles -p/-P + * for some reason. The compiled form (a metafied string) is + * passed in rangestr. + * + * If outstr is non-NULL the compiled form is placed there. It + * must be sufficiently long. A terminating NULL is appended. + * + * Return the length required, not including the terminating NULL. + * + * TODO: this is non-multibyte for now. It will need to be defined + * appropriately with MULTIBYTE_SUPPORT when the completion matching + * code catches up. + */ + +/**/ +mod_export int +pattern_range_to_string(char *rangestr, char *outstr) +{ + int len = 0; + + while (*rangestr) { + if (imeta(STOUC(*rangestr))) { + int swtype = STOUC(*rangestr) - STOUC(Meta); + + if (swtype == 0) { + /* Ordindary metafied character */ + if (outstr) + { + *outstr++ = Meta; + *outstr++ = rangestr[1] ^ 32; + } + len += 2; + rangestr += 2; + } else if (swtype == PP_RANGE) { + /* X-Y range */ + int i; + + for (i = 0; i < 2; i++) { + if (*rangestr == Meta) { + if (outstr) { + *outstr++ = Meta; + *outstr++ = rangestr[1]; + } + len += 2; + rangestr += 2; + } else { + if (outstr) + *outstr++ = *rangestr; + len++; + rangestr++; + } + + if (i == 0) { + if (outstr) + *outstr++ = '-'; + len++; + } + } + } else if (swtype >= PP_FIRST && swtype <= PP_LAST) { + /* [:stuff:]; we need to output [: and :] */ + const char *found = colon_stuffs[swtype - PP_FIRST]; + int newlen = strlen(found); + if (outstr) { + strcpy(outstr, "[:"); + outstr += 2; + memcpy(outstr, found, newlen); + outstr += newlen; + strcpy(outstr, ":]"); + outstr += 2; + } + len += newlen + 4; + rangestr++; + } else { + /* shouldn't happen */ + DPUTS(1, "BUG: unknown PP_ code in pattern range"); + rangestr++; + } + } else { + /* ordinary character, guaranteed no Meta handling needed */ + if (outstr) + *outstr++ = *rangestr; + len++; + rangestr++; + } + } + + if (outstr) + *outstr = '\0'; + return len; +} + +/* + * compile a chunk such as a literal string or a [...] followed + * by a possible hash operator + */ + +/**/ +static long +patcomppiece(int *flagp, int paren) +{ + long starter = 0, next, op, opnd; + int flags, flags2, kshchar, len, ch, patch, nmeta; + int hash, count; + union upat up; + char *nptr, *str0, *ptr, *patprev; + zrange_t from, to; + char *charstart; + + flags = 0; + str0 = patprev = patparse; + for (;;) { + /* + * Check if we have a string. First, we need to make sure + * the string doesn't introduce a ksh-like parenthesized expression. + */ + kshchar = '\0'; + if (*patparse && patparse[1] == Inpar) { + if (*patparse == zpc_special[ZPC_KSH_PLUS]) + kshchar = STOUC('+'); + else if (*patparse == zpc_special[ZPC_KSH_BANG]) + kshchar = STOUC('!'); + else if (*patparse == zpc_special[ZPC_KSH_BANG2]) + kshchar = STOUC('!'); + else if (*patparse == zpc_special[ZPC_KSH_AT]) + kshchar = STOUC('@'); + else if (*patparse == zpc_special[ZPC_KSH_STAR]) + kshchar = STOUC('*'); + else if (*patparse == zpc_special[ZPC_KSH_QUEST]) + kshchar = STOUC('?'); + } + + /* + * If '(' is disabled as a pattern char, allow ')' as + * an ordinary string character if there are no parentheses to + * close. Don't allow it otherwise, it changes the syntax. + */ + if (zpc_special[ZPC_INPAR] != Marker || *patparse != Outpar || + paren) { + /* + * End of string (or no string at all) if ksh-type parentheses, + * or special character, unless that character is a tilde and + * the character following is an end-of-segment character. Thus + * tildes are not special if there is nothing following to + * be excluded. + * + * Don't look for X()-style kshglobs at this point; we've + * checked above for the case with parentheses and we don't + * want to match without parentheses. + */ + if (kshchar || + (memchr(zpc_special, *patparse, ZPC_NO_KSH_GLOB) && + (*patparse != zpc_special[ZPC_TILDE] || + patparse[1] == '/' || + !memchr(zpc_special, patparse[1], ZPC_SEG_COUNT)))) { + break; + } + } + + /* Remember the previous character for backtracking */ + patprev = patparse; + METACHARINC(patparse); + } + + if (patparse > str0) { + long slen = patparse - str0; + int morelen; + + /* Ordinary string: cancel kshchar lookahead */ + kshchar = '\0'; + /* + * Assume it matches a simple string until we find otherwise. + */ + flags |= P_PURESTR; + DPUTS(patparse == str0, "BUG: matched nothing in patcomppiece."); + /* more than one character matched? */ + morelen = (patprev > str0); + /* + * If we have more than one character, a following hash + * or (#c...) only applies to the last, so backtrack one character. + */ + if ((*patparse == zpc_special[ZPC_HASH] || + (*patparse == zpc_special[ZPC_INPAR] && + patparse[1] == zpc_special[ZPC_HASH] && + patparse[2] == 'c') || + (*patparse == zpc_special[ZPC_KSH_AT] && + patparse[1] == Inpar && + patparse[2] == zpc_special[ZPC_HASH] && + patparse[3] == 'c')) && morelen) + patparse = patprev; + /* + * If len is 1, we can't have an active # following, so doesn't + * matter that we don't make X in `XX#' simple. + */ + if (!morelen) + flags |= P_SIMPLE; + starter = patnode(P_EXACTLY); + + /* Get length of string without metafication. */ + nmeta = 0; + /* inherited from domatch, but why, exactly? */ + if (*str0 == Nularg) + str0++; + for (ptr = str0; ptr < patparse; ptr++) { + if (*ptr == Meta) { + nmeta++; + ptr++; + } + } + slen = (patparse - str0) - nmeta; + /* First add length, which is a long */ + patadd((char *)&slen, 0, sizeof(long), 0); + /* + * Then the string, not null terminated. + * Unmetafy and untokenize; pass the final length, + * which is what we need to allocate, i.e. not including + * a count for each Meta in the string. + */ + patadd(str0, 0, slen, PA_UNMETA); + nptr = P_LS_STR((Upat)patout + starter); + /* + * It's much simpler to turn off pure string mode for + * any case-insensitive or approximate matching; usually, + * that is correct, or they wouldn't have been turned on. + * However, we need to make sure we match a "." or ".." + * in a file name as a pure string. There's a minor bug + * that this will also apply to something like + * ..(#a1).. (i.e. the (#a1) has no effect), but if you're + * going to write funny patterns, you get no sympathy from me. + */ + if (patglobflags & +#ifdef __CYGWIN__ + /* + * As above: don't use pattern matching for files + * just because of case insensitivity if file system + * is known to be case insensitive. + * + * This is known to be necessary in at least one case: + * if "mount -c /" is in effect, so that drives appear + * directly under / instead of the usual /cygdrive, they + * aren't shown by readdir(). So it's vital we don't use + * globbing to find "/c", since that'll fail. + */ + ((patflags & PAT_FILE) ? + (0xFF|GF_LCMATCHUC) : + (0xFF|GF_LCMATCHUC|GF_IGNCASE)) +#else + (0xFF|GF_LCMATCHUC|GF_IGNCASE) +#endif + ) { + if (!(patflags & PAT_FILE)) + flags &= ~P_PURESTR; + else if (!(nptr[0] == '.' && + (slen == 1 || (nptr[1] == '.' && slen == 2)))) + flags &= ~P_PURESTR; + } + } else { + if (kshchar) + patparse++; + + patch = *patparse; + METACHARINC(patparse); + switch(patch) { + case Quest: + DPUTS(zpc_special[ZPC_QUEST] == Marker, + "Treating '?' as pattern character although disabled"); + flags |= P_SIMPLE; + starter = patnode(P_ANY); + break; + case Star: + DPUTS(zpc_special[ZPC_STAR] == Marker, + "Treating '*' as pattern character although disabled"); + /* kshchar is used as a sign that we can't have #'s. */ + kshchar = -1; + starter = patnode(P_STAR); + break; + case Inbrack: + DPUTS(zpc_special[ZPC_INBRACK] == Marker, + "Treating '[' as pattern character although disabled"); + flags |= P_SIMPLE; + if (*patparse == Hat || *patparse == Bang) { + patparse++; + starter = patnode(P_ANYBUT); + } else + starter = patnode(P_ANYOF); + /* + * []...] means match a "]" or other included characters. + * However, to be a bit helpful and for compatibility + * with other shells, don't take in that sense if + * there's no further "]". That's still imperfect, + * but it's all we can do --- we're required to + * treat [$var]*[$var]with empty var as [ ... ] + * containing "]*[". + */ + if (*patparse == Outbrack && strchr(patparse+1, Outbrack)) { + patparse++; + patadd(NULL, ']', 1, PA_NOALIGN); + } + while (*patparse && *patparse != Outbrack) { + /* Meta is not a token */ + if (*patparse == Inbrack && patparse[1] == ':' && + (nptr = strchr(patparse+2, ':')) && + nptr[1] == Outbrack) { + /* Posix range. */ + patparse += 2; + len = nptr - patparse; + ch = range_type(patparse, len); + patparse = nptr + 2; + if (ch != PP_UNKWN) + patadd(NULL, STOUC(Meta) + ch, 1, PA_NOALIGN); + continue; + } + charstart = patparse; + METACHARINC(patparse); + + if (*patparse == Dash && patparse[1] && + patparse[1] != Outbrack) { + patadd(NULL, STOUC(Meta)+PP_RANGE, 1, PA_NOALIGN); + if (itok(*charstart)) { + patadd(0, STOUC(ztokens[*charstart - Pound]), 1, + PA_NOALIGN); + } else { + patadd(charstart, 0, patparse-charstart, PA_NOALIGN); + } + charstart = ++patparse; /* skip Dash token */ + METACHARINC(patparse); + } + if (itok(*charstart)) { + patadd(0, STOUC(ztokens[*charstart - Pound]), 1, + PA_NOALIGN); + } else { + patadd(charstart, 0, patparse-charstart, PA_NOALIGN); + } + } + if (*patparse != Outbrack) + return 0; + patparse++; + /* terminate null string and fix alignment */ + patadd(NULL, 0, 1, 0); + break; + case Inpar: + DPUTS(!kshchar && zpc_special[ZPC_INPAR] == Marker, + "Treating '(' as pattern character although disabled"); + DPUTS(isset(SHGLOB) && !kshchar, + "Treating bare '(' as pattern character with SHGLOB"); + if (kshchar == '!') { + /* This is nasty, we should really either handle all + * kshglobbing below or here. But most of the + * others look like non-ksh patterns, while this one + * doesn't, so we handle it here and leave the rest. + * We treat it like an extendedglob ^, except that + * it goes into parentheses. + * + * If we did do kshglob here, we could support + * the old behaviour that things like !(foo)## + * work, but it makes the code more complicated at + * the expense of allowing the user to do things + * they shouldn't. + */ + if (!(starter = patcompnot(1, &flags2))) + return 0; + } else if (!(starter = patcompswitch(1, &flags2))) + return 0; + flags |= flags2 & P_HSTART; + break; + case Inang: + /* Numeric glob */ + DPUTS(zpc_special[ZPC_INANG] == Marker, + "Treating '<' as pattern character although disabled"); + DPUTS(isset(SHGLOB), "Treating <..> as numeric range with SHGLOB"); + len = 0; /* beginning present 1, end present 2 */ + if (idigit(*patparse)) { + from = (zrange_t) zstrtol((char *)patparse, + (char **)&nptr, 10); + patparse = nptr; + len |= 1; + } + DPUTS(!IS_DASH(*patparse), "BUG: - missing from numeric glob"); + patparse++; + if (idigit(*patparse)) { + to = (zrange_t) zstrtol((char *)patparse, + (char **)&nptr, 10); + patparse = nptr; + len |= 2; + } + if (*patparse != Outang) + return 0; + patparse++; + switch(len) { + case 3: + starter = patnode(P_NUMRNG); + patadd((char *)&from, 0, sizeof(from), 0); + patadd((char *)&to, 0, sizeof(to), 0); + break; + case 2: + starter = patnode(P_NUMTO); + patadd((char *)&to, 0, sizeof(to), 0); + break; + case 1: + starter = patnode(P_NUMFROM); + patadd((char *)&from, 0, sizeof(from), 0); + break; + case 0: + starter = patnode(P_NUMANY); + break; + } + /* This can't be simple, because it isn't. + * Mention in manual that matching digits with [...] + * is more efficient. + */ + break; + case Pound: + DPUTS(zpc_special[ZPC_HASH] == Marker, + "Treating '#' as pattern character although disabled"); + DPUTS(!isset(EXTENDEDGLOB), "BUG: # not treated as string"); + /* + * A hash here is an error; it should follow something + * repeatable. + */ + return 0; + break; + case Bnullkeep: + /* + * Marker for restoring a backslash in output: + * does not match a character. + */ + next = patcomppiece(flagp, paren); + /* + * Can't match a pure string since we need to do this + * as multiple chunks. + */ + *flagp &= ~P_PURESTR; + return next; + break; +#ifdef DEBUG + default: + dputs("BUG: character not handled in patcomppiece"); + return 0; + break; +#endif + } + } + + count = 0; + if (!(hash = (*patparse == zpc_special[ZPC_HASH])) && + !(count = ((*patparse == zpc_special[ZPC_INPAR] && + patparse[1] == zpc_special[ZPC_HASH] && + patparse[2] == 'c') || + (*patparse == zpc_special[ZPC_KSH_AT] && + patparse[1] == Inpar && + patparse[2] == zpc_special[ZPC_HASH] && + patparse[3] == 'c'))) && + (kshchar <= 0 || kshchar == '@' || kshchar == '!')) { + *flagp = flags; + return starter; + } + + /* too much at once doesn't currently work */ + if (kshchar && (hash || count)) + return 0; + + if (kshchar == '*') { + op = P_ONEHASH; + *flagp = P_HSTART; + } else if (kshchar == '+') { + op = P_TWOHASH; + *flagp = P_HSTART; + } else if (kshchar == '?') { + op = 0; + *flagp = 0; + } else if (count) { + op = P_COUNT; + patparse += 3; + *flagp = P_HSTART; + } else if (*++patparse == zpc_special[ZPC_HASH]) { + op = P_TWOHASH; + patparse++; + *flagp = P_HSTART; + } else { + op = P_ONEHASH; + *flagp = P_HSTART; + } + + /* + * Note optimizations with pointers into P_NOTHING branches: some + * should logically point to next node after current piece. + * + * Backtracking is also encoded in a slightly obscure way: the + * code emitted ensures we test the non-empty branch of complex + * patterns before the empty branch on each repetition. Hence + * each time we fail on a non-empty branch, we try the empty branch, + * which is equivalent to backtracking. + */ + if (op == P_COUNT) { + /* (#cN,M) */ + union upat countargs[P_CT_OPERAND]; + char *opp = patparse; + + countargs[0].l = P_COUNT; + countargs[P_CT_CURRENT].l = 0L; + countargs[P_CT_MIN].l = (long)zstrtol(patparse, &patparse, 10); + if (patparse == opp) { + /* missing number treated as zero */ + countargs[P_CT_MIN].l = 0L; + } + if (*patparse != ',' && *patparse != Comma) { + /* either max = min or error */ + if (*patparse != Outpar) + return 0; + countargs[P_CT_MAX].l = countargs[P_CT_MIN].l; + } else { + opp = ++patparse; + countargs[P_CT_MAX].l = (long)zstrtol(patparse, &patparse, 10); + if (*patparse != Outpar) + return 0; + if (patparse == opp) { + /* missing number treated as infinity: record as -1 */ + countargs[P_CT_MAX].l = -1L; + } + } + patparse++; + countargs[P_CT_PTR].p = NULL; + /* Mark this chain as a min/max count... */ + patinsert(P_COUNTSTART, starter, (char *)countargs, sizeof(countargs)); + /* + * The next of the operand is a loop back to the P_COUNT. This is + * how we get recursion for the count. We don't loop back to + * the P_COUNTSTART; that's used for initialising the count + * and saving and restoring the count for any enclosing use + * of the match. + */ + opnd = P_OPERAND(starter) + P_CT_OPERAND; + pattail(opnd, patnode(P_BACK)); + pattail(opnd, P_OPERAND(starter)); + /* + * The next of the counter operators is what follows the + * closure. + * This handles matching of the tail. + */ + next = patnode(P_NOTHING); + pattail(starter, next); + pattail(P_OPERAND(starter), next); + } else if ((flags & P_SIMPLE) && (op == P_ONEHASH || op == P_TWOHASH) && + P_OP((Upat)patout+starter) == P_ANY) { + /* Optimize ?# to *. Silly thing to do, since who would use + * use ?# ? But it makes the later code shorter. + */ + Upat uptr = (Upat)patout + starter; + if (op == P_TWOHASH) { + /* ?## becomes ?* */ + uptr->l = (uptr->l & ~0xff) | P_ANY; + pattail(starter, patnode(P_STAR)); + } else { + uptr->l = (uptr->l & ~0xff) | P_STAR; + } + } else if ((flags & P_SIMPLE) && op && !(patglobflags & 0xff)) { + /* Simplify, but not if we need to look for approximations. */ + patinsert(op, starter, NULL, 0); + } else if (op == P_ONEHASH) { + /* Emit x# as (x&|), where & means "self". */ + up.p = NULL; + patinsert(P_WBRANCH, starter, (char *)&up, sizeof(up)); + /* Either x */ + patoptail(starter, patnode(P_BACK)); /* and loop */ + patoptail(starter, starter); /* back */ + pattail(starter, patnode(P_BRANCH)); /* or */ + pattail(starter, patnode(P_NOTHING)); /* null. */ + } else if (op == P_TWOHASH) { + /* Emit x## as x(&|) where & means "self". */ + next = patnode(P_WBRANCH); /* Either */ + up.p = NULL; + patadd((char *)&up, 0, sizeof(up), 0); + pattail(starter, next); + pattail(patnode(P_BACK), starter); /* loop back */ + pattail(next, patnode(P_BRANCH)); /* or */ + pattail(starter, patnode(P_NOTHING)); /* null. */ + } else if (kshchar == '?') { + /* Emit ?(x) as (x|) */ + patinsert(P_BRANCH, starter, NULL, 0); /* Either x */ + pattail(starter, patnode(P_BRANCH)); /* or */ + next = patnode(P_NOTHING); /* null */ + pattail(starter, next); + patoptail(starter, next); + } + if (*patparse == zpc_special[ZPC_HASH]) + return 0; + + return starter; +} + +/* + * Turn a ^foo (paren = 0) or !(foo) (paren = 1) into *~foo with + * parentheses if necessary. As you see, that's really quite easy. + */ + +/**/ +static long +patcompnot(int paren, int *flagsp) +{ + union upat up; + long excsync, br, excl, n, starter; + int dummy; + + /* Here, we're matching a star at the start. */ + *flagsp = P_HSTART; + + starter = patnode(P_BRANCH); + br = patnode(P_STAR); + excsync = patnode(P_EXCSYNC); + pattail(br, excsync); + pattail(starter, excl = patnode(P_EXCLUDE)); + up.p = NULL; + patadd((char *)&up, 0, sizeof(up), 0); + if (!(br = (paren ? patcompswitch(1, &dummy) : patcompbranch(&dummy, 0)))) + return 0; + pattail(br, patnode(P_EXCEND)); + n = patnode(P_NOTHING); /* just so much easier */ + pattail(excsync, n); + pattail(excl, n); + + return starter; +} + +/* Emit a node */ + +/**/ +static long +patnode(long op) +{ + long starter = (Upat)patcode - (Upat)patout; + union upat up; + + up.l = op; + patadd((char *)&up, 0, sizeof(union upat), 0); + return starter; +} + +/* + * insert an operator in front of an already emitted operand: + * we relocate the operand. there had better be nothing else after. + */ + +/**/ +static void +patinsert(long op, int opnd, char *xtra, int sz) +{ + char *src, *dst, *opdst; + union upat buf, *lptr; + + buf.l = 0; + patadd((char *)&buf, 0, sizeof(buf), 0); + if (sz) + patadd(xtra, 0, sz, 0); + src = patcode - sizeof(union upat) - sz; + dst = patcode; + opdst = patout + opnd * sizeof(union upat); + while (src > opdst) + *--dst = *--src; + + /* A cast can't be an lvalue */ + lptr = (Upat)opdst; + lptr->l = op; + opdst += sizeof(union upat); + while (sz--) + *opdst++ = *xtra++; +} + +/* set the 'next' pointer at the end of a node chain */ + +/**/ +static void +pattail(long p, long val) +{ + Upat scan, temp; + long offset; + + scan = (Upat)patout + p; + for (;;) { + if (!(temp = PATNEXT(scan))) + break; + scan = temp; + } + + offset = (P_OP(scan) == P_BACK) + ? (scan - (Upat)patout) - val : val - (scan - (Upat)patout); + + scan->l |= offset << 8; +} + +/* do pattail, but on operand of first argument; nop if operandless */ + +/**/ +static void +patoptail(long p, long val) +{ + Upat ptr = (Upat)patout + p; + int op = P_OP(ptr); + if (!p || !P_ISBRANCH(ptr)) + return; + if (op == P_BRANCH) + pattail(P_OPERAND(p), val); + else + pattail(P_OPERAND(p) + 1, val); +} + + +/* + * Run a pattern. + */ +struct rpat { + char *patinstart; /* Start of input string */ + char *patinend; /* End of input string */ + char *patinput; /* String input pointer */ + char *patinpath; /* Full path for use with ~ exclusions */ + int patinlen; /* Length of last successful match. + * Includes count of Meta characters. + */ + + char *patbeginp[NSUBEXP]; /* Pointer to backref beginnings */ + char *patendp[NSUBEXP]; /* Pointer to backref ends */ + int parsfound; /* parentheses (with backrefs) found */ + + int globdots; /* Glob initial dots? */ +}; + +static struct rpat pattrystate; + +#define patinstart (pattrystate.patinstart) +#define patinend (pattrystate.patinend) +#define patinput (pattrystate.patinput) +#define patinpath (pattrystate.patinpath) +#define patinlen (pattrystate.patinlen) +#define patbeginp (pattrystate.patbeginp) +#define patendp (pattrystate.patendp) +#define parsfound (pattrystate.parsfound) +#define globdots (pattrystate.globdots) + + +/* + * Character functions operating on unmetafied strings. + */ +#ifdef MULTIBYTE_SUPPORT + +/* Get a character from the start point in a string */ +#define CHARREF(x, y) charref((x), (y), (int *)NULL) +static wchar_t +charref(char *x, char *y, int *zmb_ind) +{ + wchar_t wc; + size_t ret; + + if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*x) & 0x80)) + return (wchar_t) STOUC(*x); + + ret = mbrtowc(&wc, x, y-x, &shiftstate); + + if (ret == MB_INVALID || ret == MB_INCOMPLETE) { + /* Error. */ + /* Reset the shift state for next time. */ + memset(&shiftstate, 0, sizeof(shiftstate)); + if (zmb_ind) + *zmb_ind = (ret == MB_INVALID) ? ZMB_INVALID : ZMB_INCOMPLETE; + return WCHAR_INVALID(*x); + } + + if (zmb_ind) + *zmb_ind = ZMB_VALID; + return wc; +} + +/* Get a pointer to the next character */ +#define CHARNEXT(x, y) charnext((x), (y)) +static char * +charnext(char *x, char *y) +{ + wchar_t wc; + size_t ret; + + if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*x) & 0x80)) + return x + 1; + + ret = mbrtowc(&wc, x, y-x, &shiftstate); + + if (ret == MB_INVALID || ret == MB_INCOMPLETE) { + /* Error. Treat as single byte. */ + /* Reset the shift state for next time. */ + memset(&shiftstate, 0, sizeof(shiftstate)); + return x + 1; + } + + /* Nulls here are normal characters */ + return x + (ret ? ret : 1); +} + +/* Increment a pointer past the current character. */ +#define CHARINC(x, y) ((x) = charnext((x), (y))) + + +/* Get a character and increment */ +#define CHARREFINC(x, y, z) charrefinc(&(x), (y), (z)) +static wchar_t +charrefinc(char **x, char *y, int *z) +{ + wchar_t wc; + size_t ret; + + if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(**x) & 0x80)) + return (wchar_t) STOUC(*(*x)++); + + ret = mbrtowc(&wc, *x, y-*x, &shiftstate); + + if (ret == MB_INVALID || ret == MB_INCOMPLETE) { + /* Error. Treat as single byte, but flag. */ + *z = 1; + /* Reset the shift state for next time. */ + memset(&shiftstate, 0, sizeof(shiftstate)); + return WCHAR_INVALID(*(*x)++); + } + + /* Nulls here are normal characters */ + *x += ret ? ret : 1; + + return wc; +} + + +/* + * Counter the number of characters between two pointers, smaller first + * + * This is used when setting values in parameters, so we obey + * the MULTIBYTE option (even if it's been overridden locally). + */ +#define CHARSUB(x,y) charsub(x, y) +static ptrdiff_t +charsub(char *x, char *y) +{ + ptrdiff_t res = 0; + size_t ret; + wchar_t wc; + + if (!isset(MULTIBYTE)) + return y - x; + + while (x < y) { + ret = mbrtowc(&wc, x, y-x, &shiftstate); + + if (ret == MB_INVALID || ret == MB_INCOMPLETE) { + /* Error. Treat remainder as single characters */ + return res + (y - x); + } + + /* Treat nulls as normal characters */ + if (!ret) + ret = 1; + res++; + x += ret; + } + + return res; +} + +#else /* no MULTIBYTE_SUPPORT */ + +/* Get a character from the start point in a string */ +#define CHARREF(x, y) (STOUC(*(x))) +/* Get a pointer to the next character */ +#define CHARNEXT(x, y) ((x)+1) +/* Increment a pointer past the current character. */ +#define CHARINC(x, y) ((x)++) +/* Get a character and increment */ +#define CHARREFINC(x, y, z) (STOUC(*(x)++)) +/* Counter the number of characters between two pointers, smaller first */ +#define CHARSUB(x,y) ((y) - (x)) + +#endif /* MULTIBYTE_SUPPORT */ + +/* + * The following need to be accessed in the globbing scanner for + * a multi-component file path. See horror story in glob.c. + */ +/**/ +int errsfound; /* Total error count so far */ + +/**/ +int forceerrs; /* Forced maximum error count */ + +/**/ +void +pattrystart(void) +{ + forceerrs = -1; + errsfound = 0; +} + +/* + * Fix up string length stuff. + * + * If we call patallocstr() with "force" to set things up early, it's + * done there, else it's done in pattryrefs(). The reason for the + * difference is in the latter case we may not be relying on + * patallocstr() having an effect. + */ + +/**/ +static void +patmungestring(char **string, int *stringlen, int *unmetalenin) +{ + /* + * Special signalling of empty tokenised string. + */ + if (*stringlen > 0 && **string == Nularg) { + (*string)++; + /* + * If we don't have an unmetafied length + * and need it (we may not) we'll get it later. + */ + if (*unmetalenin > 0) + (*unmetalenin)--; + if (*stringlen > 0) + (*stringlen)--; + } + + /* Ensure we have a metafied length */ + if (*stringlen < 0) + *stringlen = strlen(*string); +} + +/* + * Allocate memeory for pattern match. Note this is specific to use + * of pattern *and* trial string. + * + * Unmetafy a trial string for use in pattern matching, if needed. + * + * If it is needed, returns a heap allocated string; if not needed, + * returns NULL. + * + * prog is the pattern to be executed. + * string is the metafied trial string. + * stringlen is it's length; it will be calculated if it's negative + * (this is a simple strlen()). + * unmetalen is the unmetafied length of the string, may be -1. + * force is 1 if we always unmetafy: this is useful if we are going + * to try again with different versions of the string. If this is + * called from pattryrefs() we don't force unmetafication as it won't + * be optimal. This option should be used if the resulting + * patstralloc is going to be passed to pattrylen() / pattryrefs(). + * In patstralloc (supplied by caller, must last until last pattry is done) + * unmetalen is the unmetafied length of the string; it will be + * calculated if the input value is negative. + * unmetalenp is the umetafied length of a path segment preceeding + * the trial string needed for file mananagement; it is calculated as + * needed so does not need to be initialised. + * alloced is the memory allocated on the heap --- same as return value from + * function. + */ +/**/ +mod_export +char *patallocstr(Patprog prog, char *string, int stringlen, int unmetalen, + int force, Patstralloc patstralloc) +{ + int needfullpath; + + if (force) + patmungestring(&string, &stringlen, &unmetalen); + + /* + * For a top-level ~-exclusion, we will need the full + * path to exclude, so copy the path so far and append the + * current test string. + */ + needfullpath = (prog->flags & PAT_HAS_EXCLUDP) && pathpos; + + /* Get the length of the full string when unmetafied. */ + if (unmetalen < 0) + patstralloc->unmetalen = ztrsub(string + stringlen, string); + else + patstralloc->unmetalen = unmetalen; + if (needfullpath) { + patstralloc->unmetalenp = ztrsub(pathbuf + pathpos, pathbuf); + if (!patstralloc->unmetalenp) + needfullpath = 0; + } else + patstralloc->unmetalenp = 0; + /* Initialise cache area */ + patstralloc->progstrunmeta = NULL; + patstralloc->progstrunmetalen = 0; + + DPUTS(needfullpath && (prog->flags & (PAT_PURES|PAT_ANY)), + "rum sort of file exclusion"); + /* + * Partly for efficiency, and partly for the convenience of + * globbing, we don't unmetafy pure string patterns, and + * there's no reason to if the pattern is just a *. + */ + if (force || + (!(prog->flags & (PAT_PURES|PAT_ANY)) + && (needfullpath || patstralloc->unmetalen != stringlen))) { + /* + * We need to copy if we need to prepend the path so far + * (in which case we copy both chunks), or if we have + * Meta characters. + */ + char *dst, *ptr; + int i, icopy, ncopy; + + dst = patstralloc->alloced = + zhalloc(patstralloc->unmetalen + patstralloc->unmetalenp); + + if (needfullpath) { + /* loop twice, copy path buffer first time */ + ptr = pathbuf; + ncopy = patstralloc->unmetalenp; + } else { + /* just loop once, copy string with unmetafication */ + ptr = string; + ncopy = patstralloc->unmetalen; + } + for (icopy = 0; icopy < 2; icopy++) { + for (i = 0; i < ncopy; i++) { + if (*ptr == Meta) { + ptr++; + *dst++ = *ptr++ ^ 32; + } else { + *dst++ = *ptr++; + } + } + if (!needfullpath) + break; + /* next time append test string to path so far */ + ptr = string; + ncopy = patstralloc->unmetalen; + } + } + else + { + patstralloc->alloced = NULL; + } + + return patstralloc->alloced; +} + + +/* + * Test prog against null-terminated, metafied string. + */ + +/**/ +mod_export int +pattry(Patprog prog, char *string) +{ + return pattryrefs(prog, string, -1, -1, NULL, 0, NULL, NULL, NULL); +} + +/* + * Test prog against string of given length, no null termination + * but still metafied at this point. offset gives an offset + * to include in reported match indices + */ + +/**/ +mod_export int +pattrylen(Patprog prog, char *string, int len, int unmetalen, + Patstralloc patstralloc, int offset) +{ + return pattryrefs(prog, string, len, unmetalen, patstralloc, offset, + NULL, NULL, NULL); +} + +/* + * Test prog against string with given lengths. The input + * string is metafied; stringlen is the raw string length, and + * unmetalen the number of characters in the original string (some + * of which may now be metafied). Either value may be -1 + * to indicate a null-terminated string which will be counted. Note + * there may be a severe penalty for this if a lot of matching is done + * on one string. + * + * If patstralloc is not NULL it is used to optimise unmetafication + * of a trial string that may be passed (or any substring may be passed) to + * pattryrefs multiple times or the same pattern (N.B. so patstralloc + * depends on both prog *and* the trial string). This should only be + * done if there is no path prefix (pathpos == 0) as otherwise the path + * buffer and unmetafied string may not match. To do this, + * patallocstr() is callled (use force = 1 to ensure it is alway + * unmetafied); paststralloc points to existing storage. Memory is + * on the heap. + * + * patstralloc->alloced and patstralloc->unmetalen contain the + * unmetafied string and its length. In that case, the rules for the + * earlier arguments change: + * - string is an unmetafied string + * - stringlen is its unmetafied (i.e. actual) length + * - unmetalenin is not used. + * string and stringlen may refer to arbitrary substrings of + * patstralloc->alloced without any internal modification to patstralloc. + * + * patoffset is the position in the original string (not seen by + * the pattern module) at which we are trying to match. + * This is added in to the positions recorded in patbeginp and patendp + * when we are looking for substrings. Currently this only happens + * in the parameter substitution code. It refers to a real character + * offset, i.e. is already in the form ready for presentation to the + * general public --- this is necessary as we don't have the + * information to convert it down here. + * + * Note this is a character offset, i.e. a single possibly metafied and + * possibly multibyte character counts as 1. + * + * The last three arguments are used to report the positions for the + * backreferences. On entry, *nump should contain the maximum number + * of positions to report. In this case the match, mbegin, mend + * arrays are not altered. + * + * If nump is NULL but endp is not NULL, then *endp is set to the + * end position of the match, taking into account patinstart. + */ + +/**/ +mod_export int +pattryrefs(Patprog prog, char *string, int stringlen, int unmetalenin, + Patstralloc patstralloc, int patoffset, + int *nump, int *begp, int *endp) +{ + int i, maxnpos = 0, ret; + int origlen; + char **sp, **ep, *ptr; + char *progstr = (char *)prog + prog->startoff; + struct patstralloc patstralloc_struct; + + if (nump) { + maxnpos = *nump; + *nump = 0; + } + + if (!patstralloc) + patmungestring(&string, &stringlen, &unmetalenin); + origlen = stringlen; + + if (patstralloc) { + DPUTS(!patstralloc->alloced, + "External unmetafy didn't actually unmetafy."); + DPUTS(patstralloc->unmetalenp, + "Ooh-err: pathpos with external unmetafy. I have bad vibes."); + patinpath = NULL; + patinstart = string; + /* stringlen is unmetafied length; unmetalenin is ignored */ + } else { + patstralloc = &patstralloc_struct; + if (patallocstr(prog, string, stringlen, unmetalenin, 0, patstralloc)) { + patinstart = patstralloc->alloced + patstralloc->unmetalenp; + stringlen = patstralloc->unmetalen; + } else + patinstart = string; + if (patstralloc->unmetalenp) + patinpath = patstralloc->alloced; + else + patinpath = NULL; + } + + patflags = prog->flags; + patinend = patinstart + stringlen; + /* + * From now on we do not require NULL termination of + * the test string. There should also be no more references + * to the variable string. + */ + + if (prog->flags & (PAT_PURES|PAT_ANY)) { + /* + * Either we are testing against a pure string, + * or we can match anything at all. + */ + int pstrlen; + char *pstr; + if (patstralloc->alloced) + { + /* + * Unmetafied; we need pattern sring that's also unmetafied. + * We'll cache it in the patstralloc structure. + * Note it's on the heap. + */ + if (!patstralloc->progstrunmeta) + { + patstralloc->progstrunmeta = + dupstrpfx(progstr, (int)prog->patmlen); + unmetafy(patstralloc->progstrunmeta, + &patstralloc->progstrunmetalen); + } + pstr = patstralloc->progstrunmeta; + pstrlen = patstralloc->progstrunmetalen; + } + else + { + /* Metafied. */ + pstr = progstr; + pstrlen = (int)prog->patmlen; + } + if (prog->flags & PAT_ANY) { + /* + * Optimisation for a single "*": always matches + * (except for no_glob_dots, see below). + */ + ret = 1; + } else { + /* + * Testing a pure string. See if initial + * components match. + */ + int lendiff = stringlen - pstrlen; + if (lendiff < 0) { + /* No, the pattern string is too long. */ + ret = 0; + } else if (!memcmp(pstr, patinstart, pstrlen)) { + /* + * Initial component matches. Matches either + * if lengths are the same or we are not anchored + * to the end of the string. + */ + ret = !lendiff || (prog->flags & PAT_NOANCH); + } else { + /* No match. */ + ret = 0; + } + } + if (ret) { + /* + * For files, we won't match initial "."s unless + * glob_dots is set. + */ + if ((prog->flags & PAT_NOGLD) && *patinstart == '.') { + ret = 0; + } else { + /* + * Remember the length in case used for ${..#..} etc. + * In this case, we didn't unmetafy the pattern string + * In the orignal structure, but it might be unmetafied + * for use with an unmetafied test string. + */ + patinlen = pstrlen; + /* if matching files, must update globbing flags */ + patglobflags = prog->globend; + + if ((patglobflags & GF_MATCHREF) && + !(patflags & PAT_FILE)) { + char *str; + int mlen; + + if (patstralloc->alloced) { + /* + * Unmetafied: pstrlen contains unmetafied + * length in bytes. + */ + str = metafy(patinstart, pstrlen, META_DUP); + mlen = CHARSUB(patinstart, patinstart + pstrlen); + } else { + str = ztrduppfx(patinstart, patinlen); + /* + * Count the characters. We're not using CHARSUB() + * because the string is still metafied. + */ + MB_METACHARINIT(); + mlen = MB_METASTRLEN2END(patinstart, 0, + patinstart + patinlen); + } + + setsparam("MATCH", str); + setiparam("MBEGIN", + (zlong)(patoffset + !isset(KSHARRAYS))); + setiparam("MEND", + (zlong)(mlen + patoffset + + !isset(KSHARRAYS) - 1)); + } + } + } + } else { + /* + * Test for a `must match' string, unless we're scanning for a match + * in which case we don't need to do this each time. + */ + ret = 1; + if (!(prog->flags & PAT_SCAN) && prog->mustoff) + { + char *testptr; /* start pointer into test string */ + char *teststop; /* last point from which we can match */ + char *patptr = (char *)prog + prog->mustoff; + int patlen = prog->patmlen; + int found = 0; + + if (patlen > stringlen) { + /* Too long, can't match. */ + ret = 0; + } else { + teststop = patinend - patlen; + + for (testptr = patinstart; testptr <= teststop; testptr++) + { + if (!memcmp(testptr, patptr, patlen)) { + found = 1; + break; + } + } + + if (!found) + ret = 0; + } + } + if (!ret) + return 0; + + patglobflags = prog->globflags; + if (!(patflags & PAT_FILE)) { + forceerrs = -1; + errsfound = 0; + } + globdots = !(patflags & PAT_NOGLD); + parsfound = 0; + + patinput = patinstart; + + if (patmatch((Upat)progstr)) { + /* + * we were lazy and didn't save the globflags if an exclusion + * failed, so set it now + */ + patglobflags = prog->globend; + + /* + * Record length of successful match, including Meta + * characters. Do it here so that patmatchlen() can return + * it even if we delete the pattern strings. + */ + patinlen = patinput - patinstart; + /* + * Optimization: if we didn't find any Meta characters + * to begin with, we don't need to look for them now. + * + * For patstralloc pased in, we want the unmetafied length. + */ + if (patstralloc == &patstralloc_struct && + patstralloc->unmetalen != origlen) { + for (ptr = patinstart; ptr < patinput; ptr++) + if (imeta(*ptr)) + patinlen++; + } + + /* + * Should we clear backreferences and matches on a failed + * match? + */ + if ((patglobflags & GF_MATCHREF) && !(patflags & PAT_FILE)) { + /* + * m flag: for global match. This carries no overhead + * in the pattern matching part. + * + * Remember the test pattern is already unmetafied. + */ + char *str; + int mlen = CHARSUB(patinstart, patinput); + + str = metafy(patinstart, patinput - patinstart, META_DUP); + setsparam("MATCH", str); + setiparam("MBEGIN", (zlong)(patoffset + !isset(KSHARRAYS))); + setiparam("MEND", + (zlong)(mlen + patoffset + + !isset(KSHARRAYS) - 1)); + } + if (prog->patnpar && nump) { + /* + * b flag: for backreferences using parentheses. Reported + * directly. + */ + *nump = prog->patnpar; + + sp = patbeginp; + ep = patendp; + + for (i = 0; i < prog->patnpar && i < maxnpos; i++) { + if (parsfound & (1 << i)) { + if (begp) + *begp++ = CHARSUB(patinstart, *sp) + patoffset; + if (endp) + *endp++ = CHARSUB(patinstart, *ep) + patoffset + - 1; + } else { + if (begp) + *begp++ = -1; + if (endp) + *endp++ = -1; + } + + sp++; + ep++; + } + } else if (prog->patnpar && !(patflags & PAT_FILE)) { + /* + * b flag: for backreferences using parentheses. + */ + int palen = prog->patnpar+1; + char **matcharr, **mbeginarr, **mendarr; + char numbuf[DIGBUFSIZE]; + + matcharr = zshcalloc(palen*sizeof(char *)); + mbeginarr = zshcalloc(palen*sizeof(char *)); + mendarr = zshcalloc(palen*sizeof(char *)); + + sp = patbeginp; + ep = patendp; + + for (i = 0; i < prog->patnpar; i++) { + if (parsfound & (1 << i)) { + matcharr[i] = metafy(*sp, *ep - *sp, META_DUP); + /* + * mbegin and mend give indexes into the string + * in the standard notation, i.e. respecting + * KSHARRAYS, and with the end index giving + * the last character, not one beyond. + * For example, foo=foo; [[ $foo = (f)oo ]] gives + * (without KSHARRAYS) indexes 1 and 1, which + * corresponds to indexing as ${foo[1,1]}. + */ + sprintf(numbuf, "%ld", + (long)(CHARSUB(patinstart, *sp) + + patoffset + + !isset(KSHARRAYS))); + mbeginarr[i] = ztrdup(numbuf); + sprintf(numbuf, "%ld", + (long)(CHARSUB(patinstart, *ep) + + patoffset + + !isset(KSHARRAYS) - 1)); + mendarr[i] = ztrdup(numbuf); + } else { + /* Pattern wasn't set: either it was in an + * unmatched branch, or a hashed parenthesis + * that didn't match at all. + */ + matcharr[i] = ztrdup(""); + mbeginarr[i] = ztrdup("-1"); + mendarr[i] = ztrdup("-1"); + } + sp++; + ep++; + } + setaparam("match", matcharr); + setaparam("mbegin", mbeginarr); + setaparam("mend", mendarr); + } + + if (!nump && endp) { + /* + * We just need the overall end position. + */ + *endp = CHARSUB(patinstart, patinput) + patoffset; + } + + ret = 1; + } else + ret = 0; + } + + return ret; +} + +/* + * Return length of previous succesful match. This is + * in metafied bytes, i.e. includes a count of Meta characters, + * unless the match was done on an unmetafied string using + * a patstralloc stuct, in which case it, too is unmetafed. + * Unusual and futile attempt at modular encapsulation. + */ + +/**/ +int +patmatchlen(void) +{ + return patinlen; +} + +/* + * Match literal characters with case insensitivity test: the first + * comes from the input string, the second the current pattern. + */ +#ifdef MULTIBYTE_SUPPORT +#define ISUPPER(x) iswupper(x) +#define ISLOWER(x) iswlower(x) +#define TOUPPER(x) towupper(x) +#define TOLOWER(x) towlower(x) +#define ISDIGIT(x) iswdigit(x) +#else +#define ISUPPER(x) isupper(x) +#define ISLOWER(x) islower(x) +#define TOUPPER(x) toupper(x) +#define TOLOWER(x) tolower(x) +#define ISDIGIT(x) idigit(x) +#endif +#define CHARMATCH(chin, chpa) (chin == chpa || \ + ((patglobflags & GF_IGNCASE) ? \ + ((ISUPPER(chin) ? TOLOWER(chin) : chin) == \ + (ISUPPER(chpa) ? TOLOWER(chpa) : chpa)) : \ + (patglobflags & GF_LCMATCHUC) ? \ + (ISLOWER(chpa) && TOUPPER(chpa) == chin) : 0)) + +/* + * The same but caching an expression from the first argument, + * Requires local charmatch_cache definition. + */ +#define CHARMATCH_EXPR(expr, chpa) \ + (charmatch_cache = (expr), CHARMATCH(charmatch_cache, chpa)) + +/* + * exactpos is used to remember how far down an exact string we have + * matched, if we are doing approximation and can therefore redo from + * the same point; we never need to otherwise. + * + * exactend is a pointer to the end of the string, which isn't + * null-terminated. + */ +static char *exactpos, *exactend; + +/* + * Main matching routine. + * + * Testing the tail end of a match is usually done by recursion, but + * we try to eliminate that in favour of looping for simple cases. + */ + +/**/ +static int +patmatch(Upat prog) +{ + /* Current and next nodes */ + Upat scan = prog, next, opnd; + char *start, *save, *chrop, *chrend, *compend; + int savglobflags, op, no, min, fail = 0, saverrsfound; + zrange_t from, to, comp; + patint_t nextch; + int q = queue_signal_level(); + + /* + * To avoid overhead of saving state if there are no queued signals + * waiting, we pierce the signals.h veil and examine queue state. + */ +#define check_for_signals() do if (queue_front != queue_rear) { \ + int savpatflags = patflags, savpatglobflags = patglobflags; \ + char *savexactpos = exactpos, *savexactend = exactend; \ + struct rpat savpattrystate = pattrystate; \ + dont_queue_signals(); \ + restore_queue_signals(q); \ + exactpos = savexactpos; \ + exactend = savexactend; \ + patflags = savpatflags; \ + patglobflags = savpatglobflags; \ + pattrystate = savpattrystate; \ + } while (0) + + check_for_signals(); + + while (scan && !errflag) { + next = PATNEXT(scan); + + if (!globdots && P_NOTDOT(scan) && patinput == patinstart && + patinput < patinend && *patinput == '.') + return 0; + + switch (P_OP(scan)) { + case P_ANY: + if (patinput == patinend) + fail = 1; + else + CHARINC(patinput, patinend); + break; + case P_EXACTLY: + /* + * acts as nothing if *chrop is null: this is used by + * approx code. + */ + if (exactpos) { + chrop = exactpos; + chrend = exactend; + } else { + chrop = P_LS_STR(scan); + chrend = chrop + P_LS_LEN(scan); + } + exactpos = NULL; + while (chrop < chrend && patinput < patinend) { + char *savpatinput = patinput; + char *savchrop = chrop; + int badin = 0, badpa = 0; + /* + * Care with character matching: + * We do need to convert the character to wide + * representation if possible, because we may need + * to do case transformation. However, we should + * be careful in case one, but not the other, wasn't + * representable in the current locale---in that + * case they don't match even if the returned + * values (one properly converted, one raw) are + * the same. + */ + patint_t chin = CHARREFINC(patinput, patinend, &badin); + patint_t chpa = CHARREFINC(chrop, chrend, &badpa); + if (!CHARMATCH(chin, chpa) || badin != badpa) { + fail = 1; + patinput = savpatinput; + chrop = savchrop; + break; + } + } + if (chrop < chrend) { + exactpos = chrop; + exactend = chrend; + fail = 1; + } + break; + case P_ANYOF: + case P_ANYBUT: + if (patinput == patinend) + fail = 1; + else { +#ifdef MULTIBYTE_SUPPORT + int zmb_ind; + wchar_t cr = charref(patinput, patinend, &zmb_ind); + char *scanop = (char *)P_OPERAND(scan); + if (patglobflags & GF_MULTIBYTE) { + if (mb_patmatchrange(scanop, cr, zmb_ind, NULL, NULL) ^ + (P_OP(scan) == P_ANYOF)) + fail = 1; + else + CHARINC(patinput, patinend); + } else if (patmatchrange(scanop, (int)cr, NULL, NULL) ^ + (P_OP(scan) == P_ANYOF)) + fail = 1; + else + CHARINC(patinput, patinend); +#else + if (patmatchrange((char *)P_OPERAND(scan), + CHARREF(patinput, patinend), NULL, NULL) ^ + (P_OP(scan) == P_ANYOF)) + fail = 1; + else + CHARINC(patinput, patinend); +#endif + } + break; + case P_NUMRNG: + case P_NUMFROM: + case P_NUMTO: + /* + * To do this properly, we really have to treat numbers as + * closures: that's so things like <1-1000>33 will + * match 633 (they didn't up to 3.1.6). To avoid making this + * too inefficient, we see if there's an exact match next: + * if there is, and it's not a digit, we return 1 after + * the first attempt. + */ + op = P_OP(scan); + start = (char *)P_OPERAND(scan); + from = to = 0; + if (op != P_NUMTO) { +#ifdef ZSH_64_BIT_TYPE + /* We can't rely on pointer alignment being good enough. */ + memcpy((char *)&from, start, sizeof(zrange_t)); +#else + from = *((zrange_t *) start); +#endif + start += sizeof(zrange_t); + } + if (op != P_NUMFROM) { +#ifdef ZSH_64_BIT_TYPE + memcpy((char *)&to, start, sizeof(zrange_t)); +#else + to = *((zrange_t *) start); +#endif + } + start = compend = patinput; + comp = 0; + while (patinput < patinend && idigit(*patinput)) { + int out_of_range = 0; + int digit = *patinput - '0'; + if (comp > ZRANGE_MAX / (zlong)10) { + out_of_range = 1; + } else { + zrange_t c10 = comp ? comp * 10 : 0; + if (ZRANGE_MAX - c10 < digit) { + out_of_range = 1; + } else { + comp = c10; + comp += digit; + } + } + patinput++; + compend++; + + if (out_of_range || + (comp & ((zrange_t)1 << (sizeof(comp)*8 - +#ifdef ZRANGE_T_IS_SIGNED + 2 +#else + 1 +#endif + )))) { + /* + * Out of range (allowing for signedness, which + * we need if we are using zlongs). + * This is as far as we can go. + * If we're doing a range "from", skip all the + * remaining numbers. Otherwise, we can't + * match beyond the previous point anyway. + * Leave the pointer to the last calculated + * position (compend) where it was before. + */ + if (op == P_NUMFROM) { + while (patinput < patinend && idigit(*patinput)) + patinput++; + } + } + } + save = patinput; + no = 0; + while (patinput > start) { + /* if already too small, no power on earth can save it */ + if (comp < from && patinput <= compend) + break; + if ((op == P_NUMFROM || comp <= to) && patmatch(next)) { + return 1; + } + if (!no && P_OP(next) == P_EXACTLY && + (!P_LS_LEN(next) || + !idigit(STOUC(*P_LS_STR(next)))) && + !(patglobflags & 0xff)) + return 0; + patinput = --save; + no++; + /* + * With a range start and an unrepresentable test + * number, we just back down the test string without + * changing the number until we get to a representable + * one. + */ + if (patinput < compend) + comp /= 10; + } + patinput = start; + fail = 1; + break; + case P_NUMANY: + /* This is <->: any old set of digits, don't bother comparing */ + start = patinput; + while (patinput < patinend && idigit(*patinput)) + patinput++; + save = patinput; + no = 0; + while (patinput > start) { + if (patmatch(next)) + return 1; + if (!no && P_OP(next) == P_EXACTLY && + (!P_LS_LEN(next) || + !idigit(*P_LS_STR(next))) && + !(patglobflags & 0xff)) + return 0; + patinput = --save; + no++; + } + patinput = start; + fail = 1; + break; + case P_NOTHING: + break; + case P_BACK: + break; + case P_GFLAGS: + patglobflags = P_OPERAND(scan)->l; + break; + case P_OPEN: + case P_OPEN+1: + case P_OPEN+2: + case P_OPEN+3: + case P_OPEN+4: + case P_OPEN+5: + case P_OPEN+6: + case P_OPEN+7: + case P_OPEN+8: + case P_OPEN+9: + no = P_OP(scan) - P_OPEN; + save = patinput; + + if (patmatch(next)) { + /* + * Don't set patbeginp if some later invocation of + * the same parentheses already has. + */ + if (no && !(parsfound & (1 << (no - 1)))) { + patbeginp[no-1] = save; + parsfound |= 1 << (no - 1); + } + return 1; + } else + return 0; + break; + case P_CLOSE: + case P_CLOSE+1: + case P_CLOSE+2: + case P_CLOSE+3: + case P_CLOSE+4: + case P_CLOSE+5: + case P_CLOSE+6: + case P_CLOSE+7: + case P_CLOSE+8: + case P_CLOSE+9: + no = P_OP(scan) - P_CLOSE; + save = patinput; + + if (patmatch(next)) { + if (no && !(parsfound & (1 << (no + 15)))) { + patendp[no-1] = save; + parsfound |= 1 << (no + 15); + } + return 1; + } else + return 0; + break; + case P_EXCSYNC: + /* See the P_EXCLUDE code below for where syncptr comes from */ + { + unsigned char *syncptr; + Upat after; + after = P_OPERAND(scan); + DPUTS(!P_ISEXCLUDE(after), + "BUG: EXCSYNC not followed by EXCLUDE."); + DPUTS(!P_OPERAND(after)->p, + "BUG: EXCSYNC not handled by EXCLUDE"); + syncptr = P_OPERAND(after)->p + (patinput - patinstart); + /* + * If we already matched from here, this time we fail. + * See WBRANCH code for story about error count. + */ + if (*syncptr && errsfound + 1 >= *syncptr) + return 0; + /* + * Else record that we (possibly) matched this time. + * No harm if we don't: then the previous test will just + * short cut the attempted match that is bound to fail. + * We never try to exclude something that has already + * failed anyway. + */ + *syncptr = errsfound + 1; + } + break; + case P_EXCEND: + /* + * This is followed by a P_EXCSYNC, but only in the P_EXCLUDE + * branch. Actually, we don't bother following it: all we + * need to know is that we successfully matched so far up + * to the end of the asserted pattern; the endpoint + * in the target string is nulled out. + */ + if (!(fail = (patinput < patinend))) + return 1; + break; + case P_BRANCH: + case P_WBRANCH: + /* P_EXCLUDE shouldn't occur without a P_BRANCH */ + if (!P_ISBRANCH(next)) { + /* no choice, avoid recursion */ + DPUTS(P_OP(scan) == P_WBRANCH, + "BUG: WBRANCH with no alternative."); + next = P_OPERAND(scan); + } else { + do { + save = patinput; + savglobflags = patglobflags; + saverrsfound = errsfound; + if (P_ISEXCLUDE(next)) { + /* + * The strategy is to test the asserted pattern, + * recording via P_EXCSYNC how far the part to + * be excluded matched. We then set the + * length of the test string to that + * point and see if the exclusion as far as + * P_EXCEND also matches that string. + * We need to keep testing the asserted pattern + * by backtracking, since the first attempt + * may be excluded while a later attempt may not. + * For this we keep a pointer just after + * the P_EXCLUDE which is tested by the P_EXCSYNC + * to see if we matched there last time, in which + * case we fail. If there is nothing to backtrack + * over, that doesn't matter: we should fail anyway. + * The pointer also tells us where the asserted + * pattern matched for use by the exclusion. + * + * It's hard to allocate space for this + * beforehand since we may need to do it + * recursively. + * + * P.S. in case you were wondering, this code + * is horrible. + */ + Upat syncstrp; + char *origpatinend; + unsigned char *oldsyncstr; + char *matchpt = NULL; + int ret, savglobdots, matchederrs = 0; + int savparsfound = parsfound; + DPUTS(P_OP(scan) == P_WBRANCH, + "BUG: excluded WBRANCH"); + syncstrp = P_OPERAND(next); + /* + * Unlike WBRANCH, each test at the same exclude + * sync point (due to an external loop) is separate, + * i.e testing (foo~bar)# is no different from + * (foo~bar)(foo~bar)... from the exclusion point + * of view, so we use a different sync string. + */ + oldsyncstr = syncstrp->p; + syncstrp->p = (unsigned char *) + zshcalloc((patinend - patinstart) + 1); + origpatinend = patinend; + while ((ret = patmatch(P_OPERAND(scan)))) { + unsigned char *syncpt; + char *savpatinstart; + int savforce = forceerrs; + int savpatflags = patflags, synclen; + forceerrs = -1; + savglobdots = globdots; + matchederrs = errsfound; + matchpt = patinput; /* may not be end */ + globdots = 1; /* OK to match . first */ + /* Find the point where the scan + * matched the part to be excluded: because + * of backtracking, the one + * most recently matched will be the first. + * (Luckily, backtracking is done after all + * possibilities for approximation have been + * checked.) + */ + for (syncpt = syncstrp->p; !*syncpt; syncpt++) + ; + synclen = syncpt - syncstrp->p; + if (patinstart + synclen != patinend) { + /* + * Temporarily mark the string as + * ending at this point. + */ + DPUTS(patinstart + synclen > matchpt, + "BUG: EXCSYNC failed"); + + patinend = patinstart + synclen; + /* + * If this isn't really the end of the string, + * remember this for the (#e) assertion. + */ + patflags |= PAT_NOTEND; + } + savpatinstart = patinstart; + next = PATNEXT(scan); + while (next && P_ISEXCLUDE(next)) { + patinput = save; + /* + * turn off approximations in exclusions: + * note we keep remaining patglobflags + * set by asserted branch (or previous + * excluded branches, for consistency). + */ + patglobflags &= ~0xff; + errsfound = 0; + opnd = P_OPERAND(next) + 1; + if (P_OP(next) == P_EXCLUDP && patinpath) { + /* + * Top level exclusion with a file, + * applies to whole path so add the + * segments already matched. + * We copied these in front of the + * test pattern, so patinend doesn't + * need moving. + */ + DPUTS(patinput != patinstart, + "BUG: not at start excluding path"); + patinput = patinstart = patinpath; + } + if (patmatch(opnd)) { + ret = 0; + /* + * Another subtlety: if we exclude the + * match, any parentheses just found + * become invalidated. + */ + parsfound = savparsfound; + } + if (patinpath) { + patinput = savpatinstart + + (patinput - patinstart); + patinstart = savpatinstart; + } + if (!ret) + break; + next = PATNEXT(next); + } + /* + * Restore original end position. + */ + patinend = origpatinend; + patflags = savpatflags; + globdots = savglobdots; + forceerrs = savforce; + if (ret) + break; + patinput = save; + patglobflags = savglobflags; + errsfound = saverrsfound; + } + zfree((char *)syncstrp->p, + (patinend - patinstart) + 1); + syncstrp->p = oldsyncstr; + if (ret) { + patinput = matchpt; + errsfound = matchederrs; + return 1; + } + while ((scan = PATNEXT(scan)) && + P_ISEXCLUDE(scan)) + ; + } else { + int ret = 1, pfree = 0; + Upat ptrp = NULL; + unsigned char *ptr; + if (P_OP(scan) == P_WBRANCH) { + /* + * This is where we make sure that we are not + * repeatedly matching zero-length strings in + * a closure, which would cause an infinite loop, + * and also remove exponential behaviour in + * backtracking nested closures. + * The P_WBRANCH operator leaves a space for a + * uchar *, initialized to NULL, which is + * turned into a string the same length as the + * target string. Every time we match from a + * particular point in the target string, we + * stick a 1 at the corresponding point here. + * If we come round to the same branch again, and + * there is already a 1, then the test fails. + */ + opnd = P_OPERAND(scan); + ptrp = opnd++; + if (!ptrp->p) { + ptrp->p = (unsigned char *) + zshcalloc((patinend - patinstart) + 1); + pfree = 1; + } + ptr = ptrp->p + (patinput - patinstart); + + /* + * Without approximation, this is just a + * single bit test. With approximation, we + * need to know how many errors there were + * last time we made the test. If errsfound + * is now smaller than it was, hence we can + * make more approximations in the remaining + * code, we continue with the test. + * (This is why the max number of errors is + * 254, not 255.) + */ + if (*ptr && errsfound + 1 >= *ptr) + ret = 0; + *ptr = errsfound + 1; + } else + opnd = P_OPERAND(scan); + if (ret) + ret = patmatch(opnd); + if (pfree) { + zfree((char *)ptrp->p, + (patinend - patinstart) + 1); + ptrp->p = NULL; + } + if (ret) + return 1; + scan = PATNEXT(scan); + } + patinput = save; + patglobflags = savglobflags; + errsfound = saverrsfound; + DPUTS(P_OP(scan) == P_WBRANCH, + "BUG: WBRANCH not first choice."); + next = PATNEXT(scan); + } while (scan && P_ISBRANCH(scan)); + return 0; + } + break; + case P_STAR: + /* Handle specially for speed, although really P_ONEHASH+P_ANY */ + while (P_OP(next) == P_STAR) { + /* + * If there's another * following we can optimise it + * out. Chains of *'s can give pathologically bad + * performance. + */ + scan = next; + next = PATNEXT(scan); + } + /*FALLTHROUGH*/ + case P_ONEHASH: + case P_TWOHASH: + /* + * This is just simple cases, matching one character. + * With approximations, we still handle * this way, since + * no approximation is ever necessary, but other closures + * are handled by the more complicated branching method + */ + op = P_OP(scan); + /* Note that no counts possibly metafied characters */ + start = patinput; + { + char *lastcharstart; + /* + * Array to record the start of characters for + * backtracking. + */ + VARARR(char, charstart, patinend-patinput); + memset(charstart, 0, patinend-patinput); + + if (op == P_STAR) { + for (no = 0; patinput < patinend; + CHARINC(patinput, patinend)) + { + charstart[patinput-start] = 1; + no++; + } + /* simple optimization for reasonably common case */ + if (P_OP(next) == P_END) + return 1; + } else { + DPUTS(patglobflags & 0xff, + "BUG: wrong backtracking with approximation."); + if (!globdots && P_NOTDOT(P_OPERAND(scan)) && + patinput == patinstart && patinput < patinend && + CHARREF(patinput, patinend) == ZWC('.')) + return 0; + no = patrepeat(P_OPERAND(scan), charstart); + } + min = (op == P_TWOHASH) ? 1 : 0; + /* + * Lookahead to avoid useless matches. This is not possible + * with approximation. + */ + if (P_OP(next) == P_EXACTLY && P_LS_LEN(next) && + !(patglobflags & 0xff)) { + char *nextop = P_LS_STR(next); +#ifdef MULTIBYTE_SUPPORT + /* else second argument of CHARREF isn't used */ + int nextlen = P_LS_LEN(next); +#endif + /* + * If that P_EXACTLY is last (common in simple patterns, + * such as *.c), then it can be only be matched at one + * point in the test string, so record that. + */ + if (P_OP(PATNEXT(next)) == P_END && + !(patflags & PAT_NOANCH)) { + int ptlen = patinend - patinput; + int lenmatch = patinend - + (min ? CHARNEXT(start, patinend) : start); + /* Are we in the right range? */ + if (P_LS_LEN(next) > lenmatch || + P_LS_LEN(next) < ptlen) + return 0; + /* Yes, just position appropriately and test. */ + patinput += ptlen - P_LS_LEN(next); + /* + * Here we will need to be careful that patinput is not + * in the middle of a multibyte character. + */ + /* Continue loop with P_EXACTLY test. */ + break; + } + nextch = CHARREF(nextop, nextop + nextlen); + } else + nextch = PEOF; + savglobflags = patglobflags; + saverrsfound = errsfound; + lastcharstart = charstart + (patinput - start); + if (no >= min) { + for (;;) { + patint_t charmatch_cache; + if (nextch == PEOF || + (patinput < patinend && + CHARMATCH_EXPR(CHARREF(patinput, patinend), + nextch))) { + if (patmatch(next)) + return 1; + } + if (--no < min) + break; + /* find start of previous full character */ + while (!*--lastcharstart) + DPUTS(lastcharstart < charstart, + "lastcharstart invalid"); + patinput = start + (lastcharstart-charstart); + patglobflags = savglobflags; + errsfound = saverrsfound; + } + } + } + /* + * As with branches, the patmatch(next) stuff for * + * handles approximation, so we don't need to try + * anything here. + */ + return 0; + case P_ISSTART: + if (patinput != patinstart || (patflags & PAT_NOTSTART)) + fail = 1; + break; + case P_ISEND: + if (patinput < patinend || (patflags & PAT_NOTEND)) + fail = 1; + break; + case P_COUNTSTART: + { + /* + * Save and restore the current count and the + * start pointer in case the pattern has been + * executed by a previous repetition of a + * closure. + */ + long *curptr = &P_OPERAND(scan)[P_CT_CURRENT].l; + long savecount = *curptr; + unsigned char *saveptr = scan[P_CT_PTR].p; + int ret; + + *curptr = 0L; + ret = patmatch(P_OPERAND(scan)); + *curptr = savecount; + scan[P_CT_PTR].p = saveptr; + return ret; + } + case P_COUNT: + { + /* (#cN,M): execution is relatively straightforward */ + long cur = scan[P_CT_CURRENT].l; + long min = scan[P_CT_MIN].l; + long max = scan[P_CT_MAX].l; + + if (cur && cur >= min && + (unsigned char *)patinput == scan[P_CT_PTR].p) { + /* + * Not at the first attempt to match so + * the previous attempt managed zero length. + * We can do this indefinitely so there's + * no point in going on. Simply try to + * match the remainder of the pattern. + */ + return patmatch(next); + } + scan[P_CT_PTR].p = (unsigned char *)patinput; + + if (max < 0 || cur < max) { + char *patinput_thistime = patinput; + scan[P_CT_CURRENT].l = cur + 1; + if (patmatch(scan + P_CT_OPERAND)) + return 1; + scan[P_CT_CURRENT].l = cur; + patinput = patinput_thistime; + } + if (cur < min) + return 0; + return patmatch(next); + } + case P_END: + if (!(fail = (patinput < patinend && !(patflags & PAT_NOANCH)))) + return 1; + break; +#ifdef DEBUG + default: + dputs("BUG: bad operand in patmatch."); + return 0; + break; +#endif + } + + if (fail) { + if (errsfound < (patglobflags & 0xff) && + (forceerrs == -1 || errsfound < forceerrs)) { + /* + * Approximation code. There are four possibilities + * + * 1. omit character from input string + * 2. transpose characters in input and pattern strings + * 3. omit character in both input and pattern strings + * 4. omit character from pattern string. + * + * which we try in that order. + * + * Of these, 2, 3 and 4 require an exact match string + * (P_EXACTLY) while 1, 2 and 3 require that we not + * have reached the end of the input string. + * + * Note in each case after making the approximation we + * need to retry the *same* pattern; this is what + * requires exactpos, a slightly doleful way of + * communicating with the exact character matcher. + */ + char *savexact = exactpos; + save = patinput; + savglobflags = patglobflags; + saverrsfound = ++errsfound; + fail = 0; + + DPUTS(P_OP(scan) != P_EXACTLY && exactpos, + "BUG: non-exact match has set exactpos"); + + /* Try omitting a character from the input string */ + if (patinput < patinend) { + CHARINC(patinput, patinend); + /* If we are not on an exact match, then this is + * our last gasp effort, so we can optimize out + * the recursive call. + */ + if (P_OP(scan) != P_EXACTLY) + continue; + if (patmatch(scan)) + return 1; + } + + if (P_OP(scan) == P_EXACTLY) { + char *nextexact = savexact; + DPUTS(!savexact, + "BUG: exact match has not set exactpos"); + CHARINC(nextexact, exactend); + + if (save < patinend) { + char *nextin = save; + CHARINC(nextin, patinend); + patglobflags = savglobflags; + errsfound = saverrsfound; + exactpos = savexact; + + /* + * Try swapping two characters in patinput and + * exactpos + */ + if (save < patinend && nextin < patinend && + nextexact < exactend) { + patint_t cin0 = CHARREF(save, patinend); + patint_t cpa0 = CHARREF(exactpos, exactend); + patint_t cin1 = CHARREF(nextin, patinend); + patint_t cpa1 = CHARREF(nextexact, exactend); + + if (CHARMATCH(cin0, cpa1) && + CHARMATCH(cin1, cpa0)) { + patinput = nextin; + CHARINC(patinput, patinend); + exactpos = nextexact; + CHARINC(exactpos, exactend); + if (patmatch(scan)) + return 1; + + patglobflags = savglobflags; + errsfound = saverrsfound; + } + } + + /* + * Try moving up both strings. + */ + patinput = nextin; + exactpos = nextexact; + if (patmatch(scan)) + return 1; + + patinput = save; + patglobflags = savglobflags; + errsfound = saverrsfound; + exactpos = savexact; + } + + DPUTS(exactpos == exactend, "approximating too far"); + /* + * Try moving up the exact match pattern. + * This must be the last attempt, so just loop + * instead of calling recursively. + */ + CHARINC(exactpos, exactend); + continue; + } + } + exactpos = NULL; + return 0; + } + + scan = next; + + /* Allow handlers to run once per loop */ + check_for_signals(); + } + + return 0; +} + + +/**/ +#ifdef MULTIBYTE_SUPPORT + +/* + * See if character ch matches a pattern range specification. + * The null-terminated specification is in range; the test + * character is in ch. + * + * zmb is one of the enum defined above charref(), for indicating + * incomplete or invalid multibyte characters. + * + * indptr is used by completion matching, which is why this + * function is exported. If indptr is not NULL we set *indptr + * to the index of the character in the range string, adjusted + * in the case of "A-B" ranges such that A would count as its + * normal index (say IA), B would count as IA + (B-A), and any + * character within the range as appropriate. We're not strictly + * guaranteed this fits within a wint_t, but if this is Unicode + * in 32 bits we have a fair amount of distance left over. + * + * mtp is used in the same circumstances. *mtp returns the match type: + * 0 for a standard character, else the PP_ index. It's not + * useful if the match failed. + */ + +/**/ +mod_export int +mb_patmatchrange(char *range, wchar_t ch, int zmb_ind, wint_t *indptr, int *mtp) +{ + wchar_t r1, r2; + + if (indptr) + *indptr = 0; + /* + * Careful here: unlike other strings, range is a NULL-terminated, + * metafied string, because we need to treat the Posix and hyphenated + * ranges specially. + */ + while (*range) { + if (imeta(STOUC(*range))) { + int swtype = STOUC(*range++) - STOUC(Meta); + if (mtp) + *mtp = swtype; + switch (swtype) { + case 0: + /* ordinary metafied character */ + range--; + if (metacharinc(&range) == ch) + return 1; + break; + case PP_ALPHA: + if (iswalpha(ch)) + return 1; + break; + case PP_ALNUM: + if (iswalnum(ch)) + return 1; + break; + case PP_ASCII: + if ((ch & ~0x7f) == 0) + return 1; + break; + case PP_BLANK: +#if !defined(HAVE_ISWBLANK) && !defined(iswblank) +/* + * iswblank() is GNU and C99. There's a remote chance that some + * systems still don't support it (but would support the other ones + * if MULTIBYTE_SUPPORT is enabled). + */ +#define iswblank(c) (c == L' ' || c == L'\t') +#endif + if (iswblank(ch)) + return 1; + break; + case PP_CNTRL: + if (iswcntrl(ch)) + return 1; + break; + case PP_DIGIT: + if (iswdigit(ch)) + return 1; + break; + case PP_GRAPH: + if (iswgraph(ch)) + return 1; + break; + case PP_LOWER: + if (iswlower(ch)) + return 1; + break; + case PP_PRINT: + if (WC_ISPRINT(ch)) + return 1; + break; + case PP_PUNCT: + if (iswpunct(ch)) + return 1; + break; + case PP_SPACE: + if (iswspace(ch)) + return 1; + break; + case PP_UPPER: + if (iswupper(ch)) + return 1; + break; + case PP_XDIGIT: + if (iswxdigit(ch)) + return 1; + break; + case PP_IDENT: + if (wcsitype(ch, IIDENT)) + return 1; + break; + case PP_IFS: + if (wcsitype(ch, ISEP)) + return 1; + break; + case PP_IFSSPACE: + /* must be ASCII space character */ + if (ch < 128 && iwsep((int)ch)) + return 1; + break; + case PP_WORD: + if (wcsitype(ch, IWORD)) + return 1; + break; + case PP_RANGE: + r1 = metacharinc(&range); + r2 = metacharinc(&range); + if (r1 <= ch && ch <= r2) { + if (indptr) + *indptr += ch - r1; + return 1; + } + /* Careful not to screw up counting with bogus range */ + if (indptr && r1 < r2) { + /* + * This gets incremented again below to get + * us past the range end. This is correct. + */ + *indptr += r2 - r1; + } + break; + case PP_INCOMPLETE: + if (zmb_ind == ZMB_INCOMPLETE) + return 1; + break; + case PP_INVALID: + if (zmb_ind == ZMB_INVALID) + return 1; + break; + case PP_UNKWN: + DPUTS(1, "BUG: unknown posix range passed through.\n"); + break; + default: + DPUTS(1, "BUG: unknown metacharacter in range."); + break; + } + } else if (metacharinc(&range) == ch) { + if (mtp) + *mtp = 0; + return 1; + } + if (indptr) + (*indptr)++; + } + return 0; +} + + +/* + * This is effectively the reverse of mb_patmatchrange(). + * Given a range descriptor of the same form, and an index into it, + * try to determine the character that is matched. If the index + * points to a [:...:] generic style match, set chr to WEOF and + * return the type in mtp instead. Return 1 if successful, 0 if + * there was no corresponding index. Note all pointer arguments + * must be non-null. + */ + +/**/ +mod_export int +mb_patmatchindex(char *range, wint_t ind, wint_t *chr, int *mtp) +{ + wchar_t r1, r2, rchr; + wint_t rdiff; + + *chr = WEOF; + *mtp = 0; + + while (*range) { + if (imeta(STOUC(*range))) { + int swtype = STOUC(*range++) - STOUC(Meta); + switch (swtype) { + case 0: + range--; + rchr = metacharinc(&range); + if (!ind) { + *chr = (wint_t) rchr; + return 1; + } + break; + + case PP_ALPHA: + case PP_ALNUM: + case PP_ASCII: + case PP_BLANK: + case PP_CNTRL: + case PP_DIGIT: + case PP_GRAPH: + case PP_LOWER: + case PP_PRINT: + case PP_PUNCT: + case PP_SPACE: + case PP_UPPER: + case PP_XDIGIT: + case PP_IDENT: + case PP_IFS: + case PP_IFSSPACE: + case PP_WORD: + case PP_INCOMPLETE: + case PP_INVALID: + if (!ind) { + *mtp = swtype; + return 1; + } + break; + + case PP_RANGE: + r1 = metacharinc(&range); + r2 = metacharinc(&range); + rdiff = (wint_t)r2 - (wint_t)r1; + if (rdiff >= ind) { + *chr = (wint_t)r1 + ind; + return 1; + } + /* note the extra decrement to ind below */ + ind -= rdiff; + break; + case PP_UNKWN: + DPUTS(1, "BUG: unknown posix range passed through.\n"); + break; + default: + DPUTS(1, "BUG: unknown metacharacter in range."); + break; + } + } else { + rchr = metacharinc(&range); + if (!ind) { + *chr = (wint_t)rchr; + return 1; + } + } + if (!ind--) + break; + } + + /* No corresponding index. */ + return 0; +} + +/**/ +#endif /* MULTIBYTE_SUPPORT */ + +/* + * Identical function to mb_patmatchrange() above for single-byte + * characters. + */ + +/**/ +mod_export int +patmatchrange(char *range, int ch, int *indptr, int *mtp) +{ + int r1, r2; + + if (indptr) + *indptr = 0; + /* + * Careful here: unlike other strings, range is a NULL-terminated, + * metafied string, because we need to treat the Posix and hyphenated + * ranges specially. + */ + for (; *range; range++) { + if (imeta(STOUC(*range))) { + int swtype = STOUC(*range) - STOUC(Meta); + if (mtp) + *mtp = swtype; + switch (swtype) { + case 0: + if (STOUC(*++range ^ 32) == ch) + return 1; + break; + case PP_ALPHA: + if (isalpha(ch)) + return 1; + break; + case PP_ALNUM: + if (isalnum(ch)) + return 1; + break; + case PP_ASCII: + if ((ch & ~0x7f) == 0) + return 1; + break; + case PP_BLANK: +#if !defined(HAVE_ISBLANK) && !defined(isblank) +/* + * isblank() is GNU and C99. There's a remote chance that some + * systems still don't support it. + */ +#define isblank(c) (c == ' ' || c == '\t') +#endif + if (isblank(ch)) + return 1; + break; + case PP_CNTRL: + if (iscntrl(ch)) + return 1; + break; + case PP_DIGIT: + if (isdigit(ch)) + return 1; + break; + case PP_GRAPH: + if (isgraph(ch)) + return 1; + break; + case PP_LOWER: + if (islower(ch)) + return 1; + break; + case PP_PRINT: + if (ZISPRINT(ch)) + return 1; + break; + case PP_PUNCT: + if (ispunct(ch)) + return 1; + break; + case PP_SPACE: + if (isspace(ch)) + return 1; + break; + case PP_UPPER: + if (isupper(ch)) + return 1; + break; + case PP_XDIGIT: + if (isxdigit(ch)) + return 1; + break; + case PP_IDENT: + if (iident(ch)) + return 1; + break; + case PP_IFS: + if (isep(ch)) + return 1; + break; + case PP_IFSSPACE: + if (iwsep(ch)) + return 1; + break; + case PP_WORD: + if (iword(ch)) + return 1; + break; + case PP_RANGE: + range++; + r1 = STOUC(UNMETA(range)); + METACHARINC(range); + r2 = STOUC(UNMETA(range)); + if (*range == Meta) + range++; + if (r1 <= ch && ch <= r2) { + if (indptr) + *indptr += ch - r1; + return 1; + } + if (indptr && r1 < r2) + *indptr += r2 - r1; + break; + case PP_INCOMPLETE: + case PP_INVALID: + /* Never true if not in multibyte mode */ + break; + case PP_UNKWN: + DPUTS(1, "BUG: unknown posix range passed through.\n"); + break; + default: + DPUTS(1, "BUG: unknown metacharacter in range."); + break; + } + } else if (STOUC(*range) == ch) { + if (mtp) + *mtp = 0; + return 1; + } + if (indptr) + (*indptr)++; + } + return 0; +} + + +/**/ +#ifndef MULTIBYTE_SUPPORT + +/* + * Identical function to mb_patmatchindex() above for single-byte + * characters. Here -1 represents a character that needs a special type. + * + * Unlike patmatchrange, we only need this in ZLE, which always + * uses MULTIBYTE_SUPPORT if compiled in; hence we don't use + * this function in that case. + */ + +/**/ +mod_export int +patmatchindex(char *range, int ind, int *chr, int *mtp) +{ + int r1, r2, rdiff, rchr; + + *chr = -1; + *mtp = 0; + + for (; *range; range++) { + if (imeta(STOUC(*range))) { + int swtype = STOUC(*range) - STOUC(Meta); + switch (swtype) { + case 0: + /* ordinary metafied character */ + rchr = STOUC(*++range) ^ 32; + if (!ind) { + *chr = rchr; + return 1; + } + break; + + case PP_ALPHA: + case PP_ALNUM: + case PP_ASCII: + case PP_BLANK: + case PP_CNTRL: + case PP_DIGIT: + case PP_GRAPH: + case PP_LOWER: + case PP_PRINT: + case PP_PUNCT: + case PP_SPACE: + case PP_UPPER: + case PP_XDIGIT: + case PP_IDENT: + case PP_IFS: + case PP_IFSSPACE: + case PP_WORD: + case PP_INCOMPLETE: + case PP_INVALID: + if (!ind) { + *mtp = swtype; + return 1; + } + break; + + case PP_RANGE: + range++; + r1 = STOUC(UNMETA(range)); + METACHARINC(range); + r2 = STOUC(UNMETA(range)); + if (*range == Meta) + range++; + rdiff = r2 - r1; + if (rdiff >= ind) { + *chr = r1 + ind; + return 1; + } + /* note the extra decrement to ind below */ + ind -= rdiff; + break; + case PP_UNKWN: + DPUTS(1, "BUG: unknown posix range passed through.\n"); + break; + default: + DPUTS(1, "BUG: unknown metacharacter in range."); + break; + } + } else { + if (!ind) { + *chr = STOUC(*range); + return 1; + } + } + if (!ind--) + break; + } + + /* No corresponding index. */ + return 0; +} + +/**/ +#endif /* MULTIBYTE_SUPPORT */ + +/* + * Repeatedly match something simple and say how many times. + * charstart is an array parallel to that starting at patinput + * and records the start of (possibly multibyte) characters + * to aid in later backtracking. + */ + +/**/ +static int patrepeat(Upat p, char *charstart) +{ + int count = 0; + patint_t tch, charmatch_cache; + char *scan, *opnd; + + scan = patinput; + opnd = (char *)P_OPERAND(p); + + switch(P_OP(p)) { +#ifdef DEBUG + case P_ANY: + dputs("BUG: ?# did not get optimized to *"); + return 0; + break; +#endif + case P_EXACTLY: + DPUTS(P_LS_LEN(p) != 1, "closure following more than one character"); + tch = CHARREF(P_LS_STR(p), P_LS_STR(p) + P_LS_LEN(p)); + while (scan < patinend && + CHARMATCH_EXPR(CHARREF(scan, patinend), tch)) { + charstart[scan-patinput] = 1; + count++; + CHARINC(scan, patinend); + } + break; + case P_ANYOF: + case P_ANYBUT: + while (scan < patinend) { +#ifdef MULTIBYTE_SUPPORT + int zmb_ind; + wchar_t cr = charref(scan, patinend, &zmb_ind); + if (patglobflags & GF_MULTIBYTE) { + if (mb_patmatchrange(opnd, cr, zmb_ind, NULL, NULL) ^ + (P_OP(p) == P_ANYOF)) + break; + } else if (patmatchrange(opnd, (int)cr, NULL, NULL) ^ + (P_OP(p) == P_ANYOF)) + break; +#else + if (patmatchrange(opnd, CHARREF(scan, patinend), NULL, NULL) ^ + (P_OP(p) == P_ANYOF)) + break; +#endif + charstart[scan-patinput] = 1; + count++; + CHARINC(scan, patinend); + } + break; +#ifdef DEBUG + default: + dputs("BUG: something very strange is happening in patrepeat"); + return 0; + break; +#endif + } + + patinput = scan; + return count; +} + +/* Free a patprog. */ + +/**/ +mod_export void +freepatprog(Patprog prog) +{ + if (prog && prog != dummy_patprog1 && prog != dummy_patprog2) + zfree(prog, prog->size); +} + +/* Disable or reenable a pattern character */ + +/**/ +int +pat_enables(const char *cmd, char **patp, int enable) +{ + int ret = 0; + const char **stringp; + char *disp; + + if (!*patp) { + int done = 0; + for (stringp = zpc_strings, disp = zpc_disables; + stringp < zpc_strings + ZPC_COUNT; + stringp++, disp++) { + if (!*stringp) + continue; + if (enable ? *disp : !*disp) + continue; + if (done) + putc(' ', stdout); + printf("'%s'", *stringp); + done = 1; + } + if (done) + putc('\n', stdout); + return 0; + } + + for (; *patp; patp++) { + for (stringp = zpc_strings, disp = zpc_disables; + stringp < zpc_strings + ZPC_COUNT; + stringp++, disp++) { + if (*stringp && !strcmp(*stringp, *patp)) { + *disp = (char)!enable; + break; + } + } + if (stringp == zpc_strings + ZPC_COUNT) { + zerrnam(cmd, "invalid pattern: %s", *patp); + ret = 1; + } + } + + return ret; +} + +/* + * Save the current state of pattern disables, returning the saved value. + */ + +/**/ +unsigned int +savepatterndisables(void) +{ + unsigned int disables, bit; + char *disp; + + disables = 0; + for (bit = 1, disp = zpc_disables; + disp < zpc_disables + ZPC_COUNT; + bit <<= 1, disp++) { + if (*disp) + disables |= bit; + } + return disables; +} + +/* + * Function scope saving pattern enables. + */ + +/**/ +void +startpatternscope(void) +{ + Zpc_disables_save newdis; + + newdis = (Zpc_disables_save)zalloc(sizeof(*newdis)); + newdis->next = zpc_disables_stack; + newdis->disables = savepatterndisables(); + + zpc_disables_stack = newdis; +} + +/* + * Restore completely the state of pattern disables. + */ + +/**/ +void +restorepatterndisables(unsigned int disables) +{ + char *disp; + unsigned int bit; + + for (bit = 1, disp = zpc_disables; + disp < zpc_disables + ZPC_COUNT; + bit <<= 1, disp++) { + if (disables & bit) + *disp = 1; + else + *disp = 0; + } +} + +/* + * Function scope to restore pattern enables if localpatterns is turned on. + */ + +/**/ +void +endpatternscope(void) +{ + Zpc_disables_save olddis; + + olddis = zpc_disables_stack; + zpc_disables_stack = olddis->next; + + if (isset(LOCALPATTERNS)) + restorepatterndisables(olddis->disables); + + zfree(olddis, sizeof(*olddis)); +} + +/* Reinitialise pattern disables */ + +/**/ +void +clearpatterndisables(void) +{ + memset(zpc_disables, 0, ZPC_COUNT); +} + + +/* Check to see if str is eligible for filename generation. */ + +/**/ +mod_export int +haswilds(char *str) +{ + char *start; + + /* `[' and `]' are legal even if bad patterns are usually not. */ + if ((*str == Inbrack || *str == Outbrack) && !str[1]) + return 0; + + /* If % is immediately followed by ?, then that ? is * + * not treated as a wildcard. This is so you don't have * + * to escape job references such as %?foo. */ + if (str[0] == '%' && str[1] == Quest) + str[1] = '?'; + + /* + * Note that at this point zpc_special has not been set up. + */ + start = str; + for (; *str; str++) { + switch (*str) { + case Inpar: + if ((!isset(SHGLOB) && !zpc_disables[ZPC_INPAR]) || + (str > start && isset(KSHGLOB) && + ((str[-1] == Quest && !zpc_disables[ZPC_KSH_QUEST]) || + (str[-1] == Star && !zpc_disables[ZPC_KSH_STAR]) || + (str[-1] == '+' && !zpc_disables[ZPC_KSH_PLUS]) || + (str[-1] == Bang && !zpc_disables[ZPC_KSH_BANG]) || + (str[-1] == '!' && !zpc_disables[ZPC_KSH_BANG2]) || + (str[-1] == '@' && !zpc_disables[ZPC_KSH_AT])))) + return 1; + break; + + case Bar: + if (!zpc_disables[ZPC_BAR]) + return 1; + break; + + case Star: + if (!zpc_disables[ZPC_STAR]) + return 1; + break; + + case Inbrack: + if (!zpc_disables[ZPC_INBRACK]) + return 1; + break; + + case Inang: + if (!zpc_disables[ZPC_INANG]) + return 1; + break; + + case Quest: + if (!zpc_disables[ZPC_QUEST]) + return 1; + break; + + case Pound: + if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH]) + return 1; + break; + + case Hat: + if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HAT]) + return 1; + break; + } + } + return 0; +} diff --git a/dotfiles/system/.zsh/modules/Src/prompt.c b/dotfiles/system/.zsh/modules/Src/prompt.c new file mode 100644 index 0000000..959ed8e --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/prompt.c @@ -0,0 +1,2046 @@ +/* + * prompt.c - construct zsh prompts + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "prompt.pro" + +/* text attribute mask */ + +/**/ +mod_export unsigned txtattrmask; + +/* the command stack for use with %_ in prompts */ + +/**/ +unsigned char *cmdstack; +/**/ +int cmdsp; + +/* parser states, for %_ */ + +static char *cmdnames[CS_COUNT] = { + "for", "while", "repeat", "select", + "until", "if", "then", "else", + "elif", "math", "cond", "cmdor", + "cmdand", "pipe", "errpipe", "foreach", + "case", "function", "subsh", "cursh", + "array", "quote", "dquote", "bquote", + "cmdsubst", "mathsubst", "elif-then", "heredoc", + "heredocd", "brace", "braceparam", "always", +}; + + +struct buf_vars; + +struct buf_vars { +/* Previous set of prompt variables on the stack. */ + + struct buf_vars *last; + +/* The buffer into which an expanded and metafied prompt is being written, * + * and its size. */ + + char *buf; + int bufspc; + +/* bp is the pointer to the current position in the buffer, where the next * + * character will be added. */ + + char *bp; + +/* Position of the start of the current line in the buffer */ + + char *bufline; + +/* bp1 is an auxiliary pointer into the buffer, which when non-NULL is * + * moved whenever the buffer is reallocated. It is used when data is * + * being temporarily held in the buffer. */ + + char *bp1; + +/* The format string, for %-expansion. */ + + char *fm; + +/* Non-zero if truncating the current segment of the buffer. */ + + int truncwidth; + +/* Current level of nesting of %{ / %} sequences. */ + + int dontcount; + +/* Level of %{ / %} surrounding a truncation segment. */ + + int trunccount; + +/* Strings to use for %r and %R (for the spelling prompt). */ + + char *rstring, *Rstring; +}; + +typedef struct buf_vars *Buf_vars; + +/* The currently active prompt output variables */ +static Buf_vars bv; + +/* + * Expand path p; maximum is npath segments where 0 means the whole path. + * If tilde is 1, try and find a named directory to use. + */ + +static void +promptpath(char *p, int npath, int tilde) +{ + char *modp = p; + Nameddir nd; + + if (tilde && ((nd = finddir(p)))) + modp = tricat("~", nd->node.nam, p + strlen(nd->dir)); + + if (npath) { + char *sptr; + if (npath > 0) { + for (sptr = modp + strlen(modp); sptr > modp; sptr--) { + if (*sptr == '/' && !--npath) { + sptr++; + break; + } + } + if (*sptr == '/' && sptr[1] && sptr != modp) + sptr++; + stradd(sptr); + } else { + char cbu; + for (sptr = modp+1; *sptr; sptr++) + if (*sptr == '/' && !++npath) + break; + cbu = *sptr; + *sptr = 0; + stradd(modp); + *sptr = cbu; + } + } else + stradd(modp); + + if (p != modp) + zsfree(modp); +} + +/* + * Perform prompt expansion on a string, putting the result in a + * permanently-allocated string. If ns is non-zero, this string + * may have embedded Inpar and Outpar, which indicate a toggling + * between spacing and non-spacing parts of the prompt, and + * Nularg, which (in a non-spacing sequence) indicates a + * `glitch' space. + * + * txtchangep gives an integer controlling the attributes of + * the prompt. This is for use in zle to maintain the attributes + * consistenly. Other parts of the shell should not need to use it. + */ + +/**/ +mod_export char * +promptexpand(char *s, int ns, char *rs, char *Rs, unsigned int *txtchangep) +{ + struct buf_vars new_vars; + + if(!s) + return ztrdup(""); + + if ((termflags & TERM_UNKNOWN) && (unset(INTERACTIVE))) + init_term(); + + if (isset(PROMPTSUBST)) { + int olderr = errflag; + int oldval = lastval; + + s = dupstring(s); + if (!parsestr(&s)) + singsub(&s); + /* + * We don't need the special Nularg hack here and we're + * going to be using Nularg for other things. + */ + if (*s == Nularg && s[1] == '\0') + *s = '\0'; + + /* + * Ignore errors and status change in prompt substitution. + * However, keep any user interrupt error that occurred. + */ + errflag = olderr | (errflag & ERRFLAG_INT); + lastval = oldval; + } + + memset(&new_vars, 0, sizeof(new_vars)); + new_vars.last = bv; + bv = &new_vars; + + new_vars.rstring = rs; + new_vars.Rstring = Rs; + new_vars.fm = s; + new_vars.bufspc = 256; + new_vars.bp = new_vars.bufline = new_vars.buf = zshcalloc(new_vars.bufspc); + new_vars.bp1 = NULL; + new_vars.truncwidth = 0; + + putpromptchar(1, '\0', txtchangep); + addbufspc(2); + if (new_vars.dontcount) + *new_vars.bp++ = Outpar; + *new_vars.bp = '\0'; + if (!ns) { + /* If zero, Inpar, Outpar and Nularg should be removed. */ + for (new_vars.bp = new_vars.buf; *new_vars.bp; ) { + if (*new_vars.bp == Meta) + new_vars.bp += 2; + else if (*new_vars.bp == Inpar || *new_vars.bp == Outpar || + *new_vars.bp == Nularg) + chuck(new_vars.bp); + else + new_vars.bp++; + } + } + + bv = new_vars.last; + + return new_vars.buf; +} + +/* Parse the argument for %F and %K */ +static int +parsecolorchar(int arg, int is_fg) +{ + if (bv->fm[1] == '{') { + char *ep; + bv->fm += 2; /* skip over F{ */ + if ((ep = strchr(bv->fm, '}'))) { + char oc = *ep, *col, *coll; + *ep = '\0'; + /* expand the contents of the argument so you can use + * %v for example */ + coll = col = promptexpand(bv->fm, 0, NULL, NULL, NULL); + *ep = oc; + arg = match_colour((const char **)&coll, is_fg, 0); + free(col); + bv->fm = ep; + } else { + arg = match_colour((const char **)&bv->fm, is_fg, 0); + if (*bv->fm != '}') + bv->fm--; + } + } else + arg = match_colour(NULL, 1, arg); + return arg; +} + +/* Perform %- and !-expansion as required on a section of the prompt. The * + * section is ended by an instance of endchar. If doprint is 0, the valid * + * % sequences are merely skipped over, and nothing is stored. */ + +/**/ +static int +putpromptchar(int doprint, int endchar, unsigned int *txtchangep) +{ + char *ss, *hostnam; + int t0, arg, test, sep, j, numjobs, len; + struct tm *tm; + struct timespec ts; + time_t timet; + Nameddir nd; + + for (; *bv->fm && *bv->fm != endchar; bv->fm++) { + arg = 0; + if (*bv->fm == '%' && isset(PROMPTPERCENT)) { + int minus = 0; + bv->fm++; + if (*bv->fm == '-') { + minus = 1; + bv->fm++; + } + if (idigit(*bv->fm)) { + arg = zstrtol(bv->fm, &bv->fm, 10); + if (minus) + arg *= -1; + } else if (minus) + arg = -1; + if (*bv->fm == '(') { + int tc, otruncwidth; + + if (idigit(*++bv->fm)) { + arg = zstrtol(bv->fm, &bv->fm, 10); + } else if (arg < 0) { + /* negative numbers don't make sense here */ + arg *= -1; + } + test = 0; + ss = pwd; + switch (tc = *bv->fm) { + case 'c': + case '.': + case '~': + if ((nd = finddir(ss))) { + arg--; + ss += strlen(nd->dir); + } /*FALLTHROUGH*/ + case '/': + case 'C': + /* `/' gives 0, `/any' gives 1, etc. */ + if (*ss && *ss++ == '/' && *ss) + arg--; + for (; *ss; ss++) + if (*ss == '/') + arg--; + if (arg <= 0) + test = 1; + break; + case 't': + case 'T': + case 'd': + case 'D': + case 'w': + timet = time(NULL); + tm = localtime(&timet); + switch (tc) { + case 't': + test = (arg == tm->tm_min); + break; + case 'T': + test = (arg == tm->tm_hour); + break; + case 'd': + test = (arg == tm->tm_mday); + break; + case 'D': + test = (arg == tm->tm_mon); + break; + case 'w': + test = (arg == tm->tm_wday); + break; + } + break; + case '?': + if (lastval == arg) + test = 1; + break; + case '#': + if (geteuid() == (uid_t)arg) + test = 1; + break; + case 'g': + if (getegid() == (gid_t)arg) + test = 1; + break; + case 'j': + for (numjobs = 0, j = 1; j <= maxjob; j++) + if (jobtab[j].stat && jobtab[j].procs && + !(jobtab[j].stat & STAT_NOPRINT)) numjobs++; + if (numjobs >= arg) + test = 1; + break; + case 'l': + *bv->bp = '\0'; + countprompt(bv->bufline, &t0, 0, 0); + if (minus) + t0 = zterm_columns - t0; + if (t0 >= arg) + test = 1; + break; + case 'e': + { + Funcstack fsptr = funcstack; + test = arg; + while (fsptr && test > 0) { + test--; + fsptr = fsptr->prev; + } + test = !test; + } + break; + case 'L': + if (shlvl >= arg) + test = 1; + break; + case 'S': + if (time(NULL) - shtimer.tv_sec >= arg) + test = 1; + break; + case 'v': + if (arrlen_ge(psvar, arg)) + test = 1; + break; + case 'V': + if (psvar && *psvar && arrlen_ge(psvar, arg)) { + if (*psvar[(arg ? arg : 1) - 1]) + test = 1; + } + break; + case '_': + test = (cmdsp >= arg); + break; + case '!': + test = privasserted(); + break; + default: + test = -1; + break; + } + if (!*bv->fm || !(sep = *++bv->fm)) + return 0; + bv->fm++; + /* Don't do the current truncation until we get back */ + otruncwidth = bv->truncwidth; + bv->truncwidth = 0; + if (!putpromptchar(test == 1 && doprint, sep, + txtchangep) || !*++bv->fm || + !putpromptchar(test == 0 && doprint, ')', + txtchangep)) { + bv->truncwidth = otruncwidth; + return 0; + } + bv->truncwidth = otruncwidth; + continue; + } + if (!doprint) + switch(*bv->fm) { + case '[': + while(idigit(*++bv->fm)); + while(*++bv->fm != ']'); + continue; + case '<': + while(*++bv->fm != '<'); + continue; + case '>': + while(*++bv->fm != '>'); + continue; + case 'D': + if(bv->fm[1]=='{') + while(*++bv->fm != '}'); + continue; + default: + continue; + } + switch (*bv->fm) { + case '~': + promptpath(pwd, arg, 1); + break; + case 'd': + case '/': + promptpath(pwd, arg, 0); + break; + case 'c': + case '.': + promptpath(pwd, arg ? arg : 1, 1); + break; + case 'C': + promptpath(pwd, arg ? arg : 1, 0); + break; + case 'N': + promptpath(scriptname ? scriptname : argzero, arg, 0); + break; + case 'h': + case '!': + addbufspc(DIGBUFSIZE); + convbase(bv->bp, curhist, 10); + bv->bp += strlen(bv->bp); + break; + case 'j': + for (numjobs = 0, j = 1; j <= maxjob; j++) + if (jobtab[j].stat && jobtab[j].procs && + !(jobtab[j].stat & STAT_NOPRINT)) numjobs++; + addbufspc(DIGBUFSIZE); + sprintf(bv->bp, "%d", numjobs); + bv->bp += strlen(bv->bp); + break; + case 'M': + queue_signals(); + if ((hostnam = getsparam("HOST"))) + stradd(hostnam); + unqueue_signals(); + break; + case 'm': + if (!arg) + arg++; + queue_signals(); + if (!(hostnam = getsparam("HOST"))) { + unqueue_signals(); + break; + } + if (arg < 0) { + for (ss = hostnam + strlen(hostnam); ss > hostnam; ss--) + if (ss[-1] == '.' && !++arg) + break; + stradd(ss); + } else { + for (ss = hostnam; *ss; ss++) + if (*ss == '.' && !--arg) + break; + stradd(*ss ? dupstrpfx(hostnam, ss - hostnam) : hostnam); + } + unqueue_signals(); + break; + case 'S': + txtchangeset(txtchangep, TXTSTANDOUT, TXTNOSTANDOUT); + txtset(TXTSTANDOUT); + tsetcap(TCSTANDOUTBEG, TSC_PROMPT); + break; + case 's': + txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT); + txtunset(TXTSTANDOUT); + tsetcap(TCSTANDOUTEND, TSC_PROMPT|TSC_DIRTY); + break; + case 'B': + txtchangeset(txtchangep, TXTBOLDFACE, TXTNOBOLDFACE); + txtset(TXTBOLDFACE); + tsetcap(TCBOLDFACEBEG, TSC_PROMPT|TSC_DIRTY); + break; + case 'b': + txtchangeset(txtchangep, TXTNOBOLDFACE, TXTBOLDFACE); + txtunset(TXTBOLDFACE); + tsetcap(TCALLATTRSOFF, TSC_PROMPT|TSC_DIRTY); + break; + case 'U': + txtchangeset(txtchangep, TXTUNDERLINE, TXTNOUNDERLINE); + txtset(TXTUNDERLINE); + tsetcap(TCUNDERLINEBEG, TSC_PROMPT); + break; + case 'u': + txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE); + txtunset(TXTUNDERLINE); + tsetcap(TCUNDERLINEEND, TSC_PROMPT|TSC_DIRTY); + break; + case 'F': + arg = parsecolorchar(arg, 1); + if (arg >= 0 && !(arg & TXTNOFGCOLOUR)) { + txtchangeset(txtchangep, arg & TXT_ATTR_FG_ON_MASK, + TXTNOFGCOLOUR | TXT_ATTR_FG_COL_MASK); + txtunset(TXT_ATTR_FG_COL_MASK); + txtset(arg & TXT_ATTR_FG_ON_MASK); + set_colour_attribute(arg, COL_SEQ_FG, TSC_PROMPT); + break; + } + /* else FALLTHROUGH */ + case 'f': + txtchangeset(txtchangep, TXTNOFGCOLOUR, TXT_ATTR_FG_ON_MASK); + txtunset(TXT_ATTR_FG_ON_MASK); + set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_PROMPT); + break; + case 'K': + arg = parsecolorchar(arg, 0); + if (arg >= 0 && !(arg & TXTNOBGCOLOUR)) { + txtchangeset(txtchangep, arg & TXT_ATTR_BG_ON_MASK, + TXTNOBGCOLOUR | TXT_ATTR_BG_COL_MASK); + txtunset(TXT_ATTR_BG_COL_MASK); + txtset(arg & TXT_ATTR_BG_ON_MASK); + set_colour_attribute(arg, COL_SEQ_BG, TSC_PROMPT); + break; + } + /* else FALLTHROUGH */ + case 'k': + txtchangeset(txtchangep, TXTNOBGCOLOUR, TXT_ATTR_BG_ON_MASK); + txtunset(TXT_ATTR_BG_ON_MASK); + set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_PROMPT); + break; + case '[': + if (idigit(*++bv->fm)) + arg = zstrtol(bv->fm, &bv->fm, 10); + if (!prompttrunc(arg, ']', doprint, endchar, txtchangep)) + return *bv->fm; + break; + case '<': + case '>': + /* Test (minus) here so -0 means "at the right margin" */ + if (minus) { + *bv->bp = '\0'; + countprompt(bv->bufline, &t0, 0, 0); + arg = zterm_columns - t0 + arg; + if (arg <= 0) + arg = 1; + } + if (!prompttrunc(arg, *bv->fm, doprint, endchar, txtchangep)) + return *bv->fm; + break; + case '{': /*}*/ + if (!bv->dontcount++) { + addbufspc(1); + *bv->bp++ = Inpar; + } + if (arg <= 0) + break; + /* else */ + /* FALLTHROUGH */ + case 'G': + if (arg > 0) { + addbufspc(arg); + while (arg--) + *bv->bp++ = Nularg; + } else { + addbufspc(1); + *bv->bp++ = Nularg; + } + break; + case /*{*/ '}': + if (bv->trunccount && bv->trunccount >= bv->dontcount) + return *bv->fm; + if (bv->dontcount && !--bv->dontcount) { + addbufspc(1); + *bv->bp++ = Outpar; + } + break; + case 't': + case '@': + case 'T': + case '*': + case 'w': + case 'W': + case 'D': + { + char *tmfmt, *dd, *tmbuf = NULL; + + switch (*bv->fm) { + case 'T': + tmfmt = "%K:%M"; + break; + case '*': + tmfmt = "%K:%M:%S"; + break; + case 'w': + tmfmt = "%a %f"; + break; + case 'W': + tmfmt = "%m/%d/%y"; + break; + case 'D': + if (bv->fm[1] == '{' /*}*/) { + for (ss = bv->fm + 2; *ss && *ss != /*{*/ '}'; ss++) + if(*ss == '\\' && ss[1]) + ss++; + dd = tmfmt = tmbuf = zalloc(ss - bv->fm); + for (ss = bv->fm + 2; *ss && *ss != /*{*/ '}'; + ss++) { + if(*ss == '\\' && ss[1]) + ss++; + *dd++ = *ss; + } + *dd = 0; + bv->fm = ss - !*ss; + if (!*tmfmt) { + free(tmbuf); + continue; + } + } else + tmfmt = "%y-%m-%d"; + break; + default: + tmfmt = "%l:%M%p"; + break; + } + zgettime(&ts); + tm = localtime(&ts.tv_sec); + /* + * Hack because strftime won't say how + * much space it actually needs. Try to add it + * a few times until it works. Some formats don't + * actually have a length, so we could go on for + * ever. + */ + for(j = 0, t0 = strlen(tmfmt)*8; j < 3; j++, t0*=2) { + addbufspc(t0); + if ((len = ztrftime(bv->bp, t0, tmfmt, tm, ts.tv_nsec)) + >= 0) + break; + } + /* There is enough room for this because addbufspc(t0) + * allocates room for t0 * 2 bytes. */ + if (len >= 0) + metafy(bv->bp, len, META_NOALLOC); + bv->bp += strlen(bv->bp); + zsfree(tmbuf); + break; + } + case 'n': + stradd(get_username()); + break; + case 'l': + if (*ttystrname) { + ss = (strncmp(ttystrname, "/dev/tty", 8) ? + ttystrname + 5 : ttystrname + 8); + stradd(ss); + } else + stradd("()"); + break; + case 'y': + if (*ttystrname) { + ss = (strncmp(ttystrname, "/dev/", 5) ? + ttystrname : ttystrname + 5); + stradd(ss); + } else + stradd("()"); + break; + case 'L': + addbufspc(DIGBUFSIZE); +#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) + sprintf(bv->bp, "%lld", shlvl); +#else + sprintf(bv->bp, "%ld", (long)shlvl); +#endif + bv->bp += strlen(bv->bp); + break; + case '?': + addbufspc(DIGBUFSIZE); +#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) + sprintf(bv->bp, "%lld", lastval); +#else + sprintf(bv->bp, "%ld", (long)lastval); +#endif + bv->bp += strlen(bv->bp); + break; + case '%': + case ')': + addbufspc(1); + *bv->bp++ = *bv->fm; + break; + case '#': + addbufspc(1); + *bv->bp++ = privasserted() ? '#' : '%'; + break; + case 'v': + if (!arg) + arg = 1; + else if (arg < 0) + arg += arrlen(psvar) + 1; + if (arg > 0 && arrlen_ge(psvar, arg)) + stradd(psvar[arg - 1]); + break; + case 'E': + tsetcap(TCCLEAREOL, TSC_PROMPT); + break; + case '^': + if (cmdsp) { + if (arg >= 0) { + if (arg > cmdsp || arg == 0) + arg = cmdsp; + for (t0 = cmdsp - 1; arg--; t0--) { + stradd(cmdnames[cmdstack[t0]]); + if (arg) { + addbufspc(1); + *bv->bp++=' '; + } + } + } else { + arg = -arg; + if (arg > cmdsp) + arg = cmdsp; + for (t0 = arg - 1; arg--; t0--) { + stradd(cmdnames[cmdstack[t0]]); + if (arg) { + addbufspc(1); + *bv->bp++=' '; + } + } + } + } + break; + case '_': + if (cmdsp) { + if (arg >= 0) { + if (arg > cmdsp || arg == 0) + arg = cmdsp; + for (t0 = cmdsp - arg; arg--; t0++) { + stradd(cmdnames[cmdstack[t0]]); + if (arg) { + addbufspc(1); + *bv->bp++=' '; + } + } + } else { + arg = -arg; + if (arg > cmdsp) + arg = cmdsp; + for (t0 = 0; arg--; t0++) { + stradd(cmdnames[cmdstack[t0]]); + if (arg) { + addbufspc(1); + *bv->bp++=' '; + } + } + } + } + break; + case 'r': + if(bv->rstring) + stradd(bv->rstring); + break; + case 'R': + if(bv->Rstring) + stradd(bv->Rstring); + break; + case 'e': + { + int depth = 0; + Funcstack fsptr = funcstack; + while (fsptr) { + depth++; + fsptr = fsptr->prev; + } + addbufspc(DIGBUFSIZE); + sprintf(bv->bp, "%d", depth); + bv->bp += strlen(bv->bp); + break; + } + case 'I': + if (funcstack && funcstack->tp != FS_SOURCE && + !IN_EVAL_TRAP()) { + /* + * We're in a function or an eval with + * EVALLINENO. Calculate the line number in + * the file. + */ + zlong flineno = lineno + funcstack->flineno; + /* take account of eval line nos. starting at 1 */ + if (funcstack->tp == FS_EVAL) + lineno--; + addbufspc(DIGBUFSIZE); +#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) + sprintf(bv->bp, "%lld", flineno); +#else + sprintf(bv->bp, "%ld", (long)flineno); +#endif + bv->bp += strlen(bv->bp); + break; + } + /* else we're in a file and lineno is already correct */ + /* FALLTHROUGH */ + case 'i': + addbufspc(DIGBUFSIZE); +#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) + sprintf(bv->bp, "%lld", lineno); +#else + sprintf(bv->bp, "%ld", (long)lineno); +#endif + bv->bp += strlen(bv->bp); + break; + case 'x': + if (funcstack && funcstack->tp != FS_SOURCE && + !IN_EVAL_TRAP()) + promptpath(funcstack->filename ? funcstack->filename : "", + arg, 0); + else + promptpath(scriptfilename ? scriptfilename : argzero, + arg, 0); + break; + case '\0': + return 0; + case Meta: + bv->fm++; + break; + } + } else if(*bv->fm == '!' && isset(PROMPTBANG)) { + if(doprint) { + if(bv->fm[1] == '!') { + bv->fm++; + addbufspc(1); + pputc('!'); + } else { + addbufspc(DIGBUFSIZE); + convbase(bv->bp, curhist, 10); + bv->bp += strlen(bv->bp); + } + } + } else { + char c = *bv->fm == Meta ? *++bv->fm ^ 32 : *bv->fm; + + if (doprint) { + addbufspc(1); + pputc(c); + } + } + } + + return *bv->fm; +} + +/* pputc adds a character to the buffer, metafying. There must * + * already be space. */ + +/**/ +static void +pputc(char c) +{ + if (imeta(c)) { + *bv->bp++ = Meta; + c ^= 32; + } + *bv->bp++ = c; + if (c == '\n' && !bv->dontcount) + bv->bufline = bv->bp; +} + +/* Make sure there is room for `need' more characters in the buffer. */ + +/**/ +static void +addbufspc(int need) +{ + need *= 2; /* for metafication */ + if((bv->bp - bv->buf) + need > bv->bufspc) { + int bo = bv->bp - bv->buf; + int bo1 = bv->bp1 ? bv->bp1 - bv->buf : -1; + ptrdiff_t bufline_off = bv->bufline ? bv->bufline - bv->buf : -1; + + if(need & 255) + need = (need | 255) + 1; + bv->buf = realloc(bv->buf, bv->bufspc += need); + memset(bv->buf + bv->bufspc - need, 0, need); + bv->bp = bv->buf + bo; + if(bo1 != -1) + bv->bp1 = bv->buf + bo1; + if (bufline_off != -1) + bv->bufline = bv->buf + bufline_off; + } +} + +/* stradd() adds a metafied string to the prompt, * + * in a visible representation. */ + +/**/ +void +stradd(char *d) +{ +#ifdef MULTIBYTE_SUPPORT + char *ums, *ups; + int upslen, eol = 0; + mbstate_t mbs; + + memset(&mbs, 0, sizeof mbs); + ums = ztrdup(d); + ups = unmetafy(ums, &upslen); + + /* + * We now have a raw string of possibly multibyte characters. + * Read each character one by one. + */ + while (upslen > 0) { + wchar_t cc; + char *pc; + size_t cnt = eol ? MB_INVALID : mbrtowc(&cc, ups, upslen, &mbs); + + switch (cnt) { + case MB_INCOMPLETE: + eol = 1; + /* FALL THROUGH */ + case MB_INVALID: + /* Bad character. Take the next byte on its own. */ + pc = nicechar(*ups); + cnt = 1; + memset(&mbs, 0, sizeof mbs); + break; + case 0: + cnt = 1; + /* FALL THROUGH */ + default: + /* Take full wide character in one go */ + mb_charinit(); + pc = wcs_nicechar(cc, NULL, NULL); + break; + } + /* Keep output as metafied string. */ + addbufspc(strlen(pc)); + + upslen -= cnt; + ups += cnt; + + /* Put printed representation into the buffer */ + while (*pc) + *bv->bp++ = *pc++; + } + + free(ums); +#else + char *ps, *pc; + addbufspc(niceztrlen(d)); + /* This loop puts the nice representation of the string into the + * prompt buffer. */ + for (ps = d; *ps; ps++) { + for (pc = nicechar(*ps == Meta ? *++ps^32 : *ps); *pc; pc++) + *bv->bp++ = *pc; + } +#endif +} + +/* tsetcap(), among other things, can write a termcap string into the buffer. */ + +/**/ +mod_export void +tsetcap(int cap, int flags) +{ + if (tccan(cap) && !isset(SINGLELINEZLE) && + !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) { + switch (flags & TSC_OUTPUT_MASK) { + case TSC_RAW: + tputs(tcstr[cap], 1, putraw); + break; + case 0: + default: + tputs(tcstr[cap], 1, putshout); + break; + case TSC_PROMPT: + if (!bv->dontcount) { + addbufspc(1); + *bv->bp++ = Inpar; + } + tputs(tcstr[cap], 1, putstr); + if (!bv->dontcount) { + int glitch = 0; + + if (cap == TCSTANDOUTBEG || cap == TCSTANDOUTEND) + glitch = tgetnum("sg"); + else if (cap == TCUNDERLINEBEG || cap == TCUNDERLINEEND) + glitch = tgetnum("ug"); + if(glitch < 0) + glitch = 0; + addbufspc(glitch + 1); + while(glitch--) + *bv->bp++ = Nularg; + *bv->bp++ = Outpar; + } + break; + } + + if (flags & TSC_DIRTY) { + flags &= ~TSC_DIRTY; + if (txtisset(TXTBOLDFACE) && cap != TCBOLDFACEBEG) + tsetcap(TCBOLDFACEBEG, flags); + if (txtisset(TXTSTANDOUT)) + tsetcap(TCSTANDOUTBEG, flags); + if (txtisset(TXTUNDERLINE)) + tsetcap(TCUNDERLINEBEG, flags); + if (txtisset(TXTFGCOLOUR)) + set_colour_attribute(txtattrmask, COL_SEQ_FG, TSC_PROMPT); + if (txtisset(TXTBGCOLOUR)) + set_colour_attribute(txtattrmask, COL_SEQ_BG, TSC_PROMPT); + } + } +} + +/**/ +int +putstr(int d) +{ + addbufspc(1); + pputc(d); + return 0; +} + +/* + * Count height etc. of a prompt string returned by promptexpand(). + * This depends on the current terminal width, and tabs and + * newlines require nontrivial processing. + * Passing `overf' as -1 means to ignore columns (absolute width). + * + * If multibyte is enabled, take account of multibyte characters + * by locating them and finding out their screen width. + */ + +/**/ +mod_export void +countprompt(char *str, int *wp, int *hp, int overf) +{ + int w = 0, h = 1; + int s = 1; +#ifdef MULTIBYTE_SUPPORT + int wcw, multi = 0; + char inchar; + mbstate_t mbs; + wchar_t wc; + + memset(&mbs, 0, sizeof(mbs)); +#endif + + for (; *str; str++) { + if (w > zterm_columns && overf >= 0) { + w = 0; + h++; + } + /* + * Input string should be metafied, so tokens in it should + * be real tokens, even if there are multibyte characters. + */ + if (*str == Inpar) + s = 0; + else if (*str == Outpar) + s = 1; + else if (*str == Nularg) + w++; + else if (s) { + if (*str == Meta) { +#ifdef MULTIBYTE_SUPPORT + inchar = *++str ^ 32; +#else + str++; +#endif + } else { +#ifdef MULTIBYTE_SUPPORT + /* + * Don't look for tab or newline in the middle + * of a multibyte character. Otherwise, we are + * relying on the character set being an extension + * of ASCII so it's safe to test a single byte. + */ + if (!multi) { +#endif + if (*str == '\t') { + w = (w | 7) + 1; + continue; + } else if (*str == '\n') { + w = 0; + h++; + continue; + } +#ifdef MULTIBYTE_SUPPORT + } + + inchar = *str; +#endif + } + +#ifdef MULTIBYTE_SUPPORT + switch (mbrtowc(&wc, &inchar, 1, &mbs)) { + case MB_INCOMPLETE: + /* Character is incomplete -- keep looking. */ + multi = 1; + break; + case MB_INVALID: + memset(&mbs, 0, sizeof mbs); + /* Invalid character: assume single width. */ + multi = 0; + w++; + break; + case 0: + multi = 0; + break; + default: + /* + * If the character isn't printable, WCWIDTH() returns + * -1. We assume width 1. + */ + wcw = WCWIDTH(wc); + if (wcw >= 0) + w += wcw; + else + w++; + multi = 0; + break; + } +#else + w++; +#endif + } + } + /* + * multi may still be set if we were in the middle of the character. + * This isn't easy to handle generally; just assume there's no + * output. + */ + if(w >= zterm_columns && overf >= 0) { + if (!overf || w > zterm_columns) { + w = 0; + h++; + } + } + if(wp) + *wp = w; + if(hp) + *hp = h; +} + +/**/ +static int +prompttrunc(int arg, int truncchar, int doprint, int endchar, + unsigned int *txtchangep) +{ + if (arg > 0) { + char ch = *bv->fm, *ptr, *truncstr; + int truncatleft = ch == '<'; + int w = bv->bp - bv->buf; + + /* + * If there is already a truncation active, return so that + * can be finished, backing up so that the new truncation + * can be started afterwards. + */ + if (bv->truncwidth) { + while (*--bv->fm != '%') + ; + bv->fm--; + return 0; + } + + bv->truncwidth = arg; + if (*bv->fm != ']') + bv->fm++; + while (*bv->fm && *bv->fm != truncchar) { + if (*bv->fm == '\\' && bv->fm[1]) + ++bv->fm; + addbufspc(1); + *bv->bp++ = *bv->fm++; + } + if (!*bv->fm) + return 0; + if (bv->bp - bv->buf == w && truncchar == ']') { + addbufspc(1); + *bv->bp++ = '<'; + } + ptr = bv->buf + w; /* addbufspc() may have realloc()'d bv->buf */ + /* + * Now: + * bv->buf is the start of the output prompt buffer + * ptr is the start of the truncation string + * bv->bp is the end of the truncation string + */ + truncstr = ztrduppfx(ptr, bv->bp - ptr); + + bv->bp = ptr; + w = bv->bp - bv->buf; + bv->fm++; + bv->trunccount = bv->dontcount; + putpromptchar(doprint, endchar, txtchangep); + bv->trunccount = 0; + ptr = bv->buf + w; /* putpromptchar() may have realloc()'d */ + *bv->bp = '\0'; + /* + * Now: + * ptr is the start of the truncation string and also + * where we need to start putting any truncated output + * bv->bp is the end of the string we have just added, which + * may need truncating. + */ + + /* + * w below is screen width if multibyte support is enabled + * (note that above it was a raw string pointer difference). + * It's the full width of the string we may need to truncate. + * + * bv->truncwidth has come from the user, so we interpret this + * as a screen width, too. + */ + countprompt(ptr, &w, 0, -1); + if (w > bv->truncwidth) { + /* + * We need to truncate. t points to the truncation string + * -- which is inserted literally, without nice + * representation. twidth is its printing width, and maxwidth + * is the amount of the main string that we want to keep. + * Note that if the truncation string is longer than the + * truncation length (twidth > bv->truncwidth), the truncation + * string is used in full. + */ + char *t = truncstr; + int fullen = bv->bp - ptr; + int twidth, maxwidth; + int ntrunc = strlen(t); + + twidth = MB_METASTRWIDTH(t); + if (twidth < bv->truncwidth) { + maxwidth = bv->truncwidth - twidth; + /* + * It's not safe to assume there are no invisible substrings + * just because the width is less than the full string + * length since there may be multibyte characters. + */ + addbufspc(ntrunc+1); + /* may have realloc'd */ + ptr = bv->bp - fullen; + + if (truncatleft) { + /* + * To truncate at the left, selectively copy + * maxwidth bytes from the main prompt, preceded + * by the truncation string in full. + * + * We're overwriting the string containing the + * text to be truncated, so copy it. We've + * just ensured there's sufficient space at the + * end of the prompt string. + * + * Pointer into text to be truncated. + */ + char *fulltextptr, *fulltext; + int remw; +#ifdef MULTIBYTE_SUPPORT + mbstate_t mbs; + memset(&mbs, 0, sizeof mbs); +#endif + + fulltextptr = fulltext = ptr + ntrunc; + memmove(fulltext, ptr, fullen); + fulltext[fullen] = '\0'; + + /* Copy the truncstr into place. */ + while (*t) + *ptr++ = *t++; + + /* + * Find the point in the text at which we should + * start copying, i.e. when the remaining width + * is less than or equal to the maximum width. + */ + remw = w; + while (remw > maxwidth && *fulltextptr) { + if (*fulltextptr == Inpar) { + /* + * Text marked as invisible: copy + * regardless, since we don't know what + * this does. It only affects the width + * if there are Nularg's present. + * However, even in that case we + * can't break the sequence down, so + * we still loop over the entire group. + */ + for (;;) { + *ptr++ = *fulltextptr; + if (*fulltextptr == '\0' || + *fulltextptr++ == Outpar) + break; + if (fulltextptr[-1] == Nularg) + remw--; + } + } else { +#ifdef MULTIBYTE_SUPPORT + /* + * Normal text: build up a multibyte character. + */ + char inchar; + wchar_t cc; + int wcw; + + /* + * careful: string is still metafied (we + * need that because we don't know a + * priori when to stop and the resulting + * string must be metafied). + */ + if (*fulltextptr == Meta) + inchar = *++fulltextptr ^ 32; + else + inchar = *fulltextptr; + fulltextptr++; + switch (mbrtowc(&cc, &inchar, 1, &mbs)) { + case MB_INCOMPLETE: + /* Incomplete multibyte character. */ + break; + case MB_INVALID: + /* Reset invalid state. */ + memset(&mbs, 0, sizeof mbs); + /* FALL THROUGH */ + case 0: + /* Assume a single-byte character. */ + remw--; + break; + default: + wcw = WCWIDTH(cc); + if (wcw >= 0) + remw -= wcw; + else + remw--; + break; + } +#else + /* Single byte character */ + if (*fulltextptr == Meta) + fulltextptr++; + fulltextptr++; + remw--; +#endif + } + } + + /* + * Now simply copy the rest of the text. Still + * metafied, so this is easy. + */ + while (*fulltextptr) + *ptr++ = *fulltextptr++; + /* Mark the end of copying */ + bv->bp = ptr; + } else { + /* + * Truncating at the right is easier: just leave + * enough characters until we have reached the + * maximum width. + */ + char *skiptext = ptr; +#ifdef MULTIBYTE_SUPPORT + mbstate_t mbs; + memset(&mbs, 0, sizeof mbs); +#endif + + while (maxwidth > 0 && *skiptext) { + if (*skiptext == Inpar) { + /* see comment on left truncation above */ + for (;;) { + if (*skiptext == '\0' || + *skiptext++ == Outpar) + break; + if (skiptext[-1] == Nularg) + maxwidth--; + } + } else { +#ifdef MULTIBYTE_SUPPORT + char inchar; + wchar_t cc; + int wcw; + + if (*skiptext == Meta) + inchar = *++skiptext ^ 32; + else + inchar = *skiptext; + skiptext++; + switch (mbrtowc(&cc, &inchar, 1, &mbs)) { + case MB_INCOMPLETE: + /* Incomplete character. */ + break; + case MB_INVALID: + /* Reset invalid state. */ + memset(&mbs, 0, sizeof mbs); + /* FALL THROUGH */ + case 0: + /* Assume a single-byte character. */ + maxwidth--; + break; + default: + wcw = WCWIDTH(cc); + if (wcw >= 0) + maxwidth -= wcw; + else + maxwidth--; + break; + } +#else + if (*skiptext == Meta) + skiptext++; + skiptext++; + maxwidth--; +#endif + } + } + /* + * We don't need the visible text from now on, + * but we'd better copy any invisible bits. + * History dictates that these go after the + * truncation string. This is sensible since + * they may, for example, turn off an effect which + * should apply to all text at this point. + * + * Copy the truncstr. + */ + ptr = skiptext; + while (*t) + *ptr++ = *t++; + bv->bp = ptr; + if (*skiptext) { + /* Move remaining text so we don't overwrite it */ + memmove(bv->bp, skiptext, strlen(skiptext)+1); + skiptext = bv->bp; + + /* + * Copy anything we want, updating bv->bp + */ + while (*skiptext) { + if (*skiptext == Inpar) { + for (;;) { + *bv->bp++ = *skiptext; + if (*skiptext == Outpar || + *skiptext == '\0') + break; + skiptext++; + } + } + else + skiptext++; + } + } + } + } else { + /* Just copy truncstr; no other text appears. */ + while (*t) + *ptr++ = *t++; + bv->bp = ptr; + } + *bv->bp = '\0'; + } + zsfree(truncstr); + bv->truncwidth = 0; + /* + * We may have returned early from the previous putpromptchar * + * because we found another truncation following this one. * + * In that case we need to do the rest now. * + */ + if (!*bv->fm) + return 0; + if (*bv->fm != endchar) { + bv->fm++; + /* + * With bv->truncwidth set to zero, we always reach endchar * + * (or the terminating NULL) this time round. * + */ + if (!putpromptchar(doprint, endchar, txtchangep)) + return 0; + } + /* Now we have to trick it into matching endchar again */ + bv->fm--; + } else { + if (*bv->fm != endchar) + bv->fm++; + while(*bv->fm && *bv->fm != truncchar) { + if (*bv->fm == '\\' && bv->fm[1]) + bv->fm++; + bv->fm++; + } + if (bv->truncwidth || !*bv->fm) + return 0; + } + return 1; +} + +/**/ +void +cmdpush(int cmdtok) +{ + if (cmdsp >= 0 && cmdsp < CMDSTACKSZ) + cmdstack[cmdsp++] = (unsigned char)cmdtok; +} + +/**/ +void +cmdpop(void) +{ + if (cmdsp <= 0) { + DPUTS(1, "BUG: cmdstack empty"); + fflush(stderr); + } else + cmdsp--; +} + + +/***************************************************************************** + * Utilities dealing with colour and other forms of highlighting. + * + * These are shared by prompts and by zle, so it's easiest to have them + * in the main shell. + *****************************************************************************/ + +/* Defines standard ANSI colour names in index order */ +static const char *ansi_colours[] = { + "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", + "default", NULL +}; + +/* Defines the available types of highlighting */ +struct highlight { + const char *name; + int mask_on; + int mask_off; +}; + +static const struct highlight highlights[] = { + { "none", 0, TXT_ATTR_ON_MASK }, + { "bold", TXTBOLDFACE, 0 }, + { "standout", TXTSTANDOUT, 0 }, + { "underline", TXTUNDERLINE, 0 }, + { NULL, 0, 0 } +}; + +/* + * Return index of ANSI colour for which *teststrp is an abbreviation. + * Any non-alphabetic character ends the abbreviation. + * 8 is the special value for default (note this is *not* the + * right sequence for default which is typically 9). + * -1 is failure. + */ + +static int +match_named_colour(const char **teststrp) +{ + const char *teststr = *teststrp, *end, **cptr; + int len; + + for (end = teststr; ialpha(*end); end++) + ; + len = end - teststr; + *teststrp = end; + + for (cptr = ansi_colours; *cptr; cptr++) { + if (!strncmp(teststr, *cptr, len)) + return cptr - ansi_colours; + } + + return -1; +} + +/* + * Match just the colour part of a highlight specification. + * If teststrp is NULL, use the already parsed numeric colour. + * Return the attributes to set in the attribute variable. + * Return -1 for out of range. Does not check the character + * following the colour specification. + */ + +/**/ +mod_export int +match_colour(const char **teststrp, int is_fg, int colour) +{ + int shft, on, named = 0, tc; + + if (teststrp) { + if ((named = ialpha(**teststrp))) { + colour = match_named_colour(teststrp); + if (colour == 8) { + /* default */ + return is_fg ? TXTNOFGCOLOUR : TXTNOBGCOLOUR; + } + } + else + colour = (int)zstrtol(*teststrp, (char **)teststrp, 10); + } + if (colour < 0 || colour >= 256) + return -1; + if (is_fg) { + shft = TXT_ATTR_FG_COL_SHIFT; + on = TXTFGCOLOUR; + tc = TCFGCOLOUR; + } else { + shft = TXT_ATTR_BG_COL_SHIFT; + on = TXTBGCOLOUR; + tc = TCBGCOLOUR; + } + /* + * Try termcap for numbered characters if posible. + * Don't for named characters, since our best bet + * of getting the names right is with ANSI sequences. + */ + if (!named && tccan(tc)) { + if (tccolours >= 0 && colour >= tccolours) { + /* + * Out of range of termcap colours. + * Can we assume ANSI colours work? + */ + if (colour > 7) + return -1; /* No. */ + } else { + /* + * We can handle termcap colours and the number + * is in range, so use termcap. + */ + on |= is_fg ? TXT_ATTR_FG_TERMCAP : + TXT_ATTR_BG_TERMCAP; + } + } + return on | (colour << shft); +} + +/* + * Match a set of highlights in the given teststr. + * Set *on_var to reflect the values found. + */ + +/**/ +mod_export void +match_highlight(const char *teststr, int *on_var) +{ + int found = 1; + + *on_var = 0; + while (found && *teststr) { + const struct highlight *hl; + + found = 0; + if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) { + int is_fg = (teststr[0] == 'f'), atr; + + teststr += 3; + atr = match_colour(&teststr, is_fg, 0); + if (*teststr == ',') + teststr++; + else if (*teststr) + break; + found = 1; + /* skip out of range colours but keep scanning attributes */ + if (atr >= 0) + *on_var |= atr; + } else { + for (hl = highlights; hl->name; hl++) { + if (strpfx(hl->name, teststr)) { + const char *val = teststr + strlen(hl->name); + + if (*val == ',') + val++; + else if (*val) + break; + + *on_var |= hl->mask_on; + *on_var &= ~hl->mask_off; + teststr = val; + found = 1; + } + } + } + } +} + +/* + * Count or output a string for colour information: used + * by output_highlight(). + */ + +static int +output_colour(int colour, int fg_bg, int use_tc, char *buf) +{ + int atrlen = 3, len; + char *ptr = buf; + if (buf) { + strcpy(ptr, fg_bg == COL_SEQ_FG ? "fg=" : "bg="); + ptr += 3; + } + /* colour should only be > 7 if using termcap but let's be safe */ + if (use_tc || colour > 7) { + char digbuf[DIGBUFSIZE]; + sprintf(digbuf, "%d", colour); + len = strlen(digbuf); + atrlen += len; + if (buf) + strcpy(ptr, digbuf); + } else { + len = strlen(ansi_colours[colour]); + atrlen += len; + if (buf) + strcpy(ptr, ansi_colours[colour]); + } + + return atrlen; +} + +/* + * Count the length needed for outputting highlighting information + * as a string based on the bits for the attributes. + * + * If buf is not NULL, output the strings into the buffer, too. + * As conventional with strings, the allocated length should be + * at least the returned value plus 1 for the NUL byte. + */ + +/**/ +mod_export int +output_highlight(int atr, char *buf) +{ + const struct highlight *hp; + int atrlen = 0, len; + char *ptr = buf; + + if (atr & TXTFGCOLOUR) { + len = output_colour(txtchangeget(atr, TXT_ATTR_FG_COL), + COL_SEQ_FG, + (atr & TXT_ATTR_FG_TERMCAP), + ptr); + atrlen += len; + if (buf) + ptr += len; + } + if (atr & TXTBGCOLOUR) { + if (atrlen) { + atrlen++; + if (buf) { + strcpy(ptr, ","); + ptr++; + } + } + len = output_colour(txtchangeget(atr, TXT_ATTR_BG_COL), + COL_SEQ_BG, + (atr & TXT_ATTR_BG_TERMCAP), + ptr); + atrlen += len; + if (buf) + ptr += len; + } + for (hp = highlights; hp->name; hp++) { + if (hp->mask_on & atr) { + if (atrlen) { + atrlen++; + if (buf) { + strcpy(ptr, ","); + ptr++; + } + } + len = strlen(hp->name); + atrlen += len; + if (buf) { + strcpy(ptr, hp->name); + ptr += len; + } + } + } + + if (atrlen == 0) { + if (buf) + strcpy(ptr, "none"); + return 4; + } + return atrlen; +} + +/* Structure and array for holding special colour terminal sequences */ + +/* Start of escape sequence for foreground colour */ +#define TC_COL_FG_START "\033[3" +/* End of escape sequence for foreground colour */ +#define TC_COL_FG_END "m" +/* Code to reset foreground colour */ +#define TC_COL_FG_DEFAULT "9" + +/* Start of escape sequence for background colour */ +#define TC_COL_BG_START "\033[4" +/* End of escape sequence for background colour */ +#define TC_COL_BG_END "m" +/* Code to reset background colour */ +#define TC_COL_BG_DEFAULT "9" + +struct colour_sequences { + char *start; /* Escape sequence start */ + char *end; /* Escape sequence terminator */ + char *def; /* Code to reset default colour */ +}; +static struct colour_sequences fg_bg_sequences[2]; + +/* + * We need a buffer for colour sequence composition. It may + * vary depending on the sequences set. However, it's inefficient + * allocating it separately every time we send a colour sequence, + * so do it once per refresh. + */ +static char *colseq_buf; + +/* + * Count how often this has been allocated, for recursive usage. + */ +static int colseq_buf_allocs; + +/**/ +void +set_default_colour_sequences(void) +{ + fg_bg_sequences[COL_SEQ_FG].start = ztrdup(TC_COL_FG_START); + fg_bg_sequences[COL_SEQ_FG].end = ztrdup(TC_COL_FG_END); + fg_bg_sequences[COL_SEQ_FG].def = ztrdup(TC_COL_FG_DEFAULT); + + fg_bg_sequences[COL_SEQ_BG].start = ztrdup(TC_COL_BG_START); + fg_bg_sequences[COL_SEQ_BG].end = ztrdup(TC_COL_BG_END); + fg_bg_sequences[COL_SEQ_BG].def = ztrdup(TC_COL_BG_DEFAULT); +} + +static void +set_colour_code(char *str, char **var) +{ + char *keyseq; + int len; + + zsfree(*var); + keyseq = getkeystring(str, &len, GETKEYS_BINDKEY, NULL); + *var = metafy(keyseq, len, META_DUP); +} + +/* Allocate buffer for colour code composition */ + +/**/ +mod_export void +allocate_colour_buffer(void) +{ + char **atrs; + int lenfg, lenbg, len; + + if (colseq_buf_allocs++) + return; + + atrs = getaparam("zle_highlight"); + if (atrs) { + for (; *atrs; atrs++) { + if (strpfx("fg_start_code:", *atrs)) { + set_colour_code(*atrs + 14, &fg_bg_sequences[COL_SEQ_FG].start); + } else if (strpfx("fg_default_code:", *atrs)) { + set_colour_code(*atrs + 16, &fg_bg_sequences[COL_SEQ_FG].def); + } else if (strpfx("fg_end_code:", *atrs)) { + set_colour_code(*atrs + 12, &fg_bg_sequences[COL_SEQ_FG].end); + } else if (strpfx("bg_start_code:", *atrs)) { + set_colour_code(*atrs + 14, &fg_bg_sequences[COL_SEQ_BG].start); + } else if (strpfx("bg_default_code:", *atrs)) { + set_colour_code(*atrs + 16, &fg_bg_sequences[COL_SEQ_BG].def); + } else if (strpfx("bg_end_code:", *atrs)) { + set_colour_code(*atrs + 12, &fg_bg_sequences[COL_SEQ_BG].end); + } + } + } + + lenfg = strlen(fg_bg_sequences[COL_SEQ_FG].def); + /* always need 1 character for non-default code */ + if (lenfg < 1) + lenfg = 1; + lenfg += strlen(fg_bg_sequences[COL_SEQ_FG].start) + + strlen(fg_bg_sequences[COL_SEQ_FG].end); + + lenbg = strlen(fg_bg_sequences[COL_SEQ_BG].def); + /* always need 1 character for non-default code */ + if (lenbg < 1) + lenbg = 1; + lenbg += strlen(fg_bg_sequences[COL_SEQ_BG].start) + + strlen(fg_bg_sequences[COL_SEQ_BG].end); + + len = lenfg > lenbg ? lenfg : lenbg; + colseq_buf = (char *)zalloc(len+1); +} + +/* Free the colour buffer previously allocated. */ + +/**/ +mod_export void +free_colour_buffer(void) +{ + if (--colseq_buf_allocs) + return; + + DPUTS(!colseq_buf, "Freeing colour sequence buffer without alloc"); + /* Free buffer for colour code composition */ + free(colseq_buf); + colseq_buf = NULL; +} + +/* + * Handle outputting of a colour for prompts or zle. + * colour is the numeric colour, 0 to 255 (or less if termcap + * says fewer are supported). + * fg_bg indicates if we're changing the foreground or background. + * tc indicates the termcap code to use, if appropriate. + * def indicates if we're resetting the default colour. + * use_termcap indicates if we should use termcap to output colours. + * flags is either 0 or TSC_PROMPT. + */ + +/**/ +mod_export void +set_colour_attribute(int atr, int fg_bg, int flags) +{ + char *ptr; + int do_free, is_prompt = (flags & TSC_PROMPT) ? 1 : 0; + int colour, tc, def, use_termcap; + + if (fg_bg == COL_SEQ_FG) { + colour = txtchangeget(atr, TXT_ATTR_FG_COL); + tc = TCFGCOLOUR; + def = txtchangeisset(atr, TXTNOFGCOLOUR); + use_termcap = txtchangeisset(atr, TXT_ATTR_FG_TERMCAP); + } else { + colour = txtchangeget(atr, TXT_ATTR_BG_COL); + tc = TCBGCOLOUR; + def = txtchangeisset(atr, TXTNOBGCOLOUR); + use_termcap = txtchangeisset(atr, TXT_ATTR_BG_TERMCAP); + } + + /* + * If we're not restoring the default, and either have a + * colour value that is too large for ANSI, or have been told + * to use the termcap sequence, try to use the termcap sequence. + * + * We have already sanitised the values we allow from the + * highlighting variables, so much of this shouldn't be + * necessary at this point, but we might as well be safe. + */ + if (!def && (colour > 7 || use_termcap)) { + /* + * We can if it's available, and either we couldn't get + * the maximum number of colours, or the colour is in range. + */ + if (tccan(tc) && (tccolours < 0 || colour < tccolours)) + { + if (is_prompt) + { + if (!bv->dontcount) { + addbufspc(1); + *bv->bp++ = Inpar; + } + tputs(tgoto(tcstr[tc], colour, colour), 1, putstr); + if (!bv->dontcount) { + addbufspc(1); + *bv->bp++ = Outpar; + } + } else { + tputs(tgoto(tcstr[tc], colour, colour), 1, putshout); + } + /* That worked. */ + return; + } + /* + * Nope, that didn't work. + * If 0 to 7, assume standard ANSI works, otherwise it won't. + */ + if (colour > 7) + return; + } + + if ((do_free = (colseq_buf == NULL))) { + /* This can happen when moving the cursor in trashzle() */ + allocate_colour_buffer(); + } + + strcpy(colseq_buf, fg_bg_sequences[fg_bg].start); + + ptr = colseq_buf + strlen(colseq_buf); + if (def) { + strcpy(ptr, fg_bg_sequences[fg_bg].def); + while (*ptr) + ptr++; + } else + *ptr++ = colour + '0'; + strcpy(ptr, fg_bg_sequences[fg_bg].end); + + if (is_prompt) { + if (!bv->dontcount) { + addbufspc(1); + *bv->bp++ = Inpar; + } + tputs(colseq_buf, 1, putstr); + if (!bv->dontcount) { + addbufspc(1); + *bv->bp++ = Outpar; + } + } else + tputs(colseq_buf, 1, putshout); + + if (do_free) + free_colour_buffer(); +} diff --git a/dotfiles/system/.zsh/modules/Src/prototypes.h b/dotfiles/system/.zsh/modules/Src/prototypes.h new file mode 100644 index 0000000..e3db4f5 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/prototypes.h @@ -0,0 +1,134 @@ +/* + * prototypes.h - prototypes header file + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#ifndef HAVE_STDLIB_H +char *malloc _((size_t)); +char *realloc _((void *, size_t)); +char *calloc _((size_t, size_t)); +#endif + +#if !(defined(USES_TERMCAP_H) || defined(USES_TERM_H)) +/* + * These prototypes are only used where we don't have the + * headers. In some cases they need tweaking. + * TBD: we'd much prefer to get hold of the header where + * these are defined. + */ +#ifdef _AIX +#define TC_CONST const +#else +#define TC_CONST +#endif +extern int tgetent _((char *bp, TC_CONST char *name)); +extern int tgetnum _((char *id)); +extern int tgetflag _((char *id)); +extern char *tgetstr _((char *id, char **area)); +extern int tputs _((TC_CONST char *cp, int affcnt, int (*outc) (int))); +#undef TC_CONST +#endif + +/* + * Some systems that do have termcap headers nonetheless don't + * declare tgoto, so we detect if that is missing separately. + */ +#ifdef TGOTO_PROTO_MISSING +char *tgoto(const char *cap, int col, int row); +#endif + +/* MISSING PROTOTYPES FOR VARIOUS OPERATING SYSTEMS */ + +#if defined(__hpux) && defined(_HPUX_SOURCE) && !defined(_XPG4_EXTENDED) +# define SELECT_ARG_2_T int * +#else +# define SELECT_ARG_2_T fd_set * +#endif + +#ifdef __osf__ +char *mktemp _((char *)); +#endif + +#if defined(__osf__) && defined(__alpha) && defined(__GNUC__) +/* Digital cc does not need these prototypes, gcc does need them */ +# ifndef HAVE_IOCTL_PROTO +int ioctl _((int d, unsigned long request, void *argp)); +# endif +# ifndef HAVE_MKNOD_PROTO +int mknod _((const char *pathname, int mode, dev_t device)); +# endif +int nice _((int increment)); +int select _((int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout)); +#endif + +#if defined(DGUX) && defined(__STDC__) +/* Just plain missing. */ +extern int getrlimit _((int resource, struct rlimit *rlp)); +extern int setrlimit _((int resource, const struct rlimit *rlp)); +extern int getrusage _((int who, struct rusage *rusage)); +extern int gettimeofday _((struct timeval *tv, struct timezone *tz)); +extern int wait3 _((union wait *wait_status, int options, struct rusage *rusage)); +extern int getdomainname _((char *name, int maxlength)); +extern int select _((int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout)); +#endif /* DGUX and __STDC__ */ + +#ifdef __NeXT__ +extern pid_t getppid(void); +#endif + +#if defined(__sun__) && !defined(__SVR4) /* SunOS */ +extern char *strerror _((int errnum)); +#endif + +/**************************************************/ +/*** prototypes for functions built in compat.c ***/ +#ifndef HAVE_STRSTR +extern char *strstr _((const char *s, const char *t)); +#endif + +#ifndef HAVE_GETHOSTNAME +extern int gethostname _((char *name, size_t namelen)); +#endif + +#ifndef HAVE_GETTIMEOFDAY +extern int gettimeofday _((struct timeval *tv, struct timezone *tz)); +#endif + +#ifndef HAVE_DIFFTIME +extern double difftime _((time_t t2, time_t t1)); +#endif + +#ifndef HAVE_STRERROR +extern char *strerror _((int errnum)); +#endif + +/*** end of prototypes for functions in compat.c ***/ +/***************************************************/ + +#ifndef HAVE_MEMMOVE +extern void bcopy _((const void *, void *, size_t)); +#endif diff --git a/dotfiles/system/.zsh/modules/Src/signals.c b/dotfiles/system/.zsh/modules/Src/signals.c new file mode 100644 index 0000000..20c6fdf --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/signals.c @@ -0,0 +1,1479 @@ +/* + * signals.c - signals handling code + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "signals.pro" + +/* Array describing the state of each signal: an element contains * + * 0 for the default action or some ZSIG_* flags ored together. */ + +/**/ +mod_export int sigtrapped[VSIGCOUNT]; + +/* + * Trap programme lists for each signal. + * + * If (sigtrapped[sig] & ZSIG_FUNC) is set, this isn't used. + * The corresponding shell function is used instead. + * + * Otherwise, if sigtrapped[sig] is not zero, this is NULL when a signal + * is to be ignored, and if not NULL contains the programme list to be + * eval'd. + */ + +/**/ +mod_export Eprog siglists[VSIGCOUNT]; + +/* Total count of trapped signals */ + +/**/ +mod_export int nsigtrapped; + +/* Running an exit trap? */ + +/**/ +int in_exit_trap; + +/* + * Flag that exit trap has been set in POSIX mode. + * The setter's expectation is therefore that it is run + * on programme exit, not function exit. + */ + +/**/ +static int exit_trap_posix; + +/* Variables used by signal queueing */ + +/**/ +mod_export int queueing_enabled, queue_front, queue_rear; +/**/ +mod_export int signal_queue[MAX_QUEUE_SIZE]; +/**/ +mod_export sigset_t signal_mask_queue[MAX_QUEUE_SIZE]; +#ifdef DEBUG +/**/ +mod_export int queue_in; +#endif + +/* Variables used by trap queueing */ + +/**/ +mod_export int trap_queueing_enabled, trap_queue_front, trap_queue_rear; +/**/ +mod_export int trap_queue[MAX_QUEUE_SIZE]; + +/* This is only used on machines that don't understand signal sets. * + * On SYSV machines this will represent the signals that are blocked * + * (held) using sighold. On machines which can't block signals at * + * all, we will simulate this by ignoring them and remembering them * + * in this variable. */ +#if !defined(POSIX_SIGNALS) && !defined(BSD_SIGNALS) +static sigset_t blocked_set; +#endif + +#ifdef POSIX_SIGNALS +# define signal_jmp_buf sigjmp_buf +# define signal_setjmp(b) sigsetjmp((b),1) +# define signal_longjmp(b,n) siglongjmp((b),(n)) +#else +# define signal_jmp_buf jmp_buf +# define signal_setjmp(b) setjmp(b) +# define signal_longjmp(b,n) longjmp((b),(n)) +#endif + +#ifdef NO_SIGNAL_BLOCKING +# define signal_process(sig) signal_ignore(sig) +# define signal_reset(sig) install_handler(sig) +#else +# define signal_process(sig) ; +# define signal_reset(sig) ; +#endif + +/* Install signal handler for given signal. * + * If possible, we want to make sure that interrupted * + * system calls are not restarted. */ + +/**/ +mod_export void +install_handler(int sig) +{ +#ifdef POSIX_SIGNALS + struct sigaction act; + + act.sa_handler = (SIGNAL_HANDTYPE) zhandler; + sigemptyset(&act.sa_mask); /* only block sig while in handler */ + act.sa_flags = 0; +# ifdef SA_INTERRUPT /* SunOS 4.x */ + if (interact) + act.sa_flags |= SA_INTERRUPT; /* make sure system calls are not restarted */ +# endif + sigaction(sig, &act, (struct sigaction *)NULL); +#else +# ifdef BSD_SIGNALS + struct sigvec vec; + + vec.sv_handler = (SIGNAL_HANDTYPE) zhandler; + vec.sv_mask = sigmask(sig); /* mask out this signal while in handler */ +# ifdef SV_INTERRUPT + vec.sv_flags = SV_INTERRUPT; /* make sure system calls are not restarted */ +# endif + sigvec(sig, &vec, (struct sigvec *)NULL); +# else +# ifdef SYSV_SIGNALS + /* we want sigset rather than signal because it will * + * block sig while in handler. signal usually doesn't */ + sigset(sig, zhandler); +# else /* NO_SIGNAL_BLOCKING (bummer) */ + signal(sig, zhandler); + +# endif /* SYSV_SIGNALS */ +# endif /* BSD_SIGNALS */ +#endif /* POSIX_SIGNALS */ +} + +/* enable ^C interrupts */ + +/**/ +mod_export void +intr(void) +{ + if (interact) + install_handler(SIGINT); +} + +/* disable ^C interrupts */ + +#if 0 /**/ +void +nointr(void) +{ + if (interact) + signal_ignore(SIGINT); +} +#endif + +/* temporarily block ^C interrupts */ + +/**/ +mod_export void +holdintr(void) +{ + if (interact) + signal_block(signal_mask(SIGINT)); +} + +/* release ^C interrupts */ + +/**/ +mod_export void +noholdintr(void) +{ + if (interact) + signal_unblock(signal_mask(SIGINT)); +} + +/* create a signal mask containing * + * only the given signal */ + +/**/ +mod_export sigset_t +signal_mask(int sig) +{ + sigset_t set; + + sigemptyset(&set); + if (sig) + sigaddset(&set, sig); + return set; +} + +/* Block the signals in the given signal * + * set. Return the old signal set. */ + +/**/ +#ifndef BSD_SIGNALS + +/**/ +mod_export sigset_t +signal_block(sigset_t set) +{ + sigset_t oset; + +#ifdef POSIX_SIGNALS + sigprocmask(SIG_BLOCK, &set, &oset); + +#else +# ifdef SYSV_SIGNALS + int i; + + oset = blocked_set; + for (i = 1; i <= NSIG; ++i) { + if (sigismember(&set, i) && !sigismember(&blocked_set, i)) { + sigaddset(&blocked_set, i); + sighold(i); + } + } +# else /* NO_SIGNAL_BLOCKING */ +/* We will just ignore signals if the system doesn't have * + * the ability to block them. */ + int i; + + oset = blocked_set; + for (i = 1; i <= NSIG; ++i) { + if (sigismember(&set, i) && !sigismember(&blocked_set, i)) { + sigaddset(&blocked_set, i); + signal_ignore(i); + } + } +# endif /* SYSV_SIGNALS */ +#endif /* POSIX_SIGNALS */ + + return oset; +} + +/**/ +#endif /* BSD_SIGNALS */ + +/* Unblock the signals in the given signal * + * set. Return the old signal set. */ + +/**/ +mod_export sigset_t +signal_unblock(sigset_t set) +{ + sigset_t oset; + +#ifdef POSIX_SIGNALS + sigprocmask(SIG_UNBLOCK, &set, &oset); +#else +# ifdef BSD_SIGNALS + sigfillset(&oset); + oset = sigsetmask(oset); + sigsetmask(oset & ~set); +# else +# ifdef SYSV_SIGNALS + int i; + + oset = blocked_set; + for (i = 1; i <= NSIG; ++i) { + if (sigismember(&set, i) && sigismember(&blocked_set, i)) { + sigdelset(&blocked_set, i); + sigrelse(i); + } + } +# else /* NO_SIGNAL_BLOCKING */ +/* On systems that can't block signals, we are just ignoring them. So * + * to unblock signals, we just reenable the signal handler for them. */ + int i; + + oset = blocked_set; + for (i = 1; i <= NSIG; ++i) { + if (sigismember(&set, i) && sigismember(&blocked_set, i)) { + sigdelset(&blocked_set, i); + install_handler(i); + } + } +# endif /* SYSV_SIGNALS */ +# endif /* BSD_SIGNALS */ +#endif /* POSIX_SIGNALS */ + + return oset; +} + +/* set the process signal mask to * + * be the given signal mask */ + +/**/ +mod_export sigset_t +signal_setmask(sigset_t set) +{ + sigset_t oset; + +#ifdef POSIX_SIGNALS + sigprocmask(SIG_SETMASK, &set, &oset); +#else +# ifdef BSD_SIGNALS + oset = sigsetmask(set); +# else +# ifdef SYSV_SIGNALS + int i; + + oset = blocked_set; + for (i = 1; i <= NSIG; ++i) { + if (sigismember(&set, i) && !sigismember(&blocked_set, i)) { + sigaddset(&blocked_set, i); + sighold(i); + } else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) { + sigdelset(&blocked_set, i); + sigrelse(i); + } + } +# else /* NO_SIGNAL_BLOCKING */ + int i; + + oset = blocked_set; + for (i = 1; i < NSIG; ++i) { + if (sigismember(&set, i) && !sigismember(&blocked_set, i)) { + sigaddset(&blocked_set, i); + signal_ignore(i); + } else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) { + sigdelset(&blocked_set, i); + install_handler(i); + } + } +# endif /* SYSV_SIGNALS */ +# endif /* BSD_SIGNALS */ +#endif /* POSIX_SIGNALS */ + + return oset; +} + +#if defined(NO_SIGNAL_BLOCKING) +static int suspend_longjmp = 0; +static signal_jmp_buf suspend_jmp_buf; +#endif + +/**/ +int +signal_suspend(UNUSED(int sig), int wait_cmd) +{ + int ret; + +#if defined(POSIX_SIGNALS) || defined(BSD_SIGNALS) + sigset_t set; +# if defined(POSIX_SIGNALS) && defined(BROKEN_POSIX_SIGSUSPEND) + sigset_t oset; +# endif + + sigemptyset(&set); + + /* SIGINT from the terminal driver needs to interrupt "wait" + * and to cause traps to fire, but otherwise should not be + * handled by the shell until after any foreground job has + * a chance to decide whether to exit on that signal. + */ + if (!(wait_cmd || isset(TRAPSASYNC) || + (sigtrapped[SIGINT] & ~ZSIG_IGNORED))) + sigaddset(&set, SIGINT); +#endif /* POSIX_SIGNALS || BSD_SIGNALS */ + +#ifdef POSIX_SIGNALS +# ifdef BROKEN_POSIX_SIGSUSPEND + sigprocmask(SIG_SETMASK, &set, &oset); + ret = pause(); + sigprocmask(SIG_SETMASK, &oset, NULL); +# else /* not BROKEN_POSIX_SIGSUSPEND */ + ret = sigsuspend(&set); +# endif /* BROKEN_POSIX_SIGSUSPEND */ +#else /* not POSIX_SIGNALS */ +# ifdef BSD_SIGNALS + ret = sigpause(set); +# else +# ifdef SYSV_SIGNALS + ret = sigpause(sig); + +# else /* NO_SIGNAL_BLOCKING */ + /* need to use signal_longjmp to make this race-free * + * between the child_unblock() and pause() */ + if (signal_setjmp(suspend_jmp_buf) == 0) { + suspend_longjmp = 1; /* we want to signal_longjmp after catching signal */ + child_unblock(); /* do we need to do wait_cmd stuff as well? */ + ret = pause(); + } + suspend_longjmp = 0; /* turn off using signal_longjmp since we are past * + * the pause() function. */ +# endif /* SYSV_SIGNALS */ +# endif /* BSD_SIGNALS */ +#endif /* POSIX_SIGNALS */ + + return ret; +} + +/* last signal we handled: race prone, or what? */ +/**/ +int last_signal; + +/* + * Wait for any processes that have changed state. + * + * The main use for this is in the SIGCHLD handler. However, + * we also use it to pick up status changes of jobs when + * updating jobs. + */ +/**/ +void +wait_for_processes(void) +{ + /* keep WAITING until no more child processes to reap */ + for (;;) { + /* save the errno, since WAIT may change it */ + int old_errno = errno; + int status; + Job jn; + Process pn; + pid_t pid; + pid_t *procsubpid = &cmdoutpid; + int *procsubval = &cmdoutval; + int cont = 0; + struct execstack *es = exstack; + + /* + * Reap the child process. + * If we want usage information, we need to use wait3. + */ +#if defined(HAVE_WAIT3) || defined(HAVE_WAITPID) +# ifdef WCONTINUED +# define WAITFLAGS (WNOHANG|WUNTRACED|WCONTINUED) +# else +# define WAITFLAGS (WNOHANG|WUNTRACED) +# endif +#endif +#ifdef HAVE_WAIT3 +# ifdef HAVE_GETRUSAGE + struct rusage ru; + + pid = wait3((void *)&status, WAITFLAGS, &ru); +# else + pid = wait3((void *)&status, WAITFLAGS, NULL); +# endif +#else +# ifdef HAVE_WAITPID + pid = waitpid(-1, &status, WAITFLAGS); +# else + pid = wait(&status); +# endif +#endif + + if (!pid) /* no more children to reap */ + break; + + /* check if child returned was from process substitution */ + for (;;) { + if (pid == *procsubpid) { + *procsubpid = 0; + if (WIFSIGNALED(status)) + *procsubval = (0200 | WTERMSIG(status)); + else + *procsubval = WEXITSTATUS(status); + use_cmdoutval = 1; + get_usage(); + cont = 1; + break; + } + if (!es) + break; + procsubpid = &es->cmdoutpid; + procsubval = &es->cmdoutval; + es = es->next; + } + if (cont) + continue; + + /* check for WAIT error */ + if (pid == -1) { + if (errno != ECHILD) + zerr("wait failed: %e", errno); + /* WAIT changed errno, so restore the original */ + errno = old_errno; + 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. + */ + if (findproc(pid, &jn, &pn, 0)) { + if (((jn->stat & STAT_BUILTIN) || + (list_pipe && + (thisjob == -1 || + (jobtab[thisjob].stat & STAT_BUILTIN)))) && + WIFSTOPPED(status) && WSTOPSIG(status) == SIGTSTP) { + killjb(jn, SIGCONT); + zwarn("job can't be suspended"); + } else { +#if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE) + struct timezone dummy_tz; + gettimeofday(&pn->endtime, &dummy_tz); +#ifdef WIFCONTINUED + if (WIFCONTINUED(status)) + pn->status = SP_RUNNING; + else +#endif + pn->status = status; + pn->ti = ru; +#else + update_process(pn, status); +#endif + if (WIFEXITED(status) && + pn->pid == jn->gleader && + killpg(pn->pid, 0) == -1) { + jn->gleader = 0; + if (!(jn->stat & STAT_NOSTTY)) { + /* + * This PID was in control of the terminal; + * reclaim terminal now it has exited. + * It's still possible some future forked + * process of this job will become group + * leader, however. + */ + attachtty(mypgrp); + } + } + } + update_job(jn); + } else if (findproc(pid, &jn, &pn, 1)) { + pn->status = status; + update_job(jn); + } else { + /* If not found, update the shell record of time spent by + * children in sub processes anyway: otherwise, this + * will get added on to the next found process that + * terminates. + */ + get_usage(); + } + /* + * Accumulate a list of older jobs. We only do this for + * background jobs, which is something in the job table + * that's not marked as in the current shell or as shell builtin + * and is not equal to the current foreground job. + */ + if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) && + jn - jobtab != thisjob) { + int val = (WIFSIGNALED(status) ? + 0200 | WTERMSIG(status) : + (WIFSTOPPED(status) ? + 0200 | WEXITSTATUS(status) : + WEXITSTATUS(status))); + addbgstatus(pid, val); + } + + unqueue_signals(); + } +} + +/* the signal handler */ + +/**/ +mod_export void +zhandler(int sig) +{ + sigset_t newmask, oldmask; + +#if defined(NO_SIGNAL_BLOCKING) + int do_jump; + signal_jmp_buf jump_to; +#endif + + last_signal = sig; + signal_process(sig); + + sigfillset(&newmask); + /* Block all signals temporarily */ + oldmask = signal_block(newmask); + +#if defined(NO_SIGNAL_BLOCKING) + /* do we need to longjmp to signal_suspend */ + do_jump = suspend_longjmp; + /* In case a SIGCHLD somehow arrives */ + suspend_longjmp = 0; + + /* Traps can cause nested signal_suspend() */ + if (sig == SIGCHLD) { + if (do_jump) { + /* Copy suspend_jmp_buf */ + jump_to = suspend_jmp_buf; + } + } +#endif + + /* Are we queueing signals now? */ + if (queueing_enabled) { + int temp_rear = ++queue_rear % MAX_QUEUE_SIZE; + + DPUTS(temp_rear == queue_front, "BUG: signal queue full"); + /* Make sure it's not full (extremely unlikely) */ + if (temp_rear != queue_front) { + /* ok, not full, so add to queue */ + queue_rear = temp_rear; + /* save signal caught */ + signal_queue[queue_rear] = sig; + /* save current signal mask */ + signal_mask_queue[queue_rear] = oldmask; + } + signal_reset(sig); + return; + } + + /* Reset signal mask, signal traps ok now */ + signal_setmask(oldmask); + + switch (sig) { + case SIGCHLD: + wait_for_processes(); + break; + + case SIGPIPE: + if (!handletrap(SIGPIPE)) { + if (!interact) + _exit(SIGPIPE); + else if (!isatty(SHTTY)) { + stopmsg = 1; + zexit(SIGPIPE, 1); + } + } + break; + + case SIGHUP: + if (!handletrap(SIGHUP)) { + stopmsg = 1; + zexit(SIGHUP, 1); + } + break; + + case SIGINT: + if (!handletrap(SIGINT)) { + if ((isset(PRIVILEGED) || isset(RESTRICTED)) && + isset(INTERACTIVE) && (noerrexit & NOERREXIT_SIGNAL)) + zexit(SIGINT, 1); + if (list_pipe || chline || simple_pline) { + breaks = loops; + errflag |= ERRFLAG_INT; + inerrflush(); + check_cursh_sig(SIGINT); + } + lastval = 128 + SIGINT; + } + break; + +#ifdef SIGWINCH + case SIGWINCH: + adjustwinsize(1); /* check window size and adjust */ + (void) handletrap(SIGWINCH); + break; +#endif + + case SIGALRM: + if (!handletrap(SIGALRM)) { + int idle = ttyidlegetfn(NULL); + int tmout = getiparam("TMOUT"); + if (idle >= 0 && idle < tmout) + alarm(tmout - idle); + else { + /* + * We want to exit now. + * Cancel all errors, including a user interrupt + * which is now redundant. + */ + errflag = noerrs = 0; + zwarn("timeout"); + stopmsg = 1; + zexit(SIGALRM, 1); + } + } + break; + + default: + (void) handletrap(sig); + break; + } /* end of switch(sig) */ + + signal_reset(sig); + +/* This is used to make signal_suspend() race-free */ +#if defined(NO_SIGNAL_BLOCKING) + if (do_jump) + signal_longjmp(jump_to, 1); +#endif + +} /* handler */ + + +/* SIGHUP any jobs left running */ + +/**/ +void +killrunjobs(int from_signal) +{ + int i, killed = 0; + + if (unset(HUP)) + return; + for (i = 1; i <= maxjob; i++) + if ((from_signal || i != thisjob) && (jobtab[i].stat & STAT_LOCKED) && + !(jobtab[i].stat & STAT_NOPRINT) && + !(jobtab[i].stat & STAT_STOPPED)) { + if (jobtab[i].gleader != getpid() && + killpg(jobtab[i].gleader, SIGHUP) != -1) + killed++; + } + if (killed) + zwarn("warning: %d jobs SIGHUPed", killed); +} + + +/* send a signal to a job (simply involves kill if monitoring is on) */ + +/**/ +int +killjb(Job jn, int sig) +{ + Process pn; + int err = 0; + + if (jobbing) { + if (jn->stat & STAT_SUPERJOB) { + if (sig == SIGCONT) { + for (pn = jobtab[jn->other].procs; pn; pn = pn->next) + if (killpg(pn->pid, sig) == -1) + if (kill(pn->pid, sig) == -1 && errno != ESRCH) + err = -1; + + /* + * Note this does not kill the last process, + * which is assumed to be the one controlling the + * subjob, i.e. the forked zsh that was originally + * list_pipe_pid... + */ + for (pn = jn->procs; pn->next; pn = pn->next) + if (kill(pn->pid, sig) == -1 && errno != ESRCH) + err = -1; + + /* + * ...we only continue that once the external processes + * currently associated with the subjob are finished. + */ + if (!jobtab[jn->other].procs && pn) + if (kill(pn->pid, sig) == -1 && errno != ESRCH) + err = -1; + + return err; + } + if (killpg(jobtab[jn->other].gleader, sig) == -1 && errno != ESRCH) + err = -1; + + if (killpg(jn->gleader, sig) == -1 && errno != ESRCH) + err = -1; + + return err; + } + else + return killpg(jn->gleader, sig); + } + for (pn = jn->procs; pn; pn = pn->next) { + /* + * Do not kill this job's process if it's already dead as its + * pid could have been reused by the system. + * As the PID doesn't exist don't return an error. + */ + if (pn->status == SP_RUNNING || WIFSTOPPED(pn->status)) { + /* + * kill -0 on a job is pointless. We still call kill() for each process + * in case the user cares about it but we ignore its outcome. + */ + if ((err = kill(pn->pid, sig)) == -1 && errno != ESRCH && sig != 0) + return -1; + } + } + return err; +} + +/* + * List for saving traps. We don't usually have that many traps + * at once, so just use a linked list. + */ +struct savetrap { + int sig, flags, local, posix; + void *list; +}; + +static LinkList savetraps; +static int dontsavetrap; + +/* + * Save the current trap by copying it. This does nothing to + * the existing value of sigtrapped or siglists. + */ + +static void +dosavetrap(int sig, int level) +{ + struct savetrap *st; + st = (struct savetrap *)zalloc(sizeof(*st)); + st->sig = sig; + st->local = level; + st->posix = (sig == SIGEXIT) ? exit_trap_posix : 0; + if ((st->flags = sigtrapped[sig]) & ZSIG_FUNC) { + /* + * Get the old function: this assumes we haven't added + * the new one yet. + */ + Shfunc shf, newshf = NULL; + if ((shf = (Shfunc)gettrapnode(sig, 1))) { + /* Copy the node for saving */ + newshf = (Shfunc) zshcalloc(sizeof(*newshf)); + newshf->node.nam = ztrdup(shf->node.nam); + newshf->node.flags = shf->node.flags; + newshf->funcdef = dupeprog(shf->funcdef, 0); + if (shf->node.flags & PM_LOADDIR) { + dircache_set(&newshf->filename, shf->filename); + } else { + newshf->filename = ztrdup(shf->filename); + } + if (shf->sticky) { + newshf->sticky = sticky_emulation_dup(shf->sticky, 0); + } else + newshf->sticky = 0; + if (shf->node.flags & PM_UNDEFINED) + newshf->funcdef->shf = newshf; + } +#ifdef DEBUG + else dputs("BUG: no function present with function trap flag set."); +#endif + DPUTS(siglists[sig], "BUG: function signal has eval list, too."); + st->list = newshf; + } else if (sigtrapped[sig]) { + st->list = siglists[sig] ? dupeprog(siglists[sig], 0) : NULL; + } else { + DPUTS(siglists[sig], "BUG: siglists not null for untrapped signal"); + st->list = NULL; + } + if (!savetraps) + savetraps = znewlinklist(); + /* + * Put this at the front of the list + */ + zinsertlinknode(savetraps, (LinkNode)savetraps, st); +} + + +/* + * Set a trap: note this does not handle manipulation of + * the function table for TRAPNAL functions. + * + * sig is the signal number. + * + * l is the list to be eval'd for a trap defined with the "trap" + * builtin and should be NULL for a function trap. + * + * flags includes any additional flags to be or'd into sigtrapped[sig], + * in particular ZSIG_FUNC; the basic flags will be assigned within + * settrap. + */ + +/**/ +mod_export int +settrap(int sig, Eprog l, int flags) +{ + if (sig == -1) + return 1; + if (jobbing && (sig == SIGTTOU || sig == SIGTSTP || sig == SIGTTIN)) { + zerr("can't trap SIG%s in interactive shells", sigs[sig]); + return 1; + } + + /* + * Call unsettrap() unconditionally, to make sure trap is saved + * if necessary. + */ + queue_signals(); + unsettrap(sig); + + DPUTS((flags & ZSIG_FUNC) && l, + "BUG: trap function has passed eval list, too"); + siglists[sig] = l; + if (!(flags & ZSIG_FUNC) && empty_eprog(l)) { + sigtrapped[sig] = ZSIG_IGNORED; + if (sig && sig <= SIGCOUNT && +#ifdef SIGWINCH + sig != SIGWINCH && +#endif + sig != SIGCHLD) + signal_ignore(sig); + } else { + nsigtrapped++; + sigtrapped[sig] = ZSIG_TRAPPED; + if (sig && sig <= SIGCOUNT && +#ifdef SIGWINCH + sig != SIGWINCH && +#endif + sig != SIGCHLD) + install_handler(sig); + } + sigtrapped[sig] |= flags; + /* + * Note that introducing the locallevel does not affect whether + * sigtrapped[sig] is zero or not, i.e. a test without a mask + * works just the same. + */ + if (sig == SIGEXIT) { + /* Make POSIX behaviour of EXIT trap sticky */ + exit_trap_posix = isset(POSIXTRAPS); + /* POSIX exit traps are not local. */ + if (!exit_trap_posix) + sigtrapped[sig] |= (locallevel << ZSIG_SHIFT); + } + else + sigtrapped[sig] |= (locallevel << ZSIG_SHIFT); + unqueue_signals(); + return 0; +} + +/**/ +void +unsettrap(int sig) +{ + HashNode hn; + + queue_signals(); + hn = removetrap(sig); + if (hn) + shfunctab->freenode(hn); + unqueue_signals(); +} + +/**/ +HashNode +removetrap(int sig) +{ + int trapped; + + if (sig == -1 || + (jobbing && (sig == SIGTTOU || sig == SIGTSTP || sig == SIGTTIN))) + return NULL; + + queue_signals(); + trapped = sigtrapped[sig]; + /* + * Note that we save the trap here even if there isn't an existing + * one, to aid in removing this one. However, if there's + * already one at the current locallevel we just overwrite it. + * + * Note we save EXIT traps based on the *current* setting of + * POSIXTRAPS --- so if there is POSIX EXIT trap set but + * we are in native mode it can be saved, replaced by a function + * trap, and then restored. + */ + if (!dontsavetrap && + (sig == SIGEXIT ? !isset(POSIXTRAPS) : isset(LOCALTRAPS)) && + locallevel && + (!trapped || locallevel > (sigtrapped[sig] >> ZSIG_SHIFT))) + dosavetrap(sig, locallevel); + + if (!trapped) { + unqueue_signals(); + return NULL; + } + if (sigtrapped[sig] & ZSIG_TRAPPED) + nsigtrapped--; + sigtrapped[sig] = 0; + if (sig == SIGINT && interact) { + /* PWS 1995/05/16: added test for interactive, also noholdintr() * + * as subshells ignoring SIGINT have it blocked from delivery */ + intr(); + noholdintr(); + } else if (sig == SIGHUP) + install_handler(sig); + else if (sig == SIGPIPE && interact && !forklevel) + install_handler(sig); + else if (sig && sig <= SIGCOUNT && +#ifdef SIGWINCH + sig != SIGWINCH && +#endif + sig != SIGCHLD) + signal_default(sig); + if (sig == SIGEXIT) + exit_trap_posix = 0; + + /* + * At this point we free the appropriate structs. If we don't + * want that to happen then either the function should already have been + * removed from shfunctab, or the entry in siglists should have been set + * to NULL. This is no longer necessary for saving traps as that + * copies the structures, so here we are remove the originals. + * That causes a little inefficiency, but a good deal more reliability. + */ + if (trapped & ZSIG_FUNC) { + HashNode node = gettrapnode(sig, 1); + + /* + * As in dosavetrap(), don't call removeshfuncnode() because + * that calls back into unsettrap(); + */ + if (node) + removehashnode(shfunctab, node->nam); + unqueue_signals(); + + return node; + } else if (siglists[sig]) { + freeeprog(siglists[sig]); + siglists[sig] = NULL; + } + unqueue_signals(); + + return NULL; +} + +/**/ +void +starttrapscope(void) +{ + /* No special SIGEXIT behaviour inside another trap. */ + if (intrap) + return; + + /* + * SIGEXIT needs to be restored at the current locallevel, + * so give it the next higher one. dosavetrap() is called + * automatically where necessary. + */ + if (sigtrapped[SIGEXIT] && !exit_trap_posix) { + locallevel++; + unsettrap(SIGEXIT); + locallevel--; + } +} + +/* + * Reset traps after the end of a function: must be called after + * endparamscope() so that the locallevel has been decremented. + */ + +/**/ +void +endtrapscope(void) +{ + LinkNode ln; + struct savetrap *st; + int exittr = 0; + void *exitfn = NULL; + + /* + * Remember the exit trap, but don't run it until + * after all the other traps have been put back. + * Don't do this inside another trap. + */ + if (!intrap && + !exit_trap_posix && (exittr = sigtrapped[SIGEXIT])) { + if (exittr & ZSIG_FUNC) { + exitfn = removehashnode(shfunctab, "TRAPEXIT"); + } else { + exitfn = siglists[SIGEXIT]; + siglists[SIGEXIT] = NULL; + } + if (sigtrapped[SIGEXIT] & ZSIG_TRAPPED) + nsigtrapped--; + sigtrapped[SIGEXIT] = 0; + } + + if (savetraps) { + while ((ln = firstnode(savetraps)) && + (st = (struct savetrap *) ln->dat) && + st->local > locallevel) { + int sig = st->sig; + + remnode(savetraps, ln); + + if (st->flags && (st->list != NULL)) { + /* prevent settrap from saving this */ + dontsavetrap++; + if (st->flags & ZSIG_FUNC) + settrap(sig, NULL, ZSIG_FUNC); + else + settrap(sig, (Eprog) st->list, 0); + if (sig == SIGEXIT) + exit_trap_posix = st->posix; + dontsavetrap--; + /* + * counting of nsigtrapped should presumably be handled + * in settrap... + */ + DPUTS((sigtrapped[sig] ^ st->flags) & ZSIG_TRAPPED, + "BUG: settrap didn't restore correct ZSIG_TRAPPED"); + if ((sigtrapped[sig] = st->flags) & ZSIG_FUNC) + shfunctab->addnode(shfunctab, ((Shfunc)st->list)->node.nam, + (Shfunc) st->list); + } else if (sigtrapped[sig]) { + /* + * Don't restore the old state if someone has set a + * POSIX-style exit trap --- allow this to propagate. + */ + if (sig != SIGEXIT || !exit_trap_posix) + unsettrap(sig); + } + + zfree(st, sizeof(*st)); + } + } + + if (exittr) { + /* + * We already made sure this wasn't set as a POSIX exit trap. + * We respect the user's intention when the trap in question + * was set. + */ + dotrapargs(SIGEXIT, &exittr, exitfn); + if (exittr & ZSIG_FUNC) + shfunctab->freenode((HashNode)exitfn); + else + freeeprog(exitfn); + } + DPUTS(!locallevel && savetraps && firstnode(savetraps), + "BUG: still saved traps outside all function scope"); +} + + +/* + * Decide whether a trap needs handling. + * If so, see if the trap should be run now or queued. + * Return 1 if the trap has been or will be handled. + * This only needs to be called in place of dotrap() in the + * signal handler, since it's only while waiting for children + * to exit that we queue traps. + */ +/**/ +static int +handletrap(int sig) +{ + if (!sigtrapped[sig]) + return 0; + + if (trap_queueing_enabled) + { + /* Code borrowed from signal queueing */ + int temp_rear = ++trap_queue_rear % MAX_QUEUE_SIZE; + + DPUTS(temp_rear == trap_queue_front, "BUG: trap queue full"); + /* If queue is not full... */ + if (temp_rear != trap_queue_front) { + trap_queue_rear = temp_rear; + trap_queue[trap_queue_rear] = sig; + } + return 1; + } + + dotrap(sig); + + if (sig == SIGALRM) + { + int tmout; + /* + * Reset the alarm. + * It seems slightly more natural to do this when the + * trap is run, rather than when it's queued, since + * the user doesn't see the latter. + */ + if ((tmout = getiparam("TMOUT"))) + alarm(tmout); + } + + return 1; +} + + +/* + * Queue traps if they shouldn't be run asynchronously, i.e. + * we're not in the wait builtin and TRAPSASYNC isn't set, when + * waiting for children to exit. + * + * Note that unlike signal queuing this should only be called + * in single matching pairs and can't be nested. It is + * only needed when waiting for a job or process to finish. + * + * There is presumably a race setting this up: we shouldn't be running + * traps between forking a foreground process and this point, either. + */ +/**/ +void +queue_traps(int wait_cmd) +{ + if (!isset(TRAPSASYNC) && !wait_cmd) { + /* + * Traps need to be handled synchronously, so + * enable queueing. + */ + trap_queueing_enabled = 1; + } +} + + +/* + * Disable trap queuing and run the traps. + */ +/**/ +void +unqueue_traps(void) +{ + trap_queueing_enabled = 0; + while (trap_queue_front != trap_queue_rear) { + trap_queue_front = (trap_queue_front + 1) % MAX_QUEUE_SIZE; + (void) handletrap(trap_queue[trap_queue_front]); + } +} + + +/* Execute a trap function for a given signal, possibly + * with non-standard sigtrapped & siglists values + */ + +/* Are we already executing a trap? */ +/**/ +int intrap; + +/* Is the current trap a function? */ + +/**/ +int trapisfunc; + +/* + * If the current trap is not a function, at what function depth + * did the trap get called? + */ +/**/ +int traplocallevel; + +/* + * sig is the signal number. + * *sigtr is the value to be taken as the field in sigtrapped (since + * that may have changed by this point if we are exiting). + * sigfn is an Eprog with a non-function eval list, or a Shfunc + * with a function trap. It may be NULL with an ignored signal. + */ + +/**/ +static void +dotrapargs(int sig, int *sigtr, void *sigfn) +{ + LinkList args; + char *name, num[4]; + int obreaks = breaks; + int oretflag = retflag; + int olastval = lastval; + int isfunc; + int traperr, new_trap_state, new_trap_return; + + /* if signal is being ignored or the trap function * + * is NULL, then return * + * * + * Also return if errflag is set. In fact, the code in the * + * function will test for this, but this way we keep status flags * + * intact without working too hard. Special cases (e.g. calling * + * a trap for SIGINT after the error flag was set) are handled * + * by the calling code. (PWS 1995/06/08). * + * * + * This test is now replicated in dotrap(). */ + if ((*sigtr & ZSIG_IGNORED) || !sigfn || errflag) + return; + + /* + * Never execute special (synchronous) traps inside other traps. + * This can cause unexpected code execution when more than one + * of these is set. + * + * The down side is that it's harder to debug traps. I don't think + * that's a big issue. + */ + if (intrap) { + switch (sig) { + case SIGEXIT: + case SIGDEBUG: + case SIGZERR: + return; + } + } + + queue_signals(); /* Any time we manage memory or global state */ + + intrap++; + *sigtr |= ZSIG_IGNORED; + + zcontext_save(); + /* execsave will save the old trap_return and trap_state */ + execsave(); + breaks = retflag = 0; + traplocallevel = locallevel; + runhookdef(BEFORETRAPHOOK, NULL); + if (*sigtr & ZSIG_FUNC) { + int osc = sfcontext, old_incompfunc = incompfunc; + HashNode hn = gettrapnode(sig, 0); + + args = znewlinklist(); + /* + * In case of multiple names, try to get + * a hint of the name in use from the function table. + * In special cases, e.g. EXIT traps, the function + * has already been removed. Then it's OK to + * use the standard name. + */ + if (hn) { + name = ztrdup(hn->nam); + } else { + name = (char *) zalloc(5 + strlen(sigs[sig])); + sprintf(name, "TRAP%s", sigs[sig]); + } + zaddlinknode(args, name); + sprintf(num, "%d", sig); + zaddlinknode(args, num); + + trap_return = -1; /* incremented by doshfunc */ + trap_state = TRAP_STATE_PRIMED; + trapisfunc = isfunc = 1; + + sfcontext = SFC_SIGNAL; + incompfunc = 0; + doshfunc((Shfunc)sigfn, args, 1); /* manages signal queueing */ + sfcontext = osc; + incompfunc= old_incompfunc; + freelinklist(args, (FreeFunc) NULL); + zsfree(name); + } else { + trap_return = -2; /* not incremented, used at current level */ + trap_state = TRAP_STATE_PRIMED; + trapisfunc = isfunc = 0; + + execode((Eprog)sigfn, 1, 0, "trap"); /* manages signal queueing */ + } + runhookdef(AFTERTRAPHOOK, NULL); + + traperr = errflag; + + /* Grab values before they are restored */ + new_trap_state = trap_state; + new_trap_return = trap_return; + + execrestore(); + zcontext_restore(); + + if (new_trap_state == TRAP_STATE_FORCE_RETURN && + /* zero return from function isn't special */ + !(isfunc && new_trap_return == 0)) { + if (isfunc) { + breaks = loops; + /* + * For SIGINT we behave the same as the default behaviour + * i.e. we set the error bit indicating an interrupt. + * We do this with SIGQUIT, too, even though we don't + * handle SIGQUIT by default. That's to try to make + * it behave a bit more like its normal behaviour when + * the trap handler has told us that's what it wants. + */ + if (sig == SIGINT || sig == SIGQUIT) + errflag |= ERRFLAG_INT; + else + errflag |= ERRFLAG_ERROR; + } + lastval = new_trap_return; + /* return triggered */ + retflag = 1; + } else { + if (traperr && !EMULATION(EMULATE_SH)) + lastval = 1; + else { + /* + * With no explicit forced return, we keep the + * lastval from before the trap ran. + */ + lastval = olastval; + } + if (try_tryflag) { + if (traperr) + errflag |= ERRFLAG_ERROR; + else + errflag &= ~ERRFLAG_ERROR; + } + breaks += obreaks; + /* return not triggered: restore old flag */ + retflag = oretflag; + if (breaks > loops) + breaks = loops; + } + + /* + * If zle was running while the trap was executed, see if we + * need to restore the display. + */ + if (zleactive && resetneeded) + zleentry(ZLE_CMD_REFRESH); + + if (*sigtr != ZSIG_IGNORED) + *sigtr &= ~ZSIG_IGNORED; + intrap--; + + unqueue_signals(); +} + +/* Standard call to execute a trap for a given signal. */ + +/**/ +void +dotrap(int sig) +{ + void *funcprog; + int q = queue_signal_level(); + + if (sigtrapped[sig] & ZSIG_FUNC) { + HashNode hn = gettrapnode(sig, 0); + if (hn) + funcprog = hn; + else { +#ifdef DEBUG + dputs("BUG: running function trap which has escaped."); +#endif + funcprog = NULL; + } + } else + funcprog = siglists[sig]; + + /* + * Copied from dotrapargs(). + * (In fact, the gain from duplicating this appears to be virtually + * zero. Not sure why it's here.) + */ + if ((sigtrapped[sig] & ZSIG_IGNORED) || !funcprog || errflag) + return; + + dont_queue_signals(); + + if (sig == SIGEXIT) + ++in_exit_trap; + + dotrapargs(sig, sigtrapped+sig, funcprog); + + if (sig == SIGEXIT) + --in_exit_trap; + + restore_queue_signals(q); +} diff --git a/dotfiles/system/.zsh/modules/Src/signals.h b/dotfiles/system/.zsh/modules/Src/signals.h new file mode 100644 index 0000000..41ac88c --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/signals.h @@ -0,0 +1,142 @@ +/* + * signals.h - header file for signals handling code + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#define SIGNAL_HANDTYPE void (*)_((int)) + +#ifndef HAVE_KILLPG +# define killpg(pgrp,sig) kill(-(pgrp),sig) +#endif + +#define SIGZERR (SIGCOUNT+1) +#define SIGDEBUG (SIGCOUNT+2) +#define VSIGCOUNT (SIGCOUNT+3) +#define SIGEXIT 0 + +#ifdef SV_BSDSIG +# define SV_INTERRUPT SV_BSDSIG +#endif + +/* If not a POSIX machine, then we create our * + * own POSIX style signal sets functions. */ +#ifndef POSIX_SIGNALS +# define sigemptyset(s) (*(s) = 0) +# if NSIG == 32 +# define sigfillset(s) (*(s) = ~(sigset_t)0, 0) +# else +# define sigfillset(s) (*(s) = (1 << NSIG) - 1, 0) +# endif +# define sigaddset(s,n) (*(s) |= (1 << ((n) - 1)), 0) +# define sigdelset(s,n) (*(s) &= ~(1 << ((n) - 1)), 0) +# define sigismember(s,n) ((*(s) & (1 << ((n) - 1))) != 0) +#endif /* ifndef POSIX_SIGNALS */ + +#define child_block() signal_block(sigchld_mask) +#define child_unblock() signal_unblock(sigchld_mask) + +#ifdef SIGWINCH +# define winch_block() signal_block(signal_mask(SIGWINCH)) +# define winch_unblock() signal_unblock(signal_mask(SIGWINCH)) +#else +# define winch_block() 0 +# define winch_unblock() 0 +#endif + +/* ignore a signal */ +#define signal_ignore(S) signal(S, SIG_IGN) + +/* return a signal to it default action */ +#define signal_default(S) signal(S, SIG_DFL) + +/* Use a circular queue to save signals caught during * + * critical sections of code. You call queue_signals to * + * start queueing, and unqueue_signals to process the * + * queue and stop queueing. Since the kernel doesn't * + * queue signals, it is probably overkill for zsh to do * + * this, but it shouldn't hurt anything to do it anyway. */ + +#define MAX_QUEUE_SIZE 128 + +#define run_queued_signals() do { \ + while (queue_front != queue_rear) { /* while signals in queue */ \ + sigset_t oset; \ + queue_front = (queue_front + 1) % MAX_QUEUE_SIZE; \ + oset = signal_setmask(signal_mask_queue[queue_front]); \ + zhandler(signal_queue[queue_front]); /* handle queued signal */ \ + signal_setmask(oset); \ + } \ +} while (0) + +#ifdef DEBUG + +#define queue_signals() (queue_in++, queueing_enabled++) + +#define unqueue_signals() do { \ + DPUTS(!queueing_enabled, "BUG: unqueue_signals called but not queueing"); \ + --queue_in; \ + if (!--queueing_enabled) run_queued_signals(); \ +} while (0) + +#define dont_queue_signals() do { \ + queue_in = queueing_enabled; \ + queueing_enabled = 0; \ + run_queued_signals(); \ +} while (0) + +#define restore_queue_signals(q) do { \ + DPUTS2(queueing_enabled && queue_in != q, \ + "BUG: q = %d != queue_in = %d", q, queue_in); \ + queue_in = (queueing_enabled = (q)); \ +} while (0) + +#else /* !DEBUG */ + +#define queue_signals() (queueing_enabled++) + +#define unqueue_signals() do { \ + if (!--queueing_enabled) run_queued_signals(); \ +} while (0) + +#define dont_queue_signals() do { \ + queueing_enabled = 0; \ + run_queued_signals(); \ +} while (0) + +#define restore_queue_signals(q) (queueing_enabled = (q)) + +#endif /* DEBUG */ + +#define queue_signal_level() queueing_enabled + +#ifdef BSD_SIGNALS +#define signal_block(S) sigblock(S) +#else +extern sigset_t signal_block _((sigset_t)); +#endif /* BSD_SIGNALS */ + +extern sigset_t signal_unblock _((sigset_t)); diff --git a/dotfiles/system/.zsh/modules/Src/signames1.awk b/dotfiles/system/.zsh/modules/Src/signames1.awk new file mode 100644 index 0000000..27d21ac --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/signames1.awk @@ -0,0 +1,19 @@ +# This is an awk script which finds out what the possibilities for +# the signal names are, and dumps them out so that cpp can turn them +# into numbers. Since we don't need to decide here what the +# real signals are, we can afford to be generous about definitions, +# in case the definitions are in terms of other definitions. +# However, we need to avoid definitions with parentheses, which will +# mess up the syntax. +BEGIN { printf "#include \n\n" } + +/^[\t ]*#[\t ]*define[\t _]*SIG[A-Z][A-Z0-9]*[\t ][\t ]*[^(\t ]/ { + sigindex = index($0, "SIG") + sigtail = substr($0, sigindex, 80) + split(sigtail, tmp) + signam = substr(tmp[1], 4, 20) + if (substr($0, sigindex-1, 1) == "_") + printf("XXNAMES XXSIG%s _SIG%s\n", signam, signam) + else + printf("XXNAMES XXSIG%s SIG%s\n", signam, signam) +} diff --git a/dotfiles/system/.zsh/modules/Src/signames2.awk b/dotfiles/system/.zsh/modules/Src/signames2.awk new file mode 100644 index 0000000..4d15681 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/signames2.awk @@ -0,0 +1,106 @@ +# +# {g,n}awk script to generate signames.c +# This version relies on the previous output of the preprocessor +# on sigtmp.c, sigtmp.out, which is in turn generated by signames1.awk. +# +# NB: On SunOS 4.1.3 - user-functions don't work properly, also \" problems +# Without 0 + hacks some nawks compare numbers as strings +# +/^[\t ]*XXNAMES XXSIG[A-Z][A-Z0-9]*[\t ][\t ]*[1-9][0-9]*/ { + sigindex = index($0, "SIG") + sigtail = substr($0, sigindex, 80) + split(sigtail, tmp) + signam = substr(tmp[1], 4, 20) + signum = tmp[2] + if (signam == "CHLD" && sig[signum] == "CLD") sig[signum] = "" + if (signam == "POLL" && sig[signum] == "IO") sig[signum] = "" + if (sig[signum] == "") { + sig[signum] = signam + if (0 + max < 0 + signum && signum < 60) + max = signum + if (signam == "ABRT") { msg[signum] = "abort" } + if (signam == "ALRM") { msg[signum] = "alarm" } + if (signam == "BUS") { msg[signum] = "bus error" } + if (signam == "CHLD") { msg[signum] = "death of child" } + if (signam == "CLD") { msg[signum] = "death of child" } + if (signam == "CONT") { msg[signum] = "continued" } + if (signam == "EMT") { msg[signum] = "EMT instruction" } + if (signam == "FPE") { msg[signum] = "floating point exception" } + if (signam == "HUP") { msg[signum] = "hangup" } + if (signam == "ILL") { msg[signum] = "illegal hardware instruction" } + if (signam == "INFO") { msg[signum] = "status request from keyboard" } + if (signam == "INT") { msg[signum] = "interrupt" } + if (signam == "IO") { msg[signum] = "i/o ready" } + if (signam == "IOT") { msg[signum] = "IOT instruction" } + if (signam == "KILL") { msg[signum] = "killed" } + if (signam == "LOST") { msg[signum] = "resource lost" } + if (signam == "PIPE") { msg[signum] = "broken pipe" } + if (signam == "POLL") { msg[signum] = "pollable event occurred" } + if (signam == "PROF") { msg[signum] = "profile signal" } + if (signam == "PWR") { msg[signum] = "power fail" } + if (signam == "QUIT") { msg[signum] = "quit" } + if (signam == "SEGV") { msg[signum] = "segmentation fault" } + if (signam == "SYS") { msg[signum] = "invalid system call" } + if (signam == "TERM") { msg[signum] = "terminated" } + if (signam == "TRAP") { msg[signum] = "trace trap" } + if (signam == "URG") { msg[signum] = "urgent condition" } + if (signam == "USR1") { msg[signum] = "user-defined signal 1" } + if (signam == "USR2") { msg[signum] = "user-defined signal 2" } + if (signam == "VTALRM") { msg[signum] = "virtual time alarm" } + if (signam == "WINCH") { msg[signum] = "window size changed" } + if (signam == "XCPU") { msg[signum] = "cpu limit exceeded" } + if (signam == "XFSZ") { msg[signum] = "file size limit exceeded" } + } +} + +END { + ps = "%s" + ifdstr = sprintf("# ifdef USE_SUSPENDED\n\t%csuspended%s%c,\n%s else\n\t%cstopped%s%c,\n# endif\n", 34, ps, 34, "#", 34, ps, 34) + + printf "/** signames.c **/\n" + printf "/** architecture-customized signames.c for zsh **/\n" + printf "\n" + printf "#define SIGCOUNT\t%d\n", max + printf "\n" + printf "#include %czsh.mdh%c\n", 34, 34 + printf "\n" + printf "/**/\n" + printf "#define sigmsg(sig) ((sig) <= SIGCOUNT ? sig_msg[sig]" + printf " : %c%s%c)", 34, "unknown signal", 34 + printf "\n" + printf "/**/\n" + printf "mod_export char *sig_msg[SIGCOUNT+2] = {\n" + printf "\t%c%s%c,\n", 34, "done", 34 + + for (i = 1; i <= 0 + max; i++) + if (msg[i] == "") { + if (sig[i] == "") + printf("\t%c%c,\n", 34, 34) + else if (sig[i] == "STOP") + printf ifdstr, " (signal)", " (signal)" + else if (sig[i] == "TSTP") + printf ifdstr, "", "" + else if (sig[i] == "TTIN") + printf ifdstr, " (tty input)", " (tty input)" + else if (sig[i] == "TTOU") + printf ifdstr, " (tty output)", " (tty output)" + else + printf("\t%cSIG%s%c,\n", 34, sig[i], 34) + } else + printf("\t%c%s%c,\n", 34, msg[i], 34) + print "\tNULL" + print "};" + print "" + print "/**/" + printf "char *sigs[SIGCOUNT+4] = {\n" + printf("\t%cEXIT%c,\n", 34, 34) + for (i = 1; i <= 0 + max; i++) + if (sig[i] == "") + printf("\t%c%d%c,\n", 34, i, 34) + else + printf("\t%c%s%c,\n", 34, sig[i], 34) + printf("\t%cZERR%c,\n", 34, 34) + printf("\t%cDEBUG%c,\n", 34, 34) + print "\tNULL" + print "};" +} diff --git a/dotfiles/system/.zsh/modules/Src/string.c b/dotfiles/system/.zsh/modules/Src/string.c new file mode 100644 index 0000000..9e14ef9 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/string.c @@ -0,0 +1,213 @@ +/* + * string.c - string manipulation + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 2000 Peter Stephenson + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Peter Stephenson or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Peter Stephenson and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Peter Stephenson and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Peter Stephenson and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + */ + +#include "zsh.mdh" + +/**/ +mod_export char * +dupstring(const char *s) +{ + char *t; + + if (!s) + return NULL; + t = (char *) zhalloc(strlen((char *)s) + 1); + strcpy(t, s); + return t; +} + +/* Duplicate string on heap when length is known */ + +/**/ +mod_export char * +dupstring_wlen(const char *s, unsigned len) +{ + char *t; + + if (!s) + return NULL; + t = (char *) zhalloc(len + 1); + memcpy(t, s, len); + t[len] = '\0'; + return t; +} + +/* Duplicate string on heap, returning length of string */ + +/**/ +mod_export char * +dupstring_glen(const char *s, unsigned *len_ret) +{ + char *t; + + if (!s) + return NULL; + t = (char *) zhalloc((*len_ret = strlen((char *)s)) + 1); + strcpy(t, s); + return t; +} + +/**/ +mod_export char * +ztrdup(const char *s) +{ + char *t; + + if (!s) + return NULL; + t = (char *)zalloc(strlen((char *)s) + 1); + strcpy(t, s); + return t; +} + +/**/ +#ifdef MULTIBYTE_SUPPORT +/**/ +mod_export wchar_t * +wcs_ztrdup(const wchar_t *s) +{ + wchar_t *t; + + if (!s) + return NULL; + t = (wchar_t *)zalloc(sizeof(wchar_t) * (wcslen((wchar_t *)s) + 1)); + wcscpy(t, s); + return t; +} +/**/ +#endif /* MULTIBYTE_SUPPORT */ + + +/* concatenate s1, s2, and s3 in dynamically allocated buffer */ + +/**/ +mod_export char * +tricat(char const *s1, char const *s2, char const *s3) +{ + /* This version always uses permanently-allocated space. */ + char *ptr; + size_t l1 = strlen(s1); + size_t l2 = strlen(s2); + + ptr = (char *)zalloc(l1 + l2 + strlen(s3) + 1); + strcpy(ptr, s1); + strcpy(ptr + l1, s2); + strcpy(ptr + l1 + l2, s3); + return ptr; +} + +/**/ +mod_export char * +zhtricat(char const *s1, char const *s2, char const *s3) +{ + char *ptr; + size_t l1 = strlen(s1); + size_t l2 = strlen(s2); + + ptr = (char *)zhalloc(l1 + l2 + strlen(s3) + 1); + strcpy(ptr, s1); + strcpy(ptr + l1, s2); + strcpy(ptr + l1 + l2, s3); + return ptr; +} + +/* concatenate s1 and s2 in dynamically allocated buffer */ + +/**/ +mod_export char * +dyncat(const char *s1, const char *s2) +{ + /* This version always uses space from the current heap. */ + char *ptr; + size_t l1 = strlen(s1); + + ptr = (char *)zhalloc(l1 + strlen(s2) + 1); + strcpy(ptr, s1); + strcpy(ptr + l1, s2); + return ptr; +} + +/**/ +mod_export char * +bicat(const char *s1, const char *s2) +{ + /* This version always uses permanently-allocated space. */ + char *ptr; + size_t l1 = strlen(s1); + + ptr = (char *)zalloc(l1 + strlen(s2) + 1); + strcpy(ptr, s1); + strcpy(ptr + l1, s2); + return ptr; +} + +/* like dupstring(), but with a specified length */ + +/**/ +mod_export char * +dupstrpfx(const char *s, int len) +{ + char *r = zhalloc(len + 1); + + memcpy(r, s, len); + r[len] = '\0'; + return r; +} + +/**/ +mod_export char * +ztrduppfx(const char *s, int len) +{ + /* This version always uses permanently-allocated space. */ + char *r = zalloc(len + 1); + + memcpy(r, s, len); + r[len] = '\0'; + return r; +} + +/* Append a string to an allocated string, reallocating to make room. */ + +/**/ +mod_export char * +appstr(char *base, char const *append) +{ + return strcat(realloc(base, strlen(base) + strlen(append) + 1), append); +} + +/* Return a pointer to the last character of a string, + unless the string is empty. */ + +/**/ +mod_export char * +strend(char *str) +{ + if (*str == '\0') + return str; + return str + strlen (str) - 1; +} diff --git a/dotfiles/system/.zsh/modules/Src/utils.c b/dotfiles/system/.zsh/modules/Src/utils.c new file mode 100644 index 0000000..075d272 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/utils.c @@ -0,0 +1,7520 @@ +/* + * utils.c - miscellaneous utilities + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "utils.pro" + +/* name of script being sourced */ + +/**/ +mod_export char *scriptname; /* is sometimes a function name */ + +/* filename of script or other file containing code source e.g. autoload */ + +/**/ +mod_export char *scriptfilename; + +/* != 0 if we are in a new style completion function */ + +/**/ +mod_export int incompfunc; + +#ifdef MULTIBYTE_SUPPORT +struct widechar_array { + wchar_t *chars; + size_t len; +}; +typedef struct widechar_array *Widechar_array; + +/* + * The wordchars variable turned into a wide character array. + * This is much more convenient for testing. + */ +static struct widechar_array wordchars_wide; + +/* + * The same for the separators (IFS) array. + */ +static struct widechar_array ifs_wide; + +/* Function to set one of the above from the multibyte array */ + +static void +set_widearray(char *mb_array, Widechar_array wca) +{ + if (wca->chars) { + free(wca->chars); + wca->chars = NULL; + } + wca->len = 0; + + if (!isset(MULTIBYTE)) + return; + + if (mb_array) { + VARARR(wchar_t, tmpwcs, strlen(mb_array)); + wchar_t *wcptr = tmpwcs; + wint_t wci; + + mb_charinit(); + while (*mb_array) { + int mblen; + + if (STOUC(*mb_array) <= 0x7f) { + mb_array++; + *wcptr++ = (wchar_t)*mb_array; + continue; + } + + mblen = mb_metacharlenconv(mb_array, &wci); + + if (!mblen) + break; + /* No good unless all characters are convertible */ + if (wci == WEOF) + return; + *wcptr++ = (wchar_t)wci; +#ifdef DEBUG + /* + * This generates a warning from the compiler (and is + * indeed useless) if chars are unsigned. It's + * extreme paranoia anyway. + */ + if (wcptr[-1] < 0) + fprintf(stderr, "BUG: Bad cast to wchar_t\n"); +#endif + mb_array += mblen; + } + + wca->len = wcptr - tmpwcs; + wca->chars = (wchar_t *)zalloc(wca->len * sizeof(wchar_t)); + wmemcpy(wca->chars, tmpwcs, wca->len); + } +} +#endif + + +/* Print an error + + The following functions use the following printf-like format codes + (implemented by zerrmsg()): + + Code Argument types Prints + %s const char * C string (null terminated) + %l const char *, int C string of given length (null not required) + %L long decimal value + %d int decimal value + %% (none) literal '%' + %c int character at that codepoint + %e int strerror() message (argument is typically 'errno') + */ + +static void +zwarning(const char *cmd, const char *fmt, va_list ap) +{ + if (isatty(2)) + zleentry(ZLE_CMD_TRASH); + + char *prefix = scriptname ? scriptname : (argzero ? argzero : ""); + + if (cmd) { + if (unset(SHINSTDIN) || locallevel) { + nicezputs(prefix, stderr); + fputc((unsigned char)':', stderr); + } + nicezputs(cmd, stderr); + fputc((unsigned char)':', stderr); + } else { + /* + * scriptname is set when sourcing scripts, so that we get the + * correct name instead of the generic name of whatever + * program/script is running. It's also set in shell functions, + * so test locallevel, too. + */ + nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" : prefix, stderr); + fputc((unsigned char)':', stderr); + } + + zerrmsg(stderr, fmt, ap); +} + + +/**/ +mod_export void +zerr(VA_ALIST1(const char *fmt)) +VA_DCL +{ + va_list ap; + VA_DEF_ARG(const char *fmt); + + if (errflag || noerrs) { + if (noerrs < 2) + errflag |= ERRFLAG_ERROR; + return; + } + errflag |= ERRFLAG_ERROR; + + VA_START(ap, fmt); + VA_GET_ARG(ap, fmt, const char *); + zwarning(NULL, fmt, ap); + va_end(ap); +} + +/**/ +mod_export void +zerrnam(VA_ALIST2(const char *cmd, const char *fmt)) +VA_DCL +{ + va_list ap; + VA_DEF_ARG(const char *cmd); + VA_DEF_ARG(const char *fmt); + + if (errflag || noerrs) + return; + errflag |= ERRFLAG_ERROR; + + VA_START(ap, fmt); + VA_GET_ARG(ap, cmd, const char *); + VA_GET_ARG(ap, fmt, const char *); + zwarning(cmd, fmt, ap); + va_end(ap); +} + +/**/ +mod_export void +zwarn(VA_ALIST1(const char *fmt)) +VA_DCL +{ + va_list ap; + VA_DEF_ARG(const char *fmt); + + if (errflag || noerrs) + return; + + VA_START(ap, fmt); + VA_GET_ARG(ap, fmt, const char *); + zwarning(NULL, fmt, ap); + va_end(ap); +} + +/**/ +mod_export void +zwarnnam(VA_ALIST2(const char *cmd, const char *fmt)) +VA_DCL +{ + va_list ap; + VA_DEF_ARG(const char *cmd); + VA_DEF_ARG(const char *fmt); + + if (errflag || noerrs) + return; + + VA_START(ap, fmt); + VA_GET_ARG(ap, cmd, const char *); + VA_GET_ARG(ap, fmt, const char *); + zwarning(cmd, fmt, ap); + va_end(ap); +} + + +#ifdef DEBUG + +/**/ +mod_export void +dputs(VA_ALIST1(const char *message)) +VA_DCL +{ + char *filename; + FILE *file; + va_list ap; + VA_DEF_ARG(const char *message); + + VA_START(ap, message); + VA_GET_ARG(ap, message, const char *); + if ((filename = getsparam_u("ZSH_DEBUG_LOG")) != NULL && + (file = fopen(filename, "a")) != NULL) { + zerrmsg(file, message, ap); + fclose(file); + } else + zerrmsg(stderr, message, ap); + va_end(ap); +} + +#endif /* DEBUG */ + +#ifdef __CYGWIN__ +/* + * This works around an occasional problem with dllwrap on Cygwin, seen + * on at least two installations. It fails to find the last symbol + * exported in alphabetical order (in our case zwarnnam). Until this is + * properly categorised and fixed we add a dummy symbol at the end. + */ +mod_export void +zz_plural_z_alpha(void) +{ +} +#endif + +/**/ +void +zerrmsg(FILE *file, const char *fmt, va_list ap) +{ + const char *str; + int num; +#ifdef DEBUG + long lnum; +#endif +#ifdef HAVE_STRERROR_R +#define ERRBUFSIZE (80) + int olderrno; + char errbuf[ERRBUFSIZE]; +#endif + char *errmsg; + + if ((unset(SHINSTDIN) || locallevel) && lineno) { +#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) + fprintf(file, "%lld: ", lineno); +#else + fprintf(file, "%ld: ", (long)lineno); +#endif + } else + fputc((unsigned char)' ', file); + + while (*fmt) + if (*fmt == '%') { + fmt++; + switch (*fmt++) { + case 's': + str = va_arg(ap, const char *); + nicezputs(str, file); + break; + case 'l': { + char *s; + str = va_arg(ap, const char *); + num = va_arg(ap, int); + num = metalen(str, num); + s = zhalloc(num + 1); + memcpy(s, str, num); + s[num] = '\0'; + nicezputs(s, file); + break; + } +#ifdef DEBUG + case 'L': + lnum = va_arg(ap, long); + fprintf(file, "%ld", lnum); + break; +#endif + case 'd': + num = va_arg(ap, int); + fprintf(file, "%d", num); + break; + case '%': + putc('%', file); + break; + case 'c': + num = va_arg(ap, int); +#ifdef MULTIBYTE_SUPPORT + mb_charinit(); + zputs(wcs_nicechar(num, NULL, NULL), file); +#else + zputs(nicechar(num), file); +#endif + break; + case 'e': + /* print the corresponding message for this errno */ + num = va_arg(ap, int); + if (num == EINTR) { + fputs("interrupt\n", file); + errflag |= ERRFLAG_ERROR; + return; + } + errmsg = strerror(num); + /* If the message is not about I/O problems, it looks better * + * if we uncapitalize the first letter of the message */ + if (num == EIO) + fputs(errmsg, file); + else { + fputc(tulower(errmsg[0]), file); + fputs(errmsg + 1, file); + } + break; + /* When adding format codes, update the comment above zwarning(). */ + } + } else { + putc(*fmt == Meta ? *++fmt ^ 32 : *fmt, file); + fmt++; + } + putc('\n', file); + fflush(file); +} + +/* + * Wrapper for setupterm() and del_curterm(). + * These are called from terminfo.c and termcap.c. + */ +static int term_count; /* reference count of cur_term */ + +/**/ +mod_export void +zsetupterm(void) +{ +#ifdef HAVE_SETUPTERM + int errret; + + DPUTS(term_count < 0 || (term_count > 0 && !cur_term), + "inconsistent term_count and/or cur_term"); + /* + * Just because we can't set up the terminal doesn't + * mean the modules hasn't booted---TERM may change, + * and it should be handled dynamically---so ignore errors here. + */ + if (term_count++ == 0) + (void)setupterm((char *)0, 1, &errret); +#endif +} + +/**/ +mod_export void +zdeleteterm(void) +{ +#ifdef HAVE_SETUPTERM + DPUTS(term_count < 1 || !cur_term, + "inconsistent term_count and/or cur_term"); + if (--term_count == 0) + del_curterm(cur_term); +#endif +} + +/* Output a single character, for the termcap routines. * + * This is used instead of putchar since it can be a macro. */ + +/**/ +mod_export int +putraw(int c) +{ + putc(c, stdout); + return 0; +} + +/* Output a single character, for the termcap routines. */ + +/**/ +mod_export int +putshout(int c) +{ + putc(c, shout); + return 0; +} + +#ifdef MULTIBYTE_SUPPORT +/* + * Turn a character into a visible representation thereof. The visible + * string is put together in a static buffer, and this function returns + * a pointer to it. Printable characters stand for themselves, DEL is + * represented as "^?", newline and tab are represented as "\n" and + * "\t", and normal control characters are represented in "^C" form. + * Characters with bit 7 set, if unprintable, are represented as "\M-" + * followed by the visible representation of the character with bit 7 + * stripped off. Tokens are interpreted, rather than being treated as + * literal characters. + * + * Note that the returned string is metafied, so that it must be + * treated like any other zsh internal string (and not, for example, + * output directly). + * + * This function is used even if MULTIBYTE_SUPPORT is defined: we + * use it as a fallback in case we couldn't identify a wide character + * in a multibyte string. + */ + +/**/ +mod_export char * +nicechar_sel(int c, int quotable) +{ + static char buf[10]; + char *s = buf; + c &= 0xff; + if (ZISPRINT(c)) + goto done; + if (c & 0x80) { + if (isset(PRINTEIGHTBIT)) + goto done; + *s++ = '\\'; + *s++ = 'M'; + *s++ = '-'; + c &= 0x7f; + if(ZISPRINT(c)) + goto done; + } + if (c == 0x7f) { + if (quotable) { + *s++ = '\\'; + *s++ = 'C'; + *s++ = '-'; + } else + *s++ = '^'; + c = '?'; + } else if (c == '\n') { + *s++ = '\\'; + c = 'n'; + } else if (c == '\t') { + *s++ = '\\'; + c = 't'; + } else if (c < 0x20) { + if (quotable) { + *s++ = '\\'; + *s++ = 'C'; + *s++ = '-'; + } else + *s++ = '^'; + c += 0x40; + } + done: + /* + * The resulting string is still metafied, so check if + * we are returning a character in the range that needs metafication. + * This can't happen if the character is printed "nicely", so + * this results in a maximum of two bytes total (plus the null). + */ + if (imeta(c)) { + *s++ = Meta; + *s++ = c ^ 32; + } else + *s++ = c; + *s = 0; + return buf; +} + +/**/ +mod_export char * +nicechar(int c) +{ + return nicechar_sel(c, 0); +} + +#else /* MULTIBYTE_SUPPORT */ + +/**/ +mod_export char * +nicechar(int c) +{ + static char buf[10]; + char *s = buf; + c &= 0xff; + if (ZISPRINT(c)) + goto done; + if (c & 0x80) { + if (isset(PRINTEIGHTBIT)) + goto done; + *s++ = '\\'; + *s++ = 'M'; + *s++ = '-'; + c &= 0x7f; + if(ZISPRINT(c)) + goto done; + } + if (c == 0x7f) { + *s++ = '\\'; + *s++ = 'C'; + *s++ = '-'; + c = '?'; + } else if (c == '\n') { + *s++ = '\\'; + c = 'n'; + } else if (c == '\t') { + *s++ = '\\'; + c = 't'; + } else if (c < 0x20) { + *s++ = '\\'; + *s++ = 'C'; + *s++ = '-'; + c += 0x40; + } + done: + /* + * The resulting string is still metafied, so check if + * we are returning a character in the range that needs metafication. + * This can't happen if the character is printed "nicely", so + * this results in a maximum of two bytes total (plus the null). + */ + if (imeta(c)) { + *s++ = Meta; + *s++ = c ^ 32; + } else + *s++ = c; + *s = 0; + return buf; +} + +#endif /* MULTIBYTE_SUPPORT */ + +/* + * Return 1 if nicechar() would reformat this character. + */ + +/**/ +mod_export int +is_nicechar(int c) +{ + c &= 0xff; + if (ZISPRINT(c)) + return 0; + if (c & 0x80) + return !isset(PRINTEIGHTBIT); + return (c == 0x7f || c == '\n' || c == '\t' || c < 0x20); +} + +/**/ +#ifdef MULTIBYTE_SUPPORT +static mbstate_t mb_shiftstate; + +/* + * Initialise multibyte state: called before a sequence of + * wcs_nicechar(), mb_metacharlenconv(), or + * mb_charlenconv(). + */ + +/**/ +mod_export void +mb_charinit(void) +{ + memset(&mb_shiftstate, 0, sizeof(mb_shiftstate)); +} + +/* + * The number of bytes we need to allocate for a "nice" representation + * of a multibyte character. + * + * We double MB_CUR_MAX to take account of the fact that + * we may need to metafy. In fact the representation probably + * doesn't allow every character to be in the meta range, but + * we don't need to be too pedantic. + * + * The 12 is for the output of a UCS-4 code; we don't actually + * need this at the same time as MB_CUR_MAX, but again it's + * not worth calculating more exactly. + */ +#define NICECHAR_MAX (12 + 2*MB_CUR_MAX) +/* + * Input a wide character. Output a printable representation, + * which is a metafied multibyte string. With widthp return + * the printing width. + * + * swide, if non-NULL, is used to help the completion code, which needs + * to know the printing width of the each part of the representation. + * *swide is set to the part of the returned string where the wide + * character starts. Any string up to that point is ASCII characters, + * so the width of it is (*swide - ). Anything left is + * a single wide character corresponding to the remaining width. + * Either the initial ASCII part or the wide character part may be empty + * (but not both). (Note the complication that the wide character + * part may contain metafied characters.) + * + * The caller needs to call mb_charinit() before the first call, to + * set up the multibyte shift state for a range of characters. + */ + +/**/ +mod_export char * +wcs_nicechar_sel(wchar_t c, size_t *widthp, char **swidep, int quotable) +{ + static char *buf; + static int bufalloc = 0, newalloc; + char *s, *mbptr; + int ret = 0; + VARARR(char, mbstr, MB_CUR_MAX); + + /* + * We want buf to persist beyond the return. MB_CUR_MAX and hence + * NICECHAR_MAX may not be constant, so we have to allocate this at + * run time. (We could probably get away with just allocating a + * large buffer, in practice.) For efficiency, only reallocate if + * we really need to, since this function will be called frequently. + */ + newalloc = NICECHAR_MAX; + if (bufalloc != newalloc) + { + bufalloc = newalloc; + buf = (char *)zrealloc(buf, bufalloc); + } + + s = buf; + if (!WC_ISPRINT(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) { + if (c == 0x7f) { + if (quotable) { + *s++ = '\\'; + *s++ = 'C'; + *s++ = '-'; + } else + *s++ = '^'; + c = '?'; + } else if (c == L'\n') { + *s++ = '\\'; + c = 'n'; + } else if (c == L'\t') { + *s++ = '\\'; + c = 't'; + } else if (c < 0x20) { + if (quotable) { + *s++ = '\\'; + *s++ = 'C'; + *s++ = '-'; + } else + *s++ = '^'; + c += 0x40; + } else if (c >= 0x80) { + ret = -1; + } + } + + if (ret != -1) + ret = wcrtomb(mbstr, c, &mb_shiftstate); + + if (ret == -1) { + memset(&mb_shiftstate, 0, sizeof(mb_shiftstate)); + /* + * Can't or don't want to convert character: use UCS-2 or + * UCS-4 code in print escape format. + * + * This comparison fails and generates a compiler warning + * if wchar_t is 16 bits, but the code is still correct. + */ + if (c >= 0x10000) { + sprintf(buf, "\\U%.8x", (unsigned int)c); + if (widthp) + *widthp = 10; + } else if (c >= 0x100) { + sprintf(buf, "\\u%.4x", (unsigned int)c); + if (widthp) + *widthp = 6; + } else { + strcpy(buf, nicechar((int)c)); + /* + * There may be metafied characters from nicechar(), + * so compute width and end position independently. + */ + if (widthp) + *widthp = ztrlen(buf); + if (swidep) + *swidep = buf + strlen(buf); + return buf; + } + if (swidep) + *swidep = widthp ? buf + *widthp : buf; + return buf; + } + + if (widthp) { + int wcw = WCWIDTH(c); + *widthp = (s - buf); + if (wcw >= 0) + *widthp += wcw; + else + (*widthp)++; + } + if (swidep) + *swidep = s; + for (mbptr = mbstr; ret; s++, mbptr++, ret--) { + DPUTS(s >= buf + NICECHAR_MAX, + "BUG: buffer too small in wcs_nicechar"); + if (imeta(*mbptr)) { + *s++ = Meta; + DPUTS(s >= buf + NICECHAR_MAX, + "BUG: buffer too small for metafied char in wcs_nicechar"); + *s = *mbptr ^ 32; + } else { + *s = *mbptr; + } + } + *s = 0; + return buf; +} + +/**/ +mod_export char * +wcs_nicechar(wchar_t c, size_t *widthp, char **swidep) +{ + return wcs_nicechar_sel(c, widthp, swidep, 0); +} + +/* + * Return 1 if wcs_nicechar() would reformat this character for display. + */ + +/**/ +mod_export int is_wcs_nicechar(wchar_t c) +{ + if (!WC_ISPRINT(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) { + if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20) + return 1; + if (c >= 0x80) { + return (c >= 0x100); + } + } + return 0; +} + +/**/ +mod_export int +zwcwidth(wint_t wc) +{ + int wcw; + /* assume a single-byte character if not valid */ + if (wc == WEOF || unset(MULTIBYTE)) + return 1; + wcw = WCWIDTH(wc); + /* if not printable, assume width 1 */ + if (wcw < 0) + return 1; + return wcw; +} + +/**/ +#endif /* MULTIBYTE_SUPPORT */ + +/* + * Search the path for prog and return the file name. + * The returned value is unmetafied and in the unmeta storage + * area (N.B. should be duplicated if not used immediately and not + * equal to *namep). + * + * If namep is not NULL, *namep is set to the metafied programme + * name, which is in heap storage. + */ +/**/ +char * +pathprog(char *prog, char **namep) +{ + char **pp, ppmaxlen = 0, *buf, *funmeta; + struct stat st; + + for (pp = path; *pp; pp++) + { + int len = strlen(*pp); + if (len > ppmaxlen) + ppmaxlen = len; + } + buf = zhalloc(ppmaxlen + strlen(prog) + 2); + for (pp = path; *pp; pp++) { + sprintf(buf, "%s/%s", *pp, prog); + funmeta = unmeta(buf); + if (access(funmeta, F_OK) == 0 && + stat(funmeta, &st) >= 0 && + !S_ISDIR(st.st_mode)) { + if (namep) + *namep = buf; + return funmeta; + } + } + + return NULL; +} + +/* get a symlink-free pathname for s relative to PWD */ + +/**/ +char * +findpwd(char *s) +{ + char *t; + + if (*s == '/') + return xsymlink(s, 0); + s = tricat((pwd[1]) ? pwd : "", "/", s); + t = xsymlink(s, 0); + zsfree(s); + return t; +} + +/* Check whether a string contains the * + * name of the present directory. */ + +/**/ +int +ispwd(char *s) +{ + struct stat sbuf, tbuf; + + /* POSIX: environment PWD must be absolute */ + if (*s != '/') + return 0; + + if (stat((s = unmeta(s)), &sbuf) == 0 && stat(".", &tbuf) == 0) + if (sbuf.st_dev == tbuf.st_dev && sbuf.st_ino == tbuf.st_ino) { + /* POSIX: No element of $PWD may be "." or ".." */ + while (*s) { + if (s[0] == '.' && + (!s[1] || s[1] == '/' || + (s[1] == '.' && (!s[2] || s[2] == '/')))) + break; + while (*s++ != '/' && *s) + continue; + } + return !*s; + } + return 0; +} + +static char xbuf[PATH_MAX*2+1]; + +/**/ +static char ** +slashsplit(char *s) +{ + char *t, **r, **q; + int t0; + + if (!*s) + return (char **) zshcalloc(sizeof(char *)); + + for (t = s, t0 = 0; *t; t++) + if (*t == '/') + t0++; + q = r = (char **) zalloc(sizeof(char *) * (t0 + 2)); + + while ((t = strchr(s, '/'))) { + *q++ = ztrduppfx(s, t - s); + while (*t == '/') + t++; + if (!*t) { + *q = NULL; + return r; + } + s = t; + } + *q++ = ztrdup(s); + *q = NULL; + return r; +} + +/* expands symlinks and .. or . expressions */ + +/**/ +static int +xsymlinks(char *s, int full) +{ + char **pp, **opp; + char xbuf2[PATH_MAX*3+1], xbuf3[PATH_MAX*2+1]; + int t0, ret = 0; + zulong xbuflen = strlen(xbuf), pplen; + + opp = pp = slashsplit(s); + for (; xbuflen < sizeof(xbuf) && *pp && ret >= 0; pp++) { + if (!strcmp(*pp, ".")) + continue; + if (!strcmp(*pp, "..")) { + char *p; + + if (!strcmp(xbuf, "/")) + continue; + if (!*xbuf) + continue; + p = xbuf + xbuflen; + while (*--p != '/') + xbuflen--; + *p = '\0'; + /* The \0 isn't included in the length */ + xbuflen--; + continue; + } + /* Includes null byte. */ + pplen = strlen(*pp) + 1; + if (xbuflen + pplen + 1 > sizeof(xbuf2)) { + *xbuf = 0; + ret = -1; + break; + } + memcpy(xbuf2, xbuf, xbuflen); + xbuf2[xbuflen] = '/'; + memcpy(xbuf2 + xbuflen + 1, *pp, pplen); + t0 = readlink(unmeta(xbuf2), xbuf3, PATH_MAX); + if (t0 == -1) { + if ((xbuflen += pplen) < sizeof(xbuf)) { + strcat(xbuf, "/"); + strcat(xbuf, *pp); + } else { + *xbuf = 0; + ret = -1; + break; + } + } else { + ret = 1; + metafy(xbuf3, t0, META_NOALLOC); + if (!full) { + /* + * If only one expansion requested, ensure the + * full path is in xbuf. + */ + zulong len = xbuflen; + if (*xbuf3 == '/') + strcpy(xbuf, xbuf3); + else if ((len += strlen(xbuf3) + 1) < sizeof(xbuf)) { + strcpy(xbuf + xbuflen, "/"); + strcpy(xbuf + xbuflen + 1, xbuf3); + } else { + *xbuf = 0; + ret = -1; + break; + } + + while (*++pp) { + zulong newlen = len + strlen(*pp) + 1; + if (newlen < sizeof(xbuf)) { + strcpy(xbuf + len, "/"); + strcpy(xbuf + len + 1, *pp); + len = newlen; + } else { + *xbuf = 01; + ret = -1; + break; + } + } + /* + * No need to update xbuflen, we're finished + * the expansion (for now). + */ + break; + } + if (*xbuf3 == '/') { + strcpy(xbuf, ""); + if (xsymlinks(xbuf3 + 1, 1) < 0) + ret = -1; + else + xbuflen = strlen(xbuf); + } else + if (xsymlinks(xbuf3, 1) < 0) + ret = -1; + else + xbuflen = strlen(xbuf); + } + } + freearray(opp); + return ret; +} + +/* + * expand symlinks in s, and remove other weird things: + * note that this always expands symlinks. + * + * 'heap' indicates whether to malloc() or allocate on the heap. + */ + +/**/ +char * +xsymlink(char *s, int heap) +{ + if (*s != '/') + return NULL; + *xbuf = '\0'; + if (xsymlinks(s + 1, 1) < 0) + zwarn("path expansion failed, using root directory"); + if (!*xbuf) + return heap ? dupstring("/") : ztrdup("/"); + return heap ? dupstring(xbuf) : ztrdup(xbuf); +} + +/**/ +void +print_if_link(char *s, int all) +{ + if (*s == '/') { + *xbuf = '\0'; + if (all) { + char *start = s + 1; + char xbuflink[PATH_MAX+1]; + for (;;) { + if (xsymlinks(start, 0) > 0) { + printf(" -> "); + zputs(*xbuf ? xbuf : "/", stdout); + if (!*xbuf) + break; + strcpy(xbuflink, xbuf); + start = xbuflink + 1; + *xbuf = '\0'; + } else { + break; + } + } + } else { + if (xsymlinks(s + 1, 1) > 0) + printf(" -> "), zputs(*xbuf ? xbuf : "/", stdout); + } + } +} + +/* print a directory */ + +/**/ +void +fprintdir(char *s, FILE *f) +{ + Nameddir d = finddir(s); + + if (!d) + fputs(unmeta(s), f); + else { + putc('~', f); + fputs(unmeta(d->node.nam), f); + fputs(unmeta(s + strlen(d->dir)), f); + } +} + +/* + * Substitute a directory using a name. + * If there is none, return the original argument. + * + * At this level all strings involved are metafied. + */ + +/**/ +char * +substnamedir(char *s) +{ + Nameddir d = finddir(s); + + if (!d) + return quotestring(s, QT_BACKSLASH); + return zhtricat("~", d->node.nam, quotestring(s + strlen(d->dir), + QT_BACKSLASH)); +} + + +/* Returns the current username. It caches the username * + * and uid to try to avoid requerying the password files * + * or NIS/NIS+ database. */ + +/**/ +uid_t cached_uid; +/**/ +char *cached_username; + +/**/ +char * +get_username(void) +{ +#ifdef HAVE_GETPWUID + struct passwd *pswd; + uid_t current_uid; + + current_uid = getuid(); + if (current_uid != cached_uid) { + cached_uid = current_uid; + zsfree(cached_username); + if ((pswd = getpwuid(current_uid))) + cached_username = ztrdup(pswd->pw_name); + else + cached_username = ztrdup(""); + } +#else /* !HAVE_GETPWUID */ + cached_uid = getuid(); +#endif /* !HAVE_GETPWUID */ + return cached_username; +} + +/* static variables needed by finddir(). */ + +static char *finddir_full; +static Nameddir finddir_last; +static int finddir_best; + +/* ScanFunc used by finddir(). */ + +/**/ +static void +finddir_scan(HashNode hn, UNUSED(int flags)) +{ + Nameddir nd = (Nameddir) hn; + + if(nd->diff > finddir_best && !dircmp(nd->dir, finddir_full) + && !(nd->node.flags & ND_NOABBREV)) { + finddir_last=nd; + finddir_best=nd->diff; + } +} + +/* + * See if a path has a named directory as its prefix. + * If passed a NULL argument, it will invalidate any + * cached information. + * + * s here is metafied. + */ + +/**/ +Nameddir +finddir(char *s) +{ + static struct nameddir homenode = { {NULL, "", 0}, NULL, 0 }; + static int ffsz; + char **ares; + int len; + + /* Invalidate directory cache if argument is NULL. This is called * + * whenever a node is added to or removed from the hash table, and * + * whenever the value of $HOME changes. (On startup, too.) */ + if (!s) { + homenode.dir = home ? home : ""; + homenode.diff = home ? strlen(home) : 0; + if(homenode.diff==1) + homenode.diff = 0; + if(!finddir_full) + finddir_full = zalloc(ffsz = PATH_MAX+1); + finddir_full[0] = 0; + return finddir_last = NULL; + } + +#if 0 + /* + * It's not safe to use the cache while we have function + * transformations, and it's not clear it's worth the + * complexity of guessing here whether subst_string_by_hook + * is going to turn up the goods. + */ + if (!strcmp(s, finddir_full) && *finddir_full) + return finddir_last; +#endif + + if ((int)strlen(s) >= ffsz) { + free(finddir_full); + finddir_full = zalloc(ffsz = strlen(s) * 2); + } + strcpy(finddir_full, s); + finddir_best=0; + finddir_last=NULL; + finddir_scan(&homenode.node, 0); + scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 0); + + ares = subst_string_by_hook("zsh_directory_name", "d", finddir_full); + if (ares && arrlen_ge(ares, 2) && + (len = (int)zstrtol(ares[1], NULL, 10)) > finddir_best) { + /* better duplicate this string since it's come from REPLY */ + finddir_last = (Nameddir)hcalloc(sizeof(struct nameddir)); + finddir_last->node.nam = zhtricat("[", dupstring(ares[0]), "]"); + finddir_last->dir = dupstrpfx(finddir_full, len); + finddir_last->diff = len - strlen(finddir_last->node.nam); + finddir_best = len; + } + + return finddir_last; +} + +/* add a named directory */ + +/**/ +mod_export void +adduserdir(char *s, char *t, int flags, int always) +{ + Nameddir nd; + char *eptr; + + /* We don't maintain a hash table in non-interactive shells. */ + if (!interact) + return; + + /* The ND_USERNAME flag means that this possible hash table * + * entry is derived from a passwd entry. Such entries are * + * subordinate to explicitly generated entries. */ + if ((flags & ND_USERNAME) && nameddirtab->getnode2(nameddirtab, s)) + return; + + /* Normal parameter assignments generate calls to this function, * + * with always==0. Unless the AUTO_NAME_DIRS option is set, we * + * don't let such assignments actually create directory names. * + * Instead, a reference to the parameter as a directory name can * + * cause the actual creation of the hash table entry. */ + if (!always && unset(AUTONAMEDIRS) && + !nameddirtab->getnode2(nameddirtab, s)) + return; + + if (!t || *t != '/' || strlen(t) >= PATH_MAX) { + /* We can't use this value as a directory, so simply remove * + * the corresponding entry in the hash table, if any. */ + HashNode hn = nameddirtab->removenode(nameddirtab, s); + + if(hn) + nameddirtab->freenode(hn); + return; + } + + /* add the name */ + nd = (Nameddir) zshcalloc(sizeof *nd); + nd->node.flags = flags; + eptr = t + strlen(t); + while (eptr > t && eptr[-1] == '/') + eptr--; + if (eptr == t) { + /* + * Don't abbreviate multiple slashes at the start of a + * named directory, since these are sometimes used for + * special purposes. + */ + nd->dir = metafy(t, -1, META_DUP); + } else + nd->dir = metafy(t, eptr - t, META_DUP); + /* The variables PWD and OLDPWD are not to be displayed as ~PWD etc. */ + if (!strcmp(s, "PWD") || !strcmp(s, "OLDPWD")) + nd->node.flags |= ND_NOABBREV; + nameddirtab->addnode(nameddirtab, metafy(s, -1, META_DUP), nd); +} + +/* Get a named directory: this function can cause a directory name * + * to be added to the hash table, if it isn't there already. */ + +/**/ +char * +getnameddir(char *name) +{ + Param pm; + char *str; + Nameddir nd; + + /* Check if it is already in the named directory table */ + if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name))) + return dupstring(nd->dir); + + /* Check if there is a scalar parameter with this name whose value * + * begins with a `/'. If there is, add it to the hash table and * + * return the new value. */ + if ((pm = (Param) paramtab->getnode(paramtab, name)) && + (PM_TYPE(pm->node.flags) == PM_SCALAR) && + (str = getsparam(name)) && *str == '/') { + pm->node.flags |= PM_NAMEDDIR; + adduserdir(name, str, 0, 1); + return str; + } + +#ifdef HAVE_GETPWNAM + { + /* Retrieve an entry from the password table/database for this user. */ + struct passwd *pw; + if ((pw = getpwnam(name))) { + char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir, 0) + : ztrdup(pw->pw_dir); + if (dir) { + adduserdir(name, dir, ND_USERNAME, 1); + str = dupstring(dir); + zsfree(dir); + return str; + } else + return dupstring(pw->pw_dir); + } + } +#endif /* HAVE_GETPWNAM */ + + /* There are no more possible sources of directory names, so give up. */ + return NULL; +} + +/* + * Compare directories. Both are metafied. + */ + +/**/ +static int +dircmp(char *s, char *t) +{ + if (s) { + for (; *s == *t; s++, t++) + if (!*s) + return 0; + if (!*s && *t == '/') + return 0; + } + return 1; +} + +/* + * Extra functions to call before displaying the prompt. + * The data is a Prepromptfn. + */ + +static LinkList prepromptfns; + +/* Add a function to the list of pre-prompt functions. */ + +/**/ +mod_export void +addprepromptfn(voidvoidfnptr_t func) +{ + Prepromptfn ppdat = (Prepromptfn)zalloc(sizeof(struct prepromptfn)); + ppdat->func = func; + if (!prepromptfns) + prepromptfns = znewlinklist(); + zaddlinknode(prepromptfns, ppdat); +} + +/* Remove a function from the list of pre-prompt functions. */ + +/**/ +mod_export void +delprepromptfn(voidvoidfnptr_t func) +{ + LinkNode ln; + + for (ln = firstnode(prepromptfns); ln; ln = nextnode(ln)) { + Prepromptfn ppdat = (Prepromptfn)getdata(ln); + if (ppdat->func == func) { + (void)remnode(prepromptfns, ln); + zfree(ppdat, sizeof(struct prepromptfn)); + return; + } + } +#ifdef DEBUG + dputs("BUG: failed to delete node from prepromptfns"); +#endif +} + +/* + * Functions to call at a particular time even if not at + * the prompt. This is handled by zle. The data is a + * Timedfn. The functions must be in time order, but this + * is enforced by addtimedfn(). + * + * Note on debugging: the code in sched.c currently assumes it's + * the only user of timedfns for the purposes of checking whether + * there's a function on the list. If this becomes no longer the case, + * the DPUTS() tests in sched.c need rewriting. + */ + +/**/ +mod_export LinkList timedfns; + +/* Add a function to the list of timed functions. */ + +/**/ +mod_export void +addtimedfn(voidvoidfnptr_t func, time_t when) +{ + Timedfn tfdat = (Timedfn)zalloc(sizeof(struct timedfn)); + tfdat->func = func; + tfdat->when = when; + + if (!timedfns) { + timedfns = znewlinklist(); + zaddlinknode(timedfns, tfdat); + } else { + LinkNode ln = firstnode(timedfns); + + /* + * Insert the new element in the linked list. We do + * rather too much work here since the standard + * functions insert after a given node, whereas we + * want to insert the new data before the first element + * with a greater time. + * + * In practice, the only use of timed functions is + * sched, which only adds the one function; so this + * whole branch isn't used beyond the following block. + */ + if (!ln) { + zaddlinknode(timedfns, tfdat); + return; + } + for (;;) { + Timedfn tfdat2; + LinkNode next = nextnode(ln); + if (!next) { + zaddlinknode(timedfns, tfdat); + return; + } + tfdat2 = (Timedfn)getdata(next); + if (when < tfdat2->when) { + zinsertlinknode(timedfns, ln, tfdat); + return; + } + ln = next; + } + } +} + +/* + * Delete a function from the list of timed functions. + * Note that if the function apperas multiple times only + * the first occurrence will be removed. + * + * Note also that when zle calls the function it does *not* + * automatically delete the entry from the list. That must + * be done by the function called. This is recommended as otherwise + * the function will keep being called immediately. (It just so + * happens this "feature" fits in well with the only current use + * of timed functions.) + */ + +/**/ +mod_export void +deltimedfn(voidvoidfnptr_t func) +{ + LinkNode ln; + + for (ln = firstnode(timedfns); ln; ln = nextnode(ln)) { + Timedfn ppdat = (Timedfn)getdata(ln); + if (ppdat->func == func) { + (void)remnode(timedfns, ln); + zfree(ppdat, sizeof(struct timedfn)); + return; + } + } +#ifdef DEBUG + dputs("BUG: failed to delete node from timedfns"); +#endif +} + +/* the last time we checked mail */ + +/**/ +time_t lastmailcheck; + +/* the last time we checked the people in the WATCH variable */ + +/**/ +time_t lastwatch; + +/* + * Call a function given by "name" with optional arguments + * "lnklist". If these are present the first argument is the function name. + * + * If "arrayp" is not zero, we also look through + * the array "name"_functions and execute functions found there. + * + * If "retval" is not NULL, the return value of the first hook function to + * return non-zero is stored in *"retval". The return value is not otherwise + * available as the calling context is restored. + * + * Returns 0 if at least one function was called (regardless of that function's + * exit status), and 1 otherwise. + */ + +/**/ +mod_export int +callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval) +{ + Shfunc shfunc; + /* + * Save stopmsg, since user doesn't get a chance to respond + * to a list of jobs generated in a hook. + */ + int osc = sfcontext, osm = stopmsg, stat = 1, ret = 0; + int old_incompfunc = incompfunc; + + sfcontext = SFC_HOOK; + incompfunc = 0; + + if ((shfunc = getshfunc(name))) { + ret = doshfunc(shfunc, lnklst, 1); + stat = 0; + } + + if (arrayp) { + char **arrptr; + int namlen = strlen(name); + VARARR(char, arrnam, namlen + HOOK_SUFFIX_LEN); + memcpy(arrnam, name, namlen); + memcpy(arrnam + namlen, HOOK_SUFFIX, HOOK_SUFFIX_LEN); + + if ((arrptr = getaparam(arrnam))) { + arrptr = arrdup(arrptr); + for (; *arrptr; arrptr++) { + if ((shfunc = getshfunc(*arrptr))) { + int newret = doshfunc(shfunc, lnklst, 1); + if (!ret) + ret = newret; + stat = 0; + } + } + } + } + + sfcontext = osc; + stopmsg = osm; + incompfunc = old_incompfunc; + + if (retval) + *retval = ret; + return stat; +} + +/* do pre-prompt stuff */ + +/**/ +void +preprompt(void) +{ + static time_t lastperiodic; + time_t currentmailcheck; + LinkNode ln; + zlong period = getiparam("PERIOD"); + zlong mailcheck = getiparam("MAILCHECK"); + + /* + * Handle any pending window size changes before we compute prompts, + * then block them again to avoid interrupts during prompt display. + */ + winch_unblock(); + winch_block(); + + if (isset(PROMPTSP) && isset(PROMPTCR) && !use_exit_printed && shout) { + /* The PROMPT_SP heuristic will move the prompt down to a new line + * if there was any dangling output on the line (assuming the terminal + * has automatic margins, but we try even if hasam isn't set). + * Unfortunately it interacts badly with ZLE displaying message + * when ^D has been pressed. So just disable PROMPT_SP logic in + * this case */ + char *eolmark = getsparam("PROMPT_EOL_MARK"); + char *str; + int percents = opts[PROMPTPERCENT], w = 0; + if (!eolmark) + eolmark = "%B%S%#%s%b"; + opts[PROMPTPERCENT] = 1; + str = promptexpand(eolmark, 1, NULL, NULL, NULL); + countprompt(str, &w, 0, -1); + opts[PROMPTPERCENT] = percents; + zputs(str, shout); + fprintf(shout, "%*s\r%*s\r", (int)zterm_columns - w - !hasxn, + "", w, ""); + fflush(shout); + free(str); + } + + /* If NOTIFY is not set, then check for completed * + * jobs before we print the prompt. */ + if (unset(NOTIFY)) + scanjobs(); + if (errflag) + return; + + /* If a shell function named "precmd" exists, * + * then execute it. */ + callhookfunc("precmd", NULL, 1, NULL); + if (errflag) + return; + + /* If 1) the parameter PERIOD exists, 2) a hook function for * + * "periodic" exists, 3) it's been greater than PERIOD since we * + * executed any such hook, then execute it now. */ + if (period && ((zlong)time(NULL) > (zlong)lastperiodic + period) && + !callhookfunc("periodic", NULL, 1, NULL)) + lastperiodic = time(NULL); + if (errflag) + return; + + /* If WATCH is set, then check for the * + * specified login/logout events. */ + if (watch) { + if ((int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK")) { + dowatch(); + lastwatch = time(NULL); + } + } + if (errflag) + return; + + /* Check mail */ + currentmailcheck = time(NULL); + if (mailcheck && + (zlong) difftime(currentmailcheck, lastmailcheck) > mailcheck) { + char *mailfile; + + if (mailpath && *mailpath && **mailpath) + checkmailpath(mailpath); + else { + queue_signals(); + if ((mailfile = getsparam("MAIL")) && *mailfile) { + char *x[2]; + + x[0] = mailfile; + x[1] = NULL; + checkmailpath(x); + } + unqueue_signals(); + } + lastmailcheck = currentmailcheck; + } + + if (prepromptfns) { + for(ln = firstnode(prepromptfns); ln; ln = nextnode(ln)) { + Prepromptfn ppnode = (Prepromptfn)getdata(ln); + ppnode->func(); + } + } +} + +/**/ +static void +checkmailpath(char **s) +{ + struct stat st; + char *v, *u, c; + + while (*s) { + for (v = *s; *v && *v != '?'; v++); + c = *v; + *v = '\0'; + if (c != '?') + u = NULL; + else + u = v + 1; + if (**s == 0) { + *v = c; + zerr("empty MAILPATH component: %s", *s); + } else if (mailstat(unmeta(*s), &st) == -1) { + if (errno != ENOENT) + zerr("%e: %s", errno, *s); + } else if (S_ISDIR(st.st_mode)) { + LinkList l; + DIR *lock = opendir(unmeta(*s)); + char buf[PATH_MAX * 2 + 1], **arr, **ap; + int buflen, ct = 1; + + if (lock) { + char *fn; + + pushheap(); + l = newlinklist(); + while ((fn = zreaddir(lock, 1)) && !errflag) { + if (u) + buflen = snprintf(buf, sizeof(buf), "%s/%s?%s", *s, fn, u); + else + buflen = snprintf(buf, sizeof(buf), "%s/%s", *s, fn); + if (buflen < 0 || buflen >= (int)sizeof(buf)) + continue; + addlinknode(l, dupstring(buf)); + ct++; + } + closedir(lock); + ap = arr = (char **) zhalloc(ct * sizeof(char *)); + + while ((*ap++ = (char *)ugetnode(l))); + checkmailpath(arr); + popheap(); + } + } else if (shout) { + if (st.st_size && st.st_atime <= st.st_mtime && + st.st_mtime >= lastmailcheck) { + if (!u) { + fprintf(shout, "You have new mail.\n"); + fflush(shout); + } else { + char *usav; + int uusav = underscoreused; + + usav = zalloc(underscoreused); + + if (usav) + memcpy(usav, zunderscore, underscoreused); + + setunderscore(*s); + + u = dupstring(u); + if (!parsestr(&u)) { + singsub(&u); + zputs(u, shout); + fputc('\n', shout); + fflush(shout); + } + if (usav) { + setunderscore(usav); + zfree(usav, uusav); + } + } + } + if (isset(MAILWARNING) && st.st_atime > st.st_mtime && + st.st_atime > lastmailcheck && st.st_size) { + fprintf(shout, "The mail in %s has been read.\n", unmeta(*s)); + fflush(shout); + } + } + *v = c; + s++; + } +} + +/* This prints the XTRACE prompt. */ + +/**/ +FILE *xtrerr = 0; + +/**/ +void +printprompt4(void) +{ + if (!xtrerr) + xtrerr = stderr; + if (prompt4) { + int l, t = opts[XTRACE]; + char *s = dupstring(prompt4); + + opts[XTRACE] = 0; + unmetafy(s, &l); + s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC), + 0, NULL, NULL, NULL), &l); + opts[XTRACE] = t; + + fprintf(xtrerr, "%s", s); + free(s); + } +} + +/**/ +mod_export void +freestr(void *a) +{ + zsfree(a); +} + +/**/ +mod_export void +gettyinfo(struct ttyinfo *ti) +{ + if (SHTTY != -1) { +#ifdef HAVE_TERMIOS_H +# ifdef HAVE_TCGETATTR + if (tcgetattr(SHTTY, &ti->tio) == -1) +# else + if (ioctl(SHTTY, TCGETS, &ti->tio) == -1) +# endif + zerr("bad tcgets: %e", errno); +#else +# ifdef HAVE_TERMIO_H + ioctl(SHTTY, TCGETA, &ti->tio); +# else + ioctl(SHTTY, TIOCGETP, &ti->sgttyb); + ioctl(SHTTY, TIOCLGET, &ti->lmodes); + ioctl(SHTTY, TIOCGETC, &ti->tchars); + ioctl(SHTTY, TIOCGLTC, &ti->ltchars); +# endif +#endif + } +} + +/**/ +mod_export void +settyinfo(struct ttyinfo *ti) +{ + if (SHTTY != -1) { +#ifdef HAVE_TERMIOS_H +# ifdef HAVE_TCGETATTR +# ifndef TCSADRAIN +# define TCSADRAIN 1 /* XXX Princeton's include files are screwed up */ +# endif + while (tcsetattr(SHTTY, TCSADRAIN, &ti->tio) == -1 && errno == EINTR) + ; +# else + while (ioctl(SHTTY, TCSETS, &ti->tio) == -1 && errno == EINTR) + ; +# endif + /* zerr("settyinfo: %e",errno);*/ +#else +# ifdef HAVE_TERMIO_H + ioctl(SHTTY, TCSETA, &ti->tio); +# else + ioctl(SHTTY, TIOCSETN, &ti->sgttyb); + ioctl(SHTTY, TIOCLSET, &ti->lmodes); + ioctl(SHTTY, TIOCSETC, &ti->tchars); + ioctl(SHTTY, TIOCSLTC, &ti->ltchars); +# endif +#endif + } +} + +/* the default tty state */ + +/**/ +mod_export struct ttyinfo shttyinfo; + +/* != 0 if we need to call resetvideo() */ + +/**/ +mod_export int resetneeded; + +#ifdef TIOCGWINSZ +/* window size changed */ + +/**/ +mod_export int winchanged; +#endif + +static int +adjustlines(int signalled) +{ + int oldlines = zterm_lines; + +#ifdef TIOCGWINSZ + if (signalled || zterm_lines <= 0) + zterm_lines = shttyinfo.winsize.ws_row; + else + shttyinfo.winsize.ws_row = zterm_lines; +#endif /* TIOCGWINSZ */ + if (zterm_lines <= 0) { + DPUTS(signalled && zterm_lines < 0, + "BUG: Impossible TIOCGWINSZ rows"); + zterm_lines = tclines > 0 ? tclines : 24; + } + + if (zterm_lines > 2) + termflags &= ~TERM_SHORT; + else + termflags |= TERM_SHORT; + + return (zterm_lines != oldlines); +} + +static int +adjustcolumns(int signalled) +{ + int oldcolumns = zterm_columns; + +#ifdef TIOCGWINSZ + if (signalled || zterm_columns <= 0) + zterm_columns = shttyinfo.winsize.ws_col; + else + shttyinfo.winsize.ws_col = zterm_columns; +#endif /* TIOCGWINSZ */ + if (zterm_columns <= 0) { + DPUTS(signalled && zterm_columns < 0, + "BUG: Impossible TIOCGWINSZ cols"); + zterm_columns = tccolumns > 0 ? tccolumns : 80; + } + + if (zterm_columns > 2) + termflags &= ~TERM_NARROW; + else + termflags |= TERM_NARROW; + + return (zterm_columns != oldcolumns); +} + +/* check the size of the window and adjust if necessary. * + * The value of from: * + * 0: called from update_job or setupvals * + * 1: called from the SIGWINCH handler * + * 2: called from the LINES parameter callback * + * 3: called from the COLUMNS parameter callback */ + +/**/ +void +adjustwinsize(int from) +{ + static int getwinsz = 1; +#ifdef TIOCGWINSZ + int ttyrows = shttyinfo.winsize.ws_row; + int ttycols = shttyinfo.winsize.ws_col; +#endif + int resetzle = 0; + + if (getwinsz || from == 1) { +#ifdef TIOCGWINSZ + if (SHTTY == -1) + return; + if (ioctl(SHTTY, TIOCGWINSZ, (char *)&shttyinfo.winsize) == 0) { + resetzle = (ttyrows != shttyinfo.winsize.ws_row || + ttycols != shttyinfo.winsize.ws_col); + if (from == 0 && resetzle && ttyrows && ttycols) + from = 1; /* Signal missed while a job owned the tty? */ + ttyrows = shttyinfo.winsize.ws_row; + ttycols = shttyinfo.winsize.ws_col; + } else { + /* Set to value from environment on failure */ + shttyinfo.winsize.ws_row = zterm_lines; + shttyinfo.winsize.ws_col = zterm_columns; + resetzle = (from == 1); + } +#else + resetzle = from == 1; +#endif /* TIOCGWINSZ */ + } /* else + return; */ + + switch (from) { + case 0: + case 1: + getwinsz = 0; + /* Calling setiparam() here calls this function recursively, but * + * because we've already called adjustlines() and adjustcolumns() * + * here, recursive calls are no-ops unless a signal intervenes. * + * The commented "else return;" above might be a safe shortcut, * + * but I'm concerned about what happens on race conditions; e.g., * + * suppose the user resizes his xterm during `eval $(resize)'? */ + if (adjustlines(from) && zgetenv("LINES")) + setiparam("LINES", zterm_lines); + if (adjustcolumns(from) && zgetenv("COLUMNS")) + setiparam("COLUMNS", zterm_columns); + getwinsz = 1; + break; + case 2: + resetzle = adjustlines(0); + break; + case 3: + resetzle = adjustcolumns(0); + break; + } + +#ifdef TIOCGWINSZ + if (interact && from >= 2 && + (shttyinfo.winsize.ws_row != ttyrows || + shttyinfo.winsize.ws_col != ttycols)) { + /* shttyinfo.winsize is already set up correctly */ + /* ioctl(SHTTY, TIOCSWINSZ, (char *)&shttyinfo.winsize); */ + } +#endif /* TIOCGWINSZ */ + + if (zleactive && resetzle) { +#ifdef TIOCGWINSZ + winchanged = +#endif /* TIOCGWINSZ */ + resetneeded = 1; + zleentry(ZLE_CMD_RESET_PROMPT); + zleentry(ZLE_CMD_REFRESH); + } +} + +/* + * Ensure the fdtable is large enough for fd, and that the + * maximum fd is set appropriately. + */ +static void +check_fd_table(int fd) +{ + if (fd <= max_zsh_fd) + return; + + if (fd >= fdtable_size) { + int old_size = fdtable_size; + while (fd >= fdtable_size) + fdtable = zrealloc(fdtable, + (fdtable_size *= 2)*sizeof(*fdtable)); + memset(fdtable + old_size, 0, + (fdtable_size - old_size) * sizeof(*fdtable)); + } + max_zsh_fd = fd; +} + +/* Move a fd to a place >= 10 and mark the new fd in fdtable. If the fd * + * is already >= 10, it is not moved. If it is invalid, -1 is returned. */ + +/**/ +mod_export int +movefd(int fd) +{ + if(fd != -1 && fd < 10) { +#ifdef F_DUPFD + int fe = fcntl(fd, F_DUPFD, 10); +#else + int fe = movefd(dup(fd)); +#endif + /* + * To close or not to close if fe is -1? + * If it is -1, we haven't moved the fd, so if we close + * it we lose it; but we're probably not going to be able + * to use it in situ anyway. So probably better to avoid a leak. + */ + zclose(fd); + fd = fe; + } + if(fd != -1) { + check_fd_table(fd); + fdtable[fd] = FDT_INTERNAL; + } + return fd; +} + +/* + * Move fd x to y. If x == -1, fd y is closed. + * Returns y for success, -1 for failure. + */ + +/**/ +mod_export int +redup(int x, int y) +{ + int ret = y; + + if(x < 0) + zclose(y); + else if (x != y) { + if (dup2(x, y) == -1) { + ret = -1; + } else { + check_fd_table(y); + fdtable[y] = fdtable[x]; + if (fdtable[y] == FDT_FLOCK || fdtable[y] == FDT_FLOCK_EXEC) + fdtable[y] = FDT_INTERNAL; + } + /* + * Closing any fd to the locked file releases the lock. + * This isn't expected to happen, it's here for completeness. + */ + if (fdtable[x] == FDT_FLOCK) + fdtable_flocks--; + zclose(x); + } + + return ret; +} + +/* + * Add an fd opened ithin a module. + * + * fdt is the type of the fd; see the FDT_ definitions in zsh.h. + * The most likely falures are: + * + * FDT_EXTERNAL: the fd can be used within the shell for normal I/O but + * it will not be closed automatically or by normal shell syntax. + * + * FDT_MODULE: as FDT_EXTERNAL, but it can only be closed by the module + * (which should included zclose() as part of the sequence), not by + * the standard shell syntax for closing file descriptors. + * + * FDT_INTERNAL: fd is treated like others created by the shell for + * internal use; it can be closed and will be closed by the shell if it + * exec's or performs an exec with a fork optimised out. + * + * Safe if fd is -1 to indicate failure. + */ +/**/ +mod_export void +addmodulefd(int fd, int fdt) +{ + if (fd >= 0) { + check_fd_table(fd); + fdtable[fd] = fdt; + } +} + +/**/ + +/* + * Indicate that an fd has a file lock; if cloexec is 1 it will be closed + * on exec. + * The fd should already be known to fdtable (e.g. by movefd). + * Note the fdtable code doesn't care what sort of lock + * is used; this simply prevents the main shell exiting prematurely + * when it holds a lock. + */ + +/**/ +mod_export void +addlockfd(int fd, int cloexec) +{ + if (cloexec) { + if (fdtable[fd] != FDT_FLOCK) + fdtable_flocks++; + fdtable[fd] = FDT_FLOCK; + } else { + fdtable[fd] = FDT_FLOCK_EXEC; + } +} + +/* Close the given fd, and clear it from fdtable. */ + +/**/ +mod_export int +zclose(int fd) +{ + if (fd >= 0) { + /* + * Careful: we allow closing of arbitrary fd's, beyond + * max_zsh_fd. In that case we don't try anything clever. + */ + if (fd <= max_zsh_fd) { + if (fdtable[fd] == FDT_FLOCK) + fdtable_flocks--; + fdtable[fd] = FDT_UNUSED; + while (max_zsh_fd > 0 && fdtable[max_zsh_fd] == FDT_UNUSED) + max_zsh_fd--; + if (fd == coprocin) + coprocin = -1; + if (fd == coprocout) + coprocout = -1; + } + return close(fd); + } + return -1; +} + +/* + * Close an fd returning 0 if used for locking; return -1 if it isn't. + */ + +/**/ +mod_export int +zcloselockfd(int fd) +{ + if (fd > max_zsh_fd) + return -1; + if (fdtable[fd] != FDT_FLOCK && fdtable[fd] != FDT_FLOCK_EXEC) + return -1; + zclose(fd); + return 0; +} + +#ifdef HAVE__MKTEMP +extern char *_mktemp(char *); +#endif + +/* 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. + * The string passed as "prefix" is expected to be metafied. */ + +/**/ +mod_export char * +gettempname(const char *prefix, int use_heap) +{ + char *ret, *suffix = prefix ? ".XXXXXX" : "XXXXXX"; + + queue_signals(); + if (!prefix && !(prefix = getsparam("TMPPREFIX"))) + prefix = DEFAULT_TMPPREFIX; + if (use_heap) + ret = dyncat(unmeta(prefix), suffix); + else + ret = bicat(unmeta(prefix), suffix); + +#ifdef HAVE__MKTEMP + /* Zsh uses mktemp() safely, so silence the warnings */ + ret = (char *) _mktemp(ret); +#else + ret = (char *) mktemp(ret); +#endif + unqueue_signals(); + + 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) +{ + char *fn; + int fd; + mode_t old_umask; +#if HAVE_MKSTEMP + char *suffix = prefix ? ".XXXXXX" : "XXXXXX"; + + queue_signals(); + old_umask = umask(0177); + if (!prefix && !(prefix = getsparam("TMPPREFIX"))) + prefix = DEFAULT_TMPPREFIX; + if (use_heap) + fn = dyncat(unmeta(prefix), suffix); + else + fn = bicat(unmeta(prefix), suffix); + + fd = mkstemp(fn); + if (fd < 0) { + if (!use_heap) + free(fn); + fn = NULL; + } +#else + int failures = 0; + + queue_signals(); + old_umask = umask(0177); + do { + if (!(fn = gettempname(prefix, use_heap))) { + fd = -1; + break; + } + if ((fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600)) >= 0) + break; + if (!use_heap) + free(fn); + fn = NULL; + } while (errno == EEXIST && ++failures < 16); +#endif + *tempname = fn; + + umask(old_umask); + unqueue_signals(); + return fd; +} + +/* Check if a string contains a token */ + +/**/ +mod_export int +has_token(const char *s) +{ + while(*s) + if(itok(*s++)) + return 1; + return 0; +} + +/* Delete a character in a string */ + +/**/ +mod_export void +chuck(char *str) +{ + while ((str[0] = str[1])) + str++; +} + +/**/ +mod_export int +tulower(int c) +{ + c &= 0xff; + return (isupper(c) ? tolower(c) : c); +} + +/**/ +mod_export int +tuupper(int c) +{ + c &= 0xff; + return (islower(c) ? toupper(c) : c); +} + +/* copy len chars from t into s, and null terminate */ + +/**/ +void +ztrncpy(char *s, char *t, int len) +{ + while (len--) + *s++ = *t++; + *s = '\0'; +} + +/* copy t into *s and update s */ + +/**/ +mod_export void +strucpy(char **s, char *t) +{ + char *u = *s; + + while ((*u++ = *t++)); + *s = u - 1; +} + +/**/ +mod_export void +struncpy(char **s, char *t, int n) +{ + char *u = *s; + + while (n-- && (*u = *t++)) + u++; + *s = u; + if (n > 0) /* just one null-byte will do, unlike strncpy(3) */ + *u = '\0'; +} + +/* Return the number of elements in an array of pointers. * + * It doesn't count the NULL pointer at the end. */ + +/**/ +mod_export int +arrlen(char **s) +{ + int count; + + for (count = 0; *s; s++, count++); + return count; +} + +/* Return TRUE iff arrlen(s) >= lower_bound, but more efficiently. */ + +/**/ +mod_export char +arrlen_ge(char **s, unsigned lower_bound) +{ + while (lower_bound--) + if (!*s++) + return 0 /* FALSE */; + + return 1 /* TRUE */; +} + +/* Return TRUE iff arrlen(s) > lower_bound, but more efficiently. */ + +/**/ +mod_export char +arrlen_gt(char **s, unsigned lower_bound) +{ + return arrlen_ge(s, 1+lower_bound); +} + +/* Return TRUE iff arrlen(s) <= upper_bound, but more efficiently. */ + +/**/ +mod_export char +arrlen_le(char **s, unsigned upper_bound) +{ + return arrlen_lt(s, 1+upper_bound); +} + +/* Return TRUE iff arrlen(s) < upper_bound, but more efficiently. */ + +/**/ +mod_export char +arrlen_lt(char **s, unsigned upper_bound) +{ + return !arrlen_ge(s, upper_bound); +} + +/* Skip over a balanced pair of parenthesis. */ + +/**/ +mod_export int +skipparens(char inpar, char outpar, char **s) +{ + int level; + + if (**s != inpar) + return -1; + + for (level = 1; *++*s && level;) + if (**s == inpar) + ++level; + else if (**s == outpar) + --level; + + return level; +} + +/**/ +mod_export zlong +zstrtol(const char *s, char **t, int base) +{ + return zstrtol_underscore(s, t, base, 0); +} + +/* Convert string to zlong (see zsh.h). This function (without the z) * + * is contained in the ANSI standard C library, but a lot of them seem * + * to be broken. */ + +/**/ +mod_export zlong +zstrtol_underscore(const char *s, char **t, int base, int underscore) +{ + const char *inp, *trunc = NULL; + zulong calc = 0, newcalc = 0; + int neg; + + while (inblank(*s)) + s++; + + if ((neg = IS_DASH(*s))) + s++; + else if (*s == '+') + s++; + + if (!base) { + if (*s != '0') + base = 10; + else if (*++s == 'x' || *s == 'X') + base = 16, s++; + else if (*s == 'b' || *s == 'B') + base = 2, s++; + else + base = 8; + } + inp = s; + if (base < 2 || base > 36) { + zerr("invalid base (must be 2 to 36 inclusive): %d", base); + return (zlong)0; + } else if (base <= 10) { + for (; (*s >= '0' && *s < ('0' + base)) || + (underscore && *s == '_'); s++) { + if (trunc || *s == '_') + continue; + newcalc = calc * base + *s - '0'; + if (newcalc < calc) + { + trunc = s; + continue; + } + calc = newcalc; + } + } else { + for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10)) + || (*s >= 'A' && *s < ('A' + base - 10)) + || (underscore && *s == '_'); s++) { + if (trunc || *s == '_') + continue; + newcalc = calc*base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9); + if (newcalc < calc) + { + trunc = s; + continue; + } + calc = newcalc; + } + } + + /* + * Special case: check for a number that was just too long for + * signed notation. + * Extra special case: the lowest negative number would trigger + * the first test, but is actually representable correctly. + * This is a 1 in the top bit, all others zero, so test for + * that explicitly. + */ + if (!trunc && (zlong)calc < 0 && + (!neg || calc & ~((zulong)1 << (8*sizeof(zulong)-1)))) + { + trunc = s - 1; + calc /= base; + } + + if (trunc) + zwarn("number truncated after %d digits: %s", (int)(trunc - inp), inp); + + if (t) + *t = (char *)s; + return neg ? -(zlong)calc : (zlong)calc; +} + +/* + * If s represents a complete unsigned integer (and nothing else) + * return 1 and set retval to the value. Otherwise return 0. + * + * Underscores are always allowed. + * + * Sensitive to OCTAL_ZEROES. + */ + +/**/ +mod_export int +zstrtoul_underscore(const char *s, zulong *retval) +{ + zulong calc = 0, newcalc = 0, base; + + if (*s == '+') + s++; + + if (*s != '0') + base = 10; + else if (*++s == 'x' || *s == 'X') + base = 16, s++; + else if (*s == 'b' || *s == 'B') + base = 2, s++; + else + base = isset(OCTALZEROES) ? 8 : 10; + if (base <= 10) { + for (; (*s >= '0' && *s < ('0' + base)) || + *s == '_'; s++) { + if (*s == '_') + continue; + newcalc = calc * base + *s - '0'; + if (newcalc < calc) + { + return 0; + } + calc = newcalc; + } + } else { + for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10)) + || (*s >= 'A' && *s < ('A' + base - 10)) + || *s == '_'; s++) { + if (*s == '_') + continue; + newcalc = calc*base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9); + if (newcalc < calc) + { + return 0; + } + calc = newcalc; + } + } + + if (*s) + return 0; + *retval = calc; + return 1; +} + +/**/ +mod_export int +setblock_fd(int turnonblocking, int fd, long *modep) +{ +#ifdef O_NDELAY +# ifdef O_NONBLOCK +# define NONBLOCK (O_NDELAY|O_NONBLOCK) +# else /* !O_NONBLOCK */ +# define NONBLOCK O_NDELAY +# endif /* !O_NONBLOCK */ +#else /* !O_NDELAY */ +# ifdef O_NONBLOCK +# define NONBLOCK O_NONBLOCK +# else /* !O_NONBLOCK */ +# define NONBLOCK 0 +# endif /* !O_NONBLOCK */ +#endif /* !O_NDELAY */ + +#if NONBLOCK + struct stat st; + + if (!fstat(fd, &st) && !S_ISREG(st.st_mode)) { + *modep = fcntl(fd, F_GETFL, 0); + if (*modep != -1) { + if (!turnonblocking) { + /* We want to know if blocking was off */ + if ((*modep & NONBLOCK) || + !fcntl(fd, F_SETFL, *modep | NONBLOCK)) + return 1; + } else if ((*modep & NONBLOCK) && + !fcntl(fd, F_SETFL, *modep & ~NONBLOCK)) { + /* Here we want to know if the state changed */ + return 1; + } + } + } else +#endif /* NONBLOCK */ + *modep = -1; + return 0; + +#undef NONBLOCK +} + +/**/ +int +setblock_stdin(void) +{ + long mode; + return setblock_fd(1, 0, &mode); +} + +/* + * Check for pending input on fd. If polltty is set, we may need to + * use termio to look for input. As a final resort, go to non-blocking + * input and try to read a character, which in this case will be + * returned in *readchar. + * + * Note that apart from setting (and restoring) non-blocking input, + * this function does not change the input mode. The calling function + * should have set cbreak mode if necessary. + * + * fd may be -1 to sleep until the timeout in microseconds. This is a + * fallback for old systems that don't have nanosleep(). Some very old + * systems might not have select: get with it, daddy-o. + */ + +/**/ +mod_export int +read_poll(int fd, int *readchar, int polltty, zlong microseconds) +{ + int ret = -1; + long mode = -1; + char c; +#ifdef HAVE_SELECT + fd_set foofd; + struct timeval expire_tv; +#else +#ifdef FIONREAD + int val; +#endif +#endif +#ifdef HAS_TIO + struct ttyinfo ti; +#endif + + if (fd < 0 || (polltty && !isatty(fd))) + polltty = 0; /* no tty to poll */ + +#if defined(HAS_TIO) && !defined(__CYGWIN__) + /* + * Under Solaris, at least, reading from the terminal in non-canonical + * mode requires that we use the VMIN mechanism to poll. Any attempt + * to check any other way, or to set the terminal to non-blocking mode + * and poll that way, fails; it will just for canonical mode input. + * We should probably use this mechanism if the user has set non-canonical + * mode, in which case testing here for isatty() and ~ICANON would be + * better than testing whether bin_read() set it, but for now we've got + * enough problems. + * + * Under Cygwin, you won't be surprised to here, this mechanism, + * although present, doesn't work, and we *have* to use ordinary + * non-blocking reads to find out if there is a character present + * in non-canonical mode. + * + * I am assuming Solaris is nearer the UNIX norm. This is not necessarily + * as plausible as it sounds, but it seems the right way to guess. + * pws 2000/06/26 + */ + if (polltty && fd >= 0) { + gettyinfo(&ti); + if ((polltty = ti.tio.c_cc[VMIN])) { + ti.tio.c_cc[VMIN] = 0; + /* termios timeout is 10ths of a second */ + ti.tio.c_cc[VTIME] = (int) (microseconds / (zlong)100000); + settyinfo(&ti); + } + } +#else + polltty = 0; +#endif +#ifdef HAVE_SELECT + expire_tv.tv_sec = (int) (microseconds / (zlong)1000000); + expire_tv.tv_usec = microseconds % (zlong)1000000; + FD_ZERO(&foofd); + if (fd > -1) { + FD_SET(fd, &foofd); + ret = select(fd+1, (SELECT_ARG_2_T) &foofd, NULL, NULL, &expire_tv); + } else + ret = select(0, NULL, NULL, NULL, &expire_tv); +#else + if (fd < 0) { + /* OK, can't do that. Just quietly sleep for a second. */ + sleep(1); + return 1; + } +#ifdef FIONREAD + if (ioctl(fd, FIONREAD, (char *) &val) == 0) + ret = (val > 0); +#endif +#endif + + if (fd >= 0 && ret < 0 && !errflag) { + /* + * Final attempt: set non-blocking read and try to read a character. + * Praise Bill, this works under Cygwin (nothing else seems to). + */ + if ((polltty || setblock_fd(0, fd, &mode)) && read(fd, &c, 1) > 0) { + *readchar = c; + ret = 1; + } + if (mode != -1) + fcntl(fd, F_SETFL, mode); + } +#ifdef HAS_TIO + if (polltty) { + ti.tio.c_cc[VMIN] = 1; + ti.tio.c_cc[VTIME] = 0; + settyinfo(&ti); + } +#endif + return (ret > 0); +} + +/* + * Sleep for the given number of microseconds --- must be within + * range of a long at the moment, but this is only used for + * limited internal purposes. + */ + +/**/ +int +zsleep(long us) +{ +#ifdef HAVE_NANOSLEEP + struct timespec sleeptime; + + sleeptime.tv_sec = (time_t)us / (time_t)1000000; + sleeptime.tv_nsec = (us % 1000000L) * 1000L; + for (;;) { + struct timespec rem; + int ret = nanosleep(&sleeptime, &rem); + + if (ret == 0) + return 1; + else if (errno != EINTR) + return 0; + sleeptime = rem; + } +#else + int dummy; + return read_poll(-1, &dummy, 0, us); +#endif +} + +/** + * Sleep for time (fairly) randomly up to max_us microseconds. + * Don't let the wallclock time extend beyond end_time. + * Return 1 if that seemed to work, else 0. + * + * For best results max_us should be a multiple of 2**16 or large + * enough that it doesn't matter. + */ + +/**/ +int +zsleep_random(long max_us, time_t end_time) +{ + long r; + time_t now = time(NULL); + + /* + * Randomish backoff. Doesn't need to be fundamentally + * unpredictable, just probably unlike the value another + * exiting shell is using. On some systems the bottom 16 + * bits aren't that random but the use here doesn't + * really care. + */ + r = (long)(rand() & 0xFFFF); + /* + * Turn this into a fraction of sleep_us. Again, this + * doesn't need to be particularly accurate and the base time + * is sufficient that we can do the division first and not + * worry about the range. + */ + r = (max_us >> 16) * r; + /* + * Don't sleep beyond timeout. + * Not that important as timeout is ridiculously long, but + * if there's an interface, interface to it... + */ + while (r && now + (time_t)(r / 1000000) > end_time) + r >>= 1; + if (r) /* pedantry */ + return zsleep(r); + return 0; +} + +/**/ +int +checkrmall(char *s) +{ + DIR *rmd; + int count = 0; + if (!shout) + return 1; + if (*s != '/') { + if (pwd[1]) + s = zhtricat(pwd, "/", s); + else + s = dyncat("/", s); + } + const int max_count = 100; + if ((rmd = opendir(unmeta(s)))) { + int ignoredots = !isset(GLOBDOTS); + char *fname; + + while ((fname = zreaddir(rmd, 1))) { + if (ignoredots && *fname == '.') + continue; + count++; + if (count > max_count) + break; + } + closedir(rmd); + } + if (count > max_count) + fprintf(shout, "zsh: sure you want to delete more than %d files in ", + max_count); + else if (count == 1) + fprintf(shout, "zsh: sure you want to delete the only file in "); + else if (count > 0) + fprintf(shout, "zsh: sure you want to delete all %d files in ", + count); + else { + /* We don't know how many files the glob will expand to; see 41707. */ + fprintf(shout, "zsh: sure you want to delete all the files in "); + } + nicezputs(s, shout); + if(isset(RMSTARWAIT)) { + fputs("? (waiting ten seconds)", shout); + fflush(shout); + zbeep(); + sleep(10); + fputc('\n', shout); + } + if (errflag) + return 0; + fputs(" [yn]? ", shout); + fflush(shout); + zbeep(); + return (getquery("ny", 1) == 'y'); +} + +/**/ +mod_export ssize_t +read_loop(int fd, char *buf, size_t len) +{ + ssize_t got = len; + + while (1) { + ssize_t ret = read(fd, buf, len); + if (ret == len) + break; + if (ret <= 0) { + if (ret < 0) { + if (errno == EINTR) + continue; + if (fd != SHTTY) + zwarn("read failed: %e", errno); + } + return ret; + } + buf += ret; + len -= ret; + } + + return got; +} + +/**/ +mod_export ssize_t +write_loop(int fd, const char *buf, size_t len) +{ + ssize_t wrote = len; + + while (1) { + ssize_t ret = write(fd, buf, len); + if (ret == len) + break; + if (ret < 0) { + if (errno == EINTR) + continue; + if (fd != SHTTY) + zwarn("write failed: %e", errno); + return -1; + } + buf += ret; + len -= ret; + } + + return wrote; +} + +static int +read1char(int echo) +{ + char c; + int q = queue_signal_level(); + + dont_queue_signals(); + while (read(SHTTY, &c, 1) != 1) { + if (errno != EINTR || errflag || retflag || breaks || contflag) { + restore_queue_signals(q); + return -1; + } + } + restore_queue_signals(q); + if (echo) + write_loop(SHTTY, &c, 1); + return STOUC(c); +} + +/**/ +mod_export int +noquery(int purge) +{ + int val = 0; + +#ifdef FIONREAD + char c; + + ioctl(SHTTY, FIONREAD, (char *)&val); + if (purge) { + for (; val; val--) { + if (read(SHTTY, &c, 1) != 1) { + /* Do nothing... */ + } + } + } +#endif + + return val; +} + +/**/ +int +getquery(char *valid_chars, int purge) +{ + int c, d, nl = 0; + int isem = !strcmp(term, "emacs"); + struct ttyinfo ti; + + attachtty(mypgrp); + + gettyinfo(&ti); +#ifdef HAS_TIO + ti.tio.c_lflag &= ~ECHO; + if (!isem) { + ti.tio.c_lflag &= ~ICANON; + ti.tio.c_cc[VMIN] = 1; + ti.tio.c_cc[VTIME] = 0; + } +#else + ti.sgttyb.sg_flags &= ~ECHO; + if (!isem) + ti.sgttyb.sg_flags |= CBREAK; +#endif + settyinfo(&ti); + + if (noquery(purge)) { + if (!isem) + settyinfo(&shttyinfo); + write_loop(SHTTY, "n\n", 2); + return 'n'; + } + + while ((c = read1char(0)) >= 0) { + if (c == 'Y') + c = 'y'; + else if (c == 'N') + c = 'n'; + if (!valid_chars) + break; + if (c == '\n') { + c = *valid_chars; + nl = 1; + break; + } + if (strchr(valid_chars, c)) { + nl = 1; + break; + } + zbeep(); + } + if (c >= 0) { + char buf = (char)c; + write_loop(SHTTY, &buf, 1); + } + if (nl) + write_loop(SHTTY, "\n", 1); + + if (isem) { + if (c != '\n') + while ((d = read1char(1)) >= 0 && d != '\n'); + } else { + if (c != '\n' && !valid_chars) { +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE) && c >= 0) { + /* + * No waiting for a valid character, and no draining; + * we should ensure we haven't stopped in the middle + * of a multibyte character. + */ + mbstate_t mbs; + char cc = (char)c; + memset(&mbs, 0, sizeof(mbs)); + for (;;) { + size_t ret = mbrlen(&cc, 1, &mbs); + + if (ret != MB_INCOMPLETE) + break; + c = read1char(1); + if (c < 0) + break; + cc = (char)c; + } + } +#endif + write_loop(SHTTY, "\n", 1); + } + } + settyinfo(&shttyinfo); + return c; +} + +static int d; +static char *guess, *best; +static Patprog spckpat, spnamepat; + +/**/ +static void +spscan(HashNode hn, UNUSED(int scanflags)) +{ + int nd; + + if (spckpat && pattry(spckpat, hn->nam)) + return; + + nd = spdist(hn->nam, guess, (int) strlen(guess) / 4 + 1); + if (nd <= d) { + best = hn->nam; + d = nd; + } +} + +/* spellcheck a word */ +/* fix s ; if hist is nonzero, fix the history list too */ + +/**/ +mod_export void +spckword(char **s, int hist, int cmd, int ask) +{ + char *t, *correct_ignore; + char ic = '\0'; + int preflen = 0; + int autocd = cmd && isset(AUTOCD) && strcmp(*s, ".") && strcmp(*s, ".."); + + if ((histdone & HISTFLAG_NOEXEC) || **s == '-' || **s == '%') + return; + if (!strcmp(*s, "in")) + return; + if (!(*s)[0] || !(*s)[1]) + return; + if (cmd) { + if (shfunctab->getnode(shfunctab, *s) || + builtintab->getnode(builtintab, *s) || + cmdnamtab->getnode(cmdnamtab, *s) || + aliastab->getnode(aliastab, *s) || + reswdtab->getnode(reswdtab, *s)) + return; + else if (isset(HASHLISTALL)) { + cmdnamtab->filltable(cmdnamtab); + if (cmdnamtab->getnode(cmdnamtab, *s)) + return; + } + } + t = *s; + if (*t == Tilde || *t == Equals || *t == String) + t++; + for (; *t; t++) + if (itok(*t)) + return; + best = NULL; + for (t = *s; *t; t++) + if (*t == '/') + break; + if (**s == Tilde && !*t) + return; + + if ((correct_ignore = getsparam("CORRECT_IGNORE")) != NULL) { + tokenize(correct_ignore = dupstring(correct_ignore)); + remnulargs(correct_ignore); + spckpat = patcompile(correct_ignore, 0, NULL); + } else + spckpat = NULL; + + if ((correct_ignore = getsparam("CORRECT_IGNORE_FILE")) != NULL) { + tokenize(correct_ignore = dupstring(correct_ignore)); + remnulargs(correct_ignore); + spnamepat = patcompile(correct_ignore, 0, NULL); + } else + spnamepat = NULL; + + if (**s == String && !*t) { + guess = *s + 1; + if (itype_end(guess, IIDENT, 1) == guess) + return; + ic = String; + d = 100; + scanhashtable(paramtab, 1, 0, 0, spscan, 0); + } else if (**s == Equals) { + if (*t) + return; + if (hashcmd(guess = *s + 1, pathchecked)) + return; + d = 100; + ic = Equals; + scanhashtable(aliastab, 1, 0, 0, spscan, 0); + scanhashtable(cmdnamtab, 1, 0, 0, spscan, 0); + } else { + guess = *s; + if (*guess == Tilde || *guess == String) { + int ne; + ic = *guess; + if (!*++t) + return; + guess = dupstring(guess); + ne = noerrs; + noerrs = 2; + singsub(&guess); + noerrs = ne; + if (!guess) + return; + preflen = strlen(guess) - strlen(t); + } + if (access(unmeta(guess), F_OK) == 0) + return; + best = spname(guess); + if (!*t && cmd) { + if (hashcmd(guess, pathchecked)) + return; + d = 100; + scanhashtable(reswdtab, 1, 0, 0, spscan, 0); + scanhashtable(aliastab, 1, 0, 0, spscan, 0); + scanhashtable(shfunctab, 1, 0, 0, spscan, 0); + scanhashtable(builtintab, 1, 0, 0, spscan, 0); + scanhashtable(cmdnamtab, 1, 0, 0, spscan, 0); + if (autocd) { + char **pp; + for (pp = cdpath; *pp; pp++) { + char bestcd[PATH_MAX + 1]; + int thisdist; + /* Less than d here, instead of less than or equal * + * as used in spscan(), so that an autocd is chosen * + * only when it is better than anything so far, and * + * so we prefer directories earlier in the cdpath. */ + if ((thisdist = mindist(*pp, *s, bestcd, 1)) < d) { + best = dupstring(bestcd); + d = thisdist; + } + } + } + } + } + if (errflag) + return; + if (best && (int)strlen(best) > 1 && strcmp(best, guess)) { + int x; + if (ic) { + char *u; + if (preflen) { + /* do not correct the result of an expansion */ + if (strncmp(guess, best, preflen)) + return; + /* replace the temporarily expanded prefix with the original */ + u = (char *) zhalloc(t - *s + strlen(best + preflen) + 1); + strncpy(u, *s, t - *s); + strcpy(u + (t - *s), best + preflen); + } else { + u = (char *) zhalloc(strlen(best) + 2); + *u = '\0'; + strcpy(u + 1, best); + } + best = u; + guess = *s; + *guess = *best = ztokens[ic - Pound]; + } + if (ask) { + if (noquery(0)) { + x = 'n'; + } else if (shout) { + char *pptbuf; + pptbuf = promptexpand(sprompt, 0, best, guess, NULL); + zputs(pptbuf, shout); + free(pptbuf); + fflush(shout); + zbeep(); + x = getquery("nyae", 0); + if (cmd && x == 'n') + pathchecked = path; + } else + x = 'n'; + } else + x = 'y'; + if (x == 'y') { + *s = dupstring(best); + if (hist) + hwrep(best); + } else if (x == 'a') { + histdone |= HISTFLAG_NOEXEC; + } else if (x == 'e') { + histdone |= HISTFLAG_NOEXEC | HISTFLAG_RECALL; + } + if (ic) + **s = ic; + } +} + +/* + * Helper for ztrftime. Called with a pointer to the length left + * in the buffer, and a new string length to decrement from that. + * Returns 0 if the new length fits, 1 otherwise. We assume a terminating + * NUL and return 1 if that doesn't fit. + */ + +static int +ztrftimebuf(int *bufsizeptr, int decr) +{ + if (*bufsizeptr <= decr) + return 1; + *bufsizeptr -= decr; + return 0; +} + +/* + * Like the system function, this returns the number of characters + * copied, not including the terminating NUL. This may be zero + * if the string didn't fit. + * + * As an extension, try to detect an error in strftime --- typically + * 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. + */ + +/**/ +mod_export int +ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long nsec) +{ + int hr12; +#ifdef HAVE_STRFTIME + int decr; + char *fmtstart; +#else + static char *astr[] = + {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + static char *estr[] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec"}; +#endif + char *origbuf = buf; + + + 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++; + } else + strip = 0; + if (idigit(*fmt)) { + /* Digit --- only useful with . */ + char *dstart = fmt; + char *dend = fmt+1; + while (idigit(*dend)) + dend++; + if (*dend == '.') { + fmt = dend; + digs = atoi(dstart); + } + } + /* + * Assume this format will take up at least two + * characters. Not always true, but if that matters + * we are so close to the edge it's not a big deal. + * Fix up some longer cases specially when we get to them. + */ + 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)) + return -1; + if (digs > 9) + digs = 9; + if (digs < 9) { + int trunc; + for (trunc = 8 - digs; trunc; trunc--) + nsec /= 10; + nsec = (nsec + 8) / 10; + } + sprintf(buf, "%0*ld", digs, nsec); + buf += digs; + break; + case '\0': + /* Guard against premature end of string */ + *buf++ = '%'; + fmt--; + break; + case 'f': + strip = 1; + /* FALLTHROUGH */ + case 'e': + if (tm->tm_mday > 9) + *buf++ = '0' + tm->tm_mday / 10; + else if (!strip) + *buf++ = ' '; + *buf++ = '0' + tm->tm_mday % 10; + break; + case 'K': + strip = 1; + /* FALLTHROUGH */ + case 'H': + case 'k': + if (tm->tm_hour > 9) + *buf++ = '0' + tm->tm_hour / 10; + else if (!strip) { + if (fmt[-1] == 'H') + *buf++ = '0'; + else + *buf++ = ' '; + } + *buf++ = '0' + tm->tm_hour % 10; + break; + case 'L': + strip = 1; + /* FALLTHROUGH */ + case 'l': + hr12 = tm->tm_hour % 12; + if (hr12 == 0) + hr12 = 12; + if (hr12 > 9) + *buf++ = '1'; + else if (!strip) + *buf++ = ' '; + + *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; + *buf++ = '0' + (tm->tm_mon + 1) % 10; + break; + case 'M': + if (tm->tm_min > 9 || !strip) + *buf++ = '0' + tm->tm_min / 10; + *buf++ = '0' + tm->tm_min % 10; + break; + case 'N': + if (ztrftimebuf(&bufsize, 9)) + return -1; + sprintf(buf, "%09ld", nsec); + buf += 9; + break; + case 'S': + if (tm->tm_sec > 9 || !strip) + *buf++ = '0' + tm->tm_sec / 10; + *buf++ = '0' + tm->tm_sec % 10; + break; + case 'y': + if (tm->tm_year > 9 || !strip) + *buf++ = '0' + (tm->tm_year / 10) % 10; + *buf++ = '0' + tm->tm_year % 10; + break; +#ifndef HAVE_STRFTIME + case 'Y': + { + int year, digits, testyear; + year = tm->tm_year + 1900; + digits = 1; + testyear = year; + while (testyear > 9) { + digits++; + testyear /= 10; + } + if (ztrftimebuf(&bufsize, digits)) + return -1; + sprintf(buf, "%d", year); + buf += digits; + break; + } + case 'a': + if (ztrftimebuf(&bufsize, strlen(astr[tm->tm_wday]) - 2)) + return -1; + strucpy(&buf, astr[tm->tm_wday]); + break; + case 'b': + if (ztrftimebuf(&bufsize, strlen(estr[tm->tm_mon]) - 2)) + return -1; + strucpy(&buf, estr[tm->tm_mon]); + break; + case 'p': + *buf++ = (tm->tm_hour > 11) ? 'p' : 'a'; + *buf++ = 'm'; + break; + default: + *buf++ = '%'; + if (fmt[-1] != '%') + *buf++ = fmt[-1]; +#else + case 'E': + case 'O': + case '^': + case '#': + case '_': + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + goto morefmt; +strftimehandling: + default: + /* + * Remember we've already allowed for two characters + * in the accounting in bufsize (but nowhere else). + */ + { + char origchar = fmt[-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)) + { + /* + * Some locales don't have strings for + * AM/PM, so empty output is valid. + */ + if (*buf || (origchar != 'p' && origchar != 'P')) { + if (*buf) { + buf[0] = '\0'; + return -1; + } + return 0; + } + } + decr = strlen(buf); + buf += decr; + bufsize -= decr - 2; + } +#endif + break; + } + } else { + if (ztrftimebuf(&bufsize, 1)) + return -1; + *buf++ = *fmt++; + } + } + *buf = '\0'; + return buf - origbuf; +} + +/**/ +mod_export char * +zjoin(char **arr, int delim, int heap) +{ + int len = 0; + char **s, *ret, *ptr; + + for (s = arr; *s; s++) + len += strlen(*s) + 1 + (imeta(delim) ? 1 : 0); + if (!len) + return heap? "" : ztrdup(""); + ptr = ret = (char *) (heap ? zhalloc(len) : zalloc(len)); + for (s = arr; *s; s++) { + strucpy(&ptr, *s); + if (imeta(delim)) { + *ptr++ = Meta; + *ptr++ = delim ^ 32; + } + else + *ptr++ = delim; + } + ptr[-1 - (imeta(delim) ? 1 : 0)] = '\0'; + return ret; +} + +/* Split a string containing a colon separated list * + * of items into an array of strings. */ + +/**/ +mod_export char ** +colonsplit(char *s, int uniq) +{ + int ct; + char *t, **ret, **ptr, **p; + + for (t = s, ct = 0; *t; t++) /* count number of colons */ + if (*t == ':') + ct++; + ptr = ret = (char **) zalloc(sizeof(char *) * (ct + 2)); + + t = s; + do { + s = t; + /* move t to point at next colon */ + for (; *t && *t != ':'; t++); + if (uniq) + for (p = ret; p < ptr; p++) + if ((int)strlen(*p) == t - s && ! strncmp(*p, s, t - s)) + goto cont; + *ptr = (char *) zalloc((t - s) + 1); + ztrncpy(*ptr++, s, t - s); + cont: ; + } + while (*t++); + *ptr = NULL; + return ret; +} + +/**/ +static int +skipwsep(char **s) +{ + char *t = *s; + int i = 0; + + /* + * Don't need to handle mutlibyte characters, they can't + * be IWSEP. Do need to check for metafication. + */ + while (*t && iwsep(*t == Meta ? t[1] ^ 32 : *t)) { + if (*t == Meta) + t++; + t++; + i++; + } + *s = t; + return i; +} + +/* + * haven't worked out what allownull does; it's passed down from + * sepsplit but all the cases it's used are either 0 or 1 without + * a comment. it seems to be something to do with the `nulstring' + * which i think is some kind of a metafication thing, so probably + * allownull's value is associated with whether we are using + * metafied strings. + * see findsep() below for handling of `quote' argument + */ + +/**/ +mod_export char ** +spacesplit(char *s, int allownull, int heap, int quote) +{ + char *t, **ret, **ptr; + int l = sizeof(*ret) * (wordcount(s, NULL, -!allownull) + 1); + char *(*dup)(const char *) = (heap ? dupstring : ztrdup); + + /* ### TODO: s/calloc/alloc/ */ + ptr = ret = (char **) (heap ? hcalloc(l) : zshcalloc(l)); + + if (quote) { + /* + * we will be stripping quoted separators by hacking string, + * so make sure it's hackable. + */ + s = dupstring(s); + } + + t = s; + skipwsep(&s); + MB_METACHARINIT(); + if (*s && itype_end(s, ISEP, 1) != s) + *ptr++ = dup(allownull ? "" : nulstring); + else if (!allownull && t != s) + *ptr++ = dup(""); + while (*s) { + char *iend = itype_end(s, ISEP, 1); + if (iend != s) { + s = iend; + skipwsep(&s); + } + else if (quote && *s == '\\') { + s++; + skipwsep(&s); + } + t = s; + (void)findsep(&s, NULL, quote); + if (s > t || allownull) { + *ptr = (char *) (heap ? zhalloc((s - t) + 1) : + zalloc((s - t) + 1)); + ztrncpy(*ptr++, t, s - t); + } else + *ptr++ = dup(nulstring); + t = s; + skipwsep(&s); + } + if (!allownull && t != s) + *ptr++ = dup(""); + *ptr = NULL; + return ret; +} + +/* + * Find a separator. Return 0 if already at separator, 1 if separator + * found later, else -1. (Historical note: used to return length into + * string but this is all that is necessary and is less ambiguous with + * multibyte characters around.) + * + * *s is the string we are looking along, which will be updated + * to the point we have got to. + * + * sep is a possibly multicharacter separator to look for. If NULL, + * use normal separator characters. If *sep is NULL, split on individual + * characters. + * + * quote is a flag that '\' should not be treated as a separator. + * in this case we need to be able to strip the backslash directly + * in the string, so the calling function must have sent us something + * modifiable. currently this only works for sep == NULL. also in + * in this case only, we need to turn \\ into \. + */ + +/**/ +static int +findsep(char **s, char *sep, int quote) +{ + /* + */ + int i, ilen; + char *t, *tt; + convchar_t c; + + MB_METACHARINIT(); + if (!sep) { + for (t = *s; *t; t += ilen) { + if (quote && *t == '\\') { + if (t[1] == '\\') { + chuck(t); + ilen = 1; + continue; + } else { + ilen = MB_METACHARLENCONV(t+1, &c); + if (WC_ZISTYPE(c, ISEP)) { + chuck(t); + /* then advance over new character, length ilen */ + } else { + /* treat *t (backslash) as normal byte */ + if (isep(*t)) + break; + ilen = 1; + } + } + } else { + ilen = MB_METACHARLENCONV(t, &c); + if (WC_ZISTYPE(c, ISEP)) + break; + } + } + i = (t > *s); + *s = t; + return i; + } + if (!sep[0]) { + /* + * NULL separator just means advance past first character, + * if any. + */ + if (**s) { + *s += MB_METACHARLEN(*s); + return 1; + } + return -1; + } + for (i = 0; **s; i++) { + /* + * The following works for multibyte characters by virtue of + * the fact that sep may be a string (and we don't care how + * it divides up, we need to match all of it). + */ + for (t = sep, tt = *s; *t && *tt && *t == *tt; t++, tt++); + if (!*t) + return (i > 0); + *s += MB_METACHARLEN(*s); + } + return -1; +} + +/**/ +char * +findword(char **s, char *sep) +{ + char *r, *t; + int sl; + + if (!**s) + return NULL; + + if (sep) { + sl = strlen(sep); + r = *s; + while (! findsep(s, sep, 0)) { + r = *s += sl; + } + return r; + } + MB_METACHARINIT(); + for (t = *s; *t; t += sl) { + convchar_t c; + sl = MB_METACHARLENCONV(t, &c); + if (!WC_ZISTYPE(c, ISEP)) + break; + } + *s = t; + (void)findsep(s, sep, 0); + return t; +} + +/**/ +int +wordcount(char *s, char *sep, int mul) +{ + int r, sl, c; + + if (sep) { + r = 1; + sl = strlen(sep); + for (; (c = findsep(&s, sep, 0)) >= 0; s += sl) + if ((c || mul) && (sl || *(s + sl))) + r++; + } else { + char *t = s; + + r = 0; + if (mul <= 0) + skipwsep(&s); + if ((*s && itype_end(s, ISEP, 1) != s) || + (mul < 0 && t != s)) + r++; + for (; *s; r++) { + char *ie = itype_end(s, ISEP, 1); + if (ie != s) { + s = ie; + if (mul <= 0) + skipwsep(&s); + } + (void)findsep(&s, NULL, 0); + t = s; + if (mul <= 0) + skipwsep(&s); + } + if (mul < 0 && t != s) + r++; + } + return r; +} + +/**/ +mod_export char * +sepjoin(char **s, char *sep, int heap) +{ + char *r, *p, **t; + int l, sl; + char sepbuf[2]; + + if (!*s) + return heap ? "" : ztrdup(""); + if (!sep) { + /* optimise common case that ifs[0] is space */ + if (ifs && *ifs != ' ') { + MB_METACHARINIT(); + sep = dupstrpfx(ifs, MB_METACHARLEN(ifs)); + } else { + p = sep = sepbuf; + *p++ = ' '; + *p = '\0'; + } + } + sl = strlen(sep); + for (t = s, l = 1 - sl; *t; l += strlen(*t) + sl, t++); + r = p = (char *) (heap ? zhalloc(l) : zalloc(l)); + t = s; + while (*t) { + strucpy(&p, *t); + if (*++t) + strucpy(&p, sep); + } + *p = '\0'; + return r; +} + +/**/ +char ** +sepsplit(char *s, char *sep, int allownull, int heap) +{ + int n, sl; + char *t, *tt, **r, **p; + + /* Null string? Treat as empty string. */ + if (s[0] == Nularg && !s[1]) + s++; + + if (!sep) + return spacesplit(s, allownull, heap, 0); + + sl = strlen(sep); + n = wordcount(s, sep, 1); + r = p = (char **) (heap ? zhalloc((n + 1) * sizeof(char *)) : + zalloc((n + 1) * sizeof(char *))); + + for (t = s; n--;) { + tt = t; + (void)findsep(&t, sep, 0); + *p = (char *) (heap ? zhalloc(t - tt + 1) : + zalloc(t - tt + 1)); + strncpy(*p, tt, t - tt); + (*p)[t - tt] = '\0'; + p++; + t += sl; + } + *p = NULL; + + return r; +} + +/* Get the definition of a shell function */ + +/**/ +mod_export Shfunc +getshfunc(char *nam) +{ + return (Shfunc) shfunctab->getnode(shfunctab, nam); +} + +/* + * Call the function func to substitute string orig by setting + * the parameter reply. + * Return the array from reply, or NULL if the function returned + * non-zero status. + * The returned value comes directly from the parameter and + * so should be used before there is any chance of that + * being changed or unset. + * If arg1 is not NULL, it is used as an initial argument to + * the function, with the original string as the second argument. + */ + +/**/ +char ** +subst_string_by_func(Shfunc func, char *arg1, char *orig) +{ + int osc = sfcontext, osm = stopmsg, old_incompfunc = incompfunc; + LinkList l = newlinklist(); + char **ret; + + addlinknode(l, func->node.nam); + if (arg1) + addlinknode(l, arg1); + addlinknode(l, orig); + sfcontext = SFC_SUBST; + incompfunc = 0; + + if (doshfunc(func, l, 1)) + ret = NULL; + else + ret = getaparam("reply"); + + sfcontext = osc; + stopmsg = osm; + incompfunc = old_incompfunc; + return ret; +} + +/** + * Front end to subst_string_by_func to use hook-like logic. + * name can refer to a function, and name + "_hook" can refer + * to an array containing a list of functions. The functions + * are tried in order until one returns success. + */ +/**/ +char ** +subst_string_by_hook(char *name, char *arg1, char *orig) +{ + Shfunc func; + char **ret = NULL; + + if ((func = getshfunc(name))) { + ret = subst_string_by_func(func, arg1, orig); + } + + if (!ret) { + char **arrptr; + int namlen = strlen(name); + VARARR(char, arrnam, namlen + HOOK_SUFFIX_LEN); + memcpy(arrnam, name, namlen); + memcpy(arrnam + namlen, HOOK_SUFFIX, HOOK_SUFFIX_LEN); + + if ((arrptr = getaparam(arrnam))) { + /* Guard against internal modification of the array */ + arrptr = arrdup(arrptr); + for (; *arrptr; arrptr++) { + if ((func = getshfunc(*arrptr))) { + ret = subst_string_by_func(func, arg1, orig); + if (ret) + break; + } + } + } + } + + return ret; +} + +/**/ +mod_export char ** +mkarray(char *s) +{ + char **t = (char **) zalloc((s) ? (2 * sizeof s) : (sizeof s)); + + if ((*t = s)) + t[1] = NULL; + return t; +} + +/**/ +mod_export char ** +hmkarray(char *s) +{ + char **t = (char **) zhalloc((s) ? (2 * sizeof s) : (sizeof s)); + + if ((*t = s)) + t[1] = NULL; + return t; +} + +/**/ +mod_export void +zbeep(void) +{ + char *vb; + queue_signals(); + if ((vb = getsparam_u("ZBEEP"))) { + int len; + vb = getkeystring(vb, &len, GETKEYS_BINDKEY, NULL); + write_loop(SHTTY, vb, len); + } else if (isset(BEEP)) + write_loop(SHTTY, "\07", 1); + unqueue_signals(); +} + +/**/ +mod_export void +freearray(char **s) +{ + char **t = s; + + DPUTS(!s, "freearray() with zero argument"); + + while (*s) + zsfree(*s++); + free(t); +} + +/**/ +int +equalsplit(char *s, char **t) +{ + for (; *s && *s != '='; s++); + if (*s == '=') { + *s++ = '\0'; + *t = s; + return 1; + } + return 0; +} + + +/* the ztypes table */ + +/**/ +mod_export short int typtab[256]; +static int typtab_flags = 0; + +/* initialize the ztypes table */ + +/**/ +void +inittyptab(void) +{ + int t0; + char *s; + + if (!(typtab_flags & ZTF_INIT)) { + typtab_flags = ZTF_INIT; + if (interact && isset(SHINSTDIN)) + typtab_flags |= ZTF_INTERACT; + } + + queue_signals(); + + memset(typtab, 0, sizeof(typtab)); + for (t0 = 0; t0 != 32; t0++) + typtab[t0] = typtab[t0 + 128] = ICNTRL; + typtab[127] = ICNTRL; + for (t0 = '0'; t0 <= '9'; t0++) + typtab[t0] = IDIGIT | IALNUM | IWORD | IIDENT | IUSER; + for (t0 = 'a'; t0 <= 'z'; t0++) + typtab[t0] = typtab[t0 - 'a' + 'A'] = IALPHA | IALNUM | IIDENT | IUSER | IWORD; +#ifndef MULTIBYTE_SUPPORT + /* + * This really doesn't seem to me the right thing to do when + * we have multibyte character support... it was a hack to assume + * eight bit characters `worked' for some values of work before + * we could test for them properly. I'm not 100% convinced + * having IIDENT here is a good idea at all, but this code + * should disappear into history... + */ + for (t0 = 0240; t0 != 0400; t0++) + typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD; +#endif + /* typtab['.'] |= IIDENT; */ /* Allow '.' in variable names - broken */ + typtab['_'] = IIDENT | IUSER; + typtab['-'] = typtab['.'] = typtab[STOUC(Dash)] = IUSER; + typtab[' '] |= IBLANK | INBLANK; + typtab['\t'] |= IBLANK | INBLANK; + typtab['\n'] |= INBLANK; + typtab['\0'] |= IMETA; + typtab[STOUC(Meta) ] |= IMETA; + typtab[STOUC(Marker)] |= IMETA; + for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(LAST_NORMAL_TOK); t0++) + typtab[t0] |= ITOK | IMETA; + for (t0 = (int)STOUC(Snull); t0 <= (int)STOUC(Nularg); t0++) + typtab[t0] |= ITOK | IMETA | INULL; + for (s = ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ? + DEFAULT_IFS_SH : DEFAULT_IFS; *s; s++) { + int c = STOUC(*s == Meta ? *++s ^ 32 : *s); +#ifdef MULTIBYTE_SUPPORT + if (!isascii(c)) { + /* see comment for wordchars below */ + continue; + } +#endif + if (inblank(c)) { + if (s[1] == c) + s++; + else + typtab[c] |= IWSEP; + } + typtab[c] |= ISEP; + } + for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++) { + int c = STOUC(*s == Meta ? *++s ^ 32 : *s); +#ifdef MULTIBYTE_SUPPORT + if (!isascii(c)) { + /* + * If we have support for multibyte characters, we don't + * handle non-ASCII characters here; instead, we turn + * wordchars into a wide character array. + * (We may actually have a single-byte 8-bit character set, + * but it works the same way.) + */ + continue; + } +#endif + typtab[c] |= IWORD; + } +#ifdef MULTIBYTE_SUPPORT + set_widearray(wordchars, &wordchars_wide); + set_widearray(ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ? + DEFAULT_IFS_SH : DEFAULT_IFS, &ifs_wide); +#endif + for (s = SPECCHARS; *s; s++) + typtab[STOUC(*s)] |= ISPECIAL; + if (typtab_flags & ZTF_SP_COMMA) + typtab[STOUC(',')] |= ISPECIAL; + if (isset(BANGHIST) && bangchar && (typtab_flags & ZTF_INTERACT)) { + typtab_flags |= ZTF_BANGCHAR; + typtab[bangchar] |= ISPECIAL; + } else + typtab_flags &= ~ZTF_BANGCHAR; + for (s = PATCHARS; *s; s++) + typtab[STOUC(*s)] |= IPATTERN; + + unqueue_signals(); +} + +/**/ +mod_export void +makecommaspecial(int yesno) +{ + if (yesno != 0) { + typtab_flags |= ZTF_SP_COMMA; + typtab[STOUC(',')] |= ISPECIAL; + } else { + typtab_flags &= ~ZTF_SP_COMMA; + typtab[STOUC(',')] &= ~ISPECIAL; + } +} + +/**/ +mod_export void +makebangspecial(int yesno) +{ + /* Name and call signature for congruence with makecommaspecial(), + * but in this case when yesno is nonzero we defer to the state + * saved by inittyptab(). + */ + if (yesno == 0) { + typtab[bangchar] &= ~ISPECIAL; + } else if (typtab_flags & ZTF_BANGCHAR) { + typtab[bangchar] |= ISPECIAL; + } +} + + +/**/ +#ifdef MULTIBYTE_SUPPORT +/* A wide-character version of the iblank() macro. */ +/**/ +mod_export int +wcsiblank(wint_t wc) +{ + if (iswspace(wc) && wc != L'\n') + return 1; + return 0; +} + +/* + * zistype macro extended to support wide characters. + * Works for IIDENT, IWORD, IALNUM, ISEP. + * We don't need this for IWSEP because that only applies to + * a fixed set of ASCII characters. + * Note here that use of multibyte mode is not tested: + * that's because for ZLE this is unconditional, + * not dependent on the option. The caller must decide. + */ + +/**/ +mod_export int +wcsitype(wchar_t c, int itype) +{ + int len; + mbstate_t mbs; + VARARR(char, outstr, MB_CUR_MAX); + + if (!isset(MULTIBYTE)) + return zistype(c, itype); + + /* + * Strategy: the shell requires that the multibyte representation + * be an extension of ASCII. So see if converting the character + * produces an ASCII character. If it does, use zistype on that. + * If it doesn't, use iswalnum on the original character. + * If that fails, resort to the appropriate wide character array. + */ + memset(&mbs, 0, sizeof(mbs)); + len = wcrtomb(outstr, c, &mbs); + + if (len == 0) { + /* NULL is special */ + return zistype(0, itype); + } else if (len == 1 && isascii(outstr[0])) { + return zistype(outstr[0], itype); + } else { + switch (itype) { + case IIDENT: + if (!isset(POSIXIDENTIFIERS)) + return 0; + return iswalnum(c); + + case IWORD: + if (iswalnum(c)) + return 1; + /* + * If we are handling combining characters, any punctuation + * characters with zero width needs to be considered part of + * a word. If we are not handling combining characters then + * logically they are still part of the word, even if they + * don't get displayed properly, so always do this. + */ + if (IS_COMBINING(c)) + return 1; + return !!wmemchr(wordchars_wide.chars, c, wordchars_wide.len); + + case ISEP: + return !!wmemchr(ifs_wide.chars, c, ifs_wide.len); + + default: + return iswalnum(c); + } + } +} + +/**/ +#endif + + +/* + * Find the end of a set of characters in the set specified by itype; + * one of IALNUM, IIDENT, IWORD or IUSER. For non-ASCII characters, we assume + * alphanumerics are part of the set, with the exception that + * identifiers are not treated that way if POSIXIDENTIFIERS is set. + * + * See notes above for identifiers. + * Returns the same pointer as passed if not on an identifier character. + * If "once" is set, just test the first character, i.e. (outptr != + * inptr) tests whether the first character is valid in an identifier. + * + * Currently this is only called with itype IIDENT, IUSER or ISEP. + */ + +/**/ +mod_export char * +itype_end(const char *ptr, int itype, int once) +{ +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE) && + (itype != IIDENT || !isset(POSIXIDENTIFIERS))) { + mb_charinit(); + while (*ptr) { + int len; + if (itok(*ptr)) { + /* Not untokenised yet --- can happen in raw command line */ + len = 1; + if (!zistype(*ptr,itype)) + break; + } else { + wint_t wc; + len = mb_metacharlenconv(ptr, &wc); + + if (!len) + break; + + if (wc == WEOF) { + /* invalid, treat as single character */ + int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr); + /* in this case non-ASCII characters can't match */ + if (chr > 127 || !zistype(chr,itype)) + break; + } else if (len == 1 && isascii(*ptr)) { + /* ASCII: can't be metafied, use standard test */ + if (!zistype(*ptr,itype)) + break; + } else { + /* + * Valid non-ASCII character. + */ + switch (itype) { + case IWORD: + if (!iswalnum(wc) && + !wmemchr(wordchars_wide.chars, wc, + wordchars_wide.len)) + return (char *)ptr; + break; + + case ISEP: + if (!wmemchr(ifs_wide.chars, wc, ifs_wide.len)) + return (char *)ptr; + break; + + default: + if (!iswalnum(wc)) + return (char *)ptr; + } + } + } + ptr += len; + + if (once) + break; + } + } else +#endif + for (;;) { + int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr); + if (!zistype(chr,itype)) + break; + ptr += (*ptr == Meta) ? 2 : 1; + + if (once) + break; + } + + /* + * Nasty. The first argument is const char * because we + * don't modify it here. However, we really want to pass + * back the same type as was passed down, to allow idioms like + * p = itype_end(p, IIDENT, 0); + * So returning a const char * isn't really the right thing to do. + * Without having two different functions the following seems + * to be the best we can do. + */ + return (char *)ptr; +} + +/**/ +mod_export char ** +arrdup(char **s) +{ + char **x, **y; + + y = x = (char **) zhalloc(sizeof(char *) * (arrlen(s) + 1)); + + while ((*x++ = dupstring(*s++))); + + return y; +} + +/* Duplicate at most max elements of the array s with heap memory */ + +/**/ +mod_export char ** +arrdup_max(char **s, unsigned max) +{ + char **x, **y, **send; + int len = 0; + + if (max) + len = arrlen(s); + + /* Limit has sense only if not equal to len */ + if (max > len) + max = len; + + y = x = (char **) zhalloc(sizeof(char *) * (max + 1)); + + send = s + max; + while (s < send) + *x++ = dupstring(*s++); + *x = NULL; + + return y; +} + +/**/ +mod_export char ** +zarrdup(char **s) +{ + char **x, **y; + + y = x = (char **) zalloc(sizeof(char *) * (arrlen(s) + 1)); + + while ((*x++ = ztrdup(*s++))); + + return y; +} + +/**/ +#ifdef MULTIBYTE_SUPPORT +/**/ +mod_export wchar_t ** +wcs_zarrdup(wchar_t **s) +{ + wchar_t **x, **y; + + y = x = (wchar_t **) zalloc(sizeof(wchar_t *) * (arrlen((char **)s) + 1)); + + while ((*x++ = wcs_ztrdup(*s++))); + + return y; +} +/**/ +#endif /* MULTIBYTE_SUPPORT */ + +/**/ +static char * +spname(char *oldname) +{ + char *p, spnameguess[PATH_MAX + 1], spnamebest[PATH_MAX + 1]; + static char newname[PATH_MAX + 1]; + char *new = newname, *old = oldname; + int bestdist = 0, thisdist, thresh, maxthresh = 0; + + /* This loop corrects each directory component of the path, stopping * + * when any correction distance would exceed the distance threshold. * + * NULL is returned only if the first component cannot be corrected; * + * otherwise a copy of oldname with a corrected prefix is returned. * + * Rationale for this, if there ever was any, has been forgotten. */ + for (;;) { + while (*old == '/') { + if (new >= newname + sizeof(newname) - 1) + return NULL; + *new++ = *old++; + } + *new = '\0'; + if (*old == '\0') + return newname; + p = spnameguess; + for (; *old != '/' && *old != '\0'; old++) + if (p < spnameguess + PATH_MAX) + *p++ = *old; + *p = '\0'; + /* Every component is allowed a single distance 2 correction or two * + * distance 1 corrections. Longer ones get additional corrections. */ + thresh = (int)(p - spnameguess) / 4 + 1; + if (thresh < 3) + thresh = 3; + else if (thresh > 100) + thresh = 100; + thisdist = mindist(newname, spnameguess, spnamebest, *old == '/'); + if (thisdist >= thresh) { + /* The next test is always true, except for the first path * + * component. We could initialize bestdist to some large * + * constant instead, and then compare to that constant here, * + * because an invariant is that we've never exceeded the * + * threshold for any component so far; but I think that looks * + * odd to the human reader, and we may make use of the total * + * distance for all corrections at some point in the future. */ + if (bestdist < maxthresh) { + struncpy(&new, spnameguess, sizeof(newname) - (new - newname)); + struncpy(&new, old, sizeof(newname) - (new - newname)); + return (new >= newname + sizeof(newname) -1) ? NULL : newname; + } else + return NULL; + } else { + maxthresh = bestdist + thresh; + bestdist += thisdist; + } + for (p = spnamebest; (*new = *p++);) { + if (new >= newname + sizeof(newname) - 1) + return NULL; + new++; + } + } +} + +/**/ +static int +mindist(char *dir, char *mindistguess, char *mindistbest, int wantdir) +{ + int mindistd, nd; + DIR *dd; + char *fn; + char *buf; + struct stat st; + size_t dirlen; + + if (dir[0] == '\0') + dir = "."; + mindistd = 100; + + if (!(buf = zalloc((dirlen = strlen(dir)) + strlen(mindistguess) + 2))) + return 0; + sprintf(buf, "%s/%s", dir, mindistguess); + + if (stat(unmeta(buf), &st) == 0 && (!wantdir || S_ISDIR(st.st_mode))) { + strcpy(mindistbest, mindistguess); + free(buf); + return 0; + } + + if ((dd = opendir(unmeta(dir)))) { + while ((fn = zreaddir(dd, 0))) { + if (spnamepat && pattry(spnamepat, fn)) + continue; + nd = spdist(fn, mindistguess, + (int)strlen(mindistguess) / 4 + 1); + if (nd <= mindistd) { + if (wantdir) { + if (!(buf = zrealloc(buf, dirlen + strlen(fn) + 2))) + continue; + sprintf(buf, "%s/%s", dir, fn); + if (stat(unmeta(buf), &st) != 0 || !S_ISDIR(st.st_mode)) + continue; + } + strcpy(mindistbest, fn); + mindistd = nd; + if (mindistd == 0) + break; + } + } + closedir(dd); + } + free(buf); + return mindistd; +} + +/**/ +static int +spdist(char *s, char *t, int thresh) +{ + /* TODO: Correction for non-ASCII and multibyte-input keyboards. */ + char *p, *q; + const char qwertykeymap[] = + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\ +\t1234567890-=\t\ +\tqwertyuiop[]\t\ +\tasdfghjkl;'\n\t\ +\tzxcvbnm,./\t\t\t\ +\n\n\n\n\n\n\n\n\n\n\n\n\n\n\ +\t!@#$%^&*()_+\t\ +\tQWERTYUIOP{}\t\ +\tASDFGHJKL:\"\n\t\ +\tZXCVBNM<>?\n\n\t\ +\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; + const char dvorakkeymap[] = + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\ +\t1234567890[]\t\ +\t',.pyfgcrl/=\t\ +\taoeuidhtns-\n\t\ +\t;qjkxbmwvz\t\t\t\ +\n\n\n\n\n\n\n\n\n\n\n\n\n\n\ +\t!@#$%^&*(){}\t\ +\t\"<>PYFGCRL?+\t\ +\tAOEUIDHTNS_\n\t\ +\t:QJKXBMWVZ\n\n\t\ +\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; + const char *keymap; + if ( isset( DVORAK ) ) + keymap = dvorakkeymap; + else + keymap = qwertykeymap; + + if (!strcmp(s, t)) + return 0; + /* any number of upper/lower mistakes allowed (dist = 1) */ + for (p = s, q = t; *p && tulower(*p) == tulower(*q); p++, q++); + if (!*p && !*q) + return 1; + if (!thresh) + return 200; + for (p = s, q = t; *p && *q; p++, q++) + if (*p == *q) + continue; /* don't consider "aa" transposed, ash */ + else if (p[1] == q[0] && q[1] == p[0]) /* transpositions */ + return spdist(p + 2, q + 2, thresh - 1) + 1; + else if (p[1] == q[0]) /* missing letter */ + return spdist(p + 1, q + 0, thresh - 1) + 2; + else if (p[0] == q[1]) /* missing letter */ + return spdist(p + 0, q + 1, thresh - 1) + 2; + else if (*p != *q) + break; + if ((!*p && strlen(q) == 1) || (!*q && strlen(p) == 1)) + return 2; + for (p = s, q = t; *p && *q; p++, q++) + if (p[0] != q[0] && p[1] == q[1]) { + int t0; + char *z; + + /* mistyped letter */ + + if (!(z = strchr(keymap, p[0])) || *z == '\n' || *z == '\t') + return spdist(p + 1, q + 1, thresh - 1) + 1; + t0 = z - keymap; + if (*q == keymap[t0 - 15] || *q == keymap[t0 - 14] || + *q == keymap[t0 - 13] || + *q == keymap[t0 - 1] || *q == keymap[t0 + 1] || + *q == keymap[t0 + 13] || *q == keymap[t0 + 14] || + *q == keymap[t0 + 15]) + return spdist(p + 1, q + 1, thresh - 1) + 2; + return 200; + } else if (*p != *q) + break; + return 200; +} + +/* set cbreak mode, or the equivalent */ + +/**/ +void +setcbreak(void) +{ + struct ttyinfo ti; + + ti = shttyinfo; +#ifdef HAS_TIO + ti.tio.c_lflag &= ~ICANON; + ti.tio.c_cc[VMIN] = 1; + ti.tio.c_cc[VTIME] = 0; +#else + ti.sgttyb.sg_flags |= CBREAK; +#endif + settyinfo(&ti); +} + +/* give the tty to some process */ + +/**/ +mod_export void +attachtty(pid_t pgrp) +{ + static int ep = 0; + + if (jobbing && interact) { +#ifdef HAVE_TCSETPGRP + if (SHTTY != -1 && tcsetpgrp(SHTTY, pgrp) == -1 && !ep) +#else +# if ardent + if (SHTTY != -1 && setpgrp() == -1 && !ep) +# else + int arg = pgrp; + + if (SHTTY != -1 && ioctl(SHTTY, TIOCSPGRP, &arg) == -1 && !ep) +# endif +#endif + { + if (pgrp != mypgrp && kill(-pgrp, 0) == -1) + attachtty(mypgrp); + else { + if (errno != ENOTTY) + { + zwarn("can't set tty pgrp: %e", errno); + fflush(stderr); + } + opts[MONITOR] = 0; + ep = 1; + } + } + } +} + +/* get the process group associated with the tty */ + +/**/ +pid_t +gettygrp(void) +{ + pid_t arg; + + if (SHTTY == -1) + return -1; + +#ifdef HAVE_TCSETPGRP + arg = tcgetpgrp(SHTTY); +#else + ioctl(SHTTY, TIOCGPGRP, &arg); +#endif + + return arg; +} + + +/* Escape tokens and null characters. Buf is the string which should be * + * escaped. len is the length of the string. If len is -1, buf should be * + * null terminated. If len is non-negative and the third parameter is not * + * META_DUP, buf should point to an at least len+1 long memory area. The * + * return value points to the quoted string. If the given string does not * + * contain any special character which should be quoted and the third * + * parameter is not META_(HEAP|)DUP, buf is returned unchanged (a * + * terminating null character is appended to buf if necessary). Otherwise * + * the third `heap' argument determines the method used to allocate space * + * for the result. It can have the following values: * + * META_REALLOC: use zrealloc on buf * + * META_HREALLOC: use hrealloc on buf * + * META_USEHEAP: get memory from the heap. This leaves buf unchanged. * + * META_NOALLOC: buf points to a memory area which is long enough to hold * + * the quoted form, just quote it and return buf. * + * META_STATIC: store the quoted string in a static area. The original * + * string should be at most PATH_MAX long. * + * META_ALLOC: allocate memory for the new string with zalloc(). * + * META_DUP: leave buf unchanged and allocate space for the return * + * value even if buf does not contains special characters * + * META_HEAPDUP: same as META_DUP, but uses the heap */ + +/**/ +mod_export char * +metafy(char *buf, int len, int heap) +{ + int meta = 0; + char *t, *p, *e; + static char mbuf[PATH_MAX*2+1]; + + if (len == -1) { + for (e = buf, len = 0; *e; len++) + if (imeta(*e++)) + meta++; + } else + for (e = buf; e < buf + len;) + if (imeta(*e++)) + meta++; + + if (meta || heap == META_DUP || heap == META_HEAPDUP) { + switch (heap) { + case META_REALLOC: + buf = zrealloc(buf, len + meta + 1); + break; + case META_HREALLOC: + buf = hrealloc(buf, len, len + meta + 1); + break; + case META_ALLOC: + case META_DUP: + buf = memcpy(zalloc(len + meta + 1), buf, len); + break; + case META_USEHEAP: + case META_HEAPDUP: + buf = memcpy(zhalloc(len + meta + 1), buf, len); + break; + case META_STATIC: +#ifdef DEBUG + if (len > PATH_MAX) { + fprintf(stderr, "BUG: len = %d > PATH_MAX in metafy\n", len); + fflush(stderr); + } +#endif + buf = memcpy(mbuf, buf, len); + break; +#ifdef DEBUG + case META_NOALLOC: + break; + default: + fprintf(stderr, "BUG: metafy called with invalid heap value\n"); + fflush(stderr); + break; +#endif + } + p = buf + len; + e = t = buf + len + meta; + while (meta) { + if (imeta(*--t = *--p)) { + *t-- ^= 32; + *t = Meta; + meta--; + } + } + } + *e = '\0'; + return buf; +} + + +/* + * Duplicate a string, metafying it as we go. + * + * Typically, this is used only for strings imported from outside + * zsh, as strings internally are either already metafied or passed + * around with an associated length. + */ +/**/ +mod_export char * +ztrdup_metafy(const char *s) +{ + /* To mimic ztrdup() behaviour */ + if (!s) + return NULL; + /* + * metafy() does lots of different things, so the pointer + * isn't const. Using it with META_DUP should be safe. + */ + return metafy((char *)s, -1, META_DUP); +} + + +/* + * Take a null-terminated, metafied string in s into a literal + * representation by converting in place. The length is in *len + * len is non-NULL; if len is NULL, you don't know the length of + * the final string, but if it's to be supplied to some system + * routine that always uses NULL termination, such as a filename + * interpreter, that doesn't matter. Note the NULL termination + * is always copied for purposes of that kind. + */ + +/**/ +mod_export char * +unmetafy(char *s, int *len) +{ + char *p, *t; + + for (p = s; *p && *p != Meta; p++); + for (t = p; (*t = *p++);) + if (*t++ == Meta && *p) + t[-1] = *p++ ^ 32; + if (len) + *len = t - s; + return s; +} + +/* Return the character length of a metafied substring, given the * + * unmetafied substring length. */ + +/**/ +mod_export int +metalen(const char *s, int len) +{ + int mlen = len; + + while (len--) { + if (*s++ == Meta) { + mlen++; + s++; + } + } + return mlen; +} + +/* + * This function converts a zsh internal string to a form which can be + * passed to a system call as a filename. The result is stored in a + * single static area, sized to fit. If there is no Meta character + * the original string is returned. + */ + +/**/ +mod_export char * +unmeta(const char *file_name) +{ + static char *fn; + static int sz; + char *p; + const char *t; + int newsz, meta; + + if (!file_name) + return NULL; + + meta = 0; + for (t = file_name; *t; t++) { + if (*t == Meta) + meta = 1; + } + if (!meta) { + /* + * don't need allocation... free if it's long, see below + */ + if (sz > 4 * PATH_MAX) { + zfree(fn, sz); + fn = NULL; + sz = 0; + } + return (char *) file_name; + } + + newsz = (t - file_name) + 1; + /* + * Optimisation: don't resize if we don't have to. + * We need a new allocation if + * - nothing was allocated before + * - the new string is larger than the old one + * - the old string was larger than an arbitrary limit but the + * new string isn't so that we free up significant space by resizing. + */ + if (!fn || newsz > sz || (sz > 4 * PATH_MAX && newsz <= 4 * PATH_MAX)) + { + if (fn) + zfree(fn, sz); + sz = newsz; + fn = (char *)zalloc(sz); + if (!fn) { + sz = 0; + /* + * will quite likely crash in the caller anyway... + */ + return NULL; + } + } + + for (t = file_name, p = fn; *t; p++) + if ((*p = *t++) == Meta && *t) + *p = *t++ ^ 32; + *p = '\0'; + return fn; +} + +/* + * Unmetafy just one character and store the number of bytes it occupied. + */ +/**/ +mod_export convchar_t +unmeta_one(const char *in, int *sz) +{ + convchar_t wc; + int newsz; +#ifdef MULTIBYTE_SUPPORT + mbstate_t wstate; +#endif + + if (!sz) + sz = &newsz; + *sz = 0; + + if (!in || !*in) + return 0; + +#ifdef MULTIBYTE_SUPPORT + memset(&wstate, 0, sizeof(wstate)); + *sz = mb_metacharlenconv_r(in, &wc, &wstate); +#else + if (in[0] == Meta) { + *sz = 2; + wc = STOUC(in[1] ^ 32); + } else { + *sz = 1; + wc = STOUC(in[0]); + } +#endif + return wc; +} + +/* + * Unmetafy and compare two strings, comparing unsigned character values. + * "a\0" sorts after "a". + * + * Currently this is only used in hash table sorting, where the + * keys are names of hash nodes and where we don't use strcoll(); + * it's not clear if that's right but it does guarantee the ordering + * of shell structures on output. + * + * As we don't use strcoll(), it seems overkill to convert multibyte + * characters to wide characters for comparison every time. In the case + * of UTF-8, Unicode ordering is preserved when sorted raw, and for + * other character sets we rely on an extension of ASCII so the result, + * while it may not be correct, is at least rational. + */ + +/**/ +int +ztrcmp(char const *s1, char const *s2) +{ + int c1, c2; + + while(*s1 && *s1 == *s2) { + s1++; + s2++; + } + + if(!(c1 = *s1)) + c1 = -1; + else if(c1 == STOUC(Meta)) + c1 = *++s1 ^ 32; + if(!(c2 = *s2)) + c2 = -1; + else if(c2 == STOUC(Meta)) + c2 = *++s2 ^ 32; + + if(c1 == c2) + return 0; + else if(c1 < c2) + return -1; + else + return 1; +} + +/* Return the unmetafied length of a metafied string. */ + +/**/ +mod_export int +ztrlen(char const *s) +{ + int l; + + for (l = 0; *s; l++) { + if (*s++ == Meta) { +#ifdef DEBUG + if (! *s) { + fprintf(stderr, "BUG: unexpected end of string in ztrlen()\n"); + 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++; + } + } + return l; +} + +#endif /* MULTIBYTE_SUPPORT */ + +/* Subtract two pointers in a metafied string. */ + +/**/ +mod_export int +ztrsub(char const *t, char const *s) +{ + int l = t - s; + + while (s != t) { + if (*s++ == Meta) { +#ifdef DEBUG + if (! *s || s == t) + fprintf(stderr, "BUG: substring ends in the middle of a metachar in ztrsub()\n"); + else +#endif + s++; + l--; + } + } + return l; +} + +/* + * Wrapper for readdir(). + * + * If ignoredots is true, skip the "." and ".." entries. + * + * When __APPLE__ is defined, recode dirent names from UTF-8-MAC to UTF-8. + * + * Return the dirent's name, metafied. + */ + +/**/ +mod_export char * +zreaddir(DIR *dir, int ignoredots) +{ + struct dirent *de; +#if defined(HAVE_ICONV) && defined(__APPLE__) + static iconv_t conv_ds = (iconv_t)0; + static char *conv_name = 0; + char *conv_name_ptr, *orig_name_ptr; + size_t conv_name_len, orig_name_len; +#endif + + do { + de = readdir(dir); + if(!de) + return NULL; + } while(ignoredots && de->d_name[0] == '.' && + (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2]))); + +#if defined(HAVE_ICONV) && defined(__APPLE__) + if (!conv_ds) + conv_ds = iconv_open("UTF-8", "UTF-8-MAC"); + if (conv_ds != (iconv_t)(-1)) { + /* Force initial state in case re-using conv_ds */ + (void) iconv(conv_ds, 0, &orig_name_len, 0, &conv_name_len); + + orig_name_ptr = de->d_name; + orig_name_len = strlen(de->d_name); + conv_name = zrealloc(conv_name, orig_name_len+1); + conv_name_ptr = conv_name; + conv_name_len = orig_name_len; + if (iconv(conv_ds, + &orig_name_ptr, &orig_name_len, + &conv_name_ptr, &conv_name_len) != (size_t)(-1) && + orig_name_len == 0) { + /* Completely converted, metafy and return */ + *conv_name_ptr = '\0'; + return metafy(conv_name, -1, META_STATIC); + } + /* Error, or conversion incomplete, keep the original name */ + } +#endif + + return metafy(de->d_name, -1, META_STATIC); +} + +/* Unmetafy and output a string. Tokens are skipped. */ + +/**/ +mod_export int +zputs(char const *s, FILE *stream) +{ + int c; + + while (*s) { + if (*s == Meta) + c = *++s ^ 32; + else if(itok(*s)) { + s++; + continue; + } else + c = *s; + s++; + if (fputc(c, stream) < 0) + return EOF; + } + return 0; +} + +#ifndef MULTIBYTE_SUPPORT +/* Create a visibly-represented duplicate of a string. */ + +/**/ +mod_export char * +nicedup(char const *s, int heap) +{ + int c, len = strlen(s) * 5 + 1; + VARARR(char, buf, len); + char *p = buf, *n; + + while ((c = *s++)) { + if (itok(c)) { + if (c <= Comma) + c = ztokens[c - Pound]; + else + continue; + } + if (c == Meta) + c = *s++ ^ 32; + /* The result here is metafied */ + n = nicechar(c); + while(*n) + *p++ = *n++; + } + *p = '\0'; + return heap ? dupstring(buf) : ztrdup(buf); +} +#endif + +/**/ +mod_export char * +nicedupstring(char const *s) +{ + return nicedup(s, 1); +} + + +#ifndef MULTIBYTE_SUPPORT +/* Unmetafy and output a string, displaying special characters readably. */ + +/**/ +mod_export int +nicezputs(char const *s, FILE *stream) +{ + int c; + + while ((c = *s++)) { + if (itok(c)) { + if (c <= Comma) + c = ztokens[c - Pound]; + else + continue; + } + if (c == Meta) + c = *s++ ^ 32; + if(zputs(nicechar(c), stream) < 0) + return EOF; + } + return 0; +} + + +/* Return the length of the visible representation of a metafied string. */ + +/**/ +mod_export size_t +niceztrlen(char const *s) +{ + size_t l = 0; + int c; + + while ((c = *s++)) { + if (itok(c)) { + if (c <= Comma) + c = ztokens[c - Pound]; + else + continue; + } + if (c == Meta) + c = *s++ ^ 32; + l += strlen(nicechar(c)); + } + return l; +} +#endif + + +/**/ +#ifdef MULTIBYTE_SUPPORT +/* + * Version of both nicezputs() and niceztrlen() for use with multibyte + * characters. Input is a metafied string; output is the screen width of + * the string. + * + * If the FILE * is not NULL, output to that, too. + * + * If outstrp is not NULL, set *outstrp to a zalloc'd version of + * the output (still metafied). + * + * If flags contains NICEFLAG_HEAP, use the heap for *outstrp, else + * zalloc. + * If flags contsins NICEFLAG_QUOTE, the output is going to be within + * $'...', so quote "'" and "\" with a backslash. + */ + +/**/ +mod_export size_t +mb_niceformat(const char *s, FILE *stream, char **outstrp, int flags) +{ + size_t l = 0, newl; + int umlen, outalloc, outleft, eol = 0; + wchar_t c; + char *ums, *ptr, *fmt, *outstr, *outptr; + mbstate_t mbs; + + if (outstrp) { + outleft = outalloc = 5 * strlen(s); + outptr = outstr = zalloc(outalloc); + } else { + outleft = outalloc = 0; + outptr = outstr = NULL; + } + + ums = ztrdup(s); + /* + * is this necessary at this point? niceztrlen does this + * but it's used in lots of places. however, one day this may + * be, too. + */ + untokenize(ums); + ptr = unmetafy(ums, ¨en); + + memset(&mbs, 0, sizeof mbs); + while (umlen > 0) { + size_t cnt = eol ? MB_INVALID : mbrtowc(&c, ptr, umlen, &mbs); + + switch (cnt) { + case MB_INCOMPLETE: + eol = 1; + /* FALL THROUGH */ + case MB_INVALID: + /* The byte didn't convert, so output it as a \M-... sequence. */ + fmt = nicechar_sel(*ptr, flags & NICEFLAG_QUOTE); + newl = strlen(fmt); + cnt = 1; + /* Get mbs out of its undefined state. */ + memset(&mbs, 0, sizeof mbs); + break; + case 0: + /* Careful: converting '\0' returns 0, but a '\0' is a + * real character for us, so we should consume 1 byte. */ + cnt = 1; + /* FALL THROUGH */ + default: + if (c == L'\'' && (flags & NICEFLAG_QUOTE)) { + fmt = "\\'"; + newl = 2; + } + else if (c == L'\\' && (flags & NICEFLAG_QUOTE)) { + fmt = "\\\\"; + newl = 2; + } + else + fmt = wcs_nicechar_sel(c, &newl, NULL, flags & NICEFLAG_QUOTE); + break; + } + + umlen -= cnt; + ptr += cnt; + l += newl; + + if (stream) + zputs(fmt, stream); + if (outstr) { + /* Append to output string */ + int outlen = strlen(fmt); + if (outlen >= outleft) { + /* Reallocate to twice the length */ + int outoffset = outptr - outstr; + + outleft += outalloc; + outalloc *= 2; + outstr = zrealloc(outstr, outalloc); + outptr = outstr + outoffset; + } + memcpy(outptr, fmt, outlen); + /* Update start position */ + outptr += outlen; + /* Update available bytes */ + outleft -= outlen; + } + } + + free(ums); + if (outstrp) { + *outptr = '\0'; + /* Use more efficient storage for returned string */ + if (flags & NICEFLAG_NODUP) + *outstrp = outstr; + else { + *outstrp = (flags & NICEFLAG_HEAP) ? dupstring(outstr) : + ztrdup(outstr); + free(outstr); + } + } + + return l; +} + +/* + * Return 1 if mb_niceformat() would reformat this string, else 0. + */ + +/**/ +mod_export int +is_mb_niceformat(const char *s) +{ + int umlen, eol = 0, ret = 0; + wchar_t c; + char *ums, *ptr; + mbstate_t mbs; + + ums = ztrdup(s); + untokenize(ums); + ptr = unmetafy(ums, ¨en); + + memset(&mbs, 0, sizeof mbs); + while (umlen > 0) { + size_t cnt = eol ? MB_INVALID : mbrtowc(&c, ptr, umlen, &mbs); + + switch (cnt) { + case MB_INCOMPLETE: + eol = 1; + /* FALL THROUGH */ + case MB_INVALID: + /* The byte didn't convert, so output it as a \M-... sequence. */ + if (is_nicechar(*ptr)) { + ret = 1; + break; + } + cnt = 1; + /* Get mbs out of its undefined state. */ + memset(&mbs, 0, sizeof mbs); + break; + case 0: + /* Careful: converting '\0' returns 0, but a '\0' is a + * real character for us, so we should consume 1 byte. */ + cnt = 1; + /* FALL THROUGH */ + default: + if (is_wcs_nicechar(c)) + ret = 1; + break; + } + + if (ret) + break; + + umlen -= cnt; + ptr += cnt; + } + + free(ums); + + return ret; +} + +/* ztrdup multibyte string with nice formatting */ + +/**/ +mod_export char * +nicedup(const char *s, int heap) +{ + char *retstr; + + (void)mb_niceformat(s, NULL, &retstr, heap ? NICEFLAG_HEAP : 0); + + return retstr; +} + + +/* + * The guts of mb_metacharlenconv(). This version assumes we are + * processing a true multibyte character string without tokens, and + * takes the shift state as an argument. + */ + +/**/ +mod_export int +mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp) +{ + size_t ret = MB_INVALID; + char inchar; + const char *ptr; + wchar_t wc; + + if (STOUC(*s) <= 0x7f) { + if (wcp) + *wcp = (wint_t)*s; + return 1; + } + + for (ptr = s; *ptr; ) { + if (*ptr == Meta) { + inchar = *++ptr ^ 32; + DPUTS(!*ptr, + "BUG: unexpected end of string in mb_metacharlen()\n"); + } else if (imeta(*ptr)) { + /* + * As this is metafied input, this is a token --- this + * can't be a part of the string. It might be + * something on the end of an unbracketed parameter + * reference, for example. + */ + break; + } else + inchar = *ptr; + ptr++; + 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 + (*s == Meta); /* Treat as single byte character */ + } else + return 0; /* Probably shouldn't happen */ +} + +/* + * Length of metafied string s which contains the next multibyte + * character; single (possibly metafied) character if string is not null + * but character is not valid (e.g. possibly incomplete at end of string). + * Returned value is guaranteed not to reach beyond the end of the + * string (assuming correct metafication). + * + * If wcp is not NULL, the converted wide character is stored there. + * If no conversion could be done WEOF is used. + */ + +/**/ +mod_export int +mb_metacharlenconv(const char *s, wint_t *wcp) +{ + if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) { + /* treat as single byte, possibly metafied */ + if (wcp) + *wcp = (wint_t)(*s == Meta ? s[1] ^ 32 : *s); + return 1 + (*s == Meta); + } + /* + * We have to handle tokens here, since we may be looking + * through a tokenized input. Obviously this isn't + * a valid multibyte character, so just return WEOF + * and let the caller handle it as a single character. + * + * TODO: I've a sneaking suspicion we could do more here + * to prevent the caller always needing to handle invalid + * characters specially, but sometimes it may need to know. + */ + if (itok(*s)) { + if (wcp) + *wcp = WEOF; + return 1; + } + + return mb_metacharlenconv_r(s, wcp, &mb_shiftstate); +} + +/* + * Total number of multibyte characters in metafied string s. + * Same answer as iterating mb_metacharlen() and counting calls + * until end of string. + * + * 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_metastrlenend(char *ptr, int width, char *eptr) +{ + char inchar, *laststart; + size_t ret; + wchar_t wc; + int num, num_in_char, complete; + + if (!isset(MULTIBYTE) || MB_CUR_MAX == 1) + return eptr ? (int)(eptr - ptr) : ztrlen(ptr); + + laststart = ptr; + ret = MB_INVALID; + num = num_in_char = 0; + complete = 1; + + memset(&mb_shiftstate, 0, sizeof(mb_shiftstate)); + while (*ptr && !(eptr && ptr >= eptr)) { + if (*ptr == Meta) + inchar = *++ptr ^ 32; + else + inchar = *ptr; + ptr++; + + if (complete && STOUC(inchar) <= STOUC(0x7f)) { + /* + * We rely on 7-bit US-ASCII as a subset, so skip + * multibyte handling if we have such a character. + */ + num++; + laststart = ptr; + num_in_char = 0; + continue; + } + + ret = mbrtowc(&wc, &inchar, 1, &mb_shiftstate); + + if (ret == MB_INCOMPLETE) { + /* + * "num_in_char" is only used for incomplete characters. + * The assumption is that we will output all trailing octets + * that form part of an incomplete character as a single + * character (of single width) if we don't get a complete + * character. This is purely pragmatic --- I'm not aware + * of a standard way of dealing with incomplete characters. + * + * If we do get a complete character, num_in_char + * becomes irrelevant and is set to zero + * + * This is in contrast to "num" which counts the characters + * or widths in complete characters. The two are summed, + * so we don't count characters twice. + */ + num_in_char++; + complete = 0; + } else { + if (ret == MB_INVALID) { + /* Reset, treat as single character */ + memset(&mb_shiftstate, 0, sizeof(mb_shiftstate)); + ptr = laststart + (*laststart == Meta) + 1; + num++; + } else if (width) { + /* + * Returns -1 if not a printable character. We + * turn this into 0. + */ + int wcw = WCWIDTH(wc); + if (wcw > 0) { + if (width == 1) + num += wcw; + else + num++; + } + } else + num++; + laststart = ptr; + num_in_char = 0; + complete = 1; + } + } + + /* If incomplete, treat remainder as trailing single character */ + return num + (num_in_char ? 1 : 0); +} + +/* + * 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; + + if (slen && STOUC(*s) <= 0x7f) { + if (wcp) + *wcp = (wint_t)*s; + return 1; + } + + 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) || STOUC(*s) <= 0x7f) { + if (wcp) + *wcp = (wint_t)*s; + return 1; + } + + return mb_charlenconv_r(s, slen, wcp, &mb_shiftstate); +} + +/**/ +#else + +/* Simple replacement for mb_metacharlenconv */ + +/**/ +mod_export int +metacharlenconv(const char *x, int *c) +{ + /* + * Here we don't use STOUC() on the chars since they + * may be compared against other chars and this will fail + * if chars are signed and the high bit is set. + */ + if (*x == Meta) { + if (c) + *c = x[1] ^ 32; + return 2; + } + if (c) + *c = (char)*x; + 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 */ + +/**/ +mod_export int +hasspecial(char const *s) +{ + for (; *s; s++) { + if (ispecial(*s == Meta ? *++s ^ 32 : *s)) + return 1; + } + return 0; +} + + +static char * +addunprintable(char *v, const char *u, const char *uend) +{ + for (; u < uend; u++) { + /* + * Just do this byte by byte; there's no great + * advantage in being clever with multibyte + * characters if we don't think they're printable. + */ + int c; + if (*u == Meta) + c = STOUC(*++u ^ 32); + else + c = STOUC(*u); + switch (c) { + case '\0': + *v++ = '\\'; + *v++ = '0'; + if ('0' <= u[1] && u[1] <= '7') { + *v++ = '0'; + *v++ = '0'; + } + break; + + case '\007': *v++ = '\\'; *v++ = 'a'; break; + case '\b': *v++ = '\\'; *v++ = 'b'; break; + case '\f': *v++ = '\\'; *v++ = 'f'; break; + case '\n': *v++ = '\\'; *v++ = 'n'; break; + case '\r': *v++ = '\\'; *v++ = 'r'; break; + case '\t': *v++ = '\\'; *v++ = 't'; break; + case '\v': *v++ = '\\'; *v++ = 'v'; break; + + default: + *v++ = '\\'; + *v++ = '0' + ((c >> 6) & 7); + *v++ = '0' + ((c >> 3) & 7); + *v++ = '0' + (c & 7); + break; + } + } + + return v; +} + +/* + * Quote the string s and return the result as a string from the heap. + * + * The last argument is a QT_ value defined in zsh.h other than QT_NONE. + * + * Most quote styles other than backslash assume the quotes are to + * be added outside quotestring(). QT_SINGLE_OPTIONAL is different: + * the single quotes are only added where necessary, so the + * whole expression is handled here. + * + * The string may be metafied and contain tokens. + */ + +/**/ +mod_export char * +quotestring(const char *s, int instring) +{ + const char *u; + char *v; + int alloclen; + char *buf; + int shownull = 0; + /* + * quotesub is used with QT_SINGLE_OPTIONAL. + * quotesub = 0: mechanism not active + * quotesub = 1: mechanism pending, no "'" yet; + * needs adding at quotestart. + * quotesub = 2: mechanism active, added opening "'"; need + * closing "'". + */ + int quotesub = 0, slen; + char *quotestart; + convchar_t cc; + const char *uend; + + slen = strlen(s); + switch (instring) + { + case QT_BACKSLASH_SHOWNULL: + shownull = 1; + instring = QT_BACKSLASH; + /*FALLTHROUGH*/ + case QT_BACKSLASH: + /* + * With QT_BACKSLASH we may need to use $'\300' stuff. + * Keep memory usage within limits by allocating temporary + * storage and using heap for correct size at end. + */ + alloclen = slen * 7 + 1; + break; + + case QT_BACKSLASH_PATTERN: + alloclen = slen * 2 + 1; + break; + + case QT_SINGLE_OPTIONAL: + /* + * Here, we may need to add single quotes. + * Always show empty strings. + */ + alloclen = slen * 4 + 3; + quotesub = shownull = 1; + break; + + default: + alloclen = slen * 4 + 1; + break; + } + if (!*s && shownull) + alloclen += 2; /* for '' */ + + quotestart = v = buf = zshcalloc(alloclen); + + DPUTS(instring < QT_BACKSLASH || instring == QT_BACKTICK || + instring > QT_BACKSLASH_PATTERN, + "BUG: bad quote type in quotestring"); + u = s; + if (instring == QT_DOLLARS) { + /* + * The only way to get Nularg here is when + * it is placeholding for the empty string? + */ + if (inull(*u)) + u++; + /* + * As we test for printability here we need to be able + * to look for multibyte characters. + */ + MB_METACHARINIT(); + while (*u) { + uend = u + MB_METACHARLENCONV(u, &cc); + + if ( +#ifdef MULTIBYTE_SUPPORT + cc != WEOF && +#endif + WC_ISPRINT(cc)) { + switch (cc) { + case ZWC('\\'): + case ZWC('\''): + *v++ = '\\'; + break; + + default: + if (isset(BANGHIST) && cc == (wchar_t)bangchar) + *v++ = '\\'; + break; + } + while (u < uend) + *v++ = *u++; + } else { + /* Not printable */ + v = addunprintable(v, u, uend); + u = uend; + } + } + } else if (instring == QT_BACKSLASH_PATTERN) { + while (*u) { + if (ipattern(*u)) + *v++ = '\\'; + *v++ = *u++; + } + } else { + if (shownull) { + /* We can't show an empty string with just backslash quoting. */ + if (!*u) { + *v++ = '\''; + *v++ = '\''; + } + } + /* + * Here there are syntactic special characters, so + * we start by going through bytewise. + */ + while (*u) { + int dobackslash = 0; + if (*u == Tick || *u == Qtick) { + char c = *u++; + + *v++ = c; + while (*u && *u != c) + *v++ = *u++; + *v++ = c; + if (*u) + u++; + continue; + } else if ((*u == Qstring || *u == '$') && u[1] == '\'' && + instring == QT_DOUBLE) { + /* + * We don't need to quote $'...' inside a double-quoted + * string. This is largely cosmetic; it looks neater + * if we don't but it doesn't do any harm since the + * \ is stripped. + */ + *v++ = *u++; + } else if ((*u == String || *u == Qstring) && + (u[1] == Inpar || u[1] == Inbrack || u[1] == Inbrace)) { + char c = (u[1] == Inpar ? Outpar : (u[1] == Inbrace ? + Outbrace : Outbrack)); + char beg = *u; + int level = 0; + + *v++ = *u++; + *v++ = *u++; + while (*u && (*u != c || level)) { + if (*u == beg) + level++; + else if (*u == c) + level--; + *v++ = *u++; + } + if (*u) + *v++ = *u++; + continue; + } + else if (ispecial(*u) && + ((*u != '=' && *u != '~') || + u == s || + (isset(MAGICEQUALSUBST) && + (u[-1] == '=' || u[-1] == ':')) || + (*u == '~' && isset(EXTENDEDGLOB))) && + (instring == QT_BACKSLASH || + instring == QT_SINGLE_OPTIONAL || + (isset(BANGHIST) && *u == (char)bangchar && + instring != QT_SINGLE) || + (instring == QT_DOUBLE && + (*u == '$' || *u == '`' || *u == '\"' || *u == '\\')) || + (instring == QT_SINGLE && *u == '\''))) { + if (instring == QT_SINGLE_OPTIONAL) { + if (quotesub == 1) { + /* + * We haven't yet had to quote at the start. + */ + if (*u == '\'') { + /* + * We don't need to. + */ + *v++ = '\\'; + } else { + /* + * It's now time to add quotes. + */ + if (v > quotestart) + { + char *addq; + + for (addq = v; addq > quotestart; addq--) + *addq = addq[-1]; + } + *quotestart = '\''; + v++; + quotesub = 2; + } + *v++ = *u++; + /* + * Next place to start quotes is here. + */ + quotestart = v; + } else if (*u == '\'') { + if (unset(RCQUOTES)) { + *v++ = '\''; + *v++ = '\\'; + *v++ = '\''; + /* Don't restart quotes unless we need them */ + quotesub = 1; + quotestart = v; + } else { + /* simplest just to use '' always */ + *v++ = '\''; + *v++ = '\''; + } + /* dealt with */ + u++; + } else { + /* else already quoting, just add */ + *v++ = *u++; + } + continue; + } else if (*u == '\n' || + (instring == QT_SINGLE && *u == '\'')) { + if (*u == '\n') { + *v++ = '$'; + *v++ = '\''; + *v++ = '\\'; + *v++ = 'n'; + *v++ = '\''; + } else if (unset(RCQUOTES)) { + *v++ = '\''; + if (*u == '\'') + *v++ = '\\'; + *v++ = *u; + *v++ = '\''; + } else + *v++ = '\'', *v++ = '\''; + u++; + continue; + } else { + /* + * We'll need a backslash, but don't add it + * yet since if the character isn't printable + * we'll have to upgrade it to $'...'. + */ + dobackslash = 1; + } + } + + if (itok(*u) || instring != QT_BACKSLASH) { + /* Needs to be passed straight through. */ + if (dobackslash) + *v++ = '\\'; + 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; + } + + /* + * Now check if the output is unprintable in the + * current character set. + */ + uend = u + MB_METACHARLENCONV(u, &cc); + if ( +#ifdef MULTIBYTE_SUPPORT + cc != WEOF && +#endif + WC_ISPRINT(cc)) { + if (dobackslash) + *v++ = '\\'; + while (u < uend) { + if (*u == Meta) + *v++ = *u++; + *v++ = *u++; + } + } else { + /* Not printable */ + *v++ = '$'; + *v++ = '\''; + v = addunprintable(v, u, uend); + *v++ = '\''; + u = uend; + } + } + } + if (quotesub == 2) + *v++ = '\''; + *v = '\0'; + + v = dupstring(buf); + zfree(buf, alloclen); + return v; +} + +/* + * Unmetafy and output a string, quoted if it contains special + * characters. + * + * If stream is NULL, return the same output with any allocation on the + * heap. + */ + +/**/ +mod_export char * +quotedzputs(char const *s, FILE *stream) +{ + int inquote = 0, c; + char *outstr, *ptr; + + /* check for empty string */ + if(!*s) { + if (!stream) + return dupstring("''"); + fputs("''", stream); + return NULL; + } + +#ifdef MULTIBYTE_SUPPORT + if (is_mb_niceformat(s)) { + if (stream) { + fputs("$'", stream); + mb_niceformat(s, stream, NULL, NICEFLAG_QUOTE); + fputc('\'', stream); + return NULL; + } else { + char *substr; + mb_niceformat(s, NULL, &substr, NICEFLAG_QUOTE|NICEFLAG_NODUP); + outstr = (char *)zhalloc(4 + strlen(substr)); + sprintf(outstr, "$'%s'", substr); + free(substr); + return outstr; + } + } +#endif /* MULTIBYTE_SUPPORT */ + + if (!hasspecial(s)) { + if (stream) { + zputs(s, stream); + return NULL; + } else { + return dupstring(s); + } + } + + if (!stream) { + const char *cptr; + int l = strlen(s) + 2; + for (cptr = s; *cptr; cptr++) { + if (*cptr == Meta) + cptr++; + else if (*cptr == '\'') + l += isset(RCQUOTES) ? 1 : 3; + } + ptr = outstr = zhalloc(l + 1); + } else { + ptr = outstr = NULL; + } + if (isset(RCQUOTES)) { + /* use rc-style quotes-within-quotes for the whole string */ + if (stream) { + if (fputc('\'', stream) < 0) + return NULL; + } else + *ptr++ = '\''; + while(*s) { + if (*s == Dash) + c = '-'; + else if (*s == Meta) + c = *++s ^ 32; + else + c = *s; + s++; + if (c == '\'') { + if (stream) { + if (fputc('\'', stream) < 0) + return NULL; + } else + *ptr++ = '\''; + } else if (c == '\n' && isset(CSHJUNKIEQUOTES)) { + if (stream) { + if (fputc('\\', stream) < 0) + return NULL; + } else + *ptr++ = '\\'; + } + if (stream) { + if (fputc(c, stream) < 0) + return NULL; + } else { + if (imeta(c)) { + *ptr++ = Meta; + *ptr++ = c ^ 32; + } else + *ptr++ = c; + } + } + if (stream) { + if (fputc('\'', stream) < 0) + return NULL; + } else + *ptr++ = '\''; + } else { + /* use Bourne-style quoting, avoiding empty quoted strings */ + while (*s) { + if (*s == Dash) + c = '-'; + else if (*s == Meta) + c = *++s ^ 32; + else + c = *s; + s++; + if (c == '\'') { + if (inquote) { + if (stream) { + if (putc('\'', stream) < 0) + return NULL; + } else + *ptr++ = '\''; + inquote=0; + } + if (stream) { + if (fputs("\\'", stream) < 0) + return NULL; + } else { + *ptr++ = '\\'; + *ptr++ = '\''; + } + } else { + if (!inquote) { + if (stream) { + if (fputc('\'', stream) < 0) + return NULL; + } else + *ptr++ = '\''; + inquote=1; + } + if (c == '\n' && isset(CSHJUNKIEQUOTES)) { + if (stream) { + if (fputc('\\', stream) < 0) + return NULL; + } else + *ptr++ = '\\'; + } + if (stream) { + if (fputc(c, stream) < 0) + return NULL; + } else { + if (imeta(c)) { + *ptr++ = Meta; + *ptr++ = c ^ 32; + } else + *ptr++ = c; + } + } + } + if (inquote) { + if (stream) { + if (fputc('\'', stream) < 0) + return NULL; + } else + *ptr++ = '\''; + } + } + if (!stream) + *ptr++ = '\0'; + + return outstr; +} + +/* Double-quote a metafied string. */ + +/**/ +mod_export char * +dquotedztrdup(char const *s) +{ + int len = strlen(s) * 4 + 2; + char *buf = zalloc(len); + char *p = buf, *ret; + + if(isset(CSHJUNKIEQUOTES)) { + int inquote = 0; + + while(*s) { + int c = *s++; + + if (c == Meta) + c = *s++ ^ 32; + switch(c) { + case '"': + case '$': + case '`': + if(inquote) { + *p++ = '"'; + inquote = 0; + } + *p++ = '\\'; + *p++ = c; + break; + default: + if(!inquote) { + *p++ = '"'; + inquote = 1; + } + if(c == '\n') + *p++ = '\\'; + *p++ = c; + break; + } + } + if (inquote) + *p++ = '"'; + } else { + int pending = 0; + + *p++ = '"'; + while(*s) { + int c = *s++; + + if (c == Meta) + c = *s++ ^ 32; + switch(c) { + case '\\': + if(pending) + *p++ = '\\'; + *p++ = '\\'; + pending = 1; + break; + case '"': + case '$': + case '`': + if(pending) + *p++ = '\\'; + *p++ = '\\'; + /* FALL THROUGH */ + default: + *p++ = c; + pending = 0; + break; + } + } + if(pending) + *p++ = '\\'; + *p++ = '"'; + } + ret = metafy(buf, p - buf, META_DUP); + zfree(buf, len); + return ret; +} + +/* Unmetafy and output a string, double quoting it in its entirety. */ + +#if 0 /**/ +int +dquotedzputs(char const *s, FILE *stream) +{ + char *d = dquotedztrdup(s); + int ret = zputs(d, stream); + + zsfree(d); + return ret; +} +#endif + +# if defined(HAVE_NL_LANGINFO) && defined(CODESET) && !defined(__STDC_ISO_10646__) +/* Convert a character from UCS4 encoding to UTF-8 */ + +static size_t +ucs4toutf8(char *dest, unsigned int wval) +{ + size_t len; + + if (wval < 0x80) + len = 1; + else if (wval < 0x800) + len = 2; + else if (wval < 0x10000) + len = 3; + else if (wval < 0x200000) + len = 4; + else if (wval < 0x4000000) + len = 5; + else + len = 6; + + switch (len) { /* falls through except to the last case */ + case 6: dest[5] = (wval & 0x3f) | 0x80; wval >>= 6; + case 5: dest[4] = (wval & 0x3f) | 0x80; wval >>= 6; + case 4: dest[3] = (wval & 0x3f) | 0x80; wval >>= 6; + case 3: dest[2] = (wval & 0x3f) | 0x80; wval >>= 6; + case 2: dest[1] = (wval & 0x3f) | 0x80; wval >>= 6; + *dest = wval | ((0xfc << (6 - len)) & 0xfc); + break; + case 1: *dest = wval; + } + + return len; +} +#endif + + +/* + * The following only occurs once or twice in the code, but in different + * places depending how character set conversion is implemented. + */ +#define CHARSET_FAILED() \ + if (how & GETKEY_DOLLAR_QUOTE) { \ + while ((*tdest++ = *++s)) { \ + if (how & GETKEY_UPDATE_OFFSET) { \ + if (s - sstart > *misc) \ + (*misc)++; \ + } \ + if (*s == Snull) { \ + *len = (s - sstart) + 1; \ + *tdest = '\0'; \ + return buf; \ + } \ + } \ + *len = tdest - buf; \ + return buf; \ + } \ + *t = '\0'; \ + *len = t - buf; \ + return buf + +/* + * Decode a key string, turning it into the literal characters. + * The value returned is a newly allocated string from the heap. + * + * The length is returned in *len. This is usually the length of + * the final unmetafied string. The exception is the case of + * a complete GETKEY_DOLLAR_QUOTE conversion where *len is the + * length of the input string which has been used (up to and including + * the terminating single quote); as the final string is metafied and + * NULL-terminated its length is not required. If both GETKEY_DOLLAR_QUOTE + * and GETKEY_UPDATE_OFFSET are present in "how", the string is not + * expected to be terminated (this is used in completion to parse + * a partial $'...'-quoted string) and the length passed back is + * that of the converted string. Note in both cases that this is a length + * in bytes (i.e. the same as given by a raw pointer difference), not + * characters, which may occupy multiple bytes. + * + * how is a set of bits from the GETKEY_ values defined in zsh.h; + * not all combinations of bits are useful. Callers will typically + * use one of the GETKEYS_ values which define sets of bits. + * Note, for example that: + * - GETKEY_SINGLE_CHAR must not be combined with GETKEY_DOLLAR_QUOTE. + * - GETKEY_UPDATE_OFFSET is only allowed if GETKEY_DOLLAR_QUOTE is + * also present. + * + * *misc is used for various purposes: + * - If GETKEY_BACKSLASH_MINUS is set, it indicates the presence + * of \- in the input. + * - If GETKEY_BACKSLASH_C is set, it indicates the presence + * of \c in the input. + * - If GETKEY_UPDATE_OFFSET is set, it is set on input to some + * mystical completion offset and is updated to a new offset based + * on the converted characters. All Hail the Completion System + * [makes the mystic completion system runic sign in the air]. + * + * The return value is unmetafied unless GETKEY_DOLLAR_QUOTE is + * in use. + */ + +/**/ +mod_export char * +getkeystring(char *s, int *len, int how, int *misc) +{ + char *buf, tmp[1]; + char *t, *tdest = NULL, *u = NULL, *sstart = s, *tbuf = NULL; + char svchar = '\0'; + int meta = 0, control = 0, ignoring = 0; + int i; +#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__) + wint_t wval; + int count; +#else + unsigned int wval; +# if defined(HAVE_NL_LANGINFO) && defined(CODESET) +# if defined(HAVE_ICONV) + iconv_t cd; + char inbuf[4]; + size_t inbytes, outbytes; +# endif + size_t count; +# endif +#endif + + DPUTS((how & GETKEY_UPDATE_OFFSET) && + (how & ~(GETKEYS_DOLLARS_QUOTE|GETKEY_UPDATE_OFFSET)), + "BUG: offset updating in getkeystring only supported with $'."); + DPUTS((how & (GETKEY_DOLLAR_QUOTE|GETKEY_SINGLE_CHAR)) == + (GETKEY_DOLLAR_QUOTE|GETKEY_SINGLE_CHAR), + "BUG: incompatible options in getkeystring"); + + if (how & GETKEY_SINGLE_CHAR) + t = buf = tmp; + else { + /* Length including terminating NULL */ + int maxlen = 1; + /* + * We're not necessarily guaranteed the output string will + * be no longer than the input with \u and \U when output + * characters need to be metafied. As this is the only + * case where the string can get longer (?I think), + * include it in the allocation length here but don't + * bother taking account of other factors. + */ + for (t = s; *t; t++) { + if (*t == '\\') { + if (!t[1]) { + maxlen++; + break; + } + if (t[1] == 'u' || t[1] == 'U') + maxlen += MB_CUR_MAX * 2; + else + maxlen += 2; + /* skip the backslash and the following character */ + t++; + } else + maxlen++; + } + if (how & GETKEY_DOLLAR_QUOTE) { + /* + * We're going to unmetafy into a new string, but + * to get a proper metafied input we're going to metafy + * into an intermediate buffer. This is necessary if we have + * \u and \U's with multiple metafied bytes. We can't + * simply remetafy the entire string because there may + * be tokens (indeed, we know there are lexical nulls floating + * around), so we have to be aware character by character + * what we are converting. + * + * In this case, buf is the final buffer (as usual), + * but t points into a temporary buffer that just has + * to be long enough to hold the result of one escape + * code transformation. We count this is a full multibyte + * character (MB_CUR_MAX) with every character metafied + * (*2) plus a little bit of fuzz (for e.g. the odd backslash). + */ + buf = tdest = zhalloc(maxlen); + t = tbuf = zhalloc(MB_CUR_MAX * 3 + 1); + } else { + t = buf = zhalloc(maxlen); + } + } + for (; *s; s++) { + if (*s == '\\' && s[1]) { + int miscadded; + if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) { + (*misc)--; + miscadded = 1; + } else + miscadded = 0; + switch (*++s) { + case 'a': +#ifdef __STDC__ + *t++ = '\a'; +#else + *t++ = '\07'; +#endif + break; + case 'n': + *t++ = '\n'; + break; + case 'b': + *t++ = '\b'; + break; + case 't': + *t++ = '\t'; + break; + case 'v': + *t++ = '\v'; + break; + case 'f': + *t++ = '\f'; + break; + case 'r': + *t++ = '\r'; + break; + case 'E': + if (!(how & GETKEY_EMACS)) { + *t++ = '\\', s--; + if (miscadded) + (*misc)++; + continue; + } + /* FALL THROUGH */ + case 'e': + *t++ = '\033'; + break; + case 'M': + /* HERE: GETKEY_UPDATE_OFFSET */ + if (how & GETKEY_EMACS) { + if (s[1] == '-') + s++; + meta = 1 + control; /* preserve the order of ^ and meta */ + } else { + if (miscadded) + (*misc)++; + *t++ = '\\', s--; + } + continue; + case 'C': + /* HERE: GETKEY_UPDATE_OFFSET */ + if (how & GETKEY_EMACS) { + if (s[1] == '-') + s++; + control = 1; + } else { + if (miscadded) + (*misc)++; + *t++ = '\\', s--; + } + continue; + case Meta: + if (miscadded) + (*misc)++; + *t++ = '\\', s--; + break; + case '-': + if (how & GETKEY_BACKSLASH_MINUS) { + *misc = 1; + break; + } + goto def; + case 'c': + if (how & GETKEY_BACKSLASH_C) { + *misc = 1; + *t = '\0'; + *len = t - buf; + return buf; + } + goto def; + case 'U': + if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) + (*misc) -= 4; + /* FALLTHROUGH */ + case 'u': + if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) { + (*misc) -= 6; /* HERE don't really believe this */ + /* + * We've now adjusted the offset for all the input + * characters, so we need to add for each + * byte of output below. + */ + } + wval = 0; + for (i=(*s == 'u' ? 4 : 8); i>0; i--) { + if (*++s && idigit(*s)) + wval = wval * 16 + (*s - '0'); + else if (*s && ((*s >= 'a' && *s <= 'f') || + (*s >= 'A' && *s <= 'F'))) + wval = wval * 16 + (*s & 0x1f) + 9; + else { + s--; + break; + } + } + if (how & GETKEY_SINGLE_CHAR) { + *misc = wval; + return s+1; + } +#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__) + count = wctomb(t, (wchar_t)wval); + if (count == -1) { + zerr("character not in range"); + CHARSET_FAILED(); + } + if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) + (*misc) += count; + t += count; +# else +# if defined(HAVE_NL_LANGINFO) && defined(CODESET) + if (!strcmp(nl_langinfo(CODESET), "UTF-8")) { + count = ucs4toutf8(t, wval); + t += count; + if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) + (*misc) += count; + } else { +# ifdef HAVE_ICONV + ICONV_CONST char *inptr = inbuf; + const char *codesetstr = nl_langinfo(CODESET); + inbytes = 4; + outbytes = 6; + /* store value in big endian form */ + for (i=3;i>=0;i--) { + inbuf[i] = wval & 0xff; + wval >>= 8; + } + + /* + * If the code set isn't handled, we'd better + * assume it's US-ASCII rather than just failing + * hopelessly. Solaris has a weird habit of + * returning 646. This is handled by the + * native iconv(), but not by GNU iconv; what's + * more, some versions of the native iconv don't + * handle standard names like ASCII. + * + * This should only be a problem if there's a + * mismatch between the NLS and the iconv in use, + * which probably only means if libiconv is in use. + * We checked at configure time if our libraries + * pulled in _libiconv_version, which should be + * a good test. + * + * It shouldn't ever be NULL, but while we're + * being paranoid... + */ +#ifdef ICONV_FROM_LIBICONV + if (!codesetstr || !*codesetstr) + codesetstr = "US-ASCII"; +#endif + cd = iconv_open(codesetstr, "UCS-4BE"); +#ifdef ICONV_FROM_LIBICONV + if (cd == (iconv_t)-1 && !strcmp(codesetstr, "646")) { + codesetstr = "US-ASCII"; + cd = iconv_open(codesetstr, "UCS-4BE"); + } +#endif + if (cd == (iconv_t)-1) { + zerr("cannot do charset conversion (iconv failed)"); + CHARSET_FAILED(); + } + count = iconv(cd, &inptr, &inbytes, &t, &outbytes); + iconv_close(cd); + if (count == (size_t)-1) { + zerr("character not in range"); + CHARSET_FAILED(); + } + if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) + (*misc) += count; +# else + zerr("cannot do charset conversion (iconv not available)"); + CHARSET_FAILED(); +# endif + } +# else + zerr("cannot do charset conversion (NLS not supported)"); + CHARSET_FAILED(); +# endif +# endif + if (how & GETKEY_DOLLAR_QUOTE) { + char *t2; + for (t2 = tbuf; t2 < t; t2++) { + if (imeta(*t2)) { + *tdest++ = Meta; + *tdest++ = *t2 ^ 32; + } else + *tdest++ = *t2; + } + /* reset temporary buffer after handling */ + t = tbuf; + } + continue; + case '\'': + case '\\': + if (how & GETKEY_DOLLAR_QUOTE) { + /* + * Usually \' and \\ will have the initial + * \ turned into a Bnull, however that's not + * necessarily the case when called from + * completion. + */ + *t++ = *s; + break; + } + /* FALLTHROUGH */ + default: + def: + /* HERE: GETKEY_UPDATE_OFFSET? */ + if ((idigit(*s) && *s < '8') || *s == 'x') { + if (!(how & GETKEY_OCTAL_ESC)) { + if (*s == '0') + s++; + else if (*s != 'x') { + *t++ = '\\', s--; + continue; + } + } + if (s[1] && s[2] && s[3]) { + svchar = s[3]; + s[3] = '\0'; + u = s; + } + *t++ = zstrtol(s + (*s == 'x'), &s, + (*s == 'x') ? 16 : 8); + if ((how & GETKEY_PRINTF_PERCENT) && t[-1] == '%') + *t++ = '%'; + if (svchar) { + u[3] = svchar; + svchar = '\0'; + } + s--; + } else { + if (!(how & GETKEY_EMACS) && *s != '\\') { + if (miscadded) + (*misc)++; + *t++ = '\\'; + } + *t++ = *s; + } + break; + } + } else if ((how & GETKEY_DOLLAR_QUOTE) && *s == Snull) { + /* return length to following character */ + *len = (s - sstart) + 1; + *tdest = '\0'; + return buf; + } else if (*s == '^' && !control && (how & GETKEY_CTRL) && s[1]) { + control = 1; + continue; +#ifdef MULTIBYTE_SUPPORT + } else if ((how & GETKEY_SINGLE_CHAR) && + isset(MULTIBYTE) && STOUC(*s) > 127) { + wint_t wc; + int len; + len = mb_metacharlenconv(s, &wc); + if (wc != WEOF) { + *misc = (int)wc; + return s + len; + } +#endif + + } else if (*s == Meta) + *t++ = *++s ^ 32; + else { + if (itok(*s)) { + /* + * We need to be quite careful here. We haven't + * necessarily got an input stream with all tokens + * removed, so the majority of tokens need passing + * through untouched and without Meta handling. + * However, me may need to handle tokenized + * backslashes. + */ + if (meta || control) { + /* + * Presumably we should be using meta or control + * on the character representing the token. + * + * Special case: $'\M-\\' where the token is a Bnull. + * This time we dump the Bnull since we're + * replacing the whole thing. The lexer + * doesn't know about the meta or control modifiers. + */ + if ((how & GETKEY_DOLLAR_QUOTE) && *s == Bnull) + *t++ = *++s; + else + *t++ = ztokens[*s - Pound]; + } else if (how & GETKEY_DOLLAR_QUOTE) { + /* + * We don't want to metafy this, it's a real + * token. + */ + *tdest++ = *s; + if (*s == Bnull) { + /* + * Bnull is a backslash which quotes a couple + * of special characters that always appear + * literally next. See strquote handling + * in gettokstr() in lex.c. We need + * to retain the Bnull (as above) so that quote + * handling in completion can tell where the + * backslash was. + */ + *tdest++ = *++s; + } + /* reset temporary buffer, now handled */ + t = tbuf; + continue; + } else + *t++ = *s; + } else + *t++ = *s; + } + if (meta == 2) { + t[-1] |= 0x80; + meta = 0; + } + if (control) { + if (t[-1] == '?') + t[-1] = 0x7f; + else + t[-1] &= 0x9f; + control = 0; + } + if (meta) { + t[-1] |= 0x80; + meta = 0; + } + if (how & GETKEY_DOLLAR_QUOTE) { + char *t2; + for (t2 = tbuf; t2 < t; t2++) { + /* + * In POSIX mode, an embedded NULL is discarded and + * terminates processing. It just does, that's why. + */ + if (isset(POSIXSTRINGS)) { + if (*t2 == '\0') + ignoring = 1; + if (ignoring) + break; + } + if (imeta(*t2)) { + *tdest++ = Meta; + *tdest++ = *t2 ^ 32; + } else { + *tdest++ = *t2; + } + } + /* + * Reset use of temporary buffer. + */ + t = tbuf; + } + if ((how & GETKEY_SINGLE_CHAR) && t != tmp) { + *misc = STOUC(tmp[0]); + return s + 1; + } + } + /* + * When called from completion, where we use GETKEY_UPDATE_OFFSET to + * update the index into the metafied editor line, we don't necessarily + * have the end of a $'...' quotation, else we should do. + */ + DPUTS((how & (GETKEY_DOLLAR_QUOTE|GETKEY_UPDATE_OFFSET)) == + GETKEY_DOLLAR_QUOTE, "BUG: unterminated $' substitution"); + *t = '\0'; + if (how & GETKEY_DOLLAR_QUOTE) + *tdest = '\0'; + if (how & GETKEY_SINGLE_CHAR) + *misc = 0; + else + *len = ((how & GETKEY_DOLLAR_QUOTE) ? tdest : t) - buf; + return buf; +} + +/* Return non-zero if s is a prefix of t. */ + +/**/ +mod_export int +strpfx(const char *s, const char *t) +{ + while (*s && *s == *t) + s++, t++; + return !*s; +} + +/* Return non-zero if s is a suffix of t. */ + +/**/ +mod_export int +strsfx(char *s, char *t) +{ + int ls = strlen(s), lt = strlen(t); + + if (ls <= lt) + return !strcmp(t + lt - ls, s); + return 0; +} + +/**/ +static int +upchdir(int n) +{ + char buf[PATH_MAX+1]; + char *s; + int err = -1; + + while (n > 0) { + for (s = buf; s < buf + PATH_MAX - 4 && n--; ) + *s++ = '.', *s++ = '.', *s++ = '/'; + s[-1] = '\0'; + if (chdir(buf)) + return err; + err = -2; + } + return 0; +} + +/* + * Initialize a "struct dirsav". + * The structure will be set to the directory we want to save + * the first time we change to a different directory. + */ + +/**/ +mod_export void +init_dirsav(Dirsav d) +{ + d->ino = d->dev = 0; + d->dirname = NULL; + 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. + * + * path is an unmetafied but null-terminated string, as needed by system + * calls. + */ + +/**/ +mod_export int +lchdir(char const *path, struct dirsav *d, int hard) +{ + char const *pptr; + int level; + struct stat st1; + struct dirsav ds; +#ifdef HAVE_LSTAT + char buf[PATH_MAX + 1], *ptr; + int err; + struct stat st2; +#endif +#ifdef HAVE_FCHDIR + int close_dir = 0; +#endif + + if (!d) { + init_dirsav(&ds); + d = &ds; + } +#ifdef HAVE_LSTAT + if ((*path == '/' || !hard) && + (d != &ds || hard)){ +#else + if (*path == '/') { +#endif + level = -1; +#ifndef HAVE_FCHDIR + if (!d->dirname) + zgetdir(d); +#endif + } else { + level = 0; + if (!d->dev && !d->ino) { + stat(".", &st1); + d->dev = st1.st_dev; + d->ino = st1.st_ino; + } + } + +#ifdef HAVE_LSTAT + if (!hard) +#endif + { + if (d != &ds) { + for (pptr = path; *pptr; level++) { + while (*pptr && *pptr++ != '/'); + while (*pptr == '/') + pptr++; + } + d->level = level; + } + return zchdir((char *) path); + } + +#ifdef HAVE_LSTAT +#ifdef HAVE_FCHDIR + if (d->dirfd < 0) { + close_dir = 1; + if ((d->dirfd = open(".", O_RDONLY | O_NOCTTY)) < 0 && + zgetdir(d) && *d->dirname != '/') + d->dirfd = open("..", O_RDONLY | O_NOCTTY); + } +#endif + if (*path == '/') + if (chdir("/") < 0) + zwarn("failed to chdir(/): %e", errno); + for(;;) { + while(*path == '/') + path++; + if(!*path) { + if (d == &ds) + zsfree(ds.dirname); + else + d->level = level; +#ifdef HAVE_FCHDIR + if (d->dirfd >=0 && close_dir) { + close(d->dirfd); + d->dirfd = -1; + } +#endif + return 0; + } + for(pptr = path; *++pptr && *pptr != '/'; ) ; + if(pptr - path > PATH_MAX) { + err = ENAMETOOLONG; + break; + } + for(ptr = buf; path != pptr; ) + *ptr++ = *path++; + *ptr = 0; + if(lstat(buf, &st1)) { + err = errno; + break; + } + if(!S_ISDIR(st1.st_mode)) { + err = ENOTDIR; + break; + } + if(chdir(buf)) { + err = errno; + break; + } + if (level >= 0) + level++; + if(lstat(".", &st2)) { + err = errno; + break; + } + if(st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) { + err = ENOTDIR; + break; + } + } + if (restoredir(d)) { + int restoreerr = errno; + int i; + /* + * Failed to restore the directory. + * Just be definite, cd to root and report the result. + */ + for (i = 0; i < 2; i++) { + const char *cdest; + if (i) + cdest = "/"; + else { + if (!home) + continue; + cdest = home; + } + zsfree(pwd); + pwd = ztrdup(cdest); + if (chdir(pwd) == 0) + break; + } + if (i == 2) + zerr("lost current directory, failed to cd to /: %e", errno); + else + zerr("lost current directory: %e: changed to `%s'", restoreerr, + pwd); + if (d == &ds) + zsfree(ds.dirname); +#ifdef HAVE_FCHDIR + if (d->dirfd >=0 && close_dir) { + close(d->dirfd); + d->dirfd = -1; + } +#endif + errno = err; + return -2; + } + if (d == &ds) + zsfree(ds.dirname); +#ifdef HAVE_FCHDIR + if (d->dirfd >=0 && close_dir) { + close(d->dirfd); + d->dirfd = -1; + } +#endif + errno = err; + return -1; +#endif /* HAVE_LSTAT */ +} + +/**/ +mod_export int +restoredir(struct dirsav *d) +{ + int err = 0; + struct stat sbuf; + + if (d->dirname && *d->dirname == '/') + return chdir(d->dirname); +#ifdef HAVE_FCHDIR + if (d->dirfd >= 0) { + if (!fchdir(d->dirfd)) { + if (!d->dirname) { + return 0; + } else if (chdir(d->dirname)) { + close(d->dirfd); + d->dirfd = -1; + err = -2; + } + } else { + close(d->dirfd); + d->dirfd = err = -1; + } + } else +#endif + if (d->level > 0) + err = upchdir(d->level); + else if (d->level < 0) + err = -1; + if (d->dev || d->ino) { + stat(".", &sbuf); + if (sbuf.st_ino != d->ino || sbuf.st_dev != d->dev) + err = -2; + } + return err; +} + + +/* Check whether the shell is running with privileges in effect. * + * This is the case if EITHER the euid is zero, OR (if the system * + * supports POSIX.1e (POSIX.6) capability sets) the process' * + * Effective or Inheritable capability sets are non-empty. */ + +/**/ +int +privasserted(void) +{ + if(!geteuid()) + return 1; +#ifdef HAVE_CAP_GET_PROC + { + cap_t caps = cap_get_proc(); + if(caps) { + /* POSIX doesn't define a way to test whether a capability set * + * is empty or not. Typical. I hope this is conforming... */ + cap_flag_value_t val; + cap_value_t n; + for(n = 0; !cap_get_flag(caps, n, CAP_EFFECTIVE, &val); n++) + if(val) { + cap_free(caps); + return 1; + } + } + cap_free(caps); + } +#endif /* HAVE_CAP_GET_PROC */ + return 0; +} + +/**/ +mod_export int +mode_to_octal(mode_t mode) +{ + int m = 0; + + if(mode & S_ISUID) + m |= 04000; + if(mode & S_ISGID) + m |= 02000; + if(mode & S_ISVTX) + m |= 01000; + if(mode & S_IRUSR) + m |= 00400; + if(mode & S_IWUSR) + m |= 00200; + if(mode & S_IXUSR) + m |= 00100; + if(mode & S_IRGRP) + m |= 00040; + if(mode & S_IWGRP) + m |= 00020; + if(mode & S_IXGRP) + m |= 00010; + if(mode & S_IROTH) + m |= 00004; + if(mode & S_IWOTH) + m |= 00002; + if(mode & S_IXOTH) + m |= 00001; + return m; +} + +#ifdef MAILDIR_SUPPORT +/* + * Stat a file. If it's a maildir, check all messages + * in the maildir and present the grand total as a file. + * The fields in the 'struct stat' are from the mail directory. + * The following fields are emulated: + * + * st_nlink always 1 + * st_size total number of bytes in all files + * st_blocks total number of messages + * st_atime access time of newest file in maildir + * st_mtime modify time of newest file in maildir + * st_mode S_IFDIR changed to S_IFREG + * + * This is good enough for most mail-checking applications. + */ + +/**/ +int +mailstat(char *path, struct stat *st) +{ + DIR *dd; + struct dirent *fn; + struct stat st_ret, st_tmp; + static struct stat st_ret_last; + char *dir, *file = 0; + int i; + time_t atime = 0, mtime = 0; + size_t plen = strlen(path), dlen; + + /* First see if it's a directory. */ + if ((i = stat(path, st)) != 0 || !S_ISDIR(st->st_mode)) + return i; + + st_ret = *st; + st_ret.st_nlink = 1; + st_ret.st_size = 0; + st_ret.st_blocks = 0; + st_ret.st_mode &= ~S_IFDIR; + st_ret.st_mode |= S_IFREG; + + /* See if cur/ is present */ + dir = appstr(ztrdup(path), "/cur"); + if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0; + st_ret.st_atime = st_tmp.st_atime; + + /* See if tmp/ is present */ + dir[plen] = 0; + dir = appstr(dir, "/tmp"); + if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0; + st_ret.st_mtime = st_tmp.st_mtime; + + /* And new/ */ + dir[plen] = 0; + dir = appstr(dir, "/new"); + if (stat(dir, &st_tmp) || !S_ISDIR(st_tmp.st_mode)) return 0; + st_ret.st_mtime = st_tmp.st_mtime; + +#if THERE_IS_EXACTLY_ONE_MAILDIR_IN_MAILPATH + { + static struct stat st_new_last; + /* Optimization - if new/ didn't change, nothing else did. */ + if (st_tmp.st_dev == st_new_last.st_dev && + st_tmp.st_ino == st_new_last.st_ino && + st_tmp.st_atime == st_new_last.st_atime && + st_tmp.st_mtime == st_new_last.st_mtime) { + *st = st_ret_last; + return 0; + } + st_new_last = st_tmp; + } +#endif + + /* Loop over new/ and cur/ */ + for (i = 0; i < 2; i++) { + dir[plen] = 0; + dir = appstr(dir, i ? "/cur" : "/new"); + if ((dd = opendir(dir)) == NULL) { + zsfree(file); + zsfree(dir); + return 0; + } + dlen = strlen(dir) + 1; /* include the "/" */ + while ((fn = readdir(dd)) != NULL) { + if (fn->d_name[0] == '.') + continue; + if (file) { + file[dlen] = 0; + file = appstr(file, fn->d_name); + } else { + file = tricat(dir, "/", fn->d_name); + } + if (stat(file, &st_tmp) != 0) + continue; + st_ret.st_size += st_tmp.st_size; + st_ret.st_blocks++; + if (st_tmp.st_atime != st_tmp.st_mtime && + st_tmp.st_atime > atime) + atime = st_tmp.st_atime; + if (st_tmp.st_mtime > mtime) + mtime = st_tmp.st_mtime; + } + closedir(dd); + } + zsfree(file); + zsfree(dir); + + if (atime) st_ret.st_atime = atime; + if (mtime) st_ret.st_mtime = mtime; + + *st = st_ret_last = st_ret; + return 0; +} +#endif diff --git a/dotfiles/system/.zsh/modules/Src/wcwidth9.h b/dotfiles/system/.zsh/modules/Src/wcwidth9.h new file mode 100644 index 0000000..448f548 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/wcwidth9.h @@ -0,0 +1,1325 @@ +#ifndef WCWIDTH9_H +#define WCWIDTH9_H + +#include +#include + +struct wcwidth9_interval { + long first; + long last; +}; + +static const struct wcwidth9_interval wcwidth9_private[] = { + {0x00e000, 0x00f8ff}, + {0x0f0000, 0x0ffffd}, + {0x100000, 0x10fffd}, +}; + +static const struct wcwidth9_interval wcwidth9_nonprint[] = { + {0x0000, 0x001f}, + {0x007f, 0x009f}, + {0x00ad, 0x00ad}, + {0x070f, 0x070f}, + {0x180b, 0x180e}, + {0x200b, 0x200f}, + {0x2028, 0x2029}, + {0x202a, 0x202e}, + {0x206a, 0x206f}, + {0xd800, 0xdfff}, + {0xfeff, 0xfeff}, + {0xfff9, 0xfffb}, + {0xfffe, 0xffff}, +}; + +static const struct wcwidth9_interval wcwidth9_combining[] = { + {0x0300, 0x036f}, + {0x0483, 0x0489}, + {0x0591, 0x05bd}, + {0x05bf, 0x05bf}, + {0x05c1, 0x05c2}, + {0x05c4, 0x05c5}, + {0x05c7, 0x05c7}, + {0x0610, 0x061a}, + {0x064b, 0x065f}, + {0x0670, 0x0670}, + {0x06d6, 0x06dc}, + {0x06df, 0x06e4}, + {0x06e7, 0x06e8}, + {0x06ea, 0x06ed}, + {0x0711, 0x0711}, + {0x0730, 0x074a}, + {0x07a6, 0x07b0}, + {0x07eb, 0x07f3}, + {0x0816, 0x0819}, + {0x081b, 0x0823}, + {0x0825, 0x0827}, + {0x0829, 0x082d}, + {0x0859, 0x085b}, + {0x08d4, 0x08e1}, + {0x08e3, 0x0903}, + {0x093a, 0x093c}, + {0x093e, 0x094f}, + {0x0951, 0x0957}, + {0x0962, 0x0963}, + {0x0981, 0x0983}, + {0x09bc, 0x09bc}, + {0x09be, 0x09c4}, + {0x09c7, 0x09c8}, + {0x09cb, 0x09cd}, + {0x09d7, 0x09d7}, + {0x09e2, 0x09e3}, + {0x0a01, 0x0a03}, + {0x0a3c, 0x0a3c}, + {0x0a3e, 0x0a42}, + {0x0a47, 0x0a48}, + {0x0a4b, 0x0a4d}, + {0x0a51, 0x0a51}, + {0x0a70, 0x0a71}, + {0x0a75, 0x0a75}, + {0x0a81, 0x0a83}, + {0x0abc, 0x0abc}, + {0x0abe, 0x0ac5}, + {0x0ac7, 0x0ac9}, + {0x0acb, 0x0acd}, + {0x0ae2, 0x0ae3}, + {0x0b01, 0x0b03}, + {0x0b3c, 0x0b3c}, + {0x0b3e, 0x0b44}, + {0x0b47, 0x0b48}, + {0x0b4b, 0x0b4d}, + {0x0b56, 0x0b57}, + {0x0b62, 0x0b63}, + {0x0b82, 0x0b82}, + {0x0bbe, 0x0bc2}, + {0x0bc6, 0x0bc8}, + {0x0bca, 0x0bcd}, + {0x0bd7, 0x0bd7}, + {0x0c00, 0x0c03}, + {0x0c3e, 0x0c44}, + {0x0c46, 0x0c48}, + {0x0c4a, 0x0c4d}, + {0x0c55, 0x0c56}, + {0x0c62, 0x0c63}, + {0x0c81, 0x0c83}, + {0x0cbc, 0x0cbc}, + {0x0cbe, 0x0cc4}, + {0x0cc6, 0x0cc8}, + {0x0cca, 0x0ccd}, + {0x0cd5, 0x0cd6}, + {0x0ce2, 0x0ce3}, + {0x0d01, 0x0d03}, + {0x0d3e, 0x0d44}, + {0x0d46, 0x0d48}, + {0x0d4a, 0x0d4d}, + {0x0d57, 0x0d57}, + {0x0d62, 0x0d63}, + {0x0d82, 0x0d83}, + {0x0dca, 0x0dca}, + {0x0dcf, 0x0dd4}, + {0x0dd6, 0x0dd6}, + {0x0dd8, 0x0ddf}, + {0x0df2, 0x0df3}, + {0x0e31, 0x0e31}, + {0x0e34, 0x0e3a}, + {0x0e47, 0x0e4e}, + {0x0eb1, 0x0eb1}, + {0x0eb4, 0x0eb9}, + {0x0ebb, 0x0ebc}, + {0x0ec8, 0x0ecd}, + {0x0f18, 0x0f19}, + {0x0f35, 0x0f35}, + {0x0f37, 0x0f37}, + {0x0f39, 0x0f39}, + {0x0f3e, 0x0f3f}, + {0x0f71, 0x0f84}, + {0x0f86, 0x0f87}, + {0x0f8d, 0x0f97}, + {0x0f99, 0x0fbc}, + {0x0fc6, 0x0fc6}, + {0x102b, 0x103e}, + {0x1056, 0x1059}, + {0x105e, 0x1060}, + {0x1062, 0x1064}, + {0x1067, 0x106d}, + {0x1071, 0x1074}, + {0x1082, 0x108d}, + {0x108f, 0x108f}, + {0x109a, 0x109d}, + {0x135d, 0x135f}, + {0x1712, 0x1714}, + {0x1732, 0x1734}, + {0x1752, 0x1753}, + {0x1772, 0x1773}, + {0x17b4, 0x17d3}, + {0x17dd, 0x17dd}, + {0x180b, 0x180d}, + {0x1885, 0x1886}, + {0x18a9, 0x18a9}, + {0x1920, 0x192b}, + {0x1930, 0x193b}, + {0x1a17, 0x1a1b}, + {0x1a55, 0x1a5e}, + {0x1a60, 0x1a7c}, + {0x1a7f, 0x1a7f}, + {0x1ab0, 0x1abe}, + {0x1b00, 0x1b04}, + {0x1b34, 0x1b44}, + {0x1b6b, 0x1b73}, + {0x1b80, 0x1b82}, + {0x1ba1, 0x1bad}, + {0x1be6, 0x1bf3}, + {0x1c24, 0x1c37}, + {0x1cd0, 0x1cd2}, + {0x1cd4, 0x1ce8}, + {0x1ced, 0x1ced}, + {0x1cf2, 0x1cf4}, + {0x1cf8, 0x1cf9}, + {0x1dc0, 0x1df5}, + {0x1dfb, 0x1dff}, + {0x20d0, 0x20f0}, + {0x2cef, 0x2cf1}, + {0x2d7f, 0x2d7f}, + {0x2de0, 0x2dff}, + {0x302a, 0x302f}, + {0x3099, 0x309a}, + {0xa66f, 0xa672}, + {0xa674, 0xa67d}, + {0xa69e, 0xa69f}, + {0xa6f0, 0xa6f1}, + {0xa802, 0xa802}, + {0xa806, 0xa806}, + {0xa80b, 0xa80b}, + {0xa823, 0xa827}, + {0xa880, 0xa881}, + {0xa8b4, 0xa8c5}, + {0xa8e0, 0xa8f1}, + {0xa926, 0xa92d}, + {0xa947, 0xa953}, + {0xa980, 0xa983}, + {0xa9b3, 0xa9c0}, + {0xa9e5, 0xa9e5}, + {0xaa29, 0xaa36}, + {0xaa43, 0xaa43}, + {0xaa4c, 0xaa4d}, + {0xaa7b, 0xaa7d}, + {0xaab0, 0xaab0}, + {0xaab2, 0xaab4}, + {0xaab7, 0xaab8}, + {0xaabe, 0xaabf}, + {0xaac1, 0xaac1}, + {0xaaeb, 0xaaef}, + {0xaaf5, 0xaaf6}, + {0xabe3, 0xabea}, + {0xabec, 0xabed}, + {0xfb1e, 0xfb1e}, + {0xfe00, 0xfe0f}, + {0xfe20, 0xfe2f}, + {0x101fd, 0x101fd}, + {0x102e0, 0x102e0}, + {0x10376, 0x1037a}, + {0x10a01, 0x10a03}, + {0x10a05, 0x10a06}, + {0x10a0c, 0x10a0f}, + {0x10a38, 0x10a3a}, + {0x10a3f, 0x10a3f}, + {0x10ae5, 0x10ae6}, + {0x11000, 0x11002}, + {0x11038, 0x11046}, + {0x1107f, 0x11082}, + {0x110b0, 0x110ba}, + {0x11100, 0x11102}, + {0x11127, 0x11134}, + {0x11173, 0x11173}, + {0x11180, 0x11182}, + {0x111b3, 0x111c0}, + {0x111ca, 0x111cc}, + {0x1122c, 0x11237}, + {0x1123e, 0x1123e}, + {0x112df, 0x112ea}, + {0x11300, 0x11303}, + {0x1133c, 0x1133c}, + {0x1133e, 0x11344}, + {0x11347, 0x11348}, + {0x1134b, 0x1134d}, + {0x11357, 0x11357}, + {0x11362, 0x11363}, + {0x11366, 0x1136c}, + {0x11370, 0x11374}, + {0x11435, 0x11446}, + {0x114b0, 0x114c3}, + {0x115af, 0x115b5}, + {0x115b8, 0x115c0}, + {0x115dc, 0x115dd}, + {0x11630, 0x11640}, + {0x116ab, 0x116b7}, + {0x1171d, 0x1172b}, + {0x11c2f, 0x11c36}, + {0x11c38, 0x11c3f}, + {0x11c92, 0x11ca7}, + {0x11ca9, 0x11cb6}, + {0x16af0, 0x16af4}, + {0x16b30, 0x16b36}, + {0x16f51, 0x16f7e}, + {0x16f8f, 0x16f92}, + {0x1bc9d, 0x1bc9e}, + {0x1d165, 0x1d169}, + {0x1d16d, 0x1d172}, + {0x1d17b, 0x1d182}, + {0x1d185, 0x1d18b}, + {0x1d1aa, 0x1d1ad}, + {0x1d242, 0x1d244}, + {0x1da00, 0x1da36}, + {0x1da3b, 0x1da6c}, + {0x1da75, 0x1da75}, + {0x1da84, 0x1da84}, + {0x1da9b, 0x1da9f}, + {0x1daa1, 0x1daaf}, + {0x1e000, 0x1e006}, + {0x1e008, 0x1e018}, + {0x1e01b, 0x1e021}, + {0x1e023, 0x1e024}, + {0x1e026, 0x1e02a}, + {0x1e8d0, 0x1e8d6}, + {0x1e944, 0x1e94a}, + {0xe0100, 0xe01ef}, +}; + +static const struct wcwidth9_interval wcwidth9_doublewidth[] = { + {0x1100, 0x115f}, + {0x231a, 0x231b}, + {0x2329, 0x232a}, + {0x23e9, 0x23ec}, + {0x23f0, 0x23f0}, + {0x23f3, 0x23f3}, + {0x25fd, 0x25fe}, + {0x2614, 0x2615}, + {0x2648, 0x2653}, + {0x267f, 0x267f}, + {0x2693, 0x2693}, + {0x26a1, 0x26a1}, + {0x26aa, 0x26ab}, + {0x26bd, 0x26be}, + {0x26c4, 0x26c5}, + {0x26ce, 0x26ce}, + {0x26d4, 0x26d4}, + {0x26ea, 0x26ea}, + {0x26f2, 0x26f3}, + {0x26f5, 0x26f5}, + {0x26fa, 0x26fa}, + {0x26fd, 0x26fd}, + {0x2705, 0x2705}, + {0x270a, 0x270b}, + {0x2728, 0x2728}, + {0x274c, 0x274c}, + {0x274e, 0x274e}, + {0x2753, 0x2755}, + {0x2757, 0x2757}, + {0x2795, 0x2797}, + {0x27b0, 0x27b0}, + {0x27bf, 0x27bf}, + {0x2b1b, 0x2b1c}, + {0x2b50, 0x2b50}, + {0x2b55, 0x2b55}, + {0x2e80, 0x2e99}, + {0x2e9b, 0x2ef3}, + {0x2f00, 0x2fd5}, + {0x2ff0, 0x2ffb}, + {0x3000, 0x303e}, + {0x3041, 0x3096}, + {0x3099, 0x30ff}, + {0x3105, 0x312d}, + {0x3131, 0x318e}, + {0x3190, 0x31ba}, + {0x31c0, 0x31e3}, + {0x31f0, 0x321e}, + {0x3220, 0x3247}, + {0x3250, 0x32fe}, + {0x3300, 0x4dbf}, + {0x4e00, 0xa48c}, + {0xa490, 0xa4c6}, + {0xa960, 0xa97c}, + {0xac00, 0xd7a3}, + {0xf900, 0xfaff}, + {0xfe10, 0xfe19}, + {0xfe30, 0xfe52}, + {0xfe54, 0xfe66}, + {0xfe68, 0xfe6b}, + {0xff01, 0xff60}, + {0xffe0, 0xffe6}, + {0x16fe0, 0x16fe0}, + {0x17000, 0x187ec}, + {0x18800, 0x18af2}, + {0x1b000, 0x1b001}, + {0x1f004, 0x1f004}, + {0x1f0cf, 0x1f0cf}, + {0x1f18e, 0x1f18e}, + {0x1f191, 0x1f19a}, + {0x1f200, 0x1f202}, + {0x1f210, 0x1f23b}, + {0x1f240, 0x1f248}, + {0x1f250, 0x1f251}, + {0x1f300, 0x1f320}, + {0x1f32d, 0x1f335}, + {0x1f337, 0x1f37c}, + {0x1f37e, 0x1f393}, + {0x1f3a0, 0x1f3ca}, + {0x1f3cf, 0x1f3d3}, + {0x1f3e0, 0x1f3f0}, + {0x1f3f4, 0x1f3f4}, + {0x1f3f8, 0x1f43e}, + {0x1f440, 0x1f440}, + {0x1f442, 0x1f4fc}, + {0x1f4ff, 0x1f53d}, + {0x1f54b, 0x1f54e}, + {0x1f550, 0x1f567}, + {0x1f57a, 0x1f57a}, + {0x1f595, 0x1f596}, + {0x1f5a4, 0x1f5a4}, + {0x1f5fb, 0x1f64f}, + {0x1f680, 0x1f6c5}, + {0x1f6cc, 0x1f6cc}, + {0x1f6d0, 0x1f6d2}, + {0x1f6eb, 0x1f6ec}, + {0x1f6f4, 0x1f6f6}, + {0x1f910, 0x1f91e}, + {0x1f920, 0x1f927}, + {0x1f930, 0x1f930}, + {0x1f933, 0x1f93e}, + {0x1f940, 0x1f94b}, + {0x1f950, 0x1f95e}, + {0x1f980, 0x1f991}, + {0x1f9c0, 0x1f9c0}, + {0x20000, 0x2fffd}, + {0x30000, 0x3fffd}, +}; + +static const struct wcwidth9_interval wcwidth9_ambiguous[] = { + {0x00a1, 0x00a1}, + {0x00a4, 0x00a4}, + {0x00a7, 0x00a8}, + {0x00aa, 0x00aa}, + {0x00ad, 0x00ae}, + {0x00b0, 0x00b4}, + {0x00b6, 0x00ba}, + {0x00bc, 0x00bf}, + {0x00c6, 0x00c6}, + {0x00d0, 0x00d0}, + {0x00d7, 0x00d8}, + {0x00de, 0x00e1}, + {0x00e6, 0x00e6}, + {0x00e8, 0x00ea}, + {0x00ec, 0x00ed}, + {0x00f0, 0x00f0}, + {0x00f2, 0x00f3}, + {0x00f7, 0x00fa}, + {0x00fc, 0x00fc}, + {0x00fe, 0x00fe}, + {0x0101, 0x0101}, + {0x0111, 0x0111}, + {0x0113, 0x0113}, + {0x011b, 0x011b}, + {0x0126, 0x0127}, + {0x012b, 0x012b}, + {0x0131, 0x0133}, + {0x0138, 0x0138}, + {0x013f, 0x0142}, + {0x0144, 0x0144}, + {0x0148, 0x014b}, + {0x014d, 0x014d}, + {0x0152, 0x0153}, + {0x0166, 0x0167}, + {0x016b, 0x016b}, + {0x01ce, 0x01ce}, + {0x01d0, 0x01d0}, + {0x01d2, 0x01d2}, + {0x01d4, 0x01d4}, + {0x01d6, 0x01d6}, + {0x01d8, 0x01d8}, + {0x01da, 0x01da}, + {0x01dc, 0x01dc}, + {0x0251, 0x0251}, + {0x0261, 0x0261}, + {0x02c4, 0x02c4}, + {0x02c7, 0x02c7}, + {0x02c9, 0x02cb}, + {0x02cd, 0x02cd}, + {0x02d0, 0x02d0}, + {0x02d8, 0x02db}, + {0x02dd, 0x02dd}, + {0x02df, 0x02df}, + {0x0300, 0x036f}, + {0x0391, 0x03a1}, + {0x03a3, 0x03a9}, + {0x03b1, 0x03c1}, + {0x03c3, 0x03c9}, + {0x0401, 0x0401}, + {0x0410, 0x044f}, + {0x0451, 0x0451}, + {0x2010, 0x2010}, + {0x2013, 0x2016}, + {0x2018, 0x2019}, + {0x201c, 0x201d}, + {0x2020, 0x2022}, + {0x2024, 0x2027}, + {0x2030, 0x2030}, + {0x2032, 0x2033}, + {0x2035, 0x2035}, + {0x203b, 0x203b}, + {0x203e, 0x203e}, + {0x2074, 0x2074}, + {0x207f, 0x207f}, + {0x2081, 0x2084}, + {0x20ac, 0x20ac}, + {0x2103, 0x2103}, + {0x2105, 0x2105}, + {0x2109, 0x2109}, + {0x2113, 0x2113}, + {0x2116, 0x2116}, + {0x2121, 0x2122}, + {0x2126, 0x2126}, + {0x212b, 0x212b}, + {0x2153, 0x2154}, + {0x215b, 0x215e}, + {0x2160, 0x216b}, + {0x2170, 0x2179}, + {0x2189, 0x2189}, + {0x2190, 0x2199}, + {0x21b8, 0x21b9}, + {0x21d2, 0x21d2}, + {0x21d4, 0x21d4}, + {0x21e7, 0x21e7}, + {0x2200, 0x2200}, + {0x2202, 0x2203}, + {0x2207, 0x2208}, + {0x220b, 0x220b}, + {0x220f, 0x220f}, + {0x2211, 0x2211}, + {0x2215, 0x2215}, + {0x221a, 0x221a}, + {0x221d, 0x2220}, + {0x2223, 0x2223}, + {0x2225, 0x2225}, + {0x2227, 0x222c}, + {0x222e, 0x222e}, + {0x2234, 0x2237}, + {0x223c, 0x223d}, + {0x2248, 0x2248}, + {0x224c, 0x224c}, + {0x2252, 0x2252}, + {0x2260, 0x2261}, + {0x2264, 0x2267}, + {0x226a, 0x226b}, + {0x226e, 0x226f}, + {0x2282, 0x2283}, + {0x2286, 0x2287}, + {0x2295, 0x2295}, + {0x2299, 0x2299}, + {0x22a5, 0x22a5}, + {0x22bf, 0x22bf}, + {0x2312, 0x2312}, + {0x2460, 0x24e9}, + {0x24eb, 0x254b}, + {0x2550, 0x2573}, + {0x2580, 0x258f}, + {0x2592, 0x2595}, + {0x25a0, 0x25a1}, + {0x25a3, 0x25a9}, + {0x25b2, 0x25b3}, + {0x25b6, 0x25b7}, + {0x25bc, 0x25bd}, + {0x25c0, 0x25c1}, + {0x25c6, 0x25c8}, + {0x25cb, 0x25cb}, + {0x25ce, 0x25d1}, + {0x25e2, 0x25e5}, + {0x25ef, 0x25ef}, + {0x2605, 0x2606}, + {0x2609, 0x2609}, + {0x260e, 0x260f}, + {0x261c, 0x261c}, + {0x261e, 0x261e}, + {0x2640, 0x2640}, + {0x2642, 0x2642}, + {0x2660, 0x2661}, + {0x2663, 0x2665}, + {0x2667, 0x266a}, + {0x266c, 0x266d}, + {0x266f, 0x266f}, + {0x269e, 0x269f}, + {0x26bf, 0x26bf}, + {0x26c6, 0x26cd}, + {0x26cf, 0x26d3}, + {0x26d5, 0x26e1}, + {0x26e3, 0x26e3}, + {0x26e8, 0x26e9}, + {0x26eb, 0x26f1}, + {0x26f4, 0x26f4}, + {0x26f6, 0x26f9}, + {0x26fb, 0x26fc}, + {0x26fe, 0x26ff}, + {0x273d, 0x273d}, + {0x2776, 0x277f}, + {0x2b56, 0x2b59}, + {0x3248, 0x324f}, + {0xe000, 0xf8ff}, + {0xfe00, 0xfe0f}, + {0xfffd, 0xfffd}, + {0x1f100, 0x1f10a}, + {0x1f110, 0x1f12d}, + {0x1f130, 0x1f169}, + {0x1f170, 0x1f18d}, + {0x1f18f, 0x1f190}, + {0x1f19b, 0x1f1ac}, + {0xe0100, 0xe01ef}, + {0xf0000, 0xffffd}, + {0x100000, 0x10fffd}, +}; + +static const struct wcwidth9_interval wcwidth9_emoji_width[] = { + {0x1f1e6, 0x1f1ff}, + {0x1f321, 0x1f321}, + {0x1f324, 0x1f32c}, + {0x1f336, 0x1f336}, + {0x1f37d, 0x1f37d}, + {0x1f396, 0x1f397}, + {0x1f399, 0x1f39b}, + {0x1f39e, 0x1f39f}, + {0x1f3cb, 0x1f3ce}, + {0x1f3d4, 0x1f3df}, + {0x1f3f3, 0x1f3f5}, + {0x1f3f7, 0x1f3f7}, + {0x1f43f, 0x1f43f}, + {0x1f441, 0x1f441}, + {0x1f4fd, 0x1f4fd}, + {0x1f549, 0x1f54a}, + {0x1f56f, 0x1f570}, + {0x1f573, 0x1f579}, + {0x1f587, 0x1f587}, + {0x1f58a, 0x1f58d}, + {0x1f590, 0x1f590}, + {0x1f5a5, 0x1f5a5}, + {0x1f5a8, 0x1f5a8}, + {0x1f5b1, 0x1f5b2}, + {0x1f5bc, 0x1f5bc}, + {0x1f5c2, 0x1f5c4}, + {0x1f5d1, 0x1f5d3}, + {0x1f5dc, 0x1f5de}, + {0x1f5e1, 0x1f5e1}, + {0x1f5e3, 0x1f5e3}, + {0x1f5e8, 0x1f5e8}, + {0x1f5ef, 0x1f5ef}, + {0x1f5f3, 0x1f5f3}, + {0x1f5fa, 0x1f5fa}, + {0x1f6cb, 0x1f6cf}, + {0x1f6e0, 0x1f6e5}, + {0x1f6e9, 0x1f6e9}, + {0x1f6f0, 0x1f6f0}, + {0x1f6f3, 0x1f6f3}, +}; + +static const struct wcwidth9_interval wcwidth9_not_assigned[] = { + {0x0378, 0x0379}, + {0x0380, 0x0383}, + {0x038b, 0x038b}, + {0x038d, 0x038d}, + {0x03a2, 0x03a2}, + {0x0530, 0x0530}, + {0x0557, 0x0558}, + {0x0560, 0x0560}, + {0x0588, 0x0588}, + {0x058b, 0x058c}, + {0x0590, 0x0590}, + {0x05c8, 0x05cf}, + {0x05eb, 0x05ef}, + {0x05f5, 0x05ff}, + {0x061d, 0x061d}, + {0x070e, 0x070e}, + {0x074b, 0x074c}, + {0x07b2, 0x07bf}, + {0x07fb, 0x07ff}, + {0x082e, 0x082f}, + {0x083f, 0x083f}, + {0x085c, 0x085d}, + {0x085f, 0x089f}, + {0x08b5, 0x08b5}, + {0x08be, 0x08d3}, + {0x0984, 0x0984}, + {0x098d, 0x098e}, + {0x0991, 0x0992}, + {0x09a9, 0x09a9}, + {0x09b1, 0x09b1}, + {0x09b3, 0x09b5}, + {0x09ba, 0x09bb}, + {0x09c5, 0x09c6}, + {0x09c9, 0x09ca}, + {0x09cf, 0x09d6}, + {0x09d8, 0x09db}, + {0x09de, 0x09de}, + {0x09e4, 0x09e5}, + {0x09fc, 0x0a00}, + {0x0a04, 0x0a04}, + {0x0a0b, 0x0a0e}, + {0x0a11, 0x0a12}, + {0x0a29, 0x0a29}, + {0x0a31, 0x0a31}, + {0x0a34, 0x0a34}, + {0x0a37, 0x0a37}, + {0x0a3a, 0x0a3b}, + {0x0a3d, 0x0a3d}, + {0x0a43, 0x0a46}, + {0x0a49, 0x0a4a}, + {0x0a4e, 0x0a50}, + {0x0a52, 0x0a58}, + {0x0a5d, 0x0a5d}, + {0x0a5f, 0x0a65}, + {0x0a76, 0x0a80}, + {0x0a84, 0x0a84}, + {0x0a8e, 0x0a8e}, + {0x0a92, 0x0a92}, + {0x0aa9, 0x0aa9}, + {0x0ab1, 0x0ab1}, + {0x0ab4, 0x0ab4}, + {0x0aba, 0x0abb}, + {0x0ac6, 0x0ac6}, + {0x0aca, 0x0aca}, + {0x0ace, 0x0acf}, + {0x0ad1, 0x0adf}, + {0x0ae4, 0x0ae5}, + {0x0af2, 0x0af8}, + {0x0afa, 0x0b00}, + {0x0b04, 0x0b04}, + {0x0b0d, 0x0b0e}, + {0x0b11, 0x0b12}, + {0x0b29, 0x0b29}, + {0x0b31, 0x0b31}, + {0x0b34, 0x0b34}, + {0x0b3a, 0x0b3b}, + {0x0b45, 0x0b46}, + {0x0b49, 0x0b4a}, + {0x0b4e, 0x0b55}, + {0x0b58, 0x0b5b}, + {0x0b5e, 0x0b5e}, + {0x0b64, 0x0b65}, + {0x0b78, 0x0b81}, + {0x0b84, 0x0b84}, + {0x0b8b, 0x0b8d}, + {0x0b91, 0x0b91}, + {0x0b96, 0x0b98}, + {0x0b9b, 0x0b9b}, + {0x0b9d, 0x0b9d}, + {0x0ba0, 0x0ba2}, + {0x0ba5, 0x0ba7}, + {0x0bab, 0x0bad}, + {0x0bba, 0x0bbd}, + {0x0bc3, 0x0bc5}, + {0x0bc9, 0x0bc9}, + {0x0bce, 0x0bcf}, + {0x0bd1, 0x0bd6}, + {0x0bd8, 0x0be5}, + {0x0bfb, 0x0bff}, + {0x0c04, 0x0c04}, + {0x0c0d, 0x0c0d}, + {0x0c11, 0x0c11}, + {0x0c29, 0x0c29}, + {0x0c3a, 0x0c3c}, + {0x0c45, 0x0c45}, + {0x0c49, 0x0c49}, + {0x0c4e, 0x0c54}, + {0x0c57, 0x0c57}, + {0x0c5b, 0x0c5f}, + {0x0c64, 0x0c65}, + {0x0c70, 0x0c77}, + {0x0c84, 0x0c84}, + {0x0c8d, 0x0c8d}, + {0x0c91, 0x0c91}, + {0x0ca9, 0x0ca9}, + {0x0cb4, 0x0cb4}, + {0x0cba, 0x0cbb}, + {0x0cc5, 0x0cc5}, + {0x0cc9, 0x0cc9}, + {0x0cce, 0x0cd4}, + {0x0cd7, 0x0cdd}, + {0x0cdf, 0x0cdf}, + {0x0ce4, 0x0ce5}, + {0x0cf0, 0x0cf0}, + {0x0cf3, 0x0d00}, + {0x0d04, 0x0d04}, + {0x0d0d, 0x0d0d}, + {0x0d11, 0x0d11}, + {0x0d3b, 0x0d3c}, + {0x0d45, 0x0d45}, + {0x0d49, 0x0d49}, + {0x0d50, 0x0d53}, + {0x0d64, 0x0d65}, + {0x0d80, 0x0d81}, + {0x0d84, 0x0d84}, + {0x0d97, 0x0d99}, + {0x0db2, 0x0db2}, + {0x0dbc, 0x0dbc}, + {0x0dbe, 0x0dbf}, + {0x0dc7, 0x0dc9}, + {0x0dcb, 0x0dce}, + {0x0dd5, 0x0dd5}, + {0x0dd7, 0x0dd7}, + {0x0de0, 0x0de5}, + {0x0df0, 0x0df1}, + {0x0df5, 0x0e00}, + {0x0e3b, 0x0e3e}, + {0x0e5c, 0x0e80}, + {0x0e83, 0x0e83}, + {0x0e85, 0x0e86}, + {0x0e89, 0x0e89}, + {0x0e8b, 0x0e8c}, + {0x0e8e, 0x0e93}, + {0x0e98, 0x0e98}, + {0x0ea0, 0x0ea0}, + {0x0ea4, 0x0ea4}, + {0x0ea6, 0x0ea6}, + {0x0ea8, 0x0ea9}, + {0x0eac, 0x0eac}, + {0x0eba, 0x0eba}, + {0x0ebe, 0x0ebf}, + {0x0ec5, 0x0ec5}, + {0x0ec7, 0x0ec7}, + {0x0ece, 0x0ecf}, + {0x0eda, 0x0edb}, + {0x0ee0, 0x0eff}, + {0x0f48, 0x0f48}, + {0x0f6d, 0x0f70}, + {0x0f98, 0x0f98}, + {0x0fbd, 0x0fbd}, + {0x0fcd, 0x0fcd}, + {0x0fdb, 0x0fff}, + {0x10c6, 0x10c6}, + {0x10c8, 0x10cc}, + {0x10ce, 0x10cf}, + {0x1249, 0x1249}, + {0x124e, 0x124f}, + {0x1257, 0x1257}, + {0x1259, 0x1259}, + {0x125e, 0x125f}, + {0x1289, 0x1289}, + {0x128e, 0x128f}, + {0x12b1, 0x12b1}, + {0x12b6, 0x12b7}, + {0x12bf, 0x12bf}, + {0x12c1, 0x12c1}, + {0x12c6, 0x12c7}, + {0x12d7, 0x12d7}, + {0x1311, 0x1311}, + {0x1316, 0x1317}, + {0x135b, 0x135c}, + {0x137d, 0x137f}, + {0x139a, 0x139f}, + {0x13f6, 0x13f7}, + {0x13fe, 0x13ff}, + {0x169d, 0x169f}, + {0x16f9, 0x16ff}, + {0x170d, 0x170d}, + {0x1715, 0x171f}, + {0x1737, 0x173f}, + {0x1754, 0x175f}, + {0x176d, 0x176d}, + {0x1771, 0x1771}, + {0x1774, 0x177f}, + {0x17de, 0x17df}, + {0x17ea, 0x17ef}, + {0x17fa, 0x17ff}, + {0x180f, 0x180f}, + {0x181a, 0x181f}, + {0x1878, 0x187f}, + {0x18ab, 0x18af}, + {0x18f6, 0x18ff}, + {0x191f, 0x191f}, + {0x192c, 0x192f}, + {0x193c, 0x193f}, + {0x1941, 0x1943}, + {0x196e, 0x196f}, + {0x1975, 0x197f}, + {0x19ac, 0x19af}, + {0x19ca, 0x19cf}, + {0x19db, 0x19dd}, + {0x1a1c, 0x1a1d}, + {0x1a5f, 0x1a5f}, + {0x1a7d, 0x1a7e}, + {0x1a8a, 0x1a8f}, + {0x1a9a, 0x1a9f}, + {0x1aae, 0x1aaf}, + {0x1abf, 0x1aff}, + {0x1b4c, 0x1b4f}, + {0x1b7d, 0x1b7f}, + {0x1bf4, 0x1bfb}, + {0x1c38, 0x1c3a}, + {0x1c4a, 0x1c4c}, + {0x1c89, 0x1cbf}, + {0x1cc8, 0x1ccf}, + {0x1cf7, 0x1cf7}, + {0x1cfa, 0x1cff}, + {0x1df6, 0x1dfa}, + {0x1f16, 0x1f17}, + {0x1f1e, 0x1f1f}, + {0x1f46, 0x1f47}, + {0x1f4e, 0x1f4f}, + {0x1f58, 0x1f58}, + {0x1f5a, 0x1f5a}, + {0x1f5c, 0x1f5c}, + {0x1f5e, 0x1f5e}, + {0x1f7e, 0x1f7f}, + {0x1fb5, 0x1fb5}, + {0x1fc5, 0x1fc5}, + {0x1fd4, 0x1fd5}, + {0x1fdc, 0x1fdc}, + {0x1ff0, 0x1ff1}, + {0x1ff5, 0x1ff5}, + {0x1fff, 0x1fff}, + {0x2065, 0x2065}, + {0x2072, 0x2073}, + {0x208f, 0x208f}, + {0x209d, 0x209f}, + {0x20bf, 0x20cf}, + {0x20f1, 0x20ff}, + {0x218c, 0x218f}, + {0x23ff, 0x23ff}, + {0x2427, 0x243f}, + {0x244b, 0x245f}, + {0x2b74, 0x2b75}, + {0x2b96, 0x2b97}, + {0x2bba, 0x2bbc}, + {0x2bc9, 0x2bc9}, + {0x2bd2, 0x2beb}, + {0x2bf0, 0x2bff}, + {0x2c2f, 0x2c2f}, + {0x2c5f, 0x2c5f}, + {0x2cf4, 0x2cf8}, + {0x2d26, 0x2d26}, + {0x2d28, 0x2d2c}, + {0x2d2e, 0x2d2f}, + {0x2d68, 0x2d6e}, + {0x2d71, 0x2d7e}, + {0x2d97, 0x2d9f}, + {0x2da7, 0x2da7}, + {0x2daf, 0x2daf}, + {0x2db7, 0x2db7}, + {0x2dbf, 0x2dbf}, + {0x2dc7, 0x2dc7}, + {0x2dcf, 0x2dcf}, + {0x2dd7, 0x2dd7}, + {0x2ddf, 0x2ddf}, + {0x2e45, 0x2e7f}, + {0x2e9a, 0x2e9a}, + {0x2ef4, 0x2eff}, + {0x2fd6, 0x2fef}, + {0x2ffc, 0x2fff}, + {0x3040, 0x3040}, + {0x3097, 0x3098}, + {0x3100, 0x3104}, + {0x312e, 0x3130}, + {0x318f, 0x318f}, + {0x31bb, 0x31bf}, + {0x31e4, 0x31ef}, + {0x321f, 0x321f}, + {0x32ff, 0x32ff}, + {0x4db6, 0x4dbf}, + {0x9fd6, 0x9fff}, + {0xa48d, 0xa48f}, + {0xa4c7, 0xa4cf}, + {0xa62c, 0xa63f}, + {0xa6f8, 0xa6ff}, + {0xa7af, 0xa7af}, + {0xa7b8, 0xa7f6}, + {0xa82c, 0xa82f}, + {0xa83a, 0xa83f}, + {0xa878, 0xa87f}, + {0xa8c6, 0xa8cd}, + {0xa8da, 0xa8df}, + {0xa8fe, 0xa8ff}, + {0xa954, 0xa95e}, + {0xa97d, 0xa97f}, + {0xa9ce, 0xa9ce}, + {0xa9da, 0xa9dd}, + {0xa9ff, 0xa9ff}, + {0xaa37, 0xaa3f}, + {0xaa4e, 0xaa4f}, + {0xaa5a, 0xaa5b}, + {0xaac3, 0xaada}, + {0xaaf7, 0xab00}, + {0xab07, 0xab08}, + {0xab0f, 0xab10}, + {0xab17, 0xab1f}, + {0xab27, 0xab27}, + {0xab2f, 0xab2f}, + {0xab66, 0xab6f}, + {0xabee, 0xabef}, + {0xabfa, 0xabff}, + {0xd7a4, 0xd7af}, + {0xd7c7, 0xd7ca}, + {0xd7fc, 0xd7ff}, + {0xfa6e, 0xfa6f}, + {0xfada, 0xfaff}, + {0xfb07, 0xfb12}, + {0xfb18, 0xfb1c}, + {0xfb37, 0xfb37}, + {0xfb3d, 0xfb3d}, + {0xfb3f, 0xfb3f}, + {0xfb42, 0xfb42}, + {0xfb45, 0xfb45}, + {0xfbc2, 0xfbd2}, + {0xfd40, 0xfd4f}, + {0xfd90, 0xfd91}, + {0xfdc8, 0xfdef}, + {0xfdfe, 0xfdff}, + {0xfe1a, 0xfe1f}, + {0xfe53, 0xfe53}, + {0xfe67, 0xfe67}, + {0xfe6c, 0xfe6f}, + {0xfe75, 0xfe75}, + {0xfefd, 0xfefe}, + {0xff00, 0xff00}, + {0xffbf, 0xffc1}, + {0xffc8, 0xffc9}, + {0xffd0, 0xffd1}, + {0xffd8, 0xffd9}, + {0xffdd, 0xffdf}, + {0xffe7, 0xffe7}, + {0xffef, 0xfff8}, + {0xfffe, 0xffff}, + {0x1000c, 0x1000c}, + {0x10027, 0x10027}, + {0x1003b, 0x1003b}, + {0x1003e, 0x1003e}, + {0x1004e, 0x1004f}, + {0x1005e, 0x1007f}, + {0x100fb, 0x100ff}, + {0x10103, 0x10106}, + {0x10134, 0x10136}, + {0x1018f, 0x1018f}, + {0x1019c, 0x1019f}, + {0x101a1, 0x101cf}, + {0x101fe, 0x1027f}, + {0x1029d, 0x1029f}, + {0x102d1, 0x102df}, + {0x102fc, 0x102ff}, + {0x10324, 0x1032f}, + {0x1034b, 0x1034f}, + {0x1037b, 0x1037f}, + {0x1039e, 0x1039e}, + {0x103c4, 0x103c7}, + {0x103d6, 0x103ff}, + {0x1049e, 0x1049f}, + {0x104aa, 0x104af}, + {0x104d4, 0x104d7}, + {0x104fc, 0x104ff}, + {0x10528, 0x1052f}, + {0x10564, 0x1056e}, + {0x10570, 0x105ff}, + {0x10737, 0x1073f}, + {0x10756, 0x1075f}, + {0x10768, 0x107ff}, + {0x10806, 0x10807}, + {0x10809, 0x10809}, + {0x10836, 0x10836}, + {0x10839, 0x1083b}, + {0x1083d, 0x1083e}, + {0x10856, 0x10856}, + {0x1089f, 0x108a6}, + {0x108b0, 0x108df}, + {0x108f3, 0x108f3}, + {0x108f6, 0x108fa}, + {0x1091c, 0x1091e}, + {0x1093a, 0x1093e}, + {0x10940, 0x1097f}, + {0x109b8, 0x109bb}, + {0x109d0, 0x109d1}, + {0x10a04, 0x10a04}, + {0x10a07, 0x10a0b}, + {0x10a14, 0x10a14}, + {0x10a18, 0x10a18}, + {0x10a34, 0x10a37}, + {0x10a3b, 0x10a3e}, + {0x10a48, 0x10a4f}, + {0x10a59, 0x10a5f}, + {0x10aa0, 0x10abf}, + {0x10ae7, 0x10aea}, + {0x10af7, 0x10aff}, + {0x10b36, 0x10b38}, + {0x10b56, 0x10b57}, + {0x10b73, 0x10b77}, + {0x10b92, 0x10b98}, + {0x10b9d, 0x10ba8}, + {0x10bb0, 0x10bff}, + {0x10c49, 0x10c7f}, + {0x10cb3, 0x10cbf}, + {0x10cf3, 0x10cf9}, + {0x10d00, 0x10e5f}, + {0x10e7f, 0x10fff}, + {0x1104e, 0x11051}, + {0x11070, 0x1107e}, + {0x110c2, 0x110cf}, + {0x110e9, 0x110ef}, + {0x110fa, 0x110ff}, + {0x11135, 0x11135}, + {0x11144, 0x1114f}, + {0x11177, 0x1117f}, + {0x111ce, 0x111cf}, + {0x111e0, 0x111e0}, + {0x111f5, 0x111ff}, + {0x11212, 0x11212}, + {0x1123f, 0x1127f}, + {0x11287, 0x11287}, + {0x11289, 0x11289}, + {0x1128e, 0x1128e}, + {0x1129e, 0x1129e}, + {0x112aa, 0x112af}, + {0x112eb, 0x112ef}, + {0x112fa, 0x112ff}, + {0x11304, 0x11304}, + {0x1130d, 0x1130e}, + {0x11311, 0x11312}, + {0x11329, 0x11329}, + {0x11331, 0x11331}, + {0x11334, 0x11334}, + {0x1133a, 0x1133b}, + {0x11345, 0x11346}, + {0x11349, 0x1134a}, + {0x1134e, 0x1134f}, + {0x11351, 0x11356}, + {0x11358, 0x1135c}, + {0x11364, 0x11365}, + {0x1136d, 0x1136f}, + {0x11375, 0x113ff}, + {0x1145a, 0x1145a}, + {0x1145c, 0x1145c}, + {0x1145e, 0x1147f}, + {0x114c8, 0x114cf}, + {0x114da, 0x1157f}, + {0x115b6, 0x115b7}, + {0x115de, 0x115ff}, + {0x11645, 0x1164f}, + {0x1165a, 0x1165f}, + {0x1166d, 0x1167f}, + {0x116b8, 0x116bf}, + {0x116ca, 0x116ff}, + {0x1171a, 0x1171c}, + {0x1172c, 0x1172f}, + {0x11740, 0x1189f}, + {0x118f3, 0x118fe}, + {0x11900, 0x11abf}, + {0x11af9, 0x11bff}, + {0x11c09, 0x11c09}, + {0x11c37, 0x11c37}, + {0x11c46, 0x11c4f}, + {0x11c6d, 0x11c6f}, + {0x11c90, 0x11c91}, + {0x11ca8, 0x11ca8}, + {0x11cb7, 0x11fff}, + {0x1239a, 0x123ff}, + {0x1246f, 0x1246f}, + {0x12475, 0x1247f}, + {0x12544, 0x12fff}, + {0x1342f, 0x143ff}, + {0x14647, 0x167ff}, + {0x16a39, 0x16a3f}, + {0x16a5f, 0x16a5f}, + {0x16a6a, 0x16a6d}, + {0x16a70, 0x16acf}, + {0x16aee, 0x16aef}, + {0x16af6, 0x16aff}, + {0x16b46, 0x16b4f}, + {0x16b5a, 0x16b5a}, + {0x16b62, 0x16b62}, + {0x16b78, 0x16b7c}, + {0x16b90, 0x16eff}, + {0x16f45, 0x16f4f}, + {0x16f7f, 0x16f8e}, + {0x16fa0, 0x16fdf}, + {0x16fe1, 0x16fff}, + {0x187ed, 0x187ff}, + {0x18af3, 0x1afff}, + {0x1b002, 0x1bbff}, + {0x1bc6b, 0x1bc6f}, + {0x1bc7d, 0x1bc7f}, + {0x1bc89, 0x1bc8f}, + {0x1bc9a, 0x1bc9b}, + {0x1bca4, 0x1cfff}, + {0x1d0f6, 0x1d0ff}, + {0x1d127, 0x1d128}, + {0x1d1e9, 0x1d1ff}, + {0x1d246, 0x1d2ff}, + {0x1d357, 0x1d35f}, + {0x1d372, 0x1d3ff}, + {0x1d455, 0x1d455}, + {0x1d49d, 0x1d49d}, + {0x1d4a0, 0x1d4a1}, + {0x1d4a3, 0x1d4a4}, + {0x1d4a7, 0x1d4a8}, + {0x1d4ad, 0x1d4ad}, + {0x1d4ba, 0x1d4ba}, + {0x1d4bc, 0x1d4bc}, + {0x1d4c4, 0x1d4c4}, + {0x1d506, 0x1d506}, + {0x1d50b, 0x1d50c}, + {0x1d515, 0x1d515}, + {0x1d51d, 0x1d51d}, + {0x1d53a, 0x1d53a}, + {0x1d53f, 0x1d53f}, + {0x1d545, 0x1d545}, + {0x1d547, 0x1d549}, + {0x1d551, 0x1d551}, + {0x1d6a6, 0x1d6a7}, + {0x1d7cc, 0x1d7cd}, + {0x1da8c, 0x1da9a}, + {0x1daa0, 0x1daa0}, + {0x1dab0, 0x1dfff}, + {0x1e007, 0x1e007}, + {0x1e019, 0x1e01a}, + {0x1e022, 0x1e022}, + {0x1e025, 0x1e025}, + {0x1e02b, 0x1e7ff}, + {0x1e8c5, 0x1e8c6}, + {0x1e8d7, 0x1e8ff}, + {0x1e94b, 0x1e94f}, + {0x1e95a, 0x1e95d}, + {0x1e960, 0x1edff}, + {0x1ee04, 0x1ee04}, + {0x1ee20, 0x1ee20}, + {0x1ee23, 0x1ee23}, + {0x1ee25, 0x1ee26}, + {0x1ee28, 0x1ee28}, + {0x1ee33, 0x1ee33}, + {0x1ee38, 0x1ee38}, + {0x1ee3a, 0x1ee3a}, + {0x1ee3c, 0x1ee41}, + {0x1ee43, 0x1ee46}, + {0x1ee48, 0x1ee48}, + {0x1ee4a, 0x1ee4a}, + {0x1ee4c, 0x1ee4c}, + {0x1ee50, 0x1ee50}, + {0x1ee53, 0x1ee53}, + {0x1ee55, 0x1ee56}, + {0x1ee58, 0x1ee58}, + {0x1ee5a, 0x1ee5a}, + {0x1ee5c, 0x1ee5c}, + {0x1ee5e, 0x1ee5e}, + {0x1ee60, 0x1ee60}, + {0x1ee63, 0x1ee63}, + {0x1ee65, 0x1ee66}, + {0x1ee6b, 0x1ee6b}, + {0x1ee73, 0x1ee73}, + {0x1ee78, 0x1ee78}, + {0x1ee7d, 0x1ee7d}, + {0x1ee7f, 0x1ee7f}, + {0x1ee8a, 0x1ee8a}, + {0x1ee9c, 0x1eea0}, + {0x1eea4, 0x1eea4}, + {0x1eeaa, 0x1eeaa}, + {0x1eebc, 0x1eeef}, + {0x1eef2, 0x1efff}, + {0x1f02c, 0x1f02f}, + {0x1f094, 0x1f09f}, + {0x1f0af, 0x1f0b0}, + {0x1f0c0, 0x1f0c0}, + {0x1f0d0, 0x1f0d0}, + {0x1f0f6, 0x1f0ff}, + {0x1f10d, 0x1f10f}, + {0x1f12f, 0x1f12f}, + {0x1f16c, 0x1f16f}, + {0x1f1ad, 0x1f1e5}, + {0x1f203, 0x1f20f}, + {0x1f23c, 0x1f23f}, + {0x1f249, 0x1f24f}, + {0x1f252, 0x1f2ff}, + {0x1f6d3, 0x1f6df}, + {0x1f6ed, 0x1f6ef}, + {0x1f6f7, 0x1f6ff}, + {0x1f774, 0x1f77f}, + {0x1f7d5, 0x1f7ff}, + {0x1f80c, 0x1f80f}, + {0x1f848, 0x1f84f}, + {0x1f85a, 0x1f85f}, + {0x1f888, 0x1f88f}, + {0x1f8ae, 0x1f90f}, + {0x1f91f, 0x1f91f}, + {0x1f928, 0x1f92f}, + {0x1f931, 0x1f932}, + {0x1f93f, 0x1f93f}, + {0x1f94c, 0x1f94f}, + {0x1f95f, 0x1f97f}, + {0x1f992, 0x1f9bf}, + {0x1f9c1, 0x1ffff}, + {0x2a6d7, 0x2a6ff}, + {0x2b735, 0x2b73f}, + {0x2b81e, 0x2b81f}, + {0x2cea2, 0x2f7ff}, + {0x2fa1e, 0xe0000}, + {0xe0002, 0xe001f}, + {0xe0080, 0xe00ff}, + {0xe01f0, 0xeffff}, + {0xffffe, 0xfffff}, +}; + +#define WCWIDTH9_ARRAY_SIZE(arr) ((sizeof(arr)/sizeof((arr)[0])) / ((size_t)(!(sizeof(arr) % sizeof((arr)[0]))))) + +static inline bool wcwidth9_intable(const struct wcwidth9_interval *table, size_t n_items, int c) { + int mid, bot, top; + + if (c < table[0].first) { + return false; + } + + bot = 0; + top = (int)(n_items - 1); + while (top >= bot) { + mid = (bot + top) / 2; + + if (table[mid].last < c) { + bot = mid + 1; + } else if (table[mid].first > c) { + top = mid - 1; + } else { + return true; + } + } + + return false; +} + +static inline int wcwidth9(int c) { + if (c == 0) { + return 0; + } + if (c < 0|| c > 0x10ffff) { + return -1; + } + + if (wcwidth9_intable(wcwidth9_nonprint, WCWIDTH9_ARRAY_SIZE(wcwidth9_nonprint), c)) { + return -1; + } + + if (wcwidth9_intable(wcwidth9_combining, WCWIDTH9_ARRAY_SIZE(wcwidth9_combining), c)) { + return 0; + } + + if (wcwidth9_intable(wcwidth9_not_assigned, WCWIDTH9_ARRAY_SIZE(wcwidth9_not_assigned), c)) { + return -1; + } + + if (wcwidth9_intable(wcwidth9_private, WCWIDTH9_ARRAY_SIZE(wcwidth9_private), c)) { + return -3; + } + + if (wcwidth9_intable(wcwidth9_ambiguous, WCWIDTH9_ARRAY_SIZE(wcwidth9_ambiguous), c)) { + return -2; + } + + if (wcwidth9_intable(wcwidth9_doublewidth, WCWIDTH9_ARRAY_SIZE(wcwidth9_doublewidth), c)) { + return 2; + } + + if (wcwidth9_intable(wcwidth9_emoji_width, WCWIDTH9_ARRAY_SIZE(wcwidth9_emoji_width), c)) { + return 2; + } + + return 1; +} + +#endif /* WCWIDTH9_H */ diff --git a/dotfiles/system/.zsh/modules/Src/zsh.h b/dotfiles/system/.zsh/modules/Src/zsh.h new file mode 100644 index 0000000..8e7f20b --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/zsh.h @@ -0,0 +1,3305 @@ +/* + * zsh.h - standard header file + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +/* A few typical macros */ +#define minimum(a,b) ((a) < (b) ? (a) : (b)) + +/* + * Our longest integer type: will be a 64 bit either if long already is, + * or if we found some alternative such as long long. + */ +#ifdef ZSH_64_BIT_TYPE +typedef ZSH_64_BIT_TYPE zlong; +#if defined(ZLONG_IS_LONG_LONG) && defined(LLONG_MAX) +#define ZLONG_MAX LLONG_MAX +#else +#ifdef ZLONG_IS_LONG_64 +#define ZLONG_MAX LONG_MAX +#else +/* umm... */ +#define ZLONG_MAX ((zlong)9223372036854775807) +#endif +#endif +#ifdef ZSH_64_BIT_UTYPE +typedef ZSH_64_BIT_UTYPE zulong; +#else +typedef unsigned zlong zulong; +#endif +#else +typedef long zlong; +typedef unsigned long zulong; +#define ZLONG_MAX LONG_MAX +#endif + +/* + * Work out how to define large integer constants that will fit + * in a zlong. + */ +#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT) +/* We have some 64-bit type */ +#ifdef LONG_IS_64_BIT +/* It's long */ +#define ZLONG_CONST(x) x ## l +#else +/* It's long long */ +#ifdef ZLONG_IS_LONG_LONG +#define ZLONG_CONST(x) x ## ll +#else +/* + * There's some 64-bit type, but we don't know what it is. + * We'll just cast it and hope the compiler does the right thing. + */ +#define ZLONG_CONST(x) ((zlong)x) +#endif +#endif +#else +/* We're stuck with long */ +#define ZLONG_CONST(x) (x ## l) +#endif + +/* + * Double float support requires 64-bit alignment, so if longs and + * pointers are less we need to pad out. + */ +#ifndef LONG_IS_64_BIT +# define PAD_64_BIT 1 +#endif + +/* math.c */ +typedef struct { + union { + zlong l; + double d; + } u; + int type; +} mnumber; + +#define MN_INTEGER 1 /* mnumber is integer */ +#define MN_FLOAT 2 /* mnumber is floating point */ +#define MN_UNSET 4 /* mnumber not yet retrieved */ + +typedef struct mathfunc *MathFunc; +typedef mnumber (*NumMathFunc)(char *, int, mnumber *, int); +typedef mnumber (*StrMathFunc)(char *, char *, int); + +struct mathfunc { + MathFunc next; + char *name; + int flags; /* MFF_* flags defined below */ + NumMathFunc nfunc; + StrMathFunc sfunc; + char *module; + int minargs; + int maxargs; + int funcid; +}; + +/* Math function takes a string argument */ +#define MFF_STR 1 +/* Math function has been loaded from library */ +#define MFF_ADDED 2 +/* Math function is implemented by a shell function */ +#define MFF_USERFUNC 4 +/* When autoloading, enable all features in module */ +#define MFF_AUTOALL 8 + + +#define NUMMATHFUNC(name, func, min, max, id) \ + { NULL, name, 0, func, NULL, NULL, min, max, id } +#define STRMATHFUNC(name, func, id) \ + { NULL, name, MFF_STR, NULL, func, NULL, 0, 0, id } + +/* Character tokens are sometimes casted to (unsigned char)'s. * + * Unfortunately, some compilers don't correctly cast signed to * + * unsigned promotions; i.e. (int)(unsigned char)((char) -1) evaluates * + * to -1, instead of 255 like it should. We circumvent the troubles * + * of such shameful delinquency by casting to a larger unsigned type * + * then back down to unsigned char. */ + +#ifdef BROKEN_SIGNED_TO_UNSIGNED_CASTING +# define STOUC(X) ((unsigned char)(unsigned short)(X)) +#else +# define STOUC(X) ((unsigned char)(X)) +#endif + +/* Meta together with the character following Meta denotes the character * + * which is the exclusive or of 32 and the character following Meta. * + * This is used to represent characters which otherwise has special * + * meaning for zsh. These are the characters for which the imeta() test * + * is true: the null character, and the characters from Meta to Marker. */ + +#define Meta ((char) 0x83) + +/* Note that the fourth character in DEFAULT_IFS is Meta * + * followed by a space which denotes the null character. */ + +#define DEFAULT_IFS " \t\n\203 " + +/* As specified in the standard (POSIX 2008) */ + +#define DEFAULT_IFS_SH " \t\n" + +/* + * Character tokens. + * These should match the characters in ztokens, defined in lex.c + */ +#define Pound ((char) 0x84) +#define String ((char) 0x85) +#define Hat ((char) 0x86) +#define Star ((char) 0x87) +#define Inpar ((char) 0x88) +#define Inparmath ((char) 0x89) +#define Outpar ((char) 0x8a) +#define Outparmath ((char) 0x8b) +#define Qstring ((char) 0x8c) +#define Equals ((char) 0x8d) +#define Bar ((char) 0x8e) +#define Inbrace ((char) 0x8f) +#define Outbrace ((char) 0x90) +#define Inbrack ((char) 0x91) +#define Outbrack ((char) 0x92) +#define Tick ((char) 0x93) +#define Inang ((char) 0x94) +#define Outang ((char) 0x95) +#define OutangProc ((char) 0x96) +#define Quest ((char) 0x97) +#define Tilde ((char) 0x98) +#define Qtick ((char) 0x99) +#define Comma ((char) 0x9a) +#define Dash ((char) 0x9b) /* Only in patterns */ +#define Bang ((char) 0x9c) /* Only in patterns */ +/* + * Marks the last of the group above. + * Remaining tokens are even more special. + */ +#define LAST_NORMAL_TOK Bang +/* + * Null arguments: placeholders for single and double quotes + * and backslashes. + */ +#define Snull ((char) 0x9d) +#define Dnull ((char) 0x9e) +#define Bnull ((char) 0x9f) +/* + * Backslash which will be returned to "\" instead of being stripped + * when we turn the string into a printable format. + */ +#define Bnullkeep ((char) 0xa0) +/* + * Null argument that does not correspond to any character. + * This should be last as it does not appear in ztokens and + * is used to initialise the IMETA type in inittyptab(). + */ +#define Nularg ((char) 0xa1) + +/* + * Take care to update the use of IMETA appropriately when adding + * tokens here. + */ +/* + * Marker is used in the following special circumstances: + * - In paramsubst for rc_expand_param. + * - In pattern character arrays as guaranteed not to mark a character in + * a string. + * - In assignments with the ASSPM_KEY_VALUE flag set in order to + * mark that there is a key / value pair following. If this + * comes from [key]=value the Marker is followed by a null; + * if from [key]+=value the Marker is followed by a '+' then a null. + * All the above are local uses --- any case where the Marker has + * escaped beyond the context in question is an error. + */ +#define Marker ((char) 0xa2) + +/* chars that need to be quoted if meant literally */ + +#define SPECCHARS "#$^*()=|{}[]`<>?~;&\n\t \\\'\"" + +/* chars that need to be quoted for pattern matching */ + +#define PATCHARS "#^*()|[]<>?~\\" + +/* + * Check for a possibly tokenized dash. + * + * A dash only needs to be a token in a character range, [a-z], but + * it's difficult in general to ensure that. So it's turned into + * a token at the usual point in the lexer. However, we need + * to check for a literal dash at many points. + */ +#define IS_DASH(x) ((x) == '-' || (x) == Dash) + +/* + * Types of quote. This is used in various places, so care needs + * to be taken when changing them. (Oooh, don't you look surprised.) + * - Passed to quotestring() to indicate style. This is the ultimate + * destiny of most of the other uses of members of the enum. + * - In paramsubst(), to count q's in parameter substitution. + * - In the completion code, where we maintain a stack of quotation types. + */ +enum { + /* + * No quote. Not a valid quote, but useful in the substitution + * and completion code to indicate we're not doing any quoting. + */ + QT_NONE, + /* Backslash: \ */ + QT_BACKSLASH, + /* Single quote: ' */ + QT_SINGLE, + /* Double quote: " */ + QT_DOUBLE, + /* Print-style quote: $' */ + QT_DOLLARS, + /* + * Backtick: ` + * Not understood by many parts of the code; here for a convenience + * in those cases where we need to represent a complete set. + */ + QT_BACKTICK, + /* + * Single quotes, but the default is not to quote unless necessary. + * This is only useful as an argument to quotestring(). + */ + QT_SINGLE_OPTIONAL, + /* + * Only quote pattern characters. + * ${(b)foo} guarantees that ${~foo} matches the string + * contained in foo. + */ + QT_BACKSLASH_PATTERN, + /* + * As QT_BACKSLASH, but a NULL string is shown as ''. + */ + QT_BACKSLASH_SHOWNULL, + /* + * Quoting as produced by quotedzputs(), used for human + * readability of parameter values. + */ + QT_QUOTEDZPUTS +}; + +#define QT_IS_SINGLE(x) ((x) == QT_SINGLE || (x) == QT_SINGLE_OPTIONAL) + +/* + * Lexical tokens: unlike the character tokens above, these never + * appear in strings and don't necessarily represent a single character. + */ + +enum lextok { + NULLTOK, /* 0 */ + SEPER, + NEWLIN, + SEMI, + DSEMI, + AMPER, /* 5 */ + INPAR, + OUTPAR, + DBAR, + DAMPER, + OUTANG, /* 10 */ + OUTANGBANG, + DOUTANG, + DOUTANGBANG, + INANG, + INOUTANG, /* 15 */ + DINANG, + DINANGDASH, + INANGAMP, + OUTANGAMP, + AMPOUTANG, /* 20 */ + OUTANGAMPBANG, + DOUTANGAMP, + DOUTANGAMPBANG, + TRINANG, + BAR, /* 25 */ + BARAMP, + INOUTPAR, + DINPAR, + DOUTPAR, + AMPERBANG, /* 30 */ + SEMIAMP, + SEMIBAR, + DOUTBRACK, + STRING, + ENVSTRING, /* 35 */ + ENVARRAY, + ENDINPUT, + LEXERR, + + /* Tokens for reserved words */ + BANG, /* ! */ + DINBRACK, /* [[ */ /* 40 */ + INBRACE, /* { */ + OUTBRACE, /* } */ + CASE, /* case */ + COPROC, /* coproc */ + DOLOOP, /* do */ /* 45 */ + DONE, /* done */ + ELIF, /* elif */ + ELSE, /* else */ + ZEND, /* end */ + ESAC, /* esac */ /* 50 */ + FI, /* fi */ + FOR, /* for */ + FOREACH, /* foreach */ + FUNC, /* function */ + IF, /* if */ /* 55 */ + NOCORRECT, /* nocorrect */ + REPEAT, /* repeat */ + SELECT, /* select */ + THEN, /* then */ + TIME, /* time */ /* 60 */ + UNTIL, /* until */ + WHILE, /* while */ + TYPESET /* typeset or similar */ +}; + +/* Redirection types. If you modify this, you may also have to modify * + * redirtab in parse.c and getredirs() in text.c and the IS_* macros * + * below. */ + +enum { + REDIR_WRITE, /* > */ + REDIR_WRITENOW, /* >| */ + REDIR_APP, /* >> */ + REDIR_APPNOW, /* >>| */ + REDIR_ERRWRITE, /* &>, >& */ + REDIR_ERRWRITENOW, /* >&| */ + REDIR_ERRAPP, /* >>& */ + REDIR_ERRAPPNOW, /* >>&| */ + REDIR_READWRITE, /* <> */ + REDIR_READ, /* < */ + REDIR_HEREDOC, /* << */ + REDIR_HEREDOCDASH, /* <<- */ + REDIR_HERESTR, /* <<< */ + REDIR_MERGEIN, /* <&n */ + REDIR_MERGEOUT, /* >&n */ + REDIR_CLOSE, /* >&-, <&- */ + REDIR_INPIPE, /* < <(...) */ + REDIR_OUTPIPE /* > >(...) */ +}; +#define REDIR_TYPE_MASK (0x1f) +/* Redir using {var} syntax */ +#define REDIR_VARID_MASK (0x20) +/* Mark here-string that came from a here-document */ +#define REDIR_FROM_HEREDOC_MASK (0x40) + +#define IS_WRITE_FILE(X) ((X)>=REDIR_WRITE && (X)<=REDIR_READWRITE) +#define IS_APPEND_REDIR(X) (IS_WRITE_FILE(X) && ((X) & 2)) +#define IS_CLOBBER_REDIR(X) (IS_WRITE_FILE(X) && ((X) & 1)) +#define IS_ERROR_REDIR(X) ((X)>=REDIR_ERRWRITE && (X)<=REDIR_ERRAPPNOW) +#define IS_READFD(X) (((X)>=REDIR_READWRITE && (X)<=REDIR_MERGEIN) || (X)==REDIR_INPIPE) +#define IS_REDIROP(X) ((X)>=OUTANG && (X)<=TRINANG) + +/* + * Values for the fdtable array. They say under what circumstances + * the fd will be close. The fdtable is an unsigned char, so these are + * #define's rather than an enum. + */ +/* Entry not used. */ +#define FDT_UNUSED 0 +/* + * Entry used internally by the shell, should not be visible to other + * processes. + */ +#define FDT_INTERNAL 1 +/* + * Entry visible to other processes, for example created using + * the {varid}> file syntax. + */ +#define FDT_EXTERNAL 2 +/* + * Entry visible to other processes but controlled by a module. + * The difference from FDT_EXTERNAL is that closing this using + * standard fd syntax will fail as there is some tidying up that + * needs to be done by the module's own mechanism. + */ +#define FDT_MODULE 3 +/* + * Entry used by output from the XTRACE option. + */ +#define FDT_XTRACE 4 +/* + * Entry used for file locking. + */ +#define FDT_FLOCK 5 +/* + * As above, but the fd is not marked for closing on exec, + * so the shell can still exec the last process. + */ +#define FDT_FLOCK_EXEC 6 +/* + * Entry used by a process substition. + * This marker is not tested internally as we associated the file + * descriptor with a job for closing. + * + * This is not used unless PATH_DEV_FD is defined. + */ +#define FDT_PROC_SUBST 7 +/* + * Mask to get the basic FDT type. + */ +#define FDT_TYPE_MASK 15 + +/* + * Bit flag that fd is saved for later restoration. + * Currently this is only use with FDT_INTERNAL. We use this fact so as + * not to have to mask checks against other types. + */ +#define FDT_SAVED_MASK 16 + +/* Flags for input stack */ +#define INP_FREE (1<<0) /* current buffer can be free'd */ +#define INP_ALIAS (1<<1) /* expanding alias or history */ +#define INP_HIST (1<<2) /* expanding history */ +#define INP_CONT (1<<3) /* continue onto previously stacked input */ +#define INP_ALCONT (1<<4) /* stack is continued from alias expn. */ +#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 +#define META_USEHEAP 1 +#define META_STATIC 2 +#define META_DUP 3 +#define META_ALLOC 4 +#define META_NOALLOC 5 +#define META_HEAPDUP 6 +#define META_HREALLOC 7 + +/* Context to save and restore (bit fields) */ +enum { + /* History mechanism */ + ZCONTEXT_HIST = (1<<0), + /* Lexical analyser */ + ZCONTEXT_LEX = (1<<1), + /* Parser */ + ZCONTEXT_PARSE = (1<<2) +}; + +/**************************/ +/* Abstract types for zsh */ +/**************************/ + +typedef struct alias *Alias; +typedef struct asgment *Asgment; +typedef struct builtin *Builtin; +typedef struct cmdnam *Cmdnam; +typedef struct complist *Complist; +typedef struct conddef *Conddef; +typedef struct dirsav *Dirsav; +typedef struct emulation_options *Emulation_options; +typedef struct execcmd_params *Execcmd_params; +typedef struct features *Features; +typedef struct feature_enables *Feature_enables; +typedef struct funcstack *Funcstack; +typedef struct funcwrap *FuncWrap; +typedef struct hashnode *HashNode; +typedef struct hashtable *HashTable; +typedef struct heap *Heap; +typedef struct heapstack *Heapstack; +typedef struct histent *Histent; +typedef struct hookdef *Hookdef; +typedef struct imatchdata *Imatchdata; +typedef struct jobfile *Jobfile; +typedef struct job *Job; +typedef struct linkedmod *Linkedmod; +typedef struct linknode *LinkNode; +typedef union linkroot *LinkList; +typedef struct module *Module; +typedef struct nameddir *Nameddir; +typedef struct options *Options; +typedef struct optname *Optname; +typedef struct param *Param; +typedef struct paramdef *Paramdef; +typedef struct patstralloc *Patstralloc; +typedef struct patprog *Patprog; +typedef struct prepromptfn *Prepromptfn; +typedef struct process *Process; +typedef struct redir *Redir; +typedef struct reswd *Reswd; +typedef struct shfunc *Shfunc; +typedef struct timedfn *Timedfn; +typedef struct value *Value; + +/********************************/ +/* Definitions for linked lists */ +/********************************/ + +/* linked list abstract data type */ + +struct linknode { + LinkNode next; + LinkNode prev; + void *dat; +}; + +struct linklist { + LinkNode first; + LinkNode last; + int flags; +}; + +union linkroot { + struct linklist list; + struct linknode node; +}; + +/* Macros for manipulating link lists */ + +#define firstnode(X) ((X)->list.first) +#define lastnode(X) ((X)->list.last) +#define peekfirst(X) (firstnode(X)->dat) +#define peeklast(X) (lastnode(X)->dat) +#define addlinknode(X,Y) insertlinknode(X,lastnode(X),Y) +#define zaddlinknode(X,Y) zinsertlinknode(X,lastnode(X),Y) +#define uaddlinknode(X,Y) uinsertlinknode(X,lastnode(X),Y) +#define empty(X) (firstnode(X) == NULL) +#define nonempty(X) (firstnode(X) != NULL) +#define getaddrdata(X) (&((X)->dat)) +#define getdata(X) ((X)->dat) +#define setdata(X,Y) ((X)->dat = (Y)) +#define nextnode(X) ((X)->next) +#define prevnode(X) ((X)->prev) +#define pushnode(X,Y) insertlinknode(X,&(X)->node,Y) +#define zpushnode(X,Y) zinsertlinknode(X,&(X)->node,Y) +#define incnode(X) (X = nextnode(X)) +#define decnode(X) (X = prevnode(X)) +#define firsthist() (hist_ring? hist_ring->down->histnum : curhist) +#define setsizednode(X,Y,Z) (firstnode(X)[(Y)].dat = (void *) (Z)) + +/* stack allocated linked lists */ + +#define local_list0(N) union linkroot N +#define init_list0(N) \ + do { \ + (N).list.first = NULL; \ + (N).list.last = &(N).node; \ + (N).list.flags = 0; \ + } while (0) +#define local_list1(N) union linkroot N; struct linknode __n0 +#define init_list1(N,V0) \ + do { \ + (N).list.first = &__n0; \ + (N).list.last = &__n0; \ + (N).list.flags = 0; \ + __n0.next = NULL; \ + __n0.prev = &(N).node; \ + __n0.dat = (void *) (V0); \ + } while (0) + +/*************************************/ +/* Specific elements of linked lists */ +/*************************************/ + +typedef void (*voidvoidfnptr_t) _((void)); + +/* + * Element of the prepromptfns list. + */ +struct prepromptfn { + voidvoidfnptr_t func; +}; + + +/* + * Element of the timedfns list. + */ +struct timedfn { + voidvoidfnptr_t func; + time_t when; +}; + +/********************************/ +/* Definitions for syntax trees */ +/********************************/ + +/* These are control flags that are passed * + * down the execution pipeline. */ +#define Z_TIMED (1<<0) /* pipeline is being timed */ +#define Z_SYNC (1<<1) /* run this sublist synchronously (;) */ +#define Z_ASYNC (1<<2) /* run this sublist asynchronously (&) */ +#define Z_DISOWN (1<<3) /* run this sublist without job control (&|) */ +/* (1<<4) is used for Z_END, see the wordcode definitions */ +/* (1<<5) is used for Z_SIMPLE, see the wordcode definitions */ + +/* + * Condition types. + * + * Careful when changing these: both cond_binary_ops in text.c and + * condstr in cond.c depend on these. (The zsh motto is "two instances + * are better than one". Or something.) + */ + +#define COND_NOT 0 +#define COND_AND 1 +#define COND_OR 2 +#define COND_STREQ 3 +#define COND_STRDEQ 4 +#define COND_STRNEQ 5 +#define COND_STRLT 6 +#define COND_STRGTR 7 +#define COND_NT 8 +#define COND_OT 9 +#define COND_EF 10 +#define COND_EQ 11 +#define COND_NE 12 +#define COND_LT 13 +#define COND_GT 14 +#define COND_LE 15 +#define COND_GE 16 +#define COND_REGEX 17 +#define COND_MOD 18 +#define COND_MODI 19 + +typedef int (*CondHandler) _((char **, int)); + +struct conddef { + Conddef next; /* next in list */ + char *name; /* the condition name */ + int flags; /* see CONDF_* below */ + CondHandler handler; /* handler function */ + int min; /* minimum number of strings */ + int max; /* maximum number of strings */ + int condid; /* for overloading handler functions */ + char *module; /* module to autoload */ +}; + +/* Condition is an infix */ +#define CONDF_INFIX 1 +/* Condition has been loaded from library */ +#define CONDF_ADDED 2 +/* When autoloading, enable all features in library */ +#define CONDF_AUTOALL 4 + +#define CONDDEF(name, flags, handler, min, max, condid) \ + { NULL, name, flags, handler, min, max, condid, NULL } + +/* Flags for redirections */ + +enum { + /* Mark a here-string that came from a here-document */ + REDIRF_FROM_HEREDOC = 1 +}; + +/* tree element for redirection lists */ + +struct redir { + int type; + int flags; + int fd1, fd2; + char *name; + char *varid; + char *here_terminator; + char *munged_here_terminator; +}; + +/* The number of fds space is allocated for * + * each time a multio must increase in size. */ +#define MULTIOUNIT 8 + +/* A multio is a list of fds associated with a certain fd. * + * Thus if you do "foo >bar >ble", the multio for fd 1 will have * + * two fds, the result of open("bar",...), and the result of * + * open("ble",....). */ + +/* structure used for multiple i/o redirection */ +/* one for each fd open */ + +struct multio { + int ct; /* # of redirections on this fd */ + int rflag; /* 0 if open for reading, 1 if open for writing */ + int pipe; /* fd of pipe if ct > 1 */ + int fds[MULTIOUNIT]; /* list of src/dests redirected to/from this fd */ +}; + +/* lvalue for variable assignment/expansion */ + +struct value { + int isarr; + Param pm; /* parameter node */ + int flags; /* flags defined below */ + int start; /* first element of array slice, or -1 */ + int end; /* 1-rel last element of array slice, or -1 */ + char **arr; /* cache for hash turned into array */ +}; + +enum { + VALFLAG_INV = 0x0001, /* We are performing inverse subscripting */ + VALFLAG_EMPTY = 0x0002, /* Subscripted range is empty */ + VALFLAG_SUBST = 0x0004 /* Substitution, so apply padding, case flags */ +}; + +#define MAX_ARRLEN 262144 + +/********************************************/ +/* Definitions for word code */ +/********************************************/ + +typedef unsigned int wordcode; +typedef wordcode *Wordcode; + +typedef struct funcdump *FuncDump; +typedef struct eprog *Eprog; + +struct funcdump { + FuncDump next; /* next in list */ + dev_t dev; /* device */ + ino_t ino; /* indoe number */ + int fd; /* file descriptor */ + Wordcode map; /* pointer to header */ + Wordcode addr; /* mapped region */ + int len; /* length */ + int count; /* reference count */ + char *filename; +}; + +/* + * A note on the use of reference counts in Eprogs. + * + * When an Eprog is created, nref is set to -1 if the Eprog is on the + * heap; then no attempt is ever made to free it. (This information is + * already present in EF_HEAP; we use the redundancy for debugging + * checks.) + * + * Otherwise, nref is initialised to 1. Calling freeprog() decrements + * nref and frees the Eprog if the count is now zero. When the Eprog + * is in use, we call useeprog() at the start and freeprog() at the + * end to increment and decrement the reference counts. If an attempt + * is made to free the Eprog from within, this will then take place + * when execution is finished, typically in the call to freeeprog() + * in execode(). If the Eprog was on the heap, neither useeprog() + * nor freeeprog() has any effect. + */ +struct eprog { + int flags; /* EF_* below */ + int len; /* total block length */ + int npats; /* Patprog cache size */ + int nref; /* number of references: delete when zero */ + Patprog *pats; /* the memory block, the patterns */ + Wordcode prog; /* memory block ctd, the code */ + char *strs; /* memory block ctd, the strings */ + Shfunc shf; /* shell function for autoload */ + FuncDump dump; /* dump file this is in */ +}; + +#define EF_REAL 1 +#define EF_HEAP 2 +#define EF_MAP 4 +#define EF_RUN 8 + +typedef struct estate *Estate; + +struct estate { + Eprog prog; /* the eprog executed */ + Wordcode pc; /* program counter, current pos */ + char *strs; /* strings from prog */ +}; + +typedef struct eccstr *Eccstr; + +struct eccstr { + Eccstr left, right; + char *str; + wordcode offs, aoffs; + int nfunc; + int hashval; +}; + +#define EC_NODUP 0 +#define EC_DUP 1 +#define EC_DUPTOK 2 + +#define WC_CODEBITS 5 + +#define wc_code(C) ((C) & ((wordcode) ((1 << WC_CODEBITS) - 1))) +#define wc_data(C) ((C) >> WC_CODEBITS) +#define wc_bdata(D) ((D) << WC_CODEBITS) +#define wc_bld(C,D) (((wordcode) (C)) | (((wordcode) (D)) << WC_CODEBITS)) + +#define WC_END 0 +#define WC_LIST 1 +#define WC_SUBLIST 2 +#define WC_PIPE 3 +#define WC_REDIR 4 +#define WC_ASSIGN 5 +#define WC_SIMPLE 6 +#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 22 + +#define WCB_END() wc_bld(WC_END, 0) + +#define WC_LIST_TYPE(C) wc_data(C) +#define Z_END (1<<4) +#define Z_SIMPLE (1<<5) +#define WC_LIST_FREE (6) /* Next bit available in integer */ +#define WC_LIST_SKIP(C) (wc_data(C) >> WC_LIST_FREE) +#define WCB_LIST(T,O) wc_bld(WC_LIST, ((T) | ((O) << WC_LIST_FREE))) + +#define WC_SUBLIST_TYPE(C) (wc_data(C) & ((wordcode) 3)) +#define WC_SUBLIST_END 0 +#define WC_SUBLIST_AND 1 +#define WC_SUBLIST_OR 2 +#define WC_SUBLIST_FLAGS(C) (wc_data(C) & ((wordcode) 0x1c)) +#define WC_SUBLIST_COPROC 4 +#define WC_SUBLIST_NOT 8 +#define WC_SUBLIST_SIMPLE 16 +#define WC_SUBLIST_FREE (5) /* Next bit available in integer */ +#define WC_SUBLIST_SKIP(C) (wc_data(C) >> WC_SUBLIST_FREE) +#define WCB_SUBLIST(T,F,O) wc_bld(WC_SUBLIST, \ + ((T) | (F) | ((O) << WC_SUBLIST_FREE))) + +#define WC_PIPE_TYPE(C) (wc_data(C) & ((wordcode) 1)) +#define WC_PIPE_END 0 +#define WC_PIPE_MID 1 +#define WC_PIPE_LINENO(C) (wc_data(C) >> 1) +#define WCB_PIPE(T,L) wc_bld(WC_PIPE, ((T) | ((L) << 1))) + +#define WC_REDIR_TYPE(C) ((int)(wc_data(C) & REDIR_TYPE_MASK)) +#define WC_REDIR_VARID(C) ((int)(wc_data(C) & REDIR_VARID_MASK)) +#define WC_REDIR_FROM_HEREDOC(C) ((int)(wc_data(C) & REDIR_FROM_HEREDOC_MASK)) +#define WCB_REDIR(T) wc_bld(WC_REDIR, (T)) +/* Size of redir is 4 words if REDIR_VARID_MASK is set, else 3 */ +#define WC_REDIR_WORDS(C) \ + ((WC_REDIR_VARID(C) ? 4 : 3) + \ + (WC_REDIR_FROM_HEREDOC(C) ? 2 : 0)) + +#define WC_ASSIGN_TYPE(C) (wc_data(C) & ((wordcode) 1)) +#define WC_ASSIGN_TYPE2(C) ((wc_data(C) & ((wordcode) 2)) >> 1) +#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))) + +#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)) + +#define WC_CURSH_SKIP(C) wc_data(C) +#define WCB_CURSH(O) wc_bld(WC_CURSH, (O)) + +#define WC_TIMED_TYPE(C) wc_data(C) +#define WC_TIMED_EMPTY 0 +#define WC_TIMED_PIPE 1 +#define WCB_TIMED(T) wc_bld(WC_TIMED, (T)) + +#define WC_FUNCDEF_SKIP(C) wc_data(C) +#define WCB_FUNCDEF(O) wc_bld(WC_FUNCDEF, (O)) + +#define WC_FOR_TYPE(C) (wc_data(C) & 3) +#define WC_FOR_PPARAM 0 +#define WC_FOR_LIST 1 +#define WC_FOR_COND 2 +#define WC_FOR_SKIP(C) (wc_data(C) >> 2) +#define WCB_FOR(T,O) wc_bld(WC_FOR, ((T) | ((O) << 2))) + +#define WC_SELECT_TYPE(C) (wc_data(C) & 1) +#define WC_SELECT_PPARAM 0 +#define WC_SELECT_LIST 1 +#define WC_SELECT_SKIP(C) (wc_data(C) >> 1) +#define WCB_SELECT(T,O) wc_bld(WC_SELECT, ((T) | ((O) << 1))) + +#define WC_WHILE_TYPE(C) (wc_data(C) & 1) +#define WC_WHILE_WHILE 0 +#define WC_WHILE_UNTIL 1 +#define WC_WHILE_SKIP(C) (wc_data(C) >> 1) +#define WCB_WHILE(T,O) wc_bld(WC_WHILE, ((T) | ((O) << 1))) + +#define WC_REPEAT_SKIP(C) wc_data(C) +#define WCB_REPEAT(O) wc_bld(WC_REPEAT, (O)) + +#define WC_TRY_SKIP(C) wc_data(C) +#define WCB_TRY(O) wc_bld(WC_TRY, (O)) + +#define WC_CASE_TYPE(C) (wc_data(C) & 7) +#define WC_CASE_HEAD 0 +#define WC_CASE_OR 1 +#define WC_CASE_AND 2 +#define WC_CASE_TESTAND 3 +#define WC_CASE_FREE (3) /* Next bit available in integer */ +#define WC_CASE_SKIP(C) (wc_data(C) >> WC_CASE_FREE) +#define WCB_CASE(T,O) wc_bld(WC_CASE, ((T) | ((O) << WC_CASE_FREE))) + +#define WC_IF_TYPE(C) (wc_data(C) & 3) +#define WC_IF_HEAD 0 +#define WC_IF_IF 1 +#define WC_IF_ELIF 2 +#define WC_IF_ELSE 3 +#define WC_IF_SKIP(C) (wc_data(C) >> 2) +#define WCB_IF(T,O) wc_bld(WC_IF, ((T) | ((O) << 2))) + +#define WC_COND_TYPE(C) (wc_data(C) & 127) +#define WC_COND_SKIP(C) (wc_data(C) >> 7) +#define WCB_COND(T,O) wc_bld(WC_COND, ((T) | ((O) << 7))) + +#define WCB_ARITH() wc_bld(WC_ARITH, 0) + +#define WCB_AUTOFN() wc_bld(WC_AUTOFN, 0) + +/********************************************/ +/* Definitions for job table and job control */ +/********************************************/ + +/* Entry in filelist linked list in job table */ + +struct jobfile { + /* Record to be deleted or closed */ + union { + char *name; /* Name of file to delete */ + int fd; /* File descriptor to close */ + } u; + /* Discriminant */ + int is_fd; +}; + +/* entry in the job table */ + +struct job { + pid_t gleader; /* process group leader of this job */ + pid_t other; /* subjob id (SUPERJOB) + * or subshell pid (SUBJOB) */ + int stat; /* see STATs below */ + char *pwd; /* current working dir of shell when * + * this job was spawned */ + struct process *procs; /* list of processes */ + struct process *auxprocs; /* auxiliary processes e.g multios */ + LinkList filelist; /* list of files to delete when done */ + /* elements are struct jobfile */ + int stty_in_env; /* if STTY=... is present */ + struct ttyinfo *ty; /* the modes specified by STTY */ +}; + +#define STAT_CHANGED (0x0001) /* status changed and not reported */ +#define STAT_STOPPED (0x0002) /* all procs stopped or exited */ +#define STAT_TIMED (0x0004) /* job is being timed */ +#define STAT_DONE (0x0008) /* job is done */ +#define STAT_LOCKED (0x0010) /* shell is finished creating this job, */ + /* may be deleted from job table */ +#define STAT_NOPRINT (0x0020) /* job was killed internally, */ + /* we don't want to show that */ +#define STAT_INUSE (0x0040) /* this job entry is in use */ +#define STAT_SUPERJOB (0x0080) /* job has a subjob */ +#define STAT_SUBJOB (0x0100) /* job is a subjob */ +#define STAT_WASSUPER (0x0200) /* was a super-job, sub-job needs to be */ + /* deleted */ +#define STAT_CURSH (0x0400) /* last command is in current shell */ +#define STAT_NOSTTY (0x0800) /* the tty settings are not inherited */ + /* from this job when it exits. */ +#define STAT_ATTACH (0x1000) /* delay reattaching shell to tty */ +#define STAT_SUBLEADER (0x2000) /* is super-job, but leader is sub-shell */ + +#define STAT_BUILTIN (0x4000) /* job at tail of pipeline is a builtin */ +#define STAT_SUBJOB_ORPHANED (0x8000) + /* STAT_SUBJOB with STAT_SUPERJOB exited */ +#define STAT_DISOWN (0x10000) /* STAT_SUPERJOB with disown pending */ + +#define SP_RUNNING -1 /* fake status for jobs currently running */ + +struct timeinfo { + long ut; /* user space time */ + long st; /* system space time */ +}; + +#define JOBTEXTSIZE 80 + +/* Size to initialise the job table to, and to increment it by when needed. */ +#define MAXJOBS_ALLOC (50) + +/* node in job process lists */ + +#ifdef HAVE_GETRUSAGE +typedef struct rusage child_times_t; +#else +typedef struct timeinfo child_times_t; +#endif + +struct process { + struct process *next; + pid_t pid; /* process id */ + char text[JOBTEXTSIZE]; /* text to print when 'jobs' is run */ + int status; /* return code from waitpid/wait3() */ + child_times_t ti; + struct timeval bgtime; /* time job was spawned */ + struct timeval endtime; /* time job exited */ +}; + +struct execstack { + struct execstack *next; + + pid_t list_pipe_pid; + int nowait; + int pline_level; + int list_pipe_child; + int list_pipe_job; + char list_pipe_text[JOBTEXTSIZE]; + int lastval; + int noeval; + int badcshglob; + pid_t cmdoutpid; + int cmdoutval; + int use_cmdoutval; + pid_t procsubstpid; + int trap_return; + int trap_state; + int trapisfunc; + int traplocallevel; + int noerrs; + int this_noerrexit; + char *underscore; +}; + +struct heredocs { + struct heredocs *next; + int type; + int pc; + char *str; +}; + +struct dirsav { + int dirfd, level; + char *dirname; + dev_t dev; + ino_t ino; +}; + +#define MAX_PIPESTATS 256 + +/*******************************/ +/* Definitions for Hash Tables */ +/*******************************/ + +typedef void *(*VFunc) _((void *)); +typedef void (*FreeFunc) _((void *)); + +typedef unsigned (*HashFunc) _((const char *)); +typedef void (*TableFunc) _((HashTable)); +/* + * Note that this is deliberately "char *", not "const char *", + * since the AddNodeFunc is passed a pointer to a string that + * will be stored and later freed. + */ +typedef void (*AddNodeFunc) _((HashTable, char *, void *)); +typedef HashNode (*GetNodeFunc) _((HashTable, const char *)); +typedef HashNode (*RemoveNodeFunc) _((HashTable, const char *)); +typedef void (*FreeNodeFunc) _((HashNode)); +typedef int (*CompareFunc) _((const char *, const char *)); + +/* type of function that is passed to * + * scanhashtable or scanmatchtable */ +typedef void (*ScanFunc) _((HashNode, int)); +typedef void (*ScanTabFunc) _((HashTable, ScanFunc, int)); + +typedef void (*PrintTableStats) _((HashTable)); + +/* hash table for standard open hashing */ + +struct hashtable { + /* HASHTABLE DATA */ + int hsize; /* size of nodes[] (number of hash values) */ + int ct; /* number of elements */ + HashNode *nodes; /* array of size hsize */ + void *tmpdata; + + /* HASHTABLE METHODS */ + HashFunc hash; /* pointer to hash function for this table */ + TableFunc emptytable; /* pointer to function to empty table */ + TableFunc filltable; /* pointer to function to fill table */ + CompareFunc cmpnodes; /* pointer to function to compare two nodes */ + AddNodeFunc addnode; /* pointer to function to add new node */ + GetNodeFunc getnode; /* pointer to function to get an enabled node */ + GetNodeFunc getnode2; /* pointer to function to get node */ + /* (getnode2 will ignore DISABLED flag) */ + RemoveNodeFunc removenode; /* pointer to function to delete a node */ + ScanFunc disablenode; /* pointer to function to disable a node */ + ScanFunc enablenode; /* pointer to function to enable a node */ + FreeNodeFunc freenode; /* pointer to function to free a node */ + ScanFunc printnode; /* pointer to function to print a node */ + ScanTabFunc scantab; /* pointer to function to scan table */ + +#ifdef HASHTABLE_INTERNAL_MEMBERS + HASHTABLE_INTERNAL_MEMBERS /* internal use in hashtable.c */ +#endif +}; + +/* generic hash table node */ + +struct hashnode { + HashNode next; /* next in hash chain */ + char *nam; /* hash key */ + int flags; /* various flags */ +}; + +/* The flag to disable nodes in a hash table. Currently * + * you can disable builtins, shell functions, aliases and * + * reserved words. */ +#define DISABLED (1<<0) + +/* node in shell option table */ + +struct optname { + struct hashnode node; + int optno; /* option number */ +}; + +/* node in shell reserved word hash table (reswdtab) */ + +struct reswd { + struct hashnode node; + int token; /* corresponding lexer token */ +}; + +/* node in alias hash table (aliastab) */ + +struct alias { + struct hashnode node; + char *text; /* expansion of alias */ + int inuse; /* alias is being expanded */ +}; + +/* bit 0 of flags is the DISABLED flag */ +/* is this alias global? */ +#define ALIAS_GLOBAL (1<<1) +/* 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 flags; + union { + char *scalar; + LinkList array; + } value; +}; + +/* Flags for flags element of asgment */ +enum { + /* Array value */ + ASG_ARRAY = 1, + /* Key / value array pair */ + ASG_KEY_VALUE = 2 +}; + +/* + * Assignment is array? + */ +#define ASG_ARRAYP(asg) ((asg)->flags & ASG_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 { + struct hashnode node; + union { + char **name; /* full pathname for external commands */ + char *cmd; /* file name for hashed commands */ + } + u; +}; + +/* flag for nodes explicitly added to * + * cmdnamtab with hash builtin */ +#define HASHED (1<<1) + +/* node in shell function hash table (shfunctab) */ + +struct shfunc { + struct hashnode node; + char *filename; /* Name of file located in. + For not yet autoloaded file, name + of explicit directory, if not NULL. */ + zlong lineno; /* line number in above file */ + Eprog funcdef; /* function definition */ + Eprog redir; /* redirections to apply */ + Emulation_options sticky; /* sticky emulation definitions, if any */ +}; + +/* Shell function context types. */ + +#define SFC_NONE 0 /* no function running */ +#define SFC_DIRECT 1 /* called directly from the user */ +#define SFC_SIGNAL 2 /* signal handler */ +#define SFC_HOOK 3 /* one of the special functions */ +#define SFC_WIDGET 4 /* user defined widget */ +#define SFC_COMPLETE 5 /* called from completion code */ +#define SFC_CWIDGET 6 /* new style completion widget */ +#define SFC_SUBST 7 /* used to perform substitution task */ + +/* tp in funcstack */ + +enum { + FS_SOURCE, + FS_FUNC, + FS_EVAL +}; + +/* node in function stack */ + +struct funcstack { + Funcstack prev; /* previous in stack */ + char *name; /* name of function/sourced file called */ + char *filename; /* file function resides in */ + char *caller; /* name of caller */ + zlong flineno; /* line number in file */ + zlong lineno; /* line offset from beginning of function */ + int tp; /* type of entry: sourced file, func, eval */ +}; + +/* node in list of function call wrappers */ + +typedef int (*WrapFunc) _((Eprog, FuncWrap, char *)); + +struct funcwrap { + FuncWrap next; + int flags; + WrapFunc handler; + Module module; +}; + +#define WRAPF_ADDED 1 + +#define WRAPDEF(func) \ + { NULL, 0, func, NULL } + +/* + * User-defined hook arrays + */ + +/* Name appended to function name to get hook array */ +#define HOOK_SUFFIX "_functions" +/* Length of that including NUL byte */ +#define HOOK_SUFFIX_LEN 11 + +/* node in builtin command hash table (builtintab) */ + +/* + * Handling of options. + * + * Option strings are standard in that a trailing `:' indicates + * a mandatory argument. In addition, `::' indicates an optional + * argument which must immediately follow the option letter if it is present. + * `:%' indicates an optional numeric argument which may follow + * the option letter or be in the next word; the only test is + * that the next character is a digit, and no actual conversion is done. + */ + +#define MAX_OPS 128 + +/* Macros taking struct option * and char argument */ +/* Option was set as -X */ +#define OPT_MINUS(ops,c) ((ops)->ind[c] & 1) +/* Option was set as +X */ +#define OPT_PLUS(ops,c) ((ops)->ind[c] & 2) +/* + * Option was set any old how, maybe including an argument + * (cheap test when we don't care). Some bits of code + * expect this to be 1 or 0. + */ +#define OPT_ISSET(ops,c) ((ops)->ind[c] != 0) +/* Option has an argument */ +#define OPT_HASARG(ops,c) ((ops)->ind[c] > 3) +/* The argument for the option; not safe if it doesn't have one */ +#define OPT_ARG(ops,c) ((ops)->args[((ops)->ind[c] >> 2) - 1]) +/* Ditto, but safely returns NULL if there is no argument. */ +#define OPT_ARG_SAFE(ops,c) (OPT_HASARG(ops,c) ? OPT_ARG(ops,c) : NULL) + +struct options { + unsigned char ind[MAX_OPS]; + char **args; + int argscount, argsalloc; +}; + +/* Flags to parseargs() */ + +enum { + PARSEARGS_TOPLEVEL = 0x1, /* Call to initialise shell */ + PARSEARGS_LOGIN = 0x2 /* Shell is login shell */ +}; + + +/* + * Handler arguments are: builtin name, null-terminated argument + * list excluding command name, option structure, the funcid element from the + * builtin structure. + */ + +typedef int (*HandlerFunc) _((char *, char **, Options, int)); +typedef int (*HandlerFuncAssign) _((char *, char **, LinkList, Options, int)); +#define NULLBINCMD ((HandlerFunc) 0) + +struct builtin { + struct hashnode node; + HandlerFunc handlerfunc; /* pointer to function that executes this builtin */ + int minargs; /* minimum number of arguments */ + int maxargs; /* maximum number of arguments, or -1 for no limit */ + int funcid; /* xbins (see above) for overloaded handlerfuncs */ + char *optstr; /* string of legal options */ + char *defopts; /* options set by default for overloaded handlerfuncs */ +}; + +#define BUILTIN(name, flags, handler, min, max, funcid, optstr, defopts) \ + { { NULL, name, flags }, handler, min, max, funcid, optstr, defopts } +#define BIN_PREFIX(name, flags) \ + BUILTIN(name, flags | BINF_PREFIX, NULLBINCMD, 0, 0, 0, NULL, NULL) + +/* builtin flags */ +/* DISABLE IS DEFINED AS (1<<0) */ +#define BINF_PLUSOPTS (1<<1) /* +xyz legal */ +#define BINF_PRINTOPTS (1<<2) +#define BINF_ADDED (1<<3) /* is in the builtins hash table */ +#define BINF_MAGICEQUALS (1<<4) /* needs automatic MAGIC_EQUAL_SUBST substitution */ +#define BINF_PREFIX (1<<5) +#define BINF_DASH (1<<6) +#define BINF_BUILTIN (1<<7) +#define BINF_COMMAND (1<<8) +#define BINF_EXEC (1<<9) +#define BINF_NOGLOB (1<<10) +#define BINF_PSPECIAL (1<<11) +/* Builtin option handling */ +#define BINF_SKIPINVALID (1<<12) /* Treat invalid option as argument */ +#define BINF_KEEPNUM (1<<13) /* `[-+]NUM' can be an option */ +#define BINF_SKIPDASH (1<<14) /* Treat `-' as argument (maybe `+') */ +#define BINF_DASHDASHVALID (1<<15) /* Handle `--' even if SKIPINVALD */ +#define BINF_CLEARENV (1<<16) /* new process started with cleared env */ +#define BINF_AUTOALL (1<<17) /* autoload all features at once */ + /* + * Handles options itself. This is only useful if the option string for a + * builtin with an empty option string. It is used to indicate that "--" + * 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) + +/** + * Parameters passed to execcmd(). + * These are not opaque --- they are also used by the pipeline manager. + */ +struct execcmd_params { + LinkList args; /* All command prefixes, arguments & options */ + LinkList redir; /* Redirections */ + Wordcode beg; /* The code at the start of the command */ + Wordcode varspc; /* The code for assignment parsed as such */ + Wordcode assignspc; /* The code for assignment parsed as typeset */ + int type; /* The WC_* type of the command */ + int postassigns; /* The number of assignspc assiguments */ + int htok; /* tokens in parameter list */ +}; + +struct module { + struct hashnode node; + union { + void *handle; + Linkedmod linked; + char *alias; + } u; + LinkList autoloads; + LinkList deps; + int wrapper; +}; + +/* We are in the process of loading the module */ +#define MOD_BUSY (1<<0) +/* + * We are in the process of unloading the module. + * Note this is not needed to indicate a module is actually + * unloaded: for that, the handle (or linked pointer) is set to NULL. + */ +#define MOD_UNLOAD (1<<1) +/* We are in the process of setting up the module */ +#define MOD_SETUP (1<<2) +/* Module is statically linked into the main binary */ +#define MOD_LINKED (1<<3) +/* Module setup has been carried out (and module has not been finished) */ +#define MOD_INIT_S (1<<4) +/* Module boot has been carried out (and module has not been finished) */ +#define MOD_INIT_B (1<<5) +/* Module record is an alias */ +#define MOD_ALIAS (1<<6) + +typedef int (*Module_generic_func) _((void)); +typedef int (*Module_void_func) _((Module)); +typedef int (*Module_features_func) _((Module, char ***)); +typedef int (*Module_enables_func) _((Module, int **)); + +struct linkedmod { + char *name; + Module_void_func setup; + Module_features_func features; + Module_enables_func enables; + Module_void_func boot; + Module_void_func cleanup; + Module_void_func finish; +}; + +/* + * Structure combining all the concrete features available in + * a module and with space for information about abstract features. + */ +struct features { + /* List of builtins provided by the module and the size thereof */ + Builtin bn_list; + int bn_size; + /* List of conditions provided by the module and the size thereof */ + Conddef cd_list; + int cd_size; + /* List of math functions provided by the module and the size thereof */ + MathFunc mf_list; + int mf_size; + /* List of parameters provided by the module and the size thereof */ + Paramdef pd_list; + int pd_size; + /* Number of abstract features */ + int n_abstract; +}; + +/* + * Structure describing enables for one feature. + */ +struct feature_enables { + /* String feature to enable (N.B. no leading +/- allowed) */ + char *str; + /* Optional compiled pattern for str sans +/-, NULL for string match */ + Patprog pat; +}; + +/* C-function hooks */ + +typedef int (*Hookfn) _((Hookdef, void *)); + +struct hookdef { + Hookdef next; + char *name; + Hookfn def; + int flags; + LinkList funcs; +}; + +#define HOOKF_ALL 1 + +#define HOOKDEF(name, func, flags) { NULL, name, (Hookfn) func, flags, NULL } + +/* + * Types used in pattern matching. Most of these longs could probably + * happily be ints. + */ + +struct patprog { + long startoff; /* length before start of programme */ + long size; /* total size from start of struct */ + long mustoff; /* offset to string that must be present */ + long patmlen; /* length of pure string or longest match */ + int globflags; /* globbing flags to set at start */ + int globend; /* globbing flags set after finish */ + int flags; /* PAT_* flags */ + int patnpar; /* number of active parentheses */ + char patstartch; +}; + +struct patstralloc { + int unmetalen; /* Unmetafied length of trial string */ + int unmetalenp; /* Unmetafied length of path prefix. + If 0, no path prefix. */ + char *alloced; /* Allocated string, may be NULL */ + char *progstrunmeta; /* Unmetafied pure string in pattern, cached */ + int progstrunmetalen; /* Length of the foregoing */ +}; + +/* Flags used in pattern matchers (Patprog) and passed down to patcompile */ + +#define PAT_HEAPDUP 0x0000 /* Dummy flag for default behavior */ +#define PAT_FILE 0x0001 /* Pattern is a file name */ +#define PAT_FILET 0x0002 /* Pattern is top level file, affects ~ */ +#define PAT_ANY 0x0004 /* Match anything (cheap "*") */ +#define PAT_NOANCH 0x0008 /* Not anchored at end */ +#define PAT_NOGLD 0x0010 /* Don't glob dots */ +#define PAT_PURES 0x0020 /* Pattern is a pure string: set internally */ +#define PAT_STATIC 0x0040 /* Don't copy pattern to heap as per default */ +#define PAT_SCAN 0x0080 /* Scanning, so don't try must-match test */ +#define PAT_ZDUP 0x0100 /* Copy pattern in real memory */ +#define PAT_NOTSTART 0x0200 /* Start of string is not real start */ +#define PAT_NOTEND 0x0400 /* End of string is not real end */ +#define PAT_HAS_EXCLUDP 0x0800 /* (internal): top-level path1~path2. */ +#define PAT_LCMATCHUC 0x1000 /* equivalent to setting (#l) */ + +/** + * Indexes into the array of active pattern characters. + * This must match the array zpc_chars in pattern.c. + */ +enum zpc_chars { + /* + * These characters both terminate a pattern segment and + * a pure string segment. + */ + ZPC_SLASH, /* / active as file separator */ + ZPC_NULL, /* \0 as string terminator */ + ZPC_BAR, /* | for "or" */ + ZPC_OUTPAR, /* ) for grouping */ + ZPC_TILDE, /* ~ for exclusion (extended glob) */ + ZPC_SEG_COUNT, /* No. of the above characters */ + /* + * These characters terminate a pure string segment. + */ + ZPC_INPAR = ZPC_SEG_COUNT, /* ( for grouping */ + ZPC_QUEST, /* ? as wildcard */ + ZPC_STAR, /* * as wildcard */ + ZPC_INBRACK, /* [ for character class */ + ZPC_INANG, /* < for numeric glob */ + ZPC_HAT, /* ^ for exclusion (extended glob) */ + ZPC_HASH, /* # for repetition (extended glob) */ + ZPC_BNULLKEEP, /* Special backslashed null not removed */ + /* + * These characters are only valid before a parenthesis + */ + ZPC_NO_KSH_GLOB, + ZPC_KSH_QUEST = ZPC_NO_KSH_GLOB, /* ? for ?(...) in KSH_GLOB */ + ZPC_KSH_STAR, /* * for *(...) in KSH_GLOB */ + ZPC_KSH_PLUS, /* + for +(...) in KSH_GLOB */ + ZPC_KSH_BANG, /* ! for !(...) in KSH_GLOB */ + ZPC_KSH_BANG2, /* ! for !(...) in KSH_GLOB, untokenised */ + ZPC_KSH_AT, /* @ for @(...) in KSH_GLOB */ + ZPC_COUNT /* Number of special chararacters */ +}; + +/* + * Structure to save disables special characters for function scope. + */ +struct zpc_disables_save { + struct zpc_disables_save *next; + /* + * Bit vector of ZPC_COUNT disabled characters. + * We'll live dangerously and assume ZPC_COUNT is no greater + * than the number of bits in an unsigned int. + */ + unsigned int disables; +}; + +typedef struct zpc_disables_save *Zpc_disables_save; + +/* + * Special match types used in character classes. These + * are represented as tokens, with Meta added. The character + * class is represented as a metafied string, with only these + * tokens special. Note that an active leading "!" or "^" for + * negation is not part of the string but is flagged in the + * surrounding context. + * + * These types are also used in character and equivalence classes + * in completion matching. + * + * This must be kept ordered by the array colon_stuffs in pattern.c. + */ +/* Special value for first definition */ +#define PP_FIRST 1 +/* POSIX-defined types: [:alpha:] etc. */ +#define PP_ALPHA 1 +#define PP_ALNUM 2 +#define PP_ASCII 3 +#define PP_BLANK 4 +#define PP_CNTRL 5 +#define PP_DIGIT 6 +#define PP_GRAPH 7 +#define PP_LOWER 8 +#define PP_PRINT 9 +#define PP_PUNCT 10 +#define PP_SPACE 11 +#define PP_UPPER 12 +#define PP_XDIGIT 13 +/* Zsh additions: [:IDENT:] etc. */ +#define PP_IDENT 14 +#define PP_IFS 15 +#define PP_IFSSPACE 16 +#define PP_WORD 17 +#define PP_INCOMPLETE 18 +#define PP_INVALID 19 +/* Special value for last definition */ +#define PP_LAST 19 + +/* Unknown type. Not used in a valid token. */ +#define PP_UNKWN 20 +/* Range: token followed by the (possibly multibyte) start and end */ +#define PP_RANGE 21 + +/* + * Argument to get_match_ret() in glob.c + */ +struct imatchdata { + /* Metafied trial string */ + char *mstr; + /* Its length */ + int mlen; + /* Unmetafied string */ + char *ustr; + /* Its length */ + int ulen; + /* Flags (SUB_*) */ + int flags; + /* Replacement string (metafied) */ + char *replstr; + /* + * List of bits of matches to concatenate with replacement string. + * The data is a struct repldata. It is not used in cases like + * ${...//#foo/bar} even though SUB_GLOBAL is set, since the match + * is anchored. It goes on the heap. + */ + LinkList repllist; +}; + +/* Globbing flags: lower 8 bits gives approx count */ +#define GF_LCMATCHUC 0x0100 +#define GF_IGNCASE 0x0200 +#define GF_BACKREF 0x0400 +#define GF_MATCHREF 0x0800 +#define GF_MULTIBYTE 0x1000 /* Use multibyte if supported by build */ + +enum { + /* Valid multibyte character from charref */ + ZMB_VALID, + /* Incomplete multibyte character from charref */ + ZMB_INCOMPLETE, + /* Invalid multibyte character charref */ + ZMB_INVALID +}; + +/* Dummy Patprog pointers. Used mainly in executable code, but the + * pattern code needs to know about it, too. */ + +#define dummy_patprog1 ((Patprog) 1) +#define dummy_patprog2 ((Patprog) 2) + +/* standard node types for get/set/unset union in parameter */ + +/* + * note non-standard const in pointer declaration: structures are + * assumed to be read-only. + */ +typedef const struct gsu_scalar *GsuScalar; +typedef const struct gsu_integer *GsuInteger; +typedef const struct gsu_float *GsuFloat; +typedef const struct gsu_array *GsuArray; +typedef const struct gsu_hash *GsuHash; + +struct gsu_scalar { + char *(*getfn) _((Param)); + void (*setfn) _((Param, char *)); + void (*unsetfn) _((Param, int)); +}; + +struct gsu_integer { + zlong (*getfn) _((Param)); + void (*setfn) _((Param, zlong)); + void (*unsetfn) _((Param, int)); +}; + +struct gsu_float { + double (*getfn) _((Param)); + void (*setfn) _((Param, double)); + void (*unsetfn) _((Param, int)); +}; + +struct gsu_array { + char **(*getfn) _((Param)); + void (*setfn) _((Param, char **)); + void (*unsetfn) _((Param, int)); +}; + +struct gsu_hash { + HashTable (*getfn) _((Param)); + void (*setfn) _((Param, HashTable)); + void (*unsetfn) _((Param, int)); +}; + + +/* node used in parameter hash table (paramtab) */ + +struct param { + struct hashnode node; + + /* the value of this parameter */ + union { + void *data; /* used by special parameter functions */ + char **arr; /* value if declared array (PM_ARRAY) */ + char *str; /* value if declared string (PM_SCALAR) */ + zlong val; /* value if declared integer (PM_INTEGER) */ + zlong *valptr; /* value if special pointer to integer */ + double dval; /* value if declared float + (PM_EFLOAT|PM_FFLOAT) */ + HashTable hash; /* value if declared assoc (PM_HASHED) */ + } u; + + /* + * get/set/unset methods. + * + * Unlike the data union, this points to a single instance + * for every type (although there are special types, e.g. + * tied arrays have a different gsu_scalar struct from the + * normal one). It's really a poor man's vtable. + */ + union { + GsuScalar s; + GsuInteger i; + GsuFloat f; + GsuArray a; + GsuHash h; + } gsu; + + int base; /* output base or floating point prec */ + int width; /* field width */ + char *env; /* location in environment, if exported */ + char *ename; /* name of corresponding environment var */ + Param old; /* old struct for use with local */ + int level; /* if (old != NULL), level of localness */ +}; + +/* structure stored in struct param's u.data by tied arrays */ +struct tieddata { + char ***arrptr; /* pointer to corresponding array */ + int joinchar; /* character used to join arrays */ +}; + +/* flags for parameters */ + +/* parameter types */ +#define PM_SCALAR 0 /* scalar */ +#define PM_ARRAY (1<<0) /* array */ +#define PM_INTEGER (1<<1) /* integer */ +#define PM_EFLOAT (1<<2) /* double with %e output */ +#define PM_FFLOAT (1<<3) /* double with %f output */ +#define PM_HASHED (1<<4) /* association */ + +#define PM_TYPE(X) \ + (X & (PM_SCALAR|PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_ARRAY|PM_HASHED)) + +#define PM_LEFT (1<<5) /* left justify, remove leading blanks */ +#define PM_RIGHT_B (1<<6) /* right justify, fill with leading blanks */ +#define PM_RIGHT_Z (1<<7) /* right justify, fill with leading zeros */ +#define PM_LOWER (1<<8) /* all lower case */ + +/* The following are the same since they * + * both represent -u option to typeset */ +#define PM_UPPER (1<<9) /* all upper case */ +#define PM_UNDEFINED (1<<9) /* undefined (autoloaded) shell function */ + +#define PM_READONLY (1<<10) /* readonly */ +#define PM_TAGGED (1<<11) /* tagged */ +#define PM_EXPORTED (1<<12) /* exported */ +#define PM_ABSPATH_USED (1<<12) /* (function): loaded using absolute path */ + +/* The following are the same since they * + * both represent -U option to typeset */ +#define PM_UNIQUE (1<<13) /* remove duplicates */ +#define PM_UNALIASED (1<<13) /* do not expand aliases when autoloading */ + +#define PM_HIDE (1<<14) /* Special behaviour hidden by local */ +#define PM_CUR_FPATH (1<<14) /* (function): can use $fpath with filename */ +#define PM_HIDEVAL (1<<15) /* Value not shown in `typeset' commands */ +#define PM_WARNNESTED (1<<15) /* (function): non-recursive WARNNESTEDVAR */ +#define PM_TIED (1<<16) /* array tied to colon-path or v.v. */ +#define PM_TAGGED_LOCAL (1<<16) /* (function): non-recursive PM_TAGGED */ + +#define PM_KSHSTORED (1<<17) /* function stored in ksh form */ +#define PM_ZSHSTORED (1<<18) /* function stored in zsh form */ + +/* Remaining flags do not correspond directly to command line arguments */ +#define PM_DONTIMPORT_SUID (1<<19) /* do not import if running setuid */ +#define PM_LOADDIR (1<<19) /* (function) filename gives load directory */ +#define PM_SINGLE (1<<20) /* special can only have a single instance */ +#define PM_ANONYMOUS (1<<20) /* (function) anonymous function */ +#define PM_LOCAL (1<<21) /* this parameter will be made local */ +#define PM_SPECIAL (1<<22) /* special builtin parameter */ +#define PM_DONTIMPORT (1<<23) /* do not import this variable */ +#define PM_RESTRICTED (1<<24) /* cannot be changed in restricted mode */ +#define PM_UNSET (1<<25) /* has null value */ +#define PM_REMOVABLE (1<<26) /* special can be removed from paramtab */ +#define PM_AUTOLOAD (1<<27) /* autoloaded from module */ +#define PM_NORESTORE (1<<28) /* do not restore value of local special */ +#define PM_AUTOALL (1<<28) /* autoload all features in module + * when loading: valid only if PM_AUTOLOAD + * is also present. + */ +#define PM_HASHELEM (1<<29) /* is a hash-element */ +#define PM_NAMEDDIR (1<<30) /* has a corresponding nameddirtab entry */ + +/* The option string corresponds to the first of the variables above */ +#define TYPESET_OPTSTR "aiEFALRZlurtxUhHTkz" + +/* These typeset options take an optional numeric argument */ +#define TYPESET_OPTNUM "LRZiEF" + +/* Flags for extracting elements of arrays and associative arrays */ +#define SCANPM_WANTVALS (1<<0) /* Return value includes hash values */ +#define SCANPM_WANTKEYS (1<<1) /* Return value includes hash keys */ +#define SCANPM_WANTINDEX (1<<2) /* Return value includes array index */ +#define SCANPM_MATCHKEY (1<<3) /* Subscript matched against key */ +#define SCANPM_MATCHVAL (1<<4) /* Subscript matched against value */ +#define SCANPM_MATCHMANY (1<<5) /* Subscript matched repeatedly, return all */ +#define SCANPM_ASSIGNING (1<<6) /* Assigning whole array/hash */ +#define SCANPM_KEYMATCH (1<<7) /* keys of hash treated as patterns */ +#define SCANPM_DQUOTED (1<<8) /* substitution was double-quoted + * (only used for testing early end of + * subscript) + */ +#define SCANPM_ARRONLY (1<<9) /* value is array but we don't + * necessarily want to match multiple + * elements + */ +#define SCANPM_CHECKING (1<<10) /* Check if set, no need to create */ +/* "$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. + * ${...#...} and friends. This could be an enum, but so + * could a lot of other things. + */ + +#define SUB_END 0x0001 /* match end instead of beginning, % or %% */ +#define SUB_LONG 0x0002 /* % or # doubled, get longest match */ +#define SUB_SUBSTR 0x0004 /* match a substring */ +#define SUB_MATCH 0x0008 /* include the matched portion */ +#define SUB_REST 0x0010 /* include the unmatched portion */ +#define SUB_BIND 0x0020 /* index of beginning of string */ +#define SUB_EIND 0x0040 /* index of end of string */ +#define SUB_LEN 0x0080 /* length of match */ +#define SUB_ALL 0x0100 /* match complete string */ +#define SUB_GLOBAL 0x0200 /* global substitution ${..//all/these} */ +#define SUB_DOSUBST 0x0400 /* replacement string needs substituting */ +#define SUB_RETFAIL 0x0800 /* return status 0 if no match */ +#define SUB_START 0x1000 /* force match at start with SUB_END + * and no SUB_SUBSTR */ +#define SUB_LIST 0x2000 /* no substitution, return list of matches */ + +/* + * Structure recording multiple matches inside a test string. + * b and e are the beginning and end of the match. + * replstr is the replacement string, if any. + */ +struct repldata { + int b, e; /* beginning and end of chunk to replace */ + char *replstr; /* replacement string to use */ +}; +typedef struct repldata *Repldata; + +/* + * Flags to zshtokenize. + */ +enum { + /* Do glob substitution */ + ZSHTOK_SUBST = 0x0001, + /* Use sh-style globbing */ + ZSHTOK_SHGLOB = 0x0002 +}; + +/* Flags as the second argument to prefork */ +enum { + /* argument handled like typeset foo=bar */ + PREFORK_TYPESET = 0x01, + /* argument handled like the RHS of foo=bar */ + PREFORK_ASSIGN = 0x02, + /* single word substitution */ + PREFORK_SINGLE = 0x04, + /* explicitly split nested substitution */ + PREFORK_SPLIT = 0x08, + /* SHWORDSPLIT in parameter expn */ + PREFORK_SHWORDSPLIT = 0x10, + /* SHWORDSPLIT forced off in nested subst */ + PREFORK_NOSHWORDSPLIT = 0x20, + /* Prefork is part of a parameter subexpression */ + PREFORK_SUBEXP = 0x40, + /* Prefork detected an assignment list with [key]=value syntax, + * Only used on return from prefork, not meaningful passed down. + * Also used as flag to globlist. + */ + PREFORK_KEY_VALUE = 0x80, + /* No untokenise: used only as flag to globlist */ + PREFORK_NO_UNTOK = 0x100 +}; + +/* + * Bit flags passed back from multsub() to paramsubst(). + * Some flags go from a nested parmsubst() through the enclosing + * stringsubst() and prefork(). + */ +enum { + /* + * Set if the string had whitespace at the start + * that should cause word splitting against any preceeding string. + */ + MULTSUB_WS_AT_START = 1, + /* + * Set if the string had whitespace at the end + * that should cause word splitting against any following string. + */ + MULTSUB_WS_AT_END = 2, + /* + * Set by nested paramsubst() to indicate the return + * value is a parameter name, rather than a value. + */ + MULTSUB_PARAM_NAME = 4 +}; + +/* + * Structure for adding parameters in a module. + * The flags should declare the type; note PM_SCALAR is zero. + * + * Special hashes are recognized by getnfn so the PM_HASHED + * is optional. These get slightly non-standard attention: + * the function createspecialhash is used to create them. + * + * The get/set/unset attribute may be NULL; in that case the + * parameter is assigned methods suitable for handling the + * tie variable var, if that is not NULL, else standard methods. + * + * pm is set when the parameter is added to the parameter table + * and serves as a flag that the parameter has been added. + */ +struct paramdef { + char *name; + int flags; + void *var; /* tied internal variable, if any */ + const void *gsu; /* get/set/unset structure, if special */ + GetNodeFunc getnfn; /* function to get node, if special hash */ + ScanTabFunc scantfn; /* function to scan table, if special hash */ + Param pm; /* structure inserted into param table */ +}; + +/* + * Shorthand for common uses of adding parameters, with no special + * hash properties. + */ +#define PARAMDEF(name, flags, var, gsu) \ + { name, flags, (void *) var, (void *) gsu, \ + NULL, NULL, NULL \ + } +/* + * Note that the following definitions are appropriate for defining + * parameters that reference a variable (var). Hence the get/set/unset + * methods used will assume var needs dereferencing to get the value. + */ +#define INTPARAMDEF(name, var) \ + { name, PM_INTEGER, (void *) var, NULL, NULL, NULL, NULL } +#define STRPARAMDEF(name, var) \ + { name, PM_SCALAR, (void *) var, NULL, NULL, NULL, NULL } +#define ARRPARAMDEF(name, var) \ + { name, PM_ARRAY, (void *) var, NULL, NULL, NULL, NULL } +/* + * The following is appropriate for a module function that behaves + * in a special fashion. Parameters used in a module that don't + * have special behaviour shouldn't be declared in a table but + * should just be added with the standard parameter functions. + * + * These parameters are not marked as removable, since they + * shouldn't be loaded as local parameters, unlike the special + * Zle parameters that are added and removed on each call to Zle. + * We add the PM_REMOVABLE flag when removing the feature corresponding + * to the parameter. + */ +#define SPECIALPMDEF(name, flags, gsufn, getfn, scanfn) \ + { name, flags | PM_SPECIAL | PM_HIDE | PM_HIDEVAL, \ + NULL, gsufn, getfn, scanfn, NULL } + +/* + * Flags for assignsparam and assignaparam. + */ +enum { + /* Add to rather than override value */ + ASSPM_AUGMENT = 1 << 0, + /* Test for warning if creating global variable in function */ + ASSPM_WARN_CREATE = 1 << 1, + /* Test for warning if using nested variable in function */ + ASSPM_WARN_NESTED = 1 << 2, + ASSPM_WARN = (ASSPM_WARN_CREATE|ASSPM_WARN_NESTED), + /* Import from environment, so exercise care evaluating value */ + ASSPM_ENV_IMPORT = 1 << 3, + /* Array is key / value pairs. + * This is normal for associative arrays but variant behaviour for + * normal arrays. + */ + ASSPM_KEY_VALUE = 1 << 4 +}; + +/* node for named directory hash table (nameddirtab) */ + +struct nameddir { + struct hashnode node; + char *dir; /* the directory in full */ + int diff; /* strlen(.dir) - strlen(.nam) */ +}; + +/* flags for named directories */ +/* DISABLED is defined (1<<0) */ +#define ND_USERNAME (1<<1) /* nam is actually a username */ +#define ND_NOABBREV (1<<2) /* never print as abbrev (PWD or OLDPWD) */ + +/* Storage for single group/name mapping */ +typedef struct { + /* Name of group */ + char *name; + /* Group identifier */ + gid_t gid; +} groupmap; +typedef groupmap *Groupmap; + +/* Storage for a set of group/name mappings */ +typedef struct { + /* The set of name to gid mappings */ + Groupmap array; + /* A count of the valid entries in groupmap. */ + int num; +} groupset; +typedef groupset *Groupset; + +/* flags for controlling printing of hash table nodes */ +#define PRINT_NAMEONLY (1<<0) +#define PRINT_TYPE (1<<1) +#define PRINT_LIST (1<<2) +#define PRINT_KV_PAIR (1<<3) +#define PRINT_INCLUDEVALUE (1<<4) +#define PRINT_TYPESET (1<<5) +#define PRINT_LINE (1<<6) + +/* flags for printing for the whence builtin */ +#define PRINT_WHENCE_CSH (1<<7) +#define PRINT_WHENCE_VERBOSE (1<<8) +#define PRINT_WHENCE_SIMPLE (1<<9) +#define PRINT_WHENCE_FUNCDEF (1<<10) +#define PRINT_WHENCE_WORD (1<<11) + +/* Return values from loop() */ + +enum loop_return { + /* Loop executed OK */ + LOOP_OK, + /* Loop executed no code */ + LOOP_EMPTY, + /* Loop encountered an error */ + LOOP_ERROR +}; + +/* Return values from source() */ + +enum source_return { + /* Source ran OK */ + SOURCE_OK = 0, + /* File not found */ + SOURCE_NOT_FOUND = 1, + /* Internal error sourcing file */ + SOURCE_ERROR = 2 +}; + +enum noerrexit_bits { + /* Suppress ERR_EXIT and traps: global */ + NOERREXIT_EXIT = 1, + /* Suppress ERR_RETURN: per function call */ + NOERREXIT_RETURN = 2, + /* NOERREXIT only needed on way down */ + NOERREXIT_UNTIL_EXEC = 4, + /* Force exit on SIGINT */ + NOERREXIT_SIGNAL = 8 +}; + +/***********************************/ +/* Definitions for history control */ +/***********************************/ + +/* history entry */ + +struct histent { + struct hashnode node; + + Histent up; /* previous line (moving upward) */ + Histent down; /* next line (moving downward) */ + char *zle_text; /* the edited history line, + * a metafied, NULL-terminated string, + * i.e the same format as the original + * entry + */ + time_t stim; /* command started time (datestamp) */ + time_t ftim; /* command finished time */ + short *words; /* Position of words in history */ + /* line: as pairs of start, end */ + int nwords; /* Number of words in history line */ + zlong histnum; /* A sequential history number */ +}; + +#define HIST_MAKEUNIQUE 0x00000001 /* Kill this new entry if not unique */ +#define HIST_OLD 0x00000002 /* Command is already written to disk*/ +#define HIST_READ 0x00000004 /* Command was read back from disk*/ +#define HIST_DUP 0x00000008 /* Command duplicates a later line */ +#define HIST_FOREIGN 0x00000010 /* Command came from another shell */ +#define HIST_TMPSTORE 0x00000020 /* Kill when user enters another cmd */ +#define HIST_NOWRITE 0x00000040 /* Keep internally but don't write */ + +#define GETHIST_UPWARD (-1) +#define GETHIST_DOWNWARD 1 +#define GETHIST_EXACT 0 + +/* Parts of the code where history expansion is disabled * + * should be within a pair of STOPHIST ... ALLOWHIST */ + +#define STOPHIST (stophist += 4); +#define ALLOWHIST (stophist -= 4); + +#define HISTFLAG_DONE 1 +#define HISTFLAG_NOEXEC 2 +#define HISTFLAG_RECALL 4 +#define HISTFLAG_SETTY 8 + +#define HFILE_APPEND 0x0001 +#define HFILE_SKIPOLD 0x0002 +#define HFILE_SKIPDUPS 0x0004 +#define HFILE_SKIPFOREIGN 0x0008 +#define HFILE_FAST 0x0010 +#define HFILE_NO_REWRITE 0x0020 +#define HFILE_USE_OPTIONS 0x8000 + +/* + * Flags argument to bufferwords() used + * also by lexflags variable. + */ +/* + * Kick the lexer into special string-analysis + * mode without parsing. Any bit set in + * the flags has this effect, but this + * has otherwise all the default effects. + */ +#define LEXFLAGS_ACTIVE 0x0001 +/* + * Being used from zle. This is slightly more intrusive + * (=> grotesquely non-modular) than use from within + * the main shell, so it's a separate flag. + */ +#define LEXFLAGS_ZLE 0x0002 +/* + * Parse comments and treat each comment as a single string + */ +#define LEXFLAGS_COMMENTS_KEEP 0x0004 +/* + * Parse comments and strip them. + */ +#define LEXFLAGS_COMMENTS_STRIP 0x0008 +/* + * Either of the above + */ +#define LEXFLAGS_COMMENTS (LEXFLAGS_COMMENTS_KEEP|LEXFLAGS_COMMENTS_STRIP) +/* + * Treat newlines as whitespace + */ +#define LEXFLAGS_NEWLINE 0x0010 + +/******************************************/ +/* Definitions for programable completion */ +/******************************************/ + +/* Nothing special. */ +#define IN_NOTHING 0 +/* In command position. */ +#define IN_CMD 1 +/* In a mathematical environment. */ +#define IN_MATH 2 +/* In a condition. */ +#define IN_COND 3 +/* In a parameter assignment (e.g. `foo=bar'). */ +#define IN_ENV 4 +/* In a parameter name in an assignment. */ +#define IN_PAR 5 + + +/******************************/ +/* Definition for zsh options */ +/******************************/ + +/* Possible values of emulation */ + +#define EMULATE_CSH (1<<1) /* C shell */ +#define EMULATE_KSH (1<<2) /* Korn shell */ +#define EMULATE_SH (1<<3) /* Bourne shell */ +#define EMULATE_ZSH (1<<4) /* `native' mode */ + +/* Test for a shell emulation. Use this rather than emulation directly. */ +#define EMULATION(X) (emulation & (X)) + +/* Return only base shell emulation field. */ +#define SHELL_EMULATION() (emulation & ((1<<5)-1)) + +/* Additional flags */ + +#define EMULATE_FULLY (1<<5) /* "emulate -R" in effect */ +/* + * Higher bits are used in options.c, record lowest unused bit... + */ +#define EMULATE_UNUSED (1<<6) + +/* option indices */ + +enum { + OPT_INVALID, + ALIASESOPT, + ALIASFUNCDEF, + ALLEXPORT, + ALWAYSLASTPROMPT, + ALWAYSTOEND, + APPENDHISTORY, + AUTOCD, + AUTOCONTINUE, + AUTOLIST, + AUTOMENU, + AUTONAMEDIRS, + AUTOPARAMKEYS, + AUTOPARAMSLASH, + AUTOPUSHD, + AUTOREMOVESLASH, + AUTORESUME, + BADPATTERN, + BANGHIST, + BAREGLOBQUAL, + BASHAUTOLIST, + BASHREMATCH, + BEEP, + BGNICE, + BRACECCL, + BSDECHO, + CASEGLOB, + CASEMATCH, + CBASES, + CDABLEVARS, + CHASEDOTS, + CHASELINKS, + CHECKJOBS, + CHECKRUNNINGJOBS, + CLOBBER, + APPENDCREATE, + COMBININGCHARS, + COMPLETEALIASES, + COMPLETEINWORD, + CORRECT, + CORRECTALL, + CONTINUEONERROR, + CPRECEDENCES, + CSHJUNKIEHISTORY, + CSHJUNKIELOOPS, + CSHJUNKIEQUOTES, + CSHNULLCMD, + CSHNULLGLOB, + DEBUGBEFORECMD, + EMACSMODE, + EQUALS, + ERREXIT, + ERRRETURN, + EXECOPT, + EXTENDEDGLOB, + EXTENDEDHISTORY, + EVALLINENO, + FLOWCONTROL, + FORCEFLOAT, + FUNCTIONARGZERO, + GLOBOPT, + GLOBALEXPORT, + GLOBALRCS, + GLOBASSIGN, + GLOBCOMPLETE, + GLOBDOTS, + GLOBSTARSHORT, + GLOBSUBST, + HASHCMDS, + HASHDIRS, + HASHEXECUTABLESONLY, + HASHLISTALL, + HISTALLOWCLOBBER, + HISTBEEP, + HISTEXPIREDUPSFIRST, + HISTFCNTLLOCK, + HISTFINDNODUPS, + HISTIGNOREALLDUPS, + HISTIGNOREDUPS, + HISTIGNORESPACE, + HISTLEXWORDS, + HISTNOFUNCTIONS, + HISTNOSTORE, + HISTREDUCEBLANKS, + HISTSAVEBYCOPY, + HISTSAVENODUPS, + HISTSUBSTPATTERN, + HISTVERIFY, + HUP, + IGNOREBRACES, + IGNORECLOSEBRACES, + IGNOREEOF, + INCAPPENDHISTORY, + INCAPPENDHISTORYTIME, + INTERACTIVE, + INTERACTIVECOMMENTS, + KSHARRAYS, + KSHAUTOLOAD, + KSHGLOB, + KSHOPTIONPRINT, + KSHTYPESET, + KSHZEROSUBSCRIPT, + LISTAMBIGUOUS, + LISTBEEP, + LISTPACKED, + LISTROWSFIRST, + LISTTYPES, + LOCALLOOPS, + LOCALOPTIONS, + LOCALPATTERNS, + LOCALTRAPS, + LOGINSHELL, + LONGLISTJOBS, + MAGICEQUALSUBST, + MAILWARNING, + MARKDIRS, + MENUCOMPLETE, + MONITOR, + MULTIBYTE, + MULTIFUNCDEF, + MULTIOS, + NOMATCH, + NOTIFY, + NULLGLOB, + NUMERICGLOBSORT, + OCTALZEROES, + OVERSTRIKE, + PATHDIRS, + PATHSCRIPT, + PIPEFAIL, + POSIXALIASES, + POSIXARGZERO, + POSIXBUILTINS, + POSIXCD, + POSIXIDENTIFIERS, + POSIXJOBS, + POSIXSTRINGS, + POSIXTRAPS, + PRINTEIGHTBIT, + PRINTEXITVALUE, + PRIVILEGED, + PROMPTBANG, + PROMPTCR, + PROMPTPERCENT, + PROMPTSP, + PROMPTSUBST, + PUSHDIGNOREDUPS, + PUSHDMINUS, + PUSHDSILENT, + PUSHDTOHOME, + RCEXPANDPARAM, + RCQUOTES, + RCS, + RECEXACT, + REMATCHPCRE, + RESTRICTED, + RMSTARSILENT, + RMSTARWAIT, + SHAREHISTORY, + SHFILEEXPANSION, + SHGLOB, + SHINSTDIN, + SHNULLCMD, + SHOPTIONLETTERS, + SHORTLOOPS, + SHWORDSPLIT, + SINGLECOMMAND, + SINGLELINEZLE, + SOURCETRACE, + SUNKEYBOARDHACK, + TRANSIENTRPROMPT, + TRAPSASYNC, + TYPESETSILENT, + UNSET, + VERBOSE, + VIMODE, + WARNCREATEGLOBAL, + WARNNESTEDVAR, + XTRACE, + USEZLE, + DVORAK, + OPT_SIZE +}; + +/* + * Size required to fit an option number. + * If OPT_SIZE goes above 256 this will need to expand. + */ +typedef unsigned char OptIndex; + +#undef isset +#define isset(X) (opts[X]) +#define unset(X) (!opts[X]) + +#define interact (isset(INTERACTIVE)) +#define jobbing (isset(MONITOR)) +#define islogin (isset(LOGINSHELL)) + +/* + * Record of emulation and options that need to be set + * for a full "emulate". + */ +struct emulation_options { + /* The emulation itself */ + int emulation; + /* The number of options in on_opts. */ + int n_on_opts; + /* The number of options in off_opts. */ + int n_off_opts; + /* + * Array of options to be turned on. + * Only options specified explicitly in the emulate command + * are recorded. Null if n_on_opts is zero. + */ + OptIndex *on_opts; + /* Array of options to be turned off, similar. */ + OptIndex *off_opts; +}; + +/***********************************************/ +/* Definitions for terminal and display control */ +/***********************************************/ + +/* tty state structure */ + +struct ttyinfo { +#ifdef HAVE_TERMIOS_H + struct termios tio; +#else +# ifdef HAVE_TERMIO_H + struct termio tio; +# else + struct sgttyb sgttyb; + int lmodes; + struct tchars tchars; + struct ltchars ltchars; +# endif +#endif +#ifdef TIOCGWINSZ + struct winsize winsize; +#endif +}; + +#ifndef __INTERIX +/* defines for whether tabs expand to spaces */ +#if defined(HAVE_TERMIOS_H) || defined(HAVE_TERMIO_H) +#define SGTTYFLAG shttyinfo.tio.c_oflag +#else /* we're using sgtty */ +#define SGTTYFLAG shttyinfo.sgttyb.sg_flags +#endif +# ifdef TAB3 +#define SGTABTYPE TAB3 +# else +# ifdef OXTABS +#define SGTABTYPE OXTABS +# else +# ifdef XTABS +#define SGTABTYPE XTABS +# endif +# endif +# endif +#endif + +/* flags for termflags */ + +#define TERM_BAD 0x01 /* terminal has extremely basic capabilities */ +#define TERM_UNKNOWN 0x02 /* unknown terminal type */ +#define TERM_NOUP 0x04 /* terminal has no up capability */ +#define TERM_SHORT 0x08 /* terminal is < 3 lines high */ +#define TERM_NARROW 0x10 /* terminal is < 3 columns wide */ + +/* interesting termcap strings */ + +#define TCCLEARSCREEN 0 +#define TCLEFT 1 +#define TCMULTLEFT 2 +#define TCRIGHT 3 +#define TCMULTRIGHT 4 +#define TCUP 5 +#define TCMULTUP 6 +#define TCDOWN 7 +#define TCMULTDOWN 8 +#define TCDEL 9 +#define TCMULTDEL 10 +#define TCINS 11 +#define TCMULTINS 12 +#define TCCLEAREOD 13 +#define TCCLEAREOL 14 +#define TCINSLINE 15 +#define TCDELLINE 16 +#define TCNEXTTAB 17 +#define TCBOLDFACEBEG 18 +#define TCSTANDOUTBEG 19 +#define TCUNDERLINEBEG 20 +#define TCALLATTRSOFF 21 +#define TCSTANDOUTEND 22 +#define TCUNDERLINEEND 23 +#define TCHORIZPOS 24 +#define TCUPCURSOR 25 +#define TCDOWNCURSOR 26 +#define TCLEFTCURSOR 27 +#define TCRIGHTCURSOR 28 +#define TCSAVECURSOR 29 +#define TCRESTRCURSOR 30 +#define TCBACKSPACE 31 +#define TCFGCOLOUR 32 +#define TCBGCOLOUR 33 +#define TC_COUNT 34 + +#define tccan(X) (tclen[X]) + +/* + * Text attributes for displaying in ZLE + */ + +#define TXTBOLDFACE 0x0001 +#define TXTSTANDOUT 0x0002 +#define TXTUNDERLINE 0x0004 +#define TXTFGCOLOUR 0x0008 +#define TXTBGCOLOUR 0x0010 + +#define TXT_ATTR_ON_MASK 0x001F + +#define txtisset(X) (txtattrmask & (X)) +#define txtset(X) (txtattrmask |= (X)) +#define txtunset(X) (txtattrmask &= ~(X)) + +#define TXTNOBOLDFACE 0x0020 +#define TXTNOSTANDOUT 0x0040 +#define TXTNOUNDERLINE 0x0080 +#define TXTNOFGCOLOUR 0x0100 +#define TXTNOBGCOLOUR 0x0200 + +#define TXT_ATTR_OFF_MASK 0x03E0 +/* Bits to shift off right to get on */ +#define TXT_ATTR_OFF_ON_SHIFT 5 +#define TXT_ATTR_OFF_FROM_ON(attr) \ + (((attr) & TXT_ATTR_ON_MASK) << TXT_ATTR_OFF_ON_SHIFT) +#define TXT_ATTR_ON_FROM_OFF(attr) \ + (((attr) & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT) +/* + * Indicates to zle_refresh.c that the character entry is an + * index into the list of multiword symbols. + */ +#define TXT_MULTIWORD_MASK 0x0400 + +/* Mask for colour to use in foreground */ +#define TXT_ATTR_FG_COL_MASK 0x000FF000 +/* Bits to shift the foreground colour */ +#define TXT_ATTR_FG_COL_SHIFT (12) +/* Mask for colour to use in background */ +#define TXT_ATTR_BG_COL_MASK 0x0FF00000 +/* Bits to shift the background colour */ +#define TXT_ATTR_BG_COL_SHIFT (20) + +/* Flag to use termcap AF sequence to set colour, if available */ +#define TXT_ATTR_FG_TERMCAP 0x10000000 +/* Flag to use termcap AB sequence to set colour, if available */ +#define TXT_ATTR_BG_TERMCAP 0x20000000 + +/* Things to turn on, including values for the colour elements */ +#define TXT_ATTR_ON_VALUES_MASK \ + (TXT_ATTR_ON_MASK|TXT_ATTR_FG_COL_MASK|TXT_ATTR_BG_COL_MASK|\ + TXT_ATTR_FG_TERMCAP|TXT_ATTR_BG_TERMCAP) + +/* Mask out everything to do with setting a foreground colour */ +#define TXT_ATTR_FG_ON_MASK \ + (TXTFGCOLOUR|TXT_ATTR_FG_COL_MASK|TXT_ATTR_FG_TERMCAP) + +/* Mask out everything to do with setting a background colour */ +#define TXT_ATTR_BG_ON_MASK \ + (TXTBGCOLOUR|TXT_ATTR_BG_COL_MASK|TXT_ATTR_BG_TERMCAP) + +/* Mask out everything to do with activating colours */ +#define TXT_ATTR_COLOUR_ON_MASK \ + (TXT_ATTR_FG_ON_MASK|TXT_ATTR_BG_ON_MASK) + +#define txtchangeisset(T,X) ((T) & (X)) +#define txtchangeget(T,A) (((T) & A ## _MASK) >> A ## _SHIFT) +#define txtchangeset(T, X, Y) ((void)(T && (*T &= ~(Y), *T |= (X)))) + +/* + * For outputting sequences to change colour: specify foreground + * or background. + */ +#define COL_SEQ_FG (0) +#define COL_SEQ_BG (1) +#define COL_SEQ_COUNT (2) + +/* + * Flags to testcap() and set_colour_attribute (which currently only + * handles TSC_PROMPT). + */ +enum { + /* Raw output: use stdout rather than shout */ + TSC_RAW = 0x0001, + /* Output to current prompt buffer: only used when assembling prompt */ + TSC_PROMPT = 0x0002, + /* Mask to get the output mode */ + TSC_OUTPUT_MASK = 0x0003, + /* Change needs reset of other attributes */ + TSC_DIRTY = 0x0004 +}; + +/****************************************/ +/* Definitions for the %_ prompt escape */ +/****************************************/ + +#define CMDSTACKSZ 256 + +#define CS_FOR 0 +#define CS_WHILE 1 +#define CS_REPEAT 2 +#define CS_SELECT 3 +#define CS_UNTIL 4 +#define CS_IF 5 +#define CS_IFTHEN 6 +#define CS_ELSE 7 +#define CS_ELIF 8 +#define CS_MATH 9 +#define CS_COND 10 +#define CS_CMDOR 11 +#define CS_CMDAND 12 +#define CS_PIPE 13 +#define CS_ERRPIPE 14 +#define CS_FOREACH 15 +#define CS_CASE 16 +#define CS_FUNCDEF 17 +#define CS_SUBSH 18 +#define CS_CURSH 19 +#define CS_ARRAY 20 +#define CS_QUOTE 21 +#define CS_DQUOTE 22 +#define CS_BQUOTE 23 +#define CS_CMDSUBST 24 +#define CS_MATHSUBST 25 +#define CS_ELIFTHEN 26 +#define CS_HEREDOC 27 +#define CS_HEREDOCD 28 +#define CS_BRACE 29 +#define CS_BRACEPAR 30 +#define CS_ALWAYS 31 + +/* Increment as necessary */ +#define CS_COUNT 32 + +/********************* + * Memory management * + *********************/ + +/* + * A Heapid is a type for identifying, uniquely up to the point where + * the count of new identifiers wraps. all heaps that are or + * (importantly) have been valid. Each valid heap is given an + * identifier, and every time we push a heap we save the old identifier + * and give the heap a new identifier so that when the heap is popped + * or freed we can spot anything using invalid memory from the popped + * heap. + * + * We could make this unsigned long long if we wanted a big range. + */ +typedef unsigned int Heapid; + +#ifdef ZSH_HEAP_DEBUG + +/* printf format specifier corresponding to Heapid */ +#define HEAPID_FMT "%x" + +/* Marker that memory is permanently allocated */ +#define HEAPID_PERMANENT (UINT_MAX) + +/* + * Heap debug verbosity. + * Bits to be 'or'ed into the variable also called heap_debug_verbosity. + */ +enum heap_debug_verbosity { + /* Report when we push a heap */ + HDV_PUSH = 0x01, + /* Report when we pop a heap */ + HDV_POP = 0x02, + /* Report when we create a new heap from which to allocate */ + HDV_CREATE = 0x04, + /* Report every time we free a complete heap */ + HDV_FREE = 0x08, + /* Report when we temporarily install a new set of heaps */ + HDV_NEW = 0x10, + /* Report when we restore an old set of heaps */ + HDV_OLD = 0x20, + /* Report when we temporarily switch heaps */ + HDV_SWITCH = 0x40, + /* + * Report every time we allocate memory from the heap. + * This is very verbose, and arguably not very useful: we + * would expect to allocate memory from a heap we create. + * For much debugging heap_debug_verbosity = 0x7f should be sufficient. + */ + HDV_ALLOC = 0x80 +}; + +#define HEAP_ERROR(heap_id) \ + fprintf(stderr, "%s:%d: HEAP DEBUG: invalid heap: " HEAPID_FMT ".\n", \ + __FILE__, __LINE__, heap_id) +#endif + +/* heappush saves the current heap state using this structure */ + +struct heapstack { + struct heapstack *next; /* next one in list for this heap */ + size_t used; +#ifdef ZSH_HEAP_DEBUG + Heapid heap_id; +#endif +}; + +/* A zsh heap. */ + +struct heap { + struct heap *next; /* next one */ + size_t size; /* size of heap */ + size_t used; /* bytes used from the heap */ + struct heapstack *sp; /* used by pushheap() to save the value used */ + +#ifdef ZSH_HEAP_DEBUG + unsigned int heap_id; +#endif + +/* Uncomment the following if the struct needs padding to 64-bit size. */ +/* Make sure sizeof(heap) is a multiple of 8 +#if defined(PAD_64_BIT) && !defined(__GNUC__) + size_t dummy; +#endif +*/ +#define arena(X) ((char *) (X) + sizeof(struct heap)) +} +#if defined(PAD_64_BIT) && defined(__GNUC__) + __attribute__ ((aligned (8))) +#endif +; + +# define NEWHEAPS(h) do { Heap _switch_oldheaps = h = new_heaps(); do +# define OLDHEAPS while (0); old_heaps(_switch_oldheaps); } while (0); + +# define SWITCHHEAPS(o, h) do { o = switch_heaps(h); do +# define SWITCHBACKHEAPS(o) while (0); switch_heaps(o); } while (0); + +/****************/ +/* Debug macros */ +/****************/ + +#ifdef DEBUG +#define STRINGIFY_LITERAL(x) # x +#define STRINGIFY(x) STRINGIFY_LITERAL(x) +#define ERRMSG(x) (__FILE__ ":" STRINGIFY(__LINE__) ": " x) +# define DPUTS(X,Y) if (!(X)) {;} else dputs(ERRMSG(Y)) +# define DPUTS1(X,Y,Z1) if (!(X)) {;} else dputs(ERRMSG(Y), Z1) +# define DPUTS2(X,Y,Z1,Z2) if (!(X)) {;} else dputs(ERRMSG(Y), Z1, Z2) +# define DPUTS3(X,Y,Z1,Z2,Z3) if (!(X)) {;} else dputs(ERRMSG(Y), Z1, Z2, Z3) +#else +# define DPUTS(X,Y) +# define DPUTS1(X,Y,Z1) +# define DPUTS2(X,Y,Z1,Z2) +# define DPUTS3(X,Y,Z1,Z2,Z3) +#endif + +/**************************/ +/* Signal handling macros */ +/**************************/ + +/* These used in the sigtrapped[] array */ + +#define ZSIG_TRAPPED (1<<0) /* Signal is trapped */ +#define ZSIG_IGNORED (1<<1) /* Signal is ignored */ +#define ZSIG_FUNC (1<<2) /* Trap is a function, not an eval list */ +/* Mask to get the above flags */ +#define ZSIG_MASK (ZSIG_TRAPPED|ZSIG_IGNORED|ZSIG_FUNC) +/* No. of bits to shift local level when storing in sigtrapped */ +#define ZSIG_ALIAS (1<<3) /* Trap is stored under an alias */ +#define ZSIG_SHIFT 4 + +/* + * State of traps, stored in trap_state. + */ +enum trap_state { + /* Traps are not active; trap_return is not useful. */ + TRAP_STATE_INACTIVE, + /* + * Traps are set but haven't triggered; trap_return gives + * minus function depth. + */ + TRAP_STATE_PRIMED, + /* + * Trap has triggered to force a return; trap_return givens + * return value. + */ + TRAP_STATE_FORCE_RETURN +}; + +#define IN_EVAL_TRAP() \ + (intrap && !trapisfunc && traplocallevel == locallevel) + +/* + * Bits in the errflag variable. + */ +enum errflag_bits { + /* + * Standard internal error bit. + */ + ERRFLAG_ERROR = 1, + /* + * User interrupt. + */ + ERRFLAG_INT = 2, + /* + * Hard error --- return to top-level prompt in interactive + * shell. In non-interactive shell we'll typically already + * have exited. This is reset by "errflag = 0" in + * loop(toplevel = 1, ...). + */ + ERRFLAG_HARD = 4 +}; + +/***********/ +/* Sorting */ +/***********/ + +typedef int (*CompareFn) _((const void *, const void *)); + +enum { + SORTIT_ANYOLDHOW = 0, /* Defaults */ + SORTIT_IGNORING_CASE = 1, + SORTIT_NUMERICALLY = 2, + SORTIT_BACKWARDS = 4, + /* + * Ignore backslashes that quote another character---which may + * be another backslash; the second backslash is active. + */ + SORTIT_IGNORING_BACKSLASHES = 8, + /* + * Ignored by strmetasort(); used by paramsubst() to indicate + * there is some sorting to do. + */ + SORTIT_SOMEHOW = 16, +}; + +/* + * Element of array passed to qsort(). + */ +struct sortelt { + /* The original string. */ + char *orig; + /* The string used for comparison. */ + const char *cmp; + /* + * The length of the string if passed down to the sort algorithm. + * Used to sort the lengths together with the strings. + */ + int origlen; + /* + * The length of the string, if needed, else -1. + * The length is only needed if there are embededded nulls. + */ + int len; +}; + +typedef struct sortelt *SortElt; + +/*********************************************************/ +/* Structures to save and restore for individual modules */ +/*********************************************************/ + +/* History */ +struct hist_stack { + int histactive; + int histdone; + int stophist; + int hlinesz; + zlong defev; + char *hline; + char *hptr; + short *chwords; + int chwordlen; + int chwordpos; + int (*hgetc) _((void)); + void (*hungetc) _((int)); + void (*hwaddc) _((int)); + void (*hwbegin) _((int)); + void (*hwabort) _((void)); + void (*hwend) _((void)); + void (*addtoline) _((int)); + unsigned char *cstack; + int csp; + int hist_keep_comment; +}; + +/* + * State of a lexical token buffer. + * + * It would be neater to include the pointer to the start of the buffer, + * however the current code structure means that the standard instance + * of this, tokstr, is visible in lots of places, so that's not + * convenient. + */ + +struct lexbufstate { + /* + * Next character to be added. + * Set to NULL when the buffer is to be visible from elsewhere. + */ + char *ptr; + /* Allocated buffer size */ + int siz; + /* Length in use */ + int len; +}; + +/* Lexical analyser */ +struct lex_stack { + int dbparens; + int isfirstln; + int isfirstch; + int lexflags; + enum lextok tok; + char *tokstr; + char *zshlextext; + struct lexbufstate lexbuf; + int lex_add_raw; + char *tokstr_raw; + struct lexbufstate lexbuf_raw; + int lexstop; + zlong toklineno; +}; + +/* Parser */ +struct parse_stack { + struct heredocs *hdocs; + + int incmdpos; + int aliasspaceflag; + int incond; + int inredir; + int incasepat; + int isnewlin; + int infor; + int inrepeat_; + int intypeset; + + int eclen, ecused, ecnpats; + Wordcode ecbuf; + Eccstr ecstrs; + int ecsoffs, ecssub, ecnfunc; +}; + +/************************/ +/* Flags to casemodifiy */ +/************************/ + +enum { + CASMOD_NONE, /* dummy for tests */ + CASMOD_UPPER, + CASMOD_LOWER, + CASMOD_CAPS +}; + +/*******************************************/ +/* Flags to third argument of getkeystring */ +/*******************************************/ + +/* + * By default handles some subset of \-escapes. The following bits + * turn on extra features. + */ +enum { + /* + * Handle octal where the first digit is non-zero e.g. \3, \33, \333 + * Otherwise \0333 etc. is handled, i.e. one of \0123 or \123 will + * work, but not both. + */ + GETKEY_OCTAL_ESC = (1 << 0), + /* + * Handle Emacs-like key sequences \C-x etc. + * Also treat \E like \e and use backslashes to escape the + * next character if not special, i.e. do all the things we + * don't do with the echo builtin. + */ + GETKEY_EMACS = (1 << 1), + /* Handle ^X etc. */ + GETKEY_CTRL = (1 << 2), + /* Handle \c (uses misc arg to getkeystring()) */ + GETKEY_BACKSLASH_C = (1 << 3), + /* Do $'...' quoting (len arg to getkeystring() not used) */ + GETKEY_DOLLAR_QUOTE = (1 << 4), + /* Handle \- (uses misc arg to getkeystring()) */ + GETKEY_BACKSLASH_MINUS = (1 << 5), + /* Parse only one character (len arg to getkeystring() not used) */ + GETKEY_SINGLE_CHAR = (1 << 6), + /* + * If beyond offset in misc arg, add 1 to it for each character removed. + * Yes, I know that doesn't seem to make much sense. + * It's for use in completion, comprenez? + */ + GETKEY_UPDATE_OFFSET = (1 << 7), + /* + * When replacing numeric escapes for printf format strings, % -> %% + */ + GETKEY_PRINTF_PERCENT = (1 << 8) +}; + +/* + * Standard combinations used within the shell. + * Note GETKEYS_... instead of GETKEY_...: this is important in some cases. + */ +/* echo builtin */ +#define GETKEYS_ECHO (GETKEY_BACKSLASH_C) +/* printf format string: \123 -> S, \0123 -> NL 3, \045 -> %% */ +#define GETKEYS_PRINTF_FMT \ + (GETKEY_OCTAL_ESC|GETKEY_BACKSLASH_C|GETKEY_PRINTF_PERCENT) +/* printf argument: \123 -> \123, \0123 -> S */ +#define GETKEYS_PRINTF_ARG (GETKEY_BACKSLASH_C) +/* Full print without -e */ +#define GETKEYS_PRINT (GETKEY_OCTAL_ESC|GETKEY_BACKSLASH_C|GETKEY_EMACS) +/* bindkey */ +#define GETKEYS_BINDKEY (GETKEY_OCTAL_ESC|GETKEY_EMACS|GETKEY_CTRL) +/* $'...' */ +#define GETKEYS_DOLLARS_QUOTE (GETKEY_OCTAL_ESC|GETKEY_EMACS|GETKEY_DOLLAR_QUOTE) +/* Single character for math processing */ +#define GETKEYS_MATH \ + (GETKEY_OCTAL_ESC|GETKEY_EMACS|GETKEY_CTRL|GETKEY_SINGLE_CHAR) +/* Used to process separators etc. with print-style escapes */ +#define GETKEYS_SEP (GETKEY_OCTAL_ESC|GETKEY_EMACS) +/* Used for suffix removal */ +#define GETKEYS_SUFFIX \ + (GETKEY_OCTAL_ESC|GETKEY_EMACS|GETKEY_CTRL|GETKEY_BACKSLASH_MINUS) + +/**********************************/ +/* Flags to third argument of zle */ +/**********************************/ + +#define ZLRF_HISTORY 0x01 /* OK to access the history list */ +#define ZLRF_NOSETTY 0x02 /* Don't set tty before return */ +#define ZLRF_IGNOREEOF 0x04 /* Ignore an EOF from the keyboard */ + +/***************************/ +/* Context of zleread call */ +/***************************/ + +enum { + ZLCON_LINE_START, /* Command line at PS1 */ + ZLCON_LINE_CONT, /* Command line at PS2 */ + ZLCON_SELECT, /* Select loop */ + ZLCON_VARED /* Vared command */ +}; + +/****************/ +/* Entry points */ +/****************/ + +/* compctl entry point pointers */ + +typedef int (*CompctlReadFn) _((char *, char **, Options, char *)); + +/* ZLE entry point pointer */ + +typedef char * (*ZleEntryPoint)(int cmd, va_list ap); + +/* Commands to pass to entry point */ + +enum { + ZLE_CMD_GET_LINE, + ZLE_CMD_READ, + ZLE_CMD_ADD_TO_LINE, + ZLE_CMD_TRASH, + ZLE_CMD_RESET_PROMPT, + ZLE_CMD_REFRESH, + ZLE_CMD_SET_KEYMAP, + ZLE_CMD_GET_KEY, + ZLE_CMD_SET_HIST_LINE +}; + +/***************************************/ +/* Hooks in core. */ +/***************************************/ + +#define EXITHOOK (zshhooks + 0) +#define BEFORETRAPHOOK (zshhooks + 1) +#define AFTERTRAPHOOK (zshhooks + 2) + +#ifdef MULTIBYTE_SUPPORT +/* Final argument to mb_niceformat() */ +enum { + NICEFLAG_HEAP = 1, /* Heap allocation where needed */ + NICEFLAG_QUOTE = 2, /* Result will appear in $'...' */ + NICEFLAG_NODUP = 4, /* Leave allocated */ +}; + +/* Metafied input */ +#define nicezputs(str, outs) (void)mb_niceformat((str), (outs), NULL, 0) +#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_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 + * characters directly as wide characters. In principle this is only + * likely to work if __STDC_ISO_10646__ is defined, since that's pretty + * much what the definition tells us. However, we happen to know this + * works on MacOS which doesn't define that. + */ +#ifdef ENABLE_UNICODE9 +#define WCWIDTH(wc) u9_wcwidth(wc) +#else +#define WCWIDTH(wc) wcwidth(wc) +#endif +/* + * Note WCWIDTH_WINT() takes wint_t, typically as a convchar_t. + * It's written to use the wint_t from mb_metacharlenconv() without + * further tests. + * + * This version has a non-multibyte definition that simply returns + * 1. We never expose WCWIDTH() in the non-multibyte world since + * it's just a proxy for wcwidth() itself. + */ +#define WCWIDTH_WINT(wc) zwcwidth(wc) + +#define MB_INCOMPLETE ((size_t)-2) +#define MB_INVALID ((size_t)-1) + +/* + * MB_CUR_MAX is the maximum number of bytes that a single wide + * character will convert into. We use it to keep strings + * sufficiently long. It should always be defined, but if it isn't + * just assume we are using Unicode which requires 6 characters. + * (Note that it's not necessarily defined to a constant.) + */ +#ifndef MB_CUR_MAX +#define MB_CUR_MAX 6 +#endif + +/* Convert character or string to wide character or string */ +#define ZWC(c) L ## c +#define ZWS(s) L ## s + +/* + * Test for a combining character. + * + * wc is assumed to be a wchar_t (i.e. we don't need zwcwidth). + * + * Pedantic note: in Unicode, a combining character need not be + * zero length. However, we are concerned here about display; + * we simply need to know whether the character will be displayed + * on top of another one. We use "combining character" in this + * sense throughout the shell. I am not aware of a way of + * detecting the Unicode trait in standard libraries. + */ +#define IS_COMBINING(wc) (wc != 0 && WCWIDTH(wc) == 0) +/* + * Test for the base of a combining character. + * + * We assume a combining character can be successfully displayed with + * any non-space printable character, which is what a graphic character + * is, as long as it has non-zero width. We need to avoid all forms of + * space because the shell will split words on any whitespace. + */ +#define IS_BASECHAR(wc) (iswgraph(wc) && WCWIDTH(wc) > 0) + +#else /* not MULTIBYTE_SUPPORT */ + +#define MB_METACHARINIT() +typedef int convchar_t; +#define MB_METACHARLENCONV(str, cp) metacharlenconv((str), (cp)) +#define MB_METACHARLEN(str) (*(str) == Meta ? 2 : 1) +#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) + +/* Leave character or string as is. */ +#define ZWC(c) c +#define ZWS(s) s + +#endif /* MULTIBYTE_SUPPORT */ diff --git a/dotfiles/system/.zsh/modules/Src/zsh.mdd b/dotfiles/system/.zsh/modules/Src/zsh.mdd new file mode 100644 index 0000000..d95f5d5 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/zsh.mdd @@ -0,0 +1,147 @@ +name=zsh/main +link=static +load=yes +# load=static should replace use of alwayslink +functions='Functions/Chpwd/* Functions/Exceptions/* Functions/Math/* Functions/Misc/* Functions/MIME/* Functions/Prompts/* Functions/VCS_Info/* Functions/VCS_Info/Backends/*' + +nozshdep=1 +alwayslink=1 + +# autofeatures not specified because of alwayslink + +objects="signames.o builtin.o module.o lex.o exec.o mem.o \ +string.o parse.o hashtable.o init.o input.o loop.o utils.o params.o options.o \ +signals.o pattern.o prompt.o compat.o jobs.o glob.o" + +headers="../config.h zsh_system.h zsh.h sigcount.h signals.h \ +prototypes.h hashtable.h ztype.h" +hdrdeps="zshcurses.h zshterm.h" + +:<<\Make +@CONFIG_MK@ + +# If we're using gcc as the preprocessor, get rid of the additional +# lines generated by the preprocessor as they can confuse the script. +# We don't need these in other cases either, but can't necessarily rely +# on the option to remove them being the same. +signames.c: signames1.awk signames2.awk ../config.h @SIGNAL_H@ + $(AWK) -f $(sdir)/signames1.awk @SIGNAL_H@ >sigtmp.c + case "`$(CPP) --version &1`" in \ + *"Free Software Foundation"*) \ + $(CPP) -P sigtmp.c >sigtmp.out;; \ + *) \ + $(CPP) sigtmp.c >sigtmp.out;; \ + esac + $(AWK) -f $(sdir)/signames2.awk sigtmp.out > $@ + rm -f sigtmp.c sigtmp.out + +sigcount.h: signames.c + grep 'define.*SIGCOUNT' signames.c > $@ + +init.o: bltinmods.list zshpaths.h zshxmods.h + +init.o params.o parse.o: version.h + +params.o: patchlevel.h + +version.h: $(sdir_top)/Config/version.mk zshcurses.h zshterm.h + echo '#define ZSH_VERSION "'$(VERSION)'"' > $@ + +patchlevel.h: FORCE + @if [ -f $(sdir)/$@.release ]; then \ + cp -f $(sdir)/$@.release $@; \ + else \ + echo '#define ZSH_PATCHLEVEL "'`cd $(sdir) && git describe --tags --long`'"' > $@.tmp; \ + cmp $@ $@.tmp >/dev/null 2>&1 && rm -f $@.tmp || mv $@.tmp $@; \ + fi +FORCE: + +zshcurses.h: ../config.h + @if test x$(ZSH_CURSES_H) != x; then \ + echo "#include <$(ZSH_CURSES_H)>" >zshcurses.h; \ + else \ + echo >zshcurses.h; \ + fi + +zshterm.h: ../config.h + @if test x$(ZSH_TERM_H) != x; then \ + echo "#include <$(ZSH_TERM_H)>" >zshterm.h; \ + else \ + echo >zshterm.h; \ + fi + +zshpaths.h: Makemod $(CONFIG_INCS) + @echo '#define MODULE_DIR "'$(MODDIR)'"' > zshpaths.h.tmp + @if test x$(sitescriptdir) != xno; then \ + echo '#define SITESCRIPT_DIR "'$(sitescriptdir)'"' >> zshpaths.h.tmp; \ + fi + @if test x$(scriptdir) != xno; then \ + echo '#define SCRIPT_DIR "'$(scriptdir)'"' >> zshpaths.h.tmp; \ + fi + @if test x$(sitefndir) != xno; then \ + echo '#define SITEFPATH_DIR "'$(sitefndir)'"' >> zshpaths.h.tmp; \ + fi + @if test x$(fixed_sitefndir) != x; then \ + echo '#define FIXED_FPATH_DIR "'$(fixed_sitefndir)'"' >> zshpaths.h.tmp; \ + fi + @if test x$(fndir) != xno; then \ + echo '#define FPATH_DIR "'$(fndir)'"' >> zshpaths.h.tmp; \ + if test x$(FUNCTIONS_SUBDIRS) != x && \ + test x$(FUNCTIONS_SUBDIRS) != xno; then \ + fpath_tmp="`grep ' functions=.' \ + $(dir_top)/config.modules | sed -e '/^#/d' -e '/ link=no/d' \ + -e 's/^.* functions=//'`"; \ + fpath_tmp=`for f in $$fpath_tmp; do \ + echo $$f | sed -e 's%^Functions/%%' -e 's%/[^/]*$$%%' -e 's%/\*%%'; \ + done | grep -v Scripts | sort | uniq`; \ + fpath_tmp=`echo $$fpath_tmp | sed 's/ /\", \"/g'`; \ + echo "#define FPATH_SUBDIRS { \"$$fpath_tmp\" }" \ + >>zshpaths.h.tmp; \ + fi; \ + fi + @if test x$(additionalfpath) != x; then \ + fpath_tmp="`echo $(additionalfpath) | sed -e 's:,:\", \":g'`"; \ + echo "#define ADDITIONAL_FPATH { \"$$fpath_tmp\" }" >> zshpaths.h.tmp; \ + fi + @if cmp -s zshpaths.h zshpaths.h.tmp; then \ + rm -f zshpaths.h.tmp; \ + echo "\`zshpaths.h' is up to date." ; \ + else \ + mv -f zshpaths.h.tmp zshpaths.h; \ + echo "Updated \`zshpaths.h'." ; \ + fi + +bltinmods.list: modules.stamp mkbltnmlst.sh $(dir_top)/config.modules + srcdir='$(sdir)' CFMOD='$(dir_top)/config.modules' \ + $(SHELL) $(sdir)/mkbltnmlst.sh $@ + +zshxmods.h: $(dir_top)/config.modules + @echo "Creating \`$@'." + @( \ + for q_mod in `grep ' load=yes' $(dir_top)/config.modules | \ + grep ' link=static' | sed -e '/^#/d' -e 's/ .*//' \ + -e 's/^name=//' -e 's,Q,Qq,g;s,_,Qu,g;s,/,Qs,g'`; do \ + test x$q_mod = xzshQsmain && continue; \ + echo "#define LINKED_XMOD_$$q_mod 1"; \ + done; \ + for q_mod in `grep ' load=yes' $(dir_top)/config.modules | \ + grep ' link=dynamic' | sed -e '/^#/d' -e 's/ .*//' \ + -e 's/^name=//' -e 's,Q,Qq,g;s,_,Qu,g;s,/,Qs,g'`; do \ + test x$q_mod = x && continue; \ + echo "#ifdef DYNAMIC"; \ + echo "# define UNLINKED_XMOD_$$q_mod 1"; \ + echo "#endif"; \ + done; \ + ) > $@ + +clean-here: clean.zsh +clean.zsh: + rm -f sigcount.h signames.c bltinmods.list version.h zshpaths.h zshxmods.h + +# This is not properly part of this module, but it is built as if it were. +main.o: main.c zsh.mdh main.epro + $(CC) -c -I. -I$(sdir_top)/Src $(CPPFLAGS) $(DEFS) $(CFLAGS) -o $@ $(sdir)/main.c + +main.syms: $(PROTODEPS) +proto.zsh: main.epro +Make diff --git a/dotfiles/system/.zsh/modules/Src/zsh.rc b/dotfiles/system/.zsh/modules/Src/zsh.rc new file mode 100644 index 0000000..93c82ba --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/zsh.rc @@ -0,0 +1,8 @@ +// Use this file as follows +// +// myapp.exe : myapp.o myapp.res +// gcc -mwindows myapp.o myapp.res -o $@ +// +// myapp.res : myapp.rc resource.h +// windres $< -O coff -o $@ +IDR_MAINFRAME ICON DISCARDABLE "zsh.ico" diff --git a/dotfiles/system/.zsh/modules/Src/zsh_system.h b/dotfiles/system/.zsh/modules/Src/zsh_system.h new file mode 100644 index 0000000..8289ee9 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/zsh_system.h @@ -0,0 +1,900 @@ +/* + * system.h - system configuration header file + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#if 0 +/* + * Setting _XPG_IV here is actually wrong and is not needed + * with currently supported versions (5.43C20 and above) + */ +#ifdef sinix +# define _XPG_IV 1 +#endif +#endif + +#if defined(__linux) || defined(__GNU__) || defined(__GLIBC__) || defined(LIBC_MUSL) || defined(__CYGWIN__) +/* + * Turn on numerous extensions. + * This is in order to get the functions for manipulating /dev/ptmx. + */ +#define _GNU_SOURCE 1 +#endif +#ifdef LIBC_MUSL +#define _POSIX_C_SOURCE 200809L +#endif + +/* NeXT has half-implemented POSIX support * + * which currently fools configure */ +#ifdef __NeXT__ +# undef HAVE_TERMIOS_H +# undef HAVE_SYS_UTSNAME_H +#endif + +#ifndef ZSH_NO_XOPEN +# ifdef ZSH_CURSES_SOURCE +# define _XOPEN_SOURCE_EXTENDED 1 +# else +# ifdef MULTIBYTE_SUPPORT +/* + * Needed for wcwidth() which is part of XSI. + * Various other uses of the interface mean we can't get away with just + * _XOPEN_SOURCE. + */ +# define _XOPEN_SOURCE_EXTENDED 1 +# endif /* MULTIBYTE_SUPPORT */ +# endif /* ZSH_CURSES_SOURCE */ +#endif /* ZSH_NO_XOPEN */ + +/* + * Solaris by default zeroes all elements of the tm structure in + * strptime(). Unfortunately that gives us no way of telling whether + * the tm_isdst element has been set from the input pattern. If it + * hasn't we want it to be -1 (undetermined) on input to mktime(). So + * we stop strptime() zeroing the struct tm and instead set all the + * elements ourselves. + * + * This is likely to be harmless everywhere else. + */ +#define _STRPTIME_DONTZERO + +#ifdef PROTOTYPES +# define _(Args) Args +#else +# define _(Args) () +#endif + +#ifndef HAVE_ALLOCA +# define alloca zhalloc +#else +# ifdef __GNUC__ +# define alloca __builtin_alloca +# else +# if HAVE_ALLOCA_H +# include +# else +# ifdef _AIX + # pragma alloca +# else +# ifndef alloca +char *alloca _((size_t)); +# endif +# endif +# endif +# endif +#endif + +/* + * libc.h in an optional package for Debian Linux is broken (it + * defines dup() as a synonym for dup2(), which has a different + * number of arguments), so just include it for next. + */ +#ifdef __NeXT__ +# ifdef HAVE_LIBC_H +# include +# endif +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef HAVE_STDDEF_H +/* + * Seen on Solaris 8 with gcc: stddef defines offsetof, which clashes + * with system.h's definition of the symbol unless we include this + * first. Otherwise, this will be hooked in by wchar.h, too late + * for comfort. + */ +#include +#endif + +#include +#include +#include +#include +#include + +#ifdef HAVE_PWD_H +# include +#endif + +#ifdef HAVE_GRP_H +# include +#endif + +#ifdef HAVE_DIRENT_H +# include +#else /* !HAVE_DIRENT_H */ +# ifdef HAVE_SYS_NDIR_H +# include +# endif +# ifdef HAVE_SYS_DIR_H +# include +# endif +# ifdef HAVE_NDIR_H +# include +# endif +# define dirent direct +# undef HAVE_STRUCT_DIRENT_D_INO +# undef HAVE_STRUCT_DIRENT_D_STAT +# ifdef HAVE_STRUCT_DIRECT_D_INO +# define HAVE_STRUCT_DIRENT_D_INO HAVE_STRUCT_DIRECT_D_INO +# endif +# ifdef HAVE_STRUCT_DIRECT_D_STAT +# define HAVE_STRUCT_DIRENT_D_STAT HAVE_STRUCT_DIRECT_D_STAT +# endif +#endif /* !HAVE_DIRENT_H */ + +#ifdef HAVE_STDLIB_H +# ifdef ZSH_MEM + /* malloc and calloc are macros in GNU's stdlib.h unless the + * the __MALLOC_0_RETURNS_NULL macro is defined */ +# define __MALLOC_0_RETURNS_NULL +# endif +# include +#endif + +/* + * Stuff with variable arguments. We use definitions to make the + * same code work with varargs (the original K&R-style, just to + * be maximally compatible) and stdarg (which all modern systems + * should have). + * + * Ideally this should somehow be merged with the tricks performed + * with "_" in makepro.awk, but I don't understand makepro.awk. + * Currently we simply rely on the fact that makepro.awk has been + * hacked to leave alone argument lists that already contains VA_ALIST + * except for removing the VA_DCL and turning VA_ALIST into VA_ALIST_PROTO. + */ +#ifdef HAVE_STDARG_H +# include +# define VA_ALIST1(x) x, ... +# define VA_ALIST2(x,y) x, y, ... +# define VA_ALIST_PROTO1(x) VA_ALIST1(x) +# define VA_ALIST_PROTO2(x,y) VA_ALIST2(x,y) +# define VA_DCL +# define VA_DEF_ARG(x) +# define VA_START(ap,x) va_start(ap, x) +# define VA_GET_ARG(ap,x,t) +#else +# if HAVE_VARARGS_H +# include +# define VA_ALIST1(x) va_alist +# define VA_ALIST2(x,y) va_alist +/* + * In prototypes, assume K&R form and remove the variable list. + * This is about the best we can do without second-guessing the way + * varargs works on this system. The _ trick should be able to + * do this for us but we've turned it off here. + */ +# define VA_ALIST_PROTO1(x) +# define VA_ALIST_PROTO2(x,y) +# define VA_DCL va_dcl +# define VA_DEF_ARG(x) x +# define VA_START(ap,x) va_start(ap); +# define VA_GET_ARG(ap,x,t) (x = va_arg(ap, t)) +# else +# error "Your system has neither stdarg.h or varargs.h." +# endif +#endif + +#ifdef HAVE_ERRNO_H +# include +#endif + +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +/* This is needed by some old SCO unices */ +#if !defined(HAVE_STRUCT_TIMEZONE) && !defined(ZSH_OOT_MODULE) +struct timezone { + int tz_minuteswest; + int tz_dsttime; +}; +#endif + +/* Used to provide compatibility with clock_gettime() */ +#if !defined(HAVE_STRUCT_TIMESPEC) && !defined(ZSH_OOT_MODULE) +struct timespec { + time_t tv_sec; + long tv_nsec; +}; +#endif + +/* There's more than one non-standard way to get at this data */ +#if !defined(HAVE_STRUCT_DIRENT_D_INO) && defined(HAVE_STRUCT_DIRENT_D_STAT) +# define d_ino d_stat.st_ino +# define HAVE_STRUCT_DIRENT_D_INO HAVE_STRUCT_DIRENT_D_STAT +#endif /* !HAVE_STRUCT_DIRENT_D_INO && HAVE_STRUCT_DIRENT_D_STAT */ + +/* Sco needs the following include for struct utimbuf * + * which is strange considering we do not use that * + * anywhere in the code */ +#ifdef __sco +# include +#endif + +#ifdef HAVE_SYS_TIMES_H +# include +#endif + +#if STDC_HEADERS || HAVE_STRING_H +# include +/* An ANSI string.h and pre-ANSI memory.h might conflict. */ +# if !STDC_HEADERS && HAVE_MEMORY_H +# include +# endif /* not STDC_HEADERS and HAVE_MEMORY_H */ +#else /* not STDC_HEADERS and not HAVE_STRING_H */ +# include +/* memory.h and strings.h conflict on some systems. */ +#endif /* not STDC_HEADERS and not HAVE_STRING_H */ + +#ifdef HAVE_LOCALE_H +# include +#endif + +#ifdef HAVE_LIMITS_H +# include +#endif + +#ifdef USE_STACK_ALLOCATION +#ifdef HAVE_VARIABLE_LENGTH_ARRAYS +# define VARARR(X,Y,Z) X (Y)[Z] +#else +# define VARARR(X,Y,Z) X *(Y) = (X *) alloca(sizeof(X) * (Z)) +#endif +#else +# define VARARR(X,Y,Z) X *(Y) = (X *) zhalloc(sizeof(X) * (Z)) +#endif + +/* we should handle unlimited sizes from pathconf(_PC_PATH_MAX) */ +/* but this is too much trouble */ +#ifndef PATH_MAX +# ifdef MAXPATHLEN +# define PATH_MAX MAXPATHLEN +# else +# ifdef _POSIX_PATH_MAX +# define PATH_MAX _POSIX_PATH_MAX +# else + /* so we will just pick something */ +# define PATH_MAX 1024 +# endif +# endif +#endif + +/* + * The number of file descriptors we'll allocate initially. + * We will reallocate later if necessary. + */ +#define ZSH_INITIAL_OPEN_MAX 64 +#ifndef OPEN_MAX +# ifdef NOFILE +# define OPEN_MAX NOFILE +# else + /* so we will just pick something */ +# define OPEN_MAX ZSH_INITIAL_OPEN_MAX +# endif +#endif +#ifndef HAVE_SYSCONF +# define zopenmax() ((long) (OPEN_MAX > ZSH_INITIAL_OPEN_MAX ? \ + ZSH_INITIAL_OPEN_MAX : OPEN_MAX)) +#endif + +#ifdef HAVE_FCNTL_H +# include +#else +# include +#endif + +/* The following will only be defined if is POSIX. * + * So we don't have to worry about union wait. But some machines * + * (NeXT) include from other include files, so we * + * need to undef and then redefine the wait macros if * + * is not POSIX. */ + +#ifdef HAVE_SYS_WAIT_H +# include +#else +# undef WIFEXITED +# undef WEXITSTATUS +# undef WIFSIGNALED +# undef WTERMSIG +# undef WCOREDUMP +# undef WIFSTOPPED +# undef WSTOPSIG +#endif + +/* missing macros for wait/waitpid/wait3 */ +#ifndef WIFEXITED +# define WIFEXITED(X) (((X)&0377)==0) +#endif +#ifndef WEXITSTATUS +# define WEXITSTATUS(X) (((X)>>8)&0377) +#endif +#ifndef WIFSIGNALED +# define WIFSIGNALED(X) (((X)&0377)!=0&&((X)&0377)!=0177) +#endif +#ifndef WTERMSIG +# define WTERMSIG(X) ((X)&0177) +#endif +#ifndef WCOREDUMP +# define WCOREDUMP(X) ((X)&0200) +#endif +#ifndef WIFSTOPPED +# define WIFSTOPPED(X) (((X)&0377)==0177) +#endif +#ifndef WSTOPSIG +# define WSTOPSIG(X) (((X)>>8)&0377) +#endif + +#ifdef HAVE_SYS_SELECT_H +# ifndef TIME_H_SELECT_H_CONFLICTS +# include +# endif +#elif defined(SELECT_IN_SYS_SOCKET_H) +# include +#endif + +#if defined(__APPLE__) && defined(HAVE_SELECT) +/* + * Prefer select() to poll() on MacOS X since poll() is known + * to be problematic in 10.4 + */ +#undef HAVE_POLL +#undef HAVE_POLL_H +#endif + +#ifdef HAVE_SYS_FILIO_H +# include +#endif + +#ifdef HAVE_TERMIOS_H +# ifdef __sco + /* termios.h includes sys/termio.h instead of sys/termios.h; * + * hence the declaration for struct termios is missing */ +# include +# else +# include +# endif +# ifdef _POSIX_VDISABLE +# define VDISABLEVAL _POSIX_VDISABLE +# else +# define VDISABLEVAL 0 +# endif +# define HAS_TIO 1 +#else /* not TERMIOS */ +# ifdef HAVE_TERMIO_H +# include +# define VDISABLEVAL -1 +# define HAS_TIO 1 +# else /* not TERMIOS and TERMIO */ +# include +# endif /* HAVE_TERMIO_H */ +#endif /* HAVE_TERMIOS_H */ + +#if defined(GWINSZ_IN_SYS_IOCTL) || defined(IOCTL_IN_SYS_IOCTL) +# include +#endif +#ifdef WINSIZE_IN_PTEM +# include +# include +#endif + +#ifdef HAVE_SYS_PARAM_H +# include +#endif + +#ifdef HAVE_SYS_UTSNAME_H +# include +#endif + +#define DEFAULT_WORDCHARS "*?_-.[]~=/&;!#$%^(){}<>" +#define DEFAULT_TIMEFMT "%J %U user %S system %P cpu %*E total" + +/* Posix getpgrp takes no argument, while the BSD version * + * takes the process ID as an argument */ +#ifdef GETPGRP_VOID +# define GETPGRP() getpgrp() +#else +# define GETPGRP() getpgrp(0) +#endif + +#ifndef HAVE_GETLOGIN +# define getlogin() cuserid(NULL) +#endif + +#ifdef HAVE_SETPGID +# define setpgrp setpgid +#endif + +/* can we set the user/group id of a process */ + +#ifndef HAVE_SETUID +# ifdef HAVE_SETREUID +# define setuid(X) setreuid(X,X) +# define setgid(X) setregid(X,X) +# define HAVE_SETUID +# endif +#endif + +/* can we set the effective user/group id of a process */ + +#ifndef HAVE_SETEUID +# ifdef HAVE_SETREUID +# define seteuid(X) setreuid(-1,X) +# define setegid(X) setregid(-1,X) +# define HAVE_SETEUID +# else +# ifdef HAVE_SETRESUID +# define seteuid(X) setresuid(-1,X,-1) +# define setegid(X) setresgid(-1,X,-1) +# define HAVE_SETEUID +# endif +# endif +#endif + +#ifdef HAVE_SYS_RESOURCE_H +# include +# if defined(__hpux) && !defined(RLIMIT_CPU) +/* HPUX does have the BSD rlimits in the kernel. Officially they are * + * unsupported but quite a few of them like RLIMIT_CORE seem to work. * + * All the following are in the but made visible * + * only for the kernel. */ +# define RLIMIT_CPU 0 +# define RLIMIT_FSIZE 1 +# define RLIMIT_DATA 2 +# define RLIMIT_STACK 3 +# define RLIMIT_CORE 4 +# define RLIMIT_RSS 5 +# define RLIMIT_NOFILE 6 +# define RLIMIT_OPEN_MAX RLIMIT_NOFILE +# define RLIM_NLIMITS 7 +# define RLIM_INFINITY 0x7fffffff +# endif +#endif + +/* we use the SVR4 constant instead of the BSD one */ +#if !defined(RLIMIT_NOFILE) && defined(RLIMIT_OFILE) +# define RLIMIT_NOFILE RLIMIT_OFILE +#endif +#if !defined(RLIMIT_VMEM) && defined(RLIMIT_AS) +# define RLIMIT_VMEM RLIMIT_AS +#endif + +#ifdef HAVE_SYS_CAPABILITY_H +# include +#endif + +/* DIGBUFSIZ is the length of a buffer which can hold the -LONG_MAX-1 * + * (or with ZSH_64_BIT_TYPE maybe -LONG_LONG_MAX-1) * + * converted to printable decimal form including the sign and the * + * terminating null character. Below 0.30103 > lg 2. * + * BDIGBUFSIZE is for a number converted to printable binary form. */ +#define DIGBUFSIZE ((int)(((sizeof(zlong) * 8) - 1) * 30103/100000) + 3) +#define BDIGBUFSIZE ((int)((sizeof(zlong) * 8) + 4)) + +/* If your stat macros are broken, we will * + * just undefine them. */ + +#ifdef STAT_MACROS_BROKEN +# undef S_ISBLK +# undef S_ISCHR +# undef S_ISDIR +# undef S_ISDOOR +# undef S_ISFIFO +# undef S_ISLNK +# undef S_ISMPB +# undef S_ISMPC +# undef S_ISNWK +# undef S_ISOFD +# undef S_ISOFL +# undef S_ISREG +# undef S_ISSOCK +#endif /* STAT_MACROS_BROKEN. */ + +/* If you are missing the stat macros, we * + * define our own */ + +#ifndef S_IFMT +# define S_IFMT 0170000 +#endif + +#if !defined(S_ISBLK) && defined(S_IFBLK) +# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISDIR) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISDOOR) && defined(S_IFDOOR) /* Solaris */ +# define S_ISDOOR(m) (((m) & S_IFMT) == S_IFDOOR) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */ +# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +#endif +#if !defined(S_ISMPC) && defined(S_IFMPC) /* V7 */ +# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +#endif +#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */ +# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +#endif +#if !defined(S_ISOFD) && defined(S_IFOFD) /* Cray */ +# define S_ISOFD(m) (((m) & S_IFMT) == S_IFOFD) +#endif +#if !defined(S_ISOFL) && defined(S_IFOFL) /* Cray */ +# define S_ISOFL(m) (((m) & S_IFMT) == S_IFOFL) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif + +/* We will pretend to have all file types on any system. */ + +#ifndef S_ISBLK +# define S_ISBLK(m) ((void)(m), 0) +#endif +#ifndef S_ISCHR +# define S_ISCHR(m) ((void)(m), 0) +#endif +#ifndef S_ISDIR +# define S_ISDIR(m) ((void)(m), 0) +#endif +#ifndef S_ISDOOR +# define S_ISDOOR(m) ((void)(m), 0) +#endif +#ifndef S_ISFIFO +# define S_ISFIFO(m) ((void)(m), 0) +#endif +#ifndef S_ISLNK +# define S_ISLNK(m) ((void)(m), 0) +#endif +#ifndef S_ISMPB +# define S_ISMPB(m) ((void)(m), 0) +#endif +#ifndef S_ISMPC +# define S_ISMPC(m) ((void)(m), 0) +#endif +#ifndef S_ISNWK +# define S_ISNWK(m) ((void)(m), 0) +#endif +#ifndef S_ISOFD +# define S_ISOFD(m) ((void)(m), 0) +#endif +#ifndef S_ISOFL +# define S_ISOFL(m) ((void)(m), 0) +#endif +#ifndef S_ISREG +# define S_ISREG(m) ((void)(m), 0) +#endif +#ifndef S_ISSOCK +# define S_ISSOCK(m) ((void)(m), 0) +#endif + +/* file mode permission bits */ + +#ifndef S_ISUID +# define S_ISUID 04000 +#endif +#ifndef S_ISGID +# define S_ISGID 02000 +#endif +#ifndef S_ISVTX +# define S_ISVTX 01000 +#endif +#ifndef S_IRUSR +# define S_IRUSR 00400 +#endif +#ifndef S_IWUSR +# define S_IWUSR 00200 +#endif +#ifndef S_IXUSR +# define S_IXUSR 00100 +#endif +#ifndef S_IRGRP +# define S_IRGRP 00040 +#endif +#ifndef S_IWGRP +# define S_IWGRP 00020 +#endif +#ifndef S_IXGRP +# define S_IXGRP 00010 +#endif +#ifndef S_IROTH +# define S_IROTH 00004 +#endif +#ifndef S_IWOTH +# define S_IWOTH 00002 +#endif +#ifndef S_IXOTH +# define S_IXOTH 00001 +#endif +#ifndef S_IRWXU +# define S_IRWXU (S_IRUSR|S_IWUSR|S_IXUSR) +#endif +#ifndef S_IRWXG +# define S_IRWXG (S_IRGRP|S_IWGRP|S_IXGRP) +#endif +#ifndef S_IRWXO +# define S_IRWXO (S_IROTH|S_IWOTH|S_IXOTH) +#endif +#ifndef S_IRUGO +# define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) +#endif +#ifndef S_IWUGO +# define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) +#endif +#ifndef S_IXUGO +# define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH) +#endif + +#ifndef HAVE_LSTAT +# define lstat stat +#endif + +#ifndef HAVE_READLINK +# define readlink(PATH, BUF, BUFSZ) \ + ((void)(PATH), (void)(BUF), (void)(BUFSZ), errno = ENOSYS, -1) +#endif + +#ifndef F_OK /* missing macros for access() */ +# define F_OK 0 +# define X_OK 1 +# define W_OK 2 +# define R_OK 4 +#endif + +#ifndef HAVE_LCHOWN +# define lchown chown +#endif + +#ifndef HAVE_MEMCPY +# define memcpy memmove +#endif + +#ifndef HAVE_MEMMOVE +# ifndef memmove +static char *zmmv; +# define memmove(dest, src, len) (bcopy((src), zmmv = (dest), (len)), zmmv) +# endif +#endif + +#ifndef offsetof +# define offsetof(TYPE, MEM) ((char *)&((TYPE *)0)->MEM - (char *)(TYPE *)0) +#endif + +extern char **environ; + +/* + * We always need setenv and unsetenv in pairs, because + * we don't know how to do memory management on the values set. + */ +#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) && !defined(__APPLE__) +# define USE_SET_UNSET_ENV +#endif + + +/* These variables are sometimes defined in, * + * and needed by, the termcap library. */ +#if MUST_DEFINE_OSPEED +extern char PC, *BC, *UP; +extern short ospeed; +#endif + +#ifndef O_NOCTTY +# define O_NOCTTY 0 +#endif + +#ifdef _LARGEFILE_SOURCE +#ifdef HAVE_FSEEKO +#define fseek fseeko +#endif +#ifdef HAVE_FTELLO +#define ftell ftello +#endif +#endif + +/* Can't support job control without working tcsetgrp() */ +#ifdef BROKEN_TCSETPGRP +#undef JOB_CONTROL +#endif /* BROKEN_TCSETPGRP */ + +#ifdef BROKEN_KILL_ESRCH +#undef ESRCH +#define ESRCH EINVAL +#endif /* BROKEN_KILL_ESRCH */ + +/* Can we do locale stuff? */ +#undef USE_LOCALE +#if defined(CONFIG_LOCALE) && defined(HAVE_SETLOCALE) && defined(LC_ALL) +# define USE_LOCALE 1 +#endif /* CONFIG_LOCALE && HAVE_SETLOCALE && LC_ALL */ + +#ifndef MAILDIR_SUPPORT +#define mailstat(X,Y) stat(X,Y) +#endif + +#ifdef __CYGWIN__ +# include +# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\') +#else +# define IS_DIRSEP(c) ((c) == '/') +#endif + +#if defined(__GNUC__) && (!defined(__APPLE__) || defined(__clang__)) +/* Does the OS X port of gcc still gag on __attribute__? */ +#define UNUSED(x) x __attribute__((__unused__)) +#else +#define UNUSED(x) x +#endif + +/* + * The MULTIBYTE_SUPPORT configure-define specifies that we want to enable + * complete Unicode conversion between wide characters and multibyte strings. + */ +#if defined MULTIBYTE_SUPPORT \ + || (defined HAVE_WCHAR_H && defined HAVE_WCTOMB && defined __STDC_ISO_10646__) +/* + * If MULTIBYTE_SUPPORT is not defined, these includes provide a subset of + * Unicode support that makes the \u and \U printf escape sequences work. + */ + +#if defined(__hpux) && !defined(_INCLUDE__STDC_A1_SOURCE) +#define _INCLUDE__STDC_A1_SOURCE +#endif + +# include +# include +#endif +#ifdef HAVE_LANGINFO_H +# include +# ifdef HAVE_ICONV +# include +# endif +#endif + +#if defined(HAVE_INITGROUPS) && !defined(DISABLE_DYNAMIC_NSS) +# define USE_INITGROUPS +#endif + +#if defined(HAVE_GETGRGID) && !defined(DISABLE_DYNAMIC_NSS) +# define USE_GETGRGID +#endif + +#if defined(HAVE_GETGRNAM) && !defined(DISABLE_DYNAMIC_NSS) +# define USE_GETGRNAM +#endif + +#if defined(HAVE_GETPWENT) && !defined(DISABLE_DYNAMIC_NSS) +# define USE_GETPWENT +#endif + +#if defined(HAVE_GETPWNAM) && !defined(DISABLE_DYNAMIC_NSS) +# define USE_GETPWNAM +#endif + +#if defined(HAVE_GETPWUID) && !defined(DISABLE_DYNAMIC_NSS) +# define USE_GETPWUID +#endif + +#ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC +# define GET_ST_ATIME_NSEC(st) (st).st_atim.tv_nsec +#elif HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC +# define GET_ST_ATIME_NSEC(st) (st).st_atimespec.tv_nsec +#elif HAVE_STRUCT_STAT_ST_ATIMENSEC +# define GET_ST_ATIME_NSEC(st) (st).st_atimensec +#endif +#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC +# define GET_ST_MTIME_NSEC(st) (st).st_mtim.tv_nsec +#elif HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC +# define GET_ST_MTIME_NSEC(st) (st).st_mtimespec.tv_nsec +#elif HAVE_STRUCT_STAT_ST_MTIMENSEC +# define GET_ST_MTIME_NSEC(st) (st).st_mtimensec +#endif +#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC +# define GET_ST_CTIME_NSEC(st) (st).st_ctim.tv_nsec +#elif HAVE_STRUCT_STAT_ST_CTIMESPEC_TV_NSEC +# define GET_ST_CTIME_NSEC(st) (st).st_ctimespec.tv_nsec +#elif HAVE_STRUCT_STAT_ST_CTIMENSEC +# define GET_ST_CTIME_NSEC(st) (st).st_ctimensec +#endif + +#if defined(HAVE_TGETENT) && !defined(ZSH_NO_TERM_HANDLING) +# if defined(ZSH_HAVE_CURSES_H) && defined(ZSH_HAVE_TERM_H) +# define USES_TERM_H 1 +# else +# ifdef HAVE_TERMCAP_H +# define USES_TERMCAP_H 1 +# endif +# endif + +# ifdef USES_TERM_H +# ifdef HAVE_TERMIO_H +# include +# endif +# ifdef ZSH_HAVE_CURSES_H +# include "zshcurses.h" +# endif +# include "zshterm.h" +# else +# ifdef USES_TERMCAP_H +# include +# endif +# endif +#endif + +#ifdef HAVE_SRAND_DETERMINISTIC +# define srand srand_deterministic +#endif + +#ifdef ZSH_VALGRIND +# include "valgrind/valgrind.h" +# include "valgrind/memcheck.h" +#endif diff --git a/dotfiles/system/.zsh/modules/Src/ztype.h b/dotfiles/system/.zsh/modules/Src/ztype.h new file mode 100644 index 0000000..ae72367 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Src/ztype.h @@ -0,0 +1,89 @@ +/* + * ztype.h - character classification macros + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#define IDIGIT (1 << 0) +#define IALNUM (1 << 1) +#define IBLANK (1 << 2) +#define INBLANK (1 << 3) +#define ITOK (1 << 4) +#define ISEP (1 << 5) +#define IALPHA (1 << 6) +#define IIDENT (1 << 7) +#define IUSER (1 << 8) +#define ICNTRL (1 << 9) +#define IWORD (1 << 10) +#define ISPECIAL (1 << 11) +#define IMETA (1 << 12) +#define IWSEP (1 << 13) +#define INULL (1 << 14) +#define IPATTERN (1 << 15) +#define zistype(X,Y) (typtab[STOUC(X)] & Y) +#define idigit(X) zistype(X,IDIGIT) +#define ialnum(X) zistype(X,IALNUM) +#define iblank(X) zistype(X,IBLANK) /* blank, not including \n */ +#define inblank(X) zistype(X,INBLANK) /* blank or \n */ +#define itok(X) zistype(X,ITOK) +#define isep(X) zistype(X,ISEP) +#define ialpha(X) zistype(X,IALPHA) +#define iident(X) zistype(X,IIDENT) +#define iuser(X) zistype(X,IUSER) /* username char */ +#define icntrl(X) zistype(X,ICNTRL) +#define iword(X) zistype(X,IWORD) +#define ispecial(X) zistype(X,ISPECIAL) +#define imeta(X) zistype(X,IMETA) +#define iwsep(X) zistype(X,IWSEP) +#define inull(X) zistype(X,INULL) +#define ipattern(X) zistype(X,IPATTERN) + +/* + * Bit flags for typtab_flags --- preserved after + * shell initialisation. + */ +#define ZTF_INIT (0x0001) /* One-off initialisation done */ +#define ZTF_INTERACT (0x0002) /* Shell interative and reading from stdin */ +#define ZTF_SP_COMMA (0x0004) /* Treat comma as a special characters */ +#define ZTF_BANGCHAR (0x0008) /* Treat bangchar as a special character */ + +#ifdef MULTIBYTE_SUPPORT +#define WC_ZISTYPE(X,Y) wcsitype((X),(Y)) +# ifdef ENABLE_UNICODE9 +# define WC_ISPRINT(X) u9_iswprint(X) +# else +# define WC_ISPRINT(X) iswprint(X) +# endif +#else +#define WC_ZISTYPE(X,Y) zistype((X),(Y)) +#define WC_ISPRINT(X) isprint(X) +#endif + +#if defined(__APPLE__) && defined(BROKEN_ISPRINT) +#define ZISPRINT(c) isprint_ascii(c) +#else +#define ZISPRINT(c) isprint(c) +#endif diff --git a/dotfiles/system/.zsh/modules/Test/.cvsignore b/dotfiles/system/.zsh/modules/Test/.cvsignore new file mode 100644 index 0000000..855d729 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/.cvsignore @@ -0,0 +1,3 @@ +Makefile +*.tmp +*.swp diff --git a/dotfiles/system/.zsh/modules/Test/.distfiles b/dotfiles/system/.zsh/modules/Test/.distfiles new file mode 100644 index 0000000..f03668b --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/.distfiles @@ -0,0 +1,2 @@ +DISTFILES_SRC=' +' diff --git a/dotfiles/system/.zsh/modules/Test/A01grammar.ztst b/dotfiles/system/.zsh/modules/Test/A01grammar.ztst new file mode 100644 index 0000000..e4b6870 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A01grammar.ztst @@ -0,0 +1,790 @@ +# +# This file contains tests corresponding to the `Shell Grammar' texinfo node. +# + +%prep + + mkdir basic.tmp && cd basic.tmp + + touch foo bar + echo "'" >unmatched_quote.txt + +%test +# +# 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 + + false | true +0:Exit status of pipeline with builtins (true) + + true | false +1:Exit status of pipeline with builtins (false) + + false + $nonexistent_variable +0:Executing command that evaluates to empty resets status + + false + sleep 1 & + print $? + # a tidy test is a happy test + wait $! +0:Starting background command resets status +>0 + + false + . /dev/null +0:Sourcing empty file resets status + + fn() { local foo; read foo; print $foo; } + coproc fn + print -p coproc test output + read -p bar + print $bar +0:Basic coprocess handling +>coproc test output + + true | false && print true || print false +0:Basic sublist (i) +>false + + false | true && print true || print false +0:Basic sublist (ii) +>true + + (cd /NonExistentDirectory >&/dev/null) || print false +0:Basic subshell list with error +>false + + { cd /NonExistentDirectory >&/dev/null } || print false +0:Basic current shell list with error +>false + +# +# Tests for `Precommand Modifiers' +# + - $ZTST_testdir/../Src/zsh -fc "[[ \$0 = \"-$ZTST_testdir/../Src/zsh\" ]]" +0:`-' precommand modifier + + echo f* + noglob echo f* +0:`noglob' precommand modifier +>foo +>f* + + (exec /bin/sh; echo bar) +0:`exec' precommand modifier + + (exec -l $ZTST_testdir/../Src/zsh -fc 'echo $0' | sed 's%/.*/%%' ) +0:`exec' with -l option +>-zsh + + (exec -a /bin/SPLATTER /bin/sh -c 'echo $0') +0:`exec' with -a option +>/bin/SPLATTER + + (exec -a/bin/SPLOOSH /bin/sh -c 'echo $0') +0:`exec' with -a option, no space +>/bin/SPLOOSH + + (export FOO=bar; exec -c /bin/sh -c 'echo x${FOO}x') +0:`exec' with -c option +>xx + + cat() { echo Function cat executed; } + command cat && unfunction cat +0:`command' precommand modifier +External command cat executed + + command -pv cat + command -pv echo + command -p -V cat + command -p -V -- echo +0:command -p in combination +*>*/cat +>echo +>cat is /*/cat +>echo is a shell builtin + + cd() { echo Not cd at all; } + builtin cd . && unfunction cd +0:`builtin' precommand modifier + +# +# Tests for `Complex Commands' +# + + if true; then + print true-1 + elif true; then + print true-2 + else + print false + fi +0:`if ...' (i) +>true-1 + + if false; then + print true-1 + elif true; then + print true-2 + else + print false + fi +0:`if ...' (ii) +>true-2 + + if false; then + print true-1 + elif false; then + print true-2 + else + print false + fi +0:`if ...' (iii) +>false + + if true; + : + fi +1d:`if ...' (iv) +?(eval):3: parse error near `fi' + + for name in word to term; do + print $name + done +0:`for' loop +>word +>to +>term + + for name + in word to term; do + print $name + done +0:`for' loop with newline before in keyword +>word +>to +>term + + for (( name = 0; name < 3; name++ )); do + print $name + done +0:arithmetic `for' loop +>0 +>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 +0:enhanced `for' syntax with two loop variables +>key=key1 val=val1 +>key=key2 val=val2 + + for keyvar valvar stuffvar in keyA valA stuffA keyB valB stuffB; do + print key=$keyvar val=$valvar stuff=$stuffvar + done +0:enhanced `for' syntax with three loop variables +>key=keyA val=valA stuff=stuffA +>key=keyB val=valB stuff=stuffB + + for in in in in in stop; do + print in=$in + done +0:compatibility of enhanced `for' syntax with standard syntax +>in=in +>in=in +>in=in +>in=stop + + name=0 + while (( name < 3 )); do + print $name + (( name++ )) + done +0:`while' loop +>0 +>1 +>2 + + name=0 + until (( name == 3 )); do + print $name + (( name++ )) + done +0:`until' loop +>0 +>1 +>2 + + repeat 3 do + echo over and over + done +0:`repeat' loop +>over and over +>over and over +>over and over + + word=Trinity + case $word in + Michaelmas) print 0 + ;; + Hilary) print 1 + ;; + Trinity) print 2 + ;; + *) print 3 + ;; + esac +0:`case', old syntax +>2 + + word=Trinity + case $word in + (Michaelmas) print 0 + ;; + (Hilary) print 1 + ;; + (Trinity) print 2 + ;; + (*) print 3 + ;; + esac +0:`case', new syntax +>2 + + word=Hilary + case $word in + (Michaelmas) print 0 + ;; + (Hilary) print 1 + ;& + (Trinity) print 2 + ;& + (*) print 3 + ;; + esac +0:`case', new syntax, cascaded +>1 +>2 +>3 + + case whatever in + (*) print yeah, right ;& + esac + print but well +0:'case', redundant final ";&" +>yeah, right +>but well + +## Select now reads from stdin if the shell is not interactive. +## Its own output goes to stderr. + (COLUMNS=80 LINES=3 + PS3="input> " + select name in one two three; do + print $name + done) +0:`select' loop +<2 +?1) one 2) two 3) three +?input> input> +>two + + function name1 name2 () { print This is $0; } + name2 + name1 name2() { print This is still $0; } + name2 +0:`function' keyword +>This is name2 +>This is still name2 + + (time cat) >&/dev/null +0:`time' keyword (status only) + + if [[ -f foo && -d . && -n $ZTST_testdir ]]; then + true + else + false + fi +0:basic [[ ... ]] test + +# +# Current shell execution with try/always form. +# We put those with errors in subshells so that any unhandled error doesn't +# propagate. +# + + { + print The try block. + } always { + print The always block. + } + print After the always block. +0:Basic `always' syntax +>The try block. +>The always block. +>After the always block. + + ({ + print Position one. + print ${*this is an error*} + print Position two. + } always { + if (( TRY_BLOCK_ERROR )); then + print An error occurred. + else + print No error occurred. + fi + } + print Position three) +1:Always block with error not reset +>Position one. +>An error occurred. +?(eval):3: bad substitution + + ({ + print Stelle eins. + print ${*voici une erreur} + print Posizione due. + } always { + if (( TRY_BLOCK_ERROR )); then + print Erratum factum est. Retro ponetur. + (( TRY_BLOCK_ERROR = 0 )) + else + print unray touay foay anguageslay + fi + } + print Status after always block is $?.) +0:Always block with error reset +>Stelle eins. +>Erratum factum est. Retro ponetur. +>Status after always block is 1. +?(eval):3: bad substitution + + fn() { { return } always { echo always 1 }; echo not executed } + fn + fn() { { echo try 2 } always { return }; echo not executed } + fn +0:Always block interaction with return +>always 1 +>try 2 + +# Outputting of structures from the wordcode is distinctly non-trivial, +# we probably ought to have more like the following... + fn1() { { echo foo; } } + fn2() { { echo foo; } always { echo bar; } } + fn3() { ( echo foo; ) } + functions fn1 fn2 fn3 +0:Output of syntactic structures with and without always blocks +>fn1 () { +> { +> echo foo +> } +>} +>fn2 () { +> { +> echo foo +> } always { +> echo bar +> } +>} +>fn3 () { +> ( +> echo foo +> ) +>} + + +# +# Tests for `Alternate Forms For Complex Commands' +# + + if (true) { print true-1 } elif (true) { print true-2 } else { print false } + if (false) { print true-1 } elif (true) { print true-2 } else { print false } + if (false) { print true-1 } elif (false) { print true-2 } else { print false } +0:Alternate `if' with braces +>true-1 +>true-2 +>false + + if { true } print true + if { false } print false +0:Short form of `if' +>true + + eval "if" +1:Short form of `if' can't be too short +?(eval):1: parse error near `if' + + for name ( word1 word2 word3 ) print $name +0:Form of `for' with parentheses. +>word1 +>word2 +>word3 + + for name in alpha beta gamma; print $name +0:Short form of `for' +>alpha +>beta +>gamma + + for (( val = 2; val < 10; val *= val )) print $val +0:Short arithmetic `for' +>2 +>4 + + foreach name ( verbiage words periphrasis ) + print $name + end +0:Csh-like `for' +>verbiage +>words +>periphrasis + +# see comment with braces used in if loops + val=0; + while (( val < 2 )) { print $((val++)); } +0:Alternative `while' +>0 +>1 + + val=2; + until (( val == 0 )) { print $((val--)); } +0:Alternative `until' +>2 +>1 + + repeat 3 print Hip hip hooray +0:Short `repeat' +>Hip hip hooray +>Hip hip hooray +>Hip hip hooray + + case bravo { + (alpha) print schmalpha + ;; + (bravo) print schmavo + ;; + (charlie) print schmarlie + ;; + } +0:`case' with braces +>schmavo + + for word in artichoke bladderwort chrysanthemum Zanzibar + case $word in + (*der*) print $word contains the forbidden incantation der + ;; + (a*) print $word begins with a + ;& + ([[:upper:]]*) print $word either begins with a or an upper case letter + ;| + ([[:lower:]]*) print $word begins with a lower case letter + ;| + (*e*) print $word contains an e + ;; + esac +0:`case' with mixed ;& and ;| +>artichoke begins with a +>artichoke either begins with a or an upper case letter +>artichoke begins with a lower case letter +>artichoke contains an e +>bladderwort contains the forbidden incantation der +>chrysanthemum begins with a lower case letter +>chrysanthemum contains an e +>Zanzibar either begins with a or an upper case letter + + print -u $ZTST_fd 'This test hangs the shell when it fails...' + name=0 +# The number 4375 here is chosen to produce more than 16384 bytes of output + while (( name < 4375 )); do + print -n $name + (( name++ )) + done < /dev/null | { read name; print done } +0:Bug regression: `while' loop with redirection and pipeline +>done + +# This used to be buggy and print X at the end of each iteration. + for f in 1 2 3 4; do + print $f || break + done && print X +0:Handling of ||'s and &&'s with a for loop in between +>1 +>2 +>3 +>4 +>X + +# Same bug for &&, used to print `no' at the end of each iteration + for f in 1 2 3 4; do + false && print strange + done || print no +0:Handling of &&'s and ||'s with a for loop in between +>no + + $ZTST_testdir/../Src/zsh -f unmatched_quote.txt +1:Parse error with file causes non-zero exit status +?unmatched_quote.txt:2: unmatched ' + + $ZTST_testdir/../Src/zsh -f value +>not#comment + + . ./nonexistent +127: Attempt to "." non-existent file. +?(eval):.:1: no such file or directory: ./nonexistent + + echo '[[' >bad_syntax + . ./bad_syntax +126: Attempt to "." file with bad syntax. +?./bad_syntax:2: parse error near `\n' +# ` + + echo 'false' >dot_false + . ./dot_false + print $? + echo 'true' >dot_true + . ./dot_true + print $? +0:Last status of successfully executed "." file is retained +>1 +>0 + + echo 'echo $?' >dot_status + false + . ./dot_status +0:"." file sees status from previous command +>1 + + mkdir test_path_script + print "#!/bin/sh\necho Found the script." >test_path_script/myscript + chmod u+x test_path_script/myscript + path=($PWD/test_path_script $path) + export PATH + $ZTST_testdir/../Src/zsh -f -o pathscript myscript +0:PATHSCRIPT option +>Found the script. + + $ZTST_testdir/../Src/zsh -f myscript +127q:PATHSCRIPT option not used. +?$ZTST_testdir/../Src/zsh: can't open input file: myscript +# ' + + $ZTST_testdir/../Src/zsh -fc 'echo $0; echo $1' myargzero myargone +0:$0 is traditionally if bizarrely set to the first argument with -c +>myargzero +>myargone + + (setopt shglob + eval ' + if ! (echo success1); then echo failure1; fi + if !(echo success2); then echo failure2; fi + print -l one two | while(read foo)do(print read it)done + ') +0:Parentheses in shglob +>success1 +>success2 +>read it +>read it + + ( + mywrap() { echo BEGIN; true; echo END } + mytest() { { exit 3 } always { mywrap }; print Exited before this } + mytest + print Exited before this, too + ) +3:Exit and always block with functions: simple +>BEGIN +>END + + ( + mytrue() { echo mytrue; return 0 } + mywrap() { echo BEGIN; mytrue; echo END } + mytest() { { exit 4 } always { mywrap }; print Exited before this } + mytest + print Exited before this, too + ) +4:Exit and always block with functions: nested +>BEGIN +>mytrue +>END + + (emulate sh -c ' + fn() { + case $1 in + ( one | two | three ) + print Matched $1 + ;; + ( fo* | fi* | si* ) + print Pattern matched $1 + ;; + ( []x | a[b]* ) + print Character class matched $1 + ;; + esac + } + ' + which fn + fn one + fn two + fn three + fn four + fn five + fn six + fn abecedinarian + fn xylophone) +0: case word handling in sh emulation (SH_GLOB parentheses) +>fn () { +> case $1 in +> (one | two | three) print Matched $1 ;; +> (fo* | fi* | si*) print Pattern matched $1 ;; +> ([]x | a[b]*) print Character class matched $1 ;; +> esac +>} +>Matched one +>Matched two +>Matched three +>Pattern matched four +>Pattern matched five +>Pattern matched six +>Character class matched abecedinarian + + case grumph in + ( no | (grumph) ) + print 1 OK + ;; + esac + case snruf in + ( fleer | (|snr(|[au]f)) ) + print 2 OK + ;; + esac +0: case patterns within words +>1 OK +>2 OK + + case horrible in + ([a-m])(|[n-z])rr(|ib(um|le|ah))) + print It worked + ;; + esac + case "a string with separate words" in + (*with separate*)) + print That worked, too + ;; + esac +0:Unbalanced parentheses and spaces with zsh pattern +>It worked +>That worked, too + + case horrible in + (([a-m])(|[n-z])rr(|ib(um|le|ah))) + print It worked + ;; + esac + case "a string with separate words" in + (*with separate*) + print That worked, too + ;; + esac +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 + + (exit 37) + case $? in + (37) echo $? + ;; + esac +0:case retains exit status for execution of cases +>37 + + false + case stuff in + (nomatch) foo + ;; + esac + echo $? +0:case sets exit status to zero if no patterns are matched +>0 + + case match in + (match) true; false; (exit 37) + ;; + esac + echo $? +0:case keeps exit status of last command executed in compound-list +>37 + + x=1 + x=2 | echo $x + echo $x +0:Assignment-only current shell commands in LHS of pipelin +>1 +>1 diff --git a/dotfiles/system/.zsh/modules/Test/A02alias.ztst b/dotfiles/system/.zsh/modules/Test/A02alias.ztst new file mode 100644 index 0000000..e68e93e --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A02alias.ztst @@ -0,0 +1,139 @@ +# To get the "command not found" message when aliasing is suppressed +# we need, er, a command that isn't found. +# The other aliases are only ever used as aliases. + +%prep + alias ThisCommandDefinitelyDoesNotExist=echo + + alias -g bar=echo + + alias '\bar=echo' + +%test + ThisCommandDefinitelyDoesNotExist ThisCommandDefinitelyDoesNotExist +0:Basic aliasing +>ThisCommandDefinitelyDoesNotExist + + bar bar +0:Global aliasing +>echo + + \ThisCommandDefinitelyDoesNotExist ThisCommandDefinitelyDoesNotExist +127:Not aliasing +?(eval):1: command not found: ThisCommandDefinitelyDoesNotExist + + \bar \bar +0:Aliasing with a backslash +>bar + + (alias '!=echo This command has the argument' + eval 'print Without + ! true' + setopt posixaliases + eval 'print With + ! true') +1:POSIX_ALIASES option +>Without +>This command has the argument true +>With + + print -u $ZTST_fd 'This test hangs the shell when it fails...' + alias cat='LC_ALL=C cat' + cat <(echo foo | cat) +0:Alias expansion works at the end of parsed strings +>foo + + alias -g '&&=(){ return $?; } && ' + alias not_the_print_command=print + eval 'print This is output + && print And so is this + && { print And this too; false; } + && print But not this + && print Nor this + true + && not_the_print_command And aliases are expanded' +0:We can now alias special tokens. Woo hoo. +>This is output +>And so is this +>And this too +>And aliases are expanded + + $ZTST_testdir/../Src/zsh -fis <<<' + unsetopt PROMPT_SP + PROMPT="" PS2="" PS3="" PS4="" RPS1="" RPS2="" + exec 2>&1 + alias \{=echo + { begin + {end + fc -l -2' 2>/dev/null +0:Aliasing reserved tokens +>begin +>end +*>*5*{ begin +*>*6*{end + + $ZTST_testdir/../Src/zsh -fis <<<' + unsetopt PROMPT_SP + PROMPT="" PS2="" PS3="" PS4="" RPS1="" RPS2="" + exec 2>&1 + alias -g S=\" + echo S a string S " + fc -l -1' 2>/dev/null +0:Global aliasing quotes +> a string S +*>*5*echo S a string S " +# " +# Note there is a trailing space on the "> a string S " line + + ( + unalias -a + alias + ) +0:unalias -a + + alias -s foo=print + type bar.foo; type -w bar.foo + unalias -as +0:unalias -as +>foo is a suffix alias for print +>foo: suffix alias + + aliases[x=y]=z + alias -L | grep x=y + echo $pipestatus[1] +0:printing invalid aliases warns +>0 +?(eval):2: invalid alias 'x=y' encountered while printing aliases +# Currently, 'alias -L' returns 0 in this case. Perhaps it should return 1. + + alias -s mysuff='print -r "You said it.";' + eval 'thingummy.mysuff' +127:No endless loop with suffix alias in command position +>You said it. +?(eval):1: command not found: thingummy.mysuff + + alias +x; alias -z +1:error message has the correct sign +?(eval):alias:1: bad option: +x +?(eval):alias:1: bad option: -z + + # Usual issue that aliases aren't expanded until we + # trigger a new parse... + (alias badalias=notacommand + eval 'badalias() { print does not work; }') +1:ALIAS_FUNC_DEF off by default. +?(eval):1: defining function based on alias `badalias' +?(eval):1: parse error near `()' + + (alias goodalias=isafunc + setopt ALIAS_FUNC_DEF + eval 'goodalias() { print does now work; }' + isafunc) +0:ALIAS_FUNC_DEF causes the icky behaviour to be avaliable +>does now work + + (alias thisisokthough='thisworks() { print That worked; }' + eval thisisokthough + thisworks) +0:NO_ALIAS_FUNC_DEF works if the alias is a complete definition +>That worked diff --git a/dotfiles/system/.zsh/modules/Test/A03quoting.ztst b/dotfiles/system/.zsh/modules/Test/A03quoting.ztst new file mode 100644 index 0000000..da3ce35 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A03quoting.ztst @@ -0,0 +1,80 @@ +%test + print 'single quotes' "double quotes" `echo backquotes` +0:Simple use of quotes +>single quotes double quotes backquotes + + foo=text + print -r '$foo\\\' "$foo\$foo\\\"\``echo bar`\`\"" `print -r $foo\\\`` +0:Quoting inside quotes +>$foo\\\ text$foo\"`bar`" text` + + print -r $'\'ut queant laxis\'\n"resonare fibris"' +0:$'-style quotes +>'ut queant laxis' +>"resonare fibris" + + print -r $'\'a \\\' is \'a backslash\' is \'a \\\'' +0:$'-style quotes with backslashed backslashes +>'a \' is 'a backslash' is 'a \' + + chars=$(print -r $'BS\\MBS\M-\\') + for (( i = 1; i <= $#chars; i++ )); do + char=$chars[$i] + print $(( [#16] #char )) + done +0:$'-style quote with metafied backslash +>16#42 +>16#53 +>16#5C +>16#4D +>16#42 +>16#53 +>16#DC + + print -r '''' + setopt rcquotes +# We need to set rcquotes here for the next example since it is +# needed while parsing. +0:No RC_QUOTES with single quotes +> + + print -r '''' + unsetopt rcquotes +0:Yes RC_QUOTES with single quotes +>' +# ' Deconfuse Emacs quoting rules + + print '<\u0041>' + printf '%s\n' $'<\u0042>' + print '<\u0043>' + printf '%s\n' $'<\u0044>' +0:\u in both print and printf +> +> +> +> + + null1="$(print -r a$'b\0c'd)" + null2="$(setopt posixstrings; print -r a$'b\0c'd)" + for string in $null1 $null2; do + print ":" + for (( i = 1; i <= $#string; i++ )); do + char=$string[$i] + print $(( [#16] #char )) + done + done +0:Embedded null characters in $'...' strings. +>: +>16#61 +>16#62 +>16#0 +>16#63 +>16#64 +>: +>16#61 +>16#62 +>16#64 + + () { print $# } '' "" $'' +0:$'' should not be elided, in common with other empty quotes +>3 diff --git a/dotfiles/system/.zsh/modules/Test/A04redirect.ztst b/dotfiles/system/.zsh/modules/Test/A04redirect.ztst new file mode 100644 index 0000000..d7fe22f --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A04redirect.ztst @@ -0,0 +1,588 @@ +# Tests corresponding to the `Redirection' texinfo node. + +%prep + mkdir redir.tmp && cd redir.tmp + + myfd=99 + (echo >&$myfd) 2>msg + bad_fd_msg="${$(redir && cat redir +0:'>' and '<' redirection +>This is file redir + + rm -f redir + print 'This is still file redir' <>redir >&0 && cat <>redir +0:'<>' redirection +>This is still file redir + + rm -f redir + print 'With a bar' >|redir && cat redir +0:'>|' redirection +>With a bar + + rm -f redir + print 'With a bang' >!redir && cat redir +0:'>!' redirection +>With a bang + + rm -f redir + print 'Line 1' >>redir && print 'Line 2' >>redir && cat redir +0:'>>' redirection +>Line 1 +>Line 2 + + rm -f redir + print 'Line a' >>|redir && print 'Line b' >>!redir +0:'>>|' and '>>!' redirection + + foo=bar + cat <<' HERE' + $foo + HERE + eval "$(print 'cat < $foo +>bar + + cat <<-HERE +# note tabs at the start of the following lines + $foo$foo + HERE +0:Here-documents stripping tabs +>barbar + + cat <<-$'$HERE '`$(THERE) `'$((AND)) '"\EVERYWHERE" # +# tabs again. sorry about the max miller. + Here's a funny thing. Here is a funny thing. + I went home last night. There's a funny thing. + Man walks into a $foo. Ouch, it's an iron $foo. + $HERE `$(THERE) `$((AND)) \EVERYWHERE +0:Here-documents don't perform shell expansion on the initial word +>Here's a funny thing. Here is a funny thing. +>I went home last night. There's a funny thing. +>Man walks into a $foo. Ouch, it's an iron $foo. + + cat <<-$'\x45\x4e\x44\t\x44\x4f\x43' +# tabs again + This message is unfathomable. + END DOC +0:Here-documents do perform $'...' expansion on the initial word +>This message is unfathomable. + + cat <<<"This is a line with a $foo in it" +0:'<<<' redirection +>This is a line with a bar in it + + cat <<<$'a\nb\nc' +0:here-strings with $'...' quoting +>a +>b +>c + +# The following tests check that output of parsed here-documents works. +# This isn't completely trivial because we convert the here-documents +# internally to here-strings. So we check again that we can output +# the reevaluated here-strings correctly. Hence there are three slightly +# different stages. We don't care how the output actually looks, so +# we don't test that. + heretest() { + print First line + cat <<-HERE + $foo$foo met celeste 'but with extra' "stuff to test quoting" + HERE + print Last line + } + heretest + eval "$(functions heretest)" + heretest + eval "$(functions heretest)" + heretest +0:Re-evaluation of function output with here document, unquoted +>First line +>barbar met celeste 'but with extra' "stuff to test quoting" +>Last line +>First line +>barbar met celeste 'but with extra' "stuff to test quoting" +>Last line +>First line +>barbar met celeste 'but with extra' "stuff to test quoting" +>Last line + + heretest() { + print First line + cat <<' HERE' + $foo$foo met celeste 'but with extra' "stuff to test quoting" + HERE + print Last line + } + heretest + eval "$(functions heretest)" + heretest + eval "$(functions heretest)" + heretest +0:Re-evaluation of function output with here document, quoted +>First line +> $foo$foo met celeste 'but with extra' "stuff to test quoting" +>Last line +>First line +> $foo$foo met celeste 'but with extra' "stuff to test quoting" +>Last line +>First line +> $foo$foo met celeste 'but with extra' "stuff to test quoting" +>Last line + + read -r line <<' HERE' + HERE +1:No input, not even newline, from empty here document. + + # + # exec tests: perform these in subshells so if they fail the + # shell won't exit. + # + (exec 3>redir && print hello >&3 && print goodbye >&3 && cat redir) +0:'>&' redirection +>hello +>goodbye + + (exec 3hello +>goodbye + + ({exec 3<&- } 2>/dev/null + exec 3<&- + read foo <&-) +1:'<&-' redirection with numeric fd (no error message on failure) + + (exec {varid}<&0 + exec {varid}<&- + print About to close a second time >&2 + read {varid}<&-) +1:'<&-' redirection with fd in variable (error message on failure) +?About to close a second time +*?\(eval\):*: failed to close file descriptor * + + print foo >&- +0:'>&-' redirection + + (exec >&- + print foo) +0:'>&-' with attempt to use closed fd +*?\(eval\):2: write error:* + + fn() { local foo; read foo; print $foo; } + coproc fn + print test output >&p + read bar <&p + print $bar +0:'>&p' and '<&p' redirection +>test output + + ( print Output; print Error >& 2 ) >&errout && cat errout +0:'>&FILE' handling +>Output +>Error + + rm -f errout + ( print Output2; print Error2 >& 2 ) &>errout && cat errout +0:'&>FILE' handling +>Output2 +>Error2 + + rm -f errout + ( print Output3; print Error3 >& 2 ) >&|errout && cat errout + ( print Output4; print Error4 >& 2 ) >&!errout && cat errout + ( print Output5; print Error5 >& 2 ) &>|errout && cat errout + ( print Output6; print Error6 >& 2 ) &>!errout && + ( print Output7; print Error7 >& 2 ) >>&errout && + ( print Output8; print Error8 >& 2 ) &>>errout && + ( print Output9; print Error9 >& 2 ) >>&|errout && + ( print Output10; print Error10 >& 2 ) &>>|errout && + ( print Output11; print Error11 >& 2 ) >>&!errout && + ( print Output12; print Error12 >& 2 ) &>>!errout && cat errout +0:'>&|', '>&!', '&>|', '&>!' redirection +>Output3 +>Error3 +>Output4 +>Error4 +>Output5 +>Error5 +>Output6 +>Error6 +>Output7 +>Error7 +>Output8 +>Error8 +>Output9 +>Error9 +>Output10 +>Error10 +>Output11 +>Error11 +>Output12 +>Error12 + + rm -f errout + ( print Output; print Error 1>&2 ) 1>errout 2>&1 && cat errout +0:'Combining > with >& (1)' +>Output +>Error + + rm -f errout + ( print Output; print Error 1>&2 ) 2>&1 1>errout && print errout: && + cat errout +0:'Combining > with >& (2)' +>Error +>errout: +>Output + + rm -f errout + print doo be doo be doo >foo >bar + print "foo: $(foo: doo be doo be doo +>bar: doo be doo be doo + + rm -f foo bar + print dont be dont be dont >foo | sed 's/dont/wont/g' >bar +0:setup file+pipe multio + + print "foo: $(foo: dont be dont be dont +>bar: wont be wont be wont + + rm -f * + touch out1 out2 + print All files >* + print * + print "out1: $(out1 out2 +>out1: All files +>out2: All files + + print This is out1 >out1 + print This is out2 >out2 +0:setup multio for input + +# Currently, This is out1 +>This is out2 + + cat out1 | sed s/out/bout/ This is bout1 +>This is bout2 + + unset NULLCMD + >out1 +1:null redir with NULLCMD unset +?(eval):2: redirection with no command + + echo this should still work >out1 + print "$(this should still work + + READNULLCMD=cat + print cat input >out1 + out1 + [[ ! -s out1 ]] || print out1 is not empty +0:null redir with NULLCMD=: +out1 + cat input + + NULLCMD=cat + >out1 + cat out1 +0:null redir with NULLCMD=cat +input + + (myfd= + exec {myfd}>logfile + if [[ -z $myfd ]]; then + print "Ooops, failed to set myfd to a file descriptor." >&2 + else + print This is my logfile. >&$myfd + print Examining contents of logfile... + cat logfile + fi) +0:Using {fdvar}> syntax to open a new file descriptor +>Examining contents of logfile... +>This is my logfile. + + (setopt noclobber + exec {myfd}>logfile2 + echo $myfd + exec {myfd}>logfile3) | read myfd + (( ! ${pipestatus[1]} )) +1q:NO_CLOBBER prevents overwriting parameter with allocated fd +?(eval):4: can't clobber parameter myfd containing file descriptor $myfd + + (setopt noclobber + exec {myfd}>logfile2b + print First open >&$myfd + rm -f logfile2b # prevent normal file no_clobberation + myotherfd="${myfd}+0" + exec {myotherfd}>logfile2b + print Overwritten >&$myotherfd) + cat logfile2b +0:NO_CLOBBER doesn't complain about any other expression +>Overwritten + + (exec {myfd}>logfile4 + echo $myfd + exec {myfd}>&- + print This message should disappear >&$myfd) | read myfd + (( ! ${pipestatus[1]} )) +1q:Closing file descriptor using brace syntax +?(eval):4: $myfd:$bad_fd_msg + + typeset -r myfd + echo This should not appear {myfd}>nologfile +1:Error opening file descriptor using readonly variable +?(eval):2: can't allocate file descriptor to readonly parameter myfd + + (typeset +r myfd + exec {myfd}>newlogfile + typeset -r myfd + exec {myfd}>&-) +1:Error closing file descriptor using readonly variable +?(eval):4: can't close file descriptor from readonly parameter myfd + +# This tests the here-string to filename optimisation; we can't +# test that it's actually being optimised, but we can test that it +# still works. + cat =(<<<$'This string has been replaced\nby a file containing it.\n') +0:Optimised here-string to filename +>This string has been replaced +>by a file containing it. + + print This f$'\x69'le contains d$'\x61'ta. >redirfile + print redirection: + catoutfile + print output: + cat outfile + print append: + cat>>outfileredirection: +>output: +>This file contains data. +>append: +>more output: +>This file contains data. +>This file contains data. + + $ZTST_testdir/../Src/zsh -fc 'exec >/nonexistent/nonexistent + echo output' +0:failed exec redir, no POSIX_BUILTINS +>output +?zsh:1: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -o POSIX_BUILTINS -c ' + exec >/nonexistent/nonexistent + echo output' +1:failed exec redir, POSIX_BUILTINS +?zsh:2: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -o POSIX_BUILTINS -c ' + set >/nonexistent/nonexistent + echo output' +1:failed special builtin redir, POSIX_BUILTINS +?zsh:2: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -o POSIX_BUILTINS -c ' + command set >/nonexistent/nonexistent + echo output' +0:failed special builtin redir with command prefix, POSIX_BUILTINS +>output +?zsh:2: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -o POSIX_BUILTINS -c ' + echo >/nonexistent/nonexistent + echo output' +0:failed unspecial builtin redir, POSIX_BUILTINS +>output +?zsh:2: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -o POSIX_BUILTINS -c ' + . /nonexistent/nonexistent + echo output' +1:failed dot, POSIX_BUILTINS +?zsh:.:2: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -c ' + . /nonexistent/nonexistent + echo output' +0:failed dot, NO_POSIX_BUILTINS +>output +?zsh:.:2: no such file or directory: /nonexistent/nonexistent + + $ZTST_testdir/../Src/zsh -f -o CONTINUE_ON_ERROR <<<' + readonly foo + foo=bar set output + echo output' +0:failed assignment on posix special, CONTINUE_ON_ERROR +>output +?zsh: read-only variable: foo + + $ZTST_testdir/../Src/zsh -f <<<' + readonly foo + foo=bar set output + echo output' +1:failed assignment on posix special, NO_CONTINUE_ON_ERROR +?zsh: read-only variable: foo + + $ZTST_testdir/../Src/zsh -f -o CONTINUE_ON_ERROR <<<' + readonly foo + foo=bar echo output + echo output' +0:failed assignment on non-posix-special, CONTINUE_ON_ERROR +>output +?zsh: read-only variable: foo + + [input1 + () { + local var + read var + print I just read $var + } output1 + print Nothing output yet + cat output1 +0:anonymous function redirections are applied immediately +>Nothing output yet +>I just read any old rubbish + + redirfn() { + local var + read var + print I want to tell you about $var + print Also, this might be an error >&2 + } output2 2>&1 + print something I heard on the radio >input2 + redirfn + print No output until after this + cat output2 +0:redirections with normal function definition +>No output until after this +>I want to tell you about something I heard on the radio +>Also, this might be an error + + which redirfn +0:text output of function with redirections +>redirfn () { +> local var +> read var +> print I want to tell you about $var +> print Also, this might be an error >&2 +>} < input2 > output2 2>&1 + + 1func 2func 3func() { print Ich heisse $0 } >output3 + for i in 1 2 3; do + f=${i}func + print Running $f + $f + cat output3 + unfunction $f + done +0:multiply named functions with redirection +>Running 1func +>Ich heisse 1func +>Running 2func +>Ich heisse 2func +>Running 3func +>Ich heisse 3func + + redirfn2() { print The latest output; } >&3 + redirfn2 3>output4 + print No output yet + cat output4 +0:Redirections in both function definition and command line +>No output yet +>The latest output + +# This relies on the fact that the test harness always loads +# the zsh/parameter module. + print $functions[redirfn] +0:Output from $functions[] for definition with redirection +>{ +> local var +> read var +> print I want to tell you about $var +> print Also, this might be an error >&2 +>} < input2 > output2 2>&1 + + noredirfn() { print This rather boring function has no redirection.; } + print $functions[noredirfn] +0:Output from $functions[] for definition with no redirection +> print This rather boring function has no redirection. + + (x=43 + x=$(print This should appear, really >&2; print Not used) exec >test.log + print x=$x) + cat test.log +0:Assignment with exec used for redirection: no POSIX_BUILTINS +>x=43 +?This should appear, really + + (setopt POSIX_BUILTINS + x=45 + x=$(print This should appear, too >&2; print And this) exec >test.log + print x=$x) + cat test.log +0:Assignment with exec used for redirection: POSIX_BUILTINS +>x=And this +?This should appear, too + + fn-two-heres() { +# tabs below + cat <<-x <<-y + foo + x + bar + y + } + which -x2 fn-two-heres + fn-two-heres + eval "$(which -x2 fn-two-heres)" + fn-two-heres + print $functions[fn-two-heres] +0:Two here-documents in a line are shown correctly. +>fn-two-heres () { +> cat <foo +>x +>bar +>y +>} +>foo +>bar +>foo +>bar +> cat <foo +>x +>bar +>y diff --git a/dotfiles/system/.zsh/modules/Test/A05execution.ztst b/dotfiles/system/.zsh/modules/Test/A05execution.ztst new file mode 100644 index 0000000..0804691 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A05execution.ztst @@ -0,0 +1,312 @@ +%prep + + storepath=($path) + + mkdir command.tmp command.tmp/dir1 command.tmp/dir2 + + cd command.tmp + + print '#!/bin/sh\necho This is top' >tstcmd + + print '#!/bin/sh\necho This is dir1' >dir1/tstcmd + + print '#!/bin/sh\necho This is dir2' >dir2/tstcmd + + chmod 755 tstcmd dir1/tstcmd dir2/tstcmd + +%test + ./tstcmd +0:./prog execution +>This is top + + path=($ZTST_testdir/command.tmp/dir1 + $ZTST_testdir/command.tmp/dir2 + .) + tstcmd + path=($storepath) +0:path (1) +>This is dir1 + + path=(. command.tmp/dir{1,2}) + tstcmd + path=($storepath) +0:path (2) +>This is top + + functst() { print $# arguments:; print -l $*; } + functst "Eines Morgens" "als Gregor Samsa" + functst "" + functst "aus unrhigen Trไumen erwachte" + foo="fand er sich in seinem Bett" + bar= + rod="zu einem ungeheuren Ungeziefer verwandelt." + functst $foo $bar $rod +# set up alias for next test + alias foo='print This is alias one' +0:function argument passing +>2 arguments: +>Eines Morgens +>als Gregor Samsa +>1 arguments: +> +>1 arguments: +>aus unrhigen Trไumen erwachte +>2 arguments: +>fand er sich in seinem Bett +>zu einem ungeheuren Ungeziefer verwandelt. + + alias foo='print This is alias two' + fn() { foo; } + fn +0:Aliases in functions +>This is alias one + + foo='Global foo' + traptst() { local foo="Local foo"; trap 'print $foo' EXIT; } + traptst +0:EXIT trap environment +>Global foo + + functst() { return 0; print Ha ha; return 1; } + functst +0:return (1) + + functst() { return 1; print Ho ho; return 0; } + functst +1:return (2) + + unfunction functst + fpath=(.) + print "print This is functst." >functst + autoload functst + functst +0:autoloading (1) +>This is functst. + + unfunction functst + print "functst() { print This, too, is functst; }; print Hello." >functst + typeset -fu functst + functst + functst +0:autoloading with initialization +>Hello. +>This, too, is functst + + unfunction functst + print "print Yet another version" >functst + functst() { autoload -X; } + functst +0:autoloading via -X +>Yet another version + + chpwd() { print Changed to $PWD; } + cd . + unfunction chpwd +0q:chpwd +>Changed to $ZTST_testdir/command.tmp + + chpwd() { print chpwd: changed to $PWD; } + chpwdfn1() { print chpwdfn1: changed to $PWD; } + chpwdfn2() { print chpwdfn2: changed to $PWD; } + chpwd_functions=(chpwdfn1 '' chpwdnonexistentfn chpwdfn2) + cd . + unfunction chpwd + unset chpwd_functions +0q:chpwd_functions +>chpwd: changed to $ZTST_testdir/command.tmp +>chpwdfn1: changed to $ZTST_testdir/command.tmp +>chpwdfn2: changed to $ZTST_testdir/command.tmp + +# Hard to test periodic, precmd and preexec non-interactively. + + fn() { TRAPEXIT() { print Exit; }; } + fn +0:TRAPEXIT +>Exit + + unsetopt DEBUG_BEFORE_CMD + unfunction fn + print 'TRAPDEBUG() { + print Line $LINENO + } + : + unfunction TRAPDEBUG + ' > fn + autoload fn + fn + rm fn +0:TRAPDEBUG +>Line 1 +>Line 1 + + unsetopt DEBUG_BEFORE_CMD + unfunction fn + print 'trap '\''print Line $LINENO'\'' DEBUG + : + trap - DEBUG + ' > fn + autoload fn + fn + rm fn +0:trap DEBUG +>Line 1 +>Line 2 + + TRAPZERR() { print Command failed; } + true + false + true + false + unfunction TRAPZERR +0:TRAPZERR +>Command failed +>Command failed + + trap 'print Command failed again.' ZERR + true + false + true + false + trap - ZERR +0:trap ZERR +>Command failed again. +>Command failed again. + + false + sleep 1000 & + print $? + kill $! +0:Status reset by starting a backgrounded command +>0 + + { setopt MONITOR } 2>/dev/null + [[ -o MONITOR ]] || print -u $ZTST_fd 'Unable to change MONITOR option' + repeat 2048; do (return 2 | + return 1 | + while true; do + false + break + done; + print "${pipestatus[@]}") + ZTST_hashmark + done | sort | uniq -c | sed 's/^ *//' +0:Check whether '$pipestatus[]' behaves. +>2048 2 1 0 +F:This test checks for a bug in '$pipestatus[]' handling. If it breaks then +F:the bug is still there or it reappeared. See workers-29973 for details. + + { setopt MONITOR } 2>/dev/null + externFunc() { awk >/dev/null 2>&1; true; } + false | true | false | true | externFunc + echo $pipestatus +0:Check $pipestatus with a known difficult case +>1 0 1 0 0 +F:This similar test was triggering a reproducible failure with pipestatus. + + { unsetopt MONITOR } 2>/dev/null + coproc { read -et 5 || { print -u $ZTST_fd KILLED; kill -HUP -$$ } } + print -u $ZTST_fd 'This test takes 5 seconds to fail...' + { printf "%d\n" {1..20000} } 2>/dev/null | ( read -e ) + hang(){ printf "%d\n" {2..20000} | cat }; hang 2>/dev/null | ( read -e ) + print -p done + read -et 6 -p +0:Bug regression: piping a shell construct to an external process may hang +>1 +>2 +>done +F:This test checks for a file descriptor leak that could cause the left +F:side of a pipe to block on write after the right side has exited + + { setopt MONITOR } 2>/dev/null + if [[ -o MONITOR ]] + then + ( while :; do print "This is a line"; done ) | () : & + sleep 1 + jobs -l + else + print -u $ZTST_fd "Skipping pipe leak test, requires MONITOR option" + print "[0] 0 0" + fi +0:Bug regression: piping to anonymous function; piping to backround function +*>\[<->\] <-> <-> +F:This test checks for two different bugs, a parser segfault piping to an +F:anonymous function, and a descriptor leak when backgrounding a pipeline + + print "autoload_redir() { print Autoloaded ksh style; } >autoload.log" >autoload_redir + autoload -Uk autoload_redir + autoload_redir + print No output yet + cat autoload.log + functions autoload_redir +0: +>No output yet +>Autoloaded ksh style +>autoload_redir () { +> print Autoloaded ksh style +>} > autoload.log + +# This tests that we record the status of processes that have already exited +# for when we wait for them. +# +# Actually, we don't guarantee here that the jobs have already exited, but +# the order of the waits means it's highly likely we do need to recall a +# previous status, barring accidents which shouldn't happen very often. In +# other words, we rely on the test working repeatedly rather than just +# once. The monitor option is irrelevant to the logic, so we'll make +# our job easier by turning it off. + { unsetopt MONITOR } 2>/dev/null + (exit 1) & + one=$! + (exit 2) & + two=$! + (exit 3) & + three=$! + wait $three + print $? + wait $two + print $? + wait $one + 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 + if false; then :; else echo if:$?; fi + if false; then :; else for x in _*_; do :; done; echo for:$?; fi +0:False "if" condition handled correctly by "for" loops with ERR_EXIT +>if:1 +>for:0 + +# Regression test for workers/34065 (uses setopt from preceding test) + select x; do :; done; echo $? + select x in; do :; done; echo $? + select x in _*_; do :; done; echo $? + unsetopt ERR_EXIT NULL_GLOB +0:The status of "select" is zero when the loop body does not execute +>0 +>0 +>0 + +# Regression test for workers/36392 + print -u $ZTST_fd 'This test takes 3 seconds and hangs the shell when it fails...' + callfromchld() { true && { print CHLD } } + TRAPCHLD() { callfromchld } + sleep 2 & sleep 3; print OK +0:Background job exit does not affect reaping foreground job +>CHLD +>OK + +# Regression test for workers/39839 and workers/39844 + () { if return 11; then :; fi }; echo $? + () { while return 13; do :; done }; echo $? + () { until return 17; do :; done }; echo $? + () { until false; do return 19; done }; echo $? +0:"return" in "if" or "while" conditional +>11 +>13 +>17 +>19 + diff --git a/dotfiles/system/.zsh/modules/Test/A06assign.ztst b/dotfiles/system/.zsh/modules/Test/A06assign.ztst new file mode 100644 index 0000000..bf39aee --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A06assign.ztst @@ -0,0 +1,631 @@ +# Tests of parameter assignments + +%prep + mkdir assign.tmp && cd assign.tmp + + touch tmpfile1 tmpfile2 + +%test + + typeset -A assoc + assoc=(one 1 two 2 odd) +1:assign to association with odd no. of values +?(eval):2: bad set of key/value pairs for associative array + +# tests of array element assignment + + array=(1 2 3 4 5) + array[1]=42 + print $array +0:Replacement of array element +>42 2 3 4 5 + + array=(1 2 3 4 5) + array[1]=(42 43) + print $array +0:Replacement of array element with array +>42 43 2 3 4 5 + + array=(1 2 3 4 5) + array[1,2]=(42 43) + print $array +0:Replacement of start of array +>42 43 3 4 5 + + array=(1 2 3 4 5) + array[1,4]=(42 43) + print $array +0:Replacement of start of array with shorter slice +>42 43 5 + + array=(1 2 3 4 5) + array[1,6]=(42 43) + print $array +0:Replacement of array by extending slice +>42 43 + + array=(1 2 3 4 5) + array[3]=(42 43) + print $array +0:Replacement of middle element with array +>1 2 42 43 4 5 + + array=(1 2 3 4 5) + array[3,4]=(42 43 44) + print $array +0:Replacement of slice in middle +>1 2 42 43 44 5 + + array=(1 2 3 4 5) + array[7,8]=(42 43) + print $array + # check that [6] was left empty... + array[6]=41 + print $array +0:Appending by replacing elements off the end +>1 2 3 4 5 42 43 +>1 2 3 4 5 41 42 43 + + array=(1 2 3 4 5) + array[-1]=42 + print $array +0:Replacement of last element of array, negative indices +>1 2 3 4 42 + + array=(1 2 3 4 5) + array[-1]=(42 43) + print $array +0:Replacement of last element of array with array, negative indices +>1 2 3 4 42 43 + + array=(1 2 3 4 5) + array[-3,-2]=(42 43 44) + print $array +0:Replacement of middle of array, negative indices +>1 2 42 43 44 5 + + array=(1 2 3 4 5) + array[-5,-1]=(42 43) + print $array +0:Replacement of entire array, negative indices +>42 43 + + array=(1 2 3 4 5) + array[-7,-1]=(42 43) + print $array +0:Replacement of more than entire array, negative indices +>42 43 + + array=(1 2 3 4 5) + array[-7]=42 + print $array +0:Replacement of element off start of array. +>42 1 2 3 4 5 + + array=(1 2 3 4 5) + array[-7]=42 + array[-6]=43 + print $array +0:Replacement off start doesn't leave gaps. Hope this is right. +>43 1 2 3 4 5 + + array=(1 2 3 4 5) + array[1,-1]=(42 43) + print $array + array[-3,3]=(1 2 3 4 5) + print $array +0:Replacement of entire array, mixed indices +>42 43 +>1 2 3 4 5 + + array=(1 2 3 4 5) + array[-7,7]=(42 43) + print $array +0:Replacement of more than entire array, mixed indices +>42 43 + + array=(1 2 3 4 5) + array[3,-2]=(42 43 44) + print $array + array[-3,5]=(100 99) + print $array +0:Replacement of slice in middle, mixed indices +>1 2 42 43 44 5 +>1 2 42 100 99 5 + +# tests of var+=scalar + + s+=foo + echo $s +0:append scalar to unset scalar +>foo + + s=foo + s+=bar + echo $s +0:append to scalar +>foobar + + set -- a b c + 2+=end + echo $2 +0:append to positional parameter +>bend + + a=(first second) + a+=last + print -l $a +0:add scalar to array +>first +>second +>last + + setopt ksharrays + a=(first second) + a+=last + print -l $a + unsetopt ksharrays +0:add scalar to array with ksharrays set +>firstlast + + a=(1 2) + a[@]+=3 + print -l $a +0:add scalar to array with alternate syntax +>1 +>2 +>3 + + integer i=10 + i+=20 + (( i == 30 )) +0:add to integer + + float f=3.4 + f+=2.3 + printf "%g\n" f +0:add to float +>5.7 + + typeset -A hash + hash=(one 1) + hash+=string + [[ $hash[@] == string ]] +0:add scalar to association + +# tests of var+=(array) + + unset a + a+=(1 2 3) + print -l $a +0:add array to unset parameter +>1 +>2 +>3 + + a=(a) + a+=(b) + print -l $a +0:add array to existing array +>a +>b + + s=foo + s+=(bar) + print -l $s +0:add array to scalar +>foo +>bar + + integer i=1 + i+=(2 3) + print -l $i +0:add array to integer +>1 +>2 +>3 + + float f=2.5 + f+=(3.5 4.5) + printf '%g\n' $f +0:add array to float +>2.5 +>3.5 +>4.5 + + typeset -A h + h+=(a 1 b 2) + print -l $h +0:add to empty association +>1 +>2 + + typeset -A h + h=(a 1) + h+=(b 2 c 3) + print -l $h +0:add to association +>1 +>2 +>3 + + typeset -A h + h=(a 1 b 2) + h+=() + print -l $h +0:add empty array to association +>1 +>2 + +# tests of var[range]+=scalar + + s=sting + s[2]+=art + echo $s +0:insert scalar inside another +>starting + + s=inert + s[-4]+=s + echo $s +0:insert scalar inside another with negative index +>insert + + s=append + s[2,6]+=age + echo $s +0:append scalar to scalar using a range +>appendage + + s=123456789 + s[3,-5]+=X + echo $s +0:insert scalar inside another, specifying a slice +>12345X6789 + + a=(a b c) + a[2]+=oo + echo $a +0:append to array element +>a boo c + + a=(a b c d) + a[-2]+=ool + echo $a +0:append to array element with negative index +>a b cool d + + a=(a b c d) + a[2,-1]+=oom + echo $a +0:append to array element, specifying a slice +>a b c doom + + setopt ksharrays + a=(a b c d) + a[0]+=0 + echo $a + unsetopt ksharrays +0:append to array element with ksharrays set +>a0 + + typeset -A h + h=(one foo) + h[one]+=bar + echo $h +0:append to association element +>foobar + + typeset -A h + h[foo]+=bar + echo ${(kv)h} +0:append to non-existent association element +>foo bar + + typeset -A h + h=(one a two b three c four d) + h[(I)*o*]+=append +1:attempt to append to slice of association +?(eval):3: h: attempt to set slice of associative array + + integer i=123 + i[2]+=6 +1:attempt to add to indexed integer variable +?(eval):2: attempt to add to slice of a numeric variable + + float f=1234.5 + f[2,4]+=3 +1:attempt to add to slice of float variable +?(eval):2: attempt to add to slice of a numeric variable + + unset u + u[3]+=third + echo $u[1]:$u[3] +0:append to unset variable with index +>:third + +# tests of var[range]+=(array) + + a=(1 2 3) + a[2]+=(a b) + echo $a +0:insert array inside another +>1 2 a b 3 + + a=(a b c) + a[-1]+=(d) + echo $a +0:append to array using negative index +>a b c d + + a=(1 2 3 4) + a[-1,-3]+=(x) + echo $a +0:insert array using negative range +>1 2 x 3 4 + + s=string + s[2]+=(a b) +1:attempt to insert array into string +?(eval):2: s: attempt to assign array value to non-array + + integer i=365 + i[2]+=(1 2) +1:attempt to insert array into string +?(eval):2: i: attempt to assign array value to non-array + + typeset -A h + h=(a 1) + h[a]+=(b 2) +1:attempt to append array to hash element +?(eval):3: h: attempt to set slice of associative array + + unset u + u[-34,-2]+=(a z) + echo $u +0:add array to indexed unset variable +>a z + + repeat 10 PATH=. echo hello +0:saving and restoring of exported special parameters +>hello +>hello +>hello +>hello +>hello +>hello +>hello +>hello +>hello +>hello + + repeat 10 FOO=BAR BAR=FOO echo $FOO $BAR +0:save and restore multiple variables around builtin +> +> +> +> +> +> +> +> +> +> + + call() { print $HELLO; } + export HELLO=world + call + HELLO=universe call + call + HELLO=${HELLO}liness call + call + unset HELLO +0:save and restore when using original value in temporary +>world +>universe +>world +>worldliness +>world + + (integer i n x + float f + setopt globassign + i=tmpfile1 + n=tmpf* + x=*2 + f=2+2 + typeset -p i n x f) +0:GLOB_ASSIGN with numeric types +>typeset -i i=0 +>typeset -a 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[@]}" +0:command execution with assignments shadowing array parameter +>first second +>first +>second + + setopt ksharrays + A=(first second) + A="${A[*]}" /bin/sh -c 'echo $A' + print -l "${A[@]}" + unsetopt ksharrays +0:command execution with assignments shadowing array parameter with ksharrays +>first second +>first +>second + + typeset -aU unique_array=(first second) + unique_array[1]=second + print $unique_array +0:assignment to unique array +>second + + typeset -a array=(first) + array[1,3]=(FIRST) + print $array +0:slice beyond length of array +>FIRST + +# tests of string assignments + + a="abc" + a[1]=x + print $a +0:overwrite first character in string +>xbc + + a="abc" + a[2]="x" + print $a +0:overwrite middle character in string +>axc + + a="abc" + a[3]="x" + print $a +0:overwrite last character in string +>abx + + a="abc" + a[-1]="x" + print $a +0:overwrite -1 character in string +>abx + + a="abc" + a[-2]="x" + print $a +0:overwrite -2 character (middle) in string +>axc + + a="ab" + a[-2]="x" + print $a +0:overwrite -2 character (first) in string +>xb + + a="abc" + a[-3]="x" + print $a +0:overwrite -3 character (first) in string +>xbc + + a="abc" + a[-4]="x" + print $a +0:overwrite -4 character (before first) in string +>xabc + + a="abc" + a[-5]="x" + print $a +0:overwrite -5 character (before-before first) in string +>xabc + + a="abc" + a[-4,0]="x" + print $a +0:overwrite [-4,0] characters (before first) in string +>xabc + + a="abc" + a[-4,-4]="x" + print $a +0:overwrite [-4,-4] character (before first) in string +>xabc + + a="abc" + a[-40,-30]="x" + print $a +0:overwrite [-40,-30] characters (far before first) in string +>xabc + + a="abc" + a[-40,1]="x" + print $a +0:overwrite [-40,1] characters in short string +>xbc + + a="abc" + a[-40,40]="x" + print $a +0:overwrite [-40,40] characters in short string +>x + + a="abc" + a[2,40]="x" + print $a +0:overwrite [2,40] characters in short string +>ax + + a="abc" + a[2,-1]="x" + print $a +0:overwrite [2,-1] characters in short string +>ax + + a="abc" + a[-2,-1]="x" + print $a +0:overwrite [-2,-1] characters in short string +>ax + + a="a" + a[-1]="xx" + print $a +0:overwrite [-1] character with "xx" +>xx + + a="a" + a[-2]="xx" + print $a +0:overwrite [-2] character (before first) with "xx" +>xxa + + a="a" + a[2]="xx" + print $a +0:overwrite [2] character (after last) with "xx" +>axx + + a="" + a[1]="xx" + print $a +0:overwrite [1] character (string: "") with "xx" +>xx + + a="" + a[-1]="xx" + print $a +0:overwrite [-1] character (string: "") with "xx" +>xx + + a="" + a[2]="xx" + print $a +0:overwrite [2] character (string: "") with "xx" +>xx diff --git a/dotfiles/system/.zsh/modules/Test/A07control.ztst b/dotfiles/system/.zsh/modules/Test/A07control.ztst new file mode 100644 index 0000000..b1a2487 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/A07control.ztst @@ -0,0 +1,165 @@ +# Test control commands for loops and functions. + +%test + + fn3() { return $1; print Error } + fn2() { fn3 $1 } + fn() { + print start $1 + fn2 $1 + return + print Error + } + for val in -1 0 1 255; do + fn $val; print $? + done +0:Passing of return values back through functions +>start -1 +>-1 +>start 0 +>0 +>start 1 +>1 +>start 255 +>255 + + $ZTST_testdir/../Src/zsh -fc 'fn() { + continue + } + fn' +1:continue outside loop +?fn:continue:1: not in while, until, select, or repeat loop + + for outer in 0 1 2 3; do + print outer $outer + for inner in 0 1 2 3; do + print inner $inner + continue $(( (outer & 1) ? 2 : 1 )) + print error + done + print outer end + done +0:continue with valid argument +>outer 0 +>inner 0 +>inner 1 +>inner 2 +>inner 3 +>outer end +>outer 1 +>inner 0 +>outer 2 +>inner 0 +>inner 1 +>inner 2 +>inner 3 +>outer end +>outer 3 +>inner 0 + + for outer in 0 1; do + continue 0 + print -- $outer got here, status $? + done +1:continue error case 0 +?(eval):continue:2: argument is not positive: 0 + + for outer in 0 1; do + continue -1 + print -- $outer got here, status $? + done +1:continue error case -1 +?(eval):continue:2: argument is not positive: -1 + + fn() { + break + } + for outer in 0 1; do + print $outer + fn + done +0:break from within function (this is a feature, I disovered) +>0 + + for outer in 0 1 2 3; do + print outer $outer + for inner in 0 1 2 3; do + print inner $inner + break $(( (outer & 1) ? 2 : 1 )) + print error + done + print outer end + done +0:break with valid argument +>outer 0 +>inner 0 +>outer end +>outer 1 +>inner 0 + + for outer in 0 1; do + break 0 + print -- $outer got here, status $? + done +1:break error case 0 +?(eval):break:2: argument is not positive: 0 + + for outer in 0 1; do + break -1 + print -- $outer got here, status $? + done +1:break error case -1 +?(eval):break:2: argument is not positive: -1 + + false + for x in; do + print nothing executed + done +0:Status 0 from for with explicit empty list + + set -- + false + for x; do + print nothing executed + done +0:Status 0 from for with implicit empty list + + (exit 2) + for x in 1 2; do + print $? + done +0:Status from previous command propagated into for loop +>2 +>0 + + false + for x in $(echo 1 2; (exit 3)); do + print $? + done +0:Status from expansion propagated into for loop +>3 +>0 + + false + for x in $(exit 4); do + print not executed + done +0:Status from expansion not propagated after unexecuted for loop + + false + for x in NonExistentFilePrefix*(N); do + print not executed, either + done +0:Status from before for loop not propagated if empty after expansion + + for x in $(echo 1; false); do + done +0:Status reset by empty list in for loop + + false + for x in $(echo 1; false); do + echo $? + (exit 4) + done +4:Last status from loop body is kept even with other funny business going on +>1 diff --git a/dotfiles/system/.zsh/modules/Test/B01cd.ztst b/dotfiles/system/.zsh/modules/Test/B01cd.ztst new file mode 100644 index 0000000..94447e7 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B01cd.ztst @@ -0,0 +1,144 @@ +# This file serves as a model for how to write tests, so is more heavily +# commented than the others. All tests are run in the Test subdirectory +# of the distribution, which must be writable. They should end with +# the suffix `.ztst': this is not required by the test harness itself, +# but it is needed by the Makefile to run all the tests. + +# Blank lines with no other special meaning (e.g. separating chunks of +# code) and all those with a `#' in the first column are ignored. + +# All section names start with a % in the first column. The names +# must be in the expected order, though not all sections are required. +# The sections are %prep (preparatory setup: code executed should return +# status 0, but no other tests are performed), %test (the main tests), and +# %clean (to cleanup: the code is simply unconditionally executed). +# +# Literal shell code to be evaluated must be indented with any number +# of spaces and/or tabs, to differentiate it from tags with a special +# meaning to the test harness. Note that this is true even in sections +# where there are no such tags. Also note that file descriptor 9 +# is reserved for input from the test script, and file descriptor 8 +# preserves the original stdout. Option settings are preserved between the +# execution of different code chunks; initially, all standard zsh options +# (the effect of `emulate -R zsh') are set. + +%prep +# This optional section prepares the test, creating directories and files +# and so on. Chunks of code are separated by blank lines (which is not +# necessary before the end of the section); each chunk of code is evaluated +# in one go and must return status 0, or the preparation is deemed to have +# failed and the test ends with an appropriate error message. Standard +# output from this section is redirected to /dev/null, but standard error +# is not redirected. +# +# Tests should use subdirectories ending in `.tmp'. These will be +# removed with all the contents even if the test is aborted. + mkdir cdtst.tmp cdtst.tmp/real cdtst.tmp/sub + + ln -s ../real cdtst.tmp/sub/fake + + setopt chaselinks + cd . + unsetopt chaselinks + mydir=$PWD + +%test +# This is where the tests are run. It consists of blocks separated +# by blank lines. Each block has the same format and there may be any +# number of them. It consists of indented code, plus optional sets of lines +# beginning '<', '>' and '?' which may appear in any order. These correspond +# to stdin (fed to the code), stdout (compared with code output) and +# stderr (compared with code error output) respectively. These subblocks +# may occur in any order, but the natural one is: code, stdin, stdout, +# stderr. +# +# The rules for '<', '>' and '?' lines are the same: only the first +# character is stripped (with the excpetion for '*' noted below), with +# subsequent whitespace being significant; lines are not subject to any +# substitution unless the `q' flag (see below) is set. +# +# Each line of a '>' and '?' chunk may be preceded by a '*', so the line +# starts '*>' or '*?'. This signifies that for any line with '*' in front +# the actual output should be pattern matched against the corresponding +# lines in the test output. Each line following '>' or '?' must be a +# valid pattern, so characters special to patterns such as parentheses +# must be quoted with a backslash. The EXTENDED_GLOB option is used for +# all such patterns. +# +# Each chunk of indented code is to be evaluated in one go and is to +# be followed by a line starting (in the first column) with +# 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 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 +# expansion (i.e. not globbing). +# This can be followed by a `:' and a message describing the +# test, which will be printed if the test fails, along with a +# description of the failure that occurred. The `:' and message are +# optional, but highly recommended. +# Hence a complete status line looks something like: +# 0dDq:Checking whether the world will end with a bang or a whimper +# +# If either or both of the '>' and '?' sets of lines is absent, it is +# assumed the corresponding output should be empty and it is an error if it +# is not. If '<' is empty, stdin is an empty (but opened) file. +# +# It is also possible to add lines in the redirection section beginning +# with `F:'. The remaining text on all such lines will be concatenated +# (with newlines in between) and displayed in the event of an error. +# This text is useful for explaining certain frequent errors, for example +# 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 +0q:Preserving symbolic links in the current directory string +>$mydir/cdtst.tmp/sub/fake +>$mydir/cdtst.tmp/sub/fake +F:This test shouldn't really fail. The fact that it has indicates +F:something is broken. But you already knew that. + + cd ../../.. && + pwd && + print $PWD +0q:Changing directory up through symbolic links without following them +>$mydir +>$mydir + + setopt chaselinks + cd cdtst.tmp/sub/fake && + pwd && + print $PWD +0q:Resolving symbolic links with chaselinks set +>$mydir/cdtst.tmp/real +>$mydir/cdtst.tmp/real + + ln -s nonexistent link_to_nonexistent + pwd1=$(pwd -P) + cd -s link_to_nonexistent + pwd2=$(pwd -P) + [[ $pwd1 = $pwd2 ]] || print "Ooops, changed to directory '$pwd2'" +0: +?(eval):cd:3: not a directory: link_to_nonexistent + +%clean +# This optional section cleans up after the test, if necessary, +# e.g. killing processes etc. This is in addition to the removal of *.tmp +# subdirectories. This is essentially like %prep, except that status +# return values are ignored. diff --git a/dotfiles/system/.zsh/modules/Test/B02typeset.ztst b/dotfiles/system/.zsh/modules/Test/B02typeset.ztst new file mode 100644 index 0000000..b27bb4f --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B02typeset.ztst @@ -0,0 +1,723 @@ +# There are certain usages of typeset and its synonyms that it is not +# possible to test here, because they must appear at the top level and +# everything that follows is processed by an "eval" within a function. + +# Equivalences: +# declare typeset +# export typeset -xg +# float typeset -E +# functions typeset -f +# integer typeset -i +# local typeset +g -m approximately +# readonly typeset -r + +# Tested elsewhere: +# Equivalence of autoload and typeset -fu A05execution +# Associative array creation & assignment D04parameter, D06subscript +# Effects of GLOBAL_EXPORT E01options +# Function tracing (typeset -ft) E02xtrace + +# Not yet tested: +# Assorted illegal flag combinations + +%prep + ## Do not remove the next line, it's used by V10private.ztst + # test_zsh_param_private + + mkdir typeset.tmp && cd typeset.tmp + + setopt noglob + + scalar=scalar + array=(a r r a y) + + scope00() { + typeset scalar + scalar=local + typeset -a array + array=(l o c a l) + print $scalar $array + } + scope01() { + local scalar + scalar=local + local -a array + array=(l o c a l) + print $scalar $array + } + scope02() { + declare scalar + scalar=local + declare -a array + array=(l o c a l) + print $scalar $array + } + scope10() { + export outer=outer + /bin/sh -fc 'echo $outer' + } + scope11() { + typeset -x outer=outer + /bin/sh -fc 'echo $outer' + } + scope12() { + local -x outer=inner + /bin/sh -fc 'echo $outer' + } + scope13() { + local -xT OUTER outer + outer=(i n n e r) + /bin/sh -fc 'echo $OUTER' + } + + # Bug? `typeset -h' complains that ! # $ * - ? @ are not identifiers. + stress00() { + typeset -h +g -m [[:alpha:]_]* + unset -m [[:alpha:]_]* + typeset +m [[:alpha:]_]* + } + +%test + + typeset +m scalar array +0:Report types of parameters with typeset +m +>scalar +>array array + + scope00 + print $scalar $array +0:Simple local declarations +>local l o c a l +>scalar a r r a y + + scope01 + print $scalar $array +0:Equivalence of local and typeset in functions +>local l o c a l +>scalar a r r a y + + scope02 + print $scalar $array +0:Basic equivalence of declare and typeset +>local l o c a l +>scalar a r r a y + + declare +m scalar +0:declare previously lacked -m/+m options +>scalar + + scope10 + print $outer +0:Global export +>outer +>outer + + scope11 + print $outer +0:Equivalence of export and typeset -x +>outer +>outer + + scope12 + print $outer +0:Local export +>inner +>outer + + float f=3.14159 + typeset +m f + float -E3 f + print $f + float -F f + print $f +0:Floating point, adding a precision, and fixed point +>float local f +>3.14e+00 +>3.142 + + integer i=3.141 + typeset +m i + integer -i2 i + print $i +0:Integer and changing the base +>integer local i +>2#11 + + float -E3 f=3.141 + typeset +m f + integer -i2 f + typeset +m f + print $f +0:Conversion of floating point to integer +>float local f +>integer 2 local f +>2#11 + + typeset -f +0q:Equivalence of functions and typeset -f +>$(functions) + + readonly r=success + print $r + r=failure +1:Readonly declaration +>success +?(eval):3: read-only variable: r + + typeset r=success + readonly r + print $r + r=failure +1:Convert to readonly +>success +?(eval):4: read-only variable: r + + typeset -gU array + print $array +0:Uniquified arrays and non-local scope +>a r y + + typeset -T SCALAR=l:o:c:a:l array + print $array + typeset -U SCALAR + print $SCALAR $array +0:Tied parameters and uniquified colon-arrays +>l o c a l +>l:o:c:a l o c a + + (setopt NO_multibyte cbases + LC_ALL=C 2>/dev/null + typeset -T SCALAR=$'l\x83o\x83c\x83a\x83l' array $'\x83' + print $array + typeset -U SCALAR + for (( i = 1; i <= ${#SCALAR}; i++ )); do + char=$SCALAR[i] + print $(( [#16] #char )) + done + print $array) +0:Tied parameters and uniquified arrays with meta-character as separator +>l o c a l +>0x6C +>0x83 +>0x6F +>0x83 +>0x63 +>0x83 +>0x61 +>l o c a + + typeset -T SCALAR=$'l\000o\000c\000a\000l' array $'\000' + typeset -U SCALAR + print $array + [[ $SCALAR == $'l\000o\000c\000a' ]] +0:Tied parameters and uniquified arrays with NUL-character as separator +>l o c a + + typeset -T SCALAR array + typeset +T SCALAR +1:Untying is prohibited +?(eval):typeset:2: use unset to remove tied variables + + OUTER=outer + scope13 + print $OUTER +0:Export of tied parameters +>i:n:n:e:r +>outer + + typeset -TU MORESTUFF=here-we-go-go-again morestuff '-' + print -l $morestuff +0:Tied arrays with separator specified +>here +>we +>go +>again + + typeset -T THIS will not work +1:Tied array syntax +?(eval):typeset:1: too many arguments for -T + + local array[2]=x +1:Illegal local array element assignment +?(eval):local:1: array[2]: can't create local array elements + + local -a array + typeset array[1]=a array[2]=b array[3]=c + print $array +0:Legal local array element assignment +>a b c + + local -A assoc + local b=1 ;: to stomp assoc[1] if assoc[b] is broken + typeset assoc[1]=a assoc[b]=2 assoc[3]=c + print $assoc[1] $assoc[b] $assoc[3] +0:Legal local associative array element assignment +>a 2 c + + local scalar scalar[1]=a scalar[2]=b scalar[3]=c + print $scalar +0:Local scalar subscript assignment +>abc + + typeset -L 10 fools + for fools in " once" "twice" " thrice" " oops too long here"; do + print "'$fools'" + done +0:Left justification of scalars +>'once ' +>'twice ' +>'thrice ' +>'oops too l' + + typeset -L 10 -F 3 foolf + for foolf in 1.3 4.6 -2.987 -4.91031; do + print "'$foolf'" + done +0:Left justification of floating point +>'1.300 ' +>'4.600 ' +>'-2.987 ' +>'-4.910 ' + + typeset -L 10 -Z foolzs + for foolzs in 001.3 04.6 -2.987 -04.91231; do + print "'$foolzs'" + done +0:Left justification of scalars with zero suppression +>'1.3 ' +>'4.6 ' +>'-2.987 ' +>'-04.91231 ' + + typeset -R 10 foors + for foors in short longer even-longer; do + print "'$foors'" + done +0:Right justification of scalars +>' short' +>' longer' +>'ven-longer' + + typeset -Z 10 foozs + for foozs in 42 -42 " 43" " -43"; do + print "'$foozs'" + done +0:Right justification of scalars with zeroes +>'0000000042' +>' -42' +>' 000000043' +>' -43' + + integer -Z 10 foozi + for foozi in 42 -42 " 43" " -43"; do + print "'$foozi'" + done +0:Right justification of integers with zero, no initial base +>'0000000042' +>'-000000042' +>'0000000043' +>'-000000043' +# In case you hadn't twigged, the spaces are absorbed in the initial +# math evaluation, so don't get through. + + unsetopt cbases + integer -Z 10 -i 16 foozi16 + for foozi16 in 42 -42 " 43" " -43"; do + print "'$foozi16'" + done +0:Right justification of integers with zero, base 16, C_BASES off +>'16#000002A' +>'-16#00002A' +>'16#000002B' +>'-16#00002B' + + setopt cbases + integer -Z 10 -i 16 foozi16c + for foozi16c in 42 -42 " 43" " -43"; do + print "'$foozi16c'" + done +0:Right justification of integers with zero, base 16, C_BASES on +>'0x0000002A' +>'-0x000002A' +>'0x0000002B' +>'-0x000002B' + + setopt cbases + integer -Z 10 -i 16 foozi16c + for foozi16c in 0x1234 -0x1234; do + for (( i = 1; i <= 5; i++ )); do + print "'${foozi16c[i,11-i]}'" + done + print "'${foozi16c[-2]}'" + done +0:Extracting substrings from padded integers +>'0x00001234' +>'x0000123' +>'000012' +>'0001' +>'00' +>'3' +>'-0x0001234' +>'0x000123' +>'x00012' +>'0001' +>'00' +>'3' + + typeset -F 3 -Z 10 foozf + for foozf in 3.14159 -3.14159 4 -4; do + print "'$foozf'" + done +0:Right justification of fixed point numbers with zero +>'000003.142' +>'-00003.142' +>'000004.000' +>'-00004.000' + + stress00 + print $scalar $array +0q:Stress test: all parameters are local and unset, using -m +>scalar a r y + + local parentenv=preserved + fn() { + typeset -h +g -m \* + unset -m \* + integer i=9 + float -H f=9 + declare -t scalar + declare -H -a array + typeset + typeset + + } + fn + echo $parentenv +0:Parameter hiding and tagging, printing types and values +>array local array +>float local f +>integer local i=9 +>local tagged scalar='' +>array local array +>float local f +>integer local i +>local tagged scalar +>preserved + + export ENVFOO=bar + print ENVFOO in environment + env | grep '^ENVFOO' + print Changing ENVFOO + ENVFOO="not bar any more" + env | grep '^ENVFOO' + unset ENVFOO + print ENVFOO no longer in environment + env | grep '^ENVFOO' +1:Adding and removing values to and from the environment +>ENVFOO in environment +>ENVFOO=bar +>Changing ENVFOO +>ENVFOO=not bar any more +>ENVFOO no longer in environment + + (export FOOENV=BAR + env | grep '^FOOENV' + print Exec + exec $ZTST_testdir/../Src/zsh -fc ' + print Unset + unset FOOENV + env | grep "^FOOENV"') +1:Can unset environment variables after exec +>FOOENV=BAR +>Exec +>Unset + + local case1=upper + typeset -u case1 + print $case1 + upper="VALUE OF \$UPPER" + print ${(P)case1} +0:Upper case conversion, does not apply to values used internally +>UPPER +>VALUE OF $UPPER + + local case2=LOWER + typeset -l case2 + print $case2 + LOWER="value of \$lower" + print ${(P)case2} +0:Lower case conversion, does not apply to values used internally +>lower +>value of $lower + + typeset -a array + array=(foo bar) + fn() { typeset -p array nonexistent; } + fn +1:declare -p shouldn't create scoped values +>typeset -g -a array=( foo bar ) +?fn:typeset: no such variable: nonexistent + + unsetopt typesetsilent + silent1(){ typeset -g silence; } + silent2(){ local silence; silent1; } + silent2 +0:typeset -g should be silent even without TYPESET_SILENT + + typeset -T TIED_SCALAR tied_array + TIED_SCALAR=foo:bar + 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 + readonly pbro + print ${+pbro} >&2 + (typeset -g pbro=3) + (pbro=4) + readonly -p pbro >&2 # shows up as "readonly" although unset + typeset -gr pbro # idempotent (no error)... + print ${+pbro} >&2 # ...so still readonly... + typeset -g +r pbro # ...can't turn it off + ) +1:readonly with POSIX_BUILTINS +?0 +?(eval):5: read-only variable: pbro +?(eval):6: read-only variable: pbro +?typeset -g -r pbro +?0 +?(eval):10: read-only variable: pbro + + readonly foo=bar novalue + readonly -p +0:readonly -p output (no readonly specials) +>typeset -r foo=bar +>typeset -r novalue='' + + local -a a1 a2 + local -r r1=yes r2=no + a1=(one two) a2=(three four) + readonly a1 + typeset -pm 'a[12]' + typeset -pm 'r[12]' +0:readonly -p output +>typeset -ar a1=( one two ) +>typeset -a 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=( '' two '' four ) +>typeset -a array=( one '' three ) +>no really nothing here + + readonly isreadonly=yes + typeset isreadonly=still +1:typeset returns status 1 if setting readonly variable +?(eval):2: read-only variable: isreadonly + + if (( UID )); then + UID=$((UID+1)) date; echo "Status is printed, $?" + else + ZTST_skip="cannot test setuid error when tests run as superuser" + fi +0:when cannot change UID, the command isn't run +# 'date' did not run. +>Status is printed, 1 +*?*: failed to change user ID: * diff --git a/dotfiles/system/.zsh/modules/Test/B03print.ztst b/dotfiles/system/.zsh/modules/Test/B03print.ztst new file mode 100644 index 0000000..c65568a --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B03print.ztst @@ -0,0 +1,336 @@ +# Tests for the echo, print, printf and pushln builtins + +# Tested elsewhere: +# Use of print -p to output to coprocess A01grammar +# Prompt expansion with print -P D01prompt +# -l, -r, -R and -n indirectly tested in various places + +# Not yet tested: +# echo and pushln +# print's -b -c -s -z -N options + + +%test + + print -D "${HOME:-~}" +0:replace directory name +>~ + + print -u2 'error message' +0:output to file-descriptor +?error message + + print -o foo bar Baz +0:argument sorting +>Baz bar foo + + print -f +1:print -f needs a format specified +?(eval):print:1: argument expected: -f + + print -Of '%s\n' foo bar baz +0:reverse argument sorting +>foo +>baz +>bar + +# some locales force case-insensitive sorting + (LC_ALL=C; print -o a B c) +0:case-sensitive argument sorting +>B a c + + (LC_ALL=C; print -io a B c) +0:case-insensitive argument sorting +>a B c + + print -m '[0-9]' one 2 three 4 five 6 +0:removal of non-matching arguments +>2 4 6 + + printf '%s\n' string +0:test s format specifier +>string + + printf '%b' '\t\\\n' +0:test b format specifier +> \ + + printf '%q\n' '=a=b \ c!' +0: test q format specifier +>\=a=b\ \\\ c! + + printf '%c\n' char +0:test c format specifier +>c + + printf '%.10e%n\n' 1 count >/dev/null + printf '%d\n' $count +0:test n format specifier +>16 + + printf '%5b%n\n' abc count >/dev/null; echo $count +0:check count of width-specified %b +>5 + + printf '%s!%5b!\n' abc +0:ensure width is applied to empty param +>abc! ! + + printf '%d %d\n' 123.45 678 90.1 +0:test d format specifier +>123 678 +>90 0 + + printf '%g %g\n' 123.45 678 90.1 +0:test g format specifier +>123.45 678 +>90.1 0 + + print -f 'arg: %b\n' -C2 '\x41' '\x42' '\x43' +0:override -C when -f was given +>arg: A +>arg: B +>arg: C + +# Is anyone not using ASCII + printf '%d\n' \'A +0:initial quote to get numeric value of character with int +>65 + + printf '%.1E\n' \'B +0:initial quote to get numeric value of character with double +>6.6E+01 + + printf '%x\n' $(printf '"\xf0') +0:numeric value of high numbered character +>f0 + + printf '\x25s\n' arg +0:using \x25 to print a literal % in format +>%s + + printf '%3c\n' c +0:width specified in format specifier +> c + + printf '%.4s\n' chopped +0:precision specified in format specifier +>chop + + printf '%*.*f\n' 6 2 10.2 +0:width/precision specified in arguments +> 10.20 + + printf '%z' +1:use of invalid directive +?(eval):printf:1: %z: invalid directive + + printf '%d\n' 3a +1:bad arithmetic expression +?(eval):1: bad math expression: operator expected at `a' +>0 + + printf '%12$s' 1 2 3 +1:out of range argument specifier +?(eval):printf:1: 12: argument specifier out of range + + printf '%2$s\n' 1 2 3 +1:out of range argument specifier on format reuse +?(eval):printf:1: 2: argument specifier out of range +>2 + + printf '%*0$d' +1:out of range argument specifier on width +?(eval):printf:1: 0: argument specifier out of range + + print -m -f 'format - %s.\n' 'z' a b c +0:format not printed if no arguments left after -m removal + + print -f 'format - %s%b.\n' +0:format printed despite lack of arguments +>format - . + + printf 'x%4sx\n' +0:with no arguments empty string where string needed +>x x + + printf '%d\n' +0:with no arguments, zero used where number needed +>0 + + printf '%s\t%c:%#x%%\n' one a 1 two b 2 three c 3 +0:multiple arguments with format reused +>one a:0x1% +>two b:0x2% +>three c:0x3% + + printf '%d%n' 123 val val val > /dev/null + printf '%d\n' val +0:%n count zeroed on format reuse +>1 + +# 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 + + printf '%.*g\n' -1 .1 +0:negative precision specified +>0.1 + + printf '%2$s %1$d\n' 1 2 +0:specify argument to output explicitly +>2 1 + + printf '%3$.*1$d\n' 4 0 3 +0:specify output and precision arguments explicitly +>0003 + + printf '%2$d%1$d\n' 1 2 3 4 +0:reuse format where arguments are explicitly specified +>21 +>43 + + printf '%1$*2$d' 1 2 3 4 5 6 7 8 9 10; echo .EoL. +0:reuse of specified arguments +> 1 3 5 7 9.EoL. + + echo -n 'Now is the time'; echo .EoL. +0:testing -n with echo +>Now is the time.EoL. + + printf '%1$0+.3d\n' 3 +0:flags mixed with specified argument +>+003 + +# Test the parsing of the \c escape. + + echo '1 2!\c3 4' a b c d; echo .EoL. +0:Truncating first echo arg using backslash-c +>1 2!.EoL. + + echo a b '1 2?\c5 6' c d; echo .EoL. +0:Truncating third echo arg using backslash-c +>a b 1 2?.EoL. + + printf '1 2!\c3 4'; echo .EoL. +0:Truncating printf literal using backslash-c +>1 2!.EoL. + + printf '%s %b!\c%s %s' 1 2 3 4 5 6 7 8 9; echo .EoL. +0:Truncating printf format using backslash-c +>1 2!.EoL. + + printf '%s %b!\c%s %s' '1\c' '2\n\c' 3 4 5 6 7 8 9 +0:Truncating printf early %b arg using backslash-c +>1\c 2 + + printf '%b %b\n' 1 2 3 4 '5\c' 6 7 8 9; echo .EoL. +0:Truncating printf late %b arg using backslash-c +>1 2 +>3 4 +>5.EoL. + +# The following usage, as stated in the manual, is not recommended and the +# results are undefined. Tests are here anyway to ensure some form of +# half-sane behaviour. + + printf '%2$s %s %3$s\n' Morning Good World +0:mixed style of argument selection +>Good Morning World + + printf '%*1$.*d\n' 1 2 +0:argument specified for width only +>00 + + print -f '%*.*1$d\n' 1 2 3 +0:argument specified for precision only +>2 +>000 + + printf -- '%s\n' str +0:initial `--' ignored to satisfy POSIX +>str + + printf '%' +1:nothing after % in format specifier +?(eval):printf:1: %: invalid directive + + printf $'%\0' +1:explicit null after % in format specifier +?(eval):printf:1: %: invalid directive + + printf '%b\n' '\0123' +0:printf handles \0... octal escapes in replacement text +>S + + print -lO $'a' $'a\0' $'a\0b' $'a\0b\0' $'a\0b\0a' $'a\0b\0b' $'a\0c' | + while read -r line; do + for (( i = 1; i <= ${#line}; i++ )); do + foo=$line[i] + printf "%02x" $(( #foo )) + done + print + done +0:sorting with embedded nulls +>610063 +>6100620062 +>6100620061 +>61006200 +>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 + + unset foo + print -v foo once more + typeset -p foo + printf -v foo "%s\0%s-" into the breach + typeset -p foo +0:print and printf into a variable +>typeset -g foo='once more' +>typeset -g foo=$'into\C-@the-breach\C-@-' + + typeset -a foo + print -f '%2$d %4s' -v foo one 1 two 2 three 3 + typeset -p foo +0:printf into an array variable +>typeset -a foo=( '1 one' '2 two' '3 three' ) + + typeset -a foo + print -f '%s' -v foo string + typeset -p foo +0:printf to an array variable without format string reuse +>typeset foo=string + + printf - + printf - - + printf -- + printf -- - + printf -- -- + printf -x -v foo + # Final print for newline on stdout + print +0:regression test of printf with assorted ambiguous options or formats +>------x +?(eval):printf:3: not enough arguments diff --git a/dotfiles/system/.zsh/modules/Test/B04read.ztst b/dotfiles/system/.zsh/modules/Test/B04read.ztst new file mode 100644 index 0000000..25c3d41 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B04read.ztst @@ -0,0 +1,112 @@ +# Tests for the read builtin + +# Tested elsewhere: +# reading from a coprocess A01grammar, A04redirect + +# Not tested: +# -c/-l/-n (options for compctl functions) +# -q/-s (needs a tty) + +%test + + read <<<'hello world' + print $REPLY +0:basic read command +>hello world + + read -A <<<'hello world' + print $reply[2] +0:array read +>world + + read -k3 -u0 <<foo + + for char in y Y n N X $'\n'; do + read -q -u0 <<<$char + print $? + done +0:read yes or no, default no +>0 +>0 +>1 +>1 +>1 +>1 + + read -d: <<foo + + print foo:bar|IFS=: read -A + print $reply +0:use different, IFS separator to array +>foo bar + + print -z hello world; read -z + print $REPLY +0:read from editor buffer stack +>hello world + + unset REPLY + read -E <<hello +>hello + + unset REPLY + read -e <<hello +> + + read -e -t <<hello + + SECONDS=0 + read -e -t 5 <<hello +>0 + + print -n 'Testing the\0null hypothesis\0' | + while read -d $'\0' line; do print $line; done +0:read with null delimiter +>Testing the +>null hypothesis + +# Note that trailing NULLs are not stripped even if they are in +# $IFS; only whitespace characters contained in $IFS are stripped. + print -n $'Aaargh, I hate nulls.\0\0\0' | read line + print ${#line} +0:read with trailing metafied characters +>24 + + (typeset -r foo + read foo) <<one +>two +>three +>one:two:three + + array=() + read -Ae array <<<'four five six' + print ${(j.:.)array} +0:Behaviour of -A and -e combination +>four +>five +>six +> diff --git a/dotfiles/system/.zsh/modules/Test/B05eval.ztst b/dotfiles/system/.zsh/modules/Test/B05eval.ztst new file mode 100644 index 0000000..6427d6f --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B05eval.ztst @@ -0,0 +1,34 @@ +# Tests for the eval builtin. +# This is quite short; eval is widely tested throughout the test suite +# and its basic behaviour is fairly straightforward. + +%prep + + cmd='print $?' + +%test + + false + eval $cmd +0:eval retains value of $? +>1 + + # no point getting worked up over what the error message is... + ./command_not_found 2>/dev/null + eval $cmd +0:eval after command not found +>127 + + # trick the test system + sp= + false + eval " + $sp + $sp + $sp + " +0:eval with empty command resets the status + + false + eval +0:eval with empty command resets the status diff --git a/dotfiles/system/.zsh/modules/Test/B06fc.ztst b/dotfiles/system/.zsh/modules/Test/B06fc.ztst new file mode 100644 index 0000000..922b001 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B06fc.ztst @@ -0,0 +1,25 @@ +# Tests of fc command +%prep + + mkdir fc.tmp + cd fc.tmp + print 'fc -l foo' >fcl + +%test + $ZTST_testdir/../Src/zsh -f ./fcl +1:Checking that fc -l foo doesn't core dump when history is empty +?./fcl:fc:1: event not found: foo + + PS1='%% ' $ZTST_testdir/../Src/zsh +Z -fsi <<< $'fc -p /dev/null 0 0\n:' +0:Checking that fc -p doesn't core dump when history size is zero +*?*%* + + PS1='%% ' $ZTST_testdir/../Src/zsh +Z -fsi <<< 'fc -p /dev/null a 0' +1:Checking that fc -p rejects non-integer history size +*?*% fc: HISTSIZE must be an integer +*?*%* + + PS1='%% ' $ZTST_testdir/../Src/zsh +Z -fsi <<< 'fc -p /dev/null 0 a' +1:Checking that fc -p rejects non-integer history save size +*?*% fc: SAVEHIST must be an integer +*?*%* diff --git a/dotfiles/system/.zsh/modules/Test/B07emulate.ztst b/dotfiles/system/.zsh/modules/Test/B07emulate.ztst new file mode 100644 index 0000000..2de097e --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B07emulate.ztst @@ -0,0 +1,253 @@ +# Test the "emulate" builtin and related functions. + +%prep + + isset() { + print -n "${1}: " + if [[ -o $1 ]]; then print yes; else print no; fi + } + showopts() { + # Set for Bourne shell emulation + isset shwordsplit + # Set in native mode and unless "emulate -R" is in use + isset banghist + } + cshowopts() { + showopts + # Show a csh option, too + isset cshnullglob + } + +%test + + (print Before + showopts + fn() { + emulate sh + } + fn + print After + showopts) +0:Basic use of emulate +>Before +>shwordsplit: no +>banghist: yes +>After +>shwordsplit: yes +>banghist: yes + + fn() { + emulate -L sh + print During + showopts + } + print Before + showopts + fn + print After + showopts +0:Use of emulate -L +>Before +>shwordsplit: no +>banghist: yes +>During +>shwordsplit: yes +>banghist: yes +>After +>shwordsplit: no +>banghist: yes + + (print Before + showopts + emulate -R sh + print After + showopts) +0:Use of emulate -R +>Before +>shwordsplit: no +>banghist: yes +>After +>shwordsplit: yes +>banghist: no + + print Before + showopts + emulate sh -c 'print During; showopts' + print After + showopts +0:Use of emulate -c +>Before +>shwordsplit: no +>banghist: yes +>During +>shwordsplit: yes +>banghist: yes +>After +>shwordsplit: no +>banghist: yes + + print Before + showopts + emulate -R sh -c 'print During; showopts' + print After + showopts +0:Use of emulate -R -c +>Before +>shwordsplit: no +>banghist: yes +>During +>shwordsplit: yes +>banghist: no +>After +>shwordsplit: no +>banghist: yes + + print Before + showopts + emulate -R sh -c 'shshowopts() { showopts; }' + print After definition + showopts + print In sticky emulation + shshowopts + print After sticky emulation + showopts +0:Basic sticky function emulation +>Before +>shwordsplit: no +>banghist: yes +>After definition +>shwordsplit: no +>banghist: yes +>In sticky emulation +>shwordsplit: yes +>banghist: no +>After sticky emulation +>shwordsplit: no +>banghist: yes + + print Before + cshowopts + emulate -R sh -c 'shshowopts() { cshowopts; }' + emulate csh -c 'cshshowopts() { + cshowopts + print In nested sh emulation + shshowopts + }' + print After definition + cshowopts + print In sticky csh emulation + cshshowopts + print After sticky emulation + cshowopts +0:Basic sticky function emulation +>Before +>shwordsplit: no +>banghist: yes +>cshnullglob: no +>After definition +>shwordsplit: no +>banghist: yes +>cshnullglob: no +>In sticky csh emulation +>shwordsplit: no +>banghist: yes +>cshnullglob: yes +>In nested sh emulation +>shwordsplit: yes +>banghist: no +>cshnullglob: no +>After sticky emulation +>shwordsplit: no +>banghist: yes +>cshnullglob: no + + isalp() { if [[ -o alwayslastprompt ]]; then print on; else print off; fi; } + emulate sh -c 'shfunc_inner() { setopt alwayslastprompt; }' + emulate csh -c 'cshfunc_inner() { setopt alwayslastprompt; }' + emulate sh -c 'shfunc_outer() { + unsetopt alwayslastprompt; + shfunc_inner; + isalp + unsetopt alwayslastprompt + cshfunc_inner + isalp + }' + shfunc_outer +0:Sticky emulation not triggered if sticky emulation unchanged +>on +>off + + ( + setopt ignorebraces + emulate zsh -o extendedglob -c ' + [[ -o ignorebraces ]] || print "Yay, ignorebraces was reset" + [[ -o extendedglob ]] && print "Yay, extendedglob is set" + ' + ) +0:emulate -c with options +>Yay, ignorebraces was reset +>Yay, extendedglob is set + + ( + setopt ignorebraces + emulate zsh -o extendedglob + [[ -o ignorebraces ]] || print "Yay, ignorebraces is no longer set" + [[ -o extendedglob ]] && print "Yay, extendedglob is set" + ) +0:emulate with options but no -c +>Yay, ignorebraces is no longer set +>Yay, extendedglob is set + + emulate zsh -o fixallmybugs 'print This was executed, bad' +1:emulate -c with incorrect options +?(eval):emulate:1: no such option: fixallmybugs + + emulate zsh -c ' + func() { [[ -o extendedglob ]] || print extendedglob is off } + ' + func + emulate zsh -o extendedglob -c ' + func() { [[ -o extendedglob ]] && print extendedglob is on } + ' + func +0:options specified alongside emulation are also sticky +>extendedglob is off +>extendedglob is on + + emulate zsh -o extendedglob -c ' + func_inner() { setopt nobareglobqual } + ' + emulate zsh -o extendedglob -c ' + func_outer() { + func_inner + [[ -o bareglobqual ]] || print bareglobqual was turned off + [[ -o extendedglob ]] && print extendedglob is on, though + } + ' + [[ -o extendedglob ]] || print extendedglob is initially off + func_outer +0:options propagate between identical emulations +>extendedglob is initially off +>bareglobqual was turned off +>extendedglob is on, though + + emulate zsh -o extendedglob -c ' + func_inner() { setopt nobareglobqual } + ' + emulate zsh -o extendedglob -o cbases -c ' + func_outer() { + func_inner + [[ -o bareglobqual ]] && print bareglobqual is still on + [[ -o extendedglob ]] && print extendedglob is on, too + } + ' + [[ -o extendedglob ]] || print extendedglob is initially off + func_outer +0:options do not propagate between different emulations +>extendedglob is initially off +>bareglobqual is still on +>extendedglob is on, too + + emulate sh -c '[[ a == a ]]' +0:regression test for POSIX_ALIASES reserved words +F:Some reserved tokens are handled in alias expansion diff --git a/dotfiles/system/.zsh/modules/Test/B08shift.ztst b/dotfiles/system/.zsh/modules/Test/B08shift.ztst new file mode 100644 index 0000000..0aa9226 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B08shift.ztst @@ -0,0 +1,33 @@ +# Test the shift builtin. + +%test + + set -- one two three four five six seven eight nine ten + shift + print $* + shift 2 + print $* + shift -p 3 + print $* + shift -p + print $* +0:shifting positional parameters +>two three four five six seven eight nine ten +>four five six seven eight nine ten +>four five six seven +>four five six + + array=(yan tan tether mether pip azer sezar akker conter dick) + shift 2 array + print $array + shift array + print $array + shift -p 3 array + print $array + shift -p array + print $array +0:shifting array +>tether mether pip azer sezar akker conter dick +>mether pip azer sezar akker conter dick +>mether pip azer sezar +>mether pip azer diff --git a/dotfiles/system/.zsh/modules/Test/B09hash.ztst b/dotfiles/system/.zsh/modules/Test/B09hash.ztst new file mode 100644 index 0000000..7b5dfb4 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/B09hash.ztst @@ -0,0 +1,79 @@ +# The hash builtin is most used for the command hash table, which is +# populated automatically. This is therefore highly system specific, +# so mostly we'll test with the directory hash table: the logic is +# virtually identical but with the different table, and furthermore +# the shell doesn't care whether the directory exists unless you refer +# to it in a context that needs one. + +%prep + populate_hash() { + hash -d one=/first/directory + hash -d two=/directory/the/second + hash -d three=/noch/ein/verzeichnis + hash -d four=/bored/with/this/now + } + +%test + + hash -d +0:Directory hash initially empty + + populate_hash + hash -d +0:Populating directory hash and output with sort +>four=/bored/with/this/now +>one=/first/directory +>three=/noch/ein/verzeichnis +>two=/directory/the/second + + hash -rd + hash -d +0:Empty hash + + populate_hash + hash -d +0:Refill hash +>four=/bored/with/this/now +>one=/first/directory +>three=/noch/ein/verzeichnis +>two=/directory/the/second + + hash -dL +0:hash -L option +>hash -d four=/bored/with/this/now +>hash -d one=/first/directory +>hash -d three=/noch/ein/verzeichnis +>hash -d two=/directory/the/second + + hash -dm 't*' +0:hash -m option +>three=/noch/ein/verzeichnis +>two=/directory/the/second + + hash -d five=/yet/more six=/here/we/go seven=/not/yet/eight + hash -d +0:Multiple assignments +>five=/yet/more +>four=/bored/with/this/now +>one=/first/directory +>seven=/not/yet/eight +>six=/here/we/go +>three=/noch/ein/verzeichnis +>two=/directory/the/second + + hash -d one two three +0:Multiple arguments with no assignment not in verbose mode + + hash -vd one two three +0:Multiple arguments with no assignment in verbose mode +>one=/first/directory +>two=/directory/the/second +>three=/noch/ein/verzeichnis + + hash -d t-t=/foo + i="~t-t" + print ~t-t/bar + print ${~i}/rab +0:Dashes are untokenized in directory hash names +>/foo/bar +>/foo/rab diff --git a/dotfiles/system/.zsh/modules/Test/C01arith.ztst b/dotfiles/system/.zsh/modules/Test/C01arith.ztst new file mode 100644 index 0000000..61da763 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/C01arith.ztst @@ -0,0 +1,422 @@ +# Tests corresponding to the texinfo node `Arithmetic Evaluation' + +%test + + integer light there + (( light = 42 )) && + let 'there = light' && + print $(( there )) +0:basic integer arithmetic +>42 + + float light there + integer rnd + (( light = 3.1415 )) && + let 'there = light' && + print -- $(( rnd = there * 10000 )) +# save rounding problems by converting to integer +0:basic floating point arithmetic +>31415 + + integer rnd + (( rnd = ((29.1 % 13.0 * 10) + 0.5) )) + print $rnd +0:Test floating point modulo function +>31 + + print $(( 0x10 + 0X01 + 2#1010 )) +0:base input +>27 + + float light + (( light = 4 )) + print $light + typeset -F light + print $light +0:conversion to float +>4.000000000e+00 +>4.0000000000 + + integer i + (( i = 32.5 )) + print $i +0:conversion to int +>32 + + integer i + (( i = 4 - - 3 * 7 << 1 & 7 ^ 1 | 16 ** 2 )) + print $i +0:precedence (arithmetic) +>1591 + + fn() { + setopt localoptions c_precedences + integer i + (( i = 4 - - 3 * 7 << 1 & 7 ^ 1 | 16 ** 2 )) + print $i + } + fn +0:precedence (arithmetic, with C_PRECEDENCES) +>259 + + print $(( 1 < 2 || 2 < 2 && 3 > 4 )) +0:precedence (logical) +>1 + + print $(( 1 + 4 ? 3 + 2 ? 4 + 3 ? 5 + 6 ? 4 * 8 : 0 : 0 : 0 : 0 )) +0:precedence (ternary) +>32 + + print $(( 3 ? 2 )) +1:parsing ternary (1) +?(eval):1: bad math expression: ':' expected + + print $(( 3 ? 2 : 1 : 4 )) +1:parsing ternary (2) +?(eval):1: bad math expression: ':' without '?' + + print $(( 0, 4 ? 3 : 1, 5 )) +0:comma operator +>5 + + foo=000 + print $(( ##A + ##\C-a + #foo + $#foo )) +0:#, ## and $# +>117 + + print $((##)) +1:## without following character +?(eval):1: bad math expression: character missing after ## + + print $((## )) +0:## followed by a space +>32 + + integer i + (( i = 3 + 5 * 1.75 )) + print $i +0:promotion to float +>11 + + typeset x && + (( x = 3.5 )) && + print $x && + (( x = 4 )) && + print $x +0:use of scalars to store integers and floats +>3.5 +>4 + + (( newarray[unsetvar] = 1 )) +2:error using unset variable as index +?(eval):1: newarray: assignment to invalid subscript range + + integer setvar=1 + (( newarray[setvar]++ )) + (( newarray[setvar]++ )) + print ${(t)newarray} ${#newarray} ${newarray[1]} +0:setting array elements in math context +>array 1 2 + + xarr=() + (( xarr = 3 )) + print ${(t)xarr} $xarr +0:converting type from array +>integer 3 + + print $(( 13 = 42 )) +1:bad lvalue +?(eval):1: bad math expression: lvalue required + + x=/bar + (( x = 32 )) + print $x +0:assigning to scalar which contains non-math string +>32 + + print $(( )) +0:empty math parse e.g. $(( )) acts like a zero +>0 + + print $(( a = )) +1:empty assignment +?(eval):1: bad math expression: operand expected at end of string + + print $(( 3, )) +1:empty right hand of comma +?(eval):1: bad math expression: operand expected at end of string + + print $(( 3,,4 )) +1:empty middle of comma +?(eval):1: bad math expression: operand expected at `,4 ' + + print $(( (3 + 7, 4), 5 )) +0:commas and parentheses, part 1 +>5 + + print $(( 5, (3 + 7, 4) )) +0:commas and parentheses, part 1 +>4 + + print $(( 07.5 )) + (setopt octalzeroes; print $(( 09.5 ))) +0:leading zero doesn't affect floating point +>7.5 +>9.5 + + (setopt octalzeroes; print $(( 09 ))) +1:octalzeroes rejects invalid constants +?(eval):1: bad math expression: operator expected at `9 ' + + (setopt octalzeroes; print $(( 08#77 ))) +0:octalzeroes doesn't affect bases +>63 + + print $(( 36#z )) +0:bases up to 36 work +>35 + + print $(( 37#z )) +1:bases beyond 36 don't work +?(eval):1: invalid base (must be 2 to 36 inclusive): 37 + + print $(( 3 + "fail" )) +1:parse failure in arithmetic +?(eval):1: bad math expression: operand expected at `"fail" ' + + alias 3=echo + print $(( 3 + "OK"); echo "Worked") +0:not a parse failure because not arithmetic +>+ OK Worked + + fn() { + emulate -L zsh + print $(( [#16] 255 )) + print $(( [##16] 255 )) + setopt cbases + print $(( [#16] 255 )) + print $(( [##16] 255 )) + } + fn +0:doubled # in base removes radix +>16#FF +>FF +>0xFF +>FF + + array=(1) + x=0 + (( array[++x]++ )) + print $x + print $#array + print $array +0:no double increment for subscript +>1 +>1 +>2 + + # This is a bit naughty... the value of array + # isn't well defined since there's no sequence point + # between the increments of x, however we just want + # to be sure that in this case, unlike the above, + # x does get incremented twice. + x=0 + array=(1 2) + (( array[++x] = array[++x] + 1 )) + print $x +0:double increment for repeated expression +>2 + + # Floating point. Default precision should take care of rounding errors. + print $(( 1_0.000_000e0_1 )) + # Integer. + print $(( 0x_ff_ff_ )) + # _ are parts of variable names that don't start with a digit + __myvar__=42 + print $(( __myvar__ + $__myvar__ )) + # _ is not part of variable name that does start with a digit + # (which are substituted before math eval) + set -- 6 + print $(( $1_000_000 )) + # Underscores in expressions with no whitespace + print $(( 3_000_+4_000_/2 )) + # Underscores may appear in the base descriptor, for what it's worth... + print $(( 1_6_#f_f_ )) +0:underscores in math constants +>100. +>65535 +>84 +>6000000 +>5000 +>255 + + # Force floating point. + for expr in "3/4" "0x100/0x200" "0x30/0x10"; do + print $(( $expr )) + setopt force_float + print $(( $expr )) + unsetopt force_float + done +0:Forcing floating point constant evaluation, or not. +>0 +>0.75 +>0 +>0.5 +>3 +>3. + + print $(( 0x30 + 0.5 )) + print $(( 077 + 0.5 )) + (setopt octalzeroes; print $(( 077 + 0.5 )) ) +0:Mixed float and non-decimal integer constants +>48.5 +>77.5 +>63.5 + + underscore_integer() { + setopt cbases localoptions + print $(( [#_] 1000000 )) + print $(( [#16_] 65536 )) + print $(( [#16_4] 65536 * 32768 )) + } + underscore_integer +0:Grouping output with underscores: integers +>1_000_000 +>0x10_000 +>0x8000_0000 + + print $(( [#_] (5. ** 10) / 16. )) +0:Grouping output with underscores: floating point +>610_351.562_5 + + env SHLVL=1+RANDOM $ZTST_testdir/../Src/zsh -f -c 'print $SHLVL' +0:Imported integer functions are not evaluated +>2 + + print $(( 0b0 + 0b1 + 0b11 + 0b110 )) +0:Binary input +>10 + + print $(( 0b2 )) +1:Binary numbers don't tend to have 2's in +?(eval):1: bad math expression: operator expected at `2 ' +# ` for emacs shell mode + + integer varassi + print $(( varassi = 5.5 / 2.0 )) + print $varassi +0:Integer variable assignment converts result to integer +>2 +>2 +# It's hard to test for integer to float. + + integer ff1=3 ff2=4 + print $(( ff1/ff2 )) + setopt force_float + print $(( ff1/ff2 )) + unsetopt force_float +0:Variables are forced to floating point where necessary +# 0.75 is exactly representable, don't expect rounding error. +>0 +>0.75 + + # The following tests for a bug that only happens when + # backing up over input read a line at a time, so we'll + # read the input from stdin. + $ZTST_testdir/../Src/zsh -f <<<' + print $((echo first command + ); echo second command) + print third command + ' +0:Backing up a line of input when finding out it's not arithmetic +>first command second command +>third command + + $ZTST_testdir/../Src/zsh -f <<<' + print $((3 + + 4)) + print next line + ' +0:Not needing to back up a line when reading multiline arithmetic +>7 +>next line + + $ZTST_testdir/../Src/zsh -f <<<' + print $((case foo in + bar) + echo not this no, no + ;; + foo) + echo yes, this one + ;; + esac) + print after case in subshell) + ' +0:Non-arithmetic subst with command subsitution parse from hell +>yes, this one after case in subshell + + print "a$((echo one subst) + (echo two subst))b" +0:Another tricky case that is actually a command substitution +>aone subst +>two substb + + print "x$((echo one frob); (echo two frob))y" +0:Same on a single line +>xone frob +>two froby + + # This case actually only works by accident: if it wasn't for the + # unbalanced parenthesis this would be a valid math substitution. + # Hence it's definitely not recommended code. However, it does give + # the algorithm an extra check. + print $((case foo in + foo) + print Worked OK + ;; + esac)) +0:Would-be math expansion with extra parenthesis making it a cmd subst +>Worked OK + + (setopt extendedglob + set -- 32.463 + print ${$(( $1 * 100 ))%%.[0-9]#}) +0:Arithmetic substitution nested in parameter substitution +>3246 + + print $((`:`)) +0:Null string in arithmetic evaluation after command substitution +>0 + + print $(( 1 + $(( 2 + 3 )) )) + print $(($((3+4)))) + print $((1*$((2*$((3))*4))*5)) +0:Nested math substitutions. Yes, I know, very useful. +>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 ')' + + unset number + (( number = 3 )) + print ${(t)number} + unset number + (setopt posix_identifiers + (( number = 3 )) + print ${(t)number}) +0:type of variable when created in arithmetic context +>integer +>scalar diff --git a/dotfiles/system/.zsh/modules/Test/C02cond.ztst b/dotfiles/system/.zsh/modules/Test/C02cond.ztst new file mode 100644 index 0000000..3852501 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/C02cond.ztst @@ -0,0 +1,448 @@ +# Tests corresponding to the texinfo node `Conditional Expressions' + +%prep + + umask 077 + + mkdir cond.tmp + + cd cond.tmp + + typeset -gi isnfs + [[ "$(find . -prune -fstype nfs 2>/dev/null)" == "." ]] && isnfs=1 + if (( isnfs )) && + (cd -q ${ZTST_tmp} >/dev/null 2>&1 && + [[ "$(find . -prune -fstype nfs 2>/dev/null)" != "." ]]); then + filetmpprefix=${ZTST_tmp}/condtest-$$- + isnfs=0 + else + filetmpprefix= + fi + newnewnew=${filetmpprefix}newnewnew + unmodified=${filetmpprefix}unmodified + zlnfs=${filetmpprefix}zlnfs + + touch $unmodified + + touch zerolength + chgrp $EGID zerolength + + touch $zlnfs + chgrp $EGID $zlnfs + + print 'Garbuglio' >nonzerolength + + mkdir modish + chgrp $EGID modish + + chmod 7710 modish # g+xs,u+s,+t + chmod g+s modish # two lines combined work around chmod bugs + + touch unmodish + chmod 000 unmodish + + print 'MZ' > cmd.exe + chmod +x cmd.exe +%test + + [[ -a zerolength && ! -a nonexistent ]] +0:-a cond + + # Find a block special file system. This is a little tricky. + block=$(find /dev(|ices)/ -type b -print) + if [[ -n $block ]]; then + [[ -b $block[(f)1] && ! -b zerolength ]] + else + print -u$ZTST_fd 'Warning: Not testing [[ -b blockdevice ]] (no devices found)' + [[ ! -b zerolength ]] + fi +0D:-b cond + + # Use hardcoded /dev/tty because globbing inside /dev fails on Cygwin + char=/dev/tty + [[ -c $char && ! -c $zerolength ]] +0:-c cond + + [[ -d . && ! -d zerolength ]] +0:-d cond + + [[ -e zerolength && ! -e nonexistent ]] +0:-e cond + + if [[ -n $block ]]; then + [[ -f zerolength && ! -f cond && ! -f $char && ! -f $block[(f)1] && ! -f . ]] + else + print -u$ZTST_fd 'Warning: Not testing [[ -f blockdevice ]] (no devices found)' + [[ -f zerolength && ! -f cond && ! -f $char && ! -f . ]] + fi +0:-f cond + + [[ -g modish && ! -g zerolength ]] +0:-g cond + + ln -s zerolength link + [[ -h link && ! -h zerolength ]] +0:-h cond + + [[ -k modish && ! -k zerolength ]] +0:-k cond + + foo=foo + bar= + [[ -n $foo && ! -n $bar && ! -n '' ]] +0:-n cond + + [[ -o rcs && ! -o norcs && -o noerrexit && ! -o errexit ]] +0:-o cond + + if ! grep '#define HAVE_FIFOS' $ZTST_testdir/../config.h; then + print -u$ZTST_fd 'Warning: Not testing [[ -p pipe ]] (FIFOs not supported)' + [[ ! -p zerolength ]] + else + if whence mkfifo && mkfifo pipe || mknod pipe p; then + [[ -p pipe && ! -p zerolength ]] + else + print -u$ZTST_fd 'Warning: Not testing [[ -p pipe ]] (cannot create FIFO)' + [[ ! -p zerolength ]] + fi + fi +0dD:-p cond + + if (( EUID == 0 )); then + print -u$ZTST_fd 'Warning: Not testing [[ ! -r file ]] (root reads anything)' + [[ -r zerolength && -r unmodish ]] + elif [[ $OSTYPE = cygwin ]]; then + print -u$ZTST_fd 'Warning: Not testing [[ ! -r file ]] + (all files created by user may be readable)' + [[ -r zerolength ]] + else + [[ -r zerolength && ! -r unmodish ]] + fi +0:-r cond + + [[ -s nonzerolength && ! -s zerolength ]] +0:-s cond + +# no simple way of guaranteeing test for -t + + [[ -u modish && ! -u zerolength ]] +0:-u cond + + [[ -x cmd.exe && ! -x zerolength ]] +0:-x cond + + [[ -z $bar && -z '' && ! -z $foo ]] +0:-z cond + + [[ -L link && ! -L zerolength ]] +0:-L cond + +# hard to guarantee a file not owned by current uid + [[ -O zerolength ]] +0:-O cond + + [[ -G zerolength ]] +0:-G cond + +# can't be bothered with -S + + if [[ ${mtab::="$({mount || /sbin/mount || /usr/sbin/mount} 2>/dev/null)"} = *[(]?*[)] ]]; then + print -u $ZTST_fd 'This test takes two seconds...' + else + unmodified_ls="$(ls -lu $unmodified)" + print -u $ZTST_fd 'This test takes up to 60 seconds...' + fi + sleep 2 + touch $newnewnew + if [[ $OSTYPE == "cygwin" ]]; then + ZTST_skip="[[ -N file ]] not supported on Cygwin" + elif (( isnfs )); then + ZTST_skip="[[ -N file ]] not supported with NFS" + elif { (( ! $+unmodified_ls )) && + cat $unmodified && + { df -k -- ${$(print -r -- "$mtab" | + awk '/noatime/ {print $1,$3}'):-""} | tr -s ' ' | + fgrep -- "$(df -k . | tail -1 | tr -s ' ')" } >&/dev/null } || + { (( $+unmodified_ls )) && SECONDS=0 && + ! until (( SECONDS >= 58 )); do + ZTST_hashmark; sleep 2; cat $unmodified + [[ $unmodified_ls != "$(ls -lu $unmodified)" ]] && break + done }; then + ZTST_skip="[[ -N file ]] not supported with noatime file system" + else + [[ -N $newnewnew && ! -N $unmodified ]] + fi +0:-N cond +F:This test can fail on NFS-mounted filesystems as the access and +F:modification times are not updated separately. The test will fail +F:on HFS+ (Apple Mac OS X default) filesystems because access times +F:are not recorded. Also, Linux ext3 filesystems may be mounted +F:with the noatime option which does not update access times. +F:Failures in these cases do not indicate a problem in the shell. + + [[ $newnewnew -nt $zlnfs && ! ($unmodified -nt $zlnfs) ]] +0:-nt cond + + [[ $zlnfs -ot $newnewnew && ! ($zlnfs -ot $unmodified) ]] +0:-ot cond + + [[ link -ef zerolength && ! (link -ef nonzerolength) ]] +0:-ef cond + + [[ foo = foo && foo != bar && foo == foo && foo != '' ]] +0:=, == and != conds + + [[ bar < foo && foo > bar ]] +0:< and > conds + + [[ $(( 3 + 4 )) -eq 0x07 && $(( 5 * 2 )) -ne 0x10 ]] +0:-eq and -ne conds + + [[ 3 -lt 04 && 05 -gt 2 ]] +0:-lt and -gt conds + + [[ 3 -le 3 && ! (4 -le 3) ]] +0:-le cond + + [[ 3 -ge 3 && ! (3 -ge 4) ]] +0:-ge cond + + [[ 1 -lt 2 || 2 -lt 2 && 3 -gt 4 ]] +0:|| and && in conds + + if ! grep '#define PATH_DEV_FD' $ZTST_testdir/../config.h; then + print -u$ZTST_fd "Warning: not testing [[ -e /dev/fd/0 ]] (/dev/fd not supported)" + true + else + [[ -e /dev/fd/0 ]] + fi +0dD:/dev/fd support in conds handled by access + + if ! grep '#define PATH_DEV_FD' $ZTST_testdir/../config.h; then + print -u$ZTST_fd "Warning: not testing [[ -O /dev/fd/0 ]] (/dev/fd not supported)" + true + else + [[ -O /dev/fd/0 ]] + fi +0dD:/dev/fd support in conds handled by stat + + [[ ( -z foo && -z foo ) || -z foo ]] +1:complex conds with skipping + + [ '' != bar -a '' = '' ] +0:strings with `[' builtin + + [ `echo 0` -lt `echo 1` ] +0:substitution in `[' builtin + + [ -n foo scrimble ] +2:argument checking for [ builtin +?(eval):[:1: too many arguments + + test -n foo scramble +2:argument checking for test builtin +?(eval):test:1: too many arguments + + [ -n foo scrimble scromble ] +2:argument checking for [ builtin +?(eval):[:1: too many arguments + + test -n foo scramble scrumble +2:argument checking for test builtin +?(eval):test:1: too many arguments + + [ -n foo -a -n bar scrimble ] +2:argument checking for [ builtin +?(eval):[:1: too many arguments + + test -n foo -a -z "" scramble +2:argument checking for test builtin +?(eval):test:1: too many arguments + + fn() { + # careful: first file must exist to trigger bug + [[ -e $unmodified ]] || print Where\'s my file\? + [[ $unmodified -nt NonExistentFile ]] + print status = $? + } + fn +0:-nt shouldn't abort on non-existent files +>status = 1 + + str='string' empty='' + [[ -v IFS && -v str && -v empty && ! -v str[3] && ! -v not_a_variable ]] +0:-v cond + + arr=( 1 2 3 4 ) empty=() + [[ -v arr && -v arr[1,4] && -v arr[1] && -v arr[4] && -v arr[-4] && + -v arr[(i)3] && ! -v arr[(i)x] && + ! -v arr[0] && ! -v arr[5] && ! -v arr[-5] && ! -v arr[2][1] && + ! -v arr[3]extra && -v empty && ! -v empty[1] ]] +0:-v cond with array + + typeset -A assoc=( key val num 4 ) + [[ -v assoc && -v assoc[key] && -v assoc[(i)*] && -v assoc[(I)*] && + ! -v assoc[x] && ! -v assoc[key][1] ]] +0:-v cond with association + + () { [[ -v 0 && -v 1 && -v 2 && ! -v 3 ]] } arg '' +0:-v cond with positional parameters + +# core dumps on failure + if zmodload zsh/regex 2>/dev/null; then + echo >regex_test.sh 'if [[ $# = 1 ]]; then + if [[ $1 =~ /?[^/]+:[0-9]+:$ ]]; then + : + fi + fi + exit 0' + $ZTST_testdir/../Src/zsh -f ./regex_test.sh + fi +0:regex tests shouldn't crash + + if zmodload zsh/regex 2>/dev/null; then + ( # subshell in case coredump test failed + string="this has stuff in it" + bad_regex=0 + if [[ $string =~ "h([a-z]*) s([a-z]*) " ]]; then + if [[ "$MATCH $MBEGIN $MEND" != "has stuff 6 15" ]]; then + print -r "regex variables MATCH MBEGIN MEND: + '$MATCH $MBEGIN $MEND' + should be: + 'has stuff 6 15'" + bad_regex=1 + else + results=("as 7 8" "tuff 11 14") + for i in 1 2; do + if [[ "$match[$i] $mbegin[$i] $mend[$i]" != $results[i] ]]; then + print -r "regex variables match[$i] mbegin[$i] mend[$i]: + '$match[$i] $mbegin[$i] $mend[$i]' + should be + '$results[$i]'" + bad_regex=1 + break + fi + done + fi + (( bad_regex )) || print OK + else + print -r "regex failed to match '$string'" + fi + ) + else + # if it didn't load, tough, but not a test error + ZTST_skip="regexp library not found." + fi +0:MATCH, MBEGIN, MEND, match, mbegin, mend +>OK + + if zmodload zsh/regex 2>/dev/null; then + ( # subshell because regex module may dump core, see above + if [[ a =~ a && b == b ]]; then + print OK + else + print "regex =~ inverted following test" + fi + ) + else + # not a test error + ZTST_skip="regexp library not found." + fi +0:regex infix operator should not invert following conditions +>OK + + [[ -fail badly ]] +2:Error message for unknown prefix condition +?(eval):1: unknown condition: -fail + + [[ really -fail badly ]] +2:Error message for unknown infix condition +?(eval):1: unknown condition: -fail + + crashme() { + if [[ $1 =~ ^http:* ]] + then + url=${1#*=} + fi + } + which crashme +0:Regression test for examining code with regular expression match +>crashme () { +> if [[ $1 =~ ^http:* ]] +> then +> url=${1#*=} +> fi +>} + + weirdies=( + '! -a !' + '! -o !' + '! -a' + '! -o' + '! -a ! -a !' + '! = !' + '! !' + '= -a o' + '! = -a o') + for w in $weirdies; do + eval test $w + print $? + done +0:test compatability weirdness: treat ! as a string sometimes +>0 +>0 +>1 +>0 +>0 +>0 +>1 +>0 +>1 + + foo='' + [[ $foo ]] || print foo is empty + foo=full + [[ $foo ]] && print foo is full +0:bash compatibility with single [[ ... ]] argument +>foo is empty +>foo is full + + test -z \( || print Not zero 1 + test -z \< || print Not zero 2 + test -n \( && print Not zero 3 + test -n \) && print Not zero 4 + [ -n \> ] && print Not zero 5 + [ -n \! ] && print Not zero 6 +0:test with two arguments and a token +>Not zero 1 +>Not zero 2 +>Not zero 3 +>Not zero 4 +>Not zero 5 +>Not zero 6 + + [ '(' = ')' ] || print OK 1 + [ '((' = '))' ] || print OK 2 + [ '(' = '(' ] && print OK 3 + [ '(' non-empty-string ')' ] && echo OK 4 + [ '(' '' ')' ] || echo OK 5 +0:yet more old-fashioned test fix ups: prefer comparison to parentheses +>OK 1 +>OK 2 +>OK 3 +>OK 4 +>OK 5 + + fn() { [[ 'a' == 'b' || 'b' = 'c' || 'c' != 'd' ]] } + which -x2 fn +0: = and == appear as input +>fn () { +> [[ 'a' == 'b' || 'b' = 'c' || 'c' != 'd' ]] +>} + +%clean + # This works around a bug in rm -f in some versions of Cygwin + chmod 644 unmodish + for tmpfile in $newnewnew $unmodified $zlnfs; do + [[ -f $tmpfile ]] && rm -f $tmpfile + done diff --git a/dotfiles/system/.zsh/modules/Test/C03traps.ztst b/dotfiles/system/.zsh/modules/Test/C03traps.ztst new file mode 100644 index 0000000..7bc0b48 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/C03traps.ztst @@ -0,0 +1,761 @@ +# Tests for both trap builtin and TRAP* functions. + +%prep + + setopt localtraps + mkdir traps.tmp && cd traps.tmp + +%test + + fn1() { + trap 'print EXIT1' EXIT + fn2() { trap 'print EXIT2' EXIT; } + fn2 + } + fn1 +0:Nested `trap ... EXIT' +>EXIT2 +>EXIT1 + + fn1() { + TRAPEXIT() { print EXIT1; } + fn2() { TRAPEXIT() { print EXIT2; }; } + fn2 + } + fn1 +0: Nested TRAPEXIT +>EXIT2 +>EXIT1 + + fn1() { + trap 'print EXIT1' EXIT + fn2() { trap - EXIT; } + fn2 + } + fn1 +0:Nested `trap - EXIT' on `trap ... EXIT' +>EXIT1 + + fn1() { + TRAPEXIT() { print EXIT1; } + fn2() { trap - EXIT; } + fn2 + } + fn1 +0:Nested `trap - EXIT' on `TRAPEXIT' +>EXIT1 + +# We can't test an EXIT trap for the shell as a whole, because +# we're inside a function scope which we don't leave when the +# subshell exits. Not sure if that's the correct behaviour, but +# it's sort of consistent. + ( fn1() { trap 'print Function 1 going' EXIT; exit; print Not reached; } + fn2() { trap 'print Function 2 going' EXIT; fn1; print Not reached; } + fn2 + ) +0:EXIT traps on functions when exiting from function +>Function 1 going +>Function 2 going + +# $ZTST_exe is relative to the parent directory. +# We ought to fix this in ztst.zsh... + (cd .. + $ZTST_exe -fc 'TRAPEXIT() { print Exited.; }') +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 + trap 'print INT1' INT + fn2() { trap 'print INT2' INT; trap; } + trap + fn2 + trap + } + fn1 +0: Nested `trap ... INT', not triggered +>trap -- 'print INT1' INT +>trap -- 'print INT2' INT +>trap -- 'print INT1' INT + + fn1() { + trap - + trap + TRAPINT() { print INT1; } + fn2() { TRAPINT() { print INT2; }; trap; } + trap + fn2 + trap + } + fn1 +0: Nested TRAPINT, not triggered +>TRAPINT () { +> print INT1 +>} +>TRAPINT () { +> print INT2 +>} +>TRAPINT () { +> print INT1 +>} + + fn1() { + trap - + trap 'print INT1' INT + fn2() { trap - INT; trap; } + trap + fn2 + trap + } + fn1 +0: Nested `trap - INT' on untriggered `trap ... INT' +>trap -- 'print INT1' INT +>trap -- 'print INT1' INT + +# Testing the triggering of traps here is very unpleasant. +# The delays are attempts to avoid race conditions, though there is +# no guarantee that they will work. Note the subtlety that the +# `sleep' in the function which receives the trap does *not* get the +# signal, only the parent shell, which is waiting for a SIGCHILD. +# (At least, that's what I think is happening.) Thus we have to wait at +# least the full two seconds to make sure we have got the output from the +# execution of the trap. + + print -u $ZTST_fd 'This test takes at least three seconds...' + fn1() { + trap 'print TERM1' TERM + fn2() { trap 'print TERM2; return 1' TERM; sleep 2; } + fn2 & + sleep 1 + kill -TERM $! + sleep 2 + } + fn1 +0: Nested `trap ... TERM', triggered on inner loop +>TERM2 + + print -u $ZTST_fd 'This test, too, takes at least three seconds...' + fn1() { + trap 'print TERM1; return 1' TERM + fn2() { trap 'print TERM2; return 1' TERM; } + fn2 + sleep 2 + } + fn1 & + sleep 1 + kill -TERM $! + sleep 2 +0: Nested `trap ... TERM', triggered on outer loop +>TERM1 + + TRAPZERR() { print error activated; } + fn() { print start of fn; false; print end of fn; } + fn + fn() { + setopt localoptions localtraps + unfunction TRAPZERR + print start of fn + false + print end of fn + } + fn + unfunction TRAPZERR + print finish +0: basic localtraps handling +>start of fn +>error activated +>end of fn +>start of fn +>end of fn +>finish + + TRAPZERR() { print 'ERR-or!'; } + f() { print f; false; } + t() { print t; } + f + f && t + t && f && true + t && f + testunset() { + setopt localtraps + unset -f TRAPZERR + print testunset + false + true + } + testunset + f + print status $? + unfunction TRAPZERR +0: more sophisticated error trapping +>f +>ERR-or! +>f +>t +>f +>t +>f +>ERR-or! +>testunset +>f +>ERR-or! +>status 1 + + f() { + setopt localtraps + TRAPWINCH() { print "Window changed. That wrecked the test."; } + } + f + f + functions TRAPWINCH +1:Unsetting ordinary traps with localtraps. + +# +# Returns from within traps are a perennial problem. +# The following two apply to returns in and around standard +# ksh-style traps. The intention is that a return value from +# within the function is preserved (i.e. statuses set by the trap +# are ignored) unless the trap explicitly executes `return', which makes +# it return from the enclosing function. +# + fn() { trap 'true' EXIT; return 1; } + fn +1: ksh-style EXIT traps preserve return value + + inner() { trap 'return 3' EXIT; return 2; } + outer() { inner; return 1; } + outer +3: ksh-style EXIT traps can force return status of enclosing function + +# Autoloaded traps are horrid, but unfortunately people expect +# them to work if we support them. + echo "print Running exit trap" >TRAPEXIT + ${${ZTST_exe##[^/]*}:-$ZTST_testdir/$ZTST_exe} -fc ' + fpath=(. $fpath) + autoload TRAPEXIT + print "Exiting, attempt 1" + exit + print "What?" + ' + ${${ZTST_exe##[^/]*}:-$ZTST_testdir/$ZTST_exe} -fc ' + fpath=(. $fpath) + autoload TRAPEXIT; + fn() { print Some function } + fn + print "Exiting, attempt 2" + exit + ' +0: autoloaded TRAPEXIT (exit status > 128 indicates an old bug is back) +>Exiting, attempt 1 +>Running exit trap +>Some function +>Exiting, attempt 2 +>Running exit trap + + print -u $ZTST_fd Another test that takes three seconds + gotsig=0 + signal_handler() { + echo "parent received signal" + gotsig=1 + } + child() { + sleep 1 + echo "child sending signal" + kill -15 $parentpid + sleep 2 + echo "child exiting" + exit 33 + } + parentpid=$$ + child & + childpid=$! + trap signal_handler 15 + echo "parent waiting" + wait $childpid + cstatus=$? + echo "wait #1 finished, gotsig=$gotsig, status=$cstatus" + gotsig=0 + wait $childpid + cstatus=$? + echo "wait #2 finished, gotsig=$gotsig, status=$cstatus" +0:waiting for trapped signal +>parent waiting +>child sending signal +>parent received signal +>wait #1 finished, gotsig=1, status=143 +>child exiting +>wait #2 finished, gotsig=0, status=33 + + fn1() { + setopt errexit + trap 'echo error1' ZERR + false + print Shouldn\'t get here 1a + } + fn2() { + setopt errexit + trap 'echo error2' ZERR + return 1 + print Shouldn\'t get here 2a + } + fn3() { + setopt errexit + TRAPZERR() { echo error3; } + false + print Shouldn\'t get here 3a + } + fn4() { + setopt errexit + TRAPZERR() { echo error4; } + return 1 + print Shouldn\'t get here 4a + } + (fn1; print Shouldn\'t get here 1b) + (fn2; print Shouldn\'t get here 2b) + (fn3; print Shouldn\'t get here 3b) + (fn4; print Shouldn\'t get here 4b) +1: Combination of ERR_EXIT and ZERR trap +>error1 +>error2 +>error3 +>error4 + + fn1() { TRAPZERR() { print trap; return 42; }; false; print Broken; } + (fn1) + print Working $? +0: Force return of containing function from TRAPZERR. +>trap +>Working 42 + + fn2() { trap 'print trap; return 42' ZERR; false; print Broken } + (fn2) + print Working $? +0: Return with non-zero status triggered from within trap '...' ZERR. +>trap +>Working 42 + + fn3() { TRAPZERR() { print trap; return 0; }; false; print OK this time; } + (fn3) + print Working $? +0: Normal return from TRAPZERR. +>trap +>OK this time +>Working 0 + + fn4() { trap 'print trap; return 0' ZERR; false; print Broken; } + (fn4) + print Working $? +0: Return with zero status triggered from within trap '...' ZERR. +>trap +>Working 0 + + { trap 'echo This subshell is exiting' EXIT; } | cat +0: EXIT trap set in current shell at left of pipeline +>This subshell is exiting + + ( trap 'echo This subshell is also exiting' EXIT; ) | cat +0: EXIT trap set in subshell at left of pipeline +>This subshell is also exiting + + ( trap 'echo Should only appear once at the end' EXIT + ( : trap reset here ) | cat + : trap not reset but not part of shell command list | cat + echo nothing after this should appear $( : trap reset here too) + ) +0: EXIT trap set in subshell reset in subsubshell +>nothing after this should appear +>Should only appear once at the end + + echo $( trap 'echo command substitution exited' EXIT ) +0: EXIT trap set in command substitution +>command substitution exited + + (cd ..; $ZTST_exe -fc 'setopt posixtraps; + TRAPEXIT() { print Exited; } + fn1() { trap; } + setopt localtraps # should be ignored by EXIT + fn2() { TRAPEXIT() { print No, really exited; } } + fn1 + fn2 + fn1') +0:POSIX_TRAPS option +>TRAPEXIT () { +> print Exited +>} +>TRAPEXIT () { +> print No, really exited +>} +>No, really exited + + (cd ..; $ZTST_exe -fc 'unsetopt posixtraps; + echo start program + emulate sh -c '\''testfn() { + echo start function + set -o | grep posixtraps + trap "echo EXIT TRAP TRIGGERED" EXIT + echo end function + }'\'' + testfn + echo program continuing + echo end of program') +0:POSIX_TRAPS effect on EXIT trap is sticky +>start program +>start function +>noposixtraps off +>end function +>program continuing +>end of program +>EXIT TRAP TRIGGERED + + (cd ..; $ZTST_exe -fc ' + echo entering program + emulate sh -c '\''trap "echo POSIX exit trap triggered" EXIT'\'' + fn() { + trap "echo native zsh function-local exit trap triggered" EXIT + echo entering native zsh function + } + fn + echo exiting program + ') +0:POSIX EXIT trap can have nested native mode EXIT trap +>entering program +>entering native zsh function +>native zsh function-local exit trap triggered +>exiting program +>POSIX exit trap triggered + + (cd ..; $ZTST_exe -fc ' + echo entering program + emulate sh -c '\''spt() { trap "echo POSIX exit trap triggered" EXIT; }'\'' + fn() { + trap "echo native zsh function-local exit trap triggered" EXIT + echo entering native zsh function + } + spt + fn + echo exiting program + ') +0:POSIX EXIT trap not replaced if defined within function +>entering program +>entering native zsh function +>native zsh function-local exit trap triggered +>exiting program +>POSIX exit trap triggered + + (set -e + printf "a\nb\n" | while read line + do + [[ $line = a* ]] || continue + ((ctr++)) + [[ $line = foo ]] + done + echo "ctr = $ctr" + ) +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:ERR_RETURN 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:ERR_RETURN in "if" + + fn() { + emulate -L zsh + setopt errreturn + if false; then + print No. + else + false + print No, no. + fi + } + fn +1:ERR_RETURN in "else" branch (regression test) + + $ZTST_testdir/../Src/zsh -f =(<<<" + if false; then + : + else + if [[ -n '' ]]; then + a=2 + fi + print Yes + fi + ") +0:ERR_RETURN when false "if" is the first statement in an "else" (regression) +>Yes +F:Must be tested with a top-level script rather than source or function + + fn() { + emulate -L zsh + setopt errreturn + print before + false + print after + } + fn +1:ERR_RETURN, basic case +>before + + fn() { + emulate -L zsh + setopt errreturn + print before + ! true + ! false + print after + } + fn +0:ERR_RETURN with "!" +>before +>after + + fn() { + emulate -L zsh + setopt errreturn + print before + ! true + ! false + false + print after + } + fn +1:ERR_RETURN with "!" and a following false +>before + + fn() { + emulate -L zsh + setopt errreturn + print before + ! if true; then + false + fi + print after + } + fn +0:ERR_RETURN with "!" suppressed inside complex structure +>before +>after + + fn() { + emulate -L zsh + setopt errreturn + print before + if true; then + false + fi + print after + } + fn +1:ERR_RETURN with no "!" suppression (control case) +>before + + (setopt err_return + fn() { + print before-in + false && false + } + print before-out + fn + print after-out + ) +1:ERR_RETURN with "&&" in function (regression test) +>before-out +>before-in + + (setopt err_return + fn() { + print before-in + false && false + print after-in + } + print before-out + fn + print after-out + ) +0:ERR_RETURN not triggered on LHS of "&&" in function +>before-out +>before-in +>after-in +>after-out + + (setopt err_return + fn() { + print before-in + true && false + print after-in + } + print before-out + fn + print after-out + ) +1:ERR_RETURN triggered on RHS of "&&" in function +>before-out +>before-in + + (setopt err_exit + for x in y; do + false && true + done + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of for +>OK + + (setopt err_exit + integer x=0 + while (( ! x++ )); do + false && true + done + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of while +>OK + + (setopt err_exit + repeat 1; do + false && true + done + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of repeat +>OK + + (setopt err_exit + if true; then + false && true + fi + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of if +>OK + + (setopt err_exit + { + false && true + } + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of { } +>OK + + (setopt err_exit + for x in y; do + false + done + print OK + ) +1:ERR_EXIT triggered by status 1 within for + + (setopt err_exit + integer x=0 + while (( ! x++ )); do + false + done + print OK + ) +1:ERR_EXIT triggered by status 1 within while + + (setopt err_exit + repeat 1; do + false + done + print OK + ) +1:ERR_EXIT triggered by status 1 within repeat + + (setopt err_exit + if true; then + false + fi + print OK + ) +1:ERR_EXIT triggered by status 1 within if + + (setopt err_exit + { + false + } + print OK + ) +1:ERR_EXIT triggered by status 1 within { } + + (setopt err_exit + () { + false && true + print Still functioning + false && true + } + print OK + ) +1:ERR_EXIT triggered by status 1 at end of anon func +>Still functioning + + if zmodload zsh/system 2>/dev/null; then + ( + trap 'echo TERM; exit 2' TERM + trap 'echo EXIT' EXIT + kill -s TERM "$sysparams[pid]" + echo 'FATAL: we should never get here!' 1>&2 + exit 1 + ) + else + ZTST_skip="zsh/system library not found." + fi +2:EXIT trap from TERM trap +>TERM +>EXIT + + # Should not get "hello" in the single quotes. + ( + trap "echo hello" EXIT; + { :; } | { read line; print "'$line'"; } + ) +0:EXIT trap not called in LHS of pipeline: Shell construct on LHS +>'' +>hello + + ( + trap "echo hello" EXIT; + cat '' +>hello + +%clean + + rm -f TRAPEXIT diff --git a/dotfiles/system/.zsh/modules/Test/C04funcdef.ztst b/dotfiles/system/.zsh/modules/Test/C04funcdef.ztst new file mode 100644 index 0000000..0cf2b58 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/C04funcdef.ztst @@ -0,0 +1,502 @@ +%prep + + mkdir funcdef.tmp + cd funcdef.tmp + setopt chaselinks + cd . + unsetopt chaselinks + mydir=$PWD + +%test + + fn1() { return 1; } + fn2() { + fn1 + print $? + return 2 + } + fn2 +2:Basic status returns from functions +>1 + + fnz() { } + false + fnz +0:Empty function body resets status + + fn3() { return 3; } + fnstat() { print $?; } + fn3 + fnstat +0:Status is not reset on non-empty function body +>3 + + function f$$ () { + print regress expansion of function names + } + f$$ +0:Regression test: 'function f$$ () { ... }' +>regress expansion of function names + + function foo () print bar + foo +0:Function definition without braces +>bar + + functions -M m1 + m1() { (( $# )) } + print $(( m1() )) + print $(( m1(1) )) + print $(( m1(1,2) )) +0:User-defined math functions, argument handling +>0 +>1 +>2 + + functions -M m2 + m2() { + integer sum + local val + for val in $*; do + (( sum += $val )) + done + } + print $(( m2(1) )) + print $(( m2(1,3+3,4**2) )) +0:User-defined math functions, complex argument handling +>1 +>23 + + functions -M m3 1 2 + m3() { (( 1 )) } + print zero + (print $(( m3() ))) + print one + print $(( m3(1) )) + print two + print $(( m3(1,2) )) + print three + (print $(( m3(1,2,3) ))) +1:User-defined math functions, argument checking +>zero +>one +>1 +>two +>1 +>three +?(eval):4: wrong number of arguments: m3() +?(eval):10: wrong number of arguments: m3(1,2,3) + + functions -M m4 0 0 testmathfunc + functions -M m5 0 0 testmathfunc + testmathfunc() { + if [[ $0 = m4 ]]; then + (( 4 )) + else + (( 5 )) + fi + } + print $(( m4() )) + print $(( m5() )) +0:User-defined math functions, multiple interfaces +>4 +>5 + + command_not_found_handler() { + print "Great News! I've handled the command:" + print "$1" + print "with arguments:" + print -l ${argv[2,-1]} + } + ACommandWhichHadBetterNotExistOnTheSystem and some "really useful" args +0:Command not found handler, success +>Great News! I've handled the command: +>ACommandWhichHadBetterNotExistOnTheSystem +>with arguments: +>and +>some +>really useful +>args + +# ' deconfuse emacs + + command_not_found_handler() { + print "Your command:" >&2 + print "$1" >&2 + print "has gone down the tubes. Sorry." >&2 + return 42 + } + ThisCommandDoesNotExistEither +42:Command not found handler, failure +?Your command: +?ThisCommandDoesNotExistEither +?has gone down the tubes. Sorry. + + local variable=outside + print "I am $variable" + function { + local variable=inside + print "I am $variable" + } + print "I am $variable" + () { + local variable="inside again" + print "I am $variable" + } + print "I am $variable" +0:Anonymous function scope +>I am outside +>I am inside +>I am outside +>I am inside again +>I am outside + + integer i + for (( i = 0; i < 10; i++ )); do function { + case $i in + ([13579]) + print $i is odd + ;| + ([2468]) + print $i is even + ;| + ([2357]) + print $i is prime + ;; + esac + }; done +0:Anonymous function with patterns in loop +>1 is odd +>2 is even +>2 is prime +>3 is odd +>3 is prime +>4 is even +>5 is odd +>5 is prime +>6 is even +>7 is odd +>7 is prime +>8 is even +>9 is odd + + echo stuff in file >file.in + function { + sed 's/stuff/rubbish/' + } file.out + cat file.out +0:Anonymous function redirection +>rubbish in file + + variable="Do be do" + print $variable + function { + print $variable + local variable="Da de da" + print $variable + function { + print $variable + local variable="Dum da dum" + print $variable + } + print $variable + } + print $variable +0:Nested anonymous functions +>Do be do +>Do be do +>Da de da +>Da de da +>Dum da dum +>Da de da +>Do be do + + () (cat $1 $2) <(print process expanded) =(print expanded to file) +0:Process substitution with anonymous functions +>process expanded +>expanded to file + + () { print This has arguments $*; } of all sorts; print After the function + function { print More stuff $*; } and why not; print Yet more +0:Anonymous function with arguments +>This has arguments of all sorts +>After the function +>More stuff and why not +>Yet more + + fn() { + (){ print Anonymous function 1 $*; } with args + function { print Anonymous function 2 $*; } with more args + print Following bit + } + functions fn +0:Text representation of anonymous function with arguments +>fn () { +> () { +> print Anonymous function 1 $* +> } with args +> () { +> print Anonymous function 2 $* +> } with more args +> print Following bit +>} + + touch yes no + () { echo $1 } (y|z)* + (echo here) + () { echo $* } some (y|z)* + () { echo empty };(echo here) +0:Anonymous function arguments and command arguments +>yes +>here +>some yes +>empty +>here + + if true; then f() { echo foo1; } else f() { echo bar1; } fi; f + if false; then f() { echo foo2; } else f() { echo bar2; } fi; f +0:Compatibility with other shells when not anonymous functions +>foo1 +>bar2 + + ( + setopt ignorebraces + fpath=(.) + print "{ echo OK }\n[[ -o ignorebraces ]] || print 'ignorebraces is off'" \ + >emufunctest + (autoload -z emufunctest; emufunctest) 2>&1 + emulate zsh -c 'autoload -Uz emufunctest' + emufunctest + [[ -o ignorebraces ]] && print 'ignorebraces is still on here' + ) +0:sticky emulation applies to autoloads and autoloaded function execution +>emufunctest:3: parse error near `\n' +>OK +>ignorebraces is off +>ignorebraces is still on here +#` (matching error message for editors parsing the file) + +# lsfoo should not be expanded as an anonymous function argument + alias lsfoo='This is not ls.' + () (echo anon func; echo "$@") lsfoo +0:Anonmous function with arguments in a form nobody sane would ever use but unfortunately we have to support anyway +>anon func +>lsfoo + + print foo | () cat +0:Simple anonymous function should not simplify enclosing pipeline +>foo + + alias fooalias=barexpansion + funcwithalias() { echo $(fooalias); } + functions funcwithalias + barexpansion() { print This is the correct output.; } + funcwithalias +0:Alias expanded in command substitution does not appear expanded in text +>funcwithalias () { +> echo $(fooalias) +>} +>This is the correct output. + + unfunction command_not_found_handler # amusing but unhelpful + alias first='firstfn1 firstfn2' second='secondfn1 secondfn2' + function first second { print This is function $0; } + first + second + firstfn1 + secondfn1 +127:No alias expansion after "function" keyword +>This is function first +>This is function second +?(eval):6: command not found: firstfn1 +?(eval):7: command not found: secondfn1 + + ( + fpath=(.) + print "print oops was successfully autoloaded" >oops + oops() { eval autoload -X } + oops + which -x2 oops + ) +0:autoload containing eval +>oops was successfully autoloaded +>oops () { +> print oops was successfully autoloaded +>} + + ( + fpath=(.) + printf '%s\n' 'oops(){}' 'ninjas-earring(){}' 'oops "$@"' >oops + autoload oops + oops + whence -v oops + ) +0q:whence -v of zsh-style autoload +>oops is a shell function from $mydir/oops + + ( + fpath=(.) + mkdir extra + print 'print "I have been loaded by explicit path."' >extra/spec + autoload -Uz $PWD/extra/spec + spec + ) +0:autoload with explicit path +>I have been loaded by explicit path. + + ( + fpath=(.) + print 'print "I have been loaded by default path."' >def + autoload -Uz $PWD/extra/def + def + ) +1:autoload with explicit path with function in normal path, no -d +?(eval):5: def: function definition file not found + + ( + fpath=(.) + autoload -dUz $PWD/extra/def + def + ) +0:autoload with explicit path with function in normal path, with -d +>I have been loaded by default path. + + ( + cd extra + fpath=(.) + autoload -r spec + cd .. + spec + ) +0:autoload -r +>I have been loaded by explicit path. + + ( + cd extra + fpath=(.) + autoload -r def + cd .. + def + ) +0:autoload -r is permissive +>I have been loaded by default path. + + ( + cd extra + fpath=(.) + autoload -R def + ) +1:autoload -R is not permissive +?(eval):4: def: function definition file not found + + ( + spec() { autoload -XUz $PWD/extra; } + spec + ) +0:autoload -X with path +>I have been loaded by explicit path. + +# The line number 1 here and in the next test seems suspect, +# but this example proves it's not down to the new features +# being tested here. + ( + fpath=(.) + cod() { autoload -XUz; } + cod + ) +1:autoload -X with no path, failure +?(eval):1: cod: function definition file not found + + ( + fpath=(.) + def() { autoload -XUz $PWD/extra; } + def + ) +1:autoload -X with wrong path and no -d +?(eval):1: def: function definition file not found + + ( + fpath=(.) + def() { autoload -dXUz $PWD/extra; } + def + ) +0:autoload -dX with path +>I have been loaded by default path. + + ( + fpath=(.) + print 'loadthisfunc() { autoload -X }' >loadthisfunc_sourceme + print 'print Function was loaded correctly.' >loadthisfunc + source $PWD/loadthisfunc_sourceme + loadthisfunc + ) +0: autoload -X interaction with absolute filename used for source location +>Function was loaded correctly. + + ( + fpath=() + mkdir extra2 + for f in fun2a fun2b; do + print "print $f" >extra2/$f + done + repeat 3; do + autoload $PWD/extra2/fun2{a,b} $PWD/extra/spec + fun2a + fun2b + spec + unfunction fun2a fun2b spec + autoload $PWD/extra2/fun2{a,b} $PWD/extra/spec + spec + fun2b + fun2a + unfunction fun2a fun2b spec + done + ) +0: Exercise the directory name cache for autoloads +>fun2a +>fun2b +>I have been loaded by explicit path. +>I have been loaded by explicit path. +>fun2b +>fun2a +>fun2a +>fun2b +>I have been loaded by explicit path. +>I have been loaded by explicit path. +>fun2b +>fun2a +>fun2a +>fun2b +>I have been loaded by explicit path. +>I have been loaded by explicit path. +>fun2b +>fun2a + + not_trashed() { print This function was not trashed; } + autoload -Uz /foo/bar/not_trashed + not_trashed +0:autoload with absolute path doesn't trash loaded function +>This function was not trashed + + # keep spec from getting loaded in parent shell for simplicity + ( + if whence spec; then print spec already loaded >&2; exit 1; fi + autoload -Uz $PWD/spec + autoload -Uz $PWD/extra/spec + spec + ) +0:autoload with absolute path can be overridden if not yet loaded +>I have been loaded by explicit path. + + ( + if whence spec; then print spec already loaded >&2; exit 1; fi + autoload -Uz $PWD/extra/spec + autoload spec + spec + ) +0:autoload with absolute path not cancelled by bare autoload +>I have been loaded by explicit path. + +%clean + + rm -f file.in file.out diff --git a/dotfiles/system/.zsh/modules/Test/C05debug.ztst b/dotfiles/system/.zsh/modules/Test/C05debug.ztst new file mode 100644 index 0000000..9a8df1d --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/C05debug.ztst @@ -0,0 +1,159 @@ +%prep + + setopt localtraps + +%test + + unsetopt DEBUG_BEFORE_CMD + debug-trap-bug1() { + setopt localtraps + print "print bug file here" >bug-file + print "print this is line one + print this is line two + print this is line three + print and this is line fifty-nine." >bug-file2 + function debug_trap_handler { + print $functrace[1] + do_bug + } + function do_bug { + . ./bug-file + } + trap 'echo EXIT hit' EXIT + trap 'debug_trap_handler' DEBUG + . ./bug-file2 + } + debug-trap-bug1 +0: Relationship between traps and sources +>debug-trap-bug1:15 +>bug file here +>this is line one +>./bug-file2:1 +>bug file here +>this is line two +>./bug-file2:2 +>bug file here +>this is line three +>./bug-file2:3 +>bug file here +>and this is line fifty-nine. +>./bug-file2:4 +>bug file here +>debug-trap-bug1:16 +>bug file here +>EXIT hit + + cat >zsh-trapreturn-bug2 <<-'HERE' + cmd='./fdasfsdafd' + [[ -x $cmd ]] && rm $cmd + set -o DEBUG_BEFORE_CMD + trap '[[ $? -ne 0 ]] && exit 0' DEBUG + $cmd # invalid command + # Failure + exit 10 + HERE + $ZTST_testdir/../Src/zsh -f ./zsh-trapreturn-bug2 2>erroutput.dif + mystat=$? + ( + setopt extendedglob + print ${"$(< erroutput.dif)"%%:[^:]#: ./fdasfsdafd} + ) + (( mystat == 0 )) +0: trapreturn handling bug is properly fixed +>./zsh-trapreturn-bug2:5 + + fn() { + setopt localtraps localoptions debugbeforecmd + trap '(( LINENO == 4 )) && setopt errexit' DEBUG + print $LINENO three + print $LINENO four + print $LINENO five + [[ -o errexit ]] && print "Hey, ERREXIT is set!" + } + fn +1:Skip line from DEBUG trap +>3 three +>5 five + + # Assignments are a special case, since they use a simpler + # wordcode type, so we need to test skipping them separately. + fn() { + setopt localtraps localoptions debugbeforecmd + trap '(( LINENO == 4 )) && setopt errexit' DEBUG + x=three + x=four + print $LINENO $x + [[ -o errexit ]] && print "Hey, ERREXIT is set!" + } + fn +1:Skip assignment from DEBUG trap +>5 three + + fn() { + setopt localtraps localoptions debugbeforecmd + trap 'print $LINENO' DEBUG + [[ a = a ]] && print a is ok + } + fn +0:line numbers of complex sublists +>3 +>a is ok + + fn() { + setopt localtraps localoptions debugbeforecmd + trap 'print $LINENO' DEBUG + print before + x=' first + second + third' + print $x + } + fn +0:line numbers of multiline assignments +>3 +>before +>4 +>7 +> first +> second +> third + + fn() { + emulate -L zsh; setopt debugbeforecmd + trap 'print "$LINENO: '\''$ZSH_DEBUG_CMD'\''"' DEBUG + print foo && + print bar || + print rod + x=y + print $x + fn2() { echo wow } + fn2 + } + fn +0:ZSH_DEBUG_CMD in debug traps +>3: 'print foo && print bar || print rod' +>foo +>bar +>6: 'x=y ' +>7: 'print $x' +>y +>8: 'fn2 () { +> echo wow +>}' +>9: 'fn2' +>0: 'echo wow' +>wow + + foo() { + emulate -L zsh; setopt debugbeforecmd + trap '[[ $ZSH_DEBUG_CMD == *bar* ]] && return 2' DEBUG + echo foo + echo bar + } + foo +2:Status of forced return from eval-style DEBUG trap +>foo + +%clean + + rm -f bug-file bug-file2 erroutput.dif zsh-trapreturn-bug2 diff --git a/dotfiles/system/.zsh/modules/Test/D01prompt.ztst b/dotfiles/system/.zsh/modules/Test/D01prompt.ztst new file mode 100644 index 0000000..607ffb6 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D01prompt.ztst @@ -0,0 +1,203 @@ +%prep + + mkdir prompt.tmp + cd prompt.tmp + mydir=$PWD + SHLVL=2 + setopt extendedglob + +%test + + hash -d mydir=$mydir + print -P ' %%%): %) + %%~: %~ + %%d: %d + %%1/: %1/ + %%h: %h + %%L: %L + %%M: %M + %%m: %m + %%n: %n + %%N: %N + %%i: %i + a%%{...%%}b: a%{%}b + ' +0q:Basic prompt escapes as shown. +> %): ) +> %~: ~mydir +> %d: $mydir +> %1/: ${mydir:t} +> %h: 0 +> %L: 2 +> %M: $HOST +> %m: ${HOST%%.*} +> %n: $USERNAME +> %N: (eval) +> %i: 2 +> a%{...%}b: ab +> + + true + print -P '%?' + false + print -P '%?' +0:`%?' prompt escape +>0 +>1 + + PS4="%_> " + setopt xtrace + if true; then true; else false; fi + unsetopt xtrace +0:`%_' prompt escape +?if> true +?then> true +?> unsetopt xtrace + + diff =(print -P '%#') =(print -P '%(!.#.%%)') +0:`%#' prompt escape and its equivalent + + psvar=(caesar adsum jam forte) + print -P '%v' '%4v' +0:`%v' prompt escape +>caesar forte + + true + print -P '%(?.true.false)' + false + print -P '%(?.true.false)' +0:ternary prompt escapes +>true +>false + + print -P 'start %10<......>truncated at 10%>> Not truncated%3> ...>Not shown' +0:prompt truncation +>start ...d at 10 Not truncated ... +>start truncat... Not truncated ... + +# It's hard to check the time and date as they are moving targets. +# We therefore just check that various forms of the date are consistent. +# In fact, if you perform this at midnight it can still fail. +# We could test for that, but we can't be bothered. +# I hope LC_ALL is enough to make the format what's expected. + + LC_ALL=C + date1=$(print -P %w) + date2=$(print -P %W) + date3=$(print -P %D) + if [[ $date1 != [A-Z][a-z][a-z][[:blank:]]##[0-9]## ]]; then + print "Date \`$date1' is not in the form \`Day DD' (e.g. \`Mon 1'" + fi + if [[ $date2 != [0-9][0-9]/[0-9][0-9]/[0-9][0-9] ]]; then + print "Date \`$date2' is not in the form \`DD/MM/YYYY'" + fi + if [[ $date3 != [0-9][0-9]-[0-9][0-9]-[0-9][0-9] ]]; then + print "Date \`$date3' is not in the form \`YY-MM-DD'" + fi + if (( $date1[5,-1] != $date2[4,5] )) || (( $date2[4,5] != $date3[7,8] )) + then + print "Days of month do not agree in $date1, $date2, $date3" + fi + if (( $date2[1,2] != $date3[4,5] )); then + print "Months do not agree in $date2, $date3" + fi + if (( $date2[7,8] != $date3[1,2] )); then + print "Years do not agree in $date2, $date3" + fi +0:Dates produced by prompt escapes + + mkdir foo + mkdir foo/bar + mkdir foo/bar/rod + (zsh_directory_name() { + emulate -L zsh + setopt extendedglob + local -a match mbegin mend + if [[ $1 = d ]]; then + if [[ $2 = (#b)(*bar)/rod ]]; then + reply=(barmy ${#match[1]}) + else + return 1 + fi + else + if [[ $2 = barmy ]]; then + reply=($mydir/foo/bar) + else + return 1 + fi + fi + } + # success + print ~[barmy]/anything + cd foo/bar/rod + print -P %~ + # failure + setopt nonomatch + print ~[scuzzy]/rubbish + cd ../.. + print -P %~ + # catastrophic failure + unsetopt nonomatch + print ~[scuzzy]/rubbish + ) +1q:Dynamic named directories +>$mydir/foo/bar/anything +>~[barmy]/rod +>~[scuzzy]/rubbish +>~mydir/foo +?(eval):33: no directory expansion: ~[scuzzy] + + ( + zsh_directory_name() { + emulate -L zsh + setopt extendedglob + local -a match mbegin mend + if [[ $1 = n ]]; then + if [[ $2 = *:l ]]; then + reply=(${2%%:l}/very_long_directory_name) + return 0 + else + return 1 + fi + else + if [[ $2 = (#b)(*)/very_long_directory_name ]]; then + reply=(${match[1]}:l ${#2}) + return 0 + else + return 1 + fi + fi + } + parent=$PWD + dir=$parent/very_long_directory_name + mkdir $dir + cd $dir + fn() { + PS4='+%N:%i> ' + setopt localoptions xtrace + # The following is the key to the test. + # It invokes zsh_directory_name which does PS4 output stuff + # while we're doing prompt handling for the parameter + # substitution. This checks recursion works OK. + local d=${(%):-%~} + print ${d//$parent/\} + } + fn 2>stderr + # post process error to remove variable contents + while read line; do + # tricky: reply is set to include directory length which is variable + [[ $line = *reply* ]] && continue + print ${line//$parent/\} + done &2 + ) +0:Recursive use of prompts +>~[:l] +?+zsh_directory_name:1> emulate -L zsh +?+zsh_directory_name:2> setopt extendedglob +?+zsh_directory_name:3> local -a match mbegin mend +?+zsh_directory_name:4> [[ d = n ]] +?+zsh_directory_name:12> [[ /very_long_directory_name = (#b)(*)/very_long_directory_name ]] +?+zsh_directory_name:14> return 0 +?+fn:7> local d='~[:l]' +?+fn:8> print '~[:l]' diff --git a/dotfiles/system/.zsh/modules/Test/D02glob.ztst b/dotfiles/system/.zsh/modules/Test/D02glob.ztst new file mode 100644 index 0000000..1385d57 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D02glob.ztst @@ -0,0 +1,688 @@ +# Tests for globbing + +%prep + mkdir glob.tmp + mkdir glob.tmp/dir{1,2,3,4} + mkdir glob.tmp/dir3/subdir + : >glob.tmp/{,{dir1,dir2}/}{a,b,c} + + globtest () { + $ZTST_testdir/../Src/zsh -f $ZTST_srcdir/../Misc/$1 + } + + regress_absolute_path_and_core_dump() { + local absolute_dir=$(cd glob.tmp && pwd -P) + [[ -n $absolute_dir ]] || return 1 + setopt localoptions extendedglob nullglob + print $absolute_dir/**/*~/* + setopt nonullglob nomatch + print glob.tmp/**/*~(.)# + } + +%test + + globtest globtests +0:zsh globbing +>0: [[ foo~ = foo~ ]] +>0: [[ foo~ = (foo~) ]] +>0: [[ foo~ = (foo~|) ]] +>0: [[ foo.c = *.c~boo* ]] +>1: [[ foo.c = *.c~boo*~foo* ]] +>0: [[ fofo = (fo#)# ]] +>0: [[ ffo = (fo#)# ]] +>0: [[ foooofo = (fo#)# ]] +>0: [[ foooofof = (fo#)# ]] +>0: [[ fooofoofofooo = (fo#)# ]] +>1: [[ foooofof = (fo##)# ]] +>1: [[ xfoooofof = (fo#)# ]] +>1: [[ foooofofx = (fo#)# ]] +>0: [[ ofxoofxo = ((ofo#x)#o)# ]] +>1: [[ ofooofoofofooo = (fo#)# ]] +>0: [[ foooxfooxfoxfooox = (fo#x)# ]] +>1: [[ foooxfooxofoxfooox = (fo#x)# ]] +>0: [[ foooxfooxfxfooox = (fo#x)# ]] +>0: [[ ofxoofxo = ((ofo#x)#o)# ]] +>0: [[ ofoooxoofxo = ((ofo#x)#o)# ]] +>0: [[ ofoooxoofxoofoooxoofxo = ((ofo#x)#o)# ]] +>0: [[ ofoooxoofxoofoooxoofxoo = ((ofo#x)#o)# ]] +>1: [[ ofoooxoofxoofoooxoofxofo = ((ofo#x)#o)# ]] +>0: [[ ofoooxoofxoofoooxoofxooofxofxo = ((ofo#x)#o)# ]] +>0: [[ aac = ((a))#a(c) ]] +>0: [[ ac = ((a))#a(c) ]] +>1: [[ c = ((a))#a(c) ]] +>0: [[ aaac = ((a))#a(c) ]] +>1: [[ baaac = ((a))#a(c) ]] +>0: [[ abcd = ?(a|b)c#d ]] +>0: [[ abcd = (ab|ab#)c#d ]] +>0: [[ acd = (ab|ab#)c#d ]] +>0: [[ abbcd = (ab|ab#)c#d ]] +>0: [[ effgz = (bc##d|ef#g?|(h|)i(j|k)) ]] +>0: [[ efgz = (bc##d|ef#g?|(h|)i(j|k)) ]] +>0: [[ egz = (bc##d|ef#g?|(h|)i(j|k)) ]] +>0: [[ egzefffgzbcdij = (bc##d|ef#g?|(h|)i(j|k))# ]] +>1: [[ egz = (bc##d|ef##g?|(h|)i(j|k)) ]] +>0: [[ ofoofo = (ofo##)# ]] +>0: [[ oxfoxoxfox = (oxf(ox)##)# ]] +>1: [[ oxfoxfox = (oxf(ox)##)# ]] +>0: [[ ofoofo = (ofo##|f)# ]] +>0: [[ foofoofo = (foo|f|fo)(f|ofo##)# ]] +>0: [[ oofooofo = (of|oofo##)# ]] +>0: [[ fffooofoooooffoofffooofff = (f#o#)# ]] +>1: [[ fffooofoooooffoofffooofffx = (f#o#)# ]] +>0: [[ fofoofoofofoo = (fo|foo)# ]] +>0: [[ foo = ((^x)) ]] +>0: [[ foo = ((^x)*) ]] +>1: [[ foo = ((^foo)) ]] +>0: [[ foo = ((^foo)*) ]] +>0: [[ foobar = ((^foo)) ]] +>0: [[ foobar = ((^foo)*) ]] +>1: [[ foot = z*~*x ]] +>0: [[ zoot = z*~*x ]] +>1: [[ foox = z*~*x ]] +>1: [[ zoox = z*~*x ]] +>0: [[ moo.cow = (*~*.*).(*~*.*) ]] +>1: [[ mad.moo.cow = (*~*.*).(*~*.*) ]] +>0: [[ moo.cow = (^*.*).(^*.*) ]] +>1: [[ sane.moo.cow = (^*.*).(^*.*) ]] +>1: [[ mucca.pazza = mu(^c#)?.pa(^z#)? ]] +>1: [[ _foo~ = _(|*[^~]) ]] +>0: [[ fff = ((^f)) ]] +>0: [[ fff = ((^f)#) ]] +>0: [[ fff = ((^f)##) ]] +>0: [[ ooo = ((^f)) ]] +>0: [[ ooo = ((^f)#) ]] +>0: [[ ooo = ((^f)##) ]] +>0: [[ foo = ((^f)) ]] +>0: [[ foo = ((^f)#) ]] +>0: [[ foo = ((^f)##) ]] +>1: [[ f = ((^f)) ]] +>1: [[ f = ((^f)#) ]] +>1: [[ f = ((^f)##) ]] +>0: [[ foot = (^z*|*x) ]] +>1: [[ zoot = (^z*|*x) ]] +>0: [[ foox = (^z*|*x) ]] +>0: [[ zoox = (^z*|*x) ]] +>0: [[ foo = (^foo)# ]] +>1: [[ foob = (^foo)b* ]] +>0: [[ foobb = (^foo)b* ]] +>1: [[ foob = (*~foo)b* ]] +>0: [[ foobb = (*~foo)b* ]] +>1: [[ zsh = ^z* ]] +>0: [[ a%1X = [[:alpha:][:punct:]]#[[:digit:]][^[:lower:]] ]] +>1: [[ a%1 = [[:alpha:][:punct:]]#[[:digit:]][^[:lower:]] ]] +>0: [[ [: = [[:]# ]] +>0: [[ :] = []:]# ]] +>0: [[ :] = [:]]# ]] +>0: [[ [ = [[] ]] +>0: [[ ] = []] ]] +>0: [[ [] = [^]]] ]] +>0: [[ fooxx = (#i)FOOXX ]] +>1: [[ fooxx = (#l)FOOXX ]] +>0: [[ FOOXX = (#l)fooxx ]] +>1: [[ fooxx = (#i)FOO(#I)X(#i)X ]] +>0: [[ fooXx = (#i)FOO(#I)X(#i)X ]] +>0: [[ fooxx = ((#i)FOOX)x ]] +>1: [[ fooxx = ((#i)FOOX)X ]] +>1: [[ BAR = (bar|(#i)foo) ]] +>0: [[ FOO = (bar|(#i)foo) ]] +>0: [[ Modules = (#i)*m* ]] +>0: [[ fooGRUD = (#i)(bar|(#I)foo|(#i)rod)grud ]] +>1: [[ FOOGRUD = (#i)(bar|(#I)foo|(#i)rod)grud ]] +>0: [[ readme = (#i)readme~README|readme ]] +>0: [[ readme = (#i)readme~README|readme~README ]] +>0: [[ 633 = <1-1000>33 ]] +>0: [[ 633 = <-1000>33 ]] +>0: [[ 633 = <1->33 ]] +>0: [[ 633 = <->33 ]] +>0: [[ 12345678901234567890123456789012345678901234567890123456789012345678901234567890foo = <42->foo ]] +>0: [[ READ.ME = (#ia1)readme ]] +>1: [[ READ..ME = (#ia1)readme ]] +>0: [[ README = (#ia1)readm ]] +>0: [[ READM = (#ia1)readme ]] +>0: [[ README = (#ia1)eadme ]] +>0: [[ EADME = (#ia1)readme ]] +>0: [[ READEM = (#ia1)readme ]] +>1: [[ ADME = (#ia1)readme ]] +>1: [[ README = (#ia1)read ]] +>0: [[ bob = (#a1)[b][b] ]] +>1: [[ bob = (#a1)[b][b]a ]] +>0: [[ bob = (#a1)[b]o[b]a ]] +>1: [[ bob = (#a1)[c]o[b] ]] +>0: [[ abcd = (#a2)XbcX ]] +>0: [[ abcd = (#a2)ad ]] +>0: [[ ad = (#a2)abcd ]] +>0: [[ abcd = (#a2)bd ]] +>0: [[ bd = (#a2)abcd ]] +>0: [[ badc = (#a2)abcd ]] +>0: [[ adbc = (#a2)abcd ]] +>1: [[ dcba = (#a2)abcd ]] +>0: [[ dcba = (#a3)abcd ]] +>0: [[ aabaXaaabY = (#a1)(a#b)#Y ]] +>0: [[ aabaXaaabY = (#a1)(a#b)(a#b)Y ]] +>0: [[ aaXaaaaabY = (#a1)(a#b)(a#b)Y ]] +>0: [[ aaaXaaabY = (#a1)(a##b)##Y ]] +>0: [[ aaaXbaabY = (#a1)(a##b)##Y ]] +>1: [[ read.me = (#ia1)README~READ.ME ]] +>0: [[ read.me = (#ia1)README~READ_ME ]] +>1: [[ read.me = (#ia1)README~(#a1)READ_ME ]] +>0: [[ test = *((#s)|/)test((#e)|/)* ]] +>0: [[ test/path = *((#s)|/)test((#e)|/)* ]] +>0: [[ path/test = *((#s)|/)test((#e)|/)* ]] +>0: [[ path/test/ohyes = *((#s)|/)test((#e)|/)* ]] +>1: [[ atest = *((#s)|/)test((#e)|/)* ]] +>1: [[ testy = *((#s)|/)test((#e)|/)* ]] +>1: [[ testy/path = *((#s)|/)test((#e)|/)* ]] +>1: [[ path/atest = *((#s)|/)test((#e)|/)* ]] +>1: [[ atest/path = *((#s)|/)test((#e)|/)* ]] +>1: [[ path/testy = *((#s)|/)test((#e)|/)* ]] +>1: [[ path/testy/ohyes = *((#s)|/)test((#e)|/)* ]] +>1: [[ path/atest/ohyes = *((#s)|/)test((#e)|/)* ]] +>0: [[ XabcdabcY = X(ab|c|d)(#c5)Y ]] +>0: [[ XabcdabcY = X(ab|c|d)(#c1,5)Y ]] +>0: [[ XabcdabcY = X(ab|c|d)(#c5,8)Y ]] +>0: [[ XabcdabcY = X(ab|c|d)(#c4,)Y ]] +>1: [[ XabcdabcY = X(ab|c|d)(#c6,)Y ]] +>1: [[ XabcdabcY = X(ab|c|d)(#c1,4)Y ]] +>0: [[ ZX = Z(|)(#c1)X ]] +>0: [[ froofroo = (fro(#c2))(#c2) ]] +>1: [[ froofroofroo = (fro(#c2))(#c2) ]] +>1: [[ froofro = (fro(#c2))(#c2) ]] +>0: [[ ax = ?(#c1,2)x ]] +>0: [[ ax = ?(#c1,)x ]] +>0: [[ ax = ?(#c0,1)x ]] +>1: [[ ax = ?(#c0,0)x ]] +>1: [[ ax = ?(#c2,)x ]] +>0: [[ aa = a(#c1,2)a ]] +>0: [[ aa = a(#c1,)a ]] +>0: [[ aa = a(#c0,1)a ]] +>1: [[ aa = a(#c0,0)a ]] +>1: [[ aa = a(#c2,)a ]] +>0: [[ test.zsh = *.?(#c1)sh ]] +>0: [[ test.bash = *.?(#c2)sh ]] +>0: [[ test.bash = *.?(#c1,2)sh ]] +>0: [[ test.bash = *.?(#c1,)sh ]] +>0: [[ test.zsh = *.?(#c1,)sh ]] +>0 tests failed. + + globtest globtests.ksh +0:ksh compatibility +>0: [[ fofo = *(f*(o)) ]] +>0: [[ ffo = *(f*(o)) ]] +>0: [[ foooofo = *(f*(o)) ]] +>0: [[ foooofof = *(f*(o)) ]] +>0: [[ fooofoofofooo = *(f*(o)) ]] +>1: [[ foooofof = *(f+(o)) ]] +>1: [[ xfoooofof = *(f*(o)) ]] +>1: [[ foooofofx = *(f*(o)) ]] +>0: [[ ofxoofxo = *(*(of*(o)x)o) ]] +>1: [[ ofooofoofofooo = *(f*(o)) ]] +>0: [[ foooxfooxfoxfooox = *(f*(o)x) ]] +>1: [[ foooxfooxofoxfooox = *(f*(o)x) ]] +>0: [[ foooxfooxfxfooox = *(f*(o)x) ]] +>0: [[ ofxoofxo = *(*(of*(o)x)o) ]] +>0: [[ ofoooxoofxo = *(*(of*(o)x)o) ]] +>0: [[ ofoooxoofxoofoooxoofxo = *(*(of*(o)x)o) ]] +>0: [[ ofoooxoofxoofoooxoofxoo = *(*(of*(o)x)o) ]] +>1: [[ ofoooxoofxoofoooxoofxofo = *(*(of*(o)x)o) ]] +>0: [[ ofoooxoofxoofoooxoofxooofxofxo = *(*(of*(o)x)o) ]] +>0: [[ aac = *(@(a))a@(c) ]] +>0: [[ ac = *(@(a))a@(c) ]] +>1: [[ c = *(@(a))a@(c) ]] +>0: [[ aaac = *(@(a))a@(c) ]] +>1: [[ baaac = *(@(a))a@(c) ]] +>0: [[ abcd = ?@(a|b)*@(c)d ]] +>0: [[ abcd = @(ab|a*@(b))*(c)d ]] +>0: [[ acd = @(ab|a*(b))*(c)d ]] +>0: [[ abbcd = @(ab|a*(b))*(c)d ]] +>0: [[ effgz = @(b+(c)d|e*(f)g?|?(h)i@(j|k)) ]] +>0: [[ efgz = @(b+(c)d|e*(f)g?|?(h)i@(j|k)) ]] +>0: [[ egz = @(b+(c)d|e*(f)g?|?(h)i@(j|k)) ]] +>0: [[ egzefffgzbcdij = *(b+(c)d|e*(f)g?|?(h)i@(j|k)) ]] +>1: [[ egz = @(b+(c)d|e+(f)g?|?(h)i@(j|k)) ]] +>0: [[ ofoofo = *(of+(o)) ]] +>0: [[ oxfoxoxfox = *(oxf+(ox)) ]] +>1: [[ oxfoxfox = *(oxf+(ox)) ]] +>0: [[ ofoofo = *(of+(o)|f) ]] +>0: [[ foofoofo = @(foo|f|fo)*(f|of+(o)) ]] +>0: [[ oofooofo = *(of|oof+(o)) ]] +>0: [[ fffooofoooooffoofffooofff = *(*(f)*(o)) ]] +>1: [[ fffooofoooooffoofffooofffx = *(*(f)*(o)) ]] +>0: [[ fofoofoofofoo = *(fo|foo) ]] +>0: [[ foo = !(x) ]] +>0: [[ foo = !(x)* ]] +>1: [[ foo = !(foo) ]] +>0: [[ foo = !(foo)* ]] +>0: [[ foobar = !(foo) ]] +>0: [[ foobar = !(foo)* ]] +>0: [[ moo.cow = !(*.*).!(*.*) ]] +>1: [[ mad.moo.cow = !(*.*).!(*.*) ]] +>1: [[ mucca.pazza = mu!(*(c))?.pa!(*(z))? ]] +>1: [[ _foo~ = _?(*[^~]) ]] +>0: [[ fff = !(f) ]] +>0: [[ fff = *(!(f)) ]] +>0: [[ fff = +(!(f)) ]] +>0: [[ ooo = !(f) ]] +>0: [[ ooo = *(!(f)) ]] +>0: [[ ooo = +(!(f)) ]] +>0: [[ foo = !(f) ]] +>0: [[ foo = *(!(f)) ]] +>0: [[ foo = +(!(f)) ]] +>1: [[ f = !(f) ]] +>1: [[ f = *(!(f)) ]] +>1: [[ f = +(!(f)) ]] +>0: [[ foot = @(!(z*)|*x) ]] +>1: [[ zoot = @(!(z*)|*x) ]] +>0: [[ foox = @(!(z*)|*x) ]] +>0: [[ zoox = @(!(z*)|*x) ]] +>0: [[ foo = *(!(foo)) ]] +>1: [[ foob = !(foo)b* ]] +>0: [[ foobb = !(foo)b* ]] +>0: [[ fooxx = (#i)FOOXX ]] +>1: [[ fooxx = (#l)FOOXX ]] +>0: [[ FOOXX = (#l)fooxx ]] +>1: [[ fooxx = (#i)FOO@(#I)X@(#i)X ]] +>0: [[ fooXx = (#i)FOO@(#I)X@(#i)X ]] +>0: [[ fooxx = @((#i)FOOX)x ]] +>1: [[ fooxx = @((#i)FOOX)X ]] +>1: [[ BAR = @(bar|(#i)foo) ]] +>0: [[ FOO = @(bar|(#i)foo) ]] +>0: [[ Modules = (#i)*m* ]] +>0 tests failed. + + (unsetopt multibyte + [[ bj๖rn = *[ๅไ๖ลฤึ]* ]]) +0:single byte match with top bit set + + ( regress_absolute_path_and_core_dump ) +0:exclusions regression test +> +>glob.tmp/a glob.tmp/b glob.tmp/c glob.tmp/dir1 glob.tmp/dir1/a glob.tmp/dir1/b glob.tmp/dir1/c glob.tmp/dir2 glob.tmp/dir2/a glob.tmp/dir2/b glob.tmp/dir2/c glob.tmp/dir3 glob.tmp/dir3/subdir glob.tmp/dir4 + + print glob.tmp/*(/) +0:Just directories +>glob.tmp/dir1 glob.tmp/dir2 glob.tmp/dir3 glob.tmp/dir4 + + print glob.tmp/*(.) +0:Just files +>glob.tmp/a glob.tmp/b glob.tmp/c + + print glob.tmp/*(.e^'reply=( glob.tmp/*/${REPLY:t} )'^:t) +0:Globbing used recursively (inside e glob qualifier) +>a a b b c c + + print glob.tmp/*/*(e:'reply=( glob.tmp/**/*([1]) )'::t) +0:Recursive globbing used recursively (inside e glob qualifier) +>a a a a a a a + + print glob.tmp/**/(:h) +0:Head modifier +>. glob.tmp glob.tmp glob.tmp glob.tmp glob.tmp/dir3 + + print glob.tmp(:r) +0:Remove extension modifier +>glob + + print glob.tmp/*(:s/./_/) +0:Substitute modifier +>glob_tmp/a glob_tmp/b glob_tmp/c glob_tmp/dir1 glob_tmp/dir2 glob_tmp/dir3 glob_tmp/dir4 + + print glob.tmp/*(F) +0:Just full dirs +>glob.tmp/dir1 glob.tmp/dir2 glob.tmp/dir3 + + print glob.tmp/*(^F) +0:Omit full dirs +>glob.tmp/a glob.tmp/b glob.tmp/c glob.tmp/dir4 + + print glob.tmp/*(/^F) +0:Just empty dirs +>glob.tmp/dir4 + + setopt extendedglob + print glob.tmp/**/*~*/dir3(/*|(#e))(/) +0:Exclusions with complicated path specifications +>glob.tmp/dir1 glob.tmp/dir2 glob.tmp/dir4 + + print -l -- glob.tmp/*(P:-f:) +0:Prepending words to each argument +>-f +>glob.tmp/a +>-f +>glob.tmp/b +>-f +>glob.tmp/c +>-f +>glob.tmp/dir1 +>-f +>glob.tmp/dir2 +>-f +>glob.tmp/dir3 +>-f +>glob.tmp/dir4 + + print -l -- glob.tmp/*(P:one word:P:another word:) +0:Prepending two words to each argument +>one word +>another word +>glob.tmp/a +>one word +>another word +>glob.tmp/b +>one word +>another word +>glob.tmp/c +>one word +>another word +>glob.tmp/dir1 +>one word +>another word +>glob.tmp/dir2 +>one word +>another word +>glob.tmp/dir3 +>one word +>another word +>glob.tmp/dir4 + + [[ "" = "" ]] && echo OK +0:Empty strings +>OK + + foo="this string has a : colon in it" + print ${foo%% #:*} +0:Must-match arguments in complex patterns +>this string has a + + mkdir glob.tmp/ra=1.0_et=3.5 + touch glob.tmp/ra=1.0_et=3.5/foo + print glob.tmp/ra=1.0_et=3.5/??? +0:Bug with intermediate paths with plain strings but tokenized characters +>glob.tmp/ra=1.0_et=3.5/foo + + doesmatch() { + setopt localoptions extendedglob + print -n $1 $2\ + if [[ $1 = $~2 ]]; then print yes; else print no; fi; + } + doesmatch MY_IDENTIFIER '[[:IDENT:]]##' + doesmatch YOUR:IDENTIFIER '[[:IDENT:]]##' + IFS=$'\n' doesmatch $'\n' '[[:IFS:]]' + IFS=' ' doesmatch $'\n' '[[:IFS:]]' + IFS=':' doesmatch : '[[:IFSSPACE:]]' + IFS=' ' doesmatch ' ' '[[:IFSSPACE:]]' + WORDCHARS="" doesmatch / '[[:WORD:]]' + WORDCHARS="/" doesmatch / '[[:WORD:]]' +0:Named character sets handled internally +>MY_IDENTIFIER [[:IDENT:]]## yes +>YOUR:IDENTIFIER [[:IDENT:]]## no +> +> [[:IFS:]] yes +> +> [[:IFS:]] no +>: [[:IFSSPACE:]] no +> [[:IFSSPACE:]] yes +>/ [[:WORD:]] no +>/ [[:WORD:]] yes + + [[ foo = (#c0)foo ]] +2:Misplaced (#c...) flag +?(eval):1: bad pattern: (#c0)foo + + mkdir glob.tmp/dir5 + touch glob.tmp/dir5/N123 + print glob.tmp/dir5/N<->(N) + rm -rf glob.tmp/dir5 +0:Numeric glob is not usurped by process substitution. +>glob.tmp/dir5/N123 + + tpd() { + [[ $1 = $~2 ]] + print -r "$1, $2: $?" + } + test_pattern_disables() { + emulate -L zsh + tpd 'forthcoming' 'f*g' + disable -p '*' + tpd 'forthcoming' 'f*g' + tpd 'f*g' 'f*g' + tpd '[frog]' '[frog]' + tpd '[frog]' '\[[f]rog\]' + disable -p '[' + tpd '[frog]' '[frog]' + tpd '[frog]' '\[[f]rog\]' + setopt extendedglob + tpd 'foo' '^bar' + disable -p '^' + tpd 'foo' '^bar' + tpd '^bar' '^bar' + tpd 'rumble' '(rumble|bluster)' + tpd '(thunder)' '(thunder)' + disable -p '(' + tpd 'rumble' '(rumble|bluster)' + tpd '(thunder)' '(thunder)' + setopt kshglob + tpd 'scramble' '@(panic|frenzy|scramble)' + tpd '@(scrimf)' '@(scrimf)' + disable -p '@(' + tpd 'scramble' '@(panic|frenzy|scramble)' + tpd '@(scrimf)' '@(scrimf)' + disable -p + } + test_pattern_disables + print Nothing should be disabled. + disable -p +0:disable -p +>forthcoming, f*g: 0 +>forthcoming, f*g: 1 +>f*g, f*g: 0 +>[frog], [frog]: 1 +>[frog], \[[f]rog\]: 0 +>[frog], [frog]: 0 +>[frog], \[[f]rog\]: 1 +>foo, ^bar: 0 +>foo, ^bar: 1 +>^bar, ^bar: 0 +>rumble, (rumble|bluster): 0 +>(thunder), (thunder): 1 +>rumble, (rumble|bluster): 1 +>(thunder), (thunder): 0 +>scramble, @(panic|frenzy|scramble): 0 +>@(scrimf), @(scrimf): 1 +>scramble, @(panic|frenzy|scramble): 1 +>@(scrimf), @(scrimf): 0 +>'(' '*' '[' '^' '@(' +>Nothing should be disabled. + + ( + setopt nomatch + x=( '' ) + print $^x(N) + ) +0:No error with empty null glob with (N). +> + + (setopt kshglob + test_array=( + '+fours' '+*' + '@titude' '@*' + '!bang' '!*' + # and check they work in the real kshglob cases too... + '+bus+bus' '+(+bus|-car)' + '@sinhats' '@(@sinhats|wrensinfens)' + '!kerror' '!(!somethingelse)' + # and these don't match, to be sure + '+more' '+(+less)' + '@all@all' '@(@all)' + '!goesitall' '!(!goesitall)' + ) + for str pat in $test_array; do + eval "[[ $str = $pat ]]" && print "$str matches $pat" + done + true + ) +0:kshglob option does not break +, @, ! without following open parenthesis +>+fours matches +* +>@titude matches @* +>!bang matches !* +>+bus+bus matches +(+bus|-car) +>@sinhats matches @(@sinhats|wrensinfens) +>!kerror matches !(!somethingelse) + + ( + setopt extendedglob + cd glob.tmp + [[ -n a*(#qN) ]] && print File beginning with a + [[ -z z*(#qN) ]] && print No file beginning with z + setopt nonomatch + [[ -n z*(#q) ]] && print Normal string if nullglob not set + ) +0:Force glob expansion in conditions using (#q) +>File beginning with a +>No file beginning with z +>Normal string if nullglob not set + + (){ print $#@ } glob.tmp/dir*(Y1) + (){ print $#@ } glob.tmp/file*(NY1) + (){ [[ "$*" == */dir?\ */dir? ]] && print Returns matching filenames } glob.tmp/dir*(Y2) + (){ print "Limit is upper bound:" ${(o)@:t} } glob.tmp/dir*(Y5) + (){ print "Negated:" $@:t } glob.tmp/dir*(Y1^Y) + (){ print "Sorting:" $@:t } glob.tmp/dir*(Y4On) + (){ [[ $#@ -eq 1 ]] && print Globs before last path component } glob.tmp/dir?/subdir(NY1) + (){ [[ $1 == glob.tmp/a ]] } glob.tmp/**/a(Y1) && print Breadth first + (){ [[ $#@ -eq 0 ]] && print Respects qualifiers } glob.tmp/dir*(NY1.) + (print -- *(Y)) 2>/dev/null || print "Argument required" +0:short-circuit modifier +>1 +>0 +>Returns matching filenames +>Limit is upper bound: dir1 dir2 dir3 dir4 +>Negated: dir1 dir2 dir3 dir4 +>Sorting: dir4 dir3 dir2 dir1 +>Globs before last path component +>Breadth first +>Respects qualifiers +>Argument required + + [[ "ce fichier n'existe pas" = (#b)ce\ (f[^ ]#)\ *s(#q./) ]] + print $match[1] +0:(#q) is ignored completely in conditional pattern matching +>fichier + +# The following should not cause excessive slowdown. + print glob.tmp/*.* + print glob.tmp/**************************.************************* +0:Optimisation to squeeze multiple *'s used as ordinary glob wildcards. +>glob.tmp/ra=1.0_et=3.5 +>glob.tmp/ra=1.0_et=3.5 + + [[ 1_2_ = (*_)(#c1) ]] && print 1 OK # because * matches 1_2 + [[ 1_2_ = (*_)(#c2) ]] && print 2 OK + [[ 1_2_ = (*_)(#c3) ]] || print 3 OK +0:Some more complicated backtracking with match counts. +>1 OK +>2 OK +>3 OK + + [[ foo = 'f'\o"o" ]] +0:Stripping of quotes from patterns (1) + + [[ foo = 'f'('o'|'a')('o'|'b') ]] +0:Stripping of quotes from patterns (2) + + [[ fob = 'f'('o'|'a')('o'|'b') ]] +0:Stripping of quotes from patterns (3) + + [[ fab = 'f'('o'|'a')('o'|'b') ]] +0:Stripping of quotes from patterns (4) + + [[ fib != 'f'('o'|'a')('o'|'b') ]] +0:Stripping of quotes from patterns (4) + + [[ - != [a-z] ]] +0:- is a special character in ranges + + [[ - = ['a-z'] ]] +0:- is not a special character in ranges if quoted + + [[ b-1 = [a-z]-[0-9] ]] +0:- untokenized following a bracketed subexpression + + [[ b-1 = []a-z]-[]0-9] ]] +0:- "]" after "[" is normal range character and - still works + + headremove="bcdef" + print ${headremove#[a-z]} +0:active - works in pattern in parameter +>cdef + + headremove="bcdef" + print ${headremove#['a-z']} + headremove="-cdef" + print ${headremove#['a-z']} +0:quoted - works in pattern in parameter +>bcdef +>cdef + + [[ a != [^a] ]] +0:^ active in character class if not quoted + + [[ a = ['^a'] ]] +0:^ not active in character class if quoted + + [[ a != [!a] ]] +0:! active in character class if not quoted + + [[ a = ['!a'] ]] +0:! not active in character class if quoted + + # Actually, we don't need the quoting here, + # c.f. the next test. This just makes it look + # more standard. + cset="^a-z" + [[ "^" = ["$cset"] ]] || print Fail 1 + [[ "a" = ["$cset"] ]] || print Fail 2 + [[ "-" = ["$cset"] ]] || print Fail 3 + [[ "z" = ["$cset"] ]] || print Fail 4 + [[ "1" != ["$cset"] ]] || print Fail 5 + [[ "b" != ["$cset"] ]] || print Fail 6 +0:character set specified as quoted variable + + cset="^a-z" + [[ "^" = [$~cset] ]] || print Fail 1 + [[ "a" != [$~cset] ]] || print Fail 2 + [[ "-" = [$~cset] ]] || print Fail 3 + [[ "z" != [$~cset] ]] || print Fail 4 + [[ "1" = [$~cset] ]] || print Fail 5 + [[ "b" != [$~cset] ]] || print Fail 6 +0:character set specified as active variable + + () { print -l -- $@:a } / /..{,/} /1 /nonexistent/..{,/} /deeper/nonexistent/..{,/} +0:modifier ':a' doesn't require existence +>/ +>/ +>/ +>/1 +>/ +>/ +>/deeper +>/deeper + + () { set -- ${PWD}/$^@; print -l -- $@:A } glob.tmp/nonexistent/foo/bar/baz +0:modifier ':A' doesn't require existence +*>*/glob.tmp/nonexistent/foo/bar/baz + + ln -s dir3/subdir glob.tmp/link + () { + print ${1:A} | grep glob.tmp + } glob.tmp/link/../../hello + rm glob.tmp/link +0:modifier ':A' resolves '..' components before symlinks +# There should be no output + + ln -s dir3/subdir glob.tmp/link + () { + print ${1:P} + } glob.tmp/link/../../hello/world + rm glob.tmp/link +0:modifier ':P' resolves symlinks before '..' components +*>*glob.tmp/hello/world diff --git a/dotfiles/system/.zsh/modules/Test/D03procsubst.ztst b/dotfiles/system/.zsh/modules/Test/D03procsubst.ztst new file mode 100644 index 0000000..ca8d56f --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D03procsubst.ztst @@ -0,0 +1,151 @@ +# Tests for process substitution: <(...), >(...) and =(...). + +%prep + if grep '#define PATH_DEV_FD' $ZTST_testdir/../config.h > /dev/null 2>&1 || + grep '#define HAVE_FIFOS' $ZTST_testdir/../config.h > /dev/null 2>&1; then + mkdir procsubst.tmp + cd procsubst.tmp + print 'First\tSecond\tThird\tFourth' >FILE1 + print 'Erste\tZweite\tDritte\tVierte' >FILE2 + else + ZTST_unimplemented="process substitution is not supported" + true + fi + + function copycat { cat "$@" } + +%test + paste <(cut -f1 FILE1) <(cut -f3 FILE2) +0:<(...) substitution +>First Dritte + +# slightly desperate hack to force >(...) to be synchronous + { paste <(cut -f2 FILE1) <(cut -f4 FILE2) } > >(sed 's/e/E/g' >OUTFILE) + cat OUTFILE +0:>(...) substitution +>SEcond ViErtE + + diff =(cat FILE1) =(cat FILE2) +1:=(...) substituion +>1c1 +>< First Second Third Fourth +>--- +>> Erste Zweite Dritte Vierte + + copycat <(print First) <(print Zweite) +0:FDs remain open for external commands called from functions +>First +>Zweite + + catfield2() { + local -a args + args=(${(s.,.)1}) + print $args[1] + cat $args[2] + print $args[3] + } + catfield2 up,<(print $'\x64'own),sideways +0:<(...) when embedded within an argument +>up +>down +>sideways + + outputfield2() { + local -a args + args=(${(s.,.)1}) + print $args[1] + echo 'How sweet the moonlight sits upon the bank' >$args[2] + print $args[3] + } + outputfield2 muddy,>(sed -e s/s/th/g >outputfield2.txt),vesture + # yuk + while [[ ! -e outputfield2.txt || ! -s outputfield2.txt ]]; do :; done + cat outputfield2.txt +0:>(...) when embedded within an argument +>muddy +>vesture +>How thweet the moonlight thitth upon the bank + + catfield1() { + local -a args + args=(${(s.,.)1}) + cat $args[1] + print $args[2] + } + catfield1 =(echo s$'\x69't),jessica +0:=(...) followed by something else without a break +>sit +>jessica + + ( + setopt nonomatch + # er... why is this treated as a glob? + print everything,=(here is left),alone + ) +0:=(...) preceded by other stuff has no special effect +>everything,=(here is left),alone + + print something=${:-=(echo 'C,D),(F,G)'} +1: Graceful handling of bad substitution in enclosed context +?(eval):1: unterminated `=(...)' +# '` + + () { + print -n "first: " + cat $1 + print -n "second: " + cat $2 + } =(echo This becomes argument one) =(echo and this argument two) + function { + print -n "third: " + cat $1 + print -n "fourth: " + cat $2 + } =(echo This becomes argument three) =(echo and this argument four) +0:Process environment of anonymous functions +>first: This becomes argument one +>second: and this argument two +>third: This becomes argument three +>fourth: and this argument four + + () { + # Make sure we don't close the file descriptor too early + eval 'print "Execute a complicated command first" | sed s/command/order/' + cat $1 + } <(echo This line was brought to you by the letters F and D) +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 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/dotfiles/system/.zsh/modules/Test/D04parameter.ztst b/dotfiles/system/.zsh/modules/Test/D04parameter.ztst new file mode 100644 index 0000000..9128c3c --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D04parameter.ztst @@ -0,0 +1,2058 @@ +# Test parameter expansion. Phew. +# (By the way, did I say "phew"?) + +%prep + + mkdir parameter.tmp + + cd parameter.tmp + + touch boringfile evenmoreboringfile + +%test + + foo='the first parameter' + bar='the second parameter' + print -l $foo ${bar} +0:Basic scalar parameter substitution +>the first parameter +>the second parameter + + array1=(the first array) + array2=(the second array) + print -l $array1 ${array2} +0:Basic array parameter substitution +>the +>first +>array +>the +>second +>array + + setopt ksharrays + print -l $array1 ${array2} + unsetopt ksharrays +0:Basic ksharray substitution +>the +>the + + setopt shwordsplit + print -l $foo ${bar} + print -l ${==bar} + unsetopt shwordsplit +0:Basic shwordsplit option handling +>the +>first +>parameter +>the +>second +>parameter +>the second parameter + + print $+foo ${+foo} $+notappearinginthistest ${+notappearinginthistest} +0:$+... +>1 1 0 0 + + x=() + print ${+x} ${+x[1]} ${+x[(r)foo]} ${+x[(r)bar]} + x=(foo) + print ${+x} ${+x[1]} ${+x[(r)foo]} ${+x[(r)bar]} +0:$+... with arrays +>1 0 0 0 +>1 1 1 0 + + set1=set1v + null1= + print ${set1:-set1d} ${set1-set2d} ${null1:-null1d} ${null1-null2d} x + print ${unset1:-unset1d} ${unset1-unset2d} x +0:${...:-...} and ${...-...} +>set1v set1v null1d x +>unset1d unset2d x + + set2=irrelevant + print ${set1:=set1d} ${set2::=set2d} + print $set2 + wasnull1= + wasnull2= + print ${wasnull1=wasnull1d} ${wasnull2:=wasnull2d} + print $wasnull1 $wasnull2 +0:${...:=...}, ${...::=...}, ${...=...} +>set1v set2d +>set2d +>wasnull2d +>wasnull2d + + unset array + print ${#${(A)=array=word}} +0:${#${(A)=array=word}} counts array elements +>1 + + (print ${set1:?okhere}; print ${unset1:?exiting1}; print not reached;) + (print ${null1?okhere}; print ${null1:?exiting2}; print not reached;) +1:${...:?...}, ${...?...} +>set1v +> +?(eval):1: unset1: exiting1 +?(eval):2: null1: exiting2 + + PROMPT="" $ZTST_testdir/../Src/zsh -fis <<<' + unsetopt PROMPT_SP + PS1="" PS2="" PS3="" PS4="" RPS1="" RPS2="" + exec 2>&1 + foo() { + print ${1:?no arguments given} + print not reached + } + foo + print reached + ' 2>/dev/null +0:interactive shell returns to top level on ${...?...} error +*>*foo:1: 1: no arguments given +>reached + + print ${set1:+word1} ${set1+word2} ${null1:+word3} ${null1+word4} + print ${unset1:+word5} ${unset1+word6} +0:${...:+...}, ${...+...} +>word1 word2 word4 +> + + str1='This is very boring indeed.' + print ${str1#*s} + print ${str1##*s} + print $str1##s +0:${...#...}, ${...##...} +> is very boring indeed. +> very boring indeed. +>This is very boring indeed.##s + + str2='If you'\''re reading this you should go and fix some bugs instead.' + print ${str2%d*} + print ${str2%%d*} +0:${...%...}, ${...%%...} +>If you're reading this you should go and fix some bugs instea +>If you're rea + + str1='does match' + str2='does not match' + print ${str1:#does * match} + print ${str2:#does * match} +0:${...:#...} +>does match +> + + array1=(arthur boldly claws dogs every fight) + print ${array1:#[aeiou]*} + print ${(M)array1:#[aeiou]*} +0:${...:#...}, ${(M)...:#...} with array +>boldly claws dogs fight +>arthur every + + str1="$array1" + print ${str1/[aeiou]*g/a braw bricht moonlicht nicht the nic} + print ${(S)str1/[aeiou]*g/relishe} +0:scalar ${.../.../...}, ${(S).../.../...} +>a braw bricht moonlicht nicht the nicht +>relishes every fight + + print ${array1/[aeiou]*/Y} + print ${(S)array1/[aeiou]*/Y} +0:array ${.../.../...}, ${(S).../.../...} +>Y bY clY dY Y fY +>Yrthur bYldly clYws dYgs Yvery fYght + + str1='o this is so, so so very dull' + print ${str1//o*/Please no} + print ${(S)str1//o*/Please no} +0:scalar ${...//.../...}, ${(S)...//.../...} +>Please no +>Please no this is sPlease no, sPlease no sPlease no very dull + + print ${array1//[aeiou]*/Y} + print ${(S)array1//[aeiou]*/Y} +0:array ${...//.../...}, ${(S)...//.../...} +>Y bY clY dY Y fY +>YrthYr bYldly clYws dYgs YvYry fYght + + print ${array1:/[aeiou]*/expletive deleted} +0:array ${...:/...} +>expletive deleted boldly claws dogs expletive deleted fight + + str1='a\string\with\backslashes' + str2='a/string/with/slashes' + print "${str1//\\/-}" + print ${str1//\\/-} + print "${str2//\//-}" + print ${str2//\//-} +0:use of backslashes in //-substitutions +>a-string-with-backslashes +>a-string-with-backslashes +>a-string-with-slashes +>a-string-with-slashes + + args=('one' '#foo' '(bar' "'three'" two) + mod=('#foo' '(bar' "'three'" sir_not_appearing_in_this_film) + print ${args:|mod} + print ${args:*mod} + print "${(@)args:|mod}" + print "${(@)args:*mod}" + args=(two words) + mod=('one word' 'two words') + print "${args:|mod}" + print "${args:*mod}" + scalar='two words' + print ${scalar:|mod} + print ${scalar:*mod} + print ${args:*nonexistent} + empty= + print ${args:*empty} +0:"|" array exclusion and "*" array intersection +>one two +>#foo (bar 'three' +>one two +>#foo (bar 'three' +> +>two words +> +>two words +> +> + + str1='twocubed' + array=(the number of protons in an oxygen nucleus) + print $#str1 ${#str1} "$#str1 ${#str1}" $#array ${#array} "$#array ${#array}" +0:${#...}, $#... +>8 8 8 8 8 8 8 8 + + set 1 2 3 4 5 6 7 8 9 + print ${##} + set 1 2 3 4 5 6 7 8 9 10 + print ${##} + print ${##""} + print ${##1} + print ${##2} + print ${###<->} # oh, for pete's sake... +0:${##} is length of $#, and other tales of hash horror +>1 +>2 +>10 +>0 +>10 +> + + array=(once bitten twice shy) + print IF${array}THEN + print IF${^array}THEN +0:basic ${^...} +>IFonce bitten twice shyTHEN +>IFonceTHEN IFbittenTHEN IFtwiceTHEN IFshyTHEN + + # Quote ${array} here because {...,...} doesn't like unquoted spaces. + print IF{"${array}",THEN}ELSE + print IF{${^array},THEN}ELSE +0:combined ${^...} and {...,...} +>IFonce bitten twice shyELSE IFTHENELSE +>IFonceELSE IFTHENELSE IFbittenELSE IFTHENELSE IFtwiceELSE IFTHENELSE IFshyELSE IFTHENELSE + + str1='one word' + print -l $str1 ${=str1} "split ${=str1}wise" +0:${=...} +>one word +>one +>word +>split one +>wordwise + + str1='*' + print $str1 ${~str1} $~str1 + setopt globsubst + print $str1 + unsetopt globsubst +0:${~...} and globsubst +>* boringfile evenmoreboringfile boringfile evenmoreboringfile +>boringfile evenmoreboringfile + +# The following tests a bug where globsubst didn't preserve +# backslashes when printing out the original string. + str1='\\*\\' + ( + setopt globsubst nonomatch + [[ \\\\ = $str1 ]] && print -r '\\ matched by' $str1 + [[ \\foo\\ = $str1 ]] && print -r '\\foo matched by' $str1 + [[ a\\b\\ = $str1 ]] || print -r 'a\\b not matched by' $str1 + ) +0:globsubst with backslashes +>\\ matched by \\*\\ +>\\foo matched by \\*\\ +>a\\b not matched by \\*\\ + + ( + setopt globsubst + foo="boring*" + print ${foo+$foo} + print ${foo+"$foo"} + print ${~foo+"$foo"} + ) +0:globsubst together with nested quoted expansion +>boringfile +>boring* +>boringfile + + print -l "${$(print one word)}" "${=$(print two words)}" +0:splitting of $(...) inside ${...} +>one word +>two +>words + + (setopt shwordsplit # ensure this doesn't get set in main shell... + test_splitting () + { + array="one two three" + for e in $array; do + echo "'$e'" + done + } + test_split_var= + echo _${test_split_var:=$(test_splitting)}_ + echo "_${test_split_var}_") +0:SH_WORD_SPLIT inside $(...) inside ${...} +>_'one' 'two' 'three'_ +>_'one' +>'two' +>'three'_ + + print -l "${(f)$(print first line\\nsecond line\\nthird line)}" +0:${(f)$(...)} +>first line +>second line +>third line + + array1=( uno ) + print -l ${(A)newarray=splitting by numbers} + print -l ${(t)newarray} + print -l ${(A)=newarray::=splitting by spaces, actually} + print -l ${(t)newarray} + print -l ${(A)newarray::=$array1} + print -l ${(t)newarray} + print -l ${newarray::=$array1} + print -l ${(t)newarray} + print -l ${newarray::=$array2} + print -l ${(t)newarray} +0:${(A)...=...}, ${(A)...::=...}, ${scalar=$array} +>splitting by numbers +>array +>splitting +>by +>spaces, +>actually +>array +>uno +>array +>uno +>scalar +>the second array +>scalar + + newarray=("split me" "split me" "I\'m yours") + print -l "${(@)newarray}" +0:"${(@)...}" +>split me +>split me +>I'm yours + + foo='$(print Howzat usay)' + print -l ${(e)foo} +0:${(e)...} +>Howzat +>usay + + foo='`print Howzat usay`' + print -l ${(e)foo} +0:Regress ${(e)...} with backticks (see zsh-workers/15871) +>Howzat +>usay + + foo='\u65\123' + print -r ${(g:o:)foo} + foo='\u65\0123^X\C-x' + print -r ${(g::)foo} + foo='^X' + bar='\C-\130' + [[ ${(g:c:)foo} == ${(g:oe:)bar} ]] + echo $? +0:${(g)...} +>eS +>eS^X\C-x +>0 + + foo='I'\''m nearly out of my mind with tedium' + bar=foo + print ${(P)bar} +0:${(P)...} +>I'm nearly out of my mind with tedium +#' deconfuse emacs + + foo=(I could be watching that programme I recorded) + print ${(o)foo} + print ${(oi)foo} + print ${(O)foo} + print ${(Oi)foo} +0:${(o)...}, ${(O)...} +>I I be could programme recorded that watching +>be could I I programme recorded that watching +>watching that recorded programme could be I I +>watching that recorded programme I I could be + + foo=(yOU KNOW, THE ONE WITH wILLIAM dALRYMPLE) + bar=(doing that tour of India.) + print ${(L)foo} + print ${(U)bar} +0:${(L)...}, ${(U)...} +>you know, the one with william dalrymple +>DOING THAT TOUR OF INDIA. + + foo='instead here I am stuck by the computer' + print ${(C)foo} +0:${(C)...} +>Instead Here I Am Stuck By The Computer + + foo=$'\x7f\x00' + print -r -- ${(V)foo} +0:${(V)...} +>^?^@ + + foo='playing '\''stupid'\'' "games" \w\i\t\h $quoting.' + print -r ${(q)foo} + print -r ${(qq)foo} + print -r ${(qqq)foo} + print -r ${(qqqq)foo} + print -r ${(q-)foo} +0:${(q...)...} +>playing\ \'stupid\'\ \"games\"\ \\w\\i\\t\\h\ \$quoting. +>'playing '\''stupid'\'' "games" \w\i\t\h $quoting.' +>"playing 'stupid' \"games\" \\w\\i\\t\\h \$quoting." +>$'playing \'stupid\' "games" \\w\\i\\t\\h $quoting.' +>'playing '\'stupid\'' "games" \w\i\t\h $quoting.' + + print -r ${(qqqq):-""} +0:workers/36551: literal empty string in ${(qqqq)...} +>$'' + + x=( a '' '\b' 'c d' '$e' ) + print -r ${(q)x} + print -r ${(q-)x} +0:Another ${(q...)...} test +>a '' \\b c\ d \$e +>a '' '\b' 'c d' '$e' + + print -r -- ${(q-):-foo} + print -r -- ${(q-):-foo bar} + print -r -- ${(q-):-"*(.)"} + print -r -- ${(q-):-"wow 'this is cool' or is it?"} + print -r -- ${(q-):-"no-it's-not"} +0:${(q-)...} minimal single quoting +>foo +>'foo bar' +>'*(.)' +>'wow '\''this is cool'\'' or is it?' +>no-it\'s-not + + foo="'and now' \"even the pubs\" \\a\\r\\e shut." + print -r ${(Q)foo} +0:${(Q)...} +>and now even the pubs are shut. + + foo="X$'\x41'$'\x42'Y" + print -r ${(Q)foo} +0:${(Q)...} with handling of $'...' +>XABY + + # The following may look a bit random. + # For the split we are checking that anything that + # would normally be followed by a different word has + # an argument break after it and anything that wouldn't doesn't. + # For the (Q) we are simply checking that nothing disappears + # in the parsing. + foo=' {six} (seven) >eight< }nine{ |forty-two| $many$ )ten( more' + array=(${(z)foo}) + print -l ${(Q)array} +0:${(z)...} and ${(Q)...} for some hard to parse cases +>< +>five +>> +>{six} +>( +>seven +>) +>> +>eight +>< +>}nine{ +>| +>forty-two +>| +>$many$ +>) +>ten( more + + strings=( + 'foo=(1 2 3)' + '(( 3 + 1 == 8 / 2 ))' + 'for (( i = 1 ; i < 10 ; i++ ))' + '((0.25542 * 60) - 15)*60' + 'repeat 3 (x)' + 'repeat 3 (echo foo; echo bar)' + 'repeat $(( 2 + 4 )) (x)' + 'repeat $( : foo bar; echo 4) (x)' + 'repeat "1"'\''2'\''$(( 3 + 0 ))$((echo 4);)\ 5 (x)' + ) + for string in $strings; do + array=(${(z)string}) + for (( i = 1; i <= ${#array}; i++ )); do + print -r -- "${i}:${array[i]}:" + done + done +0:Some syntactical expressions that are hard to split into words with (z). +>1:foo=(: +>2:1: +>3:2: +>4:3: +>5:): +>1:(( 3 + 1 == 8 / 2 )): +>1:for: +>2:((: +# Leading whitespace is removed, because the word proper hasn't started; +# trailing whitespace is left because the word is terminated by the +# semicolon or double parentheses. Bit confusing but sort of consistent. +>3:i = 1 ;: +>4:i < 10 ;: +>5:i++ : +>6:)): +# This one needs resolving between a math expression and +# a command, which causes interesting effects internally. +>1:(: +>2:(: +>3:0.25542: +>4:*: +>5:60: +>6:): +>7:-: +>8:15: +>9:): +>10:*60: +>1:repeat: +>2:3: +>3:(: +>4:x: +>5:): +>1:repeat: +>2:3: +>3:(: +>4:echo: +>5:foo: +>6:;: +>7:echo: +>8:bar: +>9:): +>1:repeat: +>2:$(( 2 + 4 )): +>3:(: +>4:x: +>5:): +>1:repeat: +>2:$( : foo bar; echo 4): +>3:(: +>4:x: +>5:): +>1:repeat: +>2:"1"'2'$(( 3 + 0 ))$((echo 4);)\ 5: +>3:(: +>4:x: +>5:): + + + line=$'A line with # someone\'s comment\nanother line # (1 more\nanother one' + print "*** Normal ***" + print -l ${(z)line} + print "*** Kept ***" + print -l ${(Z+c+)line} + print "*** Removed ***" + print -l ${(Z+C+)line} +0:Comments with (z) +>*** Normal *** +>A +>line +>with +># +>someone's comment +>another line # (1 more +>another one +>*** Kept *** +>A +>line +>with +># someone's comment +>; +>another +>line +># (1 more +>; +>another +>one +>*** Removed *** +>A +>line +>with +>; +>another +>line +>; +>another +>one + + line='with comment # at the end' + print -l ${(Z+C+)line} +0:Test we don't get an additional newline token +>with +>comment + + line=$'echo one\necho two # with a comment\necho three' + print -l ${(Z+nc+)line} +0:Treating zplit newlines as ordinary whitespace +>echo +>one +>echo +>two +># with a comment +>echo +>three + + print -rl - ${(z):-":;(( echo 42 "} +0:${(z)} with incomplete math expressions +>: +>; +>(( echo 42 + + # From parse error on it's not possible to split. + # Just check we get the complete string. + foo='echo $(|||) bar' + print -rl ${(z)foo} +0:$($(z)} with parse error in command substitution. +>echo +>$(|||) bar + + psvar=(dog) + setopt promptsubst + foo='It shouldn'\''t $(happen) to a %1v.' + bar='But `echo what can you do\?`' + print -r ${(%)foo} + print -r ${(%%)bar} +0:${(%)...} +>It shouldn't $(happen) to a dog. +>But what can you do? + + foo='unmatched "' + print ${(QX)foo} +1:${(QX)...} +?(eval):2: unmatched " +# " deconfuse emacs + + array=(characters in an array) + print ${(c)#array} +0:${(c)#...} +>22 + + print ${(w)#array} + str='colon::bolon::solon' + print ${(ws.:.)#str} + print ${(Ws.:.)#str} +0:${(w)...}, ${(W)...} +>4 +>3 +>5 + + typeset -A assoc + assoc=(key1 val1 key2 val2) + print ${(o)assoc} + print ${(ok)assoc} + print ${(ov)assoc} + print ${(okv)assoc} +0:${(k)...}, ${(v)...} +>val1 val2 +>key1 key2 +>val1 val2 +>key1 key2 val1 val2 + + word="obfuscatory" + print !${(l.16.)word}! +${(r.16.)word}+ +0:simple padding +>! obfuscatory! +obfuscatory + + + foo=(resulting words uproariously padded) + print ${(pl.10..\x22..X.)foo} +0:${(pl...)...} +>Xresulting """"Xwords roariously """Xpadded +#" deconfuse emacs + + print ${(l.5..X.r.5..Y.)foo} + print ${(l.6..X.r.4..Y.)foo} + print ${(l.7..X.r.3..Y.)foo} + print ${(l.6..X..A.r.6..Y..B.)foo} + print ${(l.6..X..AROOGA.r.6..Y..BARSOOM.)foo} +0:simultaneous left and right padding +>Xresulting XXXwordsYY proariousl XXpaddedYY +>XXresultin XXXXwordsY uproarious XXXpaddedY +>XXXresulti XXXXXwords Xuproariou XXXXpadded +>XAresultingB XXXAwordsBYY uproariously XXApaddedBYY +>GAresultingB OOGAwordsBAR uproariously OGApaddedBAR + + foo=(why in goodness name am I doing this) + print ${(r.5..!..?.)foo} +0:${(r...)...} +>why?! in?!! goodn name? am?!! I?!!! doing this? + + array=(I\'m simply putting a brave face on) + print ${(j:--:)array} +0:${(j)...} +>I'm--simply--putting--a--brave--face--on + + print ${(F)array} +0:${(F)...} +>I'm +>simply +>putting +>a +>brave +>face +>on + + string='zometimez zis getz zplit on a z' + print -l ${(s?z?)string} +0:${(s...)...} +>ometime +> +>is get +> +>plit on a + + str=s + arr=(a) + typeset -A ass + ass=(a a) + integer i + float f + print ${(t)str} ${(t)arr} ${(t)ass} ${(t)i} ${(t)f} +0:${(t)...} +>scalar array association-local integer-local float-local + + # it's not quite clear that these are actually right unless you know + # the algorithm: search along the string for the point at which the + # first (last) match occurs, for ## (%%), then take the shortest possible + # version of that for # (%). it's as good a definition as anything. + string='where is the white windmill, whispered walter wisely' + print ${(S)string#h*e} + print ${(S)string##h*e} + print ${(S)string%h*e} + print ${(S)string%%h*e} +0:${(S)...#...} etc. +>wre is the white windmill, whispered walter wisely +>wly +>where is the white windmill, wred walter wisely +>where is the white windmill, wly + + setopt extendedglob + print ${(SI:1:)string##w[^[:space:]]# } + print ${(SI:1+1:)string##w[^[:space:]]# } + print ${(SI:1+1+1:)string##w[^[:space:]]# } + print ${(SI:1+1+1+1:)string##w[^[:space:]]# } +0:${(I:...:)...} +>is the white windmill, whispered walter wisely +>where is the windmill, whispered walter wisely +>where is the white whispered walter wisely +>where is the white windmill, walter wisely + + print ${(MSI:1:)string##w[^[:space:]]# } +0:${(M...)...} +>where + + print ${(R)string//w[a-z]# #} +0:${(R)...} +>is the , + + # This (1) doesn't work with // or / + # (2) perhaps ought to be 18, to be consistent with normal zsh + # substring indexing and with backreferences. + print ${(BES)string##white} +0:${(BE...)...} +>14 19 + + print ${(NS)string##white} +0:${(N)...} +>5 + + string='abcdefghijklmnopqrstuvwxyz' + print ${${string%[aeiou]*}/(#m)?(#e)/${(U)MATCH}} +0:Rule 1: Nested substitutions +>abcdefghijklmnopqrsT + + array=(et Swann avec cette muflerie intermittente) + string="qui reparaissait chez lui" + print ${array[4,5]} + print ${array[4,5][1]} + print ${array[4,5][1][2,3]} + print ${string[4,5]} + print ${string[4,5][1]} +0:Rule 2: Parameter subscripting +>cette muflerie +>cette +>et +> r +> + + foo=stringalongamax + print ${${(P)foo[1,6]}[1,3]} +0:Rule 3: Parameter Name Replacement +>qui + + print "${array[5,6]}" + print "${(j.:.)array[1,2]}" +0:Rule 4: Double-Quoted Joining +>muflerie intermittente +>et:Swann + + print "${${array}[5,7]}" + print "${${(@)array}[1,2]}" +0:Rule 5: Nested Subscripting +>wan +>et Swann + + print "${${(@)array}[1,2]#?}" + print "${(@)${(@)array}[1,2]#?}" +0:Rule 6: Modifiers +>t Swann +>t wann + + array=(she sells z shells by the z shore) + (IFS='+'; print ${(s.s.)array}) +0:Rule 7: Forced Joining, and 8: Forced splitting +>he+ ell +z+ hell +by+the+z+ hore + + setopt shwordsplit + string='another poxy boring string' + print -l ${${string}/o/ } + unsetopt shwordsplit +0:Rule 9: Shell Word Splitting +>an +>ther +>p +>xy +>b +>ring +>string + + setopt nonomatch + foo='b* e*' + print ${(e)~foo} + print ${(e)~=foo} + setopt nomatch +0:Rule 10: Re-Evaluation +>b* e* +>boringfile evenmoreboringfile + + # ${bar} -> $bar here would yield "bad substitution". + bar=confinement + print ${(el.20..X.)${bar}} +0:Rule 11: Padding +>XXXXXXXXXconfinement + + foo=(bar baz) + bar=(ax1 bx1) + print "${(@)${foo}[1]}" + print "${${(@)foo}[1]}" + print -l ${(s/x/)bar} + print -l ${(j/x/s/x/)bar} + print -l ${(s/x/)bar%%1*} +0:Examples in manual on parameter expansion +>b +>bar +>a +>1 b +>1 +>a +>1 +>b +>1 +>a +> b + + set If "this test fails" "we have broken" the shell again + print -l ${1+"$@"} +0:Regression test of ${1+"$@"} bug +>If +>this test fails +>we have broken +>the +>shell +>again + + set If "this test fails" "we have broken" the shell again + print -l "${(A)foo::=$@}" + print -l ${(t)foo} + print -l $foo +0:Regression test of "${(A)foo=$@}" bug +>If this test fails we have broken the shell again +>array +>If +>this test fails +>we have broken +>the +>shell +>again + + local sure_that='sure that' varieties_of='varieties of' one=1 two=2 + extra=(5 4 3) + unset foo + set Make $sure_that "this test keeps" on 'preserving all' "$varieties_of" quoted whitespace + print -l ${=1+"$@"} + print -l ${(A)=foo=Make $sure_that "this test keeps" on 'preserving all' "$varieties_of" quoted whitespace} + print ${(t)foo} + print -l ${=1+$one $two} + print -l ${1+$extra$two$one} +0:Regression test of ${=1+"$@"} bug and some related expansions +>Make +>sure that +>this test keeps +>on +>preserving all +>varieties of +>quoted +>whitespace +>Make +>sure +>that +>this test keeps +>on +>preserving all +>varieties of +>quoted +>whitespace +>array +>1 +>2 +>5 +>4 +>321 + + splitfn() { + emulate -L sh + local HOME="/differs from/bash" foo='1 2' bar='3 4' + print -l ${1:-~} + touch has\ space + print -l ${1:-*[ ]*} + print -l ${1:-*[\ ]*} + print -l ${1:-*} + print -l ${1:-"$foo" $bar} + print -l ${==1:-$foo $bar} + rm has\ space + } + splitfn +0:More bourne-shell-compatible nested word-splitting with wildcards and ~ +>/differs from/bash +>*[ +>]* +>has space +>boringfile +>evenmoreboringfile +>has space +>1 2 +>3 +>4 +>1 2 3 4 + + splitfn() { + local IFS=.- + local foo=1-2.3-4 + # + print "Called with argument '$1'" + print "No quotes" + print -l ${=1:-1-2.3-4} ${=1:-$foo} + print "With quotes on default argument only" + print -l ${=1:-"1-2.3-4"} ${=1:-"$foo"} + } + print 'Using "="' + splitfn + splitfn 5.6-7.8 + # + splitfn() { + emulate -L zsh + setopt shwordsplit + local IFS=.- + local foo=1-2.3-4 + # + print "Called with argument '$1'" + print "No quotes" + print -l ${1:-1-2.3-4} ${1:-$foo} + print "With quotes on default argument only" + print -l ${1:-"1-2.3-4"} ${1:-"$foo"} + } + print Using shwordsplit + splitfn + splitfn 5.6-7.8 +0:Test of nested word splitting with and without quotes +>Using "=" +>Called with argument '' +>No quotes +>1 +>2 +>3 +>4 +>1 +>2 +>3 +>4 +>With quotes on default argument only +>1-2.3-4 +>1-2.3-4 +>Called with argument '5.6-7.8' +>No quotes +>5 +>6 +>7 +>8 +>5 +>6 +>7 +>8 +>With quotes on default argument only +>5 +>6 +>7 +>8 +>5 +>6 +>7 +>8 +>Using shwordsplit +>Called with argument '' +>No quotes +>1 +>2 +>3 +>4 +>1 +>2 +>3 +>4 +>With quotes on default argument only +>1-2.3-4 +>1-2.3-4 +>Called with argument '5.6-7.8' +>No quotes +>5 +>6 +>7 +>8 +>5 +>6 +>7 +>8 +>With quotes on default argument only +>5 +>6 +>7 +>8 +>5 +>6 +>7 +>8 + +# Tests a long-standing bug with joining on metafied characters in IFS + (array=(one two three) + IFS=$'\0' + foo="$array" + for (( i = 1; i <= ${#foo}; i++ )); do + char=${foo[i]} + print $(( #char )) + done) +0:Joining with NULL character from IFS +>111 +>110 +>101 +>0 +>116 +>119 +>111 +>0 +>116 +>104 +>114 +>101 +>101 + + unset SHLVL + (( SHLVL++ )) + print $SHLVL +0:Unsetting and recreation of numerical special parameters +>1 + + unset manpath + print $+MANPATH + manpath=(/here /there) + print $MANPATH + unset MANPATH + print $+manpath + MANPATH=/elsewhere:/somewhere + print $manpath +0:Unsetting and recreation of tied special parameters +>0 +>/here:/there +>0 +>/elsewhere /somewhere + + local STRING=a:b + typeset -T STRING string + print $STRING $string + unset STRING + set -A string x y z + print $STRING $string + STRING=a:b + typeset -T STRING string + print $STRING $string + unset STRING + set -A string x y z + print $STRING $string + STRING=a:b + typeset -T STRING string + print $STRING $string + unset string + STRING=x:y:z + print $STRING $string + STRING=a:b + typeset -T STRING string + print $STRING $string + unset string + STRING=x:y:z + print $STRING $string +0:Unsetting and recreation of tied normal parameters +>a:b a b +>x y z +>a:b a b +>x y z +>a:b a b +>x:y:z +>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]] + print $#match $#mbegin $#mend + else + print That didn\'t work. + fi +0:Parameters associated with backreferences +>match 12 16 match +>1 1 1 +#' deconfuse emacs + + string='and look for a MATCH in here' + if [[ ${(S)string%%(#m)M*H} = "and look for a in here" ]]; then + print $MATCH $MBEGIN $MEND $string[$MBEGIN,$MEND] + print $#MATCH + else + print Oh, dear. Back to the drawing board. + fi +0:Parameters associated with (#m) flag +>MATCH 16 20 MATCH +>5 + + string='this is a string' + print ${string//(#m)s/$MATCH $MBEGIN $MEND} +0:(#m) flag with pure string +>this 4 4 is 7 7 a s 11 11tring + + print ${${~:-*}//(#m)*/$MATCH=$MATCH} +0:(#m) flag with tokenized input +>*=* + + print -l JAMES${(u)${=:-$(echo yes yes)}}JOYCE + print -l JAMES${(u)${=:-$(echo yes yes she said yes i will yes)}}JOYCE +0:Bug with (u) flag reducing arrays to one element +>JAMESyesJOYCE +>JAMESyes +>she +>said +>i +>willJOYCE + + print -l JAMES${(u)${=:-$(echo yes yes she said yes i will yes she said she will and yes she did yes)}}JOYCE +0:New hash seive unique algorithm for arrays of more than 10 elements +>JAMESyes +>she +>said +>i +>will +>and +>didJOYCE + + foo= + print "${${foo}/?*/replacement}" +0:Quoted zero-length strings are handled properly +> + + file=aleftkept + print ${file//(#b)(*)left/${match/a/andsome}} + print ${file//(#b)(*)left/${match//a/andsome}} +0:Substitutions where $match is itself substituted in the replacement +>andsomekept +>andsomekept + + file=/one/two/three/four + print ${file:fh} + print ${file:F.1.h} + print ${file:F+2+h} + print ${file:F(3)h} + print ${file:F<4>h} + print ${file:F{5}h} +0:Modifiers with repetition +>/ +>/one/two/three +>/one/two +>/one +>/ +>/ + + baz=foo/bar + zab=oof+rab + print ${baz:s/\//+/} + print "${baz:s/\//+/}" + print ${zab:s/+/\//} + print "${zab:s/+/\//}" +0:Quoting of separator in substitution modifier +>foo+bar +>foo+bar +>oof/rab +>oof/rab + + bsbs='X\\\\Y' + print -r -- ${bsbs:s/\\/\\/} + print -r -- "${bsbs:s/\\/\\/}" + print -r -- ${bsbs:s/\\\\/\\\\/} + print -r -- "${bsbs:s/\\\\/\\\\/}" + print -r -- ${bsbs:gs/\\/\\/} + print -r -- "${bsbs:gs/\\/\\/}" + print -r -- ${bsbs:gs/\\\\/\\\\/} + print -r -- "${bsbs:gs/\\\\/\\\\/}" +0:Handling of backslashed backslashes in substitution modifier +>X\\\\Y +>X\\\\Y +>X\\\\Y +>X\\\\Y +>X\\\\Y +>X\\\\Y +>X\\\\Y +>X\\\\Y + + print -r ${${:-one/two}:s,/,X&Y,} + print -r ${${:-one/two}:s,/,X\&Y,} + print -r ${${:-one/two}:s,/,X\\&Y,} + print -r "${${:-one/two}:s,/,X&Y,}" + print -r "${${:-one/two}:s,/,X\&Y,}" + print -r "${${:-one/two}:s,/,X\\&Y,}" +0:Quoting of ampersand in substitution modifier RHS +>oneX/Ytwo +>oneX&Ytwo +>oneX\/Ytwo +>oneX/Ytwo +>oneX&Ytwo +>oneX\/Ytwo + + nully=($'a\0c' $'a\0b\0b' $'a\0b\0a' $'a\0b\0' $'a\0b' $'a\0' $'a') + for string in ${(o)nully}; do + for (( i = 1; i <= ${#string}; i++ )); do + foo=$string[i] + printf "%02x" $(( #foo )) + done + print + done +0:Sorting arrays with embedded nulls +>61 +>6100 +>610062 +>61006200 +>6100620061 +>6100620062 +>610063 + + array=(X) + patterns=("*X*" "spong" "a[b") + for pat in $patterns; do + print A${array[(r)$pat]}B C${array[(I)$pat]}D + done +0:Bad patterns should never match array elements +>AXB C1D +>AB C0D +>AB C0D + + foo=(a6 a117 a17 b6 b117 b17) + print ${(n)foo} + print ${(On)foo} +0:Numeric sorting +>a6 a17 a117 b6 b17 b117 +>b117 b17 b6 a117 a17 a6 + + x=sprodj + x[-10]=scrumf + print $x +0:Out of range negative scalar subscripts +>scrumfsprodj + + a=(some sunny day) + a[-10]=(we\'ll meet again) + print -l $a +0:Out of range negative array subscripts +>we'll +>meet +>again +>some +>sunny +>day + +# ' emacs likes this close quote + + a=(sping spang spong bumble) + print ${a[(i)spong]} + print ${a[(i)spung]} + print ${a[(ib.1.)spong]} + print ${a[(ib.4.)spong]} + print ${a[(ib.10.)spong]} +0:In and out of range reverse matched indices without and with b: arrays +>3 +>5 +>3 +>5 +>5 + + a="thrimblewuddlefrong" + print ${a[(i)w]} + print ${a[(i)x]} + print ${a[(ib.3.)w]} + print ${a[(ib.10.)w]} + print ${a[(ib.30.)w]} +0:In and out of range reverse matched indices without and with b: strings +>9 +>20 +>9 +>20 +>20 + + foo="line:with::missing::fields:in:it" + print -l ${(s.:.)foo} +0:Removal of empty fields in unquoted splitting +>line +>with +>missing +>fields +>in +>it + + foo="line:with::missing::fields:in:it" + print -l "${(s.:.)foo}" +0:Hacky removal of empty fields in quoted splitting with no "@" +>line +>with +>missing +>fields +>in +>it + + foo="line:with::missing::fields:in:it:" + print -l "${(@s.:.)foo}" +0:Retention of empty fields in quoted splitting with "@" +>line +>with +> +>missing +> +>fields +>in +>it +> + + str=abcd + print -l ${(s..)str} + print -l "${(s..)str}" +0:splitting of strings into characters +>a +>b +>c +>d +>a +>b +>c +>d + + array=('%' '$' 'j' '*' '$foo') + print ${array[(i)*]} "${array[(i)*]}" + print ${array[(ie)*]} "${array[(ie)*]}" + key='$foo' + print ${array[(ie)$key]} "${array[(ie)$key]}" + key='*' + print ${array[(ie)$key]} "${array[(ie)$key]}" +0:Matching array indices with and without quoting +>1 1 +>4 4 +>5 5 +>4 4 + +# Ordering of associative arrays is arbitrary, so we need to use +# patterns that only match one element. + typeset -A assoc_r + assoc_r=(star '*' of '*this*' and '!that!' or '(the|other)') + print ${(kv)assoc_r[(re)*]} + print ${(kv)assoc_r[(re)*this*]} + print ${(kv)assoc_r[(re)!that!]} + print ${(kv)assoc_r[(re)(the|other)]} + print ${(kv)assoc_r[(r)*at*]} + print ${(kv)assoc_r[(r)*(ywis|bliss|kiss|miss|this)*]} + print ${(kv)assoc_r[(r)(this|that|\(the\|other\))]} +0:Reverse subscripting associative arrays with literal matching +>star * +>of *this* +>and !that! +>or (the|other) +>and !that! +>of *this* +>or (the|other) + + print $ZSH_SUBSHELL + (print $ZSH_SUBSHELL) + ( (print $ZSH_SUBSHELL) ) + ( (print $ZSH_SUBSHELL); print $ZSH_SUBSHELL ) + print $(print $ZSH_SUBSHELL) + cat =(print $ZSH_SUBSHELL) +0:ZSH_SUBSHELL +>0 +>1 +>2 +>2 +>1 +>1 +>1 + + foo=("|" "?") + [[ "|" = ${(j.|.)foo} ]] && print yes || print no + [[ "|" = ${(j.|.)~foo} ]] && print yes || print no + [[ "|" = ${(~j.|.)foo} ]] && print yes || print no + [[ "|" = ${(~~j.|.)foo} ]] && print yes || print no + [[ "|" = ${(j.|.~)foo} ]] && print yes || print no + [[ "x" = ${(j.|.)foo} ]] && print yes || print no + [[ "x" = ${(j.|.)~foo} ]] && print yes || print no + [[ "x" = ${(~j.|.)foo} ]] && print yes || print no + [[ "x" = ${(~~j.|.)foo} ]] && print yes || print no + [[ "x" = ${(j.|.~)foo} ]] && print yes || print no +0:GLOBSUBST only on parameter substitution arguments +>no +>yes +>yes +>no +>no +>no +>yes +>no +>no +>no + + rcexbug() { + emulate -L zsh + setopt rcexpandparam + local -A hash + local -a full empty + full=(X x) + hash=(X x) + print ORDINARY ARRAYS + : The following behaves as documented in zshoptions + print FULL expand=$full + : Empty arrays remove the adjacent argument + print EMPTY expand=$empty + print ASSOCIATIVE ARRAY + print Subscript flags returning many values + print FOUND key=$hash[(I)X] val=$hash[(R)x] + : This should behave like $empty, and does + print LOST key=$hash[(I)y] val=$hash[(R)Y] + print Subscript flags returning single values + : Doc says "substitutes ... empty string" + : so must not behave like an empty array + print STRING key=$hash[(i)y] val=$hash[(r)Y] + } + rcexbug +0:Lookup failures on elements of arrays with RC_EXPAND_PARAM +>ORDINARY ARRAYS +>FULL expand=X expand=x +>EMPTY +>ASSOCIATIVE ARRAY +>Subscript flags returning many values +>FOUND key=X val=x +>LOST +>Subscript flags returning single values +>STRING key= val= + + print $zsh_eval_context[1] + [[ $ZSH_EVAL_CONTEXT = ${(j.:.)zsh_eval_context} ]] || print Not equal! + (( icontext = ${#zsh_eval_context} + 1 )) + contextfn() { print $(print $zsh_eval_context[icontext,-1]); } + contextfn +0:$ZSH_EVAL_CONTEXT and $zsh_eval_context +>toplevel +>shfunc cmdsubst + + foo="123456789" + print ${foo:3} + print ${foo: 1 + 3} + print ${foo:$(( 2 + 3))} + print ${foo:$(echo 3 + 3)} + print ${foo:3:1} + print ${foo: 1 + 3:(4-2)/2} + print ${foo:$(( 2 + 3)):$(( 7 - 6 ))} + print ${foo:$(echo 3 + 3):`echo 4 - 3`} + print ${foo: -1} + print ${foo: -10} + print ${foo:5:-2} +0:Bash-style offsets, scalar +>456789 +>56789 +>6789 +>789 +>4 +>5 +>6 +>7 +>9 +>123456789 +>67 + + foo=(1 2 3 4 5 6 7 8 9) + print ${foo:3} + print ${foo: 1 + 3} + print ${foo:$(( 2 + 3))} + print ${foo:$(echo 3 + 3)} + print ${foo:3:1} + print ${foo: 1 + 3:(4-2)/2} + print ${foo:$(( 2 + 3)):$(( 7 - 6 ))} + print ${foo:$(echo 3 + 3):`echo 4 - 3`} + print ${foo: -1} + print ${foo: -10} + print ${foo:5:-2} +0:Bash-style offsets, array +>4 5 6 7 8 9 +>5 6 7 8 9 +>6 7 8 9 +>7 8 9 +>4 +>5 +>6 +>7 +>9 +>1 2 3 4 5 6 7 8 9 +>6 7 + + testfn() { + emulate -L sh + set -A foo 1 2 3 + set -- 1 2 3 + str=abc + echo ${foo[*]:0:1} + echo ${foo[*]:1:1} + echo ${foo[*]: -1:1} + : + echo ${*:0:1} + echo ${*:1:1} + echo ${*: -1:1} + : + echo ${str:0:1} + echo ${str:1:1} + echo ${str: -1:1} + } + testfn +0:Bash-style offsets, Bourne-style indexing +>1 +>2 +>3 +>testfn +>1 +>3 +>a +>b +>c + + printf "%n" '[0]' +1:Regression test for identifier test +?(eval):1: not an identifier: [0] + + str=rts + print ${str:0:} +1:Regression test for missing length after offset +?(eval):2: unrecognized modifier + + foo="123456789" + print ${foo:5:-6} +1:Regression test for total length < 0 in string +?(eval):2: substring expression: 3 < 5 + + foo=(1 2 3 4 5 6 7 8 9) + print ${foo:5:-6} +1:Regression test for total length < 0 in array +?(eval):2: substring expression: 3 < 5 + + foo=(${(0)"$(print -n)"}) + print ${#foo} +0:Nularg removed from split empty string +>0 + + (set -- a b c + setopt shwordsplit + IFS= + print -rl "$*" + unset IFS + print -rl "$*") +0:Regression test for shwordsplit with null or unset IFS and quoted array +>abc +>a b c + + foo= + print ${foo:wq} + print ${:wq} +0:Empty parameter should not cause modifiers to crash the shell +> +> + +# This used to cause uncontrolled behaviour, but at best +# you got the wrong output so the check is worth it. + args() { print $#; } + args ${:*} + args ${:|} +0:Intersection and disjunction with empty parameters +>0 +>0 + + foo=(a b c) + bar=(1 2 3) + print ${foo:^bar} + print ${foo:^^bar} + foo=(a b c d) + bar=(1 2) + print ${foo:^bar} + print ${foo:^^bar} + foo=('a a' b) + bar=(1 '2 2') + print -l "${foo:^bar}" + print -l "${(@)foo:^bar}" +0:Zipping arrays, correct output +>a 1 b 2 c 3 +>a 1 b 2 c 3 +>a 1 b 2 +>a 1 b 2 c 1 d 2 +# maybe this should be changed to output "a a b 1" +>a a b +>1 +>a a +>1 +>b +>2 2 + + foo=(a b c) + bar=() + print ${foo:^bar} + print ${foo:^^bar} + print ${bar:^foo} + print ${bar:^^foo} + print ${bar:^bar} + print ${bar:^^bar} +0:Zipping arrays, one or both inputs empty +> +>a b c +> +>a b c +> +> + + foo=text + bar=() + print ${foo:^bar} + print ${bar:^^foo} + bar=other + print ${foo:^bar} + bar=(array elements) + print ${foo:^bar} + print ${foo:^^bar} + print ${bar:^foo} + print ${bar:^^foo} +0:Zipping arrays, scalar input +> +>text +>text other +>text array +>text array text elements +>array text +>array text elements text + + foo=(a b c) + print ${foo:^^^bar} +1:Zipping arrays, parsing +?(eval):2: not an identifier: ^bar + + (setopt nounset + print ${foo:^noexist}) +1:Zipping arrays, NO_UNSET part 1 +?(eval):2: noexist: parameter not set + + (setopt nounset + print ${noexist:^foo}) +1:Zipping arrays, NO_UNSET part 2 +?(eval):2: noexist: parameter not set + + expr="a@b,c@d:e@f,g@h:i@j,k@l" + for sep in : , @; do + print -l ${(ps.$sep.)expr} + done +0:Use of variable to get separator when splitting parameter +>a@b,c@d +>e@f,g@h +>i@j,k@l +>a@b +>c@d:e@f +>g@h:i@j +>k@l +>a +>b,c +>d:e +>f,g +>h:i +>j,k +>l + + SHLVL=1 + $ZTST_testdir/../Src/zsh -fc 'echo $SHLVL' + $ZTST_testdir/../Src/zsh -fc '(echo $SHLVL)' +0:SHLVL appears sensible when about to exit shell +>2 +>2 + + # SHLVL is incremented twice and decremented once in between. + SHLVL=1 + $ZTST_testdir/../Src/zsh -fc $ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"' + $ZTST_testdir/../Src/zsh -fc '('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL")' + $ZTST_testdir/../Src/zsh -fc '( ('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"))' +0:SHLVL decremented upon implicit exec optimisation +>2 +>2 +>2 + + # SHLVL is incremented twice with no decrement in between. + SHLVL=1 + $ZTST_testdir/../Src/zsh -fc '('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"); exit' + $ZTST_testdir/../Src/zsh -fc '(exec '$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"); exit' + $ZTST_testdir/../Src/zsh -fc '( ('$ZTST_testdir/../Src/zsh' -fc "echo \$SHLVL"); exit)' +0:SHLVL not decremented upon exec in subshells +>3 +>3 +>3 + +# The following tests the return behaviour of parsestr/parsestrnoerr + alias param-test-alias='print $'\''\x45xpanded in substitution'\' + param='$(param-test-alias)' + print ${(e)param} +0:Alias expansion in command substitution in parameter evaluation +>Expanded in substitution + + a=1 b=2 c=3 + : One; + function { + : Two + echo $_ + print -l $argv + } $_ Three + print -l $_ Four; +0:$_ with anonymous function +>Two +>One +>Three +>Three +>Four + + a=1 b=2 c=3 + : One + function { + : Two + echo $_ + print -l $argv + } + print -l "$_" Four +0:$_ with anonymous function without arguments +>Two +> +> +>Four + + funnychars='The qu*nk br!wan f@x j/mps o[]r \(e la~# ^"&;' + [[ $funnychars = ${~${(b)funnychars}} ]] +0:${(b)...} quoting protects from GLOB_SUBST + + set -- foo + echo $(( $#*3 )) + emulate sh -c 'nolenwithoutbrace() { echo $#-1; }' + nolenwithoutbrace +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 + + a=1_2_3_4_5_6 + print ${a#(*_)(#c2)} + print ${a#(*_)(#c5)} + print ${a#(*_)(#c7)} +0:Complicated backtracking with match counts +>3_4_5_6 +>6 +>1_2_3_4_5_6 + + (setopt shwordsplit + do_test() { + print $#: "$@" + } + foo=bar + foo2="bar bar" + do_test ${:- foo } + do_test ${:- foo bar } + do_test ${:- $foo } + do_test ${:- $foo2 } + do_test x${:- foo }y + do_test x${:- foo bar }y + do_test x${:- $foo }y + do_test x${:- $foo2 }y + do_test x${foo:+ $foo }y + ) +0:We Love SH_WORD_SPLIT Day celebrated with space at start of internal subst +>1: foo +>2: foo bar +>1: bar +>2: bar bar +>3: x foo y +>4: x foo bar y +>3: x bar y +>4: x bar bar y +>3: x bar y + + (unsetopt shwordsplit # default, for clarity + do_test() { + print $#: "$@" + } + foo=bar + foo2="bar bar" + do_test ${:- foo } + do_test ${:- foo bar } + do_test ${:- $foo } + do_test ${:- $foo2 } + do_test x${:- foo }y + do_test x${:- foo bar }y + do_test x${:- $foo }y + do_test x${:- $foo2 }y + do_test x${foo:+ $foo }y + ) +0:We Love NO_SH_WORD_SPLIT Even More Day celebrated as sanity check +>1: foo +>1: foo bar +>1: bar +>1: bar bar +>1: x foo y +>1: x foo bar y +>1: x bar y +>1: x bar bar y +>1: x bar y + + testfn() { + local scalar=obfuscation + local -a array=(alpha bravo charlie delta echo foxtrot) + local -A assoc=(one eins two zwei three drei four vier) + local name subscript + for name subscript in scalar 3 array 5 assoc three; do + print ${${(P)name}[$subscript]} + done + } + testfn +0:${(P)...} with normal subscripting +>f +>echo +>drei + + testfn() { + local s1=foo s2=bar + local -a val=(s1) + print ${${(P)val}[1,3]} + val=(s1 s2) + print ${${(P)val}[1,3]} + } + testfn +1:${(P)...} with array as name +>foo +?testfn:5: parameter name reference used with array + + testfn() { + local -A assoc=(one buckle two show three knock four door) + local name='assoc[two]' + print ${${(P)name}[2,3]} + } + testfn +0:${(P)...} with internal subscripting +>ho + + testfn() { + local one=two + local two=three + local three=four + local -a four=(all these worlds belong to foo) + print ${(P)${(P)${(P)one}}} + print ${${(P)${(P)${(P)one}}}[3]} + } + testfn +0:nested parameter name references +>all these worlds belong to foo +>worlds + + ( + path=(/random /value) + testfn1() { + local path= + print $#path + } + testfn1 + testfn2() { + local path=/somewhere + print $#path $path + } + testfn2 + print $#path $path + ) +0:Local special variables with loose typing +>0 +>1 /somewhere +>2 /random /value + + print -r -- ${(q+):-} + print -r -- ${(q+)IFS} + print -r -- ${(q+):-oneword} + print -r -- ${(q+):-two words} + print -r -- ${(q+):-three so-called \'words\'} + (setopt rcquotes; print -r -- ${(q+):-three so-called \'words\'}) +0:${(q+)...} +>'' +>$' \t\n\C-@' +>oneword +>'two words' +>'three so-called '\''words'\' +>'three so-called ''words''' + + array=(one two three) + array[1]=${nonexistent:-foo} + print $array +0:"-" works after "[" in same expression (Dash problem) +>foo two three + + ( + setopt shwordsplit + set -- whim:wham:whom + IFS=: + print -l $@ + ) +0:Splitting of $@ on IFS: single element +>whim +>wham +>whom + + ( + setopt shwordsplit + set -- one:two bucklemy:shoe + IFS=: + print -l $@ + ) +0:Splitting of $@ on IFS: multiple elements +# No forced joining in this case +>one +>two +>bucklemy +>shoe + + ( + set -- one:two bucklemy:shoe + print -l ${(s.:.)@} + ) +0:Splitting of $@ on (s): multiple elements +# Forced joining in this case +>one +>two bucklemy +>shoe + + ( + set -- one:two bucklemy:shoe + print -l ${(@s.:.)@} + ) +0:Splitting of $@ on (@s): multiple elements +# Forced non-joining in this case +>one +>two +>bucklemy +>shoe + + ( + set -- one:two bucklemy:shoe + IFS= + setopt shwordsplit + print -l ${@} ${(s.:.)*} ${(s.:.j.-.)*} + ) +0:Joining of $@ does not happen when IFS is empty, but splitting $* does +>one:two +>bucklemy:shoe +>one +>twobucklemy +>shoe +>one +>two-bucklemy +>shoe + + ( + set -- "one two" "bucklemy shoe" + IFS= + setopt shwordsplit rcexpandparam + print -l "X${(@j.-.)*}" + ) +0:Use of @ does not prevent forced join with j +>Xone two-bucklemy shoe + + () { print -r -- "${(q)1}" "${(b)1}" "${(qq)1}" } '=foo' +0:(q) and (b) quoting deal with the EQUALS option +>\=foo =foo '=foo' + + args() { print $#; } + a=(foo) + args "${a[3,-1]}" + args "${(@)a[3,-1]}" +0:Out-of-range multiple array subscripts with quoting, with and without (@) +>1 +>0 + + a='~-/'; echo $~a +0:Regression: "-" became Dash in workers/37689, breaking ~- expansion +*>* +F:We do not care what $OLDPWD is, as long as it doesn't cause an error diff --git a/dotfiles/system/.zsh/modules/Test/D05array.ztst b/dotfiles/system/.zsh/modules/Test/D05array.ztst new file mode 100644 index 0000000..1fa607d --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D05array.ztst @@ -0,0 +1,112 @@ +# Tests for array indexing + +%prep + + foo=(a b c d e f g) + arr=(foo bar baz) + mkdir array.tmp + touch array.tmp/{1..9} + +%test + + echo .$foo[1]. +0:The first element +>.a. + + echo .$foo[1,4]. +0:Normal multi-item indexing +>.a b c d. + + echo .$foo[1,0]. +0:This should be empty +>.. + + echo .$foo[4,1]. +0:Another empty slice +>.. + + echo .$foo[1,-8]. +0:An empty slice with a negative end +>.. + + echo .$foo[0]. +0:Treat 0 as empty +>.. + + echo .$foo[0,0]. +0:Treat 0,0 as empty +>.. + + echo .$foo[0,1]. +0:Another weird way to access the first element +>.a. + + echo .$foo[3]. +0:An inner element +>.c. + + echo .$foo[2,2]. +0:Another inner element +>.b. + + echo .$foo[2,-4]. +0:A slice with a negative end +>.b c d. + + echo .$foo[-4,5]. +0:A slice with a negative start +>.d e. + + echo .$foo[-6,-2]. +0:A slice with a negative start and end +>.b c d e f. + + echo .${${arr[2]}[1]}. + echo .${${arr[-2]}[1]}. + echo .${${arr[2,2]}[1]}. + echo .${${arr[-2,-2]}[1]}. + echo .${${arr[2,-2]}[1]}. + echo .${${arr[-2,2]}[1]}. +0:Slices should return an array, elements a scalar +>.b. +>.b. +>.bar. +>.bar. +>.bar. +>.bar. + + setopt ksh_arrays + echo .${foo[1,2]}. + unsetopt ksh_arrays +0:Ksh array indexing +>.b c. + + setopt ksh_arrays + echo .${foo[0,1]}. + unsetopt ksh_arrays +0:Ksh array indexing (ii) +>.a b. + + setopt ksh_arrays + echo .${foo[1,-1]}. + unsetopt ksh_arrays +0:Ksh array indexing (iii) +>.b c d e f g. + + cd array.tmp + echo . ?([3,5]) . + cd .. +0:Glob array indexing +>. 3 4 5 . + + cd array.tmp + echo . ?([2,-2]) . + cd .. +0:Glob array indexing (ii) +>. 2 3 4 5 6 7 8 . + + cd array.tmp + echo . ?([-6,-4]) . + cd .. +0:Glob array indexing (iii) +>. 4 5 6 . diff --git a/dotfiles/system/.zsh/modules/Test/D06subscript.ztst b/dotfiles/system/.zsh/modules/Test/D06subscript.ztst new file mode 100644 index 0000000..1449236 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D06subscript.ztst @@ -0,0 +1,268 @@ +# Test parameter subscripting. + +%prep + + s='Twinkle, twinkle, little *, [how] I [wonder] what? You are!' + a=('1' ']' '?' '\2' '\]' '\?' '\\3' '\\]' '\\?' '\\\4' '\\\]' '\\\?') + typeset -g -A A + A=($a) + +%test + + x=',' + print $s[(i)winkle] $s[(I)winkle] + print ${s[(i)You are]} $#s + print ${s[(r)$x,(R)$x]} +0:Scalar pattern subscripts without wildcards +>2 11 +>53 60 +>, twinkle, little *, + + x='*' + print $s[(i)*] $s[(i)\*] $s[(i)$x*] $s[(i)${(q)x}*] $s[(I)$x\*] + print $s[(r)?,(R)\?] $s[(r)\?,(R)?] + print $s[(r)\*,(R)*] + print $s[(r)\],(R)\[] +0:Scalar pattern subscripts with wildcards +>1 26 1 26 26 +>Twinkle, twinkle, little *, [how] I [wonder] what? ? You are! +>*, [how] I [wonder] what? You are! +>] I [ + + print $s[(i)x] : $s[(I)x] + print $s[(r)x] : $s[(R)x] +0:Scalar pattern subscripts that do not match +>61 : 0 +>: + + print -R $s[$s[(i)\[]] $s[(i)$s[(r)\*]] $s[(i)${(q)s[(r)\]]}] +0:Scalar subscripting using a pattern subscript to get the index +>[ 1 33 + + print -R $a[(r)?] $a[(R)?] + print $a[(n:2:i)?] $a[(n:2:I)?] + print $a[(i)\?] $a[(I)\?] + print $a[(i)*] $a[(i)\*] +0:Array pattern subscripts +>1 ? +>2 2 +>3 3 +>1 13 + + # It'd be nice to do some of the following with (r), but we run into + # limitations of the ztst script parsing of backslashes in the output. + print -R $a[(i)\\\\?] $a[(i)\\\\\?] + print -R $a[(i)\\\\\\\\?] $a[(i)\\\\\\\\\?] + print -R ${a[(i)\\\\\\\\?]} ${a[(i)\\\\\\\\\?]} + print -R "$a[(i)\\\\\\\\?] $a[(i)\\\\\\\\\?]" + print -R $a[(i)\]] $a[(i)\\\\\]] $a[(i)\\\\\\\\\]] $a[(i)\\\\\\\\\\\\\]] + print -R $a[(i)${(q)a[5]}] $a[(i)${(q)a[8]}] $a[(i)${(q)a[11]}] + print -R $a[(i)${a[3]}] $a[(i)${a[6]}] $a[(i)${a[9]}] $a[(i)${a[12]}] +0:Array pattern subscripts with multiple backslashes +>4 6 +>7 9 +>7 9 +>7 9 +>2 5 8 11 +>5 8 11 +>1 3 4 6 + + print -R $A[1] $A[?] $A[\\\\3] $A[\\\]] + print -R $A[$a[11]] + print -R $A[${(q)a[5]}] +0:Associative array lookup (direct subscripting) +>] \2 \\] \? +>\\\? +>\\\? + + # The (o) is necessary here for predictable output ordering + print -R $A[(I)\?] ${(o)A[(I)?]} + print -R $A[(i)\\\\\\\\3] + print -R $A[(I)\\\\\\\\\?] ${(o)A[(I)\\\\\\\\?]} +0:Associative array lookup (pattern subscripting) +>? 1 ? +>\\3 +>\\? \\3 \\? + + print -R $A[(R)\?] : ${(o)A[(R)?]} + print -R $A[(R)\\\\\?] ${(o)A[(R)\\\\?]} ${(o)A[(R)\\\\\?]} + print -R ${(o)A[(R)\\\\\\\\\]]} +0:Associative array lookup (reverse subscripting) +>: ] +>\? \2 \? \? +>\\] + + eval 'A[*]=star' +1:Illegal associative array assignment +?(eval):1: A: attempt to set slice of associative array + + x='*' + A[$x]=xstar + A[${(q)x}]=qxstar + print -R ${(k)A[(r)xstar]} $A[$x] + print -R ${(k)A[(r)qxstar]} $A[${(q)x}] + A[(e)*]=star + A[\*]=backstar + print -R ${(k)A[(r)star]} $A[(e)*] + print -R ${(k)A[(r)backstar]} $A[\*] +0:Associative array assignment +>* xstar +>\* qxstar +>* star +>\* backstar + + o='[' + c=']' + A[\]]=cbrack + A[\[]=obrack + A[\\\[]=backobrack + A[\\\]]=backcbrack + print -R $A[$o] $A[$c] $A[\[] $A[\]] $A[\\\[] $A[\\\]] + print -R $A[(i)\[] $A[(i)\]] $A[(i)\\\\\[] $A[(i)\\\\\]] +0:Associative array keys with open and close brackets +>obrack cbrack obrack cbrack backobrack backcbrack +>[ ] \[ \] + + print -R $A[$o] $A[$s[(r)\[]] + print -R $A[(r)$c] $A[(r)$s[(r)\]]] + print -R $A[$A[(i)\\\\\]]] +0:Associative array lookup using a pattern subscript to get the key +>obrack obrack +>] ] +>backcbrack + + print -R ${A[${A[(r)\\\\\\\\\]]}]::=zounds} + print -R ${A[${A[(r)\\\\\\\\\]]}]} + print -R $A[\\\\\]] +0:Associative array substitution-assignment with reverse pattern subscript key +>zounds +>zounds +>zounds + + print -R ${(o)A[(K)\]]} + print -R ${(o)A[(K)\\\]]} +0:Associative array keys interpreted as patterns +>\2 backcbrack cbrack star +>\\\4 \\\? star zounds + +# It doesn't matter which element we get, since we never guarantee +# ordering of an associative array. So just test the number of matches. + array=(${(o)A[(k)\]]}) + print ${#array} + array=(${(o)A[(k)\\\]]}) + print ${#array} +0:Associative array keys interpreted as patterns, single match +>1 +>1 + + typeset -g "A[one\"two\"three\"quotes]"=QQQ + typeset -g 'A[one\"two\"three\"quotes]'=qqq + print -R "$A[one\"two\"three\"quotes]" + print -R $A[one\"two\"three\"quotes] + A[one"two"three"four"quotes]=QqQq + print -R $A[one"two"three"four"quotes] + print -R $A[$A[(i)one\"two\"three\"quotes]] + print -R "$A[$A[(i)one\"two\"three\"quotes]]" +0:Associative array keys with double quotes +>QQQ +>qqq +>QqQq +>qqq +>QQQ + + print ${x::=$A[$A[(i)one\"two\"three\"quotes]]} + print $x + print ${x::="$A[$A[(i)one\"two\"three\"quotes]]"} + print $x +0:More keys with double quotes, used in assignment-expansion +>qqq +>qqq +>QQQ +>QQQ + + qqq=lower + QQQ=upper + print ${(P)A[one\"two\"three\"quotes]} + print "${(P)A[$A[(i)one\"two\"three\"quotes]]}" +0:Keys with double quotes and the (P) expansion flag +>lower +>upper + + typeset -ga empty + echo X${${empty##*}[-1]}X +0:Negative index applied to substition result from empty array +>XX + + print $empty[(i)] $empty[(I)] +0:(i) returns 1 for empty array, (I) returns 0. +>1 0 + + array=(one two three four) + print X$array[0]X +0:Element zero is empty if KSH_ZERO_SUBSCRIPT is off. +>XX + + array[0]=fumble +1:Can't set element zero if KSH_ZERO_SUBSCRIPT is off. +?(eval):1: array: assignment to invalid subscript range + + print X$array[(R)notfound]X +0:(R) returns empty if not found if KSH_ZERO_SUBSCRIPT is off. +>XX + + setopt KSH_ZERO_SUBSCRIPT + print X$array[0]X +0:Element zero is element one if KSH_ZERO_SUBSCRIPT is on. +>XoneX + + array[0]=fimble + print $array +0:Can set element zero if KSH_ZERO_SUBSCRIPT is on. +>fimble two three four + + print X$array[(R)notfound]X +0:(R) yuckily returns the first element on failure withe KSH_ZERO_SUBSCRIPT +>XfimbleX + + unsetopt KSH_ZERO_SUBSCRIPT + array[(R)notfound,(r)notfound]=(help help here come the seventies retreads) + print $array +0:[(R)notfound,(r)notfound] replaces the whole array +>help help here come the seventies retreads + + string="Why, if it isn't Officer Dibble" + print "[${string[0]}][${string[1]}][${string[0,3]}]" +0:String subscripts with KSH_ZERO_SUBSCRIPT unset +>[][W][Why] + + setopt KSH_ZERO_SUBSCRIPT + print "[${string[0]}][${string[1]}][${string[0,3]}]" +0:String subscripts with KSH_ZERO_SUBSCRIPT set +>[W][W][Why] + + unsetopt KSH_ZERO_SUBSCRIPT + string[0,3]="Goodness" + print $string +0:Assignment to chunk of string ignores element 0 +>Goodness, if it isn't Officer Dibble + + string[0]=! +1:Can't set only element zero of string +?(eval):1: string: assignment to invalid subscript range + + typeset -A assoc=(leader topcat officer dibble sidekick choochoo) + alias myind='echo leader' myletter='echo 1' myletter2='echo 4' + print ${assoc[$(myind)]} + print $assoc[$(myind)] + print ${assoc[$(myind)][$(myletter)]}${assoc[$(myind)][$(myletter2)]} + assoc[$(myind)]='of the gang' + print ${assoc[$(myind)]} + print $assoc[$(myind)] + print $assoc[leader] +0: Parsing subscript with non-trivial tokenisation +>topcat +>topcat +>tc +>of the gang +>of the gang +>of the gang diff --git a/dotfiles/system/.zsh/modules/Test/D07multibyte.ztst b/dotfiles/system/.zsh/modules/Test/D07multibyte.ztst new file mode 100644 index 0000000..e203153 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D07multibyte.ztst @@ -0,0 +1,587 @@ +%prep + +# Find a UTF-8 locale. + setopt multibyte +# Don't let LC_* override our choice of locale. + unset -m LC_\* + mb_ok= + langs=(en_{US,GB}.{UTF-,utf}8 en.UTF-8 + $(locale -a 2>/dev/null | egrep 'utf8|UTF-8')) + for LANG in $langs; do + if [[ รฉ = ? ]]; then + mb_ok=1 + break; + fi + done + if [[ -z $mb_ok ]]; then + ZTST_unimplemented="no UTF-8 locale or multibyte mode is not implemented" + else + print -u $ZTST_fd Testing multibyte with locale $LANG + mkdir multibyte.tmp && cd multibyte.tmp + fi + +%test + + a=tรฉnรฉbreux + for i in {1..9}; do + print ${a[i]} + for j in {$i..9}; do + print $i $j ${a[i,j]} ${a[-j,-i]} + done + done +0:Basic indexing with multibyte characters +>t +>1 1 t x +>1 2 tรฉ ux +>1 3 tรฉn eux +>1 4 tรฉnรฉ reux +>1 5 tรฉnรฉb breux +>1 6 tรฉnรฉbr รฉbreux +>1 7 tรฉnรฉbre nรฉbreux +>1 8 tรฉnรฉbreu รฉnรฉbreux +>1 9 tรฉnรฉbreux tรฉnรฉbreux +>รฉ +>2 2 รฉ u +>2 3 รฉn eu +>2 4 รฉnรฉ reu +>2 5 รฉnรฉb breu +>2 6 รฉnรฉbr รฉbreu +>2 7 รฉnรฉbre nรฉbreu +>2 8 รฉnรฉbreu รฉnรฉbreu +>2 9 รฉnรฉbreux tรฉnรฉbreu +>n +>3 3 n e +>3 4 nรฉ re +>3 5 nรฉb bre +>3 6 nรฉbr รฉbre +>3 7 nรฉbre nรฉbre +>3 8 nรฉbreu รฉnรฉbre +>3 9 nรฉbreux tรฉnรฉbre +>รฉ +>4 4 รฉ r +>4 5 รฉb br +>4 6 รฉbr รฉbr +>4 7 รฉbre nรฉbr +>4 8 รฉbreu รฉnรฉbr +>4 9 รฉbreux tรฉnรฉbr +>b +>5 5 b b +>5 6 br รฉb +>5 7 bre nรฉb +>5 8 breu รฉnรฉb +>5 9 breux tรฉnรฉb +>r +>6 6 r รฉ +>6 7 re nรฉ +>6 8 reu รฉnรฉ +>6 9 reux tรฉnรฉ +>e +>7 7 e n +>7 8 eu รฉn +>7 9 eux tรฉn +>u +>8 8 u รฉ +>8 9 ux tรฉ +>x +>9 9 x t + + s=รฉ + print A${s[-2]}A B${s[-1]}B C${s[0]}C D${s[1]}D E${s[2]}E +0:Out of range subscripts with multibyte characters +>AA BรฉB CC DรฉD EE + + print ${a[(i)รฉ]} ${a[(I)รฉ]} ${a[${a[(i)รฉ]},${a[(I)รฉ]}]} +0:Reverse indexing with multibyte characters +>2 4 รฉnรฉ + + print ${a[(r)รฉn,(r)รฉb]} +0:Subscript searching with multibyte characters +>รฉnรฉb + + print ${a[(rb:1:)รฉ,-1]} + print ${a[(rb:2:)รฉ,-1]} + print ${a[(rb:3:)รฉ,-1]} + print ${a[(rb:4:)รฉ,-1]} + print ${a[(rb:5:)รฉ,-1]} +0:Subscript searching with initial offset +>รฉnรฉbreux +>รฉnรฉbreux +>รฉbreux +>รฉbreux +> + + print ${a[(rn:1:)รฉ,-1]} + print ${a[(rn:2:)รฉ,-1]} + print ${a[(rn:3:)รฉ,-1]} +0:Subscript searching with count +>รฉnรฉbreux +>รฉbreux +> + + print ${a[(R)รฉn,(R)รฉb]} +0:Backward subscript searching with multibyte characters +>รฉnรฉb + +# Starting offsets with (R) seem to be so strange as to be hardly +# worth testing. + + setopt extendedglob + [[ $a = (#b)t(รฉn)(รฉb)reux ]] || print "Failed to match." >&2 + for i in {1..${#match}}; do + print $match[i] $mbegin[i] $mend[i] ${a[$mbegin[i],$mend[i]]} + done +0:Multibyte offsets in pattern tests +>รฉn 2 3 รฉn +>รฉb 4 5 รฉb + + b=${(U)a} + print $b + print ${(L)b} + desdichado="Je suis le $a, le veuf, l'inconsolรฉ" + print ${(C)desdichado} + lxiv="l'รฉtat c'est moi" + print ${(C)lxiv} +0:Case modification of multibyte strings +>Tร‰Nร‰BREUX +>tรฉnรฉbreux +>Je Suis Le Tรฉnรฉbreux, Le Veuf, L'Inconsolรฉ +>L'ร‰tat C'Est Moi + + array=(รธlaf รธdd รธpened รกn encyclopรฆdia) + barray=(${(U)array}) + print $barray + print ${(L)barray} + print ${(C)array} + print ${(C)barray} +0:Case modification of arrays with multibyte strings +>ร˜LAF ร˜DD ร˜PENED รN ENCYCLOPร†DIA +>รธlaf รธdd รธpened รกn encyclopรฆdia +>ร˜laf ร˜dd ร˜pened รn Encyclopรฆdia +>ร˜laf ร˜dd ร˜pened รn Encyclopรฆdia + + print $(( ##ยฅ )) + pound=ยฃ + print $(( #pound )) + alpha=ฮฑ + print $(( ##ฮฑ )) $(( #alpha )) +0:Conversion to Unicode in mathematical expressions +>165 +>163 +>945 945 + + unsetopt posix_identifiers + expr='hรคhรค=3 || exit 1; print $hรคhรค' + eval $expr + setopt posix_identifiers + (eval $expr) +1:POSIX_IDENTIFIERS option +>3 +?(eval):1: command not found: hรคhรค=3 + + foo="ร˜lafยซร˜ddยซรธpรฉnรซdยซรฅnยซร pple" + print -l ${(s.ยซ.)foo} + ioh="แผ˜ฮฝ แผ€ฯฯ‡แฟ‡ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚, ฮบฮฑแฝถ แฝ ฮปแฝนฮณฮฟฯ‚ แผฆฮฝ ฯ€ฯแฝธฯ‚ ฯ„แฝธฮฝ ฮธฮตแฝนฮฝ, ฮบฮฑแฝถ ฮธฮตแฝธฯ‚ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚." + print -l ${=ioh} + print ${(w)#ioh} +0:Splitting with multibyte characters +>ร˜laf +>ร˜dd +>รธpรฉnรซd +>รฅn +>ร pple +>แผ˜ฮฝ +>แผ€ฯฯ‡แฟ‡ +>แผฆฮฝ +>แฝ +>ฮปแฝนฮณฮฟฯ‚, +>ฮบฮฑแฝถ +>แฝ +>ฮปแฝนฮณฮฟฯ‚ +>แผฆฮฝ +>ฯ€ฯแฝธฯ‚ +>ฯ„แฝธฮฝ +>ฮธฮตแฝนฮฝ, +>ฮบฮฑแฝถ +>ฮธฮตแฝธฯ‚ +>แผฆฮฝ +>แฝ +>ฮปแฝนฮณฮฟฯ‚. +>17 + + read -d ยฃ one + read -d ยฃ two + print $one + print $two +0:read with multibyte delimiter +first +>second + + (IFS=ยซ + read -d ยป -A array + print -l $array) +0:read -A with multibyte IFS +dominus +>illuminatio +>mea + + read -k2 -u0 twochars + print $twochars +0:read multibyte characters +<ยซยปignored +>ยซยป + + read -q -u0 mb + print $? +0:multibyte character makes read -q return false +<ยซ +>1 + + # See if the system grokks first-century Greek... + ioh="แผ˜ฮฝ แผ€ฯฯ‡แฟ‡ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚, ฮบฮฑแฝถ แฝ ฮปแฝนฮณฮฟฯ‚ แผฆฮฝ ฯ€ฯแฝธฯ‚ ฯ„แฝธฮฝ ฮธฮตแฝนฮฝ, ฮบฮฑแฝถ ฮธฮตแฝธฯ‚ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚." + for (( i = 1; i <= ${#ioh}; i++ )); do + # FC3 doesn't recognise แฟ‡ (U+1FC7: Greek small letter eta with + # perispomeni and ypogegrammeni, of course) as a lower case character. + if [[ $ioh[i] != [[:lower:]] && $i != 7 ]]; then + for tp in upper space punct invalid; do + if [[ $tp = invalid || $ioh[i] = [[:${tp}:]] ]]; then + print "$i: $tp" + break + fi + done + fi + done +0:isw* functions on non-ASCII wide characters +>1: upper +>3: space +>8: space +>11: space +>13: space +>19: punct +>20: space +>24: space +>26: space +>32: space +>35: space +>40: space +>44: space +>49: punct +>50: space +>54: space +>59: space +>62: space +>64: space +>70: punct + + ioh="แผ˜ฮฝ แผ€ฯฯ‡แฟ‡ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚, ฮบฮฑแฝถ แฝ ฮปแฝนฮณฮฟฯ‚ แผฆฮฝ ฯ€ฯแฝธฯ‚ ฯ„แฝธฮฝ ฮธฮตแฝนฮฝ, ฮบฮฑแฝถ ฮธฮตแฝธฯ‚ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚" + print ${ioh#[[:alpha:]]##} + print ${ioh##[[:alpha:]]##} + print ${ioh%[[:alpha:]]##} + print ${ioh%%[[:alpha:]]##} + print ${(S)ioh#ฮป*ฯ‚} + print ${(S)ioh##ฮป*ฯ‚} + print ${(S)ioh%ฮธ*ฯ‚} + print ${(S)ioh%%ฮธ*ฯ‚} +0:Parameter #, ##, %, %% with multibyte characters +>ฮฝ แผ€ฯฯ‡แฟ‡ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚, ฮบฮฑแฝถ แฝ ฮปแฝนฮณฮฟฯ‚ แผฆฮฝ ฯ€ฯแฝธฯ‚ ฯ„แฝธฮฝ ฮธฮตแฝนฮฝ, ฮบฮฑแฝถ ฮธฮตแฝธฯ‚ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚ +> แผ€ฯฯ‡แฟ‡ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚, ฮบฮฑแฝถ แฝ ฮปแฝนฮณฮฟฯ‚ แผฆฮฝ ฯ€ฯแฝธฯ‚ ฯ„แฝธฮฝ ฮธฮตแฝนฮฝ, ฮบฮฑแฝถ ฮธฮตแฝธฯ‚ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚ +>แผ˜ฮฝ แผ€ฯฯ‡แฟ‡ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚, ฮบฮฑแฝถ แฝ ฮปแฝนฮณฮฟฯ‚ แผฆฮฝ ฯ€ฯแฝธฯ‚ ฯ„แฝธฮฝ ฮธฮตแฝนฮฝ, ฮบฮฑแฝถ ฮธฮตแฝธฯ‚ แผฆฮฝ แฝ ฮปแฝนฮณฮฟ +>แผ˜ฮฝ แผ€ฯฯ‡แฟ‡ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚, ฮบฮฑแฝถ แฝ ฮปแฝนฮณฮฟฯ‚ แผฆฮฝ ฯ€ฯแฝธฯ‚ ฯ„แฝธฮฝ ฮธฮตแฝนฮฝ, ฮบฮฑแฝถ ฮธฮตแฝธฯ‚ แผฆฮฝ แฝ +>แผ˜ฮฝ แผ€ฯฯ‡แฟ‡ แผฆฮฝ แฝ , ฮบฮฑแฝถ แฝ ฮปแฝนฮณฮฟฯ‚ แผฆฮฝ ฯ€ฯแฝธฯ‚ ฯ„แฝธฮฝ ฮธฮตแฝนฮฝ, ฮบฮฑแฝถ ฮธฮตแฝธฯ‚ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚ +>แผ˜ฮฝ แผ€ฯฯ‡แฟ‡ แผฆฮฝ แฝ +>แผ˜ฮฝ แผ€ฯฯ‡แฟ‡ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚, ฮบฮฑแฝถ แฝ ฮปแฝนฮณฮฟฯ‚ แผฆฮฝ ฯ€ฯแฝธฯ‚ ฯ„แฝธฮฝ ฮธฮตแฝนฮฝ, ฮบฮฑแฝถ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚ +>แผ˜ฮฝ แผ€ฯฯ‡แฟ‡ แผฆฮฝ แฝ ฮปแฝนฮณฮฟฯ‚, ฮบฮฑแฝถ แฝ ฮปแฝนฮณฮฟฯ‚ แผฆฮฝ ฯ€ฯแฝธฯ‚ ฯ„แฝธฮฝ ฮธฮตแฝนฮฝ, ฮบฮฑแฝถ + + a="1รซ34รซ6" + print ${(BEN)a#*4} + print ${(BEN)a##*รซ} + print ${(BEN)a%4*} + print ${(BEN)a%%รซ*} + print ${(SBEN)a#รซ3} + print ${(SBEN)a%4รซ} +0:Flags B, E, N and S in ${...#...} and ${...%...} +>1 5 4 +>1 6 5 +>4 7 3 +>2 7 5 +>2 4 2 +>4 6 2 + + foo=(ฮบฮฑฯ„แฝณฮฒฮทฮฝ ฯ‡ฮธแฝฒฯ‚ ฮตแผฐฯ‚ ฮ ฮตฮนฯฮฑฮนแพถ) + print ${(l.3..ยฅ.r.3..ยฃ.)foo} + print ${(l.4..ยฅ.r.2..ยฃ.)foo} + print ${(l.5..ยฅ.r.1..ยฃ.)foo} + print ${(l.4..ยฅ..ยซ.r.4..ยฃ..ยป.)foo} + print ${(l.4..ยฅ..ฮฃฯ‰ฮบฯแฝฑฯ„ฮทฯ‚.r.4..ยฃ..ฮ“ฮปฮฑแฝปฮบฯ‰ฮฝฮฟฯ‚.)foo} +0:simultaneous left and right padding +>ฮบฮฑฯ„แฝณฮฒฮท ยฅฯ‡ฮธแฝฒฯ‚ยฃ ยฅยฅฮตแผฐฯ‚ยฃ ฮ ฮตฮนฯฮฑฮน +>ยฅฮบฮฑฯ„แฝณฮฒ ยฅยฅฯ‡ฮธแฝฒฯ‚ ยฅยฅยฅฮตแผฐฯ‚ ยฅฮ ฮตฮนฯฮฑ +>ยฅยฅฮบฮฑฯ„แฝณ ยฅยฅยฅฯ‡ฮธแฝฒ ยฅยฅยฅยฅฮตแผฐ ยฅยฅฮ ฮตฮนฯ +>ยซฮบฮฑฯ„แฝณฮฒฮทฮฝ ยฅยซฯ‡ฮธแฝฒฯ‚ยปยฃ ยฅยฅยซฮตแผฐฯ‚ยปยฃ ยซฮ ฮตฮนฯฮฑฮนแพถ +>ฯ‚ฮบฮฑฯ„แฝณฮฒฮทฮฝ ฮทฯ‚ฯ‡ฮธแฝฒฯ‚ฮ“ฮป ฯ„ฮทฯ‚ฮตแผฐฯ‚ฮ“ฮป ฯ‚ฮ ฮตฮนฯฮฑฮนแพถ +# er... yeah, that looks right... + + foo=picobarn + print ${foo:sยฃbarยฃrodยฃ:sยฅrodยฅstickยฅ} +0:Delimiters in modifiers +>picostickn + +# TODO: if we get paired multibyte bracket delimiters to work +# (as Emacs does, the smug so-and-so), the following should change. + foo=bar + print ${(rยฃ5ยฃยฃXยฃ)foo} + print ${(lยซ10ยซยซYยซยซHIยซ)foo} +0:Delimiters in parameter flags +>barXX +>YYYYYHIbar + + printf "%4.3s\n" fล‘obar +0:Multibyte characters in printf widths +> fล‘o + +# We ask for case-insensitive sorting here (and supply upper case +# characters) so that we exercise the logic in the shell that lowers the +# case of the string for case-insensitive sorting. + print -oi Hร›H Hร”H HรŽH HรŠH Hร‚H + (LC_ALL=C; print -oi HAH HUH HEH Hร‰H HรˆH) +0:Multibyte characters in print sorting +>Hร‚H HรŠH HรŽH Hร”H Hร›H +>HAH HEH HUH HรˆH Hร‰H + +# These are control characters in Unicode, so don't show up. +# We just want to check they're not being treated as tokens. + for x in {128..150}; do + print ${(#)x} + done | while read line; do + print ${#line} $(( #line )) + done +0:evaluated character number with multibyte characters +>1 128 +>1 129 +>1 130 +>1 131 +>1 132 +>1 133 +>1 134 +>1 135 +>1 136 +>1 137 +>1 138 +>1 139 +>1 140 +>1 141 +>1 142 +>1 143 +>1 144 +>1 145 +>1 146 +>1 147 +>1 148 +>1 149 +>1 150 + + touch ngs1txt ngs2txt ngs10txt ngs20txt ngs100txt ngs200txt + setopt numericglobsort + print -l ngs* +0:NUMERIC_GLOB_SORT option in UTF-8 locale +>ngs1txt +>ngs2txt +>ngs10txt +>ngs20txt +>ngs100txt +>ngs200txt + +# Not strictly multibyte, but gives us a well-defined locale for testing. + foo=$'X\xc0Y\x07Z\x7fT' + print -r ${(q)foo} +0:Backslash-quoting of unprintable/invalid characters uses $'...' +>X$'\300'Y$'\a'Z$'\177'T + +# This also isn't strictly multibyte and is here to reduce the +# likelihood of a "cannot do character set conversion" error. + (print $'\u00e9') 2>&1 | read + if [[ $REPLY != รฉ ]]; then + print "warning: your system can't do simple Unicode conversion." >&$ZTST_fd + print "Check you have a correctly installed iconv library." >&$ZTST_fd + # cheat + repeat 4 print OK + else + testfn() { (LC_ALL=C; print $'\u00e9') } + repeat 4 testfn 2>&1 | while read line; do + if [[ $line = *"character not in range"* ]]; then + print OK + elif [[ $line = "?" ]]; then + print OK + else + print Failed: no error message and no question mark + fi + done + fi + true +0:error handling in Unicode quoting +>OK +>OK +>OK +>OK + + tmp1='glob/\(\)ฤ„/*' + [[ glob/'()ฤ„'/foo == $~tmp1 ]] && print "Matched against $tmp1" + tmp1='glob/\(\)ฤ€/*' + [[ glob/'()ฤ€'/bar == $~tmp1 ]] && print "Matched against $tmp1" +0:Backslashes and metafied characters in patterns +>Matched against glob/()ฤ„/* +>Matched against glob/()ฤ€/* + + mkdir ๆขถๆตฆ็”ฑ่จ˜ 'ะŸั‘ั‚ั€ ะ˜ะปัŒะธั‡ ะงะฐะนะบะพะฒัะบะธะน' + (cd ๆขถๆตฆ็”ฑ่จ˜; print ${${(%):-%~}:t}) + (cd 'ะŸั‘ั‚ั€ ะ˜ะปัŒะธั‡ ะงะฐะนะบะพะฒัะบะธะน'; print ${${(%):-%~}:t}) +0:Metafied characters in prompt expansion +>ๆขถๆตฆ็”ฑ่จ˜ +>ะŸั‘ั‚ั€ ะ˜ะปัŒะธั‡ ะงะฐะนะบะพะฒัะบะธะน + + ( + setopt nonomatch + tmp1=ฤ„ + tmpA=(ฤ„ 'ะŸั‘ั‚ั€ ะ˜ะปัŒะธั‡ ะงะฐะนะบะพะฒัะบะธะน' ๆขถๆตฆ็”ฑ่จ˜) + print ${tmp1} ${(%)tmp1} ${(%%)tmp1} + print ${#tmp1} ${#${(%)tmp1}} ${#${(%%)tmp1}} + print ${tmpA} + print ${(%)tmpA} + print ${(%%)tmpA} + ) +0:More metafied characters in prompt expansion +>ฤ„ ฤ„ ฤ„ +>1 1 1 +>ฤ„ ะŸั‘ั‚ั€ ะ˜ะปัŒะธั‡ ะงะฐะนะบะพะฒัะบะธะน ๆขถๆตฆ็”ฑ่จ˜ +>ฤ„ ะŸั‘ั‚ั€ ะ˜ะปัŒะธั‡ ะงะฐะนะบะพะฒัะบะธะน ๆขถๆตฆ็”ฑ่จ˜ +>ฤ„ ะŸั‘ั‚ั€ ะ˜ะปัŒะธั‡ ะงะฐะนะบะพะฒัะบะธะน ๆขถๆตฆ็”ฑ่จ˜ + + setopt cbases + print $'\xc5' | read + print $(( [#16] #REPLY )) +0:read passes through invalid multibyte characters +>0xC5 + + word=abcใพ + word[-1]= + print $word + word=abcใพ + word[-2]= + print $word + word=abcใพ + word[4]=d + print $word + word=abcใพ + word[3]=not_c + print $word +0:assignment with negative indices +>abc +>abใพ +>abcd +>abnot_cใพ + + # The following doesn't necessarily need UTF-8, but this gives + # us the full effect --- if we parse this wrongly the \xe9 + # in combination with the tokenized input afterwards looks like a + # valid UTF-8 character. But it isn't. + print $'$\xe9#``' >test_bad_param + (setopt nonomatch + . ./test_bad_param) +127:Invalid parameter name with following tokenized input +?./test_bad_param:1: command not found: $\M-i# + + lines=$'one\t๏ผบ๏ผณ๏ผจ\tthree\nfour\tfive\tsix' + print -X8 -r -- $lines +0:Tab expansion with extra-wide characters +>one ๏ผบ๏ผณ๏ผจ 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. + + () { + emulate -L zsh + setopt errreturn + local cdpath=(.) + mkdir ใƒ› + cd ใƒ› + cd .. + cd ./ใƒ› + cd .. + } +0:cd with special characters + + test_array=( + '[[ \xcc = \xcc ]]' + '[[ \xcc != \xcd ]]' + '[[ \xcc != \ucc ]]' + '[[ \ucc = \ucc ]]' + '[[ \ucc = [\ucc] ]]' + '[[ \xcc != [\ucc] ]]' + # Not clear how useful the following is... + '[[ \xcc = [\xcc] ]]' + ) + for test in $test_array; do + if ! eval ${(g::)test} ; then + print -rl "Test $test failed" >&2 + fi + done +0:Invalid characters in pattern matching + + [[ $'\xe3' == [[:INCOMPLETE:]] ]] || print fail 1 + [[ $'\xe3\x83' == [[:INCOMPLETE:]][[:INVALID:]] ]] || print fail 2 + [[ $'\xe3\x83\x9b' != [[:INCOMPLETE:][:INVALID:]] ]] || print fail 3 + [[ $'\xe3\x83\x9b' = ? ]] || print fail 4 +0:Testing incomplete and invalid multibyte character components + + print -r -- ${(q+):-ใƒ›} + foo='She said "ใƒ›". I said "You can'\''t '\''ใƒ›'\'' me!' + print -r -- ${(q+)foo} +0:${(q+)...} with printable multibyte characters +>ใƒ› +>'She said "ใƒ›". I said "You can'\''t '\''ใƒ›'\'' me!' + +# This will silently succeed if zsh/parameter isn't available + (zmodload zsh/parameter >/dev/null 2>&1 + f() { + : $(:) + "โ†“" + } + : $functions) +0:Multibyte handling of functions parameter + +# c1=U+0104 (ฤ„) and c2=U+0120 (ฤ ) are chosen so that +# u1 = utf8(c1) = c4 84 < u2 = utf8(c2) = c4 a0 +# metafy(u1) = c4 83 a4 > metafy(u2) = c4 83 80 +# in both UTF-8 and ASCII collations (the latter is used in macOS +# and some versions of BSDs). + local -a names=( $'\u0104' $'\u0120' ) + print -o $names + mkdir -p colltest + cd colltest + touch $names + print ? +0:Sorting of metafied characters +>ฤ„ ฤ  +>ฤ„ ฤ  + + printf '%q%q\n' ไฝ ไฝ  +0:printf %q and quotestring and general metafy / token madness +>ไฝ ไฝ  + +# This test is kept last as it introduces an additional +# dependency on the system regex library. + if zmodload zsh/regex 2>/dev/null; then + [[ $'\ua0' =~ '^.$' ]] && print OK + [[ $'\ua0' =~ $'^\ua0$' ]] && print OK + [[ $'\ua0'X =~ '^X$' ]] || print OK + else + ZTST_skip="regexp library not found." + fi +0:Ensure no confusion on metafied input to regex module +>OK +>OK +>OK +F:A failure here may indicate the system regex library does not +F:support character sets outside the portable 7-bit range. diff --git a/dotfiles/system/.zsh/modules/Test/D08cmdsubst.ztst b/dotfiles/system/.zsh/modules/Test/D08cmdsubst.ztst new file mode 100644 index 0000000..3625373 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D08cmdsubst.ztst @@ -0,0 +1,169 @@ +# Tests for command substitution. + +%prep + mkdir cmdsubst.tmp + touch cmdsubst.tmp/file{1,2}.txt + +%test + foo="two words" + print -l `echo $foo bar` +0:Basic `...` substitution +>two +>words +>bar + + foo="two words" + print -l $(echo $foo bar) +0:Basic $(...) substitution +>two +>words +>bar + + foo='intricate buffoonery' + print -l "`echo $foo and licentiousness`" +0:Quoted `...` substitution +>intricate buffoonery and licentiousness + + foo="more words" + print -l "$(echo $foo here)" +0:Quoted $(...) substitution +>more words here + +# we used never to get this one right, but I think it is now... + print -r "`print -r \\\\\\\\`" +0:Stripping of backslasshes in quoted `...` +>\\ + + print -r "$(print -r \\\\\\\\)" +0:Stripping of backslashes in quoted $(...) +>\\\\ + + fnify() { print \"$*\"; } + print `fnify \`fnify understatement\`` +0:Nested `...` +>""understatement"" + + print $(fnify $(fnify overboard)) +0:Nested $(...) +>""overboard"" + + fructify() { print \'$*\'; } + print "`fructify \`fructify indolence\``" +0:Nested quoted `...` +>''indolence'' + + print "$(fructify $(fructify obtuseness))" +0:Nested quoted $(...) +>''obtuseness'' + + gesticulate() { print \!$*\!; } + print $((gesticulate wildly); gesticulate calmly) +0:$(( ... ) ... ) is not arithmetic +>!wildly! !calmly! + + commencify() { print +$*+; } + print "$((commencify output); commencify input)" +0:quoted $(( ... ) .. ) is not arithmetic +>+output+ +>+input+ + + ( + cd cmdsubst.tmp + print first: ${$(print \*)} + print second: ${~$(print \*)} + print third: ${$(print *)} + print fourth: "${~$(print \*)}" + print fifth: ${~"$(print \*)"} + ) +0:mixing $(...) with parameter substitution and globbing +>first: * +>second: file1.txt file2.txt +>third: file1.txt file2.txt +>fourth: * +>fifth: file1.txt file2.txt + + $(exit 0) $(exit 3) || print $? +0:empty command uses exit value of last substitution +>3 + + X=$(exit 2) $(exit 0) || print $? +0:variable assignments processed after other substitutions +>2 + + false + `` +0:Empty command substitution resets status + + false + echo `echo $?` +0:Non-empty command substitution inherits status +>1 + + echo $(( ##\" )) + echo $(echo \") + echo $((echo \"); echo OK) +0:Handling of backslash double quote in parenthesised substitutions +>34 +>" +>" OK + + echo $(case foo in + foo) + echo This test worked. + ;; + bar) + echo This test failed in a rather bizarre way. + ;; + *) + echo This test failed. + ;; + esac) +0:Parsing of command substitution with unmatched parentheses: case, basic +>This test worked. + + echo "$(case bar in + foo) + echo This test spoobed. + ;; + bar) + echo This test plurbled. + ;; + *) + echo This test bzonked. + ;; + esac)" +0:Parsing of command substitution with unmatched parentheses: case with quotes +>This test plurbled. + + echo before $( + echo start; echo unpretentious | + while read line; do + case $line in + u*) + print Word began with u + print and ended with a crunch + ;; + esac + done | sed -e 's/Word/Universe/'; echo end + ) 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? + + ( + set errexit + show_nargs() { print $#; } + print a $() b + print c "$()" d + ) +0:Empty $() is a valid empty substitution. +>a b +>c d + + empty=$() && print "'$empty'" +0:Empty $() is a valid assignment +>'' diff --git a/dotfiles/system/.zsh/modules/Test/D09brace.ztst b/dotfiles/system/.zsh/modules/Test/D09brace.ztst new file mode 100644 index 0000000..3e667a8 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/D09brace.ztst @@ -0,0 +1,114 @@ +# Tests for brace expansion + +%prep + + foo=(a b c) + arr=(foo bar baz) + +%test + + print X{1,2,{3..6},7,8}Y +0:Basic brace expansion +>X1Y X2Y X3Y X4Y X5Y X6Y X7Y X8Y + + print ${foo}{one,two,three}$arr +0:Brace expansion with arrays, no RC_EXPAND_PARAM +>a b conefoo ctwofoo cthreefoo bar baz + + print ${^foo}{one,two,three}$arr +0:Brace expansion with arrays, with RC_EXPAND_PARAM (1) +>aonefoo atwofoo athreefoo bonefoo btwofoo bthreefoo conefoo ctwofoo cthreefoo bar baz + + print ${foo}{one,two,three}$^arr +0:Brace expansion with arrays, with RC_EXPAND_PARAM (2) +>a b conefoo ctwofoo cthreefoo conebar ctwobar cthreebar conebaz ctwobaz cthreebaz + + print ${^foo}{one,two,three}$^arr +0:Brace expansion with arrays, with RC_EXPAND_PARAM (3) +>aonefoo atwofoo athreefoo aonebar atwobar athreebar aonebaz atwobaz athreebaz bonefoo btwofoo bthreefoo bonebar btwobar bthreebar bonebaz btwobaz bthreebaz conefoo ctwofoo cthreefoo conebar ctwobar cthreebar conebaz ctwobaz cthreebaz + + print X{01..4}Y +0:Numeric range expansion, padding (1) +>X01Y X02Y X03Y X04Y + + print X{1..04}Y +0:Numeric range expansion, padding (2) +>X01Y X02Y X03Y X04Y + + print X{7..12}Y +0:Numeric range expansion, padding (or not) (3) +>X7Y X8Y X9Y X10Y X11Y X12Y + + print X{07..12}Y +0:Numeric range expansion, padding (4) +>X07Y X08Y X09Y X10Y X11Y X12Y + + print X{7..012}Y +0:Numeric range expansion, padding (5) +>X007Y X008Y X009Y X010Y X011Y X012Y + + print X{4..1}Y +0:Numeric range expansion, decreasing +>X4Y X3Y X2Y X1Y + + print X{1..4}{1..4}Y +0:Numeric range expansion, combined braces +>X11Y X12Y X13Y X14Y X21Y X22Y X23Y X24Y X31Y X32Y X33Y X34Y X41Y X42Y X43Y X44Y + + print X{-4..4}Y +0:Numeric range expansion, negative numbers (1) +>X-4Y X-3Y X-2Y X-1Y X0Y X1Y X2Y X3Y X4Y + + print X{4..-4}Y +0:Numeric range expansion, negative numbers (2) +>X4Y X3Y X2Y X1Y X0Y X-1Y X-2Y X-3Y X-4Y + + print X{004..-4..2}Y +0:Numeric range expansion, stepping and padding (1) +>X004Y X002Y X000Y X-02Y X-04Y + + print X{4..-4..02}Y +0:Numeric range expansion, stepping and padding (1) +>X04Y X02Y X00Y X-2Y X-4Y + + print X{1..32..3}Y +0:Numeric range expansion, step alignment (1) +>X1Y X4Y X7Y X10Y X13Y X16Y X19Y X22Y X25Y X28Y X31Y + + print X{1..32..-3}Y +0:Numeric range expansion, step alignment (2) +>X31Y X28Y X25Y X22Y X19Y X16Y X13Y X10Y X7Y X4Y X1Y + + print X{32..1..3}Y +0:Numeric range expansion, step alignment (3) +>X32Y X29Y X26Y X23Y X20Y X17Y X14Y X11Y X8Y X5Y X2Y + + print X{32..1..-3}Y +0:Numeric range expansion, step alignment (4) +>X2Y X5Y X8Y X11Y X14Y X17Y X20Y X23Y X26Y X29Y X32Y + + setopt brace_ccl + print X{za-q521}Y + unsetopt brace_ccl +0:BRACE_CCL on +>X1Y X2Y X5Y XaY XbY XcY XdY XeY XfY XgY XhY XiY XjY XkY XlY XmY XnY XoY XpY XqY XzY + + print X{za-q521}Y +0:BRACE_CCL off +>X{za-q521}Y + + print -r hey{a..j}there +0:{char..char} ranges, simple case +>heyathere heybthere heycthere heydthere heyethere heyfthere heygthere heyhthere heyithere heyjthere + + print -r gosh{1,{Z..a},2}cripes +0:{char..char} ranges, ASCII ordering +>gosh1cripes goshZcripes gosh[cripes gosh\cripes gosh]cripes gosh^cripes gosh_cripes gosh`cripes goshacripes gosh2cripes + + print -r crumbs{y..p}ooh +0:{char..char} ranges, reverse +>crumbsyooh crumbsxooh crumbswooh crumbsvooh crumbsuooh crumbstooh crumbssooh crumbsrooh crumbsqooh crumbspooh + + print -r left{[..]}right +0:{char..char} ranges with tokenized characters +>left[right left\right left]right diff --git a/dotfiles/system/.zsh/modules/Test/E01options.ztst b/dotfiles/system/.zsh/modules/Test/E01options.ztst new file mode 100644 index 0000000..2bd4fdb --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/E01options.ztst @@ -0,0 +1,1313 @@ +# Test various shell options. +# Interactive options not tested here: +# ALWAYS_LAST_PROMPT +# ALWAYS_TO_END +# APPEND_HISTORY (history not maintained) +# AUTO_LIST +# AUTO_MENU +# AUTO_NAME_DIRS (named directory table not maintained) +# AUTO_PARAM_KEYS +# AUTO_PARAM_SLASH +# AUTO_REMOVE_SLASH +# AUTO_RESUME +# BANG_HIST +# BASH_AUTO_LIST +# BEEP (!) +# BG_NICE +# CHECK_JOBS +# COMPLETE_ALIASES +# COMPLETE_IN_WORD +# CORRECT +# CORRECT_ALL +# CSH_JUNKIE_HISTORY +# DVORAK +# EXTENDED_HISTORY +# FLOW_CONTROL +# GLOB_COMPLETE +# HIST_ALLOW_CLOBBER +# HIST_BEEP +# HIST_EXPIRE_DUPS_FIRST +# HIST_FIND_NO_DUPS +# HIST_IGNORE_ALL_DUPS +# HIST_IGNORE_DUPS (-h) +# HIST_IGNORE_SPACE (-g) +# HIST_NO_FUNCTIONS +# HIST_NO_STORE +# HIST_REDUCE_BLANKS +# HIST_SAVE_NO_DUPS +# HIST_VERIFY +# HUP +# IGNORE_EOF +# INC_APPEND_HISTORY +# INTERACTIVE +# INTERACTIVE_COMMENTS +# LIST_AMBIGUOUS +# LIST_BEEP +# LIST_PACKED +# LIST_ROWS_FIRST +# LIST_TYPES +# LOGIN +# LONG_LIST_JOBS +# MAIL_WARNING +# MENU_COMPLETE +# MONITOR +# NOTIFY +# OVERSTRIKE +# PRINT_EIGHT_BIT +# PROMPT_CR +# PUSHD_SILENT +# REC_EXACT +# RM_STAR_SILENT +# RM_STAR_WAIT +# SHARE_HISTORY +# SINGLE_LINE_ZLE +# SUN_KEYBOARD_HACK +# ZLE +# The following require SHINSTDIN and are not (yet) tested: +# AUTO_CD +# SHINSTDIN +# +# Other difficult things I haven't done: +# GLOBAL_RCS (uses fixed files outside build area) +# HASH_CMDS ) +# HASH_DIRS ) fairly seriously internal, hard to test at all +# HASH_LIST_ALL ) +# PRINT_EXIT_STATUS haven't worked out what this does yet, although +# Bart suggested a fix. +# PRIVILEGED (similar to GLOBAL_RCS) +# RCS ( " " " " ) +# SH_OPTION_LETTERS even I found this too dull to set up a test for +# SINGLE_COMMAND kills shell +# VERBOSE hard because done on input (c.f. SHINSTDIN). + +%prep + mkdir options.tmp && cd options.tmp + + mkdir tmpcd homedir + + touch tmpfile1 tmpfile2 + + mydir=$PWD + mydirt=`print -P %~` + mydirhome=`export HOME=$mydir/homedir; print -P %~` + catpath=$(which cat) + lspath==ls + +%test + + alias echo='print foo' + unsetopt aliases + # use eval else aliases are all parsed at start + eval echo bar + setopt aliases + eval echo bar + unalias echo +0:ALIASES option +>bar +>foo bar + + setopt allexport + testpm1=exported + unsetopt allexport + testpm2=unexported + print ${(t)testpm1} + print ${(t)testpm2} +0:ALL_EXPORT option +>scalar-export +>scalar + + # Count the number of directories on the stack. Don't care what they are. + dircount() { dirs -v | tail -1 | awk '{ print $1 + 1}'; } + unsetopt autopushd + cd tmpcd + dircount + cd .. + setopt autopushd + cd tmpcd + dircount + unsetopt autopushd + popd >/dev/null +0:AUTO_PUSHD option +>1 +>2 + + unsetopt badpattern + print [a + setopt badpattern + print [b +1:BAD_PATTERN option +>[a +?(eval):4: bad pattern: [b + + unsetopt bareglobqual nomatch + print *(.) + setopt bareglobqual nomatch + print *(.) +0:BARE_GLOB_QUAL option +>*(.) +>tmpfile1 tmpfile2 + + setopt braceccl + print {abcd} + unsetopt braceccl + print {abcd} +0:BRACE_CCL option +>a b c d +>{abcd} + +# Don't use NUL as a field separator in the following. + setopt braceccl + print {$'\0'-$'\5'} | IFS=' ' read -A chars + for c in $chars; do print $(( #c )); done + unsetopt braceccl +0:BRACE_CCL option starting from NUL +>0 +>1 +>2 +>3 +>4 +>5 + + setopt bsdecho + echo "histon\nimpington" + echo -e "girton\ncottenham" + unsetopt bsdecho + echo "newnham\ncomberton" +0:BSD_ECHO option +>histon\nimpington +>girton +>cottenham +>newnham +>comberton + + unsetopt c_bases + print $(( [#16]15 )) + print $(( [#8]9 )) + setopt c_bases + print $(( [#16]31 )) + print $(( [#8]17 )) + setopt octal_zeroes + print $(( [#8]19 )) + unsetopt c_bases octal_zeroes +0:C_BASES option +>16#F +>8#11 +>0x1F +>8#21 +>023 + + setopt cdablevars + # only absolute paths are eligible for ~-expansion + cdablevar1=tmpcd + (cd cdablevar1) + cdablevar2=$PWD/tmpcd + cd cdablevar2 + cd .. + print back in ${PWD:t} + unsetopt cdablevars + cd cdablevar2 +1q:CDABLE_VARS option +>back in options.tmp +?(eval):cd:4: no such file or directory: cdablevar1 +?(eval):cd:10: no such file or directory: cdablevar2 + +# CHASE_DOTS should go with CHASE_LINKS in B01cd.ztst +# which saves me having to write it here. + + setopt noclobber + rm -f foo1 bar1 rod1 + echo waterbeach >foo1 + (echo landbeach >foo1) + cat foo1 + (echo lode >>bar1) + [[ -f bar1 ]] && print That shouldn\'t be there. + echo denny >rod1 + echo wicken >>rod1 + cat rod1 + unsetopt noclobber + rm -f foo2 bar2 rod2 + echo ely >foo2 + echo march >foo2 + cat foo2 + echo wimpole >>bar2 + cat bar2 + echo royston >rod2 + echo foxton >>rod2 + cat rod2 + rm -f foo* bar* rod* +0:CLOBBER option +>waterbeach +>denny +>wicken +>march +>wimpole +>royston +>foxton +?(eval):4: file exists: foo1 +?(eval):6: no such file or directory: bar1 + + setopt cshjunkieloops + eval 'for f in swaffham bulbeck; print $f; end' + print next one should fail >&2 + unsetopt cshjunkieloops + eval 'for f in chesterton arbury; print $f; end' +1:CSH_JUNKIE_LOOPS option (for loop) +>swaffham +>bulbeck +?next one should fail +?(eval):1: parse error near `end' + +# ` emacs deconfusion + + setopt cshjunkiequotes + print this should cause an error >&2 + eval "print 'line one + line two'" + print this should not >&2 + eval "print 'line three\\ + line four'" + unsetopt cshjunkiequotes +0:CSH_JUNKIE_QUOTES option +>line three +> line four +?this should cause an error +?(eval):1: unmatched ' +?this should not + +# ' emacs deconfusion + + nullcmd() { print '$NULLCMD run'; } + readnullcmd() { print 'Running $READNULLCMD'; cat; } + NULLCMD=nullcmd + READNULLCMD=readnullcmd + setopt cshnullcmd + rm -f foo + print "This should fail" >&2 + (>foo) + print "This should succeed" >&2 + print "These are the contents of foo" >foo + cat foo + print "This should also fail" >&2 + (foo + These are the contents of foo +>Running $READNULLCMD +>$NULLCMD run +?This should fail +?(eval):8: redirection with no command +?This should succeed +?This should also fail +?(eval):13: redirection with no command + +# nomatch should be overridden by cshnullglob + setopt nomatch cshnullglob + print tmp* nothing* blah + print -n 'hoping for no match: ' >&2 + (print nothing* blah) + print >&2 + unsetopt cshnullglob nomatch + print tmp* nothing* blah + print nothing* blah +0:CSH_NULL_GLOB option +>tmpcd tmpfile1 tmpfile2 blah +>tmpcd tmpfile1 tmpfile2 nothing* blah +>nothing* blah +?hoping for no match: (eval):4: no match +? + +# The trick is to avoid =cat being expanded in the output while $catpath is. + setopt NO_equals + print -n trick; print =cat + setopt equals + print -n trick; print =cat +0q:EQUALS option +>trick=cat +>trick$catpath + +# explanation of expected TRAPZERR output: from false and from +# testfn() with ERR_EXIT on (hmm, should we really get a second one from +# the function exiting?), then from the false only with ERR_EXIT off. + TRAPZERR() { print ZERR trapped; } + testfn() { setopt localoptions $2; print $1 before; false; print $1 after; } + (testfn on errexit) + testfn off + unfunction TRAPZERR testfn +0:ERR_EXIT option +>on before +>ZERR trapped +>ZERR trapped +>off before +>ZERR trapped +>off after + + (print before; setopt noexec; print after) +0:NO_EXEC option +>before + + (setopt noexec + typeset -A hash + hash['this is a string']) +0:NO_EXEC option should not attempt to parse subscripts + + (setopt noexec nomatch + echo *NonExistentFile*) +0:NO_EXEC option should not do globbing + + (setopt noexec + 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 + eval 'print $LINENO' +0:EVAL_LINENO option +>2 +>1 + + # The EXTENDED_GLOB test doesn't test globbing fully --- it just tests + # that certain patterns are treated literally with the option off + # and as patterns with the option on. + testfn() { print -n "$1 $2 $3 "; if [[ $1 = ${~2} ]]; + then print yes; else print no; fi; } + tests=('a#' '?~b' '^aa') + strings=('a' 'aa' 'b' 'a#' '?~b' '^aa') + for opt in noextendedglob extendedglob; do + setopt $opt + for test in $tests; do + for string in $strings; do + testfn $string $test $opt + done + done + done +0:EXTENDED_GLOB option +>a a# noextendedglob no +>aa a# noextendedglob no +>b a# noextendedglob no +>a# a# noextendedglob yes +>?~b a# noextendedglob no +>^aa a# noextendedglob no +>a ?~b noextendedglob no +>aa ?~b noextendedglob no +>b ?~b noextendedglob no +>a# ?~b noextendedglob no +>?~b ?~b noextendedglob yes +>^aa ?~b noextendedglob no +>a ^aa noextendedglob no +>aa ^aa noextendedglob no +>b ^aa noextendedglob no +>a# ^aa noextendedglob no +>?~b ^aa noextendedglob no +>^aa ^aa noextendedglob yes +>a a# extendedglob yes +>aa a# extendedglob yes +>b a# extendedglob no +>a# a# extendedglob no +>?~b a# extendedglob no +>^aa a# extendedglob no +>a ?~b extendedglob yes +>aa ?~b extendedglob no +>b ?~b extendedglob no +>a# ?~b extendedglob no +>?~b ?~b extendedglob no +>^aa ?~b extendedglob no +>a ^aa extendedglob yes +>aa ^aa extendedglob no +>b ^aa extendedglob yes +>a# ^aa extendedglob yes +>?~b ^aa extendedglob yes +>^aa ^aa extendedglob yes + + foo() { print My name is $0; } + unsetopt functionargzero + foo + setopt functionargzero + foo + unfunction foo +0:FUNCTION_ARGZERO option +>My name is (anon) +>My name is foo + + setopt _NO_glob_ + print tmp* + set -o glob + print tmp* +0:GLOB option +>tmp* +>tmpcd tmpfile1 tmpfile2 + + showit() { local v; + for v in first second third; do + eval print \$$v \$\{\(t\)$v\} + done; + } + setit() { typeset -x first=inside1; + typeset +g -x second=inside2; + typeset -g -x third=inside3; + showit; + } + first=outside1 second=outside2 third=outside3 + unsetopt globalexport + setit + showit + setopt globalexport + setit + showit + unfunction setit showit +0:GLOBAL_EXPORT option +>inside1 scalar-local-export +>inside2 scalar-local-export +>inside3 scalar-export +>outside1 scalar +>outside2 scalar +>inside3 scalar-export +>inside1 scalar-export +>inside2 scalar-local-export +>inside3 scalar-export +>inside1 scalar-export +>outside2 scalar +>inside3 scalar-export + +# GLOB_ASSIGN is tested in A06assign.ztst. + + mkdir onlysomefiles + touch onlysomefiles/.thisfile onlysomefiles/thatfile + setopt globdots + print onlysomefiles/* + unsetopt globdots + print onlysomefiles/* + rm -rf onlysomefiles +0:GLOB_DOTS option +>onlysomefiles/.thisfile onlysomefiles/thatfile +>onlysomefiles/thatfile + + # we've tested this enough times already... + # could add some stuff for other sorts of expansion + foo='tmp*' + setopt globsubst + print ${foo} + unsetopt globsubst + print ${foo} +0:GLOB_SUBST option +>tmpcd tmpfile1 tmpfile2 +>tmp* + + setopt histsubstpattern + print *(:s/t??/TING/) + foo=(tmp*) + print ${foo:s/??p/THUMP/} + foo=(one.c two.c three.c) + print ${foo:s/#%(#b)t(*).c/T${match[1]}.X/} + print *(#q:s/#(#b)tmp(*e)/'scrunchy${match[1]}'/) + unsetopt histsubstpattern +0:HIST_SUBST_PATTERN option +>TINGcd TINGfile1 TINGfile2 homedir +>THUMPcd THUMPfile1 THUMPfile2 +>one.c Two.X Three.X +>homedir scrunchyfile1 scrunchyfile2 tmpcd + + setopt ignorebraces + echo X{a,b}Y + unsetopt ignorebraces + echo X{a,b}Y +0:IGNORE_BRACES option +>X{a,b}Y +>XaY XbY + + setopt ksh_arrays + array=(one two three) + print $array $array[2] + print ${array[0]} ${array[1]} ${array[2]} ${array[3]} + unsetopt ksh_arrays + print $array $array[2] + print ${array[0]} ${array[1]} ${array[2]} ${array[3]} + unset array +0:KSH_ARRAYS option +>one one[2] +>one two three +>one two three two +>one two three + + fpath=(.) + echo >foo 'echo foo loaded; foo() { echo foo run; }' + echo >bar 'bar() { echo bar run; }' + setopt kshautoload + autoload foo bar + foo + bar + unfunction foo bar + unsetopt kshautoload + autoload foo bar + foo + bar +0:KSH_AUTOLOAD option +>foo loaded +>foo run +>bar run +>foo loaded +>bar run + +# ksh_glob is tested by the glob tests. + + setopt kshoptionprint globassign + print set + setopt | grep kshoptionprint + setopt | grep globassign + unsetopt kshoptionprint + print unset + setopt | grep kshoptionprint + setopt | grep globassign + unsetopt globassign +0:KSH_OPTION_PRINT option +>set +>kshoptionprint on +>globassign on +>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 + print $+ktv1 $+ktv2 $+ktv3 + print $ktfoo + unsetopt kshtypeset + typeset noktfoo=`echo noktarg1 noktarg2` + print $noktfoo + print $+noktarg1 $+noktarg2 + unset ktfoo ktv1 ktv2 noktfoo noktarg2 + ' + ) +0:KSH_TYPESET option +>1 1 0 +>arg1 arg2 +>noktarg1 +>0 1 + + showopt() { setopt | egrep 'localoptions|ksharrays'; } + f1() { setopt localoptions ksharrays; showopt } + f2() { setopt ksharrays; showopt } + setopt kshoptionprint + showopt + f1 + showopt + f2 + showopt + unsetopt ksh_arrays +0:LOCAL_OPTIONS option +>ksharrays off +>localoptions off +>ksharrays on +>localoptions on +>ksharrays off +>localoptions off +>ksharrays on +>localoptions off +>ksharrays on +>localoptions off + +# LOCAL_TRAPS was tested in C03traps (phew). + + fn() { + local HOME=/any/old/name + print -l var=~ 'anything goes/here'=~ split=`echo maybe not`; + } + setopt magicequalsubst + fn + setopt kshtypeset + fn + unsetopt magicequalsubst kshtypeset + fn +0:MAGIC_EQUAL_SUBST option +>var=/any/old/name +>anything goes/here=/any/old/name +>split=maybe +>not +>var=/any/old/name +>anything goes/here=/any/old/name +>split=maybe not +>var=~ +>anything goes/here=~ +>split=maybe +>not + + setopt MARK_DIRS + print tmp* + unsetopt MARK_DIRS + print tmp* +0:MARK_DIRS option +>tmpcd/ tmpfile1 tmpfile2 +>tmpcd tmpfile1 tmpfile2 + +# maybe should be in A04redirect + print "This is in1" >in1 + print "This is in2" >in2 + unsetopt multios + print Test message >foo1 >foo2 + print foo1: $(foo1 >foo2 + sleep 1 # damn, race in multios + print foo1: $(foo1: +>foo2: Test message +>This is in2 +>foo1: Test message +>foo2: Test message +>This is in1 +>This is in2 + +# This is trickier than it looks. There's a hack at the end of +# execcmd() to catch the multio processes attached to the +# subshell, which otherwise sort of get lost in the general turmoil. +# Without that, the multios aren't synchronous with the subshell +# or the main shell starting the "cat", so the output files appear +# empty. + setopt multios + ( echo hello ) >multio_out1 >multio_out2 && cat multio_out* +0:Multios attached to a subshell +>hello +>hello + +# This tests for another race in multios. + print -u $ZTST_fd 'This test hangs the shell when it fails...' + setopt multios + echo These are the contents of the file >multio_race.out + multio_race_fn() { cat; } + multio_race_fn <$(echo multio_race.out multio_race.out) +0:Fix for race with input multios +>These are the contents of the file +>These are the contents of the file + +# tried this with other things, but not on its own, so much. + unsetopt nomatch + print with nonomatch: flooble* + setopt nomatch + print with nomatch flooble* +1:NOMATCH option +>with nonomatch: flooble* +?(eval):4: no matches found: flooble* + +# NULL_GLOB should override NONOMATCH... + setopt nullglob nomatch + print frooble* tmp* + unsetopt nullglob nomatch + print frooble* tmp* +0:NULL_GLOB option +>tmpcd tmpfile1 tmpfile2 +>frooble* tmpcd tmpfile1 tmpfile2 + + touch ngs1.txt ngs2.txt ngs10.txt ngs20.txt ngs100.txt ngs200.txt + setopt numericglobsort + print -l ngs* + unsetopt numericglobsort + print -l ngs* +0:NUMERIC_GLOB_SORT option +>ngs1.txt +>ngs2.txt +>ngs10.txt +>ngs20.txt +>ngs100.txt +>ngs200.txt +>ngs1.txt +>ngs10.txt +>ngs100.txt +>ngs2.txt +>ngs20.txt +>ngs200.txt + + typeset -i 10 oznum + setopt octalzeroes + (( oznum = 012 + 013 )) + print $oznum + unsetopt octalzeroes + (( oznum = 012 + 013 )) + print $oznum + unset oznum +0:OCTAL_ZEROES options +>21 +>25 + + typeset -a oldpath + oldpath=($path) + mkdir pdt_topdir pathtestdir pdt_topdir/pathtestdir + print "#!/bin/sh\necho File in upper dir" >pathtestdir/findme + print "#!/bin/sh\necho File in lower dir" >pdt_topdir/pathtestdir/findme + chmod u+x pathtestdir/findme pdt_topdir/pathtestdir/findme + pathtestdir/findme + rm -f pathtestdir/findme + setopt pathdirs + path=($PWD $PWD/pdt_topdir) + pathtestdir/findme + print unsetting option... + unsetopt pathdirs + pathtestdir/findme + path=($oldpath) + unset oldpath + rm -rf pdt_topdir pathtestdir +0:PATH_DIRS option +>File in upper dir +>File in lower dir +>unsetting option... +?(eval):14: no such file or directory: pathtestdir/findme + + (setopt pathdirs; path+=( /usr/bin ); type ./env) +1:whence honours PATH_DIRS option +>./env not found + + setopt posixbuiltins + PATH= command -v print + PATH= command -V print + PATH= command print foo + unsetopt posixbuiltins + print unsetting... + PATH= command -V print + PATH= command print foo +127:POSIX_BUILTINS option +>print +>print is a shell builtin +>foo +>unsetting... +>print is a shell builtin +?(eval):8: command not found: print + + # With non-special command: original value restored + # With special builtin: new value kept + # With special builtin preceeded by "command": original value restored. + (setopt posixbuiltins + FOO=val0 + FOO=val1 true; echo $FOO + FOO=val2 times 1>/dev/null 2>&1; echo $FOO + FOO=val3 command times 1>/dev/null 2>&1; echo $FOO) +0:POSIX_BUILTINS and restoring variables +>val0 +>val2 +>val2 + +# PRINTEXITVALUE only works if shell input is coming from standard input. +# Goodness only knows why. + $ZTST_testdir/../Src/zsh -f <<<' + setopt printexitvalue + func() { + false + } + func + ' +1:PRINT_EXIT_VALUE option +?zsh: exit 1 + + $ZTST_testdir/../Src/zsh -f <<<' + setopt printexitvalue + () { false; } + ' +1:PRINT_EXIT_VALUE option for anonymous function +?zsh: exit 1 + + setopt promptbang + print -P ! + setopt nopromptbang + print -P ! +0:PROMPT_BANG option +>0 +>! + + unsetopt promptpercent + print -P '%/' + setopt promptpercent + print -P '%/' +0q:PROMPT_PERCENT option +>%/ +>$mydir + + setopt promptsubst + print -P '`echo waaah`' + unsetopt promptsubst + print -P '`echo waaah`' +0:PROMPT_SUBST option +>waaah +>`echo waaah` + + dirs + pushd $mydir/tmpcd + dirs + pushd $mydir/tmpcd + dirs + setopt pushdignoredups + pushd $mydir/tmpcd + dirs + unsetopt pushdignoredups + popd >/dev/null + popd >/dev/null +0q:PUSHD_IGNOREDUPS option +>$mydirt +>$mydirt/tmpcd $mydirt +>$mydirt/tmpcd $mydirt/tmpcd $mydirt +>$mydirt/tmpcd $mydirt/tmpcd $mydirt + + mkdir newcd + cd $mydir + pushd $mydir/tmpcd + pushd $mydir/newcd + dirs + pushd -0 + dirs + setopt pushdminus pushdsilent + pushd -0 + dirs + unsetopt pushdminus + popd >/dev/null + popd >/dev/null + cd $mydir +0q:PUSHD_MINUS option +>$mydirt/newcd $mydirt/tmpcd $mydirt +>$mydirt $mydirt/newcd $mydirt/tmpcd +>$mydirt $mydirt/newcd $mydirt/tmpcd + +# Do you have any idea how dull this is? + + (export HOME=$mydir/homedir + pushd $mydir/tmpcd + pushd + dirs + setopt pushdtohome + pushd + dirs + unsetopt pushdtohome + popd + pushd + popd + dirs) +0q:PUSHD_TO_HOME option +>$mydirhome $mydirhome/tmpcd +>~ $mydirhome $mydirhome/tmpcd +>$mydirhome + + array=(one two three four) + setopt rcexpandparam + print aa${array}bb + unsetopt rcexpandparam + print aa${array}bb +0:RC_EXPAND_PARAM option +>aaonebb aatwobb aathreebb aafourbb +>aaone two three fourbb + + setopt rcquotes + # careful, this is done when parsing a complete block + eval "print 'one''quoted''expression'" + unsetopt rcquotes + eval "print 'another''quoted''expression'" +0:RC_QUOTES option +>one'quoted'expression +>anotherquotedexpression + +# too lazy to test jobs -Z and ARGV0. + (setopt restricted; cd /) + (setopt restricted; PATH=/bin:/usr/bin) + (setopt restricted; /bin/ls) + (setopt restricted; hash ls=/bin/ls) + (setopt restricted; print ha >outputfile) + (setopt restricted; exec ls) + (setopt restricted; unsetopt restricted) + : +0:RESTRICTED option +?(eval):cd:1: restricted +?(eval):2: PATH: restricted +?(eval):3: /bin/ls: restricted +?(eval):hash:4: restricted: /bin/ls +?(eval):5: writing redirection not allowed in restricted mode +?(eval):exec:6: ls: restricted +?(eval):unsetopt:7: can't change option: restricted + +# ' emacs deconfusion + + fn() { + print =ls ={ls,} + local foo='=ls' + print ${~foo} + } + setopt shfileexpansion + fn + unsetopt shfileexpansion + fn +0q:SH_FILE_EXPANSION option +>$lspath =ls = +>=ls +>$lspath $lspath = +>$lspath + + testpat() { + if [[ $1 = ${~2} ]]; then print $1 $2 yes; else print $1 $2 no; fi + } + print option on + setopt shglob + repeat 2; do + for str in 'a(b|c)' ab; do + testpat $str 'a(b|c)' + done + for str in 'a<1-10>' a9; do + testpat $str 'a<1-10>' + done + [[ ! -o shglob ]] && break + print option off + unsetopt shglob + done +0:SH_GLOB option +>option on +>a(b|c) a(b|c) yes +>ab a(b|c) no +>a<1-10> a<1-10> yes +>a9 a<1-10> no +>option off +>a(b|c) a(b|c) no +>ab a(b|c) yes +>a<1-10> a<1-10> no +>a9 a<1-10> yes + + print this is bar >bar + fn() { + local NULLCMD=cat READNULLCMD=cat + { echo hello | >foo } 2>/dev/null + cat foo + option set +>option unset +>hello +>this is bar + + fn() { + eval 'for f in foo bar; print $f' + eval 'for f (word1 word2) print $f' + eval 'repeat 3 print nonsense' + } + unsetopt shortloops + print option unset + fn + setopt shortloops + print option set + fn +0:SHORT_LOOPS option +>option unset +>option set +>foo +>bar +>word1 +>word2 +>nonsense +>nonsense +>nonsense +?(eval):1: parse error near `print' +?(eval):1: parse error near `print' +?(eval):1: parse error near `print' + + fn() { print -l $*; } + setopt shwordsplit + print option set + repeat 2; do + foo='two words' + fn $foo + fn "${=foo}" + [[ ! -o shwordsplit ]] && break + unsetopt shwordsplit + print option unset + done +0:SH_WORD_SPLIT option +>option set +>two +>words +>two +>words +>option unset +>two words +>two +>words + + fn() { unset foo; print value is $foo; } + setopt nounset + print option unset unset by setting nounset + eval fn + print option unset reset + setopt unset + fn +0:UNSET option +>option unset unset by setting nounset +>option unset reset +>value is +?fn: foo: parameter not set + + fn1() { unset foo; print value 1 is ${foo#bar}; } + fn2() { unset foo; print value 2 is ${foo%bar}; } + fn3() { unset foo; print value 3 is ${foo/bar}; } + setopt nounset + print option unset unset by setting nounset + eval fn1 + eval fn2 + eval fn3 + print option unset reset + setopt unset + fn1 + fn2 + fn3 +0:UNSET option with operators +>option unset unset by setting nounset +>option unset reset +>value 1 is +>value 2 is +>value 3 is +?fn1: foo: parameter not set +?fn2: foo: parameter not set +?fn3: foo: parameter not set + + fn() { + emulate -L zsh + setopt warncreateglobal + foo1=bar1 + unset foo1 + foo1=bar2 + local foo2=bar3 + unset foo2 + foo2=bar4 + typeset -g foo3 + foo3=bar5 + fn2() { + foo3=bar6 + } + foo4=bar7 =true + (( foo5=8 )) + integer foo6=9 + (( foo6=10 )) + } + # don't pollute the test environment with the variables... + (fn) +0:WARN_CREATE_GLOBAL option +?fn:3: scalar parameter foo1 created globally in function fn +?fn:5: scalar parameter foo1 created globally in function fn +?fn:15: numeric parameter foo5 created globally in function fn + + fn() { + emulate -L zsh + setopt warncreateglobal + TZ=UTC date >&/dev/null + local um=$(TZ=UTC date 2>/dev/null) + } + fn +0:WARN_CREATE_GLOBAL negative cases + + ( + foo1=global1 foo2=global2 foo3=global3 foo4=global4 + integer foo5=5 + # skip foo6, defined in fn_wnv + foo7=(one two) + fn_wnv() { + # warns + foo1=bar1 + # doesn't warn + local foo2=bar3 + unset foo2 + # still doesn't warn + foo2=bar4 + # doesn't warn + typeset -g foo3=bar5 + # warns + foo3=bar6 + fn2() { + # warns if global option, not attribute + foo3=bar6 + } + fn2 + # doesn't warn + foo4=bar7 =true + # warns + (( foo5=8 )) + integer foo6=9 + # doesn't warn + (( foo6=10 )) + foo7[3]=three + foo7[4]=(four) + } + print option off >&2 + fn_wnv + print option on >&2 + setopt warnnestedvar + fn_wnv + unsetopt warnnestedvar + print function attribute on >&2 + functions -W fn_wnv + fn_wnv + print all off again >&2 + functions +W fn_wnv + fn_wnv + ) +0:WARN_NESTED_VAR option +?option off +?option on +?fn_wnv:2: scalar parameter foo1 set in enclosing scope in function fn_wnv +?fn_wnv:11: scalar parameter foo3 set in enclosing scope in function fn_wnv +?fn2:2: scalar parameter foo3 set in enclosing scope in function fn2 +?fn_wnv:20: numeric parameter foo5 set in enclosing scope in function fn_wnv +?function attribute on +?fn_wnv:2: scalar parameter foo1 set in enclosing scope in function fn_wnv +?fn_wnv:11: scalar parameter foo3 set in enclosing scope in function fn_wnv +?fn_wnv:20: numeric parameter foo5 set in enclosing scope in function fn_wnv +?all off again + + + ( + setopt warnnestedvar + () { + typeset -A a + : ${a[hello world]::=foo} + print ${(t)a} + key="hello world" + print $a[$key] + } + ) +0:No false positive on parameter used with subscripted assignment +>association-local +>foo + + ( + setopt warnnestedvar + () { + local var=(one two) + () { var=three; } + print $var + } + ) +0:Warn when changing type of nested variable: array to scalar. +?(anon): scalar parameter var set in enclosing scope in function (anon) +>three + + ( + setopt warnnestedvar + () { + local var=three + () { var=(one two); } + print $var + } + ) +0:Warn when changing type of nested variable: scalar to array. +?(anon): array parameter var set in enclosing scope in function (anon) +>one two + +# This really just tests if XTRACE is egregiously broken. +# To test it properly would need a full set of its own. + fn() { print message; } + PS4='+%N:%i> ' + setopt xtrace + fn + unsetopt xtrace + fn +0:XTRACE option +>message +>message +?+(eval):4> fn +?+fn:0> print message +?+(eval):5> unsetopt xtrace + + setopt ignoreclosebraces + eval "icb_test() { echo this is OK; }" + icb_test + icb_args() { print $#; } + eval "icb_args { this, is, ok, too }" +0:IGNORE_CLOSE_BRACES option +>this is OK +>6 + + (setopt pipefail + true | true | true + print $? + true | false | true + print $? + exit 2 | false | true + print $? + false | exit 2 | true + print $?) +0:PIPE_FAIL option +>0 +>1 +>1 +>2 + + for (( i = 0; i < 10; i++ )); do + () { + print $i + break + } + done +0:NO_LOCAL_LOOPS +>0 + + () { + emulate -L zsh + setopt localloops + for (( i = 0; i < 10; i++ )); do + () { + setopt nolocalloops # ignored in parent + print $i + break + } + done + } +0:LOCAL_LOOPS +>0 +>1 +>2 +>3 +>4 +>5 +>6 +>7 +>8 +>9 +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope +?(anon):4: `break' active at end of function scope diff --git a/dotfiles/system/.zsh/modules/Test/E02xtrace.ztst b/dotfiles/system/.zsh/modules/Test/E02xtrace.ztst new file mode 100644 index 0000000..da6191c --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/E02xtrace.ztst @@ -0,0 +1,148 @@ +# Test that xtrace output is correctly generated + +%prep + mkdir xtrace.tmp && cd xtrace.tmp + + function xtf { + local regression_test_dummy_variable + print "$*" + } + function xtfx { + local regression_test_dummy_variable + print "Tracing: (){ builtin 2>file }" 2>>xtrace.err + { print "Tracing: (){ { builtin } 2>file }" } 2>>xtrace.err + } + echo 'print "$*"' > xt.in + +%test + + PS4='+%N:%i> ' + set -x + print 'Tracing: builtin' + print 'Tracing: builtin 2>file' 2>xtrace.err + cat <<<'Tracing: external' + cat <<<'Tracing: external 2>file' 2>>xtrace.err + ( print 'Tracing: ( builtin )' ) + ( print 'Tracing: ( builtin ) 2>file' ) 2>>xtrace.err + ( cat <<<'Tracing: ( external )' ) + ( cat <<<'Tracing: ( external ) 2>file' ) 2>>xtrace.err + { print 'Tracing: { builtin }' } + { print 'Tracing: { builtin } 2>file' } 2>>xtrace.err + { cat <<<'Tracing: { external }' } + { cat <<<'Tracing: { external } 2>file' } 2>>xtrace.err + repeat 1 do print 'Tracing: do builtin done'; done + repeat 1 do print 'Tracing: do builtin done 2>file'; done 2>>xtrace.err + repeat 1 do cat <<<'Tracing: do external done'; done + repeat 1 do cat <<<'Tracing: do external done 2>file'; done 2>>xtrace.err + xtf 'Tracing: function' + xtf 'Tracing: function 2>file' 2>>xtrace.err + xtfx + . ./xt.in 'Tracing: source' + . ./xt.in 'Tracing: source 2>file' 2>>xtrace.err + set +x + cat xtrace.err +0:xtrace with and without redirection +>Tracing: builtin +>Tracing: builtin 2>file +>Tracing: external +>Tracing: external 2>file +>Tracing: ( builtin ) +>Tracing: ( builtin ) 2>file +>Tracing: ( external ) +>Tracing: ( external ) 2>file +>Tracing: { builtin } +>Tracing: { builtin } 2>file +>Tracing: { external } +>Tracing: { external } 2>file +>Tracing: do builtin done +>Tracing: do builtin done 2>file +>Tracing: do external done +>Tracing: do external done 2>file +>Tracing: function +>Tracing: function 2>file +>Tracing: (){ builtin 2>file } +>Tracing: (){ { builtin } 2>file } +>Tracing: source +>Tracing: source 2>file +>+(eval):8> print 'Tracing: ( builtin ) 2>file' +>+(eval):10> cat +>+(eval):12> print 'Tracing: { builtin } 2>file' +>+(eval):14> cat +>+(eval):16> print 'Tracing: do builtin done 2>file' +>+(eval):18> cat +>+xtf:1> local regression_test_dummy_variable +>+xtf:2> print 'Tracing: function 2>file' +>+xtfx:3> print 'Tracing: (){ { builtin } 2>file }' +?+(eval):3> print 'Tracing: builtin' +?+(eval):4> print 'Tracing: builtin 2>file' +?+(eval):5> cat +?+(eval):6> cat +?+(eval):7> print 'Tracing: ( builtin )' +?+(eval):9> cat +?+(eval):11> print 'Tracing: { builtin }' +?+(eval):13> cat +?+(eval):15> print 'Tracing: do builtin done' +?+(eval):17> cat +?+(eval):19> xtf 'Tracing: function' +?+xtf:1> local regression_test_dummy_variable +?+xtf:2> print 'Tracing: function' +?+(eval):20> xtf 'Tracing: function 2>file' +?+(eval):21> xtfx +?+xtfx:1> local regression_test_dummy_variable +?+xtfx:2> print 'Tracing: (){ builtin 2>file }' +?+(eval):22> . ./xt.in 'Tracing: source' +?+./xt.in:1> print 'Tracing: source' +?+(eval):23> . ./xt.in 'Tracing: source 2>file' +?+./xt.in:1> print 'Tracing: source 2>file' +?+(eval):24> set +x + + typeset -ft xtf + xtf 'Tracing: function' +0:tracing function +>Tracing: function +?+xtf:1> local regression_test_dummy_variable +?+xtf:2> print 'Tracing: function' + + echo 'PS4="+%x:%I> " + fn() { + print This is fn. + } + : + fn + ' >fnfile + $ZTST_testdir/../Src/zsh -fx ./fnfile 2>errfile + grep '\./fnfile' errfile 1>&2 +0:Trace output with sourcefile and line number. +>This is fn. +?+./fnfile:1> PS4='+%x:%I> ' +?+./fnfile:5> : +?+./fnfile:6> fn +?+./fnfile:3> print This is fn. + + set -x + [[ 'f o' == 'f x'* || 'b r' != 'z o' && 'squashy sound' < 'squishy sound' ]] + [[ 'f o' = 'f x'* || 'b r' != 'z o' && 'squashy sound' < 'squishy sound' ]] + [[ -e nonexistentfile || ( -z '' && -t 3 ) ]] + set +x +0:Trace for conditions +?+(eval):2> [[ 'f o' == f\ x* || 'b r' != z\ o && 'squashy sound' < 'squishy sound' ]] +?+(eval):3> [[ 'f o' = f\ x* || 'b r' != z\ o && 'squashy sound' < 'squishy sound' ]] +?+(eval):4> [[ -e nonexistentfile || -z '' && -t 3 ]] +?+(eval):5> set +x + + # Part 1: Recurses into nested anonymous functions + fn() { + () { () { true } } + } + functions -T fn + fn + # Part 2: Doesn't recurse into named functions + gn() { true } + fn() { gn } + functions -T fn + fn +0:tracing recurses into anonymous functions +?+fn:1> '(anon)' +?+(anon):0> '(anon)' +?+(anon):0> true +?+fn:0> gn diff --git a/dotfiles/system/.zsh/modules/Test/Makefile.in b/dotfiles/system/.zsh/modules/Test/Makefile.in new file mode 100644 index 0000000..083df49 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/Makefile.in @@ -0,0 +1,75 @@ +# +# Makefile for Test subdirectory +# +# Copyright (c) 1999 Peter Stephensons +# All rights reserved. +# +# Permission is hereby granted, without written agreement and without +# license or royalty fees, to use, copy, modify, and distribute this +# software and to distribute modified versions of this software for any +# purpose, provided that the above copyright notice and the following +# two paragraphs appear in all copies of this software. +# +# In no event shall Peter Stephenson or the Zsh Development Group be liable +# to any party for direct, indirect, special, incidental, or consequential +# damages arising out of the use of this software and its documentation, +# even if Peter Stephenson and the Zsh Development Group have been advised of +# the possibility of such damage. +# +# Peter Stephenson and the Zsh Development Group specifically disclaim any +# warranties, including, but not limited to, the implied warranties of +# merchantability and fitness for a particular purpose. The software +# provided hereunder is on an "as is" basis, and Peter Stephenson and the +# Zsh Development Group have no obligation to provide maintenance, +# support, updates, enhancements, or modifications. +# + +subdir = Test +dir_top = .. +SUBDIRS = + +@VERSION_MK@ + +# source/build directories +VPATH = @srcdir@ +sdir = @srcdir@ +sdir_top = @top_srcdir@ +INSTALL = @INSTALL@ + +@DEFS_MK@ + +# ========== DEPENDENCIES FOR TESTING ========== + +check test: + if test -n "$(DLLD)"; then \ + cd $(dir_top) && DESTDIR= \ + $(MAKE) MODDIR=`pwd`/$(subdir)/Modules install.modules > /dev/null; \ + fi + if ZTST_testlist="`for f in $(sdir)/$(TESTNUM)*.ztst; \ + do echo $$f; done`" \ + ZTST_srcdir="$(sdir)" \ + ZTST_exe=$(dir_top)/Src/zsh@EXEEXT@ \ + $(dir_top)/Src/zsh@EXEEXT@ +Z -f $(sdir)/runtests.zsh; then \ + stat=0; \ + else \ + stat=1; \ + fi; \ + sleep 1; \ + rm -rf Modules .zcompdump; \ + exit $$stat + +# ========== DEPENDENCIES FOR CLEANUP ========== + +@CLEAN_MK@ + +mostlyclean-here: + rm -rf Modules .zcompdump *.tmp + +distclean-here: + rm -f Makefile + +realclean-here: + +# ========== DEPENDENCIES FOR MAINTENANCE ========== + +@CONFIG_MK@ diff --git a/dotfiles/system/.zsh/modules/Test/README b/dotfiles/system/.zsh/modules/Test/README new file mode 100644 index 0000000..d012277 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/README @@ -0,0 +1,30 @@ +There are now different sections, expressed by the first letter in the +scripts names: + + A: basic command parsing and execution + B: builtins + C: shell commands with special syntax + D: substititution + E: options + V: modules + W: builtin interactive commands and constructs + X: line editing + Y: completion + Z: separate systems and user contributions + +You will need to run these by using `make test' in the Test subdirectory of +the build area for your system (which may or may not be the same as the +Test subdirectory of the source tree), or the directory above. You can get +more information about the tests being performed with + ZTST_verbose=1 make check +(`test' is equivalent to `check') or change 1 to 2 for even more detail. + +Individual or groups of tests can be performed with + make TESTNUM=C02 check +or + make TESTNUM=C check +to perform just the test beginning C02, or all tests beginning C, +respectively. + +Instructions on how to write tests are given in B01cd.ztst, which acts as a +model. diff --git a/dotfiles/system/.zsh/modules/Test/V02zregexparse.ztst b/dotfiles/system/.zsh/modules/Test/V02zregexparse.ztst new file mode 100644 index 0000000..b4cec42 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V02zregexparse.ztst @@ -0,0 +1,382 @@ +# Tests corresponding to the texinfo node `Conditional Expressions' + +%prep + + if ! zmodload zsh/zutil 2>/dev/null; then + ZTST_unimplemented="can't load the zsh/zutil module for testing" + fi + +%test + + zregexparse p1 p2 '' +0:empty + + zregexparse p1 p2 a /a/ +0:element + + zregexparse p1 p2 aaaaaa /a/ \# +0:closure + + zregexparse p1 p2 ab /a/ /b/ +0:concatenation + + zregexparse p1 p2 a /a/ \| /b/ +0:alternation 1 + + zregexparse p1 p2 b /a/ \| /b/ +0:alternation 2 + + zregexparse p1 p2 a \( /a/ \) +0:grouping + + zregexparse p1 p2 abbaaab \( /a/ \| /b/ \) \# +0:alternation, grouping and closure + + zregexparse p1 p2 abcdef /ab/ %cd% /cdef/ +0:lookahead 1 + + zregexparse p1 p2 abcdef /ab/ %ZZ% /cdef/ +1:lookahead 2 + + zregexparse p1 p2 abcd /ab/ %cd% '-print guard' ':print caction' /cd/ +0:pattern, lookahead, guard and completion action +>guard + + zregexparse p1 p2 abcd /ab/ %cd% '-print guard; false' ':print caction' /cd/ +1:guard failure +>guard +>caction + + zregexparse p1 p2 abcdef /ab/ '{print AB}' /cd/ '{print CD}' /ef/ '{print EF}' +0:action +>AB +>CD +>EF + + zregexparse p1 p2 aaa + print $? $p1 $p2 +0:aaa +>2 0 0 + + zregexparse p1 p2 aaa /a/ + print $? $p1 $p2 +0:aaa /a/ +>2 1 1 + + zregexparse p1 p2 aaa /a/ /a/ + print $? $p1 $p2 +0:aaa 2*/a/ +>2 2 2 + + zregexparse p1 p2 aaa /a/ /a/ /a/ + print $? $p1 $p2 +0:aaa 3*/a/ +>0 3 3 + + zregexparse p1 p2 aaa /a/ /a/ /a/ /a/ + print $? $p1 $p2 +0:aaa 4*/a/ +>1 3 3 + + zregexparse p1 p2 aaa /a/ /a/ /a/ /a/ /a/ + print $? $p1 $p2 +0:aaa 5*/a/ +>1 3 3 + + zregexparse p1 p2 aaa /aaa/ + print $? $p1 $p2 +0:aaa /aaa/ +>0 3 3 + + zregexparse p1 p2 aaa /aaa/ /a/ + print $? $p1 $p2 +0:aaa /aaa/ /a/ +>1 3 3 + + zregexparse p1 p2 aaa /a/ \# + print $? $p1 $p2 +0:aaa /aaa/ # +>0 3 3 + + zregexparse p1 p2 aaa /a/ \# \# + print $? $p1 $p2 +0:aaa /aaa/ # # +>0 3 3 + + zregexparse p1 p2 aaa \( /a/ \) + print $? $p1 $p2 +0:aaa ( /a/ ) +>2 1 1 + + zregexparse p1 p2 aaa \( /a/ \) \# + print $? $p1 $p2 +0:aaa ( /a/ ) # +>0 3 3 + + zregexparse p1 p2 aaa /a/ /b/ + print $? $p1 $p2 +0:aaa /a/ /b/ +>1 1 1 + + zregexparse p1 p2 a /a/ '{print A}' + print $? $p1 $p2 +0:a /a/ '{A}' +>A +>0 1 1 + + zregexparse p1 p2 a /b/ '{print A}' + print $? $p1 $p2 +0:a /b/ '{A}' +>1 0 0 + + zregexparse p1 p2 a /b/ ':print A' '{print B}' + print $? $p1 $p2 +0:a /b/ ':A' '{B}' +>A +>1 0 0 + + zregexparse p1 p2 ab /a/ '{print A}' + print $? $p1 $p2 +0:ab /a/ '{A}' +>2 1 1 + + zregexparse p1 p2 ab /a/ '{print A}' /b/ '{print B}' + print $? $p1 $p2 +0:ab /a/ '{A}' /b/ '{B}' +>A +>B +>0 2 2 + + zregexparse p1 p2 ab /a/ ':print A' '{print B}' /b/ ':print C' '{print D}' + print $? $p1 $p2 +0:ab /a/ ':A' '{B}' /b/ ':C' '{D}' +>B +>D +>0 2 2 + + zregexparse p1 p2 abc /a/ '{print A}' /b/ '{print B}' /c/ '{print C}' + print $? $p1 $p2 +0:abc /a/ '{A}' /b/ '{B}' /c/ '{C}' +>A +>B +>C +>0 3 3 + + zregexparse p1 p2 abz /a/ '{print A}' /b/ '{print B}' /c/ '{print C}' + print $? $p1 $p2 +0:abz /a/ '{A}' /b/ '{B}' /c/ '{C}' +>A +>1 2 2 + + zregexparse p1 p2 azz /a/ '{print A}' /b/ '{print B}' /c/ '{print C}' + print $? $p1 $p2 +0:azz /a/ '{A}' /b/ '{B}' /c/ '{C}' +>1 1 1 + + zregexparse p1 p2 aba '{print A}' /a/ '{print B}' /b/ '{print C}' /c/ '{print D}' + print $? $p1 $p2 +0:aba '{A}' /a/ '{B}' /b/ '{C}' /c/ '{D}' +>A +>B +>1 2 2 + + zregexparse p1 p2 a /a/ '{print "$match[1]"}' + print $? $p1 $p2 +0:a /a/ '{M1}' +>a +>0 1 1 + + zregexparse p1 p2 aaa /a/ '{print A}' // + print $? $p1 $p2 +0:aaa /a/ '{A}' // +>A +>2 1 1 + + zregexparse p1 p2 aaa /a/ '{print "$match[1]"}' // '{print A}' + print $? $p1 $p2 +0:aaa /a/ '{M1}' // '{A}' +>a +>2 1 1 + + zregexparse p1 p2 abcdef /a/ '{print $match[1]}' /b/ '{print $match[1]}' /c/ '{print $match[1]}' // '{print A}' + print $? $p1 $p2 +0:abcdef /a/ '{M1}' /b/ '{M1}' /c/ '{M1}' // '{A}' +>a +>b +>c +>2 3 3 + + zregexparse p1 p2 abcdef /a/ '{print A}' /b/ '{print B}' /c/ '{print C}' // '{print D}' + print $? $p1 $p2 +0:abcdef /a/ '{A}' /b/ '{B}' /c/ '{C}' // '{D}' +>A +>B +>C +>2 3 3 + + zregexparse p1 p2 a /a/ '{print A}' /b/ '{print B}' + print $? $p1 $p2 +0:a /a/ {A} /b/ {B} +>1 1 1 + + zregexparse p1 p2 abcdef \ + /a/ '-print Ga:$p1:$p2:$match[1]' '{print Aa:$p1:$p2:$match[1]}' \ + /b/ '-print Gb:$p1:$p2:$match[1]' '{print Ab:$p1:$p2:$match[1]}' \ + /c/ '-print Gc:$p1:$p2:$match[1]' '{print Ac:$p1:$p2:$match[1]}' \ + // + print $? $p1 $p2 +0:abcdef /a/ -Ga {Aa} /b/ -Gb {Aa} /c/ -Gc {Ac} // +>Ga:0:0:a +>Gb:1:1:b +>Aa:1:1:a +>Gc:2:2:c +>Ab:2:2:b +>Ac:3:3:c +>2 3 3 + + zregexparse p1 p2 abcdef \ + /a/ '-print Ga:$p1:$p2:$match[1]' '{print Aa:$p1:$p2:$match[1]}' \ + /b/ '-print Gb:$p1:$p2:$match[1]' '{print Ab:$p1:$p2:$match[1]}' \ + /c/ '-print Gc:$p1:$p2:$match[1]' '{print Ac:$p1:$p2:$match[1]}' \ + '/[]/' ':print F:$p1:$p2' + print $? $p1 $p2 +0:abcdef /a/ -Ga {Aa} /b/ -Gb {Ab} /c/ -Gc {Ac} /[]/ :F +>Ga:0:0:a +>Gb:1:1:b +>Aa:1:1:a +>Gc:2:2:c +>Ab:2:2:b +>F:3:3 +>1 3 3 + + zregexparse p1 p2 abcdef \ + /a/ '-print Ga:$p1:$p2:$match[1]' '{print Aa:$p1:$p2:$match[1]}' \ + /b/ '-print Gb:$p1:$p2:$match[1]' '{print Ab:$p1:$p2:$match[1]}' \ + /c/ '-print Gc:$p1:$p2:$match[1]' '{print Ac:$p1:$p2:$match[1]}' \ + \( '/[]/' ':print F1:$p1:$p2' \| /z/ ':print F2' \) + print $? $p1 $p2 +0:abcdef /a/ -Ga {Aa} /b/ -Gb {Ab} /c/ -Gc {Ac} ( /[]/ :F1 | /z/ :F2 ) +>Ga:0:0:a +>Gb:1:1:b +>Aa:1:1:a +>Gc:2:2:c +>Ab:2:2:b +>F1:3:3 +>F2 +>1 3 3 + + zregexparse p1 p2 a '/[]/' ':print A' + print $? $p1 $p2 +0:a /[]/ :A +>A +>1 0 0 + + zregexparse p1 p2 $'\0' $'/\0/' '{print A}' + print $? $p1 $p2 +0:"\0" /\0/ {A} +>A +>0 1 1 + + zregexparse p1 p2 $'\0' $'/\0/' '{print A}' '/ /' '{print B}' + print $? $p1 $p2 +0:"\0" /\0/ {A} / / {B} +>1 1 1 + + zregexparse p1 p2 abcdef \( '/?/' '{print $match[1]}' \) \# + print $? $p1 $p2 +0:abcdef ( /?/ {M1} ) # +>a +>b +>c +>d +>e +>f +>0 6 6 + + zregexparse p1 p2 abcdef \( '/c?|?/' '{print $match[1]}' \) \# + print $? $p1 $p2 +0:abcdef ( /c?|?/ {M1} ) # +>a +>b +>cd +>e +>f +>0 6 6 + + zregexparse p1 p2 abcacdef \( /a/ '{print $match[1]}' \| /b/ '{print $match[1]}' \| /c/ '{print $match[1]}' \) \# + print $? $p1 $p2 +0:abcacdef ( /a/ {M1} | /b/ {M1} | /c/ {M1} ) # +>a +>b +>c +>a +>1 5 5 + + zregexparse p1 p2 abcdef \( /a/ ':print A' \| /b/ ':print B' \| /c/ ':print C' \) \# + print $? $p1 $p2 +0:abcdef ( /a/ :A | /b/ :B | /c/ :C ) # +>A +>B +>C +>1 3 3 + + zregexparse p1 p2 abcdef \( /a/ ':print A' '{print $match[1]}' \| /b/ ':print B' '{print $match[1]}' \| /c/ ':print C' '{print $match[1]}' \) \# + print $? $p1 $p2 +0:abcdef ( /a/ :A {M1} | /b/ :B {M1} | /c/ :C {M1} ) # +>a +>b +>A +>B +>C +>1 3 3 + + zregexparse p1 p2 $'com\0xx' /$'[^\0]#\0'/ \( /$'[^\0]#\0'/ :'print A' /$'[^\0]#\0'/ :'print B' \) \# + print $? $p1 $p2 +0:"com\0xx" /W/ ( /W/ :A /W/ :B ) # +>A +>1 4 4 + + zregexparse p1 p2 $'com\0xx\0yy' /$'[^\0]#\0'/ \( /$'[^\0]#\0'/ :'print A' /$'[^\0]#\0'/ :'print B' \) \# + print $? $p1 $p2 +0:"com\0xx\0yy" /W/ ( /W/ :A /W/ :B ) # +>B +>1 7 7 + + zregexparse p1 p2 $'com\0xx\0yy\0zz' /$'[^\0]#\0'/ \( /$'[^\0]#\0'/ :'print A' /$'[^\0]#\0'/ :'print B' \) \# + print $? $p1 $p2 +0:"com\0xx\0yy\0zz" /W/ ( /W/ :A /W/ :B ) # +>A +>1 10 10 + + zregexparse p1 p2 abcdez /abc/ ':print A:$p1:$p2' /def/ ':print B:$p1:$p2' + print $? $p1 $p2 +0:abcdez /abc/ :A /def/ :B +>B:3:3 +>1 3 3 + + zregexparse p1 p2 abcdez /abc/+ ':print A:$p1:$p2' /def/ ':print B:$p1:$p2' + print $? $p1 $p2 +0:abcdez /abc/+ :A /def/ :B +>A:0:3 +>B:0:3 +>1 0 3 + + zregexparse p1 p2 abcdez /abc/+ ':print A:$p1:$p2' // /def/ ':print B:$p1:$p2' + print $? $p1 $p2 +0:abcdez /abc/+ :A // /def/ :B +>A:0:3 +>B:0:3 +>1 0 3 + + zregexparse p1 p2 abcdez /abc/+ ':print A:$p1:$p2' //- /def/ ':print B:$p1:$p2' + print $? $p1 $p2 +0:abcdez /abc/+ :A //- /def/ :B +>B:3:3 +>1 3 3 + + zregexparse p1 p2 $'ZZZZ\0abcdef' $'/ZZZZ\0/' /abc/+ ':print A:$p1:$p2' /dee/ ':print B:$p1:$p2' + print $? $p1 $p2 +0:"ZZZZ\0abcdef" /ZZZZ\0/ /abc/+ :A /dee/ :B +>A:5:8 +>B:5:8 +>1 5 8 diff --git a/dotfiles/system/.zsh/modules/Test/V03mathfunc.ztst b/dotfiles/system/.zsh/modules/Test/V03mathfunc.ztst new file mode 100644 index 0000000..1edb7a2 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V03mathfunc.ztst @@ -0,0 +1,141 @@ +# Tests for the module zsh/mathfunc + +%prep + if ! zmodload zsh/mathfunc 2>/dev/null; then + ZTST_unimplemented="The module zsh/mathfunc is not available." + fi + +%test + # -g makes pi available in later tests + float -gF 5 pi + (( pi = 4 * atan(1.0) )) + print $pi +0:Basic operation with atan +>3.14159 + + float -F 5 result + (( result = atan(3,2) )) + print $result +0:atan with two arguments +>0.98279 + + print $(( atan(1,2,3) )) +1:atan can't take three arguments +?(eval):1: wrong number of arguments: atan(1,2,3) + + float r1=$(( rand48() )) + float r2=$(( rand48() )) + float r3=$(( rand48() )) + # Yes, this is a floating point equality test like they tell + # you not to do. As the pseudrandom sequence is deterministic, + # this is the right thing to do in this case. + if (( r1 == r2 )); then + print "Seed not updated correctly the first time" + else + print "First two random numbers differ, OK" + fi + if (( r2 == r3 )); then + print "Seed not updated correctly the second time" + else + print "Second two random numbers differ, OK" + fi +0:rand48 with default initialisation +F:This test fails if your math library doesn't have erand48(). +>First two random numbers differ, OK +>Second two random numbers differ, OK + + seed=f45677a6cbe4 + float r1=$(( rand48(seed) )) + float r2=$(( rand48(seed) )) + seed2=$seed + float r3=$(( rand48(seed) )) + float r4=$(( rand48(seed2) )) + # Yes, this is a floating point equality test like they tell + # you not to do. As the pseudrandom sequence is deterministic, + # this is the right thing to do in this case. + if (( r1 == r2 )); then + print "Seed not updated correctly the first time" + else + print "First two random numbers differ, OK" + fi + if (( r2 == r3 )); then + print "Seed not updated correctly the second time" + else + print "Second two random numbers differ, OK" + fi + if (( r3 == r4 )); then + print "Identical seeds generate identical numbers, OK" + else + print "Indeterminate result from identical seeds" + fi +0:rand48 with pre-generated seed +F:This test fails if your math library doesn't have erand48(). +>First two random numbers differ, OK +>Second two random numbers differ, OK +>Identical seeds generate identical numbers, OK + + float -F 5 pitest + (( pitest = 4.0 * atan(1) )) + # This is a string test of the output to 5 digits. + if [[ $pi = $pitest ]]; then + print "OK, atan on an integer seemed to work" + else + print "BAD: got $pitest instead of $pi" + fi +0:Conversion of arguments from integer +>OK, atan on an integer seemed to work + + float -F 5 result + typeset str + for str in 0 0.0 1 1.5 -1 -1.5; do + (( result = abs($str) )) + print $result + done +0:Use of abs on various numbers +>0.00000 +>0.00000 +>1.00000 +>1.50000 +>1.00000 +>1.50000 + + print $(( sqrt(-1) )) +1:Non-negative argument checking for square roots. +?(eval):1: math: argument to sqrt out of range + +# Simple test that the pseudorandom number generators are producing +# something that could conceivably be pseudorandom numbers in a +# linear range. Not a detailed quantitative verification. + integer N=10000 isource ok=1 + float -F f sum sumsq max max2 av sd + typeset -a randoms + randoms=('f = RANDOM' 'f = rand48()') + for isource in 1 2; do + (( sum = sumsq = max = 0 )) + repeat $N; do + let $randoms[$isource] + (( f > max )) && (( max = f )) + (( sum += f, sumsq += f * f )) + done + (( av = sum / N )) + (( sd = sqrt((sumsq - N * av * av) / (N-1)) )) + (( max2 = 0.5 * max )) + if (( av > max2 * 1.1 )) || (( av < max2 * 0.9 )); then + print "WARNING: average of random numbers is suspicious. + Was testing: $randoms[$isource]" + (( ok = 0 )) + fi + if (( sd < max / 4 )); then + print "WARNING: distribution of random numbers is suspicious. + Was testing: $randoms[$isource]" + (( ok = 0 )) + fi + done + (( ok )) +0:Test random number generator distributions are not grossly broken + + float -F 5 g l + (( g = gamma(2), l = lgamma(2) )) + print $g, $l +0:Test Gamma function gamma and lgamma +>1.00000, 0.00000 diff --git a/dotfiles/system/.zsh/modules/Test/V04features.ztst b/dotfiles/system/.zsh/modules/Test/V04features.ztst new file mode 100644 index 0000000..6939053 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V04features.ztst @@ -0,0 +1,172 @@ +%prep + +# Do some tests on handling of features. +# This also does some slightly more sophisticated loading and +# unloading tests than we did in V01zmodload.ztst. +# +# We use zsh/datetime because it has a list of features that is short +# but contains two types. + + # Subshell for prep test so we can load individual features later + if ! (zmodload zsh/datetime 2>/dev/null); then + ZTST_unimplemented="can't load the zsh/datetime module for testing" + fi + +%test + zmodload -F zsh/datetime + zmodload -lF zsh/datetime +0:Loading modules with no features +>-b:strftime +>-p:EPOCHSECONDS +>-p:EPOCHREALTIME +>-p:epochtime + + zmodload -F zsh/datetime b:strftime + zmodload -lF zsh/datetime +0:Enabling features +>+b:strftime +>-p:EPOCHSECONDS +>-p:EPOCHREALTIME +>-p:epochtime + + zmodload -F zsh/datetime +p:EPOCHSECONDS -b:strftime + zmodload -lF zsh/datetime +0:Disabling features +>-b:strftime +>+p:EPOCHSECONDS +>-p:EPOCHREALTIME +>-p:epochtime + + zmodload -Fe zsh/datetime p:EPOCHSECONDS b:strftime +0:Testing existing features + + zmodload -Fe zsh/datetime +p:EPOCHSECONDS +0:Testing features are in given state (on feature is on) + + zmodload -Fe zsh/datetime -p:EPOCHSECONDS +1:Testing features are in given state (on feature is not off + + zmodload -Fe zsh/datetime +p:strftime +1:Testing features are in given state (off feature is not on) + + zmodload -Fe zsh/datetime -b:strftime +0:Testing features are in given state (off feature is off + + zmodload -Fe zsh/datetime p:EPOCHSECONDS b:strftime b:mktimebetter +1:Testing non-existent features + + zmodload -FlP dtf zsh/datetime + for feature in b:strftime p:EPOCHSECONDS; do + if [[ ${${dtf[(R)?$feature]}[1]} = + ]]; then + print $feature is enabled + else + print $feature is disabled + fi + done +0:Testing features via array parameter +>b:strftime is disabled +>p:EPOCHSECONDS is enabled + + fn() { + local EPOCHSECONDS=scruts + print $EPOCHSECONDS + print ${(t)EPOCHSECONDS} + } + fn + if [[ $EPOCHSECONDS = <-> ]]; then + print EPOCHSECONDS is a number + else + print EPOCHSECONDS is some random piece of junk + fi + print ${(t)EPOCHSECONDS} +0:Module special parameter is hidden by a local parameter +>scruts +>scalar-local +>EPOCHSECONDS is a number +>integer-readonly-hide-hideval-special + + typeset +h EPOCHSECONDS + fn() { + local EPOCHSECONDS=scruts + print Didn\'t get here >&2 + } + fn +1:Unhidden readonly special can't be assigned to when made local +?fn:1: read-only variable: EPOCHSECONDS + + zmodload -u zsh/datetime +0:Module unloaded + + zmodload -e zsh/datetime +1:Module doesn't exist when unloaded + + zmodload -Fe zsh/datetime p:EPOCHSECONDS +1:Module doesn't have features when unloaded + + fn() { + local EPOCHSECONDS=scrimf + zmodload zsh/datetime + } + fn +2:Failed to add parameter if local parameter present +?fn:2: Can't add module parameter `EPOCHSECONDS': local parameter exists +?fn:zsh/datetime:2: error when adding parameter `EPOCHSECONDS' + + zmodload -lF zsh/datetime +0:Feature state with loading after error enabling +>+b:strftime +>-p:EPOCHSECONDS +>+p:EPOCHREALTIME +>+p:epochtime + + zmodload -F zsh/datetime p:EPOCHSECONDS + zmodload -Fe zsh/datetime +p:EPOCHSECONDS +0:Successfully added feature parameter that previously failed + + fn() { + local EPOCHSECONDS=scrooble + zmodload -u zsh/datetime + print $EPOCHSECONDS + } + fn + print ${+EPOCHSECONDS} +0:Successfully unloaded a module despite a parameter being hidden +>scrooble +>0 + + EPOCHSECONDS=(any old parameter) + print -l $EPOCHSECONDS +0:Using parameter as normal after unloading is OK +>any +>old +>parameter + + print strftime is ${builtins[strftime]:-undefined} + zmodload -F zsh/datetime b:strftime + print strftime is ${builtins[strftime]:-undefined} + zmodload -F zsh/datetime -b:strftime + print strftime is ${builtins[strftime]:-undefined} +0:Enabling and disabling of builtins as features +>strftime is undefined +>strftime is defined +>strftime is undefined + + zmodload -u zsh/datetime + zmodload zsh/datetime +2:Loading won't override global parameter +?(eval):2: Can't add module parameter `EPOCHSECONDS': parameter already exists +?(eval):zsh/datetime:2: error when adding parameter `EPOCHSECONDS' + + unset EPOCHSECONDS + zmodload -F zsh/datetime p:EPOCHSECONDS + zmodload -Fe zsh/datetime +p:EPOCHSECONDS +0:unsetting a global parameter allows feature parameter to be enabled + + zmodload -F zsh/datetime -b:strftime -p:EPOCHSECONDS + zmodload zsh/datetime + zmodload -lF zsh/datetime +0:zmodload with no -F enables all features +>+b:strftime +>+p:EPOCHSECONDS +>+p:EPOCHREALTIME +>+p:epochtime diff --git a/dotfiles/system/.zsh/modules/Test/V05styles.ztst b/dotfiles/system/.zsh/modules/Test/V05styles.ztst new file mode 100644 index 0000000..ca95b63 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V05styles.ztst @@ -0,0 +1,143 @@ +%prep + +# Test the use of styles, if the zsh/zutil module is available. + + if ! zmodload zsh/zutil 2>/dev/null; then + ZTST_unimplemented="can't load the zsh/zutil module for testing" + fi + +%test + zstyle :random:stuff any-old-style with any old value + zstyle :randomly:chosen some-other-style I can go on and on + zstyle -d + zstyle +0:zstyle -d restores a pristine state + +# patterns should be ordered by weight, so add in reverse order to check + zstyle ':ztst:context*' scalar-style other-scalar-value + zstyle ':ztst:context:*' scalar-style second-scalar-value + zstyle ':ztst:context:sub1' scalar-style scalar-value + zstyle ':ztst:context:sub1' array-style array value elements 'with spaces' + zstyle ':ztst:context*' boolean-style false + zstyle ':ztst:context:sub1' boolean-style true +0:defining styles + +# styles are now sorted, but patterns are in order of definition + zstyle +0:listing styles in default format +>array-style +> :ztst:context:sub1 array value elements 'with spaces' +>boolean-style +> :ztst:context:sub1 true +> :ztst:context* false +>scalar-style +> :ztst:context:sub1 scalar-value +> :ztst:context:* second-scalar-value +> :ztst:context* other-scalar-value + + zstyle -L +0:listing styles in zstyle format +>zstyle :ztst:context:sub1 array-style array value elements 'with spaces' +>zstyle :ztst:context:sub1 boolean-style true +>zstyle ':ztst:context*' boolean-style false +>zstyle :ztst:context:sub1 scalar-style scalar-value +>zstyle ':ztst:context:*' scalar-style second-scalar-value +>zstyle ':ztst:context*' scalar-style other-scalar-value + + zstyle -b :ztst:context:sub1 boolean-style bool; print $bool + zstyle -t :ztst:context:sub1 boolean-style +0:boolean test -b/-t + true +>yes + + zstyle -b :ztst:context:sub2 boolean-style bool; print $bool + zstyle -t :ztst:context:sub2 boolean-style +1:boolean test -b/-t + false +>no + + zstyle -b :ztst:context:sub1 boolean-unset-style bool; print $bool + zstyle -t :ztst:context:sub1 boolean-unset-style +2:boolean test -b/-t + unset +>no + + zstyle -T :ztst:context:sub1 boolean-style +0:boolean test -T + true + + zstyle -T :ztst:context:sub2 boolean-style +1:boolean test -T + false + + zstyle -T :ztst:context:sub1 boolean-unset-style +0:boolean test -T + unset + + zstyle -s :ztst:context:sub1 scalar-style scalar && print $scalar + zstyle -s :ztst:context:sub2 scalar-style scalar && print $scalar + zstyle -s :ztst:contextual-psychedelia scalar-style scalar && print $scalar + zstyle -s :ztst:contemplative scalar-style scalar || print no match +0:pattern matching rules +>scalar-value +>second-scalar-value +>other-scalar-value +>no match + + zstyle -s :ztst:context:sub1 array-style scalar + && print $scalar +0:scalar with separator +>array+value+elements+with spaces + + zstyle -e :ztst:\* eval-style 'reply=($something)' + something=(one two three) + zstyle -a :ztst:eval eval-style array && print -l $array +0:zstyle -e evaluations +>one +>two +>three + +# pattern ordering on output is not specified, so although in the +# current implementation it's deterministic we shouldn't +# assume it's always the same. Thus we sort the array. +# (It might be a nice touch to order patterns by weight, which is +# the way they are stored for each separate style.) + zstyle -g array && print -l ${(o)array} +0:retrieving patterns +>:ztst:* +>:ztst:context* +>:ztst:context:* +>:ztst:context:sub1 + + zstyle -m :ztst:context:sub1 array-style 'w* *s' +0:positive pattern match + + zstyle -m :ztst:context:sub1 array-style 'v' +1:negative pattern match + + zstyle -g array ':ztst:context*' && print -l $array +0:retrieving styles by pattern +>boolean-style +>scalar-style + + zstyle -g array ':ztst:context:sub1' array-style && print -l $array +0:retrieving values by pattern and name +>array +>value +>elements +>with spaces + + zstyle -d :ztst:context:sub1 + zstyle +0:deleting styles by pattern only +>boolean-style +> :ztst:context* false +>eval-style +>(eval) :ztst:* 'reply=($something)' +>scalar-style +> :ztst:context:* second-scalar-value +> :ztst:context* other-scalar-value + + zstyle -d :ztst:context\* scalar-style + zstyle +0:deleting styles by pattern and style name +>boolean-style +> :ztst:context* false +>eval-style +>(eval) :ztst:* 'reply=($something)' +>scalar-style +> :ztst:context:* second-scalar-value + diff --git a/dotfiles/system/.zsh/modules/Test/V07pcre.ztst b/dotfiles/system/.zsh/modules/Test/V07pcre.ztst new file mode 100644 index 0000000..ad17707 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V07pcre.ztst @@ -0,0 +1,139 @@ +%prep + + if ! zmodload -F zsh/pcre C:pcre-match 2>/dev/null + then + ZTST_unimplemented="the zsh/pcre module is not available" + return 0 + fi +# Load the rest of the builtins + zmodload zsh/pcre + setopt rematch_pcre +# Find a UTF-8 locale. + setopt multibyte +# Don't let LC_* override our choice of locale. + unset -m LC_\* + mb_ok= + langs=(en_{US,GB}.{UTF-,utf}8 en.UTF-8 + $(locale -a 2>/dev/null | egrep 'utf8|UTF-8')) + for LANG in $langs; do + if [[ รฉ = ? ]]; then + mb_ok=1 + break; + fi + done + if [[ -z $mb_ok ]]; then + ZTST_unimplemented="no UTF-8 locale or multibyte mode is not implemented" + else + print -u $ZTST_fd Testing PCRE multibyte with locale $LANG + mkdir multibyte.tmp && cd multibyte.tmp + fi + +%test + + [[ 'fooโ†’bar' =~ .([^[:ascii:]]). ]] + print $MATCH + print $match[1] +0:Basic non-ASCII regexp matching +>oโ†’b +>โ†’ + + unset match mend + s=$'\u00a0' + [[ $s =~ '^.$' ]] && print OK + [[ A${s}B =~ .(.). && $match[1] == $s ]] && print OK + [[ A${s}${s}B =~ A([^[:ascii:]]*)B && $mend[1] == 3 ]] && print OK + unset s +0:Raw IMETA characters in input string +>OK +>OK +>OK + + [[ foo =~ f.+ ]] ; print $? + [[ foo =~ x.+ ]] ; print $? + [[ ! foo =~ f.+ ]] ; print $? + [[ ! foo =~ x.+ ]] ; print $? + [[ foo =~ f.+ && bar =~ b.+ ]] ; print $? + [[ foo =~ x.+ && bar =~ b.+ ]] ; print $? + [[ foo =~ f.+ && bar =~ x.+ ]] ; print $? + [[ ! foo =~ f.+ && bar =~ b.+ ]] ; print $? + [[ foo =~ f.+ && ! bar =~ b.+ ]] ; print $? + [[ ! ( foo =~ f.+ && bar =~ b.+ ) ]] ; print $? + [[ ! foo =~ x.+ && bar =~ b.+ ]] ; print $? + [[ foo =~ x.+ && ! bar =~ b.+ ]] ; print $? + [[ ! ( foo =~ x.+ && bar =~ b.+ ) ]] ; print $? +0:Regex result inversion detection +>0 +>1 +>1 +>0 +>0 +>1 +>1 +>1 +>1 +>1 +>0 +>1 +>0 + +# Note that PCRE_ANCHORED only means anchored at the start +# Also note that we don't unset MATCH/match on failed match (and it's an +# open issue as to whether or not we should) + pcre_compile '.(โ†’.)' + pcre_match fooโ†’bar + print $? $MATCH $match ; unset MATCH match + pcre_match foo.bar + print $? $MATCH $match ; unset MATCH match + pcre_match fooโ€ bar + print $? $MATCH $match ; unset MATCH match + pcre_match fooโ†’โ€ ar + print $? $MATCH $match ; unset MATCH match + pcre_study + pcre_match fooโ†’bar + print $? $MATCH $match ; unset MATCH match + pcre_compile -a '.(โ†’.)' + pcre_match fooโ†’bar + print $? $MATCH $match ; unset MATCH match + pcre_match oโ†’bar + print $? $MATCH $match ; unset MATCH match + pcre_match oโ†’b + print $? $MATCH $match ; unset MATCH match + pcre_compile 'x.(โ†’.)' + pcre_match xoโ†’t + print $? $MATCH $match ; unset MATCH match + pcre_match Xoโ†’t + print $? $MATCH $match ; unset MATCH match + pcre_compile -i 'x.(โ†’.)' + pcre_match xoโ†’t + print $? $MATCH $match ; unset MATCH match + pcre_match Xoโ†’t + print $? $MATCH $match ; unset MATCH match +0:pcre_compile interface testing: basic, anchored & case-insensitive +>0 oโ†’b โ†’b +>1 +>1 +>0 oโ†’โ€  โ†’โ€  +>0 oโ†’b โ†’b +>1 +>0 oโ†’b โ†’b +>0 oโ†’b โ†’b +>0 xoโ†’t โ†’t +>1 +>0 xoโ†’t โ†’t +>0 Xoโ†’t โ†’t + + string="The following zip codes: 78884 90210 99513" + pcre_compile -m "\d{5}" + pcre_match -b -- $string && print "$MATCH; ZPCRE_OP: $ZPCRE_OP" + pcre_match -b -n $ZPCRE_OP[(w)2] -- $string || print failed + print "$MATCH; ZPCRE_OP: $ZPCRE_OP" +0:pcre_match -b and pcre_match -n +>78884; ZPCRE_OP: 25 30 +>90210; ZPCRE_OP: 31 36 + +# Subshell because crash on failure + ( setopt re_match_pcre + [[ test.txt =~ '^(.*_)?(test)' ]] + echo $match[2] ) +0:regression for segmentation fault, workers/38307 +>test diff --git a/dotfiles/system/.zsh/modules/Test/V08zpty.ztst b/dotfiles/system/.zsh/modules/Test/V08zpty.ztst new file mode 100644 index 0000000..b0cbfa0 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V08zpty.ztst @@ -0,0 +1,29 @@ +# zpty is required by tests of interactive modes of the shell itself. +# This tests some extra things. + +%prep + + if ! zmodload zsh/zpty 2>/dev/null + then + ZTST_unimplemented="the zsh/zpty module is not available" + elif [[ $OSTYPE = cygwin ]]; then + ZTST_unimplemented="the zsh/zpty module does not work on Cygwin" + fi + +%test + + zpty cat cat + zpty -w cat a line of text + var= + zpty -r cat var && print -r -- ${var%%$'\r\n'} + zpty -d cat +0:zpty with a process that does not set up the terminal: internal write +>a line of text + + zpty cat cat + print a line of text | zpty -w cat + var= + zpty -r cat var && print -r -- ${var%%$'\r\n'} + zpty -d cat +0:zpty with a process that does not set up the terminal: write via stdin +>a line of text diff --git a/dotfiles/system/.zsh/modules/Test/V09datetime.ztst b/dotfiles/system/.zsh/modules/Test/V09datetime.ztst new file mode 100644 index 0000000..7905155 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V09datetime.ztst @@ -0,0 +1,74 @@ +%prep + + if zmodload zsh/datetime 2>/dev/null; then + setopt multibyte + unset LC_ALL + LC_TIME=C + TZ=UTC+0 + # It's not clear this skip_extensions is correct, but the + # format in question is causing problems on Solaris. + # We'll revist this after the release. + [[ "$(strftime %^_10B 0)" = " JANUARY" ]] || skip_extensions=1 + [[ "$(LC_TIME=ja_JP.UTF-8 strftime %OS 1)" = ไธ€ ]] || skip_japanese=1 + else + ZTST_unimplemented="can't load the zsh/datetime module for testing" + fi + +%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 -r -- ${(V)"$(strftime $'%Y\0%m\0%d' 100000000)"} +0:Embedded nulls +>1973^@03^@03 diff --git a/dotfiles/system/.zsh/modules/Test/V10private.ztst b/dotfiles/system/.zsh/modules/Test/V10private.ztst new file mode 100644 index 0000000..78ecd48 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/V10private.ztst @@ -0,0 +1,304 @@ +# Tests for the zsh/param/private module + +%prep + + if ! zmodload zsh/param/private 2>/dev/null; then + ZTST_unimplemented="can't load the zsh/param/private module for testing" + else + # Do not use .tmp here, ztst.zsh will remove it too soon (see %cleanup) + mkdir private.TMP + sed -e 's,# test_zsh_param_private,zmodload zsh/param/private,' < $ZTST_srcdir/B02typeset.ztst > private.TMP/B02 + fi + +%test + + (zmodload -u zsh/param/private && zmodload zsh/param/private) +0:unload and reload the module without crashing + + typeset scalar_test=toplevel + () { + print $scalar_test + private scalar_test + print $+scalar_test + unset scalar_test + print $+scalar_test + } + print $scalar_test +0:basic scope hiding +>toplevel +>1 +>0 +>toplevel + + typeset scalar_test=toplevel + print $scalar_test + () { + private scalar_test=function + print $scalar_test + } + print $scalar_test +0:enter and exit a scope +>toplevel +>function +>toplevel + + print $+unset_test + () { + private unset_test + print $+unset_test + unset_test=setme + print $unset_test + } + print $+unset_test +0:variable defined only in scope +>0 +>1 +>setme +>0 + + # Depends on zsh-5.0.9 typeset keyword + typeset -a array_test=(top level) + () { + local -Pa array_test=(in function) + () { + private array_test + print $+array_test + } + print $array_test + } + print $array_test +0:nested scope with different type, correctly restored +>1 +>in function +>top level + + typeset -a array_test=(top level) + () { + private array_test + array_test=(in function) + } +1:type of private may not be changed by assignment +?(anon):2: array_test: attempt to assign array value to non-array + + typeset -A hash_test=(top level) + () { + setopt localoptions noglob + private hash_test[top] + } +1:associative array fields may not be private +?(anon):private:2: hash_test[top]: can't create local array elements + + () { + private path + } +1:tied params may not be private, part 1 +?(anon):private:1: can't change scope of existing param: path + + () { + private PATH + } +1:tied params may not be private, part 2 +?(anon):private:1: can't change scope of existing param: PATH + + () { + private -h path + print X$path + } +0:privates may hide tied paramters +>X + + # Deliberate type mismatch here + typeset -a hash_test=(top level) + typeset -p hash_test + inner () { + private -p hash_test + print ${(t)hash_test} ${(kv)hash_test} + } + outer () { + local -PA hash_test=(in function) + typeset -p hash_test + inner + } + outer + print ${(kv)hash_test} +0:private hides value from surrounding scope in nested scope +>typeset -a hash_test=( top level ) +>typeset -A hash_test=( in function ) +>typeset -g -a hash_test=( top level ) +>array-local top level +>top level +F:note "typeset" rather than "private" in output from outer + + () { + private -a array_test + local array_test=scalar + } +1:private cannot be re-declared as local +?(anon):local:2: array_test: inconsistent type for assignment + + () { + local hash_test=scalar + private -A hash_test + } +1:local cannot be re-declared as private +?(anon):private:2: can't change scope of existing param: hash_test + + inner () { + print $+scalar_test + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + } + () { + private -x scalar_test=whaat + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + inner + print Y $scalar_test + } +0:exported private behaves like a local, part 1 +>X whaat +>0 +>X whaat +>Y whaat + + inner () { + typeset -p array_test + $ZTST_testdir/../Src/zsh -fc 'print X $array_test' + } + () { + local -Pax array_test=(whaat) + print Y $array_test + $ZTST_testdir/../Src/zsh -fc 'print X $array_test' + inner + } +0:exported private behaves like a local, part 2 (arrays do not export) +?inner:typeset:1: no such variable: array_test +>Y whaat +>X +>X + + inner () { + print $+scalar_test + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + } + () { + private scalar_test=whaat + export scalar_test + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + inner + () { + print $+scalar_test + $ZTST_testdir/../Src/zsh -fc 'print X $scalar_test' + } + print Y $scalar_test + } +0:exported private behaves like a local, part 3 (export does not change scope) +>X whaat +>0 +>X whaat +>0 +>X whaat +>Y whaat + + typeset -A hash_test=(top level) + () { + local -PA hash_test=(in function) + () { + print X ${(kv)hash_test} + } + print Y ${(kv)hash_test} + } + print ${(kv)hash_test} +0:privates are not visible in anonymous functions, part 1 +>X top level +>Y in function +>top level + + typeset -A hash_test=(top level) + () { + local -PA hash_test=(in function) + () { + print X ${(kv)hash_test} + hash_test[in]=deeper + } + print Y ${(kv)hash_test} + } + print ${(okv)hash_test} +0:privates are not visible in anonymous functions, part 2 +>X top level +>Y in function +>deeper in level top + + typeset -A hash_test=(top level) + () { + local -Pa array_test=(in function) + local -PA hash_test=($array_test) + () { + print X ${(kv)hash_test} + hash_test=(even deeper) + { + array_test+=(${(kv)hash_test}) + } always { + print ${array_test-array_test not set} ${(t)array_test} + } + } + print Y ${(kv)hash_test} Z $array_test + } + print ${(kv)hash_test} ${(t)array_test} +1:privates are not visible in anonymous functions, part 3 +>X top level +>array_test not set +?(anon):4: array_test: attempt to assign private in nested scope +F:future revision will create a global with this assignment + + typeset -a array_test + typeset -A hash_test=(top level) + () { + local -Pa array_test=(in function) + local -PA hash_test=($array_test) + () { + print X ${(kv)hash_test} + hash_test=(even deeper) + array_test+=(${(kv)hash_test}) + } + print Y ${(kv)hash_test} Z $array_test + } + print ${(kv)hash_test} $array_test +0:privates are not visible in anonymous functions, part 4 +>X top level +>Y in function Z in function +>even deeper even deeper + + typeset -A hash_test=(top level) + () { + local -PA hash_test=(in function) + () { + print X ${(kv)hash_test} + unset hash_test + } + print Y ${(kv)hash_test} + } + print ${(t)hash_test} ${(kv)hash_test} +0:privates are not visible in anonymous functions, part 5 +>X top level +>Y in function +> + + # Subshell because otherwise this silently dumps core when broken + ( () { private SECONDS } ) +1:special parameters cannot be made private +?(anon):private: can't change scope of existing param: SECONDS + + () { private -h SECONDS } +0:private parameter may hide a special parameter + + if (( UID )); then + ZTST_verbose=0 $ZTST_exe +Z -f $ZTST_srcdir/ztst.zsh private.TMP/B02 + else + ZTST_skip="cannot re-run typeset tests when tests run as superuser" + fi +0:typeset still works with zsh/param/private module loaded +*>* +*>* + +%clean + + rm -r private.TMP diff --git a/dotfiles/system/.zsh/modules/Test/W01history.ztst b/dotfiles/system/.zsh/modules/Test/W01history.ztst new file mode 100644 index 0000000..6ef9b11 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/W01history.ztst @@ -0,0 +1,60 @@ +# Tests for BANG_HIST replacements + +%prep + + if [[ -t 0 ]]; then print -u $ZTST_fd History tests write to /dev/tty; fi + +%test + + $ZTST_testdir/../Src/zsh -fis <<<' + print one two three four five six seven eight nine ten + print !:$ !:10 !:9 !:1 !:0 + print one two three four five six seven eight nine ten + print !:0-$ !:1-2 + ' 2>/dev/null +0:History word references +>one two three four five six seven eight nine ten +>ten ten nine one print +>one two three four five six seven eight nine ten +>print one two three four five six seven eight nine ten one two + + $ZTST_testdir/../Src/zsh -fis <<<' + print line one of an arbitrary series + print issue two for some mystery sequence + print !-1:5-$ + print !1:2 + print !2:2 + print !-3:1-$ + ' 2>/dev/null +0:History line numbering +>line one of an arbitrary series +>issue two for some mystery sequence +>mystery sequence +>one +>two +>mystery sequence + + $ZTST_testdir/../Src/zsh -fis <<<' + print All metaphor, Malachi, stilts and all + print !1:2:s/,/\\\\?/ !1:2:s/m/shm/:s/,/\!/ + print !1:2:& + print -l !1:2-3:gs/a/o/ + ' 2>/dev/null +0:History substitution +>All metaphor, Malachi, stilts and all +>metaphor? shmetaphor! +>metaphor! +>metophor, +>Molochi, + + $ZTST_testdir/../Src/zsh -fis <<<' + echo foo bar + echo $(!!) again + echo more $( !! )' 2>/dev/null +0:Regression test for history references in command substitution +>foo bar +>foo bar again +>more foo bar again +*?* +F:Check that a history bug introduced by workers/34160 is working again. +# Discarded line of error output consumes prompts printed by "zsh -i". diff --git a/dotfiles/system/.zsh/modules/Test/comptest b/dotfiles/system/.zsh/modules/Test/comptest new file mode 100644 index 0000000..166d0b4 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/comptest @@ -0,0 +1,177 @@ +comptestinit () { + setopt extendedglob + [[ -d $ZTST_testdir/Modules/zsh ]] && module_path=( $ZTST_testdir/Modules ) + fpath=( $ZTST_srcdir/../Functions/*~*/CVS(/) + $ZTST_srcdir/../Completion + $ZTST_srcdir/../Completion/*/*~*/CVS(/) ) + + zmodload zsh/zpty || return $? + + comptest_zsh=${ZSH:-zsh} + comptest_keymap=e + + while getopts vz: opt; do + case $opt in + z) comptest_zsh="$OPTARG";; + v) comptest_keymap="v";; + esac + done + (( OPTIND > 1 )) && shift $(( OPTIND - 1 )) + + export PS1="" + zpty zsh "$comptest_zsh -f +Z" + + zpty -r zsh log1 "**" || { + print "first prompt hasn't appeared." + return 1 + } + + comptesteval \ +"export LC_ALL=${ZSH_TEST_LANG:-C}" \ +"emulate -R zsh" \ +"export ZDOTDIR=$ZTST_testdir" \ +"module_path=( $module_path )" \ +"fpath=( $fpath )" \ +"bindkey -$comptest_keymap" \ +'LISTMAX=10000000 +stty 38400 columns 80 rows 24 tabs -icanon -iexten +TERM=vt100 +KEYTIMEOUT=1 +setopt zle +autoload -U compinit +compinit -u +zstyle ":completion:*:default" list-colors "no=" "fi=" "di=" "ln=" "pi=" "so=" "bd=" "cd=" "ex=" "mi=" "tc=" "sp=" "lc=" "ec=\n" "rc=" +zstyle ":completion:*" group-name "" +zstyle ":completion:*:messages" format "%d +" +zstyle ":completion:*:descriptions" format "%d +" +zstyle ":completion:*:options" verbose yes +zstyle ":completion:*:values" verbose yes +setopt noalwayslastprompt listrowsfirst completeinword +zmodload zsh/complist +expand-or-complete-with-report () { + print -lr "" + zle expand-or-complete + print -lr - "$LBUFFER" "$RBUFFER" + zle clear-screen + zle -R +} +list-choices-with-report () { + print -lr "" + zle list-choices + zle clear-screen + zle -R +} +comp-finish () { + print "" + zle kill-whole-line + zle clear-screen + zle -R +} +zle-finish () { + local buffer="$BUFFER" cursor="$CURSOR" mark="$MARK" + (( region_active)) || unset mark + BUFFER="" + zle -I + zle clear-screen + zle redisplay + print -lr "" "BUFFER: $buffer" "CURSOR: $cursor" + (( $+mark )) && print -lr "MARK: $mark" + zle accept-line +} +zle -N expand-or-complete-with-report +zle -N list-choices-with-report +zle -N comp-finish +zle -N zle-finish +bindkey "^I" expand-or-complete-with-report +bindkey "^D" list-choices-with-report +bindkey "^Z" comp-finish +bindkey "^X" zle-finish +bindkey -a "^X" zle-finish +' +} + +zpty_flush() { + local junk + if zpty -r -t zsh junk \*; then + (( ZTST_verbose > 2 )) && print -n -u $ZTST_fd "$*: ${(V)junk}" + while zpty -r -t zsh junk \* ; do + (( ZTST_verbose > 2 )) && print -n -u $ZTST_fd "${(V)junk}" + done + (( ZTST_verbose > 2 )) && print -u $ZTST_fd '' + fi +} + +zpty_run() { + zpty -w zsh "$*" + zpty -r -m zsh log "**" || { + print "prompt hasn't appeared." + return 1 + } +} + +comptesteval () { + local tmp=/tmp/comptest.$$ + + print -lr - "$@" > $tmp + # zpty_flush Before comptesteval + zpty -w zsh ". $tmp" + zpty -r -m zsh log_eval "**" || { + print "prompt hasn't appeared." + return 1 + } + zpty_flush After comptesteval + rm $tmp +} + +comptest () { + input="$*" + zpty -n -w zsh "$input"$'\C-Z' + zpty -r -m zsh log "***" || { + print "failed to invoke finish widget." + return 1 + } + + logs=(${(s::)log}) + shift logs + + for log in "$logs[@]"; do + if [[ "$log" = (#b)*$''(*)$'\r\n'(*)$''* ]]; then + print -lr "line: {$match[1]}{$match[2]}" + fi + while (( ${(N)log#*(#b)(<(??)>(*)|(*)|(*)|(*)|(*))} )); do + log="${log[$mend[1]+1,-1]}" + if (( 0 <= $mbegin[2] )); then + if [[ $match[2] != TC && $match[3] != \ # ]]; then + print -lr "$match[2]:{${match[3]%${(%):-%E}}}" + fi + elif (( 0 <= $mbegin[4] )); then + print -lr "DESCRIPTION:{$match[4]}" + elif (( 0 <= $mbegin[5] )); then + print -lr "MESSAGE:{$match[5]}" + elif (( 0 <= $mbegin[6] )); then + print -lr "COMPADD:{${${match[6]}//[$'\r\n']/}}" + elif (( 0 <= $mbegin[7] )); then + print -lr "INSERT_POSITIONS:{${${match[7]}//[$'\r\n']/}}" + fi + done + done +} + +zletest () { + local first=0 + for input; do + # zpty_flush Before zletest + # sleep for $KEYTIMEOUT + (( first++ )) && { sleep 2 & } | read -t 0.011 -u 0 -k 1 + zpty -n -w zsh "$input" + done + zpty -n -w zsh $'\C-X' + zpty -r -m zsh log "***" || { + print "failed to invoke finish widget." + return 1 + } + # zpty_flush After zletest + print -lr "${(@)${(@ps:\r\n:)log##*}[2,-2]}" +} diff --git a/dotfiles/system/.zsh/modules/Test/runtests.zsh b/dotfiles/system/.zsh/modules/Test/runtests.zsh new file mode 100644 index 0000000..562234d --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/runtests.zsh @@ -0,0 +1,27 @@ +#!/bin/zsh -f + +emulate zsh + +# Run all specified tests, keeping count of which succeeded. +# The reason for this extra layer above the test script is to +# protect from catastrophic failure of an individual test. +# We could probably do that with subshells instead. + +integer success failure skipped retval +for file in "${(f)ZTST_testlist}"; do + $ZTST_exe +Z -f $ZTST_srcdir/ztst.zsh $file + retval=$? + if (( $retval == 2 )); then + (( skipped++ )) + elif (( $retval )); then + (( failure++ )) + else + (( success++ )) + fi +done +print "************************************** +$success successful test script${${success:#1}:+s}, \ +$failure failure${${failure:#1}:+s}, \ +$skipped skipped +**************************************" +return $(( failure ? 1 : 0 )) diff --git a/dotfiles/system/.zsh/modules/Test/ztst.zsh b/dotfiles/system/.zsh/modules/Test/ztst.zsh new file mode 100755 index 0000000..f172ae1 --- /dev/null +++ b/dotfiles/system/.zsh/modules/Test/ztst.zsh @@ -0,0 +1,547 @@ +#!/bin/zsh -f +# The line above is just for convenience. Normally tests will be run using +# a specified version of zsh. With dynamic loading, any required libraries +# must already have been installed in that case. +# +# Takes one argument: the name of the test file. Currently only one such +# file will be processed each time ztst.zsh is run. This is slower, but +# much safer in terms of preserving the correct status. +# To avoid namespace pollution, all functions and parameters used +# only by the script begin with ZTST_. +# +# Options (without arguments) may precede the test file argument; these +# are interpreted as shell options to set. -x is probably the most useful. + +# Produce verbose messages if non-zero. +# If 1, produce reports of tests executed; if 2, also report on progress. +# Defined in such a way that any value from the environment is used. +: ${ZTST_verbose:=0} + +# We require all options to be reset, not just emulation options. +# Unfortunately, due to the crud which may be in /etc/zshenv this might +# still not be good enough. Maybe we should trick it somehow. +emulate -R zsh + +# Ensure the locale does not screw up sorting. Don't supply a locale +# unless there's one set, to minimise problems. +[[ -n $LC_ALL ]] && LC_ALL=C +[[ -n $LC_COLLATE ]] && LC_COLLATE=C +[[ -n $LC_NUMERIC ]] && LC_NUMERIC=C +[[ -n $LC_MESSAGES ]] && LC_MESSAGES=C +[[ -n $LANG ]] && LANG=C + +# Don't propagate variables that are set by default in the shell. +typeset +x WORDCHARS + +# Set the module load path to correspond to this build of zsh. +# This Modules directory should have been created by "make check". +[[ -d Modules/zsh ]] && module_path=( $PWD/Modules ) +# Allow this to be passed down. +export MODULE_PATH + +# We need to be able to save and restore the options used in the test. +# We use the $options variable of the parameter module for this. +zmodload zsh/parameter + +# Note that both the following are regular arrays, since we only use them +# in whole array assignments to/from $options. +# Options set in test code (i.e. by default all standard options) +ZTST_testopts=(${(kv)options}) + +setopt extendedglob nonomatch +while [[ $1 = [-+]* ]]; do + set $1 + shift +done +# Options set in main script +ZTST_mainopts=(${(kv)options}) + +# We run in the current directory, so remember it. +ZTST_testdir=$PWD +ZTST_testname=$1 + +integer ZTST_testfailed + +# This is POSIX nonsense. Because of the vague feeling someone, somewhere +# may one day need to examine the arguments of "tail" using a standard +# option parser, every Unix user in the world is expected to switch +# to using "tail -n NUM" instead of "tail -NUM". Older versions of +# tail don't support this. +tail() { + emulate -L zsh + + if [[ -z $TAIL_SUPPORTS_MINUS_N ]]; then + local test + test=$(echo "foo\nbar" | command tail -n 1 2>/dev/null) + if [[ $test = bar ]]; then + TAIL_SUPPORTS_MINUS_N=1 + else + TAIL_SUPPORTS_MINUS_N=0 + fi + fi + + integer argi=${argv[(i)-<->]} + + if [[ $argi -le $# && $TAIL_SUPPORTS_MINUS_N = 1 ]]; then + argv[$argi]=(-n ${argv[$argi][2,-1]}) + fi + + command tail "$argv[@]" +} + +# The source directory is not necessarily the current directory, +# but if $0 doesn't contain a `/' assume it is. +if [[ $0 = */* ]]; then + ZTST_srcdir=${0%/*} +else + ZTST_srcdir=$PWD +fi +[[ $ZTST_srcdir = /* ]] || ZTST_srcdir="$ZTST_testdir/$ZTST_srcdir" + +# Set the function autoload paths to correspond to this build of zsh. +fpath=( $ZTST_srcdir/../Functions/*~*/CVS(/) + $ZTST_srcdir/../Completion + $ZTST_srcdir/../Completion/*/*~*/CVS(/) ) + +: ${TMPPREFIX:=/tmp/zsh} +ZTST_tmp=${TMPPREFIX}.ztst.$$ +if ! rm -f $ZTST_tmp || ! mkdir -p $ZTST_tmp || ! chmod go-w $ZTST_tmp; then + print "Can't create $ZTST_tmp for exclusive use." >&2 + exit 1 +fi +# Temporary files for redirection inside tests. +ZTST_in=${ZTST_tmp}/ztst.in +# hold the expected output +ZTST_out=${ZTST_tmp}/ztst.out +ZTST_err=${ZTST_tmp}/ztst.err +# hold the actual output from the test +ZTST_tout=${ZTST_tmp}/ztst.tout +ZTST_terr=${ZTST_tmp}/ztst.terr + +ZTST_cleanup() { + cd $ZTST_testdir + rm -rf $ZTST_testdir/dummy.tmp $ZTST_testdir/*.tmp(N) ${ZTST_tmp} +} + +# This cleanup always gets performed, even if we abort. Later, +# we should try and arrange that any test-specific cleanup +# always gets called as well. +##trap 'print cleaning up... +##ZTST_cleanup' INT QUIT TERM +# Make sure it's clean now. +rm -rf dummy.tmp *.tmp + +# Report failure. Note that all output regarding the tests goes to stdout. +# That saves an unpleasant mixture of stdout and stderr to sort out. +ZTST_testfailed() { + print -r "Test $ZTST_testname failed: $1" + if [[ -n $ZTST_message ]]; then + print -r "Was testing: $ZTST_message" + fi + print -r "$ZTST_testname: test failed." + if [[ -n $ZTST_failmsg ]]; then + print -r "The following may (or may not) help identifying the cause: +$ZTST_failmsg" + fi + ZTST_testfailed=1 + return 1 +} + +# Print messages if $ZTST_verbose is non-empty +ZTST_verbose() { + local lev=$1 + shift + if [[ -n $ZTST_verbose && $ZTST_verbose -ge $lev ]]; then + print -r -u $ZTST_fd -- $* + fi +} +ZTST_hashmark() { + if [[ ZTST_verbose -le 0 && -t $ZTST_fd ]]; then + print -n -u$ZTST_fd -- ${(pl:SECONDS::\#::\#\r:)} + fi + (( SECONDS > COLUMNS+1 && (SECONDS -= COLUMNS) )) +} + +if [[ ! -r $ZTST_testname ]]; then + ZTST_testfailed "can't read test file." + exit 1 +fi + +exec {ZTST_fd}>&1 +exec {ZTST_input}<$ZTST_testname + +# The current line read from the test file. +ZTST_curline='' +# The current section being run +ZTST_cursect='' + +# Get a new input line. Don't mangle spaces; set IFS locally to empty. +# We shall skip comments at this level. +ZTST_getline() { + local IFS= + while true; do + read -u $ZTST_input -r ZTST_curline || return 1 + [[ $ZTST_curline == \#* ]] || return 0 + done +} + +# Get the name of the section. It may already have been read into +# $curline, or we may have to skip some initial comments to find it. +# If argument present, it's OK to skip the reset of the current section, +# so no error if we find garbage. +ZTST_getsect() { + local match mbegin mend + + while [[ $ZTST_curline != '%'(#b)([[:alnum:]]##)* ]]; do + ZTST_getline || return 1 + [[ $ZTST_curline = [[:blank:]]# ]] && continue + if [[ $# -eq 0 && $ZTST_curline != '%'[[:alnum:]]##* ]]; then + ZTST_testfailed "bad line found before or after section: +$ZTST_curline" + exit 1 + fi + done + # have the next line ready waiting + ZTST_getline + ZTST_cursect=${match[1]} + ZTST_verbose 2 "ZTST_getsect: read section name: $ZTST_cursect" + return 0 +} + +# Read in an indented code chunk for execution +ZTST_getchunk() { + # Code chunks are always separated by blank lines or the + # end of a section, so if we already have a piece of code, + # we keep it. Currently that shouldn't actually happen. + ZTST_code='' + # First find the chunk. + while [[ $ZTST_curline = [[:blank:]]# ]]; do + ZTST_getline || break + done + while [[ $ZTST_curline = [[:blank:]]##[^[:blank:]]* ]]; do + ZTST_code="${ZTST_code:+${ZTST_code} +}${ZTST_curline}" + ZTST_getline || break + done + ZTST_verbose 2 "ZTST_getchunk: read code chunk: +$ZTST_code" + [[ -n $ZTST_code ]] +} + +# Read in a piece for redirection. +ZTST_getredir() { + local char=${ZTST_curline[1]} fn + ZTST_redir=${ZTST_curline[2,-1]} + while ZTST_getline; do + [[ $ZTST_curline[1] = $char ]] || break + ZTST_redir="${ZTST_redir} +${ZTST_curline[2,-1]}" + done + ZTST_verbose 2 "ZTST_getredir: read redir for '$char': +$ZTST_redir" + + case $char in + ('<') fn=$ZTST_in + ;; + ('>') fn=$ZTST_out + ;; + ('?') fn=$ZTST_err + ;; + (*) ZTST_testfailed "bad redir operator: $char" + return 1 + ;; + esac + if [[ $ZTST_flags = *q* && $char = '<' ]]; then + # delay substituting output until variables are set + print -r -- "${(e)ZTST_redir}" >>$fn + else + print -r -- "$ZTST_redir" >>$fn + fi + + return 0 +} + +# Execute an indented chunk. Redirections will already have +# been set up, but we need to handle the options. +ZTST_execchunk() { + setopt localloops # don't let continue & break propagate out + options=($ZTST_testopts) + () { + unsetopt localloops + eval "$ZTST_code" + } + ZTST_status=$? + # careful... ksh_arrays may be in effect. + ZTST_testopts=(${(kv)options[*]}) + options=(${ZTST_mainopts[*]}) + ZTST_verbose 2 "ZTST_execchunk: status $ZTST_status" + return $ZTST_status +} + +# Functions for preparation and cleaning. +# When cleaning up (non-zero string argument), we ignore status. +ZTST_prepclean() { + # Execute indented code chunks. + while ZTST_getchunk; do + ZTST_execchunk >/dev/null || [[ -n $1 ]] || { + [[ -n "$ZTST_unimplemented" ]] || + ZTST_testfailed "non-zero status from preparation code: +$ZTST_code" && return 0 + } + done +} + +# diff wrapper +ZTST_diff() { + emulate -L zsh + setopt extendedglob + + local diff_out + integer diff_pat diff_ret + + case $1 in + (p) + diff_pat=1 + ;; + + (d) + ;; + + (*) + print "Bad ZTST_diff code: d for diff, p for pattern match" + ;; + esac + shift + + if (( diff_pat )); then + local -a diff_lines1 diff_lines2 + integer failed i + + diff_lines1=("${(f)$(<$argv[-2])}") + diff_lines2=("${(f)$(<$argv[-1])}") + if (( ${#diff_lines1} != ${#diff_lines2} )); then + failed=1 + else + for (( i = 1; i <= ${#diff_lines1}; i++ )); do + if [[ ${diff_lines2[i]} != ${~diff_lines1[i]} ]]; then + failed=1 + break + fi + done + fi + if (( failed )); then + print -rl "Pattern match failed:" \<${^diff_lines1} \>${^diff_lines2} + diff_ret=1 + fi + else + diff_out=$(diff "$@") + diff_ret="$?" + if [[ "$diff_ret" != "0" ]]; then + print -r -- "$diff_out" + fi + fi + + return "$diff_ret" +} + +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 + touch $ZTST_in $ZTST_out $ZTST_err + ZTST_message='' + ZTST_failmsg='' + found=0 + diff_out=d + diff_err=d + + ZTST_verbose 2 "ZTST_test: looking for new test" + + while true; do + ZTST_verbose 2 "ZTST_test: examining line: +$ZTST_curline" + case $ZTST_curline in + (%*) if [[ $found = 0 ]]; then + break 2 + else + last=1 + break + fi + ;; + ([[:space:]]#) + if [[ $found = 0 ]]; then + ZTST_getline || break 2 + continue + else + break + fi + ;; + ([[:space:]]##[^[:space:]]*) ZTST_getchunk + if [[ $ZTST_curline == (#b)([-0-9]##)([[:alpha:]]#)(:*)# ]]; then + ZTST_xstatus=$match[1] + ZTST_flags=$match[2] + ZTST_message=${match[3]:+${match[3][2,-1]}} + else + ZTST_testfailed "expecting test status at: +$ZTST_curline" + return 1 + fi + ZTST_getline + found=1 + ;; + ('<'*) ZTST_getredir || return 1 + found=1 + ;; + ('*>'*) + ZTST_curline=${ZTST_curline[2,-1]} + diff_out=p + ;& + ('>'*) + ZTST_getredir || return 1 + found=1 + ;; + ('*?'*) + ZTST_curline=${ZTST_curline[2,-1]} + diff_err=p + ;& + ('?'*) + ZTST_getredir || return 1 + found=1 + ;; + ('F:'*) ZTST_failmsg="${ZTST_failmsg:+${ZTST_failmsg} +} ${ZTST_curline[3,-1]}" + ZTST_getline + found=1 + ;; + (*) ZTST_testfailed "bad line in test block: +$ZTST_curline" + return 1 + ;; + esac + done + + # If we found some code to execute... + if [[ -n $ZTST_code ]]; then + ZTST_hashmark + ZTST_verbose 1 "Running test: $ZTST_message" + ZTST_verbose 2 "ZTST_test: expecting status: $ZTST_xstatus" + ZTST_verbose 2 "Input: $ZTST_in, output: $ZTST_out, error: $ZTST_terr" + + 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: +$ZTST_code${$(<$ZTST_terr):+ +Error output: +$(<$ZTST_terr)}" + return 1 + fi + + ZTST_verbose 2 "ZTST_test: test produced standard output: +$(<$ZTST_tout) +ZTST_test: and standard error: +$(<$ZTST_terr)" + + # Now check output and error. + if [[ $ZTST_flags = *q* && -s $ZTST_out ]]; then + substlines="$(<$ZTST_out)" + rm -rf $ZTST_out + print -r -- "${(e)substlines}" >$ZTST_out + fi + if [[ $ZTST_flags != *d* ]] && ! ZTST_diff $diff_out -u $ZTST_out $ZTST_tout; then + ZTST_testfailed "output differs from expected as shown above for: +$ZTST_code${$(<$ZTST_terr):+ +Error output: +$(<$ZTST_terr)}" + return 1 + fi + if [[ $ZTST_flags = *q* && -s $ZTST_err ]]; then + substlines="$(<$ZTST_err)" + rm -rf $ZTST_err + print -r -- "${(e)substlines}" >$ZTST_err + fi + if [[ $ZTST_flags != *D* ]] && ! ZTST_diff $diff_err -u $ZTST_err $ZTST_terr; then + ZTST_testfailed "error output differs from expected as shown above for: +$ZTST_code" + return 1 + fi + fi + ZTST_verbose 1 "Test successful." + [[ -n $last ]] && break + done + + ZTST_verbose 2 "ZTST_test: all tests successful" + + # reset message to keep ZTST_testfailed output correct + ZTST_message='' +} + + +# Remember which sections we've done. +typeset -A ZTST_sects +ZTST_sects=(prep 0 test 0 clean 0) + +print "$ZTST_testname: starting." + +# Now go through all the different sections until the end. +# prep section may set ZTST_unimplemented, in this case the actual +# tests will be skipped +ZTST_skipok= +ZTST_unimplemented= +while [[ -z "$ZTST_unimplemented" ]] && ZTST_getsect $ZTST_skipok; do + case $ZTST_cursect in + (prep) if (( ${ZTST_sects[prep]} + ${ZTST_sects[test]} + \ + ${ZTST_sects[clean]} )); then + ZTST_testfailed "\`prep' section must come first" + exit 1 + fi + ZTST_prepclean + ZTST_sects[prep]=1 + ;; + (test) + if (( ${ZTST_sects[test]} + ${ZTST_sects[clean]} )); then + ZTST_testfailed "bad placement of \`test' section" + exit 1 + fi + # careful here: we can't execute ZTST_test before || or && + # because that affects the behaviour of traps in the tests. + ZTST_test + (( $? )) && ZTST_skipok=1 + ZTST_sects[test]=1 + ;; + (clean) + if (( ${ZTST_sects[test]} == 0 || ${ZTST_sects[clean]} )); then + ZTST_testfailed "bad use of \`clean' section" + else + ZTST_prepclean 1 + ZTST_sects[clean]=1 + fi + ZTST_skipok= + ;; + *) ZTST_testfailed "bad section name: $ZTST_cursect" + ;; + esac +done + +if [[ -n "$ZTST_unimplemented" ]]; then + print "$ZTST_testname: skipped ($ZTST_unimplemented)" + ZTST_testfailed=2 +elif (( ! $ZTST_testfailed )); then + print "$ZTST_testname: all tests successful." +fi +ZTST_cleanup +exit $(( ZTST_testfailed )) diff --git a/dotfiles/system/.zsh/modules/aclocal.m4 b/dotfiles/system/.zsh/modules/aclocal.m4 new file mode 100644 index 0000000..e91be3c --- /dev/null +++ b/dotfiles/system/.zsh/modules/aclocal.m4 @@ -0,0 +1,77 @@ +# Local additions to Autoconf macros. +# Copyright (C) 1992, 1994 Free Software Foundation, Inc. +# Francois Pinard , 1992. + +# @defmac fp_PROG_CC_STDC +# @maindex PROG_CC_STDC +# @ovindex CC +# If the C compiler in not in ANSI C mode by default, try to add an option +# to output variable @code{CC} to make it so. This macro tries various +# options that select ANSI C on some system or another. It considers the +# compiler to be in ANSI C mode if it defines @code{__STDC__} to 1 and +# handles function prototypes correctly. +# +# If you use this macro, you should check after calling it whether the C +# compiler has been set to accept ANSI C; if not, the shell variable +# @code{fp_cv_prog_cc_stdc} is set to @samp{no}. If you wrote your source +# code in ANSI C, you can make an un-ANSIfied copy of it by using the +# program @code{ansi2knr}, which comes with Ghostscript. +# @end defmac + +define(fp_PROG_CC_STDC, +[AC_CACHE_CHECK(for ${CC-cc} option to accept ANSI C, +fp_cv_prog_cc_stdc, +[fp_cv_prog_cc_stdc=no +ac_save_CFLAGS="$CFLAGS" +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX -Ae or -Aa -D_HPUX_SOURCE +# SVR4 -Xc +# For HP-UX, we try -Ae first; this turns on ANSI but also extensions, +# as well as defining _HPUX_SOURCE, and we can then use long long. +# We keep the old version for backward compatibility. +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" -Xc +do + CFLAGS="$ac_save_CFLAGS $ac_arg" + AC_TRY_COMPILE( +[#ifndef __STDC__ +choke me +#endif +], [int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);};], +[fp_cv_prog_cc_stdc="$ac_arg"; break]) +done +CFLAGS="$ac_save_CFLAGS" +]) +case "x$fp_cv_prog_cc_stdc" in + x|xno) ;; + *) CC="$CC $fp_cv_prog_cc_stdc" ;; +esac +]) + +AC_DEFUN(AC_PROG_LN, +[AC_MSG_CHECKING(whether ln works) +AC_CACHE_VAL(ac_cv_prog_LN, +[rm -f conftestdata conftestlink +echo > conftestdata +if ln conftestdata conftestlink 2>/dev/null +then + rm -f conftestdata conftestlink + ac_cv_prog_LN="ln" +else + rm -f conftestdata + ac_cv_prog_LN="cp" +fi])dnl +LN="$ac_cv_prog_LN" +if test "$ac_cv_prog_LN" = "ln"; then + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi +AC_SUBST(LN)dnl +]) + +builtin(include, aczsh.m4) diff --git a/dotfiles/system/.zsh/modules/aczsh.m4 b/dotfiles/system/.zsh/modules/aczsh.m4 new file mode 100644 index 0000000..0219ae2 --- /dev/null +++ b/dotfiles/system/.zsh/modules/aczsh.m4 @@ -0,0 +1,690 @@ +dnl +dnl Autconf tests for zsh. +dnl +dnl Copyright (c) 1995-1997 Richard Coleman +dnl All rights reserved. +dnl +dnl Permission is hereby granted, without written agreement and without +dnl license or royalty fees, to use, copy, modify, and distribute this +dnl software and to distribute modified versions of this software for any +dnl purpose, provided that the above copyright notice and the following +dnl two paragraphs appear in all copies of this software. +dnl +dnl In no event shall Richard Coleman or the Zsh Development Group be liable +dnl to any party for direct, indirect, special, incidental, or consequential +dnl damages arising out of the use of this software and its documentation, +dnl even if Richard Coleman and the Zsh Development Group have been advised of +dnl the possibility of such damage. +dnl +dnl Richard Coleman and the Zsh Development Group specifically disclaim any +dnl warranties, including, but not limited to, the implied warranties of +dnl merchantability and fitness for a particular purpose. The software +dnl provided hereunder is on an "as is" basis, and Richard Coleman and the +dnl Zsh Development Group have no obligation to provide maintenance, +dnl support, updates, enhancements, or modifications. +dnl + +dnl +dnl zsh_64_BIT_TYPE +dnl Check whether the first argument works as a 64-bit type. +dnl If there is a non-zero third argument, we just assume it works +dnl when we're cross compiling. This is to allow a type to be +dnl specified directly as --enable-lfs="long long". +dnl Sets the variable given in the second argument to the first argument +dnl if the test worked, `no' otherwise. Be careful testing this, as it +dnl may produce two words `long long' on an unquoted substitution. +dnl Also check that the compiler does not mind it being cast to int. +dnl This macro does not produce messages as it may be run several times +dnl before finding the right type. +dnl + +AC_DEFUN(zsh_64_BIT_TYPE, +[AC_TRY_RUN([ +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +main() +{ + $1 foo = 0; + int bar = (int) foo; + return sizeof($1) != 8; +} +], $2="$1", $2=no, + [if test x$3 != x ; then + $2="$1" + else + $2=no + fi]) +]) + + +dnl +dnl zsh_SHARED_FUNCTION +dnl +dnl This is just a frontend to zsh_SHARED_SYMBOL +dnl +dnl Usage: zsh_SHARED_FUNCTION(name[,rettype[,paramtype]]) +dnl + +AC_DEFUN(zsh_SHARED_FUNCTION, +[zsh_SHARED_SYMBOL($1, ifelse([$2], ,[int ],[$2]) $1 [(]ifelse([$3], ,[ ],[$3])[)], $1)]) + +dnl +dnl zsh_SHARED_VARIABLE +dnl +dnl This is just a frontend to zsh_SHARED_SYMBOL +dnl +dnl Usage: zsh_SHARED_VARIABLE(name[,type]) +dnl + +AC_DEFUN(zsh_SHARED_VARIABLE, +[zsh_SHARED_SYMBOL($1, ifelse([$2], ,[int ],[$2]) $1, [&$1])]) + +dnl +dnl zsh_SHARED_SYMBOL +dnl Check whether symbol is available in static or shared library +dnl +dnl On some systems, static modifiable library symbols (such as environ) +dnl may appear only in statically linked libraries. If this is the case, +dnl then two shared libraries that reference the same symbol, each linked +dnl with the static library, could be given distinct copies of the symbol. +dnl +dnl Usage: zsh_SHARED_SYMBOL(name,declaration,address) +dnl Sets zsh_cv_shared_$1 cache variable to yes/no +dnl + +AC_DEFUN(zsh_SHARED_SYMBOL, +[AC_CACHE_CHECK([if $1 is available in shared libraries], +zsh_cv_shared_$1, +[if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ +else + us= +fi +echo ' +void *zsh_getaddr1() +{ +#ifdef __CYGWIN__ + __attribute__((__dllimport__)) +#endif + extern $2; + return $3; +}; +' > conftest1.c +sed 's/zsh_getaddr1/zsh_getaddr2/' < conftest1.c > conftest2.c +if AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&AC_FD_CC) && +AC_TRY_COMMAND($DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&AC_FD_CC) && +AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&AC_FD_CC) && +AC_TRY_COMMAND($DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&AC_FD_CC); then + AC_TRY_RUN([ +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +main() +{ + void *handle1, *handle2; + void *(*zsh_getaddr1)(), *(*zsh_getaddr2)(); + void *sym1, *sym2; + handle1 = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle1) exit(1); + handle2 = dlopen("./conftest2.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle2) exit(1); + zsh_getaddr1 = (void *(*)()) dlsym(handle1, "${us}zsh_getaddr1"); + zsh_getaddr2 = (void *(*)()) dlsym(handle2, "${us}zsh_getaddr2"); + sym1 = zsh_getaddr1(); + sym2 = zsh_getaddr2(); + if(!sym1 || !sym2) exit(1); + if(sym1 != sym2) exit(1); + dlclose(handle1); + handle1 = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle1) exit(1); + zsh_getaddr1 = (void *(*)()) dlsym(handle1, "${us}zsh_getaddr1"); + sym1 = zsh_getaddr1(); + if(!sym1) exit(1); + if(sym1 != sym2) exit(1); + exit(0); +} +], [zsh_cv_shared_$1=yes], +[zsh_cv_shared_$1=no], +[zsh_cv_shared_$1=no] +) +else + zsh_cv_shared_$1=no +fi +]) +]) + +dnl +dnl zsh_SYS_DYNAMIC_CLASH +dnl Check whether symbol name clashes in shared libraries are acceptable. +dnl + +AC_DEFUN(zsh_SYS_DYNAMIC_CLASH, +[AC_CACHE_CHECK([if name clashes in shared objects are OK], +zsh_cv_sys_dynamic_clash_ok, +[if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ +else + us= +fi +echo 'int fred () { return 42; }' > conftest1.c +echo 'int fred () { return 69; }' > conftest2.c +if AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&AC_FD_CC) && +AC_TRY_COMMAND($DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&AC_FD_CC) && +AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&AC_FD_CC) && +AC_TRY_COMMAND($DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&AC_FD_CC); then + AC_TRY_RUN([ +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + + +main() +{ + void *handle1, *handle2; + int (*fred1)(), (*fred2)(); + handle1 = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle1) exit(1); + handle2 = dlopen("./conftest2.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle2) exit(1); + fred1 = (int (*)()) dlsym(handle1, "${us}fred"); + fred2 = (int (*)()) dlsym(handle2, "${us}fred"); + if(!fred1 || !fred2) exit(1); + exit((*fred1)() != 42 || (*fred2)() != 69); +} +], [zsh_cv_sys_dynamic_clash_ok=yes], +[zsh_cv_sys_dynamic_clash_ok=no], +[zsh_cv_sys_dynamic_clash_ok=no] +) +else + zsh_cv_sys_dynamic_clash_ok=no +fi +]) +if test "$zsh_cv_sys_dynamic_clash_ok" = yes; then + AC_DEFINE(DYNAMIC_NAME_CLASH_OK) +fi +]) + +dnl +dnl zsh_SYS_DYNAMIC_GLOBAL +dnl Check whether symbols in one dynamically loaded library are +dnl available to another dynamically loaded library. +dnl + +AC_DEFUN(zsh_SYS_DYNAMIC_GLOBAL, +[AC_CACHE_CHECK([for working RTLD_GLOBAL], +zsh_cv_sys_dynamic_rtld_global, +[if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ +else + us= +fi +echo 'int fred () { return 42; }' > conftest1.c +echo 'extern int fred(); int barney () { return fred() + 27; }' > conftest2.c +if AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&AC_FD_CC) && +AC_TRY_COMMAND($DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&AC_FD_CC) && +AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&AC_FD_CC) && +AC_TRY_COMMAND($DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&AC_FD_CC); then + AC_TRY_RUN([ +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +main() +{ + void *handle; + int (*barneysym)(); + handle = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle) exit(1); + handle = dlopen("./conftest2.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle) exit(1); + barneysym = (int (*)()) dlsym(handle, "${us}barney"); + if(!barneysym) exit(1); + exit((*barneysym)() != 69); +} +], [zsh_cv_sys_dynamic_rtld_global=yes], +[zsh_cv_sys_dynamic_rtld_global=no], +[zsh_cv_sys_dynamic_rtld_global=no] +) +else + zsh_cv_sys_dynamic_rtld_global=no +fi +]) +]) + +dnl +dnl zsh_SYS_DYNAMIC_EXECSYMS +dnl Check whether symbols in the executable are available to dynamically +dnl loaded libraries. +dnl + +AC_DEFUN(zsh_SYS_DYNAMIC_EXECSYMS, +[AC_CACHE_CHECK([whether symbols in the executable are available], +zsh_cv_sys_dynamic_execsyms, +[if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ +else + us= +fi +echo 'extern int fred(); int barney () { return fred() + 27; }' > conftest1.c +if AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&AC_FD_CC) && +AC_TRY_COMMAND($DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&AC_FD_CC); then + save_ldflags=$LDFLAGS + LDFLAGS="$LDFLAGS $EXTRA_LDFLAGS" + AC_TRY_RUN([ +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +main() +{ + void *handle; + int (*barneysym)(); + handle = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle) exit(1); + barneysym = (int (*)()) dlsym(handle, "${us}barney"); + if(!barneysym) exit(1); + exit((*barneysym)() != 69); +} + +int fred () { return 42; } +], [zsh_cv_sys_dynamic_execsyms=yes], +[zsh_cv_sys_dynamic_execsyms=no], +[zsh_cv_sys_dynamic_execsyms=no] +) + LDFLAGS=$save_ldflags +else + zsh_cv_sys_dynamic_execsyms=no +fi +]) +]) + +dnl +dnl zsh_SYS_DYNAMIC_STRIP_EXE +dnl Check whether it is safe to strip executables. +dnl + +AC_DEFUN(zsh_SYS_DYNAMIC_STRIP_EXE, +[AC_REQUIRE([zsh_SYS_DYNAMIC_EXECSYMS]) +AC_CACHE_CHECK([whether executables can be stripped], +zsh_cv_sys_dynamic_strip_exe, +[if test "$zsh_cv_sys_dynamic_execsyms" != yes; then + zsh_cv_sys_dynamic_strip_exe=yes +elif + if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ + else + us= + fi + echo 'extern int fred(); int barney() { return fred() + 27; }' > conftest1.c + AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&AC_FD_CC) && + AC_TRY_COMMAND($DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&AC_FD_CC); then + save_ldflags=$LDFLAGS + LDFLAGS="$LDFLAGS $EXTRA_LDFLAGS -s" + AC_TRY_RUN([ +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +main() +{ + void *handle; + int (*barneysym)(); + handle = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle) exit(1); + barneysym = (int (*)()) dlsym(handle, "${us}barney"); + if(!barneysym) exit(1); + exit((*barneysym)() != 69); +} + +int fred () { return 42; } +], [zsh_cv_sys_dynamic_strip_exe=yes], +[zsh_cv_sys_dynamic_strip_exe=no], +[zsh_cv_sys_dynamic_strip_exe=no] +) + LDFLAGS=$save_ldflags +else + zsh_cv_sys_dynamic_strip_exe=no +fi +]) +]) + +dnl +dnl zsh_SYS_DYNAMIC_STRIP_EXE +dnl Check whether it is safe to strip dynamically loaded libraries. +dnl + +AC_DEFUN(zsh_SYS_DYNAMIC_STRIP_LIB, +[AC_CACHE_CHECK([whether libraries can be stripped], +zsh_cv_sys_dynamic_strip_lib, +[if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ +else + us= +fi +echo 'int fred () { return 42; }' > conftest1.c +if AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&AC_FD_CC) && +AC_TRY_COMMAND($DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS -s conftest1.o $LIBS 1>&AC_FD_CC); then + AC_TRY_RUN([ +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +main() +{ + void *handle; + int (*fredsym)(); + handle = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle) exit(1); + fredsym = (int (*)()) dlsym(handle, "${us}fred"); + if(!fredsym) exit(1); + exit((*fredsym)() != 42); +} +], [zsh_cv_sys_dynamic_strip_lib=yes], +[zsh_cv_sys_dynamic_strip_lib=no], +[zsh_cv_sys_dynamic_strip_lib=no] +) +else + zsh_cv_sys_dynamic_strip_lib=no +fi +]) +]) + +dnl +dnl zsh_PATH_UTMP(filename) +dnl Search for a specified utmp-type file. +dnl + +AC_DEFUN(zsh_PATH_UTMP, +[AC_CACHE_CHECK([for $1 file], [zsh_cv_path_$1], +[for dir in /etc /usr/etc /var/adm /usr/adm /var/run /var/log ./conftest; do + zsh_cv_path_$1=${dir}/$1 + test -f $zsh_cv_path_$1 && break + zsh_cv_path_$1=no +done +]) +AH_TEMPLATE([PATH_]translit($1, [a-z], [A-Z])[_FILE], +[Define to be location of ]$1[ file.]) +if test $zsh_cv_path_$1 != no; then + AC_DEFINE_UNQUOTED([PATH_]translit($1, [a-z], [A-Z])[_FILE], + "$zsh_cv_path_$1") +fi +]) + +dnl +dnl zsh_TYPE_EXISTS(#includes, type name) +dnl Check whether a specified type exists. +dnl + +AC_DEFUN(zsh_TYPE_EXISTS, +[AC_CACHE_CHECK([for $2], [zsh_cv_type_exists_[]translit($2, [ ], [_])], +[AC_TRY_COMPILE([$1], [$2 testvar;], +[zsh_cv_type_exists_[]translit($2, [ ], [_])=yes], +[zsh_cv_type_exists_[]translit($2, [ ], [_])=no]) +]) +AH_TEMPLATE([HAVE_]translit($2, [ a-z], [_A-Z]), +[Define to 1 if ]$2[ is defined by a system header]) +if test $zsh_cv_type_exists_[]translit($2, [ ], [_]) = yes; then + AC_DEFINE([HAVE_]translit($2, [ a-z], [_A-Z])) +fi +]) + +dnl +dnl zsh_STRUCT_MEMBER(#includes, type name, member name) +dnl Check whether a specified aggregate type exists and contains +dnl a specified member. +dnl + +AC_DEFUN(zsh_STRUCT_MEMBER, +[AC_CACHE_CHECK([for $3 in $2], [zsh_cv_struct_member_[]translit($2, [ ], [_])_$3], +[AC_TRY_COMPILE([$1], [$2 testvar; testvar.$3;], +[zsh_cv_struct_member_[]translit($2, [ ], [_])_$3=yes], +[zsh_cv_struct_member_[]translit($2, [ ], [_])_$3=no]) +]) +AH_TEMPLATE([HAVE_]translit($2_$3, [ a-z], [_A-Z]), +[Define if your system's ]$2[ has a member named ]$3[.]) +if test $zsh_cv_struct_member_[]translit($2, [ ], [_])_$3 = yes; then + AC_DEFINE([HAVE_]translit($2_$3, [ a-z], [_A-Z])) +fi +]) + +dnl +dnl zsh_ARG_PROGRAM +dnl Handle AC_ARG_PROGRAM substitutions into other zsh configure macros. +dnl After processing this macro, the configure script may refer to +dnl and $tzsh_name, and @tzsh@ is defined for make substitutions. +dnl + +AC_DEFUN(zsh_ARG_PROGRAM, +[AC_ARG_PROGRAM +# Un-double any \ or $ (doubled by AC_ARG_PROGRAM). +cat <<\EOF_SED > conftestsed +s,\\\\,\\,g; s,\$\$,$,g +EOF_SED +zsh_transform_name=`echo "${program_transform_name}" | sed -f conftestsed` +rm -f conftestsed +tzsh_name=`echo zsh | sed -e "${zsh_transform_name}"` +# Double any \ or $ in the transformed name that results. +cat <<\EOF_SED >> conftestsed +s,\\,\\\\,g; s,\$,$$,g +EOF_SED +tzsh=`echo ${tzsh_name} | sed -f conftestsed` +rm -f conftestsed +AC_SUBST(tzsh)dnl +]) + +AC_DEFUN(zsh_COMPILE_FLAGS, + [AC_ARG_ENABLE(cppflags, + AC_HELP_STRING([--enable-cppflags=...], [specify C preprocessor flags]), + if test "$enableval" = "yes" + then CPPFLAGS="$1" + else CPPFLAGS="$enable_cppflags" + fi) + AC_ARG_ENABLE(cflags, + AC_HELP_STRING([--enable-cflags=...], [specify C compiler flags]), + if test "$enableval" = "yes" + then CFLAGS="$2" + else CFLAGS="$enable_cflags" + fi) + AC_ARG_ENABLE(ldflags, + AC_HELP_STRING([--enable-ldflags=...], [specify linker flags]), + if test "$enableval" = "yes" + then LDFLAGS="$3" + else LDFLAGS="$enable_ldflags" + fi) + AC_ARG_ENABLE(libs, + AC_HELP_STRING([--enable-libs=...], [specify link libraries]), + if test "$enableval" = "yes" + then LIBS="$4" + else LIBS="$enable_libs" + fi)]) + +dnl +dnl zsh_CHECK_SOCKLEN_T +dnl +dnl check type of third argument of some network functions; currently +dnl tested are size_t *, unsigned long *, int *. +dnl call the result ZSOCKLEN_T since some systems have SOCKLEN_T already +dnl +AC_DEFUN([zsh_CHECK_SOCKLEN_T],[ + AC_CACHE_CHECK( + [base type of the third argument to accept], + [zsh_cv_type_socklen_t], + [zsh_cv_type_socklen_t= + for zsh_type in socklen_t int "unsigned long" size_t ; do + AC_TRY_COMPILE( + [#include + #include ], + [extern int accept (int, struct sockaddr *, $zsh_type *);], + [zsh_cv_type_socklen_t="$zsh_type"; break], + [] + ) + done + if test -z "$zsh_cv_type_socklen_t"; then + zsh_cv_type_socklen_t=int + fi] + ) + AC_DEFINE_UNQUOTED([ZSOCKLEN_T], [$zsh_cv_type_socklen_t], + [Define to the base type of the third argument of accept])] +) + +dnl Check for limit $1 e.g. RLIMIT_RSS. +AC_DEFUN(zsh_LIMIT_PRESENT, +[AH_TEMPLATE([HAVE_]$1, +[Define to 1 if ]$1[ is present (whether or not as a macro).]) +AC_CACHE_CHECK([for limit $1], +zsh_cv_have_$1, +[AC_TRY_COMPILE([ +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include ], +[$1], + zsh_cv_have_$1=yes, + zsh_cv_have_$1=no)]) + +if test $zsh_cv_have_$1 = yes; then + AC_DEFINE(HAVE_$1) +fi]) + diff --git a/dotfiles/system/.zsh/modules/config.guess b/dotfiles/system/.zsh/modules/config.guess new file mode 100755 index 0000000..dc84c68 --- /dev/null +++ b/dotfiles/system/.zsh/modules/config.guess @@ -0,0 +1,1501 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 +# Free Software Foundation, Inc. + +timestamp='2009-11-20' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Originally written by Per Bothner. Please send patches (context +# diff format) to and include a ChangeLog +# entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm:riscos:*:*|arm:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[456]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + case ${UNAME_MACHINE} in + pc98) + echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-gnu + else + echo ${UNAME_MACHINE}-unknown-linux-gnueabi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit ;; + i*86:Linux:*:*) + LIBC=gnu + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + or32:Linux:*:*) + echo or32-unknown-linux-gnu + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-gnu + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-gnu + exit ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + i386) + eval $set_cc_for_build + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + UNAME_PROCESSOR="x86_64" + fi + fi ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NSE-?:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/dotfiles/system/.zsh/modules/config.h.in b/dotfiles/system/.zsh/modules/config.h.in new file mode 100644 index 0000000..89a65b7 --- /dev/null +++ b/dotfiles/system/.zsh/modules/config.h.in @@ -0,0 +1,1242 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/***** begin user configuration section *****/ + +/* Define this to be the location of your password file */ +#define PASSWD_FILE "/etc/passwd" + +/* Define this to be the name of your NIS/YP password * + * map (if applicable) */ +#define PASSWD_MAP "passwd.byname" + +/* Define to 1 if you want user names to be cached */ +#define CACHE_USERNAMES 1 + +/* Define to 1 if system supports job control */ +#define JOB_CONTROL 1 + +/* Define this if you use "suspended" instead of "stopped" */ +#define USE_SUSPENDED 1 + +/* The default history buffer size in lines */ +#define DEFAULT_HISTSIZE 30 + +/* The default editor for the fc builtin */ +#define DEFAULT_FCEDIT "vi" + +/* The default prefix for temporary files */ +#define DEFAULT_TMPPREFIX "/tmp/zsh" + +/***** end of user configuration section *****/ +/***** shouldn't have to change anything below here *****/ + + + +/* Define to 1 if you want to use dynamically loaded modules on AIX. */ +#undef AIXDYNAMIC + +/* Define to 1 if the isprint() function is broken under UTF-8 locale. */ +#undef BROKEN_ISPRINT + +/* Define to 1 if kill(pid, 0) doesn't return ESRCH, ie BeOS R4.51. */ +#undef BROKEN_KILL_ESRCH + +/* Define to 1 if sigsuspend() is broken */ +#undef BROKEN_POSIX_SIGSUSPEND + +/* Define to 1 if compiler incorrectly cast signed to unsigned. */ +#undef BROKEN_SIGNED_TO_UNSIGNED_CASTING + +/* Define to 1 if tcsetpgrp() doesn't work, ie BeOS R4.51. */ +#undef BROKEN_TCSETPGRP + +/* Define to 1 if you use BSD style signal handling (and can block signals). + */ +#undef BSD_SIGNALS + +/* Undefine if you don't want local features. By default this is defined. */ +#undef CONFIG_LOCALE + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +#undef CRAY_STACKSEG_END + +/* Define to a custom value for the ZSH_PATCHLEVEL parameter */ +#undef CUSTOM_PATCHLEVEL + +/* Define to 1 if using `alloca.c'. */ +#undef C_ALLOCA + +/* Define to 1 if you want to debug zsh. */ +#undef DEBUG + +/* The default path; used when running commands with command -p */ +#undef DEFAULT_PATH + +/* Define default pager used by readnullcmd */ +#undef DEFAULT_READNULLCMD + +/* Define to 1 if you want to avoid calling functions that will require + dynamic NSS modules. */ +#undef DISABLE_DYNAMIC_NSS + +/* Define to 1 if an underscore has to be prepended to dlsym() argument. */ +#undef DLSYM_NEEDS_UNDERSCORE + +/* The extension used for dynamically loaded modules. */ +#undef DL_EXT + +/* Define to 1 if you want to use dynamically loaded modules. */ +#undef DYNAMIC + +/* Define to 1 if multiple modules defining the same symbol are OK. */ +#undef DYNAMIC_NAME_CLASH_OK + +/* Define to 1 if you want use unicode9 character widths. */ +#undef ENABLE_UNICODE9 + +/* Define to 1 if getcwd() calls malloc to allocate memory. */ +#undef GETCWD_CALLS_MALLOC + +/* Define to 1 if the `getpgrp' function requires zero arguments. */ +#undef GETPGRP_VOID + +/* Define to 1 if getpwnam() is faked, ie BeOS R4.51. */ +#undef GETPWNAM_FAKED + +/* The global file to source whenever zsh is run as a login shell; if + undefined, don't source anything */ +#undef GLOBAL_ZLOGIN + +/* The global file to source whenever zsh was run as a login shell. This is + sourced right before exiting. If undefined, don't source anything. */ +#undef GLOBAL_ZLOGOUT + +/* The global file to source whenever zsh is run as a login shell, before + zshrc is read; if undefined, don't source anything. */ +#undef GLOBAL_ZPROFILE + +/* The global file to source absolutely first whenever zsh is run; if + undefined, don't source anything. */ +#undef GLOBAL_ZSHENV + +/* The global file to source whenever zsh is run; if undefined, don't source + anything */ +#undef GLOBAL_ZSHRC + +/* Define if TIOCGWINSZ is defined in sys/ioctl.h but not in termios.h. */ +#undef GWINSZ_IN_SYS_IOCTL + +/* Define to 1 if you have `alloca', as a function or macro. */ +#undef HAVE_ALLOCA + +/* Define to 1 if you have and it should be used (not on Ultrix). + */ +#undef HAVE_ALLOCA_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_BIND_NETDB_H + +/* Define if you have the termcap boolcodes symbol. */ +#undef HAVE_BOOLCODES + +/* Define if you have the terminfo boolnames symbol. */ +#undef HAVE_BOOLNAMES + +/* Define to 1 if you have the `brk' function. */ +#undef HAVE_BRK + +/* Define to 1 if there is a prototype defined for brk() on your system. */ +#undef HAVE_BRK_PROTO + +/* Define to 1 if you have the `canonicalize_file_name' function. */ +#undef HAVE_CANONICALIZE_FILE_NAME + +/* Define to 1 if you have the `cap_get_proc' function. */ +#undef HAVE_CAP_GET_PROC + +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + +/* Define to 1 if you have the header file. */ +#undef HAVE_CURSES_H + +/* Define to 1 if you have the `cygwin_conv_path' function. */ +#undef HAVE_CYGWIN_CONV_PATH + +/* Define to 1 if you have the `difftime' function. */ +#undef HAVE_DIFFTIME + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you have the `dlclose' function. */ +#undef HAVE_DLCLOSE + +/* Define to 1 if you have the `dlerror' function. */ +#undef HAVE_DLERROR + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the `dlopen' function. */ +#undef HAVE_DLOPEN + +/* Define to 1 if you have the `dlsym' function. */ +#undef HAVE_DLSYM + +/* Define to 1 if you have the header file. */ +#undef HAVE_DL_H + +/* Define to 1 if you have the `endutxent' function. */ +#undef HAVE_ENDUTXENT + +/* Define to 1 if you have the `erand48' function. */ +#undef HAVE_ERAND48 + +/* Define to 1 if you have the header file. */ +#undef HAVE_ERRNO_H + +/* Define to 1 if you have the `faccessx' function. */ +#undef HAVE_FACCESSX + +/* Define to 1 if you have the `fchdir' function. */ +#undef HAVE_FCHDIR + +/* Define to 1 if you have the `fchmod' function. */ +#undef HAVE_FCHMOD + +/* Define to 1 if you have the `fchown' function. */ +#undef HAVE_FCHOWN + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if system has working FIFOs. */ +#undef HAVE_FIFOS + +/* Define to 1 if you have the `fseeko' function. */ +#undef HAVE_FSEEKO + +/* Define to 1 if you have the `fstat' function. */ +#undef HAVE_FSTAT + +/* Define to 1 if you have the `ftello' function. */ +#undef HAVE_FTELLO + +/* Define to 1 if you have the `ftruncate' function. */ +#undef HAVE_FTRUNCATE + +/* Define to 1 if you have the header file. */ +#undef HAVE_GDBM_H + +/* Define to 1 if you have the `gdbm_open' function. */ +#undef HAVE_GDBM_OPEN + +/* Define to 1 if you have the `getcchar' function. */ +#undef HAVE_GETCCHAR + +/* Define to 1 if you have the `getcwd' function. */ +#undef HAVE_GETCWD + +/* Define to 1 if you have the `getenv' function. */ +#undef HAVE_GETENV + +/* Define to 1 if you have the `getgrgid' function. */ +#undef HAVE_GETGRGID + +/* Define to 1 if you have the `getgrnam' function. */ +#undef HAVE_GETGRNAM + +/* Define to 1 if you have the `gethostbyname2' function. */ +#undef HAVE_GETHOSTBYNAME2 + +/* Define to 1 if you have the `gethostname' function. */ +#undef HAVE_GETHOSTNAME + +/* Define to 1 if you have the `getipnodebyname' function. */ +#undef HAVE_GETIPNODEBYNAME + +/* Define to 1 if you have the `getline' function. */ +#undef HAVE_GETLINE + +/* Define to 1 if you have the `getlogin' function. */ +#undef HAVE_GETLOGIN + +/* Define to 1 if you have the `getpagesize' function. */ +#undef HAVE_GETPAGESIZE + +/* Define to 1 if you have the `getpwent' function. */ +#undef HAVE_GETPWENT + +/* Define to 1 if you have the `getpwnam' function. */ +#undef HAVE_GETPWNAM + +/* Define to 1 if you have the `getpwuid' function. */ +#undef HAVE_GETPWUID + +/* Define to 1 if you have the `getrlimit' function. */ +#undef HAVE_GETRLIMIT + +/* Define to 1 if you have the `getrusage' function. */ +#undef HAVE_GETRUSAGE + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the `getutent' function. */ +#undef HAVE_GETUTENT + +/* Define to 1 if you have the `getutxent' function. */ +#undef HAVE_GETUTXENT + +/* Define to 1 if you have the `getxattr' function. */ +#undef HAVE_GETXATTR + +/* Define to 1 if you have the `grantpt' function. */ +#undef HAVE_GRANTPT + +/* Define to 1 if you have the header file. */ +#undef HAVE_GRP_H + +/* Define to 1 if you have the `htons' function. */ +#undef HAVE_HTONS + +/* Define to 1 if you have the `iconv' function. */ +#undef HAVE_ICONV + +/* Define to 1 if you have the header file. */ +#undef HAVE_ICONV_H + +/* Define to 1 if you have the `inet_aton' function. */ +#undef HAVE_INET_ATON + +/* Define to 1 if you have the `inet_ntop' function. */ +#undef HAVE_INET_NTOP + +/* Define to 1 if you have the `inet_pton' function. */ +#undef HAVE_INET_PTON + +/* Define to 1 if you have the `initgroups' function. */ +#undef HAVE_INITGROUPS + +/* Define to 1 if you have the `initscr' function. */ +#undef HAVE_INITSCR + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if there is a prototype defined for ioctl() on your system. */ +#undef HAVE_IOCTL_PROTO + +/* Define to 1 if you have the `isblank' function. */ +#undef HAVE_ISBLANK + +/* Define to 1 if you have the `isinf' function. */ +#undef HAVE_ISINF + +/* Define to 1 if you have the `isnan' function. */ +#undef HAVE_ISNAN + +/* Define to 1 if you have the `iswblank' function. */ +#undef HAVE_ISWBLANK + +/* Define to 1 if you have the `killpg' function. */ +#undef HAVE_KILLPG + +/* Define to 1 if you have the header file. */ +#undef HAVE_LANGINFO_H + +/* Define to 1 if you have the `lchown' function. */ +#undef HAVE_LCHOWN + +/* Define to 1 if you have the `cap' library (-lcap). */ +#undef HAVE_LIBCAP + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBC_H + +/* Define to 1 if you have the `dl' library (-ldl). */ +#undef HAVE_LIBDL + +/* Define to 1 if you have the `gdbm' library (-lgdbm). */ +#undef HAVE_LIBGDBM + +/* Define to 1 if you have the `m' library (-lm). */ +#undef HAVE_LIBM + +/* Define to 1 if you have the `rt' library (-lrt). */ +#undef HAVE_LIBRT + +/* Define to 1 if you have the `socket' library (-lsocket). */ +#undef HAVE_LIBSOCKET + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if system has working link(). */ +#undef HAVE_LINK + +/* Define to 1 if you have the `load' function. */ +#undef HAVE_LOAD + +/* Define to 1 if you have the `loadbind' function. */ +#undef HAVE_LOADBIND + +/* Define to 1 if you have the `loadquery' function. */ +#undef HAVE_LOADQUERY + +/* Define to 1 if you have the header file. */ +#undef HAVE_LOCALE_H + +/* Define to 1 if you have the `log2' function. */ +#undef HAVE_LOG2 + +/* Define to 1 if you have the `lstat' function. */ +#undef HAVE_LSTAT + +/* Define to 1 if you have the `memcpy' function. */ +#undef HAVE_MEMCPY + +/* Define to 1 if you have the `memmove' function. */ +#undef HAVE_MEMMOVE + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `mkfifo' function. */ +#undef HAVE_MKFIFO + +/* Define to 1 if there is a prototype defined for mknod() on your system. */ +#undef HAVE_MKNOD_PROTO + +/* Define to 1 if you have the `mkstemp' function. */ +#undef HAVE_MKSTEMP + +/* Define to 1 if you have the `mktime' function. */ +#undef HAVE_MKTIME + +/* Define to 1 if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define to 1 if you have the `msync' function. */ +#undef HAVE_MSYNC + +/* Define to 1 if you have the `munmap' function. */ +#undef HAVE_MUNMAP + +/* Define to 1 if you have the `nanosleep' function. */ +#undef HAVE_NANOSLEEP + +/* Define to 1 if you have the header file. */ +#undef HAVE_NCURSESW_NCURSES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NCURSESW_TERM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NCURSES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NCURSES_NCURSES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NCURSES_TERM_H + +/* Define to 1 if you have the header file, and it defines `DIR'. */ +#undef HAVE_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_SYSTM_H + +/* Define to 1 if you have the `nice' function. */ +#undef HAVE_NICE + +/* Define to 1 if you have NIS. */ +#undef HAVE_NIS + +/* Define to 1 if you have the `nis_list' function. */ +#undef HAVE_NIS_LIST + +/* Define to 1 if you have NISPLUS. */ +#undef HAVE_NIS_PLUS + +/* Define to 1 if you have the `nl_langinfo' function. */ +#undef HAVE_NL_LANGINFO + +/* Define to 1 if you have the `ntohs' function. */ +#undef HAVE_NTOHS + +/* Define if you have the termcap numcodes symbol. */ +#undef HAVE_NUMCODES + +/* Define if you have the terminfo numnames symbol. */ +#undef HAVE_NUMNAMES + +/* Define to 1 if you have the `open_memstream' function. */ +#undef HAVE_OPEN_MEMSTREAM + +/* Define to 1 if your termcap library has the ospeed variable */ +#undef HAVE_OSPEED + +/* Define to 1 if you have the `pathconf' function. */ +#undef HAVE_PATHCONF + +/* Define to 1 if you have the `poll' function. */ +#undef HAVE_POLL + +/* Define to 1 if you have the header file. */ +#undef HAVE_POLL_H + +/* Define to 1 if you have the `posix_openpt' function. */ +#undef HAVE_POSIX_OPENPT + +/* Define to 1 if you have the `ptsname' function. */ +#undef HAVE_PTSNAME + +/* Define to 1 if you have the `putenv' function. */ +#undef HAVE_PUTENV + +/* Define to 1 if you have the header file. */ +#undef HAVE_PWD_H + +/* Define to 1 if you have the `readlink' function. */ +#undef HAVE_READLINK + +/* Define to 1 if you have the `realpath' function. */ +#undef HAVE_REALPATH + +/* Define to 1 if you have the `regcomp' function. */ +#undef HAVE_REGCOMP + +/* Define to 1 if you have the `regerror' function. */ +#undef HAVE_REGERROR + +/* Define to 1 if you have the `regexec' function. */ +#undef HAVE_REGEXEC + +/* Define to 1 if you have the `regfree' function. */ +#undef HAVE_REGFREE + +/* Define to 1 if you have the `resize_term' function. */ +#undef HAVE_RESIZE_TERM + +/* Define to 1 if RLIMIT_AIO_MEM is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_AIO_MEM + +/* Define to 1 if RLIMIT_AIO_OPS is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_AIO_OPS + +/* Define to 1 if RLIMIT_AS is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_AS + +/* Define to 1 if RLIMIT_KQUEUES is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_KQUEUES + +/* Define to 1 if RLIMIT_LOCKS is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_LOCKS + +/* Define to 1 if RLIMIT_MEMLOCK is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_MEMLOCK + +/* Define to 1 if RLIMIT_MSGQUEUE is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_MSGQUEUE + +/* Define to 1 if RLIMIT_NICE is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_NICE + +/* Define to 1 if RLIMIT_NOFILE is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_NOFILE + +/* Define to 1 if RLIMIT_NPROC is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_NPROC + +/* Define to 1 if RLIMIT_NPTS is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_NPTS + +/* Define to 1 if RLIMIT_NTHR is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_NTHR + +/* Define to 1 if RLIMIT_POSIXLOCKS is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_POSIXLOCKS + +/* Define to 1 if RLIMIT_PTHREAD is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_PTHREAD + +/* Define to 1 if RLIMIT_RSS is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_RSS + +/* Define to 1 if RLIMIT_RTPRIO is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_RTPRIO + +/* Define to 1 if RLIMIT_SBSIZE is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_SBSIZE + +/* Define to 1 if RLIMIT_SIGPENDING is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_SIGPENDING + +/* Define to 1 if RLIMIT_SWAP is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_SWAP + +/* Define to 1 if RLIMIT_TCACHE is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_TCACHE + +/* Define to 1 if RLIMIT_VMEM is present (whether or not as a macro). */ +#undef HAVE_RLIMIT_VMEM + +/* Define to 1 if you have the `sbrk' function. */ +#undef HAVE_SBRK + +/* Define to 1 if there is a prototype defined for sbrk() on your system. */ +#undef HAVE_SBRK_PROTO + +/* Define to 1 if you have the `scalbn' function. */ +#undef HAVE_SCALBN + +/* Define to 1 if you have the `select' function. */ +#undef HAVE_SELECT + +/* Define to 1 if you have the `setcchar' function. */ +#undef HAVE_SETCCHAR + +/* Define to 1 if you have the `setenv' function. */ +#undef HAVE_SETENV + +/* Define to 1 if you have the `seteuid' function. */ +#undef HAVE_SETEUID + +/* Define to 1 if you have the `setlocale' function. */ +#undef HAVE_SETLOCALE + +/* Define to 1 if you have the `setpgid' function. */ +#undef HAVE_SETPGID + +/* Define to 1 if you have the `setpgrp' function. */ +#undef HAVE_SETPGRP + +/* Define to 1 if the system supports `setproctitle' to change process name */ +#undef HAVE_SETPROCTITLE + +/* Define to 1 if you have the `setresuid' function. */ +#undef HAVE_SETRESUID + +/* Define to 1 if you have the `setreuid' function. */ +#undef HAVE_SETREUID + +/* Define to 1 if you have the `setsid' function. */ +#undef HAVE_SETSID + +/* Define to 1 if you have the `setuid' function. */ +#undef HAVE_SETUID + +/* Define to 1 if you have the `setupterm' function. */ +#undef HAVE_SETUPTERM + +/* Define to 1 if you have the `setutxent' function. */ +#undef HAVE_SETUTXENT + +/* Define to 1 if you have the `shl_findsym' function. */ +#undef HAVE_SHL_FINDSYM + +/* Define to 1 if you have the `shl_load' function. */ +#undef HAVE_SHL_LOAD + +/* Define to 1 if you have the `shl_unload' function. */ +#undef HAVE_SHL_UNLOAD + +/* Define to 1 if you have the `sigaction' function. */ +#undef HAVE_SIGACTION + +/* Define to 1 if you have the `sigblock' function. */ +#undef HAVE_SIGBLOCK + +/* Define to 1 if you have the `sighold' function. */ +#undef HAVE_SIGHOLD + +/* Define to 1 if you have the `signgam' function. */ +#undef HAVE_SIGNGAM + +/* Define to 1 if you have the `sigprocmask' function. */ +#undef HAVE_SIGPROCMASK + +/* Define to 1 if you have the `sigrelse' function. */ +#undef HAVE_SIGRELSE + +/* Define to 1 if you have the `sigsetmask' function. */ +#undef HAVE_SIGSETMASK + +/* Define to 1 if you have the `srand_deterministic' function. */ +#undef HAVE_SRAND_DETERMINISTIC + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDARG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDDEF_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define if you have the termcap strcodes symbol. */ +#undef HAVE_STRCODES + +/* Define to 1 if you have the `strcoll' function and it is properly defined. + */ +#undef HAVE_STRCOLL + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `strftime' function. */ +#undef HAVE_STRFTIME + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define if you have the terminfo strnames symbol. */ +#undef HAVE_STRNAMES + +/* Define to 1 if you have the `strptime' function. */ +#undef HAVE_STRPTIME + +/* Define to 1 if you have the `strstr' function. */ +#undef HAVE_STRSTR + +/* Define to 1 if you have the `strtoul' function. */ +#undef HAVE_STRTOUL + +/* Define if your system's struct direct has a member named d_ino. */ +#undef HAVE_STRUCT_DIRECT_D_INO + +/* Define if your system's struct direct has a member named d_stat. */ +#undef HAVE_STRUCT_DIRECT_D_STAT + +/* Define if your system's struct dirent has a member named d_ino. */ +#undef HAVE_STRUCT_DIRENT_D_INO + +/* Define if your system's struct dirent has a member named d_stat. */ +#undef HAVE_STRUCT_DIRENT_D_STAT + +/* Define to 1 if `ru_idrss' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_IDRSS + +/* Define to 1 if `ru_inblock' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_INBLOCK + +/* Define to 1 if `ru_isrss' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_ISRSS + +/* Define to 1 if `ru_ixrss' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_IXRSS + +/* Define to 1 if `ru_majflt' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_MAJFLT + +/* Define to 1 if `ru_maxrss' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_MAXRSS + +/* Define to 1 if `ru_minflt' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_MINFLT + +/* Define to 1 if `ru_msgrcv' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_MSGRCV + +/* Define to 1 if `ru_msgsnd' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_MSGSND + +/* Define to 1 if `ru_nivcsw' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_NIVCSW + +/* Define to 1 if `ru_nsignals' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_NSIGNALS + +/* Define to 1 if `ru_nswap' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_NSWAP + +/* Define to 1 if `ru_nvcsw' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_NVCSW + +/* Define to 1 if `ru_oublock' is a member of `struct rusage'. */ +#undef HAVE_STRUCT_RUSAGE_RU_OUBLOCK + +/* Define if your system's struct sockaddr_in6 has a member named + sin6_scope_id. */ +#undef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID + +/* Define to 1 if `st_atimensec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIMENSEC + +/* Define to 1 if `st_atimespec.tv_nsec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC + +/* Define to 1 if `st_atim.tv_nsec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC + +/* Define to 1 if `st_ctimensec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_CTIMENSEC + +/* Define to 1 if `st_ctimespec.tv_nsec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_CTIMESPEC_TV_NSEC + +/* Define to 1 if `st_ctim.tv_nsec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC + +/* Define to 1 if `st_mtimensec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_MTIMENSEC + +/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC + +/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC + +/* Define to 1 if struct timespec is defined by a system header */ +#undef HAVE_STRUCT_TIMESPEC + +/* Define to 1 if struct timezone is defined by a system header */ +#undef HAVE_STRUCT_TIMEZONE + +/* Define to 1 if struct utmp is defined by a system header */ +#undef HAVE_STRUCT_UTMP + +/* Define to 1 if struct utmpx is defined by a system header */ +#undef HAVE_STRUCT_UTMPX + +/* Define if your system's struct utmpx has a member named ut_host. */ +#undef HAVE_STRUCT_UTMPX_UT_HOST + +/* Define if your system's struct utmpx has a member named ut_tv. */ +#undef HAVE_STRUCT_UTMPX_UT_TV + +/* Define if your system's struct utmpx has a member named ut_xtime. */ +#undef HAVE_STRUCT_UTMPX_UT_XTIME + +/* Define if your system's struct utmp has a member named ut_host. */ +#undef HAVE_STRUCT_UTMP_UT_HOST + +/* Define to 1 if you have RFS superroot directory. */ +#undef HAVE_SUPERROOT + +/* Define to 1 if you have the `symlink' function. */ +#undef HAVE_SYMLINK + +/* Define to 1 if you have the `sysconf' function. */ +#undef HAVE_SYSCONF + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_CAPABILITY_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_DIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_FILIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define to 1 if you have the header file, and it defines `DIR'. + */ +#undef HAVE_SYS_NDIR_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_RESOURCE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STROPTS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIMES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UTSNAME_H + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_XATTR_H + +/* Define to 1 if you have the `tcgetattr' function. */ +#undef HAVE_TCGETATTR + +/* Define to 1 if you have the `tcsetpgrp' function. */ +#undef HAVE_TCSETPGRP + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMCAP_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIOS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERM_H + +/* Define to 1 if you have the `tgamma' function. */ +#undef HAVE_TGAMMA + +/* Define to 1 if you have the `tgetent' function. */ +#undef HAVE_TGETENT + +/* Define to 1 if you have the `tigetflag' function. */ +#undef HAVE_TIGETFLAG + +/* Define to 1 if you have the `tigetnum' function. */ +#undef HAVE_TIGETNUM + +/* Define to 1 if you have the `tigetstr' function. */ +#undef HAVE_TIGETSTR + +/* Define to 1 if you have the `timelocal' function. */ +#undef HAVE_TIMELOCAL + +/* Define to 1 if you have the `uname' function. */ +#undef HAVE_UNAME + +/* Define to 1 if the compiler can initialise a union. */ +#undef HAVE_UNION_INIT + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `unload' function. */ +#undef HAVE_UNLOAD + +/* Define to 1 if you have the `unlockpt' function. */ +#undef HAVE_UNLOCKPT + +/* Define to 1 if you have the `unsetenv' function. */ +#undef HAVE_UNSETENV + +/* Define to 1 if you have the `use_default_colors' function. */ +#undef HAVE_USE_DEFAULT_COLORS + +/* Define to 1 if you have the header file. */ +#undef HAVE_UTMPX_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UTMP_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_VARARGS_H + +/* Define to 1 if compiler supports variable-length arrays */ +#undef HAVE_VARIABLE_LENGTH_ARRAYS + +/* Define to 1 if you have the `waddwstr' function. */ +#undef HAVE_WADDWSTR + +/* Define to 1 if you have the `wait3' function. */ +#undef HAVE_WAIT3 + +/* Define to 1 if you have the `waitpid' function. */ +#undef HAVE_WAITPID + +/* Define to 1 if you have the header file. */ +#undef HAVE_WCHAR_H + +/* Define to 1 if you have the `wctomb' function. */ +#undef HAVE_WCTOMB + +/* Define to 1 if you have the `wget_wch' function. */ +#undef HAVE_WGET_WCH + +/* Define to 1 if you have the `win_wch' function. */ +#undef HAVE_WIN_WCH + +/* Define to 1 if you have the `xw' function. */ +#undef HAVE_XW + +/* Define to 1 if you have the `_mktemp' function. */ +#undef HAVE__MKTEMP + +/* Define to 1 if you want to use dynamically loaded modules on HPUX 10. */ +#undef HPUX10DYNAMIC + +/* Define as const if the declaration of iconv() needs const. */ +#undef ICONV_CONST + +/* Define to 1 if iconv() is linked from libiconv */ +#undef ICONV_FROM_LIBICONV + +/* Define to 1 if ino_t is 64 bit (for large file support). */ +#undef INO_T_IS_64_BIT + +/* Define to 1 if we must include to get a prototype for + ioctl(). */ +#undef IOCTL_IN_SYS_IOCTL + +/* Define to 1 if musl is being used as the C library */ +#undef LIBC_MUSL + +/* Definitions used when a long is less than eight byte, to try to provide + some support for eight byte operations. Note that ZSH_64_BIT_TYPE, + OFF_T_IS_64_BIT, INO_T_IS_64_BIT do *not* get defined if long is already 64 + bits, since in that case no special handling is required. Define to 1 if + long is 64 bits */ +#undef LONG_IS_64_BIT + +/* Define to be the machine type (microprocessor class or machine model). */ +#undef MACHTYPE + +/* Define for Maildir support */ +#undef MAILDIR_SUPPORT + +/* Define for function depth limits */ +#undef MAX_FUNCTION_DEPTH + +/* Define to 1 if you want support for multibyte character sets. */ +#undef MULTIBYTE_SUPPORT + +/* Define to 1 if you have ospeed, but it is not defined in termcap.h */ +#undef MUST_DEFINE_OSPEED + +/* Define to 1 if you have no signal blocking at all (bummer). */ +#undef NO_SIGNAL_BLOCKING + +/* Define to 1 if off_t is 64 bit (for large file support) */ +#undef OFF_T_IS_64_BIT + +/* Define to be the name of the operating system. */ +#undef OSTYPE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to the path of the /dev/fd filesystem. */ +#undef PATH_DEV_FD + +/* Define to be location of utmpx file. */ +#undef PATH_UTMPX_FILE + +/* Define to be location of utmp file. */ +#undef PATH_UTMP_FILE + +/* Define to be location of wtmpx file. */ +#undef PATH_WTMPX_FILE + +/* Define to be location of wtmp file. */ +#undef PATH_WTMP_FILE + +/* Define to 1 if you use POSIX style signal handling. */ +#undef POSIX_SIGNALS + +/* Define to 1 if printf and sprintf support %lld for long long. */ +#undef PRINTF_HAS_LLD + +/* Define to 1 if ANSI function prototypes are usable. */ +#undef PROTOTYPES + +/* Define if realpath() accepts NULL as its second argument. */ +#undef REALPATH_ACCEPTS_NULL + +/* Undefine this if you don't want to get a restricted shell when zsh is + exec'd with basename that starts with r. By default this is defined. */ +#undef RESTRICTED_R + +/* Define to 1 if RLIMIT_RSS and RLIMIT_AS both exist and are equal. */ +#undef RLIMIT_RSS_IS_AS + +/* Define to 1 if RLIMIT_VMEM and RLIMIT_AS both exist and are equal. */ +#undef RLIMIT_VMEM_IS_AS + +/* Define to 1 if RLIMIT_VMEM and RLIMIT_RSS both exist and are equal. */ +#undef RLIMIT_VMEM_IS_RSS + +/* Define to 1 if struct rlimit uses long long */ +#undef RLIM_T_IS_LONG_LONG + +/* Define to 1 if struct rlimit uses quad_t. */ +#undef RLIM_T_IS_QUAD_T + +/* Define to 1 if struct rlimit uses unsigned. */ +#undef RLIM_T_IS_UNSIGNED + +/* Define to 1 if select() is defined in , ie BeOS R4.51 */ +#undef SELECT_IN_SYS_SOCKET_H + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +#undef STACK_DIRECTION + +/* Define to 1 if the `S_IS*' macros in do not work properly. */ +#undef STAT_MACROS_BROKEN + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you use SYS style signal handling (and can block signals). + */ +#undef SYSV_SIGNALS + +/* Define to 1 if tgetent() accepts NULL as a buffer. */ +#undef TGETENT_ACCEPTS_NULL + +/* Define to what tgetent() returns on success (0 on HP-UX X/Open curses). */ +#undef TGETENT_SUCCESS + +/* Define if there is no prototype for the tgoto() terminal function. */ +#undef TGOTO_PROTO_MISSING + +/* Define if sys/time.h and sys/select.h cannot be both included. */ +#undef TIME_H_SELECT_H_CONFLICTS + +/* Define to 1 if you can safely include both and . */ +#undef TIME_WITH_SYS_TIME + +/* Define to 1 if all the kit for using /dev/ptmx for ptys is available. */ +#undef USE_DEV_PTMX + +/* Define to 1 if you need to use the native getcwd. */ +#undef USE_GETCWD + +/* Define to 1 if h_errno is not defined by the system. */ +#undef USE_LOCAL_H_ERRNO + +/* Define to 1 if you want to allocate stack memory e.g. with `alloca'. */ +#undef USE_STACK_ALLOCATION + +/* Define to be a string corresponding the vendor of the machine. */ +#undef VENDOR + +/* Define if your should include sys/stream.h and sys/ptem.h. */ +#undef WINSIZE_IN_PTEM + +/* Define if getxattr() etc. require additional MacOS-style arguments */ +#undef XATTR_EXTRA_ARGS + +/* Define to 1 if the zlong type uses 64-bit long int. */ +#undef ZLONG_IS_LONG_64 + +/* Define to 1 if the zlong type uses long long int. */ +#undef ZLONG_IS_LONG_LONG + +/* Define to a 64 bit integer type if there is one, but long is shorter. */ +#undef ZSH_64_BIT_TYPE + +/* Define to an unsigned variant of ZSH_64_BIT_TYPE if that is defined. */ +#undef ZSH_64_BIT_UTYPE + +/* Define to 1 if you want to get debugging information on internal hash + tables. This turns on the `hashinfo' builtin. */ +#undef ZSH_HASH_DEBUG + +/* Define to 1 if some variant of a curses header can be included */ +#undef ZSH_HAVE_CURSES_H + +/* Define to 1 if some variant of term.h can be included */ +#undef ZSH_HAVE_TERM_H + +/* Define to 1 if you want to turn on error checking for heap allocation. */ +#undef ZSH_HEAP_DEBUG + +/* Define to 1 if you want to use zsh's own memory allocation routines */ +#undef ZSH_MEM + +/* Define to 1 if you want to debug zsh memory allocation routines. */ +#undef ZSH_MEM_DEBUG + +/* Define to 1 if you want to turn on warnings of memory allocation errors */ +#undef ZSH_MEM_WARNING + +/* Define if _XOPEN_SOURCE_EXTENDED should not be defined to avoid clashes */ +#undef ZSH_NO_XOPEN + +/* Define to 1 if you want to turn on memory checking for free(). */ +#undef ZSH_SECURE_FREE + +/* Define to 1 if you want to add code for valgrind to debug heap memory. */ +#undef ZSH_VALGRIND + +/* Define to the base type of the third argument of accept */ +#undef ZSOCKLEN_T + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `int' if doesn't define. */ +#undef gid_t + +/* Define to `unsigned long' if doesn't define. */ +#undef ino_t + +/* Define to `int' if does not define. */ +#undef mode_t + +/* Define to `long int' if does not define. */ +#undef off_t + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to the type used in struct rlimit. */ +#undef rlim_t + +/* Define to `unsigned int' if or doesn't define */ +#undef sigset_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to `int' if doesn't define. */ +#undef uid_t diff --git a/dotfiles/system/.zsh/modules/config.sub b/dotfiles/system/.zsh/modules/config.sub new file mode 100755 index 0000000..2a55a50 --- /dev/null +++ b/dotfiles/system/.zsh/modules/config.sub @@ -0,0 +1,1705 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 +# Free Software Foundation, Inc. + +timestamp='2009-11-20' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Please send patches to . Submit a context +# diff and a properly formatted GNU ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ + uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nios | nios2 \ + | ns16k | ns32k \ + | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e \ + | we32k \ + | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12 | picochip) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nios-* | nios2-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze) + basic_machine=microblaze-xilinx + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tile*) + basic_machine=tile-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/dotfiles/system/.zsh/modules/configure b/dotfiles/system/.zsh/modules/configure new file mode 100755 index 0000000..2b2a912 --- /dev/null +++ b/dotfiles/system/.zsh/modules/configure @@ -0,0 +1,14547 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69. +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= +PACKAGE_URL= + +ac_unique_file="Src/zsh.h" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_header_list= +ac_subst_vars='LTLIBOBJS +LIBOBJS +EXTRAZSHOBJS +MOD_IMPORT_FUNCTION +MOD_IMPORT_VARIABLE +MOD_EXPORT +LINKMODS +L +IMPOPT +EXPOPT +EXTRA_LDFLAGS +E +DLLDFLAGS +DLCFLAGS +DLLD +DL_EXT +D +UNINSTLIB +INSTLIB +SHORTBOOTNAMES +RLIMITS_INC_H +ZSH_TERM_H +CURSES_KEYS_H +ZSH_CURSES_H +ERRNO_H +SIGNAL_H +ANSI2KNR +PAPERSIZE +TEXI2HTML +TEXI2PDF +TEXI2DVI +YODL_OPTIONS +YODL +LN_S +LN +AWK +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +SET_MAKE +ALLOCA +EGREP +GREP +U +CPP +LIBLDFLAGS +EXELDFLAGS +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +sitescriptdir +scriptdir +FUNCTIONS_SUBDIRS +fixed_sitefndir +sitefndir +fndir +additionalfpath +runhelp +runhelpdir +zlogout +zlogin +zprofile +zshrc +zshenv +tzsh +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='CLEAN_MK +CONFIG_MK +DEFS_MK +VERSION_MK' +ac_user_opts=' +enable_option_checking +enable_cppflags +enable_cflags +enable_ldflags +enable_libs +enable_zsh_debug +enable_zsh_mem +enable_zsh_mem_debug +enable_zsh_mem_warning +enable_zsh_secure_free +enable_zsh_heap_debug +enable_zsh_valgrind +enable_zsh_hash_debug +enable_stack_allocation +enable_etcdir +enable_zshenv +enable_zshrc +enable_zprofile +enable_zlogin +enable_zlogout +enable_dynamic +enable_restricted_r +enable_locale +enable_ansi2knr +enable_runhelpdir +enable_fndir +enable_site_fndir +enable_function_subdirs +enable_additional_fpath +enable_scriptdir +enable_site_scriptdir +enable_custom_patchlevel +enable_maildir_support +enable_max_function_depth +enable_readnullcmd +enable_cap +enable_gdbm +enable_largefile +with_term_lib +with_tcsetpgrp +enable_multibyte +enable_unicode9 +enable_libc_musl +enable_dynamic_nss +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-cppflags=... specify C preprocessor flags + --enable-cflags=... specify C compiler flags + --enable-ldflags=... specify linker flags + --enable-libs=... specify link libraries + --enable-zsh-debug compile with debug code and debugger symbols + --enable-zsh-mem compile with zsh memory allocation routines + --enable-zsh-mem-debug debug zsh memory allocation routines + --enable-zsh-mem-warning + print warnings for errors in memory allocation + --enable-zsh-secure-free + turn on error checking for free() + --enable-zsh-heap-debug turn on error checking for heap allocation + --enable-zsh-valgrind turn on support for valgrind debugging of heap + memory + --enable-zsh-hash-debug turn on debugging of internal hash tables + --enable-stack-allocation + allocate stack memory e.g. with `alloca' + --enable-etcdir=DIR the default directory for global zsh scripts + --enable-zshenv=FILE the full pathname of the global zshenv script + --enable-zshrc=FILE the full pathname of the global zshrc script + --enable-zprofile=FILE the full pathname of the global zprofile script + --enable-zlogin=FILE the full pathname of the global zlogin script + --enable-zlogout=FILE the full pathname of the global zlogout script + --disable-dynamic turn off dynamically loaded binary modules + --disable-restricted-r turn off r* invocation for restricted shell + --disable-locale turn off locale features + --enable-ansi2knr translate source to K&R C before compiling + --enable-runhelpdir=DIR the directory in which to install run-help files + --enable-fndir=DIR the directory in which to install functions + --enable-site-fndir=DIR same for site functions (not version specific) + --enable-function-subdirs + install functions in subdirectories + --enable-additional-fpath=DIR + add directories to default function path + --enable-scriptdir=DIR the directory in which to install scripts + --enable-site-scriptdir=DIR + same for site scripts (not version specific) + --enable-custom-patchlevel + set a custom ZSH_PATCHLEVEL value + --enable-maildir-support + enable maildir support in MAIL and MAILPATH + --enable-max-function-depth=MAX + limit function depth to MAX, default 500 + --enable-readnullcmd=PAGER + pager used when READNULLCMD is not set + --enable-cap enable the search for POSIX capabilities (may + require additional headers to be added by hand) + --disable-gdbm turn off search for gdbm library + --disable-largefile omit support for large files + --enable-multibyte support multibyte characters + --enable-unicode9 compile with unicode9 character widths + --enable-libc-musl compile with musl as the C library + --disable-dynamic-nss do not call functions that will require dynamic NSS + modules + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-term-lib=LIBS search space-separated LIBS for terminal handling + --with-tcsetpgrp assumes that tcsetpgrp() exists and works correctly + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES +# --------------------------------------------- +# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR +# accordingly. +ac_fn_c_check_decl () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + as_decl_name=`echo $2|sed 's/ *(.*//'` + as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 +$as_echo_n "checking whether $as_decl_name is declared... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +#ifndef $as_decl_name +#ifdef __cplusplus + (void) $as_decl_use; +#else + (void) $as_decl_name; +#endif +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_decl + +# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES +# ---------------------------------------------------- +# Tries to find if the field MEMBER exists in type AGGR, after including +# INCLUDES, setting cache variable VAR accordingly. +ac_fn_c_check_member () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 +$as_echo_n "checking for $2.$3... " >&6; } +if eval \${$4+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (sizeof ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + eval "$4=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$4 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_member +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +as_fn_append ac_header_list " stdlib.h" +as_fn_append ac_header_list " unistd.h" +as_fn_append ac_header_list " sys/param.h" +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + +ac_config_headers="$ac_config_headers config.h" + + +. ${srcdir}/Config/version.mk +echo "configuring for zsh $VERSION" + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + + +cat >>confdefs.h <<_ACEOF +#define MACHTYPE "$host_cpu" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VENDOR "$host_vendor" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define OSTYPE "$host_os" +_ACEOF + + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + + +# Un-double any \ or $ (doubled by AC_ARG_PROGRAM). +cat <<\EOF_SED > conftestsed +s,\\\\,\\,g; s,\$\$,$,g +EOF_SED +zsh_transform_name=`echo "${program_transform_name}" | sed -f conftestsed` +rm -f conftestsed +tzsh_name=`echo zsh | sed -e "${zsh_transform_name}"` +# Double any \ or $ in the transformed name that results. +cat <<\EOF_SED >> conftestsed +s,\\,\\\\,g; s,\$,$$,g +EOF_SED +tzsh=`echo ${tzsh_name} | sed -f conftestsed` +rm -f conftestsed + + +# Check whether --enable-cppflags was given. +if test "${enable_cppflags+set}" = set; then : + enableval=$enable_cppflags; if test "$enableval" = "yes" + then CPPFLAGS="$CPPFLAGS" + else CPPFLAGS="$enable_cppflags" + fi +fi + + # Check whether --enable-cflags was given. +if test "${enable_cflags+set}" = set; then : + enableval=$enable_cflags; if test "$enableval" = "yes" + then CFLAGS="$CFLAGS" + else CFLAGS="$enable_cflags" + fi +fi + + # Check whether --enable-ldflags was given. +if test "${enable_ldflags+set}" = set; then : + enableval=$enable_ldflags; if test "$enableval" = "yes" + then LDFLAGS="$LDFLAGS" + else LDFLAGS="$enable_ldflags" + fi +fi + + # Check whether --enable-libs was given. +if test "${enable_libs+set}" = set; then : + enableval=$enable_libs; if test "$enableval" = "yes" + then LIBS="$LIBS" + else LIBS="$enable_libs" + fi +fi + + + +# Check whether --enable-zsh-debug was given. +if test "${enable_zsh_debug+set}" = set; then : + enableval=$enable_zsh_debug; if test x$enableval = xyes; then + $as_echo "#define DEBUG 1" >>confdefs.h + +fi +fi + + + +# Check whether --enable-zsh-mem was given. +if test "${enable_zsh_mem+set}" = set; then : + enableval=$enable_zsh_mem; if test x$enableval = xyes; then + $as_echo "#define ZSH_MEM 1" >>confdefs.h + +fi +fi + + + +# Check whether --enable-zsh-mem-debug was given. +if test "${enable_zsh_mem_debug+set}" = set; then : + enableval=$enable_zsh_mem_debug; if test x$enableval = xyes; then + $as_echo "#define ZSH_MEM_DEBUG 1" >>confdefs.h + +fi +fi + + + +# Check whether --enable-zsh-mem-warning was given. +if test "${enable_zsh_mem_warning+set}" = set; then : + enableval=$enable_zsh_mem_warning; if test x$enableval = xyes; then + $as_echo "#define ZSH_MEM_WARNING 1" >>confdefs.h + +fi +fi + + + +# Check whether --enable-zsh-secure-free was given. +if test "${enable_zsh_secure_free+set}" = set; then : + enableval=$enable_zsh_secure_free; if test x$enableval = xyes; then + $as_echo "#define ZSH_SECURE_FREE 1" >>confdefs.h + +fi +fi + + + +# Check whether --enable-zsh-heap-debug was given. +if test "${enable_zsh_heap_debug+set}" = set; then : + enableval=$enable_zsh_heap_debug; if test x$enableval = xyes; then + $as_echo "#define ZSH_HEAP_DEBUG 1" >>confdefs.h + +fi +fi + + + +# Check whether --enable-zsh-valgrind was given. +if test "${enable_zsh_valgrind+set}" = set; then : + enableval=$enable_zsh_valgrind; if test x$enableval = xyes; then + $as_echo "#define ZSH_VALGRIND 1" >>confdefs.h + +fi +fi + + + +# Check whether --enable-zsh-hash-debug was given. +if test "${enable_zsh_hash_debug+set}" = set; then : + enableval=$enable_zsh_hash_debug; if test x$enableval = xyes; then + $as_echo "#define ZSH_HASH_DEBUG 1" >>confdefs.h + +fi +fi + + + +# Check whether --enable-stack-allocation was given. +if test "${enable_stack_allocation+set}" = set; then : + enableval=$enable_stack_allocation; if test x$enableval = xyes; then + $as_echo "#define USE_STACK_ALLOCATION 1" >>confdefs.h + +fi +fi + + +# Check whether --enable-etcdir was given. +if test "${enable_etcdir+set}" = set; then : + enableval=$enable_etcdir; etcdir="$enableval" +else + etcdir=/etc +fi + + +# Check whether --enable-zshenv was given. +if test "${enable_zshenv+set}" = set; then : + enableval=$enable_zshenv; zshenv="$enableval" +else + if test "x$etcdir" = xno; then + zshenv=no +else + zshenv="$etcdir/zshenv" +fi +fi + + +if test "x$zshenv" != xno; then + cat >>confdefs.h <<_ACEOF +#define GLOBAL_ZSHENV "$zshenv" +_ACEOF + +fi + +# Check whether --enable-zshrc was given. +if test "${enable_zshrc+set}" = set; then : + enableval=$enable_zshrc; zshrc="$enableval" +else + if test "x$etcdir" = xno; then + zshrc=no +else + zshrc="$etcdir/zshrc" +fi +fi + + +if test "x$zshrc" != xno; then + cat >>confdefs.h <<_ACEOF +#define GLOBAL_ZSHRC "$zshrc" +_ACEOF + +fi + +# Check whether --enable-zprofile was given. +if test "${enable_zprofile+set}" = set; then : + enableval=$enable_zprofile; zprofile="$enableval" +else + if test "x$etcdir" = xno; then + zprofile=no +else + zprofile="$etcdir/zprofile" +fi +fi + + +if test "x$zprofile" != xno; then + cat >>confdefs.h <<_ACEOF +#define GLOBAL_ZPROFILE "$zprofile" +_ACEOF + +fi + +# Check whether --enable-zlogin was given. +if test "${enable_zlogin+set}" = set; then : + enableval=$enable_zlogin; zlogin="$enableval" +else + if test "x$etcdir" = xno; then + zlogin=no +else + zlogin="$etcdir/zlogin" +fi +fi + + +if test "x$zlogin" != xno; then + cat >>confdefs.h <<_ACEOF +#define GLOBAL_ZLOGIN "$zlogin" +_ACEOF + +fi + +# Check whether --enable-zlogout was given. +if test "${enable_zlogout+set}" = set; then : + enableval=$enable_zlogout; zlogout="$enableval" +else + if test "x$etcdir" = xno; then + zlogout=no +else + zlogout="$etcdir/zlogout" +fi +fi + + +if test "x$zlogout" != xno; then + cat >>confdefs.h <<_ACEOF +#define GLOBAL_ZLOGOUT "$zlogout" +_ACEOF + +fi + + +# Check whether --enable-dynamic was given. +if test "${enable_dynamic+set}" = set; then : + enableval=$enable_dynamic; dynamic="$enableval" +else + dynamic=yes +fi + + + +# Check whether --enable-restricted-r was given. +if test "${enable_restricted_r+set}" = set; then : + enableval=$enable_restricted_r; if test x$enableval = xyes; then + $as_echo "#define RESTRICTED_R 1" >>confdefs.h + +fi +else + $as_echo "#define RESTRICTED_R 1" >>confdefs.h + + +fi + + + +# Check whether --enable-locale was given. +if test "${enable_locale+set}" = set; then : + enableval=$enable_locale; if test x$enableval = xyes; then + $as_echo "#define CONFIG_LOCALE 1" >>confdefs.h + +fi +else + $as_echo "#define CONFIG_LOCALE 1" >>confdefs.h + + +fi + + +# Check whether --enable-ansi2knr was given. +if test "${enable_ansi2knr+set}" = set; then : + enableval=$enable_ansi2knr; ansi2knr="$enableval" +else + ansi2knr=default +fi + + +# Check whether --enable-runhelpdir was given. +if test "${enable_runhelpdir+set}" = set; then : + enableval=$enable_runhelpdir; if test x"$enableval" = xno; then + runhelpdir= +else + runhelpdir="$enableval" +fi +else + runhelpdir=yes +fi + +if test x"$runhelpdir" = xyes; then + runhelpdir=${datadir}/${tzsh_name}/'${VERSION}'/help +fi +if test x"$runhelpdir" = x; then + runhelp= +else + runhelp=runhelp +fi + +# Check whether --enable-fndir was given. +if test "${enable_fndir+set}" = set; then : + enableval=$enable_fndir; if test x$enableval = xyes; then + fndir=${datadir}/${tzsh_name}/'${VERSION}'/functions +else + fndir="$enableval" +fi +else + fndir=${datadir}/${tzsh_name}/'${VERSION}'/functions +fi + + +# Check whether --enable-site-fndir was given. +if test "${enable_site_fndir+set}" = set; then : + enableval=$enable_site_fndir; if test x$enableval = xyes; then + sitefndir=${datadir}/${tzsh_name}/site-functions +else + sitefndir="$enableval" +fi +else + sitefndir=${datadir}/${tzsh_name}/site-functions +fi + + +if test X$sitefndir = X/usr/local/share/zsh/site-functions || \ + test X$sitefndir = Xno +then fixed_sitefndir='' +elif test X$prefix != X/usr/local; then + if test X$prefix = XNONE && test X$ac_default_prefix = X/usr/local; then + if test X$tzsh_name != Xzsh + then fixed_sitefndir=/usr/local/share/zsh/site-functions + else fixed_sitefndir='' + fi + else fixed_sitefndir=/usr/local/share/zsh/site-functions + fi +elif test X$tzsh_name != Xzsh +then fixed_sitefndir=/usr/local/share/zsh/site-functions +else fixed_sitefndir='' +fi + + +# Check whether --enable-function-subdirs was given. +if test "${enable_function_subdirs+set}" = set; then : + enableval=$enable_function_subdirs; +fi + + +if test "x${enable_function_subdirs}" != x && + test "x${enable_function_subdirs}" != xno; then + FUNCTIONS_SUBDIRS=yes +else + FUNCTIONS_SUBDIRS=no +fi + +# Check whether --enable-additional-fpath was given. +if test "${enable_additional_fpath+set}" = set; then : + enableval=$enable_additional_fpath; if test x$enableval = xyes; then + additionalfpath="" +else + additionalfpath="${enableval}" +fi +else + additionalfpath="" +fi + + + + +# Check whether --enable-scriptdir was given. +if test "${enable_scriptdir+set}" = set; then : + enableval=$enable_scriptdir; if test x$enableval = xyes; then + scriptdir=${datadir}/${tzsh_name}/'${VERSION}'/scripts +else + scriptdir="$enableval" +fi +else + scriptdir=${datadir}/${tzsh_name}/'${VERSION}'/scripts +fi + + +# Check whether --enable-site-scriptdir was given. +if test "${enable_site_scriptdir+set}" = set; then : + enableval=$enable_site_scriptdir; if test x$enableval = xyes; then + sitescriptdir=${datadir}/${tzsh_name}/scripts +else + sitescriptdir="$enableval" +fi +else + sitescriptdir=${datadir}/${tzsh_name}/scripts +fi + + + +if test x$htmldir = x'${docdir}' || test x$htmldir = x; then + htmldir='$(datadir)/$(tzsh)/htmldoc' +fi + + +# Check whether --enable-custom-patchlevel was given. +if test "${enable_custom_patchlevel+set}" = set; then : + enableval=$enable_custom_patchlevel; if test x$enableval != x && test x$enableval != xno; then + cat >>confdefs.h <<_ACEOF +#define CUSTOM_PATCHLEVEL "$enableval" +_ACEOF + +fi +fi + + + +# Check whether --enable-maildir-support was given. +if test "${enable_maildir_support+set}" = set; then : + enableval=$enable_maildir_support; if test x$enableval = xyes; then + $as_echo "#define MAILDIR_SUPPORT 1" >>confdefs.h + +fi +fi + + + +# Check whether --enable-max-function-depth was given. +if test "${enable_max_function_depth+set}" = set; then : + enableval=$enable_max_function_depth; if test x$enableval = xyes; then + $as_echo "#define MAX_FUNCTION_DEPTH 500" >>confdefs.h + +elif test x$enableval != xno; then + cat >>confdefs.h <<_ACEOF +#define MAX_FUNCTION_DEPTH $enableval +_ACEOF + +fi +else + $as_echo "#define MAX_FUNCTION_DEPTH 500" >>confdefs.h + + +fi + + + +# Check whether --enable-readnullcmd was given. +if test "${enable_readnullcmd+set}" = set; then : + enableval=$enable_readnullcmd; if test x$enableval = xyes; then + $as_echo "#define DEFAULT_READNULLCMD \"more\"" >>confdefs.h + +elif test x$enableval != xno; then + cat >>confdefs.h <<_ACEOF +#define DEFAULT_READNULLCMD "$enableval" +_ACEOF + +fi +else + $as_echo "#define DEFAULT_READNULLCMD \"more\"" >>confdefs.h + + +fi + + +# Check whether --enable-cap was given. +if test "${enable_cap+set}" = set; then : + enableval=$enable_cap; +fi + + +# Check whether --enable-gdbm was given. +if test "${enable_gdbm+set}" = set; then : + enableval=$enable_gdbm; gdbm="$enableval" +else + gdbm=yes +fi + + +test -z "${CFLAGS+set}" && CFLAGS= auto_cflags=1 +test -z "${LDFLAGS+set}" && LDFLAGS= auto_ldflags=1 + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +if test "$host" = mips-sni-sysv4 && test -n "$GCC"; then + : +else + +# Check whether --enable-largefile was given. +if test "${enable_largefile+set}" = set; then : + enableval=$enable_largefile; +fi + +if test "$enable_largefile" != no; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 +$as_echo_n "checking for special C compiler options needed for large files... " >&6; } +if ${ac_cv_sys_largefile_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + if ac_fn_c_try_compile "$LINENO"; then : + break +fi +rm -f core conftest.err conftest.$ac_objext + CC="$CC -n32" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_largefile_CC=' -n32'; break +fi +rm -f core conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 +$as_echo "$ac_cv_sys_largefile_CC" >&6; } + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } +if ${ac_cv_sys_file_offset_bits+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=64; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_file_offset_bits=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 +$as_echo "$ac_cv_sys_file_offset_bits" >&6; } +case $ac_cv_sys_file_offset_bits in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF +;; +esac +rm -rf conftest* + if test $ac_cv_sys_file_offset_bits = unknown; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 +$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } +if ${ac_cv_sys_large_files+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=1; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_large_files=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 +$as_echo "$ac_cv_sys_large_files" >&6; } +case $ac_cv_sys_large_files in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF +;; +esac +rm -rf conftest* + fi + + +fi + +fi + +if test -n "$auto_cflags" && test ."$ansi2knr" != .yes; then + if test "${enable_zsh_debug}" = yes; then + if test -n "$GCC"; then + CFLAGS="$CFLAGS -Wall -Wmissing-prototypes -ggdb" + else + CFLAGS="$CFLAGS -g" + fi + else + if test -n "$GCC"; then + CFLAGS="$CFLAGS -Wall -Wmissing-prototypes -O2" + else + CFLAGS="$CFLAGS -O" + fi + fi +fi +if test -n "$auto_ldflags"; then + case "${enable_zsh_debug}$host_os" in + yesaix*|yeshpux*|yesnetbsd*|yesopenbsd*) ;; # "ld -g" is not valid on these systems + darwin*) LDFLAGS=-Wl,-x ;; + yes*) LDFLAGS=-g ;; + *) LDFLAGS=-s ;; + esac +fi + +case "$host_os" in + sco*) CFLAGS="-D__sco $CFLAGS" ;; +esac + +sed=':1 + s/ -s / /g + t1 + s/^ *// + s/ *$//' + +case " $LDFLAGS " in + *" -s "*) strip_exeldflags=true strip_libldflags=true + LDFLAGS=`echo " $LDFLAGS " | sed "$sed"` ;; + *) strip_exeldflags=false strip_libldflags=false ;; +esac + +case " ${EXELDFLAGS+$EXELDFLAGS }" in + " ") ;; + *" -s "*) strip_exeldflags=true + EXELDFLAGS=`echo " $EXELDFLAGS " | sed "$sed"` ;; + *) strip_exeldflags=false ;; +esac + +case " ${LIBLDFLAGS+$LIBLDFLAGS }" in + " ") ;; + *" -s "*) strip_libldflags=true + LIBLDFLAGS=`echo " $LIBLDFLAGS " | sed "$sed"` ;; + *) strip_libldflags=false ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 +$as_echo_n "checking for an ANSI C-conforming const... " >&6; } +if ${ac_cv_c_const+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + +#ifndef __cplusplus + /* Ultrix mips cc rejects this sort of thing. */ + typedef int charset[2]; + const charset cs = { 0, 0 }; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this sort of thing. */ + char tx; + char *t = &tx; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; } bx; + struct s *b = &bx; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_const=yes +else + ac_cv_c_const=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 +$as_echo "$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +$as_echo "#define const /**/" >>confdefs.h + +fi + +case "$host_os" in + darwin[0-9].*) CPP="$CPP -traditional-cpp" ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${CC-cc} option to accept ANSI C" >&5 +$as_echo_n "checking for ${CC-cc} option to accept ANSI C... " >&6; } +if ${fp_cv_prog_cc_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + fp_cv_prog_cc_stdc=no +ac_save_CFLAGS="$CFLAGS" +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX -Ae or -Aa -D_HPUX_SOURCE +# SVR4 -Xc +# For HP-UX, we try -Ae first; this turns on ANSI but also extensions, +# as well as defining _HPUX_SOURCE, and we can then use long long. +# We keep the old version for backward compatibility. +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" -Xc +do + CFLAGS="$ac_save_CFLAGS $ac_arg" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __STDC__ +choke me +#endif + +int +main () +{ +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + fp_cv_prog_cc_stdc="$ac_arg"; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +CFLAGS="$ac_save_CFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $fp_cv_prog_cc_stdc" >&5 +$as_echo "$fp_cv_prog_cc_stdc" >&6; } +case "x$fp_cv_prog_cc_stdc" in + x|xno) ;; + *) CC="$CC $fp_cv_prog_cc_stdc" ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use prototypes" >&5 +$as_echo_n "checking whether to use prototypes... " >&6; } +if test ."$ansi2knr" = .yes || test ."$ansi2knr" = .no; then + msg="(overridden) " +else + msg= + if test ."$fp_cv_prog_cc_stdc" = .no; then + ansi2knr=yes + else + ansi2knr=no + fi +fi + +if test "$ansi2knr" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${msg}no" >&5 +$as_echo "${msg}no" >&6; } + U=_ +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${msg}yes" >&5 +$as_echo "${msg}yes" >&6; } + $as_echo "#define PROTOTYPES 1" >>confdefs.h + + U= +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works +# for constant arguments. Useless! +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working alloca.h" >&5 +$as_echo_n "checking for working alloca.h... " >&6; } +if ${ac_cv_working_alloca_h+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +char *p = (char *) alloca (2 * sizeof (int)); + if (p) return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_working_alloca_h=yes +else + ac_cv_working_alloca_h=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_working_alloca_h" >&5 +$as_echo "$ac_cv_working_alloca_h" >&6; } +if test $ac_cv_working_alloca_h = yes; then + +$as_echo "#define HAVE_ALLOCA_H 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for alloca" >&5 +$as_echo_n "checking for alloca... " >&6; } +if ${ac_cv_func_alloca_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __GNUC__ +# define alloca __builtin_alloca +#else +# ifdef _MSC_VER +# include +# define alloca _alloca +# else +# ifdef HAVE_ALLOCA_H +# include +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ +void *alloca (size_t); +# endif +# endif +# endif +# endif +#endif + +int +main () +{ +char *p = (char *) alloca (1); + if (p) return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_func_alloca_works=yes +else + ac_cv_func_alloca_works=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_alloca_works" >&5 +$as_echo "$ac_cv_func_alloca_works" >&6; } + +if test $ac_cv_func_alloca_works = yes; then + +$as_echo "#define HAVE_ALLOCA 1" >>confdefs.h + +else + # The SVR3 libPW and SVR4 libucb both contain incompatible functions +# that cause trouble. Some versions do not even contain alloca or +# contain a buggy version. If you still want to use their alloca, +# use ar to extract alloca.o from them instead of compiling alloca.c. + +ALLOCA=\${LIBOBJDIR}alloca.$ac_objext + +$as_echo "#define C_ALLOCA 1" >>confdefs.h + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether \`alloca.c' needs Cray hooks" >&5 +$as_echo_n "checking whether \`alloca.c' needs Cray hooks... " >&6; } +if ${ac_cv_os_cray+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#if defined CRAY && ! defined CRAY2 +webecray +#else +wenotbecray +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "webecray" >/dev/null 2>&1; then : + ac_cv_os_cray=yes +else + ac_cv_os_cray=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_os_cray" >&5 +$as_echo "$ac_cv_os_cray" >&6; } +if test $ac_cv_os_cray = yes; then + for ac_func in _getb67 GETB67 getb67; do + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + +cat >>confdefs.h <<_ACEOF +#define CRAY_STACKSEG_END $ac_func +_ACEOF + + break +fi + + done +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking stack direction for C alloca" >&5 +$as_echo_n "checking stack direction for C alloca... " >&6; } +if ${ac_cv_c_stack_direction+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_c_stack_direction=0 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +find_stack_direction (int *addr, int depth) +{ + int dir, dummy = 0; + if (! addr) + addr = &dummy; + *addr = addr < &dummy ? 1 : addr == &dummy ? 0 : -1; + dir = depth ? find_stack_direction (addr, depth - 1) : 0; + return dir + dummy; +} + +int +main (int argc, char **argv) +{ + return find_stack_direction (0, argc + !argv + 20) < 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_c_stack_direction=1 +else + ac_cv_c_stack_direction=-1 +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_stack_direction" >&5 +$as_echo "$ac_cv_c_stack_direction" >&6; } +cat >>confdefs.h <<_ACEOF +#define STACK_DIRECTION $ac_cv_c_stack_direction +_ACEOF + + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the compiler supports union initialisation" >&5 +$as_echo_n "checking if the compiler supports union initialisation... " >&6; } +if ${zsh_cv_c_have_union_init+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +union{void *p;long l;}u={0}; +int +main () +{ +u.l=1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_c_have_union_init=yes +else + zsh_cv_c_have_union_init=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_c_have_union_init" >&5 +$as_echo "$zsh_cv_c_have_union_init" >&6; } + +if test x$zsh_cv_c_have_union_init = xyes; then + $as_echo "#define HAVE_UNION_INIT 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if signed to unsigned casting is broken" >&5 +$as_echo_n "checking if signed to unsigned casting is broken... " >&6; } +if ${zsh_cv_c_broken_signed_to_unsigned_casting+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_c_broken_signed_to_unsigned_casting=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +main(){return((int)(unsigned char)((char) -1) == 255);} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_c_broken_signed_to_unsigned_casting=yes +else + zsh_cv_c_broken_signed_to_unsigned_casting=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_c_broken_signed_to_unsigned_casting" >&5 +$as_echo "$zsh_cv_c_broken_signed_to_unsigned_casting" >&6; } + +if test x$zsh_cv_c_broken_signed_to_unsigned_casting = xyes; then + $as_echo "#define BROKEN_SIGNED_TO_UNSIGNED_CASTING 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the compiler supports variable-length arrays" >&5 +$as_echo_n "checking if the compiler supports variable-length arrays... " >&6; } +if ${zsh_cv_c_variable_length_arrays+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo(), n; +int +main () +{ +int i[foo()], a[n+1]; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_c_variable_length_arrays=yes +else + zsh_cv_c_variable_length_arrays=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_c_variable_length_arrays" >&5 +$as_echo "$zsh_cv_c_variable_length_arrays" >&6; } + +if test x$zsh_cv_c_variable_length_arrays = xyes; then + $as_echo "#define HAVE_VARIABLE_LENGTH_ARRAYS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + # Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln works" >&5 +$as_echo_n "checking whether ln works... " >&6; } +if ${ac_cv_prog_LN+:} false; then : + $as_echo_n "(cached) " >&6 +else + rm -f conftestdata conftestlink +echo > conftestdata +if ln conftestdata conftestlink 2>/dev/null +then + rm -f conftestdata conftestlink + ac_cv_prog_LN="ln" +else + rm -f conftestdata + ac_cv_prog_LN="cp" +fi +fi +LN="$ac_cv_prog_LN" +if test "$ac_cv_prog_LN" = "ln"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + for ac_prog in yodl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_YODL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$YODL"; then + ac_cv_prog_YODL="$YODL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_YODL="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +YODL=$ac_cv_prog_YODL +if test -n "$YODL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $YODL" >&5 +$as_echo "$YODL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$YODL" && break +done +test -n "$YODL" || YODL=": yodl" + + +YODL_OPTIONS='' +if test "x$ac_cv_prog_YODL" = xyodl; then + case `yodl --version` in + *"version 2."*) YODL_OPTIONS='-k' ;; + *"version 3."*) YODL_OPTIONS='-k -L' ;; + *"version 4."*) YODL_OPTIONS='-k -L' ;; + esac +fi + + +for ac_prog in texi2dvi +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_TEXI2DVI+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$TEXI2DVI"; then + ac_cv_prog_TEXI2DVI="$TEXI2DVI" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_TEXI2DVI="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +TEXI2DVI=$ac_cv_prog_TEXI2DVI +if test -n "$TEXI2DVI"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TEXI2DVI" >&5 +$as_echo "$TEXI2DVI" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$TEXI2DVI" && break +done +test -n "$TEXI2DVI" || TEXI2DVI=": texi2dvi" + +for ac_prog in texi2pdf +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_TEXI2PDF+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$TEXI2PDF"; then + ac_cv_prog_TEXI2PDF="$TEXI2PDF" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_TEXI2PDF="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +TEXI2PDF=$ac_cv_prog_TEXI2PDF +if test -n "$TEXI2PDF"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TEXI2PDF" >&5 +$as_echo "$TEXI2PDF" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$TEXI2PDF" && break +done +test -n "$TEXI2PDF" || TEXI2PDF=": texi2pdf" + +for ac_prog in texi2any texi2html +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_TEXI2HTML+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$TEXI2HTML"; then + ac_cv_prog_TEXI2HTML="$TEXI2HTML" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_TEXI2HTML="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +TEXI2HTML=$ac_cv_prog_TEXI2HTML +if test -n "$TEXI2HTML"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TEXI2HTML" >&5 +$as_echo "$TEXI2HTML" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$TEXI2HTML" && break +done +test -n "$TEXI2HTML" || TEXI2HTML=": texi2html" + + +if test x"$TEXI2PDF" != xtexi2pdf && test x"$TEXI2DVI" = xtexi2dvi; then + TEXI2PDF='texi2dvi --pdf' +fi + +if test x"$TEXI2HTML" = xtexi2any; then + TEXI2HTML='texi2any -c TEXI2HTML=1' +fi + +case "$LC_PAPER" in + ??_US*) PAPERSIZE=us ;; + *) PAPERSIZE=a4 ;; +esac + + +for ac_prog in ansi2knr +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ANSI2KNR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ANSI2KNR"; then + ac_cv_prog_ANSI2KNR="$ANSI2KNR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ANSI2KNR="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ANSI2KNR=$ac_cv_prog_ANSI2KNR +if test -n "$ANSI2KNR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ANSI2KNR" >&5 +$as_echo "$ANSI2KNR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ANSI2KNR" && break +done +test -n "$ANSI2KNR" || ANSI2KNR=": ansi2knr" + + +if test x"$ansi2knr" = xyes && test x"$ANSI2KNR" = x": ansi2knr"; then + echo "----------" + echo "configure fatal error:" + echo "ansi2knr was specified (--enable-ansi2knr) but the program could not be found." + echo "Either remove the configure option if it is not required or build the ansi2knr" + echo "program before reconfiguring Zsh. The source code for ansi2knr is also" + echo "available in the GPL directory on Zsh distribution sites." + exit 1 +fi + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do + as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5 +$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; } +if eval \${$as_ac_Header+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include <$ac_hdr> + +int +main () +{ +if ((DIR *) 0) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$as_ac_Header=yes" +else + eval "$as_ac_Header=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$as_ac_Header + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 +_ACEOF + +ac_header_dirent=$ac_hdr; break +fi + +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 +$as_echo_n "checking for library containing opendir... " >&6; } +if ${ac_cv_search_opendir+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char opendir (); +int +main () +{ +return opendir (); + ; + return 0; +} +_ACEOF +for ac_lib in '' dir; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_opendir=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_opendir+:} false; then : + break +fi +done +if ${ac_cv_search_opendir+:} false; then : + +else + ac_cv_search_opendir=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 +$as_echo "$ac_cv_search_opendir" >&6; } +ac_res=$ac_cv_search_opendir +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 +$as_echo_n "checking for library containing opendir... " >&6; } +if ${ac_cv_search_opendir+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char opendir (); +int +main () +{ +return opendir (); + ; + return 0; +} +_ACEOF +for ac_lib in '' x; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_opendir=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_opendir+:} false; then : + break +fi +done +if ${ac_cv_search_opendir+:} false; then : + +else + ac_cv_search_opendir=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5 +$as_echo "$ac_cv_search_opendir" >&6; } +ac_res=$ac_cv_search_opendir +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 +$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } +if ${ac_cv_header_time+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_time=yes +else + ac_cv_header_time=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5 +$as_echo "$ac_cv_header_time" >&6; } +if test $ac_cv_header_time = yes; then + +$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat file-mode macros are broken" >&5 +$as_echo_n "checking whether stat file-mode macros are broken... " >&6; } +if ${ac_cv_header_stat_broken+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include + +#if defined S_ISBLK && defined S_IFDIR +extern char c1[S_ISBLK (S_IFDIR) ? -1 : 1]; +#endif + +#if defined S_ISBLK && defined S_IFCHR +extern char c2[S_ISBLK (S_IFCHR) ? -1 : 1]; +#endif + +#if defined S_ISLNK && defined S_IFREG +extern char c3[S_ISLNK (S_IFREG) ? -1 : 1]; +#endif + +#if defined S_ISSOCK && defined S_IFREG +extern char c4[S_ISSOCK (S_IFREG) ? -1 : 1]; +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stat_broken=no +else + ac_cv_header_stat_broken=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stat_broken" >&5 +$as_echo "$ac_cv_header_stat_broken" >&6; } +if test $ac_cv_header_stat_broken = yes; then + +$as_echo "#define STAT_MACROS_BROKEN 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5 +$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } +if ${ac_cv_header_sys_wait_h+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +int +main () +{ + int s; + wait (&s); + s = WIFEXITED (s) ? WEXITSTATUS (s) : 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_sys_wait_h=yes +else + ac_cv_header_sys_wait_h=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5 +$as_echo "$ac_cv_header_sys_wait_h" >&6; } +if test $ac_cv_header_sys_wait_h = yes; then + +$as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h + +fi + + +oldcflags="$CFLAGS" + +for ac_header in sys/time.h sys/times.h sys/select.h termcap.h termio.h \ + termios.h sys/param.h sys/filio.h string.h memory.h \ + limits.h fcntl.h libc.h sys/utsname.h sys/resource.h \ + locale.h errno.h stdio.h stdarg.h varargs.h stdlib.h \ + unistd.h sys/capability.h \ + utmp.h utmpx.h sys/types.h pwd.h grp.h poll.h sys/mman.h \ + netinet/in_systm.h langinfo.h wchar.h stddef.h \ + sys/stropts.h iconv.h ncurses.h ncursesw/ncurses.h \ + ncurses/ncurses.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +if test x$dynamic = xyes; then + for ac_header in dlfcn.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default" +if test "x$ac_cv_header_dlfcn_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DLFCN_H 1 +_ACEOF + +fi + +done + + for ac_header in dl.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "dl.h" "ac_cv_header_dl_h" "$ac_includes_default" +if test "x$ac_cv_header_dl_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DL_H 1 +_ACEOF + +fi + +done + +fi + + +if test x$ac_cv_header_sys_time_h = xyes && test x$ac_cv_header_sys_select_h = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for conflicts in sys/time.h and sys/select.h" >&5 +$as_echo_n "checking for conflicts in sys/time.h and sys/select.h... " >&6; } +if ${zsh_cv_header_time_h_select_h_conflicts+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_header_time_h_select_h_conflicts=no +else + zsh_cv_header_time_h_select_h_conflicts=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_header_time_h_select_h_conflicts" >&5 +$as_echo "$zsh_cv_header_time_h_select_h_conflicts" >&6; } + if test x$zsh_cv_header_time_h_select_h_conflicts = xyes; then + $as_echo "#define TIME_H_SELECT_H_CONFLICTS 1" >>confdefs.h + + fi +fi + + +if test x$ac_cv_header_termios_h = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking TIOCGWINSZ in termios.h" >&5 +$as_echo_n "checking TIOCGWINSZ in termios.h... " >&6; } +if ${zsh_cv_header_termios_h_tiocgwinsz+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#include +int +main () +{ +int x = TIOCGWINSZ; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + zsh_cv_header_termios_h_tiocgwinsz=yes +else + zsh_cv_header_termios_h_tiocgwinsz=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_header_termios_h_tiocgwinsz" >&5 +$as_echo "$zsh_cv_header_termios_h_tiocgwinsz" >&6; } +else + zsh_cv_header_termios_h_tiocgwinsz=no +fi +if test x$zsh_cv_header_termios_h_tiocgwinsz = xno; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking TIOCGWINSZ in sys/ioctl.h" >&5 +$as_echo_n "checking TIOCGWINSZ in sys/ioctl.h... " >&6; } +if ${zsh_cv_header_sys_ioctl_h_tiocgwinsz+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#include +int +main () +{ +int x = TIOCGWINSZ; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + zsh_cv_header_sys_ioctl_h_tiocgwinsz=yes +else + zsh_cv_header_sys_ioctl_h_tiocgwinsz=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_header_sys_ioctl_h_tiocgwinsz" >&5 +$as_echo "$zsh_cv_header_sys_ioctl_h_tiocgwinsz" >&6; } + if test x$zsh_cv_header_sys_ioctl_h_tiocgwinsz = xyes; then + $as_echo "#define GWINSZ_IN_SYS_IOCTL 1" >>confdefs.h + + fi +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for streams headers including struct winsize" >&5 +$as_echo_n "checking for streams headers including struct winsize... " >&6; } +if ${ac_cv_winsize_in_ptem+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ +struct winsize wsz + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_winsize_in_ptem=yes +else + ac_cv_winsize_in_ptem=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_winsize_in_ptem" >&5 +$as_echo "$ac_cv_winsize_in_ptem" >&6; } +if test x$ac_cv_winsize_in_ptem = xyes; then + $as_echo "#define WINSIZE_IN_PTEM 1" >>confdefs.h + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for printf in -lc" >&5 +$as_echo_n "checking for printf in -lc... " >&6; } +if ${ac_cv_lib_c_printf+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lc $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char printf (); +int +main () +{ +return printf (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_c_printf=yes +else + ac_cv_lib_c_printf=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_printf" >&5 +$as_echo "$ac_cv_lib_c_printf" >&6; } +if test "x$ac_cv_lib_c_printf" = xyes; then : + LIBS="$LIBS -lc" +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pow in -lm" >&5 +$as_echo_n "checking for pow in -lm... " >&6; } +if ${ac_cv_lib_m_pow+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lm $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pow (); +int +main () +{ +return pow (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_m_pow=yes +else + ac_cv_lib_m_pow=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_pow" >&5 +$as_echo "$ac_cv_lib_m_pow" >&6; } +if test "x$ac_cv_lib_m_pow" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBM 1 +_ACEOF + + LIBS="-lm $LIBS" + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5 +$as_echo_n "checking for clock_gettime in -lrt... " >&6; } +if ${ac_cv_lib_rt_clock_gettime+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lrt $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char clock_gettime (); +int +main () +{ +return clock_gettime (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_rt_clock_gettime=yes +else + ac_cv_lib_rt_clock_gettime=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_gettime" >&5 +$as_echo "$ac_cv_lib_rt_clock_gettime" >&6; } +if test "x$ac_cv_lib_rt_clock_gettime" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBRT 1 +_ACEOF + + LIBS="-lrt $LIBS" + +fi + + +if test x$ac_cv_header_ncurses_h = xyes || test x$ac_cv_header_ncurses_ncurses_h = xyes || test x$ac_cv_header_ncursesw_ncurses_h = xyes; then + ncursesw_test=ncursesw + ncurses_test=ncurses +else + ncursesw_test= + ncurses_test= +fi + + +# Check whether --with-term-lib was given. +if test "${with_term_lib+set}" = set; then : + withval=$with_term_lib; if test "x$withval" != xno && test "x$withval" != x ; then + termcap_curses_order="$withval" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing tigetstr" >&5 +$as_echo_n "checking for library containing tigetstr... " >&6; } +if ${ac_cv_search_tigetstr+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char tigetstr (); +int +main () +{ +return tigetstr (); + ; + return 0; +} +_ACEOF +for ac_lib in '' $termcap_curses_order; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_tigetstr=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_tigetstr+:} false; then : + break +fi +done +if ${ac_cv_search_tigetstr+:} false; then : + +else + ac_cv_search_tigetstr=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_tigetstr" >&5 +$as_echo "$ac_cv_search_tigetstr" >&6; } +ac_res=$ac_cv_search_tigetstr +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +else + termcap_curses_order="$ncursesw_test tinfo termcap $ncurses_test curses" +fi +else + case "$host_os" in + solaris*) + termcap_curses_order="$ncursesw_test $ncurses_test curses termcap" ;; + hpux10.*|hpux11.*) + DL_EXT="${DL_EXT=sl}" + termcap_curses_order="Hcurses $ncursesw_test $ncurses_test curses termcap" ;; + *) + termcap_curses_order="$ncursesw_test tinfo termcap $ncurses_test curses" ;; +esac +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if _XOPEN_SOURCE_EXTENDED should not be defined" >&5 +$as_echo_n "checking if _XOPEN_SOURCE_EXTENDED should not be defined... " >&6; } +if ${zsh_cv_no_xopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + case "$host_os" in + *freebsd5*|*freebsd6.[012]*|*aix*) + zsh_cv_no_xopen=yes + ;; + *) + zsh_cv_no_xopen=no + ;; +esac +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_no_xopen" >&5 +$as_echo "$zsh_cv_no_xopen" >&6; } +if test x$zsh_cv_no_xopen = xyes; then + $as_echo "#define ZSH_NO_XOPEN 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing tigetflag" >&5 +$as_echo_n "checking for library containing tigetflag... " >&6; } +if ${ac_cv_search_tigetflag+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char tigetflag (); +int +main () +{ +return tigetflag (); + ; + return 0; +} +_ACEOF +for ac_lib in '' $termcap_curses_order; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_tigetflag=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_tigetflag+:} false; then : + break +fi +done +if ${ac_cv_search_tigetflag+:} false; then : + +else + ac_cv_search_tigetflag=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_tigetflag" >&5 +$as_echo "$ac_cv_search_tigetflag" >&6; } +ac_res=$ac_cv_search_tigetflag +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing tgetent" >&5 +$as_echo_n "checking for library containing tgetent... " >&6; } +if ${ac_cv_search_tgetent+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char tgetent (); +int +main () +{ +return tgetent (); + ; + return 0; +} +_ACEOF +for ac_lib in '' $termcap_curses_order; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_tgetent=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_tgetent+:} false; then : + break +fi +done +if ${ac_cv_search_tgetent+:} false; then : + +else + ac_cv_search_tgetent=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_tgetent" >&5 +$as_echo "$ac_cv_search_tgetent" >&6; } +ac_res=$ac_cv_search_tgetent +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + true +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 255 "\"No terminal handling library was found on your system. +This is probably a library called 'curses' or 'ncurses'. You may +need to install a package called 'curses-devel' or 'ncurses-devel' on your +system.\" +See \`config.log' for more details" "$LINENO" 5; } +fi + +for ac_header in curses.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "curses.h" "ac_cv_header_curses_h" "$ac_includes_default" +if test "x$ac_cv_header_curses_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_CURSES_H 1 +_ACEOF + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Solaris 8 curses.h mistake" >&5 +$as_echo_n "checking for Solaris 8 curses.h mistake... " >&6; } +if ${ac_cv_header_curses_solaris+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_curses_h=yes +ac_cv_header_curses_solaris=yes +else + ac_cv_header_curses_h=no +ac_cv_header_curses_solaris=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_curses_solaris" >&5 +$as_echo "$ac_cv_header_curses_solaris" >&6; } +if test x$ac_cv_header_curses_solaris = xyes; then +$as_echo "#define HAVE_CURSES_H 1" >>confdefs.h + +fi +fi + +done + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if we need to ignore ncurses" >&5 +$as_echo_n "checking if we need to ignore ncurses... " >&6; } +if ${zsh_cv_ignore_ncurses+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $LIBS in + *-lncurses*) + zsh_cv_ignore_ncurses=no + ;; + *) + zsh_cv_ignore_ncurses=yes + ;; +esac +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_ignore_ncurses" >&5 +$as_echo "$zsh_cv_ignore_ncurses" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing getpwnam" >&5 +$as_echo_n "checking for library containing getpwnam... " >&6; } +if ${ac_cv_search_getpwnam+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char getpwnam (); +int +main () +{ +return getpwnam (); + ; + return 0; +} +_ACEOF +for ac_lib in '' nsl; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_getpwnam=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_getpwnam+:} false; then : + break +fi +done +if ${ac_cv_search_getpwnam+:} false; then : + +else + ac_cv_search_getpwnam=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_getpwnam" >&5 +$as_echo "$ac_cv_search_getpwnam" >&6; } +ac_res=$ac_cv_search_getpwnam +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + +if test `echo $host_os | sed 's/^\(unicos\).*/\1/'` = unicos; then + LIBS="-lcraylm -lkrb -lnisdb -lnsl -lrpcsvc $LIBS" +fi + +if test "x$dynamic" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBDL 1 +_ACEOF + + LIBS="-ldl $LIBS" + +fi + +fi + +if test x$enable_cap = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cap_get_proc in -lcap" >&5 +$as_echo_n "checking for cap_get_proc in -lcap... " >&6; } +if ${ac_cv_lib_cap_cap_get_proc+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcap $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char cap_get_proc (); +int +main () +{ +return cap_get_proc (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_cap_cap_get_proc=yes +else + ac_cv_lib_cap_cap_get_proc=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cap_cap_get_proc" >&5 +$as_echo "$ac_cv_lib_cap_cap_get_proc" >&6; } +if test "x$ac_cv_lib_cap_cap_get_proc" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBCAP 1 +_ACEOF + + LIBS="-lcap $LIBS" + +fi + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lsocket" >&5 +$as_echo_n "checking for socket in -lsocket... " >&6; } +if ${ac_cv_lib_socket_socket+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsocket $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char socket (); +int +main () +{ +return socket (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_socket_socket=yes +else + ac_cv_lib_socket_socket=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_socket" >&5 +$as_echo "$ac_cv_lib_socket_socket" >&6; } +if test "x$ac_cv_lib_socket_socket" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBSOCKET 1 +_ACEOF + + LIBS="-lsocket $LIBS" + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gethostbyname2" >&5 +$as_echo_n "checking for library containing gethostbyname2... " >&6; } +if ${ac_cv_search_gethostbyname2+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char gethostbyname2 (); +int +main () +{ +return gethostbyname2 (); + ; + return 0; +} +_ACEOF +for ac_lib in '' bind; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_gethostbyname2=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_gethostbyname2+:} false; then : + break +fi +done +if ${ac_cv_search_gethostbyname2+:} false; then : + +else + ac_cv_search_gethostbyname2=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostbyname2" >&5 +$as_echo "$ac_cv_search_gethostbyname2" >&6; } +ac_res=$ac_cv_search_gethostbyname2 +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + +case $LIBS in + *-lbind*) + for ac_header in bind/netdb.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "bind/netdb.h" "ac_cv_header_bind_netdb_h" "$ac_includes_default" +if test "x$ac_cv_header_bind_netdb_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_BIND_NETDB_H 1 +_ACEOF + +fi + +done + + ;; +esac + + +if test "x$ac_cv_header_iconv_h" = "xyes"; then + ac_fn_c_check_func "$LINENO" "iconv" "ac_cv_func_iconv" +if test "x$ac_cv_func_iconv" = xyes; then : + ac_found_iconv=yes +else + ac_found_iconv=no +fi + + if test "x$ac_found_iconv" = "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv in -liconv" >&5 +$as_echo_n "checking for iconv in -liconv... " >&6; } +if ${ac_cv_lib_iconv_iconv+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-liconv $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char iconv (); +int +main () +{ +return iconv (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_iconv_iconv=yes +else + ac_cv_lib_iconv_iconv=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_iconv_iconv" >&5 +$as_echo "$ac_cv_lib_iconv_iconv" >&6; } +if test "x$ac_cv_lib_iconv_iconv" = xyes; then : + ac_found_iconv=yes +fi + + if test "x$ac_found_iconv" = "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libiconv in -liconv" >&5 +$as_echo_n "checking for libiconv in -liconv... " >&6; } +if ${ac_cv_lib_iconv_libiconv+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-liconv $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char libiconv (); +int +main () +{ +return libiconv (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_iconv_libiconv=yes +else + ac_cv_lib_iconv_libiconv=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_iconv_libiconv" >&5 +$as_echo "$ac_cv_lib_iconv_libiconv" >&6; } +if test "x$ac_cv_lib_iconv_libiconv" = xyes; then : + ac_found_iconv=yes +fi + + fi + if test "x$ac_found_iconv" != "xno"; then + LIBS="-liconv $LIBS" + fi + else + ac_fn_c_check_decl "$LINENO" "_libiconv_version" "ac_cv_have_decl__libiconv_version" " #include +" +if test "x$ac_cv_have_decl__libiconv_version" = xyes; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libiconv in -liconv" >&5 +$as_echo_n "checking for libiconv in -liconv... " >&6; } +if ${ac_cv_lib_iconv_libiconv+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-liconv $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char libiconv (); +int +main () +{ +return libiconv (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_iconv_libiconv=yes +else + ac_cv_lib_iconv_libiconv=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_iconv_libiconv" >&5 +$as_echo "$ac_cv_lib_iconv_libiconv" >&6; } +if test "x$ac_cv_lib_iconv_libiconv" = xyes; then : + LIBS="-liconv $LIBS" +fi + +fi + + fi +fi + +if test "x$ac_found_iconv" = xyes; then + +$as_echo "#define HAVE_ICONV 1" >>confdefs.h + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +int myversion = _libiconv_version + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + $as_echo "#define ICONV_FROM_LIBICONV 1" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi + +if test "x$ac_found_iconv" = "xyes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv declaration" >&5 +$as_echo_n "checking for iconv declaration... " >&6; } +if ${ac_cv_iconv_const+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include +int +main () +{ +#ifdef __cplusplus + "C" + #endif + #if defined(__STDC__) || defined(__cplusplus) + size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); + #else + size_t iconv(); + #endif + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_iconv_const= +else + ac_cv_iconv_const=const +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_iconv_const" >&5 +$as_echo "$ac_cv_iconv_const" >&6; } + +cat >>confdefs.h <<_ACEOF +#define ICONV_CONST $ac_cv_iconv_const +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if an include file defines ospeed" >&5 +$as_echo_n "checking if an include file defines ospeed... " >&6; } +if ${zsh_cv_decl_ospeed_include_defines+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#if HAVE_TERMIOS_H +#include +#endif +#if HAVE_TERMCAP_H +#include +#endif +int +main () +{ +ospeed = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + zsh_cv_decl_ospeed_include_defines=yes +else + zsh_cv_decl_ospeed_include_defines=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_decl_ospeed_include_defines" >&5 +$as_echo "$zsh_cv_decl_ospeed_include_defines" >&6; } + +if test x$zsh_cv_decl_ospeed_include_defines = xno; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if you must define ospeed" >&5 +$as_echo_n "checking if you must define ospeed... " >&6; } +if ${zsh_cv_decl_ospeed_must_define+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +extern short ospeed; ospeed = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + zsh_cv_decl_ospeed_must_define=yes +else + zsh_cv_decl_ospeed_must_define=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_decl_ospeed_must_define" >&5 +$as_echo "$zsh_cv_decl_ospeed_must_define" >&6; } +fi + + + +if test x$zsh_cv_decl_ospeed_include_defines = xyes; then + $as_echo "#define HAVE_OSPEED 1" >>confdefs.h + +elif test x$zsh_cv_decl_ospeed_must_define = xyes; then + $as_echo "#define HAVE_OSPEED 1" >>confdefs.h + + $as_echo "#define MUST_DEFINE_OSPEED 1" >>confdefs.h + +fi + +if test x$gdbm != xno; then + for ac_header in gdbm.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "gdbm.h" "ac_cv_header_gdbm_h" "$ac_includes_default" +if test "x$ac_cv_header_gdbm_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_GDBM_H 1 +_ACEOF + +fi + +done + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gdbm_open in -lgdbm" >&5 +$as_echo_n "checking for gdbm_open in -lgdbm... " >&6; } +if ${ac_cv_lib_gdbm_gdbm_open+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lgdbm $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char gdbm_open (); +int +main () +{ +return gdbm_open (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_gdbm_gdbm_open=yes +else + ac_cv_lib_gdbm_gdbm_open=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gdbm_gdbm_open" >&5 +$as_echo "$ac_cv_lib_gdbm_gdbm_open" >&6; } +if test "x$ac_cv_lib_gdbm_gdbm_open" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBGDBM 1 +_ACEOF + + LIBS="-lgdbm $LIBS" + +fi + +fi + +for ac_header in sys/xattr.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "sys/xattr.h" "ac_cv_header_sys_xattr_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_xattr_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SYS_XATTR_H 1 +_ACEOF + +fi + +done + + + +ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" +if test "x$ac_cv_type_pid_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" +if test "x$ac_cv_type_off_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define off_t long int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "ino_t" "ac_cv_type_ino_t" "$ac_includes_default" +if test "x$ac_cv_type_ino_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define ino_t unsigned long +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default" +if test "x$ac_cv_type_mode_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define mode_t int +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5 +$as_echo_n "checking for uid_t in sys/types.h... " >&6; } +if ${ac_cv_type_uid_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "uid_t" >/dev/null 2>&1; then : + ac_cv_type_uid_t=yes +else + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5 +$as_echo "$ac_cv_type_uid_t" >&6; } +if test $ac_cv_type_uid_t = no; then + +$as_echo "#define uid_t int" >>confdefs.h + + +$as_echo "#define gid_t int" >>confdefs.h + +fi + +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if long is 64 bits" >&5 +$as_echo_n "checking if long is 64 bits... " >&6; } +if ${zsh_cv_long_is_64_bit+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_long_is_64_bit=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int main() { return sizeof(long) < 8; } +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_long_is_64_bit=yes +else + zsh_cv_long_is_64_bit=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_long_is_64_bit" >&5 +$as_echo "$zsh_cv_long_is_64_bit" >&6; } + + + + + + + +if test x$zsh_cv_long_is_64_bit = xyes; then + $as_echo "#define LONG_IS_64_BIT 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if off_t is 64 bit" >&5 +$as_echo_n "checking if off_t is 64 bit... " >&6; } +if ${zsh_cv_off_t_is_64_bit+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_off_t_is_64_bit=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include + +main() { return sizeof(off_t) < 8; } + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_off_t_is_64_bit=yes +else + zsh_cv_off_t_is_64_bit=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_off_t_is_64_bit" >&5 +$as_echo "$zsh_cv_off_t_is_64_bit" >&6; } + if test x$zsh_cv_off_t_is_64_bit = xyes; then + $as_echo "#define OFF_T_IS_64_BIT 1" >>confdefs.h + + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if ino_t is 64 bit" >&5 +$as_echo_n "checking if ino_t is 64 bit... " >&6; } +if ${zsh_cv_ino_t_is_64_bit+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_ino_t_is_64_bit=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include + +main() { return sizeof(ino_t) < 8; } + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_ino_t_is_64_bit=yes +else + zsh_cv_ino_t_is_64_bit=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_ino_t_is_64_bit" >&5 +$as_echo "$zsh_cv_ino_t_is_64_bit" >&6; } + if test x$zsh_cv_ino_t_is_64_bit = xyes; then + $as_echo "#define INO_T_IS_64_BIT 1" >>confdefs.h + + fi + + if test x$enable_largefile != xno -o x$zsh_cv_off_t_is_64_bit = xyes \ + -o $zsh_cv_ino_t_is_64_bit = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler has a 64 bit type" >&5 +$as_echo_n "checking if compiler has a 64 bit type... " >&6; } +if ${zsh_cv_64_bit_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + if test x != x ; then + zsh_cv_64_bit_type="long long" + else + zsh_cv_64_bit_type=no + fi +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +main() +{ + long long foo = 0; + int bar = (int) foo; + return sizeof(long long) != 8; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_64_bit_type="long long" +else + zsh_cv_64_bit_type=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + + if test "$zsh_cv_64_bit_type" = no; then + if test "$cross_compiling" = yes; then : + if test x != x ; then + zsh_cv_64_bit_type="quad_t" + else + zsh_cv_64_bit_type=no + fi +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +main() +{ + quad_t foo = 0; + int bar = (int) foo; + return sizeof(quad_t) != 8; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_64_bit_type="quad_t" +else + zsh_cv_64_bit_type=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + + fi + if test "$zsh_cv_64_bit_type" = no; then + if test "$cross_compiling" = yes; then : + if test x != x ; then + zsh_cv_64_bit_type="__int64_t" + else + zsh_cv_64_bit_type=no + fi +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +main() +{ + __int64_t foo = 0; + int bar = (int) foo; + return sizeof(__int64_t) != 8; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_64_bit_type="__int64_t" +else + zsh_cv_64_bit_type=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + + fi + if test "$zsh_cv_64_bit_type" = no && + test "$zsh_cv_off_t_is_64_bit" = yes; then + if test "$cross_compiling" = yes; then : + if test x != x ; then + zsh_cv_64_bit_type="off_t" + else + zsh_cv_64_bit_type=no + fi +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +main() +{ + off_t foo = 0; + int bar = (int) foo; + return sizeof(off_t) != 8; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_64_bit_type="off_t" +else + zsh_cv_64_bit_type=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_64_bit_type" >&5 +$as_echo "$zsh_cv_64_bit_type" >&6; } + if test "$zsh_cv_64_bit_type" != no; then + cat >>confdefs.h <<_ACEOF +#define ZSH_64_BIT_TYPE $zsh_cv_64_bit_type +_ACEOF + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a corresponding unsigned 64 bit type" >&5 +$as_echo_n "checking for a corresponding unsigned 64 bit type... " >&6; } +if ${zsh_cv_64_bit_utype+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + if test xforce != x ; then + zsh_cv_64_bit_utype="unsigned $zsh_cv_64_bit_type" + else + zsh_cv_64_bit_utype=no + fi +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +main() +{ + unsigned $zsh_cv_64_bit_type foo = 0; + int bar = (int) foo; + return sizeof(unsigned $zsh_cv_64_bit_type) != 8; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_64_bit_utype="unsigned $zsh_cv_64_bit_type" +else + zsh_cv_64_bit_utype=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + + if test "$zsh_cv_64_bit_utype" = no; then + if test "$cross_compiling" = yes; then : + if test x != x ; then + zsh_cv_64_bit_utype="__uint64_t" + else + zsh_cv_64_bit_utype=no + fi +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +main() +{ + __uint64_t foo = 0; + int bar = (int) foo; + return sizeof(__uint64_t) != 8; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_64_bit_utype="__uint64_t" +else + zsh_cv_64_bit_utype=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_64_bit_utype" >&5 +$as_echo "$zsh_cv_64_bit_utype" >&6; } + if test "$zsh_cv_64_bit_utype" != no; then + cat >>confdefs.h <<_ACEOF +#define ZSH_64_BIT_UTYPE $zsh_cv_64_bit_utype +_ACEOF + + fi + fi + fi +fi + + +if test "$zsh_cv_64_bit_type" = "long long"; then + $as_echo "#define ZLONG_IS_LONG_LONG 1" >>confdefs.h + +else + if test "$zsh_cv_64_bit_type" = "long"; then + $as_echo "#define ZLONG_IS_LONG_64 1" >>confdefs.h + + fi +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for %lld printf support" >&5 +$as_echo_n "checking for %lld printf support... " >&6; } +if ${zsh_cv_printf_has_lld+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_printf_has_lld=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int main(int argc, char **argv) +{ + long long foo = ((long long)0xdead << 40) | 0xf00d; + char buf[80]; + sprintf(buf, "before%lldafter", foo); + if (!strcmp(buf, "before62677660341432333after")) { + return 0; + } + return 1; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_printf_has_lld=yes +else + zsh_cv_printf_has_lld=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_printf_has_lld" >&5 +$as_echo "$zsh_cv_printf_has_lld" >&6; } + +if test x$zsh_cv_printf_has_lld = xyes; then + $as_echo "#define PRINTF_HAS_LLD 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sigset_t" >&5 +$as_echo_n "checking for sigset_t... " >&6; } +if ${zsh_cv_type_sigset_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _POSIX_C_SOURCE 200809L +#include +#include +int +main () +{ +sigset_t tempsigset; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_type_sigset_t=yes +else + zsh_cv_type_sigset_t=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_type_sigset_t" >&5 +$as_echo "$zsh_cv_type_sigset_t" >&6; } + +if test x$zsh_cv_type_sigset_t = xno; then + $as_echo "#define sigset_t unsigned int" >>confdefs.h + +fi + +ac_fn_c_check_member "$LINENO" "struct stat" "st_atim.tv_nsec" "ac_cv_member_struct_stat_st_atim_tv_nsec" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_atim_tv_nsec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct stat" "st_atimespec.tv_nsec" "ac_cv_member_struct_stat_st_atimespec_tv_nsec" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_atimespec_tv_nsec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct stat" "st_atimensec" "ac_cv_member_struct_stat_st_atimensec" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_atimensec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_ATIMENSEC 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct stat" "st_mtim.tv_nsec" "ac_cv_member_struct_stat_st_mtim_tv_nsec" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_mtim_tv_nsec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct stat" "st_mtimespec.tv_nsec" "ac_cv_member_struct_stat_st_mtimespec_tv_nsec" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_mtimespec_tv_nsec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct stat" "st_mtimensec" "ac_cv_member_struct_stat_st_mtimensec" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_mtimensec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct stat" "st_ctim.tv_nsec" "ac_cv_member_struct_stat_st_ctim_tv_nsec" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_ctim_tv_nsec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct stat" "st_ctimespec.tv_nsec" "ac_cv_member_struct_stat_st_ctimespec_tv_nsec" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_ctimespec_tv_nsec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_CTIMESPEC_TV_NSEC 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct stat" "st_ctimensec" "ac_cv_member_struct_stat_st_ctimensec" "$ac_includes_default" +if test "x$ac_cv_member_struct_stat_st_ctimensec" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_STAT_ST_CTIMENSEC 1 +_ACEOF + + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct timezone" >&5 +$as_echo_n "checking for struct timezone... " >&6; } +if ${zsh_cv_type_exists_struct_timezone+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#define _GNU_SOURCE 1 +#ifdef HAVE_SYS_TIME_H +# include +#endif + +int +main () +{ +struct timezone testvar; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_type_exists_struct_timezone=yes +else + zsh_cv_type_exists_struct_timezone=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_type_exists_struct_timezone" >&5 +$as_echo "$zsh_cv_type_exists_struct_timezone" >&6; } + +if test $zsh_cv_type_exists_struct_timezone = yes; then + $as_echo "#define HAVE_STRUCT_TIMEZONE 1" >>confdefs.h + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct timespec" >&5 +$as_echo_n "checking for struct timespec... " >&6; } +if ${zsh_cv_type_exists_struct_timespec+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#define _GNU_SOURCE 1 +#ifdef HAVE_SYS_TIME_H +# include +#endif + +int +main () +{ +struct timespec testvar; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_type_exists_struct_timespec=yes +else + zsh_cv_type_exists_struct_timespec=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_type_exists_struct_timespec" >&5 +$as_echo "$zsh_cv_type_exists_struct_timespec" >&6; } + +if test $zsh_cv_type_exists_struct_timespec = yes; then + $as_echo "#define HAVE_STRUCT_TIMESPEC 1" >>confdefs.h + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct utmp" >&5 +$as_echo_n "checking for struct utmp... " >&6; } +if ${zsh_cv_type_exists_struct_utmp+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UTMP_H +# include +#endif + +int +main () +{ +struct utmp testvar; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_type_exists_struct_utmp=yes +else + zsh_cv_type_exists_struct_utmp=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_type_exists_struct_utmp" >&5 +$as_echo "$zsh_cv_type_exists_struct_utmp" >&6; } + +if test $zsh_cv_type_exists_struct_utmp = yes; then + $as_echo "#define HAVE_STRUCT_UTMP 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for struct utmpx" >&5 +$as_echo_n "checking for struct utmpx... " >&6; } +if ${zsh_cv_type_exists_struct_utmpx+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UTMPX_H +# include +#endif + +int +main () +{ +struct utmpx testvar; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_type_exists_struct_utmpx=yes +else + zsh_cv_type_exists_struct_utmpx=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_type_exists_struct_utmpx" >&5 +$as_echo "$zsh_cv_type_exists_struct_utmpx" >&6; } + +if test $zsh_cv_type_exists_struct_utmpx = yes; then + $as_echo "#define HAVE_STRUCT_UTMPX 1" >>confdefs.h + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ut_host in struct utmp" >&5 +$as_echo_n "checking for ut_host in struct utmp... " >&6; } +if ${zsh_cv_struct_member_struct_utmp_ut_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UTMP_H +# include +#endif + +int +main () +{ +struct utmp testvar; testvar.ut_host; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_struct_member_struct_utmp_ut_host=yes +else + zsh_cv_struct_member_struct_utmp_ut_host=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_struct_member_struct_utmp_ut_host" >&5 +$as_echo "$zsh_cv_struct_member_struct_utmp_ut_host" >&6; } + +if test $zsh_cv_struct_member_struct_utmp_ut_host = yes; then + $as_echo "#define HAVE_STRUCT_UTMP_UT_HOST 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ut_host in struct utmpx" >&5 +$as_echo_n "checking for ut_host in struct utmpx... " >&6; } +if ${zsh_cv_struct_member_struct_utmpx_ut_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UTMPX_H +# include +#endif + +int +main () +{ +struct utmpx testvar; testvar.ut_host; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_struct_member_struct_utmpx_ut_host=yes +else + zsh_cv_struct_member_struct_utmpx_ut_host=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_struct_member_struct_utmpx_ut_host" >&5 +$as_echo "$zsh_cv_struct_member_struct_utmpx_ut_host" >&6; } + +if test $zsh_cv_struct_member_struct_utmpx_ut_host = yes; then + $as_echo "#define HAVE_STRUCT_UTMPX_UT_HOST 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ut_xtime in struct utmpx" >&5 +$as_echo_n "checking for ut_xtime in struct utmpx... " >&6; } +if ${zsh_cv_struct_member_struct_utmpx_ut_xtime+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UTMPX_H +# include +#endif + +int +main () +{ +struct utmpx testvar; testvar.ut_xtime; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_struct_member_struct_utmpx_ut_xtime=yes +else + zsh_cv_struct_member_struct_utmpx_ut_xtime=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_struct_member_struct_utmpx_ut_xtime" >&5 +$as_echo "$zsh_cv_struct_member_struct_utmpx_ut_xtime" >&6; } + +if test $zsh_cv_struct_member_struct_utmpx_ut_xtime = yes; then + $as_echo "#define HAVE_STRUCT_UTMPX_UT_XTIME 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ut_tv in struct utmpx" >&5 +$as_echo_n "checking for ut_tv in struct utmpx... " >&6; } +if ${zsh_cv_struct_member_struct_utmpx_ut_tv+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UTMPX_H +# include +#endif + +int +main () +{ +struct utmpx testvar; testvar.ut_tv; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_struct_member_struct_utmpx_ut_tv=yes +else + zsh_cv_struct_member_struct_utmpx_ut_tv=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_struct_member_struct_utmpx_ut_tv" >&5 +$as_echo "$zsh_cv_struct_member_struct_utmpx_ut_tv" >&6; } + +if test $zsh_cv_struct_member_struct_utmpx_ut_tv = yes; then + $as_echo "#define HAVE_STRUCT_UTMPX_UT_TV 1" >>confdefs.h + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for d_ino in struct dirent" >&5 +$as_echo_n "checking for d_ino in struct dirent... " >&6; } +if ${zsh_cv_struct_member_struct_dirent_d_ino+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_DIRENT_H +# include +#endif + +int +main () +{ +struct dirent testvar; testvar.d_ino; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_struct_member_struct_dirent_d_ino=yes +else + zsh_cv_struct_member_struct_dirent_d_ino=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_struct_member_struct_dirent_d_ino" >&5 +$as_echo "$zsh_cv_struct_member_struct_dirent_d_ino" >&6; } + +if test $zsh_cv_struct_member_struct_dirent_d_ino = yes; then + $as_echo "#define HAVE_STRUCT_DIRENT_D_INO 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for d_stat in struct dirent" >&5 +$as_echo_n "checking for d_stat in struct dirent... " >&6; } +if ${zsh_cv_struct_member_struct_dirent_d_stat+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_DIRENT_H +# include +#endif + +int +main () +{ +struct dirent testvar; testvar.d_stat; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_struct_member_struct_dirent_d_stat=yes +else + zsh_cv_struct_member_struct_dirent_d_stat=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_struct_member_struct_dirent_d_stat" >&5 +$as_echo "$zsh_cv_struct_member_struct_dirent_d_stat" >&6; } + +if test $zsh_cv_struct_member_struct_dirent_d_stat = yes; then + $as_echo "#define HAVE_STRUCT_DIRENT_D_STAT 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for d_ino in struct direct" >&5 +$as_echo_n "checking for d_ino in struct direct... " >&6; } +if ${zsh_cv_struct_member_struct_direct_d_ino+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_NDIR_H +# include +#endif +#ifdef HAVE_SYS_DIR_H +# include +#endif +#ifdef HAVE_NDIR_H +# include +#endif + +int +main () +{ +struct direct testvar; testvar.d_ino; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_struct_member_struct_direct_d_ino=yes +else + zsh_cv_struct_member_struct_direct_d_ino=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_struct_member_struct_direct_d_ino" >&5 +$as_echo "$zsh_cv_struct_member_struct_direct_d_ino" >&6; } + +if test $zsh_cv_struct_member_struct_direct_d_ino = yes; then + $as_echo "#define HAVE_STRUCT_DIRECT_D_INO 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for d_stat in struct direct" >&5 +$as_echo_n "checking for d_stat in struct direct... " >&6; } +if ${zsh_cv_struct_member_struct_direct_d_stat+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_NDIR_H +# include +#endif +#ifdef HAVE_SYS_DIR_H +# include +#endif +#ifdef HAVE_NDIR_H +# include +#endif + +int +main () +{ +struct direct testvar; testvar.d_stat; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_struct_member_struct_direct_d_stat=yes +else + zsh_cv_struct_member_struct_direct_d_stat=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_struct_member_struct_direct_d_stat" >&5 +$as_echo "$zsh_cv_struct_member_struct_direct_d_stat" >&6; } + +if test $zsh_cv_struct_member_struct_direct_d_stat = yes; then + $as_echo "#define HAVE_STRUCT_DIRECT_D_STAT 1" >>confdefs.h + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sin6_scope_id in struct sockaddr_in6" >&5 +$as_echo_n "checking for sin6_scope_id in struct sockaddr_in6... " >&6; } +if ${zsh_cv_struct_member_struct_sockaddr_in6_sin6_scope_id+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#include + +int +main () +{ +struct sockaddr_in6 testvar; testvar.sin6_scope_id; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_struct_member_struct_sockaddr_in6_sin6_scope_id=yes +else + zsh_cv_struct_member_struct_sockaddr_in6_sin6_scope_id=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_struct_member_struct_sockaddr_in6_sin6_scope_id" >&5 +$as_echo "$zsh_cv_struct_member_struct_sockaddr_in6_sin6_scope_id" >&6; } + +if test $zsh_cv_struct_member_struct_sockaddr_in6_sin6_scope_id = yes; then + $as_echo "#define HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID 1" >>confdefs.h + +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if we need our own h_errno" >&5 +$as_echo_n "checking if we need our own h_errno... " >&6; } +if ${zsh_cv_decl_h_errno_use_local+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +extern int h_errno; h_errno = 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + zsh_cv_decl_h_errno_use_local=no +else + zsh_cv_decl_h_errno_use_local=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_decl_h_errno_use_local" >&5 +$as_echo "$zsh_cv_decl_h_errno_use_local" >&6; } + +if test x$zsh_cv_decl_h_errno_use_local = xyes; then + $as_echo "#define USE_LOCAL_H_ERRNO 1" >>confdefs.h + +fi + + + +for ac_func in strftime strptime mktime timelocal \ + difftime gettimeofday clock_gettime \ + select poll \ + readlink faccessx fchdir ftruncate \ + fstat lstat lchown fchown fchmod \ + fseeko ftello \ + mkfifo _mktemp mkstemp \ + waitpid wait3 \ + sigaction sigblock sighold sigrelse sigsetmask sigprocmask \ + killpg setpgid setpgrp tcsetpgrp tcgetattr nice \ + gethostname gethostbyname2 getipnodebyname \ + inet_aton inet_pton inet_ntop \ + getlogin getpwent getpwnam getpwuid getgrgid getgrnam \ + initgroups nis_list \ + setuid seteuid setreuid setresuid setsid \ + memcpy memmove strstr strerror strtoul \ + getrlimit getrusage \ + setlocale \ + isblank iswblank \ + uname \ + signgam tgamma \ + log2 \ + scalbn \ + putenv getenv setenv unsetenv xw\ + brk sbrk \ + pathconf sysconf \ + tgetent tigetflag tigetnum tigetstr setupterm initscr resize_term \ + getcchar setcchar waddwstr wget_wch win_wch use_default_colors \ + nl_langinfo \ + erand48 open_memstream \ + posix_openpt \ + wctomb iconv \ + isinf isnan \ + grantpt unlockpt ptsname \ + htons ntohs \ + regcomp regexec regerror regfree \ + gdbm_open getxattr \ + realpath canonicalize_file_name \ + symlink getcwd \ + cygwin_conv_path \ + nanosleep \ + srand_deterministic \ + setutxent getutxent endutxent getutent \ + getline +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strcoll" >&5 +$as_echo_n "checking for working strcoll... " >&6; } +if ${ac_cv_func_strcoll_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_strcoll_works=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +return (strcoll ("abc", "def") >= 0 || + strcoll ("ABC", "DEF") >= 0 || + strcoll ("123", "456") >= 0) + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_strcoll_works=yes +else + ac_cv_func_strcoll_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strcoll_works" >&5 +$as_echo "$ac_cv_func_strcoll_works" >&6; } +if test $ac_cv_func_strcoll_works = yes; then + +$as_echo "#define HAVE_STRCOLL 1" >>confdefs.h + +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if realpath accepts NULL" >&5 +$as_echo_n "checking if realpath accepts NULL... " >&6; } +if ${zsh_cv_func_realpath_accepts_null+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_func_realpath_accepts_null=$ac_cv_func_canonicalize_file_name +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include + +int +main () +{ + +exit(!realpath("/", (char*)0)); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_func_realpath_accepts_null=yes +else + zsh_cv_func_realpath_accepts_null=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_func_realpath_accepts_null" >&5 +$as_echo "$zsh_cv_func_realpath_accepts_null" >&6; } +if test x$zsh_cv_func_realpath_accepts_null = xyes; then + $as_echo "#define REALPATH_ACCEPTS_NULL 1" >>confdefs.h + +fi + +if test x$enable_cap = xyes; then + for ac_func in cap_get_proc +do : + ac_fn_c_check_func "$LINENO" "cap_get_proc" "ac_cv_func_cap_get_proc" +if test "x$ac_cv_func_cap_get_proc" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_CAP_GET_PROC 1 +_ACEOF + +fi +done + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if tgetent accepts NULL" >&5 +$as_echo_n "checking if tgetent accepts NULL... " >&6; } +if ${zsh_cv_func_tgetent_accepts_null+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_func_tgetent_accepts_null=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +main() +{ + char buf[4096]; + int r1 = tgetent(buf, "vt100"); + int r2 = tgetent((char*)0,"vt100"); + if (r1 >= 0 && r1 == r2) { + char tbuf[1024], *u; + u = tbuf; + tgetstr("cl", &u); + creat("conftest.tgetent", 0640); + } + exit((r1 != r2) || r2 == -1); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + if test -f conftest.tgetent; then + zsh_cv_func_tgetent_accepts_null=yes + else + zsh_cv_func_tgetent_accepts_null=no + fi +else + zsh_cv_func_tgetent_accepts_null=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_func_tgetent_accepts_null" >&5 +$as_echo "$zsh_cv_func_tgetent_accepts_null" >&6; } +if test x$zsh_cv_func_tgetent_accepts_null = xyes; then + $as_echo "#define TGETENT_ACCEPTS_NULL 1" >>confdefs.h + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if tgetent returns 0 on success" >&5 +$as_echo_n "checking if tgetent returns 0 on success... " >&6; } +if ${zsh_cv_func_tgetent_zero_success+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_func_tgetent_zero_success=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +main() +{ + char buf[4096]; + int r1 = tgetent(buf, "!@#$%^&*"); + int r2 = tgetent(buf, "vt100"); + if (r1 < 0 && r2 == 0) { + char tbuf[1024], *u; + u = tbuf; + tgetstr("cl", &u); + creat("conftest.tgetent0", 0640); + } + exit(r1 == r2); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + if test -f conftest.tgetent0; then + zsh_cv_func_tgetent_zero_success=yes + else + zsh_cv_func_tgetent_zero_success=no + fi +else + zsh_cv_func_tgetent_zero_success=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_func_tgetent_zero_success" >&5 +$as_echo "$zsh_cv_func_tgetent_zero_success" >&6; } + +if test x$zsh_cv_func_tgetent_zero_success = xyes; then + $as_echo "#define TGETENT_SUCCESS 0" >>confdefs.h + +else + $as_echo "#define TGETENT_SUCCESS 1" >>confdefs.h + +fi + + + + + for ac_header in $ac_header_list +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + + + +for ac_func in getpagesize +do : + ac_fn_c_check_func "$LINENO" "getpagesize" "ac_cv_func_getpagesize" +if test "x$ac_cv_func_getpagesize" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_GETPAGESIZE 1 +_ACEOF + +fi +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working mmap" >&5 +$as_echo_n "checking for working mmap... " >&6; } +if ${ac_cv_func_mmap_fixed_mapped+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_mmap_fixed_mapped=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +/* malloc might have been renamed as rpl_malloc. */ +#undef malloc + +/* Thanks to Mike Haertel and Jim Avera for this test. + Here is a matrix of mmap possibilities: + mmap private not fixed + mmap private fixed at somewhere currently unmapped + mmap private fixed at somewhere already mapped + mmap shared not fixed + mmap shared fixed at somewhere currently unmapped + mmap shared fixed at somewhere already mapped + For private mappings, we should verify that changes cannot be read() + back from the file, nor mmap's back from the file at a different + address. (There have been systems where private was not correctly + implemented like the infamous i386 svr4.0, and systems where the + VM page cache was not coherent with the file system buffer cache + like early versions of FreeBSD and possibly contemporary NetBSD.) + For shared mappings, we should conversely verify that changes get + propagated back to all the places they're supposed to be. + + Grep wants private fixed already mapped. + The main things grep needs to know about mmap are: + * does it exist and is it safe to write into the mmap'd area + * how to use it (BSD variants) */ + +#include +#include + +#if !defined STDC_HEADERS && !defined HAVE_STDLIB_H +char *malloc (); +#endif + +/* This mess was copied from the GNU getpagesize.h. */ +#ifndef HAVE_GETPAGESIZE +# ifdef _SC_PAGESIZE +# define getpagesize() sysconf(_SC_PAGESIZE) +# else /* no _SC_PAGESIZE */ +# ifdef HAVE_SYS_PARAM_H +# include +# ifdef EXEC_PAGESIZE +# define getpagesize() EXEC_PAGESIZE +# else /* no EXEC_PAGESIZE */ +# ifdef NBPG +# define getpagesize() NBPG * CLSIZE +# ifndef CLSIZE +# define CLSIZE 1 +# endif /* no CLSIZE */ +# else /* no NBPG */ +# ifdef NBPC +# define getpagesize() NBPC +# else /* no NBPC */ +# ifdef PAGESIZE +# define getpagesize() PAGESIZE +# endif /* PAGESIZE */ +# endif /* no NBPC */ +# endif /* no NBPG */ +# endif /* no EXEC_PAGESIZE */ +# else /* no HAVE_SYS_PARAM_H */ +# define getpagesize() 8192 /* punt totally */ +# endif /* no HAVE_SYS_PARAM_H */ +# endif /* no _SC_PAGESIZE */ + +#endif /* no HAVE_GETPAGESIZE */ + +int +main () +{ + char *data, *data2, *data3; + const char *cdata2; + int i, pagesize; + int fd, fd2; + + pagesize = getpagesize (); + + /* First, make a file with some known garbage in it. */ + data = (char *) malloc (pagesize); + if (!data) + return 1; + for (i = 0; i < pagesize; ++i) + *(data + i) = rand (); + umask (0); + fd = creat ("conftest.mmap", 0600); + if (fd < 0) + return 2; + if (write (fd, data, pagesize) != pagesize) + return 3; + close (fd); + + /* Next, check that the tail of a page is zero-filled. File must have + non-zero length, otherwise we risk SIGBUS for entire page. */ + fd2 = open ("conftest.txt", O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fd2 < 0) + return 4; + cdata2 = ""; + if (write (fd2, cdata2, 1) != 1) + return 5; + data2 = (char *) mmap (0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0L); + if (data2 == MAP_FAILED) + return 6; + for (i = 0; i < pagesize; ++i) + if (*(data2 + i)) + return 7; + close (fd2); + if (munmap (data2, pagesize)) + return 8; + + /* Next, try to mmap the file at a fixed address which already has + something else allocated at it. If we can, also make sure that + we see the same garbage. */ + fd = open ("conftest.mmap", O_RDWR); + if (fd < 0) + return 9; + if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED, fd, 0L)) + return 10; + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data2 + i)) + return 11; + + /* Finally, make sure that changes to the mapped area do not + percolate back to the file as seen by read(). (This is a bug on + some variants of i386 svr4.0.) */ + for (i = 0; i < pagesize; ++i) + *(data2 + i) = *(data2 + i) + 1; + data3 = (char *) malloc (pagesize); + if (!data3) + return 12; + if (read (fd, data3, pagesize) != pagesize) + return 13; + for (i = 0; i < pagesize; ++i) + if (*(data + i) != *(data3 + i)) + return 14; + close (fd); + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_mmap_fixed_mapped=yes +else + ac_cv_func_mmap_fixed_mapped=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_mmap_fixed_mapped" >&5 +$as_echo "$ac_cv_func_mmap_fixed_mapped" >&6; } +if test $ac_cv_func_mmap_fixed_mapped = yes; then + +$as_echo "#define HAVE_MMAP 1" >>confdefs.h + +fi +rm -f conftest.mmap conftest.txt + +if test x$ac_cv_func_mmap_fixed_mapped = xyes; then + for ac_func in munmap msync +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +fi + +if test x$ac_cv_func_setpgrp = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether getpgrp requires zero arguments" >&5 +$as_echo_n "checking whether getpgrp requires zero arguments... " >&6; } +if ${ac_cv_func_getpgrp_void+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Use it with a single arg. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +getpgrp (0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_func_getpgrp_void=no +else + ac_cv_func_getpgrp_void=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getpgrp_void" >&5 +$as_echo "$ac_cv_func_getpgrp_void" >&6; } +if test $ac_cv_func_getpgrp_void = yes; then + +$as_echo "#define GETPGRP_VOID 1" >>confdefs.h + +fi + +else + ac_cv_func_getpgrp_void=yes + $as_echo "#define GETPGRP_VOID 1" >>confdefs.h + +fi + +if test x$dynamic = xyes; then + for ac_func in dlopen dlerror dlsym dlclose load loadquery loadbind unload \ + shl_load shl_unload shl_findsym +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +fi + + +if test x$ac_cv_func_getxattr = xyes && test x$ac_cv_header_sys_xattr_h = xyes +then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if getxattr etc. are Linux-like" >&5 +$as_echo_n "checking if getxattr etc. are Linux-like... " >&6; } +if ${zsh_cv_getxattr_linux+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ + + (void)listxattr("", 0, 0); + (void)getxattr("", "", 0, 0); + (void)setxattr("", "", "", 0, 0); + (void)removexattr("", ""); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_getxattr_linux=yes +else + zsh_cv_getxattr_linux=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_getxattr_linux" >&5 +$as_echo "$zsh_cv_getxattr_linux" >&6; } + + if test x$zsh_cv_getxattr_linux != xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if getxattr etc. are MAC-like" >&5 +$as_echo_n "checking if getxattr etc. are MAC-like... " >&6; } +if ${zsh_cv_getxattr_mac+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ +(void)listxattr("", 0, 0, 0); + (void)getxattr("", "", 0, 0, 0, 0); + (void)setxattr("", "", "", 0, 0, 0); + (void)removexattr("", "", 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_getxattr_mac=yes +else + zsh_cv_getxattr_mac=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_getxattr_mac" >&5 +$as_echo "$zsh_cv_getxattr_mac" >&6; } + + if test x$zsh_cv_getxattr_mac = xyes; then + $as_echo "#define XATTR_EXTRA_ARGS 1" >>confdefs.h + + fi + fi +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if getxattr etc. are usable" >&5 +$as_echo_n "checking if getxattr etc. are usable... " >&6; } +if ${zsh_cv_use_xattr+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test x$zsh_cv_getxattr_linux = xyes || test x$zsh_cv_getxattr_mac = xyes +then +zsh_cv_use_xattr=yes +else +zsh_cv_use_xattr=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_use_xattr" >&5 +$as_echo "$zsh_cv_use_xattr" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking what style of signals to use" >&5 +$as_echo_n "checking what style of signals to use... " >&6; } +if test x$ac_cv_func_sigaction = xyes && test x$ac_cv_func_sigprocmask = xyes; then + signals_style=POSIX_SIGNALS + $as_echo "#define POSIX_SIGNALS 1" >>confdefs.h + +elif test x$ac_cv_func_sigblock = xyes && test x$ac_cv_func_sigsetmask = xyes; then + signals_style=BSD_SIGNALS + $as_echo "#define BSD_SIGNALS 1" >>confdefs.h + +elif test x$ac_cv_func_sighold = xyes && test x$ac_cv_func_sigrelse = xyes; then + signals_style=SYSV_SIGNALS + $as_echo "#define SYSV_SIGNALS 1" >>confdefs.h + +else + signals_style=NO_SIGNAL_BLOCKING + $as_echo "#define NO_SIGNAL_BLOCKING 1" >>confdefs.h + +fi +cat >>confdefs.h <<_ACEOF +#define $signals_style 1 +_ACEOF + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $signals_style" >&5 +$as_echo "$signals_style" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where signal.h is located" >&5 +$as_echo_n "checking where signal.h is located... " >&6; } +if ${zsh_cv_path_signal_h+:} false; then : + $as_echo_n "(cached) " >&6 +else + echo "#include " > nametmp.c +sigfile_list="`$CPP $CPPFLAGS nametmp.c | +sed -n -e 's/^#line[ ].*\"\(.*\)\"/\1/p' \ + -e 's/^#[ ].*\"\(.*\)\"/\1/p' | +sed 's/\\\\\\\\/\//g' | +$AWK '{ if ($1 ~ /sig/) files[$1] = $1 } + END { for (var in files) print var }'`" +rm -f nametmp.c +if test -z "$sigfile_list"; then + sigfile_list="/usr/include/sys/iso/signal_iso.h +/usr/include/bsd/sys/signal.h +/usr/include/signum.h +/usr/include/asm/signum.h +/usr/include/asm/signal.h +/usr/include/linux/signal.h +/usr/include/sys/signal.h +/usr/include/bits/signum.h +/dev/null" +fi +for SIGNAL_TRY_H in $sigfile_list +do + nsigs=`test -f $SIGNAL_TRY_H && \ + grep '#[ ]*define[ ][ ]*SIG[0-9A-Z]*[ ]*[0-9][0-9]*' $SIGNAL_TRY_H | \ + wc -l | sed 's/ //g'` + if test "x$nsigs" != x && test "$nsigs" -ge 7 + then + SIGNAL_H="$SIGNAL_H $SIGNAL_TRY_H" + fi +done +if test "x$SIGNAL_H" = x; then + as_fn_error $? "SIGNAL MACROS NOT FOUND: please report to developers" "$LINENO" 5 +fi +zsh_cv_path_signal_h="$SIGNAL_H" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_path_signal_h" >&5 +$as_echo "$zsh_cv_path_signal_h" >&6; } +SIGNAL_H="$zsh_cv_path_signal_h" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where error names are located" >&5 +$as_echo_n "checking where error names are located... " >&6; } +if ${zsh_cv_path_errno_h+:} false; then : + $as_echo_n "(cached) " >&6 +else + echo "#include " > nametmp.c +errfile_list="`$CPP $CPPFLAGS nametmp.c | +sed -n -e 's/^#line[ ].*\"\(.*\)\"/\1/p' \ + -e 's/^#[ 0-9].*\"\(.*\)\"/\1/p' | +sed 's/\\\\\\\\/\//g' | +$AWK '{ if ($1 ~ /err/) files[$1] = $1 } + END { for (var in files) print var }'`" +rm -f nametmp.c +for ERRNO_TRY_H in $errfile_list /dev/null +do + nerrs=`test -f $ERRNO_TRY_H && \ + $EGREP '#[ ]*define[ ][ ]*E[0-9A-Z]*[ ]*(_HURD_ERRNO )?\(?[_A-Z0-9]' $ERRNO_TRY_H | \ + wc -l | sed 's/ //g'` + if test "x$nerrs" != x && test "$nerrs" -ge 1 + then + ERRNO_H="$ERRNO_H $ERRNO_TRY_H" + fi +done +if test x"$ERRNO_H" = x; then + as_fn_error $? "ERROR MACROS NOT FOUND: please report to developers" "$LINENO" 5 +fi +zsh_cv_path_errno_h="$ERRNO_H" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_path_errno_h" >&5 +$as_echo "$zsh_cv_path_errno_h" >&6; } +ERRNO_H="$zsh_cv_path_errno_h" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking location of curses header" >&5 +$as_echo_n "checking location of curses header... " >&6; } +if ${zsh_cv_path_curses_header+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test x$zsh_cv_ignore_ncurses = xyes; then + if test x$ac_cv_header_curses_h = xyes; then + zsh_cv_path_curses_header=curses.h + else + zsh_cv_path_curses_header=none + fi +elif test x$ac_cv_header_ncursesw_ncurses_h = xyes; then + zsh_cv_path_curses_header=ncursesw/ncurses.h +elif test x$ac_cv_header_ncurses_ncurses_h = xyes; then + zsh_cv_path_curses_header=ncurses/ncurses.h +elif test x$ac_cv_header_ncurses_h = xyes; then + zsh_cv_path_curses_header=ncurses.h +elif test x$ac_cv_header_curses_h = xyes; then + zsh_cv_path_curses_header=curses.h +else + zsh_cv_path_curses_header=none +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_path_curses_header" >&5 +$as_echo "$zsh_cv_path_curses_header" >&6; } + +if test x$zsh_cv_path_curses_header != xnone; then + $as_echo "#define ZSH_HAVE_CURSES_H 1" >>confdefs.h + + ZSH_CURSES_H=$zsh_cv_path_curses_header +else + ZSH_CURSES_H= +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where curses key definitions are located" >&5 +$as_echo_n "checking where curses key definitions are located... " >&6; } +if ${zsh_cv_path_curses_keys_h+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test x$zsh_cv_path_curses_header = xnone; then + echo >nametmp.c +else + echo "#include <$zsh_cv_path_curses_header>" >nametmp.c +fi + +curses_list="`$CPP $CPPFLAGS nametmp.c | +sed -n -e 's/^#line[ ].*\"\(.*\)\"/\1/p' \ + -e 's/^#[ 0-9].*\"\(.*\)\"/\1/p' | +sed 's/\\\\\\\\/\//g' | +$AWK '{ if ($1 ~ /\.h/) files[$1] = $1 } + END { for (var in files) print var }'`" +rm -f nametmp.c +for CURSES_TRY_H in $curses_list /dev/null +do + nkeys=`test -f $CURSES_TRY_H && \ + $EGREP '#[ ]*define[ ][ ]*KEY_' $CURSES_TRY_H | \ + wc -l | sed 's/ //g'` + if test "x$nkeys" != x && test "$nkeys" -ge 10 + then + CURSES_KEYS_H=$CURSES_TRY_H + break + fi +done +zsh_cv_path_curses_keys_h="$CURSES_KEYS_H" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_path_curses_keys_h" >&5 +$as_echo "$zsh_cv_path_curses_keys_h" >&6; } +CURSES_KEYS_H="$zsh_cv_path_curses_keys_h" + +for ac_header in ncursesw/term.h +do : + ac_fn_c_check_header_compile "$LINENO" "ncursesw/term.h" "ac_cv_header_ncursesw_term_h" "#include +" +if test "x$ac_cv_header_ncursesw_term_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_NCURSESW_TERM_H 1 +_ACEOF + true +else + true +fi + +done + +for ac_header in ncurses/term.h +do : + ac_fn_c_check_header_compile "$LINENO" "ncurses/term.h" "ac_cv_header_ncurses_term_h" "#include +" +if test "x$ac_cv_header_ncurses_term_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_NCURSES_TERM_H 1 +_ACEOF + true +else + true +fi + +done + +for ac_header in term.h +do : + ac_fn_c_check_header_compile "$LINENO" "term.h" "ac_cv_header_term_h" "#include +" +if test "x$ac_cv_header_term_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_TERM_H 1 +_ACEOF + true +else + true +fi + +done + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where term.h is located" >&5 +$as_echo_n "checking where term.h is located... " >&6; } +if ${zsh_cv_path_term_header+:} false; then : + $as_echo_n "(cached) " >&6 +else + case x$zsh_cv_path_curses_header in + xncursesw/*) + if test x$ac_cv_header_ncursesw_term_h = xyes; then + zsh_cv_path_term_header=ncursesw/term.h + fi + ;; + xncurses/*) + if test x$ac_cv_header_ncurses_term_h = xyes; then + zsh_cv_path_term_header=ncurses/term.h + fi + ;; +esac +if test x$zsh_cv_path_term_header = x; then + if test x$ac_cv_header_term_h = xyes; then + zsh_cv_path_term_header=term.h + else + zsh_cv_path_term_header=none + fi +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_path_term_header" >&5 +$as_echo "$zsh_cv_path_term_header" >&6; } + + + + + + + + + + +if test x$zsh_cv_path_term_header != xnone; then + $as_echo "#define ZSH_HAVE_TERM_H 1" >>confdefs.h + + ZSH_TERM_H=$zsh_cv_path_term_header + if test x$zsh_cv_path_curses_header != xnone; then + term_includes="#include <$zsh_cv_path_curses_header> +#include <$zsh_cv_path_term_header>" + else + term_includes="#include <$zsh_cv_path_term_header>" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if boolcodes is available" >&5 +$as_echo_n "checking if boolcodes is available... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$term_includes +int +main () +{ +char **test = boolcodes; puts(*test); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + $as_echo "#define HAVE_BOOLCODES 1" >>confdefs.h + boolcodes=yes +else + boolcodes=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $boolcodes" >&5 +$as_echo "$boolcodes" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if numcodes is available" >&5 +$as_echo_n "checking if numcodes is available... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$term_includes +int +main () +{ +char **test = numcodes; puts(*test); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + $as_echo "#define HAVE_NUMCODES 1" >>confdefs.h + numcodes=yes +else + numcodes=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $numcodes" >&5 +$as_echo "$numcodes" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if strcodes is available" >&5 +$as_echo_n "checking if strcodes is available... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$term_includes +int +main () +{ +char **test = strcodes; puts(*test); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + $as_echo "#define HAVE_STRCODES 1" >>confdefs.h + strcodes=yes +else + strcodes=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $strcodes" >&5 +$as_echo "$strcodes" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if boolnames is available" >&5 +$as_echo_n "checking if boolnames is available... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$term_includes +int +main () +{ +char **test = boolnames; puts(*test); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + $as_echo "#define HAVE_BOOLNAMES 1" >>confdefs.h + boolnames=yes +else + boolnames=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $boolnames" >&5 +$as_echo "$boolnames" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if numnames is available" >&5 +$as_echo_n "checking if numnames is available... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$term_includes +int +main () +{ +char **test = numnames; puts(*test); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + $as_echo "#define HAVE_NUMNAMES 1" >>confdefs.h + numnames=yes +else + numnames=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $numnames" >&5 +$as_echo "$numnames" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if strnames is available" >&5 +$as_echo_n "checking if strnames is available... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$term_includes +int +main () +{ +char **test = strnames; puts(*test); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + $as_echo "#define HAVE_STRNAMES 1" >>confdefs.h + strnames=yes +else + strnames=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $strnames" >&5 +$as_echo "$strnames" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if tgoto prototype is missing" >&5 +$as_echo_n "checking if tgoto prototype is missing... " >&6; } + tgoto_includes="$term_includes +/* guaranteed to clash with any valid tgoto prototype */ +extern void tgoto(int **stuff, float **more_stuff);" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$tgoto_includes +int +main () +{ +int *stuff; float *more_stuff; tgoto(&stuff, &more_stuff); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + $as_echo "#define TGOTO_PROTO_MISSING 1" >>confdefs.h + tgotoprotomissing=yes +else + tgotoprotomissing=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tgotoprotomissing" >&5 +$as_echo "$tgotoprotomissing" >&6; } +else + ZSH_TERM_H= +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking where the RLIMIT macros are located" >&5 +$as_echo_n "checking where the RLIMIT macros are located... " >&6; } +if ${zsh_cv_path_rlimit_h+:} false; then : + $as_echo_n "(cached) " >&6 +else + echo "#include " >restmp.c +resourcefile_list="`$CPP $CPPFLAGS restmp.c | +sed -n -e 's/^#line[ ].*\"\(.*\)\"/\1/p' \ + -e 's/^#[ ].*\"\(.*\)\"/\1/p' | +sed 's/\\\\\\\\/\//g' | +$AWK '{ if ($1 ~ /resource/) files[$1] = $1 } + END { for (var in files) print var }'`" +rm -f restmp.c +if test -z "$resourcefile_list"; then + resourcefile_list="/usr/include/bsd/sys/resource.h +/usr/include/asm/resource.h +/usr/include/linux/resource.h +/usr/include/sys/resource.h +/usr/include/bits/resource.h +/usr/include/resourcebits.h" +fi +for RESOURCE_H in $resourcefile_list /dev/null; +do + test -f $RESOURCE_H && \ + grep '#[ ]*define[ ][ ]*RLIMIT_[A-Z]*[ ]*[0-9A-Z][0-9]*' $RESOURCE_H > /dev/null && \ + break +done +zsh_cv_path_rlimit_h=$RESOURCE_H +if test x$RESOURCE_H = x"/dev/null" && test x$ac_cv_func_getrlimit = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: RLIMIT MACROS NOT FOUND: please report to developers" >&5 +$as_echo "$as_me: WARNING: RLIMIT MACROS NOT FOUND: please report to developers" >&2;} +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_path_rlimit_h" >&5 +$as_echo "$zsh_cv_path_rlimit_h" >&6; } +RLIMITS_INC_H=$zsh_cv_path_rlimit_h +if test "$RLIMITS_INC_H" = "/dev/null"; then + RLIMITS_INC_H='' +fi + + + + + +DEFAULT_RLIM_T=long +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if rlim_t is longer than a long" >&5 +$as_echo_n "checking if rlim_t is longer than a long... " >&6; } +if ${zsh_cv_rlim_t_is_longer+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_rlim_t_is_longer=yes +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +main(){struct rlimit r;exit(sizeof(r.rlim_cur) <= sizeof(long));} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_rlim_t_is_longer=yes +else + zsh_cv_rlim_t_is_longer=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_rlim_t_is_longer" >&5 +$as_echo "$zsh_cv_rlim_t_is_longer" >&6; } +if test x$zsh_cv_rlim_t_is_longer = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if rlim_t is a quad" >&5 +$as_echo_n "checking if rlim_t is a quad... " >&6; } +if ${zsh_cv_rlim_t_is_quad_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_rlim_t_is_quad_t=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#include +main() { + struct rlimit r; + char buf[20]; + r.rlim_cur = 0; + sprintf(buf, "%qd", r.rlim_cur); + exit(strcmp(buf, "0")); +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_rlim_t_is_quad_t=yes +else + zsh_cv_rlim_t_is_quad_t=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_rlim_t_is_quad_t" >&5 +$as_echo "$zsh_cv_rlim_t_is_quad_t" >&6; } + if test x$zsh_cv_rlim_t_is_quad_t = xyes; then + $as_echo "#define RLIM_T_IS_QUAD_T 1" >>confdefs.h + + DEFAULT_RLIM_T=quad_t + else + $as_echo "#define RLIM_T_IS_LONG_LONG 1" >>confdefs.h + + DEFAULT_RLIM_T='long long' + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the rlim_t is unsigned" >&5 +$as_echo_n "checking if the rlim_t is unsigned... " >&6; } +if ${zsh_cv_type_rlim_t_is_unsigned+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_type_rlim_t_is_unsigned=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include + main(){struct rlimit r;r.rlim_cur=-1;exit(r.rlim_cur<0);} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_type_rlim_t_is_unsigned=yes +else + zsh_cv_type_rlim_t_is_unsigned=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_type_rlim_t_is_unsigned" >&5 +$as_echo "$zsh_cv_type_rlim_t_is_unsigned" >&6; } + if test x$zsh_cv_type_rlim_t_is_unsigned = xyes; then + $as_echo "#define RLIM_T_IS_UNSIGNED 1" >>confdefs.h + + DEFAULT_RLIM_T="unsigned $DEFAULT_RLIM_T" + fi +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rlim_t" >&5 +$as_echo_n "checking for rlim_t... " >&6; } +if ${zsh_cv_type_rlim_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +rlim_t l; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_type_rlim_t=yes +else + zsh_cv_type_rlim_t=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_type_rlim_t" >&5 +$as_echo "$zsh_cv_type_rlim_t" >&6; } +if test x$zsh_cv_type_rlim_t = xno; then + cat >>confdefs.h <<_ACEOF +#define rlim_t $DEFAULT_RLIM_T +_ACEOF + +fi + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_AIO_MEM" >&5 +$as_echo_n "checking for limit RLIMIT_AIO_MEM... " >&6; } +if ${zsh_cv_have_RLIMIT_AIO_MEM+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_AIO_MEM + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_AIO_MEM=yes +else + zsh_cv_have_RLIMIT_AIO_MEM=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_AIO_MEM" >&5 +$as_echo "$zsh_cv_have_RLIMIT_AIO_MEM" >&6; } + +if test $zsh_cv_have_RLIMIT_AIO_MEM = yes; then + $as_echo "#define HAVE_RLIMIT_AIO_MEM 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_AIO_OPS" >&5 +$as_echo_n "checking for limit RLIMIT_AIO_OPS... " >&6; } +if ${zsh_cv_have_RLIMIT_AIO_OPS+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_AIO_OPS + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_AIO_OPS=yes +else + zsh_cv_have_RLIMIT_AIO_OPS=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_AIO_OPS" >&5 +$as_echo "$zsh_cv_have_RLIMIT_AIO_OPS" >&6; } + +if test $zsh_cv_have_RLIMIT_AIO_OPS = yes; then + $as_echo "#define HAVE_RLIMIT_AIO_OPS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_AS" >&5 +$as_echo_n "checking for limit RLIMIT_AS... " >&6; } +if ${zsh_cv_have_RLIMIT_AS+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_AS + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_AS=yes +else + zsh_cv_have_RLIMIT_AS=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_AS" >&5 +$as_echo "$zsh_cv_have_RLIMIT_AS" >&6; } + +if test $zsh_cv_have_RLIMIT_AS = yes; then + $as_echo "#define HAVE_RLIMIT_AS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_LOCKS" >&5 +$as_echo_n "checking for limit RLIMIT_LOCKS... " >&6; } +if ${zsh_cv_have_RLIMIT_LOCKS+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_LOCKS + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_LOCKS=yes +else + zsh_cv_have_RLIMIT_LOCKS=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_LOCKS" >&5 +$as_echo "$zsh_cv_have_RLIMIT_LOCKS" >&6; } + +if test $zsh_cv_have_RLIMIT_LOCKS = yes; then + $as_echo "#define HAVE_RLIMIT_LOCKS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_MEMLOCK" >&5 +$as_echo_n "checking for limit RLIMIT_MEMLOCK... " >&6; } +if ${zsh_cv_have_RLIMIT_MEMLOCK+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_MEMLOCK + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_MEMLOCK=yes +else + zsh_cv_have_RLIMIT_MEMLOCK=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_MEMLOCK" >&5 +$as_echo "$zsh_cv_have_RLIMIT_MEMLOCK" >&6; } + +if test $zsh_cv_have_RLIMIT_MEMLOCK = yes; then + $as_echo "#define HAVE_RLIMIT_MEMLOCK 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_NPROC" >&5 +$as_echo_n "checking for limit RLIMIT_NPROC... " >&6; } +if ${zsh_cv_have_RLIMIT_NPROC+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_NPROC + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_NPROC=yes +else + zsh_cv_have_RLIMIT_NPROC=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_NPROC" >&5 +$as_echo "$zsh_cv_have_RLIMIT_NPROC" >&6; } + +if test $zsh_cv_have_RLIMIT_NPROC = yes; then + $as_echo "#define HAVE_RLIMIT_NPROC 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_NTHR" >&5 +$as_echo_n "checking for limit RLIMIT_NTHR... " >&6; } +if ${zsh_cv_have_RLIMIT_NTHR+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_NTHR + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_NTHR=yes +else + zsh_cv_have_RLIMIT_NTHR=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_NTHR" >&5 +$as_echo "$zsh_cv_have_RLIMIT_NTHR" >&6; } + +if test $zsh_cv_have_RLIMIT_NTHR = yes; then + $as_echo "#define HAVE_RLIMIT_NTHR 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_NOFILE" >&5 +$as_echo_n "checking for limit RLIMIT_NOFILE... " >&6; } +if ${zsh_cv_have_RLIMIT_NOFILE+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_NOFILE + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_NOFILE=yes +else + zsh_cv_have_RLIMIT_NOFILE=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_NOFILE" >&5 +$as_echo "$zsh_cv_have_RLIMIT_NOFILE" >&6; } + +if test $zsh_cv_have_RLIMIT_NOFILE = yes; then + $as_echo "#define HAVE_RLIMIT_NOFILE 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_PTHREAD" >&5 +$as_echo_n "checking for limit RLIMIT_PTHREAD... " >&6; } +if ${zsh_cv_have_RLIMIT_PTHREAD+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_PTHREAD + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_PTHREAD=yes +else + zsh_cv_have_RLIMIT_PTHREAD=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_PTHREAD" >&5 +$as_echo "$zsh_cv_have_RLIMIT_PTHREAD" >&6; } + +if test $zsh_cv_have_RLIMIT_PTHREAD = yes; then + $as_echo "#define HAVE_RLIMIT_PTHREAD 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_RSS" >&5 +$as_echo_n "checking for limit RLIMIT_RSS... " >&6; } +if ${zsh_cv_have_RLIMIT_RSS+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_RSS + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_RSS=yes +else + zsh_cv_have_RLIMIT_RSS=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_RSS" >&5 +$as_echo "$zsh_cv_have_RLIMIT_RSS" >&6; } + +if test $zsh_cv_have_RLIMIT_RSS = yes; then + $as_echo "#define HAVE_RLIMIT_RSS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_SBSIZE" >&5 +$as_echo_n "checking for limit RLIMIT_SBSIZE... " >&6; } +if ${zsh_cv_have_RLIMIT_SBSIZE+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_SBSIZE + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_SBSIZE=yes +else + zsh_cv_have_RLIMIT_SBSIZE=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_SBSIZE" >&5 +$as_echo "$zsh_cv_have_RLIMIT_SBSIZE" >&6; } + +if test $zsh_cv_have_RLIMIT_SBSIZE = yes; then + $as_echo "#define HAVE_RLIMIT_SBSIZE 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_TCACHE" >&5 +$as_echo_n "checking for limit RLIMIT_TCACHE... " >&6; } +if ${zsh_cv_have_RLIMIT_TCACHE+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_TCACHE + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_TCACHE=yes +else + zsh_cv_have_RLIMIT_TCACHE=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_TCACHE" >&5 +$as_echo "$zsh_cv_have_RLIMIT_TCACHE" >&6; } + +if test $zsh_cv_have_RLIMIT_TCACHE = yes; then + $as_echo "#define HAVE_RLIMIT_TCACHE 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_VMEM" >&5 +$as_echo_n "checking for limit RLIMIT_VMEM... " >&6; } +if ${zsh_cv_have_RLIMIT_VMEM+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_VMEM + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_VMEM=yes +else + zsh_cv_have_RLIMIT_VMEM=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_VMEM" >&5 +$as_echo "$zsh_cv_have_RLIMIT_VMEM" >&6; } + +if test $zsh_cv_have_RLIMIT_VMEM = yes; then + $as_echo "#define HAVE_RLIMIT_VMEM 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_SIGPENDING" >&5 +$as_echo_n "checking for limit RLIMIT_SIGPENDING... " >&6; } +if ${zsh_cv_have_RLIMIT_SIGPENDING+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_SIGPENDING + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_SIGPENDING=yes +else + zsh_cv_have_RLIMIT_SIGPENDING=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_SIGPENDING" >&5 +$as_echo "$zsh_cv_have_RLIMIT_SIGPENDING" >&6; } + +if test $zsh_cv_have_RLIMIT_SIGPENDING = yes; then + $as_echo "#define HAVE_RLIMIT_SIGPENDING 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_MSGQUEUE" >&5 +$as_echo_n "checking for limit RLIMIT_MSGQUEUE... " >&6; } +if ${zsh_cv_have_RLIMIT_MSGQUEUE+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_MSGQUEUE + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_MSGQUEUE=yes +else + zsh_cv_have_RLIMIT_MSGQUEUE=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_MSGQUEUE" >&5 +$as_echo "$zsh_cv_have_RLIMIT_MSGQUEUE" >&6; } + +if test $zsh_cv_have_RLIMIT_MSGQUEUE = yes; then + $as_echo "#define HAVE_RLIMIT_MSGQUEUE 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_NICE" >&5 +$as_echo_n "checking for limit RLIMIT_NICE... " >&6; } +if ${zsh_cv_have_RLIMIT_NICE+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_NICE + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_NICE=yes +else + zsh_cv_have_RLIMIT_NICE=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_NICE" >&5 +$as_echo "$zsh_cv_have_RLIMIT_NICE" >&6; } + +if test $zsh_cv_have_RLIMIT_NICE = yes; then + $as_echo "#define HAVE_RLIMIT_NICE 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_RTPRIO" >&5 +$as_echo_n "checking for limit RLIMIT_RTPRIO... " >&6; } +if ${zsh_cv_have_RLIMIT_RTPRIO+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_RTPRIO + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_RTPRIO=yes +else + zsh_cv_have_RLIMIT_RTPRIO=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_RTPRIO" >&5 +$as_echo "$zsh_cv_have_RLIMIT_RTPRIO" >&6; } + +if test $zsh_cv_have_RLIMIT_RTPRIO = yes; then + $as_echo "#define HAVE_RLIMIT_RTPRIO 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_POSIXLOCKS" >&5 +$as_echo_n "checking for limit RLIMIT_POSIXLOCKS... " >&6; } +if ${zsh_cv_have_RLIMIT_POSIXLOCKS+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_POSIXLOCKS + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_POSIXLOCKS=yes +else + zsh_cv_have_RLIMIT_POSIXLOCKS=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_POSIXLOCKS" >&5 +$as_echo "$zsh_cv_have_RLIMIT_POSIXLOCKS" >&6; } + +if test $zsh_cv_have_RLIMIT_POSIXLOCKS = yes; then + $as_echo "#define HAVE_RLIMIT_POSIXLOCKS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_NPTS" >&5 +$as_echo_n "checking for limit RLIMIT_NPTS... " >&6; } +if ${zsh_cv_have_RLIMIT_NPTS+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_NPTS + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_NPTS=yes +else + zsh_cv_have_RLIMIT_NPTS=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_NPTS" >&5 +$as_echo "$zsh_cv_have_RLIMIT_NPTS" >&6; } + +if test $zsh_cv_have_RLIMIT_NPTS = yes; then + $as_echo "#define HAVE_RLIMIT_NPTS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_SWAP" >&5 +$as_echo_n "checking for limit RLIMIT_SWAP... " >&6; } +if ${zsh_cv_have_RLIMIT_SWAP+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_SWAP + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_SWAP=yes +else + zsh_cv_have_RLIMIT_SWAP=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_SWAP" >&5 +$as_echo "$zsh_cv_have_RLIMIT_SWAP" >&6; } + +if test $zsh_cv_have_RLIMIT_SWAP = yes; then + $as_echo "#define HAVE_RLIMIT_SWAP 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for limit RLIMIT_KQUEUES" >&5 +$as_echo_n "checking for limit RLIMIT_KQUEUES... " >&6; } +if ${zsh_cv_have_RLIMIT_KQUEUES+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int +main () +{ +RLIMIT_KQUEUES + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_have_RLIMIT_KQUEUES=yes +else + zsh_cv_have_RLIMIT_KQUEUES=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_have_RLIMIT_KQUEUES" >&5 +$as_echo "$zsh_cv_have_RLIMIT_KQUEUES" >&6; } + +if test $zsh_cv_have_RLIMIT_KQUEUES = yes; then + $as_echo "#define HAVE_RLIMIT_KQUEUES 1" >>confdefs.h + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if RLIMIT_VMEM and RLIMIT_RSS are the same" >&5 +$as_echo_n "checking if RLIMIT_VMEM and RLIMIT_RSS are the same... " >&6; } +if ${zsh_cv_rlimit_vmem_is_rss+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_rlimit_vmem_is_rss=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int main() +{ +int ret = 1; +#if defined(HAVE_RLIMIT_VMEM) && defined(HAVE_RLIMIT_RSS) +if (RLIMIT_RSS == RLIMIT_VMEM) ret = 0; +#endif +return ret; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_rlimit_vmem_is_rss=yes +else + zsh_cv_rlimit_vmem_is_rss=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_rlimit_vmem_is_rss" >&5 +$as_echo "$zsh_cv_rlimit_vmem_is_rss" >&6; } + +if test x$zsh_cv_rlimit_vmem_is_rss = xyes; then + $as_echo "#define RLIMIT_VMEM_IS_RSS 1" >>confdefs.h + +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if RLIMIT_VMEM and RLIMIT_AS are the same" >&5 +$as_echo_n "checking if RLIMIT_VMEM and RLIMIT_AS are the same... " >&6; } +if ${zsh_cv_rlimit_vmem_is_as+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_rlimit_vmem_is_as=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int main() +{ +int ret = 1; +#if defined(HAVE_RLIMIT_VMEM) && defined(HAVE_RLIMIT_AS) +if (RLIMIT_AS == RLIMIT_VMEM) ret = 0; +#endif +return ret; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_rlimit_vmem_is_as=yes +else + zsh_cv_rlimit_vmem_is_as=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_rlimit_vmem_is_as" >&5 +$as_echo "$zsh_cv_rlimit_vmem_is_as" >&6; } + +if test x$zsh_cv_rlimit_vmem_is_as = xyes; then + $as_echo "#define RLIMIT_VMEM_IS_AS 1" >>confdefs.h + +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if RLIMIT_RSS and RLIMIT_AS are the same" >&5 +$as_echo_n "checking if RLIMIT_RSS and RLIMIT_AS are the same... " >&6; } +if ${zsh_cv_rlimit_rss_is_as+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_rlimit_rss_is_as=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int main() +{ +int ret = 1; +#if defined(HAVE_RLIMIT_RSS) && defined(HAVE_RLIMIT_AS) +if (RLIMIT_AS == RLIMIT_RSS) ret = 0; +#endif +return ret; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_rlimit_rss_is_as=yes +else + zsh_cv_rlimit_rss_is_as=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_rlimit_rss_is_as" >&5 +$as_echo "$zsh_cv_rlimit_rss_is_as" >&6; } + +if test x$zsh_cv_rlimit_rss_is_as = xyes; then + $as_echo "#define RLIMIT_RSS_IS_AS 1" >>confdefs.h + +fi + + +if test x$ac_cv_func_getrusage = xyes; then + ac_fn_c_check_member "$LINENO" "struct rusage" "ru_maxrss" "ac_cv_member_struct_rusage_ru_maxrss" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_maxrss" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_MAXRSS 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct rusage" "ru_ixrss" "ac_cv_member_struct_rusage_ru_ixrss" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_ixrss" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_IXRSS 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct rusage" "ru_idrss" "ac_cv_member_struct_rusage_ru_idrss" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_idrss" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_IDRSS 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct rusage" "ru_isrss" "ac_cv_member_struct_rusage_ru_isrss" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_isrss" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_ISRSS 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct rusage" "ru_minflt" "ac_cv_member_struct_rusage_ru_minflt" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_minflt" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_MINFLT 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct rusage" "ru_majflt" "ac_cv_member_struct_rusage_ru_majflt" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_majflt" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_MAJFLT 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct rusage" "ru_nswap" "ac_cv_member_struct_rusage_ru_nswap" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_nswap" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_NSWAP 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct rusage" "ru_inblock" "ac_cv_member_struct_rusage_ru_inblock" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_inblock" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_INBLOCK 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct rusage" "ru_oublock" "ac_cv_member_struct_rusage_ru_oublock" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_oublock" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_OUBLOCK 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct rusage" "ru_msgsnd" "ac_cv_member_struct_rusage_ru_msgsnd" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_msgsnd" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_MSGSND 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct rusage" "ru_msgrcv" "ac_cv_member_struct_rusage_ru_msgrcv" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_msgrcv" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_MSGRCV 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct rusage" "ru_nsignals" "ac_cv_member_struct_rusage_ru_nsignals" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_nsignals" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_NSIGNALS 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct rusage" "ru_nvcsw" "ac_cv_member_struct_rusage_ru_nvcsw" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_nvcsw" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_NVCSW 1 +_ACEOF + + +fi +ac_fn_c_check_member "$LINENO" "struct rusage" "ru_nivcsw" "ac_cv_member_struct_rusage_ru_nivcsw" "#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +" +if test "x$ac_cv_member_struct_rusage_ru_nivcsw" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE_STRUCT_RUSAGE_RU_NIVCSW 1 +_ACEOF + + +fi + +fi + + +if ${zsh_cv_cs_path+:} false; then : + $as_echo_n "(cached) " >&6 +else + if getconf _CS_PATH >/dev/null 2>&1; then + zsh_cv_cs_path=`getconf _CS_PATH` +elif getconf CS_PATH >/dev/null 2>&1; then + zsh_cv_cs_path=`getconf CS_PATH` +elif getconf PATH >/dev/null 2>&1; then + zsh_cv_cs_path=`getconf PATH` +else + zsh_cv_cs_path="/bin:/usr/bin" +fi +fi + + +cat >>confdefs.h <<_ACEOF +#define DEFAULT_PATH "$zsh_cv_cs_path" +_ACEOF + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for /dev/fd filesystem" >&5 +$as_echo_n "checking for /dev/fd filesystem... " >&6; } +if ${zsh_cv_sys_path_dev_fd+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$host_os" = cygwin; then +zsh_cv_sys_path_dev_fd=no +else +for zsh_cv_sys_path_dev_fd in /proc/self/fd /dev/fd no; do + test x`echo ok|(exec 3<&0; cat $zsh_cv_sys_path_dev_fd/3 2>/dev/null;)` = xok && break + done +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_path_dev_fd" >&5 +$as_echo "$zsh_cv_sys_path_dev_fd" >&6; } +if test x$zsh_cv_sys_path_dev_fd != xno; then + cat >>confdefs.h <<_ACEOF +#define PATH_DEV_FD "$zsh_cv_sys_path_dev_fd" +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for RFS superroot directory" >&5 +$as_echo_n "checking for RFS superroot directory... " >&6; } +if ${zsh_cv_sys_superroot+:} false; then : + $as_echo_n "(cached) " >&6 +else + test -d /../.LOCALROOT && zsh_cv_sys_superroot=yes || zsh_cv_sys_superroot=no +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_superroot" >&5 +$as_echo "$zsh_cv_sys_superroot" >&6; } + +if test x$zsh_cv_sys_superroot = xyes; then + $as_echo "#define HAVE_SUPERROOT 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we should use the native getcwd" >&5 +$as_echo_n "checking whether we should use the native getcwd... " >&6; } +if ${zsh_cv_use_getcwd+:} false; then : + $as_echo_n "(cached) " >&6 +else + case "${host_cpu}-${host_vendor}-${host_os}" in + *QNX*) zsh_cv_use_getcwd=yes ;; + *) zsh_cv_use_getcwd=no ;; + esac +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_use_getcwd" >&5 +$as_echo "$zsh_cv_use_getcwd" >&6; } + +if test x$zsh_cv_use_getcwd = xyes; then + $as_echo "#define USE_GETCWD 1" >>confdefs.h + +fi + + +if test x$ac_cv_func_getcwd = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether getcwd calls malloc to allocate memory" >&5 +$as_echo_n "checking whether getcwd calls malloc to allocate memory... " >&6; } +if ${zsh_cv_getcwd_malloc+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_getcwd_malloc=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +int main() { + char buf[1024], *ptr1, *ptr2; + ptr1 = getcwd(buf, 1024); + ptr2 = getcwd(NULL, 0); + if (ptr1 && ptr2 && !strcmp(ptr1, ptr2)) { + return 0; + } + return 1; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_getcwd_malloc=yes +else + zsh_cv_getcwd_malloc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_getcwd_malloc" >&5 +$as_echo "$zsh_cv_getcwd_malloc" >&6; } + if test x$zsh_cv_getcwd_malloc = xyes; then + $as_echo "#define GETCWD_CALLS_MALLOC 1" >>confdefs.h + + fi +fi + + +ac_fn_c_check_func "$LINENO" "setproctitle" "ac_cv_func_setproctitle" +if test "x$ac_cv_func_setproctitle" = xyes; then : + $as_echo "#define HAVE_SETPROCTITLE 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing setproctitle" >&5 +$as_echo_n "checking for library containing setproctitle... " >&6; } +if ${ac_cv_search_setproctitle+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char setproctitle (); +int +main () +{ +return setproctitle (); + ; + return 0; +} +_ACEOF +for ac_lib in '' util; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_setproctitle=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_setproctitle+:} false; then : + break +fi +done +if ${ac_cv_search_setproctitle+:} false; then : + +else + ac_cv_search_setproctitle=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_setproctitle" >&5 +$as_echo "$ac_cv_search_setproctitle" >&6; } +ac_res=$ac_cv_search_setproctitle +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + $as_echo "#define HAVE_SETPROCTITLE 1" >>confdefs.h + +fi + +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for NIS" >&5 +$as_echo_n "checking for NIS... " >&6; } +if ${zsh_cv_sys_nis+:} false; then : + $as_echo_n "(cached) " >&6 +else + test -f /usr/bin/ypcat && /usr/bin/ypcat passwd.byname > /dev/null 2>&1 && \ +zsh_cv_sys_nis=yes || zsh_cv_sys_nis=no +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_nis" >&5 +$as_echo "$zsh_cv_sys_nis" >&6; } +if test x$zsh_cv_sys_nis = xyes; then + $as_echo "#define HAVE_NIS 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing yp_all" >&5 +$as_echo_n "checking for library containing yp_all... " >&6; } +if ${ac_cv_search_yp_all+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char yp_all (); +int +main () +{ +return yp_all (); + ; + return 0; +} +_ACEOF +for ac_lib in '' nsl; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_yp_all=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_yp_all+:} false; then : + break +fi +done +if ${ac_cv_search_yp_all+:} false; then : + +else + ac_cv_search_yp_all=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_yp_all" >&5 +$as_echo "$ac_cv_search_yp_all" >&6; } +ac_res=$ac_cv_search_yp_all +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for NIS+" >&5 +$as_echo_n "checking for NIS+... " >&6; } +if ${zsh_cv_sys_nis_plus+:} false; then : + $as_echo_n "(cached) " >&6 +else + test x$ac_cv_func_nis_list = xyes && test -f /usr/bin/nisls && \ + /usr/bin/nisls > /dev/null 2>&1 && \ +zsh_cv_sys_nis_plus=yes || zsh_cv_sys_nis_plus=no +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_nis_plus" >&5 +$as_echo "$zsh_cv_sys_nis_plus" >&6; } +if test x$zsh_cv_sys_nis_plus = xyes; then + $as_echo "#define HAVE_NIS_PLUS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for utmp file" >&5 +$as_echo_n "checking for utmp file... " >&6; } +if ${zsh_cv_path_utmp+:} false; then : + $as_echo_n "(cached) " >&6 +else + for dir in /etc /usr/etc /var/adm /usr/adm /var/run /var/log ./conftest; do + zsh_cv_path_utmp=${dir}/utmp + test -f $zsh_cv_path_utmp && break + zsh_cv_path_utmp=no +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_path_utmp" >&5 +$as_echo "$zsh_cv_path_utmp" >&6; } + +if test $zsh_cv_path_utmp != no; then + cat >>confdefs.h <<_ACEOF +#define PATH_UTMP_FILE "$zsh_cv_path_utmp" +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for wtmp file" >&5 +$as_echo_n "checking for wtmp file... " >&6; } +if ${zsh_cv_path_wtmp+:} false; then : + $as_echo_n "(cached) " >&6 +else + for dir in /etc /usr/etc /var/adm /usr/adm /var/run /var/log ./conftest; do + zsh_cv_path_wtmp=${dir}/wtmp + test -f $zsh_cv_path_wtmp && break + zsh_cv_path_wtmp=no +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_path_wtmp" >&5 +$as_echo "$zsh_cv_path_wtmp" >&6; } + +if test $zsh_cv_path_wtmp != no; then + cat >>confdefs.h <<_ACEOF +#define PATH_WTMP_FILE "$zsh_cv_path_wtmp" +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for utmpx file" >&5 +$as_echo_n "checking for utmpx file... " >&6; } +if ${zsh_cv_path_utmpx+:} false; then : + $as_echo_n "(cached) " >&6 +else + for dir in /etc /usr/etc /var/adm /usr/adm /var/run /var/log ./conftest; do + zsh_cv_path_utmpx=${dir}/utmpx + test -f $zsh_cv_path_utmpx && break + zsh_cv_path_utmpx=no +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_path_utmpx" >&5 +$as_echo "$zsh_cv_path_utmpx" >&6; } + +if test $zsh_cv_path_utmpx != no; then + cat >>confdefs.h <<_ACEOF +#define PATH_UTMPX_FILE "$zsh_cv_path_utmpx" +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for wtmpx file" >&5 +$as_echo_n "checking for wtmpx file... " >&6; } +if ${zsh_cv_path_wtmpx+:} false; then : + $as_echo_n "(cached) " >&6 +else + for dir in /etc /usr/etc /var/adm /usr/adm /var/run /var/log ./conftest; do + zsh_cv_path_wtmpx=${dir}/wtmpx + test -f $zsh_cv_path_wtmpx && break + zsh_cv_path_wtmpx=no +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_path_wtmpx" >&5 +$as_echo "$zsh_cv_path_wtmpx" >&6; } + +if test $zsh_cv_path_wtmpx != no; then + cat >>confdefs.h <<_ACEOF +#define PATH_WTMPX_FILE "$zsh_cv_path_wtmpx" +_ACEOF + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for brk() prototype in " >&5 +$as_echo_n "checking for brk() prototype in ... " >&6; } +if ${zsh_cv_header_unistd_h_brk_proto+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +double brk(); +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_header_unistd_h_brk_proto=no +else + zsh_cv_header_unistd_h_brk_proto=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_header_unistd_h_brk_proto" >&5 +$as_echo "$zsh_cv_header_unistd_h_brk_proto" >&6; } + +if test x$zsh_cv_header_unistd_h_brk_proto = xyes; then + $as_echo "#define HAVE_BRK_PROTO 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sbrk() prototype in " >&5 +$as_echo_n "checking for sbrk() prototype in ... " >&6; } +if ${zsh_cv_header_unistd_h_sbrk_proto+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +double sbrk(); +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_header_unistd_h_sbrk_proto=no +else + zsh_cv_header_unistd_h_sbrk_proto=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_header_unistd_h_sbrk_proto" >&5 +$as_echo "$zsh_cv_header_unistd_h_sbrk_proto" >&6; } + +if test x$zsh_cv_header_unistd_h_sbrk_proto = xyes; then + $as_echo "#define HAVE_SBRK_PROTO 1" >>confdefs.h + +fi + + +if test "$ac_cv_prog_cc_stdc" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mknod prototype in " >&5 +$as_echo_n "checking for mknod prototype in ... " >&6; } +if ${zsh_cv_header_sys_stat_h_mknod_proto+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + int mknod(double x); +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_header_sys_stat_h_mknod_proto=no +else + zsh_cv_header_sys_stat_h_mknod_proto=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_header_sys_stat_h_mknod_proto" >&5 +$as_echo "$zsh_cv_header_sys_stat_h_mknod_proto" >&6; } + if test x$zsh_cv_header_sys_stat_h_mknod_proto = xyes; then + $as_echo "#define HAVE_MKNOD_PROTO 1" >>confdefs.h + + fi +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ioctl prototype in or " >&5 +$as_echo_n "checking for ioctl prototype in or ... " >&6; } +if ${zsh_cv_header_unistd_h_termios_h_ioctl_proto+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_TERMIOS_H +# include +#endif +double ioctl(); +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_header_unistd_h_termios_h_ioctl_proto=no +else + zsh_cv_header_unistd_h_termios_h_ioctl_proto=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_header_unistd_h_termios_h_ioctl_proto" >&5 +$as_echo "$zsh_cv_header_unistd_h_termios_h_ioctl_proto" >&6; } + +if test x$zsh_cv_header_unistd_h_termios_h_ioctl_proto = xno; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ioctl prototype in " >&5 +$as_echo_n "checking for ioctl prototype in ... " >&6; } +if ${zsh_cv_header_sys_ioctl_h_ioctl_proto+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + double ioctl(); +int +main () +{ +int i; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_header_sys_ioctl_h_ioctl_proto=no +else + zsh_cv_header_sys_ioctl_h_ioctl_proto=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_header_sys_ioctl_h_ioctl_proto" >&5 +$as_echo "$zsh_cv_header_sys_ioctl_h_ioctl_proto" >&6; } +else + zsh_cv_header_sys_ioctl_h_ioctl_proto=no +fi + + +if test x$zsh_cv_header_unistd_h_termios_h_ioctl_proto = xyes || \ + test x$zsh_cv_header_sys_ioctl_h_ioctl_proto = xyes; then + $as_echo "#define HAVE_IOCTL_PROTO 1" >>confdefs.h + +fi + +if test x$zsh_cv_header_sys_ioctl_h_ioctl_proto = xyes; then + $as_echo "#define IOCTL_IN_SYS_IOCTL 1" >>confdefs.h + +fi + + +if test x$ac_cv_header_sys_select_h != xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for select() in " >&5 +$as_echo_n "checking for select() in ... " >&6; } +if ${zsh_cv_header_socket_h_select_proto+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +fd_set fd; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_header_socket_h_select_proto=yes +else + zsh_cv_header_socket_h_select_proto=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_header_socket_h_select_proto" >&5 +$as_echo "$zsh_cv_header_socket_h_select_proto" >&6; } + if test x$zsh_cv_header_socket_h_select_proto = xyes; then + $as_echo "#define SELECT_IN_SYS_SOCKET_H 1" >>confdefs.h + + fi +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if named FIFOs work" >&5 +$as_echo_n "checking if named FIFOs work... " >&6; } +if ${zsh_cv_sys_fifo+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$host_os" = cygwin; then +zsh_cv_sys_fifo=yes +else +if test "$cross_compiling" = yes; then : + zsh_cv_sys_fifo=yes +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +main() +{ + char c; + int fd; + int pid, ret; + unlink("/tmp/fifo$$"); +#ifdef HAVE_MKFIFO + if(mkfifo("/tmp/fifo$$", 0600) < 0) +#else + if(mknod("/tmp/fifo$$", 0010600, 0) < 0) +#endif + exit(1); + pid = fork(); + if(pid < 0) + exit(1); + if(pid) { + fd = open("/tmp/fifo$$", O_RDONLY); + exit(fd < 0 || read(fd, &c, 1) != 1 || c != 'x'); + } + fd = open("/tmp/fifo$$", O_WRONLY); + ret = (fd < 0 || write(fd, "x", 1) < 1); + unlink("/tmp/fifo$$"); + exit(ret); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_sys_fifo=yes +else + zsh_cv_sys_fifo=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_fifo" >&5 +$as_echo "$zsh_cv_sys_fifo" >&6; } + +if test x$zsh_cv_sys_fifo = xyes; then + $as_echo "#define HAVE_FIFOS 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if link() works" >&5 +$as_echo_n "checking if link() works... " >&6; } +if ${zsh_cv_sys_link+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_sys_link=yes +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +main() +{ + int ret; + char *tmpfile, *newfile; + tmpfile="/tmp/zsh.linktest$$"; + newfile="/tmp/zsh.linktest2$$"; + unlink(tmpfile); + unlink(newfile); + if(creat(tmpfile, 0644) < 0) + exit(1); + ret = link(tmpfile, newfile); + unlink(tmpfile); + unlink(newfile); + exit(ret<0); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_sys_link=yes +else + zsh_cv_sys_link=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_link" >&5 +$as_echo "$zsh_cv_sys_link" >&6; } + +if test x$zsh_cv_sys_link = xyes; then + $as_echo "#define HAVE_LINK 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if kill(pid, 0) returns ESRCH correctly" >&5 +$as_echo_n "checking if kill(pid, 0) returns ESRCH correctly... " >&6; } +if ${zsh_cv_sys_killesrch+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_sys_killesrch=yes +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#include +main() +{ + int pid = (getpid() + 10000) & 0xffffff; + while (pid && (kill(pid, 0) == 0 || errno != ESRCH)) pid >>= 1; + exit(errno!=ESRCH); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_sys_killesrch=yes +else + zsh_cv_sys_killesrch=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_killesrch" >&5 +$as_echo "$zsh_cv_sys_killesrch" >&6; } + +if test x$zsh_cv_sys_killesrch = xno; then + $as_echo "#define BROKEN_KILL_ESRCH 1" >>confdefs.h + +fi + + +if test x$signals_style = xPOSIX_SIGNALS; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if POSIX sigsuspend() works" >&5 +$as_echo_n "checking if POSIX sigsuspend() works... " >&6; } +if ${zsh_cv_sys_sigsuspend+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_sys_sigsuspend=yes +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +int child=0; +void handler(sig) + int sig; +{if(sig==SIGCHLD) child=1;} +main() { + struct sigaction act; + sigset_t set; + int pid, ret; + act.sa_handler = &handler; + sigfillset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGCHLD, &act, 0); + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, 0); + pid=fork(); + if(pid==0) return 0; + if(pid>0) { + sigemptyset(&set); + ret=sigsuspend(&set); + exit(child==0); + } +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_sys_sigsuspend=yes +else + zsh_cv_sys_sigsuspend=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_sigsuspend" >&5 +$as_echo "$zsh_cv_sys_sigsuspend" >&6; } + if test x$zsh_cv_sys_sigsuspend = xno; then + $as_echo "#define BROKEN_POSIX_SIGSUSPEND 1" >>confdefs.h + + fi +fi + + + +# Check whether --with-tcsetpgrp was given. +if test "${with_tcsetpgrp+set}" = set; then : + withval=$with_tcsetpgrp; +case "x$withval" in + xyes) zsh_working_tcsetpgrp=yes;; + xno) zsh_working_tcsetpgrp=no;; + *) as_fn_error $? "please use --with-tcsetpgrp=yes or --with-tcsetpgrp=no" "$LINENO" 5;; +esac +else + zsh_working_tcsetpgrp=check +fi + +if test "x$ac_cv_func_tcsetpgrp" = xyes; then +case "x$zsh_working_tcsetpgrp" in + xcheck) + trap "" TTOU > /dev/null 2>&1 || : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if tcsetpgrp() actually works" >&5 +$as_echo_n "checking if tcsetpgrp() actually works... " >&6; } +if ${zsh_cv_sys_tcsetpgrp+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_sys_tcsetpgrp=yes +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#include +main() { + int fd; + int ret; + fd=open("/dev/tty", O_RDWR); + if (fd < 0) exit(2); + ret=tcsetpgrp(fd, tcgetpgrp(fd)); + if (ret < 0) exit(1); + exit(0); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_sys_tcsetpgrp=yes +else + +case $? in + 1) zsh_cv_sys_tcsetpgrp=no;; + 2) zsh_cv_sys_tcsetpgrp=notty;; + *) zsh_cv_sys_tcsetpgrp=error;; +esac + +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_tcsetpgrp" >&5 +$as_echo "$zsh_cv_sys_tcsetpgrp" >&6; } + case "x$zsh_cv_sys_tcsetpgrp" in + xno) $as_echo "#define BROKEN_TCSETPGRP 1" >>confdefs.h +;; + xyes) :;; + xnotty) as_fn_error $? "no controlling tty +Try running configure with --with-tcsetpgrp or --without-tcsetpgrp" "$LINENO" 5;; + *) as_fn_error $? "unexpected return status" "$LINENO" 5;; + esac + trap - TTOU > /dev/null 2>&1 || : + ;; + xyes) :;; + xno) $as_echo "#define BROKEN_TCSETPGRP 1" >>confdefs.h +;; + *) as_fn_error $? "unexpected value zsh_working_tcsetpgrp=$zsh_working_tcsetpgrp" "$LINENO" 5;; +esac +fi + + +if test x$ac_cv_func_getpwnam = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if getpwnam() is faked" >&5 +$as_echo_n "checking if getpwnam() is faked... " >&6; } +if ${zsh_cv_sys_getpwnam_faked+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_sys_getpwnam_faked=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +main() { + struct passwd *pw1, *pw2; + char buf[1024], name[1024]; + sprintf(buf, "%d:%d", getpid(), rand()); + pw1=getpwnam(buf); + if (pw1) strcpy(name, pw1->pw_name); + sprintf(buf, "%d:%d", rand(), getpid()); + pw2=getpwnam(buf); + exit(pw1!=0 && pw2!=0 && !strcmp(name, pw2->pw_name)); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_sys_getpwnam_faked=no +else + zsh_cv_sys_getpwnam_faked=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_getpwnam_faked" >&5 +$as_echo "$zsh_cv_sys_getpwnam_faked" >&6; } + if test x$zsh_cv_sys_getpwnam_faked = xyes; then + $as_echo "#define GETPWNAM_FAKED 1" >>confdefs.h + + fi +fi + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking base type of the third argument to accept" >&5 +$as_echo_n "checking base type of the third argument to accept... " >&6; } +if ${zsh_cv_type_socklen_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + zsh_cv_type_socklen_t= + for zsh_type in socklen_t int "unsigned long" size_t ; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include +int +main () +{ +extern int accept (int, struct sockaddr *, $zsh_type *); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + zsh_cv_type_socklen_t="$zsh_type"; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done + if test -z "$zsh_cv_type_socklen_t"; then + zsh_cv_type_socklen_t=int + fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_type_socklen_t" >&5 +$as_echo "$zsh_cv_type_socklen_t" >&6; } + +cat >>confdefs.h <<_ACEOF +#define ZSOCKLEN_T $zsh_cv_type_socklen_t +_ACEOF + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if your system has /dev/ptmx" >&5 +$as_echo_n "checking if your system has /dev/ptmx... " >&6; } +if ${ac_cv_have_dev_ptmx+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -w /dev/ptmx; then + ac_cv_have_dev_ptmx=yes +else + ac_cv_have_dev_ptmx=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_dev_ptmx" >&5 +$as_echo "$ac_cv_have_dev_ptmx" >&6; } + + +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 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if /dev/ptmx is usable" >&5 +$as_echo_n "checking if /dev/ptmx is usable... " >&6; } +if ${ac_cv_use_dev_ptmx+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __linux +#define _GNU_SOURCE 1 +#endif +#include +int ptsname(); +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_use_dev_ptmx=no +else + ac_cv_use_dev_ptmx=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_use_dev_ptmx" >&5 +$as_echo "$ac_cv_use_dev_ptmx" >&6; } + if test x$ac_cv_use_dev_ptmx = xyes; then + $as_echo "#define USE_DEV_PTMX 1" >>confdefs.h + + fi +fi + +# Check whether --enable-multibyte was given. +if test "${enable_multibyte+set}" = set; then : + enableval=$enable_multibyte; zsh_cv_c_unicode_support=$enableval +else + if ${zsh_cv_c_unicode_support+:} false; then : + $as_echo_n "(cached) " >&6 +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for functions supporting multibyte characters" >&5 +$as_echo "$as_me: checking for functions supporting multibyte characters" >&6;} + zfuncs_absent= + for zfunc in iswalnum iswcntrl iswdigit iswgraph iswlower iswprint \ +iswpunct iswspace iswupper iswxdigit mbrlen mbrtowc towupper towlower \ +wcschr wcscpy wcslen wcsncmp wcsncpy wcrtomb wcwidth wmemchr wmemcmp \ +wmemcpy wmemmove wmemset; do + as_ac_var=`$as_echo "ac_cv_func_$zfunc" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$zfunc" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + : +else + zfuncs_absent="$zfuncs_absent $zfunc" +fi + + done + if test x"$zfuncs_absent" = x; then + { $as_echo "$as_me:${as_lineno-$LINENO}: all functions found, multibyte support enabled" >&5 +$as_echo "$as_me: all functions found, multibyte support enabled" >&6;} + zsh_cv_c_unicode_support=yes + else + { $as_echo "$as_me:${as_lineno-$LINENO}: missing functions, multibyte support disabled" >&5 +$as_echo "$as_me: missing functions, multibyte support disabled" >&6;} + zsh_cv_c_unicode_support=no + fi + +fi + + +fi + + + + +# Check whether --enable-unicode9 was given. +if test "${enable_unicode9+set}" = set; then : + enableval=$enable_unicode9; if test x$enableval = xyes; then + $as_echo "#define ENABLE_UNICODE9 1" >>confdefs.h + +fi +fi + + + + +if test x$zsh_cv_c_unicode_support = xyes; then + $as_echo "#define MULTIBYTE_SUPPORT 1" >>confdefs.h + + + locale_prog='char *my_locales[] = { + "en_US.UTF-8", "en_GB.UTF-8", "en.UTF-8", ' + locale_prog="$locale_prog"`locale -a 2>/dev/null | \ + sed -e 's/utf8/UTF-8/' | grep UTF-8 | \ + while read line; do echo " \"$line\","; done;` + locale_prog="$locale_prog 0 }; + #define _XOPEN_SOURCE + #include + #include + #include + #include + + int main() { + char **localep; + char comb_acute_mb[] = { (char)0xcc, (char)0x81 }; + char u_0234[] = { (char)0xc8, (char)0xb4 }; + wchar_t wc; + #if !defined(__STDC_ISO_10646__) && !defined(__APPLE__) + return 1; + #endif + + for (localep = my_locales; *localep; localep++) + if (setlocale(LC_ALL, *localep)) + break; + if (!*localep) + return 1; + if (mbtowc(&wc, comb_acute_mb, 2) == 2 && (wcwidth(wc) != 0 || !iswprint(wc))) + return 0; + if (mbtowc(&wc, u_0234, 2) == 2 && (wcwidth(wc) != 1 || !iswprint(wc))) + return 0; + return 1; + } + " + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the wcwidth() and/or iswprint() functions are broken" >&5 +$as_echo_n "checking if the wcwidth() and/or iswprint() functions are broken... " >&6; } +if ${zsh_cv_c_broken_wcwidth+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_c_broken_wcwidth=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$locale_prog +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_c_broken_wcwidth=yes +else + zsh_cv_c_broken_wcwidth=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_c_broken_wcwidth" >&5 +$as_echo "$zsh_cv_c_broken_wcwidth" >&6; } + if test x$zsh_cv_c_broken_wcwidth = xyes; then + $as_echo "#define ENABLE_UNICODE9 1" >>confdefs.h + + fi + + locale_prog='char *my_locales[] = { + "en_US.UTF-8", "en_GB.UTF-8", "en.UTF-8", ' + locale_prog="$locale_prog"`locale -a 2>/dev/null | \ + sed -e 's/utf8/UTF-8/' | grep UTF-8 | \ + while read line; do echo " \"$line\","; done;` + locale_prog="$locale_prog 0 }; + #include + #include + + int main() { + char **localep; + for (localep = my_locales; *localep; localep++) + if (setlocale(LC_ALL, *localep) && isprint(0xa0)) + return 0; + return 1; + } + " + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the isprint() function is broken" >&5 +$as_echo_n "checking if the isprint() function is broken... " >&6; } +if ${zsh_cv_c_broken_isprint+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_c_broken_isprint=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$locale_prog +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_c_broken_isprint=yes +else + zsh_cv_c_broken_isprint=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_c_broken_isprint" >&5 +$as_echo "$zsh_cv_c_broken_isprint" >&6; } + if test x$zsh_cv_c_broken_isprint = xyes; then + $as_echo "#define BROKEN_ISPRINT 1" >>confdefs.h + + fi +fi + + +# Check whether --enable-libc-musl was given. +if test "${enable_libc_musl+set}" = set; then : + enableval=$enable_libc_musl; if test x$enableval = xyes; then + $as_echo "#define LIBC_MUSL 1" >>confdefs.h + +fi +fi + + +# Check whether --enable-dynamic-nss was given. +if test "${enable_dynamic_nss+set}" = set; then : + enableval=$enable_dynamic_nss; zsh_cv_c_dynamic_nss=$enableval +fi + + + +if test x$zsh_cv_c_dynamic_nss = xno; then + $as_echo "#define DISABLE_DYNAMIC_NSS 1" >>confdefs.h + +fi + + +L=N +INSTLIB="install.bin-\$(L)" +UNINSTLIB="uninstall.bin-\$(L)" +LINKMODS=NOLINKMODS +MOD_EXPORT= +MOD_IMPORT_VARIABLE= +MOD_IMPORT_FUNCTION= +aixdynamic=no +hpuxdynamic=no +if test "$ac_cv_func_load" = yes && + test "$ac_cv_func_unload" = yes && + test "$ac_cv_func_loadbind" = yes && + test "$ac_cv_func_loadquery" = yes; then + if test "x$dynamic" = xyes; then + aixdynamic=yes + fi +elif test "$ac_cv_func_dlopen" != yes || + test "$ac_cv_func_dlsym" != yes || + test "$ac_cv_func_dlerror" != yes; then + if test "$ac_cv_func_shl_load" != yes || + test "$ac_cv_func_shl_unload" != yes || + test "$ac_cv_func_shl_findsym" != yes; then + dynamic=no + elif test "x$dynamic" = xyes; then + hpuxdynamic=yes + DL_EXT="${DL_EXT=sl}" + $as_echo "#define HPUX10DYNAMIC 1" >>confdefs.h + fi +fi + +test -n "$GCC" && LDARG=-Wl, + + + +if test "x$aixdynamic" = xyes; then + DL_EXT="${DL_EXT=so}" + DLLD="${DLLD=$CC}" + zsh_cv_func_dlsym_needs_underscore=no + if test -n "$GCC"; then + DLLDFLAGS=${DLLDFLAGS=-shared} + else + DLLDFLAGS=${DLLDFLAGS=-bM:SRE} + fi + DLLDFLAGS=${DLLDFLAGS=} + EXTRA_LDFLAGS=${EXTRA_LDFLAGS=} + EXPOPT=${LDARG}-bE: + IMPOPT=${LDARG}-bI: + zsh_cv_sys_dynamic_clash_ok="${zsh_cv_sys_dynamic_clash_ok=yes}" + zsh_cv_sys_dynamic_rtld_global="${zsh_cv_sys_dynamic_rtld_global=yes}" + zsh_cv_sys_dynamic_execsyms="${zsh_cv_sys_dynamic_execsyms=yes}" + zsh_cv_sys_dynamic_strip_exe="${zsh_cv_sys_dynamic_strip_exe=yes}" + zsh_cv_sys_dynamic_strip_lib="${zsh_cv_sys_dynamic_strip_lib=yes}" + zsh_cv_shared_environ="${zsh_cv_shared_environ=yes}" +elif test "$host_os" = cygwin; then + DL_EXT="${DL_EXT=dll}" +##DLLD="${DLLD=dllwrap}" + DLLD="${DLLD=$CC}" +##DLLDFLAGS="${DLLDFLAGS=--export-all-symbols}" + DLLDFLAGS=${DLLDFLAGS=-shared -Wl,--export-all-symbols} + zsh_cv_func_dlsym_needs_underscore=no + DLLDFLAGS=${DLLDFLAGS=} + EXTRA_LDFLAGS=${EXTRA_LDFLAGS=} + zsh_cv_sys_dynamic_clash_ok="${zsh_cv_sys_dynamic_clash_ok=no}" + zsh_cv_sys_dynamic_rtld_global="${zsh_cv_sys_dynamic_rtld_global=yes}" + zsh_cv_sys_dynamic_execsyms="${zsh_cv_sys_dynamic_execsyms=no}" + zsh_cv_sys_dynamic_strip_exe="${zsh_cv_sys_dynamic_strip_exe=yes}" + zsh_cv_sys_dynamic_strip_lib="${zsh_cv_sys_dynamic_strip_lib=yes}" + # + # THAT SUCKS! and must be changed + # + zsh_cv_shared_environ="${zsh_cv_shared_environ=yes}" + LINKMODS=LINKMODS + MOD_EXPORT="__attribute__((__dllexport__))" + MOD_IMPORT_VARIABLE="__attribute__((__dllimport__))" + MOD_IMPORT_FUNCTION= +elif test "x$dynamic" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if your system uses ELF binaries" >&5 +$as_echo_n "checking if your system uses ELF binaries... " >&6; } +if ${zsh_cv_sys_elf+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + zsh_cv_sys_elf=yes +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Test for whether ELF binaries are produced */ +#include +#include +main(argc, argv) +int argc; +char *argv[]; +{ + char b[4]; + int i = open(argv[0],O_RDONLY); + if(i == -1) + exit(1); /* fail */ + if(read(i,b,4)==4 && b[0]==127 && b[1]=='E' && b[2]=='L' && b[3]=='F') + exit(0); /* succeed (yes, it's ELF) */ + else + exit(1); /* fail */ +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_sys_elf=yes +else + zsh_cv_sys_elf=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_elf" >&5 +$as_echo "$zsh_cv_sys_elf" >&6; } + + # We use [0-9]* in case statements, so need to change quoting + + + DL_EXT="${DL_EXT=so}" + if test x$zsh_cv_sys_elf = xyes; then + case "$host" in + mips-sni-sysv4*) + # Forcibly set ld to native compiler to avoid obscure GCC problems + DLLD="${DLLD=/usr/ccs/bin/cc}" + DLLDARG="${LDARG}" + ;; + * ) + DLLD="${DLLD=$CC}" + DLLDARG="${LDARG}" + ;; + esac + else + case "$host" in + *openbsd*) + case "$host_os" in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + DLLD="${DLLD=ld}" + ;; + *) + DLLD="${DLLD=$CC}" + ;; + esac + DLLDARG="${LDARG}" + ;; + *darwin*) + DLLD="${DLLD=$CC}" + DLLDARG="" + ;; + *interix*) + DLLD="${DLLD=$CC}" + DLLDARG="" + ;; + * ) + DLLD="${DLLD=ld}" + DLLDARG="" + ;; + esac + fi + if test -n "$GCC"; then + case "$host_os" in + hpux*) DLLDFLAGS="${DLLDFLAGS=-shared}" ;; + darwin*) DLCFLAGS="${DLCFLAGS=-fno-common}" ;; + interix*) DLCFLAGS="${DLCFLAGS=}" ;; + *) DLCFLAGS="${DLCFLAGS=-fPIC}" ;; + esac + else + case "$host_os" in + hpux*) + DLCFLAGS="${DLCFLAGS=+z}" + DLLDFLAGS="${DLLDFLAGS=-b}" + ;; + sunos*) DLCFLAGS="${DLCFLAGS=-pic}" ;; + solaris*|sysv4*|esix*) DLCFLAGS="${DLCFLAGS=-KPIC}" ;; + esac + fi + case "$host_os" in + osf*) DLLDFLAGS="${DLLDFLAGS=-shared -expect_unresolved '*'}" ;; + *freebsd*|*netbsd*|linux*|irix*|gnu*|interix*|dragonfly*) DLLDFLAGS="${DLLDFLAGS=-shared}" ;; + sunos*) DLLDFLAGS="${DLLDFLAGS=-assert nodefinitions}" ;; + sysv4*|esix*) DLLDFLAGS="${DLLDFLAGS=-G $ldflags}" ;; + aix*) DLLDFLAGS="${DLLDFLAGS=-G -bexpall -lc}" ;; + solaris*|sysv4*|esix*) DLLDFLAGS="${DLLDFLAGS=-G}" ;; + darwin*) DLLDFLAGS="${DLLDFLAGS=-bundle -flat_namespace -undefined suppress}" ;; + beos*|haiku*) DLLDFLAGS="${DLLDFLAGS=-nostart}" ;; + openbsd*) + if test x$zsh_cv_sys_elf = xyes; then + DLLDFLAGS="${DLLDFLAGS=-shared -fPIC}" + else + case "$host_os" in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + DLLDFLAGS="${DLLDFLAGS=-Bshareable}" + ;; + *) + DLLDFLAGS="${DLLDFLAGS=-shared -fPIC}" + ;; + esac + fi + ;; + esac + case "$host" in + *-hpux*) EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-E}" ;; + *openbsd*) + if test x$zsh_cv_sys_elf = xyes; then + EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-E}" + fi + ;; + mips-sni-sysv4) + # + # unfortunately, we have different compilers + # that need different flags + # + if test -n "$GCC"; then + sni_cc_version=GCC + else + sni_cc_version=`$CC -V 2>&1 | head -1` + fi + case "$sni_cc_version" in + *CDS*|GCC ) + EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-Blargedynsym}" + ;; + * ) + EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-LD-Blargedynsym}" + ;; + esac + ;; + *-beos*) + # gcc on BeOS doesn't like -rdynamic... + EXTRA_LDFLAGS="${EXTRA_LDFLAGS= }" + # also, dlopen() at least in Zeta respects $LIBRARY_PATH, so needs %A added to it. + export LIBRARY_PATH="$LIBRARY_PATH:%A/" + ;; + *-haiku*) + # + ;; + esac + + # Done with our shell code, so restore autotools quoting + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can use -rdynamic" >&5 +$as_echo_n "checking if we can use -rdynamic... " >&6; } +if ${zsh_cv_rdynamic_available+:} false; then : + $as_echo_n "(cached) " >&6 +else + old_LDFLAGS="$LDFLAGS" +LDFLAGS="$LDFLAGS -rdynamic" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + zsh_cv_rdynamic_available=yes +EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-rdynamic}" +else + zsh_cvs_rdynamic_available=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LDFLAGS="$old_LDFLAGS" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_rdynamic_available" >&5 +$as_echo "$zsh_cv_rdynamic_available" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if your dlsym() needs a leading underscore" >&5 +$as_echo_n "checking if your dlsym() needs a leading underscore... " >&6; } +if ${zsh_cv_func_dlsym_needs_underscore+:} false; then : + $as_echo_n "(cached) " >&6 +else + echo failed >conftestval && cat >conftest.c <&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && + { ac_try='$DLLD $LDFLAGS $DLLDFLAGS -o conftest.$DL_EXT conftest.o 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && + if test "$cross_compiling" = yes; then : + zsh_cv_func_dlsym_needs_underscore=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif + +extern int fred() ; + +main() +{ + void * handle ; + void * symbol ; + FILE *f=fopen("conftestval", "w"); + if (!f) exit(1); + handle = dlopen("./conftest.$DL_EXT", RTLD_LAZY) ; + if (handle == NULL) { + fprintf (f, "dlopen failed") ; + exit(1); + } + symbol = dlsym(handle, "fred") ; + if (symbol == NULL) { + /* try putting a leading underscore */ + symbol = dlsym(handle, "_fred") ; + if (symbol == NULL) { + fprintf (f, "dlsym failed") ; + exit(1); + } + fprintf (f, "yes") ; + } + else + fprintf (f, "no") ; + exit(0); +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_func_dlsym_needs_underscore=`cat conftestval` +else + zsh_cv_func_dlsym_needs_underscore=failed + dynamic=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_func_dlsym_needs_underscore" >&5 +$as_echo "$zsh_cv_func_dlsym_needs_underscore" >&6; } + if test "x$zsh_cv_func_dlsym_needs_underscore" = xyes; then + $as_echo "#define DLSYM_NEEDS_UNDERSCORE 1" >>confdefs.h + + elif test "x$zsh_cv_func_dlsym_needs_underscore" != xno; then + unset zsh_cv_func_dlsym_needs_underscore + fi +fi + +if test "x$dynamic" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if environ is available in shared libraries" >&5 +$as_echo_n "checking if environ is available in shared libraries... " >&6; } +if ${zsh_cv_shared_environ+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ +else + us= +fi +echo ' +void *zsh_getaddr1() +{ +#ifdef __CYGWIN__ + __attribute__((__dllimport__)) +#endif + extern char ** environ; + return &environ; +}; +' > conftest1.c +sed 's/zsh_getaddr1/zsh_getaddr2/' < conftest1.c > conftest2.c +if { ac_try='$CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + if test "$cross_compiling" = yes; then : + zsh_cv_shared_environ=no + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +main() +{ + void *handle1, *handle2; + void *(*zsh_getaddr1)(), *(*zsh_getaddr2)(); + void *sym1, *sym2; + handle1 = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle1) exit(1); + handle2 = dlopen("./conftest2.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle2) exit(1); + zsh_getaddr1 = (void *(*)()) dlsym(handle1, "${us}zsh_getaddr1"); + zsh_getaddr2 = (void *(*)()) dlsym(handle2, "${us}zsh_getaddr2"); + sym1 = zsh_getaddr1(); + sym2 = zsh_getaddr2(); + if(!sym1 || !sym2) exit(1); + if(sym1 != sym2) exit(1); + dlclose(handle1); + handle1 = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle1) exit(1); + zsh_getaddr1 = (void *(*)()) dlsym(handle1, "${us}zsh_getaddr1"); + sym1 = zsh_getaddr1(); + if(!sym1) exit(1); + if(sym1 != sym2) exit(1); + exit(0); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_shared_environ=yes +else + zsh_cv_shared_environ=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +else + zsh_cv_shared_environ=no +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_shared_environ" >&5 +$as_echo "$zsh_cv_shared_environ" >&6; } + + test "$zsh_cv_shared_environ" = yes || dynamic=no + if test "$ac_cv_func_tgetent" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if tgetent is available in shared libraries" >&5 +$as_echo_n "checking if tgetent is available in shared libraries... " >&6; } +if ${zsh_cv_shared_tgetent+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ +else + us= +fi +echo ' +void *zsh_getaddr1() +{ +#ifdef __CYGWIN__ + __attribute__((__dllimport__)) +#endif + extern int tgetent ( ); + return tgetent; +}; +' > conftest1.c +sed 's/zsh_getaddr1/zsh_getaddr2/' < conftest1.c > conftest2.c +if { ac_try='$CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + if test "$cross_compiling" = yes; then : + zsh_cv_shared_tgetent=no + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +main() +{ + void *handle1, *handle2; + void *(*zsh_getaddr1)(), *(*zsh_getaddr2)(); + void *sym1, *sym2; + handle1 = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle1) exit(1); + handle2 = dlopen("./conftest2.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle2) exit(1); + zsh_getaddr1 = (void *(*)()) dlsym(handle1, "${us}zsh_getaddr1"); + zsh_getaddr2 = (void *(*)()) dlsym(handle2, "${us}zsh_getaddr2"); + sym1 = zsh_getaddr1(); + sym2 = zsh_getaddr2(); + if(!sym1 || !sym2) exit(1); + if(sym1 != sym2) exit(1); + dlclose(handle1); + handle1 = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle1) exit(1); + zsh_getaddr1 = (void *(*)()) dlsym(handle1, "${us}zsh_getaddr1"); + sym1 = zsh_getaddr1(); + if(!sym1) exit(1); + if(sym1 != sym2) exit(1); + exit(0); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_shared_tgetent=yes +else + zsh_cv_shared_tgetent=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +else + zsh_cv_shared_tgetent=no +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_shared_tgetent" >&5 +$as_echo "$zsh_cv_shared_tgetent" >&6; } + + fi + if test "$ac_cv_func_tigetstr" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if tigetstr is available in shared libraries" >&5 +$as_echo_n "checking if tigetstr is available in shared libraries... " >&6; } +if ${zsh_cv_shared_tigetstr+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ +else + us= +fi +echo ' +void *zsh_getaddr1() +{ +#ifdef __CYGWIN__ + __attribute__((__dllimport__)) +#endif + extern int tigetstr ( ); + return tigetstr; +}; +' > conftest1.c +sed 's/zsh_getaddr1/zsh_getaddr2/' < conftest1.c > conftest2.c +if { ac_try='$CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + if test "$cross_compiling" = yes; then : + zsh_cv_shared_tigetstr=no + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +main() +{ + void *handle1, *handle2; + void *(*zsh_getaddr1)(), *(*zsh_getaddr2)(); + void *sym1, *sym2; + handle1 = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle1) exit(1); + handle2 = dlopen("./conftest2.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle2) exit(1); + zsh_getaddr1 = (void *(*)()) dlsym(handle1, "${us}zsh_getaddr1"); + zsh_getaddr2 = (void *(*)()) dlsym(handle2, "${us}zsh_getaddr2"); + sym1 = zsh_getaddr1(); + sym2 = zsh_getaddr2(); + if(!sym1 || !sym2) exit(1); + if(sym1 != sym2) exit(1); + dlclose(handle1); + handle1 = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle1) exit(1); + zsh_getaddr1 = (void *(*)()) dlsym(handle1, "${us}zsh_getaddr1"); + sym1 = zsh_getaddr1(); + if(!sym1) exit(1); + if(sym1 != sym2) exit(1); + exit(0); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_shared_tigetstr=yes +else + zsh_cv_shared_tigetstr=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +else + zsh_cv_shared_tigetstr=no +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_shared_tigetstr" >&5 +$as_echo "$zsh_cv_shared_tigetstr" >&6; } + + fi +fi + +if test "x$dynamic" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if name clashes in shared objects are OK" >&5 +$as_echo_n "checking if name clashes in shared objects are OK... " >&6; } +if ${zsh_cv_sys_dynamic_clash_ok+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ +else + us= +fi +echo 'int fred () { return 42; }' > conftest1.c +echo 'int fred () { return 69; }' > conftest2.c +if { ac_try='$CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + if test "$cross_compiling" = yes; then : + zsh_cv_sys_dynamic_clash_ok=no + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + + +main() +{ + void *handle1, *handle2; + int (*fred1)(), (*fred2)(); + handle1 = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle1) exit(1); + handle2 = dlopen("./conftest2.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle2) exit(1); + fred1 = (int (*)()) dlsym(handle1, "${us}fred"); + fred2 = (int (*)()) dlsym(handle2, "${us}fred"); + if(!fred1 || !fred2) exit(1); + exit((*fred1)() != 42 || (*fred2)() != 69); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_sys_dynamic_clash_ok=yes +else + zsh_cv_sys_dynamic_clash_ok=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +else + zsh_cv_sys_dynamic_clash_ok=no +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_dynamic_clash_ok" >&5 +$as_echo "$zsh_cv_sys_dynamic_clash_ok" >&6; } +if test "$zsh_cv_sys_dynamic_clash_ok" = yes; then + $as_echo "#define DYNAMIC_NAME_CLASH_OK 1" >>confdefs.h + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working RTLD_GLOBAL" >&5 +$as_echo_n "checking for working RTLD_GLOBAL... " >&6; } +if ${zsh_cv_sys_dynamic_rtld_global+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ +else + us= +fi +echo 'int fred () { return 42; }' > conftest1.c +echo 'extern int fred(); int barney () { return fred() + 27; }' > conftest2.c +if { ac_try='$CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + if test "$cross_compiling" = yes; then : + zsh_cv_sys_dynamic_rtld_global=no + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +main() +{ + void *handle; + int (*barneysym)(); + handle = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle) exit(1); + handle = dlopen("./conftest2.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle) exit(1); + barneysym = (int (*)()) dlsym(handle, "${us}barney"); + if(!barneysym) exit(1); + exit((*barneysym)() != 69); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_sys_dynamic_rtld_global=yes +else + zsh_cv_sys_dynamic_rtld_global=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +else + zsh_cv_sys_dynamic_rtld_global=no +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_dynamic_rtld_global" >&5 +$as_echo "$zsh_cv_sys_dynamic_rtld_global" >&6; } + + RTLD_GLOBAL_OK=$zsh_cv_sys_dynamic_rtld_global + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether symbols in the executable are available" >&5 +$as_echo_n "checking whether symbols in the executable are available... " >&6; } +if ${zsh_cv_sys_dynamic_execsyms+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ +else + us= +fi +echo 'extern int fred(); int barney () { return fred() + 27; }' > conftest1.c +if { ac_try='$CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + save_ldflags=$LDFLAGS + LDFLAGS="$LDFLAGS $EXTRA_LDFLAGS" + if test "$cross_compiling" = yes; then : + zsh_cv_sys_dynamic_execsyms=no + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +main() +{ + void *handle; + int (*barneysym)(); + handle = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle) exit(1); + barneysym = (int (*)()) dlsym(handle, "${us}barney"); + if(!barneysym) exit(1); + exit((*barneysym)() != 69); +} + +int fred () { return 42; } + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_sys_dynamic_execsyms=yes +else + zsh_cv_sys_dynamic_execsyms=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + LDFLAGS=$save_ldflags +else + zsh_cv_sys_dynamic_execsyms=no +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_dynamic_execsyms" >&5 +$as_echo "$zsh_cv_sys_dynamic_execsyms" >&6; } + + if test "$zsh_cv_sys_dynamic_execsyms" != yes; then + L=L + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether executables can be stripped" >&5 +$as_echo_n "checking whether executables can be stripped... " >&6; } +if ${zsh_cv_sys_dynamic_strip_exe+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$zsh_cv_sys_dynamic_execsyms" != yes; then + zsh_cv_sys_dynamic_strip_exe=yes +elif + if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ + else + us= + fi + echo 'extern int fred(); int barney() { return fred() + 27; }' > conftest1.c + { ac_try='$CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && + { ac_try='$DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + save_ldflags=$LDFLAGS + LDFLAGS="$LDFLAGS $EXTRA_LDFLAGS -s" + if test "$cross_compiling" = yes; then : + zsh_cv_sys_dynamic_strip_exe=no + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +main() +{ + void *handle; + int (*barneysym)(); + handle = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle) exit(1); + barneysym = (int (*)()) dlsym(handle, "${us}barney"); + if(!barneysym) exit(1); + exit((*barneysym)() != 69); +} + +int fred () { return 42; } + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_sys_dynamic_strip_exe=yes +else + zsh_cv_sys_dynamic_strip_exe=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + LDFLAGS=$save_ldflags +else + zsh_cv_sys_dynamic_strip_exe=no +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_dynamic_strip_exe" >&5 +$as_echo "$zsh_cv_sys_dynamic_strip_exe" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether libraries can be stripped" >&5 +$as_echo_n "checking whether libraries can be stripped... " >&6; } +if ${zsh_cv_sys_dynamic_strip_lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then + us=_ +else + us= +fi +echo 'int fred () { return 42; }' > conftest1.c +if { ac_try='$CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } && +{ ac_try='$DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS -s conftest1.o $LIBS 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + if test "$cross_compiling" = yes; then : + zsh_cv_sys_dynamic_strip_lib=no + +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +main() +{ + void *handle; + int (*fredsym)(); + handle = dlopen("./conftest1.$DL_EXT", RTLD_LAZY | RTLD_GLOBAL); + if(!handle) exit(1); + fredsym = (int (*)()) dlsym(handle, "${us}fred"); + if(!fredsym) exit(1); + exit((*fredsym)() != 42); +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + zsh_cv_sys_dynamic_strip_lib=yes +else + zsh_cv_sys_dynamic_strip_lib=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +else + zsh_cv_sys_dynamic_strip_lib=no +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $zsh_cv_sys_dynamic_strip_lib" >&5 +$as_echo "$zsh_cv_sys_dynamic_strip_lib" >&6; } + + if $strip_exeldflags && test "$zsh_cv_sys_dynamic_strip_exe" = yes; then + EXELDFLAGS="$EXELDFLAGS -s" + fi + if $strip_libldflags && test "$zsh_cv_sys_dynamic_strip_lib" = yes; then + LIBLDFLAGS="$LIBLDFLAGS -s" + fi + if test "$host_os" = cygwin; then + INSTLIB="install.cygwin-lib" + UNINSTLIB="uninstall.cygwin-lib" + fi +else + $strip_exeldflags && EXELDFLAGS="$EXELDFLAGS -s" + $strip_libldflags && LIBLDFLAGS="$LIBLDFLAGS -s" + RTLD_GLOBAL_OK=no +fi + + +if test "x$dynamic" = xyes; then + D=D + $as_echo "#define DYNAMIC 1" >>confdefs.h +else + D=N +fi + + +if test "x$aixdynamic" = xyes; then + E=E + $as_echo "#define AIXDYNAMIC 1" >>confdefs.h +else + E=N +fi + +if test "x$zsh_cv_sys_dynamic_clash_ok" = xyes; then + SHORTBOOTNAMES=yes +else + SHORTBOOTNAMES=no +fi + + + +if test "$host_os" = cygwin; then + EXTRAZSHOBJS="$EXTRAZSHOBJS zsh.res.o" +fi + + +cat >>confdefs.h <<_ACEOF +#define DL_EXT "$DL_EXT" +_ACEOF + +# Generate config.modules. We look for *.mdd files in first and second +# level subdirectories. Any existing line not containing 'auto=y' will be +# retained, provided the .mdd file itself was found. +CONFIG_MODULES=./config.modules +cat < ${CONFIG_MODULES}.sh +srcdir="$srcdir" +dynamic="$dynamic" +CONFIG_MODULES="${CONFIG_MODULES}" +EOM +cat <<\EOM >> ${CONFIG_MODULES}.sh +echo "creating ${CONFIG_MODULES}" +userlist=" " +if test -f ${CONFIG_MODULES}; then + userlist="`sed -e '/^#/d' -e '/auto=y/d' -e 's/ .*/ /' -e 's/^name=/ /' \ + ${CONFIG_MODULES}`" + mv ${CONFIG_MODULES} ${CONFIG_MODULES}.old +else + # Save testing for existence each time. + echo > ${CONFIG_MODULES}.old +fi +(echo "# Edit this file to change the way modules are loaded." +echo "# The format is strict; do not break lines or add extra spaces." +echo "# Run \`make prep' if you change anything here after compiling" +echo "# (there is no need if you change this just after the first time" +echo "# you run \`configure')." +echo "#" +echo "# Values of \`link' are \`static', \`dynamic' or \`no' to compile the" +echo "# module into the shell, link it in at run time, or not use it at all." +echo "# In the final case, no attempt will be made to compile it." +echo "# Use \`static' or \`no' if you do not have dynamic loading." +echo "#" +echo "# Values of \`load' are \`yes' or \`no'; if yes, any builtins etc." +echo "# provided by the module will be autoloaded by the main shell" +echo "# (so long as \`link' is not set to \`no')." +echo "#" +echo "# Values of \`auto' are \`yes' or \`no'. configure sets the value to" +echo "# \`yes'. If you set it by hand to \`no', the line will be retained" +echo "# when the file is regenerated in future." +echo "#" +echo "# Note that the \`functions' entry extends to the end of the line." +echo "# It should not be quoted; it is used verbatim to find files to install." +echo "#" +echo "# You will need to run \`config.status --recheck' if you add a new" +echo "# module." +echo "#" +echo "# You should not change the values for the pseudo-module zsh/main," +echo "# which is the main shell (apart from the functions entry)." +EOM +for modfile in `cd ${srcdir}; echo */*.mdd */*/*.mdd`; do + name= + link= + load= + functions= + result= + . ${srcdir}/$modfile + if test x$name != x && test x"$link" != x; then + case "$link" in + *\ *) eval "link=\`$link\`" + ;; + esac + case "${load}" in + y*) load=" load=yes" + ;; + *) load=" load=no" + ;; + esac + if test "x$functions" != x; then + # N.B. no additional quotes + f=" functions=$functions" + else + f= + fi + case "$link" in + static) result="name=$name modfile=$modfile link=static auto=yes${load}$f" + ;; + dynamic) if test x$dynamic != xno; then + result="name=$name modfile=$modfile link=dynamic\ + auto=yes${load}$f" + else + result="name=$name modfile=$modfile link=no\ + auto=yes load=no$f" + fi + ;; + either) if test x$dynamic != xno; then + result="name=$name modfile=$modfile link=dynamic\ + auto=yes${load}$f" + else + result="name=$name modfile=$modfile link=static\ + auto=yes${load}$f" + fi + ;; + *) result="name=$name modfile=$modfile link=no auto=yes load=no$f" + ;; + esac +cat <> ${CONFIG_MODULES}.sh +case "\$userlist" in + *" $name "*) grep "^name=$name " \${CONFIG_MODULES}.old;; + *) echo "$result";; +esac +EOM + fi +done +cat <<\EOM >> ${CONFIG_MODULES}.sh +) >${CONFIG_MODULES} +rm -f ${CONFIG_MODULES}.old +EOM + + + +CLEAN_MK="${srcdir}/Config/clean.mk" +CONFIG_MK="${srcdir}/Config/config.mk" +DEFS_MK="Config/defs.mk" +VERSION_MK="${srcdir}/Config/version.mk" + + +ac_config_files="$ac_config_files Config/defs.mk Makefile Src/Makefile Test/Makefile" + +ac_config_commands="$ac_config_commands config.modules" + +ac_config_commands="$ac_config_commands stamp-h" + + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Config/defs.mk") CONFIG_FILES="$CONFIG_FILES Config/defs.mk" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "Src/Makefile") CONFIG_FILES="$CONFIG_FILES Src/Makefile" ;; + "Test/Makefile") CONFIG_FILES="$CONFIG_FILES Test/Makefile" ;; + "config.modules") CONFIG_COMMANDS="$CONFIG_COMMANDS config.modules" ;; + "stamp-h") CONFIG_COMMANDS="$CONFIG_COMMANDS stamp-h" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + +if $AWK 'BEGIN { getline <"/dev/null" }' /dev/null; then + ac_cs_awk_getline=: + ac_cs_awk_pipe_init= + ac_cs_awk_read_file=' + while ((getline aline < (F[key])) > 0) + print(aline) + close(F[key])' + ac_cs_awk_pipe_fini= +else + ac_cs_awk_getline=false + ac_cs_awk_pipe_init="print \"cat <<'|#_!!_#|' &&\"" + ac_cs_awk_read_file=' + print "|#_!!_#|" + print "cat " F[key] " &&" + '$ac_cs_awk_pipe_init + # The final `:' finishes the AND list. + ac_cs_awk_pipe_fini='END { print "|#_!!_#|"; print ":" }' +fi +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + +# Create commands to substitute file output variables. +{ + echo "cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1" && + echo 'cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&' && + echo "$ac_subst_files" | sed 's/.*/F["&"]="$&"/' && + echo "_ACAWK" && + echo "_ACEOF" +} >conf$$files.sh && +. ./conf$$files.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +rm -f conf$$files.sh + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + \$ac_cs_awk_pipe_init +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + if (nfields == 3 && !substed) { + key = field[2] + if (F[key] != "" && line ~ /^[ ]*@.*@[ ]*$/) { + \$ac_cs_awk_read_file + next + } + } + print line +} +\$ac_cs_awk_pipe_fini +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | +if $ac_cs_awk_getline; then + $AWK -f "$ac_tmp/subs.awk" +else + $AWK -f "$ac_tmp/subs.awk" | $SHELL +fi \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "config.modules":C) . ./config.modules.sh ;; + "stamp-h":C) echo >stamp-h ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + +eval "zshbin1=${bindir}" +eval "zshbin2=${zshbin1}" +eval "zshman1=${mandir}" +eval "zshman2=${zshman1}" +eval "zshinfo1=${infodir}" +eval "zshinfo2=${zshinfo1}" +eval "zshfndir1=${fndir}" +eval "zshfndir2=${zshfndir1}" + +echo " +zsh configuration +----------------- +zsh version : ${VERSION} +host operating system : ${host_cpu}-${host_vendor}-${host_os} +source code location : ${srcdir} +compiler : ${CC} +preprocessor flags : ${CPPFLAGS} +executable compiler flags : ${CFLAGS}" +if test "x$dynamic" = xyes; then + echo "\ +module compiler flags : ${CFLAGS} ${DLCFLAGS}" +fi +echo "\ +executable linker flags : ${LDFLAGS} ${EXELDFLAGS} ${EXTRA_LDFLAGS}" +if test "x$dynamic" = xyes; then + echo "\ +module linker flags : ${LDFLAGS} ${LIBLDFLAGS} ${DLLDFLAGS}" +fi +echo "\ +library flags : ${LIBS} +installation basename : ${tzsh_name} +binary install path : ${zshbin2} +man page install path : ${zshman2} +info install path : ${zshinfo2}" +if test "$zshfndir2" != no; then + echo "functions install path : ${zshfndir2}" +fi +if test "x$additionalfpath" != x; then + echo "additional fpath entries : ${additionalfpath}" +fi +echo "See config.modules for installed modules and functions. +" + +case x$LIBS in + *-lgdbm*) + echo "WARNING: zsh will be linked against libgdbm. +This means the binary is covered by the GNU General Public License. +This does not affect the source code. +Run configure with --disable-gdbm if required." + ;; +esac + +exit 0 diff --git a/dotfiles/system/.zsh/modules/configure.ac b/dotfiles/system/.zsh/modules/configure.ac new file mode 100644 index 0000000..4dd4eba --- /dev/null +++ b/dotfiles/system/.zsh/modules/configure.ac @@ -0,0 +1,3213 @@ +dnl +dnl configure.ac: Configure template for zsh. +dnl Process this file with autoconf to produce a configure script. +dnl +dnl Copyright (c) 1995-1997 Richard Coleman +dnl All rights reserved. +dnl +dnl Permission is hereby granted, without written agreement and without +dnl license or royalty fees, to use, copy, modify, and distribute this +dnl software and to distribute modified versions of this software for any +dnl purpose, provided that the above copyright notice and the following +dnl two paragraphs appear in all copies of this software. +dnl +dnl In no event shall Richard Coleman or the Zsh Development Group be liable +dnl to any party for direct, indirect, special, incidental, or consequential +dnl damages arising out of the use of this software and its documentation, +dnl even if Richard Coleman and the Zsh Development Group have been advised of +dnl the possibility of such damage. +dnl +dnl Richard Coleman and the Zsh Development Group specifically disclaim any +dnl warranties, including, but not limited to, the implied warranties of +dnl merchantability and fitness for a particular purpose. The software +dnl provided hereunder is on an "as is" basis, and Richard Coleman and the +dnl Zsh Development Group have no obligation to provide maintenance, +dnl support, updates, enhancements, or modifications. +dnl + +AC_INIT +AC_CONFIG_SRCDIR([Src/zsh.h]) +AC_PREREQ([2.69]) +AC_CONFIG_HEADER(config.h) + +dnl What version of zsh are we building ? +. ${srcdir}/Config/version.mk +echo "configuring for zsh $VERSION" + +dnl ---------------------------------------------- +dnl CHECK FOR MACHINE/VENDOR/OPERATING SYSTEM TYPE +dnl ---------------------------------------------- +dnl Find out machine type, vendor, and operating system +dnl What type of host is this? +AC_CANONICAL_HOST +AC_DEFINE_UNQUOTED(MACHTYPE, "$host_cpu", +[Define to be the machine type (microprocessor class or machine model).]) +AC_DEFINE_UNQUOTED(VENDOR, "$host_vendor", +[Define to be a string corresponding the vendor of the machine.]) +AC_DEFINE_UNQUOTED(OSTYPE, "$host_os", +[Define to be the name of the operating system.]) + +dnl ----------------------------- +dnl CHECKING COMMAND LINE OPTIONS +dnl ----------------------------- +dnl Handle --program-prefix, --program-suffix, etc. +zsh_ARG_PROGRAM + +dnl Handle setting of compile flags (CPPFLAGS, CFLAGS, LDFLAGS, LIBS). +zsh_COMPILE_FLAGS($CPPFLAGS, $CFLAGS, $LDFLAGS, $LIBS) + +dnl Do you want to debug zsh? +ifdef([zsh-debug],[undefine([zsh-debug])])dnl +AH_TEMPLATE([DEBUG], +[Define to 1 if you want to debug zsh.]) +AC_ARG_ENABLE(zsh-debug, +AS_HELP_STRING([--enable-zsh-debug],[compile with debug code and debugger symbols]), +[if test x$enableval = xyes; then + AC_DEFINE(DEBUG) +fi]) + +dnl Do you want zsh memory allocation routines. +ifdef([zsh-mem],[undefine([zsh-mem])])dnl +AH_TEMPLATE([ZSH_MEM], +[Define to 1 if you want to use zsh's own memory allocation routines]) +AC_ARG_ENABLE(zsh-mem, +AS_HELP_STRING([--enable-zsh-mem],[compile with zsh memory allocation routines]), +[if test x$enableval = xyes; then + AC_DEFINE(ZSH_MEM) +fi]) + +dnl Do you want to debug zsh memory allocation routines. +ifdef([zsh-mem-debug],[undefine([zsh-mem-debug])])dnl +AH_TEMPLATE([ZSH_MEM_DEBUG], +[Define to 1 if you want to debug zsh memory allocation routines.]) +AC_ARG_ENABLE(zsh-mem-debug, +AS_HELP_STRING([--enable-zsh-mem-debug],[debug zsh memory allocation routines]), +[if test x$enableval = xyes; then + AC_DEFINE(ZSH_MEM_DEBUG) +fi]) + +dnl Do you want to print warnings when errors in memory allocation. +AH_TEMPLATE([ZSH_MEM_WARNING], +[Define to 1 if you want to turn on warnings of memory allocation errors]) +ifdef([zsh-mem-warning],[undefine([zsh-mem-warning])])dnl +AC_ARG_ENABLE(zsh-mem-warning, +AS_HELP_STRING([--enable-zsh-mem-warning],[print warnings for errors in memory allocation]), +[if test x$enableval = xyes; then + AC_DEFINE(ZSH_MEM_WARNING) +fi]) + +dnl Do you want to turn on error checking for free(). +ifdef([zsh-secure-free],[undefine([zsh-secure-free])])dnl +AH_TEMPLATE([ZSH_SECURE_FREE], +[Define to 1 if you want to turn on memory checking for free().]) +AC_ARG_ENABLE(zsh-secure-free, +AS_HELP_STRING([--enable-zsh-secure-free],[turn on error checking for free()]), +[if test x$enableval = xyes; then + AC_DEFINE(ZSH_SECURE_FREE) +fi]) + +dnl Do you want to debug zsh heap allocation? +dnl Does not depend on zsh-mem. +ifdef([zsh-heap-debug],[undefine([zsh-heap-debug])])dnl +AH_TEMPLATE([ZSH_HEAP_DEBUG], +[Define to 1 if you want to turn on error checking for heap allocation.]) +AC_ARG_ENABLE(zsh-heap-debug, +AS_HELP_STRING([--enable-zsh-heap-debug],[turn on error checking for heap allocation]), +[if test x$enableval = xyes; then + AC_DEFINE(ZSH_HEAP_DEBUG) +fi]) + +dnl Do you want to allow Valgrind to debug heap allocation? +ifdef([zsh-valgrind],[undefine([zsh-valgrind])])dnl +AH_TEMPLATE([ZSH_VALGRIND], +[Define to 1 if you want to add code for valgrind to debug heap memory.]) +AC_ARG_ENABLE(zsh-valgrind, +AS_HELP_STRING([--enable-zsh-valgrind],[turn on support for valgrind debugging of heap memory]), +[if test x$enableval = xyes; then + AC_DEFINE(ZSH_VALGRIND) +fi]) + +dnl Do you want debugging information on internal hash tables. +dnl This turns on the `hashinfo' builtin command. +ifdef([zsh-hash-debug],[undefine([zsh-hash-debug])])dnl +AH_TEMPLATE([ZSH_HASH_DEBUG], +[Define to 1 if you want to get debugging information on internal + hash tables. This turns on the `hashinfo' builtin.]) +AC_ARG_ENABLE(zsh-hash-debug, +AS_HELP_STRING([--enable-zsh-hash-debug],[turn on debugging of internal hash tables]), +[if test x$enableval = xyes; then + AC_DEFINE(ZSH_HASH_DEBUG) +fi]) + +dnl Do you want to dynamically allocate memory on the stack where possible? +ifdef([stack-allocation],[undefine([stack-allocation])])dnl +AH_TEMPLATE([USE_STACK_ALLOCATION], +[Define to 1 if you want to allocate stack memory e.g. with `alloca'.]) +AC_ARG_ENABLE(stack-allocation, +AS_HELP_STRING([--enable-stack-allocation],[allocate stack memory e.g. with `alloca']), +[if test x$enableval = xyes; then + AC_DEFINE(USE_STACK_ALLOCATION) +fi]) + +dnl Pathnames for global zsh scripts +ifdef([etcdir],[undefine([etcdir])])dnl +AC_ARG_ENABLE(etcdir, +AS_HELP_STRING([--enable-etcdir=DIR],[the default directory for global zsh scripts]), +[etcdir="$enableval"], [etcdir=/etc]) + +ifdef([zshenv],[undefine([zshenv])])dnl +AC_ARG_ENABLE(zshenv, +AS_HELP_STRING([--enable-zshenv=FILE],[the full pathname of the global zshenv script]), +[zshenv="$enableval"], +[if test "x$etcdir" = xno; then + zshenv=no +else + zshenv="$etcdir/zshenv" +fi]) +AH_TEMPLATE([GLOBAL_ZSHENV], +[The global file to source absolutely first whenever zsh is run; + if undefined, don't source anything.]) +if test "x$zshenv" != xno; then + AC_DEFINE_UNQUOTED(GLOBAL_ZSHENV, "$zshenv") +fi + +ifdef([zshrc],[undefine([zshrc])])dnl +AC_ARG_ENABLE(zshrc, +AS_HELP_STRING([--enable-zshrc=FILE],[the full pathname of the global zshrc script]), +[zshrc="$enableval"], +[if test "x$etcdir" = xno; then + zshrc=no +else + zshrc="$etcdir/zshrc" +fi]) +AH_TEMPLATE([GLOBAL_ZSHRC], +[The global file to source whenever zsh is run; + if undefined, don't source anything]) +if test "x$zshrc" != xno; then + AC_DEFINE_UNQUOTED(GLOBAL_ZSHRC, "$zshrc") +fi + +ifdef([zprofile],[undefine([zprofile])])dnl +AC_ARG_ENABLE(zprofile, +AS_HELP_STRING([--enable-zprofile=FILE],[the full pathname of the global zprofile script]), +[zprofile="$enableval"], +[if test "x$etcdir" = xno; then + zprofile=no +else + zprofile="$etcdir/zprofile" +fi]) +AH_TEMPLATE([GLOBAL_ZPROFILE], +[The global file to source whenever zsh is run as a login shell, + before zshrc is read; if undefined, don't source anything.]) +if test "x$zprofile" != xno; then + AC_DEFINE_UNQUOTED(GLOBAL_ZPROFILE, "$zprofile") +fi + +ifdef([zlogin],[undefine([zlogin])])dnl +AC_ARG_ENABLE(zlogin, +AS_HELP_STRING([--enable-zlogin=FILE],[the full pathname of the global zlogin script]), +[zlogin="$enableval"], +[if test "x$etcdir" = xno; then + zlogin=no +else + zlogin="$etcdir/zlogin" +fi]) +AH_TEMPLATE([GLOBAL_ZLOGIN], +[The global file to source whenever zsh is run as a login shell; + if undefined, don't source anything]) +if test "x$zlogin" != xno; then + AC_DEFINE_UNQUOTED(GLOBAL_ZLOGIN, "$zlogin") +fi + +ifdef([zlogout],[undefine([zlogout])])dnl +AC_ARG_ENABLE(zlogout, +AS_HELP_STRING([--enable-zlogout=FILE],[the full pathname of the global zlogout script]), +[zlogout="$enableval"], +[if test "x$etcdir" = xno; then + zlogout=no +else + zlogout="$etcdir/zlogout" +fi]) +AH_TEMPLATE([GLOBAL_ZLOGOUT], +[The global file to source whenever zsh was run as a login shell. + This is sourced right before exiting. If undefined, don't source + anything.]) +if test "x$zlogout" != xno; then + AC_DEFINE_UNQUOTED(GLOBAL_ZLOGOUT, "$zlogout") +fi + +AC_SUBST(zshenv)dnl +AC_SUBST(zshrc)dnl +AC_SUBST(zprofile)dnl +AC_SUBST(zlogin)dnl +AC_SUBST(zlogout)dnl + +dnl Do you want dynamically loaded binary modules. +ifdef([dynamic],[undefine([dynamic])])dnl +AC_ARG_ENABLE(dynamic, +AS_HELP_STRING([--disable-dynamic],[turn off dynamically loaded binary modules]), +[dynamic="$enableval"], [dynamic=yes]) + +dnl Do you want to disable restricted on r* commands +ifdef([restricted-r],[undefine([restricted-r])])dnl +AH_TEMPLATE([RESTRICTED_R], +[Undefine this if you don't want to get a restricted shell + when zsh is exec'd with basename that starts with r. + By default this is defined.]) +AC_ARG_ENABLE(restricted-r, +AS_HELP_STRING([--disable-restricted-r],[turn off r* invocation for restricted shell]), +[if test x$enableval = xyes; then + AC_DEFINE(RESTRICTED_R) +fi], +AC_DEFINE(RESTRICTED_R) +) + +dnl Do you want to disable use of locale functions +AH_TEMPLATE([CONFIG_LOCALE], +[Undefine if you don't want local features. By default this is defined.]) +AC_ARG_ENABLE([locale], +AS_HELP_STRING([--disable-locale],[turn off locale features]), +[if test x$enableval = xyes; then + AC_DEFINE(CONFIG_LOCALE) +fi], +AC_DEFINE(CONFIG_LOCALE) +) + +dnl Do you want to compile as K&R C. +AC_ARG_ENABLE(ansi2knr, +AS_HELP_STRING([--enable-ansi2knr],[translate source to K&R C before compiling]), +[ansi2knr="$enableval"], [ansi2knr=default]) + +ifdef([runhelpdir],[undefine([runhelpdir])])dnl +AC_ARG_ENABLE(runhelpdir, +AS_HELP_STRING([--enable-runhelpdir=DIR],[the directory in which to install run-help files]), +[if test x"$enableval" = xno; then + runhelpdir= +else + runhelpdir="$enableval" +fi], [runhelpdir=yes]) +if test x"$runhelpdir" = xyes; then + runhelpdir=${datadir}/${tzsh_name}/'${VERSION}'/help +fi +if test x"$runhelpdir" = x; then + runhelp= +else + runhelp=runhelp +fi + +ifdef([fndir],[undefine([fndir])])dnl +AC_ARG_ENABLE(fndir, +AS_HELP_STRING([--enable-fndir=DIR],[the directory in which to install functions]), +dnl ${VERSION} to be determined at compile time. +[if test x$enableval = xyes; then + fndir=${datadir}/${tzsh_name}/'${VERSION}'/functions +else + fndir="$enableval" +fi], [fndir=${datadir}/${tzsh_name}/'${VERSION}'/functions]) + +ifdef([sitefndir],[undefine([sitefndir])])dnl +AC_ARG_ENABLE(site-fndir, +AS_HELP_STRING([--enable-site-fndir=DIR],[same for site functions (not version specific)]), +[if test x$enableval = xyes; then + sitefndir=${datadir}/${tzsh_name}/site-functions +else + sitefndir="$enableval" +fi], [sitefndir=${datadir}/${tzsh_name}/site-functions]) + +dnl Add /usr/local/share/zsh/site-functions if not yet present +dnl owing to $sitefndir, whether or not explicitly given. +dnl If not explicitly given, it hasn't been expanded yet. +if test X$sitefndir = X/usr/local/share/zsh/site-functions || \ + test X$sitefndir = Xno +then fixed_sitefndir='' +elif test X$prefix != X/usr/local; then + if test X$prefix = XNONE && test X$ac_default_prefix = X/usr/local; then + if test X$tzsh_name != Xzsh + then fixed_sitefndir=/usr/local/share/zsh/site-functions + else fixed_sitefndir='' + fi + else fixed_sitefndir=/usr/local/share/zsh/site-functions + fi +elif test X$tzsh_name != Xzsh +then fixed_sitefndir=/usr/local/share/zsh/site-functions +else fixed_sitefndir='' +fi + +ifdef([function_subdirs],[undefine([function_subdirs])]) +AC_ARG_ENABLE(function-subdirs, +AS_HELP_STRING([--enable-function-subdirs],[install functions in subdirectories])) + +if test "x${enable_function_subdirs}" != x && + test "x${enable_function_subdirs}" != xno; then + FUNCTIONS_SUBDIRS=yes +else + FUNCTIONS_SUBDIRS=no +fi + +ifdef([additionalfpath],[undefine([additionalfpath])])dnl +AC_ARG_ENABLE(additional-fpath, +AS_HELP_STRING([--enable-additional-fpath=DIR],[add directories to default function path]), +[if test x$enableval = xyes; then + additionalfpath="" +else + additionalfpath="${enableval}" +fi], [additionalfpath=""]) + +AC_SUBST(runhelpdir)dnl +AC_SUBST(runhelp)dnl +AC_SUBST(additionalfpath)dnl +AC_SUBST(fndir)dnl +AC_SUBST(sitefndir)dnl +AC_SUBST(fixed_sitefndir)dnl +AC_SUBST(FUNCTIONS_SUBDIRS)dnl + +dnl Directories for scripts such as newuser. + +ifdef([scriptdir],[undefine([scriptdir])])dnl +AC_ARG_ENABLE(scriptdir, +AS_HELP_STRING([--enable-scriptdir=DIR],[the directory in which to install scripts]), +dnl ${VERSION} to be determined at compile time. +[if test x$enableval = xyes; then + scriptdir=${datadir}/${tzsh_name}/'${VERSION}'/scripts +else + scriptdir="$enableval" +fi], [scriptdir=${datadir}/${tzsh_name}/'${VERSION}'/scripts]) + +ifdef([sitescriptdir],[undefine([sitescriptdir])])dnl +AC_ARG_ENABLE(site-scriptdir, +AS_HELP_STRING([--enable-site-scriptdir=DIR],[same for site scripts (not version specific)]), +[if test x$enableval = xyes; then + sitescriptdir=${datadir}/${tzsh_name}/scripts +else + sitescriptdir="$enableval" +fi], [sitescriptdir=${datadir}/${tzsh_name}/scripts]) + +AC_SUBST(scriptdir)dnl +AC_SUBST(sitescriptdir)dnl + +dnl htmldir is already handled, but if it wasn't set, use +dnl the standard zsh default. +if test x$htmldir = x'${docdir}' || test x$htmldir = x; then + htmldir='$(datadir)/$(tzsh)/htmldoc' +fi + +AH_TEMPLATE([CUSTOM_PATCHLEVEL], +[Define to a custom value for the ZSH_PATCHLEVEL parameter]) +AC_ARG_ENABLE(custom-patchlevel, +AS_HELP_STRING([--enable-custom-patchlevel],[set a custom ZSH_PATCHLEVEL value]), +[if test x$enableval != x && test x$enableval != xno; then + AC_DEFINE_UNQUOTED([CUSTOM_PATCHLEVEL], ["$enableval"]) +fi]) + +dnl Do you want maildir support? +ifdef([maildir_support],[undefine([maildir_support])])dnl +AH_TEMPLATE([MAILDIR_SUPPORT], +[Define for Maildir support]) +AC_ARG_ENABLE(maildir-support, +AS_HELP_STRING([--enable-maildir-support],[enable maildir support in MAIL and MAILPATH]), +[if test x$enableval = xyes; then + AC_DEFINE(MAILDIR_SUPPORT) +fi]) + +dnl Do you want to set a maximum function depth? +ifdef([max_function_depth],[undefine([max_function_depth])])dnl +AH_TEMPLATE([MAX_FUNCTION_DEPTH], +[Define for function depth limits]) +AC_ARG_ENABLE(max-function-depth, +AS_HELP_STRING([--enable-max-function-depth=MAX],[limit function depth to MAX, default 500]), +[if test x$enableval = xyes; then + AC_DEFINE(MAX_FUNCTION_DEPTH, 500) +elif test x$enableval != xno; then + AC_DEFINE_UNQUOTED(MAX_FUNCTION_DEPTH, $enableval) +fi], +[AC_DEFINE(MAX_FUNCTION_DEPTH, 500)] +) + +ifdef([default_readnullcmd],[undefine([default_readnullcmd])])dnl +AH_TEMPLATE([DEFAULT_READNULLCMD], +[Define default pager used by readnullcmd]) +AC_ARG_ENABLE(readnullcmd, +AS_HELP_STRING([--enable-readnullcmd=PAGER],[pager used when READNULLCMD is not set]), +[if test x$enableval = xyes; then + AC_DEFINE(DEFAULT_READNULLCMD,"more") +elif test x$enableval != xno; then + AC_DEFINE_UNQUOTED(DEFAULT_READNULLCMD,"$enableval") +fi], +[AC_DEFINE(DEFAULT_READNULLCMD,"more")] +) + +dnl Do you want to look for capability support? +AC_ARG_ENABLE(cap, +AS_HELP_STRING([--enable-cap],[enable the search for POSIX capabilities (may require additional headers to be added by hand)])) + +AC_ARG_ENABLE(gdbm, +AS_HELP_STRING([--disable-gdbm],[turn off search for gdbm library]), +[gdbm="$enableval"], [gdbm=yes]) + +dnl ------------------ +dnl CHECK THE COMPILER +dnl ------------------ +dnl We want these before the checks, so the checks can modify their values. +test -z "${CFLAGS+set}" && CFLAGS= auto_cflags=1 +test -z "${LDFLAGS+set}" && LDFLAGS= auto_ldflags=1 + +AC_PROG_CC + +dnl Check for large file support. + +dnl Gross hack for ReliantUNIX - GCC does not understand getconf options +dnl For now just disable LFS in this case +dnl Any takers? +if test "$host" = mips-sni-sysv4 && test -n "$GCC"; then + : +else + AC_SYS_LARGEFILE +fi + +dnl if the user hasn't specified CFLAGS, then +dnl if compiler is gcc, then use -O2 and some warning flags +dnl else use -O +if test -n "$auto_cflags" && test ."$ansi2knr" != .yes; then + if test "${enable_zsh_debug}" = yes; then + if test -n "$GCC"; then + CFLAGS="$CFLAGS -Wall -Wmissing-prototypes -ggdb" + else + CFLAGS="$CFLAGS -g" + fi + else + if test -n "$GCC"; then + CFLAGS="$CFLAGS -Wall -Wmissing-prototypes -O2" + else + CFLAGS="$CFLAGS -O" + fi + fi +fi +if test -n "$auto_ldflags"; then + case "${enable_zsh_debug}$host_os" in + yesaix*|yeshpux*|yesnetbsd*|yesopenbsd*) ;; # "ld -g" is not valid on these systems + darwin*) LDFLAGS=-Wl,-x ;; + yes*) LDFLAGS=-g ;; + *) LDFLAGS=-s ;; + esac +fi + +dnl ---------- +dnl SCO KLUDGE +dnl ---------- +dnl Sco doesn't define any useful compiler symbol, +dnl so we will check for sco and define __sco if +dnl found. +case "$host_os" in + sco*) CFLAGS="-D__sco $CFLAGS" ;; +esac + +sed=':1 + s/ -s / /g + t1 + s/^ *// + s/ *$//' + +case " $LDFLAGS " in + *" -s "*) strip_exeldflags=true strip_libldflags=true + LDFLAGS=`echo " $LDFLAGS " | sed "$sed"` ;; + *) strip_exeldflags=false strip_libldflags=false ;; +esac + +case " ${EXELDFLAGS+$EXELDFLAGS }" in + " ") ;; + *" -s "*) strip_exeldflags=true + EXELDFLAGS=`echo " $EXELDFLAGS " | sed "$sed"` ;; + *) strip_exeldflags=false ;; +esac + +case " ${LIBLDFLAGS+$LIBLDFLAGS }" in + " ") ;; + *" -s "*) strip_libldflags=true + LIBLDFLAGS=`echo " $LIBLDFLAGS " | sed "$sed"` ;; + *) strip_libldflags=false ;; +esac + +AC_SUBST(CFLAGS)dnl +AC_SUBST(LDFLAGS)dnl +AC_SUBST(EXELDFLAGS)dnl +AC_SUBST(LIBLDFLAGS)dnl + +AC_PROG_CPP dnl Figure out how to run C preprocessor. +AC_C_CONST dnl Does compiler support `const'. + +dnl Default preprocessing on Mac OS X produces warnings +dnl Mac OS X 10.6 (darwin10.x.x) does not need this. +case "$host_os" in + darwin[[0-9]].*) CPP="$CPP -traditional-cpp" ;; +esac + +fp_PROG_CC_STDC +AC_MSG_CHECKING([whether to use prototypes]) +if test ."$ansi2knr" = .yes || test ."$ansi2knr" = .no; then + msg="(overridden) " +else + msg= + if test ."$fp_cv_prog_cc_stdc" = .no; then + ansi2knr=yes + else + ansi2knr=no + fi +fi +AH_TEMPLATE([PROTOTYPES], +[Define to 1 if ANSI function prototypes are usable.]) +if test "$ansi2knr" = yes; then + AC_MSG_RESULT(${msg}no) + U=_ +else + AC_MSG_RESULT(${msg}yes) + AC_DEFINE(PROTOTYPES) + U= +fi +AC_SUBST(U) + +AC_FUNC_ALLOCA dnl Check how to get `alloca'. + +dnl If the compiler supports union initialisation +AC_CACHE_CHECK(if the compiler supports union initialisation, +zsh_cv_c_have_union_init, +[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[union{void *p;long l;}u={0};]], [[u.l=1;]])],[zsh_cv_c_have_union_init=yes],[zsh_cv_c_have_union_init=no])]) +AH_TEMPLATE([HAVE_UNION_INIT], +[Define to 1 if the compiler can initialise a union.]) +if test x$zsh_cv_c_have_union_init = xyes; then + AC_DEFINE(HAVE_UNION_INIT) +fi + +dnl Checking if compiler correctly cast signed to unsigned. +AC_CACHE_CHECK(if signed to unsigned casting is broken, +zsh_cv_c_broken_signed_to_unsigned_casting, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[main(){return((int)(unsigned char)((char) -1) == 255);}]])],[zsh_cv_c_broken_signed_to_unsigned_casting=yes],[zsh_cv_c_broken_signed_to_unsigned_casting=no],[zsh_cv_c_broken_signed_to_unsigned_casting=no])]) +AH_TEMPLATE([BROKEN_SIGNED_TO_UNSIGNED_CASTING], +[Define to 1 if compiler incorrectly cast signed to unsigned.]) +if test x$zsh_cv_c_broken_signed_to_unsigned_casting = xyes; then + AC_DEFINE(BROKEN_SIGNED_TO_UNSIGNED_CASTING) +fi + +dnl Checking if the compiler supports variable-length arrays +AC_CACHE_CHECK(if the compiler supports variable-length arrays, +zsh_cv_c_variable_length_arrays, +[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[int foo(), n;]], [[int i[foo()], a[n+1];]])],[zsh_cv_c_variable_length_arrays=yes],[zsh_cv_c_variable_length_arrays=no])]) +AH_TEMPLATE([HAVE_VARIABLE_LENGTH_ARRAYS], +[Define to 1 if compiler supports variable-length arrays]) +if test x$zsh_cv_c_variable_length_arrays = xyes; then + AC_DEFINE(HAVE_VARIABLE_LENGTH_ARRAYS) +fi + +dnl ------------------ +dnl CHECK FOR PROGRAMS +dnl ------------------ +AC_PROG_MAKE_SET dnl Does make define $MAKE +AC_PROG_INSTALL dnl Check for BSD compatible `install' +AC_PROG_AWK dnl Check for mawk,gawk,nawk, then awk. +AC_PROG_LN dnl Check for working ln, for "make install" +AC_PROG_LN_S dnl Use ln -s/ln/cp for "make install.runhelp" +AC_PROG_EGREP dnl sets $EGREP to grep -E or egrep +AC_CHECK_PROGS([YODL], [yodl], [: yodl]) + +YODL_OPTIONS='' +if test "x$ac_cv_prog_YODL" = xyodl; then + case `yodl --version` in + *"version 2."*) YODL_OPTIONS='-k' ;; + *"version 3."*) YODL_OPTIONS='-k -L' ;; + *"version 4."*) YODL_OPTIONS='-k -L' ;; + esac +fi +AC_SUBST(YODL_OPTIONS) + +AC_CHECK_PROGS([TEXI2DVI], [texi2dvi], [: texi2dvi]) +AC_CHECK_PROGS([TEXI2PDF], [texi2pdf], [: texi2pdf]) +AC_CHECK_PROGS([TEXI2HTML], [texi2any texi2html], [: texi2html]) + +if test x"$TEXI2PDF" != xtexi2pdf && test x"$TEXI2DVI" = xtexi2dvi; then + TEXI2PDF='texi2dvi --pdf' +fi + +if test x"$TEXI2HTML" = xtexi2any; then + TEXI2HTML='texi2any -c TEXI2HTML=1' +fi + +case "$LC_PAPER" in + ??_US*) PAPERSIZE=us ;; + *) PAPERSIZE=a4 ;; +esac +AC_SUBST(PAPERSIZE) + +AC_CHECK_PROGS([ANSI2KNR], [ansi2knr], [: ansi2knr]) + +if test x"$ansi2knr" = xyes && test x"$ANSI2KNR" = x": ansi2knr"; then + echo "----------" + echo "configure fatal error:" + echo "ansi2knr was specified (--enable-ansi2knr) but the program could not be found." + echo "Either remove the configure option if it is not required or build the ansi2knr" + echo "program before reconfiguring Zsh. The source code for ansi2knr is also" + echo "available in the GPL directory on Zsh distribution sites." + exit 1 +fi + +dnl ------------------ +dnl CHECK HEADER FILES +dnl ------------------ +AC_HEADER_DIRENT +AC_HEADER_STDC +AC_HEADER_TIME +AC_HEADER_STAT +AC_HEADER_SYS_WAIT + +oldcflags="$CFLAGS" + +AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \ + termios.h sys/param.h sys/filio.h string.h memory.h \ + limits.h fcntl.h libc.h sys/utsname.h sys/resource.h \ + locale.h errno.h stdio.h stdarg.h varargs.h stdlib.h \ + unistd.h sys/capability.h \ + utmp.h utmpx.h sys/types.h pwd.h grp.h poll.h sys/mman.h \ + netinet/in_systm.h langinfo.h wchar.h stddef.h \ + sys/stropts.h iconv.h ncurses.h ncursesw/ncurses.h \ + ncurses/ncurses.h) +if test x$dynamic = xyes; then + AC_CHECK_HEADERS(dlfcn.h) + AC_CHECK_HEADERS(dl.h) +fi + +dnl Some SCO systems cannot include both sys/time.h and sys/select.h +AH_TEMPLATE([TIME_H_SELECT_H_CONFLICTS], +[Define if sys/time.h and sys/select.h cannot be both included.]) +if test x$ac_cv_header_sys_time_h = xyes && test x$ac_cv_header_sys_select_h = xyes; then + AC_CACHE_CHECK(for conflicts in sys/time.h and sys/select.h, + zsh_cv_header_time_h_select_h_conflicts, + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +#include ]], [[int i;]])],[zsh_cv_header_time_h_select_h_conflicts=no],[zsh_cv_header_time_h_select_h_conflicts=yes])]) + if test x$zsh_cv_header_time_h_select_h_conflicts = xyes; then + AC_DEFINE(TIME_H_SELECT_H_CONFLICTS) + fi +fi + +AH_TEMPLATE([GWINSZ_IN_SYS_IOCTL], +[Define if TIOCGWINSZ is defined in sys/ioctl.h but not in termios.h.]) +if test x$ac_cv_header_termios_h = xyes; then + AC_CACHE_CHECK(TIOCGWINSZ in termios.h, + zsh_cv_header_termios_h_tiocgwinsz, + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#include ]], [[int x = TIOCGWINSZ;]])],[zsh_cv_header_termios_h_tiocgwinsz=yes],[zsh_cv_header_termios_h_tiocgwinsz=no])]) +else + zsh_cv_header_termios_h_tiocgwinsz=no +fi +if test x$zsh_cv_header_termios_h_tiocgwinsz = xno; then + AC_CACHE_CHECK(TIOCGWINSZ in sys/ioctl.h, + zsh_cv_header_sys_ioctl_h_tiocgwinsz, + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#include ]], [[int x = TIOCGWINSZ;]])],[zsh_cv_header_sys_ioctl_h_tiocgwinsz=yes],[zsh_cv_header_sys_ioctl_h_tiocgwinsz=no])]) + if test x$zsh_cv_header_sys_ioctl_h_tiocgwinsz = xyes; then + AC_DEFINE(GWINSZ_IN_SYS_IOCTL) + fi +fi + +AH_TEMPLATE([WINSIZE_IN_PTEM], +[Define if your should include sys/stream.h and sys/ptem.h.]) +AC_CACHE_CHECK(for streams headers including struct winsize, +ac_cv_winsize_in_ptem, +[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +#include ]], [[struct winsize wsz]])],[ac_cv_winsize_in_ptem=yes],[ac_cv_winsize_in_ptem=no])]) +if test x$ac_cv_winsize_in_ptem = xyes; then + AC_DEFINE(WINSIZE_IN_PTEM) +fi + +dnl ------------------- +dnl CHECK FOR LIBRARIES +dnl ------------------- + +dnl On some systems, modules need to be linked against libc explicitly, +dnl in case they require objects that exist only in the static version +dnl and might not be compiled into the zsh executable. +dnl On ReliantUNIX -lc better be the last library, else funny things +dnl may happen. +AC_CHECK_LIB(c, printf, [LIBS="$LIBS -lc"]) + +AC_CHECK_LIB(m, pow) + +AC_CHECK_LIB(rt, clock_gettime) + +dnl Various features of ncurses depend on having the right header +dnl (the system's own curses.h may well not be good enough). +dnl So don't search for ncurses unless we found the header. +if test x$ac_cv_header_ncurses_h = xyes || test x$ac_cv_header_ncurses_ncurses_h = xyes || test x$ac_cv_header_ncursesw_ncurses_h = xyes; then + ncursesw_test=ncursesw + ncurses_test=ncurses +else + ncursesw_test= + ncurses_test= +fi + +dnl Prefer BSD termcap library to SysV curses library, except on certain +dnl SYSV-derived systems. However, if we find terminfo and termcap +dnl stuff in the same library we will use that; typically this +dnl is ncurses or curses. +dnl On pre-11.11 HPUX, Hcurses is reported to work better than curses. +dnl Prefer ncurses to curses on all systems. tinfo isn't very common now. +AC_ARG_WITH(term-lib, +AS_HELP_STRING([--with-term-lib=LIBS],[search space-separated LIBS for terminal handling]), +[if test "x$withval" != xno && test "x$withval" != x ; then + termcap_curses_order="$withval" + AC_SEARCH_LIBS(tigetstr, [$termcap_curses_order]) +else + termcap_curses_order="$ncursesw_test tinfo termcap $ncurses_test curses" +fi], +[case "$host_os" in + solaris*) + termcap_curses_order="$ncursesw_test $ncurses_test curses termcap" ;; + hpux10.*|hpux11.*) + DL_EXT="${DL_EXT=sl}" + termcap_curses_order="Hcurses $ncursesw_test $ncurses_test curses termcap" ;; + *) + termcap_curses_order="$ncursesw_test tinfo termcap $ncurses_test curses" ;; +esac])dnl + +AH_TEMPLATE([ZSH_NO_XOPEN], +[Define if _XOPEN_SOURCE_EXTENDED should not be defined to avoid clashes]) +AC_CACHE_CHECK(if _XOPEN_SOURCE_EXTENDED should not be defined, +zsh_cv_no_xopen, +[[case "$host_os" in + *freebsd5*|*freebsd6.[012]*|*aix*) + zsh_cv_no_xopen=yes + ;; + *) + zsh_cv_no_xopen=no + ;; +esac]]) +if test x$zsh_cv_no_xopen = xyes; then + AC_DEFINE(ZSH_NO_XOPEN) +fi + +dnl Check for tigetflag (terminfo) before tgetent (termcap). +dnl That's so that on systems where termcap and [n]curses are +dnl both available and both contain termcap functions, while +dnl only [n]curses contains terminfo functions, we only link against +dnl [n]curses. +AC_SEARCH_LIBS(tigetflag, [$termcap_curses_order]) +AC_SEARCH_LIBS(tgetent, [$termcap_curses_order], + true, + AC_MSG_FAILURE(["No terminal handling library was found on your system. +This is probably a library called 'curses' or 'ncurses'. You may +need to install a package called 'curses-devel' or 'ncurses-devel' on your +system."], 255)) +AC_CHECK_HEADERS(curses.h, [], +[AC_CACHE_CHECK(for Solaris 8 curses.h mistake, ac_cv_header_curses_solaris, +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[]])],[ac_cv_header_curses_h=yes +ac_cv_header_curses_solaris=yes],[ac_cv_header_curses_h=no +ac_cv_header_curses_solaris=no])) +if test x$ac_cv_header_curses_solaris = xyes; then +AC_DEFINE(HAVE_CURSES_H) +fi]) + +dnl If our terminal library is not ncurses, don't try including +dnl any ncurses headers. +AC_CACHE_CHECK(if we need to ignore ncurses, zsh_cv_ignore_ncurses, +[case $LIBS in + *-lncurses*) + zsh_cv_ignore_ncurses=no + ;; + *) + zsh_cv_ignore_ncurses=yes + ;; +esac]) + +AC_SEARCH_LIBS(getpwnam, nsl) + +dnl I am told that told that unicos reqire these for nis_list +if test `echo $host_os | sed 's/^\(unicos\).*/\1/'` = unicos; then + LIBS="-lcraylm -lkrb -lnisdb -lnsl -lrpcsvc $LIBS" +fi + +if test "x$dynamic" = xyes; then + AC_CHECK_LIB(dl, dlopen) +fi + +if test x$enable_cap = xyes; then + AC_CHECK_LIB(cap, cap_get_proc) +fi + +AC_CHECK_LIB(socket, socket) +AC_SEARCH_LIBS(gethostbyname2, bind) + +case $LIBS in + *-lbind*) + AC_CHECK_HEADERS(bind/netdb.h) + ;; +esac + +dnl --------------- +dnl CHECK FOR ICONV +dnl --------------- + +dnl Find iconv. It may be in libiconv and may be iconv() or libiconv() +if test "x$ac_cv_header_iconv_h" = "xyes"; then + AC_CHECK_FUNC(iconv, ac_found_iconv=yes, ac_found_iconv=no) + if test "x$ac_found_iconv" = "xno"; then + AC_CHECK_LIB(iconv, iconv, ac_found_iconv=yes) + if test "x$ac_found_iconv" = "xno"; then + AC_CHECK_LIB(iconv, libiconv, ac_found_iconv=yes) + fi + if test "x$ac_found_iconv" != "xno"; then + LIBS="-liconv $LIBS" + fi + else + dnl Handle case where there is a native iconv but iconv.h is from libiconv + AC_CHECK_DECL(_libiconv_version, + [ AC_CHECK_LIB(iconv, libiconv, LIBS="-liconv $LIBS") ],, + [ #include ]) + fi +fi +AH_TEMPLATE([ICONV_FROM_LIBICONV], +[Define to 1 if iconv() is linked from libiconv]) +if test "x$ac_found_iconv" = xyes; then + AC_DEFINE(HAVE_ICONV, 1, [Define if you have the iconv() function.]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int myversion = _libiconv_version]])],[AC_DEFINE(ICONV_FROM_LIBICONV)],[]) +fi + +dnl Check if iconv uses const in prototype declaration +if test "x$ac_found_iconv" = "xyes"; then + AC_CACHE_CHECK(for iconv declaration, ac_cv_iconv_const, + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + #include ]], + [[#ifdef __cplusplus + "C" + #endif + #if defined(__STDC__) || defined(__cplusplus) + size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); + #else + size_t iconv(); + #endif]])], + [ac_cv_iconv_const=], + [ac_cv_iconv_const=const])]) + AC_DEFINE_UNQUOTED([ICONV_CONST], $ac_cv_iconv_const, + [Define as const if the declaration of iconv() needs const.]) +fi + +dnl --------------------- +dnl CHECK TERMCAP LIBRARY +dnl --------------------- +dnl Checks for external variable ospeed in the termcap library. +AC_CACHE_CHECK(if an include file defines ospeed, +zsh_cv_decl_ospeed_include_defines, +[AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include +#if HAVE_TERMIOS_H +#include +#endif +#if HAVE_TERMCAP_H +#include +#endif]], [[ospeed = 0;]])],[zsh_cv_decl_ospeed_include_defines=yes],[zsh_cv_decl_ospeed_include_defines=no])]) + +if test x$zsh_cv_decl_ospeed_include_defines = xno; then + AC_CACHE_CHECK(if you must define ospeed, + zsh_cv_decl_ospeed_must_define, + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[extern short ospeed; ospeed = 0;]])],[zsh_cv_decl_ospeed_must_define=yes],[zsh_cv_decl_ospeed_must_define=no])]) +fi + +AH_TEMPLATE([HAVE_OSPEED], +[Define to 1 if your termcap library has the ospeed variable]) +AH_TEMPLATE([MUST_DEFINE_OSPEED], +[Define to 1 if you have ospeed, but it is not defined in termcap.h]) +if test x$zsh_cv_decl_ospeed_include_defines = xyes; then + AC_DEFINE(HAVE_OSPEED) +elif test x$zsh_cv_decl_ospeed_must_define = xyes; then + AC_DEFINE(HAVE_OSPEED) + AC_DEFINE(MUST_DEFINE_OSPEED) +fi + +if test x$gdbm != xno; then + AC_CHECK_HEADERS(gdbm.h) + AC_CHECK_LIB(gdbm, gdbm_open) +fi + +AC_CHECK_HEADERS(sys/xattr.h) + +dnl -------------- +dnl CHECK TYPEDEFS +dnl -------------- + +AC_TYPE_PID_T +AC_TYPE_OFF_T +AC_CHECK_TYPE(ino_t, unsigned long) +AC_TYPE_MODE_T +AC_TYPE_UID_T +AC_TYPE_SIZE_T + +dnl ------------------------------------------------ +dnl Check size of long and try to find a 64-bit type +dnl ------------------------------------------------ +dnl AC_CHECK_SIZEOF is no good, because we need the result here, +dnl and that doesn't seem to define a shell parameter. +AC_CACHE_CHECK(if long is 64 bits, zsh_cv_long_is_64_bit, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[int main() { return sizeof(long) < 8; }]])],[zsh_cv_long_is_64_bit=yes],[zsh_cv_long_is_64_bit=no],[zsh_cv_long_is_64_bit=no])]) + +AH_TEMPLATE([ino_t], +[Define to `unsigned long' if doesn't define.]) +AH_TEMPLATE([LONG_IS_64_BIT], +[Definitions used when a long is less than eight byte, to try to + provide some support for eight byte operations. + + Note that ZSH_64_BIT_TYPE, OFF_T_IS_64_BIT, INO_T_IS_64_BIT do *not* get + defined if long is already 64 bits, since in that case no special handling + is required. + + Define to 1 if long is 64 bits]) +AH_TEMPLATE([ZSH_64_BIT_TYPE], +[Define to a 64 bit integer type if there is one, but long is shorter.]) +AH_TEMPLATE([ZSH_64_BIT_UTYPE], +[Define to an unsigned variant of ZSH_64_BIT_TYPE if that is defined.]) +AH_TEMPLATE([OFF_T_IS_64_BIT], +[Define to 1 if off_t is 64 bit (for large file support)]) +AH_TEMPLATE([INO_T_IS_64_BIT], +[Define to 1 if ino_t is 64 bit (for large file support).]) +if test x$zsh_cv_long_is_64_bit = xyes; then + AC_DEFINE(LONG_IS_64_BIT) +else + AC_CACHE_CHECK(if off_t is 64 bit, zsh_cv_off_t_is_64_bit, + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include + +main() { return sizeof(off_t) < 8; } +]])],[zsh_cv_off_t_is_64_bit=yes],[zsh_cv_off_t_is_64_bit=no],[zsh_cv_off_t_is_64_bit=no])]) + if test x$zsh_cv_off_t_is_64_bit = xyes; then + AC_DEFINE(OFF_T_IS_64_BIT) + fi + + AC_CACHE_CHECK(if ino_t is 64 bit, zsh_cv_ino_t_is_64_bit, + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include + +main() { return sizeof(ino_t) < 8; } +]])],[zsh_cv_ino_t_is_64_bit=yes],[zsh_cv_ino_t_is_64_bit=no],[zsh_cv_ino_t_is_64_bit=no])]) + if test x$zsh_cv_ino_t_is_64_bit = xyes; then + AC_DEFINE(INO_T_IS_64_BIT) + fi + + if test x$enable_largefile != xno -o x$zsh_cv_off_t_is_64_bit = xyes \ + -o $zsh_cv_ino_t_is_64_bit = yes; then + AC_CACHE_CHECK(if compiler has a 64 bit type, zsh_cv_64_bit_type, + [zsh_64_BIT_TYPE(long long, zsh_cv_64_bit_type) + if test "$zsh_cv_64_bit_type" = no; then + zsh_64_BIT_TYPE(quad_t, zsh_cv_64_bit_type) + fi + if test "$zsh_cv_64_bit_type" = no; then + zsh_64_BIT_TYPE(__int64_t, zsh_cv_64_bit_type) + fi + dnl As a last resort, if we know off_t has 64 bits, use that as + dnl the 64-bit integer type. I don't dare try ino_t since there's + dnl probably nothing to stop that being unsigned. + if test "$zsh_cv_64_bit_type" = no && + test "$zsh_cv_off_t_is_64_bit" = yes; then + zsh_64_BIT_TYPE(off_t, zsh_cv_64_bit_type) + fi]) + if test "$zsh_cv_64_bit_type" != no; then + AC_DEFINE_UNQUOTED(ZSH_64_BIT_TYPE, $zsh_cv_64_bit_type) + + dnl Handle cases where unsigned type cannot be simply + dnl `unsigned ZSH_64_BIT_TYPE'. More tests may be required. + AC_CACHE_CHECK(for a corresponding unsigned 64 bit type, + zsh_cv_64_bit_utype, + [zsh_64_BIT_TYPE(unsigned $zsh_cv_64_bit_type, zsh_cv_64_bit_utype, + force) + if test "$zsh_cv_64_bit_utype" = no; then + zsh_64_BIT_TYPE(__uint64_t, zsh_cv_64_bit_utype) + fi]) + if test "$zsh_cv_64_bit_utype" != no; then + AC_DEFINE_UNQUOTED(ZSH_64_BIT_UTYPE, $zsh_cv_64_bit_utype) + fi + fi + fi +fi +AH_TEMPLATE([ZLONG_IS_LONG_LONG], +[Define to 1 if the zlong type uses long long int.]) +AH_TEMPLATE([ZLONG_IS_LONG_64], +[Define to 1 if the zlong type uses 64-bit long int.]) +if test "$zsh_cv_64_bit_type" = "long long"; then + dnl Remember this so we can get (s)printf output right. + AC_DEFINE(ZLONG_IS_LONG_LONG) +else + if test "$zsh_cv_64_bit_type" = "long"; then + AC_DEFINE(ZLONG_IS_LONG_64) + fi +fi + +dnl We'll blithely assume (f)printf supports the same types as sprintf. +AC_CACHE_CHECK(for %lld printf support, zsh_cv_printf_has_lld, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[#include +#include +int main(int argc, char **argv) +{ + long long foo = ((long long)0xdead << 40) | 0xf00d; + char buf[80]; + sprintf(buf, "before%lldafter", foo); + if (!strcmp(buf, "before62677660341432333after")) { + return 0; + } + return 1; +} +]])],[zsh_cv_printf_has_lld=yes],[zsh_cv_printf_has_lld=no],[zsh_cv_printf_has_lld=no])]) +AH_TEMPLATE(PRINTF_HAS_LLD, +[Define to 1 if printf and sprintf support %lld for long long.]) +if test x$zsh_cv_printf_has_lld = xyes; then + AC_DEFINE(PRINTF_HAS_LLD) +fi + +dnl Check for sigset_t. Currently I'm looking in +dnl and . Others might need +dnl to be added. +AC_CACHE_CHECK(for sigset_t, zsh_cv_type_sigset_t, +[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#define _POSIX_C_SOURCE 200809L +#include +#include ]], [[sigset_t tempsigset;]])],[zsh_cv_type_sigset_t=yes],[zsh_cv_type_sigset_t=no])]) +AH_TEMPLATE([sigset_t], +[Define to `unsigned int' if or doesn't define]) +if test x$zsh_cv_type_sigset_t = xno; then + AC_DEFINE(sigset_t, unsigned int) +fi + +dnl check structures for high resolution timestamps +AC_CHECK_MEMBERS([struct stat.st_atim.tv_nsec, + struct stat.st_atimespec.tv_nsec, + struct stat.st_atimensec, + struct stat.st_mtim.tv_nsec, + struct stat.st_mtimespec.tv_nsec, + struct stat.st_mtimensec, + struct stat.st_ctim.tv_nsec, + struct stat.st_ctimespec.tv_nsec, + struct stat.st_ctimensec]) + +dnl Check for struct timezone since some old SCO versions do not define it +zsh_TYPE_EXISTS([ +#define _GNU_SOURCE 1 +#ifdef HAVE_SYS_TIME_H +# include +#endif +], struct timezone) + +dnl Check for struct timespec since POSIX only gained it in 2008 +zsh_TYPE_EXISTS([ +#define _GNU_SOURCE 1 +#ifdef HAVE_SYS_TIME_H +# include +#endif +], struct timespec) + +dnl Check for utmp structures, for watch +zsh_TYPE_EXISTS([ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UTMP_H +# include +#endif +], struct utmp) +zsh_TYPE_EXISTS([ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UTMPX_H +# include +#endif +], struct utmpx) + +dnl Check contents of utmp structures +zsh_STRUCT_MEMBER([ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UTMP_H +# include +#endif +], struct utmp, ut_host) +zsh_STRUCT_MEMBER([ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UTMPX_H +# include +#endif +], struct utmpx, ut_host) +zsh_STRUCT_MEMBER([ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UTMPX_H +# include +#endif +], struct utmpx, ut_xtime) +zsh_STRUCT_MEMBER([ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UTMPX_H +# include +#endif +], struct utmpx, ut_tv) + +dnl Check for inode numbers in directory entry structures +zsh_STRUCT_MEMBER([ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_DIRENT_H +# include +#endif +], struct dirent, d_ino) +zsh_STRUCT_MEMBER([ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_DIRENT_H +# include +#endif +], struct dirent, d_stat) +zsh_STRUCT_MEMBER([ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_NDIR_H +# include +#endif +#ifdef HAVE_SYS_DIR_H +# include +#endif +#ifdef HAVE_NDIR_H +# include +#endif +], struct direct, d_ino) +zsh_STRUCT_MEMBER([ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_NDIR_H +# include +#endif +#ifdef HAVE_SYS_DIR_H +# include +#endif +#ifdef HAVE_NDIR_H +# include +#endif +], struct direct, d_stat) + +dnl Check IPv6 socket address structure type +zsh_STRUCT_MEMBER([ +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#include +], struct sockaddr_in6, sin6_scope_id) + +dnl Check for h_errno external variable +AH_TEMPLATE([USE_LOCAL_H_ERRNO], +[Define to 1 if h_errno is not defined by the system.]) +AC_CACHE_CHECK(if we need our own h_errno, + zsh_cv_decl_h_errno_use_local, + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[extern int h_errno; h_errno = 0;]])],[zsh_cv_decl_h_errno_use_local=no],[zsh_cv_decl_h_errno_use_local=yes])]) + +if test x$zsh_cv_decl_h_errno_use_local = xyes; then + AC_DEFINE(USE_LOCAL_H_ERRNO) +fi + +dnl --------------- +dnl CHECK FUNCTIONS +dnl --------------- + +dnl need to integrate this function +dnl AC_FUNC_STRFTIME + +AC_CHECK_FUNCS(strftime strptime mktime timelocal \ + difftime gettimeofday clock_gettime \ + select poll \ + readlink faccessx fchdir ftruncate \ + fstat lstat lchown fchown fchmod \ + fseeko ftello \ + mkfifo _mktemp mkstemp \ + waitpid wait3 \ + sigaction sigblock sighold sigrelse sigsetmask sigprocmask \ + killpg setpgid setpgrp tcsetpgrp tcgetattr nice \ + gethostname gethostbyname2 getipnodebyname \ + inet_aton inet_pton inet_ntop \ + getlogin getpwent getpwnam getpwuid getgrgid getgrnam \ + initgroups nis_list \ + setuid seteuid setreuid setresuid setsid \ + memcpy memmove strstr strerror strtoul \ + getrlimit getrusage \ + setlocale \ + isblank iswblank \ + uname \ + signgam tgamma \ + log2 \ + scalbn \ + putenv getenv setenv unsetenv xw\ + brk sbrk \ + pathconf sysconf \ + tgetent tigetflag tigetnum tigetstr setupterm initscr resize_term \ + getcchar setcchar waddwstr wget_wch win_wch use_default_colors \ + nl_langinfo \ + erand48 open_memstream \ + posix_openpt \ + wctomb iconv \ + isinf isnan \ + grantpt unlockpt ptsname \ + htons ntohs \ + regcomp regexec regerror regfree \ + gdbm_open getxattr \ + realpath canonicalize_file_name \ + symlink getcwd \ + cygwin_conv_path \ + nanosleep \ + srand_deterministic \ + setutxent getutxent endutxent getutent \ + getline) +AC_FUNC_STRCOLL + +AH_TEMPLATE([REALPATH_ACCEPTS_NULL], +[Define if realpath() accepts NULL as its second argument.]) +AC_CACHE_CHECK([if realpath accepts NULL], +zsh_cv_func_realpath_accepts_null, +[AC_RUN_IFELSE([AC_LANG_PROGRAM([ +#include +#include +],[ +exit(!realpath("/", (char*)0)); +])], +[zsh_cv_func_realpath_accepts_null=yes], +[zsh_cv_func_realpath_accepts_null=no], +[zsh_cv_func_realpath_accepts_null=$ac_cv_func_canonicalize_file_name])]) +if test x$zsh_cv_func_realpath_accepts_null = xyes; then + AC_DEFINE(REALPATH_ACCEPTS_NULL) +fi + +if test x$enable_cap = xyes; then + AC_CHECK_FUNCS(cap_get_proc) +fi + +dnl Check if tgetent accepts NULL (and will allocate its own termcap buffer) +dnl Some termcaps reportedly accept a zero buffer, but then dump core +dnl in tgetstr(). +dnl Under Cygwin test program crashes but exit code is still 0. So, +dnl we test for a file that porgram should create +AH_TEMPLATE([TGETENT_ACCEPTS_NULL], +[Define to 1 if tgetent() accepts NULL as a buffer.]) +AC_CACHE_CHECK(if tgetent accepts NULL, +zsh_cv_func_tgetent_accepts_null, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[ +main() +{ + char buf[4096]; + int r1 = tgetent(buf, "vt100"); + int r2 = tgetent((char*)0,"vt100"); + if (r1 >= 0 && r1 == r2) { + char tbuf[1024], *u; + u = tbuf; + tgetstr("cl", &u); + creat("conftest.tgetent", 0640); + } + exit((r1 != r2) || r2 == -1); +} +]])],[if test -f conftest.tgetent; then + zsh_cv_func_tgetent_accepts_null=yes + else + zsh_cv_func_tgetent_accepts_null=no + fi],[zsh_cv_func_tgetent_accepts_null=no],[zsh_cv_func_tgetent_accepts_null=no])]) +if test x$zsh_cv_func_tgetent_accepts_null = xyes; then + AC_DEFINE(TGETENT_ACCEPTS_NULL) +fi +AC_CACHE_CHECK(if tgetent returns 0 on success, +zsh_cv_func_tgetent_zero_success, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[ +main() +{ + char buf[4096]; + int r1 = tgetent(buf, "!@#$%^&*"); + int r2 = tgetent(buf, "vt100"); + if (r1 < 0 && r2 == 0) { + char tbuf[1024], *u; + u = tbuf; + tgetstr("cl", &u); + creat("conftest.tgetent0", 0640); + } + exit(r1 == r2); +} +]])],[if test -f conftest.tgetent0; then + zsh_cv_func_tgetent_zero_success=yes + else + zsh_cv_func_tgetent_zero_success=no + fi],[zsh_cv_func_tgetent_zero_success=no],[zsh_cv_func_tgetent_zero_success=no])]) +AH_TEMPLATE([TGETENT_SUCCESS], +[Define to what tgetent() returns on success (0 on HP-UX X/Open curses).]) +if test x$zsh_cv_func_tgetent_zero_success = xyes; then + AC_DEFINE(TGETENT_SUCCESS, 0) +else + AC_DEFINE(TGETENT_SUCCESS, 1) +fi + +AC_FUNC_MMAP +if test x$ac_cv_func_mmap_fixed_mapped = xyes; then + AC_CHECK_FUNCS(munmap msync) +fi + +if test x$ac_cv_func_setpgrp = xyes; then + AC_FUNC_GETPGRP +else + dnl If there is no setpgrp, the test for getpgrp(void) will fail + dnl because the program will not compile. However, in that case + dnl we can be reasonably confident we are not dealing with a + dnl Berkeleyesque system, so assume getpgrp does take void. + ac_cv_func_getpgrp_void=yes + AC_DEFINE(GETPGRP_VOID) +fi + +if test x$dynamic = xyes; then + AC_CHECK_FUNCS(dlopen dlerror dlsym dlclose load loadquery loadbind unload \ + shl_load shl_unload shl_findsym) +fi + +AH_TEMPLATE([XATTR_EXTRA_ARGS], +Define if getxattr() etc. require additional MacOS-style arguments) +if test x$ac_cv_func_getxattr = xyes && test x$ac_cv_header_sys_xattr_h = xyes +then + AC_CACHE_CHECK(if getxattr etc. are Linux-like, + zsh_cv_getxattr_linux, + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +#include ]], + [[ + (void)listxattr("", 0, 0); + (void)getxattr("", "", 0, 0); + (void)setxattr("", "", "", 0, 0); + (void)removexattr("", ""); + ]])], + [zsh_cv_getxattr_linux=yes], + [zsh_cv_getxattr_linux=no])]) + + if test x$zsh_cv_getxattr_linux != xyes; then + AC_CACHE_CHECK(if getxattr etc. are MAC-like, + zsh_cv_getxattr_mac, + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +#include ]], + [[(void)listxattr("", 0, 0, 0); + (void)getxattr("", "", 0, 0, 0, 0); + (void)setxattr("", "", "", 0, 0, 0); + (void)removexattr("", "", 0);]])], + [zsh_cv_getxattr_mac=yes], + [zsh_cv_getxattr_mac=no])]) + + if test x$zsh_cv_getxattr_mac = xyes; then + AC_DEFINE(XATTR_EXTRA_ARGS) + fi + fi +fi + +AC_CACHE_CHECK(if getxattr etc. are usable, +zsh_cv_use_xattr, +[if test x$zsh_cv_getxattr_linux = xyes || test x$zsh_cv_getxattr_mac = xyes +then +zsh_cv_use_xattr=yes +else +zsh_cv_use_xattr=no +fi]) + +dnl ------------- +dnl CHECK SIGNALS +dnl ------------- +dnl What style of signal do you have (POSIX, BSD, or SYSV)? +AH_TEMPLATE([POSIX_SIGNALS], +[Define to 1 if you use POSIX style signal handling.]) +AH_TEMPLATE([BSD_SIGNALS], +[Define to 1 if you use BSD style signal handling (and can block signals).]) +AH_TEMPLATE([SYSV_SIGNALS], +[Define to 1 if you use SYS style signal handling (and can block signals).]) +AH_TEMPLATE([NO_SIGNAL_BLOCKING], +[Define to 1 if you have no signal blocking at all (bummer).]) +AC_MSG_CHECKING(what style of signals to use) +if test x$ac_cv_func_sigaction = xyes && test x$ac_cv_func_sigprocmask = xyes; then + signals_style=POSIX_SIGNALS + AC_DEFINE(POSIX_SIGNALS) +elif test x$ac_cv_func_sigblock = xyes && test x$ac_cv_func_sigsetmask = xyes; then + signals_style=BSD_SIGNALS + AC_DEFINE(BSD_SIGNALS) +elif test x$ac_cv_func_sighold = xyes && test x$ac_cv_func_sigrelse = xyes; then + signals_style=SYSV_SIGNALS + AC_DEFINE(SYSV_SIGNALS) +else + signals_style=NO_SIGNAL_BLOCKING + AC_DEFINE(NO_SIGNAL_BLOCKING) +fi +AC_DEFINE_UNQUOTED($signals_style) +AC_MSG_RESULT($signals_style) + +dnl Where is located? Needed as input for signals.awk +AC_CACHE_CHECK(where signal.h is located, zsh_cv_path_signal_h, +[dnl Look at the output from the preprocessor. +dnl We should get lines of the form `# 1 "/usr/include/signal.h"' +dnl The following assumes the real definitions are in a file which +dnl contains the name `sig'; we could relax this if necessary, +dnl but then you can get a rather long list of files to test. +dnl The backslash substitution is to persuade cygwin to cough up +dnl slashes rather than doubled backslashes in the path. +echo "#include " > nametmp.c +sigfile_list="`$CPP $CPPFLAGS nametmp.c | +sed -n -e 's/^#line[ ].*\"\(.*\)\"/\1/p' \ + -e 's/^#[ ].*\"\(.*\)\"/\1/p' | +sed 's/\\\\\\\\/\//g' | +$AWK '{ if ($1 ~ /sig/) files[[$1]] = $1 } + END { for (var in files) print var }'`" +rm -f nametmp.c +if test -z "$sigfile_list"; then + dnl In case we don't get the stuff from the preprocesor, use the old + dnl list of standard places. + sigfile_list="/usr/include/sys/iso/signal_iso.h +/usr/include/bsd/sys/signal.h +/usr/include/signum.h +/usr/include/asm/signum.h +/usr/include/asm/signal.h +/usr/include/linux/signal.h +/usr/include/sys/signal.h +/usr/include/bits/signum.h +/dev/null" +fi +for SIGNAL_TRY_H in $sigfile_list +do + dnl Try to make sure it doesn't get confused by files that don't + dnl have real signal definitions in, but do #define SIG* by counting + dnl the number of signals. Maybe we could even check for e.g. SIGHUP? + nsigs=`test -f $SIGNAL_TRY_H && \ + grep '#[ ]*define[ ][ ]*SIG[0-9A-Z]*[ ]*[0-9][0-9]*' $SIGNAL_TRY_H | \ + wc -l | sed 's/[ ]//g'` + if test "x$nsigs" != x && test "$nsigs" -ge 7 + then + SIGNAL_H="$SIGNAL_H $SIGNAL_TRY_H" + fi +done +if test "x$SIGNAL_H" = x; then + AC_MSG_ERROR(SIGNAL MACROS NOT FOUND: please report to developers) +fi +zsh_cv_path_signal_h="$SIGNAL_H" +]) +SIGNAL_H="$zsh_cv_path_signal_h" +AC_SUBST(SIGNAL_H)dnl + +dnl Where are error names located? Needed as input for errnames1.awk +AC_CACHE_CHECK(where error names are located, zsh_cv_path_errno_h, +[dnl Look at the output from the preprocessor. +dnl We should get lines of the form `# 1 "/usr/include/errno.h"' +dnl The following assumes the real definitions are in a file which +dnl contains the name `err'; we could relax this if necessary, +dnl but then you can get a rather long list of files to test. +dnl The backslash substitution is to persuade cygwin to cough up +dnl slashes rather than doubled backslashes in the path. +echo "#include " > nametmp.c +errfile_list="`$CPP $CPPFLAGS nametmp.c | +sed -n -e 's/^#line[ ].*\"\(.*\)\"/\1/p' \ + -e 's/^#[ 0-9].*\"\(.*\)\"/\1/p' | +sed 's/\\\\\\\\/\//g' | +$AWK '{ if ($1 ~ /err/) files[[$1]] = $1 } + END { for (var in files) print var }'`" +rm -f nametmp.c +for ERRNO_TRY_H in $errfile_list /dev/null +do + dnl Try to make sure it doesn't get confused by files that don't + dnl have real error definitions in. Count definitions to make sure. + dnl Definitions of error numbers have become more and more general, so + dnl make a list of files containing any definitions in and keep them all. + dnl Careful with cut and paste in the pattern: the square brackets + dnl must contain a space and a tab. + nerrs=`test -f $ERRNO_TRY_H && \ + $EGREP '#[ ]*define[ ][ ]*E[0-9A-Z]*[ ]*(_HURD_ERRNO )?\(?[_A-Z0-9]' $ERRNO_TRY_H | \ + wc -l | sed 's/[ ]//g'` + if test "x$nerrs" != x && test "$nerrs" -ge 1 + then + ERRNO_H="$ERRNO_H $ERRNO_TRY_H" + fi +done +if test x"$ERRNO_H" = x; then + AC_MSG_ERROR(ERROR MACROS NOT FOUND: please report to developers) +fi +zsh_cv_path_errno_h="$ERRNO_H" +]) +ERRNO_H="$zsh_cv_path_errno_h" +AC_SUBST(ERRNO_H)dnl + +AC_CACHE_CHECK(location of curses header, zsh_cv_path_curses_header, +[if test x$zsh_cv_ignore_ncurses = xyes; then + if test x$ac_cv_header_curses_h = xyes; then + zsh_cv_path_curses_header=curses.h + else + zsh_cv_path_curses_header=none + fi +elif test x$ac_cv_header_ncursesw_ncurses_h = xyes; then + zsh_cv_path_curses_header=ncursesw/ncurses.h +elif test x$ac_cv_header_ncurses_ncurses_h = xyes; then + zsh_cv_path_curses_header=ncurses/ncurses.h +elif test x$ac_cv_header_ncurses_h = xyes; then + zsh_cv_path_curses_header=ncurses.h +elif test x$ac_cv_header_curses_h = xyes; then + zsh_cv_path_curses_header=curses.h +else + zsh_cv_path_curses_header=none +fi]) +AH_TEMPLATE([ZSH_HAVE_CURSES_H], +[Define to 1 if some variant of a curses header can be included]) +if test x$zsh_cv_path_curses_header != xnone; then + AC_DEFINE(ZSH_HAVE_CURSES_H) + ZSH_CURSES_H=$zsh_cv_path_curses_header +else + ZSH_CURSES_H= +fi +AC_SUBST(ZSH_CURSES_H) + +dnl Where are curses key definitions located? Need for keypad() mode. +AC_CACHE_CHECK(where curses key definitions are located, zsh_cv_path_curses_keys_h, +[dnl This is an identical trick to errno.h, except we use ncurses.h +dnl if we can. +if test x$zsh_cv_path_curses_header = xnone; then + echo >nametmp.c +else + echo "#include <$zsh_cv_path_curses_header>" >nametmp.c +fi + +curses_list="`$CPP $CPPFLAGS nametmp.c | +sed -n -e 's/^#line[ ].*\"\(.*\)\"/\1/p' \ + -e 's/^#[ 0-9].*\"\(.*\)\"/\1/p' | +sed 's/\\\\\\\\/\//g' | +$AWK '{ if ($1 ~ /\.h/) files[[$1]] = $1 } + END { for (var in files) print var }'`" +rm -f nametmp.c +for CURSES_TRY_H in $curses_list /dev/null +do + nkeys=`test -f $CURSES_TRY_H && \ + $EGREP '#[ ]*define[ ][ ]*KEY_' $CURSES_TRY_H | \ + wc -l | sed 's/[ ]//g'` + if test "x$nkeys" != x && test "$nkeys" -ge 10 + then + CURSES_KEYS_H=$CURSES_TRY_H + break + fi +done +zsh_cv_path_curses_keys_h="$CURSES_KEYS_H" +]) +CURSES_KEYS_H="$zsh_cv_path_curses_keys_h" +AC_SUBST(CURSES_KEYS_H)dnl + +dnl See if there are variants of term.h. For testing each one +dnl we include the most likely variant of the curses header. +AC_CHECK_HEADERS(ncursesw/term.h, +true, true, +[#include ]) +AC_CHECK_HEADERS(ncurses/term.h, +true, true, +[#include ]) +AC_CHECK_HEADERS(term.h, +true, true, +[#include ]) + +dnl See if term.h is bundled along with the curses library we +dnl are using. If this isn't the default system curses, compilation +dnl could barf unless we include from the right subdirectory. +AC_CACHE_CHECK(where term.h is located, zsh_cv_path_term_header, +[case x$zsh_cv_path_curses_header in + xncursesw/*) + if test x$ac_cv_header_ncursesw_term_h = xyes; then + zsh_cv_path_term_header=ncursesw/term.h + fi + ;; + xncurses/*) + if test x$ac_cv_header_ncurses_term_h = xyes; then + zsh_cv_path_term_header=ncurses/term.h + fi + ;; +esac +if test x$zsh_cv_path_term_header = x; then + if test x$ac_cv_header_term_h = xyes; then + zsh_cv_path_term_header=term.h + else + zsh_cv_path_term_header=none + fi +fi]) + +AH_TEMPLATE([ZSH_HAVE_TERM_H], +[Define to 1 if some variant of term.h can be included]) +AH_TEMPLATE([HAVE_BOOLCODES], +[Define if you have the termcap boolcodes symbol.]) +AH_TEMPLATE([HAVE_NUMCODES], +[Define if you have the termcap numcodes symbol.]) +AH_TEMPLATE([HAVE_STRCODES], +[Define if you have the termcap strcodes symbol.]) +AH_TEMPLATE([HAVE_BOOLNAMES], +[Define if you have the terminfo boolnames symbol.]) +AH_TEMPLATE([HAVE_NUMNAMES], +[Define if you have the terminfo numnames symbol.]) +AH_TEMPLATE([HAVE_STRNAMES], +[Define if you have the terminfo strnames symbol.]) +AH_TEMPLATE([TGOTO_PROTO_MISSING], +[Define if there is no prototype for the tgoto() terminal function.]) + +if test x$zsh_cv_path_term_header != xnone; then + AC_DEFINE(ZSH_HAVE_TERM_H) + ZSH_TERM_H=$zsh_cv_path_term_header + if test x$zsh_cv_path_curses_header != xnone; then + term_includes="#include <$zsh_cv_path_curses_header> +#include <$zsh_cv_path_term_header>" + else + term_includes="#include <$zsh_cv_path_term_header>" + fi + + AC_MSG_CHECKING(if boolcodes is available) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[$term_includes]], [[char **test = boolcodes; puts(*test);]])],[AC_DEFINE(HAVE_BOOLCODES) boolcodes=yes],[boolcodes=no]) + AC_MSG_RESULT($boolcodes) + + AC_MSG_CHECKING(if numcodes is available) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[$term_includes]], [[char **test = numcodes; puts(*test);]])],[AC_DEFINE(HAVE_NUMCODES) numcodes=yes],[numcodes=no]) + AC_MSG_RESULT($numcodes) + + AC_MSG_CHECKING(if strcodes is available) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[$term_includes]], [[char **test = strcodes; puts(*test);]])],[AC_DEFINE(HAVE_STRCODES) strcodes=yes],[strcodes=no]) + AC_MSG_RESULT($strcodes) + + AC_MSG_CHECKING(if boolnames is available) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[$term_includes]], [[char **test = boolnames; puts(*test);]])],[AC_DEFINE(HAVE_BOOLNAMES) boolnames=yes],[boolnames=no]) + AC_MSG_RESULT($boolnames) + + AC_MSG_CHECKING(if numnames is available) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[$term_includes]], [[char **test = numnames; puts(*test);]])],[AC_DEFINE(HAVE_NUMNAMES) numnames=yes],[numnames=no]) + AC_MSG_RESULT($numnames) + + AC_MSG_CHECKING(if strnames is available) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[$term_includes]], [[char **test = strnames; puts(*test);]])],[AC_DEFINE(HAVE_STRNAMES) strnames=yes],[strnames=no]) + AC_MSG_RESULT($strnames) + + dnl There are apparently defective terminal library headers on some + dnl versions of Solaris before 11. + AC_MSG_CHECKING(if tgoto prototype is missing) + tgoto_includes="$term_includes +/* guaranteed to clash with any valid tgoto prototype */ +extern void tgoto(int **stuff, float **more_stuff);" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[$tgoto_includes]], [[int *stuff; float *more_stuff; tgoto(&stuff, &more_stuff);]])],[AC_DEFINE(TGOTO_PROTO_MISSING) tgotoprotomissing=yes],[tgotoprotomissing=no]) + AC_MSG_RESULT($tgotoprotomissing) +else + ZSH_TERM_H= +fi +AC_SUBST(ZSH_TERM_H) + + +dnl ----------------------------------------------------- +dnl Look for the file containing the RLIMIT_* definitions +dnl ----------------------------------------------------- +dnl CALL FOR MORE (FEWER?) LOCATIONS: I've just copied the signal checking. +AC_CACHE_CHECK(where the RLIMIT macros are located,zsh_cv_path_rlimit_h, +[dnl Look at the output from the preprocessor. +dnl Copied from the search for the signal names above. +echo "#include " >restmp.c +resourcefile_list="`$CPP $CPPFLAGS restmp.c | +sed -n -e 's/^#line[ ].*\"\(.*\)\"/\1/p' \ + -e 's/^#[ ].*\"\(.*\)\"/\1/p' | +sed 's/\\\\\\\\/\//g' | +$AWK '{ if ($1 ~ /resource/) files[[$1]] = $1 } + END { for (var in files) print var }'`" +rm -f restmp.c +if test -z "$resourcefile_list"; then + dnl No list: look at standard places. + resourcefile_list="/usr/include/bsd/sys/resource.h +/usr/include/asm/resource.h +/usr/include/linux/resource.h +/usr/include/sys/resource.h +/usr/include/bits/resource.h +/usr/include/resourcebits.h" +fi +for RESOURCE_H in $resourcefile_list /dev/null; +do + test -f $RESOURCE_H && \ + grep '#[ ]*define[ ][ ]*RLIMIT_[A-Z]*[ ]*[0-9A-Z][0-9]*' $RESOURCE_H > /dev/null && \ + break +done +zsh_cv_path_rlimit_h=$RESOURCE_H +if test x$RESOURCE_H = x"/dev/null" && test x$ac_cv_func_getrlimit = xyes; then + AC_MSG_WARN(RLIMIT MACROS NOT FOUND: please report to developers) +fi]) +RLIMITS_INC_H=$zsh_cv_path_rlimit_h +if test "$RLIMITS_INC_H" = "/dev/null"; then + RLIMITS_INC_H='' +fi +dnl rlimits.h only appears in dependencies if we are actually using it. +dnl We are using it any time we have getrlimit, though if the macros were +dnl not found we simply awk through /dev/null and fail to find them. +dnl Thus, limit won't work, but at least the shell will compile. +AC_SUBST(RLIMITS_INC_H)dnl + +dnl ------------------ +dnl rlimit type checks +dnl ------------------ +AH_TEMPLATE([RLIM_T_IS_QUAD_T], +[Define to 1 if struct rlimit uses quad_t.]) +AH_TEMPLATE([RLIM_T_IS_LONG_LONG], +[Define to 1 if struct rlimit uses long long]) +AH_TEMPLATE([RLIM_T_IS_UNSIGNED], +[Define to 1 if struct rlimit uses unsigned.]) +AH_TEMPLATE([rlim_t], +[Define to the type used in struct rlimit.]) +DEFAULT_RLIM_T=long +AC_CACHE_CHECK(if rlim_t is longer than a long, +zsh_cv_rlim_t_is_longer, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +main(){struct rlimit r;exit(sizeof(r.rlim_cur) <= sizeof(long));}]])],[zsh_cv_rlim_t_is_longer=yes],[zsh_cv_rlim_t_is_longer=no],[zsh_cv_rlim_t_is_longer=yes])]) +if test x$zsh_cv_rlim_t_is_longer = xyes; then + AC_CACHE_CHECK(if rlim_t is a quad, + zsh_cv_rlim_t_is_quad_t, + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#include +main() { + struct rlimit r; + char buf[20]; + r.rlim_cur = 0; + sprintf(buf, "%qd", r.rlim_cur); + exit(strcmp(buf, "0")); +}]])],[zsh_cv_rlim_t_is_quad_t=yes],[zsh_cv_rlim_t_is_quad_t=no],[zsh_cv_rlim_t_is_quad_t=no])]) + if test x$zsh_cv_rlim_t_is_quad_t = xyes; then + AC_DEFINE(RLIM_T_IS_QUAD_T) + DEFAULT_RLIM_T=quad_t + else + AC_DEFINE(RLIM_T_IS_LONG_LONG) + DEFAULT_RLIM_T='long long' + fi +else + AC_CACHE_CHECK(if the rlim_t is unsigned, + zsh_cv_type_rlim_t_is_unsigned, + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include + main(){struct rlimit r;r.rlim_cur=-1;exit(r.rlim_cur<0);}]])],[zsh_cv_type_rlim_t_is_unsigned=yes],[zsh_cv_type_rlim_t_is_unsigned=no],[zsh_cv_type_rlim_t_is_unsigned=no])]) + if test x$zsh_cv_type_rlim_t_is_unsigned = xyes; then + AC_DEFINE(RLIM_T_IS_UNSIGNED) + DEFAULT_RLIM_T="unsigned $DEFAULT_RLIM_T" + fi +fi + +AC_CACHE_CHECK(for rlim_t, zsh_cv_type_rlim_t, +[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include ]], [[rlim_t l;]])],[zsh_cv_type_rlim_t=yes],[zsh_cv_type_rlim_t=no])]) +if test x$zsh_cv_type_rlim_t = xno; then + AC_DEFINE_UNQUOTED(rlim_t, $DEFAULT_RLIM_T) +fi + + +dnl On some systems the RLIMIT_* don't evaluate to integers at compile time +dnl (they may be enums). In this case we are not able to do preprocessor +dnl comparisons and need our tests to determine what values exist and +dnl if there are clashing definitions. + +zsh_LIMIT_PRESENT(RLIMIT_AIO_MEM) +zsh_LIMIT_PRESENT(RLIMIT_AIO_OPS) +zsh_LIMIT_PRESENT(RLIMIT_AS) +zsh_LIMIT_PRESENT(RLIMIT_LOCKS) +zsh_LIMIT_PRESENT(RLIMIT_MEMLOCK) +zsh_LIMIT_PRESENT(RLIMIT_NPROC) +zsh_LIMIT_PRESENT(RLIMIT_NTHR) +zsh_LIMIT_PRESENT(RLIMIT_NOFILE) +zsh_LIMIT_PRESENT(RLIMIT_PTHREAD) +zsh_LIMIT_PRESENT(RLIMIT_RSS) +zsh_LIMIT_PRESENT(RLIMIT_SBSIZE) +zsh_LIMIT_PRESENT(RLIMIT_TCACHE) +zsh_LIMIT_PRESENT(RLIMIT_VMEM) +zsh_LIMIT_PRESENT(RLIMIT_SIGPENDING) +zsh_LIMIT_PRESENT(RLIMIT_MSGQUEUE) +zsh_LIMIT_PRESENT(RLIMIT_NICE) +zsh_LIMIT_PRESENT(RLIMIT_RTPRIO) +zsh_LIMIT_PRESENT(RLIMIT_POSIXLOCKS) +zsh_LIMIT_PRESENT(RLIMIT_NPTS) +zsh_LIMIT_PRESENT(RLIMIT_SWAP) +zsh_LIMIT_PRESENT(RLIMIT_KQUEUES) + +AH_TEMPLATE([RLIMIT_VMEM_IS_RSS], +[Define to 1 if RLIMIT_VMEM and RLIMIT_RSS both exist and are equal.]) +AC_CACHE_CHECK(if RLIMIT_VMEM and RLIMIT_RSS are the same, +zsh_cv_rlimit_vmem_is_rss, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int main() +{ +int ret = 1; +#if defined(HAVE_RLIMIT_VMEM) && defined(HAVE_RLIMIT_RSS) +if (RLIMIT_RSS == RLIMIT_VMEM) ret = 0; +#endif +return ret; +}]])],[zsh_cv_rlimit_vmem_is_rss=yes],[zsh_cv_rlimit_vmem_is_rss=no],[zsh_cv_rlimit_vmem_is_rss=no])]) + +if test x$zsh_cv_rlimit_vmem_is_rss = xyes; then + AC_DEFINE(RLIMIT_VMEM_IS_RSS) +fi + + +AH_TEMPLATE([RLIMIT_VMEM_IS_AS], +[Define to 1 if RLIMIT_VMEM and RLIMIT_AS both exist and are equal.]) +AC_CACHE_CHECK(if RLIMIT_VMEM and RLIMIT_AS are the same, +zsh_cv_rlimit_vmem_is_as, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int main() +{ +int ret = 1; +#if defined(HAVE_RLIMIT_VMEM) && defined(HAVE_RLIMIT_AS) +if (RLIMIT_AS == RLIMIT_VMEM) ret = 0; +#endif +return ret; +}]])],[zsh_cv_rlimit_vmem_is_as=yes],[zsh_cv_rlimit_vmem_is_as=no],[zsh_cv_rlimit_vmem_is_as=no])]) + +if test x$zsh_cv_rlimit_vmem_is_as = xyes; then + AC_DEFINE(RLIMIT_VMEM_IS_AS) +fi + + +AH_TEMPLATE([RLIMIT_RSS_IS_AS], +[Define to 1 if RLIMIT_RSS and RLIMIT_AS both exist and are equal.]) +AC_CACHE_CHECK(if RLIMIT_RSS and RLIMIT_AS are the same, +zsh_cv_rlimit_rss_is_as, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +int main() +{ +int ret = 1; +#if defined(HAVE_RLIMIT_RSS) && defined(HAVE_RLIMIT_AS) +if (RLIMIT_AS == RLIMIT_RSS) ret = 0; +#endif +return ret; +}]])],[zsh_cv_rlimit_rss_is_as=yes],[zsh_cv_rlimit_rss_is_as=no],[zsh_cv_rlimit_rss_is_as=no])]) + +if test x$zsh_cv_rlimit_rss_is_as = xyes; then + AC_DEFINE(RLIMIT_RSS_IS_AS) +fi + + +dnl -------------------------------------------- +dnl Check for members of struct rusage +dnl -------------------------------------------- +if test x$ac_cv_func_getrusage = xyes; then + AC_CHECK_MEMBERS([struct rusage.ru_maxrss, + struct rusage.ru_ixrss, + struct rusage.ru_idrss, + struct rusage.ru_isrss, + struct rusage.ru_minflt, + struct rusage.ru_majflt, + struct rusage.ru_nswap, + struct rusage.ru_inblock, + struct rusage.ru_oublock, + struct rusage.ru_msgsnd, + struct rusage.ru_msgrcv, + struct rusage.ru_nsignals, + struct rusage.ru_nvcsw, + struct rusage.ru_nivcsw],,, +[#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include ]) +fi + + +dnl -------------------------------------------- +dnl CHECK FOR DEFAULT PATH (used for command -p) +dnl -------------------------------------------- +AC_CACHE_VAL(zsh_cv_cs_path, +[if getconf _CS_PATH >/dev/null 2>&1; then + zsh_cv_cs_path=`getconf _CS_PATH` +elif getconf CS_PATH >/dev/null 2>&1; then + zsh_cv_cs_path=`getconf CS_PATH` +elif getconf PATH >/dev/null 2>&1; then + zsh_cv_cs_path=`getconf PATH` +else + zsh_cv_cs_path="/bin:/usr/bin" +fi]) +AC_DEFINE_UNQUOTED(DEFAULT_PATH, "$zsh_cv_cs_path", +[The default path; used when running commands with command -p]) + + +dnl ---------------------------- +dnl CHECK FOR /dev/fd FILESYSTEM +dnl ---------------------------- +dnl FreeBSD 5 only supports /dev/fd/0 to /dev/fd/2 without mounting +dnl a special file system. As zsh needs arbitrary /dev/fd (typically +dnl >10) for its own use, we need to make sure higher fd's are available. +dnl Since we're using the shell, we're restricted to 0 to 9 but 3 should +dnl be good enough. +AH_TEMPLATE([PATH_DEV_FD], +[Define to the path of the /dev/fd filesystem.]) +AC_CACHE_CHECK(for /dev/fd filesystem, zsh_cv_sys_path_dev_fd, +[if test "$host_os" = cygwin; then +dnl In current (2008/12/01) versions of Cygwin these are present but don't +dnl seem to work smoothly for process substitution; no great surprise +dnl since getting processes to work at all on Cygwin is a big challenge. +dnl We'll rely on FIFOs, since they do what we need. +zsh_cv_sys_path_dev_fd=no +else +[for zsh_cv_sys_path_dev_fd in /proc/self/fd /dev/fd no; do + test x`echo ok|(exec 3<&0; cat $zsh_cv_sys_path_dev_fd/3 2>/dev/null;)` = xok && break + done] +fi]) +if test x$zsh_cv_sys_path_dev_fd != xno; then + AC_DEFINE_UNQUOTED(PATH_DEV_FD, "$zsh_cv_sys_path_dev_fd") +fi + +dnl --------------------------------- +dnl CHECK FOR RFS SUPERROOT DIRECTORY +dnl --------------------------------- +AC_CACHE_CHECK(for RFS superroot directory, zsh_cv_sys_superroot, +[test -d /../.LOCALROOT && zsh_cv_sys_superroot=yes || zsh_cv_sys_superroot=no]) +AH_TEMPLATE([HAVE_SUPERROOT], +[Define to 1 if you have RFS superroot directory.]) +if test x$zsh_cv_sys_superroot = xyes; then + AC_DEFINE(HAVE_SUPERROOT) +fi + +dnl CHECK FOR SYSTEMS REQUIRING GETCWD +AC_CACHE_CHECK(whether we should use the native getcwd, +zsh_cv_use_getcwd, +[case "${host_cpu}-${host_vendor}-${host_os}" in + *QNX*) zsh_cv_use_getcwd=yes ;; + *) zsh_cv_use_getcwd=no ;; + esac]) +AH_TEMPLATE([USE_GETCWD], +[Define to 1 if you need to use the native getcwd.]) +if test x$zsh_cv_use_getcwd = xyes; then + AC_DEFINE(USE_GETCWD) +fi + +dnl GNU getcwd() can allocate as much space as necessary for a +dnl directory name, preventing guessing games. +AH_TEMPLATE([GETCWD_CALLS_MALLOC], +[Define to 1 if getcwd() calls malloc to allocate memory.]) +if test x$ac_cv_func_getcwd = xyes; then + AC_CACHE_CHECK(whether getcwd calls malloc to allocate memory, + zsh_cv_getcwd_malloc, + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include +int main() { + char buf[1024], *ptr1, *ptr2; + ptr1 = getcwd(buf, 1024); + ptr2 = getcwd(NULL, 0); + if (ptr1 && ptr2 && !strcmp(ptr1, ptr2)) { + return 0; + } + return 1; +} +]])],[zsh_cv_getcwd_malloc=yes],[zsh_cv_getcwd_malloc=no],[zsh_cv_getcwd_malloc=no])]) + if test x$zsh_cv_getcwd_malloc = xyes; then + AC_DEFINE(GETCWD_CALLS_MALLOC) + fi +fi + +dnl CHECK FOR setproctitle() FOR jobs -Z / ARGV0 +AH_TEMPLATE([HAVE_SETPROCTITLE], +[Define to 1 if the system supports `setproctitle' to change process name]) +AC_CHECK_FUNC(setproctitle,AC_DEFINE(HAVE_SETPROCTITLE), +AC_SEARCH_LIBS(setproctitle,util,AC_DEFINE(HAVE_SETPROCTITLE))) + +dnl ------------- +dnl CHECK FOR NIS +dnl ------------- +AH_TEMPLATE([HAVE_NIS], +[Define to 1 if you have NIS.]) +AC_CACHE_CHECK(for NIS, zsh_cv_sys_nis, +[test -f /usr/bin/ypcat && /usr/bin/ypcat passwd.byname > /dev/null 2>&1 && \ +zsh_cv_sys_nis=yes || zsh_cv_sys_nis=no]) +if test x$zsh_cv_sys_nis = xyes; then + AC_DEFINE(HAVE_NIS) +dnl Some systems (Solaris 2.x, Linux Redhat 5.x) require +dnl libnsl (Network Services Library) to find yp_all + AC_SEARCH_LIBS(yp_all, nsl) +fi + +dnl ----------------- +dnl CHECK FOR NISPLUS +dnl ----------------- +AH_TEMPLATE([HAVE_NIS_PLUS], +[Define to 1 if you have NISPLUS.]) +AC_CACHE_CHECK(for NIS+, zsh_cv_sys_nis_plus, +[test x$ac_cv_func_nis_list = xyes && test -f /usr/bin/nisls && \ + /usr/bin/nisls > /dev/null 2>&1 && \ +zsh_cv_sys_nis_plus=yes || zsh_cv_sys_nis_plus=no]) +if test x$zsh_cv_sys_nis_plus = xyes; then + AC_DEFINE(HAVE_NIS_PLUS) +fi + +dnl ---------------------------------------- +dnl CHECK FOR LOCATION OF {U,W}TMP{,X} FILES +dnl ---------------------------------------- +zsh_PATH_UTMP(utmp) +zsh_PATH_UTMP(wtmp) +zsh_PATH_UTMP(utmpx) +zsh_PATH_UTMP(wtmpx) + +dnl ------------------- +dnl brk/sbrk PROTOTYPES +dnl ------------------- +AC_CACHE_CHECK(for brk() prototype in , +zsh_cv_header_unistd_h_brk_proto, +[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +double brk();]], [[int i;]])],[zsh_cv_header_unistd_h_brk_proto=no],[zsh_cv_header_unistd_h_brk_proto=yes])]) +AH_TEMPLATE([HAVE_BRK_PROTO], +[Define to 1 if there is a prototype defined for brk() on your system.]) +if test x$zsh_cv_header_unistd_h_brk_proto = xyes; then + AC_DEFINE(HAVE_BRK_PROTO) +fi + +AC_CACHE_CHECK(for sbrk() prototype in , +zsh_cv_header_unistd_h_sbrk_proto, +[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +double sbrk();]], [[int i;]])],[zsh_cv_header_unistd_h_sbrk_proto=no],[zsh_cv_header_unistd_h_sbrk_proto=yes])]) +AH_TEMPLATE([HAVE_SBRK_PROTO], +[Define to 1 if there is a prototype defined for sbrk() on your system.]) +if test x$zsh_cv_header_unistd_h_sbrk_proto = xyes; then + AC_DEFINE(HAVE_SBRK_PROTO) +fi + +dnl ----------------------- +dnl mknod prototype for OSF +dnl ----------------------- +AH_TEMPLATE([HAVE_MKNOD_PROTO], +[Define to 1 if there is a prototype defined for mknod() on your system.]) +if test "$ac_cv_prog_cc_stdc" != no; then + AC_CACHE_CHECK(for mknod prototype in , + zsh_cv_header_sys_stat_h_mknod_proto, + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + int mknod(double x);]], [[int i;]])],[zsh_cv_header_sys_stat_h_mknod_proto=no],[zsh_cv_header_sys_stat_h_mknod_proto=yes])]) + if test x$zsh_cv_header_sys_stat_h_mknod_proto = xyes; then + AC_DEFINE(HAVE_MKNOD_PROTO) + fi +fi + +dnl ---------------------------------------- +dnl presence and location of ioctl prototype +dnl ---------------------------------------- +AC_CACHE_CHECK(for ioctl prototype in or , +zsh_cv_header_unistd_h_termios_h_ioctl_proto, +[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_TERMIOS_H +# include +#endif +double ioctl();]], [[int i;]])],[zsh_cv_header_unistd_h_termios_h_ioctl_proto=no],[zsh_cv_header_unistd_h_termios_h_ioctl_proto=yes])]) + +if test x$zsh_cv_header_unistd_h_termios_h_ioctl_proto = xno; then + AC_CACHE_CHECK(for ioctl prototype in , + zsh_cv_header_sys_ioctl_h_ioctl_proto, + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include + double ioctl();]], [[int i;]])],[zsh_cv_header_sys_ioctl_h_ioctl_proto=no],[zsh_cv_header_sys_ioctl_h_ioctl_proto=yes])]) +else + zsh_cv_header_sys_ioctl_h_ioctl_proto=no +fi + +AH_TEMPLATE([HAVE_IOCTL_PROTO], +[Define to 1 if there is a prototype defined for ioctl() on your system.]) +if test x$zsh_cv_header_unistd_h_termios_h_ioctl_proto = xyes || \ + test x$zsh_cv_header_sys_ioctl_h_ioctl_proto = xyes; then + AC_DEFINE(HAVE_IOCTL_PROTO) +fi +AH_TEMPLATE([IOCTL_IN_SYS_IOCTL], +[Define to 1 if we must include to get a prototype for ioctl().]) +if test x$zsh_cv_header_sys_ioctl_h_ioctl_proto = xyes; then + AC_DEFINE(IOCTL_IN_SYS_IOCTL) +fi + +dnl ------------------- +dnl select() defined in , ie BeOS R4.51 +dnl ------------------- +AH_TEMPLATE([SELECT_IN_SYS_SOCKET_H], +[Define to 1 if select() is defined in , ie BeOS R4.51]) +if test x$ac_cv_header_sys_select_h != xyes; then + AC_CACHE_CHECK(for select() in , + zsh_cv_header_socket_h_select_proto, + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[fd_set fd;]])],[zsh_cv_header_socket_h_select_proto=yes],[zsh_cv_header_socket_h_select_proto=no])]) + if test x$zsh_cv_header_socket_h_select_proto = xyes; then + AC_DEFINE(SELECT_IN_SYS_SOCKET_H) + fi +fi + +dnl ----------- +dnl named FIFOs +dnl ----------- +dnl +dnl Named FIFOs work well enough on recent versions of Cygwin +dnl to provide what we want. Simply enable them. +AC_CACHE_CHECK(if named FIFOs work, +zsh_cv_sys_fifo, +[if test "$host_os" = cygwin; then +zsh_cv_sys_fifo=yes +else +AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include +main() +{ + char c; + int fd; + int pid, ret; + unlink("/tmp/fifo$$"); +#ifdef HAVE_MKFIFO + if(mkfifo("/tmp/fifo$$", 0600) < 0) +#else + if(mknod("/tmp/fifo$$", 0010600, 0) < 0) +#endif + exit(1); + pid = fork(); + if(pid < 0) + exit(1); + if(pid) { + fd = open("/tmp/fifo$$", O_RDONLY); + exit(fd < 0 || read(fd, &c, 1) != 1 || c != 'x'); + } + fd = open("/tmp/fifo$$", O_WRONLY); + ret = (fd < 0 || write(fd, "x", 1) < 1); + unlink("/tmp/fifo$$"); + exit(ret); +} +]])],[zsh_cv_sys_fifo=yes],[zsh_cv_sys_fifo=no],[zsh_cv_sys_fifo=yes]) +fi]) +AH_TEMPLATE([HAVE_FIFOS], +[Define to 1 if system has working FIFOs.]) +if test x$zsh_cv_sys_fifo = xyes; then + AC_DEFINE(HAVE_FIFOS) +fi + +dnl ----------- +dnl test for whether link() works +dnl for instance, BeOS R4.51 doesn't support hard links yet +dnl ----------- +AC_CACHE_CHECK(if link() works, +zsh_cv_sys_link, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include +main() +{ + int ret; + char *tmpfile, *newfile; + tmpfile="/tmp/zsh.linktest$$"; + newfile="/tmp/zsh.linktest2$$"; + unlink(tmpfile); + unlink(newfile); + if(creat(tmpfile, 0644) < 0) + exit(1); + ret = link(tmpfile, newfile); + unlink(tmpfile); + unlink(newfile); + exit(ret<0); +} +]])],[zsh_cv_sys_link=yes],[zsh_cv_sys_link=no],[zsh_cv_sys_link=yes])]) +AH_TEMPLATE([HAVE_LINK], +[Define to 1 if system has working link().]) +if test x$zsh_cv_sys_link = xyes; then + AC_DEFINE(HAVE_LINK) +fi + +dnl ----------- +dnl test for whether kill(pid, 0) where pid doesn't exit +dnl should set errno to ESRCH, but some like BeOS R4.51 set to EINVAL +dnl ----------- +AC_CACHE_CHECK(if kill(pid, 0) returns ESRCH correctly, +zsh_cv_sys_killesrch, +[AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include +#include +main() +{ + int pid = (getpid() + 10000) & 0xffffff; + while (pid && (kill(pid, 0) == 0 || errno != ESRCH)) pid >>= 1; + exit(errno!=ESRCH); +} +]])],[zsh_cv_sys_killesrch=yes],[zsh_cv_sys_killesrch=no],[zsh_cv_sys_killesrch=yes])]) +AH_TEMPLATE([BROKEN_KILL_ESRCH], +[Define to 1 if kill(pid, 0) doesn't return ESRCH, ie BeOS R4.51.]) +if test x$zsh_cv_sys_killesrch = xno; then + AC_DEFINE(BROKEN_KILL_ESRCH) +fi + +dnl ----------- +dnl if POSIX, test for working sigsuspend(). +dnl for instance, BeOS R4.51 is broken. +dnl ----------- +AH_TEMPLATE([BROKEN_POSIX_SIGSUSPEND], +Define to 1 if sigsuspend() is broken, ie BeOS R4.51.]) +if test x$signals_style = xPOSIX_SIGNALS; then + AC_CACHE_CHECK(if POSIX sigsuspend() works, + zsh_cv_sys_sigsuspend, + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include +int child=0; +void handler(sig) + int sig; +{if(sig==SIGCHLD) child=1;} +main() { + struct sigaction act; + sigset_t set; + int pid, ret; + act.sa_handler = &handler; + sigfillset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGCHLD, &act, 0); + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, 0); + pid=fork(); + if(pid==0) return 0; + if(pid>0) { + sigemptyset(&set); + ret=sigsuspend(&set); + exit(child==0); + } +} +]])],[zsh_cv_sys_sigsuspend=yes],[zsh_cv_sys_sigsuspend=no],[zsh_cv_sys_sigsuspend=yes])]) + if test x$zsh_cv_sys_sigsuspend = xno; then + AC_DEFINE(BROKEN_POSIX_SIGSUSPEND) + fi +fi + +dnl ----------- +dnl if found tcsetpgrp, test to see if it actually works +dnl for instance, BeOS R4.51 does not support it yet +dnl ----------- +AH_TEMPLATE([BROKEN_TCSETPGRP], +[Define to 1 if tcsetpgrp() doesn't work, ie BeOS R4.51.]) +AC_ARG_WITH(tcsetpgrp, +AS_HELP_STRING([--with-tcsetpgrp],[assumes that tcsetpgrp() exists and works correctly]),[ +case "x$withval" in + xyes) zsh_working_tcsetpgrp=yes;; + xno) zsh_working_tcsetpgrp=no;; + *) AC_MSG_ERROR(please use --with-tcsetpgrp=yes or --with-tcsetpgrp=no);; +esac],[zsh_working_tcsetpgrp=check]) +if test "x$ac_cv_func_tcsetpgrp" = xyes; then +case "x$zsh_working_tcsetpgrp" in + xcheck) + trap "" TTOU > /dev/null 2>&1 || : + AC_CACHE_CHECK(if tcsetpgrp() actually works, + zsh_cv_sys_tcsetpgrp, + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include +#include +main() { + int fd; + int ret; + fd=open("/dev/tty", O_RDWR); + if (fd < 0) exit(2); + ret=tcsetpgrp(fd, tcgetpgrp(fd)); + if (ret < 0) exit(1); + exit(0); +} +]])],[zsh_cv_sys_tcsetpgrp=yes],[ +case $? in + 1) zsh_cv_sys_tcsetpgrp=no;; + 2) zsh_cv_sys_tcsetpgrp=notty;; + *) zsh_cv_sys_tcsetpgrp=error;; +esac + ],[zsh_cv_sys_tcsetpgrp=yes])]) + case "x$zsh_cv_sys_tcsetpgrp" in + xno) AC_DEFINE(BROKEN_TCSETPGRP);; + xyes) :;; + xnotty) AC_MSG_ERROR([no controlling tty +Try running configure with --with-tcsetpgrp or --without-tcsetpgrp]);; + *) AC_MSG_ERROR([unexpected return status]);; + esac + trap - TTOU > /dev/null 2>&1 || : + ;; + xyes) :;; + xno) AC_DEFINE(BROKEN_TCSETPGRP);; + *) AC_MSG_ERROR([unexpected value zsh_working_tcsetpgrp=$zsh_working_tcsetpgrp]);; +esac +fi + +dnl ----------- +dnl test for faked getpwnam() entry, ie a single entry returned for any username +dnl for instance, BeOS R4.51 is not multiuser yet, and fakes getpwnam() +dnl test by looking up two usernames that shouldn't succeed, and compare entry +dnl ----------- +AH_TEMPLATE([GETPWNAM_FAKED], +[Define to 1 if getpwnam() is faked, ie BeOS R4.51.]) +if test x$ac_cv_func_getpwnam = xyes; then + AC_CACHE_CHECK(if getpwnam() is faked, + zsh_cv_sys_getpwnam_faked, + [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +main() { + struct passwd *pw1, *pw2; + char buf[1024], name[1024]; + sprintf(buf, "%d:%d", getpid(), rand()); + pw1=getpwnam(buf); + if (pw1) strcpy(name, pw1->pw_name); + sprintf(buf, "%d:%d", rand(), getpid()); + pw2=getpwnam(buf); + exit(pw1!=0 && pw2!=0 && !strcmp(name, pw2->pw_name)); +} +]])],[zsh_cv_sys_getpwnam_faked=no],[zsh_cv_sys_getpwnam_faked=yes],[zsh_cv_sys_getpwnam_faked=no])]) + if test x$zsh_cv_sys_getpwnam_faked = xyes; then + AC_DEFINE(GETPWNAM_FAKED) + fi +fi + + +dnl --------------- +dnl check for the type of third argument of accept +dnl --------------- + +zsh_CHECK_SOCKLEN_T + +dnl --------------- +dnl Check for pty multiplexer for use in pty module. +dnl We need to open it read/write, so make sure it is writeable. +dnl Yet another test which won't work when cross-compiling. +dnl --------------- +AC_CACHE_CHECK(if your system has /dev/ptmx, +ac_cv_have_dev_ptmx, +[if test -w /dev/ptmx; then + ac_cv_have_dev_ptmx=yes +else + ac_cv_have_dev_ptmx=no +fi]) + +dnl -------- +dnl Check if the ptmx functions are usable. +dnl We need to be able to find the prototypes, which may +dnl require non-POSIX source definitions. So test to see +dnl if ptsname is correctly recognised as returning a char *. +dnl We do this by making sure a program where ptsname() is declared +dnl as returning int does *not* compile. +dnl On Linux we need the XOPEN extensions. The easiest way to get +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 -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 + AC_CACHE_CHECK([if /dev/ptmx is usable], + ac_cv_use_dev_ptmx, + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#ifdef __linux +#define _GNU_SOURCE 1 +#endif +#include +int ptsname();]], [[]])],[ac_cv_use_dev_ptmx=no],[ac_cv_use_dev_ptmx=yes])]) + if test x$ac_cv_use_dev_ptmx = xyes; then + AC_DEFINE(USE_DEV_PTMX) + fi +fi + +dnl ----------------- +dnl multibyte support +dnl ----------------- +AC_ARG_ENABLE(multibyte, +AS_HELP_STRING([--enable-multibyte],[support multibyte characters]), +[zsh_cv_c_unicode_support=$enableval], +[AC_CACHE_VAL(zsh_cv_c_unicode_support, + AC_MSG_NOTICE([checking for functions supporting multibyte characters]) + [zfuncs_absent= +dnl +dnl Note that iswblank is not included and checked separately. +dnl As iswblank() was added to C long after the others, we still +dnl want to enabled unicode support even if iswblank is not available +dnl (we then just do the SPC+TAB approximation) +dnl + for zfunc in iswalnum iswcntrl iswdigit iswgraph iswlower iswprint \ +iswpunct iswspace iswupper iswxdigit mbrlen mbrtowc towupper towlower \ +wcschr wcscpy wcslen wcsncmp wcsncpy wcrtomb wcwidth wmemchr wmemcmp \ +wmemcpy wmemmove wmemset; do + AC_CHECK_FUNC($zfunc, + [:], [zfuncs_absent="$zfuncs_absent $zfunc"]) + done + if test x"$zfuncs_absent" = x; then + AC_MSG_NOTICE([all functions found, multibyte support enabled]) + zsh_cv_c_unicode_support=yes + else + AC_MSG_NOTICE([missing functions, multibyte support disabled]) + zsh_cv_c_unicode_support=no + fi + ]) +]) +AH_TEMPLATE([MULTIBYTE_SUPPORT], +[Define to 1 if you want support for multibyte character sets.]) + +dnl +dnl unicode9 support +dnl +AH_TEMPLATE([ENABLE_UNICODE9], +[Define to 1 if you want use unicode9 character widths.]) +AC_ARG_ENABLE(unicode9, +AS_HELP_STRING([--enable-unicode9],[compile with unicode9 character widths]), +[if test x$enableval = xyes; then + AC_DEFINE(ENABLE_UNICODE9) +fi]) + +AH_TEMPLATE([BROKEN_ISPRINT], +[Define to 1 if the isprint() function is broken under UTF-8 locale.]) + +if test x$zsh_cv_c_unicode_support = xyes; then + AC_DEFINE(MULTIBYTE_SUPPORT) + + dnl Test if wcwidth() and/or iswprint() is broken for + dnl zero-width combining characters, or + dnl some characters in the Latin Extended-B. + dnl If either of the functions is broken, both functions will be replaced + dnl by the ones from wcwidth9.h by defining ENABLE_UNICODE9. We will do + dnl this only if __STDC_ISO_10646__ is defined (or if building on macOS, + dnl where __STDC_ISO_10646__ is not defined but wchar_t is UCS). + dnl For the test we use a combining acute accent (\u0301) or + dnl a LATIN SMALL LETTER L WITH CURL (\u0234). + dnl We input it as UTF-8 since that is the standard we can rely + dnl upon most: we can't rely on a wchar_t being stored as a + dnl Unicode code point on all systems. + dnl The programme returns 0 only if all the conditions for brokenness + dnl are met: + dnl - the programme compiled, linked and ran + dnl - we successfully set a UTF-8 locale + dnl - the locale we set plausibly converted the UTF-8 string + dnl into the correct wide character + dnl - but wcwidth() or iswprint() is broken for the converted wide character. + dnl locale -a is a fallback; on most systems we should find en_US.UTF-8. + [locale_prog='char *my_locales[] = { + "en_US.UTF-8", "en_GB.UTF-8", "en.UTF-8", ' + locale_prog="$locale_prog"`locale -a 2>/dev/null | \ + sed -e 's/utf8/UTF-8/' | grep UTF-8 | \ + while read line; do echo " \"$line\","; done;` + locale_prog="$locale_prog 0 }; + #define _XOPEN_SOURCE + #include + #include + #include + #include + + int main() { + char **localep; + char comb_acute_mb[] = { (char)0xcc, (char)0x81 }; + char u_0234[] = { (char)0xc8, (char)0xb4 }; + wchar_t wc; + #if !defined(__STDC_ISO_10646__) && !defined(__APPLE__) + return 1; + #endif + + for (localep = my_locales; *localep; localep++) + if (setlocale(LC_ALL, *localep)) + break; + if (!*localep) + return 1; + if (mbtowc(&wc, comb_acute_mb, 2) == 2 && (wcwidth(wc) != 0 || !iswprint(wc))) + return 0; + if (mbtowc(&wc, u_0234, 2) == 2 && (wcwidth(wc) != 1 || !iswprint(wc))) + return 0; + return 1; + } + "] + + AC_CACHE_CHECK(if the wcwidth() and/or iswprint() functions are broken, + zsh_cv_c_broken_wcwidth, + [AC_RUN_IFELSE([AC_LANG_SOURCE([[$locale_prog]])],[zsh_cv_c_broken_wcwidth=yes],[zsh_cv_c_broken_wcwidth=no],[zsh_cv_c_broken_wcwidth=no])]) + if test x$zsh_cv_c_broken_wcwidth = xyes; then + AC_DEFINE(ENABLE_UNICODE9) + fi + + dnl Check if isprint() behaves correctly under UTF-8 locale. + dnl On some platform (maybe only on Mac OS X), isprint() returns + dnl true for all characters in the range from 0xa0 to 0xff if + dnl called under UTF-8 locale. + [locale_prog='char *my_locales[] = { + "en_US.UTF-8", "en_GB.UTF-8", "en.UTF-8", ' + locale_prog="$locale_prog"`locale -a 2>/dev/null | \ + sed -e 's/utf8/UTF-8/' | grep UTF-8 | \ + while read line; do echo " \"$line\","; done;` + locale_prog="$locale_prog 0 }; + #include + #include + + int main() { + char **localep; + for (localep = my_locales; *localep; localep++) + if (setlocale(LC_ALL, *localep) && isprint(0xa0)) + return 0; + return 1; + } + "] + + AC_CACHE_CHECK(if the isprint() function is broken, + zsh_cv_c_broken_isprint, + [AC_RUN_IFELSE([AC_LANG_SOURCE([[$locale_prog]])],[zsh_cv_c_broken_isprint=yes],[zsh_cv_c_broken_isprint=no],[zsh_cv_c_broken_isprint=no])]) + if test x$zsh_cv_c_broken_isprint = xyes; then + AC_DEFINE(BROKEN_ISPRINT) + fi +fi + +dnl +dnl musl support +dnl +AH_TEMPLATE([LIBC_MUSL], +[Define to 1 if musl is being used as the C library]) +AC_ARG_ENABLE(libc-musl, +AS_HELP_STRING([--enable-libc-musl],[compile with musl as the C library]), +[if test x$enableval = xyes; then + AC_DEFINE(LIBC_MUSL) +fi]) + +dnl +dnl static user lookup +dnl +AC_ARG_ENABLE(dynamic-nss, + AS_HELP_STRING([--disable-dynamic-nss],[do not call + functions that will require dynamic NSS + modules]), +[zsh_cv_c_dynamic_nss=$enableval], +[]) + +AH_TEMPLATE([DISABLE_DYNAMIC_NSS], +[Define to 1 if you want to avoid calling functions that will require + dynamic NSS modules.]) +if test x$zsh_cv_c_dynamic_nss = xno; then + AC_DEFINE(DISABLE_DYNAMIC_NSS) +fi + +dnl --------------- +dnl dynamic loading +dnl --------------- +AH_TEMPLATE([HPUX10DYNAMIC], +[Define to 1 if you want to use dynamically loaded modules on HPUX 10.]) +L=N +INSTLIB="install.bin-\$(L)" +UNINSTLIB="uninstall.bin-\$(L)" +LINKMODS=NOLINKMODS +MOD_EXPORT= +MOD_IMPORT_VARIABLE= +MOD_IMPORT_FUNCTION= +aixdynamic=no +hpuxdynamic=no +if test "$ac_cv_func_load" = yes && + test "$ac_cv_func_unload" = yes && + test "$ac_cv_func_loadbind" = yes && + test "$ac_cv_func_loadquery" = yes; then + dnl Force AIXDYNAMIC even on newer versions that have dl family + if test "x$dynamic" = xyes; then + aixdynamic=yes + fi +elif test "$ac_cv_func_dlopen" != yes || + test "$ac_cv_func_dlsym" != yes || + test "$ac_cv_func_dlerror" != yes; then + if test "$ac_cv_func_shl_load" != yes || + test "$ac_cv_func_shl_unload" != yes || + test "$ac_cv_func_shl_findsym" != yes; then + dynamic=no + elif test "x$dynamic" = xyes; then + hpuxdynamic=yes + DL_EXT="${DL_EXT=sl}" + dnl autoheader won't allow us to define anything which isn't + dnl going into a header, and we can't undefine anything, so + dnl just define this anyway and rely on the later tests to + dnl define DYNAMIC or not. + AC_DEFINE(HPUX10DYNAMIC)dnl + fi +fi + +test -n "$GCC" && LDARG=-Wl, + +AH_TEMPLATE([DLSYM_NEEDS_UNDERSCORE], +[Define to 1 if an underscore has to be prepended to dlsym() argument.]) +AH_TEMPLATE([DYNAMIC_NAME_CLASH_OK], +[Define to 1 if multiple modules defining the same symbol are OK.]) +if test "x$aixdynamic" = xyes; then + DL_EXT="${DL_EXT=so}" + DLLD="${DLLD=$CC}" + zsh_cv_func_dlsym_needs_underscore=no + if test -n "$GCC"; then + DLLDFLAGS=${DLLDFLAGS=-shared} + else + DLLDFLAGS=${DLLDFLAGS=-bM:SRE} + fi + DLLDFLAGS=${DLLDFLAGS=} + EXTRA_LDFLAGS=${EXTRA_LDFLAGS=} + EXPOPT=${LDARG}-bE: + IMPOPT=${LDARG}-bI: + zsh_cv_sys_dynamic_clash_ok="${zsh_cv_sys_dynamic_clash_ok=yes}" + zsh_cv_sys_dynamic_rtld_global="${zsh_cv_sys_dynamic_rtld_global=yes}" + zsh_cv_sys_dynamic_execsyms="${zsh_cv_sys_dynamic_execsyms=yes}" + zsh_cv_sys_dynamic_strip_exe="${zsh_cv_sys_dynamic_strip_exe=yes}" + zsh_cv_sys_dynamic_strip_lib="${zsh_cv_sys_dynamic_strip_lib=yes}" + zsh_cv_shared_environ="${zsh_cv_shared_environ=yes}" +elif test "$host_os" = cygwin; then + DL_EXT="${DL_EXT=dll}" +##DLLD="${DLLD=dllwrap}" + DLLD="${DLLD=$CC}" +##DLLDFLAGS="${DLLDFLAGS=--export-all-symbols}" + DLLDFLAGS=${DLLDFLAGS=-shared -Wl,--export-all-symbols} + zsh_cv_func_dlsym_needs_underscore=no + DLLDFLAGS=${DLLDFLAGS=} + EXTRA_LDFLAGS=${EXTRA_LDFLAGS=} + zsh_cv_sys_dynamic_clash_ok="${zsh_cv_sys_dynamic_clash_ok=no}" + zsh_cv_sys_dynamic_rtld_global="${zsh_cv_sys_dynamic_rtld_global=yes}" + zsh_cv_sys_dynamic_execsyms="${zsh_cv_sys_dynamic_execsyms=no}" + zsh_cv_sys_dynamic_strip_exe="${zsh_cv_sys_dynamic_strip_exe=yes}" + zsh_cv_sys_dynamic_strip_lib="${zsh_cv_sys_dynamic_strip_lib=yes}" + # + # THAT SUCKS! and must be changed + # + zsh_cv_shared_environ="${zsh_cv_shared_environ=yes}" + LINKMODS=LINKMODS + MOD_EXPORT="__attribute__((__dllexport__))" + MOD_IMPORT_VARIABLE="__attribute__((__dllimport__))" + MOD_IMPORT_FUNCTION= +elif test "x$dynamic" = xyes; then + AC_CACHE_CHECK(if your system uses ELF binaries, + zsh_cv_sys_elf, + [AC_RUN_IFELSE([AC_LANG_SOURCE([[/* Test for whether ELF binaries are produced */ +#include +#include +main(argc, argv) +int argc; +char *argv[]; +{ + char b[4]; + int i = open(argv[0],O_RDONLY); + if(i == -1) + exit(1); /* fail */ + if(read(i,b,4)==4 && b[0]==127 && b[1]=='E' && b[2]=='L' && b[3]=='F') + exit(0); /* succeed (yes, it's ELF) */ + else + exit(1); /* fail */ +}]])],[zsh_cv_sys_elf=yes],[zsh_cv_sys_elf=no],[zsh_cv_sys_elf=yes])]) + + # We use [0-9]* in case statements, so need to change quoting + changequote(, ) + + DL_EXT="${DL_EXT=so}" + if test x$zsh_cv_sys_elf = xyes; then + case "$host" in + mips-sni-sysv4*) + # Forcibly set ld to native compiler to avoid obscure GCC problems + DLLD="${DLLD=/usr/ccs/bin/cc}" + DLLDARG="${LDARG}" + ;; + * ) + DLLD="${DLLD=$CC}" + DLLDARG="${LDARG}" + ;; + esac + else + case "$host" in + *openbsd*) + case "$host_os" in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + DLLD="${DLLD=ld}" + ;; + *) + DLLD="${DLLD=$CC}" + ;; + esac + DLLDARG="${LDARG}" + ;; + *darwin*) + DLLD="${DLLD=$CC}" + DLLDARG="" + ;; + *interix*) + DLLD="${DLLD=$CC}" + DLLDARG="" + ;; + * ) + DLLD="${DLLD=ld}" + DLLDARG="" + ;; + esac + fi + if test -n "$GCC"; then + case "$host_os" in + hpux*) DLLDFLAGS="${DLLDFLAGS=-shared}" ;; + darwin*) DLCFLAGS="${DLCFLAGS=-fno-common}" ;; + interix*) DLCFLAGS="${DLCFLAGS=}" ;; + *) DLCFLAGS="${DLCFLAGS=-fPIC}" ;; + esac + else + case "$host_os" in + hpux*) + DLCFLAGS="${DLCFLAGS=+z}" + DLLDFLAGS="${DLLDFLAGS=-b}" + ;; + sunos*) DLCFLAGS="${DLCFLAGS=-pic}" ;; + solaris*|sysv4*|esix*) DLCFLAGS="${DLCFLAGS=-KPIC}" ;; + esac + fi + case "$host_os" in + osf*) DLLDFLAGS="${DLLDFLAGS=-shared -expect_unresolved '*'}" ;; + *freebsd*|*netbsd*|linux*|irix*|gnu*|interix*|dragonfly*) DLLDFLAGS="${DLLDFLAGS=-shared}" ;; + sunos*) DLLDFLAGS="${DLLDFLAGS=-assert nodefinitions}" ;; + sysv4*|esix*) DLLDFLAGS="${DLLDFLAGS=-G $ldflags}" ;; + aix*) DLLDFLAGS="${DLLDFLAGS=-G -bexpall -lc}" ;; + solaris*|sysv4*|esix*) DLLDFLAGS="${DLLDFLAGS=-G}" ;; + darwin*) DLLDFLAGS="${DLLDFLAGS=-bundle -flat_namespace -undefined suppress}" ;; + beos*|haiku*) DLLDFLAGS="${DLLDFLAGS=-nostart}" ;; + openbsd*) + if test x$zsh_cv_sys_elf = xyes; then + DLLDFLAGS="${DLLDFLAGS=-shared -fPIC}" + else + case "$host_os" in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + DLLDFLAGS="${DLLDFLAGS=-Bshareable}" + ;; + *) + DLLDFLAGS="${DLLDFLAGS=-shared -fPIC}" + ;; + esac + fi + ;; + esac + case "$host" in + *-hpux*) EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-E}" ;; + *openbsd*) + if test x$zsh_cv_sys_elf = xyes; then + EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-E}" + fi + ;; + mips-sni-sysv4) + # + # unfortunately, we have different compilers + # that need different flags + # + if test -n "$GCC"; then + sni_cc_version=GCC + else + sni_cc_version=`$CC -V 2>&1 | head -1` + fi + case "$sni_cc_version" in + *CDS*|GCC ) + EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-Blargedynsym}" + ;; + * ) + EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-LD-Blargedynsym}" + ;; + esac + ;; + *-beos*) + # gcc on BeOS doesn't like -rdynamic... + EXTRA_LDFLAGS="${EXTRA_LDFLAGS= }" + # also, dlopen() at least in Zeta respects $LIBRARY_PATH, so needs %A added to it. + export LIBRARY_PATH="$LIBRARY_PATH:%A/" + ;; + *-haiku*) + # + ;; + esac + + # Done with our shell code, so restore autotools quoting + changequote([, ]) + +AC_CACHE_CHECK(if we can use -rdynamic, zsh_cv_rdynamic_available, +old_LDFLAGS="$LDFLAGS" +LDFLAGS="$LDFLAGS -rdynamic" +AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[zsh_cv_rdynamic_available=yes +EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-rdynamic}"],[zsh_cvs_rdynamic_available=no]) +LDFLAGS="$old_LDFLAGS") + AC_CACHE_CHECK(if your dlsym() needs a leading underscore, + zsh_cv_func_dlsym_needs_underscore, + [echo failed >conftestval && cat >conftest.c <&AS_MESSAGE_LOG_FD) && + AC_TRY_COMMAND($DLLD $LDFLAGS $DLLDFLAGS -o conftest.$DL_EXT conftest.o 1>&AS_MESSAGE_LOG_FD) && + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#ifdef HPUX10DYNAMIC +#include +#define RTLD_LAZY BIND_DEFERRED +#define RTLD_GLOBAL DYNAMIC_PATH + +char *zsh_gl_sym_addr ; + +#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0) +#define dlclose(handle) shl_unload((shl_t)(handle)) +#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr) +#define dlerror() 0 +#else +#ifdef HAVE_DLFCN_H +#include +#else +#include +#include +#include +#endif +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif + +extern int fred() ; + +main() +{ + void * handle ; + void * symbol ; + FILE *f=fopen("conftestval", "w"); + if (!f) exit(1); + handle = dlopen("./conftest.$DL_EXT", RTLD_LAZY) ; + if (handle == NULL) { + fprintf (f, "dlopen failed") ; + exit(1); + } + symbol = dlsym(handle, "fred") ; + if (symbol == NULL) { + /* try putting a leading underscore */ + symbol = dlsym(handle, "_fred") ; + if (symbol == NULL) { + fprintf (f, "dlsym failed") ; + exit(1); + } + fprintf (f, "yes") ; + } + else + fprintf (f, "no") ; + exit(0); +}]])],[zsh_cv_func_dlsym_needs_underscore=`cat conftestval`],[zsh_cv_func_dlsym_needs_underscore=failed + dynamic=no],[zsh_cv_func_dlsym_needs_underscore=no])]) + if test "x$zsh_cv_func_dlsym_needs_underscore" = xyes; then + AC_DEFINE(DLSYM_NEEDS_UNDERSCORE) + elif test "x$zsh_cv_func_dlsym_needs_underscore" != xno; then + dnl Do not cache failed value + unset zsh_cv_func_dlsym_needs_underscore + fi +fi + +if test "x$dynamic" = xyes; then + zsh_SHARED_VARIABLE([environ], [char **]) + test "$zsh_cv_shared_environ" = yes || dynamic=no + if test "$ac_cv_func_tgetent" = yes; then + zsh_SHARED_FUNCTION([tgetent]) + fi + if test "$ac_cv_func_tigetstr" = yes; then + zsh_SHARED_FUNCTION([tigetstr]) + fi +fi + +if test "x$dynamic" = xyes; then + zsh_SYS_DYNAMIC_CLASH + zsh_SYS_DYNAMIC_GLOBAL + RTLD_GLOBAL_OK=$zsh_cv_sys_dynamic_rtld_global + zsh_SYS_DYNAMIC_EXECSYMS + if test "$zsh_cv_sys_dynamic_execsyms" != yes; then + L=L + fi + zsh_SYS_DYNAMIC_STRIP_EXE + zsh_SYS_DYNAMIC_STRIP_LIB + if $strip_exeldflags && test "$zsh_cv_sys_dynamic_strip_exe" = yes; then + EXELDFLAGS="$EXELDFLAGS -s" + fi + if $strip_libldflags && test "$zsh_cv_sys_dynamic_strip_lib" = yes; then + LIBLDFLAGS="$LIBLDFLAGS -s" + fi + if test "$host_os" = cygwin; then + INSTLIB="install.cygwin-lib" + UNINSTLIB="uninstall.cygwin-lib" + fi +else + $strip_exeldflags && EXELDFLAGS="$EXELDFLAGS -s" + $strip_libldflags && LIBLDFLAGS="$LIBLDFLAGS -s" + RTLD_GLOBAL_OK=no +fi + +AH_TEMPLATE([DYNAMIC], +[Define to 1 if you want to use dynamically loaded modules.]) +if test "x$dynamic" = xyes; then + D=D + AC_DEFINE(DYNAMIC)dnl +else + D=N +fi + +AH_TEMPLATE([AIXDYNAMIC], +[Define to 1 if you want to use dynamically loaded modules on AIX.]) +if test "x$aixdynamic" = xyes; then + E=E + AC_DEFINE(AIXDYNAMIC)dnl +else + E=N +fi + +if test "x$zsh_cv_sys_dynamic_clash_ok" = xyes; then + SHORTBOOTNAMES=yes +else + SHORTBOOTNAMES=no +fi +AC_SUBST(SHORTBOOTNAMES) + +AC_SUBST(INSTLIB)dnl +AC_SUBST(UNINSTLIB)dnl + +if test "$host_os" = cygwin; then + EXTRAZSHOBJS="$EXTRAZSHOBJS zsh.res.o" +fi + +AC_DEFINE_UNQUOTED(DL_EXT, "$DL_EXT", +[The extension used for dynamically loaded modules.])dnl +AC_SUBST(D)dnl +AC_SUBST(DL_EXT)dnl +AC_SUBST(DLLD)dnl +AC_SUBST(DLCFLAGS)dnl +AC_SUBST(DLLDFLAGS)dnl +AC_SUBST(E)dnl +AC_SUBST(EXTRA_LDFLAGS)dnl +AC_SUBST(EXPOPT)dnl +AC_SUBST(IMPOPT)dnl +AC_SUBST(L)dnl +AC_SUBST(LINKMODS)dnl +AC_SUBST(MOD_EXPORT)dnl +AC_SUBST(MOD_IMPORT_VARIABLE)dnl +AC_SUBST(MOD_IMPORT_FUNCTION)dnl +AC_SUBST(EXTRAZSHOBJS)dnl + +# Generate config.modules. We look for *.mdd files in first and second +# level subdirectories. Any existing line not containing 'auto=y' will be +# retained, provided the .mdd file itself was found. +CONFIG_MODULES=./config.modules +cat < ${CONFIG_MODULES}.sh +srcdir="$srcdir" +dynamic="$dynamic" +CONFIG_MODULES="${CONFIG_MODULES}" +EOM +cat <<\EOM >> ${CONFIG_MODULES}.sh +echo "creating ${CONFIG_MODULES}" +userlist=" " +if test -f ${CONFIG_MODULES}; then + userlist="`sed -e '/^#/d' -e '/auto=y/d' -e 's/ .*/ /' -e 's/^name=/ /' \ + ${CONFIG_MODULES}`" + mv ${CONFIG_MODULES} ${CONFIG_MODULES}.old +else + # Save testing for existence each time. + echo > ${CONFIG_MODULES}.old +fi +(echo "# Edit this file to change the way modules are loaded." +echo "# The format is strict; do not break lines or add extra spaces." +echo "# Run \`make prep' if you change anything here after compiling" +echo "# (there is no need if you change this just after the first time" +echo "# you run \`configure')." +echo "#" +echo "# Values of \`link' are \`static', \`dynamic' or \`no' to compile the" +echo "# module into the shell, link it in at run time, or not use it at all." +echo "# In the final case, no attempt will be made to compile it." +echo "# Use \`static' or \`no' if you do not have dynamic loading." +echo "#" +echo "# Values of \`load' are \`yes' or \`no'; if yes, any builtins etc." +echo "# provided by the module will be autoloaded by the main shell" +echo "# (so long as \`link' is not set to \`no')." +echo "#" +echo "# Values of \`auto' are \`yes' or \`no'. configure sets the value to" +echo "# \`yes'. If you set it by hand to \`no', the line will be retained" +echo "# when the file is regenerated in future." +echo "#" +echo "# Note that the \`functions' entry extends to the end of the line." +echo "# It should not be quoted; it is used verbatim to find files to install." +echo "#" +echo "# You will need to run \`config.status --recheck' if you add a new" +echo "# module." +echo "#" +echo "# You should not change the values for the pseudo-module zsh/main," +echo "# which is the main shell (apart from the functions entry)." +EOM +dnl The autoconf macros are only available in configure, not +dnl config.status, and only change when configure is rerun. +dnl So we need to run the autoconf tests here and store the results. +dnl We then generate config.modules, preserving any user-generated +dnl information, from config.status. +for modfile in `cd ${srcdir}; echo */*.mdd */*/*.mdd`; do + name= + link= + load= + functions= + result= + . ${srcdir}/$modfile + if test x$name != x && test x"$link" != x; then + case "$link" in + *\ *) eval "link=\`$link\`" + ;; + esac + case "${load}" in + y*) load=" load=yes" + ;; + *) load=" load=no" + ;; + esac + if test "x$functions" != x; then + # N.B. no additional quotes + f=" functions=$functions" + else + f= + fi + case "$link" in + static) result="name=$name modfile=$modfile link=static auto=yes${load}$f" + ;; + dynamic) if test x$dynamic != xno; then + result="name=$name modfile=$modfile link=dynamic\ + auto=yes${load}$f" + else + result="name=$name modfile=$modfile link=no\ + auto=yes load=no$f" + fi + ;; + either) if test x$dynamic != xno; then + result="name=$name modfile=$modfile link=dynamic\ + auto=yes${load}$f" + else + result="name=$name modfile=$modfile link=static\ + auto=yes${load}$f" + fi + ;; + *) result="name=$name modfile=$modfile link=no auto=yes load=no$f" + ;; + esac +dnl $result is the default output for config.modules. We generate +dnl code to check if we should use this. +cat <> ${CONFIG_MODULES}.sh +case "\$userlist" in + *" $name "*) grep "^name=$name " \${CONFIG_MODULES}.old;; + *) echo "$result";; +esac +EOM + fi +done +cat <<\EOM >> ${CONFIG_MODULES}.sh +) >${CONFIG_MODULES} +rm -f ${CONFIG_MODULES}.old +EOM + +dnl AH_TOP replaces the code which used to appear at the top +dnl of acconfig.h. +AH_TOP([/***** begin user configuration section *****/ + +/* Define this to be the location of your password file */ +#define PASSWD_FILE "/etc/passwd" + +/* Define this to be the name of your NIS/YP password * + * map (if applicable) */ +#define PASSWD_MAP "passwd.byname" + +/* Define to 1 if you want user names to be cached */ +#define CACHE_USERNAMES 1 + +/* Define to 1 if system supports job control */ +#define JOB_CONTROL 1 + +/* Define this if you use "suspended" instead of "stopped" */ +#define USE_SUSPENDED 1 + +/* The default history buffer size in lines */ +#define DEFAULT_HISTSIZE 30 + +/* The default editor for the fc builtin */ +#define DEFAULT_FCEDIT "vi" + +/* The default prefix for temporary files */ +#define DEFAULT_TMPPREFIX "/tmp/zsh" + +/***** end of user configuration section *****/ +/***** shouldn't have to change anything below here *****/ + +]) + +CLEAN_MK="${srcdir}/Config/clean.mk" +CONFIG_MK="${srcdir}/Config/config.mk" +dnl defs.mk is in the build tree, not the source tree +DEFS_MK="Config/defs.mk" +VERSION_MK="${srcdir}/Config/version.mk" + +AC_SUBST_FILE(CLEAN_MK)dnl +AC_SUBST_FILE(CONFIG_MK)dnl +AC_SUBST_FILE(DEFS_MK)dnl +AC_SUBST_FILE(VERSION_MK)dnl + +AC_CONFIG_FILES(Config/defs.mk Makefile Src/Makefile Test/Makefile) +AC_CONFIG_COMMANDS([config.modules], [. ./config.modules.sh]) +AC_CONFIG_COMMANDS([stamp-h], [echo >stamp-h]) + +AC_OUTPUT + +eval "zshbin1=${bindir}" +eval "zshbin2=${zshbin1}" +eval "zshman1=${mandir}" +eval "zshman2=${zshman1}" +eval "zshinfo1=${infodir}" +eval "zshinfo2=${zshinfo1}" +eval "zshfndir1=${fndir}" +eval "zshfndir2=${zshfndir1}" + +echo " +zsh configuration +----------------- +zsh version : ${VERSION} +host operating system : ${host_cpu}-${host_vendor}-${host_os} +source code location : ${srcdir} +compiler : ${CC} +preprocessor flags : ${CPPFLAGS} +executable compiler flags : ${CFLAGS}" +if test "x$dynamic" = xyes; then + echo "\ +module compiler flags : ${CFLAGS} ${DLCFLAGS}" +fi +echo "\ +executable linker flags : ${LDFLAGS} ${EXELDFLAGS} ${EXTRA_LDFLAGS}" +if test "x$dynamic" = xyes; then + echo "\ +module linker flags : ${LDFLAGS} ${LIBLDFLAGS} ${DLLDFLAGS}" +fi +echo "\ +library flags : ${LIBS} +installation basename : ${tzsh_name} +binary install path : ${zshbin2} +man page install path : ${zshman2} +info install path : ${zshinfo2}" +if test "$zshfndir2" != no; then + echo "functions install path : ${zshfndir2}" +fi +if test "x$additionalfpath" != x; then + echo "additional fpath entries : ${additionalfpath}" +fi +echo "See config.modules for installed modules and functions. +" + +case x$LIBS in + *-lgdbm*) + echo "WARNING: zsh will be linked against libgdbm. +This means the binary is covered by the GNU General Public License. +This does not affect the source code. +Run configure with --disable-gdbm if required." + ;; +esac + +exit 0 diff --git a/dotfiles/system/.zsh/modules/copy_from_zsh_src.zsh b/dotfiles/system/.zsh/modules/copy_from_zsh_src.zsh new file mode 100755 index 0000000..89eee01 --- /dev/null +++ b/dotfiles/system/.zsh/modules/copy_from_zsh_src.zsh @@ -0,0 +1,29 @@ +#!/usr/bin/env zsh + +[[ -z "$1" || "$1" = "-h" || "$1" = "--help" ]] && { print "Single argument: path to Zsh source tree"; exit 0; } + +print "Will invoke git clean -dxf, 3 seconds" +sleep 3 + +git clean -dxf + +[[ ! -d "$1" ]] && { print "Path to Zsh source doesn't exist (i.e.: $1)"; exit 1; } + +local from="$1" + +autoload -Uz colors +colors + +integer count=0 + +for i in configure.ac Src/*.c Src/*.h; do + if [[ -f "$from/$i" ]]; then + cp -vf "$from/$i" "$i" && (( ++ count )) || print "${fg_bold[red]}Copy error for: $i${reset_color}" + else + print "${fg[red]}$i Doesn't exist${reset_color}" + fi +done + +echo "${fg[green]}Copied ${fg[yellow]}$count${fg[green]} files${reset_color}" + +patch -p2 -i ./patch_cfgac.diff diff --git a/dotfiles/system/.zsh/modules/install-sh b/dotfiles/system/.zsh/modules/install-sh new file mode 100755 index 0000000..4fbbae7 --- /dev/null +++ b/dotfiles/system/.zsh/modules/install-sh @@ -0,0 +1,507 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2006-10-14.15 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +posix_glob= +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chmodcmd=$chmodprog +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + shift + shift + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac +done + +if test $# -ne 0 && test -z "$dir_arg$dstarg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + trap '(exit $?); exit' 1 2 13 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writeable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix=/ ;; + -*) prefix=./ ;; + *) prefix= ;; + esac + + case $posix_glob in + '') + if (set -f) 2>/dev/null; then + posix_glob=true + else + posix_glob=false + fi ;; + esac + + oIFS=$IFS + IFS=/ + $posix_glob && set -f + set fnord $dstdir + shift + $posix_glob && set +f + IFS=$oIFS + + prefixes= + + for d + do + test -z "$d" && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dst"; then + $doit $rmcmd -f "$dst" 2>/dev/null \ + || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null \ + && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }; }\ + || { + echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + } || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/dotfiles/system/.zsh/modules/mkinstalldirs b/dotfiles/system/.zsh/modules/mkinstalldirs new file mode 100755 index 0000000..1c3d072 --- /dev/null +++ b/dotfiles/system/.zsh/modules/mkinstalldirs @@ -0,0 +1,162 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy + +scriptversion=2006-05-11.19 + +# Original author: Noah Friedman +# Created: 1993-05-16 +# Public domain. +# +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' +IFS=" "" $nl" +errstatus=0 +# Default directory mode for all zsh files is world read- and executable +dirmode=755 + +usage="\ +Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ... + +Create each directory DIR (with mode MODE, if specified), including all +leading file name components. + +Report bugs to ." + +# process command line arguments +while test $# -gt 0 ; do + case $1 in + -h | --help | --h*) # -h for help + echo "$usage" + exit $? + ;; + -m) # -m PERM arg + shift + test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } + dirmode=$1 + shift + ;; + --version) + echo "$0 $scriptversion" + exit $? + ;; + --) # stop option processing + shift + break + ;; + -*) # unknown option + echo "$usage" 1>&2 + exit 1 + ;; + *) # first non-opt arg + break + ;; + esac +done + +for file +do + if test -d "$file"; then + shift + else + break + fi +done + +case $# in + 0) exit 0 ;; +esac + +# Solaris 8's mkdir -p isn't thread-safe. If you mkdir -p a/b and +# mkdir -p a/c at the same time, both will detect that a is missing, +# one will create a, then the other will try to create a and die with +# a "File exists" error. This is a problem when calling mkinstalldirs +# from a parallel make. We use --version in the probe to restrict +# ourselves to GNU mkdir, which is thread-safe. +case $dirmode in + '') + if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + echo "mkdir -p -- $*" + exec mkdir -p -- "$@" + else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + test -d ./-p && rmdir ./-p + test -d ./--version && rmdir ./--version + fi + ;; + *) + if mkdir -m "$dirmode" -p --version . >/dev/null 2>&1 && + test ! -d ./--version; then + echo "mkdir -m $dirmode -p -- $*" + exec mkdir -m "$dirmode" -p -- "$@" + else + # Clean up after NextStep and OpenStep mkdir. + for d in ./-m ./-p ./--version "./$dirmode"; + do + test -d $d && rmdir $d + done + fi + ;; +esac + +for file +do + case $file in + /*) pathcomp=/ ;; + *) pathcomp= ;; + esac + oIFS=$IFS + IFS=/ + set fnord $file + shift + IFS=$oIFS + + for d + do + test "x$d" = x && continue + + pathcomp=$pathcomp$d + case $pathcomp in + -*) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + else + if test ! -z "$dirmode"; then + echo "chmod $dirmode $pathcomp" + lasterr= + chmod "$dirmode" "$pathcomp" || lasterr=$? + + if test ! -z "$lasterr"; then + errstatus=$lasterr + fi + fi + fi + fi + + pathcomp=$pathcomp/ + done +done + +exit $errstatus + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/dotfiles/system/.zsh/modules/patch_cfgac.diff b/dotfiles/system/.zsh/modules/patch_cfgac.diff new file mode 100644 index 0000000..50624bc --- /dev/null +++ b/dotfiles/system/.zsh/modules/patch_cfgac.diff @@ -0,0 +1,73 @@ +diff --git a/module/configure.ac b/module/configure.ac +index 298af02..b116b80 100644 +--- a/module/configure.ac ++++ b/module/configure.ac +@@ -437,11 +437,6 @@ fi], + [AC_DEFINE(DEFAULT_READNULLCMD,"more")] + ) + +-dnl Do you want to look for pcre support? +-AC_ARG_ENABLE(pcre, +-AC_HELP_STRING([--enable-pcre], +-[enable the search for the pcre library (may create run-time library dependencies)])) +- + dnl Do you want to look for capability support? + AC_ARG_ENABLE(cap, + AC_HELP_STRING([--enable-cap], +@@ -672,16 +667,6 @@ AC_HEADER_STAT + AC_HEADER_SYS_WAIT + + oldcflags="$CFLAGS" +-if test x$enable_pcre = xyes; then +-AC_CHECK_PROG([PCRECONF], pcre-config, pcre-config) +-dnl Typically (meaning on this single RedHat 9 box in front of me) +-dnl pcre-config --cflags produces a -I output which needs to go into +-dnl CPPFLAGS else configure's preprocessor tests don't pick it up, +-dnl producing a warning. +-if test "x$ac_cv_prog_PCRECONF" = xpcre-config; then +- CPPFLAGS="$CPPFLAGS `pcre-config --cflags`" +-fi +-fi + + AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \ + termios.h sys/param.h sys/filio.h string.h memory.h \ +@@ -689,7 +674,7 @@ AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \ + locale.h errno.h stdio.h stdarg.h varargs.h stdlib.h \ + unistd.h sys/capability.h \ + utmp.h utmpx.h sys/types.h pwd.h grp.h poll.h sys/mman.h \ +- netinet/in_systm.h pcre.h langinfo.h wchar.h stddef.h \ ++ netinet/in_systm.h langinfo.h wchar.h stddef.h \ + sys/stropts.h iconv.h ncurses.h ncursesw/ncurses.h \ + ncurses/ncurses.h) + if test x$dynamic = xyes; then +@@ -932,12 +917,6 @@ if test "x$ac_found_iconv" = "xyes"; then + [Define as const if the declaration of iconv() needs const.]) + fi + +-if test x$enable_pcre = xyes; then +-dnl pcre-config should probably be employed here +-dnl AC_SEARCH_LIBS(pcre_compile, pcre) +- LIBS="`$ac_cv_prog_PCRECONF --libs` $LIBS" +-fi +- + dnl --------------------- + dnl CHECK TERMCAP LIBRARY + dnl --------------------- +@@ -1311,7 +1290,6 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \ + pathconf sysconf \ + tgetent tigetflag tigetnum tigetstr setupterm initscr resize_term \ + getcchar setcchar waddwstr wget_wch win_wch use_default_colors \ +- pcre_compile pcre_study pcre_exec \ + nl_langinfo \ + erand48 open_memstream \ + posix_openpt \ +@@ -3278,8 +3256,7 @@ AC_SUBST_FILE(CONFIG_MK)dnl + AC_SUBST_FILE(DEFS_MK)dnl + AC_SUBST_FILE(VERSION_MK)dnl + +-AC_CONFIG_FILES(Config/defs.mk Makefile Doc/Makefile Etc/Makefile \ +-Src/Makefile Test/Makefile) ++AC_CONFIG_FILES(Config/defs.mk Makefile Src/Makefile Test/Makefile) + AC_CONFIG_COMMANDS([config.modules], [. ./config.modules.sh]) + AC_CONFIG_COMMANDS([stamp-h], [echo >stamp-h]) + diff --git a/dotfiles/system/.zsh/modules/stamp-h.in b/dotfiles/system/.zsh/modules/stamp-h.in new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/dotfiles/system/.zsh/modules/stamp-h.in @@ -0,0 +1 @@ + -- cgit v1.2.3