summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Functions/Misc/is-at-least2
-rw-r--r--Functions/Misc/zrecompile13
-rw-r--r--Functions/Zftp/zfcd_match12
-rw-r--r--Functions/Zftp/zfget_match31
-rw-r--r--Functions/Zle/incremental-complete-word187
-rw-r--r--Functions/Zle/predict-on119
-rw-r--r--Misc/make-zsh-urls2
-rw-r--r--Src/Modules/parameter.c1699
-rw-r--r--Src/Modules/zpty.c153
-rw-r--r--Src/Zle/comp.h449
-rw-r--r--Src/Zle/compcore.c1500
-rw-r--r--Src/Zle/complete.c436
-rw-r--r--Src/Zle/compmatch.c124
-rw-r--r--Src/Zle/compresult.c515
-rw-r--r--Src/Zle/computil.c1498
-rw-r--r--Src/Zle/iwidgets.list43
-rw-r--r--Src/Zle/zle_utils.c92
-rw-r--r--Src/builtin.c1512
-rw-r--r--Src/cond.c326
-rw-r--r--Src/exec.c1882
-rw-r--r--Src/hashtable.c506
-rw-r--r--Src/jobs.c347
-rw-r--r--Src/loop.c380
-rw-r--r--Src/params.c1818
-rw-r--r--Src/parse.c2753
-rw-r--r--Src/pattern.c1116
-rw-r--r--Src/text.c858
-rw-r--r--Src/zsh.h964
-rw-r--r--Test/07cond.ztst35
-rw-r--r--Test/11glob.ztst19
-rw-r--r--Test/53completion.ztst13
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'