diff options
-rw-r--r-- | Functions/Misc/is-at-least | 2 | ||||
-rw-r--r-- | Functions/Misc/zrecompile | 13 | ||||
-rw-r--r-- | Functions/Zftp/zfcd_match | 12 | ||||
-rw-r--r-- | Functions/Zftp/zfget_match | 31 | ||||
-rw-r--r-- | Functions/Zle/incremental-complete-word | 187 | ||||
-rw-r--r-- | Functions/Zle/predict-on | 119 | ||||
-rw-r--r-- | Misc/make-zsh-urls | 2 | ||||
-rw-r--r-- | Src/Modules/parameter.c | 1699 | ||||
-rw-r--r-- | Src/Modules/zpty.c | 153 | ||||
-rw-r--r-- | Src/Zle/comp.h | 449 | ||||
-rw-r--r-- | Src/Zle/compcore.c | 1500 | ||||
-rw-r--r-- | Src/Zle/complete.c | 436 | ||||
-rw-r--r-- | Src/Zle/compmatch.c | 124 | ||||
-rw-r--r-- | Src/Zle/compresult.c | 515 | ||||
-rw-r--r-- | Src/Zle/computil.c | 1498 | ||||
-rw-r--r-- | Src/Zle/iwidgets.list | 43 | ||||
-rw-r--r-- | Src/Zle/zle_utils.c | 92 | ||||
-rw-r--r-- | Src/builtin.c | 1512 | ||||
-rw-r--r-- | Src/cond.c | 326 | ||||
-rw-r--r-- | Src/exec.c | 1882 | ||||
-rw-r--r-- | Src/hashtable.c | 506 | ||||
-rw-r--r-- | Src/jobs.c | 347 | ||||
-rw-r--r-- | Src/loop.c | 380 | ||||
-rw-r--r-- | Src/params.c | 1818 | ||||
-rw-r--r-- | Src/parse.c | 2753 | ||||
-rw-r--r-- | Src/pattern.c | 1116 | ||||
-rw-r--r-- | Src/text.c | 858 | ||||
-rw-r--r-- | Src/zsh.h | 964 | ||||
-rw-r--r-- | Test/07cond.ztst | 35 | ||||
-rw-r--r-- | Test/11glob.ztst | 19 | ||||
-rw-r--r-- | Test/53completion.ztst | 13 |
31 files changed, 13788 insertions, 5616 deletions
diff --git a/Functions/Misc/is-at-least b/Functions/Misc/is-at-least index b574c154f..3eb62ef67 100644 --- a/Functions/Misc/is-at-least +++ b/Functions/Misc/is-at-least @@ -1,4 +1,4 @@ -# $Id: is-at-least,v 1.1 2000/02/11 19:46:46 akr Exp $ -*- shell-script -*- +# $Id: is-at-least,v 1.2 2000/04/01 20:49:47 pws Exp $ -*- shell-script -*- # # Test whether $ZSH_VERSION (or some value of your choice, if a second argument # is provided) is greater than or equal to x.y.z-r (in argument one). In fact, diff --git a/Functions/Misc/zrecompile b/Functions/Misc/zrecompile index 70410a580..1cdd05e0e 100644 --- a/Functions/Misc/zrecompile +++ b/Functions/Misc/zrecompile @@ -18,8 +18,8 @@ # seperated by `--'. For example: # # zrecompile -p \ -# -r ~/.zshrc -- \ -# -m ~/.zcompdump -- \ +# -R ~/.zshrc -- \ +# -M ~/.zcompdump -- \ # ~/zsh/comp.zwc ~/zsh/Completion/*/_* \ # # This makes ~/.zshrc be compiled into ~/.zshrc.zwc if that doesn't @@ -33,8 +33,7 @@ # that needed re-compilation could be compiled and non-zero if compilation # for at least one of the files failed. -emulate -L zsh -setopt extendedglob +setopt localoptions extendedglob local opt check quiet zwc files re file pre ret map tmp mesg pats @@ -68,7 +67,7 @@ if [[ -n $pats ]]; then fi files=( ${files:#*(.zwc|~)} ) - if [[ $files[1] = -[rm] ]]; then + if [[ $files[1] = -[RM] ]]; then map=( $files[1] ) shift 1 files else @@ -146,10 +145,10 @@ for zwc; do # See if the wordcode file will be mapped. if [[ $files[1] = *\(mapped\)* ]]; then - map=-m + map=-M mesg='succeeded (old saved)' else - map=-r + map=-R mesg=succeeded fi diff --git a/Functions/Zftp/zfcd_match b/Functions/Zftp/zfcd_match index 67e719888..02a19af21 100644 --- a/Functions/Zftp/zfcd_match +++ b/Functions/Zftp/zfcd_match @@ -15,7 +15,7 @@ local tmpf=${TMPPREFIX}zfcm$$ if [[ $ZFTP_SYSTEM = UNIX* ]]; then # hoo, aren't we lucky: this makes things so much easier - setopt localoptions rcexpandparam + setopt rcexpandparam local dir if [[ $1 = ?*/* ]]; then dir=${1%/*} @@ -25,13 +25,15 @@ if [[ $ZFTP_SYSTEM = UNIX* ]]; then # If we're using -F, we get away with using a directory # to list, but not a glob. Don't ask me why. # I hate having to rely on awk here. - zftp ls -F $dir >$tmpf + zftp ls -LF $dir >$tmpf reply=($(awk '/\/$/ { print substr($1, 0, length($1)-1) }' $tmpf)) rm -f $tmpf - if [[ $dir = / ]]; then - reply=(${dir}$reply) + [[ -n $dir && $dir != */ ]] && dir="$dir/" + if [[ -n $WIDGET ]]; then + _all_labels directories expl 'remote directory' + compadd -S/ -q -P "$dir" - $reply elif [[ -n $dir ]]; then - reply=($dir/$reply) + reply=(${dir}$reply) fi else # I simply don't know what to do here. diff --git a/Functions/Zftp/zfget_match b/Functions/Zftp/zfget_match index 677108ede..0fe2bc06f 100644 --- a/Functions/Zftp/zfget_match +++ b/Functions/Zftp/zfget_match @@ -10,18 +10,27 @@ fi local tmpf=${TMPPREFIX}zfgm$$ if [[ $ZFTP_SYSTEM == UNIX* && $1 == */* ]]; then - # On the first argument to ls, we usually get away with a glob. - zftp ls "$1*$2" >$tmpf - reply=($(<$tmpf)) - rm -f $tmpf -else - if (( $#zftp_fcache == 0 )); then - # Always cache the current directory and use it - # even if the system is UNIX. - zftp ls >$tmpf - zftp_fcache=($(<$tmpf)) + if [[ -n $WIDGET ]]; then + local dir=${1:h} + [[ $dir = */ ]] || dir="$dir/" + zftp ls -LF $dir >$tmpf + local reply + reply=(${${${(f)"$(<$tmpf)"}##$dir}%\*}) + rm -f $tmpf + _all_labels files expl 'remote file' compadd -P $dir - $reply + else + # On the first argument to ls, we usually get away with a glob. + zftp ls "$1*$2" >$tmpf + reply=($(<$tmpf)) rm -f $tmpf fi - reply=($zftp_fcache); +else + local fcache_name + zffcache + if [[ -n $WIDGET ]]; then + _all_labels files expl 'remote file' compadd -F fignore - ${(P)fcache_name} + else + reply=(${(P)fcache_name}); + fi fi # } diff --git a/Functions/Zle/incremental-complete-word b/Functions/Zle/incremental-complete-word index 2a9c1aff2..e021bf6f7 100644 --- a/Functions/Zle/incremental-complete-word +++ b/Functions/Zle/incremental-complete-word @@ -1,89 +1,124 @@ -# incremental-complete-word() { - # Autoload this function, run `zle -N <func-name>' and bind <func-name> # to a key. + # This allows incremental completion of a word. After starting this -# command, a list of completion choices is shown after every character you -# type, which you can delete with ^h or DEL. RET will accept the -# completion so far. You can hit TAB to do normal completion and ^g to -# abort back to the state when you started. +# command, a list of completion choices can be shown after every character +# you type, which you can delete with ^h or DEL. RET will accept the +# completion so far. You can hit TAB to do normal completion, ^g to +# abort back to the state when you started, and ^d to list the matches. # -# Completion keys: -# incremental_prompt Prompt to show in status line during icompletion; -# the sequence `%u' is replaced by the unambiguous -# part of all matches if there is any and it is -# different from the word on the line -# incremental_stop Pattern matching keys which will cause icompletion -# to stop and the key to be re-executed -# incremental_break Pattern matching keys which will cause icompletion -# to stop and the key to be discarded -# incremental_completer Set of completers, like the `completer' key -# incremental_list If set to a non-empty string, the matches will be -# listed on every key-press - -emulate -L zsh -unsetopt autolist menucomplete automenu # doesn't work well - -local key lbuf="$LBUFFER" rbuf="$RBUFFER" pmpt word lastl lastr wid twid - -[[ -n "$compconfig[incremental_completer]" ]] && -set ${(s.:.)compconfig[incremental_completer]} -pmpt="${compconfig[incremental_prompt]-incremental completion...}" - -if [[ -n "$compconfig[incremental_list]" ]]; then - wid=list-choices -else - wid=complete-word -fi - -zle $wid "$@" -LBUFFER="$lbuf" -RBUFFER="$rbuf" -if [[ "${LBUFFER}${RBUFFER}" = *${_lastcomp[unambiguous]}* ]]; then - word='' -else - word="${_lastcomp[unambiguous]}" -fi -zle -R "${pmpt//\\%u/$word}" -read -k key - -while [[ '#key' -ne '#\\r' && '#key' -ne '#\\n' && - '#key' -ne '#\\C-g' ]]; do - twid=$wid - if [[ "$key" = ${~compconfig[incremental_stop]} ]]; then - zle -U "$key" - return - elif [[ "$key" = ${~compconfig[incremental_break]} ]]; then - return - elif [[ '#key' -eq '#\\C-h' || '#key' -eq '#\\C-?' ]]; then - [[ $#LBUFFER -gt $#l ]] && LBUFFER="$LBUFFER[1,-2]" - elif [[ '#key' -eq '#\\t' ]]; then - zle complete-word "$@" - lbuf="$LBUFFER" - rbuf="$RBUFFER" - elif [[ '#key' -eq '#\\C-d' ]]; then - twid=list-choices +# This works only with the new function based completion system. + +# The main widget function. + +incremental-complete-word() { + #emulate -L zsh + unsetopt autolist menucomplete automenu # doesn't work well + + local key lbuf="$LBUFFER" rbuf="$RBUFFER" pmpt pstr word + local lastl lastr wid twid num post toolong + local curcontext="${curcontext}" stop brk + + [[ -z "$curcontext" ]] && curcontext=::: + curcontext="incremental:${curcontext#*:}" + + zstyle -s ":completion:${curcontext}" prompt pmpt || + pmpt='incremental (%c): %u%s %l' + zstyle -s ":completion:${curcontext}" stop stop + zstyle -s ":completion:${curcontext}" break brk + + if zstyle -t ":completion:${curcontext}" list; then + wid=list-choices + post=( icw-list-helper ) else - LBUFFER="$LBUFFER$key" + wid=complete-word + post=() fi - lastl="$LBUFFER" - lastr="$RBUFFER" - zle $twid "$@" - LBUFFER="$lastl" - RBUFFER="$lastr" - if [[ "${LBUFFER}${RBUFFER}" = *${_lastcomp[unambiguous]}* ]]; then + + comppostfuncs=( "$post[@]" ) + zle $wid "$@" + LBUFFER="$lbuf" + RBUFFER="$rbuf" + num=$_lastcomp[nmatches] + if (( ! num )); then + word='' + state='-no match-' + elif [[ "${LBUFFER}${RBUFFER}" = *${_lastcomp[unambiguous]}* ]]; then word='' + state='-no prefix-' else word="${_lastcomp[unambiguous]}" + state='' fi - zle -R "${pmpt//\\%u/$word}" + zformat -f pstr "$pmpt" "u:${word}" "s:$state" "n:$num" \ + "l:$toolong" "c:${_lastcomp[completer][2,-1]}" + zle -R "$pstr" read -k key -done -if [[ '#key' -eq '#\\C-g' ]]; then - LBUFFER="$lbuf" - RBUFFER="$rbuf" -fi -zle -Rc -# } + while [[ '#key' -ne '#\\r' && '#key' -ne '#\\n' && + '#key' -ne '#\\C-g' ]]; do + twid=$wid + if [[ "$key" = ${~stop} ]]; then + zle -U "$key" + return + elif [[ "$key" = ${~brk} ]]; then + return + elif [[ '#key' -eq '#\\C-h' || '#key' -eq '#\\C-?' ]]; then + [[ $#LBUFFER -gt $#l ]] && LBUFFER="$LBUFFER[1,-2]" + elif [[ '#key' -eq '#\\t' ]]; then + zle complete-word "$@" + lbuf="$LBUFFER" + rbuf="$RBUFFER" + elif [[ '#key' -eq '#\\C-d' ]]; then + twid=list-choices + else + LBUFFER="$LBUFFER$key" + fi + lastl="$LBUFFER" + lastr="$RBUFFER" + [[ "$twid" = "$wid" ]] && comppostfuncs=( "$post[@]" ) + toolong='' + zle $twid "$@" + LBUFFER="$lastl" + RBUFFER="$lastr" + num=$_lastcomp[nmatches] + if (( ! num )); then + word='' + state='-no match-' + elif [[ "${LBUFFER}${RBUFFER}" = *${_lastcomp[unambiguous]}* ]]; then + word='' + state='-no prefix-' + else + word="${_lastcomp[unambiguous]}" + state='' + fi + zformat -f pstr "$pmpt" "u:${word}" "s:$state" "n:$num" \ + "l:$toolong" "c:${_lastcomp[completer][2,-1]}" + zle -R "$pstr" + read -k key + done + + if [[ '#key' -eq '#\\C-g' ]]; then + LBUFFER="$lbuf" + RBUFFER="$rbuf" + fi + zle -Rc +} + +# Helper function used as a completion post-function used to make sure that +# the list of matches in only shown if it fits on the screen. + +icw-list-helper() { + + # +1 for the status line we will add... + + if [[ compstate[list_lines]+BUFFERLINES+1 -gt LINES ]]; then + compstate[list]='list explanations' + [[ compstate[list_lines]+BUFFERLINES+1 -gt LINES ]] && compstate[list]='' + + toolong='...' + fi +} + +incremental-complete-word "$@" diff --git a/Functions/Zle/predict-on b/Functions/Zle/predict-on index 07ce0703a..bd7212050 100644 --- a/Functions/Zle/predict-on +++ b/Functions/Zle/predict-on @@ -1,64 +1,141 @@ # This set of functions implements a sort of magic history searching. # After predict-on, typing characters causes the editor to look backward -# in the history for the first line beginning with what you have typed -# so far. After predict-off, editing returns to normal for the line found. +# in the history for the first line beginning with what you have typed so +# far. After predict-off, editing returns to normal for the line found. # In fact, you often don't even need to use predict-off, because if the -# line doesn't match something in the history, adding a key at the end -# behaves as normal --- though editing in the middle is liable to delete +# line doesn't match something in the history, adding a key performs +# standard completion --- though editing in the middle is liable to delete # the rest of the line. # +# With the function based completion system (which is needed for this), +# you should be able to type TAB at almost any point to advance the cursor +# to the next "interesting" character position (usually the end of the +# current word, but sometimes somewhere in the middle of the word). And +# of course as soon as the entire line is what you want, you can accept +# with RETURN, without needing to move the cursor to the end first. +# # To use it: # autoload -U predict-on # zle -N predict-on # zle -N predict-off # bindkey '...' predict-on # bindkey '...' predict-off -# Note that all the functions are defined when you first call type the -# predict-on key, which means typing the predict-off key before that gives -# a harmless error message. +# Note that all functions are defined when you first type the predict-on +# key, which means typing the predict-off key before that gives a harmless +# error message. predict-on() { - zle -N self-insert insert-and-predict - zle -N magic-space insert-and-predict - zle -N backward-delete-char delete-backward-and-predict + zle -N self-insert insert-and-predict + zle -N magic-space insert-and-predict + zle -N backward-delete-char delete-backward-and-predict + zle -N delete-char-or-list delete-no-predict } predict-off() { - zle -A .self-insert self-insert - zle -A .magic-space magic-space - zle -A .backward-delete-char backward-delete-char + zle -A .self-insert self-insert + zle -A .magic-space magic-space + zle -A .backward-delete-char backward-delete-char } insert-and-predict () { - emulate -L zsh - if [[ ${RBUFFER[1]} = ${KEYS[-1]} ]] + setopt localoptions noshwordsplit noksharrays + if [[ $LBUFFER = *$'\012'* ]] + then + # Editing a multiline buffer, it's unlikely prediction is wanted + zle .$WIDGET "$@" + return + elif [[ ${RBUFFER[1]} = ${KEYS[-1]} ]] then - # same as what's typed, just move on + # Same as what's typed, just move on ((++CURSOR)) else LBUFFER="$LBUFFER$KEYS" if [[ $LASTWIDGET == (self-insert|magic-space|backward-delete-char) ]] then - zle .history-beginning-search-backward || RBUFFER="" + if ! zle .history-beginning-search-backward + then + RBUFFER="" + if [[ ${KEYS[-1]} != ' ' ]] + then + unsetopt automenu recexact + integer curs=$CURSOR pos nchar=${#LBUFFER//[^${KEYS[-1]}]} + local -a +h comppostfuncs + local crs curcontext="${curcontext}" + + [[ -z "$curcontext" ]] && curcontext=::: + curcontext="predict:${curcontext#*:}" + + comppostfuncs=( predict-limit-list ) + zle complete-word + # Decide where to leave the cursor. The dummy loop is used to + # get out of that `case'. + repeat 1 + do + zstyle -s ":completion:${curcontext}:" cursor crs + case $crs in + (complete) + # At the place where the completion left it, if it is after + # the character typed. + [[ ${LBUFFER[-1]} = ${KEYS[-1]} ]] && break + ;& + (key) + # Or maybe at the n'th occurrence of the character typed. + pos=${BUFFER[(in:nchar:)${KEYS[-1]}]} + if [[ pos -gt curs ]] + then + CURSOR=$pos + break + fi + ;& + (*) + # Or else at the previous position. + CURSOR=$curs + esac + done + fi + fi fi fi return 0 } delete-backward-and-predict() { - emulate -L zsh if [[ -n "$LBUFFER" ]] then + setopt localoptions noshwordsplit noksharrays + if [[ $LBUFFER = *$'\012'* ]] then + # Editing a multiline buffer, it's unlikely prediction is wanted + zle .$WIDGET "$@" # If the last widget was e.g. a motion, then probably the intent is # to actually edit the line, not change the search prefix. - if [[ $LASTWIDGET == (self-insert|magic-space|backward-delete-char) ]] + elif [[ $LASTWIDGET == (self-insert|magic-space|backward-delete-char) ]] then ((--CURSOR)) zle .history-beginning-search-forward || RBUFFER="" return 0 else - # Depending on preference, you might call "predict-off" here, - # and also set up forward deletions to turn off prediction. + # Depending on preference, you might call "predict-off" here. LBUFFER="$LBUFFER[1,-2]" fi fi } +delete-no-predict() { + [[ $WIDGET != delete-char-or-list || -n $RBUFFER ]] && predict-off + zle .$WIDGET "$@" +} + +# This is a helper function for autocompletion to prevent long lists +# of matches from forcing a "do you wish to see all ...?" prompt. + +predict-limit-list() { + if (( compstate[list_lines]+BUFFERLINES > LINES || + ( compstate[list_max] != 0 && + compstate[nmatches] > compstate[list_max] ) )) + then + compstate[list]='' + elif zstyle -t ":completion:predict::::" list always + then + compstate[list]='force list' + fi +} + +# Handle zsh autoloading conventions [[ -o kshautoload ]] || predict-on "$@" diff --git a/Misc/make-zsh-urls b/Misc/make-zsh-urls index 35bbf9fbb..b74b8334e 100644 --- a/Misc/make-zsh-urls +++ b/Misc/make-zsh-urls @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# $Id: make-zsh-urls,v 1.1 1999/11/09 02:36:42 akr Exp $ +# $Id: make-zsh-urls,v 1.2 2000/04/01 20:49:47 pws Exp $ use strict; diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index 2257933f5..e8e387a59 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -30,6 +30,10 @@ #include "parameter.mdh" #include "parameter.pro" +/* This says if we are cleaning up when the module is unloaded. */ + +static int incleanup; + /* Empty dummy function for special hash parameters. */ /**/ @@ -47,14 +51,14 @@ createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan) Param pm; HashTable ht; - if (!(pm = createparam(name, PM_SPECIAL|PM_REMOVABLE|PM_HASHED))) + if (!(pm = createparam(name, PM_SPECIAL|PM_HIDE|PM_REMOVABLE|PM_HASHED))) return NULL; pm->level = pm->old ? locallevel : 0; pm->gets.hfn = hashgetfn; pm->sets.hfn = hashsetfn; pm->unsetfn = stdunsetfn; - pm->u.hash = ht = newhashtable(7, name, NULL); + pm->u.hash = ht = newhashtable(0, name, NULL); ht->hash = hasher; ht->emptytable = (TableFunc) shempty; @@ -83,14 +87,21 @@ paramtypestr(Param pm) int f = pm->flags; if (!(f & PM_UNSET)) { + if (pm->flags & PM_AUTOLOAD) + return dupstring("undefined"); + switch (PM_TYPE(f)) { case PM_SCALAR: val = "scalar"; break; case PM_ARRAY: val = "array"; break; case PM_INTEGER: val = "integer"; break; + case PM_EFLOAT: + case PM_FFLOAT: val = "float"; break; case PM_HASHED: val = "association"; break; } DPUTS(!val, "BUG: type not handled in parameter"); val = dupstring(val); + if (pm->level) + val = dyncat(val, "-local"); if (f & PM_LEFT) val = dyncat(val, "-left"); if (f & PM_RIGHT_B) @@ -109,6 +120,10 @@ paramtypestr(Param pm) val = dyncat(val, "-export"); if (f & PM_UNIQUE) val = dyncat(val, "-unique"); + if (f & PM_HIDE) + val = dyncat(val, "-hide"); + if (f & PM_SPECIAL) + val = dyncat(val, "-special"); } else val = dupstring(""); @@ -121,27 +136,24 @@ getpmparameter(HashTable ht, char *name) { Param rpm, pm = NULL; - HEAPALLOC { - pm = (Param) zhalloc(sizeof(struct param)); - pm->nam = dupstring(name); - pm->flags = PM_SCALAR | PM_READONLY; - pm->sets.cfn = NULL; - pm->gets.cfn = strgetfn; - pm->unsetfn = NULL; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; - if ((rpm = (Param) realparamtab->getnode(realparamtab, name)) && - !(rpm->flags & PM_UNSET)) - pm->u.str = paramtypestr(rpm); - else { - pm->u.str = ""; - pm->flags |= PM_UNSET; - } - } LASTALLOC; - + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if ((rpm = (Param) realparamtab->getnode(realparamtab, name)) && + !(rpm->flags & PM_UNSET)) + pm->u.str = paramtypestr(rpm); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } return (HashNode) pm; } @@ -166,7 +178,9 @@ scanpmparameters(HashTable ht, ScanFunc func, int flags) for (i = 0; i < realparamtab->hsize; i++) for (hn = realparamtab->nodes[i]; hn; hn = hn->next) { pm.nam = hn->nam; - if (func != scancountparams) + if (func != scancountparams && + ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(flags & SCANPM_WANTKEYS))) pm.u.str = paramtypestr((Param) hn); func((HashNode) &pm, flags); } @@ -179,12 +193,12 @@ static void setpmcommand(Param pm, char *value) { if (isset(RESTRICTED)) - zwarnnam(NULL, "restricted: %s", value, 0); + zwarn("restricted: %s", value, 0); else { Cmdnam cn = zcalloc(sizeof(*cn)); cn->flags = HASHED; - cn->u.cmd = ztrdup(value); + cn->u.cmd = value; cmdnamtab->addnode(cmdnamtab, ztrdup(pm->nam), (HashNode) cn); } @@ -207,6 +221,9 @@ setpmcommands(Param pm, HashTable ht) int i; HashNode hn; + if (!ht) + return; + for (i = 0; i < ht->hsize; i++) for (hn = ht->nodes[i]; hn; hn = hn->next) { Cmdnam cn = zcalloc(sizeof(*cn)); @@ -222,6 +239,7 @@ setpmcommands(Param pm, HashTable ht) cmdnamtab->addnode(cmdnamtab, ztrdup(hn->nam), (HashNode) cn); } + deleteparamtable(ht); } /**/ @@ -236,34 +254,30 @@ getpmcommand(HashTable ht, char *name) cmdnamtab->filltable(cmdnamtab); cmd = (Cmdnam) cmdnamtab->getnode(cmdnamtab, name); } - HEAPALLOC { - pm = (Param) zhalloc(sizeof(struct param)); - pm->nam = dupstring(name); - pm->flags = PM_SCALAR; - pm->sets.cfn = setpmcommand; - pm->gets.cfn = strgetfn; - pm->unsetfn = unsetpmcommand; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; - if (cmd) { - if (cmd->flags & HASHED) - pm->u.str = cmd->u.cmd; - else { - pm->u.str = zhalloc(strlen(*(cmd->u.name)) + - strlen(name) + 2); - strcpy(pm->u.str, *(cmd->u.name)); - strcat(pm->u.str, "/"); - strcat(pm->u.str, name); - } - } else { - pm->u.str = ""; - pm->flags |= PM_UNSET; + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR; + pm->sets.cfn = setpmcommand; + pm->gets.cfn = strgetfn; + pm->unsetfn = unsetpmcommand; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if (cmd) { + if (cmd->flags & HASHED) + pm->u.str = cmd->u.cmd; + else { + pm->u.str = zhalloc(strlen(*(cmd->u.name)) + strlen(name) + 2); + strcpy(pm->u.str, *(cmd->u.name)); + strcat(pm->u.str, "/"); + strcat(pm->u.str, name); } - } LASTALLOC; - + } else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } return (HashNode) pm; } @@ -293,7 +307,9 @@ scanpmcommands(HashTable ht, ScanFunc func, int flags) for (hn = cmdnamtab->nodes[i]; hn; hn = hn->next) { pm.nam = hn->nam; cmd = (Cmdnam) hn; - if (func != scancountparams) { + if (func != scancountparams && + ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(flags & SCANPM_WANTKEYS))) { if (cmd->flags & HASHED) pm.u.str = cmd->u.cmd; else { @@ -312,43 +328,37 @@ scanpmcommands(HashTable ht, ScanFunc func, int flags) /**/ static void -setfunction(char *name, char *value) +setfunction(char *name, char *val, int dis) { - char *val; + char *value = dupstring(val); Shfunc shf; - List list; + Eprog prog; int sn; - val = ztrdup(value); val = metafy(val, strlen(val), META_REALLOC); - HEAPALLOC { - list = parse_string(val); - } LASTALLOC; + prog = parse_string(val, 1); - if (!list || list == &dummy_list) { - zwarnnam(NULL, "invalid function definition", val, 0); + if (!prog || prog == &dummy_eprog) { + zwarn("invalid function definition", value, 0); zsfree(val); return; } - PERMALLOC { - shf = (Shfunc) zalloc(sizeof(*shf)); - shf->funcdef = (List) dupstruct(list); - shf->flags = 0; - - if (!strncmp(name, "TRAP", 4) && - (sn = getsignum(name + 4)) != -1) { - if (settrap(sn, shf->funcdef)) { - freestruct(shf->funcdef); - zfree(shf, sizeof(*shf)); - zsfree(val); - LASTALLOC_RETURN; - } - sigtrapped[sn] |= ZSIG_FUNC; + shf = (Shfunc) zalloc(sizeof(*shf)); + shf->funcdef = dupeprog(prog, 0); + shf->flags = dis; + + if (!strncmp(name, "TRAP", 4) && + (sn = getsignum(name + 4)) != -1) { + if (settrap(sn, shf->funcdef)) { + freeeprog(shf->funcdef); + zfree(shf, sizeof(*shf)); + zsfree(val); + return; } - shfunctab->addnode(shfunctab, ztrdup(name), shf); - } LASTALLOC; - + sigtrapped[sn] |= ZSIG_FUNC; + } + shfunctab->addnode(shfunctab, ztrdup(name), shf); zsfree(val); } @@ -356,7 +366,14 @@ setfunction(char *name, char *value) static void setpmfunction(Param pm, char *value) { - setfunction(pm->nam, value); + setfunction(pm->nam, value, 0); +} + +/**/ +static void +setpmdisfunction(Param pm, char *value) +{ + setfunction(pm->nam, value, DISABLED); } /**/ @@ -371,11 +388,14 @@ unsetpmfunction(Param pm, int exp) /**/ static void -setpmfunctions(Param pm, HashTable ht) +setfunctions(Param pm, HashTable ht, int dis) { int i; HashNode hn; + if (!ht) + return; + for (i = 0; i < ht->hsize; i++) for (hn = ht->nodes[i]; hn; hn = hn->next) { struct value v; @@ -385,62 +405,100 @@ setpmfunctions(Param pm, HashTable ht) v.arr = NULL; v.pm = (Param) hn; - setfunction(hn->nam, getstrvalue(&v)); + setfunction(hn->nam, ztrdup(getstrvalue(&v)), dis); } + deleteparamtable(ht); +} + +/**/ +static void +setpmfunctions(Param pm, HashTable ht) +{ + setfunctions(pm, ht, 0); +} + +/**/ +static void +setpmdisfunctions(Param pm, HashTable ht) +{ + setfunctions(pm, ht, DISABLED); } /**/ static HashNode -getpmfunction(HashTable ht, char *name) +getfunction(HashTable ht, char *name, int dis) { Shfunc shf; Param pm = NULL; - HEAPALLOC { - pm = (Param) zhalloc(sizeof(struct param)); - pm->nam = dupstring(name); - pm->flags = PM_SCALAR; - pm->sets.cfn = setpmfunction; - pm->gets.cfn = strgetfn; - pm->unsetfn = unsetpmfunction; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; - - if ((shf = (Shfunc) shfunctab->getnode(shfunctab, name))) { - if (shf->flags & PM_UNDEFINED) - pm->u.str = "undefined"; - else { - char *t = getpermtext((void *) dupstruct((void *) - shf->funcdef)), *h; - - h = dupstring(t); - zsfree(t); - unmetafy(h, NULL); - - pm->u.str = h; - } + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR; + pm->sets.cfn = (dis ? setpmdisfunction : setpmfunction); + pm->gets.cfn = strgetfn; + pm->unsetfn = unsetpmfunction; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + + if ((shf = (Shfunc) shfunctab->getnode2(shfunctab, name)) && + (dis ? (shf->flags & DISABLED) : !(shf->flags & DISABLED))) { + if (shf->flags & PM_UNDEFINED) { + pm->u.str = dyncat("builtin autoload -X", + ((shf->flags & PM_UNALIASED) ? + ((shf->flags & PM_TAGGED) ? "Ut" : "U") : + ((shf->flags & PM_TAGGED) ? "t" : ""))); } else { - pm->u.str = ""; - pm->flags |= PM_UNSET; + char *t = getpermtext(shf->funcdef, NULL), *n, *h; + + if (shf->funcdef->flags & EF_RUN) { + n = nicedupstring(name); + h = (char *) zhalloc(strlen(t) + strlen(n) + 9); + h[0] = '\t'; + strcpy(h + 1, t); + strcat(h, "\n\t"); + strcat(h, n); + strcat(h, " \"$@\""); + } else + h = dyncat("\t", t); + zsfree(t); + unmetafy(h, NULL); + + pm->u.str = h; } - } LASTALLOC; - + } else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } return (HashNode) pm; } /**/ +static HashNode +getpmfunction(HashTable ht, char *name) +{ + return getfunction(ht, name, 0); +} + +/**/ +static HashNode +getpmdisfunction(HashTable ht, char *name) +{ + return getfunction(ht, name, DISABLED); +} + +/**/ static void -scanpmfunctions(HashTable ht, ScanFunc func, int flags) +scanfunctions(HashTable ht, ScanFunc func, int flags, int dis) { struct param pm; int i; HashNode hn; pm.flags = PM_SCALAR; - pm.sets.cfn = setpmcommand; + pm.sets.cfn = (dis ? setpmdisfunction : setpmfunction); pm.gets.cfn = strgetfn; pm.unsetfn = unsetpmcommand; pm.ct = 0; @@ -451,16 +509,32 @@ scanpmfunctions(HashTable ht, ScanFunc func, int flags) for (i = 0; i < shfunctab->hsize; i++) for (hn = shfunctab->nodes[i]; hn; hn = hn->next) { - if (!(hn->flags & DISABLED)) { + if (dis ? (hn->flags & DISABLED) : !(hn->flags & DISABLED)) { pm.nam = hn->nam; - if (func != scancountparams) { - if (((Shfunc) hn)->flags & PM_UNDEFINED) - pm.u.str = "undefined"; - else { - char *t = getpermtext((void *) - dupstruct((void *) ((Shfunc) hn)->funcdef)); - - unmetafy((pm.u.str = dupstring(t)), NULL); + if (func != scancountparams && + ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(flags & SCANPM_WANTKEYS))) { + if (((Shfunc) hn)->flags & PM_UNDEFINED) { + Shfunc shf = (Shfunc) hn; + pm.u.str = + dyncat("builtin autoload -X", + ((shf->flags & PM_UNALIASED) ? + ((shf->flags & PM_TAGGED) ? "Ut" : "U") : + ((shf->flags & PM_TAGGED) ? "t" : ""))); + } else { + char *t = getpermtext(((Shfunc) hn)->funcdef, NULL), *n; + + if (((Shfunc) hn)->funcdef->flags & EF_RUN) { + n = nicedupstring(hn->nam); + pm.u.str = (char *) zhalloc(strlen(t) + strlen(n) + 9); + pm.u.str[0] = '\t'; + strcpy(pm.u.str + 1, t); + strcat(pm.u.str, "\n\t"); + strcat(pm.u.str, n); + strcat(pm.u.str, " \"$@\""); + } else + pm.u.str = dyncat("\t", t); + unmetafy(pm.u.str, NULL); zsfree(t); } } @@ -469,6 +543,173 @@ scanpmfunctions(HashTable ht, ScanFunc func, int flags) } } +/**/ +static void +scanpmfunctions(HashTable ht, ScanFunc func, int flags) +{ + scanfunctions(ht, func, flags, 0); +} + +/**/ +static void +scanpmdisfunctions(HashTable ht, ScanFunc func, int flags) +{ + scanfunctions(ht, func, flags, DISABLED); +} + +/* Functions for the funcstack special parameter. */ + +/**/ +static char ** +funcstackgetfn(Param pm) +{ + Funcstack f; + int num; + char **ret, **p; + + for (f = funcstack, num = 0; f; f = f->prev, num++); + + ret = (char **) zhalloc((num + 1) * sizeof(char *)); + + for (f = funcstack, p = ret; f; f = f->prev, p++) + *p = f->name; + *p = NULL; + + return ret; +} + +/* Functions for the builtins special parameter. */ + +/**/ +static HashNode +getbuiltin(HashTable ht, char *name, int dis) +{ + Param pm = NULL; + Builtin bn; + + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if ((bn = (Builtin) builtintab->getnode2(builtintab, name)) && + (dis ? (bn->flags & DISABLED) : !(bn->flags & DISABLED))) { + char *t = ((bn->handlerfunc || (bn->flags & BINF_PREFIX)) ? + "defined" : "undefined"); + + pm->u.str = dupstring(t); + } else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + return (HashNode) pm; +} + +/**/ +static HashNode +getpmbuiltin(HashTable ht, char *name) +{ + return getbuiltin(ht, name, 0); +} + +/**/ +static HashNode +getpmdisbuiltin(HashTable ht, char *name) +{ + return getbuiltin(ht, name, DISABLED); +} + +/**/ +static void +scanbuiltins(HashTable ht, ScanFunc func, int flags, int dis) +{ + struct param pm; + int i; + HashNode hn; + + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (i = 0; i < builtintab->hsize; i++) + for (hn = builtintab->nodes[i]; hn; hn = hn->next) { + if (dis ? (hn->flags & DISABLED) : !(hn->flags & DISABLED)) { + pm.nam = hn->nam; + if (func != scancountparams && + ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(flags & SCANPM_WANTKEYS))) { + char *t = ((((Builtin) hn)->handlerfunc || + (hn->flags & BINF_PREFIX)) ? + "defined" : "undefined"); + + pm.u.str = dupstring(t); + } + func((HashNode) &pm, flags); + } + } +} + +/**/ +static void +scanpmbuiltins(HashTable ht, ScanFunc func, int flags) +{ + scanbuiltins(ht, func, flags, 0); +} + +/**/ +static void +scanpmdisbuiltins(HashTable ht, ScanFunc func, int flags) +{ + scanbuiltins(ht, func, flags, DISABLED); +} + +/* Functions for the reswords special parameter. */ + +/**/ +static char ** +getreswords(int dis) +{ + int i; + HashNode hn; + char **ret, **p; + + p = ret = (char **) zhalloc((reswdtab->ct + 1) * sizeof(char *)); + + for (i = 0; i < reswdtab->hsize; i++) + for (hn = reswdtab->nodes[i]; hn; hn = hn->next) + if (dis ? (hn->flags & DISABLED) : !(hn->flags & DISABLED)) + *p++ = dupstring(hn->nam); + *p = NULL; + + return ret; +} + +/**/ +static char ** +reswordsgetfn(Param pm) +{ + return getreswords(0); +} + +/**/ +static char ** +disreswordsgetfn(Param pm) +{ + return getreswords(DISABLED); +} + /* Functions for the options special parameter. */ /**/ @@ -478,11 +719,12 @@ setpmoption(Param pm, char *value) int n; if (!value || (strcmp(value, "on") && strcmp(value, "off"))) - zwarnnam(NULL, "invalid value: %s", value, 0); + zwarn("invalid value: %s", value, 0); else if (!(n = optlookup(pm->nam))) - zwarnnam(NULL, "no such option: %s", pm->nam, 0); + zwarn("no such option: %s", pm->nam, 0); else if (dosetopt(n, (value && strcmp(value, "off")), 0)) - zwarnnam(NULL, "can't change option: %s", pm->nam, 0); + zwarn("can't change option: %s", pm->nam, 0); + zsfree(value); } /**/ @@ -492,9 +734,9 @@ unsetpmoption(Param pm, int exp) int n; if (!(n = optlookup(pm->nam))) - zwarnnam(NULL, "no such option: %s", pm->nam, 0); + zwarn("no such option: %s", pm->nam, 0); else if (dosetopt(n, 0, 0)) - zwarnnam(NULL, "can't change option: %s", pm->nam, 0); + zwarn("can't change option: %s", pm->nam, 0); } /**/ @@ -504,6 +746,9 @@ setpmoptions(Param pm, HashTable ht) int i; HashNode hn; + if (!ht) + return; + for (i = 0; i < ht->hsize; i++) for (hn = ht->nodes[i]; hn; hn = hn->next) { struct value v; @@ -516,11 +761,12 @@ setpmoptions(Param pm, HashTable ht) val = getstrvalue(&v); if (!val || (strcmp(val, "on") && strcmp(val, "off"))) - zwarnnam(NULL, "invalid value: %s", val, 0); + zwarn("invalid value: %s", val, 0); else if (dosetopt(optlookup(hn->nam), (val && strcmp(val, "off")), 0)) - zwarnnam(NULL, "can't change option: %s", hn->nam, 0); + zwarn("can't change option: %s", hn->nam, 0); } + deleteparamtable(ht); } /**/ @@ -530,27 +776,24 @@ getpmoption(HashTable ht, char *name) Param pm = NULL; int n; - HEAPALLOC { - pm = (Param) zhalloc(sizeof(struct param)); - pm->nam = dupstring(name); - pm->flags = PM_SCALAR; - pm->sets.cfn = setpmoption; - pm->gets.cfn = strgetfn; - pm->unsetfn = unsetpmoption; - pm->ct = 0; - pm->env = NULL; - pm->ename = NULL; - pm->old = NULL; - pm->level = 0; - - if ((n = optlookup(name))) - pm->u.str = dupstring(opts[n] ? "on" : "off"); - else { - pm->u.str = ""; - pm->flags |= PM_UNSET; - } - } LASTALLOC; - + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR; + pm->sets.cfn = setpmoption; + pm->gets.cfn = strgetfn; + pm->unsetfn = unsetpmoption; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + + if ((n = optlookup(name))) + pm->u.str = dupstring(opts[n] ? "on" : "off"); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } return (HashNode) pm; } @@ -574,89 +817,1147 @@ scanpmoptions(HashTable ht, ScanFunc func, int flags) for (i = 0; i < optiontab->hsize; i++) for (hn = optiontab->nodes[i]; hn; hn = hn->next) { + int optno = ((Optname) hn)->optno, ison; pm.nam = hn->nam; - pm.u.str = opts[((Optname) hn)->optno] ? "on" : "off"; + ison = optno < 0 ? !opts[-optno] : opts[optno]; + pm.u.str = dupstring(ison ? "on" : "off"); func((HashNode) &pm, flags); } } -/* Names and Params for the special parameters. */ +/* Functions for the modules special parameter. */ -#define PAR_NAM "parameters" -#define CMD_NAM "commands" -#define FUN_NAM "functions" -#define OPT_NAM "options" +static char *modpmname; +static int modpmfound; -static Param parpm, cmdpm, funpm, optpm; +/**/ +static void +modpmbuiltinscan(HashNode hn, int dummy) +{ + if (!(((Builtin) hn)->flags & BINF_ADDED) && + !strcmp(((Builtin) hn)->optstr, modpmname)) + modpmfound = 1; +} + +/**/ +static void +modpmparamscan(HashNode hn, int dummy) +{ + if ((((Param) hn)->flags & PM_AUTOLOAD) && + !strcmp(((Param) hn)->u.str, modpmname)) + modpmfound = 1; +} + +/**/ +static int +findmodnode(LinkList l, char *nam) +{ + LinkNode node; + + for (node = firstnode(l); node; incnode(node)) + if (!strcmp(nam, (char *) getdata(node))) + return 1; + + return 0; +} + +/**/ +static HashNode +getpmmodule(HashTable ht, char *name) +{ + Param pm = NULL; + char *type = NULL; + LinkNode node; + + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + + if (!type) { + Module m; + + for (node = firstnode(modules); node; incnode(node)) { + m = (Module) getdata(node); + if (m->u.handle && !(m->flags & MOD_UNLOAD) && + !strcmp(name, m->nam)) { + type = "loaded"; + break; + } + } + } + modpmname = name; + modpmfound = 0; + if (!type) { + scanhashtable(builtintab, 0, 0, 0, modpmbuiltinscan, 0); + if (!modpmfound) { + Conddef p; + + for (p = condtab; p; p = p->next) + if (p->module && !strcmp(name, p->module)) { + modpmfound = 1; + break; + } + if (!modpmfound) + scanhashtable(realparamtab, 0, 0, 0, modpmparamscan, 0); + } + if (modpmfound) + type = "autoloaded"; + } + if (type) + pm->u.str = dupstring(type); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + return (HashNode) pm; +} + +/**/ +static void +scanpmmodules(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int i; + HashNode hn; + LinkList done = newlinklist(); + LinkNode node; + Module m; + Conddef p; + + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + pm.u.str = dupstring("builtin"); + pm.u.str = dupstring("loaded"); + for (node = firstnode(modules); node; incnode(node)) { + m = (Module) getdata(node); + if (m->u.handle && !(m->flags & MOD_UNLOAD)) { + pm.nam = m->nam; + addlinknode(done, pm.nam); + func((HashNode) &pm, flags); + } + } + pm.u.str = dupstring("autoloaded"); + for (i = 0; i < builtintab->hsize; i++) + for (hn = builtintab->nodes[i]; hn; hn = hn->next) { + if (!(((Builtin) hn)->flags & BINF_ADDED) && + !findmodnode(done, ((Builtin) hn)->optstr)) { + pm.nam = ((Builtin) hn)->optstr; + addlinknode(done, pm.nam); + func((HashNode) &pm, flags); + } + } + for (p = condtab; p; p = p->next) + if (p->module && !findmodnode(done, p->module)) { + pm.nam = p->module; + addlinknode(done, pm.nam); + func((HashNode) &pm, flags); + } + for (i = 0; i < realparamtab->hsize; i++) + for (hn = realparamtab->nodes[i]; hn; hn = hn->next) { + if ((((Param) hn)->flags & PM_AUTOLOAD) && + !findmodnode(done, ((Param) hn)->u.str)) { + pm.nam = ((Param) hn)->u.str; + addlinknode(done, pm.nam); + func((HashNode) &pm, flags); + } + } +} + +/* Functions for the dirstack special parameter. */ + +/**/ +static void +dirssetfn(Param pm, char **x) +{ + char **ox = x; + + if (!incleanup) { + freelinklist(dirstack, freestr); + dirstack = znewlinklist(); + while (x && *x) + zaddlinknode(dirstack, ztrdup(*x++)); + } + if (ox) + freearray(ox); +} + +/**/ +static char ** +dirsgetfn(Param pm) +{ + int l = countlinknodes(dirstack); + char **ret = (char **) zhalloc((l + 1) * sizeof(char *)), **p; + LinkNode n; + + for (n = firstnode(dirstack), p = ret; n; incnode(n), p++) + *p = dupstring((char *) getdata(n)); + *p = NULL; + + return ret; +} + +/* Functions for the history special parameter. */ + +/**/ +static HashNode +getpmhistory(HashTable ht, char *name) +{ + Param pm = NULL; + Histent he; + + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if ((he = quietgethist(atoi(name)))) + pm->u.str = dupstring(he->text); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + return (HashNode) pm; +} + +/**/ +static void +scanpmhistory(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int i = addhistnum(curhist, -1, HIST_FOREIGN); + Histent he = quietgethistent(i, GETHIST_UPWARD); + char buf[40]; + + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + while (he) { + if (func != scancountparams) { + sprintf(buf, "%d", he->histnum); + pm.nam = dupstring(buf); + if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(flags & SCANPM_WANTKEYS)) + pm.u.str = dupstring(he->text); + } + func((HashNode) &pm, flags); + + he = up_histent(he); + } +} + +/* Function for the historywords special parameter. */ + +/**/ +static char ** +histwgetfn(Param pm) +{ + char **ret, **p, *h, *e, sav; + LinkList l = newlinklist(); + LinkNode n; + int i = addhistnum(curhist, -1, HIST_FOREIGN), iw; + Histent he = quietgethistent(i, GETHIST_UPWARD); + + while (he) { + for (iw = he->nwords - 1; iw >= 0; iw--) { + h = he->text + he->words[iw * 2]; + e = he->text + he->words[iw * 2 + 1]; + sav = *e; + *e = '\0'; + addlinknode(l, dupstring(h)); + *e = sav; + } + he = up_histent(he); + } + ret = (char **) zhalloc((countlinknodes(l) + 1) * sizeof(char *)); + + for (p = ret, n = firstnode(l); n; incnode(n), p++) + *p = (char *) getdata(n); + *p = NULL; + + return ret; +} + +/* Functions for the jobtexts special parameter. */ + +/**/ +static char * +pmjobtext(int job) +{ + Process pn; + int len = 1; + char *ret; + + for (pn = jobtab[job].procs; pn; pn = pn->next) + len += strlen(pn->text) + 3; + + ret = (char *) zhalloc(len); + ret[0] = '\0'; + + for (pn = jobtab[job].procs; pn; pn = pn->next) { + strcat(ret, pn->text); + if (pn->next) + strcat(ret, " | "); + } + return ret; +} + +/**/ +static HashNode +getpmjobtext(HashTable ht, char *name) +{ + Param pm = NULL; + int job; + + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + + if ((job = atoi(name)) >= 1 && job < MAXJOB && + jobtab[job].stat && jobtab[job].procs && + !(jobtab[job].stat & STAT_NOPRINT)) + pm->u.str = pmjobtext(job); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + return (HashNode) pm; +} + +/**/ +static void +scanpmjobtexts(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int job; + char buf[40]; + + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (job = 1; job < MAXJOB; job++) { + if (jobtab[job].stat && jobtab[job].procs && + !(jobtab[job].stat & STAT_NOPRINT)) { + if (func != scancountparams) { + sprintf(buf, "%d", job); + pm.nam = dupstring(buf); + if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(flags & SCANPM_WANTKEYS)) + pm.u.str = pmjobtext(job); + } + func((HashNode) &pm, flags); + } + } +} + +/* Functions for the jobstates special parameter. */ + +/**/ +static char * +pmjobstate(int job) +{ + Process pn; + char buf[256], buf2[128], *ret, *state; + + if (jobtab[job].stat & STAT_DONE) + ret = dupstring("done"); + else if (jobtab[job].stat & STAT_STOPPED) + ret = dupstring("suspended"); + else + ret = dupstring("running"); + + for (pn = jobtab[job].procs; pn; pn = pn->next) { + + if (pn->status == SP_RUNNING) + state = "running"; + else if (WIFEXITED(pn->status)) { + if (WEXITSTATUS(pn->status)) + sprintf((state = buf2), "exit %d", (pn->status)); + else + state = "done"; + } else if (WIFSTOPPED(pn->status)) + state = sigmsg(WSTOPSIG(pn->status)); + else if (WCOREDUMP(pn->status)) + sprintf((state = buf2), "%s (core dumped)", + sigmsg(WTERMSIG(pn->status))); + else + state = sigmsg(WTERMSIG(pn->status)); + + sprintf(buf, ":%d=%s", pn->pid, state); + + ret = dyncat(ret, buf); + } + return ret; +} + +/**/ +static HashNode +getpmjobstate(HashTable ht, char *name) +{ + Param pm = NULL; + int job; + + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + + if ((job = atoi(name)) >= 1 && job < MAXJOB && + jobtab[job].stat && jobtab[job].procs && + !(jobtab[job].stat & STAT_NOPRINT)) + pm->u.str = pmjobstate(job); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + return (HashNode) pm; +} + +/**/ +static void +scanpmjobstates(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int job; + char buf[40]; + + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (job = 1; job < MAXJOB; job++) { + if (jobtab[job].stat && jobtab[job].procs && + !(jobtab[job].stat & STAT_NOPRINT)) { + if (func != scancountparams) { + sprintf(buf, "%d", job); + pm.nam = dupstring(buf); + if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(flags & SCANPM_WANTKEYS)) + pm.u.str = pmjobstate(job); + } + func((HashNode) &pm, flags); + } + } +} + +/* Functions for the jobdirs special parameter. */ + +/**/ +static char * +pmjobdir(int job) +{ + char *ret; + + ret = dupstring(jobtab[job].pwd ? jobtab[job].pwd : pwd); + return ret; +} + +/**/ +static HashNode +getpmjobdir(HashTable ht, char *name) +{ + Param pm = NULL; + int job; + + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + + if ((job = atoi(name)) >= 1 && job < MAXJOB && + jobtab[job].stat && jobtab[job].procs && + !(jobtab[job].stat & STAT_NOPRINT)) + pm->u.str = pmjobdir(job); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + return (HashNode) pm; +} + +/**/ +static void +scanpmjobdirs(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int job; + char buf[40]; + + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (job = 1; job < MAXJOB; job++) { + if (jobtab[job].stat && jobtab[job].procs && + !(jobtab[job].stat & STAT_NOPRINT)) { + if (func != scancountparams) { + sprintf(buf, "%d", job); + pm.nam = dupstring(buf); + if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(flags & SCANPM_WANTKEYS)) + pm.u.str = pmjobdir(job); + } + func((HashNode) &pm, flags); + } + } +} + +/* Functions for the nameddirs special parameter. */ + +/**/ +static void +setpmnameddir(Param pm, char *value) +{ + if (!value || *value != '/' || strlen(value) >= PATH_MAX) + zwarn("invalid value: %s", value, 0); + else + adduserdir(pm->nam, value, 0, 1); + zsfree(value); +} + +/**/ +static void +unsetpmnameddir(Param pm, int exp) +{ + HashNode hd = nameddirtab->removenode(nameddirtab, pm->nam); + + if (hd) + nameddirtab->freenode(hd); +} + +/**/ +static void +setpmnameddirs(Param pm, HashTable ht) +{ + int i; + HashNode hn, next, hd; + + if (!ht) + return; + + for (i = 0; i < nameddirtab->hsize; i++) + for (hn = nameddirtab->nodes[i]; hn; hn = next) { + next = hn->next; + if (!(((Nameddir) hn)->flags & ND_USERNAME) && + (hd = nameddirtab->removenode(nameddirtab, hn->nam))) + nameddirtab->freenode(hd); + } + + for (i = 0; i < ht->hsize; i++) + for (hn = ht->nodes[i]; hn; hn = hn->next) { + struct value v; + char *val; + + v.isarr = v.inv = v.a = 0; + v.b = -1; + v.arr = NULL; + v.pm = (Param) hn; + + if (!(val = getstrvalue(&v)) || *val != '/' || + strlen(val) >= PATH_MAX) + zwarn("invalid value: %s", val, 0); + else + adduserdir(hn->nam, val, 0, 1); + } + + /* The INTERACTIVE stuff ensures that the dirs are not immediatly removed + * when the sub-pms are deleted. */ + + i = opts[INTERACTIVE]; + opts[INTERACTIVE] = 0; + deleteparamtable(ht); + opts[INTERACTIVE] = i; +} + +/**/ +static HashNode +getpmnameddir(HashTable ht, char *name) +{ + Param pm = NULL; + Nameddir nd; + + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR; + pm->sets.cfn = setpmnameddir; + pm->gets.cfn = strgetfn; + pm->unsetfn = unsetpmnameddir; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) && + !(nd->flags & ND_USERNAME)) + pm->u.str = dupstring(nd->dir); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + return (HashNode) pm; +} + +/**/ +static void +scanpmnameddirs(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int i; + HashNode hn; + Nameddir nd; + + pm.flags = PM_SCALAR; + pm.sets.cfn = setpmnameddir; + pm.gets.cfn = strgetfn; + pm.unsetfn = unsetpmnameddir; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (i = 0; i < nameddirtab->hsize; i++) + for (hn = nameddirtab->nodes[i]; hn; hn = hn->next) { + if (!((nd = (Nameddir) hn)->flags & ND_USERNAME)) { + pm.nam = hn->nam; + if (func != scancountparams && + ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(flags & SCANPM_WANTKEYS))) + pm.u.str = dupstring(nd->dir); + func((HashNode) &pm, flags); + } + } +} + +/* Functions for the userdirs special parameter. */ + +/**/ +static HashNode +getpmuserdir(HashTable ht, char *name) +{ + Param pm = NULL; + Nameddir nd; + + nameddirtab->filltable(nameddirtab); + + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR | PM_READONLY; + pm->sets.cfn = NULL; + pm->gets.cfn = strgetfn; + pm->unsetfn = NULL; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) && + (nd->flags & ND_USERNAME)) + pm->u.str = dupstring(nd->dir); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + return (HashNode) pm; +} + +/**/ +static void +scanpmuserdirs(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + int i; + HashNode hn; + Nameddir nd; + + nameddirtab->filltable(nameddirtab); + + pm.flags = PM_SCALAR | PM_READONLY; + pm.sets.cfn = NULL; + pm.gets.cfn = strgetfn; + pm.unsetfn = NULL; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (i = 0; i < nameddirtab->hsize; i++) + for (hn = nameddirtab->nodes[i]; hn; hn = hn->next) { + if ((nd = (Nameddir) hn)->flags & ND_USERNAME) { + pm.nam = hn->nam; + if (func != scancountparams && + ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(flags & SCANPM_WANTKEYS))) + pm.u.str = dupstring(nd->dir); + func((HashNode) &pm, flags); + } + } +} + +/* Functions for the regularaliases and globalaliases special parameters. */ + +/**/ +static void +setralias(Param pm, char *value, int dis) +{ + aliastab->addnode(aliastab, ztrdup(pm->nam), createaliasnode(value, dis)); +} + +/**/ +static void +setpmralias(Param pm, char *value) +{ + setralias(pm, value, 0); +} + +/**/ +static void +setpmdisralias(Param pm, char *value) +{ + setralias(pm, value, DISABLED); +} + +/**/ +static void +setgalias(Param pm, char *value, int dis) +{ + aliastab->addnode(aliastab, ztrdup(pm->nam), + createaliasnode(value, dis | ALIAS_GLOBAL)); +} + +/**/ +static void +setpmgalias(Param pm, char *value) +{ + setgalias(pm, value, 0); +} + +/**/ +static void +setpmdisgalias(Param pm, char *value) +{ + setgalias(pm, value, DISABLED); +} + +/**/ +static void +unsetpmalias(Param pm, int exp) +{ + HashNode hd = aliastab->removenode(aliastab, pm->nam); + + if (hd) + aliastab->freenode(hd); +} + +/**/ +static void +setaliases(Param pm, HashTable ht, int global, int dis) +{ + int i; + HashNode hn, next, hd; + + if (!ht) + return; + + for (i = 0; i < aliastab->hsize; i++) + for (hn = aliastab->nodes[i]; hn; hn = next) { + next = hn->next; + if (((global && (((Alias) hn)->flags & ALIAS_GLOBAL)) || + (!global && !(((Alias) hn)->flags & ALIAS_GLOBAL))) && + (hd = aliastab->removenode(aliastab, hn->nam))) + aliastab->freenode(hd); + } + + for (i = 0; i < ht->hsize; i++) + for (hn = ht->nodes[i]; hn; hn = hn->next) { + struct value v; + char *val; + + v.isarr = v.inv = v.a = 0; + v.b = -1; + v.arr = NULL; + v.pm = (Param) hn; + + if ((val = getstrvalue(&v))) + aliastab->addnode(aliastab, ztrdup(hn->nam), + createaliasnode(ztrdup(val), + (global ? ALIAS_GLOBAL : 0) | + (dis ? DISABLED : 0))); + } + deleteparamtable(ht); +} + +/**/ +static void +setpmraliases(Param pm, HashTable ht) +{ + setaliases(pm, ht, 0, 0); +} + +/**/ +static void +setpmdisraliases(Param pm, HashTable ht) +{ + setaliases(pm, ht, 0, DISABLED); +} + +/**/ +static void +setpmgaliases(Param pm, HashTable ht) +{ + setaliases(pm, ht, 1, 0); +} + +/**/ +static void +setpmdisgaliases(Param pm, HashTable ht) +{ + setaliases(pm, ht, 1, DISABLED); +} + +/**/ +static HashNode +getalias(HashTable ht, char *name, int global, int dis) +{ + Param pm = NULL; + Alias al; + + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR; + pm->sets.cfn = (global ? (dis ? setpmdisgalias : setpmgalias) : + (dis ? setpmdisralias : setpmralias)); + pm->gets.cfn = strgetfn; + pm->unsetfn = unsetpmalias; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + if ((al = (Alias) aliastab->getnode2(aliastab, name)) && + ((global && (al->flags & ALIAS_GLOBAL)) || + (!global && !(al->flags & ALIAS_GLOBAL))) && + (dis ? (al->flags & DISABLED) : !(al->flags & DISABLED))) + pm->u.str = dupstring(al->text); + else { + pm->u.str = dupstring(""); + pm->flags |= PM_UNSET; + } + return (HashNode) pm; +} + +/**/ +static HashNode +getpmralias(HashTable ht, char *name) +{ + return getalias(ht, name, 0, 0); +} + +/**/ +static HashNode +getpmdisralias(HashTable ht, char *name) +{ + return getalias(ht, name, 0, 0); +} + +/**/ +static HashNode +getpmgalias(HashTable ht, char *name) +{ + return getalias(ht, name, 1, 0); +} + +/**/ +static HashNode +getpmdisgalias(HashTable ht, char *name) +{ + return getalias(ht, name, 1, DISABLED); +} + +/**/ +static void +scanaliases(HashTable ht, ScanFunc func, int flags, int global, int dis) +{ + struct param pm; + int i; + HashNode hn; + Alias al; + + pm.flags = PM_SCALAR; + pm.sets.cfn = (global ? (dis ? setpmdisgalias : setpmgalias) : + (dis ? setpmdisralias : setpmralias)); + pm.gets.cfn = strgetfn; + pm.unsetfn = unsetpmalias; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + for (i = 0; i < aliastab->hsize; i++) + for (hn = aliastab->nodes[i]; hn; hn = hn->next) { + if (((global && ((al = (Alias) hn)->flags & ALIAS_GLOBAL)) || + (!global && !((al = (Alias) hn)->flags & ALIAS_GLOBAL))) && + (dis ? (al->flags & DISABLED) : !(al->flags & DISABLED))) { + pm.nam = hn->nam; + if (func != scancountparams && + ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(flags & SCANPM_WANTKEYS))) + pm.u.str = dupstring(al->text); + func((HashNode) &pm, flags); + } + } +} + +/**/ +static void +scanpmraliases(HashTable ht, ScanFunc func, int flags) +{ + scanaliases(ht, func, flags, 0, 0); +} + +/**/ +static void +scanpmdisraliases(HashTable ht, ScanFunc func, int flags) +{ + scanaliases(ht, func, flags, 0, DISABLED); +} + +/**/ +static void +scanpmgaliases(HashTable ht, ScanFunc func, int flags) +{ + scanaliases(ht, func, flags, 1, 0); +} + +/**/ +static void +scanpmdisgaliases(HashTable ht, ScanFunc func, int flags) +{ + scanaliases(ht, func, flags, 1, DISABLED); +} + +/* Table for defined parameters. */ + +struct pardef { + char *name; + int flags; + GetNodeFunc getnfn; + ScanTabFunc scantfn; + void (*hsetfn) _((Param, HashTable)); + void (*setfn) _((Param, char **)); + char **(*getfn) _((Param)); + void (*unsetfn) _((Param, int)); + Param pm; +}; + +static struct pardef partab[] = { + { "parameters", PM_READONLY, + getpmparameter, scanpmparameters, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "commands", 0, + getpmcommand, scanpmcommands, setpmcommands, + NULL, NULL, stdunsetfn, NULL }, + { "functions", 0, + getpmfunction, scanpmfunctions, setpmfunctions, + NULL, NULL, stdunsetfn, NULL }, + { "dis_functions", 0, + getpmdisfunction, scanpmdisfunctions, setpmdisfunctions, + NULL, NULL, stdunsetfn, NULL }, + { "funcstack", PM_ARRAY|PM_SPECIAL|PM_READONLY, + NULL, NULL, NULL, + arrsetfn, funcstackgetfn, stdunsetfn, NULL }, + { "builtins", PM_READONLY, + getpmbuiltin, scanpmbuiltins, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "dis_builtins", PM_READONLY, + getpmdisbuiltin, scanpmdisbuiltins, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "reswords", PM_ARRAY|PM_SPECIAL|PM_READONLY, + NULL, NULL, NULL, + arrsetfn, reswordsgetfn, stdunsetfn, NULL }, + { "dis_reswords", PM_ARRAY|PM_SPECIAL|PM_READONLY, + NULL, NULL, NULL, + arrsetfn, disreswordsgetfn, stdunsetfn, NULL }, + { "options", 0, + getpmoption, scanpmoptions, setpmoptions, + NULL, NULL, stdunsetfn, NULL }, + { "modules", PM_READONLY, + getpmmodule, scanpmmodules, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "dirstack", PM_ARRAY|PM_SPECIAL|PM_REMOVABLE, + NULL, NULL, NULL, + dirssetfn, dirsgetfn, stdunsetfn, NULL }, + { "history", PM_READONLY, + getpmhistory, scanpmhistory, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "historywords", PM_ARRAY|PM_SPECIAL|PM_READONLY, + NULL, NULL, NULL, + arrsetfn, histwgetfn, stdunsetfn, NULL }, + { "jobtexts", PM_READONLY, + getpmjobtext, scanpmjobtexts, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "jobstates", PM_READONLY, + getpmjobstate, scanpmjobstates, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "jobdirs", PM_READONLY, + getpmjobdir, scanpmjobdirs, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "nameddirs", 0, + getpmnameddir, scanpmnameddirs, setpmnameddirs, + NULL, NULL, stdunsetfn, NULL }, + { "userdirs", PM_READONLY, + getpmuserdir, scanpmuserdirs, hashsetfn, + NULL, NULL, stdunsetfn, NULL }, + { "aliases", 0, + getpmralias, scanpmraliases, setpmraliases, + NULL, NULL, stdunsetfn, NULL }, + { "galiases", 0, + getpmgalias, scanpmgaliases, setpmgaliases, + NULL, NULL, stdunsetfn, NULL }, + { "dis_aliases", 0, + getpmdisralias, scanpmdisraliases, setpmdisraliases, + NULL, NULL, stdunsetfn, NULL }, + { "dis_galiases", 0, + getpmdisgalias, scanpmdisgaliases, setpmdisgaliases, + NULL, NULL, stdunsetfn, NULL }, + { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +}; /**/ int -setup_parameter(Module m) +setup_(Module m) { + incleanup = 0; + return 0; } /**/ int -boot_parameter(Module m) +boot_(Module m) { /* Create the special associative arrays. * As an example for autoloaded parameters, this is probably a bad - * example, because we the zsh core doesn't support creation of + * example, because the zsh core doesn't support creation of * special hashes, yet. */ - unsetparam(PAR_NAM); - if (!(parpm = createspecialhash(PAR_NAM, getpmparameter, - scanpmparameters))) - return 1; - parpm->flags |= PM_READONLY; - unsetparam(CMD_NAM); - if (!(cmdpm = createspecialhash(CMD_NAM, getpmcommand, - scanpmcommands))) - return 1; - cmdpm->sets.hfn = setpmcommands; - unsetparam(FUN_NAM); - if (!(funpm = createspecialhash(FUN_NAM, getpmfunction, - scanpmfunctions))) - return 1; - funpm->sets.hfn = setpmfunctions; - unsetparam(OPT_NAM); - if (!(optpm = createspecialhash(OPT_NAM, getpmoption, - scanpmoptions))) - return 1; - optpm->sets.hfn = setpmoptions; + struct pardef *def; + for (def = partab; def->name; def++) { + unsetparam(def->name); + + if (def->getnfn) { + if (!(def->pm = createspecialhash(def->name, def->getnfn, + def->scantfn))) + return 1; + def->pm->flags |= def->flags; + if (def->hsetfn) + def->pm->sets.hfn = def->hsetfn; + } else { + if (!(def->pm = createparam(def->name, def->flags | PM_HIDE | + PM_REMOVABLE))) + return 1; + def->pm->sets.afn = def->setfn; + def->pm->gets.afn = def->getfn; + def->pm->unsetfn = def->unsetfn; + } + } return 0; } -#ifdef MODULE - /**/ int -cleanup_parameter(Module m) +cleanup_(Module m) { Param pm; + struct pardef *def; - /* Remove the special parameters if they are still the same. */ + incleanup = 1; - if ((pm = (Param) paramtab->getnode(paramtab, PAR_NAM)) && pm == parpm) { - pm->flags &= ~PM_READONLY; - unsetparam_pm(pm, 0, 1); + for (def = partab; def->name; def++) { + if ((pm = (Param) paramtab->getnode(paramtab, def->name)) && + pm == def->pm) { + pm->flags &= ~PM_READONLY; + unsetparam_pm(pm, 0, 1); + } } - if ((pm = (Param) paramtab->getnode(paramtab, CMD_NAM)) && pm == cmdpm) - unsetparam_pm(pm, 0, 1); - if ((pm = (Param) paramtab->getnode(paramtab, FUN_NAM)) && pm == funpm) - unsetparam_pm(pm, 0, 1); - if ((pm = (Param) paramtab->getnode(paramtab, OPT_NAM)) && pm == optpm) - unsetparam_pm(pm, 0, 1); return 0; } /**/ int -finish_parameter(Module m) +finish_(Module m) { return 0; } - -#endif diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 088979514..26896525c 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -30,6 +30,13 @@ #include "zpty.mdh" #include "zpty.pro" +/* The number of bytes we normally read when given no pattern and the + * upper bound on the number of bytes we read (even if we are give a + * pattern). */ + +#define READ_LEN 1024 +#define READ_MAX (1024 * 1024) + typedef struct ptycmd *Ptycmd; struct ptycmd { @@ -158,7 +165,7 @@ get_pty(int *master, int *slave) #else /* ! __osf__ */ -#if __SVR4 +#if defined(__SVR4) || defined(sinix) #include <sys/stropts.h> @@ -167,11 +174,12 @@ get_pty(int *master, int *slave) { int mfd, sfd; char *name; + int ret; if ((mfd = open("/dev/ptmx", O_RDWR)) < 0) return 1; - if (!(name = ptsname(mfd)) || grantpt(mfd) || unlockpt(mfd)) { + if (grantpt(mfd) || unlockpt(mfd) || !(name = ptsname(mfd))) { close(mfd); return 1; } @@ -179,20 +187,31 @@ get_pty(int *master, int *slave) close(mfd); return 1; } - if (ioctl(sfd, I_PUSH, "ptem") || - ioctl(sfd, I_PUSH, "ldterm") || - ioctl(sfd, I_PUSH, "ttcompat")) { - close(mfd); - close(sfd); - return 1; - } + if ((ret = ioctl(sfd, I_FIND, "ptem")) != 1) + if (ret == -1 || ioctl(sfd, I_PUSH, "ptem") == -1) { + close(mfd); + close(sfd); + return 1; + } + if ((ret = ioctl(sfd, I_FIND, "ldterm")) != 1) + if (ret == -1 || ioctl(sfd, I_PUSH, "ldterm") == -1) { + close(mfd); + close(sfd); + return 1; + } + if ((ret = ioctl(sfd, I_FIND, "ttcompat")) != 1) + if (ret == -1 || ioctl(sfd, I_PUSH, "ttcompat") == -1) { + close(mfd); + close(sfd); + return 1; + } *master = mfd; *slave = sfd; return 0; } -#else /* ! __SVR4 */ +#else /* ! (defined(__SVR4) || defind(sinix)) */ static int get_pty(int *master, int *slave) @@ -242,17 +261,17 @@ newptycmd(char *nam, char *pname, char **args, int echo, int block) char *cmd; if (!(cmd = findcmd(*args, 1))) { - zerrnam(nam, "unknown command: %s", *args, 0); + zwarnnam(nam, "unknown command: %s", *args, 0); return 1; } if (get_pty(&master, &slave)) { - zerrnam(nam, "can't open pseudo terminal", NULL, 0); + zwarnnam(nam, "can't open pseudo terminal", NULL, 0); return 1; } if ((pid = fork()) == -1) { close(master); close(slave); - zerrnam(nam, "couldn't create pty command: %s", pname, 0); + zwarnnam(nam, "couldn't create pty command: %s", pname, 0); return 1; } else if (!pid) { if (!echo) { @@ -298,6 +317,8 @@ newptycmd(char *nam, char *pname, char **args, int echo, int block) close(slave); + setpgrp(0L, getpid()); + execve(cmd, args, environ); exit(0); } @@ -307,9 +328,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int block) p = (Ptycmd) zalloc(sizeof(*p)); p->name = ztrdup(pname); - PERMALLOC { - p->args = arrdup(args); - } LASTALLOC; + p->args = zarrdup(args); p->fd = master; p->pid = pid; p->echo = echo; @@ -343,7 +362,9 @@ deleteptycmd(Ptycmd cmd) zsfree(p->name); freearray(p->args); - kill(p->pid, SIGHUP); + /* We kill the process group the command put itself in. */ + + kill(-(p->pid), SIGHUP); zclose(cmd->fd); @@ -375,7 +396,7 @@ checkptycmd(Ptycmd cmd) static int ptyread(char *nam, Ptycmd cmd, char **args) { - int blen = 256, used = 0, ret; + int blen = 256, used = 0, ret = 1; char *buf = (char *) zhalloc(blen + 1); Patprog prog = NULL; @@ -383,57 +404,113 @@ ptyread(char *nam, Ptycmd cmd, char **args) char *p; if (args[2]) { - zerrnam(nam, "too many arguments", NULL, 0); + zwarnnam(nam, "too many arguments", NULL, 0); return 1; } p = dupstring(args[1]); tokenize(p); remnulargs(p); if (!(prog = patcompile(p, PAT_STATIC, NULL))) { - zerrnam(nam, "bad pattern: %s", args[1], 0); + zwarnnam(nam, "bad pattern: %s", args[1], 0); return 1; } } do { - while ((ret = read(cmd->fd, buf + used, 1)) == 1) { + if (!ret) { + checkptycmd(cmd); + if (cmd->fin) + break; + } + if ((ret = read(cmd->fd, buf + used, 1)) == 1) { if (++used == blen) { buf = hrealloc(buf, blen, blen << 1); blen <<= 1; } } buf[used] = '\0'; - } while (prog && !pattry(prog, buf)); - if (*args) - setsparam(*args, ztrdup(buf)); - else - printf("%s", buf); + /**** Hm. If we leave the loop when ret < 0 the user would have + * to make sure that `zpty -r' is tried more than once if + * there will be some output and we only got the ret == -1 + * because the output is not yet available. + * The same for the `write' below. */ + + if (ret < 0 && (cmd->block +#ifdef EWOULDBLOCK + || errno != EWOULDBLOCK +#else +#ifdef EAGAIN + || errno != EAGAIN +#endif +#endif + )) + break; + + if (!prog && !ret) + break; + } while (!errflag && + (prog ? (used < READ_MAX && (!ret || !pattry(prog, buf))) : + (used < READ_LEN))); + if (*args) + setsparam(*args, ztrdup(metafy(buf, used, META_HREALLOC))); + else { + fflush(stdout); + write(1, buf, used); + } return !used; } static int +ptywritestr(Ptycmd cmd, char *s, int len) +{ + int written; + + for (; len; len -= written, s += written) { + if ((written = write(cmd->fd, s, len)) < 0 && + (cmd->block +#ifdef EWOULDBLOCK + || errno != EWOULDBLOCK +#else +#ifdef EAGAIN + || errno != EAGAIN +#endif +#endif + )) + return 1; + if (written < 0) { + checkptycmd(cmd); + if (cmd->fin) + break; + written = 0; + } + } + return 0; +} + +static int ptywrite(Ptycmd cmd, char **args, int nonl) { if (*args) { char sp = ' '; - while (*args) { - write(cmd->fd, *args, strlen(*args)); + while (*args) + if (ptywritestr(cmd, *args, strlen(*args)) || + (*++args && ptywritestr(cmd, &sp, 1))) + return 1; - if (*++args) - write(cmd->fd, &sp, 1); - } if (!nonl) { sp = '\n'; - write(cmd->fd, &sp, 1); + if (ptywritestr(cmd, &sp, 1)) + return 1; } } else { int n; char buf[BUFSIZ]; while ((n = read(0, buf, BUFSIZ)) > 0) - write(cmd->fd, buf, n); + if (ptywritestr(cmd, buf, n)) + return 1; } return 0; } @@ -449,17 +526,17 @@ bin_zpty(char *nam, char **args, char *ops, int func) ops['d'] || ops['L'])) || (ops['d'] && (ops['b'] || ops['e'] || ops['L'])) || (ops['L'] && (ops['b'] || ops['e']))) { - zerrnam(nam, "illegal option combination", NULL, 0); + zwarnnam(nam, "illegal option combination", NULL, 0); return 1; } if (ops['r'] || ops['w']) { Ptycmd p; if (!*args) { - zerrnam(nam, "missing pty command name", NULL, 0); + zwarnnam(nam, "missing pty command name", NULL, 0); return 1; } else if (!(p = getptycmd(*args))) { - zerrnam(nam, "no such pty command: %s", *args, 0); + zwarnnam(nam, "no such pty command: %s", *args, 0); return 1; } checkptycmd(p); @@ -486,11 +563,11 @@ bin_zpty(char *nam, char **args, char *ops, int func) return ret; } else if (*args) { if (!args[1]) { - zerrnam(nam, "missing command", NULL, 0); + zwarnnam(nam, "missing command", NULL, 0); return 1; } if (getptycmd(*args)) { - zerrnam(nam, "pty command name already used: %s", *args, 0); + zwarnnam(nam, "pty command name already used: %s", *args, 0); return 1; } return newptycmd(nam, *args, args + 1, ops['e'], ops['b']); diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h index 5d7ef74e2..f513d4a5a 100644 --- a/Src/Zle/comp.h +++ b/Src/Zle/comp.h @@ -27,111 +27,362 @@ * */ -#undef compctlread +typedef struct cmatcher *Cmatcher; +typedef struct cmlist *Cmlist; +typedef struct cpattern *Cpattern; +typedef struct menuinfo *Menuinfo; +typedef struct cexpl *Cexpl; +typedef struct cmgroup *Cmgroup; +typedef struct cmatch *Cmatch; -typedef struct compctlp *Compctlp; -typedef struct compctl *Compctl; -typedef struct compcond *Compcond; +/* This is for explantion strings. */ -/* node for compctl hash table (compctltab) */ +struct cexpl { + char *str; /* the string */ + int count; /* the number of matches */ + int fcount; /* number of matches with fignore ignored */ +}; + +/* This describes a group of matches. */ + +struct cmgroup { + char *name; /* the name of this group */ + Cmgroup prev; /* previous on the list */ + Cmgroup next; /* next one in list */ + int flags; /* see CGF_* below */ + int mcount; /* number of matches */ + Cmatch *matches; /* the matches */ + int lcount; /* number of things to list here */ + int llcount; /* number of line-displays */ + char **ylist; /* things to list */ + int ecount; /* number of explanation string */ + Cexpl *expls; /* explanation strings */ + int ccount; /* number of compctls used */ + LinkList lexpls; /* list of explanation string while building */ + LinkList lmatches; /* list of matches */ + LinkList lfmatches; /* list of matches without fignore */ + LinkList lallccs; /* list of used compctls */ + int num; /* number of this group */ + int nbrbeg; /* number of opened braces */ + int nbrend; /* number of closed braces */ + /* The following is collected/used during listing. */ + int dcount; /* number of matches to list in columns */ + int cols; /* number of columns */ + int lins; /* number of lines */ + int width; /* column width */ + int *widths; /* column widths for listpacked */ + int totl; /* total length */ + int shortest; /* length of shortest match */ + Cmgroup perm; /* perm. alloced version of this group */ + int new; /* new matches since last permalloc() */ +}; + + +#define CGF_NOSORT 1 /* don't sort this group */ +#define CGF_LINES 2 /* these are to be printed on different lines */ +#define CGF_HASDL 4 /* has display strings printed on separate lines */ +#define CGF_UNIQALL 8 /* remove all duplicates */ +#define CGF_UNIQCON 16 /* remove consecutive duplicates */ +#define CGF_PACKED 32 /* LIST_PACKED for this group */ +#define CGF_ROWS 64 /* LIST_ROWS_FIRST for this group */ + +/* This is the struct used to hold matches. */ + +struct cmatch { + char *str; /* the match itself */ + char *ipre; /* ignored prefix, has to be re-inserted */ + char *ripre; /* ignored prefix, unquoted */ + char *isuf; /* ignored suffix */ + char *ppre; /* the path prefix */ + char *psuf; /* the path suffix */ + char *prpre; /* path prefix for opendir */ + char *pre; /* prefix string from -P */ + char *suf; /* suffix string from -S */ + char *disp; /* string to display (compadd -d) */ + char *autoq; /* closing quote to add automatically */ + int flags; /* see CMF_* below */ + int *brpl; /* places where to put the brace prefixes */ + int *brsl; /* ...and the suffixes */ + char *rems; /* when to remove the suffix */ + char *remf; /* shell function to call for suffix-removal */ + int qipl; /* length of quote-prefix */ + int qisl; /* length of quote-suffix */ + int rnum; /* group relative number */ + int gnum; /* global number */ +}; + +#define CMF_FILE (1<< 0) /* this is a file */ +#define CMF_REMOVE (1<< 1) /* remove the suffix */ +#define CMF_ISPAR (1<< 2) /* is paramter expansion */ +#define CMF_PARBR (1<< 3) /* paramter expansion with a brace */ +#define CMF_PARNEST (1<< 4) /* nested paramter expansion */ +#define CMF_NOLIST (1<< 5) /* should not be listed */ +#define CMF_DISPLINE (1<< 6) /* display strings one per line */ +#define CMF_HIDE (1<< 7) /* temporarily hide this one */ +#define CMF_NOSPACE (1<< 8) /* don't add a space */ +#define CMF_PACKED (1<< 9) /* prefer LIST_PACKED */ +#define CMF_ROWS (1<<10) /* prefer LIST_ROWS_FIRST */ +#define CMF_MULT (1<<11) /* string appears more than once */ +#define CMF_FMULT (1<<12) /* first of multiple equal strings */ + +/* Stuff for completion matcher control. */ + +struct cmlist { + Cmlist next; /* next one in the list of global matchers */ + Cmatcher matcher; /* the matcher definition */ + char *str; /* the string for it */ +}; + +struct cmatcher { + int refc; /* reference counter */ + Cmatcher next; /* next matcher */ + int flags; /* see CMF_* below */ + Cpattern line; /* what matches on the line */ + int llen; /* length of line pattern */ + Cpattern word; /* what matches in the word */ + int wlen; /* length of word pattern */ + Cpattern left; /* left anchor */ + int lalen; /* length of left anchor */ + Cpattern right; /* right anchor */ + int ralen; /* length of right anchor */ +}; + +#define CMF_LINE 1 +#define CMF_LEFT 2 +#define CMF_RIGHT 4 + +struct cpattern { + Cpattern next; /* next sub-pattern */ + unsigned char tab[256]; /* table of matched characters */ + int equiv; /* if this is a {...} class */ +}; + +/* This is a special return value for parse_cmatcher(), * + * signalling an error. */ + +#define pcm_err ((Cmatcher) 1) + +/* Information about what to put on the line as the unambiguous string. + * The code always keeps lists of these structs up to date while + * matches are added (in the aminfo structs below). + * The lists have two levels: in the first one we have one struct per + * word-part, where parts are separated by the anchors of `*' patterns. + * These structs have pointers (in the prefix and suffix fields) to + * lists of cline structs describing the strings before or after the + * the anchor. */ + +typedef struct cline *Cline; +typedef struct clsub Clsub; + +struct cline { + Cline next; + int flags; + char *line; + int llen; + char *word; + int wlen; + char *orig; + int olen; + int slen; + Cline prefix, suffix; + int min, max; +}; + +#define CLF_MISS 1 +#define CLF_DIFF 2 +#define CLF_SUF 4 +#define CLF_MID 8 +#define CLF_NEW 16 +#define CLF_LINE 32 +#define CLF_JOIN 64 +#define CLF_MATCHED 128 + +/* Information for ambiguous completions. One for fignore ignored and * + * one for normal completion. */ + +typedef struct aminfo *Aminfo; -struct compctlp { - HashNode next; /* next in hash chain */ - char *nam; /* command name */ - int flags; /* CURRENTLY UNUSED */ - Compctl cc; /* pointer to the compctl desc. */ +struct aminfo { + Cmatch firstm; /* the first match */ + int exact; /* if there was an exact match */ + Cmatch exactm; /* the exact match (if any) */ + int count; /* number of matches */ + Cline line; /* unambiguous line string */ }; -/* compctl -x condition */ - -struct compcond { - Compcond and, or; /* the next or'ed/and'ed conditions */ - int type; /* the type (CCT_*) */ - int n; /* the array length */ - union { /* these structs hold the data used to */ - struct { /* test this condition */ - int *a, *b; /* CCT_POS, CCT_NUMWORDS */ - } - r; - struct { /* CCT_CURSTR, CCT_CURPAT,... */ - int *p; - char **s; - } - s; - struct { /* CCT_RANGESTR,... */ - char **a, **b; - } - l; - } - u; +/* Information about menucompletion stuff. */ + +struct menuinfo { + Cmgroup group; /* position in the group list */ + Cmatch *cur; /* match currently inserted */ + int pos; /* begin on line */ + int len; /* length of inserted string */ + int end; /* end on the line */ + int we; /* non-zero if the cursor was at the end */ + int insc; /* length of suffix inserted */ + int asked; /* we asked if the list should be shown */ + char *prebr; /* prefix before a brace, if any */ + char *postbr; /* suffix after a brace */ +}; + +/* Flags for compadd and addmatches(). */ + +#define CAF_QUOTE 1 +#define CAF_NOSORT 2 +#define CAF_MATCH 4 +#define CAF_UNIQCON 8 +#define CAF_UNIQALL 16 + +/* Data for compadd and addmatches() */ + +typedef struct cadata *Cadata; + +struct cadata { + char *ipre; /* ignored prefix (-i) */ + char *isuf; /* ignored suffix (-I) */ + char *ppre; /* `path' prefix (-p) */ + char *psuf; /* `path' suffix (-s) */ + char *prpre; /* expanded `path' prefix (-W) */ + char *pre; /* prefix to insert (-P) */ + char *suf; /* suffix to insert (-S) */ + char *group; /* name of the group (-[JV]) */ + char *rems; /* remove suffix on chars... (-r) */ + char *remf; /* function to remove suffix (-R) */ + char *ign; /* ignored suffixes (-F) */ + int flags; /* CMF_* flags (-[fqn]) */ + int aflags; /* CAF_* flags (-[QUa]) */ + Cmatcher match; /* match spec (parsed from -M) */ + char *exp; /* explanation (-X) */ + char *apar; /* array to store matches in (-A) */ + char *opar; /* array to store originals in (-O) */ + char *dpar; /* array to delete non-matches in (-D) */ + char *disp; /* array with display lists (-d) */ }; -#define CCT_UNUSED 0 -#define CCT_POS 1 -#define CCT_CURSTR 2 -#define CCT_CURPAT 3 -#define CCT_WORDSTR 4 -#define CCT_WORDPAT 5 -#define CCT_CURSUF 6 -#define CCT_CURPRE 7 -#define CCT_CURSUB 8 -#define CCT_CURSUBC 9 -#define CCT_NUMWORDS 10 -#define CCT_RANGESTR 11 -#define CCT_RANGEPAT 12 - -/* Contains the real description for compctls */ - -struct compctl { - int refc; /* reference count */ - Compctl next; /* next compctl for -x */ - unsigned long mask; /* mask of things to complete (CC_*) */ - char *keyvar; /* for -k (variable) */ - char *glob; /* for -g (globbing) */ - char *str; /* for -s (expansion) */ - char *func; /* for -K (function) */ - char *explain; /* for -X (explanation) */ - char *ylist; /* for -y (user-defined desc. for listing) */ - char *prefix, *suffix; /* for -P and -S (prefix, suffix) */ - char *subcmd; /* for -l (command name to use) */ - char *withd; /* for -w (with directory */ - char *hpat; /* for -H (history pattern) */ - int hnum; /* for -H (number of events to search) */ - Compctl ext; /* for -x (first of the compctls after -x) */ - Compcond cond; /* for -x (condition for this compctl) */ - Compctl xor; /* for + (next of the xor'ed compctls) */ +/* List data. */ + +typedef struct cldata *Cldata; + +struct cldata { + int columns; /* screen width */ + int lines; /* screen height */ + int menuacc; /* value of global menuacc */ + int valid; /* no need to calculate anew */ + int nlist; /* number of matches to list */ + int nlines; /* number of lines needed */ + int hidden; /* != 0 if there are hidden matches */ + int onlyexpl; /* != 0 if only explanations to print */ + int showall; /* != 0 if hidden matches should be shown */ +}; + +typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int, + char *, struct stat *); + +/* Flags for fromcomp. */ + +#define FC_LINE 1 +#define FC_INWORD 2 + +/* Flags for special parameters. */ + +#define CPN_WORDS 0 +#define CP_WORDS (1 << CPN_WORDS) +#define CPN_CURRENT 1 +#define CP_CURRENT (1 << CPN_CURRENT) +#define CPN_PREFIX 2 +#define CP_PREFIX (1 << CPN_PREFIX) +#define CPN_SUFFIX 3 +#define CP_SUFFIX (1 << CPN_SUFFIX) +#define CPN_IPREFIX 4 +#define CP_IPREFIX (1 << CPN_IPREFIX) +#define CPN_ISUFFIX 5 +#define CP_ISUFFIX (1 << CPN_ISUFFIX) +#define CPN_QIPREFIX 6 +#define CP_QIPREFIX (1 << CPN_QIPREFIX) +#define CPN_QISUFFIX 7 +#define CP_QISUFFIX (1 << CPN_QISUFFIX) +#define CPN_COMPSTATE 8 +#define CP_COMPSTATE (1 << CPN_COMPSTATE) + +#define CP_REALPARAMS 9 +#define CP_ALLREALS ((unsigned int) 0x1ff) + + +#define CPN_NMATCHES 0 +#define CP_NMATCHES (1 << CPN_NMATCHES) +#define CPN_CONTEXT 1 +#define CP_CONTEXT (1 << CPN_CONTEXT) +#define CPN_PARAMETER 2 +#define CP_PARAMETER (1 << CPN_PARAMETER) +#define CPN_REDIRECT 3 +#define CP_REDIRECT (1 << CPN_REDIRECT) +#define CPN_QUOTE 4 +#define CP_QUOTE (1 << CPN_QUOTE) +#define CPN_QUOTING 5 +#define CP_QUOTING (1 << CPN_QUOTING) +#define CPN_RESTORE 6 +#define CP_RESTORE (1 << CPN_RESTORE) +#define CPN_LIST 7 +#define CP_LIST (1 << CPN_LIST) +#define CPN_INSERT 8 +#define CP_INSERT (1 << CPN_INSERT) +#define CPN_EXACT 9 +#define CP_EXACT (1 << CPN_EXACT) +#define CPN_EXACTSTR 10 +#define CP_EXACTSTR (1 << CPN_EXACTSTR) +#define CPN_PATMATCH 11 +#define CP_PATMATCH (1 << CPN_PATMATCH) +#define CPN_PATINSERT 12 +#define CP_PATINSERT (1 << CPN_PATINSERT) +#define CPN_UNAMBIG 13 +#define CP_UNAMBIG (1 << CPN_UNAMBIG) +#define CPN_UNAMBIGC 14 +#define CP_UNAMBIGC (1 << CPN_UNAMBIGC) +#define CPN_LISTMAX 15 +#define CP_LISTMAX (1 << CPN_LISTMAX) +#define CPN_LASTPROMPT 16 +#define CP_LASTPROMPT (1 << CPN_LASTPROMPT) +#define CPN_TOEND 17 +#define CP_TOEND (1 << CPN_TOEND) +#define CPN_OLDLIST 18 +#define CP_OLDLIST (1 << CPN_OLDLIST) +#define CPN_OLDINS 19 +#define CP_OLDINS (1 << CPN_OLDINS) +#define CPN_VARED 20 +#define CP_VARED (1 << CPN_VARED) +#define CPN_LISTLINES 21 +#define CP_LISTLINES (1 << CPN_LISTLINES) +#define CPN_QUOTES 22 +#define CP_QUOTES (1 << CPN_QUOTES) +#define CPN_IGNORED 23 +#define CP_IGNORED (1 << CPN_IGNORED) + +#define CP_KEYPARAMS 24 +#define CP_ALLKEYS ((unsigned int) 0xffffff) + +/* Hooks. */ + +#define INSERTMATCHHOOK (comphooks + 0) +#define MENUSTARTHOOK (comphooks + 1) +#define COMPCTLMAKEHOOK (comphooks + 2) +#define COMPCTLCLEANUPHOOK (comphooks + 3) +#define COMPLISTMATCHESHOOK (comphooks + 4) + +/* compctl hook data struct */ + +struct ccmakedat { + char *str; + int incmd; + int lst; +}; + +/* Data given to offered hooks. */ + +typedef struct chdata *Chdata; + +struct chdata { + Cmgroup matches; /* the matches generated */ + int num; /* the number of matches */ + Cmatch cur; /* current match or NULL */ }; -/* objects to complete */ -#define CC_FILES (1<<0) -#define CC_COMMPATH (1<<1) -#define CC_REMOVE (1<<2) -#define CC_OPTIONS (1<<3) -#define CC_VARS (1<<4) -#define CC_BINDINGS (1<<5) -#define CC_ARRAYS (1<<6) -#define CC_INTVARS (1<<7) -#define CC_SHFUNCS (1<<8) -#define CC_PARAMS (1<<9) -#define CC_ENVVARS (1<<10) -#define CC_JOBS (1<<11) -#define CC_RUNNING (1<<12) -#define CC_STOPPED (1<<13) -#define CC_BUILTINS (1<<14) -#define CC_ALREG (1<<15) -#define CC_ALGLOB (1<<16) -#define CC_USERS (1<<17) -#define CC_DISCMDS (1<<18) -#define CC_EXCMDS (1<<19) -#define CC_SCALARS (1<<20) -#define CC_READONLYS (1<<21) -#define CC_SPECIALS (1<<22) -#define CC_DELETE (1<<23) -#define CC_NAMED (1<<24) -#define CC_QUOTEFLAG (1<<25) -#define CC_EXTCMDS (1<<26) -#define CC_RESWDS (1<<27) -#define CC_DIRS (1<<28) - -#define CC_EXPANDEXPL (1<<30) -#define CC_RESERVED (1<<31) diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index ea9ff5b20..3cc080cac 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -28,9 +28,6 @@ */ #include "complete.mdh" -#define GLOBAL_PROTOTYPES -#include "zle_tricky.pro" -#undef GLOBAL_PROTOTYPES #include "compcore.pro" /* The last completion widget called. */ @@ -40,11 +37,17 @@ static Widget lastcompwidget; /* Flags saying what we have to do with the result. */ /**/ -int useexact, useline, uselist; +int useexact, useline, uselist, forcelist, startauto; + +/* Non-zero if we should go back to the last prompt. */ + +/**/ +int dolastprompt; /* Non-zero if we should keep an old list. */ /**/ +mod_export int oldlist, oldins; /* This is used to decide when the cursor should be moved to the end of * @@ -57,17 +60,17 @@ int movetoend; /* The match and group number to insert when starting menucompletion. */ /**/ -int insmnum, insgnum, insgroup, insspace; +mod_export int insmnum, insgnum, insgroup, insspace; /* Information about menucompletion. */ /**/ -struct menuinfo minfo; +mod_export struct menuinfo minfo; /* Number of matches accepted with accept-and-menu-complete */ /**/ -int menuacc; +mod_export int menuacc; /* Brace insertion stuff. */ @@ -77,7 +80,7 @@ int hasunqu, useqbr, brpcs, brscs; /* Flags saying in what kind of string we are. */ /**/ -int ispar, linwhat; +mod_export int ispar, linwhat; /* A parameter expansion prefix (like ${). */ @@ -92,7 +95,7 @@ int parflags; /* Match flags for all matches in this group. */ /**/ -int mflags; +mod_export int mflags; /* Flags saying how the parameter expression we are in is quoted. */ @@ -101,17 +104,18 @@ int parq, eparq; /* We store the following prefixes/suffixes: * * ipre,ripre -- the ignored prefix (quoted and unquoted) * - * isuf -- the ignored suffix * - * autoq -- quotes to automatically insert */ + * isuf -- the ignored suffix */ /**/ -char *ipre, *ripre, *isuf; +mod_export char *ipre, *ripre, *isuf; /* The list of matches. fmatches contains the matches we first ignore * * because of fignore. */ /**/ -LinkList matches, fmatches; +mod_export LinkList matches; +/**/ +LinkList fmatches; /* This holds the list of matches-groups. lastmatches holds the last list of * permanently allocated matches, pmatches is the same for the list @@ -120,47 +124,49 @@ LinkList matches, fmatches; * lmatches/lastlmatches is a pointer to the last element in the lists. */ /**/ -Cmgroup lastmatches, pmatches, amatches, lmatches, lastlmatches; +mod_export Cmgroup lastmatches, pmatches, amatches, lmatches, lastlmatches; /* Non-zero if we have permanently allocated matches (old and new). */ /**/ -int hasoldlist, hasperm; +mod_export int hasoldlist, hasperm; /* Non-zero if we have newly added matches. */ /**/ -int newmatches; +mod_export int newmatches; /* Number of permanently allocated matches and groups. */ /**/ -int permmnum, permgnum, lastpermmnum, lastpermgnum; +mod_export int permmnum, permgnum, lastpermmnum, lastpermgnum; /* The total number of matches and the number of matches to be listed. */ /**/ -int nmatches, smatches; +mod_export int nmatches; +/**/ +mod_export int smatches; /* != 0 if only explanation strings should be printed */ /**/ -int onlyexpl; +mod_export int onlyexpl; /* Information about the matches for listing. */ /**/ -struct cldata listdat; +mod_export struct cldata listdat; /* This flag is non-zero if we are completing a pattern (with globcomplete) */ /**/ -int ispattern, haspattern; +mod_export int ispattern, haspattern; -/* Non-zero if at least one match was added without -U. */ +/* Non-zero if at least one match was added without/with -U. */ /**/ -int hasmatched; +mod_export int hasmatched, hasunmatched; /* The current group of matches. */ @@ -170,12 +176,12 @@ Cmgroup mgroup; /* Match counter: all matches. */ /**/ -int mnum; +mod_export int mnum; /* The match counter when unambig_data() was called. */ /**/ -int unambig_mnum; +mod_export int unambig_mnum; /* Length of longest/shortest match. */ @@ -189,37 +195,37 @@ int maxmlen, minmlen; LinkList expls; /**/ -Cexpl curexpl; +mod_export Cexpl curexpl; /* A stack of completion matchers to be used. */ /**/ -Cmlist mstack; +mod_export Cmlist mstack; /* The completion matchers used when building new stuff for the line. */ /**/ -Cmlist bmatchers; +mod_export Cmlist bmatchers; /* A list with references to all matchers we used. */ /**/ -LinkList matchers; +mod_export LinkList matchers; /* A heap of free Cline structures. */ /**/ -Cline freecl; +mod_export Cline freecl; /* Ambiguous information. */ /**/ -Aminfo ainfo, fainfo; +mod_export Aminfo ainfo, fainfo; /* The memory heap to use for new style completion generation. */ /**/ -Heap compheap; +mod_export Heap compheap; /* A list of some data. * @@ -227,7 +233,7 @@ Heap compheap; * conceptually we don't know anything about compctls here... */ /**/ -LinkList allccs; +mod_export LinkList allccs; /* This says what of the state the line is in when completion is started * * came from a previous completion. If the FC_LINE bit is set, the * @@ -246,10 +252,6 @@ int fromcomp; /**/ int lastend; -/* Convenience macro for calling bslashquote() (formerly quotename()). */ - -#define quotename(s, e) bslashquote(s, e, instring) - #define inststr(X) inststrlen((X),1,-1) /* Main completion entry point, called from zle. */ @@ -260,153 +262,165 @@ do_completion(Hookdef dummy, Compldat dat) { int ret = 0, lst = dat->lst, incmd = dat->incmd; char *s = dat->s; + char *opm; + LinkNode n; - HEAPALLOC { - char *opm; - LinkNode n; - - pushheap(); - - ainfo = fainfo = NULL; - matchers = newlinklist(); - - hasunqu = 0; - useline = (lst != COMP_LIST_COMPLETE); - useexact = isset(RECEXACT); - uselist = (useline ? - ((isset(AUTOLIST) && !isset(BASHAUTOLIST)) ? - (isset(LISTAMBIGUOUS) ? 3 : 2) : 0) : 1); - zsfree(comppatmatch); - opm = comppatmatch = ztrdup(useglob ? "*" : ""); - zsfree(comppatinsert); - comppatinsert = ztrdup("menu"); - zsfree(compforcelist); - compforcelist = ztrdup(""); - haspattern = 0; - complistmax = getiparam("LISTMAX"); - zsfree(complastprompt); - complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) || - (unset(ALWAYSLASTPROMPT) && zmult != 1)) ? - "yes" : ""); - movetoend = ((cs == we || isset(ALWAYSTOEND)) ? 2 : 1); - showinglist = 0; - hasmatched = 0; - minmlen = 1000000; - maxmlen = -1; - - /* Make sure we have the completion list and compctl. */ - if (makecomplist(s, incmd, lst)) { - /* Error condition: feeeeeeeeeeeeep(). */ - cs = 0; - foredel(ll); - inststr(origline); - cs = origcs; - clearlist = 1; - ret = 1; - minfo.cur = NULL; - goto compend; - } - zsfree(lastprebr); - zsfree(lastpostbr); - lastprebr = lastpostbr = NULL; - - if (comppatmatch && *comppatmatch && comppatmatch != opm) - haspattern = 1; - if (!useline && uselist) { - /* All this and the guy only wants to see the list, sigh. */ - cs = 0; - foredel(ll); - inststr(origline); - cs = origcs; - showinglist = -2; - } else if (useline == 2 && nmatches > 1) { - int first = 1, nm = nmatches; - Cmatch *mc; + pushheap(); + + ainfo = fainfo = NULL; + matchers = newlinklist(); + + zsfree(compqstack); + compqstack = ztrdup("\\"); + if (instring == 2) + compqstack[0] = '"'; + else if (instring) + compqstack[0] = '\''; + + hasunqu = 0; + useline = (lst != COMP_LIST_COMPLETE); + useexact = isset(RECEXACT); + zsfree(compexactstr); + compexactstr = ztrdup(""); + uselist = (useline ? + ((isset(AUTOLIST) && !isset(BASHAUTOLIST)) ? + (isset(LISTAMBIGUOUS) ? 3 : 2) : 0) : 1); + zsfree(comppatmatch); + opm = comppatmatch = ztrdup(useglob ? "*" : ""); + zsfree(comppatinsert); + comppatinsert = ztrdup("menu"); + forcelist = 0; + haspattern = 0; + complistmax = getiparam("LISTMAX"); + zsfree(complastprompt); + complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) || + (unset(ALWAYSLASTPROMPT) && zmult != 1)) ? + "yes" : ""); + dolastprompt = 1; + zsfree(complist); + complist = ztrdup(isset(LISTROWSFIRST) ? + (isset(LISTPACKED) ? "packed rows" : "rows") : + (isset(LISTPACKED) ? "packed" : "")); + startauto = isset(AUTOMENU); + movetoend = ((cs == we || isset(ALWAYSTOEND)) ? 2 : 1); + showinglist = 0; + hasmatched = hasunmatched = 0; + minmlen = 1000000; + maxmlen = -1; + compignored = 0; + + /* Make sure we have the completion list and compctl. */ + if (makecomplist(s, incmd, lst)) { + /* Error condition: feeeeeeeeeeeeep(). */ + cs = 0; + foredel(ll); + inststr(origline); + cs = origcs; + clearlist = 1; + ret = 1; + minfo.cur = NULL; + goto compend; + } + zsfree(lastprebr); + zsfree(lastpostbr); + lastprebr = lastpostbr = NULL; + + if (comppatmatch && *comppatmatch && comppatmatch != opm) + haspattern = 1; + if (!useline && uselist) { + /* All this and the guy only wants to see the list, sigh. */ + cs = 0; + foredel(ll); + inststr(origline); + cs = origcs; + showinglist = -2; + } else if (useline == 2 && nmatches > 1) { + int first = 1, nm = nmatches; + Cmatch *mc; - menucmp = 1; - menuacc = 0; + menucmp = 1; + menuacc = 0; - for (minfo.group = amatches; - minfo.group && !(minfo.group)->mcount; - minfo.group = (minfo.group)->next); + for (minfo.group = amatches; + minfo.group && !(minfo.group)->mcount; + minfo.group = (minfo.group)->next); - mc = (minfo.group)->matches; + mc = (minfo.group)->matches; - while (1) { - if (!first) - acceptlast(); - first = 0; + while (1) { + if (!first) + accept_last(); + first = 0; - if (!--nm) - menucmp = 0; + if (!--nm) + menucmp = 0; - do_single(*mc); - minfo.cur = mc; + do_single(*mc); + minfo.cur = mc; - if (!*++(minfo.cur)) { - do { - if (!(minfo.group = (minfo.group)->next)) - break; - } while (!(minfo.group)->mcount); - if (!minfo.group) + if (!*++(minfo.cur)) { + do { + if (!(minfo.group = (minfo.group)->next)) break; - minfo.cur = minfo.group->matches; - } - mc = minfo.cur; + } while (!(minfo.group)->mcount); + if (!minfo.group) + break; + minfo.cur = minfo.group->matches; } - menucmp = 0; - minfo.cur = NULL; - - if (compforcelist && *compforcelist && uselist) - showinglist = -2; - else - invalidatelist(); - } else if (useline) { - /* We have matches. */ - if (nmatches > 1) { - /* There is more than one match. */ - ret = do_ambiguous(); - } else if (nmatches == 1) { - /* Only one match. */ - Cmgroup m = amatches; - - while (!m->mcount) - m = m->next; - minfo.cur = NULL; - minfo.asked = 0; - do_single(m->matches[0]); - if (compforcelist && *compforcelist) { - if (uselist) - showinglist = -2; - else - clearlist = 1; - } else - invalidatelist(); - } - } else { - invalidatelist(); - if (compforcelist && *compforcelist) - clearlist = 1; - cs = 0; - foredel(ll); - inststr(origline); - cs = origcs; + mc = minfo.cur; } - /* Print the explanation strings if needed. */ - if (!showinglist && validlist && usemenu != 2 && nmatches != 1 && - useline != 2 && (!oldlist || !listshown)) { - onlyexpl = 1; + menucmp = 0; + minfo.cur = NULL; + + if (forcelist) showinglist = -2; + else + invalidatelist(); + } else if (useline) { + /* We have matches. */ + if (nmatches > 1) { + /* There is more than one match. */ + ret = do_ambiguous(); + } else if (nmatches == 1) { + /* Only one match. */ + Cmgroup m = amatches; + + while (!m->mcount) + m = m->next; + minfo.cur = NULL; + minfo.asked = 0; + do_single(m->matches[0]); + if (forcelist) { + if (uselist) + showinglist = -2; + else + clearlist = 1; + } else + invalidatelist(); } - compend: - for (n = firstnode(matchers); n; incnode(n)) - freecmatcher((Cmatcher) getdata(n)); + } else { + invalidatelist(); + if (forcelist) + clearlist = 1; + cs = 0; + foredel(ll); + inststr(origline); + cs = origcs; + } + /* Print the explanation strings if needed. */ + if (!showinglist && validlist && usemenu != 2 && nmatches != 1 && + useline != 2 && (!oldlist || !listshown)) { + onlyexpl = 1; + showinglist = -2; + } + compend: + for (n = firstnode(matchers); n; incnode(n)) + freecmatcher((Cmatcher) getdata(n)); - ll = strlen((char *)line); - if (cs > ll) - cs = ll; - popheap(); - } LASTALLOC; + ll = strlen((char *)line); + if (cs > ll) + cs = ll; + popheap(); return ret; } @@ -428,7 +442,7 @@ before_complete(Hookdef dummy, int *lst) /* If we are doing a menu-completion... */ if (menucmp && *lst != COMP_LIST_EXPAND && - (!compwidget || compwidget == lastcompwidget)) { + (menucmp != 1 || !compwidget || compwidget == lastcompwidget)) { do_menucmp(*lst); return 1; } @@ -442,13 +456,12 @@ before_complete(Hookdef dummy, int *lst) /* We may have to reset the cursor to its position after the * * string inserted by the last completion. */ - if (fromcomp & FC_INWORD) - if ((cs = lastend) > ll) - cs = ll; + if ((fromcomp & FC_INWORD) && (cs = lastend) > ll) + cs = ll; /* Check if we have to start a menu-completion (via automenu). */ - if (isset(AUTOMENU) && lastambig && + if (startauto && lastambig && (!isset(BASHAUTOLIST) || lastambig == 2)) usemenu = 2; @@ -477,11 +490,11 @@ after_complete(Hookdef dummy, Compldat dat) static void callcompfunc(char *s, char *fn) { - List list; + Eprog prog; int lv = lastval; char buf[20]; - if ((list = getshfunc(fn)) != &dummy_list) { + if ((prog = getshfunc(fn)) != &dummy_eprog) { char **p, *tmp; int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext; unsigned int rset, kset; @@ -493,7 +506,7 @@ callcompfunc(char *s, char *fn) rset = CP_ALLREALS; kset = CP_ALLKEYS & ~(CP_PARAMETER | CP_REDIRECT | CP_QUOTE | CP_QUOTING | - CP_EXACTSTR | CP_FORCELIST | CP_OLDLIST | CP_OLDINS | + CP_EXACTSTR | CP_OLDLIST | CP_OLDINS | (useglob ? 0 : CP_PATMATCH)); zsfree(compvared); if (varedarg) { @@ -566,16 +579,11 @@ callcompfunc(char *s, char *fn) if (usea && (!aadd || clwords[0])) { char **q; - PERMALLOC { - q = compwords = (char **) - zalloc((clwnum + 1) * sizeof(char *)); - for (p = clwords + aadd; *p; p++, q++) { - tmp = dupstring(*p); - untokenize(tmp); - *q = ztrdup(tmp); - } - *q = NULL; - } LASTALLOC; + q = compwords = (char **) + zalloc((clwnum + 1) * sizeof(char *)); + for (p = clwords + aadd; *p; p++, q++) + untokenize(*q = ztrdup(*p)); + *q = NULL; } else compwords = (char **) zcalloc(sizeof(char *)); @@ -603,7 +611,7 @@ callcompfunc(char *s, char *fn) zsfree(compprefix); zsfree(compsuffix); if (unset(COMPLETEINWORD)) { - tmp = quotename(s, NULL); + tmp = multiquote(s, 0); untokenize(tmp); compprefix = ztrdup(tmp); compsuffix = ztrdup(""); @@ -614,11 +622,11 @@ callcompfunc(char *s, char *fn) sav = *ss; *ss = '\0'; - tmp = quotename(s, NULL); + tmp = multiquote(s, 0); untokenize(tmp); compprefix = ztrdup(tmp); *ss = sav; - ss = quotename(ss, NULL); + ss = multiquote(ss, 0); untokenize(ss); compsuffix = ztrdup(ss); } @@ -639,11 +647,20 @@ callcompfunc(char *s, char *fn) case 2: complist = "autolist"; break; case 3: complist = "ambiguous"; break; } + if (isset(LISTPACKED)) + complist = dyncat(complist, " packed"); + if (isset(LISTROWSFIRST)) + complist = dyncat(complist, " rows"); + complist = ztrdup(complist); zsfree(compinsert); if (useline) { switch (usemenu) { - case 0: compinsert = "unambiguous"; break; + case 0: + compinsert = (isset(AUTOMENU) ? + "automenu-unambiguous" : + "unambiguous"); + break; case 1: compinsert = "menu"; break; case 2: compinsert = "automenu"; break; } @@ -701,7 +718,7 @@ callcompfunc(char *s, char *fn) while (*p) addlinknode(largs, dupstring(*p++)); } - doshfunc(fn, list, largs, 0, 0); + doshfunc(fn, prog, largs, 0, 0); cfret = lastval; lastval = olv; } OLDHEAPS; @@ -720,13 +737,14 @@ callcompfunc(char *s, char *fn) uselist = 3; else uselist = 0; - + forcelist = (complist && strstr(complist, "force")); onlyexpl = (complist && strstr(complist, "expl")); if (!compinsert) useline = 0; else if (!strcmp(compinsert, "unambig") || - !strcmp(compinsert, "unambiguous")) + !strcmp(compinsert, "unambiguous") || + !strcmp(compinsert, "automenu-unambiguous")) useline = 1, usemenu = 0; else if (!strcmp(compinsert, "menu")) useline = 1, usemenu = 1; @@ -747,6 +765,8 @@ callcompfunc(char *s, char *fn) insspace = (compinsert[strlen(compinsert) - 1] == ' '); } else useline = usemenu = 0; + startauto = (compinsert && + !strcmp(compinsert, "automenu-unambiguous")); useexact = (compexact && !strcmp(compexact, "accept")); if (!comptoend || !*comptoend) @@ -782,60 +802,21 @@ callcompfunc(char *s, char *fn) static int makecomplist(char *s, int incmd, int lst) { - struct cmlist ms; - Cmlist m; - char *p, *os = s; - int onm = nmatches, osi = movefd(0); + char *p; /* Inside $... ? */ if (compfunc && (p = check_param(s, 0, 0))) - os = s = p; - - /* We build a copy of the list of matchers to use to make sure that this - * works even if a shell function called from the completion code changes - * the global matchers. */ - - if ((m = cmatcher)) { - Cmlist mm, *mp = &mm; - int n; - - for (n = 0; m; m = m->next, n++) { - if (m->matcher) { - *mp = (Cmlist) zhalloc(sizeof(struct cmlist)); - (*mp)->matcher = m->matcher; - (*mp)->next = NULL; - (*mp)->str = dupstring(m->str); - mp = &((*mp)->next); - addlinknode(matchers, m->matcher); - m->matcher->refc++; - } - } - m = mm; - compmatcher = 1; - compmatchertot = n; - } else - compmatcher = 0; + s = p; linwhat = inwhat; - /* Walk through the global matchers. */ - for (;;) { + if (compfunc) { + char *os = s; + int onm = nmatches, osi = movefd(0); + bmatchers = NULL; - zsfree(compmatcherstr); - if (m) { - ms.next = NULL; - ms.matcher = m->matcher; - mstack = &ms; - - /* Store the matchers used in the bmatchers list which is used - * when building new parts for the string to insert into the - * line. */ - add_bmatchers(m->matcher); - compmatcherstr = ztrdup(m->str); - } else { - mstack = NULL; - compmatcherstr = ztrdup(""); - } + mstack = NULL; + ainfo = (Aminfo) hcalloc(sizeof(struct aminfo)); fainfo = (Aminfo) hcalloc(sizeof(struct aminfo)); @@ -852,23 +833,12 @@ makecomplist(char *s, int incmd, int lst) begcmgroup("default", 0); menucmp = menuacc = newmatches = onlyexpl = 0; - runhookdef(COMPCTLBEFOREHOOK, NULL); - s = dupstring(os); - if (compfunc) - callcompfunc(s, compfunc); - else { - struct ccmakedat dat; - - dat.str = s; - dat.incmd = incmd; - dat.lst = lst; - runhookdef(COMPCTLMAKEHOOK, (void *) &dat); - } + callcompfunc(s, compfunc); endcmgroup(NULL); - runhookdef(COMPCTLAFTERHOOK, - (void *) ((amatches && !oldlist) ? 1L : 0L)); + /* Needed for compcall. */ + runhookdef(COMPCTLCLEANUPHOOK, NULL); if (oldlist) { nmatches = onm; @@ -884,16 +854,14 @@ makecomplist(char *s, int incmd, int lst) return 0; } - PERMALLOC { - if (lastmatches) { - freematches(lastmatches); - lastmatches = NULL; - } - permmatches(1); - amatches = pmatches; - lastpermmnum = permmnum; - lastpermgnum = permgnum; - } LASTALLOC; + if (lastmatches) { + freematches(lastmatches); + lastmatches = NULL; + } + permmatches(1); + amatches = pmatches; + lastpermmnum = permmnum; + lastpermgnum = permgnum; lastmatches = pmatches; lastlmatches = lmatches; @@ -908,20 +876,69 @@ makecomplist(char *s, int incmd, int lst) return 0; } - if (!m || !(m = m->next)) - break; + redup(osi, 0); + return 1; + } else { + struct ccmakedat dat; + + dat.str = s; + dat.incmd = incmd; + dat.lst = lst; + runhookdef(COMPCTLMAKEHOOK, (void *) &dat); + + /* Needed for compcall. */ + runhookdef(COMPCTLCLEANUPHOOK, NULL); + + return dat.lst; + } +} + +/**/ +mod_export char * +multiquote(char *s, int ign) +{ + if (s) { + char *os = s, *p = compqstack; + + if (p && *p && (ign < 1 || p[ign])) { + if (ign > 0) + p += ign; + while (*p) { + if (ign >= 0 || p[1]) + s = bslashquote(s, NULL, + (*p == '\'' ? 1 : (*p == '"' ? 2 : 0))); + p++; + } + } + return (s == os ? dupstring(s) : s); + } + DPUTS(1, "BUG: null pointer in multiquote()"); + return NULL; +} - errflag = 0; - compmatcher++; +/**/ +mod_export char * +tildequote(char *s, int ign) +{ + if (s) { + int tilde; + + if ((tilde = (*s == '~'))) + *s = 'x'; + s = multiquote(s, ign); + if (tilde) + *s = '~'; + + return s; } - redup(osi, 0); - return 1; + DPUTS(1, "BUG: null pointer in tildequote()"); + return NULL; } /* Check if we have to complete a parameter name. */ /**/ -char * +mod_export char * check_param(char *s, int set, int test) { char *p; @@ -1046,7 +1063,7 @@ check_param(char *s, int set, int test) /* Copy the given string and remove backslashes from the copy and return it. */ /**/ -char * +mod_export char * rembslash(char *s) { char *t = s = dupstring(s); @@ -1065,7 +1082,7 @@ rembslash(char *s) /* This should probably be moved into tokenize(). */ /**/ -char * +mod_export char * ctokenize(char *p) { char *r = p; @@ -1091,7 +1108,7 @@ ctokenize(char *p) } /**/ -char * +mod_export char * comp_str(int *ipl, int *pl, int untok) { char *p = dupstring(compprefix); @@ -1122,23 +1139,21 @@ comp_str(int *ipl, int *pl, int untok) return str; } -/* This is for compset -q. */ - /**/ int set_comp_sep(void) { int lip, lp; - char *s = comp_str(&lip, &lp, 0); + char *s = comp_str(&lip, &lp, 1); LinkList foo = newlinklist(); LinkNode n; int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs; - int tl, got = 0, i = 0, cur = -1, oll = ll, sl; - int ois = instring, oib = inbackt, noffs = lip + lp; - char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs; + int tl, got = 0, i = 0, cur = -1, oll = ll, sl, remq; + int ois = instring, oib = inbackt, noffs = lp; + char *tmp, *p, *ns, *ol = (char *) line, sav, *qp, *qs, *ts, qc = '\0'; - if (compisuffix) - s = dyncat(s, compisuffix); + s += lip; + wb += lip; untokenize(s); swb = swe = soffs = 0; @@ -1155,7 +1170,8 @@ set_comp_sep(void) memcpy(tmp + 1, s, noffs); tmp[(scs = cs = 1 + noffs)] = 'x'; strcpy(tmp + 2 + noffs, s + noffs); - tmp = rembslash(tmp); + if ((remq = (*compqstack == '\\'))) + tmp = rembslash(tmp); inpush(dupstrspace(tmp), 0, NULL); line = (unsigned char *) tmp; ll = tl - 1; @@ -1218,21 +1234,31 @@ set_comp_sep(void) *p = '\''; } offs = owb; + + untokenize(ts = dupstring(ns)); + if (*ns == Snull || *ns == Dnull) { instring = (*ns == Snull ? 1 : 2); inbackt = 0; swb++; if (ns[strlen(ns) - 1] == *ns && ns[1]) swe--; - autoq = (*ns == Snull ? '\'' : '"'); + zsfree(autoq); + autoq = ztrdup(compqstack[1] ? "" : + multiquote(*ns == Snull ? "'" : "\"", 1)); + qc = (*ns == Snull ? '\'' : '"'); + ts++; } else { instring = 0; - autoq = '\0'; + zsfree(autoq); + autoq = NULL; } for (p = ns, i = swb; *p; p++, i++) { if (INULL(*p)) { - if (i < scs) - soffs--; + if (i < scs) { + if (remq && *p == Bnull && p[1]) + swb -= 2; + } if (p[1] || *p != Bnull) { if (*p == Bnull) { if (scs == i + 1) @@ -1248,17 +1274,28 @@ set_comp_sep(void) chuck(p--); } } + ns = ts; + + if (instring && strchr(compqstack, '\\')) { + int rl = strlen(ns), ql = strlen(multiquote(ns, !!compqstack[1])); + + if (ql > rl) + swb -= ql - rl; + } sav = s[(i = swb - 1)]; s[i] = '\0'; - qp = tricat(qipre, rembslash(s), ""); + qp = rembslash(s); s[i] = sav; if (swe < swb) swe = swb; swe--; sl = strlen(s); - if (swe > sl) - swe = sl, ns[swe - swb + 1] = '\0'; - qs = tricat(rembslash(s + swe), qisuf, ""); + if (swe > sl) { + swe = sl; + if (strlen(ns) > swe - swb + 1) + ns[swe - swb + 1] = '\0'; + } + qs = rembslash(s + swe); sl = strlen(ns); if (soffs > sl) soffs = sl; @@ -1266,6 +1303,11 @@ set_comp_sep(void) { int set = CP_QUOTE | CP_QUOTING, unset = 0; + p = tricat((instring ? (instring == 1 ? "'" : "\"") : "\\"), + compqstack, ""); + zsfree(compqstack); + compqstack = p; + zsfree(compquote); zsfree(compquoting); if (instring == 2) { @@ -1283,11 +1325,11 @@ set_comp_sep(void) compquoting = ztrdup(compquoting); comp_setunset(0, 0, set, unset); + zsfree(compprefix); + zsfree(compsuffix); if (unset(COMPLETEINWORD)) { untokenize(ns); - zsfree(compprefix); compprefix = ztrdup(ns); - zsfree(compsuffix); compsuffix = ztrdup(""); } else { char *ss, sav; @@ -1302,21 +1344,16 @@ set_comp_sep(void) untokenize(ss); compsuffix = ztrdup(ss); } + tmp = tricat(compqiprefix, compiprefix, multiquote(qp, 1)); + zsfree(compqiprefix); + compqiprefix = tmp; + tmp = tricat(multiquote(qs, 1), compisuffix, compqisuffix); + zsfree(compqisuffix); + compqisuffix = tmp; zsfree(compiprefix); compiprefix = ztrdup(""); zsfree(compisuffix); compisuffix = ztrdup(""); - zsfree(compqiprefix); - zsfree(compqisuffix); - if (ois) { - compqiprefix = qp; - compqisuffix = qs; - } else { - compqiprefix = ztrdup(quotename(qp, NULL)); - zsfree(qp); - compqisuffix = ztrdup(quotename(qs, NULL)); - zsfree(qs); - } freearray(compwords); i = countlinknodes(foo); compwords = (char **) zalloc((i + 1) * sizeof(char *)); @@ -1327,7 +1364,6 @@ set_comp_sep(void) compcurrent = cur + 1; compwords[i] = NULL; } - autoq = oaq; instring = ois; inbackt = oib; @@ -1337,7 +1373,7 @@ set_comp_sep(void) /* This stores the strings from the list in an array. */ /**/ -void +mod_export void set_list_array(char *name, LinkList l) { char **a, **p; @@ -1354,7 +1390,7 @@ set_list_array(char *name, LinkList l) /* Get the words from a variable or a (list of words). */ /**/ -char ** +mod_export char ** get_user_var(char *nam) { if (!nam) @@ -1423,19 +1459,37 @@ int addmatches(Cadata dat, char **argv) { char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL; - char **aign = NULL, **dparr = NULL, oaq = autoq, *oppre = dat->ppre; - char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL; + char **aign = NULL, **dparr = NULL, *oaq = autoq, *oppre = dat->ppre; + char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL, *ibuf = NULL; int lpl, lsl, pl, sl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0; + int ppl = 0, psl = 0; int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern; - int oisalt = 0, isalt, isexact, doadd, ois = instring, oib = inbackt; + int isexact, doadd, ois = instring, oib = inbackt; Cline lc = NULL, pline = NULL, sline = NULL; Cmatch cm; struct cmlist mst; Cmlist oms = mstack; - Patprog cp = NULL; + Patprog cp = NULL, *pign = NULL; LinkList aparl = NULL, oparl = NULL, dparl = NULL; Brinfo bp, bpl = brbeg, obpl, bsl = brend, obsl; + if (!*argv) { + SWITCHHEAPS(compheap) { + /* Select the group in which to store the matches. */ + gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT : 0) | + ((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) | + ((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0)); + if (dat->group) { + endcmgroup(NULL); + begcmgroup(dat->group, gflags); + } else { + endcmgroup(NULL); + begcmgroup("default", 0); + } + } SWITCHBACKHEAPS; + + return 1; + } for (bp = brbeg; bp; bp = bp->next) bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos); for (bp = brend; bp; bp = bp->next) @@ -1447,300 +1501,373 @@ addmatches(Cadata dat, char **argv) if (qc == '`') { instring = 0; inbackt = 0; - autoq = '\0'; + autoq = ""; } else { + char buf[2]; + instring = (qc == '\'' ? 1 : 2); inbackt = 0; - autoq = qc; + buf[0] = qc; + buf[1] = '\0'; + autoq = multiquote(buf, 1); } } else { instring = inbackt = 0; - autoq = '\0'; + autoq = NULL; } qipre = ztrdup(compqiprefix ? compqiprefix : ""); qisuf = ztrdup(compqisuffix ? compqisuffix : ""); + useexact = (compexact && !strcmp(compexact, "accept")); + /* Switch back to the heap that was used when the completion widget * was invoked. */ SWITCHHEAPS(compheap) { - HEAPALLOC { - if ((doadd = (!dat->apar && !dat->opar && !dat->dpar)) && - (dat->aflags & CAF_MATCH)) + if ((doadd = (!dat->apar && !dat->opar && !dat->dpar))) { + if (dat->aflags & CAF_MATCH) hasmatched = 1; - if (dat->apar) - aparl = newlinklist(); - if (dat->opar) - oparl = newlinklist(); - if (dat->dpar) { - if (*(dat->dpar) == '(') - dparr = NULL; - else if ((dparr = get_user_var(dat->dpar)) && !*dparr) - dparr = NULL; - dparl = newlinklist(); - } - if (dat->exp) { - curexpl = (Cexpl) zhalloc(sizeof(struct cexpl)); - curexpl->count = curexpl->fcount = 0; - curexpl->str = dupstring(dat->exp); - } else - curexpl = NULL; + else + hasunmatched = 1; + } + if (dat->apar) + aparl = newlinklist(); + if (dat->opar) + oparl = newlinklist(); + if (dat->dpar) { + if (*(dat->dpar) == '(') + dparr = NULL; + else if ((dparr = get_user_var(dat->dpar)) && !*dparr) + dparr = NULL; + dparl = newlinklist(); + } + if (dat->exp) { + curexpl = (Cexpl) zhalloc(sizeof(struct cexpl)); + curexpl->count = curexpl->fcount = 0; + curexpl->str = dupstring(dat->exp); + } else + curexpl = NULL; - /* Store the matcher in our stack of matchers. */ - if (dat->match) { - mst.next = mstack; - mst.matcher = dat->match; - mstack = &mst; + /* Store the matcher in our stack of matchers. */ + if (dat->match) { + mst.next = mstack; + mst.matcher = dat->match; + mstack = &mst; - if (!mnum) - add_bmatchers(dat->match); + add_bmatchers(dat->match); - addlinknode(matchers, dat->match); - dat->match->refc++; + addlinknode(matchers, dat->match); + dat->match->refc++; + } + if (mnum && (mstack || bmatchers)) + update_bmatchers(); + + /* Get the suffixes to ignore. */ + if (dat->ign && (aign = get_user_var(dat->ign))) { + char **ap, **sp, *tmp; + Patprog *pp, prog; + + pign = (Patprog *) zhalloc((arrlen(aign) + 1) * sizeof(Patprog)); + + for (ap = sp = aign, pp = pign; (tmp = *ap); ap++) { + tokenize(tmp); + remnulargs(tmp); + if (((tmp[0] == Quest && tmp[1] == Star) || + (tmp[1] == Quest && tmp[0] == Star)) && + tmp[2] && !haswilds(tmp + 2)) + untokenize(*sp++ = tmp + 2); + else if ((prog = patcompile(tmp, 0, NULL))) + *pp++ = prog; } - if (mnum && (mstack || bmatchers)) - update_bmatchers(); - - /* Get the suffixes to ignore. */ - if (dat->ign) - aign = get_user_var(dat->ign); - /* Get the display strings. */ - if (dat->disp) - if ((disp = get_user_var(dat->disp))) - disp--; - /* Get the contents of the completion variables if we have - * to perform matching. */ - if (dat->aflags & CAF_MATCH) { - lipre = dupstring(compiprefix); - lisuf = dupstring(compisuffix); - lpre = dupstring(compprefix); - lsuf = dupstring(compsuffix); - llpl = strlen(lpre); - llsl = strlen(lsuf); - /* Test if there is an existing -P prefix. */ - if (dat->pre && *dat->pre) { - char *dp = rembslash(dat->pre); - - pl = pfxlen(dp, lpre); - llpl -= pl; - lpre += pl; + *sp = NULL; + *pp = NULL; + if (!*aign) + aign = NULL; + if (!*pign) + pign = NULL; + } + /* Get the display strings. */ + if (dat->disp) + if ((disp = get_user_var(dat->disp))) + disp--; + /* Get the contents of the completion variables if we have + * to perform matching. */ + if (dat->aflags & CAF_MATCH) { + lipre = dupstring(compiprefix); + lisuf = dupstring(compisuffix); + lpre = dupstring(compprefix); + lsuf = dupstring(compsuffix); + llpl = strlen(lpre); + llsl = strlen(lsuf); + /* Test if there is an existing -P prefix. */ + if (dat->pre && *dat->pre) { + pl = pfxlen(dat->pre, lpre); + llpl -= pl; + lpre += pl; + } + } + /* Now duplicate the strings we have from the command line. */ + if (dat->ipre) + dat->ipre = (lipre ? dyncat(lipre, dat->ipre) : + dupstring(dat->ipre)); + else if (lipre) + dat->ipre = lipre; + if (dat->isuf) + dat->isuf = (lisuf ? dyncat(lisuf, dat->isuf) : + dupstring(dat->isuf)); + else if (lisuf) + dat->isuf = lisuf; + if (dat->ppre) { + dat->ppre = ((dat->flags & CMF_FILE) ? + tildequote(dat->ppre, !!(dat->aflags & CAF_QUOTE)) : + multiquote(dat->ppre, !!(dat->aflags & CAF_QUOTE))); + lpl = strlen(dat->ppre); + } else + lpl = 0; + if (dat->psuf) { + dat->psuf = multiquote(dat->psuf, !!(dat->aflags & CAF_QUOTE)); + lsl = strlen(dat->psuf); + } else + lsl = 0; + if (dat->aflags & CAF_MATCH) { + int ml, gfl = 0; + char *globflag = NULL; + + if (comppatmatch && *comppatmatch && + dat->ppre && lpre[0] == '(' && lpre[1] == '#') { + char *p; + + for (p = lpre + 2; *p && *p != ')'; p++); + + if (*p == ')') { + char sav = p[1]; + + p[1] = '\0'; + globflag = dupstring(lpre); + gfl = p - lpre + 1; + p[1] = sav; + + lpre = p + 1; + llpl -= gfl; } } - /* Now duplicate the strings we have from the command line. */ - if (dat->ipre) - dat->ipre = (lipre ? dyncat(lipre, dat->ipre) : - dupstring(dat->ipre)); - else if (lipre) - dat->ipre = lipre; - if (dat->isuf) - dat->isuf = (lisuf ? dyncat(lisuf, dat->isuf) : - dupstring(dat->isuf)); - else if (lisuf) - dat->isuf = lisuf; - if (dat->ppre) { - if (!(dat->aflags & CAF_QUOTE)) { - dat->ppre = quotename(dat->ppre, NULL); - if ((dat->flags & CMF_FILE) && - dat->ppre[0] == '\\' && dat->ppre[1] == '~') - chuck(dat->ppre); - } else - dat->ppre = dupstring(dat->ppre); - lpl = strlen(dat->ppre); - } else - lpl = 0; - if (dat->psuf) { - if (!(dat->aflags & CAF_QUOTE)) - dat->psuf = quotename(dat->psuf, NULL); - else - dat->psuf = dupstring(dat->psuf); - lsl = strlen(dat->psuf); - } else - lsl = 0; - if (dat->aflags & CAF_MATCH) { - int ml; - - s = dat->ppre ? dat->ppre : ""; - if ((ml = match_str(lpre, s, &bpl, 0, NULL, 0, 0, 1)) >= 0) { - if (matchsubs) { - Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0); - - tmp->prefix = matchsubs; - if (matchlastpart) - matchlastpart->next = tmp; - else - matchparts = tmp; - } - pline = matchparts; - lpre += ml; - bcp = ml; - bpadd = strlen(s) - ml; - } else { - if (llpl <= lpl && strpfx(lpre, s)) - lpre = ""; - else if (llpl > lpl && strpfx(s, lpre)) - lpre += lpl; + s = dat->ppre ? dat->ppre : ""; + if ((ml = match_str(lpre, s, &bpl, 0, NULL, 0, 0, 1)) >= 0) { + if (matchsubs) { + Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0); + + tmp->prefix = matchsubs; + if (matchlastpart) + matchlastpart->next = tmp; else - *argv = NULL; - bcp = lpl; + matchparts = tmp; } - - s = dat->psuf ? dat->psuf : ""; - if ((ml = match_str(lsuf, s, &bsl, 0, NULL, 1, 0, 1)) >= 0) { - if (matchsubs) { - Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, CLF_SUF); - - tmp->suffix = matchsubs; - if (matchlastpart) - matchlastpart->next = tmp; - else - matchparts = tmp; - } - sline = revert_cline(matchparts); - lsuf[llsl - ml] = '\0'; - bcs = ml; - bsadd = strlen(s) - ml; - } else { - if (llsl <= lsl && strsfx(lsuf, s)) - lsuf = ""; - else if (llsl > lsl && strsfx(s, lsuf)) - lsuf[llsl - lsl] = '\0'; + pline = matchparts; + lpre += ml; + llpl -= ml; + bcp = ml; + bpadd = strlen(s) - ml; + } else { + if (llpl <= lpl && strpfx(lpre, s)) + lpre = ""; + else if (llpl > lpl && strpfx(s, lpre)) + lpre += lpl; + else + *argv = NULL; + bcp = lpl; + } + s = dat->psuf ? dat->psuf : ""; + if ((ml = match_str(lsuf, s, &bsl, 0, NULL, 1, 0, 1)) >= 0) { + if (matchsubs) { + Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, CLF_SUF); + + tmp->suffix = matchsubs; + if (matchlastpart) + matchlastpart->next = tmp; else - *argv = NULL; - bcs = lsl; + matchparts = tmp; } - if (comppatmatch && *comppatmatch) { - int is = (*comppatmatch == '*'); - char *tmp = (char *) zhalloc(2 + llpl + llsl); + sline = revert_cline(matchparts); + lsuf[llsl - ml] = '\0'; + llsl -= ml; + bcs = ml; + bsadd = strlen(s) - ml; + } else { + if (llsl <= lsl && strsfx(lsuf, s)) + lsuf = ""; + else if (llsl > lsl && strsfx(s, lsuf)) + lsuf[llsl - lsl] = '\0'; + else + *argv = NULL; + bcs = lsl; + } + if (comppatmatch && *comppatmatch) { + int is = (*comppatmatch == '*'); + char *tmp = (char *) zhalloc(2 + llpl + llsl + gfl); + if (gfl) { + strcpy(tmp, globflag); + strcat(tmp, lpre); + } else strcpy(tmp, lpre); - tmp[llpl] = 'x'; - strcpy(tmp + llpl + is, lsuf); - - tokenize(tmp); - remnulargs(tmp); - if (haswilds(tmp)) { - if (is) - tmp[llpl] = Star; - if ((cp = patcompile(tmp, 0, NULL))) - haspattern = 1; - } + tmp[llpl + gfl] = 'x'; + strcpy(tmp + llpl + gfl + is, lsuf); + + tokenize(tmp); + remnulargs(tmp); + if (haswilds(tmp)) { + if (is) + tmp[llpl + gfl] = Star; + if ((cp = patcompile(tmp, 0, NULL))) + haspattern = 1; } } - if (*argv) { - if (dat->pre) - dat->pre = dupstring(dat->pre); - if (dat->suf) - dat->suf = dupstring(dat->suf); - if (!dat->prpre && (dat->prpre = oppre)) { - singsub(&(dat->prpre)); - untokenize(dat->prpre); - } else - dat->prpre = dupstring(dat->prpre); - /* Select the group in which to store the matches. */ - gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT : 0) | - ((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) | - ((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0)); - if (dat->group) { - endcmgroup(NULL); - begcmgroup(dat->group, gflags); - } else { - endcmgroup(NULL); - begcmgroup("default", 0); - } - /* Select the set of matches. */ - oisalt = (dat->aflags & CAF_ALT); - - if (dat->remf) { - dat->remf = dupstring(dat->remf); - dat->rems = NULL; - } else if (dat->rems) - dat->rems = dupstring(dat->rems); + } + /* Select the group in which to store the matches. */ + gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT : 0) | + ((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) | + ((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0)); + if (dat->group) { + endcmgroup(NULL); + begcmgroup(dat->group, gflags); + } else { + endcmgroup(NULL); + begcmgroup("default", 0); + } + if (*argv) { + if (dat->pre) + dat->pre = dupstring(dat->pre); + if (dat->suf) + dat->suf = dupstring(dat->suf); + if (!dat->prpre && (dat->prpre = oppre)) { + singsub(&(dat->prpre)); + untokenize(dat->prpre); + } else + dat->prpre = dupstring(dat->prpre); + /* Select the set of matches. */ + + if (dat->remf) { + dat->remf = dupstring(dat->remf); + dat->rems = NULL; + } else if (dat->rems) + dat->rems = dupstring(dat->rems); + + if (lpre) + lpre = ((!(dat->aflags & CAF_QUOTE) && + (!dat->ppre && (dat->flags & CMF_FILE))) ? + tildequote(lpre, 1) : multiquote(lpre, 1)); + if (lsuf) + lsuf = multiquote(lsuf, 1); + } + /* Walk through the matches given. */ + obpl = bpl; + obsl = bsl; + if (aign || pign) { + int max = 0; + char **ap = argv; + + ppl = (dat->ppre ? strlen(dat->ppre) : 0); + while ((s = *ap++)) + if ((sl = strlen(s)) > max) + max = sl; + psl = (dat->psuf ? strlen(dat->psuf) : 0); + ibuf = (char *) zhalloc(1 + ppl + max + psl); + } + for (; (s = *argv); argv++) { + bpl = obpl; + bsl = obsl; + if (disp) { + if (!*++disp) + disp = NULL; } - /* Walk through the matches given. */ - obpl = bpl; - obsl = bsl; - for (; (s = *argv); argv++) { - bpl = obpl; - bsl = obsl; - if (disp) { - if (!*++disp) - disp = NULL; - } - sl = strlen(s); - isalt = oisalt; - if ((!dat->psuf || !*(dat->psuf)) && aign) { + sl = strlen(s); + if (aign || pign) { + int il = ppl + sl + psl, addit = 1; + + if (ppl) + memcpy(ibuf, dat->ppre, ppl); + strcpy(ibuf + ppl, s); + if (psl) + strcpy(ibuf + ppl + sl, dat->psuf); + + if (aign) { /* Do the suffix-test. If the match has one of the - * suffixes from ign, we put it in the alternate set. */ + * suffixes from aign, we put it in the alternate set. */ char **pt = aign; int filell; - for (isalt = 0; !isalt && *pt; pt++) - if ((filell = strlen(*pt)) < sl - && !strcmp(*pt, s + sl - filell)) - isalt = 1; + for (; addit && *pt; pt++) + addit = !((filell = strlen(*pt)) < il && + !strcmp(*pt, ibuf + il - filell)); + } + if (addit && pign) { + Patprog *pt = pign; - if (isalt && !doadd) { - if (dparr && !*++dparr) - dparr = NULL; - continue; - } + for (; addit && *pt; pt++) + addit = !pattry(*pt, ibuf); } - if (!(dat->aflags & CAF_MATCH)) { - if (dat->aflags & CAF_QUOTE) - ms = dupstring(s); - else - sl = strlen(ms = quotename(s, NULL)); - lc = bld_parts(ms, sl, -1, NULL); - isexact = 0; - } else if (!(ms = comp_match(lpre, lsuf, s, cp, &lc, - (!(dat->aflags & CAF_QUOTE) ? - ((dat->ppre && dat->ppre) || - !(dat->flags & CMF_FILE) ? 1 : 2) : 0), - &bpl, bcp, &bsl, bcs, - &isexact))) { + if (!addit) { + compignored++; if (dparr && !*++dparr) dparr = NULL; continue; } - if (doadd) { - Brinfo bp; - - for (bp = obpl; bp; bp = bp->next) - bp->curpos += bpadd; - for (bp = obsl; bp; bp = bp->next) - bp->curpos += bsadd; - - if ((cm = add_match_data(isalt, ms, lc, dat->ipre, NULL, - dat->isuf, dat->pre, dat->prpre, - dat->ppre, pline, - dat->psuf, sline, - dat->suf, dat->flags, isexact))) { - cm->rems = dat->rems; - cm->remf = dat->remf; - if (disp) - cm->disp = dupstring(*disp); - } - } else { - if (dat->apar) - addlinknode(aparl, ms); - if (dat->opar) - addlinknode(oparl, s); - if (dat->dpar && dparr) { - addlinknode(dparl, *dparr); - if (!*++dparr) - dparr = NULL; - } - free_cline(lc); + } + if (!(dat->aflags & CAF_MATCH)) { + if (dat->aflags & CAF_QUOTE) + ms = dupstring(s); + else + sl = strlen(ms = multiquote(s, 0)); + lc = bld_parts(ms, sl, -1, NULL); + isexact = 0; + } else if (!(ms = comp_match(lpre, lsuf, s, cp, &lc, + (!(dat->aflags & CAF_QUOTE) ? + (dat->ppre || + !(dat->flags & CMF_FILE) ? 1 : 2) : 0), + &bpl, bcp, &bsl, bcs, + &isexact))) { + if (dparr && !*++dparr) + dparr = NULL; + continue; + } + if (doadd) { + Brinfo bp; + + for (bp = obpl; bp; bp = bp->next) + bp->curpos += bpadd; + for (bp = obsl; bp; bp = bp->next) + bp->curpos += bsadd; + + if ((cm = add_match_data(0, ms, lc, dat->ipre, NULL, + dat->isuf, dat->pre, dat->prpre, + dat->ppre, pline, + dat->psuf, sline, + dat->suf, dat->flags, isexact))) { + cm->rems = dat->rems; + cm->remf = dat->remf; + if (disp) + cm->disp = dupstring(*disp); } + } else { + if (dat->apar) + addlinknode(aparl, ms); + if (dat->opar) + addlinknode(oparl, s); + if (dat->dpar && dparr) { + addlinknode(dparl, *dparr); + if (!*++dparr) + dparr = NULL; + } + free_cline(lc); } - if (dat->apar) - set_list_array(dat->apar, aparl); - if (dat->opar) - set_list_array(dat->opar, oparl); - if (dat->dpar) - set_list_array(dat->dpar, dparl); - if (dat->exp) - addexpl(); - } LASTALLOC; + } + if (dat->apar) + set_list_array(dat->apar, aparl); + if (dat->opar) + set_list_array(dat->opar, oparl); + if (dat->dpar) + set_list_array(dat->dpar, dparl); + if (dat->exp) + addexpl(); } SWITCHBACKHEAPS; /* We switched back to the current heap, now restore the stack of @@ -1764,7 +1891,7 @@ addmatches(Cadata dat, char **argv) /* This adds all the data we have for a match. */ /**/ -Cmatch +mod_export Cmatch add_match_data(int alt, char *str, Cline line, char *ipre, char *ripre, char *isuf, char *pre, char *prpre, @@ -1797,21 +1924,12 @@ add_match_data(int alt, char *str, Cline line, salen += (qisl = strlen(qisuf)); if (salen) { - char *asuf = (char *) zhalloc(salen); Cline pp, p, s, sl = NULL; - - - if (psl) - memcpy(asuf, psuf, psl); - if (isl) - memcpy(asuf + psl, isuf, isl); - if (qisl) - memcpy(asuf + psl + isl, qisuf, qisl); for (pp = NULL, p = line; p->next; pp = p, p = p->next); - if (salen > qisl) { - s = bld_parts(asuf, salen - qisl, salen - qisl, &sl); + if (psl) { + s = bld_parts(psuf, psl, psl, &sl); if (sline) { Cline sp; @@ -1821,6 +1939,7 @@ add_match_data(int alt, char *str, Cline line, for (sp = sline; sp->next; sp = sp->next); sp->next = s; s = sline; + sline = NULL; } if (!(p->flags & (CLF_SUF | CLF_MID)) && !p->llen && !p->wlen && !p->olen) { @@ -1832,7 +1951,7 @@ add_match_data(int alt, char *str, Cline line, s->prefix = p->prefix; p->prefix = NULL; } - s->flags |= (p->flags & CLF_MATCHED); + s->flags |= (p->flags & CLF_MATCHED) | CLF_MID; free_cline(p); if (pp) pp->next = s; @@ -1841,8 +1960,29 @@ add_match_data(int alt, char *str, Cline line, } else p->next = s; } + if (isl) { + Cline tsl; + + s = bld_parts(isuf, isl, isl, &tsl); + + if (sl) + sl->next = s; + else if (sline) { + Cline sp; + + sline = cp_cline(sline, 1); + + for (sp = sline; sp->next; sp = sp->next); + sp->next = s; + p->next = sline; + sline = NULL; + } else + p->next = s; + + sl = tsl; + } if (qisl) { - Cline qsl = bld_parts(asuf + psl + isl, qisl, qisl, NULL); + Cline qsl = bld_parts(qisuf, qisl, qisl, NULL); qsl->flags |= CLF_SUF; qsl->suffix = qsl->prefix; @@ -1888,7 +2028,7 @@ add_match_data(int alt, char *str, Cline line, p = bld_parts(ppre, ppl, ppl, &lp); if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) && - !p->llen && !p->wlen && !p->olen) { + !lp->llen && !lp->wlen && !lp->olen) { Cline lpp; for (lpp = lp->prefix; lpp->next; lpp = lpp->next); @@ -1952,8 +2092,8 @@ add_match_data(int alt, char *str, Cline line, } else for (p = lp = cp_cline(pline, 1); lp->next; lp = lp->next); - if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) && - !p->llen && !p->wlen && !p->olen) { + if (lp->prefix && !(line->flags & CLF_SUF) && + !lp->llen && !lp->wlen && !lp->olen) { Cline lpp; for (lpp = lp->prefix; lpp->next; lpp = lpp->next); @@ -1994,7 +2134,14 @@ add_match_data(int alt, char *str, Cline line, cm->isuf = (isuf && *isuf ? isuf : NULL); cm->pre = pre; cm->suf = suf; - cm->flags = flags; + cm->flags = (flags | + (complist ? + ((strstr(complist, "packed") ? CMF_PACKED : 0) | + (strstr(complist, "rows") ? CMF_ROWS : 0)) : 0)); + + if ((*compqstack == '\\' && compqstack[1]) || + (autoq && *compqstack && compqstack[1] == '\\')) + cm->flags |= CMF_NOSPACE; if (nbrbeg) { int *p; Brinfo bp; @@ -2017,7 +2164,7 @@ add_match_data(int alt, char *str, Cline line, cm->brsl = NULL; cm->qipl = qipl; cm->qisl = qisl; - cm->autoq = (autoq ? autoq : (inbackt ? '`' : '\0')); + cm->autoq = dupstring(autoq ? autoq : (inbackt ? "`" : NULL)); cm->rems = cm->remf = cm->disp = NULL; if ((lastprebr || lastpostbr) && !hasbrpsfx(cm, lastprebr, lastpostbr)) @@ -2032,7 +2179,12 @@ add_match_data(int alt, char *str, Cline line, addlinknode((alt ? fmatches : matches), cm); newmatches = 1; + mgroup->new = 1; + if (alt) + compignored++; + if (!complastprompt || !*complastprompt) + dolastprompt = 0; /* One more match for this explanation. */ if (curexpl) { if (alt) @@ -2056,8 +2208,8 @@ add_match_data(int alt, char *str, Cline line, /* Do we have an exact match? More than one? */ if (exact) { if (!ai->exact) { - ai->exact = 1; - if (incompfunc) { + ai->exact = useexact; + if (incompfunc && (!compexactstr || !*compexactstr)) { /* If a completion widget is active, we make the exact * string available in `compstate'. */ @@ -2076,7 +2228,7 @@ add_match_data(int alt, char *str, Cline line, comp_setunset(0, 0, CP_EXACTSTR, 0); } ai->exactm = cm; - } else { + } else if (useexact) { ai->exact = 2; ai->exactm = NULL; if (incompfunc) @@ -2089,7 +2241,7 @@ add_match_data(int alt, char *str, Cline line, /* This begins a new group of matches. */ /**/ -void +mod_export void begcmgroup(char *n, int flags) { if (n) { @@ -2118,6 +2270,8 @@ begcmgroup(char *n, int flags) mgroup->matches = NULL; mgroup->ylist = NULL; mgroup->expls = NULL; + mgroup->perm = NULL; + mgroup->new = 0; mgroup->lexpls = expls = newlinklist(); mgroup->lmatches = matches = newlinklist(); @@ -2132,7 +2286,7 @@ begcmgroup(char *n, int flags) /* End the current group for now. */ /**/ -void +mod_export void endcmgroup(char **ylist) { mgroup->ylist = ylist; @@ -2141,7 +2295,7 @@ endcmgroup(char **ylist) /* Add an explanation string to the current group, joining duplicates. */ /**/ -void +mod_export void addexpl(void) { LinkNode n; @@ -2221,7 +2375,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) int n, nl = 0, ll = 0; /* Build an array for the matches. */ - rp = ap = (Cmatch *) ncalloc(((n = countlinknodes(l)) + 1) * + rp = ap = (Cmatch *) hcalloc(((n = countlinknodes(l)) + 1) * sizeof(Cmatch)); /* And copy them into it. */ @@ -2252,22 +2406,28 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) (int (*) _((const void *, const void *)))matchcmp); if (!(flags & CGF_UNIQCON)) { + int dup; + /* And delete the ones that occur more than once. */ for (ap = cp = rp; *ap; ap++) { *cp++ = *ap; for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--); ap = bp; /* Mark those, that would show the same string in the list. */ - for (; bp[1] && !(*ap)->disp && !(bp[1])->disp && - !strcmp((*ap)->str, (bp[1])->str); bp++) - (bp[1])->flags |= CMF_NOLIST; + for (dup = 0; bp[1] && !(*ap)->disp && !(bp[1])->disp && + !strcmp((*ap)->str, (bp[1])->str); bp++) { + (bp[1])->flags |= CMF_MULT; + dup = 1; + } + if (dup) + (*ap)->flags |= CMF_FMULT; } *cp = NULL; } for (ap = rp; *ap; ap++) { if ((*ap)->disp && ((*ap)->flags & CMF_DISPLINE)) ll++; - if ((*ap)->flags & CMF_NOLIST) + if ((*ap)->flags & (CMF_NOLIST | CMF_MULT)) nl++; } } else { @@ -2282,20 +2442,26 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) *cp = NULL; } } else if (!(flags & CGF_UNIQCON)) { + int dup; + for (ap = cp = rp; *ap; ap++) { *cp++ = *ap; for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--); ap = bp; - for (; bp[1] && !(*ap)->disp && !(bp[1])->disp && - !strcmp((*ap)->str, (bp[1])->str); bp++) - (bp[1])->flags |= CMF_NOLIST; + for (dup = 0; bp[1] && !(*ap)->disp && !(bp[1])->disp && + !strcmp((*ap)->str, (bp[1])->str); bp++) { + (bp[1])->flags |= CMF_MULT; + dup = 1; + } + if (dup) + (*ap)->flags |= CMF_FMULT; } *cp = NULL; } for (ap = rp; *ap; ap++) { if ((*ap)->disp && ((*ap)->flags & CMF_DISPLINE)) ll++; - if ((*ap)->flags & CMF_NOLIST) + if ((*ap)->flags & (CMF_NOLIST | CMF_MULT)) nl++; } } @@ -2317,7 +2483,7 @@ dupmatch(Cmatch m, int nbeg, int nend) { Cmatch r; - r = (Cmatch) ncalloc(sizeof(struct cmatch)); + r = (Cmatch) zcalloc(sizeof(struct cmatch)); r->str = ztrdup(m->str); r->ipre = ztrdup(m->ipre); @@ -2349,10 +2515,10 @@ dupmatch(Cmatch m, int nbeg, int nend) r->brsl = NULL; r->rems = ztrdup(m->rems); r->remf = ztrdup(m->remf); - r->autoq = m->autoq; + r->autoq = ztrdup(m->autoq); r->qipl = m->qipl; r->qisl = m->qisl; - r->disp = dupstring(m->disp); + r->disp = ztrdup(m->disp); return r; } @@ -2360,24 +2526,24 @@ dupmatch(Cmatch m, int nbeg, int nend) /* This duplicates all groups of matches. */ /**/ -int +mod_export int permmatches(int last) { - Cmgroup g = amatches, n; + Cmgroup g = amatches, n, opm; Cmatch *p, *q; Cexpl *ep, *eq, e, o; LinkList mlist; static int fi = 0; - int nn, nl, ll, gn = 1, mn = 1, rn; + int nn, nl, ll, gn = 1, mn = 1, rn, ofi = fi; - if (pmatches && !newmatches) + if (pmatches && !newmatches) { + if (last && fi) + ainfo = fainfo; return fi; - + } newmatches = fi = 0; - if (pmatches) - freematches(pmatches); - + opm = pmatches; pmatches = lmatches = NULL; nmatches = smatches = 0; @@ -2387,8 +2553,8 @@ permmatches(int last) fi = 1; } while (g) { - HEAPALLOC { - if (empty(g->lmatches)) + if (fi != ofi || !g->perm || g->new) { + if (fi) /* We have no matches, try ignoring fignore. */ mlist = g->lfmatches; else @@ -2407,51 +2573,67 @@ permmatches(int last) NULL, NULL); g->ccount = 0; - } LASTALLOC; - - nmatches += g->mcount; - smatches += g->lcount; - - n = (Cmgroup) ncalloc(sizeof(struct cmgroup)); - - if (!lmatches) - lmatches = n; - if (pmatches) - pmatches->prev = n; - n->next = pmatches; - pmatches = n; - n->prev = 0; - n->num = gn++; - - n->flags = g->flags; - n->mcount = g->mcount; - n->matches = p = (Cmatch *) ncalloc((n->mcount + 1) * - sizeof(Cmatch)); - for (q = g->matches; *q; q++, p++) - *p = dupmatch(*q, nbrbeg, nbrend); - *p = NULL; - - n->lcount = g->lcount; - n->llcount = g->llcount; - if (g->ylist) - n->ylist = arrdup(g->ylist); - else - n->ylist = NULL; - - if ((n->ecount = g->ecount)) { - n->expls = ep = (Cexpl *) ncalloc((n->ecount + 1) * - sizeof(Cexpl)); - for (eq = g->expls; (o = *eq); eq++, ep++) { - *ep = e = (Cexpl) ncalloc(sizeof(struct cexpl)); - e->count = (fi ? o->fcount : o->count); - e->str = ztrdup(o->str); - } - *ep = NULL; - } else - n->expls = NULL; - n->widths = NULL; + nmatches += g->mcount; + smatches += g->lcount; + + n = (Cmgroup) zcalloc(sizeof(struct cmgroup)); + + if (g->perm) { + g->perm->next = NULL; + freematches(g->perm); + } + g->perm = n; + + if (!lmatches) + lmatches = n; + if (pmatches) + pmatches->prev = n; + n->next = pmatches; + pmatches = n; + n->prev = NULL; + n->num = gn++; + n->flags = g->flags; + n->mcount = g->mcount; + n->matches = p = (Cmatch *) zcalloc((n->mcount + 1) * sizeof(Cmatch)); + n->name = ztrdup(g->name); + for (q = g->matches; *q; q++, p++) + *p = dupmatch(*q, nbrbeg, nbrend); + *p = NULL; + + n->lcount = g->lcount; + n->llcount = g->llcount; + if (g->ylist) + n->ylist = zarrdup(g->ylist); + else + n->ylist = NULL; + + if ((n->ecount = g->ecount)) { + n->expls = ep = (Cexpl *) zcalloc((n->ecount + 1) * sizeof(Cexpl)); + for (eq = g->expls; (o = *eq); eq++, ep++) { + *ep = e = (Cexpl) zcalloc(sizeof(struct cexpl)); + e->count = (fi ? o->fcount : o->count); + e->str = ztrdup(o->str); + } + *ep = NULL; + } else + n->expls = NULL; + n->widths = NULL; + } else { + if (!lmatches) + lmatches = g->perm; + if (pmatches) + pmatches->prev = g->perm; + g->perm->next = pmatches; + pmatches = g->perm; + g->perm->prev = NULL; + + nmatches += g->mcount; + smatches += g->lcount; + g->num = gn++; + } + g->new = 0; g = g->next; } for (g = pmatches; g; g = g->next) { @@ -2490,6 +2672,7 @@ freematch(Cmatch m, int nbeg, int nend) zsfree(m->rems); zsfree(m->remf); zsfree(m->disp); + zsfree(m->autoq); zfree(m->brpl, nbeg * sizeof(int)); zfree(m->brsl, nend * sizeof(int)); @@ -2499,7 +2682,7 @@ freematch(Cmatch m, int nbeg, int nend) /* This frees the groups of matches. */ /**/ -void +mod_export void freematches(Cmgroup g) { Cmgroup n; @@ -2508,7 +2691,7 @@ freematches(Cmgroup g) while (g) { n = g->next; - + for (m = g->matches; *m; m++) freematch(*m, g->nbrbeg, g->nbrend); @@ -2523,6 +2706,7 @@ freematches(Cmgroup g) } free(g->expls); } + zsfree(g->name); free(g); g = n; diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index 283b8de62..ef961eeba 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -1,5 +1,5 @@ /* - * complete.c - the complete module + * complete.c - the complete module, interface part * * This file is part of zsh, the Z shell. * @@ -29,12 +29,50 @@ #include "complete.mdh" #include "complete.pro" -#define GLOBAL_PROTOTYPES -#include "zle_tricky.pro" -#undef GLOBAL_PROTOTYPES + +/* global variables for shell parameters in new style completion */ /**/ -void +mod_export zlong compcurrent; +/**/ +zlong complistmax, + complistlines, + compignored; + +/**/ +mod_export +char **compwords, + *compprefix, + *compsuffix, + *compisuffix, + *compqiprefix, + *compqisuffix, + *compquote, + *compqstack, + *comppatmatch, + *complastprompt; +/**/ +char *compiprefix, + *compcontext, + *compparameter, + *compredirect, + *compquoting, + *comprestore, + *complist, + *compinsert, + *compexact, + *compexactstr, + *comppatinsert, + *comptoend, + *compoldlist, + *compoldins, + *compvared; + +/**/ +Param *comprpms, *compkpms; + +/**/ +mod_export void freecmlist(Cmlist l) { Cmlist n; @@ -51,7 +89,7 @@ freecmlist(Cmlist l) } /**/ -void +mod_export void freecmatcher(Cmatcher m) { Cmatcher n; @@ -86,29 +124,10 @@ freecpattern(Cpattern p) } } -/* Copy a list of completion matchers. */ - -static Cmlist -cpcmlist(Cmlist l) -{ - Cmlist r = NULL, *p = &r, n; - - while (l) { - *p = n = (Cmlist) zalloc(sizeof(struct cmlist)); - n->next = NULL; - n->matcher = cpcmatcher(l->matcher); - n->str = ztrdup(l->str); - - p = &(n->next); - l = l->next; - } - return r; -} - /* Copy a completion matcher list. */ /**/ -Cmatcher +mod_export Cmatcher cpcmatcher(Cmatcher m) { Cmatcher r = NULL, *p = &r, n; @@ -155,37 +174,10 @@ cpcpattern(Cpattern o) return r; } -/* Set the global match specs. */ - -/**/ -int -set_gmatcher(char *name, char **argv) -{ - Cmlist l = NULL, *q = &l, n; - Cmatcher m; - - while (*argv) { - if ((m = parse_cmatcher(name, *argv)) == pcm_err) - return 1; - *q = n = (Cmlist) zhalloc(sizeof(struct cmlist)); - n->next = NULL; - n->matcher = m; - n->str = *argv++; - - q = &(n->next); - } - freecmlist(cmatcher); - PERMALLOC { - cmatcher = cpcmlist(l); - } LASTALLOC; - - return 1; -} - /* Parse a string for matcher control, containing multiple matchers. */ /**/ -Cmatcher +mod_export Cmatcher parse_cmatcher(char *name, char *s) { Cmatcher ret = NULL, r = NULL, n; @@ -262,8 +254,11 @@ parse_cmatcher(char *name, char *s) return pcm_err; } word = NULL; - wl = -1; - s++; + if (*++s == '*') { + s++; + wl = -2; + } else + wl = -1; } else { word = parse_pattern(name, &s, &wl, 0, &err); @@ -389,17 +384,17 @@ static int bin_compadd(char *name, char **argv, char *ops, int func) { struct cadata dat; - char *p, **sp, *e, *m = NULL; + char *p, **sp, *e, *m = NULL, *mstr = NULL; int dm; Cmatcher match = NULL; if (incompfunc != 1) { - zerrnam(name, "can only be called from completion function", NULL, 0); + zwarnnam(name, "can only be called from completion function", NULL, 0); return 1; } dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre = dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = - dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = dat.ylist = NULL; + dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL; dat.match = NULL; dat.flags = 0; dat.aflags = CAF_MATCH; @@ -462,10 +457,6 @@ bin_compadd(char *name, char **argv, char *ops, int func) if (!(dat.aflags & CAF_UNIQALL)) dat.aflags |= CAF_UNIQCON; break; - case 'y': - sp = &(dat.ylist); - e = "string expected after -%c"; - break; case 'i': sp = &(dat.ipre); e = "string expected after -%c"; @@ -486,9 +477,6 @@ bin_compadd(char *name, char **argv, char *ops, int func) sp = &(dat.prpre); e = "string expected after -%c"; break; - case 'a': - dat.aflags |= CAF_ALT; - break; case 'M': sp = &m; e = "matching specification expected after -%c"; @@ -531,7 +519,7 @@ bin_compadd(char *name, char **argv, char *ops, int func) argv++; goto ca_args; default: - zerrnam(name, "bad option: -%c", NULL, *p); + zwarnnam(name, "bad option: -%c", NULL, *p); return 1; } if (sp) { @@ -545,18 +533,29 @@ bin_compadd(char *name, char **argv, char *ops, int func) *sp = *argv; p = "" - 1; } else { - zerrnam(name, e, NULL, *p); + zwarnnam(name, e, NULL, *p); return 1; } - if (dm && (match = parse_cmatcher(name, m)) == pcm_err) { - match = NULL; - return 1; + if (dm) { + if (mstr) + mstr = tricat(mstr, " ", m); + else + mstr = ztrdup(m); + m = NULL; } } } } + if (mstr && (match = parse_cmatcher(name, mstr)) == pcm_err) { + zsfree(mstr); + return 1; + } + zsfree(mstr); + ca_args: - if (!*argv) + + if (!*argv && !dat.group && + !(dat.aflags & (CAF_NOSORT|CAF_UNIQALL|CAF_UNIQCON))) return 1; dat.match = match = cpcmatcher(match); @@ -574,7 +573,7 @@ bin_compadd(char *name, char **argv, char *ops, int func) #define CVT_SUFPAT 5 /**/ -void +mod_export void ignore_prefix(int l) { if (l) { @@ -598,7 +597,7 @@ ignore_prefix(int l) } /**/ -void +mod_export void ignore_suffix(int l) { if (l) { @@ -621,7 +620,7 @@ ignore_suffix(int l) } /**/ -void +mod_export void restrict_range(int b, int e) { int wl = arrlen(compwords) - 1; @@ -644,6 +643,7 @@ restrict_range(int b, int e) } } +/**/ static int do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) { @@ -740,7 +740,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) char *p, sav; if (!(l = strlen(compprefix))) - return 0; + return ((na == 1 || na == -1) && pattry(pp, compprefix)); if (na < 0) { p = compprefix + l; na = -na; @@ -766,7 +766,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod) char *p; if (!(ol = l = strlen(compsuffix))) - return 0; + return ((na == 1 || na == -1) && pattry(pp, compsuffix)); if (na < 0) { p = compsuffix; na = -na; @@ -798,11 +798,11 @@ bin_compset(char *name, char **argv, char *ops, int func) char *sa = NULL, *sb = NULL; if (incompfunc != 1) { - zerrnam(name, "can only be called from completion function", NULL, 0); + zwarnnam(name, "can only be called from completion function", NULL, 0); return 1; } if (argv[0][0] != '-') { - zerrnam(name, "missing option", NULL, 0); + zwarnnam(name, "missing option", NULL, 0); return 1; } switch (argv[0][1]) { @@ -814,7 +814,7 @@ bin_compset(char *name, char **argv, char *ops, int func) case 'S': test = CVT_SUFPAT; break; case 'q': return set_comp_sep(); default: - zerrnam(name, "bad option -%c", NULL, argv[0][1]); + zwarnnam(name, "bad option -%c", NULL, argv[0][1]); return 1; } if (argv[0][2]) { @@ -823,7 +823,7 @@ bin_compset(char *name, char **argv, char *ops, int func) na = 2; } else { if (!(sa = argv[1])) { - zerrnam(name, "missing string for option -%c", NULL, argv[0][1]); + zwarnnam(name, "missing string for option -%c", NULL, argv[0][1]); return 1; } sb = argv[2]; @@ -831,7 +831,7 @@ bin_compset(char *name, char **argv, char *ops, int func) } if (((test == CVT_PRENUM || test == CVT_SUFNUM) ? !!sb : (sb && argv[na]))) { - zerrnam(name, "too many arguments", NULL, 0); + zwarnnam(name, "too many arguments", NULL, 0); return 1; } switch (test) { @@ -841,11 +841,9 @@ bin_compset(char *name, char **argv, char *ops, int func) break; case CVT_RANGEPAT: tokenize(sa); - sa = rembslash(sa); remnulargs(sa); if (sb) { tokenize(sb); - sb = rembslash(sb); remnulargs(sb); } break; @@ -861,7 +859,6 @@ bin_compset(char *name, char **argv, char *ops, int func) } else na = -1; tokenize(sa); - sa = rembslash(sa); remnulargs(sa); break; } @@ -892,9 +889,6 @@ static struct compparam comprparams[] = { static struct compparam compkparams[] = { { "nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_nmatches) }, - { "matcher", PM_INTEGER, VAL(compmatcher), NULL, NULL }, - { "matcher_string", PM_SCALAR, VAL(compmatcherstr), NULL, NULL }, - { "total_matchers", PM_INTEGER, VAL(compmatchertot), NULL, NULL }, { "context", PM_SCALAR, VAL(compcontext), NULL, NULL }, { "parameter", PM_SCALAR, VAL(compparameter), NULL, NULL }, { "redirect", PM_SCALAR, VAL(compredirect), NULL, NULL }, @@ -902,7 +896,6 @@ static struct compparam compkparams[] = { { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL, NULL }, { "restore", PM_SCALAR, VAL(comprestore), NULL, NULL }, { "list", PM_SCALAR, NULL, VAL(set_complist), VAL(get_complist) }, - { "force_list", PM_SCALAR, VAL(compforcelist), NULL, NULL }, { "insert", PM_SCALAR, VAL(compinsert), NULL, NULL }, { "exact", PM_SCALAR, VAL(compexact), NULL, NULL }, { "exact_string", PM_SCALAR, VAL(compexactstr), NULL, NULL }, @@ -917,8 +910,9 @@ static struct compparam compkparams[] = { { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL }, { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL }, { "vared", PM_SCALAR, VAL(compvared), NULL, NULL }, - { "alternate_nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_anmatches) }, { "list_lines", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_listlines) }, + { "all_quotes", PM_SCALAR | PM_READONLY, VAL(compqstack), NULL, NULL }, + { "ignored", PM_INTEGER | PM_READONLY, VAL(compignored), NULL, NULL }, { NULL, 0, NULL, NULL, NULL } }; @@ -976,7 +970,7 @@ makecompparams(void) comprpms[CPN_COMPSTATE] = cpm; tht = paramtab; - cpm->level = locallevel; + cpm->level = locallevel + 1; cpm->gets.hfn = get_compstate; cpm->sets.hfn = set_compstate; cpm->unsetfn = compunsetfn; @@ -1029,14 +1023,7 @@ set_compstate(Param pm, HashTable ht) static zlong get_nmatches(Param pm) { - return num_matches(1); -} - -/**/ -static zlong -get_anmatches(Param pm) -{ - return num_matches(0); + return (permmatches(0) ? 0 : nmatches); } /**/ @@ -1083,14 +1070,37 @@ static void compunsetfn(Param pm, int exp) { if (exp) { - if (PM_TYPE(pm->flags) == PM_SCALAR) { - zsfree(*((char **) pm->u.data)); - *((char **) pm->u.data) = ztrdup(""); - } else if (PM_TYPE(pm->flags) == PM_ARRAY) { - freearray(*((char ***) pm->u.data)); - *((char ***) pm->u.data) = zcalloc(sizeof(char *)); + if (pm->u.data) { + if (PM_TYPE(pm->flags) == PM_SCALAR) { + zsfree(*((char **) pm->u.data)); + *((char **) pm->u.data) = ztrdup(""); + } else if (PM_TYPE(pm->flags) == PM_ARRAY) { + freearray(*((char ***) pm->u.data)); + *((char ***) pm->u.data) = zcalloc(sizeof(char *)); + } else if (PM_TYPE(pm->flags) == PM_HASHED) { + deleteparamtable(pm->u.hash); + pm->u.hash = NULL; + } } - pm->flags |= PM_UNSET; + } else if (PM_TYPE(pm->flags) == PM_HASHED) { + Param *p; + int i; + + deletehashtable(pm->u.hash); + pm->u.hash = NULL; + + for (p = compkpms, i = CP_KEYPARAMS; i--; p++) + *p = NULL; + } + if (!exp) { + Param *p; + int i; + + for (p = comprpms, i = CP_REALPARAMS; i; p++, i--) + if (*p == pm) { + *p = NULL; + break; + } } } @@ -1102,31 +1112,35 @@ comp_setunset(int rset, int runset, int kset, int kunset) if (comprpms && (rset >= 0 || runset >= 0)) { for (p = comprpms; rset || runset; rset >>= 1, runset >>= 1, p++) { - if (rset & 1) - (*p)->flags &= ~PM_UNSET; - if (runset & 1) - (*p)->flags |= PM_UNSET; + if (*p) { + if (rset & 1) + (*p)->flags &= ~PM_UNSET; + if (runset & 1) + (*p)->flags |= PM_UNSET; + } } } - if (comprpms && (kset >= 0 || kunset >= 0)) { + if (compkpms && (kset >= 0 || kunset >= 0)) { for (p = compkpms; kset || kunset; kset >>= 1, kunset >>= 1, p++) { - if (kset & 1) - (*p)->flags &= ~PM_UNSET; - if (kunset & 1) - (*p)->flags |= PM_UNSET; + if (*p) { + if (kset & 1) + (*p)->flags &= ~PM_UNSET; + if (kunset & 1) + (*p)->flags |= PM_UNSET; + } } } } /**/ static int -comp_wrapper(List list, FuncWrap w, char *name) +comp_wrapper(Eprog prog, FuncWrap w, char *name) { if (incompfunc != 1) return 1; else { char *orest, *opre, *osuf, *oipre, *oisuf, **owords; - char *oqipre, *oqisuf, *oq, *oqi; + char *oqipre, *oqisuf, *oq, *oqi, *oqs, *oaq; zlong ocur; unsigned int runset = 0, kunset = 0, m, sm; Param *pp; @@ -1142,52 +1156,65 @@ comp_wrapper(List list, FuncWrap w, char *name) orest = comprestore; comprestore = ztrdup("auto"); ocur = compcurrent; - opre = dupstring(compprefix); - osuf = dupstring(compsuffix); - oipre = dupstring(compiprefix); - oisuf = dupstring(compisuffix); - oqipre = dupstring(compqiprefix); - oqisuf = dupstring(compqisuffix); - oq = dupstring(compquote); - oqi = dupstring(compquoting); - - HEAPALLOC { - owords = arrdup(compwords); - } LASTALLOC; - - runshfunc(list, w, name); + opre = ztrdup(compprefix); + osuf = ztrdup(compsuffix); + oipre = ztrdup(compiprefix); + oisuf = ztrdup(compisuffix); + oqipre = ztrdup(compqiprefix); + oqisuf = ztrdup(compqisuffix); + oq = ztrdup(compquote); + oqi = ztrdup(compquoting); + oqs = ztrdup(compqstack); + oaq = ztrdup(autoq); + owords = zarrdup(compwords); + + runshfunc(prog, w, name); if (comprestore && !strcmp(comprestore, "auto")) { compcurrent = ocur; zsfree(compprefix); - compprefix = ztrdup(opre); + compprefix = opre; zsfree(compsuffix); - compsuffix = ztrdup(osuf); + compsuffix = osuf; zsfree(compiprefix); - compiprefix = ztrdup(oipre); + compiprefix = oipre; zsfree(compisuffix); - compisuffix = ztrdup(oisuf); + compisuffix = oisuf; zsfree(compqiprefix); - compqiprefix = ztrdup(oqipre); + compqiprefix = oqipre; zsfree(compqisuffix); - compqisuffix = ztrdup(oqisuf); + compqisuffix = oqisuf; zsfree(compquote); - compquote = ztrdup(oq); + compquote = oq; zsfree(compquoting); - compquoting = ztrdup(oqi); + compquoting = oqi; + zsfree(compqstack); + compqstack = oqs; + zsfree(autoq); + autoq = oaq; freearray(compwords); - PERMALLOC { - compwords = arrdup(owords); - } LASTALLOC; + compwords = owords; comp_setunset(CP_COMPSTATE | (~runset & (CP_WORDS | CP_CURRENT | CP_PREFIX | CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX | CP_QIPREFIX | CP_QISUFFIX)), (runset & CP_ALLREALS), (~kunset & CP_RESTORE), (kunset & CP_ALLKEYS)); - } else + } else { comp_setunset(CP_COMPSTATE, 0, (~kunset & CP_RESTORE), (kunset & CP_RESTORE)); + zsfree(opre); + zsfree(osuf); + zsfree(oipre); + zsfree(oisuf); + zsfree(oqipre); + zsfree(oqisuf); + zsfree(oq); + zsfree(oqi); + zsfree(oqs); + zsfree(oaq); + freearray(owords); + } zsfree(comprestore); comprestore = orest; @@ -1228,42 +1255,6 @@ cond_range(char **a, int id) (id ? cond_str(a, 1, 1) : NULL), 0); } -/**/ -static void -cmsetfn(Param pm, char **v) -{ - set_gmatcher(pm->nam, v); -} - -/**/ -static char ** -cmgetfn(Param pm) -{ - int num; - Cmlist p; - char **ret, **q; - - for (num = 0, p = cmatcher; p; p = p->next, num++); - - ret = (char **) zhalloc((num + 1) * sizeof(char *)); - - for (q = ret, p = cmatcher; p; p = p->next, q++) - *q = dupstring(p->str); - *q = NULL; - - return ret; -} - -/**/ -static void -cmunsetfn(Param pm, int exp) -{ - char *dummy[1]; - - dummy[0] = NULL; - set_gmatcher(pm->nam, dummy); -} - static struct builtin bintab[] = { BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL), BUILTIN("compset", 0, bin_compset, 1, 3, 0, NULL, NULL), @@ -1280,53 +1271,108 @@ static struct funcwrap wrapper[] = { WRAPDEF(comp_wrapper), }; -static struct paramdef patab[] = { - PARAMDEF("compmatchers", PM_ARRAY|PM_SPECIAL, NULL, cmsetfn, cmgetfn, cmunsetfn) +/* The order of the entries in this table has to match the *HOOK + * macros in comp.h */ + +/**/ +struct hookdef comphooks[] = { + HOOKDEF("insert_match", NULL, HOOKF_ALL), + HOOKDEF("menu_start", NULL, HOOKF_ALL), + HOOKDEF("compctl_make", NULL, 0), + HOOKDEF("compctl_cleanup", NULL, 0), + HOOKDEF("comp_list_matches", ilistmatches, 0), }; /**/ int -setup_complete(Module m) +setup_(Module m) { - makecompparamsptr = makecompparams; - comp_setunsetptr = comp_setunset; + hasperm = 0; + + comprpms = compkpms = NULL; + compwords = NULL; + compprefix = compsuffix = compiprefix = compisuffix = + compqiprefix = compqisuffix = + compcontext = compparameter = compredirect = compquote = + compquoting = comprestore = complist = compinsert = + compexact = compexactstr = comppatmatch = comppatinsert = + complastprompt = comptoend = compoldlist = compoldins = + compvared = compqstack = NULL; + + hascompmod = 1; return 0; } /**/ int -boot_complete(Module m) +boot_(Module m) { + addhookfunc("complete", (Hookfn) do_completion); + addhookfunc("before_complete", (Hookfn) before_complete); + addhookfunc("after_complete", (Hookfn) after_complete); + addhookfunc("accept_completion", (Hookfn) accept_last); + addhookfunc("reverse_menu", (Hookfn) reverse_menu); + addhookfunc("list_matches", (Hookfn) list_matches); + addhookfunc("invalidate_list", (Hookfn) invalidate_list); + addhookdefs(m->nam, comphooks, sizeof(comphooks)/sizeof(*comphooks)); if (!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) | addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) | - addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)) | !addwrapper(m, wrapper))) return 1; return 0; } -#ifdef MODULE - /**/ int -cleanup_complete(Module m) +cleanup_(Module m) { + deletehookfunc("complete", (Hookfn) do_completion); + deletehookfunc("before_complete", (Hookfn) before_complete); + deletehookfunc("after_complete", (Hookfn) after_complete); + deletehookfunc("accept_completion", (Hookfn) accept_last); + deletehookfunc("reverse_menu", (Hookfn) reverse_menu); + deletehookfunc("list_matches", (Hookfn) list_matches); + deletehookfunc("invalidate_list", (Hookfn) invalidate_list); + deletehookdefs(m->nam, comphooks, sizeof(comphooks)/sizeof(*comphooks)); deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)); - deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)); deletewrapper(m, wrapper); return 0; } /**/ int -finish_complete(Module m) +finish_(Module m) { - makecompparamsptr = NULL; - comp_setunsetptr = NULL; + if (compwords) + freearray(compwords); + zsfree(compprefix); + zsfree(compsuffix); + zsfree(compiprefix); + zsfree(compisuffix); + zsfree(compqiprefix); + zsfree(compqisuffix); + zsfree(compcontext); + zsfree(compparameter); + zsfree(compredirect); + zsfree(compquote); + zsfree(compqstack); + zsfree(compquoting); + zsfree(comprestore); + zsfree(complist); + zsfree(compinsert); + zsfree(compexact); + zsfree(compexactstr); + zsfree(comppatmatch); + zsfree(comppatinsert); + zsfree(complastprompt); + zsfree(comptoend); + zsfree(compoldlist); + zsfree(compoldins); + zsfree(compvared); + + hascompmod = 0; return 0; } - -#endif diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c index 32e0c3a68..3dda28e11 100644 --- a/Src/Zle/compmatch.c +++ b/Src/Zle/compmatch.c @@ -28,15 +28,8 @@ */ #include "complete.mdh" -#define GLOBAL_PROTOTYPES -#include "zle_tricky.pro" -#undef GLOBAL_PROTOTYPES #include "compmatch.pro" -/* Convenience macro for calling bslashquote() (formerly quotename()). */ - -#define quotename(s, e) bslashquote(s, e, instring) - /* This compares two cpattern lists and returns non-zero if they are * equal. */ @@ -75,14 +68,14 @@ cmp_cmatchers(Cmatcher a, Cmatcher b) /* Add the given matchers to the bmatcher list. */ /**/ -void +mod_export void add_bmatchers(Cmatcher m) { Cmlist old = bmatchers, *q = &bmatchers, n; for (; m; m = m->next) { if ((!m->flags && m->wlen > 0 && m->llen > 0) || - (m->flags == CMF_RIGHT && m->wlen == -1 && !m->llen)) { + (m->flags == CMF_RIGHT && m->wlen < 0 && !m->llen)) { *q = n = (Cmlist) zhalloc(sizeof(struct cmlist)); n->matcher = m; q = &(n->next); @@ -95,7 +88,7 @@ add_bmatchers(Cmatcher m) * ensure that the bmatchers list contains no matchers not in mstack. */ /**/ -void +mod_export void update_bmatchers(void) { Cmlist p = bmatchers, q = NULL, ms; @@ -141,6 +134,7 @@ get_cline(char *l, int ll, char *w, int wl, char *o, int ol, int fl) r->slen = 0; r->flags = fl; r->prefix = r->suffix = NULL; + r->min = r->max = 0; return r; } @@ -443,7 +437,7 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, int sfx, int test, int part) { int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw; - int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc; + int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc, bslash; VARARR(unsigned char, ea, ll + 1); char *ow; Cmlist ms; @@ -553,7 +547,7 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, } else t = match_str(l + llen + moff, tp + moff, NULL, 0, NULL, 0, 1, part); - if (t || !both) + if (t || (mp->wlen == -1 && !both)) break; } } @@ -743,12 +737,15 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp, if (mp) continue; - if (l[ind] == w[ind]) { + bslash = 0; + if (l[ind] == w[ind] || + (bslash = (lw > 1 && w[ind] == '\\' && + (ind ? (w[0] == l[0]) : (w[1] == l[0]))))) { /* No matcher could be used, but the strings have the same * character here, skip over it. */ - l += add; w += add; - il++; iw++; - ll--; lw--; + l += add; w += (bslash ? (add + add ) : add); + il++; iw += 1 + bslash; + ll--; lw -= 1 + bslash; bc++; if (!test) while (bp && bc >= (useqbr ? bp->qpos : bp->pos)) { @@ -839,7 +836,7 @@ match_parts(char *l, char *w, int n, int part) * and the suffix don't match the word w. */ /**/ -char * +mod_export char * comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu, Brinfo *bpl, int bcp, Brinfo *bsl, int bcs, int *exact) { @@ -853,9 +850,8 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu, if (!pattry(cp, r)) return NULL; - r = (qu ? quotename(r, NULL) : dupstring(r)); - if (qu == 2 && r[0] == '\\' && r[1] == '~') - chuck(r); + r = (qu == 2 ? tildequote(r, 0) : multiquote(r, !qu)); + /* We still break it into parts here, trying to build a sensible * cline list for these matches, too. */ w = dupstring(w); @@ -866,10 +862,7 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu, Cline pli, plil; int mpl, rpl, wl; - w = (qu ? quotename(w, NULL) : dupstring(w)); - if (qu == 2 && w[0] == '\\' && w[1] == '~') - chuck(w); - + w = (qu == 2 ? tildequote(w, 0) : multiquote(w, !qu)); wl = strlen(w); /* Always try to match the prefix. */ @@ -1016,7 +1009,7 @@ bld_parts(char *str, int len, int plen, Cline *lp) while (len) { for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { mp = ms->matcher; - if (mp->flags == CMF_RIGHT && mp->wlen == -1 && + if (mp && mp->flags == CMF_RIGHT && mp->wlen < 0 && !mp->llen && len >= mp->ralen && mp->ralen && pattern_match(mp->right, str, NULL, NULL)) { int olen = str - p, llen; @@ -1136,7 +1129,7 @@ bld_line(Cpattern pat, char *line, char *lp, t = 0; for (ms = bmatchers; ms && !t; ms = ms->next) { mp = ms->matcher; - if (!mp->flags && mp->wlen <= wlen && mp->llen <= l && + if (mp && !mp->flags && mp->wlen <= wlen && mp->llen <= l && pattern_match(mp->line, (sfx ? line - mp->llen : line), NULL, ea) && pattern_match(mp->word, (sfx ? word - mp->wlen : word), @@ -1186,7 +1179,7 @@ join_strs(int la, char *sa, int lb, char *sb) /* Different characters, try the matchers. */ for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) { mp = ms->matcher; - if (!mp->flags && mp->wlen > 0 && mp->llen > 0 && + if (mp && !mp->flags && mp->wlen > 0 && mp->llen > 0 && mp->wlen <= la && mp->wlen <= lb) { /* The pattern has no anchors and the word * pattern fits, try it. */ @@ -1373,7 +1366,7 @@ join_sub(Cmdata md, char *str, int len, int *mlen, int sfx, int join) /* We use only those patterns that match a non-empty * string in both the line and the word and that have * no anchors. */ - if (!mp->flags && mp->wlen > 0 && mp->llen > 0) { + if (mp && !mp->flags && mp->wlen > 0 && mp->llen > 0) { /* We first test, if the old string matches already the * new one. */ if (mp->llen <= ol && mp->wlen <= nl && @@ -1684,7 +1677,7 @@ join_mid(Cline o, Cline n) * didn't. */ /**/ -static void +static int sub_join(Cline a, Cline b, Cline e, int anew) { if (!e->suffix && a->prefix) { @@ -1707,31 +1700,28 @@ sub_join(Cline a, Cline b, Cline e, int anew) *p = e->prefix; ca = a->prefix; - while (n != op) { + while (n) { e->prefix = cp_cline(n, 0); a->prefix = cp_cline(ca, 0); if (anew) { join_psfx(e, a, NULL, NULL, 0); - if (e->prefix) { - e->min += min; - e->max += max; - break; - } + if (e->prefix) + return max - min; } else { - join_psfx(e, a, NULL, NULL, 0); - if (a->prefix) { - a->min += min; - a->max += max; - break; - } + join_psfx(a, e, NULL, NULL, 0); + if (a->prefix) + return max - min; } min -= n->min; - max -= n->max; + if (n == op) + break; n = n->next; } + return max - min; } + return 0; } /* This simplifies the cline list given as the first argument so that @@ -1748,7 +1738,8 @@ join_clines(Cline o, Cline n) if (!o) return n; else { - Cline oo = o, nn = n, po = NULL, pn = NULL; + Cline oo = o, nn = n, po = NULL, pn = NULL, x; + int diff; /* Walk through the lists. */ while (o && n) { @@ -1760,7 +1751,7 @@ join_clines(Cline o, Cline n) for (t = o; (tn = t->next) && (tn->flags & CLF_NEW); t = tn); if (tn && cmp_anchors(tn, n, 0)) { - sub_join(n, o, tn, 1); + diff = sub_join(n, o, tn, 1); if (po) po->next = tn; @@ -1768,8 +1759,15 @@ join_clines(Cline o, Cline n) oo = tn; t->next = NULL; free_cline(o); + x = o; o = tn; - o->flags |= CLF_MISS; + if (po && cmp_anchors(x, po, 0)) { + po->flags |= CLF_MISS; + po->max += diff; + } else { + o->flags |= CLF_MISS; + o->max += diff; + } continue; } } @@ -1778,10 +1776,16 @@ join_clines(Cline o, Cline n) for (t = n; (tn = t->next) && (tn->flags & CLF_NEW); t = tn); if (tn && cmp_anchors(o, tn, 0)) { - sub_join(o, n, tn, 0); + diff = sub_join(o, n, tn, 0); + if (po && cmp_anchors(n, pn, 0)) { + po->flags |= CLF_MISS; + po->max += diff; + } else { + o->flags |= CLF_MISS; + o->max += diff; + } n = tn; - o->flags |= CLF_MISS; continue; } } @@ -1809,6 +1813,7 @@ join_clines(Cline o, Cline n) t = tn); if (tn && cmp_anchors(tn, n, 1)) { sub_join(n, o, tn, 1); + if (po) po->next = tn; else @@ -1837,24 +1842,41 @@ join_clines(Cline o, Cline n) for (t = n; (tn = t->next) && !cmp_anchors(o, tn, 1); t = tn); if (tn) { - sub_join(o, n, tn, 0); + diff = sub_join(o, n, tn, 0); + if (po && cmp_anchors(n, pn, 0)) { + po->flags |= CLF_MISS; + po->max += diff; + } else { + o->flags |= CLF_MISS; + o->max += diff; + } n = tn; - o->flags |= CLF_MISS; + po = o; + o = o->next; + pn = n; + n = n->next; continue; } else { for (t = o; (tn = t->next) && !cmp_anchors(n, tn, 1); t = tn); if (tn) { - sub_join(n, o, tn, 1); + diff = sub_join(n, o, tn, 1); if (po) po->next = tn; else oo = tn; + x = o; o = tn; - o->flags |= CLF_MISS; + if (po && cmp_anchors(x, po, 0)) { + po->flags |= CLF_MISS; + po->max += diff; + } else { + o->flags |= CLF_MISS; + o->max += diff; + } continue; } else { if (o->flags & CLF_SUF) diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index fe997b12b..0d93b8727 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -28,16 +28,8 @@ */ #include "complete.mdh" -#define GLOBAL_PROTOTYPES -#include "zle_tricky.pro" -#undef GLOBAL_PROTOTYPES #include "compresult.pro" -/* Convenience macro for calling bslashquote() (formerly quotename()). * - * This uses the instring variable above. */ - -#define quotename(s, e) bslashquote(s, e, instring) - #define inststr(X) inststrlen((X),1,-1) /* This cuts the cline list before the stuff that isn't worth @@ -72,7 +64,8 @@ cut_cline(Cline l) q = p; } if (!e && q && !q->orig && !q->olen && (q->flags & CLF_MISS) && - !(q->flags & CLF_MATCHED) && (q->word ? q->wlen : q->llen) < 3) { + (!(q->flags & CLF_MATCHED) || (!q->prefix && !q->suffix)) && + (q->word ? q->wlen : q->llen) < 3) { q->word = q->line = NULL; q->wlen = q->llen = 0; } @@ -143,8 +136,9 @@ cline_str(Cline l, int ins, int *csp) l = cut_cline(l); - pmm = smm = dm = 0; + pmm = smm = dm = pcs = scs = 0; pm = pmax = sm = smax = d = mid = cbr = -1; + brp = brs = NULL; /* Get the information about the brace beginning and end we have * to re-insert. */ @@ -594,7 +588,7 @@ do_ambiguous(void) /* If we have to insert the first match, call do_single(). This is * * how REC_EXACT takes effect. We effectively turn the ambiguous * * completion into an unambiguous one. */ - if (ainfo && ainfo->exact == 1 && useexact && !(fromcomp & FC_LINE)) { + if (ainfo && ainfo->exact == 1 && !(fromcomp & FC_LINE)) { minfo.cur = NULL; do_single(ainfo->exactm); invalidatelist(); @@ -616,6 +610,7 @@ do_ambiguous(void) do_ambig_menu(); } else if (ainfo) { int atend = (cs == we), la, eq, tcs; + VARARR(char, old, we - wb); minfo.cur = NULL; minfo.asked = 0; @@ -623,11 +618,24 @@ do_ambiguous(void) fixsuffix(); /* First remove the old string from the line. */ + tcs = cs; cs = wb; + memcpy(old, (char *) line + wb, we - wb); foredel(we - wb); /* Now get the unambiguous string and insert it into the line. */ cline_str(ainfo->line, 1, NULL); + + /* Sometimes the different match specs used may result in a cline + * that gives an empty string. If that happened, we re-insert the + * old string. Unless there were matches added with -U, that is. */ + if (!(lastend - wb) && !hasunmatched) { + cs = wb; + foredel(lastend - wb); + inststrlen(old, 0, we - wb); + lastend = we; + cs = tcs; + } if (eparq) { tcs = cs; cs = lastend; @@ -677,7 +685,7 @@ do_ambiguous(void) if (uselist && (usemenu != 2 || (!listshown && !oldlist)) && ((!showinglist && (!listshown || !oldlist)) || (usemenu == 3 && !oldlist)) && - (smatches >= 2 || (compforcelist && *compforcelist))) + (smatches >= 2 || forcelist)) showinglist = -2; return ret; @@ -690,7 +698,7 @@ do_ambiguous(void) * (l)stat(). */ /**/ -int +mod_export int ztat(char *nam, struct stat *buf, int ls) { char b[PATH_MAX], *p; @@ -708,7 +716,7 @@ ztat(char *nam, struct stat *buf, int ls) /* Insert a single match in the command line. */ /**/ -void +mod_export void do_single(Cmatch m) { int l, sr = 0, scs; @@ -726,7 +734,8 @@ do_single(Cmatch m) /* We are currently not in a menu-completion, * * so set the position variables. */ minfo.pos = wb; - minfo.we = (movetoend >= 2 || (movetoend == 1 && !menucmp)); + minfo.we = (movetoend >= 2 || (movetoend == 1 && !menucmp) || + (!movetoend && cs == we)); minfo.end = we; } /* If we are already in a menu-completion or if we have done a * @@ -792,19 +801,40 @@ do_single(Cmatch m) else { /* Build the path name. */ if (partest && !*psuf && !(m->flags & CMF_PARNEST)) { - int ne = noerrs; + int ne = noerrs, tryit = 1; p = (char *) zhalloc(strlen((m->flags & CMF_ISPAR) ? parpre : m->ripre) + strlen(str) + 2); sprintf(p, "%s%s%c", ((m->flags & CMF_ISPAR) ? parpre : m->ripre), str, - ((m->flags & CMF_PARBR) ? Outbrace : '\0')); - noerrs = 1; - parsestr(p); - singsub(&p); - errflag = 0; - noerrs = ne; + ((m->flags & CMF_PARBR) ? '}' : '\0')); + if (*p == '$') { + char *n; + Param pm; + + if (p[1] == '{') { + char *e; + + n = dupstring(p + 2); + e = n + strlen(n) - 1; + + if (*e == '}') + *e = '\0'; + } else + n = p + 1; + + if ((pm = (Param) paramtab->getnode(paramtab, n)) && + PM_TYPE(pm->flags) != PM_SCALAR) + tryit = 0; + } + if (tryit) { + noerrs = 1; + parsestr(p); + singsub(&p); + errflag = 0; + noerrs = ne; + } } else { p = (char *) zhalloc(strlen(prpre) + strlen(str) + strlen(psuf) + 3); @@ -857,11 +887,13 @@ do_single(Cmatch m) /* If we didn't add a suffix, add a space, unless we are * * doing menu completion or we are completing files and * * the string doesn't name an existing file. */ - if (m->autoq && (!m->isuf || m->isuf[0] != m->autoq)) { - inststrlen(&(m->autoq), 1, 1); - minfo.insc++; + if (m->autoq && (!m->isuf || !strpfx(m->autoq, m->isuf))) { + int al = strlen(m->autoq); + inststrlen(m->autoq, 1, al); + minfo.insc += al; } - if (!menucmp && (usemenu != 3 || insspace)) { + if (!menucmp && !(m->flags & CMF_NOSPACE) && + (usemenu != 3 || insspace)) { inststrlen(" ", 1, 1); minfo.insc++; if (minfo.we) @@ -897,7 +929,7 @@ do_single(Cmatch m) * insert the next completion. */ /**/ -void +mod_export void do_menucmp(int lst) { /* Just list the matches if the list was requested. */ @@ -906,44 +938,40 @@ do_menucmp(int lst) return; } /* Otherwise go to the next match in the array... */ - HEAPALLOC { - do { - if (!*++(minfo.cur)) { - do { - if (!(minfo.group = (minfo.group)->next)) - minfo.group = amatches; - } while (!(minfo.group)->mcount); - minfo.cur = minfo.group->matches; - } - } while (menuacc && - !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)); - /* ... and insert it into the command line. */ - metafy_line(); - do_single(*(minfo.cur)); - unmetafy_line(); - } LASTALLOC; + do { + if (!*++(minfo.cur)) { + do { + if (!(minfo.group = (minfo.group)->next)) + minfo.group = amatches; + } while (!(minfo.group)->mcount); + minfo.cur = minfo.group->matches; + } + } while (menuacc && + !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)); + /* ... and insert it into the command line. */ + metafy_line(); + do_single(*(minfo.cur)); + unmetafy_line(); } /**/ int reverse_menu(Hookdef dummy, void *dummy2) { - HEAPALLOC { - do { - if (minfo.cur == (minfo.group)->matches) { - do { - if (!(minfo.group = (minfo.group)->prev)) - minfo.group = lmatches; - } while (!(minfo.group)->mcount); - minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1; - } else - minfo.cur--; - } while (menuacc && - !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)); - metafy_line(); - do_single(*(minfo.cur)); - unmetafy_line(); - } LASTALLOC; + do { + if (minfo.cur == (minfo.group)->matches) { + do { + if (!(minfo.group = (minfo.group)->prev)) + minfo.group = lmatches; + } while (!(minfo.group)->mcount); + minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1; + } else + minfo.cur--; + } while (menuacc && + !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr)); + metafy_line(); + do_single(*(minfo.cur)); + unmetafy_line(); return 0; } @@ -953,7 +981,7 @@ reverse_menu(Hookdef dummy, void *dummy2) * accept several selections from the list of matches. */ /**/ -int +mod_export int accept_last(void) { if (!menuacc) { @@ -1042,7 +1070,7 @@ do_ambig_menu(void) } else { if (oldlist) { if (oldins && minfo.cur) - acceptlast(); + accept_last(); } else minfo.cur = NULL; } @@ -1074,24 +1102,6 @@ do_ambig_menu(void) minfo.cur = mc; } -/* Return the real number of matches. */ - -/**/ -zlong -num_matches(int normal) -{ - int alt; - - PERMALLOC { - alt = permmatches(0); - } LASTALLOC; - - if (normal) - return (alt ? 0 : nmatches); - else - return (alt ? nmatches : 0); -} - /* Return the number of screen lines needed for the list. */ /**/ @@ -1100,14 +1110,12 @@ list_lines(void) { Cmgroup oam; - PERMALLOC { - permmatches(0); - } LASTALLOC; + permmatches(0); oam = amatches; amatches = pmatches; listdat.valid = 0; - calclist(); + calclist(0); listdat.valid = 0; amatches = oam; @@ -1124,132 +1132,25 @@ comp_list(char *v) onlyexpl = (v && strstr(v, "expl")); } -/* This is used to print the explanation string. * - * It returns the number of lines printed. */ - -/**/ -int -printfmt(char *fmt, int n, int dopr, int doesc) -{ - char *p = fmt, nc[DIGBUFSIZE]; - int l = 0, cc = 0, b = 0, s = 0, u = 0, m; - - for (; *p; p++) { - /* Handle the `%' stuff (%% == %, %n == <number of matches>). */ - if (doesc && *p == '%') { - if (*++p) { - m = 0; - switch (*p) { - case '%': - if (dopr) - putc('%', shout); - cc++; - break; - case 'n': - sprintf(nc, "%d", n); - if (dopr) - fprintf(shout, nc); - cc += strlen(nc); - break; - case 'B': - b = 1; - if (dopr) - tcout(TCBOLDFACEBEG); - break; - case 'b': - b = 0; m = 1; - if (dopr) - tcout(TCALLATTRSOFF); - break; - case 'S': - s = 1; - if (dopr) - tcout(TCSTANDOUTBEG); - break; - case 's': - s = 0; m = 1; - if (dopr) - tcout(TCSTANDOUTEND); - break; - case 'U': - u = 1; - if (dopr) - tcout(TCUNDERLINEBEG); - break; - case 'u': - u = 0; m = 1; - if (dopr) - tcout(TCUNDERLINEEND); - break; - case '{': - for (p++; *p && (*p != '%' || p[1] != '}'); p++, cc++) - if (dopr) - putc(*p, shout); - if (*p) - p++; - else - p--; - break; - } - if (dopr && m) { - if (b) - tcout(TCBOLDFACEBEG); - if (s) - tcout(TCSTANDOUTBEG); - if (u) - tcout(TCUNDERLINEBEG); - } - } else - break; - } else { - cc++; - if (*p == '\n') { - if (dopr) { - if (tccan(TCCLEAREOL)) - tcout(TCCLEAREOL); - else { - int s = columns - 1 - (cc % columns); - - while (s-- > 0) - putc(' ', shout); - } - } - l += 1 + (cc / columns); - cc = 0; - } - if (dopr) - putc(*p, shout); - } - } - if (dopr) { - if (tccan(TCCLEAREOL)) - tcout(TCCLEAREOL); - else { - int s = columns - 1 - (cc % columns); - - while (s-- > 0) - putc(' ', shout); - } - } - return l + (cc / columns); -} - /* This skips over matches that are not to be listed. */ /**/ Cmatch * -skipnolist(Cmatch *p) +skipnolist(Cmatch *p, int showall) { - while (*p && (((*p)->flags & (CMF_NOLIST | CMF_HIDE)) || - ((*p)->disp && ((*p)->flags & (CMF_DISPLINE | CMF_HIDE))))) + int mask = (showall ? 0 : (CMF_NOLIST | CMF_MULT)) | CMF_HIDE; + + while (*p && (((*p)->flags & mask) || + ((*p)->disp && + ((*p)->flags & (CMF_DISPLINE | CMF_HIDE))))) p++; return p; } /**/ -void -calclist(void) +mod_export void +calclist(int showall) { Cmgroup g; Cmatch *p, m; @@ -1259,7 +1160,7 @@ calclist(void) VARARR(int, mlens, nmatches + 1); if (listdat.valid && onlyexpl == listdat.onlyexpl && - menuacc == listdat.menuacc && + menuacc == listdat.menuacc && showall == listdat.showall && lines == listdat.lines && columns == listdat.columns) return; @@ -1267,6 +1168,8 @@ calclist(void) char **pp = g->ylist; int nl = 0, l, glong = 1, gshort = columns, ndisp = 0, totl = 0; + g->flags |= CGF_PACKED | CGF_ROWS; + if (!onlyexpl && pp) { /* We have an ylist, lets see, if it contains newlines. */ hidden = 1; @@ -1283,8 +1186,8 @@ calclist(void) while ((sptr = *pp)) { while (sptr && *sptr) { nlines += (nlptr = strchr(sptr, '\n')) - ? 1 + (nlptr-sptr)/columns - : strlen(sptr)/columns; + ? 1 + (nlptr-sptr) / columns + : strlen(sptr) / columns; sptr = nlptr ? nlptr+1 : NULL; } nlines++; @@ -1327,7 +1230,11 @@ calclist(void) mlens[m->gnum] = l; } nlist++; - } else if (!(m->flags & CMF_NOLIST)) { + if (!(m->flags & CMF_PACKED)) + g->flags &= ~CGF_PACKED; + if (!(m->flags & CMF_ROWS)) + g->flags &= ~CGF_ROWS; + } else if (showall || !(m->flags & (CMF_NOLIST | CMF_MULT))) { l = niceztrlen(m->str); ndisp++; if (l > glong) @@ -1337,6 +1244,10 @@ calclist(void) totl += l; mlens[m->gnum] = l; nlist++; + if (!(m->flags & CMF_PACKED)) + g->flags &= ~CGF_PACKED; + if (!(m->flags & CMF_ROWS)) + g->flags &= ~CGF_ROWS; } else hidden = 1; } @@ -1361,9 +1272,11 @@ calclist(void) } } if (!onlyexpl) { + char **pp; + int *ws, tlines, tline, tcols, maxlen, nth, width, glines; + for (g = amatches; g; g = g->next) { - char **pp; - int glines = 0; + glines = 0; zfree(g->widths, 0); g->widths = NULL; @@ -1396,20 +1309,19 @@ calclist(void) if (m->disp) { if (!(m->flags & CMF_DISPLINE)) glines += 1 + (mlens[m->gnum] / columns); - } else if (!(m->flags & CMF_NOLIST)) - glines += 1 + ((1 + mlens[m->gnum]) / columns); + } else if (showall || + !(m->flags & (CMF_NOLIST | CMF_MULT))) + glines += 1 + ((mlens[m->gnum]) / columns); } } } g->lins = glines; nlines += glines; } - } - if (!onlyexpl && isset(LISTPACKED)) { - char **pp; - int *ws, tlines, tline, tcols, maxlen, nth, width; - for (g = amatches; g; g = g->next) { + if (!(g->flags & CGF_PACKED)) + continue; + ws = g->widths = (int *) zalloc(columns * sizeof(int)); memset(ws, 0, columns * sizeof(int)); tlines = g->lins; @@ -1424,11 +1336,13 @@ calclist(void) for (i = 0; *pp; i++, pp++) ylens[i] = strlen(*pp) + add; - if (isset(LISTROWSFIRST)) { + if (g->flags & CGF_ROWS) { int count, tcol, first, maxlines = 0, llines; + int beg = columns / g->shortest, end = g->cols; + + while (1) { + tcols = (beg + end) >> 1; - for (tcols = columns / g->shortest; tcols > g->cols; - tcols--) { for (nth = first = maxlen = width = maxlines = llines = tcol = 0, count = g->dcount; @@ -1452,17 +1366,33 @@ calclist(void) ws[tcol++] = maxlen; width += maxlen; } - if (!count && width < columns) + if (!count && width < columns && + (tcols <= 0 || beg == end)) break; + + if (beg == end) { + beg--; + end--; + } else if (width < columns) { + if ((end = tcols) == beg - 1) + end++; + } else { + if ((beg = tcols) - 1 == end) + end++; + } } if (tcols > g->cols) tlines = maxlines; } else { - for (tlines = ((g->totl + columns) / columns); - tlines < g->lins; tlines++) { + int beg = ((g->totl + columns) / columns); + int end = g->lins; + + while (1) { + tlines = (beg + end) >> 1; + for (pp = g->ylist, nth = tline = width = maxlen = tcols = 0; - *pp; nth++, pp++) { + *pp; pp++) { if (ylens[nth] > maxlen) maxlen = ylens[nth]; if (++tline == tlines) { @@ -1471,24 +1401,41 @@ calclist(void) ws[tcols++] = maxlen; maxlen = tline = 0; } + nth++; } if (tline) { ws[tcols++] = maxlen; width += maxlen; } - if (nth == yl && width < columns) + if (nth == yl && width < columns && + (beg == end || tlines >= g->lins)) break; + + if (beg == end) { + beg++; + end++; + } else if (width < columns) { + if ((end = tlines) == beg + 1) + end--; + } else { + if ((beg = tlines) + 1 == end) + end--; + } } + if (tlines > g->lins) + tlines = g->lins; } } } else if (g->width) { - if (isset(LISTROWSFIRST)) { + if (g->flags & CGF_ROWS) { int addlen, count, tcol, maxlines = 0, llines, i; + int beg = columns / g->shortest, end = g->cols; Cmatch *first; - for (tcols = columns / g->shortest; tcols > g->cols; - tcols--) { - p = first = skipnolist(g->matches); + while (1) { + tcols = (beg + end) >> 1; + + p = first = skipnolist(g->matches, showall); for (maxlen = width = maxlines = llines = tcol = 0, count = g->dcount; count > 0; count--) { @@ -1497,7 +1444,7 @@ calclist(void) if (addlen > maxlen) maxlen = addlen; for (i = tcols; i && *p; i--) - p = skipnolist(p + 1); + p = skipnolist(p + 1, showall); llines++; if (!*p) { @@ -1510,29 +1457,46 @@ calclist(void) ws[tcol++] = maxlen; maxlen = 0; - p = first = skipnolist(first + 1); + p = first = skipnolist(first + 1, showall); } } if (tlines) { ws[tcol++] = maxlen; width += maxlen; } - if (!count && width < columns) + if (!count && width < columns && + (tcols <= 0 || beg == end)) break; + + if (beg == end) { + beg--; + end--; + } else if (width < columns) { + if ((end = tcols) == beg - 1) + end++; + } else { + if ((beg = tcols) - 1 == end) + end++; + } } if (tcols > g->cols) tlines = maxlines; } else { int addlen; + int smask = ((showall ? 0 : (CMF_NOLIST | CMF_MULT)) | + CMF_HIDE); + int beg = ((g->totl + columns) / columns); + int end = g->lins; + + while (1) { + tlines = (beg + end) >> 1; - for (tlines = ((g->totl + columns) / columns); - tlines < g->lins; tlines++) { for (p = g->matches, nth = tline = width = maxlen = tcols = 0; - (m = *p); p++, nth++) { + (m = *p); p++) { if (!(m->flags & (m->disp ? (CMF_DISPLINE | CMF_HIDE) : - (CMF_NOLIST | CMF_HIDE)))) { + smask))) { addlen = mlens[m->gnum] + add; if (addlen > maxlen) maxlen = addlen; @@ -1542,15 +1506,30 @@ calclist(void) ws[tcols++] = maxlen; maxlen = tline = 0; } + nth++; } } if (tline) { ws[tcols++] = maxlen; width += maxlen; } - if (nth == g->dcount && width < columns) + if (nth == g->dcount && width < columns && + (beg == end || tlines >= g->lins)) break; + + if (beg == end) { + beg++; + end++; + } else if (width < columns) { + if ((end = tlines) == beg + 1) + end--; + } else { + if ((beg = tlines) + 1 == end) + end--; + } } + if (tlines > g->lins) + tlines = g->lins; } } if (tlines == g->lins) { @@ -1584,21 +1563,23 @@ calclist(void) listdat.onlyexpl = onlyexpl; listdat.columns = columns; listdat.lines = lines; + listdat.showall = showall; } /**/ -int asklist(void) +mod_export int asklist(void) { /* Set the cursor below the prompt. */ trashzle(); showinglist = listshown = 0; - clearflag = (isset(USEZLE) && !termflags && - complastprompt && *complastprompt); + clearflag = (isset(USEZLE) && !termflags && dolastprompt); + lastlistlen = 0; /* Maybe we have to ask if the user wants to see the list. */ if ((!minfo.cur || !minfo.asked) && - ((complistmax && listdat.nlist > complistmax) || + ((complistmax > 0 && listdat.nlist >= complistmax) || + (complistmax < 0 && listdat.nlines <= -complistmax) || (!complistmax && listdat.nlines >= lines))) { int qup; zsetterm(); @@ -1614,8 +1595,7 @@ int asklist(void) tcmultout(TCUP, TCMULTUP, nlnct); } else putc('\n', shout); - if (minfo.cur) - minfo.asked = 2; + minfo.asked = 2; return 1; } if (clearflag) { @@ -1626,15 +1606,14 @@ int asklist(void) } else putc('\n', shout); settyinfo(&shttyinfo); - if (minfo.cur) - minfo.asked = 1; + minfo.asked = 1; } - return 0; + return (minfo.asked ? minfo.asked - 1 : 0); } /**/ -int -printlist(int over, CLPrintFunc printm) +mod_export int +printlist(int over, CLPrintFunc printm, int showall) { Cmgroup g; Cmatch *p, m; @@ -1715,7 +1694,7 @@ printlist(int over, CLPrintFunc printm) while (a--) putc(' ', shout); } - pq += (isset(LISTROWSFIRST) ? 1 : nc); + pq += ((g->flags & CGF_ROWS) ? 1 : nc); mc++; n--; } @@ -1728,10 +1707,11 @@ printlist(int over, CLPrintFunc printm) tcout(TCCLEAREOD); } } - pp += (isset(LISTROWSFIRST) ? g->cols : 1); + pp += ((g->flags & CGF_ROWS) ? g->cols : 1); } } - } else if (!listdat.onlyexpl && g->lcount) { + } else if (!listdat.onlyexpl && + (g->lcount || (showall && g->mcount))) { int n = g->dcount, nl, nc, i, j, wid; Cmatch *q; @@ -1765,7 +1745,7 @@ printlist(int over, CLPrintFunc printm) tcout(TCCLEAREOD); } } - for (p = skipnolist(g->matches); n && nl--;) { + for (p = skipnolist(g->matches, showall); n && nl--;) { i = g->cols; mc = 0; q = p; @@ -1794,14 +1774,16 @@ printlist(int over, CLPrintFunc printm) printed++; if (--n) - for (j = (isset(LISTROWSFIRST) ? 1 : nc); j && *q; j--) - q = skipnolist(q + 1); + for (j = ((g->flags & CGF_ROWS) ? 1 : nc); + j && *q; j--) + q = skipnolist(q + 1, showall); mc++; } - while (i-- > 0) - printm(g, NULL, mc++, ml, (!i), + while (i-- > 0) { + printm(g, NULL, mc, ml, (!i), (g->widths ? g->widths[mc] : g->width), NULL, NULL); - + mc++; + } if (n) { putc('\n', shout); ml++; @@ -1811,25 +1793,30 @@ printlist(int over, CLPrintFunc printm) tcout(TCCLEAREOD); } if (nl) - for (j = (isset(LISTROWSFIRST) ? g->cols : 1); j && *p; j--) - p = skipnolist(p + 1); + for (j = ((g->flags & CGF_ROWS) ? g->cols : 1); + j && *p; j--) + p = skipnolist(p + 1, showall); } } } - if (g->lcount) + if (g->lcount || (showall && g->mcount)) pnl = 1; g = g->next; } + lastlistlen = 0; if (clearflag) { /* Move the cursor up to the prompt, if always_last_prompt * * is set and all that... */ if ((ml = listdat.nlines + nlnct - 1) < lines) { tcmultout(TCUP, TCMULTUP, ml); showinglist = -1; + + lastlistlen = listdat.nlines; } else clearflag = 0, putc('\n', shout); } else putc('\n', shout); + listshown = (clearflag ? 1 : -1); return printed; @@ -1858,9 +1845,8 @@ iprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, nicezputs(m->str, shout); len = niceztrlen(m->str); - if (isset(LISTTYPES)) { - if (buf) - putc(file_type(buf->st_mode), shout); + if (isset(LISTTYPES) && buf) { + putc(file_type(buf->st_mode), shout); len++; } } @@ -1876,7 +1862,7 @@ iprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width, int ilistmatches(Hookdef dummy, Chdata dat) { - calclist(); + calclist(0); if (!listdat.nlines) { showinglist = listshown = 0; @@ -1885,7 +1871,7 @@ ilistmatches(Hookdef dummy, Chdata dat) if (asklist()) return 0; - printlist(0, iprintm); + printlist(0, iprintm, 0); return 0; } @@ -1897,6 +1883,7 @@ int list_matches(Hookdef dummy, void *dummy2) { struct chdata dat; + int ret; #ifdef DEBUG /* Sanity check */ @@ -1909,18 +1896,20 @@ list_matches(Hookdef dummy, void *dummy2) dat.matches = amatches; dat.num = nmatches; dat.cur = NULL; - return runhookdef(COMPLISTMATCHESHOOK, (void *) &dat); + ret = runhookdef(COMPLISTMATCHESHOOK, (void *) &dat); + + return ret; } /* Invalidate the completion list. */ /**/ -int +mod_export int invalidate_list(void) { - if (showinglist == -2) - listmatches(); if (validlist) { + if (showinglist == -2) + zrefresh(); freematches(lastmatches); lastmatches = NULL; hasoldlist = 0; diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index aed3d9808..a844ee1ef 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -33,22 +33,32 @@ /* Help for `_display'. */ +/* Calculation state. */ + typedef struct cdisp *Cdisp; struct cdisp { - int pre, suf, colon; + int pre; /* prefix length */ + int suf; /* suffix length */ + int colon; /* number of strings with descriptions */ }; +/* Calculate longest prefix and suffix and count the strings with + * descriptions. */ + static void cdisp_calc(Cdisp disp, char **args) { char *cp; - int i; + int i, nbc; for (; *args; args++) { - if ((cp = strchr(*args, ':')) && cp[1]) { + for (nbc = 0, cp = *args; *cp && *cp != ':'; cp++) + if (*cp == '\\' && cp[1]) + cp++, nbc++; + if (*cp == ':' && cp[1]) { disp->colon++; - if ((i = cp - *args) > disp->pre) + if ((i = cp - *args - nbc) > disp->pre) disp->pre = i; if ((i = strlen(cp + 1)) > disp->suf) disp->suf = i; @@ -56,78 +66,29 @@ cdisp_calc(Cdisp disp, char **args) } } -static char ** -cdisp_build(Cdisp disp, char *sep, char **args) -{ - int sl = strlen(sep), pre = disp->pre, suf; - VARARR(char, buf, disp->pre + disp->suf + sl + 1); - char **ret, **rp, *cp; - - ret = (char **) zalloc((arrlen(args) + 1) * sizeof(char *)); - - memcpy(buf + pre, sep, sl); - suf = pre + sl; - - for (rp = ret; *args; args++) { - if ((cp = strchr(*args, ':')) && cp[1]) { - memset(buf, ' ', pre); - memcpy(buf, *args, (cp - *args)); - strcpy(buf + suf, cp + 1); - *rp++ = ztrdup(buf); - } else { - if (cp) - *cp = '\0'; - *rp++ = ztrdup(*args); - if (cp) - *cp = ':'; - } - } - *rp = NULL; - - return ret; -} - -/**/ -static int -bin_compdisplay(char *nam, char **args, char *ops, int func) -{ - struct cdisp disp; - - if (incompfunc != 1) { - zerrnam(nam, "can only be called from completion function", NULL, 0); - return 1; - } - disp.pre = disp.suf = disp.colon = 0; - - cdisp_calc(&disp, args + 2); - setaparam(args[0], cdisp_build(&disp, args[1], args + 2)); - - return !disp.colon; -} - /* Help fuer `_describe'. */ typedef struct cdset *Cdset; struct cdstate { - int showd; - char *sep; - Cdset sets; - struct cdisp disp; + int showd; /* != 0 if descriptions should be shown */ + char *sep; /* the separator string */ + Cdset sets; /* the sets of matches */ + struct cdisp disp; /* used to calculate the alignment */ }; struct cdset { - Cdset next; - char **opts; - char **strs; - char **matches; + Cdset next; /* guess what */ + char **opts; /* the compadd-options */ + char **strs; /* the display-strings */ + char **matches; /* the matches (or NULL) */ }; static struct cdstate cd_state; static int cd_parsed = 0; static void -free_cdsets(Cdset p) +freecdsets(Cdset p) { Cdset n; @@ -143,6 +104,8 @@ free_cdsets(Cdset p) } } +/* Initialisation. Store and calculate the string and matches and so on. */ + static int cd_init(char *nam, char *sep, char **args, int disp) { @@ -151,7 +114,7 @@ cd_init(char *nam, char *sep, char **args, int disp) if (cd_parsed) { zsfree(cd_state.sep); - free_cdsets(cd_state.sets); + freecdsets(cd_state.sets); } setp = &(cd_state.sets); cd_state.sep = ztrdup(sep); @@ -164,24 +127,20 @@ cd_init(char *nam, char *sep, char **args, int disp) setp = &(set->next); if (!(ap = get_user_var(*args))) { - zerrnam(nam, "invalid argument: %s", *args, 0); + zwarnnam(nam, "invalid argument: %s", *args, 0); return 1; } - PERMALLOC { - set->strs = arrdup(ap); - } LASTALLOC; + set->strs = zarrdup(ap); if (disp) cdisp_calc(&(cd_state.disp), set->strs); if (*++args && **args != '-') { if (!(ap = get_user_var(*args))) { - zerrnam(nam, "invalid argument: %s", *args, 0); + zwarnnam(nam, "invalid argument: %s", *args, 0); return 1; } - PERMALLOC { - set->matches = arrdup(ap); - } LASTALLOC; + set->matches = zarrdup(ap); args++; } for (ap = args; *args && @@ -190,15 +149,15 @@ cd_init(char *nam, char *sep, char **args, int disp) tmp = *args; *args = NULL; - PERMALLOC { - set->opts = arrdup(ap); - } LASTALLOC; + set->opts = zarrdup(ap); if ((*args = tmp)) args++; } return 0; } +/* Get the next set. */ + static int cd_get(char **params) { @@ -206,15 +165,21 @@ cd_get(char **params) if ((set = cd_state.sets)) { char **sd, **sdp, **md, **mdp, **ss, **ssp, **ms, **msp; - char **p, **mp, *cp; + char **p, **mp, *cp, *copy, *cpp, oldc; int dl = 1, sl = 1, sepl = strlen(cd_state.sep); int pre = cd_state.disp.pre, suf = cd_state.disp.suf; VARARR(char, buf, pre + suf + sepl + 1); for (p = set->strs; *p; p++) - if (cd_state.showd && (cp = strchr(*p, ':')) && cp[1]) - dl++; - else + if (cd_state.showd) { + for (cp = *p; *cp && *cp != ':'; cp++) + if (*cp == '\\' && cp[1]) + cp++; + if (*cp == ':' && cp[1]) + dl++; + else + sl++; + } else sl++; sd = (char **) zalloc(dl * sizeof(char *)); @@ -226,41 +191,44 @@ cd_get(char **params) memcpy(buf + pre, cd_state.sep, sepl); suf = pre + sepl; } + + /* Build the aligned display strings. */ + for (sdp = sd, ssp = ss, mdp = md, msp = ms, p = set->strs, mp = set->matches; *p; p++) { - if ((cp = strchr(*p, ':')) && cp[1] && cd_state.showd) { + copy = dupstring(*p); + for (cp = cpp = copy; *cp && *cp != ':'; cp++) { + if (*cp == '\\' && cp[1]) + cp++; + *cpp++ = *cp; + } + oldc = *cpp; + *cpp = '\0'; + if (((cpp == cp && oldc == ':') || *cp == ':') && cp[1] && + cd_state.showd) { memset(buf, ' ', pre); - memcpy(buf, *p, (cp - *p)); + memcpy(buf, copy, (cpp - copy)); strcpy(buf + suf, cp + 1); *sdp++ = ztrdup(buf); if (mp) { *mdp++ = ztrdup(*mp); if (*mp) mp++; - } else { - *cp = '\0'; - *mdp++ = ztrdup(*p); - *cp = ':'; - } + } else + *mdp++ = ztrdup(copy); } else { - if (cp) - *cp = '\0'; - *ssp++ = ztrdup(*p); + *ssp++ = ztrdup(copy); if (mp) { *msp++ = ztrdup(*mp); if (*mp) mp++; } else - *msp++ = ztrdup(*p); - if (cp) - *cp = ':'; + *msp++ = ztrdup(copy); } } *sdp = *ssp = *mdp = *msp = NULL; - PERMALLOC { - p = arrdup(set->opts); - } LASTALLOC; + p = zarrdup(set->opts); setaparam(params[0], p); setaparam(params[1], sd); @@ -270,7 +238,7 @@ cd_get(char **params) cd_state.sets = set->next; set->next = NULL; - free_cdsets(set); + freecdsets(set); return 0; } @@ -282,34 +250,36 @@ static int bin_compdescribe(char *nam, char **args, char *ops, int func) { if (incompfunc != 1) { - zerrnam(nam, "can only be called from completion function", NULL, 0); + zwarnnam(nam, "can only be called from completion function", NULL, 0); return 1; } if (!args[0][0] || !args[0][1] || args[0][2]) { - zerrnam(nam, "invalid argument: %s", args[0], 0); + zwarnnam(nam, "invalid argument: %s", args[0], 0); return 1; } switch (args[0][1]) { case 'i': + cd_parsed = 1; + return cd_init(nam, "", args + 1, 0); case 'I': cd_parsed = 1; - return cd_init(nam, args[1], args + 2, (args[0][1] == 'I')); + return cd_init(nam, args[1], args + 2, 1); case 'g': if (cd_parsed) { int n = arrlen(args); if (n != 6) { - zerrnam(nam, (n < 6 ? "not enough arguments" : + zwarnnam(nam, (n < 6 ? "not enough arguments" : "too many arguments"), NULL, 0); return 1; } return cd_get(args + 1); } else { - zerrnam(nam, "no parsed state", NULL, 0); + zwarnnam(nam, "no parsed state", NULL, 0); return 1; } } - zerrnam(nam, "invalid option: %s", args[0], 0); + zwarnnam(nam, "invalid option: %s", args[0], 0); return 1; } @@ -319,29 +289,34 @@ typedef struct cadef *Cadef; typedef struct caopt *Caopt; typedef struct caarg *Caarg; +/* Cache for a set of _arguments-definitions. */ + struct cadef { - Cadef next; - Caopt opts; - int nopts, ndopts, nodopts; - Caarg args; - Caarg rest; - char **defs; - int ndefs; - int lastt; - Caopt *single; - char *match; - int argsactive; + Cadef next; /* next in cache */ + Caopt opts; /* the options */ + int nopts, ndopts, nodopts; /* number of options/direct/optional direct */ + Caarg args; /* the normal arguments */ + Caarg rest; /* the rest-argument */ + char **defs; /* the original strings */ + int ndefs; /* number of ... */ + int lastt; /* last time this was used */ + Caopt *single; /* array of single-letter options */ + char *match; /* -M spec to use */ + int argsactive; /* if arguments are still allowed */ + /* used while parsing a command line */ }; +/* Description for an option. */ + struct caopt { Caopt next; - char *name; - char *descr; - char **xor; - int type; - Caarg args; - int active; - int num; + char *name; /* option name */ + char *descr; /* the description */ + char **xor; /* if this, then not ... */ + int type; /* type, CAO_* */ + Caarg args; /* option arguments */ + int active; /* still allowed on command line */ + int num; /* it's the num'th option */ }; #define CAO_NEXT 1 @@ -349,13 +324,18 @@ struct caopt { #define CAO_ODIRECT 3 #define CAO_EQUAL 4 +/* Description for an argument */ + struct caarg { Caarg next; - char *descr; - char *action; - int type; - char *end; - int num; + char *descr; /* description */ + char **xor; /* if this, then not ... */ + char *action; /* what to do for it */ + int type; /* CAA_* below */ + char *end; /* end-pattern for ::<pat>:... */ + char *opt; /* option name if for an option */ + int num; /* it's the num'th argument */ + int active; /* still allowed on command line */ }; #define CAA_NORMAL 1 @@ -364,9 +344,13 @@ struct caarg { #define CAA_RARGS 4 #define CAA_RREST 5 +/* The cache of parsed descriptons. */ + #define MAX_CACACHE 8 static Cadef cadef_cache[MAX_CACACHE]; +/* Compare two arrays of strings for equality. */ + static int arrcmp(char **a, char **b) { @@ -383,45 +367,54 @@ arrcmp(char **a, char **b) } } +/* Memory stuff. Obviously. */ + static void -free_caargs(Caarg a) +freecaargs(Caarg a) { Caarg n; for (; a; a = n) { n = a->next; zsfree(a->descr); + if (a->xor) + freearray(a->xor); zsfree(a->action); zsfree(a->end); + zsfree(a->opt); zfree(a, sizeof(*a)); } } static void -free_cadef(Cadef d) +freecadef(Cadef d) { if (d) { Caopt p, n; zsfree(d->match); - freearray(d->defs); + if (d->defs) + freearray(d->defs); for (p = d->opts; p; p = n) { n = p->next; zsfree(p->name); zsfree(p->descr); - freearray(p->xor); - free_caargs(p->args); + if (p->xor) + freearray(p->xor); + freecaargs(p->args); zfree(p, sizeof(*p)); } - free_caargs(d->args); - free_caargs(d->rest); + freecaargs(d->args); + freecaargs(d->rest); if (d->single) zfree(d->single, 256 * sizeof(Caopt)); zfree(d, sizeof(*d)); } } +/* Remove backslashes before colons. */ + static char * rembslashcolon(char *s) { @@ -439,16 +432,41 @@ rembslashcolon(char *s) return r; } +/* Add backslashes before colons. */ + +static char * +bslashcolon(char *s) +{ + char *p, *r; + + r = p = zhalloc((2 * strlen(s)) + 1); + + while (*s) { + if (*s == ':') + *p++ = '\\'; + *p++ = *s++; + } + *p = '\0'; + + return r; +} + +/* Parse an argument definition. */ + static Caarg -parse_caarg(int mult, int type, int num, char **def) +parse_caarg(int mult, int type, int num, char *oname, char **def) { Caarg ret = (Caarg) zalloc(sizeof(*ret)); char *p = *def, *d, sav; ret->next = NULL; ret->descr = ret->action = ret->end = NULL; + ret->xor = NULL; ret->num = num; ret->type = type; + ret->opt = ztrdup(oname); + + /* Get the description. */ for (d = p; *p && *p != ':'; p++) if (*p == '\\' && p[1]) @@ -456,6 +474,9 @@ parse_caarg(int mult, int type, int num, char **def) sav = *p; *p = '\0'; ret->descr = ztrdup(rembslashcolon(d)); + + /* Get the action if there is one. */ + if (sav) { if (mult) { for (d = ++p; *p && *p != ':'; p++) @@ -474,6 +495,8 @@ parse_caarg(int mult, int type, int num, char **def) return ret; } +/* Parse an array of definitions. */ + static Cadef parse_cadef(char *nam, char **args) { @@ -485,6 +508,8 @@ parse_cadef(char *nam, char **args) nopts = ndopts = nodopts = 0; + /* First string is the auto-description definition. */ + for (p = args[0]; *p && (p[0] != '%' || p[1] != 'd'); p++); if (*p) { @@ -495,6 +520,8 @@ parse_cadef(char *nam, char **args) } else adpre = adsuf = NULL; + /* Now get the -s and -M options. */ + args++; while ((p = *args)) { if (!strcmp(p, "-s")) @@ -515,26 +542,30 @@ parse_cadef(char *nam, char **args) if (!*args) return NULL; - PERMALLOC { - ret = (Cadef) zalloc(sizeof(*ret)); - ret->next = NULL; - ret->opts = NULL; - ret->args = ret->rest = NULL; - ret->defs = arrdup(oargs); - ret->ndefs = arrlen(oargs); - ret->lastt = time(0); - if (single) { - ret->single = (Caopt *) zalloc(256 * sizeof(Caopt)); - memset(ret->single, 0, 256 * sizeof(Caopt)); - } else - ret->single = NULL; - ret->match = ztrdup(match); - } LASTALLOC; + /* Looks good. Optimistically allocate the cadef structure. */ + + ret = (Cadef) zalloc(sizeof(*ret)); + ret->next = NULL; + ret->opts = NULL; + ret->args = ret->rest = NULL; + ret->defs = zarrdup(oargs); + ret->ndefs = arrlen(oargs); + ret->lastt = time(0); + if (single) { + ret->single = (Caopt *) zalloc(256 * sizeof(Caopt)); + memset(ret->single, 0, 256 * sizeof(Caopt)); + } else + ret->single = NULL; + ret->match = ztrdup(match); + + /* Get the definitions. */ for (optp = &(ret->opts); *args; args++) { p = dupstring(*args); xnum = 0; if (*p == '(') { + /* There is a xor list, get it. */ + LinkList list = newlinklist(); LinkNode node; char **xp, sav; @@ -555,9 +586,10 @@ parse_cadef(char *nam, char **args) xnum++; *p = sav; } + /* Oops, end-of-string. */ if (*p != ')') { - free_cadef(ret); - zerrnam(nam, "invalid argument: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid argument: %s", *args, 0); return NULL; } xor = (char **) zalloc((xnum + 2) * sizeof(char *)); @@ -568,9 +600,10 @@ parse_cadef(char *nam, char **args) p++; } else xor = NULL; - + if (*p == '-' || *p == '+' || (*p == '*' && (p[1] == '-' || p[1] == '+'))) { + /* It's an option. */ Caopt opt; Caarg oargs = NULL; int multi, otype = CAO_NEXT, again = 0; @@ -578,25 +611,39 @@ parse_cadef(char *nam, char **args) rec: + /* Allowed more than once? */ if ((multi = (*p == '*'))) p++; - if ((p[0] == '-' && p[1] == '+') || - (p[0] == '+' && p[1] == '-')) { + if (((p[0] == '-' && p[1] == '+') || + (p[0] == '+' && p[1] == '-')) && + p[2] && p[2] != ':' && p[2] != '[' && + p[2] != '=' && p[2] != '-' && p[2] != '+') { + /* It's a -+ or +- definition. We just execute the whole + * stuff twice for such things. */ name = ++p; *p = (again ? '-' : '+'); again = 1 - again; } else { name = p; + /* If it's a long option skip over the first `-'. */ if (p[0] == '-' && p[1] == '-') p++; } + if (!p[1]) { + freecadef(ret); + zwarnnam(nam, "invalid argument: %s", *args, 0); + return NULL; + } + + /* Skip over the name. */ for (p++; *p && *p != ':' && *p != '[' && ((*p != '-' && *p != '+' && *p != '=') || (p[1] != ':' && p[1] != '[')); p++) if (*p == '\\' && p[1]) p++; + /* The character after the option name specifies the type. */ c = *p; *p = '\0'; if (c == '-') { @@ -609,14 +656,15 @@ parse_cadef(char *nam, char **args) otype = CAO_EQUAL; c = *++p; } + /* Get the optional description, if any. */ if (c == '[') { for (descr = ++p; *p && *p != ']'; p++) if (*p == '\\' && p[1]) p++; if (!*p) { - free_cadef(ret); - zerrnam(nam, "invalid option definition: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid option definition: %s", *args, 0); return NULL; } *p++ = '\0'; @@ -625,10 +673,11 @@ parse_cadef(char *nam, char **args) descr = NULL; if (c && c != ':') { - free_cadef(ret); - zerrnam(nam, "invalid option definition: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid option definition: %s", *args, 0); return NULL; } + /* Add the option name to the xor list if not `*-...'. */ if (!multi) { if (!xor) { xor = (char **) zalloc(2 * sizeof(char *)); @@ -637,27 +686,39 @@ parse_cadef(char *nam, char **args) xor[xnum] = ztrdup(name); } if (c == ':') { + /* There's at least one argument. */ + Caarg *oargp = &oargs; - int atype, rest; + int atype, rest, oanum = 1; char *end; + /* Loop over the arguments. */ + while (c == ':') { rest = 0; end = NULL; + /* Get the argument type. */ if (*++p == ':') { atype = CAA_OPT; p++; } else if (*p == '*') { if (*++p != ':') { - for (end = ++p; *p && *p != ':'; p++) + char sav; + + for (end = p++; *p && *p != ':'; p++) if (*p == '\\' && p[1]) p++; + sav = *p; + *p = '\0'; + end = dupstring(end); + tokenize(end); + *p = sav; } if (*p != ':') { - free_cadef(ret); - free_caargs(oargs); - zerrnam(nam, "invalid option definition: %s", + freecadef(ret); + freecaargs(oargs); + zwarnnam(nam, "invalid option definition: %s", *args, 0); return NULL; } @@ -672,54 +733,74 @@ parse_cadef(char *nam, char **args) rest = 1; } else atype = CAA_NORMAL; - *oargp = parse_caarg(!rest, atype, 0, &p); + + /* And the definition. */ + + *oargp = parse_caarg(!rest, atype, oanum++, name, &p); + if (end) + (*oargp)->end = ztrdup(end); oargp = &((*oargp)->next); if (rest) break; c = *p; } } - PERMALLOC { - *optp = opt = (Caopt) zalloc(sizeof(*opt)); - optp = &((*optp)->next); - - opt->next = NULL; - opt->name = ztrdup(name); - if (descr) - opt->descr = ztrdup(descr); - else if (adpre && oargs && !oargs->next) + /* Store the option definition. */ + + *optp = opt = (Caopt) zalloc(sizeof(*opt)); + optp = &((*optp)->next); + + opt->next = NULL; + opt->name = ztrdup(rembslashcolon(name)); + if (descr) + opt->descr = ztrdup(descr); + else if (adpre && oargs && !oargs->next) { + char *d; + + for (d = oargs->descr; *d; d++) + if (!iblank(*d)) + break; + + if (*d) opt->descr = tricat(adpre, oargs->descr, adsuf); else opt->descr = NULL; - opt->xor = xor; - opt->type = otype; - opt->args = oargs; - opt->num = nopts++; - } LASTALLOC; + } else + opt->descr = NULL; + opt->xor = xor; + opt->type = otype; + opt->args = oargs; + opt->num = nopts++; if (otype == CAO_DIRECT) ndopts++; else if (otype == CAO_ODIRECT || otype == CAO_EQUAL) nodopts++; + /* If this is for single-letter option we also store a + * pointer for the definition in the array for fast lookup. */ + if (single && name[1] && !name[2]) ret->single[STOUC(name[1])] = opt; if (again) { + /* Do it all again for `*-...'. */ p = dupstring(*args); goto rec; } } else if (*p == '*') { + /* It's a rest-argument definition. */ + int type = CAA_REST; if (*++p != ':') { - free_cadef(ret); - zerrnam(nam, "invalid rest argument definition: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid rest argument definition: %s", *args, 0); return NULL; } if (ret->rest) { - free_cadef(ret); - zerrnam(nam, "doubled rest argument definition: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "doubled rest argument definition: %s", *args, 0); return NULL; } if (*++p == ':') { @@ -729,40 +810,49 @@ parse_cadef(char *nam, char **args) } else type = CAA_RARGS; } - ret->rest = parse_caarg(0, type, -1, &p); + ret->rest = parse_caarg(0, type, -1, NULL, &p); + ret->rest->xor = xor; } else { + /* It's a normal argument definition. */ + int type = CAA_NORMAL; Caarg arg, tmp, pre; if (idigit(*p)) { + /* Argment number is given. */ int num = 0; while (*p && idigit(*p)) - num = (num * 10) + ((int) *p++); + num = (num * 10) + (((int) *p++) - '0'); anum = num + 1; } else + /* Default number. */ anum++; if (*p != ':') { - free_cadef(ret); - zerrnam(nam, "invalid argument: %s", *args, 0); + freecadef(ret); + zwarnnam(nam, "invalid argument: %s", *args, 0); return NULL; } if (*++p == ':') { + /* Optional argument. */ type = CAA_OPT; p++; } - arg = parse_caarg(0, type, anum - 1, &p); + arg = parse_caarg(0, type, anum - 1, NULL, &p); + arg->xor = xor; + + /* Sort the new definition into the existing list. */ for (tmp = ret->args, pre = NULL; tmp && tmp->num < anum - 1; pre = tmp, tmp = tmp->next); if (tmp && tmp->num == anum - 1) { - free_cadef(ret); - free_caargs(arg); - zerrnam(nam, "doubled argument definition: %s", *args, 0); + freecadef(ret); + freecaargs(arg); + zwarnnam(nam, "doubled argument definition: %s", *args, 0); return NULL; } arg->next = tmp; @@ -779,13 +869,16 @@ parse_cadef(char *nam, char **args) return ret; } +/* Given an array of definitions, return the cadef for it. From the cache + * are newly built. */ + static Cadef get_cadef(char *nam, char **args) { Cadef *p, *min, new; int i, na = arrlen(args); - for (i = MAX_CACACHE, p = cadef_cache, min = NULL; *p && i--; p++) + for (i = MAX_CACACHE, p = cadef_cache, min = NULL; *p && i; p++, i--) if (*p && na == (*p)->ndefs && arrcmp(args, (*p)->defs)) { (*p)->lastt = time(0); @@ -795,26 +888,36 @@ get_cadef(char *nam, char **args) if (i) min = p; if ((new = parse_cadef(nam, args))) { - free_cadef(*min); + freecadef(*min); *min = new; } return new; } +/* Get the option used in a word from the line, if any. */ + static Caopt ca_get_opt(Cadef d, char *line, int full, char **end) { Caopt p; - if (full) { - for (p = d->opts; p; p = p->next) - if (p->active && !strcmp(p->name, line)) - return p; - } else { + /* The full string may be an option. */ + + for (p = d->opts; p; p = p->next) + if (p->active && !strcmp(p->name, line)) { + if (end) + *end = line + strlen(line); + + return p; + } + + if (!full) { + /* The string from the line probably only begins with an option. */ for (p = d->opts; p; p = p->next) - if (p->active && p->args && p->type != CAO_NEXT && - strpfx(p->name, line)) { + if (p->active && ((!p->args || p->type == CAO_NEXT) ? + !strcmp(p->name, line) : strpfx(p->name, line))) { if (end) { + /* Return a pointer to the end of the option. */ int l = strlen(p->name); if (p->type == CAO_EQUAL && line[l] == '=') @@ -828,12 +931,13 @@ ca_get_opt(Cadef d, char *line, int full, char **end) return NULL; } +/* Same as above, only for single-letter-style. */ + static Caopt ca_get_sopt(Cadef d, char *line, int full, char **end) { Caopt p; - - line++; + char pre = *line++; if (full) { for (p = NULL; *line; line++) @@ -844,7 +948,7 @@ ca_get_sopt(Cadef d, char *line, int full, char **end) } else { for (p = NULL; *line; line++) if ((p = d->single[STOUC(*line)]) && p->active && - p->args && p->type != CAO_NEXT) { + p->args && p->type != CAO_NEXT && p->name[0] == pre) { if (end) { line++; if (p->type == CAO_EQUAL && *line == '=') @@ -852,13 +956,18 @@ ca_get_sopt(Cadef d, char *line, int full, char **end) *end = line; } break; - } else if (!p || !p->active || (line[1] && p->args)) + } else if (!p || !p->active || (line[1] && p->args) || + p->name[0] != pre) return NULL; + if (p && end) + *end = line; return p; } return NULL; } +/* Return the n'th argument definition. */ + static Caarg ca_get_arg(Cadef d, int n) { @@ -868,14 +977,16 @@ ca_get_arg(Cadef d, int n) while (a && a->num < n) a = a->next; - if (a && a->num == n) + if (a && a->num == n && a->active) return a; - return d->rest; + return (d->rest && d->rest->active ? d->rest : NULL); } return NULL; } +/* Use a xor list, marking options as inactive. */ + static void ca_inactive(Cadef d, char **xor) { @@ -885,17 +996,32 @@ ca_inactive(Cadef d, char **xor) for (; *xor; xor++) { if (xor[0][0] == ':' && !xor[0][1]) d->argsactive = 0; - else if ((opt = ca_get_opt(d, *xor, 1, NULL))) + else if (xor[0][0] == '*' && !xor[0][1]) { + if (d->rest) + d->rest->active = 0; + } else if (xor[0][0] >= '0' && xor[0][0] <= '9') { + int n = atoi(xor[0]); + Caarg a = d->args; + + while (a && a->num < n) + a = a->next; + + if (a && a->num == n) + a->active = 0; + } else if ((opt = ca_get_opt(d, *xor, 1, NULL))) opt->active = 0; } } } +/* State when parsing a command line. */ + struct castate { Cadef d; + int nopts; Caarg def, ddef; Caopt curopt; - int opt, arg, argbeg, optbeg, nargbeg, restbeg; + int opt, arg, argbeg, optbeg, nargbeg, restbeg, curpos; int inopt, inrest, inarg, nth, doff, singles; LinkList args; LinkList *oargs; @@ -904,40 +1030,55 @@ struct castate { static struct castate ca_laststate; static int ca_parsed = 0, ca_alloced = 0; +/* Pars a command line. */ + static void ca_parse_line(Cadef d) { Caarg adef, ddef; - Caopt ptr; + Caopt ptr, wasopt; struct castate state; - char *line, *pe; + char *line, *pe, **argxor = NULL; int cur, doff; Patprog endpat = NULL; + /* Free old state. */ + if (ca_alloced) { - int i = ca_laststate.d->nopts; + int i = ca_laststate.nopts; LinkList *p = ca_laststate.oargs; freelinklist(ca_laststate.args, freestr); while (i--) if (*p++) freelinklist(p[-1], freestr); + + zfree(ca_laststate.oargs, ca_laststate.d->nopts * sizeof(LinkList)); } + /* Mark everything as active. */ + for (ptr = d->opts; ptr; ptr = ptr->next) ptr->active = 1; d->argsactive = 1; + if (d->rest) + d->rest->active = 1; + for (adef = d->args; adef; adef = adef->next) + adef->active = 1; + + /* Default values for the state. */ state.d = d; + state.nopts = d->nopts; state.def = state.ddef = NULL; state.curopt = NULL; state.argbeg = state.optbeg = state.nargbeg = state.restbeg = state.nth = state.inopt = state.inarg = state.opt = state.arg = 1; state.inrest = state.doff = state.singles = state.doff = 0; - PERMALLOC { - state.args = newlinklist(); - state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList)); - memset(state.oargs, 0, d->nopts * sizeof(LinkList)); - } LASTALLOC; + state.curpos = compcurrent; + state.args = znewlinklist(); + state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList)); + memset(state.oargs, 0, d->nopts * sizeof(LinkList)); + ca_alloced = 1; memcpy(&ca_laststate, &state, sizeof(state)); @@ -947,46 +1088,67 @@ ca_parse_line(Cadef d) return; } + /* Loop over the words from the line. */ + for (line = compwords[1], cur = 2, state.curopt = NULL, state.def = NULL; line; line = compwords[cur++]) { ddef = adef = NULL; doff = state.singles = 0; + + ca_inactive(d, argxor); + + /* We've a definition for an argument, skip to the next. */ + if (state.def) { state.arg = 0; - if (state.curopt) { - PERMALLOC { - addlinknode(state.oargs[state.curopt->num], ztrdup(line)); - } LASTALLOC; - } - state.opt = (state.def->type == CAA_OPT && line[0] && line[1]); + if (state.curopt) + zaddlinknode(state.oargs[state.curopt->num], ztrdup(line)); + + state.opt = (state.def->type == CAA_OPT); if (state.def->type == CAA_REST || state.def->type == CAA_RARGS || state.def->type == CAA_RREST) { if (state.def->end && pattry(endpat, line)) { state.def = NULL; state.curopt = NULL; + state.opt = state.arg = 1; continue; } } else if ((state.def = state.def->next)) state.argbeg = cur; - else + else { state.curopt = NULL; + state.opt = 1; + } } else { - state.opt = (line[0] && line[1]); - state.arg = 1; + state.opt = state.arg = 1; state.curopt = NULL; } + if (state.opt) + state.opt = (line[0] ? (line[1] ? 2 : 1) : 0); + pe = NULL; - if (state.opt && (state.curopt = ca_get_opt(d, line, 0, &pe))) { + wasopt = NULL; + + /* See if it's an option. */ + + if (state.opt == 2 && (state.curopt = ca_get_opt(d, line, 0, &pe)) && + (state.curopt->type != CAO_EQUAL || + compwords[cur] || pe[-1] == '=')) { + ddef = state.def = state.curopt->args; doff = pe - line; state.optbeg = state.argbeg = state.inopt = cur; - PERMALLOC { - state.oargs[state.curopt->num] = newlinklist(); - } LASTALLOC; + state.singles = (d->single && (!pe || !*pe) && + state.curopt->name[1] && !state.curopt->name[2]); + + state.oargs[state.curopt->num] = znewlinklist(); + ca_inactive(d, state.curopt->xor); + /* Collect the argument strings. Maybe. */ + if (state.def && (state.curopt->type == CAO_DIRECT || (state.curopt->type == CAO_ODIRECT && pe[0]) || @@ -996,27 +1158,32 @@ ca_parse_line(Cadef d) state.def->type != CAA_RARGS && state.def->type != CAA_RREST) state.def = state.def->next; - PERMALLOC { - addlinknode(state.oargs[state.curopt->num], ztrdup(pe)); - } LASTALLOC; + + zaddlinknode(state.oargs[state.curopt->num], ztrdup(pe)); } - if (!state.def) + if (state.def) + state.opt = 0; + else { + if (!d->single || (state.curopt->name[1] && state.curopt->name[2])) + wasopt = state.curopt; state.curopt = NULL; - } else if (state.opt && d->single && + } + } else if (state.opt == 2 && d->single && (state.curopt = ca_get_sopt(d, line, 0, &pe))) { + /* Or maybe it's a single-letter option? */ + char *p; Caopt tmpopt; ddef = state.def = state.curopt->args; doff = pe - line; state.optbeg = state.argbeg = state.inopt = cur; - state.singles = !*pe; + state.singles = (!pe || !*pe); - for (p = line + 1; p <= pe; p++) { + for (p = line + 1; p < pe; p++) { if ((tmpopt = d->single[STOUC(*p)])) { - PERMALLOC { - state.oargs[tmpopt->num] = newlinklist(); - } LASTALLOC; + state.oargs[tmpopt->num] = znewlinklist(); + ca_inactive(d, tmpopt->xor); } } @@ -1029,31 +1196,40 @@ ca_parse_line(Cadef d) state.def->type != CAA_RARGS && state.def->type != CAA_RREST) state.def = state.def->next; - PERMALLOC { - addlinknode(state.oargs[state.curopt->num], ztrdup(pe)); - } LASTALLOC; + + zaddlinknode(state.oargs[state.curopt->num], ztrdup(pe)); } - if (!state.def) + if (state.def) + state.opt = 0; + else state.curopt = NULL; } else if (state.arg) { - PERMALLOC { - addlinknode(state.args, ztrdup(line)); - } LASTALLOC; + /* Otherwise it's a normal argument. */ + if (state.inopt) { + state.inopt = 0; + state.nargbeg = cur - 1; + } if ((adef = state.def = ca_get_arg(d, state.nth)) && (state.def->type == CAA_RREST || state.def->type == CAA_RARGS)) { state.inrest = 0; - for (; line; line = compwords[cur++]) { - PERMALLOC { - addlinknode(state.args, ztrdup(line)); - } LASTALLOC; - } + state.opt = (cur == state.nargbeg + 1); + state.optbeg = state.nargbeg; + state.argbeg = cur - 1; + + for (; line; line = compwords[cur++]) + zaddlinknode(state.args, ztrdup(line)); + + memcpy(&ca_laststate, &state, sizeof(state)); + ca_laststate.ddef = NULL; + ca_laststate.doff = 0; break; } - if (state.inopt) { - state.inopt = 0; - state.nargbeg = cur - 1; - } + zaddlinknode(state.args, ztrdup(line)); + + if (state.def) + argxor = state.def->xor; + if (state.def && state.def->type != CAA_NORMAL && state.def->type != CAA_OPT && state.inarg) { state.restbeg = cur; @@ -1064,6 +1240,8 @@ ca_parse_line(Cadef d) state.nth++; state.def = NULL; } + /* Do the end-pattern test if needed. */ + if (state.def && state.curopt && (state.def->type == CAA_RREST || state.def->type == CAA_RARGS)) { if (state.def->end) @@ -1071,52 +1249,77 @@ ca_parse_line(Cadef d) else { LinkList l = state.oargs[state.curopt->num]; - for (; line; line = compwords[cur++]) { - PERMALLOC { - addlinknode(l, line); - } LASTALLOC; - } + if (cur < compcurrent) + memcpy(&ca_laststate, &state, sizeof(state)); + + for (; line; line = compwords[cur++]) + zaddlinknode(l, ztrdup(line)); + + ca_laststate.ddef = NULL; + ca_laststate.doff = 0; break; } - } + } else if (state.def && state.def->end) + endpat = patcompile(state.def->end, 0, NULL); + + /* Copy the state into the global one. */ + if (cur + 1 == compcurrent) { memcpy(&ca_laststate, &state, sizeof(state)); ca_laststate.ddef = NULL; ca_laststate.doff = 0; } else if (cur == compcurrent && !ca_laststate.def) { - if ((ca_laststate.def = ddef)) - ca_laststate.doff = doff; - else { + if ((ca_laststate.def = ddef)) { + ca_laststate.singles = state.singles; + if (state.curopt && state.curopt->type == CAO_NEXT) { + ca_laststate.ddef = ddef; + ca_laststate.def = NULL; + ca_laststate.opt = 1; + state.curopt->active = 1; + } else { + ca_laststate.doff = doff; + ca_laststate.opt = 0; + } + } else { ca_laststate.def = adef; ca_laststate.ddef = NULL; - ca_laststate.argbeg = state.nargbeg; - ca_laststate.optbeg = state.restbeg; + ca_laststate.optbeg = state.nargbeg; + ca_laststate.argbeg = state.restbeg; ca_laststate.singles = state.singles; + if (wasopt) + wasopt->active = 1; } } } } +/* Build a colon-list from a list. */ + static char * ca_colonlist(LinkList l) { if (l) { LinkNode n; - int len = 1; + int len = 0; char *p, *ret, *q; - for (n = firstnode(l); n; incnode(n)) + for (n = firstnode(l); n; incnode(n)) { + len++; for (p = (char *) getdata(n); *p; p++) len += (*p == ':' ? 2 : 1); - + } ret = q = (char *) zalloc(len); - for (n = firstnode(l); n; incnode(n)) + for (n = firstnode(l); n;) { for (p = (char *) getdata(n); *p; p++) { if (*p == ':') *q++ = '\\'; *q++ = *p; } + incnode(n); + if (n) + *q++ = ':'; + } *q = '\0'; return ret; @@ -1130,36 +1333,37 @@ bin_comparguments(char *nam, char **args, char *ops, int func) int min, max, n; if (incompfunc != 1) { - zerrnam(nam, "can only be called from completion function", NULL, 0); + zwarnnam(nam, "can only be called from completion function", NULL, 0); return 1; } if (args[0][0] != '-' || !args[0][1] || args[0][2]) { - zerrnam(nam, "invalid argument: %s", args[0], 0); + zwarnnam(nam, "invalid argument: %s", args[0], 0); return 1; } if (args[0][1] != 'i' && !ca_parsed) { - zerrnam(nam, "no parsed state", NULL, 0); + zwarnnam(nam, "no parsed state", NULL, 0); return 1; } switch (args[0][1]) { case 'i': min = 2; max = -1; break; case 'D': min = 2; max = 2; break; + case 'C': min = 1; max = 1; break; case 'O': min = 4; max = 4; break; - case 'L': min = 3; max = 3; break; + case 'L': min = 3; max = 4; break; case 's': min = 1; max = 1; break; case 'M': min = 1; max = 1; break; case 'a': min = 0; max = 0; break; case 'W': min = 2; max = 2; break; default: - zerrnam(nam, "invalid option: %s", args[0], 0); + zwarnnam(nam, "invalid option: %s", args[0], 0); return 1; } n = arrlen(args) - 1; if (n < min) { - zerrnam(nam, "not enough arguments", NULL, 0); + zwarnnam(nam, "not enough arguments", NULL, 0); return 1; } else if (max >= 0 && n > max) { - zerrnam(nam, "too many arguments", NULL, 0); + zwarnnam(nam, "too many arguments", NULL, 0); return 1; } switch (args[0][1]) { @@ -1189,19 +1393,45 @@ bin_comparguments(char *nam, char **args, char *ops, int func) setsparam(args[1], ztrdup(arg->descr)); setsparam(args[2], ztrdup(arg->action)); - ignore_prefix(ca_laststate.doff); + if (ca_laststate.doff > 0) + ignore_prefix(ca_laststate.doff); if (arg->type == CAA_RARGS) - restrict_range(ca_laststate.argbeg - 1, + restrict_range(ca_laststate.optbeg, arrlen(compwords) - 1); else if (arg->type == CAA_RREST) - restrict_range(ca_laststate.optbeg - 1, + restrict_range(ca_laststate.argbeg, arrlen(compwords) - 1); return 0; } return 1; } + case 'C': + { + Caarg arg = ca_laststate.def; + + if (arg) { + char buf[20]; + + if (arg->num > 0) + sprintf(buf, "%d", arg->num); + else + strcpy(buf, "rest"); + + setsparam(args[1], (arg->opt ? tricat(arg->opt, "-", buf) : + tricat("argument-", buf, ""))); + return 0; + } + return 1; + } case 'O': - if (ca_laststate.opt) { + if ((ca_laststate.opt || (ca_laststate.doff && ca_laststate.def) || + (ca_laststate.def && + (ca_laststate.def->type == CAA_OPT || + ca_laststate.def->type >= CAA_RARGS))) && + (!ca_laststate.def || ca_laststate.def->type < CAA_RARGS || + (ca_laststate.def->type == CAA_RARGS ? + (ca_laststate.curpos == ca_laststate.argbeg + 1) : + (compcurrent == 1)))) { LinkList next = newlinklist(); LinkList direct = newlinklist(); LinkList odirect = newlinklist(); @@ -1218,14 +1448,15 @@ bin_comparguments(char *nam, char **args, char *ops, int func) default: l = equal; break; } if (p->descr) { - int len = strlen(p->name) + strlen(p->descr) + 2; + char *n = bslashcolon(p->name); + int len = strlen(n) + strlen(p->descr) + 2; str = (char *) zhalloc(len); - strcpy(str, p->name); + strcpy(str, n); strcat(str, ":"); strcat(str, p->descr); } else - str = p->name; + str = bslashcolon(p->name); addlinknode(l, str); } } @@ -1235,8 +1466,8 @@ bin_comparguments(char *nam, char **args, char *ops, int func) set_list_array(args[4], equal); return 0; - } else - return 1; + } + return 1; case 'L': { Caopt opt = ca_get_opt(ca_laststate.d, args[1], 1, NULL); @@ -1245,12 +1476,16 @@ bin_comparguments(char *nam, char **args, char *ops, int func) setsparam(args[2], ztrdup(opt->args->descr)); setsparam(args[3], ztrdup(opt->args->action)); + if (args[4]) + setsparam(args[4], tricat(opt->name, "-1", "")); + return 0; } return 1; } case 's': - if (ca_laststate.d->single && ca_laststate.singles) { + if (ca_laststate.d->single && ca_laststate.singles && + ca_laststate.opt) { setsparam(args[1], ztrdup(ca_laststate.ddef ? (ca_laststate.ddef->type == CAO_DIRECT ? @@ -1258,8 +1493,8 @@ bin_comparguments(char *nam, char **args, char *ops, int func) (ca_laststate.ddef->type == CAO_EQUAL ? "equal" : "next")) : "")); return 0; - } else - return 1; + } + return 1; case 'M': setsparam(args[1], ztrdup(ca_laststate.d->match)); return 0; @@ -1310,67 +1545,79 @@ bin_comparguments(char *nam, char **args, char *ops, int func) typedef struct cvdef *Cvdef; typedef struct cvval *Cvval; +/* Definitions for _values. */ + struct cvdef { - char *descr; - int hassep; - char sep; - Cvdef next; - Cvval vals; - char **defs; - int ndefs; - int lastt; + char *descr; /* global description */ + int hassep; /* multiple values allowed */ + char sep; /* separator character */ + Cvdef next; /* next in cache */ + Cvval vals; /* value definitions */ + char **defs; /* original strings */ + int ndefs; /* number of ... */ + int lastt; /* last time used */ }; +/* One value definition. */ + struct cvval { Cvval next; - char *name; - char *descr; - char **xor; - int type; - Caarg arg; - int active; + char *name; /* value name */ + char *descr; /* description */ + char **xor; /* xor-list */ + int type; /* CVV_* below */ + Caarg arg; /* argument definition */ + int active; /* still allowed */ }; #define CVV_NOARG 0 #define CVV_ARG 1 #define CVV_OPT 2 +/* Cache. */ + #define MAX_CVCACHE 8 static Cvdef cvdef_cache[MAX_CVCACHE]; +/* Memory stuff. */ + static void -free_cvdef(Cvdef d) +freecvdef(Cvdef d) { if (d) { Cvval p, n; zsfree(d->descr); - freearray(d->defs); + if (d->defs) + freearray(d->defs); for (p = d->vals; p; p = n) { n = p->next; zsfree(p->name); zsfree(p->descr); - freearray(p->xor); - free_caargs(p->arg); + if (p->xor) + freearray(p->xor); + freecaargs(p->arg); zfree(p, sizeof(*p)); } zfree(d, sizeof(*d)); } } +/* Parse option definitions. */ + static Cvdef parse_cvdef(char *nam, char **args) { Cvdef ret; Cvval val, *valp; Caarg arg; - char **oargs = args, sep, *name, *descr, *p, *q, **xor, c; - int xnum, multi, vtype, hassep; + char **oargs = args, sep = '\0', *name, *descr, *p, *q, **xor, c; + int xnum, multi, vtype, hassep = 0; if (args[0][0] == '-' && args[0][1] == 's' && !args[0][2]) { if (args[1][0] && args[1][1]) { - zerrnam(nam, "invalid separator: %s", args[1], 0); + zwarnnam(nam, "invalid separator: %s", args[1], 0); return NULL; } hassep = 1; @@ -1378,26 +1625,26 @@ parse_cvdef(char *nam, char **args) args += 2; } if (!args[0] || !args[1]) { - zerrnam(nam, "not enough arguments", NULL, 0); + zwarnnam(nam, "not enough arguments", NULL, 0); return NULL; } descr = *args++; - PERMALLOC { - ret = (Cvdef) zalloc(sizeof(*ret)); - ret->descr = ztrdup(descr); - ret->hassep = hassep; - ret->sep = sep; - ret->next = NULL; - ret->vals = NULL; - ret->defs = arrdup(oargs); - ret->ndefs = arrlen(oargs); - ret->lastt = time(0); - } LASTALLOC; + ret = (Cvdef) zalloc(sizeof(*ret)); + ret->descr = ztrdup(descr); + ret->hassep = hassep; + ret->sep = sep; + ret->next = NULL; + ret->vals = NULL; + ret->defs = zarrdup(oargs); + ret->ndefs = arrlen(oargs); + ret->lastt = time(0); for (valp = &(ret->vals); *args; args++) { p = dupstring(*args); xnum = 0; + + /* xor list? */ if (*p == '(') { LinkList list = newlinklist(); LinkNode node; @@ -1420,8 +1667,8 @@ parse_cvdef(char *nam, char **args) *p = sav; } if (*p != ')') { - free_cvdef(ret); - zerrnam(nam, "invalid argument: %s", *args, 0); + freecvdef(ret); + zwarnnam(nam, "invalid argument: %s", *args, 0); return NULL; } xor = (char **) zalloc((xnum + 2) * sizeof(char *)); @@ -1433,18 +1680,23 @@ parse_cvdef(char *nam, char **args) } else xor = NULL; + /* More than once allowed? */ if ((multi = (*p == '*'))) p++; + /* Skip option name. */ + for (name = p; *p && *p != ':' && *p != '['; p++) if (*p == '\\' && p[1]) p++; if (hassep && !sep && name + 1 != p) { - free_cvdef(ret); - zerrnam(nam, "no multi-letter values with empty separator allowed", NULL, 0); + freecvdef(ret); + zwarnnam(nam, "no multi-letter values with empty separator allowed", NULL, 0); return NULL; } + /* Optional description? */ + if ((c = *p) == '[') { *p = '\0'; for (descr = ++p; *p && *p != ']'; p++) @@ -1452,8 +1704,8 @@ parse_cvdef(char *nam, char **args) p++; if (!*p) { - free_cvdef(ret); - zerrnam(nam, "invalid value definition: %s", *args, 0); + freecvdef(ret); + zwarnnam(nam, "invalid value definition: %s", *args, 0); return NULL; } *p++ = '\0'; @@ -1463,8 +1715,8 @@ parse_cvdef(char *nam, char **args) descr = NULL; } if (c && c != ':') { - free_cvdef(ret); - zerrnam(nam, "invalid value definition: %s", *args, 0); + freecvdef(ret); + zwarnnam(nam, "invalid value definition: %s", *args, 0); return NULL; } if (!multi) { @@ -1474,10 +1726,12 @@ parse_cvdef(char *nam, char **args) } xor[xnum] = ztrdup(name); } + /* Get argument? */ + if (c == ':') { if (hassep && !sep) { - free_cvdef(ret); - zerrnam(nam, "no value with argument with empty separator allowed", NULL, 0); + freecvdef(ret); + zwarnnam(nam, "no value with argument with empty separator allowed", NULL, 0); return NULL; } if (*++p == ':') { @@ -1485,26 +1739,26 @@ parse_cvdef(char *nam, char **args) vtype = CVV_OPT; } else vtype = CVV_ARG; - arg = parse_caarg(0, 0, 0, &p); + arg = parse_caarg(0, 0, 0, name, &p); } else { vtype = CVV_NOARG; arg = NULL; } - PERMALLOC { - *valp = val = (Cvval) zalloc(sizeof(*val)); - valp = &((*valp)->next); - - val->next = NULL; - val->name = ztrdup(name); - val->descr = ztrdup(descr); - val->xor = xor; - val->type = vtype; - val->arg = arg; - } LASTALLOC; + *valp = val = (Cvval) zalloc(sizeof(*val)); + valp = &((*valp)->next); + + val->next = NULL; + val->name = ztrdup(name); + val->descr = ztrdup(descr); + val->xor = xor; + val->type = vtype; + val->arg = arg; } return ret; } +/* Get the definition from the cache or newly built. */ + static Cvdef get_cvdef(char *nam, char **args) { @@ -1521,12 +1775,14 @@ get_cvdef(char *nam, char **args) if (i) min = p; if ((new = parse_cvdef(nam, args))) { - free_cvdef(*min); + freecvdef(*min); *min = new; } return new; } +/* Get the definition for a value. */ + static Cvval cv_get_val(Cvdef d, char *name) { @@ -1539,6 +1795,8 @@ cv_get_val(Cvdef d, char *name) return NULL; } +/* Handle a xor list. */ + static void cv_inactive(Cvdef d, char **xor) { @@ -1551,6 +1809,8 @@ cv_inactive(Cvdef d, char **xor) } } +/* Parse state. */ + struct cvstate { Cvdef d; Caarg def; @@ -1561,6 +1821,8 @@ struct cvstate { static struct cvstate cv_laststate; static int cv_parsed = 0, cv_alloced = 0; +/* Parse the current word. */ + static void cv_parse_word(Cvdef d) { @@ -1577,9 +1839,8 @@ cv_parse_word(Cvdef d) state.d = d; state.def = NULL; state.val = NULL; - PERMALLOC { - state.vals = (LinkList) newlinklist(); - } LASTALLOC; + state.vals = (LinkList) znewlinklist(); + cv_alloced = 1; if (d->hassep) { @@ -1596,10 +1857,8 @@ cv_parse_word(Cvdef d) eq = ""; if ((ptr = cv_get_val(d, str))) { - PERMALLOC { - addlinknode(state.vals, ztrdup(str)); - addlinknode(state.vals, ztrdup(eq)); - } LASTALLOC; + zaddlinknode(state.vals, ztrdup(str)); + zaddlinknode(state.vals, ztrdup(eq)); cv_inactive(d, ptr->xor); } @@ -1625,10 +1884,8 @@ cv_parse_word(Cvdef d) eq = ""; if ((ptr = cv_get_val(d, str))) { - PERMALLOC { - addlinknode(state.vals, ztrdup(str)); - addlinknode(state.vals, ztrdup(eq)); - } LASTALLOC; + zaddlinknode(state.vals, ztrdup(str)); + zaddlinknode(state.vals, ztrdup(eq)); cv_inactive(d, ptr->xor); } @@ -1647,10 +1904,8 @@ cv_parse_word(Cvdef d) for (str = compprefix; *str; str++) { tmp[0] = *str; if ((ptr = cv_get_val(d, tmp))) { - PERMALLOC { - addlinknode(state.vals, ztrdup(tmp)); - addlinknode(state.vals, ztrdup("")); - } LASTALLOC; + zaddlinknode(state.vals, ztrdup(tmp)); + zaddlinknode(state.vals, ztrdup("")); cv_inactive(d, ptr->xor); } @@ -1658,10 +1913,8 @@ cv_parse_word(Cvdef d) for (str = compsuffix; *str; str++) { tmp[0] = *str; if ((ptr = cv_get_val(d, tmp))) { - PERMALLOC { - addlinknode(state.vals, ztrdup(tmp)); - addlinknode(state.vals, ztrdup("")); - } LASTALLOC; + zaddlinknode(state.vals, ztrdup(tmp)); + zaddlinknode(state.vals, ztrdup("")); cv_inactive(d, ptr->xor); } @@ -1696,35 +1949,36 @@ bin_compvalues(char *nam, char **args, char *ops, int func) int min, max, n; if (incompfunc != 1) { - zerrnam(nam, "can only be called from completion function", NULL, 0); + zwarnnam(nam, "can only be called from completion function", NULL, 0); return 1; } if (args[0][0] != '-' || !args[0][1] || args[0][2]) { - zerrnam(nam, "invalid argument: %s", args[0], 0); + zwarnnam(nam, "invalid argument: %s", args[0], 0); return 1; } if (args[0][1] != 'i' && !cv_parsed) { - zerrnam(nam, "no parsed state", NULL, 0); + zwarnnam(nam, "no parsed state", NULL, 0); return 1; } switch (args[0][1]) { case 'i': min = 2; max = -1; break; case 'D': min = 2; max = 2; break; + case 'C': min = 1; max = 1; break; case 'V': min = 3; max = 3; break; case 's': min = 1; max = 1; break; case 'd': min = 1; max = 1; break; - case 'L': min = 3; max = 3; break; + case 'L': min = 3; max = 4; break; case 'v': min = 1; max = 1; break; default: - zerrnam(nam, "invalid option: %s", args[0], 0); + zwarnnam(nam, "invalid option: %s", args[0], 0); return 1; } n = arrlen(args) - 1; if (n < min) { - zerrnam(nam, "not enough arguments", NULL, 0); + zwarnnam(nam, "not enough arguments", NULL, 0); return 1; } else if (max >= 0 && n > max) { - zerrnam(nam, "too many arguments", NULL, 0); + zwarnnam(nam, "too many arguments", NULL, 0); return 1; } switch (args[0][1]) { @@ -1758,6 +2012,17 @@ bin_compvalues(char *nam, char **args, char *ops, int func) } return 1; } + case 'C': + { + Caarg arg = cv_laststate.def; + + if (arg) { + setsparam(args[1], ztrdup(arg->opt)); + + return 0; + } + return 1; + } case 'V': { LinkList noarg = newlinklist(); @@ -1813,6 +2078,9 @@ bin_compvalues(char *nam, char **args, char *ops, int func) setsparam(args[2], val->arg->descr); setsparam(args[3], val->arg->action); + if (args[4]) + setsparam(args[4], ztrdup(val->name)); + return 0; } return 1; @@ -1838,37 +2106,508 @@ bin_compvalues(char *nam, char **args, char *ops, int func) return 1; } +static int +bin_compquote(char *nam, char **args, char *ops, int func) +{ + char *name; + struct value vbuf; + Value v; + + /* Anything to do? */ + + if (!compqstack || !*compqstack) + return 0; + + /* For all parameters given... */ + + while ((name = *args++)) { + name = dupstring(name); + if ((v = getvalue(&vbuf, &name, 0))) { + switch (PM_TYPE(v->pm->flags)) { + case PM_SCALAR: + { + char *val = getstrvalue(v); + + val = bslashquote(val, NULL, + (*compqstack == '\'' ? 1 : + (*compqstack == '"' ? 2 : 0))); + + setstrvalue(v, ztrdup(val)); + } + break; + case PM_ARRAY: + { + char **val = v->pm->gets.afn(v->pm); + char **new = (char **) zalloc((arrlen(val) + 1) * + sizeof(char *)); + char **p = new; + + for (; *val; val++, p++) + *p = ztrdup(bslashquote(*val, NULL, + (*compqstack == '\'' ? 1 : + (*compqstack == '"' ? 2 : + 0)))); + *p = NULL; + + setarrvalue(v, new); + } + break; + default: + zwarnnam(nam, "invalid parameter type: %s", args[-1], 0); + } + } else + zwarnnam(nam, "unknown parameter: %s", args[-1], 0); + } + return 0; +} + +/* Tags stuff. */ + +typedef struct ctags *Ctags; +typedef struct ctset *Ctset; + +/* A bunch of tag sets. */ + +struct ctags { + char **all; /* all tags offered */ + char *context; /* the current context */ + int init; /* not yet used */ + Ctset sets; /* the tag sets */ +}; + +/* A tag set. */ + +struct ctset { + Ctset next; + char **tags; /* the tags */ + char *tag; /* last tag checked for -A */ + char **ptr; /* ptr into tags for -A */ +}; + +/* Array of tag-set infos. Index is the locallevel. */ + +#define MAX_TAGS 256 +static Ctags comptags[MAX_TAGS]; + +/* locallevel at last comptags -i */ + +static int lasttaglevel; + +static void +freectset(Ctset s) +{ + Ctset n; + + while (s) { + n = s->next; + + if (s->tags) + freearray(s->tags); + zsfree(s->tag); + zfree(s, sizeof(*s)); + + s = n; + } +} + +static void +freectags(Ctags t) +{ + if (t) { + if (t->all) + freearray(t->all); + zsfree(t->context); + freectset(t->sets); + zfree(t, sizeof(*t)); + } +} + +/* Set the tags for the current local level. */ + +static void +settags(int level, char **tags) +{ + Ctags t; + + if (comptags[level]) + freectags(comptags[level]); + + comptags[level] = t = (Ctags) zalloc(sizeof(*t)); + + t->all = zarrdup(tags + 1); + t->context = ztrdup(*tags); + t->sets = NULL; + t->init = 1; +} + +/* Check if an array contains a string. */ + +static int +arrcontains(char **a, char *s, int colon) +{ + char *p, *q; + + while (*a) { + if (colon) { + for (p = s, q = *a++; *p && *q && *p != ':' && *q != ':'; p++, q++) + if (*p != *q) + break; + if ((!*p || *p == ':') && (!*q || *q == ':')) + return 1; + } else if (!strcmp(*a++, s)) + return 1; + } + return 0; +} + +static int +bin_comptags(char *nam, char **args, char *ops, int func) +{ + int min, max, n, level; + + if (incompfunc != 1) { + zwarnnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } + if (args[0][0] != '-' || !args[0][1] || + (args[0][2] && (args[0][2] != '-' || args[0][3]))) { + zwarnnam(nam, "invalid argument: %s", args[0], 0); + return 1; + } + level = locallevel - (args[0][2] ? 1 : 0); + if (level >= MAX_TAGS) { + zwarnnam(nam, "nesting level too deep", NULL, 0); + return 1; + } + if (args[0][1] != 'i' && args[0][1] != 'I' && !comptags[level]) { + zwarnnam(nam, "no tags registered", NULL, 0); + return 1; + } + switch (args[0][1]) { + case 'i': min = 2; max = -1; break; + case 'C': min = 1; max = 1; break; + case 'T': min = 0; max = 0; break; + case 'N': min = 0; max = 0; break; + case 'R': min = 1; max = 1; break; + case 'S': min = 1; max = 1; break; + case 'A': min = 2; max = 2; break; + default: + zwarnnam(nam, "invalid option: %s", args[0], 0); + return 1; + } + n = arrlen(args) - 1; + if (n < min) { + zwarnnam(nam, "not enough arguments", NULL, 0); + return 1; + } else if (max >= 0 && n > max) { + zwarnnam(nam, "too many arguments", NULL, 0); + return 1; + } + switch (args[0][1]) { + case 'i': + settags(level, args + 1); + lasttaglevel = level; + break; + case 'C': + setsparam(args[1], ztrdup(comptags[level]->context)); + break; + case 'T': + return !comptags[level]->sets; + case 'N': + { + Ctset s; + + if (comptags[level]->init) + comptags[level]->init = 0; + else if ((s = comptags[level]->sets)) { + comptags[level]->sets = s->next; + s->next = NULL; + freectset(s); + } + return !comptags[level]->sets; + } + case 'R': + { + Ctset s; + + return !((s = comptags[level]->sets) && + arrcontains(s->tags, args[1], 1)); + } + case 'A': + { + Ctset s; + + if (comptags[level] && (s = comptags[level]->sets)) { + char **q, *v = NULL; + int l = strlen(args[1]); + + if (!s->tag || strcmp(s->tag, args[1])) { + zsfree(s->tag); + s->tag = ztrdup(args[1]); + s->ptr = s->tags; + } + for (q = s->ptr; *q; q++) { + if (strpfx(args[1], *q)) { + if (!(*q)[l]) { + v = *q; + break; + } else if ((*q)[l] == ':') { + v = (*q) + l + 1; + break; + } + } + } + if (!v) { + zsfree(s->tag); + s->tag = NULL; + return 1; + } + s->ptr = q + 1; + setsparam(args[2], ztrdup(*v == '-' ? dyncat(args[1], v) : v)); + return 0; + } + return 1; + } + case 'S': + if (comptags[level]->sets) { + char **ret; + + ret = zarrdup(comptags[level]->sets->tags); + setaparam(args[1], ret); + } else + return 1; + + break; + } + return 0; +} + +static int +bin_comptry(char *nam, char **args, char *ops, int func) +{ + if (incompfunc != 1) { + zwarnnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } + if (!lasttaglevel || !comptags[lasttaglevel]) { + zwarnnam(nam, "no tags registered", NULL, 0); + return 1; + } + if (*args) { + if (!strcmp(*args, "-m")) { + char *s, *p, *q, *c, **all = comptags[lasttaglevel]->all; + LinkList list = newlinklist(); + LinkNode node; + int num = 0; + Ctset set; + + while ((s = *++args)) { + while (*s) { + while (*s && iblank(*s)) + s++; + for (p = q = s, c = NULL; *s && !iblank(*s); s++) { + if (!c && *s == ':') + c = p; + if (*s == '\\' && s[1]) + s++; + *p++ = *s; + } + if (*s) + s++; + *p = '\0'; + if (*q) { + char *qq = dupstring(q); + if (c) + *c = '\0'; + + tokenize(qq); + if (haswilds(qq)) { + Patprog prog; + + if ((prog = patcompile(qq, PAT_STATIC, NULL))) { + char **a, *n; + int l = (c ? strlen(c + 1) + 2 : 1), al; + + for (a = all; *a; a++) { + if (pattry(prog, *a)) { + n = (char *) zhalloc((al = strlen(*a)) + l); + strcpy(n, *a); + if (c) { + n[al] = ':'; + strcpy(n + al + 1, c + 1); + } + addlinknode(list, n); + num++; + } + } + } + } else if (arrcontains(all, q, 0)) { + for (set = comptags[lasttaglevel]->sets; set; + set = set->next) + if (arrcontains(set->tags, q, 0)) + break; + if (!set) { + addlinknode(list, q); + num++; + } + } + if (c) + *c = ':'; + } + } + if (num) { + char **a; + Ctset l; + + set = (Ctset) zalloc(sizeof(*set)); + + a = set->tags = (char **) zalloc((num + 1) * sizeof(char *)); + for (node = firstnode(list); node; incnode(node)) + *a++ = ztrdup((char *) getdata(node)); + + *a = NULL; + set->next = NULL; + set->ptr = NULL; + set->tag = NULL; + + if ((l = comptags[lasttaglevel]->sets)) { + while (l->next) + l = l->next; + + l->next = set; + } else + comptags[lasttaglevel]->sets = set; + } + } + } else { + char **p, **q, **all; + int sep = 0; + + if ((sep = !strcmp(*args, "-s"))) + args++; + + for (p = q = args, all = comptags[lasttaglevel]->all; *p; p++) + if (arrcontains(all, *p, 1)) { + Ctset s; + + for (s = comptags[lasttaglevel]->sets; s; s = s->next) + if (arrcontains(s->tags, *p, 0)) + break; + + if (!s) + *q++ = *p; + } + *q = NULL; + + if (*args) { + char *dummy[2]; + + do { + Ctset s = (Ctset) zalloc(sizeof(*s)), l; + + if (sep) { + dummy[0] = *args++; + dummy[1] = NULL; + s->tags = zarrdup(dummy); + } else + s->tags = zarrdup(args); + s->next = NULL; + s->ptr = NULL; + s->tag = NULL; + + if ((l = comptags[lasttaglevel]->sets)) { + while (l->next) + l = l->next; + + l->next = s; + } else + comptags[lasttaglevel]->sets = s; + } while (sep && *args); + } + } + } + return 0; +} + +static char * +fmtstr(char *str, char c, char *repl) +{ + int len, num, rlen; + char *s, *ret, *rp; + + len = strlen(str); + rlen = strlen(repl); + + for (num = 0, s = str; *s; s++) + if (*s == '%' && s[1] == c) + num++, s++; + + ret = (char *) zhalloc((num * (rlen - 2)) + len + 1); + + for (s = str, rp = ret; *s; s++) { + if (*s == '%' && s[1] == c) { + strcpy(rp, repl); + rp += rlen; + s++; + } else + *rp++ = *s; + } + *rp = '\0'; + + return ret; +} + +static int +bin_compfmt(char *nam, char **args, char *ops, int func) +{ + char *param = args[0], *str = args[1]; + + for (args += 2; *args; args++) { + if (args[0][1] != ':') { + zwarnnam(nam, "invalid argument `%s'", args[0], 0); + return 1; + } + str = fmtstr(str, **args, *args + 2); + } + setsparam(param, ztrdup(str)); + return 0; +} static struct builtin bintab[] = { - BUILTIN("compdisplay", 0, bin_compdisplay, 2, -1, 0, NULL, NULL), BUILTIN("compdescribe", 0, bin_compdescribe, 3, -1, 0, NULL, NULL), BUILTIN("comparguments", 0, bin_comparguments, 1, -1, 0, NULL, NULL), BUILTIN("compvalues", 0, bin_compvalues, 1, -1, 0, NULL, NULL), + BUILTIN("compquote", 0, bin_compquote, 1, -1, 0, NULL, NULL), + BUILTIN("comptags", 0, bin_comptags, 1, -1, 0, NULL, NULL), + BUILTIN("comptry", 0, bin_comptry, 0, -1, 0, NULL, NULL), + BUILTIN("compfmt", 0, bin_compfmt, 2, -1, 0, NULL, NULL), }; /**/ int -setup_computil(Module m) +setup_(Module m) { memset(cadef_cache, 0, sizeof(cadef_cache)); memset(cvdef_cache, 0, sizeof(cvdef_cache)); + memset(comptags, 0, sizeof(comptags)); + + lasttaglevel = 0; + return 0; } /**/ int -boot_computil(Module m) +boot_(Module m) { return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); } -#ifdef MODULE - /**/ int -cleanup_computil(Module m) +cleanup_(Module m) { deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); return 0; @@ -1876,16 +2615,17 @@ cleanup_computil(Module m) /**/ int -finish_computil(Module m) +finish_(Module m) { int i; for (i = 0; i < MAX_CACACHE; i++) - free_cadef(cadef_cache[i]); + freecadef(cadef_cache[i]); for (i = 0; i < MAX_CVCACHE; i++) - free_cvdef(cvdef_cache[i]); + freecvdef(cvdef_cache[i]); + + for (i = 0; i < MAX_TAGS; i++) + freectags(comptags[i]); return 0; } - -#endif diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list index 01862160e..33d36a18c 100644 --- a/Src/Zle/iwidgets.list +++ b/Src/Zle/iwidgets.list @@ -19,20 +19,21 @@ "backward-kill-line", backwardkillline, ZLE_KILL | ZLE_KEEPSUFFIX "backward-kill-word", backwardkillword, ZLE_KILL | ZLE_KEEPSUFFIX "backward-word", backwardword, 0 +"beep", handlefeep, 0 "beginning-of-buffer-or-history", beginningofbufferorhistory, 0 "beginning-of-history", beginningofhistory, 0 "beginning-of-line", beginningofline, 0 "beginning-of-line-hist", beginningoflinehist, 0 "capitalize-word", capitalizeword, 0 -"clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL -"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX -"copy-prev-word", copyprevword, 0 +"clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND +"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP +"copy-prev-word", copyprevword, ZLE_KEEPSUFFIX "copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX "delete-char", deletechar, ZLE_KEEPSUFFIX -"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX +"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "delete-word", deleteword, ZLE_KEEPSUFFIX "describe-key-briefly", describekeybriefly, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL -"digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "down-case-word", downcaseword, 0 "down-history", downhistory, 0 "down-line-or-history", downlineorhistory, ZLE_LINEMOVE | ZLE_LASTCOL @@ -43,13 +44,14 @@ "end-of-history", endofhistory, 0 "end-of-line", endofline, 0 "end-of-line-hist", endoflinehist, 0 +"end-of-list", endoflist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "exchange-point-and-mark", exchangepointandmark, 0 "execute-last-named-cmd", NULL, 0 "execute-named-cmd", NULL, 0 "expand-cmd-path", expandcmdpath, 0 "expand-history", expandhistory, 0 -"expand-or-complete", expandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX -"expand-or-complete-prefix", expandorcompleteprefix, ZLE_MENUCMP | ZLE_KEEPSUFFIX +"expand-or-complete", expandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP +"expand-or-complete-prefix", expandorcompleteprefix, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "expand-word", expandword, 0 "forward-char", forwardchar, 0 "forward-word", forwardword, 0 @@ -68,12 +70,12 @@ "kill-region", killregion, ZLE_KILL | ZLE_KEEPSUFFIX "kill-whole-line", killwholeline, ZLE_KILL | ZLE_KEEPSUFFIX "kill-word", killword, ZLE_KILL | ZLE_KEEPSUFFIX -"list-choices", listchoices, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"list-choices", listchoices, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_ISCOMP "list-expand", listexpand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "magic-space", magicspace, 0 -"menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX -"menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX -"neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP +"menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP +"neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "overwrite-mode", overwritemode, 0 "pound-insert", poundinsert, 0 "push-input", pushinput, 0 @@ -83,19 +85,20 @@ "quote-line", quoteline, 0 "quote-region", quoteregion, 0 "redisplay", redisplay, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL -"redo", redo, 0 -"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX +"redo", redo, ZLE_KEEPSUFFIX +"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP "run-help", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "self-insert", selfinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX "self-insert-unmeta", selfinsertunmeta, ZLE_MENUCMP | ZLE_KEEPSUFFIX "send-break", sendbreak, 0 "set-mark-command", setmarkcommand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "spell-word", spellword, 0 +"set-local-history", setlocalhistory, 0 "transpose-chars", transposechars, 0 "transpose-words", transposewords, 0 "undefined-key", undefinedkey, 0 -"undo", undo, 0 -"universal-argument", universalargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL +"undo", undo, ZLE_KEEPSUFFIX +"universal-argument", universalargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND "up-case-word", upcaseword, 0 "up-history", uphistory, 0 "up-line-or-history", uplineorhistory, ZLE_LINEMOVE | ZLE_LASTCOL @@ -145,8 +148,8 @@ "vi-open-line-below", viopenlinebelow, 0 "vi-oper-swap-case", vioperswapcase, 0 "vi-pound-insert", vipoundinsert, 0 -"vi-put-after", viputafter, ZLE_YANK -"vi-put-before", viputbefore, ZLE_YANK +"vi-put-after", viputafter, ZLE_YANK | ZLE_KEEPSUFFIX +"vi-put-before", viputbefore, ZLE_YANK | ZLE_KEEPSUFFIX "vi-quoted-insert", viquotedinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX "vi-repeat-change", virepeatchange, 0 "vi-repeat-find", virepeatfind, 0 @@ -159,7 +162,7 @@ "vi-set-mark", visetmark, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "vi-substitute", visubstitute, 0 "vi-swap-case", viswapcase, 0 -"vi-undo-change", viundochange, 0 +"vi-undo-change", viundochange, ZLE_KEEPSUFFIX "vi-unindent", viunindent, 0 "vi-up-line-or-history", viuplineorhistory, ZLE_LINEMOVE "vi-yank", viyank, 0 @@ -168,5 +171,5 @@ "what-cursor-position", whatcursorposition, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "where-is", whereis, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL "which-command", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL -"yank", yank, ZLE_YANK -"yank-pop", yankpop, ZLE_YANK +"yank", yank, ZLE_YANK | ZLE_KEEPSUFFIX +"yank-pop", yankpop, ZLE_YANK | ZLE_KEEPSUFFIX diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 8fe3e7f0b..51af32e0b 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -53,7 +53,7 @@ struct cutbuffer vibuf[35]; /**/ char *lastline; /**/ -int lastlinesz, lastll; +int lastlinesz, lastll, lastcs; /* size of line buffer */ @@ -73,7 +73,7 @@ sizeline(int sz) /* insert space for ct chars at cursor position */ /**/ -void +mod_export void spaceinline(int ct) { int i; @@ -105,7 +105,7 @@ shiftchars(int to, int cnt) } /**/ -void +mod_export void backkill(int ct, int dir) { int i = (cs -= ct); @@ -115,7 +115,7 @@ backkill(int ct, int dir) } /**/ -void +mod_export void forekill(int ct, int dir) { int i = cs; @@ -191,14 +191,14 @@ cut(int i, int ct, int dir) } /**/ -void +mod_export void backdel(int ct) { shiftchars(cs -= ct, ct); } /**/ -void +mod_export void foredel(int ct) { shiftchars(cs, ct); @@ -283,7 +283,7 @@ hstrnstr(char *haystack, int pos, char *needle, int len, int dir, int sens) * characters are read. Case is folded. */ /**/ -int +mod_export int getzlequery(void) { int c; @@ -409,19 +409,11 @@ showmsg(char const *msg) /* handle the error flag */ /**/ -void -feep(void) -{ - feepflag = 1; -} - -/**/ -void -handlefeep(void) +int +handlefeep(char **args) { - if(feepflag) - beep(); - feepflag = 0; + zbeep(); + return 0; } /***************/ @@ -446,6 +438,7 @@ initundo(void) curchange->del = curchange->ins = NULL; lastline = zalloc(lastlinesz = linesz); memcpy(lastline, line, lastll = ll); + lastcs = cs; } /**/ @@ -519,6 +512,8 @@ mkundoent(void) ch->next = NULL; ch->hist = histline; ch->off = pre; + ch->old_cs = lastcs; + ch->new_cs = cs; if(suf + pre == lastll) ch->del = NULL; else @@ -549,32 +544,36 @@ setlastline(void) if(lastlinesz != linesz) lastline = realloc(lastline, lastlinesz = linesz); memcpy(lastline, line, lastll = ll); + lastcs = cs; } /* move backwards through the change list */ /**/ -void -undo(void) +int +undo(char **args) { handleundo(); do { - if(!curchange->prev) { - feep(); - return; - } - unapplychange(curchange = curchange->prev); + if(!curchange->prev) + return 1; + if (unapplychange(curchange->prev)) + curchange = curchange->prev; + else + break; } while(curchange->flags & CH_PREV); setlastline(); + return 0; } /**/ -static void +static int unapplychange(struct change *ch) { if(ch->hist != histline) { - remember_edits(); - setline(zle_get_event(histline = ch->hist)); + zle_setline(quietgethist(ch->hist)); + cs = ch->new_cs; + return 0; } cs = ch->off; if(ch->ins) @@ -589,33 +588,37 @@ unapplychange(struct change *ch) else line[cs++] = STOUC(*c); } + cs = ch->old_cs; + return 1; } /* move forwards through the change list */ /**/ -void -redo(void) +int +redo(char **args) { handleundo(); do { - if(!curchange->next) { - feep(); - return; - } - applychange(curchange); - curchange = curchange->next; + if(!curchange->next) + return 1; + if (applychange(curchange)) + curchange = curchange->next; + else + break; } while(curchange->prev->flags & CH_NEXT); setlastline(); + return 0; } /**/ -static void +static int applychange(struct change *ch) { if(ch->hist != histline) { - remember_edits(); - setline(zle_get_event(histline = ch->hist)); + zle_setline(quietgethist(ch->hist)); + cs = ch->old_cs; + return 0; } cs = ch->off; if(ch->del) @@ -630,13 +633,15 @@ applychange(struct change *ch) else line[cs++] = STOUC(*c); } + cs = ch->new_cs; + return 1; } /* vi undo: toggle between the end of the undo list and the preceding point */ /**/ -void -viundochange(void) +int +viundochange(char **args) { handleundo(); if(curchange->next) { @@ -645,6 +650,7 @@ viundochange(void) curchange = curchange->next; } while(curchange->next); setlastline(); + return 0; } else - undo(); + return undo(args); } diff --git a/Src/builtin.c b/Src/builtin.c index 31f396d93..dd0d3e523 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -42,29 +42,30 @@ static struct builtin builtins[] = BUILTIN("[", 0, 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, bin_alias, 0, -1, 0, "Lgmr", NULL), - BUILTIN("autoload", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "t", "u"), + BUILTIN("alias", BINF_MAGICEQUALS | BINF_PLUSOPTS, bin_alias, 0, -1, 0, "Lgmr", NULL), + BUILTIN("autoload", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "tUXwkz", "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", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL), BUILTIN("chdir", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL), BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL), - BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "LRUZfilrtux", NULL), + BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZafghilrtux", NULL), BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "v", NULL), BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmr", NULL), BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL), BUILTIN("echo", BINF_PRINTOPTS | BINF_ECHOPTS, bin_print, 0, -1, BIN_ECHO, "neE", "-"), BUILTIN("echotc", 0, bin_echotc, 1, -1, 0, NULL, NULL), - BUILTIN("emulate", 0, bin_emulate, 1, 1, 0, "R", NULL), + BUILTIN("emulate", 0, bin_emulate, 1, 1, 0, "LR", NULL), BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmr", 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_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRUZfilrtu", "x"), + BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "EFLRTUZafhilrtu", "xg"), BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL), BUILTIN("fc", BINF_FCOPTS, bin_fc, 0, -1, BIN_FC, "nlreIRWAdDfEim", NULL), BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL), - BUILTIN("functions", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "mtu", NULL), + BUILTIN("float", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "EFghlrtux", "E"), + BUILTIN("functions", BINF_TYPEOPTS, bin_functions, 0, -1, 0, "mtuU", 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, "dfmrv", NULL), @@ -74,11 +75,11 @@ static struct builtin builtins[] = #endif BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "nrdDfEim", "l"), - BUILTIN("integer", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "lrtux", "i"), + BUILTIN("integer", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ghlrtux", "i"), BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL), BUILTIN("kill", 0, bin_kill, 0, -1, 0, NULL, NULL), BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL), - BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "LRUZilrtu", NULL), + BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZahilrtu", NULL), BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL), BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL), @@ -86,14 +87,18 @@ static struct builtin builtins[] = 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", 0, bin_cd, 0, 2, BIN_POPD, NULL, NULL), - BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "RDPnrslzNu0123456789pioOcm-", NULL), + BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "RDPbnrslzNu0123456789pioOcm-", NULL), BUILTIN("pushd", 0, bin_cd, 0, 2, BIN_PUSHD, NULL, NULL), BUILTIN("pushln", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, NULL, "-nz"), BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL), BUILTIN("r", BINF_R, bin_fc, 0, -1, BIN_FC, "nrl", NULL), BUILTIN("read", 0, bin_read, 0, -1, 0, "rzu0123456789pkqecnAlE", NULL), - BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "LRUZfiltux", "r"), + BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZafghiltux", "r"), BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "dfv", "r"), BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL), BUILTIN("set", BINF_PSPECIAL, bin_set, 0, -1, 0, NULL, NULL), @@ -107,7 +112,7 @@ static struct builtin builtins[] = BUILTIN("trap", BINF_PSPECIAL, 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, "ampfsw", "v"), - BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "LRUZfilrtuxm", NULL), + BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AEFLRTUZafghilrtuxm", NULL), BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL), BUILTIN("unalias", 0, bin_unhash, 1, -1, 0, "m", "a"), BUILTIN("unfunction", 0, bin_unhash, 1, -1, 0, "m", "f"), @@ -118,10 +123,8 @@ static struct builtin builtins[] = BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsw", NULL), BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsw", "ca"), BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsw", "c"), - -#ifdef DYNAMIC - BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "Laudi", NULL), -#endif + BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "ILabcfdipue", NULL), + BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUMRcmzka", NULL), }; /****************************************/ @@ -131,7 +134,7 @@ static struct builtin builtins[] = /* hash table containing builtin commands */ /**/ -HashTable builtintab; +mod_export HashTable builtintab; /**/ void @@ -142,6 +145,7 @@ createbuiltintable(void) builtintab->hash = hasher; builtintab->emptytable = NULL; builtintab->filltable = NULL; + builtintab->cmpnodes = strcmp; builtintab->addnode = addhashnode; builtintab->getnode = gethashnode; builtintab->getnode2 = gethashnode2; @@ -206,9 +210,10 @@ int execbuiltin(LinkList args, Builtin bn) { LinkNode n; - char ops[MAX_OPS], *arg, *pp, *name, **argv, **oargv, *optstr; + char ops[MAX_OPS], *arg, *pp, *name, *optstr; char *oxarg, *xarg = NULL; - int flags, sense, argc = 0, execop; + char typenumstr[] = TYPESET_OPTNUM; + int flags, sense, argc = 0, execop, xtr = isset(XTRACE), lxarg = 0; /* initialise some static variables */ auxdata = NULL; @@ -220,14 +225,11 @@ execbuiltin(LinkList args, Builtin bn) arg = (char *) ugetnode(args); -#ifdef DYNAMIC if (!bn->handlerfunc) { zwarnnam(name, "autoload failed", NULL, 0); deletebuiltin(bn->nam); return 1; } -#endif - /* get some information about the command */ flags = bn->flags; optstr = bn->optstr; @@ -249,12 +251,21 @@ execbuiltin(LinkList args, Builtin bn) break; } /* save the options in xarg, for execution tracing */ - if (xarg) { - oxarg = tricat(xarg, " ", arg); - zsfree(xarg); - xarg = oxarg; - } else - xarg = ztrdup(arg); + if (xtr) { + if (xarg) { + int l = strlen(arg) + lxarg + 1; + + oxarg = zhalloc(l + 1); + strcpy(oxarg, xarg); + oxarg[lxarg] = ' '; + strcpy(oxarg + lxarg + 1, arg); + xarg = oxarg; + lxarg = l + 1; + } else { + xarg = dupstring(arg); + lxarg = strlen(xarg); + } + } /* handle -- or - (ops['-']), and + (ops['-'] and ops['+']) */ if (arg[1] == '-') arg++; @@ -273,8 +284,7 @@ execbuiltin(LinkList args, Builtin bn) /* "typeset" may take a numeric argument * * at the tail of the options */ if (idigit(*arg) && (flags & BINF_TYPEOPT) && - (arg[-1] == 'L' || arg[-1] == 'R' || - arg[-1] == 'Z' || arg[-1] == 'i')) + strchr(typenumstr, arg[-1])) auxlen = (int)zstrtol(arg, &arg, 10); /* The above loop may have exited on an invalid option. (We * * assume that any option requiring metafication is invalid.) */ @@ -282,7 +292,6 @@ execbuiltin(LinkList args, Builtin bn) if(*arg == Meta) *++arg ^= 32; zerr("bad option: -%c", NULL, *arg); - zsfree(xarg); return 1; } arg = (char *) ugetnode(args); @@ -300,9 +309,9 @@ execbuiltin(LinkList args, Builtin bn) auxdata = arg; arg = (char *) ugetnode(args); } - /* for "typeset", -L, -R, -Z and -i take a numeric extra argument */ - if ((flags & BINF_TYPEOPT) && (execop == 'L' || execop == 'R' || - execop == 'Z' || execop == 'i') && arg && idigit(*arg)) { + /* some "typeset" options take a numeric extra argument */ + if ((flags & BINF_TYPEOPT) && strchr(typenumstr, execop) && + arg && idigit(*arg)) { auxlen = atoi(arg); arg = (char *) ugetnode(args); } @@ -322,39 +331,42 @@ execbuiltin(LinkList args, Builtin bn) while (n) argc++, incnode(n); } - /* Get the actual arguments, into argv. Oargv saves the * - * beginning of the array for later reference. */ - oargv = argv = (char **)ncalloc(sizeof(char **) * (argc + 1)); - if ((*argv++ = arg)) - while ((*argv++ = (char *)ugetnode(args))); - argv = oargv; - if (errflag) { - zsfree(xarg); - errflag = 0; - return 1; - } + { + VARARR(char *, argarr, (argc + 1)); + char **argv, **oargv; + + /* Get the actual arguments, into argv. Oargv saves the * + * beginning of the array for later reference. */ + oargv = argv = argarr; + if ((*argv++ = arg)) + while ((*argv++ = (char *)ugetnode(args))); + argv = oargv; + if (errflag) { + errflag = 0; + 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", NULL, 0); - zsfree(xarg); - 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", NULL, 0); + return 1; + } - /* display execution trace information, if required */ - if (isset(XTRACE)) { - fprintf(stderr, "%s%s", (prompt4) ? prompt4 : "", name); - if (xarg) - fprintf(stderr, " %s", xarg); - while (*oargv) - fprintf(stderr, " %s", *oargv++); - fputc('\n', stderr); - fflush(stderr); + /* display execution trace information, if required */ + if (xtr) { + printprompt4(); + fprintf(xtrerr, "%s", name); + if (xarg) + fprintf(xtrerr, " %s", xarg); + while (*oargv) + fprintf(xtrerr, " %s", *oargv++); + fputc('\n', xtrerr); + fflush(xtrerr); + } + /* call the handler function, and return its return value */ + return (*(bn->handlerfunc)) (name, argv, ops, bn->funcid); } - zsfree(xarg); - /* call the handler function, and return its return value */ - return (*(bn->handlerfunc)) (name, argv, ops, bn->funcid); } /* Enable/disable an element in one of the internal hash tables. * @@ -368,7 +380,7 @@ bin_enable(char *name, char **argv, char *ops, int func) HashTable ht; HashNode hn; ScanFunc scanfunc; - Comp com; + Patprog pprog; int flags1 = 0, flags2 = 0; int match = 0, returnval = 0; @@ -405,8 +417,8 @@ bin_enable(char *name, char **argv, char *ops, int func) for (; *argv; argv++) { /* parse pattern */ tokenize(*argv); - if ((com = parsereg(*argv))) - match += scanmatchtable(ht, com, 0, 0, scanfunc, 0); + if ((pprog = patcompile(*argv, PAT_STATIC, 0))) + match += scanmatchtable(ht, pprog, 0, 0, scanfunc, 0); else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv, 0); @@ -537,9 +549,7 @@ bin_set(char *nam, char **args, char *ops, int func) } else { /* set shell arguments */ freearray(pparams); - PERMALLOC { - pparams = arrdup(args); - } LASTALLOC; + pparams = zarrdup(args); } return 0; } @@ -567,7 +577,7 @@ bin_pwd(char *name, char **argv, char *ops, int func) /* the directory stack */ /**/ -LinkList dirstack; +mod_export LinkList dirstack; /* dirs: list the directory stack, or replace it with a provided list */ @@ -598,15 +608,13 @@ bin_dirs(char *name, char **argv, char *ops, int func) return 0; } /* replace the stack with the specified directories */ - PERMALLOC { - l = newlinklist(); - if (*argv) { - while (*argv) - addlinknode(l, ztrdup(*argv++)); - freelinklist(dirstack, freestr); - dirstack = l; - } - } LASTALLOC; + l = znewlinklist(); + if (*argv) { + while (*argv) + zaddlinknode(l, ztrdup(*argv++)); + freelinklist(dirstack, freestr); + dirstack = l; + } return 0; } @@ -618,6 +626,8 @@ set_pwd_env(void) { Param pm; + /* update the PWD and OLDPWD shell parameters */ + pm = (Param) paramtab->getnode(paramtab, "PWD"); if (pm && PM_TYPE(pm->flags) != PM_SCALAR) { pm->flags &= ~PM_READONLY; @@ -634,17 +644,22 @@ set_pwd_env(void) setsparam("OLDPWD", ztrdup(oldpwd)); pm = (Param) paramtab->getnode(paramtab, "PWD"); - if (!(pm->flags & PM_EXPORTED)) { + if (!(pm->flags & PM_EXPORTED) && + (!pm->level || (isset(ALLEXPORT) && !pm->old))) { pm->flags |= PM_EXPORTED; - pm->env = addenv("PWD", pwd); + pm->env = addenv("PWD", pwd, pm->flags); } pm = (Param) paramtab->getnode(paramtab, "OLDPWD"); - if (!(pm->flags & PM_EXPORTED)) { + if (!(pm->flags & PM_EXPORTED) && + (!pm->level || (isset(ALLEXPORT) && !pm->old))) { pm->flags |= PM_EXPORTED; - pm->env = addenv("PWD", pwd); + pm->env = addenv("OLDPWD", oldpwd, pm->flags); } } +/* 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() * @@ -657,7 +672,6 @@ bin_cd(char *nam, char **argv, char *ops, int func) { LinkNode dir; struct stat st1, st2; - int chaselinks; if (isset(RESTRICTED)) { zwarnnam(nam, "restricted", NULL, 0); @@ -678,33 +692,32 @@ bin_cd(char *nam, char **argv, char *ops, int func) goto brk; } } while (*++s); - for (s = *argv; *++s; ops[*s] = 1); + for (s = *argv; *++s; ops[STOUC(*s)] = 1); } brk: - chaselinks = ops['P'] || (isset(CHASELINKS) && !ops['L']); - PERMALLOC { - pushnode(dirstack, ztrdup(pwd)); - if (!(dir = cd_get_dest(nam, argv, ops, func))) { - zsfree(getlinknode(dirstack)); - LASTALLOC_RETURN 1; - } - } LASTALLOC; - cd_new_pwd(func, dir, chaselinks); + chasinglinks = ops['P'] || (isset(CHASELINKS) && !ops['L']); + zpushnode(dirstack, ztrdup(pwd)); + if (!(dir = cd_get_dest(nam, argv, ops, func))) { + zsfree(getlinknode(dirstack)); + return 1; + } + cd_new_pwd(func, dir); if (stat(unmeta(pwd), &st1) < 0) { + setjobpwd(); zsfree(pwd); pwd = metafy(zgetcwd(), -1, META_DUP); } else if (stat(".", &st2) < 0) chdir(unmeta(pwd)); else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) { - if (chaselinks) { + if (chasinglinks) { + setjobpwd(); zsfree(pwd); pwd = metafy(zgetcwd(), -1, META_DUP); } else { chdir(unmeta(pwd)); } } - set_pwd_env(); return 0; } @@ -726,9 +739,9 @@ cd_get_dest(char *nam, char **argv, char *ops, int func) if (func == BIN_PUSHD && unset(PUSHDTOHOME)) dir = nextnode(firstnode(dirstack)); if (dir) - insertlinknode(dirstack, dir, getlinknode(dirstack)); + zinsertlinknode(dirstack, dir, getlinknode(dirstack)); else if (func != BIN_POPD) - pushnode(dirstack, ztrdup(home)); + zpushnode(dirstack, ztrdup(home)); } else if (!argv[1]) { int dd; char *end; @@ -749,8 +762,8 @@ cd_get_dest(char *nam, char **argv, char *ops, int func) } } if (!dir) - pushnode(dirstack, ztrdup(strcmp(argv[0], "-") - ? (doprintdir--, argv[0]) : oldpwd)); + zpushnode(dirstack, ztrdup(strcmp(argv[0], "-") + ? (doprintdir--, argv[0]) : oldpwd)); } else { char *u, *d; int len1, len2, len3; @@ -766,7 +779,7 @@ cd_get_dest(char *nam, char **argv, char *ops, int func) strncpy(d, pwd, len3); strcpy(d + len3, argv[1]); strcat(d, u + len1); - pushnode(dirstack, d); + zpushnode(dirstack, d); doprintdir++; } @@ -789,7 +802,7 @@ cd_get_dest(char *nam, char **argv, char *ops, int func) zsfree(remnode(dirstack, dir)); return NULL; } - if (dest != getdata(dir)) { + if (dest != (char *)getdata(dir)) { zsfree(getdata(dir)); setdata(dir, dest); } @@ -903,40 +916,54 @@ 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 == '/') buf = tricat(pfix, "/", dest); else { - int pwl = strlen(pwd); int pfl = strlen(pfix); + dlen = strlen(pwd); - buf = zalloc(pwl + pfl + strlen(dest) + 3); + buf = zalloc(dlen + pfl + strlen(dest) + 3); strcpy(buf, pwd); - buf[pwl] = '/'; - strcpy(buf + pwl + 1, pfix); - buf[pwl + 1 + pfl] = '/'; - strcpy(buf + pwl + pfl + 2, dest); + buf[dlen] = '/'; + strcpy(buf + dlen + 1, pfix); + buf[dlen + 1 + pfl] = '/'; + strcpy(buf + dlen + pfl + 2, dest); } } else if (*dest == '/') buf = ztrdup(dest); else { - int pwl = strlen(pwd); - - buf = zalloc(pwl + strlen(dest) + 2); + dlen = strlen(pwd); + if (pwd[dlen-1] == '/') + --dlen; + buf = zalloc(dlen + strlen(dest) + 2); strcpy(buf, pwd); - buf[pwl] = '/'; - strcpy(buf + pwl + 1, dest); + buf[dlen] = '/'; + strcpy(buf + dlen + 1, dest); } - /* Normalise path. See the definition of fixdir() for what this means. */ - fixdir(buf); - - if (lchdir(buf, NULL, hard)) { - zsfree(buf); + /* 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) && lchdir(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); } @@ -944,10 +971,9 @@ cd_try_chdir(char *pfix, char *dest, int hard) /**/ static void -cd_new_pwd(int func, LinkNode dir, int chaselinks) +cd_new_pwd(int func, LinkNode dir) { - Param pm; - List l; + Eprog prog; char *new_pwd, *s; int dirstacksize; @@ -961,7 +987,7 @@ cd_new_pwd(int func, LinkNode dir, int chaselinks) } else if (func == BIN_CD && unset(AUTOPUSHD)) zsfree(getlinknode(dirstack)); - if (chaselinks) { + if (chasinglinks) { s = new_pwd; new_pwd = findpwd(s); zsfree(s); @@ -980,14 +1006,10 @@ cd_new_pwd(int func, LinkNode dir, int chaselinks) current (i.e. new) pwd */ zsfree(oldpwd); oldpwd = pwd; + setjobpwd(); pwd = new_pwd; - /* update the PWD and OLDPWD shell parameters */ - if ((pm = (Param) paramtab->getnode(paramtab, "PWD")) && - (pm->flags & PM_EXPORTED) && pm->env) - pm->env = replenv(pm->env, pwd); - if ((pm = (Param) paramtab->getnode(paramtab, "OLDPWD")) && - (pm->flags & PM_EXPORTED) && pm->env) - pm->env = replenv(pm->env, oldpwd); + set_pwd_env(); + if (unset(PUSHDSILENT) && func != BIN_CD && isset(INTERACTIVE)) printdirstack(); else if (doprintdir) { @@ -996,10 +1018,14 @@ cd_new_pwd(int func, LinkNode dir, int chaselinks) } /* execute the chpwd function */ - if ((l = getshfunc("chpwd")) != &dummy_list) { + if ((prog = getshfunc("chpwd")) != &dummy_eprog) { + int osc = sfcontext; + fflush(stdout); fflush(stderr); - doshfunc(l, NULL, 0, 1); + sfcontext = SFC_HOOK; + doshfunc("chpwd", prog, NULL, 0, 1); + sfcontext = osc; } dirstacksize = getiparam("DIRSTACKSIZE"); @@ -1029,14 +1055,20 @@ printdirstack(void) } /* Normalise a path. Segments consisting of ., and foo/.. * - * combinations, are removed and the path is unmetafied. */ + * combinations, are removed and the path is unmetafied. + * Returns 1 if we found a ../ path which should force links to + * be chased, 0 otherwise. + */ /**/ -static void +int fixdir(char *src) { - char *dest = src; - char *d0 = dest; + char *dest = src, *d0 = dest; +#ifdef __CYGWIN__ + char *s0 = src; +#endif + int ret = 0; /*** if have RFS superroot directory ***/ #ifdef HAVE_SUPERROOT @@ -1053,6 +1085,11 @@ fixdir(char *src) for (;;) { /* compress multiple /es into single */ if (*src == '/') { +#ifdef __CYGWIN__ + /* allow leading // under cygwin */ + if (src == s0 && src[1] == '/') + *dest++ = *src++; +#endif *dest++ = *src++; while (*src == '/') src++; @@ -1063,17 +1100,40 @@ fixdir(char *src) while (dest > d0 + 1 && dest[-1] == '/') dest--; *dest = '\0'; - return; + return ret; } - if (dest > d0 + 1 && src[0] == '.' && src[1] == '.' && - (src[2] == '\0' || src[2] == '/')) { - /* remove a foo/.. combination */ - for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--); - if (dest[-1] != '/') - dest--; - src++; - while (*++src == '/'); - } else if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) { + if (src[0] == '.' && src[1] == '.' && + (src[2] == '\0' || src[2] == '/')) { + if (isset(CHASEDOTS)) { + ret = 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 { @@ -1086,7 +1146,7 @@ fixdir(char *src) } /**/ -void +mod_export void printqt(char *str) { /* Print str, but turn any single quote into '\'' or ''. */ @@ -1098,7 +1158,7 @@ printqt(char *str) } /**/ -void +mod_export void printif(char *str, int c) { /* If flag c has an argument, print that */ @@ -1119,7 +1179,7 @@ bin_fc(char *nam, char **argv, char *ops, int func) int first = -1, last = -1, retval, minflag = 0; char *s; struct asgment *asgf = NULL, *asgl = NULL; - Comp com = NULL; + Patprog pprog = NULL; /* fc is only permitted in interactive shells */ if (!interact) { @@ -1130,33 +1190,31 @@ bin_fc(char *nam, char **argv, char *ops, int func) * as a pattern that history lines have to match */ if (*argv && ops['m']) { tokenize(*argv); - if (!(com = parsereg(*argv++))) { + if (!(pprog = patcompile(*argv++, 0, NULL))) { zwarnnam(nam, "invalid match pattern", NULL, 0); return 1; } } if (ops['R']) { /* read history from a file */ - readhistfile(*argv ? *argv : getsparam("HISTFILE"), 1); + readhistfile(*argv, 1, ops['I'] ? HFILE_SKIPOLD : 0); return 0; } if (ops['W']) { /* write history to a file */ - savehistfile(*argv ? *argv : getsparam("HISTFILE"), 1, - (ops['I'] ? 2 : 0)); + savehistfile(*argv, 1, ops['I'] ? HFILE_SKIPOLD : 0); return 0; } if (ops['A']) { /* append history to a file */ - savehistfile(*argv ? *argv : getsparam("HISTFILE"), 1, - (ops['I'] ? 3 : 1)); + savehistfile(*argv, 1, HFILE_APPEND | (ops['I'] ? HFILE_SKIPOLD : 0)); return 0; } if (!(ops['l'] && unset(HISTNOSTORE))) remhist(); /* put foo=bar type arguments into the substitution list */ while (*argv && equalsplit(*argv, &s)) { - Asgment a = (Asgment) alloc(sizeof *a); + Asgment a = (Asgment) zhalloc(sizeof *a); if (!asgf) asgf = asgl = a; @@ -1191,9 +1249,9 @@ bin_fc(char *nam, char **argv, char *ops, int func) } /* default values of first and last, and range checking */ if (first == -1) - first = (ops['l']) ? curhist - 16 : curhist - 1; + first = ops['l']? addhistnum(curhist,-16,0) : addhistnum(curhist,-1,0); if (last == -1) - last = (ops['l']) ? curhist - 1 : first; + last = ops['l']? addhistnum(curhist,-1,0) : first; if (first < firsthist()) first = firsthist(); if (last == -1) @@ -1204,7 +1262,7 @@ bin_fc(char *nam, char **argv, char *ops, int func) /* list the required part of the history */ retval = fclist(stdout, !ops['n'], ops['r'], ops['D'], ops['d'] + ops['f'] * 2 + ops['E'] * 4 + ops['i'] * 8, - first, last, asgf, com); + first, last, asgf, pprog); else { /* edit history file, and (if successful) use the result as a new command */ int tempfd; @@ -1218,20 +1276,21 @@ bin_fc(char *nam, char **argv, char *ops, int func) ((out = fdopen(tempfd, "w")) == NULL)) { zwarnnam("fc", "can't open temp file: %e", NULL, errno); } else { - if (!fclist(out, 0, ops['r'], 0, 0, first, last, asgf, com)) { + if (!fclist(out, 0, ops['r'], 0, 0, first, last, asgf, pprog)) { char *editor; editor = auxdata ? auxdata : getsparam("FCEDIT"); if (!editor) editor = DEFAULT_FCEDIT; - if (fcedit(editor, fil)) + if (fcedit(editor, fil)) { if (stuff(fil)) zwarnnam("fc", "%e: %s", s, errno); else { loop(0,1); retval = lastval; } + } } } unlink(fil); @@ -1257,7 +1316,7 @@ fcgetcomm(char *s) * numbers indicate reversed numbering. */ if ((cmd = atoi(s))) { if (cmd < 0) - cmd = curhist + cmd; + cmd = addhistnum(curhist,cmd,HIST_FOREIGN); if (cmd >= curhist) { zwarnnam("fc", "bad history number: %d", 0, cmd); return -1; @@ -1289,7 +1348,7 @@ fcsubs(char **sp, struct asgment *sub) oldpos = s; /* loop over occurences of oldstr in s, replacing them with newstr */ while ((newpos = (char *)strstr(oldpos, oldstr))) { - newmem = (char *) alloc(1 + (newpos - s) + newmem = (char *) zhalloc(1 + (newpos - s) + strlen(newstr) + strlen(newpos + strlen(oldstr))); ztrncpy(newmem, s, newpos - s); strcat(newmem, newstr); @@ -1318,10 +1377,10 @@ fcsubs(char **sp, struct asgment *sub) /**/ static int -fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment *subs, Comp com) +fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment *subs, Patprog pprog) { int fclistdone = 0; - char *s, *hs; + char *s; Histent ent; /* reverse range if required */ @@ -1334,28 +1393,31 @@ fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment if (!subs) fclistdone = 1; - for (;;) { - hs = quietgetevent(first); - if (!hs) { + ent = gethistent(first, first < last? GETHIST_DOWNWARD : GETHIST_UPWARD); + if (!ent || (first < last? ent->histnum > last : ent->histnum < last)) { + if (first == last) zwarnnam("fc", "no such event: %d", NULL, first); - return 1; - } - s = dupstring(hs); + else + zwarnnam("fc", "no events in that range", NULL, 0); + return 1; + } + + for (;;) { + s = dupstring(ent->text); /* this if does the pattern matching, if required */ - if (!com || domatch(s, com, 0)) { + if (!pprog || pattry(pprog, s)) { /* perform substitution */ fclistdone |= fcsubs(&s, subs); /* do numbering */ - if (n) - fprintf(f, "%5d ", first); - ent = NULL; + if (n) { + fprintf(f, "%5d%c ", ent->histnum, + ent->flags & HIST_FOREIGN? '*' : ' '); + } /* output actual time (and possibly date) of execution of the command, if required */ if (d) { struct tm *ltm; - if (!ent) - ent = gethistent(first); ltm = localtime(&ent->stim); if (d >= 2) { if (d >= 8) { @@ -1377,8 +1439,6 @@ fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment /* display the time taken by the command, if required */ if (D) { long diff; - if (!ent) - ent = gethistent(first); diff = (ent->ftim) ? ent->ftim - ent->stim : 0; fprintf(f, "%ld:%02ld ", diff / 60, diff % 60); } @@ -1391,12 +1451,14 @@ fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment fprintf(f, "%s\n", s); } /* move on to the next history line, or quit the loop */ - if (first == last) - break; - else if (first > last) - first--; - else - first++; + if (first < last) { + if (!(ent = down_histent(ent)) || ent->histnum > last) + break; + } + else { + if (!(ent = up_histent(ent)) || ent->histnum < last) + break; + } } /* final processing */ @@ -1465,6 +1527,265 @@ getasg(char *s) return &asg; } +/* function to set a single parameter */ + +/**/ +Param +typeset_single(char *cname, char *pname, Param pm, int func, + int on, int off, int roff, char *value, Param altpm) +{ + int usepm, tc, keeplocal = 0, newspecial = 0; + + /* + * 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. + */ + usepm = pm && !(pm->flags & PM_UNSET); + + /* + * We need to compare types with an existing pm if special, + * even if that's unset + */ + if (pm && (pm->flags & PM_SPECIAL)) + usepm = 1; + + /* + * 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) flags 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. + */ + newspecial = (pm->flags & PM_SPECIAL) + && !(on & PM_HIDE) && !(pm->flags & PM_HIDE & ~off); + usepm = 0; + } + + /* attempting a type conversion, or making a tied colonarray? */ + tc = 0; + if (usepm || newspecial) { + int chflags = ((off & pm->flags) | (on & ~pm->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; + } + if (tc && (pm->flags & PM_SPECIAL)) { + zerrnam(cname, "%s: can't change type of a special parameter", + pname, 0); + return NULL; + } + + /* + * According to the manual, local parameters don't get 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 && pm->level) || + (!usepm && (pm || (locallevel && (on & PM_LOCAL))))) + on &= ~PM_EXPORTED; + + if (usepm) { + on &= ~PM_LOCAL; + if (!on && !roff && !value) { + paramtab->printnode((HashNode)pm, 0); + return pm; + } + if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) { + zerrnam(cname, "%s: restricted", pname, 0); + return pm; + } + if ((on & PM_UNIQUE) && !(pm->flags & PM_READONLY & ~off)) { + Param apm; + if (PM_TYPE(pm->flags) == PM_ARRAY) + uniqarray((*pm->gets.afn) (pm)); + else if (PM_TYPE(pm->flags) == PM_SCALAR && pm->ename && + (apm = (Param) paramtab->getnode(paramtab, pm->ename))) + uniqarray((*apm->gets.afn) (apm)); + } + pm->flags = (pm->flags | on) & ~(off | PM_UNSET); + /* This auxlen/pm->ct stuff is a nasty hack. */ + if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER | + PM_EFLOAT | PM_FFLOAT)) && + auxlen) + pm->ct = auxlen; + if (!(pm->flags & (PM_ARRAY|PM_HASHED))) { + if (pm->flags & PM_EXPORTED) { + if (!(pm->flags & PM_UNSET) && !pm->env && !value) + pm->env = addenv(pname, getsparam(pname), pm->flags); + } else if (pm->env && + (!pm->level || (isset(ALLEXPORT) && !pm->old))) { + delenv(pm->env); + zsfree(pm->env); + pm->env = NULL; + } + if (value) + setsparam(pname, ztrdup(value)); + } else if (value) { + zwarnnam(cname, "can't assign new value for array %s", pname, 0); + return NULL; + } + return pm; + } + + /* + * 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->flags; + /* ...but turn off existing readonly so we can delete it */ + pm->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. + */ + if (!value && !((pm->flags|on) & (PM_ARRAY|PM_HASHED))) + value = dupstring(getsparam(pname)); + /* pname may point to pm->nam which is about to disappear */ + pname = dupstring(pname); + unsetparam_pm(pm, 0, 1); + } + + if (newspecial) { + Param tpm, pm2; + if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) { + zerrnam(cname, "%s: restricted", pname, 0); + 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) zalloc(sizeof *tpm); + + tpm->nam = pm->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->flags = pm->flags | PM_NORESTORE; + } else { + copyparam(tpm, pm, 1); + } + tpm->old = pm->old; + tpm->level = pm->level; + tpm->ct = pm->ct; + tpm->env = pm->env; + + pm->old = tpm; + /* + * The remaining on/off flags should be harmless to use, + * because we've checked for unpleasant surprises above. + */ + pm->flags = (PM_TYPE(pm->flags) | on | PM_SPECIAL) & ~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|PM_INTEGER| + PM_EFLOAT|PM_FFLOAT)) + pm->ct = auxlen; + else + pm->ct = 0; + pm->env = NULL; + } else { + /* + * Create a new node for a parameter with the flags in `on' minus the + * readonly flag + */ + pm = createparam(pname, on & ~PM_READONLY); + DPUTS(!pm, "BUG: parameter not created"); + pm->ct = auxlen; + } + + if (altpm && PM_TYPE(pm->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. + */ + pm->sets.cfn = colonarrsetfn; + pm->gets.cfn = colonarrgetfn; + pm->u.data = &altpm->u.arr; + } + + if (keeplocal) + pm->level = keeplocal; + else if (on & PM_LOCAL) + pm->level = locallevel; + if (value && !(pm->flags & (PM_ARRAY|PM_HASHED))) + setsparam(pname, ztrdup(value)); + else if (newspecial && !(pm->old->flags & PM_NORESTORE)) { + /* + * We need to use the special setting function to re-initialise + * the special parameter to empty. + */ + switch (PM_TYPE(pm->flags)) { + case PM_SCALAR: + pm->sets.cfn(pm, ztrdup("")); + break; + case PM_INTEGER: + pm->sets.ifn(pm, 0); + break; + case PM_EFLOAT: + case PM_FFLOAT: + pm->sets.ffn(pm, 0.0); + break; + case PM_ARRAY: + pm->sets.afn(pm, mkarray(NULL)); + break; + case PM_HASHED: + pm->sets.hfn(pm, newparamtable(17, pm->nam)); + break; + } + } + pm->flags |= (on & PM_READONLY); + if (value && (pm->flags & (PM_ARRAY|PM_HASHED))) { + zerrnam(cname, "%s: can't assign initial value for array", pname, 0); + /* the only safe thing to do here seems to be unset the param */ + unsetparam_pm(pm, 0, 1); + return NULL; + } + + return pm; +} + /* declare, export, integer, local, readonly, typeset */ /**/ @@ -1473,10 +1794,10 @@ bin_typeset(char *name, char **argv, char *ops, int func) { Param pm; Asgment asg; - Comp com; - char *optstr = "iLRZlurtxU"; - int on = 0, off = 0, roff, bit = PM_INTEGER; - int initon, initoff, of, i; + Patprog pprog; + char *optstr = TYPESET_OPTSTR; + int on = 0, off = 0, roff, bit = PM_ARRAY; + int i; int returnval = 0, printflags = 0; /* hash -f is really the builtin `functions' */ @@ -1487,25 +1808,40 @@ bin_typeset(char *name, char **argv, char *ops, int func) * Unfortunately, this depends on the order * * these flags are defined in zsh.h */ for (; *optstr; optstr++, bit <<= 1) - if (ops[*optstr] == 1) + if (ops[STOUC(*optstr)] == 1) on |= bit; - else if (ops[*optstr] == 2) + else if (ops[STOUC(*optstr)] == 2) off |= bit; roff = off; /* Sanity checks on the options. Remove conficting options. */ + if (on & PM_FFLOAT) { + off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | 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_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY | + PM_HASHED | PM_INTEGER | PM_FFLOAT; if (on & PM_INTEGER) - off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY; + off |= PM_RIGHT_B | PM_LEFT | PM_RIGHT_Z | PM_UPPER | PM_ARRAY | + PM_HASHED | PM_EFLOAT | PM_FFLOAT; if (on & PM_LEFT) - off |= PM_RIGHT_B | PM_INTEGER; + off |= PM_RIGHT_B | PM_INTEGER | PM_EFLOAT | PM_FFLOAT; if (on & PM_RIGHT_B) - off |= PM_LEFT | PM_INTEGER; + off |= PM_LEFT | PM_INTEGER | PM_EFLOAT | PM_FFLOAT; if (on & PM_RIGHT_Z) - off |= PM_INTEGER; + off |= PM_INTEGER | PM_EFLOAT | PM_FFLOAT; 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; /* Given no arguments, list whatever the options specify. */ @@ -1518,148 +1854,171 @@ bin_typeset(char *name, char **argv, char *ops, int func) return 0; } + if (!ops['g'] && !ops['x']) + on |= PM_LOCAL; + + if (on & PM_TIED) { + Param apm; + struct asgment asg0; + char *oldval = NULL; + + if (ops['m']) { + zwarnnam(name, "incompatible options for -T", NULL, 0); + return 1; + } + on &= ~off; + if (!argv[1] || argv[2]) { + zwarnnam(name, "-T requires names of scalar and array", NULL, 0); + return 1; + } + + if (!(asg = getasg(argv[0]))) + return 1; + asg0 = *asg; + if (!(asg = getasg(argv[1]))) + return 1; + if (!strcmp(asg0.name, asg->name)) { + zerrnam(name, "can't tie a variable to itself", NULL, 0); + return 1; + } + /* + * 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->flags & PM_UNSET) + && (locallevel == pm->level || !(on & PM_LOCAL))) { + if (!asg0.value && !(PM_TYPE(pm->flags) & (PM_ARRAY|PM_HASHED))) + oldval = ztrdup(getsparam(asg0.name)); + on |= (pm->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. + */ + if (!(apm=typeset_single(name, asg->name, + (Param)paramtab->getnode(paramtab, + asg->name), + func, (on | PM_ARRAY) & ~PM_EXPORTED, + off, roff, asg->value, NULL))) + 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.value, apm))) { + if (oldval) + zsfree(oldval); + unsetparam_pm(apm, 1, 1); + return 1; + } + + pm->ename = ztrdup(asg->name); + apm->ename = ztrdup(asg0.name); + if (oldval) + setsparam(asg0.name, oldval); + + return 0; + } + if (off & PM_TIED) { + zerrnam(name, "use unset to remove tied variables", NULL, 0); + return 1; + } + /* With the -m option, treat arguments as glob patterns */ if (ops['m']) { while ((asg = getasg(*argv++))) { + LinkList pmlist = newlinklist(); + LinkNode pmnode; + tokenize(asg->name); /* expand argument */ - if (!(com = parsereg(asg->name))) { + if (!(pprog = patcompile(asg->name, 0, NULL))) { untokenize(asg->name); zwarnnam(name, "bad pattern : %s", argv[-1], 0); returnval = 1; continue; } - /* If no options or values are given, display all * - * parameters matching the glob pattern. */ - if (!(on || roff || asg->value)) { - scanmatchtable(paramtab, com, 0, 0, paramtab->printnode, 0); - continue; - } - /* Since either options or values are given, we search * - * through the parameter table and change all parameters * - * matching the glob pattern to have these flags and/or * - * value. */ + /* + * 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->next) { - if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) + for (pm = (Param) paramtab->nodes[i]; pm; + pm = (Param) pm->next) { + if (((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) || + (pm->flags & PM_UNSET)) continue; - if (domatch(pm->nam, com, 0)) { - /* set up flags if we have any */ - if (on || roff) { - if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) && - !(pm->flags & PM_READONLY & ~off)) - uniqarray((*pm->gets.afn) (pm)); - pm->flags = (pm->flags | on) & ~off; - if (PM_TYPE(pm->flags) != PM_ARRAY) { - if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) && auxlen) - pm->ct = auxlen; - /* did we just export this? */ - if ((pm->flags & PM_EXPORTED) && !pm->env) { - pm->env = addenv(pm->nam, (asg->value) ? asg->value : getsparam(pm->nam)); - } else if (!(pm->flags & PM_EXPORTED) && pm->env) { - /* did we just unexport this? */ - delenv(pm->env); - zsfree(pm->env); - pm->env = NULL; - } - } - } - /* set up a new value if given */ - if (asg->value) { - setsparam(pm->nam, ztrdup(asg->value)); - } - } + if (pattry(pprog, pm->nam)) + addlinknode(pmlist, pm); } } + for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) { + pm = (Param) getdata(pmnode); + if (!typeset_single(name, pm->nam, pm, func, on, off, roff, + asg->value, NULL)) + returnval = 1; + } } return returnval; } - /* Save the values of on, off, and func */ - initon = on; - initoff = off; - of = func; - /* Take arguments literally. Don't glob */ while ((asg = getasg(*argv++))) { - /* restore the original values of on, off, and func */ - on = initon; - off = initoff; - func = of; - on &= ~PM_ARRAY; - /* check if argument is a valid identifier */ if (!isident(asg->name)) { zerr("not an identifier: %s", asg->name, 0); returnval = 1; continue; } - bit = 0; /* flag for switching int<->not-int */ - if ((pm = (Param)paramtab->getnode(paramtab, asg->name)) && - (((pm->flags & PM_SPECIAL) && pm->level == locallevel) || - (!(pm->flags & PM_UNSET) && - ((locallevel == pm->level) || func == BIN_EXPORT) && - !(bit = ((off & pm->flags) | (on & ~pm->flags)) & PM_INTEGER)))) { - /* if no flags or values are given, just print this parameter */ - if (!on && !roff && !asg->value) { - paramtab->printnode((HashNode) pm, 0); - continue; - } - if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) { - zerrnam(name, "%s: restricted", pm->nam, 0); - returnval = 1; - continue; - } - if((pm->flags & PM_SPECIAL) && - PM_TYPE((pm->flags | on) & ~off) != PM_TYPE(pm->flags)) { - zerrnam(name, "%s: cannot change type of a special parameter", - pm->nam, 0); - returnval = 1; - continue; - } - if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) && - !(pm->flags & PM_READONLY & ~off)) - uniqarray((*pm->gets.afn) (pm)); - pm->flags = (pm->flags | on) & ~off; - if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) && - auxlen) - pm->ct = auxlen; - if (PM_TYPE(pm->flags) != PM_ARRAY) { - if (pm->flags & PM_EXPORTED) { - if (!(pm->flags & PM_UNSET) && !pm->env && !asg->value) - pm->env = addenv(asg->name, getsparam(asg->name)); - } else if (pm->env) { - delenv(pm->env); - zsfree(pm->env); - pm->env = NULL; - } - if (asg->value) - setsparam(asg->name, ztrdup(asg->value)); - } - } else { - if (bit) { - if (pm->flags & PM_READONLY) { - on |= ~off & PM_READONLY; - pm->flags &= ~PM_READONLY; - } - if (!asg->value) - asg->value = dupstring(getsparam(asg->name)); - unsetparam(asg->name); - } - /* create a new node for a parameter with the * - * flags in `on' minus the readonly flag */ - pm = createparam(ztrdup(asg->name), on & ~PM_READONLY); - DPUTS(!pm, "BUG: parameter not created"); - pm->ct = auxlen; - if (func != BIN_EXPORT) - pm->level = locallevel; - if (asg->value) - setsparam(asg->name, ztrdup(asg->value)); - pm->flags |= (on & PM_READONLY); - } + if (!typeset_single(name, asg->name, + (Param) (paramtab == realparamtab ? + gethashnode2(paramtab, asg->name) : + paramtab->getnode(paramtab, asg->name)), + func, on, off, roff, asg->value, NULL)) + returnval = 1; } return returnval; } +/* Helper for bin_functions() when run as "autoload -X" */ + +/**/ +int +eval_autoload(Shfunc shf, char *name, char *ops, int func) +{ + if (!(shf->flags & PM_UNDEFINED)) + return 1; + + if (shf->funcdef) { + freeeprog(shf->funcdef); + shf->funcdef = &dummy_eprog; + } + if (ops['X'] == 1) { + 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, (ops['k'] ? 2 : (ops['z'] ? 0 : 1)), 1); +} + /* Display or change the attributes of shell functions. * * If called as autoload, it will define a new autoloaded * * (undefined) shell function. */ @@ -1668,34 +2027,55 @@ bin_typeset(char *name, char **argv, char *ops, int func) int bin_functions(char *name, char **argv, char *ops, int func) { - Comp com; + Patprog pprog; Shfunc shf; int i, returnval = 0; - int on = 0, off = 0; + int on = 0, off = 0, pflags = 0; /* Do we have any flags defined? */ - if (ops['u'] || ops['t']) { - if (ops['u'] == 1) - on |= PM_UNDEFINED; - else if (ops['u'] == 2) - off |= PM_UNDEFINED; - - if (ops['t'] == 1) - on |= PM_TAGGED; - else if (ops['t'] == 2) - off |= PM_TAGGED; - } - - if (off & PM_UNDEFINED) { + if (ops['u'] == 2) + off |= PM_UNDEFINED; + else if (ops['u'] == 1 || ops['X']) + on |= PM_UNDEFINED; + if (ops['U'] == 1) + on |= PM_UNALIASED|PM_UNDEFINED; + else if (ops['U'] == 2) + off |= PM_UNALIASED; + if (ops['t'] == 1) + on |= PM_TAGGED; + else if (ops['t'] == 2) + off |= PM_TAGGED; + + if ((off & PM_UNDEFINED) || (ops['k'] && ops['z']) || + (ops['X'] != 2 && (ops['k'] || ops['z'])) || + (ops['X'] == 1 && (ops['m'] || *argv || !scriptname))) { zwarnnam(name, "invalid option(s)", NULL, 0); return 1; } + if (ops['f'] == 2 || ops['+']) + pflags |= PRINT_NAMEONLY; + /* If no arguments given, we will print functions. If flags * * are given, we will print only functions containing these * * flags, else we'll print them all. */ if (!*argv) { - scanhashtable(shfunctab, 1, on|off, DISABLED, shfunctab->printnode, 0); + if (ops['X'] == 1) { + if ((shf = (Shfunc) shfunctab->getnode(shfunctab, scriptname))) { + DPUTS(!shf->funcdef, + "BUG: Calling autoload from empty function"); + } else { + shf = (Shfunc) zcalloc(sizeof *shf); + shfunctab->addnode(shfunctab, ztrdup(scriptname), shf); + } + shf->flags = on; + return eval_autoload(shf, scriptname, ops, func); + } else { + if (ops['U'] && !ops['u']) + on &= ~PM_UNDEFINED; + scanhashtable(shfunctab, 1, on|off, DISABLED, shfunctab->printnode, + pflags); + } return 0; } @@ -1705,16 +2085,25 @@ bin_functions(char *name, char **argv, char *ops, int func) for (; *argv; argv++) { /* expand argument */ tokenize(*argv); - if ((com = parsereg(*argv))) { + if ((pprog = patcompile(*argv, PAT_STATIC, 0))) { /* with no options, just print all functions matching the glob pattern */ if (!(on|off)) { - scanmatchtable(shfunctab, com, 0, DISABLED, shfunctab->printnode, 0); + scanmatchtable(shfunctab, pprog, 0, DISABLED, + shfunctab->printnode, pflags); } 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->next) - if (domatch(shf->nam, com, 0) && !(shf->flags & DISABLED)) - shf->flags = (shf->flags | on) & (~off); + for (shf = (Shfunc) shfunctab->nodes[i]; shf; + shf = (Shfunc) shf->next) + if (pattry(pprog, shf->nam) && + !(shf->flags & DISABLED)) { + shf->flags = (shf->flags | + (on & ~PM_UNDEFINED)) & ~off; + if (ops['X'] && + eval_autoload(shf, shf->nam, ops, func)) { + returnval = 1; + } + } } } } else { @@ -1728,14 +2117,21 @@ bin_functions(char *name, char **argv, char *ops, int func) /* Take the arguments literally -- do not glob */ for (; *argv; argv++) { - if ((shf = (Shfunc) shfunctab->getnode(shfunctab, *argv))) { + if (ops['w']) { + if (dump_autoload(*argv, on, ops, func)) { + zwarnnam(name, "invalid wordcode file: %s", *argv, 0); + returnval = 1; + } + } else if ((shf = (Shfunc) shfunctab->getnode(shfunctab, *argv))) { /* if any flag was given */ - if (on|off) + if (on|off) { /* turn on/off the given flags */ shf->flags = (shf->flags | (on & ~PM_UNDEFINED)) & ~off; - else + if (ops['X'] && eval_autoload(shf, shf->nam, ops, func)) + returnval = 1; + } else /* no flags, so just print */ - shfunctab->printnode((HashNode) shf, 0); + shfunctab->printnode((HashNode) shf, pflags); } else if (on & PM_UNDEFINED) { /* Add a new undefined (autoloaded) function to the * * hash table with the corresponding flags set. */ @@ -1743,6 +2139,8 @@ bin_functions(char *name, char **argv, char *ops, int func) shf->flags = on; shf->funcdef = mkautofn(shf); shfunctab->addnode(shfunctab, ztrdup(*argv), shf); + if (ops['X'] && eval_autoload(shf, shf->nam, ops, func)) + returnval = 1; } else returnval = 1; } @@ -1750,30 +2148,28 @@ bin_functions(char *name, char **argv, char *ops, int func) } /**/ -static List +Eprog mkautofn(Shfunc shf) { - List l; - Sublist s; - Pline p; - Cmd c; - AutoFn a; - PERMALLOC { - a = (AutoFn)allocnode(N_AUTOFN); - a->shf = shf; - c = (Cmd)allocnode(N_CMD); - c->type = AUTOFN; - c->u.autofn = a; - p = (Pline)allocnode(N_PLINE); - p->left = c; - p->type = END; - s = (Sublist)allocnode(N_SUBLIST); - s->left = p; - l = (List)allocnode(N_LIST); - l->left = s; - l->type = Z_SYNC; - } LASTALLOC; - return l; + 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->pats = NULL; + 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 */ @@ -1783,7 +2179,7 @@ int bin_unset(char *name, char **argv, char *ops, int func) { Param pm, next; - Comp com; + Patprog pprog; char *s; int match = 0, returnval = 0; int i; @@ -1797,15 +2193,16 @@ bin_unset(char *name, char **argv, char *ops, int func) while ((s = *argv++)) { /* expand */ tokenize(s); - if ((com = parsereg(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->next; if ((!(pm->flags & PM_RESTRICTED) || - unset(RESTRICTED)) && domatch(pm->nam, com, 0)) { - unsetparam(pm->nam); + unset(RESTRICTED)) && + pattry(pprog, pm->nam)) { + unsetparam_pm(pm, 0, 1); match++; } } @@ -1824,14 +2221,41 @@ bin_unset(char *name, char **argv, char *ops, int func) /* do not glob -- unset the given parameter */ while ((s = *argv++)) { - pm = (Param) paramtab->getnode(paramtab, s); + char *ss = strchr(s, '['); + char *sse = ss; + if (ss) { + if (skipparens('[', ']', &sse) || *sse) { + zerrnam(name, "%s: invalid parameter name", s, 0); + returnval = 1; + continue; + } + *ss = 0; + } + pm = (Param) (paramtab == realparamtab ? + gethashnode2(paramtab, s) : + paramtab->getnode(paramtab, s)); if (!pm) returnval = 1; else if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) { zerrnam(name, "%s: restricted", pm->nam, 0); returnval = 1; + } else if (ss) { + if (PM_TYPE(pm->flags) == PM_HASHED) { + HashTable tht = paramtab; + if ((paramtab = pm->gets.hfn(pm))) { + *--sse = 0; + unsetparam(ss+1); + *sse = ']'; + } + paramtab = tht; + } else { + zerrnam(name, "%s: invalid element for unset", s, 0); + returnval = 1; + } } else - unsetparam(s); + unsetparam_pm(pm, 0, 1); + if (ss) + *ss = '['; } return returnval; } @@ -1843,7 +2267,7 @@ int bin_whence(char *nam, char **argv, char *ops, int func) { HashNode hn; - Comp com; + Patprog pprog; int returnval = 0; int printflags = 0; int csh, all, v, wd; @@ -1872,7 +2296,7 @@ bin_whence(char *nam, char **argv, char *ops, int func) for (; *argv; argv++) { /* parse the pattern */ tokenize(*argv); - if (!(com = parsereg(*argv))) { + if (!(pprog = patcompile(*argv, PAT_STATIC, NULL))) { untokenize(*argv); zwarnnam(nam, "bad pattern : %s", *argv, 0); returnval = 1; @@ -1883,21 +2307,26 @@ bin_whence(char *nam, char **argv, char *ops, int func) * We're not using it, so search for ... */ /* aliases ... */ - scanmatchtable(aliastab, com, 0, DISABLED, aliastab->printnode, printflags); + scanmatchtable(aliastab, pprog, 0, DISABLED, + aliastab->printnode, printflags); /* and reserved words ... */ - scanmatchtable(reswdtab, com, 0, DISABLED, reswdtab->printnode, printflags); + scanmatchtable(reswdtab, pprog, 0, DISABLED, + reswdtab->printnode, printflags); /* and shell functions... */ - scanmatchtable(shfunctab, com, 0, DISABLED, shfunctab->printnode, printflags); + scanmatchtable(shfunctab, pprog, 0, DISABLED, + shfunctab->printnode, printflags); /* and builtins. */ - scanmatchtable(builtintab, com, 0, DISABLED, builtintab->printnode, printflags); + scanmatchtable(builtintab, pprog, 0, DISABLED, + builtintab->printnode, printflags); } /* Done search for `internal' commands, if the -p option * * was not used. Now search the path. */ cmdnamtab->filltable(cmdnamtab); - scanmatchtable(cmdnamtab, com, 0, 0, cmdnamtab->printnode, printflags); + scanmatchtable(cmdnamtab, pprog, 0, 0, + cmdnamtab->printnode, printflags); } return returnval; @@ -1979,7 +2408,7 @@ bin_whence(char *nam, char **argv, char *ops, int func) puts(wd ? ": none" : " not found"); returnval = 1; } - } else if ((cnam = findcmd(*argv))) { + } else if ((cnam = findcmd(*argv, 1))) { /* Found external command. */ if (wd) { printf("%s: command\n", *argv); @@ -1991,7 +2420,6 @@ bin_whence(char *nam, char **argv, char *ops, int func) print_if_link(cnam); fputc('\n', stdout); } - zsfree(cnam); } else { /* Not found at all. */ if (v || csh || wd) @@ -2027,7 +2455,7 @@ int bin_hash(char *name, char **argv, char *ops, int func) { HashTable ht; - Comp com; + Patprog pprog; Asgment asg; int returnval = 0; @@ -2065,9 +2493,9 @@ bin_hash(char *name, char **argv, char *ops, int func) if (ops['m']) { /* with the -m option, treat the argument as a glob pattern */ tokenize(*argv); /* expand */ - if ((com = parsereg(*argv))) { + if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) { /* display matching hash table elements */ - scanmatchtable(ht, com, 0, 0, ht->printnode, 0); + scanmatchtable(ht, pprog, 0, 0, ht->printnode, 0); } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv, 0); @@ -2124,7 +2552,7 @@ bin_unhash(char *name, char **argv, char *ops, int func) { HashTable ht; HashNode hn, nhn; - Comp com; + Patprog pprog; int match = 0, returnval = 0; int i; @@ -2144,13 +2572,13 @@ bin_unhash(char *name, char **argv, char *ops, int func) for (; *argv; argv++) { /* expand argument */ tokenize(*argv); - if ((com = parsereg(*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 (domatch(hn->nam, com, 0)) { + if (pattry(pprog, hn->nam)) { ht->freenode(ht->removenode(ht, hn->nam)); match++; } @@ -2189,7 +2617,7 @@ int bin_alias(char *name, char **argv, char *ops, int func) { Alias a; - Comp com; + Patprog pprog; Asgment asg; int haveflags = 0, returnval = 0; int flags1 = 0, flags2 = DISABLED; @@ -2210,6 +2638,8 @@ bin_alias(char *name, char **argv, char *ops, int func) if (ops['L']) printflags |= PRINT_LIST; + else if (ops['r'] == 2 || ops['g'] == 2 || ops['m'] == 2 || 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. */ @@ -2223,9 +2653,10 @@ bin_alias(char *name, char **argv, char *ops, int func) if (ops['m']) { for (; *argv; argv++) { tokenize(*argv); /* expand argument */ - if ((com = parsereg(*argv))) { + if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) { /* display the matching aliases */ - scanmatchtable(aliastab, com, flags1, flags2, aliastab->printnode, printflags); + scanmatchtable(aliastab, pprog, flags1, flags2, + aliastab->printnode, printflags); } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv, 0); @@ -2278,7 +2709,7 @@ bin_false(char *name, char **argv, char *ops, int func) /* the zle buffer stack */ /**/ -LinkList bufstack; +mod_export LinkList bufstack; /* echo, print, pushln */ @@ -2294,38 +2725,46 @@ bin_print(char *name, char **args, char *ops, int func) /* -m option -- treat the first argument as a pattern and remove * arguments not matching */ if (ops['m']) { - Comp com; + Patprog pprog; char **t, **p; tokenize(*args); - if (!(com = parsereg(*args))) { + if (!(pprog = patcompile(*args, PAT_STATIC, NULL))) { untokenize(*args); zwarnnam(name, "bad pattern : %s", *args, 0); return 1; } for (p = ++args; *p; p++) - if (!domatch(*p, com, 0)) + if (!pattry(pprog, *p)) for (t = p--; (*t = t[1]); t++); } /* compute lengths, and interpret according to -P, -D, -e, etc. */ argc = arrlen(args); - len = (int *)ncalloc(argc * sizeof(int)); + len = (int *) hcalloc(argc * sizeof(int)); for(n = 0; n < argc; n++) { /* first \ sequences */ if (!ops['e'] && (ops['R'] || ops['r'] || ops['E'])) unmetafy(args[n], &len[n]); else - args[n] = getkeystring(args[n], &len[n], - func != BIN_ECHO && !ops['e'], &nnl); + args[n] = getkeystring(args[n], &len[n], ops['b'] ? 2 : + (func != BIN_ECHO && !ops['e']), &nnl); /* -P option -- interpret as a prompt sequence */ - if(ops['P']) - args[n] = unmetafy(promptexpand(metafy(args[n], len[n], - META_NOALLOC), 0, NULL, NULL), &len[n]); + if(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), &len[n]); + args[n] = dupstring(str); + free(str); + } /* -D option -- interpret as a directory, and use ~ */ if(ops['D']) { Nameddir d = finddir(args[n]); if(d) { - char *arg = alloc(strlen(args[n]) + 1); + char *arg = zhalloc(strlen(args[n]) + 1); sprintf(arg, "~%s%s", d->nam, args[n] + strlen(d->dir)); args[n] = arg; @@ -2336,9 +2775,7 @@ bin_print(char *name, char **args, char *ops, int func) /* -z option -- push the arguments onto the editing buffer stack */ if (ops['z']) { - PERMALLOC { - pushnode(bufstack, sepjoin(args, NULL)); - } LASTALLOC; + zpushnode(bufstack, sepjoin(args, NULL, 0)); return 0; } /* -s option -- add the arguments to the history list */ @@ -2346,28 +2783,24 @@ bin_print(char *name, char **args, char *ops, int func) int nwords = 0, nlen, iwords; char **pargs = args; - PERMALLOC { - ent = gethistent(++curhist); - zsfree(ent->text); - if (ent->nwords) - zfree(ent->words, ent->nwords*2*sizeof(short)); - while (*pargs++) - nwords++; - if ((ent->nwords = nwords)) { - ent->words = (short *)zalloc(nwords*2*sizeof(short)); - nlen = iwords = 0; - for (pargs = args; *pargs; pargs++) { - ent->words[iwords++] = nlen; - nlen += strlen(*pargs); - ent->words[iwords++] = nlen; - nlen++; - } - } else - ent->words = (short *)NULL; - ent->text = zjoin(args, ' '); - ent->stim = ent->ftim = time(NULL); - ent->flags = 0; - } LASTALLOC; + ent = prepnexthistent(++curhist); + while (*pargs++) + nwords++; + if ((ent->nwords = nwords)) { + ent->words = (short *)zalloc(nwords*2*sizeof(short)); + nlen = iwords = 0; + for (pargs = args; *pargs; pargs++) { + ent->words[iwords++] = nlen; + nlen += strlen(*pargs); + ent->words[iwords++] = nlen; + nlen++; + } + } else + ent->words = (short *)NULL; + ent->text = zjoin(args, ' ', 0); + ent->stim = ent->ftim = time(NULL); + ent->flags = 0; + addhistnode(histtab, ent->text, ent); return 0; } /* -u and -p -- output to other than standard output */ @@ -2534,7 +2967,7 @@ bin_shift(char *name, char **argv, char *ops, int func) /* optional argument can be either numeric or an array */ if (*argv && !getaparam(*argv)) - num = matheval(*argv++); + num = mathevali(*argv++); if (num < 0) { zwarnnam(name, "argument to shift must be non-negative", NULL, 0); @@ -2549,9 +2982,7 @@ bin_shift(char *name, char **argv, char *ops, int func) ret++; continue; } - PERMALLOC { - s = arrdup(s + num); - } LASTALLOC; + s = zarrdup(s + num); setaparam(*argv, s); } } else { @@ -2570,6 +3001,9 @@ bin_shift(char *name, char **argv, char *ops, int func) return ret; } +/**/ +int optcind; + /* getopts: automagical option handling for shell scripts */ /**/ @@ -2579,7 +3013,6 @@ bin_getopts(char *name, char **argv, char *ops, int func) int lenstr, lenoptstr, quiet, lenoptbuf; char *optstr = unmetafy(*argv++, &lenoptstr), *var = *argv++; char **args = (*argv) ? argv : pparams; - static int optcind = 0; char *str, optbuf[2] = " ", *p, opch; /* zoptind keeps count of the current argument number. The * @@ -2627,14 +3060,14 @@ bin_getopts(char *name, char **argv, char *ops, int func) if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) { p = "?"; err: - zsfree(zoptarg); + zsfree(zoptarg); + setsparam(var, ztrdup(p)); if(quiet) { - setsparam(var, ztrdup(p)); zoptarg = metafy(optbuf, lenoptbuf, META_DUP); } else { zerr(*p == '?' ? "bad option: -%c" : "argument expected after -%c option", NULL, opch); - zoptarg=ztrdup(""); + zoptarg=ztrdup(""); errflag = 0; } return 0; @@ -2674,7 +3107,7 @@ bin_break(char *name, char **argv, char *ops, int func) /* handle one optional numeric argument */ if (*argv) { - num = matheval(*argv++); + num = mathevali(*argv++); nump = 1; } @@ -2720,7 +3153,7 @@ bin_break(char *name, char **argv, char *ops, int func) /* we have printed a 'you have stopped (running) jobs.' message */ /**/ -int stopmsg; +mod_export int stopmsg; /* check to see if user has jobs running/stopped */ @@ -2754,42 +3187,45 @@ checkjobs(void) * because of a signal. */ /**/ -void +mod_export void zexit(int val, int from_signal) { static int in_exit; - HEAPALLOC { - if (isset(MONITOR) && !stopmsg && !from_signal) { - scanjobs(); /* check if jobs need printing */ + if (isset(MONITOR) && !stopmsg && !from_signal) { + scanjobs(); /* check if jobs need printing */ + if (isset(CHECKJOBS)) checkjobs(); /* check if any jobs are running/stopped */ - if (stopmsg) { - stopmsg = 2; - LASTALLOC_RETURN; - } - } - if (in_exit++ && from_signal) + if (stopmsg) { + stopmsg = 2; LASTALLOC_RETURN; - if (isset(MONITOR)) - /* send SIGHUP to any jobs left running */ - killrunjobs(from_signal); - if (isset(RCS) && interact) { - if (!nohistsave) - savehistfile(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0); - if (islogin && !subsh) { - sourcehome(".zlogout"); + } + } + if (in_exit++ && from_signal) + return; + + if (isset(MONITOR)) { + /* send SIGHUP to any jobs left running */ + killrunjobs(from_signal); + } + if (isset(RCS) && interact) { + if (!nohistsave) + savehistfile(NULL, 1, HFILE_USE_OPTIONS); + if (islogin && !subsh) { + sourcehome(".zlogout"); #ifdef GLOBAL_ZLOGOUT + if (isset(RCS) && isset(GLOBALRCS)) source(GLOBAL_ZLOGOUT); #endif - } } - if (sigtrapped[SIGEXIT]) - dotrap(SIGEXIT); - if (mypid != getpid()) - _exit(val); - else - exit(val); - } LASTALLOC; + } + if (sigtrapped[SIGEXIT]) + dotrap(SIGEXIT); + runhookdef(EXITHOOK, NULL); + if (mypid != getpid()) + _exit(val); + else + exit(val); } /* . (dot), source */ @@ -2808,11 +3244,9 @@ bin_dot(char *name, char **argv, char *ops, int func) return 0; old = pparams; /* get arguments for the script */ - if (argv[1]) { - PERMALLOC { - pparams = arrdup(argv + 1); - } LASTALLOC; - } + if (argv[1]) + pparams = zarrdup(argv + 1); + enam = arg0 = ztrdup(*argv); if (isset(FUNCTIONARGZERO)) { old0 = argzero; @@ -2880,6 +3314,8 @@ int bin_emulate(char *nam, char **argv, char *ops, int func) { emulate(*argv, ops['R']); + if (ops['L']) + opts[LOCALOPTIONS] = opts[LOCALTRAPS] = 1; return 0; } @@ -2889,19 +3325,14 @@ bin_emulate(char *nam, char **argv, char *ops, int func) int bin_eval(char *nam, char **argv, char *ops, int func) { - List list; - - inpush(zjoin(argv, ' '), 0, NULL); - strinbeg(); - stophist = 2; - list = parse_list(); - strinend(); - inpop(); - if (!list) { + Eprog prog; + + prog = parse_string(zjoin(argv, ' ', 1), 0); + if (!prog) { errflag = 0; return 1; } - execlist(list, 1, 0); + execode(prog, 1, 0); if (errflag) { lastval = errflag; errflag = 0; @@ -2928,7 +3359,7 @@ bin_read(char *name, char **args, char *ops, int func) char *reply, *readpmpt; int bsiz, c = 0, gotnl = 0, al = 0, first, nchars = 1, bslash; int haso = 0; /* true if /dev/tty has been opened specially */ - int isem = !strcmp(term, "emacs"); + int isem = !strcmp(term, "emacs"), izle = zleactive && getkeyptr; char *buf, *bptr, *firstarg, *zbuforig; LinkList readll = newlinklist(); @@ -2937,12 +3368,11 @@ bin_read(char *name, char **args, char *ops, int func) nchars = 1; args++; } - - firstarg = *args; - if (*args && **args == '?') - args++; - /* default result parameter */ + /* 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++ : ops['A'] ? "reply" : "REPLY"; + if (ops['A'] && *args) { zwarnnam(name, "only one array argument allowed", NULL, 0); return 1; @@ -2953,33 +3383,37 @@ bin_read(char *name, char **args, char *ops, int func) return compctlread(name, args, ops, reply); if ((ops['k'] && !ops['u'] && !ops['p']) || ops['q']) { - if (SHTTY == -1) { - /* need to open /dev/tty specially */ - SHTTY = open("/dev/tty", O_RDWR|O_NOCTTY); - haso = 1; - } - /* 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 (!zleactive) { + if (SHTTY == -1) { + /* need to open /dev/tty specially */ + SHTTY = open("/dev/tty", O_RDWR|O_NOCTTY); + haso = 1; + } + /* 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 && ops['k']) + setcbreak(); + readfd = SHTTY; } - if (unset(INTERACTIVE)) - gettyinfo(&shttyinfo); - /* attach to the tty */ - attachtty(mypgrp); - if (!isem && ops['k']) - setcbreak(); - readfd = SHTTY; } else if (ops['u'] && !ops['p']) { /* -u means take input from the specified file descriptor. * * -up means take input from the coprocess. */ for (readfd = 9; readfd && !ops[readfd + '0']; --readfd); - } else if (ops['p']) + izle = 0; + } else if (ops['p']) { readfd = coprocin; - else - readfd = 0; + izle = 0; + } else + readfd = izle = 0; /* handle prompt */ if (firstarg) { @@ -3003,18 +3437,25 @@ bin_read(char *name, char **args, char *ops, int func) bptr = buf = (char *)zalloc(nchars+1); do { - /* If read returns 0, is end of file */ - if ((val = read(readfd, bptr, nchars)) <= 0) - break; + if (izle) { + if ((val = getkeyptr(0)) < 0) + break; + *bptr++ = (char) val; + nchars--; + } else { + /* If read returns 0, is end of file */ + if ((val = read(readfd, bptr, nchars)) <= 0) + break; - /* decrement number of characters read from number required */ - nchars -= val; + /* decrement number of characters read from number required */ + nchars -= val; - /* increment pointer past read characters */ - bptr += val; + /* increment pointer past read characters */ + bptr += val; + } } while (nchars > 0); - if (!ops['u'] && !ops['p']) { + if (!izle && !ops['u'] && !ops['p']) { /* dispose of result appropriately, etc. */ if (isem) while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n'); @@ -3043,14 +3484,19 @@ bin_read(char *name, char **args, char *ops, int func) readbuf[1] = '\0'; /* get, and store, reply */ - readbuf[0] = ((char)getquery(NULL, 0)) == 'y' ? 'y' : 'n'; + if (izle) { + int key = getkeyptr(0); - /* dispose of result appropriately, etc. */ - if (haso) { - close(SHTTY); - SHTTY = -1; - } + readbuf[0] = (key == 'y' ? 'y' : 'n'); + } else { + readbuf[0] = ((char)getquery(NULL, 0)) == 'y' ? 'y' : 'n'; + /* dispose of result appropriately, etc. */ + if (haso) { + close(SHTTY); + SHTTY = -1; + } + } if (ops['e'] || ops['E']) printf("%s\n", readbuf); if (!ops['e']) @@ -3072,7 +3518,7 @@ bin_read(char *name, char **args, char *ops, int func) buf = bptr = (char *)zalloc(bsiz = 64); /* get input, a character at a time */ while (!gotnl) { - c = zread(); + c = zread(izle); /* \ at the end of a line indicates a continuation * * line, except in raw mode (-r option) */ if (bslash && c == '\n') { @@ -3162,7 +3608,7 @@ bin_read(char *name, char **args, char *ops, int func) bslash = 0; if (!gotnl) for (;;) { - c = zread(); + c = zread(izle); /* \ at the end of a line introduces a continuation line, except in raw mode (-r option) */ if (bslash && c == '\n') { @@ -3171,13 +3617,14 @@ bin_read(char *name, char **args, char *ops, int func) } if (c == EOF || (c == '\n' && !zbuf)) break; - if (!bslash && isep(c) && bptr == buf) + if (!bslash && isep(c) && bptr == buf) { if (iwsep(c)) continue; else if (!first) { first = 1; continue; } + } bslash = c == '\\' && !bslash && !ops['r']; if (bslash) continue; @@ -3225,12 +3672,17 @@ bin_read(char *name, char **args, char *ops, int func) /**/ static int -zread(void) +zread(int izle) { char cc, retry = 0; + if (izle) { + int c = getkeyptr(0); + + return (c < 0 ? EOF : c); + } /* use zbuf if possible */ - if (zbuf) + 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. */ @@ -3238,6 +3690,7 @@ zread(void) return zbuf++, STOUC(*zbuf++ ^ 32); else return (*zbuf) ? STOUC(*zbuf++) : EOF; + } for (;;) { /* read a character from readfd */ switch (read(readfd, &cc, 1)) { @@ -3305,7 +3758,8 @@ int bin_test(char *name, char **argv, char *ops, int func) { char **s; - Cond c; + Eprog prog; + struct estate state; /* if "test" was invoked as "[", it needs a matching "]" * * which is subsequently ignored */ @@ -3325,7 +3779,7 @@ bin_test(char *name, char **argv, char *ops, int func) tok = NULLTOK; condlex = testlex; testlex(); - c = par_cond(); + prog = parse_cond(); condlex = yylex; if (errflag) { @@ -3333,13 +3787,19 @@ bin_test(char *name, char **argv, char *ops, int func) return 1; } - if (!c || tok == LEXERR) { + if (!prog || tok == LEXERR) { zwarnnam(name, tokstr ? "parse error" : "argument expected", NULL, 0); return 1; } /* syntax is OK, so evaluate */ - return !evalcond(c); + + state.prog = prog; + state.pc = prog->prog; + state.strs = prog->strs; + + + return !evalcond(&state); } /* display a time, provided in units of 1/60s, as minutes and seconds */ @@ -3374,7 +3834,7 @@ bin_times(char *name, char **argv, char *ops, int func) int bin_trap(char *name, char **argv, char *ops, int func) { - List l; + Eprog prog; char *arg, *s; int sig; @@ -3396,7 +3856,7 @@ bin_trap(char *name, char **argv, char *ops, int func) if (!sigfuncs[sig]) printf("trap -- '' %s\n", sigs[sig]); else { - s = getpermtext((void *) dupstruct((void *) sigfuncs[sig])); + s = getpermtext(sigfuncs[sig], NULL); printf("trap -- "); quotedzputs(s, stdout); printf(" %s\n", sigs[sig]); @@ -3422,26 +3882,24 @@ bin_trap(char *name, char **argv, char *ops, int func) /* Sort out the command to execute on trap */ arg = *argv++; if (!*arg) - l = NULL; - else if (!(l = parse_string(arg))) { + prog = &dummy_eprog; + else if (!(prog = parse_string(arg, 0))) { zwarnnam(name, "couldn't parse trap command", NULL, 0); return 1; } /* set traps */ for (; *argv; argv++) { - List t; + Eprog t; sig = getsignum(*argv); if (sig == -1) { zwarnnam(name, "undefined signal: %s", *argv, 0); break; } - PERMALLOC { - t = (List) dupstruct(l); - } LASTALLOC; + t = dupeprog(prog, 0); if (settrap(sig, t)) - freestruct(t); + freeeprog(t); } return *argv != NULL; } @@ -3465,10 +3923,10 @@ bin_ttyctl(char *name, char **argv, char *ops, int func) int bin_let(char *name, char **argv, char *ops, int func) { - long val = 0; + zlong val = 0; while (*argv) - val = matheval(*argv++); + val = mathevali(*argv++); /* Errors in math evaluation in let are non-fatal. */ errflag = 0; return !val; @@ -3591,7 +4049,7 @@ bin_umask(char *nam, char **args, char *ops, int func) /* Generic builtin for facilities not available on this OS */ /**/ -int +mod_export int bin_notavail(char *nam, char **argv, char *ops, int func) { zwarnnam(nam, "not available on this system", NULL, 0); diff --git a/Src/cond.c b/Src/cond.c index 79886a720..8a54eeeb2 100644 --- a/Src/cond.c +++ b/Src/cond.c @@ -30,125 +30,277 @@ #include "zsh.mdh" #include "cond.pro" +int tracingcond; + +static char *condstr[COND_MOD] = { + "!", "&&", "||", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq", + "-ne", "-lt", "-gt", "-le", "-ge" +}; + /**/ int -evalcond(Cond c) +evalcond(Estate state) { struct stat *st; + char *left, *right; + Wordcode pcode; + wordcode code; + int ctype, htok = 0; + + rec: + + left = right = NULL; + pcode = state->pc++; + code = *pcode; + ctype = WC_COND_TYPE(code); - switch (c->type) { + switch (ctype) { case COND_NOT: - return !evalcond(c->left); + if (tracingcond) + fprintf(xtrerr, " %s", condstr[ctype]); + return !evalcond(state); case COND_AND: - return evalcond(c->left) && evalcond(c->right); + if (evalcond(state)) { + if (tracingcond) + fprintf(xtrerr, " %s", condstr[ctype]); + goto rec; + } else { + state->pc = pcode + (WC_COND_SKIP(code) + 1); + return 0; + } case COND_OR: - return evalcond(c->left) || evalcond(c->right); + if (!evalcond(state)) { + if (tracingcond) + fprintf(xtrerr, " %s", condstr[ctype]); + goto rec; + } else { + state->pc = pcode + (WC_COND_SKIP(code) + 1); + return 1; + } + case COND_MOD: + case COND_MODI: + { + Conddef cd; + char *name = ecgetstr(state, EC_NODUP, NULL), **strs; + int l = WC_COND_SKIP(code); + + if (ctype == COND_MOD) + strs = ecgetarr(state, l, EC_DUP, NULL); + else { + char *sbuf[3]; + + sbuf[0] = ecgetstr(state, EC_NODUP, NULL); + sbuf[1] = ecgetstr(state, EC_NODUP, NULL); + sbuf[2] = NULL; + + strs = arrdup(sbuf); + l = 2; + } + if ((cd = getconddef((ctype == COND_MODI), name + 1, 1))) { + if (ctype == COND_MOD && + (l < cd->min || (cd->max >= 0 && l > cd->max))) { + zerr("unrecognized condition: `%s'", name, 0); + return 0; + } + if (tracingcond) + tracemodcond(name, strs, ctype == COND_MODI); + return cd->handler(strs, cd->condid); + } + else { + char *s = strs[0]; + + strs[0] = dupstring(name); + name = s; + + if (name && name[0] == '-' && + (cd = getconddef(0, name + 1, 1))) { + if (l < cd->min || (cd->max >= 0 && l > cd->max)) { + zerr("unrecognized condition: `%s'", name, 0); + return 0; + } + if (tracingcond) + tracemodcond(name, strs, ctype == COND_MODI); + return cd->handler(strs, cd->condid); + } else + zerr("unrecognized condition: `%s'", name, 0); + } + return 0; + } } - singsub((char **)&c->left); - untokenize(c->left); - if (c->right) { - singsub((char **)&c->right); - if (c->type != COND_STREQ && c->type != COND_STRNEQ) - untokenize(c->right); + left = ecgetstr(state, EC_DUPTOK, &htok); + if (htok) { + singsub(&left); + untokenize(left); } - switch (c->type) { + if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRNEQ) { + right = ecgetstr(state, EC_DUPTOK, &htok); + if (htok) { + singsub(&right); + untokenize(right); + } + } + if (tracingcond) { + if (ctype < COND_MOD) { + char *rt = (char *) right; + if (ctype == COND_STREQ || ctype == COND_STRNEQ) { + rt = dupstring(ecrawstr(state->prog, state->pc, NULL)); + singsub(&rt); + untokenize(rt); + } + fprintf(xtrerr, " %s %s %s", left, condstr[ctype], rt); + } else + fprintf(xtrerr, " -%c %s", ctype, left); + } + + if (ctype >= COND_EQ && ctype <= COND_GE) { + mnumber mn1, mn2; + mn1 = matheval(left); + mn2 = matheval(right); + + if (((mn1.type|mn2.type) & (MN_INTEGER|MN_FLOAT)) == + (MN_INTEGER|MN_FLOAT)) { + /* promote to float */ + if (mn1.type & MN_INTEGER) { + mn1.type = MN_FLOAT; + mn1.u.d = (double)mn1.u.l; + } + if (mn2.type & MN_INTEGER) { + mn2.type = MN_FLOAT; + mn2.u.d = (double)mn2.u.l; + } + } + switch(ctype) { + case COND_EQ: + return (mn1.type & MN_FLOAT) ? (mn1.u.d == mn2.u.d) : + (mn1.u.l == mn2.u.l); + case COND_NE: + return (mn1.type & MN_FLOAT) ? (mn1.u.d != mn2.u.d) : + (mn1.u.l != mn2.u.l); + case COND_LT: + return (mn1.type & MN_FLOAT) ? (mn1.u.d < mn2.u.d) : + (mn1.u.l < mn2.u.l); + case COND_GT: + return (mn1.type & MN_FLOAT) ? (mn1.u.d > mn2.u.d) : + (mn1.u.l > mn2.u.l); + case COND_LE: + return (mn1.type & MN_FLOAT) ? (mn1.u.d <= mn2.u.d) : + (mn1.u.l <= mn2.u.l); + case COND_GE: + return (mn1.type & MN_FLOAT) ? (mn1.u.d >= mn2.u.d) : + (mn1.u.l >= mn2.u.l); + } + } + + switch (ctype) { case COND_STREQ: - return matchpat(c->left, c->right); case COND_STRNEQ: - return !matchpat(c->left, c->right); + { + int test, npat = state->pc[1]; + Patprog pprog = state->prog->pats[npat]; + + if (pprog == dummy_patprog1 || pprog == dummy_patprog2) { + char *opat; + int save; + + right = opat = dupstring(ecrawstr(state->prog, state->pc, + &htok)); + if (htok) + singsub(&right); + save = (!(state->prog->flags & EF_HEAP) && + !strcmp(opat, right) && pprog != dummy_patprog2); + + if (!(pprog = patcompile(right, (save ? PAT_ZDUP : PAT_STATIC), + NULL))) + zerr("bad pattern: %s", right, 0); + else if (save) + state->prog->pats[npat] = pprog; + } + state->pc += 2; + test = (pprog && pattry(pprog, left)); + + return (ctype == COND_STREQ ? test : !test); + } case COND_STRLT: - return strcmp(c->left, c->right) < 0; + return strcmp(left, right) < 0; case COND_STRGTR: - return strcmp(c->left, c->right) > 0; + return strcmp(left, right) > 0; case 'e': case 'a': - return (doaccess(c->left, F_OK)); + return (doaccess(left, F_OK)); case 'b': - return (S_ISBLK(dostat(c->left))); + return (S_ISBLK(dostat(left))); case 'c': - return (S_ISCHR(dostat(c->left))); + return (S_ISCHR(dostat(left))); case 'd': - return (S_ISDIR(dostat(c->left))); + return (S_ISDIR(dostat(left))); case 'f': - return (S_ISREG(dostat(c->left))); + return (S_ISREG(dostat(left))); case 'g': - return (!!(dostat(c->left) & S_ISGID)); + return (!!(dostat(left) & S_ISGID)); case 'k': - return (!!(dostat(c->left) & S_ISVTX)); + return (!!(dostat(left) & S_ISVTX)); case 'n': - return (!!strlen(c->left)); + return (!!strlen(left)); case 'o': - return (optison(c->left)); + return (optison(left)); case 'p': - return (S_ISFIFO(dostat(c->left))); + return (S_ISFIFO(dostat(left))); case 'r': - return (doaccess(c->left, R_OK)); + return (doaccess(left, R_OK)); case 's': - return ((st = getstat(c->left)) && !!(st->st_size)); + return ((st = getstat(left)) && !!(st->st_size)); case 'S': - return (S_ISSOCK(dostat(c->left))); + return (S_ISSOCK(dostat(left))); case 'u': - return (!!(dostat(c->left) & S_ISUID)); + return (!!(dostat(left) & S_ISUID)); case 'w': - return (doaccess(c->left, W_OK)); + return (doaccess(left, W_OK)); case 'x': if (privasserted()) { - mode_t mode = dostat(c->left); + mode_t mode = dostat(left); return (mode & S_IXUGO) || S_ISDIR(mode); } - return doaccess(c->left, X_OK); + return doaccess(left, X_OK); case 'z': - return (!strlen(c->left)); + return (!strlen(left)); case 'h': case 'L': - return (S_ISLNK(dolstat(c->left))); + return (S_ISLNK(dolstat(left))); case 'O': - return ((st = getstat(c->left)) && st->st_uid == geteuid()); + return ((st = getstat(left)) && st->st_uid == geteuid()); case 'G': - return ((st = getstat(c->left)) && st->st_gid == getegid()); + return ((st = getstat(left)) && st->st_gid == getegid()); case 'N': - return ((st = getstat(c->left)) && st->st_atime <= st->st_mtime); + return ((st = getstat(left)) && st->st_atime <= st->st_mtime); case 't': - return isatty(matheval(c->left)); - case COND_EQ: - return matheval(c->left) == matheval(c->right); - case COND_NE: - return matheval(c->left) != matheval(c->right); - case COND_LT: - return matheval(c->left) < matheval(c->right); - case COND_GT: - return matheval(c->left) > matheval(c->right); - case COND_LE: - return matheval(c->left) <= matheval(c->right); - case COND_GE: - return matheval(c->left) >= matheval(c->right); + return isatty(mathevali(left)); case COND_NT: case COND_OT: { time_t a; - if (!(st = getstat(c->left))) + if (!(st = getstat(left))) return 0; a = st->st_mtime; - if (!(st = getstat(c->right))) + if (!(st = getstat(right))) return 0; - return (c->type == COND_NT) ? a > st->st_mtime : a < st->st_mtime; + return (ctype == COND_NT) ? a > st->st_mtime : a < st->st_mtime; } case COND_EF: { dev_t d; ino_t i; - if (!(st = getstat(c->left))) + if (!(st = getstat(left))) return 0; d = st->st_dev; i = st->st_ino; - if (!(st = getstat(c->right))) + if (!(st = getstat(right))) return 0; return d == st->st_dev && i == st->st_ino; } default: - zerr("bad cond structure", NULL, 0); + zerr("bad cond code", NULL, 0); } return 0; } @@ -158,6 +310,10 @@ evalcond(Cond c) static int doaccess(char *s, int c) { +#ifdef HAVE_FACCESSX + if (!strncmp(s, "/dev/fd/", 8)) + return !faccessx(atoi(s + 8), c, ACC_SELF); +#endif return !access(unmeta(s), c); } @@ -224,3 +380,59 @@ optison(char *s) else return isset(i); } + +/**/ +mod_export char * +cond_str(char **args, int num, int raw) +{ + char *s = args[num]; + + if (has_token(s)) { + singsub(&s); + if (!raw) + untokenize(s); + } + return s; +} + +/**/ +mod_export zlong +cond_val(char **args, int num) +{ + char *s = args[num]; + + if (has_token(s)) { + singsub(&s); + untokenize(s); + } + return mathevali(s); +} + +/**/ +mod_export int +cond_match(char **args, int num, char *str) +{ + char *s = args[num]; + + singsub(&s); + + return matchpat(str, s); +} + +/**/ +static void +tracemodcond(char *name, char **args, int inf) +{ + char **aptr; + + args = arrdup(args); + for (aptr = args; *aptr; aptr++) + untokenize(*aptr); + if (inf) { + fprintf(xtrerr, " %s %s %s", args[0], name, args[1]); + } else { + fprintf(xtrerr, " %s", name); + while (*args) + fprintf(xtrerr, " %s", *args++); + } +} diff --git a/Src/exec.c b/Src/exec.c index 1b355d028..0dffaf4e2 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -38,7 +38,7 @@ int noerrexit; /* suppress error messages */ /**/ -int noerrs; +mod_export int noerrs; /* do not save history on exec and exit */ @@ -48,7 +48,7 @@ int nohistsave; /* error/break flag */ /**/ -int errflag; +mod_export int errflag; /* Status of return from a trap */ @@ -63,7 +63,7 @@ int subsh; /* != 0 if we have a return pending */ /**/ -int retflag; +mod_export int retflag; /**/ long lastval2; @@ -92,17 +92,17 @@ int max_zsh_fd; /* input fd from the coprocess */ /**/ -int coprocin; +mod_export int coprocin; /* output fd from the coprocess */ /**/ -int coprocout; +mod_export int coprocout; /* != 0 if the line editor is active */ /**/ -int zleactive; +mod_export int zleactive; /* pid of process undergoing 'process substitution' */ @@ -113,45 +113,66 @@ pid_t cmdoutpid; /**/ int 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; -#define execerr() if (!forked) { lastval = 1; return; } else _exit(1) +/* Stack with names of functions currently active. */ + +/**/ +mod_export Funcstack funcstack; + +#define execerr() if (!forked) { lastval = 1; goto done; } else _exit(1) static LinkList args; static int doneps4; +/* Execution functions. */ + +static int (*execfuncs[]) _((Estate, int)) = { + execcursh, exectime, execfuncdef, execfor, execselect, + execwhile, execrepeat, execcase, execif, execcond, + execarith, execautofn +}; + /* parse string into a list */ /**/ -List -parse_string(char *s) +mod_export Eprog +parse_string(char *s, int ln) { - List l; + Eprog p; + int oldlineno = lineno; lexsave(); - inpush(s, 0, NULL); - strinbeg(); - stophist = 2; - l = parse_list(); + inpush(s, (ln ? INP_LINENO : 0), NULL); + strinbeg(0); + lineno = ln ? 1 : -1; + p = parse_list(); + lineno = oldlineno; strinend(); inpop(); lexrestore(); - return l; + return p; } +/**/ #ifdef HAVE_GETRLIMIT /* the resource limits for the shell and its children */ /**/ -struct rlimit current_limits[RLIM_NLIMITS], limits[RLIM_NLIMITS]; +mod_export struct rlimit current_limits[RLIM_NLIMITS], limits[RLIM_NLIMITS]; /**/ -int +mod_export int zsetlimit(int limnum, char *nam) { if (limits[limnum].rlim_max != current_limits[limnum].rlim_max || @@ -167,7 +188,7 @@ zsetlimit(int limnum, char *nam) } /**/ -int +mod_export int setlimits(char *nam) { int limnum; @@ -179,6 +200,7 @@ setlimits(char *nam) return ret; } +/**/ #endif /* HAVE_GETRLIMIT */ /* fork and set limits */ @@ -206,6 +228,78 @@ zfork(void) 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 live 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 build 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; @@ -219,12 +313,18 @@ static char list_pipe_text[JOBTEXTSIZE]; /**/ static int -execcursh(Cmd cmd) +execcursh(Estate state, int do_exec) { - if (!list_pipe) + Wordcode end = state->pc + WC_CURSH_SKIP(state->pc[-1]); + + if (!list_pipe && thisjob != list_pipe_job) deletejob(jobtab + thisjob); - execlist(cmd->u.list, 1, cmd->flags & CFLAG_EXEC); - cmd->u.list = NULL; + cmdpush(CS_CURSH); + execlist(state, 1, do_exec); + cmdpop(); + + state->pc = end; + return lastval; } @@ -276,7 +376,9 @@ zexecve(char *pth, char **argv) if (execvebuf[1] == '!') { for (t0 = 0; t0 != ct; t0++) if (execvebuf[t0] == '\n') - execvebuf[t0] = '\0'; + break; + while (inblank(execvebuf[t0])) + execvebuf[t0--] = '\0'; execvebuf[POUNDBANGLIMIT] = '\0'; for (ptr = execvebuf + 2; *ptr && *ptr == ' '; ptr++); for (ptr2 = ptr; *ptr && *ptr != ' '; ptr++); @@ -383,6 +485,7 @@ execute(Cmdnam not_used_yet, int dash) } argv = makecline(args); + closem(3); child_unblock(); if ((int) strlen(arg0) >= PATH_MAX) { zerr("command too long: %s", arg0, 0); @@ -452,13 +555,17 @@ execute(Cmdnam not_used_yet, int dash) _exit(1); } -#define try(X) { if (iscom(X)) return ztrdup(X); } +#define RET_IF_COM(X) { if (iscom(X)) return docopy ? dupstring(X) : arg0; } -/* get the full pathname of an external command */ +/* + * 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). + */ /**/ -char * -findcmd(char *arg0) +mod_export char * +findcmd(char *arg0, int docopy) { char **pp; char *z, *s, buf[MAXCMDLEN]; @@ -471,7 +578,7 @@ findcmd(char *arg0) return NULL; for (s = arg0; *s; s++) if (*s == '/') { - try(arg0); + RET_IF_COM(arg0); if (arg0 == s || unset(PATHDIRS)) { return NULL; } @@ -491,13 +598,13 @@ findcmd(char *arg0) *z++ = '/'; } strcpy(z, arg0); - try(buf); + RET_IF_COM(buf); } strcpy(nn, cn->u.name ? *(cn->u.name) : ""); strcat(nn, "/"); strcat(nn, cn->nam); } - try(nn); + RET_IF_COM(nn); } for (pp = path; *pp; pp++) { z = buf; @@ -506,7 +613,7 @@ findcmd(char *arg0) *z++ = '/'; } strcpy(z, arg0); - try(buf); + RET_IF_COM(buf); } return NULL; } @@ -528,9 +635,15 @@ isreallycom(Cmdnam cn) { char fullnam[MAXCMDLEN]; - strcpy(fullnam, cn->u.name ? *(cn->u.name) : ""); - strcat(fullnam, "/"); - strcat(fullnam, cn->nam); + if (cn->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->nam); + } return iscom(fullnam); } @@ -549,7 +662,7 @@ isrelative(char *s) } /**/ -Cmdnam +mod_export Cmdnam hashcmd(char *arg0, char **pp) { Cmdnam cn; @@ -588,17 +701,60 @@ hashcmd(char *arg0, char **pp) /* execute a string */ /**/ -void +mod_export void execstring(char *s, int dont_change_job, int exiting) { - List list; + Eprog prog; pushheap(); - if ((list = parse_string(s))) - execlist(list, dont_change_job, exiting); + if ((prog = parse_string(s, 0))) + execode(prog, dont_change_job, exiting); popheap(); } +/**/ +void +execode(Eprog p, int dont_change_job, int exiting) +{ + struct estate s; + + s.prog = p; + s.pc = p->prog; + s.strs = p->strs; + + execlist(&s, dont_change_job, exiting); +} + +/* 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++; + + if (errflag) + return (lastval = 1); + + if (code) + lineno = code - 1; + + code = wc_code(*state->pc++); + + if (code == WC_ASSIGN) { + cmdoutval = 0; + addvars(state, state->pc - 1, 0); + if (isset(XTRACE)) { + fputc('\n', xtrerr); + fflush(xtrerr); + } + return (lastval = (errflag ? errflag : cmdoutval)); + } else + return (lastval = (execfuncs[code - WC_CURSH])(state, 0)); +} + /* 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 * @@ -608,45 +764,78 @@ execstring(char *s, int dont_change_job, int exiting) /**/ void -execlist(List list, int dont_change_job, int exiting) +execlist(Estate state, int dont_change_job, int exiting) { - Sublist slist; static int donetrap; - int ret, cj; - int old_pline_level, old_list_pipe; + Wordcode next; + wordcode code; + int ret, cj, csp, ltype; + int old_pline_level, old_list_pipe, 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; cj = thisjob; old_pline_level = pline_level; old_list_pipe = list_pipe; + oldlineno = lineno; if (sourcelevel && unset(SHINSTDIN)) pline_level = list_pipe = 0; /* Loop over all sets of comands separated by newline, * * semi-colon or ampersand (`sublists'). */ - while (list && list != &dummy_list && !breaks && !retflag) { + code = *state->pc++; + while (wc_code(code) == WC_LIST && !breaks && !retflag) { + ltype = WC_LIST_TYPE(code); + csp = cmdsp; + + if (ltype & Z_SIMPLE) { + next = state->pc + WC_LIST_SKIP(code); + execsimple(state); + state->pc = next; + goto sublist_done; + } /* Reset donetrap: this ensures that a trap is only * * called once for each sublist that fails. */ donetrap = 0; - simplifyright(list); - slist = list->left; /* Loop through code followed by &&, ||, or end of sublist. */ - while (slist) { - switch (slist->type) { - case END: + code = *state->pc++; + while (wc_code(code) == WC_SUBLIST) { + next = state->pc + WC_SUBLIST_SKIP(code); + if (!oldnoerrexit) + noerrexit = (WC_SUBLIST_TYPE(code) != WC_SUBLIST_END); + switch (WC_SUBLIST_TYPE(code)) { + case WC_SUBLIST_END: /* End of sublist; just execute, ignoring status. */ - execpline(slist, list->type, !list->right && exiting); + 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 ANDNEXT: + case WC_SUBLIST_AND: /* If the return code is non-zero, we skip pipelines until * * we find a sublist followed by ORNEXT. */ - if ((ret = execpline(slist, Z_SYNC, 0))) { - while ((slist = slist->right)) - if (slist->type == ORNEXT) - break; - if (!slist) { + 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. */ @@ -654,28 +843,43 @@ execlist(List list, int dont_change_job, int exiting) goto sublist_done; } } + cmdpush(CS_CMDAND); break; - case ORNEXT: + case WC_SUBLIST_OR: /* If the return code is zero, we skip pipelines until * * we find a sublist followed by ANDNEXT. */ - if (!(ret = execpline(slist, Z_SYNC, 0))) { - while ((slist = slist->right)) - if (slist->type == ANDNEXT) - break; - if (!slist) { + 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. */ donetrap = 1; goto sublist_done; - } + } } + cmdpush(CS_CMDOR); break; } - slist = slist->right; + state->pc = next; + code = *state->pc++; } + state->pc--; sublist_done: + cmdsp = csp; + noerrexit = oldnoerrexit; + if (sigtrapped[SIGDEBUG]) dotrap(SIGDEBUG); @@ -696,12 +900,13 @@ sublist_done: exit(lastval); } } - - list = list->right; + if (ltype & Z_END) + break; + code = *state->pc++; } - pline_level = old_pline_level; list_pipe = old_list_pipe; + lineno = oldlineno; if (dont_change_job) thisjob = cj; } @@ -718,15 +923,17 @@ sublist_done: /**/ static int -execpline(Sublist l, int how, int last1) +execpline(Estate state, wordcode slcode, int how, int last1) { int ipipe[2], opipe[2]; int pj, newjob; int old_simple_pline = simple_pline; - static int lastwj; + int slflags = WC_SUBLIST_FLAGS(slcode); + wordcode code = *state->pc++; + static int lastwj, lpforked; - if (!l->left) - return lastval = (l->flags & PFLAG_NOT) != 0; + if (wc_code(code) != WC_PIPE) + return lastval = (slflags & WC_SUBLIST_NOT) != 0; pj = thisjob; ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0; @@ -735,10 +942,11 @@ execpline(Sublist l, int how, int last1) /* get free entry in job table and initialize it */ if ((thisjob = newjob = initjob()) == -1) return 1; + if (how & Z_TIMED) jobtab[thisjob].stat |= STAT_TIMED; - if (l->flags & PFLAG_COPROC) { + if (slflags & WC_SUBLIST_COPROC) { how = Z_ASYNC; if (coprocin >= 0) { zclose(coprocin); @@ -750,20 +958,26 @@ execpline(Sublist l, int how, int last1) coprocout = opipe[1]; fdtable[coprocin] = fdtable[coprocout] = 0; } + /* 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_job = newjob; + list_pipe_pid = 0; nowait = 0; + simple_pline = (WC_PIPE_TYPE(code) == WC_PIPE_END); } - list_pipe_pid = lastwj = 0; - if (pline_level == 1) - simple_pline = (l->left->type == END); - execpline2(l->left, how, opipe[0], ipipe[1], last1); + lastwj = lpforked = 0; + execpline2(state, code, how, opipe[0], ipipe[1], last1); pline_level--; if (how & Z_ASYNC) { lastwj = newjob; jobtab[thisjob].stat |= STAT_NOSTTY; - if (l->flags & PFLAG_COPROC) + if (slflags & WC_SUBLIST_COPROC) { zclose(ipipe[1]); + zclose(opipe[0]); + } if (how & Z_DISOWN) { deletejob(jobtab + thisjob); thisjob = -1; @@ -775,13 +989,14 @@ execpline(Sublist l, int how, int last1) } 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) + if (list_pipe || (pline_level && !(how & Z_TIMED))) jn->stat |= STAT_NOPRINT; if (nowait) { @@ -789,8 +1004,15 @@ execpline(Sublist l, int how, int last1) struct process *pn, *qn; curjob = newjob; + DPUTS(!list_pipe_pid, "invalid list_pipe_pid"); addproc(list_pipe_pid, list_pipe_text); + /* 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; + } for (pn = jobtab[jn->other].procs; pn; pn = pn->next) if (WIFSTOPPED(pn->status)) break; @@ -801,27 +1023,42 @@ execpline(Sublist l, int how, int last1) } jn->stat &= ~(STAT_DONE | STAT_NOPRINT); - jn->stat |= STAT_STOPPED | STAT_CHANGED; + jn->stat |= STAT_STOPPED | STAT_CHANGED | STAT_LOCKED; printjob(jn, !!isset(LONGLISTJOBS), 1); } - else + else if (newjob != list_pipe_job) deletejob(jn); + else + lastwj = -1; } + errbrk_saved = 0; for (; !nowait;) { if (list_pipe_child) { jn->stat |= STAT_NOPRINT; makerunning(jn); } - if (!(jn->stat & STAT_LOCKED)) + if (!(jn->stat & STAT_LOCKED)) { + updated = !!jobtab[thisjob].procs; waitjobs(); - + child_block(); + } else + updated = 0; + if (!updated && + list_pipe_job && jobtab[list_pipe_job].procs && + !(jobtab[list_pipe_job].stat & STAT_STOPPED)) { + child_unblock(); + child_block(); + } if (list_pipe_child && jn->stat & STAT_DONE && lastval2 & 0200) killpg(mypgrp, lastval2 & ~0200); - if ((list_pipe || last1) && !list_pipe_child && - jn->stat & STAT_STOPPED) { + 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; int synch[2]; @@ -841,27 +1078,45 @@ execpline(Sublist l, int how, int last1) else if (pid) { char dummy; + lpforked = + (killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1); list_pipe_pid = pid; nowait = errflag = 1; breaks = loops; close(synch[1]); read(synch[0], &dummy, 1); close(synch[0]); - jobtab[list_pipe_job].other = newjob; - jobtab[list_pipe_job].stat |= STAT_SUPERJOB; - jn->stat |= STAT_SUBJOB | STAT_NOPRINT; - jn->other = pid; - killpg(jobtab[list_pipe_job].gleader, SIGSTOP); + /* 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 = pid; + } + if ((list_pipe || last1) && jobtab[list_pipe_job].procs) + killpg(jobtab[list_pipe_job].gleader, SIGSTOP); break; } else { close(synch[0]); entersubsh(Z_ASYNC, 0, 0); - setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader); + if (jobtab[list_pipe_job].procs) { + if (setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader) + == -1) { + setpgrp(0L, mypgrp = getpid()); + } + } else + setpgrp(0L, mypgrp = getpid()); close(synch[1]); kill(getpid(), SIGSTOP); list_pipe = 0; list_pipe_child = 1; + opts[INTERACTIVE] = 0; + if (errbrk_saved) { + errflag = prev_errflag; + breaks = prev_breaks; + } break; } } @@ -874,16 +1129,18 @@ execpline(Sublist l, int how, int last1) if (list_pipe && (lastval & 0200) && pj >= 0 && (!(jn->stat & STAT_INUSE) || (jn->stat & STAT_DONE))) { + deletejob(jn); jn = jobtab + pj; - jn->stat |= STAT_NOPRINT; - killjb(jobtab + pj, lastval & ~0200); + killjb(jn, lastval & ~0200); } - if (list_pipe_child || (list_pipe && (jn->stat & STAT_DONE))) + if (list_pipe_child || + ((jn->stat & STAT_DONE) && + (list_pipe || (pline_level && !(jn->stat & STAT_SUBJOB))))) deletejob(jn); thisjob = pj; } - if (l->flags & PFLAG_NOT) + if (slflags & WC_SUBLIST_NOT) lastval = !lastval; } if (!pline_level) @@ -897,32 +1154,43 @@ static int subsh_close = -1; /**/ static void -execpline2(Pline pline, int how, int input, int output, int last1) +execpline2(Estate state, wordcode pcode, + int how, int input, int output, int last1) { pid_t pid; int pipes[2]; - int oldlineno; if (breaks || retflag) return; - oldlineno = lineno; - lineno = pline->left->lineno; + if (WC_PIPE_LINENO(pcode)) + lineno = WC_PIPE_LINENO(pcode) - 1; - if (pline_level == 1) - strcpy(list_pipe_text, getjobtext((void *) pline->left)); - if (pline->type == END) { - execcmd(pline->left, input, output, how, last1 ? 1 : 2); - pline->left = NULL; - } else { + if (pline_level == 1) { + if ((how & Z_ASYNC) || (!sfcontext && !sourcelevel)) + 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(state, input, output, how, last1 ? 1 : 2); + else { int old_list_pipe = list_pipe; + Wordcode next = state->pc + (*state->pc); + wordcode code; + + state->pc++; + code = *state->pc; mpipe(pipes); /* if we are doing "foo | bar" where foo is a current * * shell command, do foo in a subshell and do the * * rest of the pipeline in the current shell. */ - if (pline->left->type >= CURSH && (how & Z_SYNC)) { + if (wc_code(code) >= WC_CURSH && (how & Z_SYNC)) { int synch[2]; pipe(synch); @@ -933,7 +1201,7 @@ execpline2(Pline pline, int how, int input, int output, int last1) } else if (pid) { char dummy, *text; - text = getjobtext((void *) pline->left); + text = getjobtext(state->prog, state->pc); addproc(pid, text); close(synch[1]); read(synch[0], &dummy, 1); @@ -943,28 +1211,27 @@ execpline2(Pline pline, int how, int input, int output, int last1) close(synch[0]); entersubsh(how, 2, 0); close(synch[1]); - execcmd(pline->left, input, pipes[1], how, 0); + execcmd(state, input, pipes[1], how, 0); _exit(lastval); } } else { - /* otherwise just do the pipeline normally. */ + /* otherwise just do the pipeline normally. */ subsh_close = pipes[0]; - execcmd(pline->left, input, pipes[1], how, 0); + execcmd(state, input, pipes[1], how, 0); } - pline->left = NULL; zclose(pipes[1]); - if (pline->right) { - /* if another execpline() is invoked because the command is * - * a list it must know that we're already in a pipeline */ - list_pipe = 1; - execpline2(pline->right, how, pipes[0], output, last1); - list_pipe = old_list_pipe; - zclose(pipes[0]); - subsh_close = -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(); + zclose(pipes[0]); + subsh_close = -1; } - - lineno = oldlineno; } /* make the argv array */ @@ -977,20 +1244,21 @@ makecline(LinkList list) char **argv, **ptr; /* A bigger argv is necessary for executing scripts */ - ptr = - argv = 2 + (char **) ncalloc((countlinknodes(list) + 4) * sizeof(char *)); + ptr = argv = 2 + (char **) hcalloc((countlinknodes(list) + 4) * + sizeof(char *)); + if (isset(XTRACE)) { if (!doneps4) - fprintf(stderr, "%s", (prompt4) ? prompt4 : ""); + printprompt4(); for (node = firstnode(list); node; incnode(node)) { *ptr++ = (char *)getdata(node); - zputs(getdata(node), stderr); + zputs(getdata(node), xtrerr); if (nextnode(node)) - fputc(' ', stderr); + fputc(' ', xtrerr); } - fputc('\n', stderr); - fflush(stderr); + fputc('\n', xtrerr); + fflush(xtrerr); } else { for (node = firstnode(list); node; incnode(node)) *ptr++ = (char *)getdata(node); @@ -1000,18 +1268,33 @@ makecline(LinkList list) } /**/ -void +mod_export void untokenize(char *s) { - for (; *s; s++) - if (itok(*s)) - if (*s == Nularg) - chuck(s--); - else - *s = ztokens[*s - Pound]; + 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; + } + } } -/* Open a file for writing redicection */ +/* Open a file for writing redirection */ /**/ static int @@ -1052,34 +1335,34 @@ clobber_open(struct redir *f) static void closemn(struct multio **mfds, int fd) { - struct multio *mn = mfds[fd]; - char buf[TCBUFSIZE]; - int len, i; + if (fd >= 0 && mfds[fd] && mfds[fd]->ct >= 2) { + struct multio *mn = mfds[fd]; + char buf[TCBUFSIZE]; + int len, i; - if (fd < 0 || !mfds[fd] || mfds[fd]->ct < 2) - return; - if (zfork()) { - for (i = 0; i < mn->ct; i++) - zclose(mn->fds[i]); - zclose(mn->pipe); - mn->ct = 1; - mn->fds[0] = fd; - return; - } - /* pid == 0 */ - closeallelse(mn); - if (mn->rflag) { - /* tee process */ - while ((len = read(mn->pipe, buf, TCBUFSIZE)) > 0) + if (zfork()) { for (i = 0; i < mn->ct; i++) - write(mn->fds[i], buf, len); - } else { - /* cat process */ - for (i = 0; i < mn->ct; i++) - while ((len = read(mn->fds[i], buf, TCBUFSIZE)) > 0) - write(mn->pipe, buf, len); + zclose(mn->fds[i]); + zclose(mn->pipe); + mn->ct = 1; + mn->fds[0] = fd; + return; + } + /* pid == 0 */ + closeallelse(mn); + if (mn->rflag) { + /* tee process */ + while ((len = read(mn->pipe, buf, TCBUFSIZE)) > 0) + for (i = 0; i < mn->ct; i++) + write(mn->fds[i], buf, len); + } else { + /* cat process */ + for (i = 0; i < mn->ct; i++) + while ((len = read(mn->fds[i], buf, TCBUFSIZE)) > 0) + write(mn->pipe, buf, len); + } + _exit(0); } - _exit(0); } /* close all the mnodes (failure) */ @@ -1135,7 +1418,7 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag) if (!mfds[fd1] || unset(MULTIOS)) { if(!mfds[fd1]) { /* starting a new multio */ - mfds[fd1] = (struct multio *) alloc(sizeof(struct multio)); + mfds[fd1] = (struct multio *) zhalloc(sizeof(struct multio)); if (!forked && save[fd1] == -2) save[fd1] = (fd1 == fd2) ? -1 : movefd(fd1); } @@ -1170,40 +1453,48 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag) /**/ static void -addvars(LinkList l, int export) +addvars(Estate state, Wordcode pc, int export) { - Varasg v; LinkList vl; - int xtr; - char **arr, **ptr; + int xtr, isstr, htok = 0; + char **arr, **ptr, *name; + Wordcode opc = state->pc; + wordcode ac; + local_list1(svl); xtr = isset(XTRACE); - if (xtr && nonempty(l)) { - fprintf(stderr, "%s", prompt4 ? prompt4 : ""); + if (xtr) { + printprompt4(); doneps4 = 1; } - - while (nonempty(l)) { - v = (Varasg) ugetnode(l); - singsub(&v->name); - if (errflag) - return; - untokenize(v->name); + state->pc = pc; + while (wc_code(ac = *state->pc++) == WC_ASSIGN) { + name = ecgetstr(state, EC_DUPTOK, &htok); + if (htok) + untokenize(name); if (xtr) - fprintf(stderr, "%s=", v->name); - if (v->type == PM_SCALAR) { - vl = newlinklist(); - addlinknode(vl, v->str); + fprintf(xtrerr, "%s=", name); + if ((isstr = (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR))) { + init_list1(svl, ecgetstr(state, EC_DUPTOK, &htok)); + vl = &svl; } else - vl = v->arr; - prefork(vl, v->type == PM_SCALAR ? 7 : 3); - if (errflag) - return; - if (isset(GLOBASSIGN) || v->type != PM_SCALAR) - globlist(vl); - if (errflag) - return; - if (v->type == PM_SCALAR && (empty(vl) || !nextnode(firstnode(vl)))) { + vl = ecgetlist(state, WC_ASSIGN_NUM(ac), EC_DUPTOK, &htok); + + if (vl && htok) { + prefork(vl, (isstr ? (PF_SINGLE|PF_ASSIGN) : + PF_ASSIGN)); + if (errflag) { + state->pc = opc; + return; + } + if (isset(GLOBASSIGN) || !isstr) + globlist(vl, 0); + if (errflag) { + state->pc = opc; + return; + } + } + if (isstr && (empty(vl) || !nextnode(firstnode(vl)))) { Param pm; char *val; int allexp; @@ -1215,48 +1506,100 @@ addvars(LinkList l, int export) val = ztrdup(ugetnode(vl)); } if (xtr) - fprintf(stderr, "%s ", val); - if (export) { - if (export < 0) { - /* We are going to fork so do not bother freeing this */ - pm = (Param) paramtab->removenode(paramtab, v->name); - if (isset(RESTRICTED) && (pm->flags & PM_RESTRICTED)) { - zerr("%s: restricted", pm->nam, 0); - zsfree(val); - return; - } + fprintf(xtrerr, "%s ", val); + if (export && !strchr(name, '[')) { + if (export < 0 && isset(RESTRICTED) && + (pm = (Param) paramtab->removenode(paramtab, name)) && + (pm->flags & PM_RESTRICTED)) { + zerr("%s: restricted", pm->nam, 0); + zsfree(val); + state->pc = opc; + return; } allexp = opts[ALLEXPORT]; opts[ALLEXPORT] = 1; - pm = setsparam(v->name, val); + pm = setsparam(name, val); opts[ALLEXPORT] = allexp; } else - pm = setsparam(v->name, val); - if (errflag) + pm = setsparam(name, val); + if (errflag) { + state->pc = opc; return; + } continue; } - ptr = arr = (char **) zalloc(sizeof(char **) * (countlinknodes(vl) + 1)); + if (vl) { + ptr = arr = (char **) zalloc(sizeof(char **) * + (countlinknodes(vl) + 1)); - while (nonempty(vl)) - *ptr++ = ztrdup((char *) ugetnode(vl)); + while (nonempty(vl)) + *ptr++ = ztrdup((char *) ugetnode(vl)); + } else + ptr = arr = (char **) zalloc(sizeof(char **)); *ptr = NULL; if (xtr) { - fprintf(stderr, "( "); + fprintf(xtrerr, "( "); for (ptr = arr; *ptr; ptr++) - fprintf(stderr, "%s ", *ptr); - fprintf(stderr, ") "); + fprintf(xtrerr, "%s ", *ptr); + fprintf(xtrerr, ") "); } - setaparam(v->name, arr); - if (errflag) + setaparam(name, arr); + if (errflag) { + state->pc = opc; return; + } + } + state->pc = opc; +} + +/**/ +void +setunderscore(char *str) +{ + if (str && *str) { + int l = strlen(str) + 1, nl = (l + 31) & ~31; + + if (nl > underscorelen || (underscorelen - nl) > 64) { + zfree(underscore, underscorelen); + underscore = (char *) zalloc(underscorelen = nl); + } + strcpy(underscore, str); + underscoreused = l; + } else { + if (underscorelen > 128) { + zfree(underscore, underscorelen); + underscore = (char *) zalloc(underscorelen = 32); + } + *underscore = '\0'; + underscoreused = 1; + } +} + +/* These describe the type of espansions 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); + if (esglob) { + LinkList ostrs = strs; + globlist(strs, 0); + strs = ostrs; + } } } /**/ static void -execcmd(Cmd cmd, int input, int output, int how, int last1) +execcmd(Estate state, int input, int output, int how, int last1) { HashNode hn = NULL; LinkNode node; @@ -1264,15 +1607,35 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) struct multio *mfds[10]; char *text; int save[10]; - int fil, dfil, is_cursh, type, i; + int fil, dfil, is_cursh, type, do_exec = 0, i, htok = 0; int nullexec = 0, assign = 0, forked = 0; int is_shfunc = 0, is_builtin = 0, is_exec = 0; /* Various flags to the command. */ int cflags = 0, checked = 0; + LinkList redir; + wordcode code; + Wordcode beg = state->pc, varspc; + FILE *oxtrerr = xtrerr; doneps4 = 0; - args = cmd->args; - type = cmd->type; + redir = (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL); + if (wc_code(*state->pc) == WC_ASSIGN) { + varspc = state->pc; + while (wc_code((code = *state->pc)) == WC_ASSIGN) + state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ? + 3 : WC_ASSIGN_NUM(code) + 2); + } else + varspc = NULL; + + code = *state->pc++; + + type = wc_code(code); + + /* It would be nice if we could use EC_DUPTOK instead of EC_DUP here. + * But for that we would need to check/change all builtins so that + * they don't modify their argument strings. */ + args = (type == WC_SIMPLE ? + ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok) : NULL); for (i = 0; i < 10; i++) { save[i] = -2; @@ -1281,7 +1644,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) /* If the command begins with `%', then assume it is a * * reference to a job in the job table. */ - if (type == SIMPLE && nonempty(args) && + if (type == WC_SIMPLE && args && nonempty(args) && *(char *)peekfirst(args) == '%') { pushnode(args, dupstring((how & Z_DISOWN) ? "disown" : (how & Z_ASYNC) ? "bg" : "fg")); @@ -1292,8 +1655,8 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) * 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 == SIMPLE && (how & Z_SYNC) && - nonempty(args) && empty(cmd->redir) && !input && + if (isset(AUTORESUME) && type == WC_SIMPLE && (how & Z_SYNC) && + args && nonempty(args) && (!redir || empty(redir)) && !input && !nextnode(firstnode(args))) { if (unset(NOTIFY)) scanjobs(); @@ -1306,8 +1669,8 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) * command if it contains some tokens (e.g. x=ex; ${x}port), so this * * only works in simple cases. has_token() is called to make sure * * this really is a simple case. */ - if (type == SIMPLE) { - while (nonempty(args)) { + if (type == WC_SIMPLE) { + while (args && nonempty(args)) { char *cmdarg = (char *) peekfirst(args); checked = !has_token(cmdarg); if (!checked) @@ -1323,13 +1686,12 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } if (!(hn->flags & BINF_PREFIX)) { is_builtin = 1; -#ifdef DYNAMIC + /* autoload the builtin if necessary */ if (!((Builtin) hn)->handlerfunc) { load_module(((Builtin) hn)->optstr); hn = builtintab->getnode(builtintab, cmdarg); } -#endif assign = (hn->flags & BINF_MAGICEQUALS); break; } @@ -1344,18 +1706,20 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } /* Do prefork substitutions */ - prefork(args, assign ? 2 : isset(MAGICEQUALSUBST)); + esprefork = (assign || isset(MAGICEQUALSUBST)) ? PF_TYPESET : 0; + if (args && htok) + prefork(args, esprefork); - if (type == SIMPLE) { + if (type == WC_SIMPLE) { int unglobbed = 0; for (;;) { char *cmdarg; if (!(cflags & BINF_NOGLOB)) - while (!checked && !errflag && nonempty(args) && + while (!checked && !errflag && args && nonempty(args) && has_token((char *) peekfirst(args))) - glob(args, firstnode(args)); + glob(args, firstnode(args), 0); else if (!unglobbed) { for (node = firstnode(args); node; incnode(node)) untokenize((char *) getdata(node)); @@ -1364,46 +1728,59 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) /* Current shell should not fork unless the * * exec occurs at the end of a pipeline. */ - if ((cflags & BINF_EXEC) && last1 == 2) - cmd->flags |= CFLAG_EXEC; + if ((cflags & BINF_EXEC) && last1) + do_exec = 1; /* Empty command */ - if (empty(args)) { - if (nonempty(cmd->redir)) { - if (cmd->flags & CFLAG_EXEC) { + if (!args || empty(args)) { + if (redir && nonempty(redir)) { + if (do_exec) { /* Was this "exec < foobar"? */ nullexec = 1; break; - } else if (!nullcmd || !*nullcmd || + } else if (varspc) { + nullexec = 2; + break; + } else if (!nullcmd || !*nullcmd || opts[CSHNULLCMD] || (cflags & BINF_PREFIX)) { zerr("redirection with no command", NULL, 0); errflag = lastval = 1; return; + } else if (!nullcmd || !*nullcmd || opts[SHNULLCMD]) { + if (!args) + args = newlinklist(); + addlinknode(args, dupstring(":")); } else if (readnullcmd && *readnullcmd && - ((Redir) peekfirst(cmd->redir))->type == READ && - !nextnode(firstnode(cmd->redir))) { + ((Redir) peekfirst(redir))->type == READ && + !nextnode(firstnode(redir))) { + if (!args) + args = newlinklist(); addlinknode(args, dupstring(readnullcmd)); - } else + } else { + if (!args) + args = newlinklist(); addlinknode(args, dupstring(nullcmd)); + } } else if ((cflags & BINF_PREFIX) && (cflags & BINF_COMMAND)) { lastval = 0; return; } else { cmdoutval = 0; - addvars(cmd->vars, 0); + if (varspc) + addvars(state, varspc, 0); if (errflag) lastval = errflag; else lastval = cmdoutval; if (isset(XTRACE)) { - fputc('\n', stderr); - fflush(stderr); + fputc('\n', xtrerr); + fflush(xtrerr); } return; } - } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) && - (cmd->flags & CFLAG_EXEC)) { - zerrnam("exec", "%s: restricted", (char *) getdata(firstnode(args)), 0); + } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) && do_exec) { + zerrnam("exec", "%s: restricted", + (char *) getdata(firstnode(args)), 0); lastval = 1; return; } @@ -1428,11 +1805,12 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } if (!(hn->flags & BINF_PREFIX)) { is_builtin = 1; -#ifdef DYNAMIC + /* autoload the builtin if necessary */ - if (!((Builtin) hn)->handlerfunc) + if (!((Builtin) hn)->handlerfunc) { load_module(((Builtin) hn)->optstr); -#endif + hn = builtintab->getnode(builtintab, cmdarg); + } break; } cflags &= ~BINF_BUILTIN & ~BINF_COMMAND; @@ -1448,23 +1826,20 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } /* Get the text associated with this command. */ - if (jobbing || (how & Z_TIMED)) - text = getjobtext((void *) cmd); + if ((how & Z_ASYNC) || + (!sfcontext && !sourcelevel && (jobbing || (how & Z_TIMED)))) + text = getjobtext(state->prog, beg); else text = NULL; /* Set up special parameter $_ */ - zsfree(underscore); - if (nonempty(args) - && (underscore = ztrdup((char *) getdata(lastnode(args))))) - untokenize(underscore); - else - underscore = ztrdup(""); + + setunderscore((args && nonempty(args)) ? ((char *) getdata(lastnode(args))) : ""); /* Warn about "rm *" */ - if (type == SIMPLE && interact && unset(RMSTARSILENT) - && isset(SHINSTDIN) && nonempty(args) && nextnode(firstnode(args)) - && !strcmp(peekfirst(args), "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) { @@ -1493,28 +1868,33 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) return; } - if (type == SIMPLE && !nullexec) { + if (type == WC_SIMPLE && !nullexec) { char *s; - char trycd = (isset(AUTOCD) && isset(SHINSTDIN) - && empty(cmd->redir) && !empty(args) - && !nextnode(firstnode(args)) - && *(char *)peekfirst(args)); + char trycd = (isset(AUTOCD) && isset(SHINSTDIN) && + (!redir || empty(redir)) && args && !empty(args) && + !nextnode(firstnode(args)) && *(char *)peekfirst(args)); - DPUTS(empty(args), "BUG: empty(args) in exec.c"); + 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)->flags & HASHED)) { + checkpath = path; + dohashcmd = 1; + } cmdnamtab->removenode(cmdnamtab, cmdarg); cmdnamtab->freenode(hn); hn = NULL; } - if (!hn && isset(HASHCMDS) && strcmp(cmdarg, "..")) { + if (!hn && dohashcmd && strcmp(cmdarg, "..")) { for (s = cmdarg; *s && *s != '/'; s++); if (!*s) - hn = (HashNode) hashcmd(cmdarg, pathchecked); + hn = (HashNode) hashcmd(cmdarg, checkpath); } } @@ -1529,7 +1909,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } /* This is nonzero if the command is a current shell procedure? */ - is_cursh = (is_builtin || is_shfunc || (type >= CURSH) || nullexec); + is_cursh = (is_builtin || is_shfunc || nullexec || type >= WC_CURSH); /************************************************************************** * Do we need to fork? We need to fork if: * @@ -1552,10 +1932,11 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) * current shell. * **************************************************************************/ - if ((how & Z_ASYNC) || (!(cmd->flags & CFLAG_EXEC) && - (((is_builtin || is_shfunc) && output) || - (!is_cursh && (last1 != 1 || sigtrapped[SIGZERR] || - sigtrapped[SIGEXIT] || havefiles()))))) { + if ((how & Z_ASYNC) || + (!do_exec && + (((is_builtin || is_shfunc) && output) || + (!is_cursh && (last1 != 1 || sigtrapped[SIGZERR] || + sigtrapped[SIGEXIT] || havefiles()))))) { pid_t pid; int synch[2]; @@ -1576,21 +1957,27 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) closem(2); #endif if (how & Z_ASYNC) { - lastpid = (long) pid; - } else if (!jobtab[thisjob].stty_in_env && nonempty(cmd->vars)) { + lastpid = (zlong) pid; + } else if (!jobtab[thisjob].stty_in_env && varspc) { /* search for STTY=... */ - while (nonempty(cmd->vars)) - if (!strcmp(((Varasg) ugetnode(cmd->vars))->name, "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); return; } /* pid == 0 */ close(synch[0]); - entersubsh(how, type != SUBSH && !(how & Z_ASYNC) ? 2 : 1, 0); + entersubsh(how, (type != WC_SUBSH) && !(how & Z_ASYNC) ? 2 : 1, 0); close(synch[1]); forked = 1; if (sigtrapped[SIGINT] & ZSIG_IGNORED) @@ -1613,13 +2000,26 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) is_exec = 1; } - if (!(cflags & BINF_NOGLOB)) - globlist(args); + if ((esglob = !(cflags & BINF_NOGLOB)) && args && 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 (!(xtrerr = fdopen(movefd(dup(fileno(stderr))), "w"))) + xtrerr = stderr; + else + fdtable[fileno(xtrerr)] = 3; + } + /* Add pipeline input/output to mnodes */ if (input) addfd(forked, save, mfds, 0, input, 0); @@ -1627,11 +2027,12 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) addfd(forked, save, mfds, 1, output, 1); /* Do process substitutions */ - spawnpipes(cmd->redir); + if (redir) + spawnpipes(redir); /* Do io redirections */ - while (nonempty(cmd->redir)) { - fn = (Redir) ugetnode(cmd->redir); + while (redir && nonempty(redir)) { + fn = (Redir) ugetnode(redir); DPUTS(fn->type == HEREDOC || fn->type == HEREDOCDASH, "BUG: unexpanded here document"); if (fn->type == INPIPE) { @@ -1649,7 +2050,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } addfd(forked, save, mfds, fn->fd1, fn->fd2, 1); } else { - if (fn->type != HERESTR && xpandredir(fn, cmd->redir)) + if (fn->type != HERESTR && xpandredir(fn, redir)) continue; if (errflag) { closemnodes(mfds); @@ -1691,7 +2092,8 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) addfd(forked, save, mfds, fn->fd1, fil, 0); /* If this is 'exec < file', read from stdin, * * not terminal, unless `file' is a terminal. */ - if (nullexec && fn->fd1 == 0 && isset(SHINSTDIN) && interact) + if (nullexec == 1 && fn->fd1 == 0 && + isset(SHINSTDIN) && interact) init_io(); break; case CLOSE: @@ -1702,16 +2104,28 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) break; case MERGEIN: case MERGEOUT: - if(fn->fd2 < 10) + if (fn->fd2 < 10) closemn(mfds, fn->fd2); - fil = dup(fn->fd2); + if (fn->fd2 > 9 && + (fdtable[fn->fd2] || + fn->fd2 == coprocin || + fn->fd2 == coprocout)) { + fil = -1; + errno = EBADF; + } else { + int fd = fn->fd2; + if(fd == -2) + fd = (fn->type == MERGEOUT) ? coprocout : coprocin; + fil = dup(fd); + } if (fil == -1) { char fdstr[4]; closemnodes(mfds); fixfds(save); - sprintf(fdstr, "%d", fn->fd2); - zerr("%s: %e", fdstr, errno); + if (fn->fd2 != -2) + sprintf(fdstr, "%d", fn->fd2); + zerr("%s: %e", fn->fd2 == -2 ? "coprocess" : fdstr, errno); execerr(); } addfd(forked, save, mfds, fn->fd1, fil, fn->type == MERGEOUT); @@ -1748,37 +2162,43 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) /* We are done with redirection. close the mnodes, * * spawning tee/cat processes as necessary. */ for (i = 0; i < 10; i++) - closemn(mfds, i); + if (mfds[i] && mfds[i]->ct >= 2) + closemn(mfds, i); if (nullexec) { - for (i = 0; i < 10; i++) - if (save[i] != -2) - zclose(save[i]); + 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; + } /* - * Here we specifically *don't* restore the original fd's - * before returning. + * If nullexec is 2, we have variables to add with the redirections + * in place. */ - return; - } - - if (isset(EXECOPT) && !errflag) { + if (varspc) + addvars(state, varspc, 0); + lastval = errflag ? errflag : cmdoutval; + if (isset(XTRACE)) { + fputc('\n', xtrerr); + fflush(xtrerr); + } + } else if (isset(EXECOPT) && !errflag) { /* * 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) - entersubsh(how, type != SUBSH ? 2 : 1, 1); - if (type >= CURSH) { - static int (*func[]) _((Cmd)) = { - execcursh, exectime, execfuncdef, execfor, execwhile, - execrepeat, execif, execcase, execselect, execcond, - execarith, execautofn - }; - + entersubsh(how, (type != WC_SUBSH) ? 2 : 1, 1); + if (type >= WC_CURSH) { if (last1 == 1) - cmd->flags |= CFLAG_EXEC; - lastval = (func[type - CURSH]) (cmd); + do_exec = 1; + lastval = (execfuncs[type - WC_CURSH])(state, do_exec); } else if (is_builtin || is_shfunc) { LinkList restorelist = 0, removelist = 0; /* builtin or shell function */ @@ -1786,24 +2206,29 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) if (!forked && ((cflags & BINF_COMMAND) || (unset(POSIXBUILTINS) && !assign) || (isset(POSIXBUILTINS) && !is_shfunc && - !(hn->flags & BINF_PSPECIAL)))) - save_params(cmd, &restorelist, &removelist); - - if (cmd->vars) { + !(hn->flags & BINF_PSPECIAL)))) { + if (varspc) + save_params(state, varspc, &restorelist, &removelist); + else + restorelist = removelist = NULL; + } + if (varspc) { /* Export this if the command is a shell function, * but not if it's a builtin. */ - addvars(cmd->vars, is_shfunc); + addvars(state, varspc, is_shfunc); if (errflag) { - restore_params(restorelist, removelist); + if (restorelist) + restore_params(restorelist, removelist); lastval = 1; fixfds(save); - return; + goto done; } } if (is_shfunc) { /* It's a shell function */ + #ifdef PATH_DEV_FD int i; @@ -1814,7 +2239,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) if (subsh_close >= 0) zclose(subsh_close); subsh_close = -1; - execshfunc(cmd, (Shfunc) hn); + execshfunc((Shfunc) hn, args); #ifdef PATH_DEV_FD for (i = 10; i <= max_zsh_fd; i++) if (fdtable[i] > 1) @@ -1843,30 +2268,31 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) clearerr(stdout); } - if (cmd->flags & CFLAG_EXEC) { + 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(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0); + savehistfile(NULL, 1, HFILE_USE_OPTIONS); exit(lastval); } - - restore_params(restorelist, removelist); + if (restorelist) + restore_params(restorelist, removelist); } else { - if (cmd->flags & CFLAG_EXEC) { + if (!forked) setiparam("SHLVL", --shlvl); + if (do_exec) { /* If we are exec'ing a command, and we are not * * in a subshell, then save the history file. */ if (!subsh && isset(RCS) && interact && !nohistsave) - savehistfile(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0); + savehistfile(NULL, 1, HFILE_USE_OPTIONS); } - if (type == SIMPLE) { - if (cmd->vars) { - addvars(cmd->vars, -1); + if (type == WC_SIMPLE) { + if (varspc) { + addvars(state, varspc, -1); if (errflag) _exit(1); } @@ -1881,7 +2307,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) #endif execute((Cmdnam) hn, cflags & BINF_DASH); } else { /* ( ... ) */ - DPUTS(cmd->vars && nonempty(cmd->vars), + DPUTS(varspc, "BUG: assigment before complex command"); list_pipe = 0; if (subsh_close >= 0) @@ -1889,7 +2315,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) subsh_close = -1; /* If we're forked (and we should be), no need to return */ DPUTS(last1 != 1 && !forked, "BUG: not exiting?"); - execlist(cmd->u.list, 0, 1); + execlist(state, 0, 1); } } } @@ -1898,54 +2324,48 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) if (forked) _exit(lastval); fixfds(save); + + done: + if (xtrerr != oxtrerr) { + fil = fileno(xtrerr); + fclose(xtrerr); + xtrerr = oxtrerr; + zclose(fil); + } } /* Arrange to have variables restored. */ /**/ static void -save_params(Cmd cmd, LinkList *restore_p, LinkList *remove_p) +save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p) { Param pm; - LinkNode node; char *s; - - MUSTUSEHEAP("save_params()"); + wordcode ac; *restore_p = newlinklist(); *remove_p = newlinklist(); - for (node = firstnode(cmd->vars); node; incnode(node)) { - s = ((Varasg) getdata(node))->name; + while (wc_code(ac = *pc) == WC_ASSIGN) { + s = ecrawstr(state->prog, pc + 1, NULL); if ((pm = (Param) paramtab->getnode(paramtab, s))) { if (!(pm->flags & PM_SPECIAL)) { paramtab->removenode(paramtab, s); } else if (!(pm->flags & PM_READONLY) && (unset(RESTRICTED) || !(pm->flags & PM_RESTRICTED))) { - Param tpm = (Param) alloc(sizeof *tpm); - + Param tpm = (Param) zhalloc(sizeof *tpm); tpm->nam = s; - tpm->flags = pm->flags; - switch (PM_TYPE(pm->flags)) { - case PM_SCALAR: - tpm->u.str = ztrdup(pm->gets.cfn(pm)); - break; - case PM_INTEGER: - tpm->u.val = pm->gets.ifn(pm); - break; - case PM_ARRAY: - PERMALLOC { - tpm->u.arr = arrdup(pm->gets.afn(pm)); - } LASTALLOC; - break; - } + copyparam(tpm, pm, 1); pm = tpm; } addlinknode(*remove_p, s); addlinknode(*restore_p, pm); - } else { + } else addlinknode(*remove_p, s); - } + + pc += (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR ? + 3 : WC_ASSIGN_NUM(ac) + 2); } } @@ -1958,14 +2378,12 @@ restore_params(LinkList restorelist, LinkList removelist) Param pm; char *s; - if (removelist) { - /* remove temporary parameters */ - while ((s = (char *) ugetnode(removelist))) { - if ((pm = (Param) paramtab->getnode(paramtab, s)) && - !(pm->flags & PM_SPECIAL)) { - pm->flags &= ~PM_READONLY; - unsetparam_pm(pm, 0, 0); - } + /* remove temporary parameters */ + while ((s = (char *) ugetnode(removelist))) { + if ((pm = (Param) paramtab->getnode(paramtab, s)) && + !(pm->flags & PM_SPECIAL)) { + pm->flags &= ~PM_READONLY; + unsetparam_pm(pm, 0, 0); } } @@ -1986,14 +2404,21 @@ restore_params(LinkList restorelist, LinkList removelist) case PM_INTEGER: tpm->sets.ifn(tpm, pm->u.val); break; + case PM_EFLOAT: + case PM_FFLOAT: + tpm->sets.ffn(tpm, pm->u.dval); + break; case PM_ARRAY: tpm->sets.afn(tpm, pm->u.arr); break; + case PM_HASHED: + tpm->sets.hfn(tpm, pm->u.hash); + break; } } else paramtab->addnode(paramtab, pm->nam, pm); - if (pm->flags & PM_EXPORTED) - pm->env = addenv(pm->nam, getsparam(pm->nam)); + if ((pm->flags & PM_EXPORTED) && ((s = getsparam(pm->nam)))) + pm->env = addenv(pm->nam, s, pm->flags); } } } @@ -2037,18 +2462,17 @@ entersubsh(int how, int cl, int fake) } } else if (thisjob != -1 && cl) { if (jobtab[list_pipe_job].gleader && (list_pipe || list_pipe_child)) { - if (kill(jobtab[list_pipe_job].gleader, 0) == -1 || + if (killpg(jobtab[list_pipe_job].gleader, 0) == -1 || setpgrp(0L, jobtab[list_pipe_job].gleader) == -1) { jobtab[list_pipe_job].gleader = - jobtab[thisjob].gleader = mypgrp; - setpgrp(0L, mypgrp); - + jobtab[thisjob].gleader = (list_pipe_child ? mypgrp : getpid()); + setpgrp(0L, jobtab[list_pipe_job].gleader); if (how & Z_SYNC) attachtty(jobtab[thisjob].gleader); } } else if (!jobtab[thisjob].gleader || - (setpgrp(0L, jobtab[thisjob].gleader) == -1)) { + setpgrp(0L, jobtab[thisjob].gleader) == -1) { jobtab[thisjob].gleader = getpid(); if (list_pipe_job != thisjob && !jobtab[list_pipe_job].gleader) @@ -2086,7 +2510,7 @@ entersubsh(int how, int cl, int fake) /* close internal shell fds */ /**/ -void +mod_export void closem(int how) { int i; @@ -2147,8 +2571,14 @@ gethere(char *str, int typ) if (t > buf && t[-1] == '\n') t--; *t = '\0'; - if (!qt) + if (!qt) { + int ef = errflag; + parsestr(buf); + + if (!errflag) + errflag = ef; + } s = dupstring(buf); zfree(buf, bsiz); return s; @@ -2184,24 +2614,26 @@ getherestr(struct redir *fn) LinkList getoutput(char *cmd, int qt) { - List list; + Eprog prog; int pipes[2]; pid_t pid; - Cmd c; - Redir r; + Wordcode pc; - if (!(list = parse_string(cmd))) + if (!(prog = parse_string(cmd, 0))) return NULL; - if (list != &dummy_list && !list->right && !list->left->flags && - list->left->type == END && list->left->left->type == END && - (c = list->left->left->left)->type == SIMPLE && empty(c->args) && - empty(c->vars) && nonempty(c->redir) && - !nextnode(firstnode(c->redir)) && - (r = (Redir) getdata(firstnode(c->redir)))->fd1 == 0 && - r->type == READ) { + + 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]) == READ && + !pc[4] && + wc_code(pc[6]) == WC_SIMPLE && !WC_SIMPLE_ARGC(pc[6])) { /* $(< word) */ int stream; - char *s = r->name; + char *s = dupstring(ecrawstr(prog, pc + 5, NULL)); singsub(&s); if (errflag) @@ -2213,7 +2645,6 @@ getoutput(char *cmd, int qt) } return readoutput(stream, qt); } - mpipe(pipes); child_block(); cmdoutval = 0; @@ -2231,18 +2662,19 @@ getoutput(char *cmd, int qt) zclose(pipes[1]); retval = readoutput(pipes[0], qt); fdtable[pipes[0]] = 0; - child_suspend(0); /* unblocks */ + waitforpid(pid); /* unblocks */ lastval = cmdoutval; return retval; } - /* pid == 0 */ child_unblock(); zclose(pipes[0]); redup(pipes[1], 1); opts[MONITOR] = 0; entersubsh(Z_SYNC, 1, 0); - execlist(list, 0, 1); + cmdpush(CS_CMDSUBST); + execode(prog, 0, 1); + cmdpop(); close(1); _exit(lastval); zerr("exit returned in child!!", NULL, 0); @@ -2253,7 +2685,7 @@ getoutput(char *cmd, int qt) /* read output of command substitution */ /**/ -static LinkList +mod_export LinkList readoutput(int in, int qt) { LinkList ret; @@ -2263,7 +2695,7 @@ readoutput(int in, int qt) fin = fdopen(in, "r"); ret = newlinklist(); - ptr = buf = (char *) ncalloc(bsiz = 64); + ptr = buf = (char *) hcalloc(bsiz = 64); while ((c = fgetc(fin)) != EOF || errno == EINTR) { if (c == EOF) { errno = 0; @@ -2276,7 +2708,7 @@ readoutput(int in, int qt) cnt++; } if (++cnt >= bsiz) { - char *pp = (char *) ncalloc(bsiz *= 2); + char *pp = (char *) hcalloc(bsiz *= 2); memcpy(pp, buf, cnt - 1); ptr = (buf = pp) + cnt - 1; @@ -2294,7 +2726,7 @@ readoutput(int in, int qt) } addlinknode(ret, buf); } else { - char **words = spacesplit(buf, 0); + char **words = spacesplit(buf, 0, 1); while (*words) { if (isset(GLOBSUBST)) @@ -2306,11 +2738,11 @@ readoutput(int in, int qt) } /**/ -static List +static Eprog parsecmd(char *cmd) { char *str; - List list; + Eprog prog; for (str = cmd + 2; *str && *str != Outpar; str++); if (!*str || cmd[1] != Inpar) { @@ -2318,11 +2750,11 @@ parsecmd(char *cmd) return NULL; } *str = '\0'; - if (str[1] || !(list = parse_string(cmd + 2))) { + if (str[1] || !(prog = parse_string(cmd + 2, 0))) { zerr("parse error in process substitution", NULL, 0); return NULL; } - return list; + return prog; } /* =(...) */ @@ -2333,22 +2765,22 @@ getoutputfile(char *cmd) { pid_t pid; char *nam; - List list; + Eprog prog; int fd; if (thisjob == -1) return NULL; - if (!(list = parsecmd(cmd))) + if (!(prog = parsecmd(cmd))) return NULL; if (!(nam = gettempname())) return NULL; nam = ztrdup(nam); - PERMALLOC { - if (!jobtab[thisjob].filelist) - jobtab[thisjob].filelist = newlinklist(); - addlinknode(jobtab[thisjob].filelist, nam); - } LASTALLOC; + + if (!jobtab[thisjob].filelist) + jobtab[thisjob].filelist = znewlinklist(); + zaddlinknode(jobtab[thisjob].filelist, nam); + child_block(); fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600); @@ -2371,7 +2803,9 @@ getoutputfile(char *cmd) redup(fd, 1); opts[MONITOR] = 0; entersubsh(Z_SYNC, 1, 0); - execlist(list, 0, 1); + cmdpush(CS_CMDSUBST); + execode(prog, 0, 1); + cmdpop(); close(1); _exit(lastval); zerr("exit returned in child!!", NULL, 0); @@ -2407,7 +2841,7 @@ getproc(char *cmd) zerr("doesn't look like your system supports FIFOs.", NULL, 0); return NULL; #else - List list; + Eprog prog; int out = *cmd == Inang; char *pnam; #ifndef PATH_DEV_FD @@ -2422,16 +2856,15 @@ getproc(char *cmd) if (!(pnam = namedpipe())) return NULL; #else - pnam = ncalloc(strlen(PATH_DEV_FD) + 6); + pnam = hcalloc(strlen(PATH_DEV_FD) + 6); #endif - if (!(list = parsecmd(cmd))) + if (!(prog = parsecmd(cmd))) return NULL; #ifndef PATH_DEV_FD - PERMALLOC { - if (!jobtab[thisjob].filelist) - jobtab[thisjob].filelist = newlinklist(); - addlinknode(jobtab[thisjob].filelist, ztrdup(pnam)); - } LASTALLOC; + if (!jobtab[thisjob].filelist) + jobtab[thisjob].filelist = znewlinklist(); + zaddlinknode(jobtab[thisjob].filelist, ztrdup(pnam)); + if (zfork()) { #else mpipe(pipes); @@ -2456,7 +2889,9 @@ getproc(char *cmd) redup(pipes[out], out); closem(0); /* this closes pipes[!out] as well */ #endif - execlist(list, 0, 1); + cmdpush(CS_CMDSUBST); + execode(prog, 0, 1); + cmdpop(); zclose(out); _exit(lastval); return NULL; @@ -2469,10 +2904,10 @@ getproc(char *cmd) static int getpipe(char *cmd) { - List list; + Eprog prog; int pipes[2], out = *cmd == Inang; - if (!(list = parsecmd(cmd))) + if (!(prog = parsecmd(cmd))) return -1; mpipe(pipes); if (zfork()) { @@ -2482,7 +2917,9 @@ getpipe(char *cmd) entersubsh(Z_ASYNC, 1, 0); redup(pipes[out], out); closem(0); /* this closes pipes[!out] as well */ - execlist(list, 0, 1); + cmdpush(CS_CMDSUBST); + execode(prog, 0, 1); + cmdpop(); _exit(lastval); return 0; } @@ -2518,26 +2955,62 @@ spawnpipes(LinkList l) } } +extern int tracingcond; + /* evaluate a [[ ... ]] */ /**/ static int -execcond(Cmd cmd) +execcond(Estate state, int do_exec) { - return !evalcond(cmd->u.cond); + int stat; + + state->pc--; + if (isset(XTRACE)) { + printprompt4(); + fprintf(xtrerr, "[["); + tracingcond++; + } + cmdpush(CS_COND); + stat = !evalcond(state); + cmdpop(); + if (isset(XTRACE)) { + fprintf(xtrerr, " ]]\n"); + fflush(xtrerr); + tracingcond--; + } + return stat; } /* evaluate a ((...)) arithmetic command */ /**/ static int -execarith(Cmd cmd) +execarith(Estate state, int do_exec) { char *e; - long val = 0; + zlong val = 0; + 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 = mathevali(e); - while ((e = (char *) ugetnode(cmd->args))) - val = matheval(e); + cmdpop(); + + if (isset(XTRACE)) { + fprintf(xtrerr, " ))\n"); + fflush(xtrerr); + } errflag = 0; return !val; } @@ -2546,16 +3019,16 @@ execarith(Cmd cmd) /**/ static int -exectime(Cmd cmd) +exectime(Estate state, int do_exec) { int jb; jb = thisjob; - if (!cmd->u.pline) { + if (WC_TIMED_TYPE(state->pc[-1]) == WC_TIMED_EMPTY) { shelltime(); return 0; } - execpline(cmd->u.pline, Z_TIMED|Z_SYNC, 0); + execpline(state, *state->pc++, Z_TIMED|Z_SYNC, 0); thisjob = jb; return lastval; } @@ -2564,32 +3037,74 @@ exectime(Cmd cmd) /**/ static int -execfuncdef(Cmd cmd) +execfuncdef(Estate state, int do_exec) { Shfunc shf; char *s; - int signum; - - PERMALLOC { - while ((s = (char *) ugetnode(cmd->args))) { - shf = (Shfunc) zalloc(sizeof *shf); - shf->funcdef = (List) dupstruct(cmd->u.list); - shf->flags = 0; - - /* is this shell function a signal trap? */ - if (!strncmp(s, "TRAP", 4) && (signum = getsignum(s + 4)) != -1) { - if (settrap(signum, shf->funcdef)) { - freestruct(shf->funcdef); - zfree(shf, sizeof *shf); - LASTALLOC_RETURN 1; - } - sigtrapped[signum] |= ZSIG_FUNC; + int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 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) + execsubst(names); + + while ((s = (char *) ugetnode(names))) { + prog = (Eprog) zalloc(sizeof(*prog)); + prog->npats = npats; + prog->len = len; + if (state->prog->dump) { + prog->flags = EF_MAP; + incrdumpcount(state->prog->dump); + prog->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog)); + prog->prog = state->pc; + prog->strs = state->strs + sbeg; + prog->dump = state->prog->dump; + } 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->flags = 0; + + /* is this shell function a signal trap? */ + if (!strncmp(s, "TRAP", 4) && + (signum = getsignum(s + 4)) != -1) { + if (settrap(signum, shf->funcdef)) { + freeeprog(shf->funcdef); + zfree(shf, sizeof(*shf)); + state->pc = end; + return 1; } - shfunctab->addnode(shfunctab, ztrdup(s), shf); + sigtrapped[signum] |= ZSIG_FUNC; } - } LASTALLOC; - if(isset(HISTNOFUNCTIONS)) + shfunctab->addnode(shfunctab, ztrdup(s), shf); + } + if (isset(HISTNOFUNCTIONS)) remhist(); + state->pc = end; return 0; } @@ -2597,22 +3112,45 @@ execfuncdef(Cmd cmd) /**/ static void -execshfunc(Cmd cmd, Shfunc shf) +execshfunc(Shfunc shf, LinkList args) { LinkList last_file_list = NULL; + unsigned char *ocs; + int ocsp, osfc; if (errflag) return; - if (!list_pipe) { + if (!list_pipe && thisjob != list_pipe_job) { /* 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); } - - doshfunc(shf->funcdef, cmd->args, shf->flags, 0); + if (isset(XTRACE)) { + LinkNode lptr; + printprompt4(); + if (args) + for (lptr = firstnode(args); lptr; incnode(lptr)) { + if (lptr != firstnode(args)) + fputc(' ', xtrerr); + fprintf(xtrerr, "%s", (char *)getdata(lptr)); + } + fputc('\n', xtrerr); + fflush(xtrerr); + } + ocs = cmdstack; + ocsp = cmdsp; + cmdstack = (unsigned char *) zalloc(CMDSTACKSZ); + cmdsp = 0; + if ((osfc = sfcontext) == SFC_NONE) + sfcontext = SFC_DIRECT; + doshfunc(shf->nam, shf->funcdef, args, shf->flags, 0); + sfcontext = osfc; + free(cmdstack); + cmdstack = ocs; + cmdsp = ocsp; if (!list_pipe) deletefilelist(last_file_list); @@ -2626,168 +3164,244 @@ execshfunc(Cmd cmd, Shfunc shf) /**/ static int -execautofn(Cmd cmd) +execautofn(Estate state, int do_exec) { - Shfunc shf = cmd->u.autofn->shf; - List l = getfpfunc(shf->nam); - if(l == &dummy_list) { - zerr("%s: function definition file not found", shf->nam, 0); + Shfunc shf; + char *oldscriptname; + + if (!(shf = loadautofn(state->prog->shf, 1, 0))) return 1; + + oldscriptname = scriptname; + scriptname = dupstring(state->prog->shf->nam); + execode(shf->funcdef, 1, 0); + scriptname = oldscriptname; + + return lastval; +} + +/**/ +Shfunc +loadautofn(Shfunc shf, int fksh, int autol) +{ + int noalias = noaliases, ksh = 1; + Eprog prog; + + pushheap(); + + noaliases = (shf->flags & PM_UNALIASED); + prog = getfpfunc(shf->nam, &ksh); + noaliases = noalias; + + if (ksh == 1) + ksh = fksh; + + if (prog == &dummy_eprog) { + zerr("%s: function definition file not found", shf->nam, 0); + popheap(); + return NULL; } - if(isset(KSHAUTOLOAD)) { - VARARR(char, n, strlen(shf->nam) + 1); - strcpy(n, shf->nam); - execlist(l, 1, 0); - shf = (Shfunc) shfunctab->getnode(shfunctab, n); - if(!shf || (shf->flags & PM_UNDEFINED)) { - zerr("%s: function not defined by file", n, 0); - return 1; + if (!prog) + prog = &dummy_eprog; + 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->flags &= ~PM_UNDEFINED; + } else { + VARARR(char, n, strlen(shf->nam) + 1); + strcpy(n, shf->nam); + execode(prog, 1, 0); + shf = (Shfunc) shfunctab->getnode(shfunctab, n); + if (!shf || (shf->flags & PM_UNDEFINED)) { + zerr("%s: function not defined by file", n, 0); + popheap(); + return NULL; + } } } else { - freestruct(shf->funcdef); - PERMALLOC { - shf->funcdef = dupstruct(stripkshdef(l, shf->nam)); - } LASTALLOC; + freeeprog(shf->funcdef); + if (prog->flags & EF_MAP) + shf->funcdef = stripkshdef(prog, shf->nam); + else + shf->funcdef = dupeprog(stripkshdef(prog, shf->nam), 0); shf->flags &= ~PM_UNDEFINED; } - HEAPALLOC { - execlist(dupstruct(shf->funcdef), 1, 0); - } LASTALLOC; - return lastval; + popheap(); + + return shf; } /* execute a shell function */ /**/ -void -doshfunc(List list, LinkList doshargs, int flags, int noreturnval) +mod_export void +doshfunc(char *name, Eprog prog, LinkList doshargs, int flags, int noreturnval) /* If noreturnval is nonzero, then reset the current return * * value (lastval) to its value before the shell function * * was executed. */ { - char **tab, **x, *oargv0 = NULL; - int xexittr, newexittr, oldzoptind, oldlastval; - char *ou; - void *xexitfn, *newexitfn; - char saveopts[OPT_SIZE]; - int obreaks = breaks; - - HEAPALLOC { - pushheap(); - if (trapreturn < 0) - trapreturn--; - oldlastval = lastval; - xexittr = sigtrapped[SIGEXIT]; - if (xexittr & ZSIG_FUNC) - xexitfn = shfunctab->removenode(shfunctab, "TRAPEXIT"); - else - xexitfn = sigfuncs[SIGEXIT]; - sigtrapped[SIGEXIT] = 0; - sigfuncs[SIGEXIT] = NULL; - tab = pparams; - oldzoptind = zoptind; - zoptind = 1; - - /* 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(saveopts, opts, sizeof(opts)); - - if (flags & PM_TAGGED) - opts[XTRACE] = 1; - opts[PRINTEXITVALUE] = 0; - if (doshargs) { - LinkNode node; - - node = doshargs->first; - pparams = x = (char **) zcalloc(((sizeof *x) * (1 + countlinknodes(doshargs)))); - if (isset(FUNCTIONARGZERO)) { - oargv0 = argzero; - argzero = ztrdup((char *) node->dat); - } - node = node->next; - for (; node; node = node->next, x++) - *x = ztrdup((char *) node->dat); - } else { - pparams = (char **) zcalloc(sizeof *pparams); - if (isset(FUNCTIONARGZERO)) { - oargv0 = argzero; - argzero = ztrdup(argzero); - } + char **tab, **x, *oargv0; + int oldzoptind, oldlastval, oldoptcind; + char saveopts[OPT_SIZE], *oldscriptname = NULL, *fname = dupstring(name); + int obreaks; + struct funcstack fstack; + + pushheap(); + + oargv0 = NULL; + obreaks = breaks;; + if (trapreturn < 0) + trapreturn--; + oldlastval = lastval; + + starttrapscope(); + + tab = pparams; + if (!(flags & PM_UNDEFINED)) { + oldscriptname = scriptname; + scriptname = dupstring(name); + } + oldzoptind = zoptind; + zoptind = 1; + oldoptcind = optcind; + 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(saveopts, opts, sizeof(opts)); + + if (flags & PM_TAGGED) + opts[XTRACE] = 1; + opts[PRINTEXITVALUE] = 0; + if (doshargs) { + LinkNode node; + + node = doshargs->first; + pparams = x = (char **) zcalloc(((sizeof *x) * + (1 + countlinknodes(doshargs)))); + if (isset(FUNCTIONARGZERO)) { + oargv0 = argzero; + argzero = ztrdup((char *) node->dat); } - startparamscope(); - ou = underscore; - underscore = ztrdup(underscore); - execlist(dupstruct(list), 1, 0); - zsfree(underscore); - underscore = ou; - endparamscope(); - - if (retflag) { - retflag = 0; - breaks = obreaks; - } - freearray(pparams); - if (oargv0) { - zsfree(argzero); - argzero = oargv0; - } - zoptind = oldzoptind; - pparams = tab; - - if (isset(LOCALOPTIONS)) { - /* restore all shell options except PRIVILEGED and RESTRICTED */ - saveopts[PRIVILEGED] = opts[PRIVILEGED]; - saveopts[RESTRICTED] = opts[RESTRICTED]; - memcpy(opts, saveopts, sizeof(opts)); - } else { - /* just restore a couple. */ - opts[XTRACE] = saveopts[XTRACE]; - opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE]; - opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS]; + node = node->next; + for (; node; node = node->next, x++) + *x = ztrdup((char *) node->dat); + } else { + pparams = (char **) zcalloc(sizeof *pparams); + if (isset(FUNCTIONARGZERO)) { + oargv0 = argzero; + argzero = ztrdup(argzero); } + } + fstack.name = dupstring(name); + fstack.prev = funcstack; + funcstack = &fstack; - /* - * The trap '...' EXIT runs in the environment of the caller, - * so remember it here but run it after resetting the - * traps for the parent. - */ - newexittr = sigtrapped[SIGEXIT]; - newexitfn = sigfuncs[SIGEXIT]; - if (newexittr & ZSIG_FUNC) - shfunctab->removenode(shfunctab, "TRAPEXIT"); - - sigtrapped[SIGEXIT] = xexittr; - if (xexittr & ZSIG_FUNC) { - shfunctab->addnode(shfunctab, ztrdup("TRAPEXIT"), xexitfn); - sigfuncs[SIGEXIT] = ((Shfunc) xexitfn)->funcdef; - } else - sigfuncs[SIGEXIT] = (List) xexitfn; + if (prog->flags & EF_RUN) { + Shfunc shf; + + runshfunc(prog, NULL, fstack.name); - if (newexitfn) { - dotrapargs(SIGEXIT, &newexittr, newexitfn); - freestruct(newexitfn); + prog->flags &= ~EF_RUN; + + if (!(shf = (Shfunc) shfunctab->getnode(shfunctab, + (name = fname)))) { + zerr("%s: function not defined by file", name, 0); + if (!noreturnval) + lastval = 1; + popheap(); + return; } + prog = shf->funcdef; + } + runshfunc(prog, wrappers, fstack.name); + funcstack = fstack.prev; + if (retflag) { + retflag = 0; + breaks = obreaks; + } + freearray(pparams); + if (oargv0) { + zsfree(argzero); + argzero = oargv0; + } + pparams = tab; + optcind = oldoptcind; + zoptind = oldzoptind; + if (oldscriptname) + scriptname = oldscriptname; + + if (isset(LOCALOPTIONS)) { + /* restore all shell options except PRIVILEGED and RESTRICTED */ + saveopts[PRIVILEGED] = opts[PRIVILEGED]; + saveopts[RESTRICTED] = opts[RESTRICTED]; + memcpy(opts, saveopts, sizeof(opts)); + } else { + /* just restore a couple. */ + opts[XTRACE] = saveopts[XTRACE]; + opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE]; + opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS]; + } - if (trapreturn < -1) - trapreturn++; - if (noreturnval) - lastval = oldlastval; - popheap(); - } LASTALLOC; + endtrapscope(); + + if (trapreturn < -1) + trapreturn++; + if (noreturnval) + lastval = oldlastval; + popheap(); +} + +/* 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; + VARARR(char, ou, underscoreused); + + memcpy(ou, underscore, underscoreused); + + while (wrap) { + wrap->module->wrapper++; + cont = wrap->handler(prog, wrap->next, name); + wrap->module->wrapper--; + + if (!wrap->module->wrapper && + (wrap->module->flags & MOD_UNLOAD)) + unload_module(wrap->module, NULL); + + if (!cont) + return; + wrap = wrap->next; + } + startparamscope(); + execode(prog, 1, 0); + setunderscore(ou); + endparamscope(); } /* Search fpath for an undefined function. Finds the file, and returns the * * list of its contents. */ /**/ -static List -getfpfunc(char *s) +Eprog +getfpfunc(char *s, int *ksh) { char **pp, buf[PATH_MAX]; off_t len; char *d; - List r; + Eprog r; int fd; pp = fpath; @@ -2798,29 +3412,36 @@ getfpfunc(char *s) sprintf(buf, "%s/%s", *pp, s); else strcpy(buf, s); + if ((r = try_dump_file(*pp, s, buf, ksh))) + return r; unmetafy(buf, NULL); if (!access(buf, R_OK) && (fd = open(buf, O_RDONLY | O_NOCTTY)) != -1) { if ((len = lseek(fd, 0, 2)) != -1) { + d = (char *) zalloc(len + 1); lseek(fd, 0, 0); - d = (char *) zcalloc(len + 1); if (read(fd, d, len) == len) { + char *oldscriptname = scriptname; + close(fd); + d[len] = '\0'; d = metafy(d, len, META_REALLOC); - HEAPALLOC { - r = parse_string(d); - } LASTALLOC; + + scriptname = dupstring(s); + r = parse_string(d, 1); + scriptname = oldscriptname; + zfree(d, len + 1); + return r; - } else { - zfree(d, len + 1); + } else close(fd); - } - } else { + + zfree(d, len + 1); + } else close(fd); - } } } - return &dummy_list; + return &dummy_eprog; } /* Handle the most common type of ksh-style autoloading, when doing a * @@ -2830,29 +3451,60 @@ getfpfunc(char *s) * contents of that definition. Otherwise, use the entire file. */ /**/ -static List -stripkshdef(List l, char *name) +Eprog +stripkshdef(Eprog prog, char *name) { - Sublist s; - Pline p; - Cmd c; - if(!l) + Wordcode pc = prog->prog; + wordcode code; + + if (!prog) return NULL; - if(l->type != Z_SYNC || l->right) - return l; - s = l->left; - if(s->flags || s->right) - return l; - p = s->left; - if(p->right) - return l; - c = p->left; - if(c->type != FUNCDEF || c->flags || - nonempty(c->redir) || nonempty(c->vars) || - empty(c->args) || lastnode(c->args) != firstnode(c->args) || - strcmp(name, peekfirst(c->args))) - return l; - return c->u.list; + 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 || strcmp(name, ecrawstr(prog, pc + 1, NULL))) + 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 */ @@ -2901,9 +3553,26 @@ static int cancd2(char *s) { struct stat buf; - char *us = unmeta(s); + char *us, *us2 = NULL; + int ret; - return !(access(us, X_OK) || stat(us, &buf) || !S_ISDIR(buf.st_mode)); + /* + * 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; } /**/ @@ -2928,8 +3597,7 @@ execsave(void) es->trapreturn = trapreturn; es->noerrs = noerrs; es->subsh_close = subsh_close; - es->underscore = underscore; - underscore = ztrdup(underscore); + es->underscore = ztrdup(underscore); es->next = exstack; exstack = es; noerrs = cmdoutpid = 0; @@ -2957,8 +3625,8 @@ execrestore(void) trapreturn = exstack->trapreturn; noerrs = exstack->noerrs; subsh_close = exstack->subsh_close; - zsfree(underscore); - underscore = exstack->underscore; + setunderscore(exstack->underscore); + zsfree(exstack->underscore); en = exstack->next; free(exstack); exstack = en; diff --git a/Src/hashtable.c b/Src/hashtable.c index 4adf3904d..7a881e9f8 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -77,13 +77,13 @@ static HashTable firstht, lastht; /* Generic hash function */ /**/ -unsigned +mod_export unsigned hasher(char *str) { - unsigned hashval = 0; + unsigned hashval = 0, c; - while (*str) - hashval += (hashval << 5) + ((unsigned) *str++); + while ((c = *((unsigned char *) str++))) + hashval += (hashval << 5) + c; return hashval; } @@ -91,7 +91,7 @@ hasher(char *str) /* Get a new hash table */ /**/ -HashTable +mod_export HashTable newhashtable(int size, char const *name, PrintTableStats printinfo) { HashTable ht; @@ -112,6 +112,7 @@ newhashtable(int size, char const *name, PrintTableStats printinfo) ht->hsize = size; ht->ct = 0; ht->scan = NULL; + ht->scantab = NULL; return ht; } @@ -119,7 +120,7 @@ newhashtable(int size, char const *name, PrintTableStats printinfo) * existing pointers to the hash table are invalid. */ /**/ -void +mod_export void deletehashtable(HashTable ht) { ht->emptytable(ht); @@ -132,13 +133,14 @@ deletehashtable(HashTable ht) 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. dat is a pointer * + * 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 * @@ -146,9 +148,20 @@ deletehashtable(HashTable ht) * the table is then expanded. */ /**/ -void +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 replacment. */ + +/**/ +HashNode +addhashnode2(HashTable ht, char *nam, void *nodeptr) +{ unsigned hashval; HashNode hn, hp, hq; @@ -164,15 +177,15 @@ addhashnode(HashTable ht, char *nam, void *nodeptr) ht->nodes[hashval] = hn; if (++ht->ct >= ht->hsize * 2 && !ht->scan) expandhashtable(ht); - return; + return NULL; } /* else check if the first node contains the same key */ - if (!strcmp(hp->nam, hn->nam)) { + if (ht->cmpnodes(hp->nam, hn->nam) == 0) { ht->nodes[hashval] = hn; replacing: hn->next = hp->next; - if(ht->scan) + if(ht->scan) { if(ht->scan->sorted) { HashNode *tab = ht->scan->u.s.tab; int i; @@ -181,15 +194,15 @@ addhashnode(HashTable ht, char *nam, void *nodeptr) tab[i] = hn; } else if(ht->scan->u.u == hp) ht->scan->u.u = hn; - ht->freenode(hp); - return; + } + 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 (!strcmp(hp->nam, hn->nam)) { + if (ht->cmpnodes(hp->nam, hn->nam) == 0) { hq->next = hn; goto replacing; } @@ -200,6 +213,7 @@ addhashnode(HashTable ht, char *nam, void *nodeptr) ht->nodes[hashval] = hn; if (++ht->ct >= ht->hsize * 2 && !ht->scan) expandhashtable(ht); + return NULL; } /* Get an enabled entry in a hash table. * @@ -208,7 +222,7 @@ addhashnode(HashTable ht, char *nam, void *nodeptr) * or isn't found, it returns NULL */ /**/ -HashNode +mod_export HashNode gethashnode(HashTable ht, char *nam) { unsigned hashval; @@ -216,7 +230,7 @@ gethashnode(HashTable ht, char *nam) hashval = ht->hash(nam) % ht->hsize; for (hp = ht->nodes[hashval]; hp; hp = hp->next) { - if (!strcmp(hp->nam, nam)) { + if (ht->cmpnodes(hp->nam, nam) == 0) { if (hp->flags & DISABLED) return NULL; else @@ -232,7 +246,7 @@ gethashnode(HashTable ht, char *nam) * it returns NULL. */ /**/ -HashNode +mod_export HashNode gethashnode2(HashTable ht, char *nam) { unsigned hashval; @@ -240,7 +254,7 @@ gethashnode2(HashTable ht, char *nam) hashval = ht->hash(nam) % ht->hsize; for (hp = ht->nodes[hashval]; hp; hp = hp->next) { - if (!strcmp(hp->nam, nam)) + if (ht->cmpnodes(hp->nam, nam) == 0) return hp; } return NULL; @@ -252,7 +266,7 @@ gethashnode2(HashTable ht, char *nam) * is no such node, then it returns NULL */ /**/ -HashNode +mod_export HashNode removehashnode(HashTable ht, char *nam) { unsigned hashval; @@ -266,11 +280,11 @@ removehashnode(HashTable ht, char *nam) return NULL; /* else check if the key in the first one matches */ - if (!strcmp(hp->nam, nam)) { + if (ht->cmpnodes(hp->nam, nam) == 0) { ht->nodes[hashval] = hp->next; gotit: ht->ct--; - if(ht->scan) + if(ht->scan) { if(ht->scan->sorted) { HashNode *tab = ht->scan->u.s.tab; int i; @@ -279,6 +293,7 @@ removehashnode(HashTable ht, char *nam) tab[i] = NULL; } else if(ht->scan->u.u == hp) ht->scan->u.u = hp->next; + } return hp; } @@ -286,7 +301,7 @@ removehashnode(HashTable ht, char *nam) hq = hp; hp = hp->next; for (; hp; hq = hp, hp = hp->next) { - if (!strcmp(hp->nam, nam)) { + if (ht->cmpnodes(hp->nam, nam) == 0) { hq->next = hp->next; goto gotit; } @@ -314,7 +329,7 @@ enablehashnode(HashNode hn, int flags) hn->flags &= ~DISABLED; } -/* Compare two hash table entries */ +/* Compare two hash table entries by name */ /**/ static int @@ -343,11 +358,15 @@ hnamcmp(const void *ap, const void *bp) */ /**/ -void +mod_export void scanhashtable(HashTable ht, int sorted, int flags1, int flags2, ScanFunc scanfunc, int scanflags) { struct scanstatus st; + if (ht->scantab) { + ht->scantab(ht, scanfunc, scanflags); + return; + } if (sorted) { int i, ct = ht->ct; VARARR(HashNode, hnsorttab, ct); @@ -399,7 +418,7 @@ scanhashtable(HashTable ht, int sorted, int flags1, int flags2, ScanFunc scanfun /**/ int -scanmatchtable(HashTable ht, Comp com, int flags1, int flags2, ScanFunc scanfunc, int scanflags) +scanmatchtable(HashTable ht, Patprog pprog, int flags1, int flags2, ScanFunc scanfunc, int scanflags) { int i, hsize = ht->hsize; HashNode *nodes = ht->nodes; @@ -414,9 +433,10 @@ scanmatchtable(HashTable ht, Comp com, int flags1, int flags2, ScanFunc scanfunc HashNode hn = st.u.u; st.u.u = st.u.u->next; if ((hn->flags & flags1) + !flags1 && !(hn->flags & flags2) && - domatch(hn->nam, com, 0)) + pattry(pprog, hn->nam)) { scanfunc(hn, scanflags); match++; + } } ht->scan = NULL; @@ -489,12 +509,13 @@ resizehashtable(HashTable ht, int newsize) /* Generic method to empty a hash table */ /**/ -void +mod_export void emptyhashtable(HashTable ht) { resizehashtable(ht, ht->hsize); } +/**/ #ifdef ZSH_HASH_DEBUG /* Print info about hash table */ @@ -547,6 +568,7 @@ bin_hashinfo(char *nam, char **args, char *ops, int func) return 0; } +/**/ #endif /* ZSH_HASH_DEBUG */ /********************************/ @@ -556,12 +578,12 @@ bin_hashinfo(char *nam, char **args, char *ops, int func) /* hash table containing external commands */ /**/ -HashTable cmdnamtab; +mod_export HashTable cmdnamtab; /* how far we've hashed the PATH so far */ /**/ -char **pathchecked; +mod_export char **pathchecked; /* Create a new command hash table */ @@ -574,6 +596,7 @@ createcmdnamtable(void) cmdnamtab->hash = hasher; cmdnamtab->emptytable = emptycmdnamtable; cmdnamtab->filltable = fillcmdnamtable; + cmdnamtab->cmpnodes = strcmp; cmdnamtab->addnode = addhashnode; cmdnamtab->getnode = gethashnode2; cmdnamtab->getnode2 = gethashnode2; @@ -604,6 +627,9 @@ hashdir(char **dirp) Cmdnam cn; DIR *dir; char *fn; +#ifdef _WIN32 + char *exe; +#endif if (isrelative(*dirp) || !(dir = opendir(unmeta(*dirp)))) return; @@ -615,6 +641,23 @@ hashdir(char **dirp) cn->u.name = dirp; cmdnamtab->addnode(cmdnamtab, ztrdup(fn), cn); } +#ifdef _WIN32 + /* 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) zcalloc(sizeof *cn); + cn->flags = 0; + cn->u.name = dirp; + cmdnamtab->addnode(cmdnamtab, ztrdup(fn), cn); + } + } +#endif /* _WIN32 */ } closedir(dir); } @@ -713,7 +756,7 @@ printcmdnamnode(HashNode hn, int printflags) /* hash table containing the shell functions */ /**/ -HashTable shfunctab; +mod_export HashTable shfunctab; /**/ void @@ -724,6 +767,7 @@ createshfunctable(void) shfunctab->hash = hasher; shfunctab->emptytable = NULL; shfunctab->filltable = NULL; + shfunctab->cmpnodes = strcmp; shfunctab->addnode = addhashnode; shfunctab->getnode = gethashnode; shfunctab->getnode2 = gethashnode2; @@ -798,7 +842,7 @@ freeshfuncnode(HashNode hn) zsfree(shf->nam); if (shf->funcdef) - freestruct(shf->funcdef); + freeeprog(shf->funcdef); zfree(shf, sizeof(struct shfunc)); } @@ -828,21 +872,34 @@ printshfuncnode(HashNode hn, int printflags) } if (f->flags & PM_UNDEFINED) - printf("undefined "); - if (f->flags & PM_TAGGED) - printf("traced "); - if ((f->flags & PM_UNDEFINED) || !f->funcdef) { - nicezputs(f->nam, stdout); - printf(" () { }\n"); - return; + t = tricat("builtin autoload -X", + ((f->flags & PM_UNALIASED)? "U" : ""), + ((f->flags & PM_TAGGED)? "t" : "")); + else { + if (!f->funcdef) + t = 0; + else + t = getpermtext(f->funcdef, NULL); } - - t = getpermtext((void *) dupstruct((void *) f->funcdef)); + quotedzputs(f->nam, stdout); - printf(" () {\n\t"); - zputs(t, stdout); - printf("\n}\n"); - zsfree(t); + if (t) { + printf(" () {\n\t"); + if (f->flags & PM_UNDEFINED) + printf("%c undefined\n\t", hashchar); + if (f->flags & PM_TAGGED) + printf("%c traced\n\t", hashchar); + zputs(t, stdout); + if (f->funcdef && (f->funcdef->flags & EF_RUN)) { + printf("\n\t"); + quotedzputs(f->nam, stdout); + printf(" \"$@\""); + } + printf("\n}\n"); + zsfree(t); + } else { + printf(" () { }\n"); + } } /**************************************/ @@ -882,7 +939,7 @@ static struct reswd reswds[] = { /* hash table containing the reserved words */ /**/ -HashTable reswdtab; +mod_export HashTable reswdtab; /* Build the hash table containing zsh's reserved words. */ @@ -897,6 +954,7 @@ createreswdtable(void) reswdtab->hash = hasher; reswdtab->emptytable = NULL; reswdtab->filltable = NULL; + reswdtab->cmpnodes = strcmp; reswdtab->addnode = addhashnode; reswdtab->getnode = gethashnode; reswdtab->getnode2 = gethashnode2; @@ -944,7 +1002,7 @@ printreswdnode(HashNode hn, int printflags) /* hash table containing the aliases */ /**/ -HashTable aliastab; +mod_export HashTable aliastab; /* Create new hash table for aliases */ @@ -957,6 +1015,7 @@ createaliastable(void) aliastab->hash = hasher; aliastab->emptytable = NULL; aliastab->filltable = NULL; + aliastab->cmpnodes = strcmp; aliastab->addnode = addhashnode; aliastab->getnode = gethashnode; aliastab->getnode2 = gethashnode2; @@ -974,7 +1033,7 @@ createaliastable(void) /* Create a new alias node */ /**/ -Alias +mod_export Alias createaliasnode(char *txt, int flags) { Alias al; @@ -1061,101 +1120,25 @@ printaliasnode(HashNode hn, int printflags) putchar('\n'); } -/**********************************/ -/* Parameter Hash Table Functions */ -/**********************************/ - -/**/ -void -freeparamnode(HashNode hn) -{ - Param pm = (Param) hn; - - zsfree(pm->nam); - zfree(pm, sizeof(struct param)); -} - -/* Print a parameter */ - -/**/ -void -printparamnode(HashNode hn, int printflags) -{ - Param p = (Param) hn; - char *t, **u; - - if (p->flags & PM_UNSET) - return; - - /* Print the attributes of the parameter */ - if (printflags & PRINT_TYPE) { - if (p->flags & PM_INTEGER) - printf("integer "); - if (p->flags & PM_ARRAY) - printf("array "); - if (p->flags & PM_LEFT) - printf("left justified %d ", p->ct); - if (p->flags & PM_RIGHT_B) - printf("right justified %d ", p->ct); - if (p->flags & PM_RIGHT_Z) - printf("zero filled %d ", p->ct); - if (p->flags & PM_LOWER) - printf("lowercase "); - if (p->flags & PM_UPPER) - printf("uppercase "); - if (p->flags & PM_READONLY) - printf("readonly "); - if (p->flags & PM_TAGGED) - printf("tagged "); - if (p->flags & PM_EXPORTED) - printf("exported "); - } - - if (printflags & PRINT_NAMEONLY) { - zputs(p->nam, stdout); - putchar('\n'); - return; - } - - /* How the value is displayed depends * - * on the type of the parameter */ - quotedzputs(p->nam, stdout); - putchar('='); - switch (PM_TYPE(p->flags)) { - case PM_SCALAR: - /* string: simple output */ - if (p->gets.cfn && (t = p->gets.cfn(p))) - quotedzputs(t, stdout); - putchar('\n'); - break; - case PM_INTEGER: - /* integer */ - printf("%ld\n", p->gets.ifn(p)); - break; - case PM_ARRAY: - /* array */ - putchar('('); - u = p->gets.afn(p); - if(*u) { - quotedzputs(*u++, stdout); - while (*u) { - putchar(' '); - quotedzputs(*u++, stdout); - } - } - printf(")\n"); - break; - } -} - /****************************************/ /* Named Directory Hash Table Functions */ /****************************************/ +#ifdef HAVE_NIS_PLUS +# include <rpcsvc/nis.h> +#else +# ifdef HAVE_NIS +# include <rpc/types.h> +# include <rpc/rpc.h> +# include <rpcsvc/ypclnt.h> +# include <rpcsvc/yp_prot.h> +# endif +#endif + /* hash table containing named directories */ /**/ -HashTable nameddirtab; +mod_export HashTable nameddirtab; /* != 0 if all the usernames have already been * * added to the named directory hash table. */ @@ -1173,6 +1156,7 @@ createnameddirtable(void) nameddirtab->hash = hasher; nameddirtab->emptytable = emptynameddirtable; nameddirtab->filltable = fillnameddirtable; + nameddirtab->cmpnodes = strcmp; nameddirtab->addnode = addnameddirnode; nameddirtab->getnode = gethashnode; nameddirtab->getnode2 = gethashnode2; @@ -1200,12 +1184,122 @@ emptynameddirtable(HashTable ht) /* Add all the usernames in the password file/database * * to the named directories table. */ +#ifdef HAVE_NIS_PLUS +static int +add_userdir(nis_name table, nis_object *object, void *userdata) +{ + if (object->zo_data.objdata_u.en_data.en_cols.en_cols >= 6) { + static char name[40], dir[PATH_MAX + 1]; + register entry_col *ec = + object->zo_data.objdata_u.en_data.en_cols.en_cols_val; + register int nl = minimum(ec[0].ec_value.ec_value_len, 39); + register int dl = minimum(ec[5].ec_value.ec_value_len, PATH_MAX); + + memcpy(name, ec[0].ec_value.ec_value_val, nl); + name[nl] = '\0'; + memcpy(dir, ec[5].ec_value.ec_value_val, dl); + dir[dl] = '\0'; + + adduserdir(name, dir, ND_USERNAME, 1); + } + return 0; +} +#else +# ifdef HAVE_NIS +static int +add_userdir(int status, char *key, int keylen, char *val, int vallen, char *dummy) +{ + char *p, *d, *de; + + if (status != YP_TRUE) + return 1; + + if (vallen > keylen && *(p = val + keylen) == ':') { + *p++ = '\0'; + if ((de = strrchr(p, ':'))) { + *de = '\0'; + if ((d = strrchr(p, ':'))) { + if (*++d && val[0]) + adduserdir(val, d, ND_USERNAME, 1); + } + } + } + return 0; +} +# endif /* HAVE_NIS */ +#endif /* HAVE_NIS_PLUS */ + /**/ static void fillnameddirtable(HashTable ht) { -#ifdef HAVE_GETPWENT if (!allusersadded) { +#if defined(HAVE_NIS) || defined(HAVE_NIS_PLUS) + FILE *pwf; + char buf[BUFSIZ], *p, *d, *de; + int skipping, oldct = nameddirtab->ct, usepwf = 1; + +# ifndef HAVE_NIS_PLUS + char domain[YPMAXDOMAIN]; + struct ypall_callback cb; + + /* Get potential matches from NIS and cull those without local accounts */ + if (getdomainname(domain, YPMAXDOMAIN) == 0) { + cb.foreach = (int (*)()) add_userdir; + cb.data = NULL; + yp_all(domain, PASSWD_MAP, &cb); + } +# else /* HAVE_NIS_PLUS */ + /* Maybe we should turn this string into a #define'd constant...? */ + + nis_list("passwd.org_dir", EXPAND_NAME|ALL_RESULTS|FOLLOW_LINKS|FOLLOW_PATH, + add_userdir, 0); +# endif + if (nameddirtab->ct == oldct) { + /* Using NIS or NIS+ didn't add any user directories. This seems + * fishy, so we fall back to using getpwent(). If we don't have + * that, we only use the passwd file. */ +#ifdef HAVE_GETPWENT + struct passwd *pw; + + setpwent(); + + /* loop through the password file/database * + * and add all entries returned. */ + while ((pw = getpwent()) && !errflag) + adduserdir(pw->pw_name, pw->pw_dir, ND_USERNAME, 1); + + endpwent(); + usepwf = 0; +#endif /* HAVE_GETPWENT */ + } + if (usepwf) { + /* Don't forget the non-NIS matches from the flat passwd file */ + if ((pwf = fopen(PASSWD_FILE, "r")) != NULL) { + skipping = 0; + while (fgets(buf, BUFSIZ, pwf) != NULL) { + if (strchr(buf, '\n') != NULL) { + if (!skipping) { + if ((p = strchr(buf, ':')) != NULL) { + *p++ = '\0'; + if ((de = strrchr(p, ':'))) { + *de = '\0'; + if ((d = strrchr(p, ':'))) { + if (*++d && buf[0]) + adduserdir(buf, d, ND_USERNAME, 1); + } + } + } + } else + skipping = 0; + } else + skipping = 1; + } + fclose(pwf); + } + } +#else /* no NIS or NIS_PLUS */ +#ifdef HAVE_GETPWENT struct passwd *pw; setpwent(); @@ -1213,13 +1307,13 @@ fillnameddirtable(HashTable ht) /* loop through the password file/database * * and add all entries returned. */ while ((pw = getpwent()) && !errflag) - adduserdir(ztrdup(pw->pw_name), pw->pw_dir, ND_USERNAME, 1); + adduserdir(pw->pw_name, pw->pw_dir, ND_USERNAME, 1); endpwent(); +#endif /* HAVE_GETPWENT */ +#endif allusersadded = 1; } - return; -#endif /* HAVE_GETPWENT */ } /* Add an entry to the named directory hash * @@ -1283,3 +1377,137 @@ printnameddirnode(HashNode hn, int printflags) quotedzputs(nd->dir, 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(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->flags & HIST_MAKEUNIQUE + || (he->flags & HIST_FOREIGN && (Histent)oldnode == he->up)) { + he->flags |= HIST_DUP; + addhashnode(ht, oldnode->nam, oldnode); /* Remove the new dup */ + } + else { + oldnode->flags |= HIST_DUP; + if (hist_ignore_all_dups) + freehistnode(oldnode); /* Remove the old dup */ + } + } + else + he->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->flags & HIST_DUP)) + removehashnode(histtab, he->text); + + zsfree(he->text); + 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; + } + } +} diff --git a/Src/jobs.c b/Src/jobs.c index 331902d9f..962b0ded0 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -33,12 +33,12 @@ /* the process group of the shell */ /**/ -pid_t mypgrp; +mod_export pid_t mypgrp; /* the job we are working on */ /**/ -int thisjob; +mod_export int thisjob; /* the current job (+) */ @@ -53,8 +53,8 @@ int prevjob; /* the job table */ /**/ -struct job jobtab[MAXJOB]; - +mod_export struct job jobtab[MAXJOB]; + /* shell timings */ /**/ @@ -64,13 +64,18 @@ struct tms shtms; /**/ int ttyfrozen; - -/* empty job structure for quick clearing of jobtab entries */ -static struct job zero; /* static variables are initialized to zero */ +/* 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; static struct timeval dtimeval, now; +/**/ +int numpipestats, pipestats[MAX_PIPESTATS]; + /* Diff two timevals for elapsed-time computations */ /**/ @@ -96,9 +101,13 @@ makerunning(Job jn) 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); @@ -125,6 +134,86 @@ findproc(pid_t pid, Job *jptr, Process *pptr) return 0; } +/* 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 jn = jobtab + job, sj = jobtab + jn->other; + + if ((sj->stat & STAT_DONE) || !sj->procs) { + 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); */ + /* 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); + } + 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; +} + /* Update status of process that we have just WAIT'ed for */ /**/ @@ -175,16 +264,31 @@ update_job(Job jn) jn->ty = (struct ttyinfo *) zalloc(sizeof(struct ttyinfo)); gettyinfo(jn->ty); } - if (jn->stat & STAT_STOPPED) + 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; - } else { /* job is done, so remember return value */ + } + } + { /* 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 + * and let it handle its own traps, but always allow the test + * for the pgrp. */ - if (job == thisjob && !(jn->stat & STAT_CURSH)) { - lastval = val; - inforeground = 1; + if (jn->stat & STAT_CURSH) + inforeground = 1; + else if (job == thisjob) { + lastval = val; + inforeground = 2; } } @@ -198,15 +302,72 @@ update_job(Job jn) /* is this job in the foreground of an interactive shell? */ if (mypgrp != pgrp && inforeground && (jn->gleader == pgrp || (pgrp > 1 && kill(-pgrp, 0) == -1))) { - attachtty(mypgrp); - adjustwinsize(); /* check window size and adjust if necessary */ + 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) { + if (!errbrk_saved) { + errbrk_saved = 1; + prev_breaks = breaks; + prev_errflag = errflag; + } + breaks = loops; + errflag = 1; + inerrflush(); + } + } else { + attachtty(mypgrp); + /* check window size and adjust if necessary */ + adjustwinsize(0); + } } + } else if (list_pipe && (val & 0200) && inforeground == 1) { + if (!errbrk_saved) { + errbrk_saved = 1; + prev_breaks = breaks; + prev_errflag = errflag; + } + breaks = loops; + errflag = 1; + inerrflush(); } - if (somestopped && jn->stat & STAT_SUPERJOB) return; jn->stat |= (somestopped) ? STAT_CHANGED | STAT_STOPPED : STAT_CHANGED | STAT_DONE; + if (job == thisjob && (jn->stat & STAT_DONE)) { + int i; + Process p; + + for (p = jn->procs, i = 0; p && i < MAX_PIPESTATS; p = p->next, i++) + pipestats[i] = ((WIFSIGNALED(p->status)) ? + 0200 | WTERMSIG(p->status) : + WEXITSTATUS(p->status)); + if ((jn->stat & STAT_CURSH) && i < MAX_PIPESTATS) + pipestats[i++] = lastval; + numpipestats = i; + } + 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; @@ -214,7 +375,7 @@ update_job(Job jn) if ((isset(NOTIFY) || job == thisjob) && (jn->stat & STAT_LOCKED)) { printjob(jn, !!isset(LONGLISTJOBS), 0); if (zleactive) - refresh(); + zrefresh(); } if (sigtrapped[SIGCHLD] && job != thisjob) dotrap(SIGCHLD); @@ -223,7 +384,7 @@ update_job(Job jn) * process group from the shell, so the shell will not receive * * terminal signals, therefore we we pretend that the shell got * * the signal too. */ - if (inforeground && isset(MONITOR) && WIFSIGNALED(status)) { + if (inforeground == 2 && isset(MONITOR) && WIFSIGNALED(status)) { int sig = WTERMSIG(status); if (sig == SIGINT || sig == SIGQUIT) { @@ -256,13 +417,14 @@ setprevjob(void) for (i = MAXJOB - 1; i; i--) if ((jobtab[i].stat & STAT_INUSE) && (jobtab[i].stat & STAT_STOPPED) && - i != curjob && i != thisjob) { + !(jobtab[i].stat & STAT_SUBJOB) && i != curjob && i != thisjob) { prevjob = i; return; } for (i = MAXJOB - 1; i; i--) - if ((jobtab[i].stat & STAT_INUSE) && i != curjob && i != thisjob) { + if ((jobtab[i].stat & STAT_INUSE) && !(jobtab[i].stat & STAT_SUBJOB) && + i != curjob && i != thisjob) { prevjob = i; return; } @@ -409,6 +571,7 @@ dumptime(Job jn) static int should_report_time(Job j) { + struct value vbuf; Value v; char *s = "REPORTTIME"; int reporttime; @@ -417,9 +580,10 @@ should_report_time(Job j) if (j->stat & STAT_TIMED) return 1; - if (!(v = getvalue(&s, 0)) || (reporttime = getintvalue(v)) < 0) + if (!(v = getvalue(&vbuf, &s, 0)) || + (reporttime = getintvalue(v)) < 0) { return 0; - + } /* can this ever happen? */ if (!j->procs) return 0; @@ -462,10 +626,10 @@ printjob(Job jn, int lng, int synch) if (jn->stat & STAT_SUPERJOB && jn->procs->status == SP_RUNNING && !pn->next) pn->status = SP_RUNNING; - if (pn->status != SP_RUNNING) + if (pn->status != SP_RUNNING) { if (WIFSIGNALED(pn->status)) { sig = WTERMSIG(pn->status); - llen = strlen(sigmsg[sig]); + llen = strlen(sigmsg(sig)); if (WCOREDUMP(pn->status)) llen += 14; if (llen > len) @@ -476,13 +640,14 @@ printjob(Job jn, int lng, int synch) doputnl = 1; } else if (WIFSTOPPED(pn->status)) { sig = WSTOPSIG(pn->status); - if ((int)strlen(sigmsg[sig]) > len) - len = strlen(sigmsg[sig]); + 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; + } } /* print if necessary */ @@ -508,7 +673,7 @@ printjob(Job jn, int lng, int synch) break; len2 += strlen(qn->text) + 2; } - if (job != thisjob) + if (job != thisjob) { if (fline) fprintf(fout, "[%ld] %c ", (long)(jn - jobtab), @@ -516,7 +681,7 @@ printjob(Job jn, int lng, int synch) : (job == prevjob) ? '-' : ' '); else fprintf(fout, (job > 9) ? " " : " "); - else + } else fprintf(fout, "zsh: "); if (lng & 1) fprintf(fout, "%ld ", (long) pn->pid); @@ -531,25 +696,26 @@ printjob(Job jn, int lng, int synch) lng &= ~3; } else fprintf(fout, "%*s", skip, ""); - if (pn->status == SP_RUNNING) + 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)) + } + 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 (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)])), ""); + sigmsg(WTERMSIG(pn->status)), + (int)(len - 14 + 2 - strlen(sigmsg(WTERMSIG(pn->status)))), ""); else - fprintf(fout, "%-*s", len + 2, sigmsg[WTERMSIG(pn->status)]); + fprintf(fout, "%-*s", len + 2, sigmsg(WTERMSIG(pn->status))); for (; pn != qn; pn = pn->next) fprintf(fout, (pn->next) ? "%s | " : "%s", pn->text); putc('\n', fout); @@ -565,7 +731,8 @@ printjob(Job jn, int lng, int synch) * the directory where the job is running, otherwise the current directory */ - if ((lng & 4) || (interact && job == thisjob && strcmp(jn->pwd, pwd))) { + if ((lng & 4) || (interact && job == thisjob && + jn->pwd && strcmp(jn->pwd, pwd))) { fprintf(shout, "(pwd %s: ", (lng & 4) ? "" : "now"); fprintdir((lng & 4) ? jn->pwd : pwd, shout); fprintf(shout, ")\n"); @@ -607,20 +774,31 @@ deletejob(Job jn) { struct process *pn, *nx; + if (jn->stat & STAT_ATTACH) { + attachtty(mypgrp); + adjustwinsize(0); + } + pn = jn->procs; jn->procs = NULL; for (; pn; pn = nx) { nx = pn->next; zfree(pn, sizeof(struct process)); } - zsfree(jn->pwd); - deletefilelist(jn->filelist); if (jn->ty) zfree(jn->ty, sizeof(struct ttyinfo)); - - *jn = zero; + if (jn->pwd) + zsfree(jn->pwd); + jn->pwd = NULL; + if (jn->stat & STAT_WASSUPER) + deletejob(jobtab + jn->other); + jn->gleader = jn->other = 0; + jn->stat = jn->stty_in_env = 0; + jn->procs = NULL; + jn->filelist = NULL; + jn->ty = NULL; } /* add a process to the current job */ @@ -729,40 +907,20 @@ waitjob(int job, int sig) what this might be. --oberon errflag = 0; */ - if (jn->stat & STAT_SUPERJOB) { - Job sj = jobtab + jn->other; - if (sj->stat & STAT_DONE) { - struct process *p; - - for (p = sj->procs; p; p = p->next) - if (WIFSIGNALED(p->status)) { - killpg(jn->gleader, WTERMSIG(p->status)); - kill(sj->other, SIGCONT); - kill(sj->other, WTERMSIG(p->status)); - break; - } - if (!p) { - jn->stat &= ~STAT_SUPERJOB; - kill(sj->other, SIGCONT); - deletejob(sj); - } - curjob = jn - jobtab; - } - else if (sj->stat & STAT_STOPPED) { - struct process *p; - - jn->stat |= STAT_STOPPED; - for (p = jn->procs; p; p = p->next) - p->status = sj->procs->status; - curjob = jn - jobtab; - printjob(jn, !!isset(LONGLISTJOBS), 1); - break; - } + if (subsh) { + killjb(jn, SIGCONT); + jn->stat &= ~STAT_STOPPED; } + if (jn->stat & STAT_SUPERJOB) + if (handle_sub(jn - jobtab, 1)) + break; child_block(); } - } else + } else { deletejob(jn); + pipestats[0] = lastval; + numpipestats = 1; + } child_unblock(); } @@ -772,24 +930,29 @@ waitjob(int job, int sig) void waitjobs(void) { - waitjob(thisjob, 0); + Job jn = jobtab + thisjob; + + if (jn->procs) + waitjob(thisjob, 0); + else { + deletejob(jn); + pipestats[0] = lastval; + numpipestats = 1; + } thisjob = -1; } /* clear job table when entering subshells */ /**/ -void +mod_export void clearjobtab(void) { int i; - for (i = 1; i < MAXJOB; i++) { - if (jobtab[i].pwd) - zsfree(jobtab[i].pwd); + for (i = 1; i < MAXJOB; i++) if (jobtab[i].ty) zfree(jobtab[i].ty, sizeof(struct ttyinfo)); - } memset(jobtab, 0, sizeof(jobtab)); /* zero out table */ } @@ -805,7 +968,8 @@ initjob(void) for (i = 1; i < MAXJOB; i++) if (!jobtab[i].stat) { jobtab[i].stat = STAT_INUSE; - jobtab[i].pwd = ztrdup(pwd); + if (jobtab[i].pwd) + zsfree(jobtab[i].pwd); jobtab[i].gleader = 0; return i; } @@ -814,6 +978,21 @@ initjob(void) return -1; } +/**/ +void +setjobpwd(void) +{ + int i, l; + + for (i = 1; i < MAXJOB; i++) + if (jobtab[i].stat && !jobtab[i].pwd) { + if ((l = strlen(pwd)) >= PATH_MAX) + jobtab[i].pwd = ztrdup(pwd + l - PATH_MAX); + else + jobtab[i].pwd = ztrdup(pwd); + } +} + /* print pids for & */ /**/ @@ -1071,7 +1250,7 @@ bin_fg(char *name, char **argv, char *ops, int func) /* If you immediately type "exit" after "jobs", this * * will prevent zexit from complaining about stopped jobs */ stopmsg = 2; - if (!*argv) + 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. */ @@ -1100,6 +1279,7 @@ bin_fg(char *name, char **argv, char *ops, int func) waitjob(job, SIGINT); return 0; } + } /* 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 @@ -1157,7 +1337,7 @@ bin_fg(char *name, char **argv, char *ops, int func) /* for bg and fg -- show the job we are operating on */ printjob(jobtab + job, (stopped) ? -1 : 0, 1); if (func != BIN_BG) { /* fg or wait */ - if (strcmp(jobtab[job].pwd, pwd)) { + if (jobtab[job].pwd && strcmp(jobtab[job].pwd, pwd)) { fprintf(shout, "(pwd : "); fprintdir(jobtab[job].pwd, shout); fprintf(shout, ")\n"); @@ -1165,7 +1345,14 @@ bin_fg(char *name, char **argv, char *ops, int func) fflush(shout); if (func != BIN_WAIT) { /* fg */ thisjob = job; - attachtty(jobtab[job].gleader); + 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) { diff --git a/Src/loop.c b/Src/loop.c index 5fbf2b841..d0280207a 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -43,47 +43,79 @@ int contflag; /* # of break levels */ /**/ -int breaks; - +mod_export int breaks; + /**/ int -execfor(Cmd cmd) +execfor(Estate state, int do_exec) { - List list; - Forcmd node; - char *str; - int val; - LinkList args; + Wordcode end, loop; + wordcode code = state->pc[-1]; + int iscond = (WC_FOR_TYPE(code) == WC_FOR_COND), ctok = 0, atok = 0; + char *name, *str, *cond = NULL, *advance = NULL; + zlong val = 0; + LinkList args = NULL; + + name = ecgetstr(state, EC_NODUP, NULL); + end = state->pc + WC_FOR_SKIP(code); - node = cmd->u.forcmd; - args = cmd->args; - if (node->condition) { - str = node->name; + if (iscond) { + str = dupstring(name); 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) + if (errflag) { + state->pc = end; return lastval = errflag; - } else if (!node->inflag) { + } + cond = ecgetstr(state, EC_NODUP, &ctok); + advance = ecgetstr(state, EC_NODUP, &atok); + } else if (WC_FOR_TYPE(code) == WC_FOR_LIST) { + int htok = 0; + + if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) { + state->pc = end; + return 0; + } + if (htok) + execsubst(args); + } else { char **x; args = newlinklist(); for (x = pparams; *x; x++) - addlinknode(args, ztrdup(*x)); + addlinknode(args, dupstring(*x)); } lastval = 0; loops++; pushheap(); + cmdpush(CS_FOR); + loop = state->pc; for (;;) { - if (node->condition) { - str = dupstring(node->condition); - singsub(&str); + if (iscond) { + if (ctok) { + str = dupstring(cond); + singsub(&str); + } else + str = cond; if (!errflag) { while (iblank(*str)) str++; - if (*str) - val = matheval(str); - else + if (*str) { + if (isset(XTRACE)) { + printprompt4(); + fprintf(xtrerr, "%s\n", str); + fflush(xtrerr); + } + val = mathevali(str); + } else val = 1; } if (errflag) { @@ -95,22 +127,36 @@ execfor(Cmd cmd) if (!val) break; } else { - str = (char *) ugetnode(args); - if (!str) + if (!args || !(str = (char *) ugetnode(args))) break; - setsparam(node->name, ztrdup(str)); + if (isset(XTRACE)) { + printprompt4(); + fprintf(xtrerr, "%s=%s\n", name, str); + fflush(xtrerr); + } + setsparam(name, ztrdup(str)); } - list = (List) dupstruct(node->list); - execlist(list, 1, (cmd->flags & CFLAG_EXEC) && empty(args)); + state->pc = loop; + execlist(state, 1, do_exec && args && empty(args)); if (breaks) { breaks--; if (breaks || !contflag) break; contflag = 0; } - if (node->condition && !errflag) { - str = dupstring(node->advance); - singsub(&str); + 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); } @@ -123,44 +169,67 @@ execfor(Cmd cmd) freeheap(); } popheap(); + cmdpop(); loops--; + state->pc = end; return lastval; } /**/ int -execselect(Cmd cmd) +execselect(Estate state, int do_exec) { - List list; - Forcmd node; - char *str, *s; - LinkList args; + Wordcode end, loop; + wordcode code = state->pc[-1]; + char *str, *s, *name; LinkNode n; - int i; + int i, usezle; FILE *inp; + size_t more; + LinkList args; + + end = state->pc + WC_FOR_SKIP(code); + name = ecgetstr(state, EC_NODUP, NULL); - node = cmd->u.forcmd; - args = cmd->args; - if (!node->inflag) { + if (WC_SELECT_TYPE(code) == WC_SELECT_PPARAM) { char **x; args = newlinklist(); for (x = pparams; *x; x++) - addlinknode(args, ztrdup(*x)); + addlinknode(args, dupstring(*x)); + } else { + int htok = 0; + + if (!(args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) { + state->pc = end; + return 0; + } + if (htok) + execsubst(args); } - if (empty(args)) + if (!args || empty(args)) { + state->pc = end; return 1; + } loops++; lastval = 0; pushheap(); - inp = fdopen(dup((SHTTY == -1) ? 0 : SHTTY), "r"); - selectlist(args); + 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 (interact && SHTTY != -1 && isset(USEZLE)) { + if (usezle) { + int oef = errflag; + isfirstln = 1; str = (char *)zleread(prompt3, NULL, 0); + if (errflag) + str = NULL; + errflag = oef; } else { str = promptexpand(prompt3, 0, NULL, NULL); zputs(str, stderr); @@ -181,7 +250,7 @@ execselect(Cmd cmd) *s = '\0'; if (*str) break; - selectlist(args); + more = selectlist(args, more); } setsparam("REPLY", ztrdup(str)); i = atoi(str); @@ -194,9 +263,9 @@ execselect(Cmd cmd) else str = ""; } - setsparam(node->name, ztrdup(str)); - list = (List) dupstruct(node->list); - execlist(list, 1, 0); + setsparam(name, ztrdup(str)); + state->pc = loop; + execlist(state, 1, 0); freeheap(); if (breaks) { breaks--; @@ -204,21 +273,23 @@ execselect(Cmd cmd) break; contflag = 0; } - if (errflag) + if (retflag || errflag) break; } done: + cmdpop(); popheap(); fclose(inp); loops--; + state->pc = end; return lastval; } /* And this is used to print select lists. */ /**/ -static void -selectlist(LinkList l) +size_t +selectlist(LinkList l, size_t start) { size_t longest = 1, fct, fw = 0, colsz, t0, t1, ct; LinkNode n; @@ -226,7 +297,7 @@ selectlist(LinkList l) trashzle(); ct = countlinknodes(l); - ap = arr = (char **)alloc((countlinknodes(l) + 1) * sizeof(char **)); + ap = arr = (char **) zhalloc((countlinknodes(l) + 1) * sizeof(char **)); for (n = (LinkNode) firstnode(l); n; incnode(n)) *ap++ = (char *)getdata(n); @@ -245,7 +316,7 @@ selectlist(LinkList l) else fw = (columns - 1) / fct; colsz = (ct + fct - 1) / fct; - for (t1 = 0; t1 != colsz; t1++) { + for (t1 = start; t1 != colsz && t1 - start < lines - 2; t1++) { ap = arr + t1; do { int t2 = strlen(*ap) + 2, t3; @@ -271,70 +342,86 @@ selectlist(LinkList l) } while (*ap);*/ fflush(stderr); + + return t1 < colsz ? t1 : 0; } /**/ int -execwhile(Cmd cmd) +execwhile(Estate state, int do_exec) { - List list; - struct whilecmd *node; - int olderrexit, oldval; + Wordcode end, loop; + wordcode code = state->pc[-1]; + int olderrexit, oldval, isuntil = (WC_WHILE_TYPE(code) == WC_WHILE_UNTIL); + end = state->pc + WC_WHILE_SKIP(code); olderrexit = noerrexit; - node = cmd->u.whilecmd; oldval = 0; pushheap(); + cmdpush(isuntil ? CS_UNTIL : CS_WHILE); loops++; + loop = state->pc; for (;;) { - list = (List) dupstruct(node->cont); + state->pc = loop; noerrexit = 1; - execlist(list, 1, 0); + execlist(state, 1, 0); noerrexit = olderrexit; - if (!((lastval == 0) ^ node->cond)) { + if (!((lastval == 0) ^ isuntil)) { if (breaks) breaks--; lastval = oldval; break; } - list = (List) dupstruct(node->loop); - execlist(list, 1, 0); + if (retflag) { + lastval = oldval; + break; + } + execlist(state, 1, 0); if (breaks) { breaks--; if (breaks || !contflag) break; contflag = 0; } - freeheap(); if (errflag) { lastval = 1; break; } + if (retflag) + break; + freeheap(); oldval = lastval; } + cmdpop(); popheap(); loops--; + state->pc = end; return lastval; } /**/ int -execrepeat(Cmd cmd) +execrepeat(Estate state, int do_exec) { - List list; - int count; + Wordcode end, loop; + wordcode code = state->pc[-1]; + int count, htok = 0; + char *tmp; + + end = state->pc + WC_REPEAT_SKIP(code); lastval = 0; - if (empty(cmd->args) || nextnode(firstnode(cmd->args))) { - zerr("bad argument for repeat", NULL, 0); - return 1; - } - count = atoi(peekfirst(cmd->args)); + tmp = ecgetstr(state, EC_DUPTOK, &htok); + if (htok) + singsub(&tmp); + count = atoi(tmp); pushheap(); + cmdpush(CS_REPEAT); loops++; - while (count--) { - list = (List) dupstruct(cmd->u.list); - execlist(list, 1, 0); + loop = state->pc; + while (count-- > 0) { + state->pc = loop; + execlist(state, 1, 0); freeheap(); if (breaks) { breaks--; @@ -346,76 +433,151 @@ execrepeat(Cmd cmd) lastval = 1; break; } + if (retflag) + break; } + cmdpop(); popheap(); loops--; + state->pc = end; return lastval; } /**/ int -execif(Cmd cmd) +execif(Estate state, int do_exec) { - struct ifcmd *node; - int olderrexit; - List *i, *t; + Wordcode end, next; + wordcode code = state->pc[-1]; + int olderrexit, s = 0, run = 0; olderrexit = noerrexit; - node = cmd->u.ifcmd; - i = node->ifls; - t = node->thenls; + end = state->pc + WC_IF_SKIP(code); if (!noerrexit) noerrexit = 1; - while (*i) { - execlist(*i, 1, 0); - if (!lastval) + 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; - i++; - t++; + } + 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; } noerrexit = olderrexit; - if (*t) - execlist(*t, 1, cmd->flags & CFLAG_EXEC); - else + if (run) { + cmdpush(run == 2 ? CS_ELSE : (s ? CS_ELIFTHEN : CS_IFTHEN)); + execlist(state, 1, do_exec); + cmdpop(); + } else lastval = 0; + state->pc = end; return lastval; } /**/ int -execcase(Cmd cmd) +execcase(Estate state, int do_exec) { - struct casecmd *node; - char *word; - List *l; - char **p; + Wordcode end, next; + wordcode code = state->pc[-1]; + char *word, *pat; + int npat, save; + Patprog *spprog, pprog; - node = cmd->u.casecmd; - l = node->lists; - p = node->pats; + end = state->pc + WC_CASE_SKIP(code); - word = *p++; + word = ecgetstr(state, EC_DUP, NULL); singsub(&word); untokenize(word); lastval = 0; - if (node) { - while (*p) { - char *pat = *p + 1; + cmdpush(CS_CASE); + while (state->pc < end) { + code = *state->pc++; + if (wc_code(code) != WC_CASE) + break; + + pat = NULL; + pprog = NULL; + save = 0; + npat = state->pc[1]; + spprog = state->prog->pats + npat; + + next = state->pc + WC_CASE_SKIP(code); + + if (isset(XTRACE)) { + char *pat2, *opat; + + opat = pat = ecgetstr(state, EC_DUP, NULL); singsub(&pat); - if (matchpat(word, pat)) { - do { - execlist(*l++, 1, **p == ';' && (cmd->flags & CFLAG_EXEC)); - } while(**p++ == '&' && *p); - break; + save = (!(state->prog->flags & EF_HEAP) && + !strcmp(pat, opat) && *spprog != dummy_patprog2); + + pat2 = dupstring(pat); + untokenize(pat2); + printprompt4(); + fprintf(xtrerr, "case %s (%s)\n", word, pat2); + fflush(xtrerr); + state->pc++; + } else + state->pc += 2; + + if (*spprog != dummy_patprog1 && *spprog != dummy_patprog2) + pprog = *spprog; + + if (!pprog) { + if (!pat) { + char *opat; + int htok = 0; + + opat = pat = dupstring(ecrawstr(state->prog, + state->pc - 2, &htok)); + if (htok) + singsub(&pat); + save = (!(state->prog->flags & EF_HEAP) && + !strcmp(pat, opat) && *spprog != dummy_patprog2); } - p++; - l++; + if (!(pprog = patcompile(pat, (save ? PAT_ZDUP : PAT_STATIC), + NULL))) + zerr("bad pattern: %s", pat, 0); + else if (save) + *spprog = pprog; } + if (pprog && pattry(pprog, word)) { + 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 = next; + code = *state->pc; + state->pc += 3; + next = state->pc + WC_CASE_SKIP(code) - 1; + execlist(state, 1, ((WC_CASE_TYPE(code) == WC_CASE_OR) && + do_exec)); + } + break; + } else + state->pc = next; } + cmdpop(); + + state->pc = end; + return lastval; } - diff --git a/Src/params.c b/Src/params.c index 4f7846820..a7444bfd1 100644 --- a/Src/params.c +++ b/Src/params.c @@ -35,59 +35,67 @@ /* what level of localness we are at */ /**/ -int locallevel; +mod_export int locallevel; /* Variables holding values of special parameters */ /**/ +mod_export char **pparams, /* $argv */ **cdpath, /* $cdpath */ - **fignore, /* $fignore */ **fpath, /* $fpath */ **mailpath, /* $mailpath */ **manpath, /* $manpath */ - **path, /* $path */ **psvar, /* $psvar */ **watch; /* $watch */ +/**/ +mod_export +char **path, /* $path */ + **fignore; /* $fignore */ /**/ char *argzero, /* $0 */ - *underscore, /* $_ */ *home, /* $HOME */ *hostnam, /* $HOST */ - *ifs, /* $IFS */ *nullcmd, /* $NULLCMD */ *oldpwd, /* $OLDPWD */ *zoptarg, /* $OPTARG */ - *postedit, /* $POSTEDIT */ *prompt, /* $PROMPT */ *prompt2, /* $PROMPT2 */ *prompt3, /* $PROMPT3 */ *prompt4, /* $PROMPT4 */ - *pwd, /* $PWD */ *readnullcmd, /* $READNULLCMD */ *rprompt, /* $RPROMPT */ *sprompt, /* $SPROMPT */ *term, /* $TERM */ - *ttystrname, /* $TTY */ *wordchars, /* $WORDCHARS */ *zsh_name; /* $ZSH_NAME */ +/**/ +mod_export +char *ifs, /* $IFS */ + *postedit, /* $POSTEDIT */ + *ttystrname, /* $TTY */ + *pwd; /* $PWD */ /**/ -long lastval, /* $? */ +mod_export +zlong lastval, /* $? */ mypid, /* $$ */ lastpid, /* $! */ columns, /* $COLUMNS */ - lineno, /* $LINENO */ lines, /* $LINES */ + ppid; /* $PPID */ +/**/ +zlong lineno, /* $LINENO */ zoptind, /* $OPTIND */ - ppid, /* $PPID */ shlvl; /* $SHLVL */ /* $histchars */ /**/ -unsigned char bangchar, hatchar, hashchar; +mod_export unsigned char bangchar; +/**/ +unsigned char hatchar, hashchar; /* $SECONDS = time(NULL) - shtimer.tv_sec */ @@ -97,7 +105,7 @@ struct timeval shtimer; /* 0 if this $TERM setup is usable, otherwise it contains TERM_* flags */ /**/ -int termflags; +mod_export int termflags; /* Nodes for special parameters for parameter hash table */ @@ -147,7 +155,7 @@ IPDEF2("WORDCHARS", wordcharsgetfn, wordcharssetfn, 0), IPDEF2("IFS", ifsgetfn, ifssetfn, PM_DONTIMPORT), IPDEF2("_", underscoregetfn, nullsetfn, PM_READONLY), -#ifdef LC_ALL +#ifdef USE_LOCALE # define LCIPDEF(name) IPDEF2(name, strgetfn, lcsetfn, PM_UNSET) IPDEF2("LANG", strgetfn, langsetfn, PM_UNSET), IPDEF2("LC_ALL", strgetfn, lc_allsetfn, PM_UNSET), @@ -160,10 +168,13 @@ LCIPDEF("LC_CTYPE"), # ifdef LC_MESSAGES LCIPDEF("LC_MESSAGES"), # endif +# ifdef LC_NUMERIC +LCIPDEF("LC_NUMERIC"), +# endif # ifdef LC_TIME LCIPDEF("LC_TIME"), # endif -#endif +#endif /* USE_LOCALE */ #define IPDEF4(A,B) {NULL,A,PM_INTEGER|PM_READONLY|PM_SPECIAL,BR((void *)B),SFN(nullsetfn),GFN(intvargetfn),stdunsetfn,10,NULL,NULL,NULL,0} IPDEF4("!", &lastpid), @@ -201,16 +212,15 @@ IPDEF8("WATCH", &watch, "watch", 0), IPDEF8("PATH", &path, "path", PM_RESTRICTED), IPDEF8("PSVAR", &psvar, "psvar", 0), -#ifdef DYNAMIC /* MODULE_PATH is not imported for security reasons */ IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED), -#endif #define IPDEF9F(A,B,C,D) {NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT,BR((void *)B),SFN(arrvarsetfn),GFN(arrvargetfn),stdunsetfn,0,NULL,C,NULL,0} #define IPDEF9(A,B,C) IPDEF9F(A,B,C,0) IPDEF9("*", &pparams, NULL), IPDEF9("@", &pparams, NULL), {NULL, NULL}, +#define IPDEF10(A,B,C) {NULL,A,PM_ARRAY|PM_SPECIAL,BR(NULL),SFN(C),GFN(B),stdunsetfn,10,NULL,NULL,NULL,0} /* The following parameters are not avaible in sh/ksh compatibility * * mode. All of these has sh compatible equivalents. */ @@ -232,22 +242,208 @@ IPDEF9("manpath", &manpath, "MANPATH"), IPDEF9("psvar", &psvar, "PSVAR"), IPDEF9("watch", &watch, "WATCH"), -#ifdef DYNAMIC IPDEF9F("module_path", &module_path, "MODULE_PATH", PM_RESTRICTED), -#endif IPDEF9F("path", &path, "PATH", PM_RESTRICTED), +IPDEF10("pipestatus", pipestatgetfn, pipestatsetfn), + {NULL, NULL} }; #undef BR +#define IS_UNSET_VALUE(V) \ + ((V) && (!(V)->pm || ((V)->pm->flags & PM_UNSET) || \ + !(V)->pm->nam || !*(V)->pm->nam)) + static Param argvparam; /* hash table containing the parameters */ /**/ -HashTable paramtab; - +mod_export HashTable paramtab, realparamtab; + +/**/ +mod_export HashTable +newparamtable(int size, char const *name) +{ + HashTable 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 = getparamnode; + ht->removenode = removehashnode; + ht->disablenode = NULL; + ht->enablenode = NULL; + ht->freenode = freeparamnode; + ht->printnode = printparamnode; + + return ht; +} + +/**/ +static HashNode +getparamnode(HashTable ht, char *nam) +{ + HashNode hn = gethashnode2(ht, nam); + Param pm = (Param) hn; + + if (pm && pm->u.str && (pm->flags & PM_AUTOLOAD)) { + char *mn = dupstring(pm->u.str); + + if (!load_module(mn)) + return NULL; + hn = gethashnode2(ht, nam); + if (((Param) hn) == pm && (pm->flags & PM_AUTOLOAD)) { + pm->flags &= ~PM_AUTOLOAD; + zwarnnam(nam, "autoload failed", NULL, 0); + } + } + return hn; +} + +/* Copy a parameter hash table */ + +static HashTable outtable; + +/**/ +static void +scancopyparams(HashNode hn, int flags) +{ + /* Going into a real parameter, so always use permanent storage */ + Param pm = (Param)hn; + Param tpm = (Param) zcalloc(sizeof *tpm); + tpm->nam = ztrdup(pm->nam); + copyparam(tpm, pm, 0); + addhashnode(outtable, tpm->nam, tpm); +} + +/**/ +HashTable +copyparamtable(HashTable ht, char *name) +{ + HashTable 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(HashNode hn, int flags) +{ + ++numparamvals; + if ((flags & SCANPM_WANTKEYS) && (flags & SCANPM_WANTVALS)) + ++numparamvals; +} + +static Patprog scanprog; +static char *scanstr; +static char **paramvals; + +/**/ +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->nam); + + tokenize(tmp); + remnulargs(tmp); + + if (!(prog = patcompile(tmp, 0, NULL)) || !pattry(prog, scanstr)) + return; + } else if ((flags & SCANPM_MATCHKEY) && !pattry(scanprog, v.pm->nam)) { + return; + } + if (flags & SCANPM_WANTKEYS) { + paramvals[numparamvals++] = v.pm->nam; + if (!(flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL))) + return; + } + v.isarr = (PM_TYPE(v.pm->flags) & (PM_ARRAY|PM_HASHED)); + v.inv = 0; + v.a = 0; + v.b = -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; +} + +/**/ +char ** +paramvalarr(HashTable ht, int flags) +{ + 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->flags) == PM_ARRAY) + return v->arr = v->pm->gets.afn(v->pm); + else if (PM_TYPE(v->pm->flags) == PM_HASHED) { + v->arr = paramvalarr(v->pm->gets.hfn(v->pm), v->isarr); + /* Can't take numeric slices of associative arrays */ + v->a = 0; + v->b = numparamvals; + return v->arr; + } else + return NULL; +} + /* Set up parameter hash table. This will add predefined * * parameter entries as well as setting up parameter table * * entries for environment variables we inherit. */ @@ -259,21 +455,13 @@ createparamtable(void) Param ip, pm; char **new_environ, **envp, **envp2, **sigptr, **t; char buf[50], *str, *iname; - int num_env; - - paramtab = newhashtable(151, "paramtab", NULL); + int num_env, oae = opts[ALLEXPORT]; +#ifdef HAVE_UNAME + struct utsname unamebuf; + char *machinebuf; +#endif - paramtab->hash = hasher; - paramtab->emptytable = NULL; - paramtab->filltable = NULL; - paramtab->addnode = addhashnode; - paramtab->getnode = gethashnode2; - paramtab->getnode2 = gethashnode2; - paramtab->removenode = removehashnode; - paramtab->disablenode = NULL; - paramtab->enablenode = NULL; - paramtab->freenode = freeparamnode; - paramtab->printnode = printparamnode; + paramtab = realparamtab = newparamtable(151, "paramtab"); /* Add the special parameters to the hash table */ for (ip = special_params; ip->nam; ip++) @@ -284,90 +472,137 @@ createparamtable(void) argvparam = (Param) paramtab->getnode(paramtab, "*"); - noerrs = 1; - - HEAPALLOC { - /* 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 users has * - * given them in the environment. */ - setiparam("MAILCHECK", 60); - setiparam("LOGCHECK", 60); - setiparam("KEYTIMEOUT", 40); - setiparam("LISTMAX", 100); + 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 users has * + * given them in the environment. */ + opts[ALLEXPORT] = 0; + setiparam("MAILCHECK", 60); + setiparam("LOGCHECK", 60); + setiparam("KEYTIMEOUT", 40); + setiparam("LISTMAX", 100); #ifdef HAVE_SELECT - setiparam("BAUD", getbaudrate(&shttyinfo)); /* get the output baudrate */ + setiparam("BAUD", getbaudrate(&shttyinfo)); /* get the output baudrate */ #endif - setsparam("FCEDIT", ztrdup(DEFAULT_FCEDIT)); - setsparam("TMPPREFIX", ztrdup(DEFAULT_TMPPREFIX)); - setsparam("TIMEFMT", ztrdup(DEFAULT_TIMEFMT)); - setsparam("WATCHFMT", ztrdup(default_watchfmt)); - setsparam("HOST", ztrdup(hostnam)); - setsparam("LOGNAME", ztrdup((str = getlogin()) && *str ? str : cached_username)); - - /* Copy the environment variables we are inheriting to dynamic * - * memory, so we can do mallocs and frees on it. */ - num_env = arrlen(environ); - new_environ = (char **) zalloc(sizeof(char *) * (num_env + 1)); - *new_environ = NULL; - - /* Now incorporate environment variables we are inheriting * - * into the parameter hash table. */ - for (envp = new_environ, envp2 = environ; *envp2; envp2++) { - for (str = *envp2; *str && *str != '='; str++); - if (*str == '=') { - iname = NULL; - *str = '\0'; - if (!idigit(**envp2) && isident(*envp2) && !strchr(*envp2, '[')) { - iname = *envp2; - if ((!(pm = (Param) paramtab->getnode(paramtab, iname)) || - !(pm->flags & PM_DONTIMPORT)) && - (pm = setsparam(iname, metafy(str + 1, -1, META_DUP))) && - !(pm->flags & PM_EXPORTED)) { - *str = '='; - pm->flags |= PM_EXPORTED; - pm->env = *envp++ = ztrdup(*envp2); - *envp = NULL; - if (pm->flags & PM_SPECIAL) - pm->env = replenv(pm->env, getsparam(pm->nam)); - } + setsparam("FCEDIT", ztrdup(DEFAULT_FCEDIT)); + setsparam("TMPPREFIX", ztrdup(DEFAULT_TMPPREFIX)); + setsparam("TIMEFMT", ztrdup(DEFAULT_TIMEFMT)); + setsparam("WATCHFMT", ztrdup(default_watchfmt)); + setsparam("HOST", ztrdup(hostnam)); + setsparam("LOGNAME", ztrdup((str = getlogin()) && *str ? str : cached_username)); + + /* Copy the environment variables we are inheriting to dynamic * + * memory, so we can do mallocs and frees on it. */ + num_env = arrlen(environ); + new_environ = (char **) zalloc(sizeof(char *) * (num_env + 1)); + *new_environ = NULL; + + /* Now incorporate environment variables we are inheriting * + * into the parameter hash table. */ + for (envp = new_environ, envp2 = environ; *envp2; envp2++) { + for (str = *envp2; *str && *str != '='; str++); + if (*str == '=') { + iname = NULL; + *str = '\0'; + if (!idigit(**envp2) && isident(*envp2) && !strchr(*envp2, '[')) { + iname = *envp2; + if ((!(pm = (Param) paramtab->getnode(paramtab, iname)) || + !(pm->flags & PM_DONTIMPORT)) && + (pm = setsparam(iname, metafy(str + 1, -1, META_DUP))) && + !(pm->flags & PM_EXPORTED)) { + *str = '='; + pm->flags |= PM_EXPORTED; + pm->env = *envp++ = ztrdup(*envp2); + *envp = NULL; + if (pm->flags & PM_SPECIAL) + pm->env = replenv(pm->env, getsparam(pm->nam), + pm->flags); } - *str = '='; } + *str = '='; } - environ = new_environ; + } + environ = new_environ; + opts[ALLEXPORT] = oae; - pm = (Param) paramtab->getnode(paramtab, "HOME"); - if (!(pm->flags & PM_EXPORTED)) { - pm->flags |= PM_EXPORTED; - pm->env = addenv("HOME", home); - } - pm = (Param) paramtab->getnode(paramtab, "LOGNAME"); - if (!(pm->flags & PM_EXPORTED)) { - pm->flags |= PM_EXPORTED; - pm->env = addenv("LOGNAME", pm->u.str); - } - pm = (Param) paramtab->getnode(paramtab, "SHLVL"); - if (!(pm->flags & PM_EXPORTED)) - pm->flags |= PM_EXPORTED; - sprintf(buf, "%d", (int)++shlvl); - pm->env = addenv("SHLVL", buf); - - /* Add the standard non-special parameters */ - set_pwd_env(); - setsparam("MACHTYPE", ztrdup(MACHTYPE)); - setsparam("OSTYPE", ztrdup(OSTYPE)); - setsparam("TTY", ztrdup(ttystrname)); - setsparam("VENDOR", ztrdup(VENDOR)); - setsparam("ZSH_NAME", ztrdup(zsh_name)); - setsparam("ZSH_VERSION", ztrdup(ZSH_VERSION)); - setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *))); - for (t = sigs; (*sigptr++ = ztrdup(*t++)); ); - } LASTALLOC; + pm = (Param) paramtab->getnode(paramtab, "HOME"); + if (!(pm->flags & PM_EXPORTED)) { + pm->flags |= PM_EXPORTED; + pm->env = addenv("HOME", home, pm->flags); + } + pm = (Param) paramtab->getnode(paramtab, "LOGNAME"); + if (!(pm->flags & PM_EXPORTED)) { + pm->flags |= PM_EXPORTED; + pm->env = addenv("LOGNAME", pm->u.str, pm->flags); + } + pm = (Param) paramtab->getnode(paramtab, "SHLVL"); + if (!(pm->flags & PM_EXPORTED)) + pm->flags |= PM_EXPORTED; + sprintf(buf, "%d", (int)++shlvl); + pm->env = addenv("SHLVL", buf, pm->flags); + + /* Add the standard non-special parameters */ + set_pwd_env(); +#ifdef HAVE_UNAME + if(uname(&unamebuf)) setsparam("CPUTYPE", ztrdup("unknown")); + else + { + machinebuf = ztrdup(unamebuf.machine); + setsparam("CPUTYPE", machinebuf); + } + +#else + setsparam("CPUTYPE", ztrdup("unknown")); +#endif + setsparam("MACHTYPE", ztrdup(MACHTYPE)); + setsparam("OSTYPE", ztrdup(OSTYPE)); + setsparam("TTY", ztrdup(ttystrname)); + setsparam("VENDOR", ztrdup(VENDOR)); + setsparam("ZSH_NAME", ztrdup(zsh_name)); + setsparam("ZSH_VERSION", ztrdup(ZSH_VERSION)); + setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *))); + for (t = sigs; (*sigptr++ = ztrdup(*t++)); ); noerrs = 0; } +/* assign various functions used for non-special parameters */ + +/**/ +static void +assigngetset(Param pm) +{ + switch (PM_TYPE(pm->flags)) { + case PM_SCALAR: + pm->sets.cfn = strsetfn; + pm->gets.cfn = strgetfn; + break; + case PM_INTEGER: + pm->sets.ifn = intsetfn; + pm->gets.ifn = intgetfn; + break; + case PM_EFLOAT: + case PM_FFLOAT: + pm->sets.ffn = floatsetfn; + pm->gets.ffn = floatgetfn; + break; + case PM_ARRAY: + pm->sets.afn = arrsetfn; + pm->gets.afn = arrgetfn; + break; + case PM_HASHED: + pm->sets.hfn = hashsetfn; + pm->gets.hfn = hashgetfn; + break; + default: + DPUTS(1, "BUG: tried to create param node without valid flag"); + break; + } + pm->unsetfn = stdunsetfn; +} + /* 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 * @@ -377,15 +612,19 @@ createparamtable(void) * created because it already exists, the PM_UNSET flag is cleared. */ /**/ -Param +mod_export Param createparam(char *name, int flags) { Param pm, oldpm; if (name != nulstring) { - oldpm = (Param) paramtab->getnode(paramtab, name); + oldpm = (Param) (paramtab == realparamtab ? + gethashnode2(paramtab, name) : + paramtab->getnode(paramtab, name)); - if (oldpm && oldpm->level == locallevel) { + DPUTS(oldpm && oldpm->level > locallevel, + "BUG: old local parameter not deleteed"); + if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) { if (!(oldpm->flags & PM_UNSET) || (oldpm->flags & PM_SPECIAL)) { oldpm->flags &= ~PM_UNSET; return NULL; @@ -409,33 +648,61 @@ createparam(char *name, int flags) if (isset(ALLEXPORT) && !oldpm) flags |= PM_EXPORTED; - } else - pm = (Param) alloc(sizeof *pm); - pm->flags = flags; - - if(!(pm->flags & PM_SPECIAL)) { - switch (PM_TYPE(flags)) { - case PM_SCALAR: - pm->sets.cfn = strsetfn; - pm->gets.cfn = strgetfn; - break; - case PM_INTEGER: - pm->sets.ifn = intsetfn; - pm->gets.ifn = intgetfn; - break; - case PM_ARRAY: - pm->sets.afn = arrsetfn; - pm->gets.afn = arrgetfn; - break; - default: - DPUTS(1, "BUG: tried to create param node without valid flag"); - break; - } - pm->unsetfn = stdunsetfn; + } else { + pm = (Param) zhalloc(sizeof *pm); + pm->nam = nulstring; } + pm->flags = flags & ~PM_LOCAL; + + if(!(pm->flags & PM_SPECIAL)) + assigngetset(pm); return pm; } +/* Copy a parameter */ + +/**/ +void +copyparam(Param tpm, Param pm, int toplevel) +{ + /* + * 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->flags = pm->flags; + if (!toplevel) + tpm->flags &= ~PM_SPECIAL; + switch (PM_TYPE(pm->flags)) { + case PM_SCALAR: + tpm->u.str = ztrdup(pm->gets.cfn(pm)); + break; + case PM_INTEGER: + tpm->u.val = pm->gets.ifn(pm); + break; + case PM_EFLOAT: + case PM_FFLOAT: + tpm->u.dval = pm->gets.ffn(pm); + break; + case PM_ARRAY: + tpm->u.arr = zarrdup(pm->gets.afn(pm)); + break; + case PM_HASHED: + tpm->u.hash = copyparamtable(pm->gets.hfn(pm), pm->nam); + break; + } + /* + * If called from inside an associative array, that array is later going + * to be passed as a real parameter, so we need the gets and sets + * functions to be useful. However, the saved associated array is + * not itself special, so we just use the standard ones. + * This is also why we switch off PM_SPECIAL. + */ + if (!toplevel) + assigngetset(tpm); +} + /* Return 1 if the string s is a valid identifier, else return 0. */ /**/ @@ -454,6 +721,7 @@ isident(char *s) if (!iident(*ss)) break; +#if 0 /* If this exhaust `s' or the next two characters * * are [(, then it is a valid identifier. */ if (!*ss || (*ss == '[' && ss[1] == '(')) @@ -463,6 +731,7 @@ isident(char *s) * definitely not a valid identifier. */ if (*ss != '[') return 0; + noeval = 1; (void)mathevalarg(++ss, &ss); if (*ss == ',') @@ -471,39 +740,64 @@ isident(char *s) if (*ss != ']' || ss[1]) return 0; return 1; -} +#else + /* If the next character is not [, then it is * + * definitely not a valid identifier. */ + if (!*ss) + return 1; + if (*ss != '[') + return 0; -static char **garr; + /* Require balanced [ ] pairs */ + if (skipparens('[', ']', &ss)) + return 0; + return !*ss; +#endif +} /**/ -static long -getarg(char **str, int *inv, Value v, int a2, long *w) +static zlong +getarg(char **str, int *inv, Value v, int a2, zlong *w) { - int num = 1, word = 0, rev = 0, ind = 0, down = 0, l, i; - char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt; - long r = 0; - Comp c; + int hasbeg = 0, word = 0, rev = 0, ind = 0, down = 0, l, i, ishash; + int keymatch = 0, needtok = 0; + char *s = *str, *sep = NULL, *t, sav, *d, **ta, **p, *tt, c; + zlong num = 1, beg = 0, r = 0; + Patprog pprog = NULL; + + ishash = (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED); /* first parse any subscription flags */ - if (*s == '(' || *s == Inpar) { + 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; - down = ind = 0; + 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 = 0; + down = keymatch = 0; break; case 'I': rev = ind = down = 1; + keymatch = 0; break; case 'w': /* If the parameter is a scalar, then make subscription * @@ -529,6 +823,18 @@ getarg(char **str, int *inv, Value v, int a2, long *w) *t = sav; s = t; break; + case 'b': + hasbeg = 1; + t = get_strarg(++s); + if (!*t) + goto flagerr; + sav = *t; + *t = '\0'; + if ((beg = mathevalarg(s + 1, &d)) > 0) + beg--; + *t = sav; + s = t; + break; case 'p': escapes = 1; break; @@ -548,7 +854,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w) default: flagerr: num = 1; - word = rev = ind = down = 0; + word = rev = ind = down = keymatch = 0; sep = NULL; s = *str - 1; } @@ -560,23 +866,62 @@ getarg(char **str, int *inv, Value v, int a2, long *w) down = !down; num = -num; } - *inv = ind; + 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; + if (!down && !keymatch && ishash) + v->isarr &= ~SCANPM_MATCHMANY; + } + *inv = ind; + } - for (t=s, i=0; *t && ((*t != ']' && *t != Outbrack && *t != ',') || i); t++) - if (*t == '[' || *t == Inbrack) + for (t = s, i = 0; + (c = *t) && ((c != ']' && c != Outbrack && + (ishash || c != ',')) || i); t++) { + if (c == '[' || c == Inbrack) i++; - else if (*t == ']' || *t == Outbrack) + else if (c == ']' || c == Outbrack) i--; - - if (!*t) + if (ispecial(c)) + needtok = 1; + } + if (!c) return 0; s = dupstrpfx(s, t - s); *str = tt = t; - if (parsestr(s)) - return 0; - singsub(&s); - + if (needtok) { + if (parsestr(s)) + return 0; + singsub(&s); + } if (!rev) { + if (ishash) { + HashTable ht = v->pm->gets.hfn(v->pm); + if (!ht) { + ht = newparamtable(17, v->pm->nam); + v->pm->sets.hfn(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->a = 0; + *inv = 0; /* We've already obtained the "index" (key) */ + *w = v->b = -1; + r = isset(KSHARRAYS) ? 1 : 0; + } else if (!(r = mathevalarg(s, &s)) || (isset(KSHARRAYS) && r >= 0)) r++; if (word && !v->isarr) { @@ -595,7 +940,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w) return 0; if (!a2 && *tt != ',') - *w = (long)(s - t) - 1; + *w = (zlong)(s - t) - 1; return (a2 ? s : d + 1) - t; } else if (!v->isarr && !word) { @@ -617,14 +962,14 @@ getarg(char **str, int *inv, Value v, int a2, long *w) l = strlen(s); if (a2) { if (!l || *s != '*') { - d = (char *) ncalloc(l + 2); + d = (char *) hcalloc(l + 2); *d = '*'; strcpy(d + 1, s); s = d; } } else { if (!l || s[l - 1] != '*') { - d = (char *) ncalloc(l + 2); + d = (char *) hcalloc(l + 2); strcpy(d, s); strcat(d, "*"); s = d; @@ -633,39 +978,77 @@ getarg(char **str, int *inv, Value v, int a2, long *w) } tokenize(s); - if ((c = parsereg(s))) { + if (keymatch || (pprog = patcompile(s, 0, NULL))) { + int len; + if (v->isarr) { - ta = getarrvalue(v); + if (ishash) { + scanprog = pprog; + scanstr = s; + if (keymatch) { + untokenize(s); + v->isarr |= SCANPM_KEYMATCH; + } else 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->inv; + *w = v->b; + return 1; + } + } else + ta = getarrvalue(v); if (!ta || !*ta) return 0; - if (down) - for (r = -1, p = ta + arrlen(ta) - 1; p >= ta; r--, p--) { - if (domatch(*p, c, 0) && !--num) - return r; - } else - for (r = 1, p = ta; *p; r++, p++) - if (domatch(*p, c, 0) && !--num) - return r; + len = arrlen(ta); + if (beg < 0) + beg += len; + if (beg >= 0 && beg < len) { + if (down) { + if (!hasbeg) + beg = len - 1; + for (r = 1 + beg, p = ta + beg; p >= ta; r--, p--) { + if (pattry(pprog, *p) && !--num) + return r; + } + } else + for (r = 1 + beg, p = ta + beg; *p; r++, p++) + if (pattry(pprog, *p) && !--num) + return r; + } } else if (word) { - ta = sepsplit(d = s = getstrvalue(v), sep, 1); - if (down) { - for (p = ta + (r = arrlen(ta)) - 1; p >= ta; p--, r--) - if (domatch(*p, c, 0) && !--num) - break; - if (p < ta) - return 0; - } else { - for (r = 1, p = ta; *p; r++, p++) - if (domatch(*p, c, 0) && !--num) - break; - if (!*p) - return 0; + ta = sepsplit(d = s = getstrvalue(v), sep, 1, 1); + len = arrlen(ta); + if (beg < 0) + beg += len; + if (beg >= 0 && beg < len) { + if (down) { + if (!hasbeg) + beg = len - 1; + for (r = 1 + beg, p = ta + beg; p >= ta; p--, r--) + if (pattry(pprog, *p) && !--num) + break; + if (p < ta) + return 0; + } else { + for (r = 1 + beg, p = ta + beg; *p; r++, p++) + if (pattry(pprog, *p) && !--num) + break; + if (!*p) + return 0; + } } if (a2) r++; for (i = 0; (t = findword(&d, sep)) && *t; i++) if (!--r) { - r = (long)(t - s + (a2 ? -1 : 1)); + r = (zlong)(t - s + (a2 ? -1 : 1)); if (!a2 && *tt != ',') *w = r + strlen(ta[i]) - 2; return r; @@ -675,35 +1058,50 @@ getarg(char **str, int *inv, Value v, int a2, long *w) d = getstrvalue(v); if (!d || !*d) return 0; - if (a2) { - if (down) - for (r = -2, t = d + strlen(d) - 1; t >= d; r--, t--) { - sav = *t; - *t = '\0'; - if (domatch(d, c, 0) && !--num) { + len = strlen(d); + if (beg < 0) + beg += len; + if (beg >= 0 && beg < len) { + if (a2) { + if (down) { + if (!hasbeg) + beg = len - 1; + for (r = beg, t = d + beg; t >= d; r--, t--) { + sav = *t; + *t = '\0'; + if (pattry(pprog, d) + && !--num) { + *t = sav; + return r; + } *t = sav; - return r; } - *t = sav; - } else - for (r = 0, t = d; *t; r++, t++) { - sav = *t; - *t = '\0'; - if (domatch(d, c, 0) && !--num) { + } else + for (r = beg, t = d + beg; *t; r++, t++) { + sav = *t; + *t = '\0'; + if (pattry(pprog, d) && + !--num) { + *t = sav; + return r; + } *t = sav; - return r; } - *t = sav; - } - } else { - if (down) - for (r = -1, t = d + strlen(d) - 1; t >= d; r--, t--) { - if (domatch(t, c, 0) && !--num) - return r; - } else - for (r = 1, t = d; *t; r++, t++) - if (domatch(t, c, 0) && !--num) - return r; + } else { + if (down) { + if (!hasbeg) + beg = len - 1; + for (r = beg + 1, t = d + beg; t >= d; r--, t--) { + if (pattry(pprog, t) && + !--num) + return r; + } + } else + for (r = beg + 1, t = d + beg; *t; r++, t++) + if (pattry(pprog, t) && + !--num) + return r; + } } return 0; } @@ -726,13 +1124,13 @@ getindex(char **pptr, Value v) if (*tbrack == Outbrack) *tbrack = ']'; if ((s[0] == '*' || s[0] == '@') && s[1] == ']') { - if (v->isarr) - v->isarr = (s[0] == '*') ? 1 : -1; + if ((v->isarr || IS_UNSET_VALUE(v)) && s[0] == '@') + v->isarr |= SCANPM_ISVAR_AT; v->a = 0; v->b = -1; s += 2; } else { - long we = 0, dummy; + zlong we = 0, dummy; a = getarg(&s, &inv, v, 0, &we); @@ -747,11 +1145,13 @@ getindex(char **pptr, Value v) } else a = -ztrlen(t + a + strlen(t)); } - if (a > 0 && isset(KSHARRAYS)) + if (a > 0 && (isset(KSHARRAYS) || (v->pm->flags & PM_HASHED))) a--; - v->inv = 1; - v->isarr = 0; - v->a = v->b = a; + if (v->isarr != SCANPM_WANTINDEX) { + v->inv = 1; + v->isarr = 0; + v->a = v->b = a; + } if (*s == ',') { zerr("invalid subscript", NULL, 0); while (*s != ']' && *s != Outbrack) @@ -762,9 +1162,11 @@ getindex(char **pptr, Value v) if (*s == ']' || *s == Outbrack) s++; } else { + int com; + if (a > 0) a--; - if (*s == ',') { + if ((com = (*s == ','))) { s++; b = getarg(&s, &inv, v, 1, &dummy); if (b > 0) @@ -774,7 +1176,10 @@ getindex(char **pptr, Value v) } if (*s == ']' || *s == Outbrack) { s++; - if (v->isarr && a == b) + if (v->isarr && a == b && !com && + (!(v->isarr & SCANPM_MATCHMANY) || + !(v->isarr & (SCANPM_MATCHKEY | SCANPM_MATCHVAL | + SCANPM_KEYMATCH)))) v->isarr = 0; v->a = a; v->b = b; @@ -788,37 +1193,43 @@ getindex(char **pptr, Value v) /**/ -Value -getvalue(char **pptr, int bracks) +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; - char sav; - Value v; + char sav, c; int ppar = 0; s = t = *pptr; - garr = NULL; - if (idigit(*s)) + if (idigit(c = *s)) { if (bracks >= 0) ppar = zstrtol(s, &s, 10); else ppar = *s++ - '0'; - else if (iident(*s)) + } + else if (iident(c)) while (iident(*s)) s++; - else if (*s == Quest) + else if (c == Quest) *s++ = '?'; - else if (*s == Pound) + else if (c == Pound) *s++ = '#'; - else if (*s == String) + else if (c == String) *s++ = '$'; - else if (*s == Qstring) + else if (c == Qstring) *s++ = '$'; - else if (*s == Star) + else if (c == Star) *s++ = '*'; - else if (*s == '#' || *s == '-' || *s == '?' || *s == '$' || - *s == '_' || *s == '!' || *s == '@' || *s == '*') + else if (c == '#' || c == '-' || c == '?' || c == '$' || + c == '!' || c == '@' || c == '*') s++; else return NULL; @@ -826,7 +1237,10 @@ getvalue(char **pptr, int bracks) if ((sav = *s)) *s = '\0'; if (ppar) { - v = (Value) hcalloc(sizeof *v); + if (v) + memset(v, 0, sizeof(*v)); + else + v = (Value) hcalloc(sizeof *v); v->pm = argvparam; v->inv = 0; v->a = v->b = ppar - 1; @@ -836,16 +1250,27 @@ getvalue(char **pptr, int bracks) Param pm; int isvarat; - isvarat = !strcmp(t, "@"); + isvarat = (t[0] == '@' && !t[1]); pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t); if (sav) *s = sav; *pptr = s; if (!pm || (pm->flags & PM_UNSET)) return NULL; - v = (Value) hcalloc(sizeof *v); - if (PM_TYPE(pm->flags) == PM_ARRAY) - v->isarr = isvarat ? -1 : 1; + if (v) + memset(v, 0, sizeof(*v)); + else + v = (Value) hcalloc(sizeof *v); + if (PM_TYPE(pm->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. This is a * + * bit of a hack, but makes some sense: When no subscript * + * is provided, all values are substituted. */ + if (!v->isarr) + v->isarr = SCANPM_MATCHMANY; + } v->pm = pm; v->inv = 0; v->a = 0; @@ -855,7 +1280,8 @@ getvalue(char **pptr, int bracks) *pptr = s; return v; } - } else if (v->isarr && iident(*t) && isset(KSHARRAYS)) + } else if (!(flags & SCANPM_ASSIGNING) && v->isarr && + iident(*t) && isset(KSHARRAYS)) v->b = 0, v->isarr = 0; } if (!bracks && *s) @@ -863,68 +1289,82 @@ getvalue(char **pptr, int bracks) *pptr = s; if (v->a > MAX_ARRLEN || v->a < -MAX_ARRLEN) { - zerr("subscript to %s: %d", (v->a < 0) ? "small" : "big", v->a); + zerr("subscript too %s: %d", (v->a < 0) ? "small" : "big", v->a); return NULL; } if (v->b > MAX_ARRLEN || v->b < -MAX_ARRLEN) { - zerr("subscript to %s: %d", (v->b < 0) ? "small" : "big", v->b); + zerr("subscript too %s: %d", (v->b < 0) ? "small" : "big", v->b); return NULL; } return v; } /**/ -char * +mod_export char * getstrvalue(Value v) { char *s, **ss; - static char buf[(sizeof(long) * 8) + 4]; + char buf[(sizeof(zlong) * 8) + 4]; if (!v) return hcalloc(1); - HEAPALLOC { - if (v->inv) { - sprintf(buf, "%d", v->a); - s = dupstring(buf); - LASTALLOC_RETURN s; - } - switch(PM_TYPE(v->pm->flags)) { - case PM_ARRAY: - if (v->isarr) - s = sepjoin(v->pm->gets.afn(v->pm), NULL); - else { - ss = v->pm->gets.afn(v->pm); - if (v->a < 0) - v->a += arrlen(ss); - s = (v->a >= arrlen(ss) || v->a < 0) ? (char *) hcalloc(1) : ss[v->a]; - } - LASTALLOC_RETURN s; - case PM_INTEGER: - convbase(s = buf, v->pm->gets.ifn(v->pm), v->pm->ct); - break; - case PM_SCALAR: - s = v->pm->gets.cfn(v->pm); - break; - default: - s = NULL; - DPUTS(1, "BUG: param node without valid type"); - break; + if (v->inv && !(v->pm->flags & PM_HASHED)) { + sprintf(buf, "%d", v->a); + s = dupstring(buf); + return s; + } + + switch(PM_TYPE(v->pm->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) + 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->a < 0) + v->a += arrlen(ss); + s = (v->a >= arrlen(ss) || v->a < 0) ? (char *) hcalloc(1) : ss[v->a]; } + return s; + case PM_INTEGER: + convbase(buf, v->pm->gets.ifn(v->pm), v->pm->ct); + s = dupstring(buf); + break; + case PM_EFLOAT: + case PM_FFLOAT: + s = convfloat(v->pm->gets.ffn(v->pm), v->pm->ct, v->pm->flags, NULL); + break; + case PM_SCALAR: + s = v->pm->gets.cfn(v->pm); + break; + default: + s = NULL; + DPUTS(1, "BUG: param node without valid type"); + break; + } + + if (v->a == 0 && v->b == -1) + return s; + + if (v->a < 0) + v->a += strlen(s); + if (v->b < 0) + v->b += strlen(s); + s = (v->a > (int)strlen(s)) ? dupstring("") : dupstring(s + v->a); + if (v->b < v->a) + s[0] = '\0'; + else if (v->b - v->a < (int)strlen(s)) + s[v->b - v->a + 1 + (s[v->b - v->a] == Meta)] = '\0'; - if (v->a == 0 && v->b == -1) - LASTALLOC_RETURN s; - if (v->a < 0) - v->a += strlen(s); - if (v->b < 0) - v->b += strlen(s); - s = (v->a > (int)strlen(s)) ? dupstring("") : dupstring(s + v->a); - if (v->b < v->a) - s[0] = '\0'; - else if (v->b - v->a < (int)strlen(s)) - s[v->b - v->a + 1 + (s[v->b - v->a] == Meta)] = '\0'; - } LASTALLOC; return s; } @@ -938,6 +1378,8 @@ getarrvalue(Value v) if (!v) return arrdup(nular); + else if (IS_UNSET_VALUE(v)) + return arrdup(&nular[1]); if (v->inv) { char buf[DIGBUFSIZE]; @@ -946,7 +1388,7 @@ getarrvalue(Value v) s[0] = dupstring(buf); return s; } - s = v->pm->gets.afn(v->pm); + s = getvaluearr(v); if (v->a == 0 && v->b == -1) return s; if (v->a < 0) @@ -956,7 +1398,7 @@ getarrvalue(Value v) if (v->a > arrlen(s) || v->a < 0) s = arrdup(nular); else - s = arrdup(s) + v->a; + s = arrdup(s + v->a); if (v->b < v->a) s[0] = NULL; else if (v->b - v->a < arrlen(s)) @@ -965,7 +1407,7 @@ getarrvalue(Value v) } /**/ -long +mod_export zlong getintvalue(Value v) { if (!v || v->isarr) @@ -974,14 +1416,37 @@ getintvalue(Value v) return v->a; if (PM_TYPE(v->pm->flags) == PM_INTEGER) return v->pm->gets.ifn(v->pm); - return matheval(getstrvalue(v)); + if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT)) + return (zlong)v->pm->gets.ffn(v->pm); + return mathevali(getstrvalue(v)); } /**/ -static void +mnumber +getnumvalue(Value v) +{ + mnumber mn; + mn.type = MN_INTEGER; + + if (!v || v->isarr) { + mn.u.l = 0; + } else if (v->inv) { + mn.u.l = v->a; + } else if (PM_TYPE(v->pm->flags) == PM_INTEGER) { + mn.u.l = v->pm->gets.ifn(v->pm); + } else if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT)) { + mn.type = MN_FLOAT; + mn.u.d = v->pm->gets.ffn(v->pm); + } else + return matheval(getstrvalue(v)); + return mn; +} + +/**/ +mod_export void setstrvalue(Value v, char *val) { - char buf[(sizeof(long) * 8) + 4]; + char buf[(sizeof(zlong) * 8) + 4]; if (v->pm->flags & PM_READONLY) { zerr("read-only variable: %s", v->pm->nam, 0); @@ -993,9 +1458,9 @@ setstrvalue(Value v, char *val) zsfree(val); return; } + v->pm->flags &= ~PM_UNSET; switch (PM_TYPE(v->pm->flags)) { case PM_SCALAR: - MUSTUSEHEAP("setstrvalue"); if (v->a == 0 && v->b == -1) { (v->pm->sets.cfn) (v->pm, val); if (v->pm->flags & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z) && !v->pm->ct) @@ -1029,14 +1494,22 @@ setstrvalue(Value v, char *val) break; case PM_INTEGER: if (val) { - (v->pm->sets.ifn) (v->pm, matheval(val)); + (v->pm->sets.ifn) (v->pm, mathevali(val)); zsfree(val); } if (!v->pm->ct && lastbase != -1) v->pm->ct = lastbase; break; + case PM_EFLOAT: + case PM_FFLOAT: + if (val) { + mnumber mn = matheval(val); + (v->pm->sets.ffn) (v->pm, (mn.type & MN_FLOAT) ? mn.u.d : + (double)mn.u.l); + zsfree(val); + } + break; case PM_ARRAY: - MUSTUSEHEAP("setstrvalue"); { char **ss = (char **) zalloc(2 * sizeof(char *)); @@ -1052,21 +1525,24 @@ setstrvalue(Value v, char *val) return; if (PM_TYPE(v->pm->flags) == PM_INTEGER) convbase(val = buf, v->pm->gets.ifn(v->pm), v->pm->ct); + else if (v->pm->flags & (PM_EFLOAT|PM_FFLOAT)) + val = convfloat(v->pm->gets.ffn(v->pm), v->pm->ct, + v->pm->flags, NULL); else val = v->pm->gets.cfn(v->pm); if (v->pm->env) - v->pm->env = replenv(v->pm->env, val); + v->pm->env = replenv(v->pm->env, val, v->pm->flags); else { v->pm->flags |= PM_EXPORTED; - v->pm->env = addenv(v->pm->nam, val); + v->pm->env = addenv(v->pm->nam, val, v->pm->flags); } } /**/ -static void -setintvalue(Value v, long val) +void +setnumvalue(Value v, mnumber val) { - char buf[DIGBUFSIZE]; + char buf[DIGBUFSIZE], *p; if (v->pm->flags & PM_READONLY) { zerr("read-only variable: %s", v->pm->nam, 0); @@ -1079,18 +1555,28 @@ setintvalue(Value v, long val) switch (PM_TYPE(v->pm->flags)) { case PM_SCALAR: case PM_ARRAY: - sprintf(buf, "%ld", val); - setstrvalue(v, ztrdup(buf)); + if (val.type & MN_INTEGER) + convbase(p = buf, val.u.l, 0); + else + p = convfloat(val.u.d, 0, 0, NULL); + setstrvalue(v, ztrdup(p)); break; case PM_INTEGER: - (v->pm->sets.ifn) (v->pm, val); + (v->pm->sets.ifn) (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->sets.ffn) (v->pm, (val.type & MN_INTEGER) ? + (double)val.u.l : val.u.d); setstrvalue(v, NULL); break; } } /**/ -static void +mod_export void setarrvalue(Value v, char **val) { if (v->pm->flags & PM_READONLY) { @@ -1103,17 +1589,25 @@ setarrvalue(Value v, char **val) freearray(val); return; } - if (PM_TYPE(v->pm->flags) != PM_ARRAY) { + if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED))) { freearray(val); zerr("attempt to assign array value to non-array", NULL, 0); return; } - if (v->a == 0 && v->b == -1) - (v->pm->sets.afn) (v->pm, val); - else { + if (v->a == 0 && v->b == -1) { + if (PM_TYPE(v->pm->flags) == PM_HASHED) + arrhashsetfn(v->pm, val); + else + (v->pm->sets.afn) (v->pm, val); + } else { char **old, **new, **p, **q, **r; int n, ll, i; + if ((PM_TYPE(v->pm->flags) == PM_HASHED)) { + freearray(val); + zerr("attempt to set slice of associative array", NULL, 0); + return; + } if (v->inv && unset(KSHARRAYS)) v->a--, v->b--; q = old = v->pm->gets.afn(v->pm); @@ -1150,25 +1644,45 @@ setarrvalue(Value v, char **val) /* Retrieve an integer parameter */ /**/ -long +mod_export zlong getiparam(char *s) { + struct value vbuf; Value v; - if (!(v = getvalue(&s, 1))) + 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 */ /**/ -char * +mod_export char * getsparam(char *s) { + struct value vbuf; Value v; - if (!(v = getvalue(&s, 0))) + if (!(v = getvalue(&vbuf, &s, 0))) return NULL; return getstrvalue(v); } @@ -1176,21 +1690,38 @@ getsparam(char *s) /* Retrieve an array parameter */ /**/ -char ** +mod_export char ** getaparam(char *s) { + struct value vbuf; Value v; - if (!idigit(*s) && (v = getvalue(&s, 0)) && + if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) && PM_TYPE(v->pm->flags) == PM_ARRAY) return v->pm->gets.afn(v->pm); return NULL; } +/* Retrieve an assoc array parameter as an array */ + /**/ -Param +mod_export char ** +gethparam(char *s) +{ + struct value vbuf; + Value v; + + if (!idigit(*s) && (v = getvalue(&vbuf, &s, 0)) && + PM_TYPE(v->pm->flags) == PM_HASHED) + return paramvalarr(v->pm->gets.hfn(v->pm), SCANPM_WANTVALS); + return NULL; +} + +/**/ +mod_export Param setsparam(char *s, char *val) { + struct value vbuf; Value v; char *t = s; char *ss; @@ -1203,21 +1734,21 @@ setsparam(char *s, char *val) } if ((ss = strchr(s, '['))) { *ss = '\0'; - if (!(v = getvalue(&s, 1))) + if (!(v = getvalue(&vbuf, &s, 1))) createparam(t, PM_ARRAY); *ss = '['; v = NULL; } else { - if (!(v = getvalue(&s, 1))) + if (!(v = getvalue(&vbuf, &s, 1))) createparam(t, PM_SCALAR); - else if (PM_TYPE(v->pm->flags) == PM_ARRAY && - !(v->pm->flags & PM_SPECIAL) && unset(KSHARRAYS)) { + else if ((PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) && + !(v->pm->flags & (PM_SPECIAL|PM_TIED)) && unset(KSHARRAYS)) { unsetparam(t); createparam(t, PM_SCALAR); v = NULL; } } - if (!v && !(v = getvalue(&t, 1))) { + if (!v && !(v = getvalue(&vbuf, &t, 1))) { zsfree(val); return NULL; } @@ -1226,9 +1757,10 @@ setsparam(char *s, char *val) } /**/ -Param +mod_export Param setaparam(char *s, char **val) { + struct value vbuf; Value v; char *t = s; char *ss; @@ -1241,15 +1773,21 @@ setaparam(char *s, char **val) } if ((ss = strchr(s, '['))) { *ss = '\0'; - if (!(v = getvalue(&s, 1))) + if (!(v = getvalue(&vbuf, &s, 1))) createparam(t, PM_ARRAY); *ss = '['; + if (v && PM_TYPE(v->pm->flags) == PM_HASHED) { + zerr("attempt to set slice of associative array", NULL, 0); + freearray(val); + errflag = 1; + return NULL; + } v = NULL; } else { - if (!(v = getvalue(&s, 1))) + if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) createparam(t, PM_ARRAY); - else if (PM_TYPE(v->pm->flags) != PM_ARRAY && - !(v->pm->flags & PM_SPECIAL)) { + else if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) && + !(v->pm->flags & (PM_SPECIAL|PM_TIED))) { int uniq = v->pm->flags & PM_UNIQUE; unsetparam(t); createparam(t, PM_ARRAY | uniq); @@ -1257,54 +1795,126 @@ setaparam(char *s, char **val) } } if (!v) - if (!(v = getvalue(&t, 1))) + if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) return NULL; - if (isset(KSHARRAYS) && !ss) - /* the whole array should be set instead of only the first element */ - v->b = -1; setarrvalue(v, val); return v->pm; } /**/ -Param -setiparam(char *s, long val) +mod_export Param +sethparam(char *s, char **val) { + struct value vbuf; + Value v; + char *t = s; + + if (!isident(s)) { + zerr("not an identifier: %s", s, 0); + freearray(val); + errflag = 1; + return NULL; + } + if (strchr(s, '[')) { + freearray(val); + zerr("nested associative arrays not yet supported", NULL, 0); + errflag = 1; + return NULL; + } else { + if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) + createparam(t, PM_HASHED); + else if (!(PM_TYPE(v->pm->flags) & PM_HASHED) && + !(v->pm->flags & PM_SPECIAL)) { + unsetparam(t); + createparam(t, PM_HASHED); + v = NULL; + } + } + if (!v) + if (!(v = fetchvalue(&vbuf, &t, 1, SCANPM_ASSIGNING))) + return NULL; + setarrvalue(v, val); + return v->pm; +} + +/**/ +mod_export Param +setiparam(char *s, zlong val) +{ + struct value vbuf; Value v; char *t = s; Param pm; + mnumber mnval; if (!isident(s)) { zerr("not an identifier: %s", s, 0); errflag = 1; return NULL; } - if (!(v = getvalue(&s, 1))) { + if (!(v = getvalue(&vbuf, &s, 1))) { pm = createparam(t, PM_INTEGER); DPUTS(!pm, "BUG: parameter not created"); pm->u.val = val; return pm; } - setintvalue(v, val); + mnval.type = MN_INTEGER; + mnval.u.l = val; + setnumvalue(v, mnval); + return v->pm; +} + +/* + * Like setiparam(), but can take an mnumber which can be integer or + * floating. + */ + +/**/ +Param +setnparam(char *s, mnumber val) +{ + struct value vbuf; + Value v; + char *t = s; + Param pm; + + if (!isident(s)) { + zerr("not an identifier: %s", s, 0); + errflag = 1; + return NULL; + } + if (!(v = getvalue(&vbuf, &s, 1))) { + pm = createparam(t, (val.type & MN_INTEGER) ? PM_INTEGER + : PM_FFLOAT); + DPUTS(!pm, "BUG: parameter not created"); + if (val.type & MN_INTEGER) + pm->u.val = val.u.l; + else + pm->u.dval = val.u.d; + return pm; + } + setnumvalue(v, val); return v->pm; } /* Unset a parameter */ /**/ -void +mod_export void unsetparam(char *s) { Param pm; - if ((pm = (Param) paramtab->getnode(paramtab, s))) + if ((pm = (Param) (paramtab == realparamtab ? + gethashnode2(paramtab, s) : + paramtab->getnode(paramtab, s)))) unsetparam_pm(pm, 0, 1); } /* Unset a parameter */ /**/ -void +mod_export void unsetparam_pm(Param pm, int altflag, int exp) { Param oldpm, altpm; @@ -1331,22 +1941,30 @@ unsetparam_pm(Param pm, int altflag, int exp) unsetparam_pm(altpm, 1, exp); } - /* 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". */ - if (locallevel >= pm->level) + /* + * 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->flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL) return; - paramtab->removenode(paramtab, pm->nam); /* remove parameter node from table */ + /* remove parameter node from table */ + paramtab->removenode(paramtab, pm->nam); if (pm->old) { oldpm = pm->old; paramtab->addnode(paramtab, oldpm->nam, oldpm); - if ((PM_TYPE(oldpm->flags) == PM_SCALAR) && oldpm->sets.cfn == strsetfn) + if ((PM_TYPE(oldpm->flags) == PM_SCALAR) && + oldpm->sets.cfn == strsetfn) adduserdir(oldpm->nam, oldpm->u.str, 0, 0); } @@ -1357,12 +1975,13 @@ unsetparam_pm(Param pm, int altflag, int exp) * the specific set function. */ /**/ -void +mod_export void stdunsetfn(Param pm, int exp) { switch (PM_TYPE(pm->flags)) { case PM_SCALAR: pm->sets.cfn(pm, NULL); break; case PM_ARRAY: pm->sets.afn(pm, NULL); break; + case PM_HASHED: pm->sets.hfn(pm, NULL); break; } pm->flags |= PM_UNSET; } @@ -1370,7 +1989,7 @@ stdunsetfn(Param pm, int exp) /* Function to get value of an integer parameter */ /**/ -static long +static zlong intgetfn(Param pm) { return pm->u.val; @@ -1380,15 +1999,33 @@ intgetfn(Param pm) /**/ static void -intsetfn(Param pm, long x) +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 */ /**/ -char * +mod_export char * strgetfn(Param pm) { return pm->u.str ? pm->u.str : (char *) hcalloc(1); @@ -1408,7 +2045,7 @@ strsetfn(Param pm, char *x) /* Function to get value of an array parameter */ /**/ -static char ** +char ** arrgetfn(Param pm) { static char *nullarray = NULL; @@ -1419,7 +2056,7 @@ arrgetfn(Param pm) /* Function to set value of an array parameter */ /**/ -static void +mod_export void arrsetfn(Param pm, char **x) { if (pm->u.arr && pm->u.arr != x) @@ -1427,6 +2064,70 @@ arrsetfn(Param pm, char **x) if (pm->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); +} + +/* 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 set value of an association parameter using key/value pairs */ + +/**/ +static void +arrhashsetfn(Param pm, char **val) +{ + /* 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->sets.hfn() what to do. */ + int alen = arrlen(val); + HashTable opmtab = paramtab, ht = 0; + char **aptr = val; + Value v = (Value) hcalloc(sizeof *v); + v->b = -1; + + if (alen % 2) { + freearray(val); + zerr("bad set of key/value pairs for associative array", + NULL, 0); + return; + } + if (alen) + ht = paramtab = newparamtable(17, pm->nam); + while (*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. */ + setstrvalue(v, *aptr++); + } + paramtab = opmtab; + pm->sets.hfn(pm, ht); + free(val); /* not freearray() */ } /* This function is used as the set function for * @@ -1444,10 +2145,10 @@ nullsetfn(Param pm, char *x) * containing the integer value. */ /**/ -long +mod_export zlong intvargetfn(Param pm) { - return *((long *)pm->u.data); + return *((zlong *)pm->u.data); } /* Function to set value of generic special integer * @@ -1455,10 +2156,10 @@ intvargetfn(Param pm) * where the value is to be stored. */ /**/ -void -intvarsetfn(Param pm, long x) +mod_export void +intvarsetfn(Param pm, zlong x) { - *((long *)pm->u.data) = x; + *((zlong *)pm->u.data) = x; } /* Function to set value of any ZLE-related integer * @@ -1467,25 +2168,13 @@ intvarsetfn(Param pm, long x) /**/ void -zlevarsetfn(Param pm, long x) +zlevarsetfn(Param pm, zlong x) { - if ((long *)pm->u.data == & columns) { - if(x <= 0) - x = tccolumns > 0 ? tccolumns : 80; - if (x > 2) - termflags &= ~TERM_NARROW; - else - termflags |= TERM_NARROW; - } else if ((long *)pm->u.data == & lines) { - if(x <= 0) - x = tclines > 0 ? tclines : 24; - if (x > 2) - termflags &= ~TERM_SHORT; - else - termflags |= TERM_SHORT; - } + zlong *p = (zlong *)pm->u.data; - *((long *)pm->u.data) = x; + *p = x; + if (p == &lines || p == &columns) + adjustwinsize(2 + (p == &columns)); } /* Function to set value of generic special scalar * @@ -1493,7 +2182,7 @@ zlevarsetfn(Param pm, long x) * representing the scalar (string). */ /**/ -void +mod_export void strvarsetfn(Param pm, char *x) { char **q = ((char **)pm->u.data); @@ -1507,7 +2196,7 @@ strvarsetfn(Param pm, char *x) * representing the scalar (string). */ /**/ -char * +mod_export char * strvargetfn(Param pm) { char *s = *((char **)pm->u.data); @@ -1523,7 +2212,7 @@ strvargetfn(Param pm) * of pointers). */ /**/ -char ** +mod_export char ** arrvargetfn(Param pm) { return *((char ***)pm->u.data); @@ -1536,7 +2225,7 @@ arrvargetfn(Param pm) * version of this array which will need to be updated. */ /**/ -void +mod_export void arrvarsetfn(Param pm, char **x) { char ***dptr = (char ***)pm->u.data; @@ -1554,7 +2243,8 @@ arrvarsetfn(Param pm, char **x) char * colonarrgetfn(Param pm) { - return zjoin(*(char ***)pm->u.data, ':'); + char ***dptr = (char ***)pm->u.data; + return *dptr ? zjoin(*dptr, ':', 1) : ""; } /**/ @@ -1563,8 +2253,15 @@ colonarrsetfn(Param pm, char *x) { char ***dptr = (char ***)pm->u.data; - freearray(*dptr); - *dptr = x ? colonsplit(x, pm->flags & PM_UNIQUE) : mkarray(NULL); + /* + * If this is tied to a parameter (rather than internal) array, + * the array itself may be NULL. Otherwise, we have to make + * sure it doesn't ever get null. + */ + if (*dptr) + freearray(*dptr); + *dptr = x ? colonsplit(x, pm->flags & PM_UNIQUE) : + (pm->flags & PM_TIED) ? NULL : mkarray(NULL); if (pm->ename) arrfixenv(pm->nam, *dptr); zsfree(x); @@ -1593,7 +2290,7 @@ uniqarray(char **x) /* Function to get value of special parameter `#' and `ARGC' */ /**/ -long +zlong poundgetfn(Param pm) { return arrlen(pparams); @@ -1602,7 +2299,7 @@ poundgetfn(Param pm) /* Function to get value for special parameter `RANDOM' */ /**/ -long +zlong randomgetfn(Param pm) { return rand() & 0x7fff; @@ -1612,7 +2309,7 @@ randomgetfn(Param pm) /**/ void -randomsetfn(Param pm, long v) +randomsetfn(Param pm, zlong v) { srand((unsigned int)v); } @@ -1620,7 +2317,7 @@ randomsetfn(Param pm, long v) /* Function to get value for special parameter `SECONDS' */ /**/ -long +zlong secondsgetfn(Param pm) { return time(NULL) - shtimer.tv_sec; @@ -1630,7 +2327,7 @@ secondsgetfn(Param pm) /**/ void -secondssetfn(Param pm, long x) +secondssetfn(Param pm, zlong x) { shtimer.tv_sec = time(NULL) - x; shtimer.tv_usec = 0; @@ -1670,7 +2367,7 @@ usernamesetfn(Param pm, char *x) /* Function to get value for special parameter `UID' */ /**/ -long +zlong uidgetfn(Param pm) { return getuid(); @@ -1690,7 +2387,7 @@ uidsetfn(Param pm, uid_t x) /* Function to get value for special parameter `EUID' */ /**/ -long +zlong euidgetfn(Param pm) { return geteuid(); @@ -1710,7 +2407,7 @@ euidsetfn(Param pm, uid_t x) /* Function to get value for special parameter `GID' */ /**/ -long +zlong gidgetfn(Param pm) { return getgid(); @@ -1730,7 +2427,7 @@ gidsetfn(Param pm, gid_t x) /* Function to get value for special parameter `EGID' */ /**/ -long +zlong egidgetfn(Param pm) { return getegid(); @@ -1748,7 +2445,7 @@ egidsetfn(Param pm, gid_t x) } /**/ -long +zlong ttyidlegetfn(Param pm) { struct stat ttystat; @@ -1780,7 +2477,7 @@ ifssetfn(Param pm, char *x) /* Functions to set value of special parameters `LANG' and `LC_*' */ -#ifdef LC_ALL +#ifdef USE_LOCALE static struct localename { char *name; int category; @@ -1794,6 +2491,9 @@ static struct localename { #ifdef LC_MESSAGES {"LC_MESSAGES", LC_MESSAGES}, #endif +#ifdef LC_NUMERIC + {"LC_NUMERIC", LC_NUMERIC}, +#endif #ifdef LC_TIME {"LC_TIME", LC_TIME}, #endif @@ -1847,12 +2547,12 @@ lcsetfn(Param pm, char *x) if (!strcmp(ln->name, pm->nam)) setlocale(ln->category, x ? x : ""); } -#endif +#endif /* USE_LOCALE */ /* Function to get value for special parameter `HISTSIZE' */ /**/ -long +zlong histsizegetfn(Param pm) { return histsiz; @@ -1862,7 +2562,7 @@ histsizegetfn(Param pm) /**/ void -histsizesetfn(Param pm, long v) +histsizesetfn(Param pm, zlong v) { if ((histsiz = v) <= 2) histsiz = 2; @@ -1872,7 +2572,7 @@ histsizesetfn(Param pm, long v) /* Function to get value for special parameter `ERRNO' */ /**/ -long +zlong errnogetfn(Param pm) { return errno; @@ -1961,7 +2661,10 @@ wordcharssetfn(Param pm, char *x) char * underscoregetfn(Param pm) { - return underscore; + char *u = dupstring(underscore); + + untokenize(u); + return u; } /* Function to get value for special parameter `TERM' */ @@ -1989,6 +2692,38 @@ termsetfn(Param pm, char *x) init_term(); } +/* Function to get value for special parameter `pipestatus' */ + +/**/ +static char ** +pipestatgetfn(Param pm) +{ + char **x = (char **) zhalloc((numpipestats + 1) * sizeof(char *)); + char buf[20], **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(Param pm, char **x) +{ + int i; + + for (i = 0; *x && i < MAX_PIPESTATS; i++, x++) + pipestats[i] = atoi(*x); + numpipestats = i; +} + /* We could probably replace the replenv with the actual code to * * do the replacing, since we've already scanned for the string. */ @@ -2000,28 +2735,33 @@ arrfixenv(char *s, char **t) int len_s; Param pm; - MUSTUSEHEAP("arrfixenv"); + pm = (Param) paramtab->getnode(paramtab, s); + /* + * Only one level of a parameter can be exported. Unless + * ALLEXPORT is set, this must be global. + */ if (t == path) cmdnamtab->emptytable(cmdnamtab); - u = zjoin(t, ':'); + if (isset(ALLEXPORT) ? !!pm->old : pm->level) + return; + u = t ? zjoin(t, ':', 1) : ""; len_s = strlen(s); - pm = (Param) paramtab->getnode(paramtab, s); for (ep = environ; *ep; ep++) if (!strncmp(*ep, s, len_s) && (*ep)[len_s] == '=') { - pm->env = replenv(*ep, u); + pm->env = replenv(*ep, u, pm->flags); return; } if (isset(ALLEXPORT)) pm->flags |= PM_EXPORTED; if (pm->flags & PM_EXPORTED) - pm->env = addenv(s, u); + pm->env = addenv(s, u, pm->flags); } /* Given *name = "foo", it searchs the environment for string * * "foo=bar", and returns a pointer to the beginning of "bar" */ /**/ -char * +mod_export char * zgetenv(char *name) { char **ep, *s, *t; @@ -2034,11 +2774,25 @@ zgetenv(char *name) return NULL; } +/**/ +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); + } +} + /* Change the value of an existing environment variable */ /**/ char * -replenv(char *e, char *value) +replenv(char *e, char *value, int flags) { char **ep, *s; int len_value; @@ -2051,9 +2805,7 @@ replenv(char *e, char *value) while (*s++ != '='); *ep = (char *) zrealloc(e, s - e + len_value + 1); s = s - e + *ep - 1; - while (*s++) - if ((*s = *value++) == Meta) - *s = *value++ ^ 32; + copyenvstr(s, value, flags); return *ep; } return NULL; @@ -2064,7 +2816,7 @@ replenv(char *e, char *value) /**/ static char * -mkenvstr(char *name, char *value) +mkenvstr(char *name, char *value, int flags) { char *str, *s; int len_name, len_value; @@ -2076,9 +2828,7 @@ mkenvstr(char *name, char *value) strcpy(s, name); s += len_name; *s = '='; - while (*s++) - if ((*s = *value++) == Meta) - *s = *value++ ^ 32; + copyenvstr(s, value, flags); return str; } @@ -2089,7 +2839,7 @@ mkenvstr(char *name, char *value) /**/ char * -addenv(char *name, char *value) +addenv(char *name, char *value, int flags) { char **ep, *s, *t; int num_env; @@ -2100,7 +2850,7 @@ addenv(char *name, char *value) for (s = *ep, t = name; *s && *s == *t; s++, t++); if (*s == '=' && !*t) { zsfree(*ep); - return *ep = mkenvstr(name, value); + return *ep = mkenvstr(name, value, flags); } } @@ -2110,7 +2860,7 @@ addenv(char *name, char *value) /* Now add it at the end */ ep = environ + num_env; - *ep = mkenvstr(name, value); + *ep = mkenvstr(name, value, flags); *(ep + 1) = NULL; return *ep; } @@ -2133,11 +2883,11 @@ delenv(char *x) } /**/ -static void -convbase(char *s, long v, int base) +mod_export void +convbase(char *s, zlong v, int base) { int digs = 0; - unsigned long x; + zulong x; if (v < 0) *s++ = '-', v = -v; @@ -2162,10 +2912,65 @@ convbase(char *s, long v, int base) } } +/* + * 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"; + + /* + * 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--; + } + } + if (fout) { + fprintf(fout, fmt, digits, dval); + return NULL; + } else { + VARARR(char, buf, 512 + digits); + sprintf(buf, fmt, digits, dval); + return dupstring(buf); + } +} + /* Start a parameter scope */ /**/ -void +mod_export void startparamscope(void) { locallevel++; @@ -2174,7 +2979,7 @@ startparamscope(void) /* End a parameter scope: delete the parameters local to the scope. */ /**/ -void +mod_export void endparamscope(void) { locallevel--; @@ -2186,6 +2991,187 @@ static void scanendscope(HashNode hn, int flags) { Param pm = (Param)hn; - if(pm->level > locallevel) - unsetparam_pm(pm, 0, 0); + if (pm->level > locallevel) { + if ((pm->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; + + DPUTS(!tpm || PM_TYPE(pm->flags) != PM_TYPE(tpm->flags) || + !(tpm->flags & PM_SPECIAL), + "BUG: in restoring scope of special parameter"); + pm->old = tpm->old; + pm->flags = (tpm->flags & ~PM_NORESTORE); + pm->level = tpm->level; + pm->ct = tpm->ct; + pm->env = tpm->env; + + if (!(tpm->flags & PM_NORESTORE)) + switch (PM_TYPE(pm->flags)) { + case PM_SCALAR: + pm->sets.cfn(pm, tpm->u.str); + break; + case PM_INTEGER: + pm->sets.ifn(pm, tpm->u.val); + break; + case PM_EFLOAT: + case PM_FFLOAT: + pm->sets.ffn(pm, tpm->u.dval); + break; + case PM_ARRAY: + pm->sets.afn(pm, tpm->u.arr); + break; + case PM_HASHED: + pm->sets.hfn(pm, tpm->u.hash); + break; + } + zfree(tpm, sizeof(*tpm)); + } else + unsetparam_pm(pm, 0, 0); + } +} + + +/**********************************/ +/* Parameter Hash Table Functions */ +/**********************************/ + +/**/ +void +freeparamnode(HashNode hn) +{ + Param pm = (Param) hn; + + /* Since the second flag to unsetfn isn't used, I don't * + * know what its value should be. */ + if (delunset) + pm->unsetfn(pm, 1); + zsfree(pm->nam); + /* If this variable was tied by the user, ename was ztrdup'd */ + if (pm->flags & PM_TIED) + zsfree(pm->ename); + zfree(pm, sizeof(struct param)); +} + +/* Print a parameter */ + +/**/ +mod_export void +printparamnode(HashNode hn, int printflags) +{ + Param p = (Param) hn; + char *t, **u; + + if (p->flags & PM_UNSET) + return; + + /* Print the attributes of the parameter */ + if (printflags & PRINT_TYPE) { + if (p->flags & PM_AUTOLOAD) + printf("undefined "); + if (p->flags & PM_INTEGER) + printf("integer "); + if (p->flags & (PM_EFLOAT|PM_FFLOAT)) + printf("float "); + else if (p->flags & PM_ARRAY) + printf("array "); + else if (p->flags & PM_HASHED) + printf("association "); + if (p->level) + printf("local "); + if (p->flags & PM_LEFT) + printf("left justified %d ", p->ct); + if (p->flags & PM_RIGHT_B) + printf("right justified %d ", p->ct); + if (p->flags & PM_RIGHT_Z) + printf("zero filled %d ", p->ct); + if (p->flags & PM_LOWER) + printf("lowercase "); + if (p->flags & PM_UPPER) + printf("uppercase "); + if (p->flags & PM_READONLY) + printf("readonly "); + if (p->flags & PM_TAGGED) + printf("tagged "); + if (p->flags & PM_EXPORTED) + printf("exported "); + } + + if (printflags & PRINT_NAMEONLY) { + zputs(p->nam, stdout); + putchar('\n'); + return; + } + + quotedzputs(p->nam, stdout); + + if (p->flags & PM_AUTOLOAD) { + putchar('\n'); + return; + } + if (printflags & PRINT_KV_PAIR) + putchar(' '); + else + putchar('='); + + /* How the value is displayed depends * + * on the type of the parameter */ + switch (PM_TYPE(p->flags)) { + case PM_SCALAR: + /* string: simple output */ + if (p->gets.cfn && (t = p->gets.cfn(p))) + quotedzputs(t, stdout); + break; + case PM_INTEGER: + /* integer */ +#ifdef ZSH_64_BIT_TYPE + fputs(output64(p->gets.ifn(p)), stdout); +#else + printf("%ld", p->gets.ifn(p)); +#endif + break; + case PM_EFLOAT: + case PM_FFLOAT: + /* float */ + convfloat(p->gets.ffn(p), p->ct, p->flags, stdout); + break; + case PM_ARRAY: + /* array */ + if (!(printflags & PRINT_KV_PAIR)) + putchar('('); + u = p->gets.afn(p); + if(*u) { + quotedzputs(*u++, stdout); + while (*u) { + putchar(' '); + quotedzputs(*u++, stdout); + } + } + if (!(printflags & PRINT_KV_PAIR)) + putchar(')'); + break; + case PM_HASHED: + /* association */ + if (!(printflags & PRINT_KV_PAIR)) + putchar('('); + { + HashTable ht = p->gets.hfn(p); + if (ht) + scanhashtable(ht, 0, 0, PM_UNSET, + ht->printnode, PRINT_KV_PAIR); + } + if (!(printflags & PRINT_KV_PAIR)) + putchar(')'); + break; + } + if (printflags & PRINT_KV_PAIR) + putchar(' '); + else + putchar('\n'); } diff --git a/Src/parse.c b/Src/parse.c index d42be2f2f..3ffed46d7 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -33,17 +33,17 @@ /* != 0 if we are about to read a command word */ /**/ -int incmdpos; +mod_export int incmdpos; /* != 0 if we are in the middle of a [[ ... ]] */ /**/ -int incond; +mod_export int incond; /* != 0 if we are after a redirection (for ctxtlex only) */ /**/ -int inredir; +mod_export int inredir; /* != 0 if we are about to read a case pattern */ @@ -65,107 +65,477 @@ int infor; /**/ struct heredocs *hdocs; -/* used in arrays of lists instead of NULL pointers */ - + +#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,0); \ + herrflush(); \ + if (noerrs != 2) \ + errflag = 1; \ + YYERROR(ecused) \ +} while(0) + + +/* + * Word code. + * + * For now we simply post-process the syntax tree produced by the + * parser. We compile it into a struct eprog. Some day the parser + * above should be changed to emit the word code directly. + * + * Word code layout: + * + * WC_END + * - end of program code + * + * WC_LIST + * - data contains type (sync, ...) + * - follwed 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 + * + * WC_ASSIGN + * - data contains type (scalar, array) and number of array-elements + * - followed by name and value + * + * WC_SIMPLE + * - data contains the number of arguments (plus command) + * - followed by strings + * + * 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 + * - follwoed 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 ofsset 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 (;&), data is offset to + * next case + * - each OR/AND 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 `complex', 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 ecstr() function that adds the code for a string uses a simple + * list 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; /**/ -struct list dummy_list; +int ecsoffs, ecssub, ecnfunc; -#define YYERROR { tok = LEXERR; return NULL; } -#define YYERRORV { tok = LEXERR; return; } -#define COND_ERROR(X,Y) do{herrflush();zerr(X,Y,0);YYERROR}while(0) +/* Adjust pointers in here-doc structs. */ -#define make_list() allocnode(N_LIST) -#define make_sublist() allocnode(N_SUBLIST) -#define make_pline() allocnode(N_PLINE) -#define make_cmd() allocnode(N_CMD) -#define make_forcmd() allocnode(N_FOR) -#define make_casecmd() allocnode(N_CASE) -#define make_ifcmd() allocnode(N_IF) -#define make_whilecmd() allocnode(N_WHILE) -#define make_varnode() allocnode(N_VARASG) -#define make_cond() allocnode(N_COND) +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 = (n > 256 ? n : 256); + + ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode), + (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) { + ecbuf = (Wordcode) hrealloc((char *) ecbuf, eclen * sizeof(wordcode), + (eclen + 256) * sizeof(wordcode)); + eclen += 256; + } + ecbuf[ecused] = c; + ecused++; + + return ecused - 1; +} + +/* 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 = has_token(s); + + if ((l = strlen(s) + 1) && l <= 4) { + 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, q = NULL; + + for (p = ecstrs; p; q = p, p = p->next) + if (p->nfunc == ecnfunc && !strcmp(s, p->str)) + return p->offs; + + p = (Eccstr) zhalloc(sizeof(*p)); + p->next = NULL; + if (q) + q->next = p; + else + ecstrs = p; + p->offs = ((ecsoffs - ecssub) << 2) | (t ? 1 : 0); + p->str = s; + p->nfunc = ecnfunc; + ecsoffs += l; + + return p->offs; + } +} + +static int +ecstr(char *s) +{ + return 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) + + +/* Initialise wordcode buffer. */ + +static void +init_parse(void) +{ + ecbuf = (Wordcode) zhalloc((eclen = 256) * sizeof(wordcode)); + ecused = 0; + ecstrs = NULL; + ecsoffs = ecnpats = 0; + ecssub = 0; + ecnfunc = 0; +} + +/* Build eprog. */ + +static Eprog +bld_eprog(void) +{ + Eprog ret; + Eccstr p; + char *q; + int l; + + ecadd(WCB_END()); + + ret = (Eprog) zhalloc(sizeof(*ret)); + ret->len = ((ecnpats * sizeof(Patprog)) + + (ecused * sizeof(wordcode)) + + ecsoffs); + ret->npats = ecnpats; + ret->pats = (Patprog *) zhalloc(ret->len); + ret->prog = (Wordcode) (ret->pats + ecnpats); + ret->strs = (char *) (ret->prog + ecused); + ret->shf = NULL; + ret->flags = EF_HEAP; + ret->dump = NULL; + for (l = 0; l < ecnpats; l++) + ret->pats[l] = dummy_patprog1; + memcpy(ret->prog, ecbuf, ecused * sizeof(wordcode)); + for (p = ecstrs, q = ret->strs; p; p = p->next, q += l) { + l = strlen(p->str) + 1; + memcpy(q, p->str, l); + } + return ret; +} /* * event : ENDINPUT * | SEPER * | sublist [ SEPER | AMPER | AMPERBANG ] */ + /**/ -List +Eprog parse_event(void) { tok = ENDINPUT; incmdpos = 1; yylex(); - return par_event(); + init_parse(); + return ((par_event()) ? bld_eprog() : NULL); } /**/ -static List +static int par_event(void) { - Sublist sl; - List l = NULL; + int r = 0, p, c = 0; while (tok == SEPER) { if (isnewlin > 0) - return NULL; + return 0; yylex(); } if (tok == ENDINPUT) - return NULL; - if ((sl = par_sublist())) + return 0; + + p = ecadd(0); + + if (par_sublist(&c)) { if (tok == ENDINPUT) { - l = (List) make_list(); - l->type = Z_SYNC; - l->left = sl; + set_list_code(p, Z_SYNC, c); + r = 1; } else if (tok == SEPER) { - l = (List) make_list(); - l->type = Z_SYNC; - l->left = sl; + set_list_code(p, Z_SYNC, c); if (isnewlin <= 0) yylex(); + r = 1; } else if (tok == AMPER) { - l = (List) make_list(); - l->type = Z_ASYNC; - l->left = sl; + set_list_code(p, Z_ASYNC, c); yylex(); + r = 1; } else if (tok == AMPERBANG) { - l = (List) make_list(); - l->type = Z_ASYNC | Z_DISOWN; - l->left = sl; + set_list_code(p, (Z_ASYNC | Z_DISOWN), c); yylex(); - } else - l = NULL; - if (!l) { + r = 1; + } + } + if (!r) { if (errflag) { - yyerror(); - return NULL; + yyerror(0); + ecused--; + return 0; } + yyerror(1); herrflush(); - yyerror(); - return NULL; + if (noerrs != 2) + errflag = 1; + ecused--; + return 0; } else { - l->right = par_event(); + int oec = ecused; + + par_event(); + if (ecused == oec) + ecbuf[p] |= wc_bdata(Z_END); } - return l; + return 1; } /**/ -List +mod_export Eprog parse_list(void) { - List ret; + int c = 0; tok = ENDINPUT; incmdpos = 1; yylex(); - ret = par_list(); - if (tok == LEXERR) { - yyerror(); + init_parse(); + par_list(&c); +#if 0 + if (tok == LEXERR) +#endif + if (tok != ENDINPUT) { + yyerror(0); return NULL; } - return ret; + return bld_eprog(); +} + +/**/ +mod_export Eprog +parse_cond(void) +{ + init_parse(); + + if (!par_cond()) + return NULL; + + return bld_eprog(); +} + +/* 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 complex) +{ + if (!complex && (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 complex) +{ + if (complex) + 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]); + } } /* @@ -173,46 +543,60 @@ parse_list(void) */ /**/ -static List -par_list(void) +static int +par_list(int *complex) { - Sublist sl; - List l = NULL; + int p, lp = -1, c; + + rec: while (tok == SEPER) yylex(); - if ((sl = par_sublist())) + + p = ecadd(0); + c = 0; + + if (par_sublist(&c)) { + *complex |= c; if (tok == SEPER || tok == AMPER || tok == AMPERBANG) { - l = (List) make_list(); - l->left = sl; - l->type = (tok == SEPER) ? Z_SYNC : - (tok == AMPER) ? Z_ASYNC : Z_ASYNC | Z_DISOWN; + if (tok != SEPER) + *complex = 1; + set_list_code(p, ((tok == SEPER) ? Z_SYNC : + (tok == AMPER) ? Z_ASYNC : + (Z_ASYNC | Z_DISOWN)), c); incmdpos = 1; do { yylex(); } while (tok == SEPER); - l->right = par_list(); - } else { - l = (List) make_list(); - l->left = sl; - l->type = Z_SYNC; + lp = p; + goto rec; + } else + set_list_code(p, (Z_SYNC | Z_END), c); + return 1; + } else { + ecused--; + if (lp >= 0) { + ecbuf[lp] |= wc_bdata(Z_END); + return 1; } - return l; + return 0; + } } /**/ -static List -par_list1(void) +static int +par_list1(int *complex) { - Sublist sl; - List l = NULL; + int p = ecadd(0), c = 0; - if ((sl = par_sublist())) { - l = (List) make_list(); - l->type = Z_SYNC; - l->left = sl; + if (par_sublist(&c)) { + set_list_code(p, (Z_SYNC | Z_END), c); + *complex |= c; + return 1; + } else { + ecused--; + return 0; } - return l; } /* @@ -220,24 +604,37 @@ par_list1(void) */ /**/ -static Sublist -par_sublist(void) +static int +par_sublist(int *complex) { - Sublist sl; + int f, p, c = 0; - if ((sl = par_sublist2())) + p = ecadd(0); + + if ((f = par_sublist2(&c)) != -1) { + int e = ecused; + + *complex |= c; if (tok == DBAR || tok == DAMPER) { - int qtok = tok; + int qtok = tok, sl; cmdpush(tok == DBAR ? CS_CMDOR : CS_CMDAND); yylex(); while (tok == SEPER) yylex(); - sl->right = par_sublist(); - sl->type = (qtok == DBAR) ? ORNEXT : ANDNEXT; + sl = par_sublist(complex); + set_sublist_code(p, (sl ? (qtok == DBAR ? + WC_SUBLIST_OR : WC_SUBLIST_AND) : + WC_SUBLIST_END), + f, (e - 1 - p), c); cmdpop(); - } - return sl; + } else + set_sublist_code(p, WC_SUBLIST_END, f, (e - 1 - p), c); + return 1; + } else { + ecused--; + return 0; + } } /* @@ -245,24 +642,24 @@ par_sublist(void) */ /**/ -static Sublist -par_sublist2(void) +static int +par_sublist2(int *complex) { - Sublist sl; - Pline p; + int f = 0; - sl = (Sublist) make_sublist(); if (tok == COPROC) { - sl->flags |= PFLAG_COPROC; + *complex = 1; + f |= WC_SUBLIST_COPROC; yylex(); } else if (tok == BANG) { - sl->flags |= PFLAG_NOT; + *complex = 1; + f |= WC_SUBLIST_NOT; yylex(); } - if (!(p = par_pline()) && !sl->flags) - return NULL; - sl->left = p; - return sl; + if (!par_pline(complex) && !f) + return -1; + + return f; } /* @@ -270,48 +667,53 @@ par_sublist2(void) */ /**/ -static Pline -par_pline(void) +static int +par_pline(int *complex) { - Cmd c; - Pline p, p2; + int p, line = lineno; - if (!(c = par_cmd())) - return NULL; + p = ecadd(0); + + if (!par_cmd(complex)) { + ecused--; + return 0; + } if (tok == BAR) { + *complex = 1; cmdpush(CS_PIPE); yylex(); while (tok == SEPER) yylex(); - p2 = par_pline(); + ecbuf[p] = WCB_PIPE(WC_PIPE_MID, (line >= 0 ? line + 1 : 0)); + ecispace(p + 1, 1); + ecbuf[p + 1] = ecused - 1 - p; + par_pline(complex); cmdpop(); - p = (Pline) make_pline(); - p->left = c; - p->right = p2; - p->type = PIPE; - return p; + return 1; } else if (tok == BARAMP) { - struct redir *rdr = (struct redir *)allocnode(N_REDIR); + int r; + + for (r = p + 1; wc_code(ecbuf[r]) == WC_REDIR; r += 3); - rdr->type = MERGEOUT; - rdr->fd1 = 2; - rdr->name = dupstring("1"); - addlinknode(c->redir, rdr); + ecispace(r, 3); + ecbuf[r] = WCB_REDIR(MERGEOUT); + ecbuf[r + 1] = 2; + ecbuf[r + 2] = ecstrcode("1"); + *complex = 1; cmdpush(CS_ERRPIPE); yylex(); - p2 = par_pline(); + while (tok == SEPER) + yylex(); + ecbuf[p] = WCB_PIPE(WC_PIPE_MID, (line >= 0 ? line + 1 : 0)); + ecispace(p + 1, 1); + ecbuf[p + 1] = ecused - 1 - p; + par_pline(complex); cmdpop(); - p = (Pline) make_pline(); - p->left = c; - p->right = p2; - p->type = PIPE; - return p; + return 1; } else { - p = (Pline) make_pline(); - p->left = c; - p->type = END; - return p; + ecbuf[p] = WCB_PIPE(WC_PIPE_END, (line >= 0 ? line + 1 : 0)); + return 1; } } @@ -321,96 +723,116 @@ par_pline(void) */ /**/ -static Cmd -par_cmd(void) +static int +par_cmd(int *complex) { - Cmd c; + int r, nr = 0; + + r = ecused; - c = (Cmd) make_cmd(); - c->lineno = lineno; - c->args = newlinklist(); - c->redir = newlinklist(); - c->vars = newlinklist(); - while (IS_REDIROP(tok)) - par_redir(c->redir); + if (IS_REDIROP(tok)) { + *complex = 1; + while (IS_REDIROP(tok)) { + nr++; + par_redir(&r); + } + } switch (tok) { case FOR: cmdpush(CS_FOR); - par_for(c); + par_for(complex); cmdpop(); break; case FOREACH: cmdpush(CS_FOREACH); - par_for(c); + par_for(complex); cmdpop(); break; case SELECT: + *complex = 1; cmdpush(CS_SELECT); - par_for(c); + par_for(complex); cmdpop(); break; case CASE: cmdpush(CS_CASE); - par_case(c); + par_case(complex); cmdpop(); break; case IF: - par_if(c); + par_if(complex); break; case WHILE: cmdpush(CS_WHILE); - par_while(c); + par_while(complex); cmdpop(); break; case UNTIL: cmdpush(CS_UNTIL); - par_while(c); + par_while(complex); cmdpop(); break; case REPEAT: cmdpush(CS_REPEAT); - par_repeat(c); + par_repeat(complex); cmdpop(); break; case INPAR: + *complex = 1; cmdpush(CS_SUBSH); - par_subsh(c); + par_subsh(complex); cmdpop(); break; case INBRACE: cmdpush(CS_CURSH); - par_subsh(c); + par_subsh(complex); cmdpop(); break; case FUNC: cmdpush(CS_FUNCDEF); - par_funcdef(c); + par_funcdef(); cmdpop(); break; case TIME: - par_time(c); + *complex = 1; + par_time(); break; case DINBRACK: cmdpush(CS_COND); - par_dinbrack(c); + par_dinbrack(); cmdpop(); break; case DINPAR: - c->type = CARITH; - addlinknode(c->args, tokstr); + ecadd(WCB_ARITH()); + ecstr(tokstr); yylex(); break; default: - if (!par_simple(c)) - return NULL; + { + int sr; + + if (!(sr = par_simple(complex, nr))) { + if (!nr) + return 0; + } else { + /* Three codes per redirection. */ + if (sr > 1) { + *complex = 1; + r += (sr - 1) * 3; + } + } + } break; } - while (IS_REDIROP(tok)) - par_redir(c->redir); + if (IS_REDIROP(tok)) { + *complex = 1; + while (IS_REDIROP(tok)) + par_redir(&r); + } incmdpos = 1; incasepat = 0; incond = 0; - return c; + return 1; } /* @@ -421,82 +843,95 @@ par_cmd(void) /**/ static void -par_for(Cmd c) +par_for(int *complex) { - Forcmd f; - int csh = (tok == FOREACH); + int oecused = ecused, csh = (tok == FOREACH), p, sel = (tok == SELECT); + int type; + + p = ecadd(0); - f = (Forcmd) make_forcmd(); - c->type = (tok == SELECT) ? CSELECT : CFOR; incmdpos = 0; infor = tok == FOR ? 2 : 0; yylex(); if (tok == DINPAR) { yylex(); if (tok != DINPAR) - YYERRORV; - f->name = tokstr; + YYERRORV(oecused); + ecstr(tokstr); yylex(); if (tok != DINPAR) - YYERRORV; - f->condition = tokstr; + YYERRORV(oecused); + ecstr(tokstr); yylex(); if (tok != DOUTPAR) - YYERRORV; - f->advance = tokstr; + YYERRORV(oecused); + ecstr(tokstr); infor = 0; incmdpos = 1; yylex(); + type = WC_FOR_COND; } else { infor = 0; if (tok != STRING || !isident(tokstr)) - YYERRORV; - f->name = tokstr; + YYERRORV(oecused); + ecstr(tokstr); incmdpos = 1; yylex(); if (tok == STRING && !strcmp(tokstr, "in")) { - f->inflag = 1; + int np, n; + incmdpos = 0; yylex(); - c->args = par_wordlist(); + np = ecadd(0); + n = par_wordlist(); if (tok != SEPER) - YYERRORV; + YYERRORV(oecused); + ecbuf[np] = n; + type = (sel ? WC_SELECT_LIST : WC_FOR_LIST); } else if (tok == INPAR) { - f->inflag = 1; + int np, n; + incmdpos = 0; yylex(); - c->args = par_nl_wordlist(); + np = ecadd(0); + n = par_nl_wordlist(); if (tok != OUTPAR) - YYERRORV; + YYERRORV(oecused); + ecbuf[np] = n; incmdpos = 1; yylex(); - } + type = (sel ? WC_SELECT_LIST : WC_FOR_LIST); + } else + type = (sel ? WC_SELECT_PPARAM : WC_FOR_PPARAM); } incmdpos = 1; while (tok == SEPER) yylex(); if (tok == DO) { yylex(); - f->list = par_list(); + par_save_list(complex); if (tok != DONE) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (tok == INBRACE) { yylex(); - f->list = par_list(); + par_save_list(complex); if (tok != OUTBRACE) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (csh || isset(CSHJUNKIELOOPS)) { - f->list = par_list(); + par_save_list(complex); if (tok != ZEND) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (unset(SHORTLOOPS)) { - YYERRORV; + YYERRORV(oecused); } else - f->list = par_list1(); - c->u.forcmd = f; + par_save_list1(complex); + + ecbuf[p] = (sel ? + WCB_SELECT(type, ecused - 1 - p) : + WCB_FOR(type, ecused - 1 - p)); } /* @@ -508,35 +943,29 @@ par_for(Cmd c) /**/ static void -par_case(Cmd c) +par_case(int *complex) { - int brflag; - LinkList pats, lists; - int n = 1; - char **pp; - List *ll; - LinkNode no; - struct casecmd *cc; + int oecused = ecused, brflag, p, pp, n = 1, type; + + p = ecadd(0); - c->type = CCASE; incmdpos = 0; yylex(); if (tok != STRING) - YYERRORV; - pats = newlinklist(); - addlinknode(pats, tokstr); + YYERRORV(oecused); + ecstr(tokstr); + incmdpos = 1; yylex(); while (tok == SEPER) yylex(); if (!(tok == STRING && !strcmp(tokstr, "in")) && tok != INBRACE) - YYERRORV; + YYERRORV(oecused); brflag = (tok == INBRACE); incasepat = 1; incmdpos = 0; yylex(); - cc = c->u.casecmd = (struct casecmd *)make_casecmd(); - lists = newlinklist(); + for (;;) { char *str; @@ -545,14 +974,13 @@ par_case(Cmd c) if (tok == OUTBRACE) break; if (tok != STRING) - YYERRORV; + YYERRORV(oecused); if (!strcmp(tokstr, "esac")) break; - str = ncalloc(strlen(tokstr) + 2); - *str = ';'; - strcpy(str + 1, tokstr); + str = dupstring(tokstr); incasepat = 0; incmdpos = 1; + type = WC_CASE_OR; for (;;) { yylex(); if (tok == OUTPAR) { @@ -566,7 +994,7 @@ par_case(Cmd c) incasepat = 1; incmdpos = 0; - str2 = ncalloc(sl + 2); + str2 = hcalloc(sl + 2); strcpy(str2, str); str2[sl] = Bar; str2[sl+1] = '\0'; @@ -574,12 +1002,12 @@ par_case(Cmd c) } else { int sl = strlen(str); - if (str[sl - 1] != Bar) { + if (!sl || str[sl - 1] != Bar) { /* POSIX allows (foo*) patterns */ int pct; char *s; - for (s = str + 1, pct = 0; *s; s++) { + for (s = str, pct = 0; *s; s++) { if (*s == Inpar) pct++; if (!pct) @@ -590,54 +1018,53 @@ par_case(Cmd c) chuck(s+1); if (*s == Bar || *s == Outpar) while (iblank(s[-1]) && - (s < str+2 || s[-2] != Meta)) + (s < str + 1 || s[-2] != Meta)) chuck(--s); } if (*s == Outpar) pct--; } - if (*s || pct || s == str + 1) - YYERRORV; + 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); break; } else { char *str2; if (tok != STRING) - YYERRORV; - str2 = ncalloc(sl + strlen(tokstr) + 1); + YYERRORV(oecused); + str2 = hcalloc(sl + strlen(tokstr) + 1); strcpy(str2, str); strcpy(str2 + sl, tokstr); str = str2; } } } - addlinknode(pats, str); - addlinknode(lists, par_list()); + pp = ecadd(0); + ecstr(str); + ecadd(ecnpats++); + par_save_list(complex); n++; + if (tok == SEMIAMP) + type = WC_CASE_AND; + ecbuf[pp] = WCB_CASE(type, ecused - 1 - pp); if ((tok == ESAC && !brflag) || (tok == OUTBRACE && brflag)) break; - if(tok == SEMIAMP) - *str = '&'; - else if (tok != DSEMI) - YYERRORV; + if (tok != DSEMI && tok != SEMIAMP) + YYERRORV(oecused); incasepat = 1; incmdpos = 0; yylex(); } - incmdpos = 1; yylex(); - cc->pats = (char **)alloc((n + 1) * sizeof(char *)); - - for (pp = cc->pats, no = firstnode(pats); no; incnode(no)) - *pp++ = (char *)getdata(no); - *pp = NULL; - cc->lists = (List *) alloc((n + 1) * sizeof(List)); - for (ll = cc->lists, no = firstnode(lists); no; incnode(no), ll++) - if (!(*ll = (List) getdata(no))) - *ll = &dummy_list; - *ll = NULL; + ecbuf[p] = WCB_CASE(WC_CASE_HEAD, ecused - 1 - p); } /* @@ -649,20 +1076,13 @@ par_case(Cmd c) /**/ static void -par_if(Cmd c) +par_if(int *complex) { - struct ifcmd *i; - int xtok; + int oecused = ecused, xtok, p, pp, type, usebrace = 0; unsigned char nc; - LinkList ifsl, thensl; - LinkNode no; - int ni = 0, nt = 0, usebrace = 0; - List l, *ll; - ifsl = newlinklist(); - thensl = newlinklist(); + p = ecadd(0); - c->type = CIF; for (;;) { xtok = tok; cmdpush(xtok == IF ? CS_IF : CS_ELIF); @@ -675,10 +1095,11 @@ par_if(Cmd c) yylex(); if (!(xtok == IF || xtok == ELIF)) { cmdpop(); - YYERRORV; + YYERRORV(oecused); } - addlinknode(ifsl, par_list()); - ni++; + pp = ecadd(0); + type = (xtok == IF ? WC_IF_IF : WC_IF_ELIF); + par_save_list(complex); incmdpos = 1; while (tok == SEPER) yylex(); @@ -689,79 +1110,63 @@ par_if(Cmd c) cmdpop(); cmdpush(nc); yylex(); - addlinknode(thensl, par_list()); - nt++; + par_save_list(complex); + ecbuf[pp] = WCB_IF(type, ecused - 1 - pp); incmdpos = 1; cmdpop(); - } else { - if (tok == INBRACE) { - usebrace = 1; - cmdpop(); - cmdpush(nc); - yylex(); - l = par_list(); - if (tok != OUTBRACE) { - cmdpop(); - YYERRORV; - } - addlinknode(thensl, l); - nt++; - yylex(); - incmdpos = 1; - if (tok == SEPER) - break; - cmdpop(); - } else if (unset(SHORTLOOPS)) { - cmdpop(); - YYERRORV; - } else { + } else if (tok == INBRACE) { + usebrace = 1; + cmdpop(); + cmdpush(nc); + yylex(); + par_save_list(complex); + if (tok != OUTBRACE) { cmdpop(); - cmdpush(nc); - addlinknode(thensl, par_list1()); - nt++; - incmdpos = 1; - break; + YYERRORV(oecused); } + ecbuf[pp] = WCB_IF(type, ecused - 1 - pp); + yylex(); + incmdpos = 1; + if (tok == SEPER) + break; + cmdpop(); + } else if (unset(SHORTLOOPS)) { + cmdpop(); + YYERRORV(oecused); + } else { + cmdpop(); + cmdpush(nc); + par_save_list1(complex); + ecbuf[pp] = WCB_IF(type, ecused - 1 - pp); + incmdpos = 1; + break; } } cmdpop(); if (xtok == ELSE) { + pp = ecadd(0); cmdpush(CS_ELSE); while (tok == SEPER) yylex(); if (tok == INBRACE && usebrace) { yylex(); - l = par_list(); + par_save_list(complex); if (tok != OUTBRACE) { cmdpop(); - YYERRORV; + YYERRORV(oecused); } } else { - l = par_list(); + par_save_list(complex); if (tok != FI) { cmdpop(); - YYERRORV; + YYERRORV(oecused); } } - addlinknode(thensl, l); - nt++; + ecbuf[pp] = WCB_IF(WC_IF_ELSE, ecused - 1 - pp); yylex(); cmdpop(); } - i = (struct ifcmd *)make_ifcmd(); - i->ifls = (List *) alloc((ni + 1) * sizeof(List)); - i->thenls = (List *) alloc((nt + 1) * sizeof(List)); - - for (ll = i->ifls, no = firstnode(ifsl); no; incnode(no), ll++) - if (!(*ll = (List) getdata(no))) - *ll = &dummy_list; - *ll = NULL; - for (ll = i->thenls, no = firstnode(thensl); no; incnode(no), ll++) - if (!(*ll = (List) getdata(no))) - *ll = &dummy_list; - *ll = NULL; - - c->u.ifcmd = i; + ecbuf[p] = WCB_IF(WC_IF_HEAD, ecused - 1 - p); } /* @@ -771,37 +1176,38 @@ par_if(Cmd c) /**/ static void -par_while(Cmd c) +par_while(int *complex) { - struct whilecmd *w; + int oecused = ecused, p; + int type = (tok == UNTIL ? WC_WHILE_UNTIL : WC_WHILE_WHILE); - c->type = CWHILE; - w = c->u.whilecmd = (struct whilecmd *)make_whilecmd(); - w->cond = (tok == UNTIL); + p = ecadd(0); yylex(); - w->cont = par_list(); + par_save_list(complex); incmdpos = 1; while (tok == SEPER) yylex(); if (tok == DO) { yylex(); - w->loop = par_list(); + par_save_list(complex); if (tok != DONE) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (tok == INBRACE) { yylex(); - w->loop = par_list(); + par_save_list(complex); if (tok != OUTBRACE) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (isset(CSHJUNKIELOOPS)) { - w->loop = par_list(); + par_save_list(complex); if (tok != ZEND) - YYERRORV; + YYERRORV(oecused); yylex(); } else - YYERRORV; + YYERRORV(oecused); + + ecbuf[p] = WCB_WHILE(type, ecused - 1 - p); } /* @@ -810,39 +1216,44 @@ par_while(Cmd c) /**/ static void -par_repeat(Cmd c) +par_repeat(int *complex) { - c->type = CREPEAT; + int oecused = ecused, p; + + p = ecadd(0); + incmdpos = 0; yylex(); if (tok != STRING) - YYERRORV; - addlinknode(c->args, tokstr); + YYERRORV(oecused); + ecstr(tokstr); incmdpos = 1; yylex(); while (tok == SEPER) yylex(); if (tok == DO) { yylex(); - c->u.list = par_list(); + par_save_list(complex); if (tok != DONE) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (tok == INBRACE) { yylex(); - c->u.list = par_list(); + par_save_list(complex); if (tok != OUTBRACE) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (isset(CSHJUNKIELOOPS)) { - c->u.list = par_list(); + par_save_list(complex); if (tok != ZEND) - YYERRORV; + YYERRORV(oecused); yylex(); } else if (unset(SHORTLOOPS)) { - YYERRORV; + YYERRORV(oecused); } else - c->u.list = par_list1(); + par_save_list1(complex); + + ecbuf[p] = WCB_REPEAT(ecused - 1 - p); } /* @@ -851,13 +1262,18 @@ par_repeat(Cmd c) /**/ static void -par_subsh(Cmd c) +par_subsh(int *complex) { - c->type = (tok == INPAR) ? SUBSH : CURSH; + int oecused = ecused, otok = tok, p; + + p = ecadd(0); yylex(); - c->u.list = par_list(); - if (tok != ((c->type == SUBSH) ? OUTPAR : OUTBRACE)) - YYERRORV; + par_list(complex); + ecadd(WCB_END()); + if (tok != ((otok == INPAR) ? OUTPAR : OUTBRACE)) + YYERRORV(oecused); + ecbuf[p] = (otok == INPAR ? WCB_SUBSH(ecused - 1 - p) : + WCB_CURSH(ecused - 1 - p)); incmdpos = 1; yylex(); } @@ -869,37 +1285,74 @@ par_subsh(Cmd c) /**/ static void -par_funcdef(Cmd c) +par_funcdef(void) { + int oecused = ecused, oldlineno = lineno, num = 0, onp, p, c = 0; + int so, oecssub = ecssub; + + lineno = 0; nocorrect = 1; incmdpos = 0; yylex(); - c->type = FUNCDEF; - c->args = newlinklist(); + + p = ecadd(0); + ecadd(0); + incmdpos = 1; while (tok == STRING) { if (*tokstr == Inbrace && !tokstr[1]) { tok = INBRACE; break; } - addlinknode(c->args, tokstr); + ecstr(tokstr); + num++; yylex(); } + ecadd(0); + ecadd(0); + ecadd(0); + nocorrect = 0; if (tok == INOUTPAR) yylex(); while (tok == SEPER) yylex(); + + ecnfunc++; + ecssub = so = ecsoffs; + onp = ecnpats; + ecnpats = 0; + if (tok == INBRACE) { yylex(); - c->u.list = par_list(); - if (tok != OUTBRACE) - YYERRORV; + par_list(&c); + if (tok != OUTBRACE) { + lineno += oldlineno; + ecnpats = onp; + ecssub = oecssub; + YYERRORV(oecused); + } yylex(); } else if (unset(SHORTLOOPS)) { - YYERRORV; + lineno += oldlineno; + ecnpats = onp; + ecssub = oecssub; + YYERRORV(oecused); } else - c->u.list = par_list1(); + 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; + + lineno += oldlineno; + ecnpats = onp; + ecssub = oecssub; + ecnfunc++; + + ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); } /* @@ -908,11 +1361,17 @@ par_funcdef(Cmd c) /**/ static void -par_time(Cmd c) +par_time(void) { + int p, f, c = 0; + yylex(); - c->type = ZCTIME; - c->u.pline = par_sublist2(); + + p = ecadd(0); + ecadd(0); + f = par_sublist2(&c); + ecbuf[p] = WCB_TIMED((p + 1 == ecused) ? WC_TIMED_EMPTY : WC_TIMED_PIPE); + set_sublist_code(p + 1, WC_SUBLIST_END, f, ecused - 2 - p, c); } /* @@ -921,15 +1380,16 @@ par_time(Cmd c) /**/ static void -par_dinbrack(Cmd c) +par_dinbrack(void) { - c->type = COND; + int oecused = ecused; + incond = 1; incmdpos = 0; yylex(); - c->u.cond = par_cond(); + par_cond(); if (tok != DOUTBRACK) - YYERRORV; + YYERRORV(oecused); incond = 0; incmdpos = 1; yylex(); @@ -942,77 +1402,290 @@ par_dinbrack(Cmd c) */ /**/ -static Cmd -par_simple(Cmd c) +static int +par_simple(int *complex, int nr) { - int isnull = 1; + int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0; + int c = *complex; - c->type = SIMPLE; + r = ecused; for (;;) { - if (tok == NOCORRECT) + if (tok == NOCORRECT) { + *complex = c = 1; nocorrect = 1; - else if (tok == ENVSTRING) { - struct varasg *v = (struct varasg *)make_varnode(); - - v->type = PM_SCALAR; - equalsplit(v->name = tokstr, &v->str); - addlinknode(c->vars, v); + } else if (tok == ENVSTRING) { + char *p, *name, *str; + + ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, 0)); + name = tokstr; + for (p = tokstr; *p && *p != Inbrack && *p != '='; p++); + if (*p == Inbrack && !skipparens(Inbrack, Outbrack, &p) && + *p == '=') { + *p = '\0'; + str = p + 1; + } else + equalsplit(tokstr, &str); + ecstr(name); + ecstr(str); isnull = 0; } else if (tok == ENVARRAY) { - struct varasg *v = (struct varasg *)make_varnode(); - int oldcmdpos = incmdpos; + int oldcmdpos = incmdpos, n; - v->type = PM_ARRAY; + p = ecadd(0); incmdpos = 0; - v->name = tokstr; + ecstr(tokstr); cmdpush(CS_ARRAY); yylex(); - v->arr = par_nl_wordlist(); + n = par_nl_wordlist(); + ecbuf[p] = WCB_ASSIGN(WC_ASSIGN_ARRAY, n); cmdpop(); if (tok != OUTPAR) - YYERROR; + YYERROR(oecused); incmdpos = oldcmdpos; - addlinknode(c->vars, v); isnull = 0; } else break; yylex(); } if (tok == AMPER || tok == AMPERBANG) - YYERROR; + YYERROR(oecused); + + p = ecadd(WCB_SIMPLE(0)); + for (;;) { if (tok == STRING) { + *complex = 1; incmdpos = 0; - addlinknode(c->args, tokstr); + ecstr(tokstr); + argc++; yylex(); } else if (IS_REDIROP(tok)) { - par_redir(c->redir); + *complex = c = 1; + par_redir(&r); + p += 3; /* 3 codes per redirection */ + sr++; } else if (tok == INOUTPAR) { + int oldlineno = lineno, onp, so, oecssub = ecssub; + + *complex = c; + lineno = 0; incmdpos = 1; cmdpush(CS_FUNCDEF); yylex(); while (tok == SEPER) yylex(); + + 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; + yylex(); - c->u.list = par_list(); + par_list(&c); if (tok != OUTBRACE) { cmdpop(); - YYERROR; + lineno += oldlineno; + ecnpats = onp; + ecssub = oecssub; + YYERROR(oecused); } yylex(); - } else - c->u.list = (List) expandstruct((struct node *) par_cmd(), N_LIST); + } else { + int ll, sl, c = 0; + + ll = ecadd(0); + sl = ecadd(0); + + par_cmd(&c); + + set_sublist_code(sl, WC_SUBLIST_END, 0, ecused - 1 - sl, c); + set_list_code(ll, (Z_SYNC | Z_END), c); + } cmdpop(); - c->type = FUNCDEF; + + ecadd(WCB_END()); + ecbuf[p + argc + 2] = so - oecssub; + ecbuf[p + argc + 3] = ecsoffs - so; + ecbuf[p + argc + 4] = ecnpats; + + lineno += oldlineno; + ecnpats = onp; + ecssub = oecssub; + ecnfunc++; + + ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); + + isfunc = 1; } else break; isnull = 0; } - if (isnull && empty(c->redir)) - return NULL; + if (isnull && !(sr + nr)) { + ecused = p; + return 0; + } incmdpos = 1; - return c; + + if (!isfunc) + ecbuf[p] = WCB_SIMPLE(argc); + + return sr + 1; +} + +/* + * redir : ( OUTANG | ... | TRINANG ) STRING + */ + +static int redirtab[TRINANG - OUTANG + 1] = { + WRITE, + WRITENOW, + APP, + APPNOW, + READ, + READWRITE, + HEREDOC, + HEREDOCDASH, + MERGEIN, + MERGEOUT, + ERRWRITE, + ERRWRITENOW, + ERRAPP, + ERRAPPNOW, + HERESTR, +}; + +/**/ +static void +par_redir(int *rp) +{ + int r = *rp, type, fd1, oldcmdpos, oldnc; + char *name; + + oldcmdpos = incmdpos; + incmdpos = 0; + oldnc = nocorrect; + if (tok != INANG && tok != INOUTANG) + nocorrect = 1; + type = redirtab[tok - OUTANG]; + fd1 = tokfd; + yylex(); + if (tok != STRING && tok != ENVSTRING) + YYERRORV(ecused); + incmdpos = oldcmdpos; + nocorrect = oldnc; + + /* assign default fd */ + if (fd1 == -1) + fd1 = IS_READFD(type) ? 0 : 1; + + name = tokstr; + + switch (type) { + case HEREDOC: + case HEREDOCDASH: { + /* <<[-] name */ + struct heredocs **hd; + + /* If we ever need more than three codes (or less), we have to change + * the factors in par_cmd() and par_simple(), too. */ + ecispace(r, 3); + *rp = r + 3; + ecbuf[r] = WCB_REDIR(type); + ecbuf[r + 1] = fd1; + + for (hd = &hdocs; *hd; hd = &(*hd)->next); + *hd = zalloc(sizeof(struct heredocs)); + (*hd)->next = NULL; + (*hd)->type = type; + (*hd)->pc = r; + (*hd)->str = tokstr; + + yylex(); + return; + } + case WRITE: + case WRITENOW: + if (tokstr[0] == Outang && tokstr[1] == Inpar) + /* > >(...) */ + type = OUTPIPE; + else if (tokstr[0] == Inang && tokstr[1] == Inpar) + YYERRORV(ecused); + break; + case READ: + if (tokstr[0] == Inang && tokstr[1] == Inpar) + /* < <(...) */ + type = INPIPE; + else if (tokstr[0] == Outang && tokstr[1] == Inpar) + YYERRORV(ecused); + break; + case READWRITE: + if ((tokstr[0] == Inang || tokstr[0] == Outang) && tokstr[1] == Inpar) + type = tokstr[0] == Inang ? INPIPE : OUTPIPE; + break; + } + yylex(); + + /* If we ever need more than three codes (or less), we have to change + * the factors in par_cmd() and par_simple(), too. */ + ecispace(r, 3); + *rp = r + 3; + ecbuf[r] = WCB_REDIR(type); + ecbuf[r + 1] = fd1; + ecbuf[r + 2] = ecstrcode(name); +} + +/**/ +void +setheredoc(int pc, int type, char *str) +{ + ecbuf[pc] = WCB_REDIR(type); + ecbuf[pc + 2] = ecstrcode(str); +} + +/* + * wordlist : { STRING } + */ + +/**/ +static int +par_wordlist(void) +{ + int num = 0; + while (tok == STRING) { + ecstr(tokstr); + num++; + yylex(); + } + 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++; + } + yylex(); + } + return num; } /* @@ -1028,25 +1701,24 @@ void (*condlex) _((void)) = yylex; */ /**/ -Cond +static int par_cond(void) { - Cond c, c2; + int p = ecused, r; - c = par_cond_1(); + r = par_cond_1(); while (tok == SEPER) condlex(); if (tok == DBAR) { condlex(); while (tok == SEPER) condlex(); - c2 = (Cond) make_cond(); - c2->left = (void *) c; - c2->right = (void *) par_cond(); - c2->type = COND_OR; - return c2; + ecispace(p, 1); + par_cond(); + ecbuf[p] = WCB_COND(COND_OR, ecused - 1 - p); + return 1; } - return c; + return r; } /* @@ -1054,25 +1726,24 @@ par_cond(void) */ /**/ -static Cond +static int par_cond_1(void) { - Cond c, c2; + int r, p = ecused; - c = par_cond_2(); + r = par_cond_2(); while (tok == SEPER) condlex(); if (tok == DAMPER) { condlex(); while (tok == SEPER) condlex(); - c2 = (Cond) make_cond(); - c2->left = (void *) c; - c2->right = (void *) par_cond_1(); - c2->type = COND_AND; - return c2; + ecispace(p, 1); + par_cond_1(); + ecbuf[p] = WCB_COND(COND_AND, ecused - 1 - p); + return 1; } - return c; + return r; } /* @@ -1084,10 +1755,9 @@ par_cond_1(void) */ /**/ -static Cond +static int par_cond_2(void) { - Cond c, c2; char *s1, *s2, *s3; int dble = 0; @@ -1121,31 +1791,31 @@ par_cond_2(void) } if (tok == BANG) { condlex(); - c = par_cond_2(); - c2 = (Cond) make_cond(); - c2->left = (void *) c; - c2->type = COND_NOT; - return c2; + ecadd(WCB_COND(COND_NOT, 0)); + return par_cond_2(); } if (tok == INPAR) { + int r; + condlex(); while (tok == SEPER) condlex(); - c = par_cond(); + r = par_cond(); while (tok == SEPER) condlex(); if (tok != OUTPAR) - YYERROR; + YYERROR(ecused); condlex(); - return c; + return r; } - if (tok != STRING) + if (tok != STRING) { if (tok && tok != LEXERR && condlex == testlex) { s1 = tokstr; condlex(); return par_cond_double("-n", s1); } else - YYERROR; + YYERROR(ecused); + } s1 = tokstr; if (condlex == testlex) dble = (*s1 == '-' && strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1 @@ -1155,24 +1825,23 @@ par_cond_2(void) int xtok = tok; condlex(); if (tok != STRING) - YYERROR; + YYERROR(ecused); s3 = tokstr; condlex(); - c = (Cond) make_cond(); - c->left = (void *) s1; - c->right = (void *) s3; - c->type = (xtok == INANG) ? COND_STRLT : COND_STRGTR; - c->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0); - return c; + ecadd(WCB_COND((xtok == INANG ? COND_STRLT : COND_STRGTR), 0)); + ecstr(s1); + ecstr(s3); + return 1; } - if (tok != STRING) + if (tok != STRING) { if (tok != LEXERR && condlex == testlex) { if (!dble) return par_cond_double("-n", s1); else if (!strcmp(s1, "-t")) return par_cond_double(s1, "1"); } else - YYERROR; + YYERROR(ecused); + } s2 = tokstr; incond++; /* parentheses do globbing */ condlex(); @@ -1180,200 +1849,1192 @@ par_cond_2(void) if (tok == STRING && !dble) { s3 = tokstr; condlex(); - return par_cond_triple(s1, s2, s3); + if (tok == STRING) { + LinkList l = newlinklist(); + + addlinknode(l, s2); + addlinknode(l, s3); + + while (tok == STRING) { + addlinknode(l, tokstr); + condlex(); + } + return par_cond_multi(s1, l); + } else + return par_cond_triple(s1, s2, s3); } else return par_cond_double(s1, s2); } -/* - * redir : ( OUTANG | ... | TRINANG ) STRING - */ +/**/ +static int +par_cond_double(char *a, char *b) +{ + if (a[0] != '-' || !a[1]) + COND_ERROR("parse error: condition expected: %s", a); + else if (!a[2] && strspn(a+1, "abcdefgknoprstuwxzhLONGS") == 1) { + ecadd(WCB_COND(a[1], 0)); + ecstr(b); + } else { + ecadd(WCB_COND(COND_MOD, 1)); + ecstr(a); + ecstr(b); + } + return 1; +} -static int redirtab[TRINANG - OUTANG + 1] = { - WRITE, - WRITENOW, - APP, - APPNOW, - READ, - READWRITE, - HEREDOC, - HEREDOCDASH, - MERGEIN, - MERGEOUT, - ERRWRITE, - ERRWRITENOW, - ERRAPP, - ERRAPPNOW, - HERESTR, -}; +/**/ +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] || ((b[1] == Equals || b[1] == '=') && !b[2]))) { + ecadd(WCB_COND(COND_STREQ, 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] == '-') { + 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 (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 (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 -par_redir(LinkList l) +yyerror(int noerr) { - struct redir *fn = (struct redir *)allocnode(N_REDIR); - int oldcmdpos, oldnc; + int t0; + char *t; - oldcmdpos = incmdpos; - incmdpos = 0; - oldnc = nocorrect; - if (tok != INANG && tok != INOUTANG) - nocorrect = 1; - fn->type = redirtab[tok - OUTANG]; - fn->fd1 = tokfd; - yylex(); - if (tok != STRING && tok != ENVSTRING) - YYERRORV; - incmdpos = oldcmdpos; - nocorrect = oldnc; + if ((t = dupstring(yytext))) + untokenize(t); - /* assign default fd */ - if (fn->fd1 == -1) - fn->fd1 = IS_READFD(fn->type) ? 0 : 1; + for (t0 = 0; t0 != 20; t0++) + if (!t || !t[t0] || t[t0] == '\n') + break; + if (t0 == 20) + zwarn("parse error near `%l...'", t, 20); + else if (t0) + zwarn("parse error near `%l'", t, t0); + else + zwarn("parse error", NULL, 0); + if (!noerr && noerrs != 2) + errflag = 1; +} - fn->name = tokstr; +/**/ +mod_export Eprog +dupeprog(Eprog p, int heap) +{ + Eprog r; + int i; + Patprog *pp; - switch (fn->type) { - case HEREDOC: - case HEREDOCDASH: { - /* <<[-] name */ - struct heredocs **hd; + if (p == &dummy_eprog) + return p; - for (hd = &hdocs; *hd; hd = &(*hd)->next); - *hd = zalloc(sizeof(struct heredocs)); - (*hd)->next = NULL; - (*hd)->rd = fn; - break; + 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; + pp = r->pats = (heap ? (Patprog *) hcalloc(r->len) : + (Patprog *) zcalloc(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; +} + +static LinkList eprog_free; + +/**/ +mod_export void +freeeprog(Eprog p) +{ + if (p && p != &dummy_eprog) + zaddlinknode(eprog_free, p); +} + +/**/ +void +freeeprogs(void) +{ + Eprog p; + int i; + Patprog *pp; + + while ((p = (Eprog) getlinknode(eprog_free))) { + 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)); } - case WRITE: - case WRITENOW: - if (tokstr[0] == Outang && tokstr[1] == Inpar) - /* > >(...) */ - fn->type = OUTPIPE; - else if (tokstr[0] == Inang && tokstr[1] == Inpar) - YYERRORV; - break; - case READ: - if (tokstr[0] == Inang && tokstr[1] == Inpar) - /* < <(...) */ - fn->type = INPIPE; - else if (tokstr[0] == Outang && tokstr[1] == Inpar) - YYERRORV; - break; - case READWRITE: - if ((tokstr[0] == Inang || tokstr[0] == Outang) && tokstr[1] == Inpar) - fn->type = tokstr[0] == Inang ? INPIPE : OUTPIPE; - break; +} + +/**/ +char * +ecgetstr(Estate s, int dup, int *tok) +{ + 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); } - yylex(); - addlinknode(l, fn); + if (tok) + *tok = (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); } -/* - * wordlist : { STRING } - */ +/**/ +char * +ecrawstr(Eprog p, Wordcode pc, int *tok) +{ + static char buf[4]; + wordcode c = *pc; + + if (c == 6 || c == 7) { + if (tok) + *tok = (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 (tok) + *tok = (c & 1); + return buf; + } else { + if (tok) + *tok = (c & 1); + return p->strs + (c >> 2); + } +} /**/ -static LinkList -par_wordlist(void) +char ** +ecgetarr(Estate s, int num, int dup, int *tok) { - LinkList l; + char **ret, **rp; + int tf = 0, tmp = 0; - l = newlinklist(); - while (tok == STRING) { - addlinknode(l, tokstr); - yylex(); + ret = rp = (char **) zhalloc((num + 1) * sizeof(char *)); + + while (num--) { + *rp++ = ecgetstr(s, dup, &tmp); + tf |= tmp; } - return l; + *rp = NULL; + if (tok) + *tok = tf; + + return ret; } -/* - * nl_wordlist : { STRING | SEPER } +/**/ +LinkList +ecgetlist(Estate s, int num, int dup, int *tok) +{ + 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 (tok) + *tok = tf; + return ret; + } + if (tok) + *tok = 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); + + addlinknode(ret, r); + + code = *s->pc++; + } + s->pc--; + + return ret; +} + +/**/ +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; + + eprog_free = znewlinklist(); +} + +/* 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. + * 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 0x02030405 +#define FD_OMAGIC 0x05040302 + +#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; +} + /**/ -static LinkList -par_nl_wordlist(void) +int +bin_zcompile(char *nam, char **args, char *ops, int func) { - LinkList l; + int map, flags; + char *dump; + + if ((ops['k'] && ops['z']) || (ops['R'] && ops['M']) || + (ops['c'] && (ops['U'] || ops['k'] || ops['z'])) || + (!(ops['c'] || ops['a']) && ops['m'])) { + zwarnnam(nam, "illegal combination of options", NULL, 0); + return 1; + } + if ((ops['c'] || ops['a']) && isset(KSHAUTOLOAD)) + zwarnnam(nam, "functions will use zsh style autoloading", NULL, 0); - l = newlinklist(); - while (tok == STRING || tok == SEPER) { - if (tok != SEPER) - addlinknode(l, tokstr); - yylex(); + flags = (ops['k'] ? FDHF_KSHLOAD : + (ops['z'] ? FDHF_ZSHLOAD : 0)); + + if (ops['t']) { + Wordcode f; + + if (!*args) { + zwarnnam(nam, "too few arguments", NULL, 0); + return 1; + } + if (!(f = load_dump_header(*args)) && + !(f = load_dump_header(dyncat(*args, FD_EXT)))) { + zwarnnam(nam, "invalid dump file: %s", *args, 0); + 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("function dump 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", NULL, 0); + return 1; } - return l; + map = (ops['M'] ? 2 : (ops['R'] ? 0 : 1)); + + if (!args[1] && !(ops['c'] || ops['a'])) + return build_dump(nam, dyncat(*args, FD_EXT), args, ops['U'], map, flags); + + dump = (strsfx(FD_EXT, *args) ? *args : dyncat(*args, FD_EXT)); + + return ((ops['c'] || ops['a']) ? + build_cur_dump(nam, dump, args + 1, ops['m'], map, + (ops['c'] ? 1 : 0) | (ops['a'] ? 2 : 0)) : + build_dump(nam, dump, args + 1, ops['U'], map, flags)); } +/* Load the header of a dump file. Returns NULL if the file isn't a + * valid dump file. */ + /**/ -static Cond -par_cond_double(char *a, char *b) +static Wordcode +load_dump_header(char *name) { - Cond n = (Cond) make_cond(); + int fd; + wordcode buf[FD_PRELEN + 1]; - if (a[0] != '-' || !a[1] || a[2]) - COND_ERROR("parse error: condition expected: %s", a); - n->left = (void *) b; - n->type = a[1]; - n->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0); - return n; + if ((fd = open(name, O_RDONLY)) < 0) + return NULL; + + if (read(fd, buf, (FD_PRELEN + 1) * sizeof(wordcode)) != + ((FD_PRELEN + 1) * sizeof(wordcode)) || + (fdmagic(buf) != FD_MAGIC && fdmagic(buf) != FD_OMAGIC) || + strcmp(ZSH_VERSION, fdversion(buf))) { + 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))) { + close(fd); + return NULL; + } + len = fdheaderlen(buf) * sizeof(wordcode); + head = (Wordcode) zhalloc(len); + } + memcpy(head, buf, (FD_PRELEN + 1) * sizeof(wordcode)); + + if (read(fd, head + (FD_PRELEN + 1), + len - ((FD_PRELEN + 1) * sizeof(wordcode))) != + len - ((FD_PRELEN + 1) * sizeof(wordcode))) { + close(fd); + 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); + + 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(dfd, 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(dfd, &head, sizeof(head)); + tmp = strlen(n) + 1; + write(dfd, n, tmp); + if ((tmp &= (sizeof(wordcode) - 1))) + write(dfd, &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(dfd, prog->prog, tmp * sizeof(wordcode)); + } + if (other) + break; + other = FDF_OTHER; + } } /**/ static int -get_cond_num(char *tst) +build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) { - static char *condstrs[] = - { - "nt", "ot", "ef", "eq", "ne", "lt", "gt", "le", "ge", NULL - }; - int t0; + 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); + + if ((dfd = open(dump, O_WRONLY|O_CREAT, 0600)) < 0) { + zwarnnam(nam, "can't write dump file: %s", dump, 0); + return 1; + } + progs = newlinklist(); + noaliases = ali; + + for (hlen = FD_PRELEN, tlen = 0; *files; files++) { + if (!strcmp(*files, "-k")) { + flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_KSHLOAD; + continue; + } else if (!strcmp(*files, "-z")) { + flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_ZSHLOAD; + continue; + } + if ((fd = open(*files, O_RDONLY)) < 0 || + (flen = lseek(fd, 0, 2)) == -1) { + if (fd >= 0) + close(fd); + close(dfd); + zwarnnam(nam, "can't open file: %s", *files, 0); + 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, 0); + noaliases = ona; + unlink(dump); + return 1; + } + close(fd); + file = metafy(file, flen, META_REALLOC); + + if (!(prog = parse_string(file, 1)) || errflag) { + errflag = 0; + close(dfd); + zfree(file, flen); + zwarnnam(nam, "can't read file: %s", *files, 0); + noaliases = ona; + unlink(dump); + return 1; + } + zfree(file, flen); - for (t0 = 0; condstrs[t0]; t0++) - if (!strcmp(condstrs[t0], tst)) - return t0; - return -1; + 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->flags & PM_UNDEFINED) { + int ona = noaliases; + + if (!(what & 2)) { + zwarnnam(nam, "function is not loaded: %s", shf->nam, 0); + return 1; + } + noaliases = (shf->flags & PM_UNALIASED); + if (!(prog = getfpfunc(shf->nam, NULL)) || prog == &dummy_eprog) { + noaliases = ona; + zwarnnam(nam, "can't load function: %s", shf->nam, 0); + return 1; + } + if (prog->dump) + prog = dupeprog(prog, 1); + noaliases = ona; + } else { + if (!(what & 1)) { + zwarnnam(nam, "function is already loaded: %s", shf->nam, 0); + return 1; + } + prog = dupeprog(shf->funcdef, 1); + } + wcf = (WCFunc) zhalloc(sizeof(*wcf)); + wcf->name = shf->nam; + wcf->prog = prog; + wcf->flags = ((prog->flags & EF_RUN) ? FDHF_KSHLOAD : FDHF_ZSHLOAD); + addlinknode(progs, wcf); + addlinknode(names, shf->nam); + + *hlen += ((sizeof(struct fdhead) / sizeof(wordcode)) + + ((strlen(shf->nam) + sizeof(wordcode)) / sizeof(wordcode))); + *tlen += (prog->len - (prog->npats * sizeof(Patprog)) + + sizeof(wordcode) - 1) / sizeof(wordcode); + + return 0; } /**/ -static Cond -par_cond_triple(char *a, char *b, char *c) +static int +build_cur_dump(char *nam, char *dump, char **names, int match, int map, + int what) { - Cond n = (Cond) make_cond(); - int t0; + int dfd, hlen, tlen; + LinkList progs, lnames; + Shfunc shf = NULL; - if ((b[0] == Equals || b[0] == '=') && - (!b[1] || ((b[1] == Equals || b[1] == '=') && !b[2]))) - n->type = COND_STREQ; - else if (b[0] == '!' && (b[1] == Equals || b[1] == '=') && !b[2]) - n->type = COND_STRNEQ; - else if (b[0] == '-') { - if ((t0 = get_cond_num(b + 1)) > -1) - n->type = t0 + COND_NT; - else - COND_ERROR("unrecognized condition: %s", b); + if (!strsfx(FD_EXT, dump)) + dump = dyncat(dump, FD_EXT); + + if ((dfd = open(dump, O_WRONLY|O_CREAT, 0600)) < 0) { + zwarnnam(nam, "can't write dump file: %s", dump, 0); + 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 = 0; + close(dfd); + unlink(dump); + return 1; + } + } else if (match) { + char *pat; + Patprog pprog; + int i; + HashNode hn; + + for (; *names; names++) { + tokenize(pat = dupstring(*names)); + if (!(pprog = patcompile(pat, PAT_STATIC, NULL))) { + zwarnnam(nam, "bad pattern: %s", *names, 0); + close(dfd); + unlink(dump); + return 1; + } + for (i = 0; i < shfunctab->hsize; i++) + for (hn = shfunctab->nodes[i]; hn; hn = hn->next) + if (!listcontains(lnames, hn->nam) && + pattry(pprog, hn->nam) && + cur_add_func(nam, (Shfunc) hn, lnames, progs, + &hlen, &tlen, what)) { + errflag = 0; + close(dfd); + unlink(dump); + return 1; + } + } + } else { + for (; *names; names++) { + if (errflag || + !(shf = (Shfunc) shfunctab->getnode(shfunctab, *names))) { + zwarnnam(nam, "unknown function: %s", *names, 0); + errflag = 0; + close(dfd); + unlink(dump); + return 1; + } + if (cur_add_func(nam, shf, lnames, progs, &hlen, &tlen, what)) { + errflag = 0; + close(dfd); + unlink(dump); + return 1; + } + } + } + if (empty(progs)) { + zwarnnam(nam, "no functions", NULL, 0); + errflag = 0; + 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 <sys/mman.h> + +#if defined(MAP_SHARED) && defined(PROT_READ) + +#define USE_MMAP 1 + +#endif +#endif + +#ifdef USE_MMAP + +/* List of dump files mapped. */ + +static FuncDump dumps; + +/* Load a dump file (i.e. map it). */ + +static void +load_dump_file(char *dump, int other, int len) +{ + FuncDump d; + Wordcode addr; + int fd, off; + + 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; } else - COND_ERROR("condition expected: %s", b); - n->left = (void *) a; - n->right = (void *) c; - n->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0); - return n; + off = 0; + + if ((fd = open(dump, O_RDONLY)) < 0) + return; + + fd = movefd(fd); + + if ((addr = (Wordcode) mmap(NULL, len, PROT_READ, MAP_SHARED, fd, off)) == + ((Wordcode) -1)) { + close(fd); + return; + } + d = (FuncDump) zalloc(sizeof(*d)); + d->next = dumps; + dumps = d; + d->name = ztrdup(dump); + d->fd = fd; + d->map = addr + (other ? (len - off) / sizeof(wordcode) : 0); + d->addr = addr; + d->len = len; + d->count = 0; } +#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 (<path>/<func>). */ + /**/ -static void -yyerror(void) +Eprog +try_dump_file(char *path, char *name, char *file, int *ksh) { - int t0; + Eprog prog; + struct stat std, stc, stn; + int rd, rc, rn; + char *dig, *wc; + + if (strsfx(FD_EXT, path)) + return check_dump_file(path, name, ksh); + + dig = dyncat(path, FD_EXT); + wc = dyncat(file, FD_EXT); + + rd = stat(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. */ + if (!rd && + (rc || std.st_mtime > stc.st_mtime) && + (rn || std.st_mtime > stn.st_mtime) && + (prog = check_dump_file(dig, name, ksh))) + 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, name, ksh))) + return prog; + + /* No compiled file for the function. The caller (getfpfunc() will + * check if the directory contains the uncompiled file for it. */ + return NULL; +} - for (t0 = 0; t0 != 20; t0++) - if (!yytext || !yytext[t0] || yytext[t0] == '\n') - break; - if (t0 == 20) - zerr("parse error near `%l...'", yytext, 20); - else if (t0) - zerr("parse error near `%l'", yytext, t0); +/* 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 - zerr("parse error", NULL, 0); + tail = file; + + if (strsfx(FD_EXT, file)) + return check_dump_file(file, tail, NULL); + + wc = dyncat(file, FD_EXT); + + rc = stat(wc, &stc); + rn = stat(file, &stn); + + if (!rc && (rn || stc.st_mtime > stn.st_mtime) && + (prog = check_dump_file(wc, tail, NULL))) + return prog; + + 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, char *name, int *ksh) +{ + int isrec = 0; + Wordcode d; + FDHead h; + FuncDump f; + +#ifdef USE_MMAP + + rec: + +#endif + + d = NULL; + +#ifdef USE_MMAP + + for (f = dumps; f; f = f->next) + if (!strcmp(file, f->name)) { + d = f->map; + break; + } + +#else + + f = NULL; + +#endif + + if (!f && (isrec || !(d = load_dump_header(file)))) + 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. */ + +#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->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, (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) != 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->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++; +} + +/* 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; + munmap((void *) f->addr, f->len); + zclose(f->fd); + zsfree(f->name); + zfree(f, sizeof(*f)); + } + } +} + +#else + +void +incrdumpcount(FuncDump f) +{ +} + +void +decrdumpcount(FuncDump f) +{ +} + +#endif + +/**/ +int +dump_autoload(char *file, int on, char *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(file))) + return 1; + + for (n = firstfdhead(h), e = (FDHead) (h + fdheaderlen(h)); n < e; + n = nextfdhead(n)) { + shf = (Shfunc) zcalloc(sizeof *shf); + shf->flags = on; + shf->funcdef = mkautofn(shf); + shfunctab->addnode(shfunctab, ztrdup(fdname(n) + fdhtail(n)), shf); + if (ops['X'] && eval_autoload(shf, shf->nam, ops, func)) + ret = 1; + } + return ret; } diff --git a/Src/pattern.c b/Src/pattern.c index 048e3d3ec..914479847 100644 --- a/Src/pattern.c +++ b/Src/pattern.c @@ -1,5 +1,5 @@ /* - * glob.c - filename generation + * pattern.c - pattern matching * * This file is part of zsh, the Z shell. * @@ -50,13 +50,28 @@ */ #include "zsh.mdh" -#include "pattern.pro" /* - * Globbing flags: lower 8 bits gives approx count + * 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. */ -#define C_LCMATCHUC 0x0100 -#define C_IGNCASE 0x0200 +union upat { + long l; + unsigned char *p; +}; + +typedef union upat *Upat; + +#include "pattern.pro" + +/* Number of active parenthesised expressions allowed in backreferencing */ +#define NSUBEXP 9 /* definition number opnd? meaning */ #define P_END 0x00 /* no End of program. */ @@ -154,36 +169,17 @@ #define PP_UNKWN 13 #define PP_RANGE 14 -/* Align everything to the pointer type. */ -typedef char *zalign_t; - -#define P_OP(p) (*(long *)(p) & 0xff) -#define P_NEXT(p) (*(long *)(p) >> 8) -#define P_OPERAND(p) ((p) + sizeof(zalign_t)) -#define P_ISBRANCH(p) (*(long *)(p) & 0x20) -#define P_ISEXCLUDE(p) ((*(long *)(p) & 0x30) == 0x30) -#define P_NOTDOT(p) (*(long *)(p) & 0x40) +#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) #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 */ -/* - * pointer is end string to be parsed? - * a bit dire because of extendedglob possibilities: - * we need to make sure a ~ at the end of a string isn't mistaken - * for an excluder or lots of emacs users get very cross. - */ -#define ISENDCHAR(X) (!*(X) || ((patflags & PAT_FILE) && *(X) == '/') || \ - *(X) == Bar || *(X) == Outpar || \ - (isset(EXTENDEDGLOB) && \ - (*(X) == Hat || \ - (*(X) == Tilde && \ - !(!(X)[1] || ((patflags & PAT_FILE) && \ - (X)[1] == '/') || \ - (X)[1] == Bar || (X)[1] == Outpar || \ - (X)[1] == Tilde))))) - /* Next character after one which may be a Meta (x is any char *) */ #define METANEXT(x) (*(x) == Meta ? (x)+2 : (x)+1) /* @@ -202,6 +198,36 @@ typedef zlong zrange_t; typedef unsigned long zrange_t; #endif +/* + * Characters which terminate a pattern segment. We actually use + * a pointer patendseg which skips the first character if we are not + * parsing a file pattern. + * Note that the size of this and the next array are hard-wired + * via the definitions. + */ + +static char endseg[] = { + '/', /* file only */ + '\0', Bar, Outpar, /* all patterns */ + Tilde /* extended glob only */ +}; + +#define PATENDSEGLEN_NORM 4 +#define PATENDSEGLEN_EXT 5 + +/* Characters which terminate a simple string */ + +static char endstr[] = { + '/', /* file only */ + '\0', Bar, Outpar, Quest, Star, Inbrack, Inpar, Inang, + /* all patterns */ + Tilde, Hat, Pound /* extended glob only */ +}; + +#define PATENDSTRLEN_NORM 9 +#define PATENDSTRLEN_EXT 12 + + /* Default size for pattern buffer */ #define P_DEF_ALLOC 256 @@ -212,6 +238,10 @@ 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 */ +static char *patendseg; /* characters ending segment */ +static int patendseglen; /* length of same */ +static char *patendstr; /* characters ending plain string */ +static int patendstrlen; /* length of sameo */ /* Flags used in both compilation and execution */ static int patflags; /* flags passed down to patcompile */ @@ -226,8 +256,8 @@ patadd(char *add, int ch, long n, int noalgn) /* Make sure everything gets aligned unless we get noalgn. */ long newpatsize = patsize + n; if (!noalgn) - newpatsize = (newpatsize + sizeof(zalign_t) - 1) & - ~(sizeof(zalign_t) - 1); + newpatsize = (newpatsize + sizeof(union upat) - 1) & + ~(sizeof(union upat) - 1); if (patalloc < newpatsize) { long newpatalloc = 2*(newpatsize > patalloc ? newpatsize : patalloc); @@ -245,7 +275,7 @@ patadd(char *add, int ch, long n, int noalgn) } static long rn_offs; -/* operates on poiners, returns a pointer */ +/* 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) @@ -262,24 +292,18 @@ patcompstart(void) /* Top level pattern compilation subroutine */ /**/ -Patprog +mod_export Patprog patcompile(char *exp, int inflags, char **endexp) { - int flags, len; + int flags = 0, len = 0; long startoff; - char *pscan, *lng; + Upat pscan; + char *lng, *strp = NULL; Patprog p; - DPUTS(sizeof(long) > sizeof(zalign_t), "BUG: patprog alignment too small"); - -#ifdef BACKREFERENCES - startoff = (inflags & PAT_BACKR) ? sizeof(struct patprog) : - sizeof(struct patprog_short); -#else startoff = sizeof(struct patprog); -#endif /* Ensure alignment of start of program string */ - startoff = (startoff + sizeof(zalign_t) - 1) & ~(sizeof(zalign_t) - 1); + 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) @@ -287,10 +311,23 @@ patcompile(char *exp, int inflags, char **endexp) 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; + patflags = inflags & ~PAT_PURES; + + patendseg = endseg; + patendseglen = isset(EXTENDEDGLOB) ? PATENDSEGLEN_EXT : PATENDSEGLEN_NORM; + patendstr = endstr; + patendstrlen = isset(EXTENDEDGLOB) ? PATENDSTRLEN_EXT : PATENDSTRLEN_NORM; if (!(patflags & PAT_FILE)) { + patendseg++; + patendstr++; + patendseglen--; + patendstrlen--; remnulargs(exp); patglobflags = 0; } @@ -299,65 +336,88 @@ patcompile(char *exp, int inflags, char **endexp) */ ((Patprog)patout)->globflags = patglobflags; - if (patflags & PAT_ANY) - flags = 0; - else if (patcompswitch(0, &flags) == 0) - return NULL; + if (!(patflags & PAT_ANY)) { + /* Look for a really pure string, with no tokens at all. */ + if (!patglobflags) + 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) + return NULL; + } else { + /* Yes, copy the string and skip compilation altogether */ + 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 & ~PAT_PURES); + p->flags = patflags; p->mustoff = 0; p->size = patsize; - p->patmlen = 0; - pscan = patout + startoff; + p->patmlen = len; + p->patnpar = patnpar-1; - if (!(patflags & PAT_ANY) && P_OP(PATNEXT(pscan)) == P_END) { - /* only one top level choice */ - pscan = P_OPERAND(pscan); + if (!strp) { + pscan = (Upat)(patout + startoff); - 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, *next; - p->flags |= PAT_PURES; - for (; pscan; pscan = next) { - next = PATNEXT(pscan); - if (P_OP(pscan) == P_EXACTLY) { - char *opnd = P_OPERAND(pscan); - while ((*dst = *opnd++)) - dst++; + 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 = (char *)P_OPERAND(pscan); + while ((*dst = *opnd++)) + dst++; + } } - } - *dst++ = '\0'; - p->size = dst - patout; - /* patmlen is reall strlen, don't include null byte */ - p->patmlen = p->size - startoff - 1; - } else { - /* starting point info */ - if (P_OP(pscan) == P_EXACTLY && !p->globflags) - p->patstartch = *P_OPERAND(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 && - strlen((char *)P_OPERAND(pscan)) >= len) { - lng = P_OPERAND(pscan); - len = strlen((char *)P_OPERAND(pscan)); + *dst++ = '\0'; + p->size = dst - patout; + /* patmlen is really strlen, don't include null byte */ + p->patmlen = p->size - startoff - 1; + } else { + /* starting point info */ + if (P_OP(pscan) == P_EXACTLY && !p->globflags) + p->patstartch = *(char *)P_OPERAND(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 && + strlen((char *)P_OPERAND(pscan)) >= len) { + lng = (char *)P_OPERAND(pscan); + len = strlen(lng); + } + if (lng) { + p->mustoff = lng - patout; + p->patmlen = len; } - if (lng) { - p->mustoff = lng - patout; - p->patmlen = len; } } } @@ -367,8 +427,13 @@ patcompile(char *exp, int inflags, char **endexp) * 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 w always put it in zalloc()ed memory. */ - if (!(patflags & PAT_STATIC)) { + 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; @@ -389,26 +454,22 @@ static long patcompswitch(int paren, int *flagp) { long starter, br, ender, excsync = 0; -#ifdef BACKREFERENCES int parno = 0; -#endif - int flags, gfchanged = 0, savflags = patflags, savglobflags = patglobflags; - char *ptr; + int flags, gfchanged = 0, savglobflags = patglobflags; + Upat ptr; *flagp = 0; -#ifdef BACKREFERENCES - if (paren && (patflags & PAT_BACKR)) { + 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 >= NSUBEXP ? 0 : patnpar++; + parno = patnpar++; starter = patnode(P_OPEN + parno); } else -#endif starter = 0; br = patnode(P_BRANCH); @@ -424,17 +485,16 @@ patcompswitch(int paren, int *flagp) *flagp |= flags & (P_HSTART|P_PURESTR); while (*patparse == Bar || - (isset(EXTENDEDGLOB) && - *patparse == Tilde && patparse[1] && patparse[1] != Bar && - patparse[1] != Outpar && patparse[1] != Tilde && - !((patflags & PAT_FILE) && patparse[1] == '/'))) { + (isset(EXTENDEDGLOB) && *patparse == Tilde && + (patparse[1] == '/' || + !memchr(patendseg, patparse[1], patendseglen)))) { int tilde = *patparse++ == Tilde; long gfnode = 0, newbr; *flagp &= ~P_PURESTR; if (tilde) { - unsigned char *unull = NULL; + 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. @@ -455,10 +515,16 @@ patcompswitch(int paren, int *flagp) patglobflags &= ~0xff; br = patnode(!(patflags & PAT_FILET) || paren ? P_EXCLUDE : P_EXCLUDP); - patadd((char *)&unull, 0, sizeof(unull), 0); + up.p = NULL; + patadd((char *)&up, 0, sizeof(up), 0); /* / is not treated as special if we are at top level */ - if (!paren) - patflags &= ~PAT_FILE; + if (!paren && *patendseg == '/') { + tilde++; + patendseg++; + patendseglen--; + patendstr++; + patendstrlen--; + } } else { excsync = 0; br = patnode(P_BRANCH); @@ -485,16 +551,23 @@ patcompswitch(int paren, int *flagp) * No gfchanged, as nothing to follow branch at top * level. */ + union upat up; gfnode = patnode(P_GFLAGS); - patadd((char *)&patglobflags, 0, sizeof(long), - 0); + up.l = patglobflags; + patadd((char *)&up, 0, sizeof(union upat), 0); } } else { patglobflags = savglobflags; } } newbr = patcompbranch(&flags); - patflags = savflags; + if (tilde == 2) { + /* restore special treatment of / */ + patendseg--; + patendseglen++; + patendstr--; + patendstrlen++; + } if (!newbr) return 0; if (gfnode) @@ -513,21 +586,16 @@ patcompswitch(int paren, int *flagp) * branch at that point would indicate the current choices continue, * which they don't. */ -#ifdef BACKREFERENCES - ender = patnode(paren ? (patflags & PAT_BACKR) ? P_CLOSE+parno - : P_NOTHING : P_END); -#else - ender = patnode(paren ? P_NOTHING : P_END); -#endif + 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 = patout + starter; ptr; ptr = PATNEXT(ptr)) + for (ptr = (Upat)patout + starter; ptr; ptr = PATNEXT(ptr)) if (!P_ISEXCLUDE(ptr)) - patoptail(ptr-patout, ender); + patoptail(ptr-(Upat)patout, ender); /* check for proper termination */ if ((paren && *patparse++ != Outpar) || @@ -566,12 +634,9 @@ patcompbranch(int *flagp) *flagp = P_PURESTR; starter = chain = 0; - while (*patparse && !((patflags & PAT_FILE) && *patparse == '/') && - *patparse != Bar && *patparse != Outpar && - (!isset(EXTENDEDGLOB) || *patparse != Tilde || - !patparse[1] || patparse[1] == Bar || patparse[1] == Outpar - || patparse[1] == Tilde || - ((patflags & PAT_FILE) && patparse[1] == '/'))) { + while (!memchr(patendseg, *patparse, patendseglen) || + (*patparse == Tilde && patparse[1] != '/' && + memchr(patendseg, patparse[1], patendseglen))) { if (isset(EXTENDEDGLOB) && ((!isset(SHGLOB) && (*patparse == Inpar && patparse[1] == Pound)) || @@ -601,8 +666,10 @@ patcompbranch(int *flagp) */ if (oldglobflags != patglobflags) { /* Flags changed */ + union upat up; latest = patnode(P_GFLAGS); - patadd((char *)&patglobflags, 0, sizeof(int), 0); + up.l = patglobflags; + patadd((char *)&up, 0, sizeof(union upat), 0); } else { /* No effect. */ continue; @@ -663,17 +730,37 @@ patgetglobflags(char **strp) case 'l': /* Lowercase in pattern matches lower or upper in target */ - patglobflags = (patglobflags & ~C_IGNCASE) | C_LCMATCHUC; + patglobflags = (patglobflags & ~GF_IGNCASE) | GF_LCMATCHUC; break; case 'i': /* Fully case insensitive */ - patglobflags = (patglobflags & ~C_LCMATCHUC) | C_IGNCASE; + patglobflags = (patglobflags & ~GF_LCMATCHUC) | GF_IGNCASE; break; case 'I': /* Restore case sensitivity */ - patglobflags &= ~(C_LCMATCHUC|C_IGNCASE); + 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; default: @@ -696,156 +783,105 @@ static long patcomppiece(int *flagp) { long starter, next, pound, op; - int flags, kshchar; - unsigned char *ptr; - - starter = patcompatom(&flags, &kshchar); - if (!starter) - return 0; - - if (!(pound = (*patparse == Pound && isset(EXTENDEDGLOB))) && - (kshchar <= 0 || kshchar == '@' || kshchar == '!')) { - *flagp = flags; - return starter; - } - - /* too much at once doesn't currently work */ - if (kshchar && pound) - 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 (*++patparse == Pound) { - 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 ((flags & P_SIMPLE) && op == P_ONEHASH && - P_OP(patout+starter) == P_ANY) { - /* Optimize ?# to *. Silly thing to do, since who would use - * use ?# ? But it makes the later code shorter. - */ - long *lptr = (long *)(patout + starter); - *lptr = (*lptr & ~0xff) | P_STAR; - } else if ((flags & P_SIMPLE) && op && !(patglobflags & 0xff)) { - /* Don't simplify if we need to look for approximations. */ - patinsert(op, starter, NULL, 0); - } else if (op == P_ONEHASH) { - /* Emit x# as (x&|), where & means "self". */ - ptr = NULL; - patinsert(P_WBRANCH, starter, (char *)&ptr, sizeof(ptr)); - /* 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 */ - ptr = NULL; - patadd((char *)&ptr , 0, sizeof(ptr), 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 == Pound) - return 0; - - return starter; -} - -/* - * Parse lowest level pattern. If doing ordinary characters, we - * gobble a complete string as far as we can get. - * kshcharp returns a character found before an Inpar, for handling - * as a closure. - */ - -/**/ -static long -patcompatom(int *flagp, int *kshcharp) -{ - long starter; - int patch, flags, len, ch; + int flags, flags2, kshchar, len, ch, patch; + union upat up; char *nptr, *str0, cbuf[2]; zrange_t from, to; - *flagp = 0; + flags = 0; str0 = patparse; for (;;) { - /* check kshglob here */ - *kshcharp = '\0'; + /* + * Check if we have a string. First, we need to make sure + * the string doesn't introduce a ksh-like parenthesised expression. + */ + kshchar = '\0'; if (isset(KSHGLOB) && *patparse && patparse[1] == Inpar) { - if (strchr("?*+!@", (char)*patparse)) - *kshcharp = STOUC(*patparse); + if (strchr("?*+!@", *patparse)) + kshchar = STOUC(*patparse); else if (*patparse == Star || *patparse == Quest) - *kshcharp = STOUC(ztokens[*patparse - Pound]); + kshchar = STOUC(ztokens[*patparse - Pound]); } - if (patparse > str0) { - /* - * This is up here instead of at the end to simplify the - * kshglob bracket testing. Note patparse doesn't - * get incremented till afterwards. - */ - if (ISENDCHAR(patparse) || *kshcharp || *patparse == Quest || - *patparse == Star || *patparse == Inbrack || - (*patparse == Inpar && !isset(SHGLOB)) || - *patparse == Inang || - (isset(EXTENDEDGLOB) && *patparse == Pound)) - break; - else { - METAINC(patparse); - continue; - } - } + /* + * 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. + */ + if (kshchar || (memchr(patendstr, *patparse, patendstrlen) && + (*patparse != Tilde || + patparse[1] == '/' || + !memchr(patendseg, patparse[1], patendseglen)))) + break; + + METAINC(patparse); + } - if (*kshcharp) + if (patparse > str0) { + /* 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? */ + len = str0 + (*str0 == Meta ? 2 : 1) < patparse; + /* + * If we have more than one character, a following hash only + * applies to the last, so decrement. + */ + if (isset(EXTENDEDGLOB) && *patparse == Pound && len) + patparse -= (patparse > str0 + 1 && patparse[-2] == Meta) ? 2 : 1; + /* + * 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 (!len) + flags |= P_SIMPLE; + starter = patnode(P_EXACTLY); + /* add enough space including null byte */ + len = patparse - str0; + patadd(str0, 0, len + 1, 0); + nptr = (char *)P_OPERAND((Upat)patout + starter); + nptr[len] = '\0'; + /* + * 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 && + (!(patflags & PAT_FILE) || (strcmp(nptr, ".") && + strcmp(nptr, "..")))) + flags &= ~P_PURESTR; + for (; *nptr; METAINC(nptr)) + if (itok(*nptr)) + *nptr = ztokens[*nptr - Pound]; + } else { + if (kshchar) patparse++; patch = *patparse; METAINC(patparse); switch(patch) { case Quest: - *flagp |= P_SIMPLE; - return patnode(P_ANY); + flags |= P_SIMPLE; + starter = patnode(P_ANY); break; case Star: /* kshchar is used as a sign that we can't have #'s. */ - *kshcharp = -1; - return patnode(P_STAR); + kshchar = -1; + starter = patnode(P_STAR); break; case Inbrack: - *flagp |= P_SIMPLE; + flags |= P_SIMPLE; if (*patparse == Hat || *patparse == '^' || *patparse == '!') { patparse++; starter = patnode(P_ANYBUT); @@ -919,15 +955,14 @@ patcompatom(int *flagp, int *kshcharp) patparse++; /* terminate null string and fix alignment */ patadd(NULL, 0, 1, 0); - return starter; break; case Inpar: /* is this how to treat parentheses in SHGLOB? */ - if (isset(SHGLOB) && !*kshcharp) + if (isset(SHGLOB) && !kshchar) return 0; - if (*kshcharp == '!') { + if (kshchar == '!') { /* This is nasty, we should really either handle all - * kshglobbing upstairs or down here. But most of the + * 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 @@ -939,12 +974,11 @@ patcompatom(int *flagp, int *kshcharp) * the expense of allowing the user to do things * they shouldn't. */ - if (!(starter = patcompnot(1, &flags))) + if (!(starter = patcompnot(1, &flags2))) return 0; - } else if (!(starter = patcompswitch(1, &flags))) + } else if (!(starter = patcompswitch(1, &flags2))) return 0; - *flagp |= flags & P_HSTART; - return starter; + flags |= flags2 & P_HSTART; break; case Inang: /* Numeric glob */ @@ -990,68 +1024,101 @@ patcompatom(int *flagp, int *kshcharp) * Mention in manual that matching digits with [...] * is more efficient. */ - return starter; break; case Pound: - if (!isset(EXTENDEDGLOB)) - break; + DPUTS(!isset(EXTENDEDGLOB), "BUG: # not treated as string"); + /* + * A hash here is an error; it should follow something + * repeatable. + */ return 0; break; #ifdef DEBUG - case Bar: - case Outpar: - case '\0': - dputs("BUG: wrong character in patcompatom."); + default: + dputs("BUG: character not handled in patcomppiece"); return 0; break; #endif } } - /* Simple string: cancel kshchar lookahead */ - *kshcharp = '\0'; - /* - * Assume it matches a simple string until we find otherwise. - */ - *flagp |= P_PURESTR; - DPUTS(patparse == str0, "BUG: matched nothing in patcompatom."); - /* more than one character matched? */ - len = str0 + (*str0 == Meta ? 2 : 1) < patparse; + if (!(pound = (*patparse == Pound && isset(EXTENDEDGLOB))) && + (kshchar <= 0 || kshchar == '@' || kshchar == '!')) { + *flagp = flags; + return starter; + } + + /* too much at once doesn't currently work */ + if (kshchar && pound) + 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 (*++patparse == Pound) { + op = P_TWOHASH; + patparse++; + *flagp = P_HSTART; + } else { + op = P_ONEHASH; + *flagp = P_HSTART; + } + /* - * Ordinary string of characters. - * If we have more than one character, a following hash only - * applies to the last, so decrement. - */ - if (isset(EXTENDEDGLOB) && *patparse == Pound && len) - patparse -= (patparse > str0 + 1 && patparse[-2] == Meta) ? 2 : 1; - /* if len is 1, we can't have an active # following, so doesn't - * matter that we don't make X in `XX#' simple. + * 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 (!len) - *flagp |= P_SIMPLE; - starter = patnode(P_EXACTLY); - while (str0 < patparse) { - if (*str0 == Meta) { - cbuf[0] = *str0++; - cbuf[1] = *str0++; - } else { - cbuf[0] = itok(*str0) ? ztokens[*str0 - Pound] : *str0; - str0++; - } - ch = UNMETA(cbuf); - /* - * HACK: this cause a string consisting of any number of - * dots in files to be matched exactly, even with approximation. - * We just want to limit it to the first two. + if ((flags & P_SIMPLE) && op == P_ONEHASH && + 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. */ - if (((patglobflags & C_IGNCASE) && (islower(ch) || isupper(ch))) || - ((patglobflags & C_LCMATCHUC) && islower(ch)) || - ((patglobflags & 0xff) && !((patflags & PAT_FILE) && ch == '.'))) - *flagp &= ~P_PURESTR; - patadd(cbuf, 0, (cbuf[0] == Meta) ? 2 : 1, 1); + Upat uptr = (Upat)patout + starter; + uptr->l = (uptr->l & ~0xff) | P_STAR; + } else if ((flags & P_SIMPLE) && op && !(patglobflags & 0xff)) { + /* Don't simplify 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); } - /* null terminate and fix alignment */ - patadd(NULL, 0, 1, 0); + if (*patparse == Pound) + return 0; + return starter; } @@ -1064,7 +1131,7 @@ patcompatom(int *flagp, int *kshcharp) static long patcompnot(int paren, int *flagsp) { - unsigned char *unull = NULL; + union upat up; long excsync, br, excl, n, starter; int dummy; @@ -1076,7 +1143,8 @@ patcompnot(int paren, int *flagsp) excsync = patnode(P_EXCSYNC); pattail(br, excsync); pattail(starter, excl = patnode(P_EXCLUDE)); - patadd((char *)&unull, 0, sizeof(unull), 0); + up.p = NULL; + patadd((char *)&up, 0, sizeof(up), 0); if (!(br = (paren ? patcompswitch(1, &dummy) : patcompbranch(&dummy)))) return 0; pattail(br, patnode(P_EXCEND)); @@ -1093,9 +1161,11 @@ patcompnot(int paren, int *flagsp) static long patnode(long op) { - long starter = patcode - patout; + long starter = (Upat)patcode - (Upat)patout; + union upat up; - patadd((char *)&op, 0, sizeof(long), 0); + up.l = op; + patadd((char *)&up, 0, sizeof(union upat), 0); return starter; } @@ -1109,22 +1179,22 @@ static void patinsert(long op, int opnd, char *xtra, int sz) { char *src, *dst, *opdst; - long buf, *lptr; + union upat buf, *lptr; - buf = 0; - patadd((char *)&buf, 0, sizeof(long), 0); + buf.l = 0; + patadd((char *)&buf, 0, sizeof(buf), 0); if (sz) patadd(xtra, 0, sz, 0); - src = patcode - sizeof(long) - sz; + src = patcode - sizeof(union upat) - sz; dst = patcode; - opdst = patout + opnd; + opdst = patout + opnd * sizeof(union upat); while (src > opdst) *--dst = *--src; /* A cast can't be an lvalue */ - lptr = (long *)opdst; - *lptr = op; - opdst += sizeof(long); + lptr = (Upat)opdst; + lptr->l = op; + opdst += sizeof(union upat); while (sz--) *opdst++ = *xtra++; } @@ -1135,10 +1205,10 @@ patinsert(long op, int opnd, char *xtra, int sz) static void pattail(long p, long val) { - char *scan, *temp; - long offset, *lptr; + Upat scan, temp; + long offset; - scan = patout + p; + scan = (Upat)patout + p; for (;;) { if (!(temp = PATNEXT(scan))) break; @@ -1146,10 +1216,9 @@ pattail(long p, long val) } offset = (P_OP(scan) == P_BACK) - ? (scan - patout) - val : val - (scan - patout); + ? (scan - (Upat)patout) - val : val - (scan - (Upat)patout); - lptr = (long *)scan; - *lptr |= offset << 8; + scan->l |= offset << 8; } /* do pattail, but on operand of first argument; nop if operandless */ @@ -1157,14 +1226,14 @@ pattail(long p, long val) /**/ static void patoptail(long p, long val) { - char *ptr = patout + p; + 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) + sizeof(char *), val); + pattail(P_OPERAND(p) + 1, val); } @@ -1178,11 +1247,20 @@ char *patinput; /* String input pointer */ /* Length of input string, plus null byte, if needed */ static int patinlen; -#ifdef BACKREFERENCES -static char **patstartp; /* Pointer to backref starts */ -static char **patendp; /* Pointer to backref ends */ -static int parsfound; /* parentheses found */ -#endif + +/* + * Offset of string 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. + */ +/**/ +int patoffset; + +static char *patbeginp[NSUBEXP]; /* Pointer to backref beginnings */ +static char *patendp[NSUBEXP]; /* Pointer to backref ends */ +static int parsfound; /* parentheses (with backrefs) found */ + static int globdots; /* Glob initial dots? */ /* @@ -1204,15 +1282,28 @@ pattrystart(void) } /**/ -int +mod_export int pattry(Patprog prog, char *string) { -#ifdef BACKREFERENCES - int i; + return pattryrefs(prog, string, NULL, NULL, NULL); +} + +/* The last three arguments are used to report the positions for the + * backreferences. On entry, *nump should contain the maximum number + * positions to report. */ + +/**/ +mod_export int +pattryrefs(Patprog prog, char *string, int *nump, int *begp, int *endp) +{ + int i, maxnpos = 0; char **sp, **ep; -#endif char *progstr = (char *)prog + prog->startoff; + if (nump) { + maxnpos = *nump; + *nump = 0; + } /* inherited from domatch, but why, exactly? */ if (*string == Nularg) string++; @@ -1248,40 +1339,111 @@ pattry(Patprog prog, char *string) errsfound = 0; } globdots = !(patflags & PAT_NOGLD); -#ifdef BACKREFERENCES parsfound = 0; - if (patflags & PAT_BACKR) { - patstartp = prog->ppStartp; - patendp = prog->ppEndp; - } else { - patstartp = patendp = NULL; - } -#endif - if (patmatch(progstr)) { -#ifdef BACKREFERENCES - if (patflags & PAT_BACKR) { - prog->ppStartp[0] = string; - prog->ppEndp[0] = patinput; - - sp = patstartp+1; - ep = patendp + 1; - for (i = 1; i < NSUBEXP; i++) { - if (!(parsfound & (1 << (i - 1)))) - *sp = 0; - if (!(parsfound & (1 << (i + 15)))) - *ep = 0; - sp++; - ep++; - } - - } -#endif + if (patmatch((Upat)progstr)) { /* * we were lazy and didn't save the globflags if an exclusion * failed, so set it now */ patglobflags = prog->globend; + /* + * 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. + */ + char *str; + int mlen = ztrsub(patinput, patinstart); + + str = ztrduppfx(patinstart, patinput - patinstart); + 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++ = ztrsub(*sp, patinstart) + patoffset; + if (endp) + *endp++ = ztrsub(*ep, patinstart) + 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 = zcalloc(palen*sizeof(char *)); + mbeginarr = zcalloc(palen*sizeof(char *)); + mendarr = zcalloc(palen*sizeof(char *)); + + sp = patbeginp; + ep = patendp; + + for (i = 0; i < prog->patnpar; i++) { + if (parsfound & (1 << i)) { + matcharr[i] = ztrduppfx(*sp, *ep - *sp); + /* + * 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)(ztrsub(*sp, patinstart) + + patoffset + + !isset(KSHARRAYS))); + mbeginarr[i] = ztrdup(numbuf); + sprintf(numbuf, "%ld", + (long)(ztrsub(*ep, patinstart) + + 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); + } return 1; } else return 0; @@ -1293,10 +1455,10 @@ pattry(Patprog prog, char *string) * comes from the input string, the second the current pattern. */ #define CHARMATCH(chin, chpa) (chin == chpa || \ - ((patglobflags & C_IGNCASE) ? \ + ((patglobflags & GF_IGNCASE) ? \ ((isupper(chin) ? tolower(chin) : chin) == \ (isupper(chpa) ? tolower(chpa) : chpa)) : \ - (patglobflags & C_LCMATCHUC) ? \ + (patglobflags & GF_LCMATCHUC) ? \ (islower(chpa) && toupper(chpa) == chin) : 0)) /* @@ -1314,11 +1476,12 @@ static char *exactpos; */ /**/ -int -patmatch(char *prog) +static int +patmatch(Upat prog) { /* Current and next nodes */ - char *scan = prog, *next, *opnd, *save, *start; + Upat scan = prog, next, opnd; + char *start, *save, *chrop; int savglobflags, op, no, min, nextch, fail = 0, saverrsfound; zrange_t from, to, comp; @@ -1338,36 +1501,38 @@ patmatch(char *prog) break; case P_EXACTLY: /* - * acts as nothing if *opnd is null: this is used by + * acts as nothing if *chrop is null: this is used by * approx code. */ - opnd = exactpos ? exactpos : P_OPERAND(scan); + chrop = exactpos ? exactpos : (char *)P_OPERAND(scan); exactpos = NULL; - while (*opnd && *patinput) { + while (*chrop && *patinput) { int chin = STOUC(UNMETA(patinput)); - int chpa = STOUC(UNMETA(opnd)); + int chpa = STOUC(UNMETA(chrop)); if (!CHARMATCH(chin, chpa)) { fail = 1; break; } - METAINC(opnd); + METAINC(chrop); METAINC(patinput); } - if (*opnd) { - exactpos = opnd; + if (*chrop) { + exactpos = chrop; fail = 1; } break; case P_ANYOF: if (!*patinput || - !patmatchrange(P_OPERAND(scan), STOUC(UNMETA(patinput)))) + !patmatchrange((char *)P_OPERAND(scan), + STOUC(UNMETA(patinput)))) fail = 1; else METAINC(patinput); break; case P_ANYBUT: if (!*patinput || - patmatchrange(P_OPERAND(scan), STOUC(UNMETA(patinput)))) + patmatchrange((char *)P_OPERAND(scan), + STOUC(UNMETA(patinput)))) fail = 1; else METAINC(patinput); @@ -1384,12 +1549,12 @@ patmatch(char *prog) * the first attempt. */ op = P_OP(scan); - start = P_OPERAND(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, (char *)start, sizeof(zrange_t)); + memcpy((char *)&from, start, sizeof(zrange_t)); #else from = *((zrange_t *) start); #endif @@ -1414,7 +1579,8 @@ patmatch(char *prog) return 1; } if (!no && P_OP(next) == P_EXACTLY && - !idigit(STOUC(*P_OPERAND(next))) && !(patglobflags & 0xff)) + !idigit(STOUC(*(char *)P_OPERAND(next))) && + !(patglobflags & 0xff)) return 0; patinput = --save; no++; @@ -1434,7 +1600,8 @@ patmatch(char *prog) if (patmatch(next)) return 1; if (!no && P_OP(next) == P_EXACTLY && - !idigit(STOUC(*P_OPERAND(next))) && !(patglobflags & 0xff)) + !idigit(STOUC(*(char *)P_OPERAND(next))) && + !(patglobflags & 0xff)) return 0; patinput = --save; no++; @@ -1447,9 +1614,8 @@ patmatch(char *prog) case P_BACK: break; case P_GFLAGS: - patglobflags = *(int *)P_OPERAND(scan); + patglobflags = P_OPERAND(scan)->l; break; -#ifdef BACKREFERENCES case P_OPEN: case P_OPEN+1: case P_OPEN+2: @@ -1464,13 +1630,12 @@ patmatch(char *prog) save = patinput; if (patmatch(next)) { - DPUTS(!patstartp, "patstartp not set for backreferencing"); /* - * Don't set ppStartp if some later invocation of + * Don't set patbeginp if some later invocation of * the same parentheses already has. */ if (no && !(parsfound & (1 << (no - 1)))) { - patstartp[no] = save; + patbeginp[no-1] = save; parsfound |= 1 << (no - 1); } return 1; @@ -1493,25 +1658,24 @@ patmatch(char *prog) if (patmatch(next)) { DPUTS(!patendp, "patendp not set for backreferencing"); if (no && !(parsfound & (1 << (no + 15)))) { - patendp[no] = save; + patendp[no-1] = save; parsfound |= 1 << (no + 15); } return 1; } else return 0; break; -#endif case P_EXCSYNC: - /* See the P_EXCLUDE code below for where syncstrp comes from */ + /* See the P_EXCLUDE code below for where syncptr comes from */ { - unsigned char **syncstrp, *syncptr; - char *after; + unsigned char *syncptr; + Upat after; after = P_OPERAND(scan); DPUTS(!P_ISEXCLUDE(after), "BUG: EXCSYNC not followed by EXCLUDE."); - syncstrp = (unsigned char **)P_OPERAND(after); - DPUTS(!*syncstrp, "BUG: EXCSYNC not handled by EXCLUDE"); - syncptr = *syncstrp + (patinput - patinstart); + 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. @@ -1570,15 +1734,14 @@ patmatch(char *prog) * The pointer also tells us where the asserted * pattern matched for use by the exclusion. */ - unsigned char **syncstrp, *oldsyncstr; + Upat syncstrp; + unsigned char *oldsyncstr; char *matchpt = NULL; int ret, savglobdots, matchederrs = 0; -#ifdef BACKREFERENCES int savparsfound = parsfound; -#endif DPUTS(P_OP(scan) == P_WBRANCH, "BUG: excluded WBRANCH"); - syncstrp = (unsigned char **)P_OPERAND(next); + syncstrp = P_OPERAND(next); /* * Unlike WBRANCH, each test at the same exclude * sync point (due to an external loop) is separate, @@ -1586,14 +1749,15 @@ patmatch(char *prog) * (foo~bar)(foo~bar)... from the exclusion point * of view, so we use a different sync string. */ - oldsyncstr = *syncstrp; + oldsyncstr = syncstrp->p; if (!patinlen) patinlen = strlen(patinstart)+1; - *syncstrp = (unsigned char *)zcalloc(patinlen); + syncstrp->p = (unsigned char *)zcalloc(patinlen); while ((ret = patmatch(P_OPERAND(scan)))) { unsigned char *syncpt; char savchar, *testptr; - int savforce = forceerrs; + char *savpatinstart = patinstart; + int savforce = forceerrs, savpatinlen = patinlen; forceerrs = -1; savglobdots = globdots; matchederrs = errsfound; @@ -1607,9 +1771,9 @@ patmatch(char *prog) * possibilities for approximation have been * checked.) */ - for (syncpt = *syncstrp; !*syncpt; syncpt++) + for (syncpt = syncstrp->p; !*syncpt; syncpt++) ; - testptr = patinstart + (syncpt - *syncstrp); + testptr = patinstart + (syncpt - syncstrp->p); DPUTS(testptr > matchpt, "BUG: EXCSYNC failed"); savchar = *testptr; *testptr = '\0'; @@ -1625,7 +1789,7 @@ patmatch(char *prog) */ patglobflags &= ~0xff; errsfound = 0; - opnd = P_OPERAND(next) + sizeof(char *); + opnd = P_OPERAND(next) + 1; if (P_OP(next) == P_EXCLUDP && pathpos) { /* * top level exclusion with a file, @@ -1638,21 +1802,23 @@ patmatch(char *prog) zalloc(pathpos + patinlen); strcpy(buf, pathbuf); strcpy(buf + pathpos, patinput); - patinput = buf; + patinput = patinstart = buf; + patinlen += pathpos; } if (patmatch(opnd)) { ret = 0; -#ifdef BACKREFERENCES /* * Another subtlety: if we exclude the * match, any parentheses just found * become invalidated. */ parsfound = savparsfound; -#endif } - if (buf) + if (buf) { + patinstart = savpatinstart; + patinlen = savpatinlen; zfree(buf, pathpos + patinlen); + } if (!ret) break; next = PATNEXT(next); @@ -1666,8 +1832,8 @@ patmatch(char *prog) patglobflags = savglobflags; errsfound = saverrsfound; } - zfree((char *)*syncstrp, patinlen); - *syncstrp = oldsyncstr; + zfree((char *)syncstrp->p, patinlen); + syncstrp->p = oldsyncstr; if (ret) { patinput = matchpt; errsfound = matchederrs; @@ -1678,7 +1844,8 @@ patmatch(char *prog) ; } else { int ret = 1, pfree = 0; - unsigned char **ptrp = NULL, *ptr; + Upat ptrp = NULL; + unsigned char *ptr; if (P_OP(scan) == P_WBRANCH) { /* * This is where we make sure that we are not @@ -1696,15 +1863,14 @@ patmatch(char *prog) * there is already a 1, then the test fails. */ opnd = P_OPERAND(scan); - ptrp = (unsigned char **)opnd; - opnd += sizeof(unsigned char *); - if (!*ptrp) { + ptrp = opnd++; + if (!ptrp->p) { if (!patinlen) patinlen = strlen((char *)patinstart)+1; - *ptrp = (unsigned char *)zcalloc(patinlen); + ptrp->p = (unsigned char *)zcalloc(patinlen); pfree = 1; } - ptr = *ptrp + (patinput - patinstart); + ptr = ptrp->p + (patinput - patinstart); /* * Without approximation, this is just a @@ -1712,7 +1878,7 @@ patmatch(char *prog) * 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 - * maker more approximations in the remaining + * make more approximations in the remaining * code, we continue with the test. * (This is why the max number of errors is * 254, not 255.) @@ -1725,8 +1891,8 @@ patmatch(char *prog) if (ret) ret = patmatch(opnd); if (pfree) { - zfree((char *)*ptrp, patinlen); - *ptrp = NULL; + zfree((char *)ptrp->p, patinlen); + ptrp->p = NULL; } if (ret) return 1; @@ -1769,12 +1935,38 @@ patmatch(char *prog) return 0; no = patrepeat(P_OPERAND(scan)); } - /* Lookahead to avoid useless matches */ - if (P_OP(next) == P_EXACTLY && !(patglobflags & 0xff)) - nextch = STOUC(UNMETA(P_OPERAND(next))); - else - nextch = -1; min = (op == P_TWOHASH) ? 1 : 0; + /* + * Lookahead to avoid useless matches. This is not possible + * with approximation. + */ + if (P_OP(next) == P_EXACTLY && !(patglobflags & 0xff)) { + char *nextop = (char *)P_OPERAND(next); + /* + * 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 = strlen(patinput); + int oplen = strlen(nextop); + /* Are we in the right range? */ + if (oplen > strlen(min ? METANEXT(start) : start) || + oplen < ptlen) + return 0; + /* Yes, just position appropriately and test. */ + patinput += ptlen - oplen; + if (patinput > start && patinput[-1] == Meta) { + /* doesn't align properly, no go */ + return 0; + } + /* Continue loop with P_EXACTLY test. */ + break; + } + nextch = STOUC(UNMETA(nextop)); + } else + nextch = -1; save = patinput; savglobflags = patglobflags; saverrsfound = errsfound; @@ -2010,13 +2202,13 @@ patmatchrange(char *range, int ch) /* repeatedly match something simple and say how many times */ /**/ -static int patrepeat(char *p) +static int patrepeat(Upat p) { int count = 0, tch, inch; char *scan, *opnd; scan = patinput; - opnd = P_OPERAND(p); + opnd = (char *)P_OPERAND(p); switch(P_OP(p)) { #ifdef DEBUG @@ -2056,6 +2248,16 @@ static int patrepeat(char *p) return count; } +/* Free a patprog. */ + +/**/ +mod_export void +freepatprog(Patprog prog) +{ + if (prog && prog != dummy_patprog1 && prog != dummy_patprog2) + zfree(prog, prog->size); +} + /**/ #ifdef ZSH_PAT_DEBUG @@ -2067,7 +2269,8 @@ static int patrepeat(char *p) static void patdump(Patprog r) { - char *s, *next, *codestart, *base, op = P_EXACTLY; + char *s, *base, op = P_EXACTLY; + Upat up, codestart, next; base = (char *)r; s = base + r->startoff; @@ -2075,13 +2278,14 @@ patdump(Patprog r) if (r->flags & PAT_PURES) { printf("STRING:%s\n", (char *)s); } else { - codestart = s; + codestart = (Upat)s; while (op != P_END) { - op = P_OP(s); - printf("%2d%s", s-codestart, patprop(s)); - next = PATNEXT(s); - printf("(%d)", next ? (s-codestart)+(next-s) : 0); - s += sizeof(zalign_t); + up = (Upat)s; + op = P_OP(up); + printf("%2d%s", up-codestart, patprop(up)); + next = PATNEXT(up); + printf("(%d)", next ? next-codestart : 0); + s += sizeof(union upat); if (op == P_ANYOF || op == P_ANYBUT || op == P_EXACTLY) { while (*s != '\0') { if (itok(*s)) { @@ -2107,16 +2311,15 @@ patdump(Patprog r) s += sizeof(zrange_t); } } else if (op == P_GFLAGS) { - long *lptr = (long *)s; - printf("%ld, %ld", *lptr & ~0xff, *lptr & 0xff); - s += sizeof(long); + printf("%ld, %ld", (++up)->l & ~0xff, (++up)->l & 0xff); + s += sizeof(union upat); } else if (op == P_WBRANCH || op == P_EXCLUDE || op == P_EXCLUDP) { - s += sizeof(char *); + s += sizeof(union upat); } putchar('\n'); - s = base + (((s - base) + sizeof(zalign_t) - 1) & - ~(sizeof(zalign_t) - 1)); + s = base + (((s - base) + sizeof(union upat) - 1) & + ~(sizeof(union upat) - 1)); } } @@ -2125,18 +2328,16 @@ patdump(Patprog r) printf("start `%c' ", r->patstartch); if (!(r->flags & PAT_NOANCH)) printf("EOL-anchor "); -#ifdef BACKREFERENCES - if (r->flags & PAT_BACKR) - printf("backreferences "); -#endif + if (r->patnpar) + printf("%d active backreferences ", r->patnpar); if (r->mustoff) printf("must have \"%s\"", (char *)r + r->mustoff); printf("\n"); if (r->globflags) { printf("Globbing flags: "); - if (r->globflags & C_LCMATCHUC) + if (r->globflags & GF_LCMATCHUC) printf("LC matches UC "); - if (r->globflags & C_IGNCASE) + if (r->globflags & GF_IGNCASE) printf("Ignore case"); printf("\n"); if (r->globflags & 0xff) @@ -2146,7 +2347,7 @@ patdump(Patprog r) /**/ static char * -patprop(char *op) +patprop(Upat op) { char *p = NULL; static char buf[50]; @@ -2258,16 +2459,11 @@ int bin_patdebug(char *name, char **args, char *ops, int func) { Patprog prog; - int ret = 0, flags; + int ret = 0; tokenize(*args); -#ifdef BACKREFERENCES - flags = ops['b'] ? PAT_BACKR : 0; -#else - flags = 0; -#endif - if (!(prog = patcompile((char *)*args, flags, 0))) + if (!(prog = patcompile((char *)*args, 0, 0))) return 1; if (ops['p'] || !args[1]) { patdump(prog); diff --git a/Src/text.c b/Src/text.c index b7df8012f..ab6ca5eb9 100644 --- a/Src/text.c +++ b/Src/text.c @@ -31,7 +31,7 @@ #include "text.pro" static char *tptr, *tbuf, *tlim; -static int tsiz, tindent, tnewlins; +static int tsiz, tindent, tnewlins, tjob; /* add a character to the text buffer */ @@ -72,19 +72,18 @@ taddstr(char *s) tptr += sl; } -#if 0 -/* add an integer to the text buffer */ - /**/ -void -taddint(int x) +static void +taddlist(Estate state, int num) { - char buf[DIGBUFSIZE]; - - sprintf(buf, "%d", x); - taddstr(buf); + if (num) { + while (num--) { + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddchr(' '); + } + tptr--; + } } -#endif /* add a newline, or something equivalent, to the text buffer */ @@ -105,15 +104,26 @@ taddnl(void) /* get a permanent textual representation of n */ /**/ -char * -getpermtext(struct node *n) +mod_export char * +getpermtext(Eprog prog, Wordcode c) { + struct estate s; + + if (!c) + c = prog->prog; + + s.prog = prog; + s.pc = c; + s.strs = prog->strs; + tnewlins = 1; tbuf = (char *)zalloc(tsiz = 32); tptr = tbuf; tlim = tbuf + tsiz; tindent = 1; - gettext2(n); + tjob = 0; + if (prog->len) + gettext2(&s); *tptr = '\0'; untokenize(tbuf); return tbuf; @@ -123,344 +133,587 @@ getpermtext(struct node *n) /**/ char * -getjobtext(struct node *n) +getjobtext(Eprog prog, Wordcode c) { static char jbuf[JOBTEXTSIZE]; + struct estate s; + + if (!c) + c = prog->prog; + + s.prog = prog; + s.pc = c; + s.strs = prog->strs; + tnewlins = 0; tbuf = NULL; tptr = jbuf; tlim = tptr + JOBTEXTSIZE - 1; tindent = 1; - gettext2(n); + tjob = 1; + gettext2(&s); *tptr = '\0'; untokenize(jbuf); return jbuf; } -#define gt2(X) gettext2((struct node *) (X)) - /* - "gettext2" or "type checking and how to avoid it" - an epic function by Paul Falstad -*/ - -#define _Cond(X) ((Cond) (X)) -#define _Cmd(X) ((Cmd) (X)) -#define _Pline(X) ((Pline) (X)) -#define _Sublist(X) ((Sublist) (X)) -#define _List(X) ((List) (X)) -#define _casecmd(X) ((struct casecmd *) (X)) -#define _ifcmd(X) ((struct ifcmd *) (X)) -#define _whilecmd(X) ((struct whilecmd *) (X)) + * gettext2() shows one way to walk through the word code without + * recursion. We start by reading a word code and executing the + * action for it. Some codes have sub-structures (like, e.g. WC_FOR) + * and require something to be done after the sub-structure has been + * handled. For these codes a tstack structure which describes what + * has to be done is pushed onto a stack. Codes without sub-structures + * arrange for the next structure being taken from the stack so that + * the action for it is executed instead of the one for the next + * word code. If the stack is empty at this point, we have handled + * the whole structure we were called for. + */ + +typedef struct tstack *Tstack; + +struct tstack { + Tstack prev; + wordcode code; + int pop; + union { + struct { + LinkList list; + } _redir; + struct { + char *strs; + Wordcode end; + } _funcdef; + struct { + Wordcode end; + } _case; + struct { + int cond; + Wordcode end; + } _if; + struct { + int par; + } _cond; + struct { + Wordcode end; + } _subsh; + } u; +}; + +static Tstack tstack, tfree; + +static Tstack +tpush(wordcode code, int pop) +{ + Tstack s; + + if ((s = tfree)) + tfree = s->prev; + else + s = (Tstack) zalloc(sizeof(*s)); + + s->prev = tstack; + tstack = s; + s->code = code; + s->pop = pop; + + return s; +} /**/ static void -gettext2(struct node *n) +gettext2(Estate state) { - Cmd nn; - - if (!n || ((List) n) == &dummy_list) - return; - switch (NT_TYPE(n->ntype)) { - case N_LIST: - gt2(_List(n)->left); - if (_List(n)->type & Z_ASYNC) { - taddstr(" &"); - if (_List(n)->type & Z_DISOWN) - taddstr("|"); - } - simplifyright(_List(n)); - if (_List(n)->right) { - if (tnewlins) - taddnl(); - else - taddstr((_List(n)->type & Z_ASYNC) ? " " : "; "); - gt2(_List(n)->right); - } - break; - case N_SUBLIST: - if (_Sublist(n)->flags & PFLAG_NOT) - taddstr("! "); - if (_Sublist(n)->flags & PFLAG_COPROC) - taddstr("coproc "); - gt2(_Sublist(n)->left); - if (_Sublist(n)->right) { - taddstr((_Sublist(n)->type == ORNEXT) ? " || " : " && "); - gt2(_Sublist(n)->right); - } - break; - case N_PLINE: - gt2(_Pline(n)->left); - if (_Pline(n)->type == PIPE) { - taddstr(" | "); - gt2(_Pline(n)->right); + Tstack s, n; + int stack = 0; + wordcode code; + + while (1) { + if (stack) { + if (!(s = tstack)) + return; + if (s->pop) { + tstack = s->prev; + s->prev = tfree; + tfree = s; + } + code = s->code; + stack = 0; + } else { + s = NULL; + code = *state->pc++; } - break; - case N_CMD: - nn = _Cmd(n); - switch (nn->type) { - case SIMPLE: - getsimptext(nn); + switch (wc_code(code)) { + case WC_LIST: + if (!s) { + s = tpush(code, (WC_LIST_TYPE(code) & Z_END)); + stack = 0; + } else { + if (WC_LIST_TYPE(code) & Z_ASYNC) { + taddstr(" &"); + if (WC_LIST_TYPE(code) & Z_DISOWN) + taddstr("|"); + } + if (!(stack = (WC_LIST_TYPE(code) & Z_END))) { + if (tnewlins) + taddnl(); + else + taddstr((WC_LIST_TYPE(code) & Z_ASYNC) ? " " : "; "); + s->code = *state->pc++; + s->pop = (WC_LIST_TYPE(s->code) & Z_END); + } + } + if (!stack && (WC_LIST_TYPE(s->code) & Z_SIMPLE)) + state->pc++; + break; + case WC_SUBLIST: + if (!s) { + if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT) + taddstr("! "); + if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_COPROC) + taddstr("coproc "); + s = tpush(code, (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END)); + } else { + if (!(stack = (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END))) { + taddstr((WC_SUBLIST_TYPE(code) == WC_SUBLIST_OR) ? + " || " : " && "); + s->code = *state->pc++; + s->pop = (WC_SUBLIST_TYPE(s->code) == WC_SUBLIST_END); + if (WC_SUBLIST_FLAGS(s->code) & WC_SUBLIST_NOT) + taddstr("! "); + if (WC_SUBLIST_FLAGS(s->code) & WC_SUBLIST_COPROC) + taddstr("coproc "); + } + } + if (!stack && (WC_SUBLIST_FLAGS(s->code) & WC_SUBLIST_SIMPLE)) + state->pc++; + break; + case WC_PIPE: + if (!s) { + tpush(code, (WC_PIPE_TYPE(code) == WC_PIPE_END)); + if (WC_PIPE_TYPE(code) == WC_PIPE_MID) + state->pc++; + } else { + if (!(stack = (WC_PIPE_TYPE(code) == WC_PIPE_END))) { + taddstr(" | "); + s->code = *state->pc++; + if (!(s->pop = (WC_PIPE_TYPE(s->code) == WC_PIPE_END))) + state->pc++; + } + } break; - case SUBSH: - taddstr("( "); - tindent++; - gt2(nn->u.list); - tindent--; - taddstr(" )"); + case WC_REDIR: + if (!s) { + state->pc--; + n = tpush(code, 1); + n->u._redir.list = ecgetredirs(state); + } else { + getredirs(s->u._redir.list); + stack = 1; + } break; - case ZCTIME: - taddstr("time "); - tindent++; - gt2(nn->u.pline); - tindent--; + case WC_ASSIGN: + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddchr('='); + if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) { + taddchr('('); + taddlist(state, WC_ASSIGN_NUM(code)); + taddstr(") "); + } else { + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddchr(' '); + } break; - case FUNCDEF: - taddlist(nn->args); - taddstr(" () {"); - tindent++; - taddnl(); - gt2(nn->u.list); - tindent--; - taddnl(); - taddstr("}"); + case WC_SIMPLE: + taddlist(state, WC_SIMPLE_ARGC(code)); + stack = 1; break; - case CURSH: - taddstr("{ "); - tindent++; - gt2(nn->u.list); - tindent--; - taddstr(" }"); + case WC_SUBSH: + if (!s) { + taddstr("( "); + tindent++; + n = tpush(code, 1); + n->u._subsh.end = state->pc + WC_SUBSH_SKIP(code); + } else { + state->pc = s->u._subsh.end; + tindent--; + taddstr(" )"); + stack = 1; + } break; - case CFOR: - case CSELECT: - taddstr((nn->type == CFOR) ? "for " : "select "); - if (nn->u.forcmd->condition) { - taddstr("(("); - taddstr(nn->u.forcmd->name); - taddstr("; "); - taddstr(nn->u.forcmd->condition); - taddstr("; "); - taddstr(nn->u.forcmd->advance); - taddstr(")) do"); + case WC_CURSH: + if (!s) { + taddstr("{ "); + tindent++; + n = tpush(code, 1); + n->u._subsh.end = state->pc + WC_CURSH_SKIP(code); } else { - taddstr(nn->u.forcmd->name); - if (nn->u.forcmd->inflag) { - taddstr(" in "); - taddlist(nn->args); - } - taddnl(); - taddstr("do"); + state->pc = s->u._subsh.end; + tindent--; + taddstr(" }"); + stack = 1; } - tindent++; - taddnl(); - gt2(nn->u.forcmd->list); - tindent--; - taddnl(); - taddstr("done"); break; - case CIF: - gt2(nn->u.ifcmd); - taddstr("fi"); + case WC_TIMED: + if (!s) { + taddstr("time"); + if (WC_TIMED_TYPE(code) == WC_TIMED_PIPE) { + taddchr(' '); + tindent++; + tpush(code, 1); + } else + stack = 1; + } else { + tindent--; + stack = 1; + } break; - case CCASE: - gt2(nn->u.casecmd); + case WC_FUNCDEF: + if (!s) { + Wordcode p = state->pc; + Wordcode end = p + WC_FUNCDEF_SKIP(code); + + taddlist(state, *state->pc++); + if (tjob) { + taddstr(" () { ... }"); + state->pc = end; + stack = 1; + } else { + taddstr(" () {"); + tindent++; + taddnl(); + n = tpush(code, 1); + n->u._funcdef.strs = state->strs; + n->u._funcdef.end = end; + state->strs += *state->pc; + state->pc += 3; + } + } else { + state->strs = s->u._funcdef.strs; + state->pc = s->u._funcdef.end; + tindent--; + taddnl(); + taddstr("}"); + stack = 1; + } break; - case COND: - taddstr("[[ "); - gt2(nn->u.cond); - taddstr(" ]]"); + case WC_FOR: + if (!s) { + taddstr("for "); + if (WC_FOR_TYPE(code) == WC_FOR_COND) { + taddstr("(("); + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddstr("; "); + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddstr("; "); + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddstr(")) do"); + } else { + taddstr(ecgetstr(state, EC_NODUP, NULL)); + if (WC_FOR_TYPE(code) == WC_FOR_LIST) { + taddstr(" in "); + taddlist(state, *state->pc++); + } + taddnl(); + taddstr("do"); + } + tindent++; + taddnl(); + tpush(code, 1); + } else { + tindent--; + taddnl(); + taddstr("done"); + stack = 1; + } break; - case CARITH: - taddstr("(("); - taddlist(nn->args); - taddstr("))"); + case WC_SELECT: + if (!s) { + taddstr("select "); + taddstr(ecgetstr(state, EC_NODUP, NULL)); + if (WC_SELECT_TYPE(code) == WC_SELECT_LIST) { + taddstr(" in "); + taddlist(state, *state->pc++); + } + tindent++; + taddnl(); + tpush(code, 1); + } else { + tindent--; + taddnl(); + taddstr("done"); + stack = 1; + } break; - case CREPEAT: - taddstr("repeat "); - taddlist(nn->args); - taddnl(); - taddstr("do"); - tindent++; - taddnl(); - gt2(nn->u.list); - tindent--; - taddnl(); - taddstr("done"); + case WC_WHILE: + if (!s) { + taddstr(WC_WHILE_TYPE(code) == WC_WHILE_UNTIL ? + "until " : "while "); + tindent++; + tpush(code, 0); + } else if (!s->pop) { + tindent--; + taddnl(); + taddstr("do"); + tindent++; + taddnl(); + s->pop = 1; + } else { + tindent--; + taddnl(); + taddstr("done"); + stack = 1; + } break; - case CWHILE: - gt2(nn->u.whilecmd); + case WC_REPEAT: + if (!s) { + taddstr("repeat "); + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddnl(); + taddstr("do"); + tindent++; + taddnl(); + tpush(code, 1); + } else { + tindent--; + taddnl(); + taddstr("done"); + stack = 1; + } break; - } - getredirs(nn); - break; - case N_COND: - getcond(_Cond(n), 0); - break; - case N_CASE: - { - List *l; - char **p; - - l = _casecmd(n)->lists; - p = _casecmd(n)->pats; - - taddstr("case "); - taddstr(*p++); - taddstr(" in"); - tindent++; - for (; *l; p++, l++) { + case WC_CASE: + if (!s) { + Wordcode end = state->pc + WC_CASE_SKIP(code); + + taddstr("case "); + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddstr(" in"); + + if (state->pc >= end) { + if (tnewlins) + taddnl(); + else + taddchr(' '); + taddstr("esac"); + stack = 1; + } else { + tindent++; + if (tnewlins) + taddnl(); + else + taddchr(' '); + code = *state->pc++; + taddstr(ecgetstr(state, EC_NODUP, NULL)); + state->pc++; + taddstr(") "); + tindent++; + n = tpush(code, 0); + n->u._case.end = end; + n->pop = (state->pc - 2 + WC_CASE_SKIP(code) >= end); + } + } else if (state->pc < s->u._case.end) { + tindent--; + taddstr(WC_CASE_TYPE(code) == WC_CASE_OR ? " ;;" : ";&"); if (tnewlins) taddnl(); else taddchr(' '); - taddstr(*p + 1); + code = *state->pc++; + taddstr(ecgetstr(state, EC_NODUP, NULL)); + state->pc++; taddstr(") "); tindent++; - gt2(*l); + s->code = code; + s->pop = ((state->pc - 2 + WC_CASE_SKIP(code)) >= + s->u._case.end); + } else { + tindent--; + taddstr(WC_CASE_TYPE(code) == WC_CASE_OR ? " ;;" : ";&"); tindent--; - taddstr(" ;"); - taddchr(**p); + if (tnewlins) + taddnl(); + else + taddchr(' '); + taddstr("esac"); + stack = 1; } - tindent--; - if (tnewlins) - taddnl(); - else - taddchr(' '); - taddstr("esac"); break; - } - case N_IF: - { - List *i, *t; + case WC_IF: + if (!s) { + Wordcode end = state->pc + WC_IF_SKIP(code); - taddstr("if "); - for (i = _ifcmd(n)->ifls, t = _ifcmd(n)->thenls; *i; i++, t++) { + taddstr("if "); tindent++; - gt2(*i); + state->pc++; + + n = tpush(code, 0); + n->u._if.end = end; + n->u._if.cond = 1; + } else if (s->pop) { + stack = 1; + } else if (s->u._if.cond) { tindent--; taddnl(); taddstr("then"); tindent++; taddnl(); - gt2(*t); + s->u._if.cond = 0; + } else if (state->pc < s->u._if.end) { tindent--; taddnl(); - if (i[1]) { + code = *state->pc++; + if (WC_IF_TYPE(code) == WC_IF_ELIF) { taddstr("elif "); + tindent++; + s->u._if.cond = 1; + } else { + taddstr("else"); + tindent++; + taddnl(); } - } - if (*t) { - taddstr("else"); - tindent++; - taddnl(); - gt2(*t); + } else { + s->pop = 1; tindent--; taddnl(); + taddstr("fi"); + stack = 1; } break; - } - case N_WHILE: - taddstr((_whilecmd(n)->cond) ? "until " : "while "); - tindent++; - gt2(_whilecmd(n)->cont); - tindent--; - taddnl(); - taddstr("do"); - tindent++; - taddnl(); - gt2(_whilecmd(n)->loop); - tindent--; - taddnl(); - taddstr("done"); - break; - } -} - -/* Print a condition bracketed by [[ ... ]]. * - * With addpar non-zero, parenthesise the subexpression. */ - -/**/ -static void -getcond(Cond nm, int addpar) -{ - static char *c1[] = - { - "=", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq", - "-ne", "-lt", "-gt", "-le", "-ge" - }; - - if (addpar) - taddstr("( "); - switch (nm->type) { - case COND_NOT: - taddstr("! "); - getcond(nm->left, _Cond(nm->left)->type <= COND_OR); - break; - case COND_AND: - getcond(nm->left, _Cond(nm->left)->type == COND_OR); - taddstr(" && "); - getcond(nm->right, _Cond(nm->right)->type == COND_OR); - break; - case COND_OR: - /* This is deliberately over-generous with parentheses: * - * in fact omitting them gives correct precedence. */ - getcond(nm->left, _Cond(nm->left)->type == COND_AND); - taddstr(" || "); - getcond(nm->right, _Cond(nm->right)->type == COND_AND); - break; - default: - if (nm->type <= COND_GE) { - /* Binary test: `a = b' etc. */ - taddstr(nm->left); - taddstr(" "); - taddstr(c1[nm->type - COND_STREQ]); - taddstr(" "); - taddstr(nm->right); - } else { - /* Unary test: `-f foo' etc. */ - char c2[4]; - - c2[0] = '-'; - c2[1] = nm->type; - c2[2] = ' '; - c2[3] = '\0'; - taddstr(c2); - taddstr(nm->left); - } - break; - } - if (addpar) - taddstr(" )"); -} - -/**/ -static void -getsimptext(Cmd cmd) -{ - LinkNode n; - - for (n = firstnode(cmd->vars); n; incnode(n)) { - struct varasg *v = (struct varasg *)getdata(n); - - taddstr(v->name); - taddchr('='); - if (PM_TYPE(v->type) == PM_ARRAY) { - taddchr('('); - taddlist(v->arr); - taddstr(") "); - } else { - taddstr(v->str); - taddchr(' '); + case WC_COND: + { + static char *c1[] = { + "=", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq", + "-ne", "-lt", "-gt", "-le", "-ge" + }; + + int ctype; + + if (!s) { + taddstr("[[ "); + n = tpush(code, 1); + n->u._cond.par = 2; + } else if (s->u._cond.par == 2) { + taddstr(" ]]"); + stack = 1; + break; + } else if (s->u._cond.par == 1) { + taddstr(" )"); + stack = 1; + break; + } else if (WC_COND_TYPE(s->code) == COND_AND) { + taddstr(" && "); + code = *state->pc++; + if (WC_COND_TYPE(code) == COND_OR) { + taddstr("( "); + n = tpush(code, 1); + n->u._cond.par = 1; + } + } else if (WC_COND_TYPE(s->code) == COND_OR) { + taddstr(" || "); + code = *state->pc++; + if (WC_COND_TYPE(code) == COND_AND) { + taddstr("( "); + n = tpush(code, 1); + n->u._cond.par = 1; + } + } + while (!stack) { + switch ((ctype = WC_COND_TYPE(code))) { + case COND_NOT: + taddstr("! "); + code = *state->pc++; + if (WC_COND_TYPE(code) <= COND_OR) { + taddstr("( "); + n = tpush(code, 1); + n->u._cond.par = 1; + } + break; + case COND_AND: + n = tpush(code, 1); + n->u._cond.par = 0; + code = *state->pc++; + if (WC_COND_TYPE(code) == COND_OR) { + taddstr("( "); + n = tpush(code, 1); + n->u._cond.par = 1; + } + break; + case COND_OR: + n = tpush(code, 1); + n->u._cond.par = 0; + code = *state->pc++; + if (WC_COND_TYPE(code) == COND_AND) { + taddstr("( "); + n = tpush(code, 1); + n->u._cond.par = 1; + } + break; + case COND_MOD: + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddchr(' '); + taddlist(state, WC_COND_SKIP(code)); + stack = 1; + break; + case COND_MODI: + { + char *name = ecgetstr(state, EC_NODUP, NULL); + + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddchr(' '); + taddstr(name); + taddchr(' '); + taddstr(ecgetstr(state, EC_NODUP, NULL)); + stack = 1; + } + break; + default: + if (ctype <= COND_GE) { + /* Binary test: `a = b' etc. */ + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddstr(" "); + taddstr(c1[ctype - COND_STREQ]); + taddstr(" "); + taddstr(ecgetstr(state, EC_NODUP, NULL)); + if (ctype == COND_STREQ || + ctype == COND_STRNEQ) + state->pc++; + } else { + /* Unary test: `-f foo' etc. */ + char c2[4]; + + c2[0] = '-'; + c2[1] = ctype; + c2[2] = ' '; + c2[3] = '\0'; + taddstr(c2); + taddstr(ecgetstr(state, EC_NODUP, NULL)); + } + stack = 1; + break; + } + } + } + break; + case WC_ARITH: + taddstr("(("); + taddstr(ecgetstr(state, EC_NODUP, NULL)); + taddstr("))"); + stack = 1; + break; + case WC_END: + stack = 1; + break; + default: + DPUTS(1, "unknown word code in gettext2()"); + return; } } - taddlist(cmd->args); } /**/ void -getredirs(Cmd cmd) +getredirs(LinkList redirs) { LinkNode n; static char *fstr[] = @@ -468,10 +721,9 @@ getredirs(Cmd cmd) ">", ">|", ">>", ">>|", "&>", "&>|", "&>>", "&>>|", "<>", "<", "<<", "<<-", "<<<", "<&", ">&", NULL /* >&- */, "<", ">" }; - taddchr(' '); - for (n = firstnode(cmd->redir); n; incnode(n)) { - struct redir *f = (struct redir *)getdata(n); + for (n = firstnode(redirs); n; incnode(n)) { + Redir f = (Redir) getdata(n); switch (f->type) { case WRITE: @@ -493,7 +745,12 @@ getredirs(Cmd cmd) taddchr('0' + f->fd1); taddstr(fstr[f->type]); taddchr(' '); - taddstr(f->name); + if (f->type == HERESTR) { + taddchr('\''); + taddstr(bslashquote(f->name, NULL, 1)); + taddchr('\''); + } else + taddstr(f->name); taddchr(' '); break; #ifdef DEBUG @@ -509,18 +766,3 @@ getredirs(Cmd cmd) } tptr--; } - -/**/ -static void -taddlist(LinkList l) -{ - LinkNode n; - - if (!(n = firstnode(l))) - return; - for (; n; incnode(n)) { - taddstr(getdata(n)); - taddchr(' '); - } - tptr--; -} diff --git a/Src/zsh.h b/Src/zsh.h index e96fc6e86..35b9c6737 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -31,15 +31,75 @@ #define zleread(X,Y,H) zlereadptr(X,Y,H) #define spaceinline(X) spaceinlineptr(X) #define gotword() gotwordptr() -#define refresh() refreshptr() +#define zrefresh() refreshptr() #define compctlread(N,A,O,R) compctlreadptr(N,A,O,R) /* 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. + * Currently we only define this to be longer than a long if --enable-lfs + * was given. That enables internal use of 64-bit types even if + * no actual large file support is present. + */ +#ifdef ZSH_64_BIT_TYPE +typedef ZSH_64_BIT_TYPE zlong; +#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; +#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 int LV; +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 */ + +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; + NumMathFunc nfunc; + StrMathFunc sfunc; + char *module; + int minargs; + int maxargs; + int funcid; +}; + +#define MFF_STR 1 +#define MFF_ADDED 2 + +#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 * @@ -207,6 +267,7 @@ enum { #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_LINENO (1<<5) /* update line number */ /* Flags for metafy */ #define META_REALLOC 0 @@ -228,34 +289,33 @@ typedef struct linklist *LinkList; typedef struct hashnode *HashNode; typedef struct hashtable *HashTable; +typedef struct optname *Optname; typedef struct reswd *Reswd; typedef struct alias *Alias; typedef struct param *Param; +typedef struct paramdef *Paramdef; typedef struct cmdnam *Cmdnam; typedef struct shfunc *Shfunc; +typedef struct funcstack *Funcstack; +typedef struct funcwrap *FuncWrap; typedef struct builtin *Builtin; typedef struct nameddir *Nameddir; typedef struct module *Module; +typedef struct linkedmod *Linkedmod; +typedef struct patprog *Patprog; typedef struct process *Process; typedef struct job *Job; typedef struct value *Value; -typedef struct varasg *Varasg; -typedef struct cond *Cond; -typedef struct cmd *Cmd; -typedef struct pline *Pline; -typedef struct sublist *Sublist; -typedef struct list *List; -typedef struct comp *Comp; +typedef struct conddef *Conddef; typedef struct redir *Redir; typedef struct complist *Complist; typedef struct heap *Heap; typedef struct heapstack *Heapstack; typedef struct histent *Histent; -typedef struct forcmd *Forcmd; -typedef struct autofn *AutoFn; +typedef struct hookdef *Hookdef; -typedef struct asgment *Asgment; +typedef struct asgment *Asgment; /********************************/ @@ -277,167 +337,57 @@ struct linklist { /* Macros for manipulating link lists */ -#define addlinknode(X,Y) insertlinknode(X,(X)->last,Y) -#define uaddlinknode(X,Y) uinsertlinknode(X,(X)->last,Y) -#define empty(X) ((X)->first == NULL) -#define nonempty(X) ((X)->first != NULL) -#define firstnode(X) ((X)->first) -#define getaddrdata(X) (&((X)->dat)) -#define getdata(X) ((X)->dat) -#define setdata(X,Y) ((X)->dat = (Y)) -#define lastnode(X) ((X)->last) -#define nextnode(X) ((X)->next) -#define prevnode(X) ((X)->last) -#define peekfirst(X) ((X)->first->dat) -#define pushnode(X,Y) insertlinknode(X,(LinkNode) X,Y) -#define incnode(X) (X = nextnode(X)) -#define gethistent(X) (histentarr+((X)%histentct)) - +#define addlinknode(X,Y) insertlinknode(X,(X)->last,Y) +#define zaddlinknode(X,Y) zinsertlinknode(X,(X)->last,Y) +#define uaddlinknode(X,Y) uinsertlinknode(X,(X)->last,Y) +#define empty(X) ((X)->first == NULL) +#define nonempty(X) ((X)->first != NULL) +#define firstnode(X) ((X)->first) +#define getaddrdata(X) (&((X)->dat)) +#define getdata(X) ((X)->dat) +#define setdata(X,Y) ((X)->dat = (Y)) +#define lastnode(X) ((X)->last) +#define nextnode(X) ((X)->next) +#define prevnode(X) ((X)->last) +#define peekfirst(X) ((X)->first->dat) +#define pushnode(X,Y) insertlinknode(X,(LinkNode) X,Y) +#define zpushnode(X,Y) zinsertlinknode(X,(LinkNode) X,Y) +#define incnode(X) (X = nextnode(X)) +#define firsthist() (hist_ring? hist_ring->down->histnum : curhist) +#define setsizednode(X,Y,Z) ((X)->first[(Y)].dat = (void *) (Z)) + +/* stack allocated linked lists */ + +#define local_list0(N) struct linklist N +#define init_list0(N) \ + do { \ + (N).first = NULL; \ + (N).last = (LinkNode) &(N); \ + } while (0) +#define local_list1(N) struct linklist N; struct linknode __n0 +#define init_list1(N,V0) \ + do { \ + (N).first = &__n0; \ + (N).last = &__n0; \ + __n0.next = NULL; \ + __n0.last = (LinkNode) &(N); \ + __n0.dat = (void *) (V0); \ + } while (0) /********************************/ /* Definitions for syntax trees */ /********************************/ -/* struct list, struct sublist, struct pline, etc. all fit the form * - * of this structure and are used interchangably. The ptrs may hold * - * integers or pointers, depending on the type of the node. */ - -/* Generic node structure for syntax trees */ -struct node { - int ntype; /* node type */ -}; - -#define N_LIST 0 -#define N_SUBLIST 1 -#define N_PLINE 2 -#define N_CMD 3 -#define N_REDIR 4 -#define N_COND 5 -#define N_FOR 6 -#define N_CASE 7 -#define N_IF 8 -#define N_WHILE 9 -#define N_VARASG 10 -#define N_AUTOFN 11 -#define N_COUNT 12 - -/* values for types[4] */ - -#define NT_EMPTY 0 -#define NT_NODE 1 -#define NT_STR 2 -#define NT_LIST 4 -#define NT_ARR 8 - -#define NT_TYPE(T) ((T) & 0xff) -#define NT_N(T, N) (((T) >> (8 + (N) * 4)) & 0xf) -#define NT_SET(T0, T1, T2, T3, T4) \ - ((T0) | ((T1) << 8) | ((T2) << 12) | ((T3) << 16) | ((T4) << 20)) -#define NT_HEAP (1 << 30) - -/* tree element for lists */ - -struct list { - int ntype; /* node type */ - int type; - Sublist left; - List right; -}; - /* 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_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 */ -/* tree element for sublists */ - -struct sublist { - int ntype; /* node type */ - int type; - int flags; /* see PFLAGs below */ - Pline left; - Sublist right; -}; - -#define ORNEXT 10 /* || */ -#define ANDNEXT 11 /* && */ - -#define PFLAG_NOT 1 /* ! ... */ -#define PFLAG_COPROC 32 /* coproc ... */ - -/* tree element for pipes */ - -struct pline { - int ntype; /* node type */ - int type; - Cmd left; - Pline right; -}; - -#define END 0 /* pnode *right is null */ -#define PIPE 1 /* pnode *right is the rest of the pipeline */ - -/* tree element for commands */ - -struct cmd { - int ntype; /* node type */ - int type; - int flags; /* see CFLAGs below */ - int lineno; /* lineno of script for command */ - union { - List list; /* for SUBSH/CURSH/SHFUNC */ - Forcmd forcmd; - struct casecmd *casecmd; - struct ifcmd *ifcmd; - struct whilecmd *whilecmd; - Sublist pline; - Cond cond; - AutoFn autofn; - void *generic; - } u; - LinkList args; /* command & argmument List (char *'s) */ - LinkList redir; /* i/o redirections (struct redir *'s) */ - LinkList vars; /* param assignments (struct varasg *'s) */ -}; - -/* cmd types */ -#define SIMPLE 0 -#define SUBSH 1 -#define CURSH 2 -#define ZCTIME 3 -#define FUNCDEF 4 -#define CFOR 5 -#define CWHILE 6 -#define CREPEAT 7 -#define CIF 8 -#define CCASE 9 -#define CSELECT 10 -#define COND 11 -#define CARITH 12 -#define AUTOFN 13 - -/* flags for command modifiers */ -#define CFLAG_EXEC (1<<0) /* exec ... */ - -/* tree element for redirection lists */ - -struct redir { - int ntype; /* node type */ - int type; - int fd1, fd2; - char *name; -}; - -/* tree element for conditionals */ - -struct cond { - int ntype; /* node type */ - int type; /* can be cond_type, or a single */ - /* letter (-a, -b, ...) */ - void *left, *right; -}; +/* Condition types. */ #define COND_NOT 0 #define COND_AND 1 @@ -455,50 +405,34 @@ struct cond { #define COND_GT 13 #define COND_LE 14 #define COND_GE 15 - -struct forcmd { /* for/select */ -/* Cmd->args contains list of words to loop thru */ - int ntype; /* node type */ - int inflag; /* if there is an in ... clause */ - char *name; /* initializer or parameter name */ - char *condition; /* arithmetic terminating condition */ - char *advance; /* evaluated after each loop */ - List list; /* list to look through for each name */ -}; - -struct casecmd { -/* Cmd->args contains word to test */ - int ntype; /* node type */ - char **pats; - List *lists; /* list to execute */ +#define COND_MOD 16 +#define COND_MODI 17 + +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 */ }; +#define CONDF_INFIX 1 +#define CONDF_ADDED 2 -/* A command like "if foo then bar elif baz then fubar else fooble" */ -/* generates a tree like: */ -/* */ -/* struct ifcmd a = { next = &b, ifl = "foo", thenl = "bar" } */ -/* struct ifcmd b = { next = &c, ifl = "baz", thenl = "fubar" } */ -/* struct ifcmd c = { next = NULL, ifl = NULL, thenl = "fooble" } */ - -struct ifcmd { - int ntype; /* node type */ - List *ifls; - List *thenls; -}; +#define CONDDEF(name, flags, handler, min, max, condid) \ + { NULL, name, flags, handler, min, max, condid, NULL } -struct whilecmd { - int ntype; /* node type */ - int cond; /* 0 for while, 1 for until */ - List cont; /* condition */ - List loop; /* list to execute until condition met */ -}; - -/* node for autoloading functions */ +/* tree element for redirection lists */ -struct autofn { - int ntype; /* node type */ - Shfunc shf; /* the shell function to define */ +struct redir { + int type; + int fd1, fd2; + char *name; }; /* The number of fds space is allocated for * @@ -520,14 +454,12 @@ struct multio { int fds[MULTIOUNIT]; /* list of src/dests redirected to/from this fd */ }; -/* variable assignment tree element */ +/* structure for foo=bar assignments */ -struct varasg { - int ntype; /* node type */ - int type; /* nonzero means array */ +struct asgment { + struct asgment *next; char *name; - char *str; /* should've been a union here. oh well */ - LinkList arr; + char *value; }; /* lvalue for variable assignment/expansion */ @@ -538,18 +470,191 @@ struct value { int inv; /* should we return the index ? */ int a; /* first element of array slice, or -1 */ int b; /* last element of array slice, or -1 */ + char **arr; /* cache for hash turned into array */ }; -/* structure for foo=bar assignments */ +#define MAX_ARRLEN 262144 -struct asgment { - struct asgment *next; - char *name; - char *value; +/********************************************/ +/* Defintions 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 */ + char *name; /* path name */ + int fd; /* file descriptor */ + Wordcode map; /* pointer to header */ + Wordcode addr; /* mapped region */ + int len; /* length */ + int count; /* reference count */ }; -#define MAX_ARRLEN 262144 +struct eprog { + int flags; /* EF_* below */ + int len; /* total block length */ + int npats; /* Patprog cache size */ + 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 next; + char *str; + wordcode offs; + int nfunc; +}; +#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_SUBSH 7 +#define WC_CURSH 8 +#define WC_TIMED 9 +#define WC_FUNCDEF 10 +#define WC_FOR 11 +#define WC_SELECT 12 +#define WC_WHILE 13 +#define WC_REPEAT 14 +#define WC_CASE 15 +#define WC_IF 16 +#define WC_COND 17 +#define WC_ARITH 18 +#define WC_AUTOFN 19 + +#define 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_SKIP(C) (wc_data(C) >> 6) +#define WCB_LIST(T,O) wc_bld(WC_LIST, ((T) | ((O) << 6))) + +#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_SKIP(C) (wc_data(C) >> 5) +#define WCB_SUBLIST(T,F,O) wc_bld(WC_SUBLIST, ((T) | (F) | ((O) << 5))) + +#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) wc_data(C) +#define WCB_REDIR(T) wc_bld(WC_REDIR, (T)) + +#define WC_ASSIGN_TYPE(C) (wc_data(C) & ((wordcode) 1)) +#define WC_ASSIGN_SCALAR 0 +#define WC_ASSIGN_ARRAY 1 +#define WC_ASSIGN_NUM(C) (wc_data(C) >> 1) +#define WCB_ASSIGN(T,N) wc_bld(WC_ASSIGN, ((T) | ((N) << 1))) + +#define WC_SIMPLE_ARGC(C) wc_data(C) +#define WCB_SIMPLE(N) wc_bld(WC_SIMPLE, (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_CASE_TYPE(C) (wc_data(C) & 3) +#define WC_CASE_HEAD 0 +#define WC_CASE_OR 1 +#define WC_CASE_AND 2 +#define WC_CASE_SKIP(C) (wc_data(C) >> 2) +#define WCB_CASE(T,O) wc_bld(WC_CASE, ((T) | ((O) << 2))) + +#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) /********************************************/ /* Defintions for job table and job control */ @@ -583,9 +688,13 @@ struct job { #define STAT_INUSE (1<<6) /* this job entry is in use */ #define STAT_SUPERJOB (1<<7) /* job has a subjob */ #define STAT_SUBJOB (1<<8) /* job is a subjob */ -#define STAT_CURSH (1<<9) /* last command is in current shell */ -#define STAT_NOSTTY (1<<10) /* the tty settings are not inherited */ +#define STAT_WASSUPER (1<<9) /* was a super-job, sub-job needs to be */ + /* deleted */ +#define STAT_CURSH (1<<10) /* last command is in current shell */ +#define STAT_NOSTTY (1<<11) /* the tty settings are not inherited */ /* from this job when it exits. */ +#define STAT_ATTACH (1<<12) /* delay reattaching shell to tty */ +#define STAT_SUBLEADER (1<<13) /* is super-job, but leader is sub-shell */ #define SP_RUNNING -1 /* fake status for jobs currently running */ @@ -631,7 +740,9 @@ struct execstack { struct heredocs { struct heredocs *next; - Redir rd; + int type; + int pc; + char *str; }; struct dirsav { @@ -641,6 +752,8 @@ struct dirsav { ino_t ino; }; +#define MAX_PIPESTATS 256 + /*******************************/ /* Definitions for Hash Tables */ /*******************************/ @@ -654,10 +767,12 @@ typedef void (*AddNodeFunc) _((HashTable, char *, void *)); typedef HashNode (*GetNodeFunc) _((HashTable, char *)); typedef HashNode (*RemoveNodeFunc) _((HashTable, 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)); @@ -673,6 +788,7 @@ struct hashtable { 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 */ @@ -682,6 +798,7 @@ struct hashtable { 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 */ @@ -701,6 +818,15 @@ struct hashnode { * reserved words. */ #define DISABLED (1<<0) +/* node in shell option table */ + +struct optname { + HashNode next; /* next in hash chain */ + char *nam; /* hash data */ + int flags; + int optno; /* option number */ +}; + /* node in shell reserved word hash table (reswdtab) */ struct reswd { @@ -746,9 +872,42 @@ struct shfunc { HashNode next; /* next in hash chain */ char *nam; /* name of shell function */ int flags; /* various flags */ - List funcdef; /* function definition */ + Eprog funcdef; /* function definition */ }; +/* 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 */ + +/* node in function stack */ + +struct funcstack { + Funcstack prev; /* previous in stack */ + char *name; /* name of function called */ +}; + +/* 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 } + /* node in builtin command hash table (builtintab) */ typedef int (*HandlerFunc) _((char *, char **, char *, int)); @@ -794,11 +953,87 @@ struct builtin { struct module { char *nam; int flags; - void *handle; + union { + void *handle; + Linkedmod linked; + } u; LinkList deps; + int wrapper; }; #define MOD_BUSY (1<<0) +#define MOD_UNLOAD (1<<1) +#define MOD_SETUP (1<<2) +#define MOD_LINKED (1<<3) +#define MOD_INIT_S (1<<4) +#define MOD_INIT_B (1<<5) + +typedef int (*Module_func) _((Module)); + +struct linkedmod { + char *name; + Module_func setup; + Module_func boot; + Module_func cleanup; + Module_func finish; +}; + +/* 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 */ + int globflags; /* globbing flags to set at start */ + int globend; /* globbing flags set after finish */ + int flags; /* PAT_* flags */ + int patmlen; /* length of pure string or longest match */ + int patnpar; /* number of active parentheses */ + char patstartch; +}; + +/* Flags used in pattern matchers (Patprog) and passed down to patcompile */ + +#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 */ + +/* Globbing flags: lower 8 bits gives approx count */ +#define GF_LCMATCHUC 0x0100 +#define GF_IGNCASE 0x0200 +#define GF_BACKREF 0x0400 +#define GF_MATCHREF 0x0800 + +/* 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) /* node used in parameter hash table (paramtab) */ @@ -812,21 +1047,28 @@ struct param { void *data; /* used by special parameter functions */ char **arr; /* value if declared array (PM_ARRAY) */ char *str; /* value if declared string (PM_SCALAR) */ - long val; /* value if declared integer (PM_INTEGER) */ + zlong val; /* value if declared integer (PM_INTEGER) */ + double dval; /* value if declared float + (PM_EFLOAT|PM_FFLOAT) */ + HashTable hash; /* value if declared assoc (PM_HASHED) */ } u; /* pointer to function to set value of this parameter */ union { void (*cfn) _((Param, char *)); - void (*ifn) _((Param, long)); + void (*ifn) _((Param, zlong)); + void (*ffn) _((Param, double)); void (*afn) _((Param, char **)); + void (*hfn) _((Param, HashTable)); } sets; /* pointer to function to get value of this parameter */ union { char *(*cfn) _((Param)); - long (*ifn) _((Param)); + zlong (*ifn) _((Param)); + double (*ffn) _((Param)); char **(*afn) _((Param)); + HashTable (*hfn) _((Param)); } gets; /* pointer to function to unset this parameter */ @@ -842,30 +1084,108 @@ struct param { /* flags for parameters */ /* parameter types */ -#define PM_SCALAR 0 /* scalar */ -#define PM_ARRAY (1<<0) /* array */ -#define PM_INTEGER (1<<1) /* integer */ +#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_ARRAY)) +#define PM_TYPE(X) \ + (X & (PM_SCALAR|PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_ARRAY|PM_HASHED)) -#define PM_LEFT (1<<2) /* left justify and remove leading blanks */ -#define PM_RIGHT_B (1<<3) /* right justify and fill with leading blanks */ -#define PM_RIGHT_Z (1<<4) /* right justify and fill with leading zeros */ -#define PM_LOWER (1<<5) /* all lower case */ +#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<<6) /* all upper case */ -#define PM_UNDEFINED (1<<6) /* undefined (autoloaded) shell function */ - -#define PM_READONLY (1<<7) /* readonly */ -#define PM_TAGGED (1<<8) /* tagged */ -#define PM_EXPORTED (1<<9) /* exported */ -#define PM_UNIQUE (1<<10) /* remove duplicates */ -#define PM_SPECIAL (1<<11) /* special builtin parameter */ -#define PM_DONTIMPORT (1<<12) /* do not import this variable */ -#define PM_RESTRICTED (1<<13) /* cannot be changed in restricted mode */ -#define PM_UNSET (1<<14) /* has null value */ +#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 */ + +/* 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_TIED (1<<15) /* array tied to colon-path or v.v. */ + +/* Remaining flags do not correspond directly to command line arguments */ +#define PM_LOCAL (1<<16) /* this parameter will be made local */ +#define PM_SPECIAL (1<<17) /* special builtin parameter */ +#define PM_DONTIMPORT (1<<18) /* do not import this variable */ +#define PM_RESTRICTED (1<<19) /* cannot be changed in restricted mode */ +#define PM_UNSET (1<<20) /* has null value */ +#define PM_REMOVABLE (1<<21) /* special can be removed from paramtab */ +#define PM_AUTOLOAD (1<<22) /* autoloaded from module */ +#define PM_NORESTORE (1<<23) /* do not restore value of local special */ + +/* The option string corresponds to the first of the variables above */ +#define TYPESET_OPTSTR "aiEFALRZlurtxUhT" + +/* 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) +#define SCANPM_WANTKEYS (1<<1) +#define SCANPM_WANTINDEX (1<<2) +#define SCANPM_MATCHKEY (1<<3) +#define SCANPM_MATCHVAL (1<<4) +#define SCANPM_MATCHMANY (1<<5) +#define SCANPM_ASSIGNING (1<<6) +#define SCANPM_KEYMATCH (1<<7) +#define SCANPM_ISVAR_AT ((-1)<<15) /* Only sign bit is significant */ + +/* + * 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 begining, % 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 */ + +/* Flags as the second argument to prefork */ +#define PF_TYPESET 0x01 /* argument handled like typeset foo=bar */ +#define PF_ASSIGN 0x02 /* argument handled like the RHS of foo=bar */ +#define PF_SINGLE 0x04 /* single word substitution */ + +struct paramdef { + char *name; + int flags; + void *var; + void *set; + void *get; + void *unset; +}; + +#define PARAMDEF(name, flags, var, set, get, unset) \ + { name, flags, (void *) var, (void *) set, (void *) get, (void *) unset } +#define INTPARAMDEF(name, var) \ + { name, PM_INTEGER, (void *) var, (void *) intvarsetfn, \ + (void *) intvargetfn, (void *) stdunsetfn } +#define STRPARAMDEF(name, var) \ + { name, PM_SCALAR, (void *) var, (void *) strvarsetfn, \ + (void *) strvargetfn, (void *) stdunsetfn } +#define ARRPARAMDEF(name, var) \ + { name, PM_ARRAY, (void *) var, (void *) arrvarsetfn, \ + (void *) arrvargetfn, (void *) stdunsetfn } /* node for named directory hash table (nameddirtab) */ @@ -880,19 +1200,21 @@ struct nameddir { /* 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) */ /* 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) /* flags for printing for the whence builtin */ -#define PRINT_WHENCE_CSH (1<<3) -#define PRINT_WHENCE_VERBOSE (1<<4) -#define PRINT_WHENCE_SIMPLE (1<<5) -#define PRINT_WHENCE_FUNCDEF (1<<6) -#define PRINT_WHENCE_WORD (1<<7) +#define PRINT_WHENCE_CSH (1<<4) +#define PRINT_WHENCE_VERBOSE (1<<5) +#define PRINT_WHENCE_SIMPLE (1<<6) +#define PRINT_WHENCE_FUNCDEF (1<<7) +#define PRINT_WHENCE_WORD (1<<8) /***********************************/ /* Definitions for history control */ @@ -901,18 +1223,30 @@ struct nameddir { /* history entry */ struct histent { + HashNode hash_next; /* next in hash chain */ char *text; /* the history line itself */ + int flags; /* Misc flags */ + + Histent up; /* previous line (moving upward) */ + Histent down; /* next line (moving downward) */ char *zle_text; /* the edited history line */ 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 */ - int flags; /* Misc flags */ + int histnum; /* A sequential history number */ }; -#define HIST_OLD 0x00000001 /* Command is already written to disk*/ -#define HIST_READ 0x00000002 /* Command was read back from disk*/ +#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 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 */ @@ -923,7 +1257,14 @@ struct histent { #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_USE_OPTIONS 0x8000 /******************************************/ /* Definitions for programable completion */ @@ -972,12 +1313,15 @@ enum { BADPATTERN, BANGHIST, BAREGLOBQUAL, + BASHAUTOLIST, BEEP, BGNICE, BRACECCL, BSDECHO, CDABLEVARS, + CHASEDOTS, CHASELINKS, + CHECKJOBS, CLOBBER, COMPLETEALIASES, COMPLETEINWORD, @@ -986,6 +1330,7 @@ enum { CSHJUNKIEHISTORY, CSHJUNKIELOOPS, CSHJUNKIEQUOTES, + CSHNULLCMD, CSHNULLGLOB, EQUALS, ERREXIT, @@ -994,6 +1339,7 @@ enum { EXTENDEDHISTORY, FLOWCONTROL, FUNCTIONARGZERO, + GLOBALRCS, GLOBOPT, GLOBASSIGN, GLOBCOMPLETE, @@ -1004,15 +1350,20 @@ enum { HASHLISTALL, HISTALLOWCLOBBER, HISTBEEP, + HISTEXPIREDUPSFIRST, + HISTFINDNODUPS, + HISTIGNOREALLDUPS, HISTIGNOREDUPS, HISTIGNORESPACE, HISTNOFUNCTIONS, HISTNOSTORE, HISTREDUCEBLANKS, + HISTSAVENODUPS, HISTVERIFY, HUP, IGNOREBRACES, IGNOREEOF, + INCAPPENDHISTORY, INTERACTIVE, INTERACTIVECOMMENTS, KSHARRAYS, @@ -1021,8 +1372,11 @@ enum { KSHOPTIONPRINT, LISTAMBIGUOUS, LISTBEEP, + LISTPACKED, + LISTROWSFIRST, LISTTYPES, LOCALOPTIONS, + LOCALTRAPS, LOGINSHELL, LONGLISTJOBS, MAGICEQUALSUBST, @@ -1056,9 +1410,11 @@ enum { RESTRICTED, RMSTARSILENT, RMSTARWAIT, + SHAREHISTORY, SHFILEEXPANSION, SHGLOB, SHINSTDIN, + SHNULLCMD, SHOPTIONLETTERS, SHORTLOOPS, SHWORDSPLIT, @@ -1154,7 +1510,8 @@ struct ttyinfo { #define TCALLATTRSOFF 21 #define TCSTANDOUTEND 22 #define TCUNDERLINEEND 23 -#define TC_COUNT 24 +#define TCHORIZPOS 24 +#define TC_COUNT 25 #define tccan(X) (tclen[X]) @@ -1178,14 +1535,20 @@ struct ttyinfo { /* Definitions for the %_ prompt escape */ /****************************************/ -#define cmdpush(X) if (!(cmdsp >= 0 && cmdsp < 256)) {;} else cmdstack[cmdsp++]=(X) +#define CMDSTACKSZ 256 +#define cmdpush(X) do { \ + if (cmdsp >= 0 && cmdsp < CMDSTACKSZ) \ + cmdstack[cmdsp++]=(X); \ + } while (0) #ifdef DEBUG -# define cmdpop() if (cmdsp <= 0) { \ - fputs("BUG: cmdstack empty\n", stderr); \ - fflush(stderr); \ - } else cmdsp-- +# define cmdpop() do { \ + if (cmdsp <= 0) { \ + fputs("BUG: cmdstack empty\n", stderr); \ + fflush(stderr); \ + } else cmdsp--; \ + } while (0) #else -# define cmdpop() if (cmdsp <= 0) {;} else cmdsp-- +# define cmdpop() do { if (cmdsp > 0) cmdsp--; } while (0) #endif #define CS_FOR 0 @@ -1224,35 +1587,41 @@ struct ttyinfo { * Memory management * *********************/ -#ifndef DEBUG -# define HEAPALLOC do { int nonlocal_useheap = global_heapalloc(); do +/* heappush saves the current heap state using this structure */ -# define PERMALLOC do { int nonlocal_useheap = global_permalloc(); do +struct heapstack { + struct heapstack *next; /* next one in list for this heap */ + size_t used; +}; -# define LASTALLOC while (0); \ - if (nonlocal_useheap) global_heapalloc(); \ - else global_permalloc(); \ - } while(0) +/* A zsh heap. */ -# define LASTALLOC_RETURN \ - if ((nonlocal_useheap ? global_heapalloc() : \ - global_permalloc()), 0) {;} else return -#else -# define HEAPALLOC do { int nonlocal_useheap = global_heapalloc(); \ - alloc_stackp++; do +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 */ -# define PERMALLOC do { int nonlocal_useheap = global_permalloc(); \ - alloc_stackp++; do +/* 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 LASTALLOC while (0); alloc_stackp--; \ - if (nonlocal_useheap) global_heapalloc(); \ - else global_permalloc(); \ - } while(0) +# define LASTALLOC_RETURN return -# define LASTALLOC_RETURN \ - if ((nonlocal_useheap ? global_heapalloc() : \ - global_permalloc()),alloc_stackp--,0){;}else return -#endif +# define NEWHEAPS(h) do { Heap _switch_oldheaps = h = new_heaps(); do +# define OLDHEAPS while (0); old_heaps(_switch_oldheaps); } while (0); + +# define SWITCHHEAPS(h) do { Heap _switch_oldheaps = switch_heaps(h); do +# define SWITCHBACKHEAPS while (0); switch_heaps(_switch_oldheaps); } while (0); /****************/ /* Debug macros */ @@ -1260,12 +1629,8 @@ struct ttyinfo { #ifdef DEBUG # define DPUTS(X,Y) if (!(X)) {;} else dputs(Y) -# define MUSTUSEHEAP(X) if (useheap) {;} else \ - fprintf(stderr, "BUG: permanent allocation in %s\n", X), \ - fflush(stderr) #else # define DPUTS(X,Y) -# define MUSTUSEHEAP(X) #endif /**************************/ @@ -1274,9 +1639,20 @@ struct ttyinfo { /* These used in the sigtrapped[] array */ -#define ZSIG_TRAPPED (1<<0) -#define ZSIG_IGNORED (1<<1) -#define ZSIG_FUNC (1<<2) +#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_SHIFT 3 + +/**********************************/ +/* 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 */ /****************/ /* Entry points */ @@ -1291,3 +1667,17 @@ typedef int (*CompctlReadFn) _((char *, char **, char *, char *)); typedef void (*ZleVoidFn) _((void)); typedef void (*ZleVoidIntFn) _((int)); typedef unsigned char * (*ZleReadFn) _((char *, char *, int)); + +/***************************************/ +/* Pseudo-keyword to mark exportedness */ +/***************************************/ + +#define mod_export + +/***************************************/ +/* Hooks in core. */ +/***************************************/ + +#define EXITHOOK (zshhooks + 0) +#define BEFORETRAPHOOK (zshhooks + 1) +#define AFTERTRAPHOOK (zshhooks + 2) diff --git a/Test/07cond.ztst b/Test/07cond.ztst index 7fff51ce2..1d1ba6679 100644 --- a/Test/07cond.ztst +++ b/Test/07cond.ztst @@ -11,10 +11,11 @@ touch unmodified touch zerolength + chgrp $EGID zerolength print 'Garbuglio' >nonzerolength - touch modish - chmod g+s modish + mkdir modish + chmod g+xs modish chmod u+s modish chmod +t modish @@ -26,7 +27,11 @@ 0:-a cond # Find a block special file system. This is a little tricky. - block=$(df / | tail -1 | awk '{ print $1 }') && + block=$(df / | awk ' + $NF == "/" {print $1} + $1 == "/" && substr($2,0,1) == "(" { + if((l = index($2,")") - 2) < 0) l = length($2) - 1; + print substr($2,2,l)}') && [[ -b $block && ! -b zerolength ]] 0:-b cond @@ -61,7 +66,11 @@ [[ -o rcs && ! -o norcs && -o noerrexit && ! -o errexit ]] 0:-o cond - mknod pipe p + if whence mkfifo >/dev/null; then + mkfifo pipe + else + mknod pipe p + fi [[ -p pipe && ! -p zerolength ]] 0:-p cond @@ -76,7 +85,7 @@ [[ -u modish && ! -u zerolength ]] 0:-u cond - [[ -x $ZTST_testdir/ztst.zsh && ! -x zerolength ]] + [[ -x $ZTST_srcdir/ztst.zsh && ! -x zerolength ]] 0:-x cond [[ -z $bar && -z '' && ! -z $foo ]] @@ -89,8 +98,6 @@ [[ -O zerolength ]] 0:-O cond -# there may be strange cases where this doesn't work, e.g. -# inherited funny groups for directories via setgid. [[ -G zerolength ]] 0:-G cond @@ -132,4 +139,16 @@ 0:|| and && in conds [[ -e /dev/fd/0 ]] -0:/dev/fd support in conds +0:/dev/fd support in conds handled by access + + [[ -O /dev/fd/0 ]] +0:/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:substituion in `[' builtin diff --git a/Test/11glob.ztst b/Test/11glob.ztst index 17c33eb63..f5819595b 100644 --- a/Test/11glob.ztst +++ b/Test/11glob.ztst @@ -1,8 +1,22 @@ # Tests for globbing %prep + mkdir glob.tmp + mkdir glob.tmp/dir1 + mkdir glob.tmp/dir2 + : >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 @@ -234,3 +248,8 @@ >0: [[ FOO = @(bar|(#i)foo) ]] >0: [[ Modules = (#i)*m* ]] >0 tests failed. + + ( 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 diff --git a/Test/53completion.ztst b/Test/53completion.ztst index 00f1c9218..2e33412d8 100644 --- a/Test/53completion.ztst +++ b/Test/53completion.ztst @@ -1,7 +1,10 @@ # Tests for completion system. %prep + zmodload -i zsh/zpty + TERM=vt100 + export ZTST_testdir ZTST_srcdir TERM comptest () { $ZTST_testdir/../Src/zsh -f $ZTST_srcdir/comptest -z $ZTST_testdir/../Src/zsh -d $ZTST_testdir/compdump.tmp "$@" } mkdir comp.tmp @@ -104,11 +107,11 @@ >DESCRIPTION:{desc1} >NO:{arg1} -# code='compdef _tst tst; _tst () { _arguments "-\+[opt]" }' -# comptest -c "$code" $'tst -\C-D' -#0:_arguments -#>DESCRIPTION:{option} -#>NO:{-+ -- opt} + code='compdef _tst tst; _tst () { _arguments "-\+[opt]" }' + comptest -c "$code" $'tst -\C-D' +0:_arguments +>DESCRIPTION:{option} +>NO:{-+ -- opt} code='compdef _tst tst; _tst () { _arguments "1:desc1:(arg1)" }' comptest -c "$code" $'tst \t' |