1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-06-18 21:38:03 +02:00

zsh-3.1.5-pws-2

This commit is contained in:
Tanaka Akira 1999-04-15 18:07:38 +00:00
parent a61dc2074a
commit f13624e0f8
27 changed files with 852 additions and 375 deletions

View file

@ -139,9 +139,13 @@ install.info: zsh.info
elif test -f $(sdir)/$$file; then \
$(INSTALL_DATA) $(sdir)/$$file $(infodir); \
else :; \
fi || exit 1; \
fi || exit 1; \
done
install.html: zsh_toc.html
$(sdir_top)/mkinstalldirs $(htmldir)
$(INSTALL_DATA) *.html $(htmldir)
# uninstall man pages
uninstall.man:
for file in $(MAN); do \

View file

@ -98,12 +98,13 @@ Change the current directory. In the first form, change the
current directory to var(arg), or to the value of tt($HOME) if
var(arg) is not specified. If var(arg) is `tt(-)', change to the
value of tt($OLDPWD), the previous directory.
If a directory named var(arg) is not found in the current directory
and var(arg) does not begin with a slash,
search each component of the shell parameter tt(cdpath).
If the option tt(CDABLE_VARS) is set, and a parameter named var(arg)
exists whose value begins with a slash, treat its value as
the directory.
Otherwise, if a directory named var(arg) is not found in the current
directory and var(arg) does not begin with a slash, search each
component of the shell parameter tt(cdpath). If no directory is found
and the option tt(CDABLE_VARS) is set, and a parameter named var(arg)
exists whose value begins with a slash, treat its value as the
directory. In that case, the parameter is added to the named
directory hash table.
The second form of tt(cd) substitutes the string var(new)
for the string var(old) in the name of the current directory,
@ -357,11 +358,20 @@ is stored in tt(OPTARG).
vindex(OPTIND, use of)
vindex(OPTARG, use of)
The first option to be examined may be changed by explicitly assigning
to tt(OPTIND). tt(OPTIND) has an initial value of tt(1), and is
normally reset to tt(1) upon exit from a shell function. tt(OPTARG)
is not reset and retains its value from the most recent call to
tt(getopts). If either of tt(OPTIND) or tt(OPTARG) is explicitly
unset, it remains unset, and the index or option argument is not
stored. The option itself is still stored in var(name) in this case.
A leading `tt(:)' in var(optstring) causes tt(getopts) to store the
letter of the invalid option in tt(OPTARG), and to set var(name)
to `tt(?)' for an unknown option and to `tt(:)' when a required option
is missing. Otherwise, tt(getopts) prints an error
message. The exit status is nonzero when there are no more options.
letter of any invalid option in tt(OPTARG), and to set var(name) to
`tt(?)' for an unknown option and to `tt(:)' when a required option is
missing. Otherwise, tt(getopts) sets var(name) to `tt(?)' and prints
an error message when an option is invalid. The exit status is
nonzero when there are no more options.
)
findex(hash)
item(tt(hash) [ tt(-dfmrv) ] [ var(name)[tt(=)var(value)] ] ...)(
@ -521,8 +531,8 @@ Same as tt(exit), except that it only works in a login shell.
prefix(noglob)
findex(popd)
item(tt(popd) [ {tt(PLUS())|tt(-)}var(n) ])(
Removes a entry from the directory stack, and perform a tt(cd) to
the new top directory. With no argument, the current top entry is
Remove an entry from the directory stack, and perform a tt(cd) to
the new top directory. With no argument, the current top entry is
removed. An argument of the form `tt(PLUS())var(n)' identifies a stack
entry by counting from the left of the list shown by the tt(dirs) command,
starting with zero. An argument of the form tt(-n) counts from the right.
@ -615,21 +625,9 @@ If var(arg) is not specified, change to the second directory
on the stack (that is, exchange the top two entries), or
change to tt($HOME) if the tt(PUSHD_TO_HOME)
option is set or if there is only one entry on the stack.
If var(arg) is `tt(-)', change to tt($OLDPWD), the previous directory.
If a directory named var(arg) is not found in the current directory
and var(arg) does not contain a slash,
search each component of the shell parameter tt(cdpath).
If the option tt(CDABLE_VARS) is set, and a parameter named var(arg)
exists whose value begins with a slash, treat its value as
the directory.
If the option tt(PUSHD_SILENT) is not set, the directory
stack will be printed after a tt(pushd) is performed.
The second form of tt(pushd) substitutes the string var(new)
for the string var(old) in the name of the current directory,
and tries to change to this new directory.
Otherwise, var(arg) is interpreted as it would be by tt(cd).
The meaning of var(old) and var(new) in the second form is also
the same as for tt(cd).
The third form of tt(pushd) changes directory by rotating the
directory list. An argument of the form `tt(PLUS())var(n)' identifies a stack
@ -637,6 +635,9 @@ entry by counting from the left of the list shown by the tt(dirs)
command, starting with zero. An argument of the form `tt(-)var(n)' counts
from the right. If the tt(PUSHD_MINUS) option is set, the meanings
of `tt(PLUS())' and `tt(-)' in this context are swapped.
If the option tt(PUSHD_SILENT) is not set, the directory
stack will be printed after a tt(pushd) is performed.
)
findex(pushln)
item(tt(pushln) [ var(arg) ... ])(
@ -727,7 +728,8 @@ is interactive.
The value (exit status) of tt(read) is 1 when an end-of-file is
encountered, or when tt(-c) or tt(-l) is present and the command is
not called from a tt(compctl) function. Otherwise the value is 0.
not called from a tt(compctl) function, or as described for tt(-q).
Otherwise the value is 0.
The behavior of some combinations of the tt(-k), tt(-p), tt(-q), tt(-u)
and tt(-z) flags is undefined. Presently tt(-q) cancels all the others,
@ -835,7 +837,7 @@ var(arg) is a command to be read and executed when the shell
receives var(sig). Each var(sig) can be given as a number
or as the name of a signal.
If var(arg) is `tt(-)', then all traps var(sig) are reset to their
default values. If var(arg) is the null string, then this signal
default values. If var(arg) is the empty string, then this signal
is ignored by the shell and by the commands it invokes.
If var(sig) is tt(ZERR) then var(arg) will be executed
@ -958,7 +960,7 @@ or functions (with the tt(-f) flag) with matching names are printed.
findex(ulimit)
cindex(resource limits)
cindex(limits, resource)
item(tt(ulimit) [ tt(-SHacdflmnpstv) [ tt(limit) ] ... ])(
item(tt(ulimit) [ tt(-SHacdflmnpstv) [ var(limit) ] ... ])(
Set or display resource limits of the shell and the processes started by
the shell. The value of var(limit) can be a number in the unit specified
below or the value `tt(unlimited)'. If the tt(-H) flag is given use

View file

@ -573,11 +573,11 @@ enditem()
subsect(Example)
The flag tt(f) is useful to split a double-quoted substitution line by
line. For example, `tt("${(f)$LPAR()<)var(file)tt(RPAR()}")'
will substitue the contents of var(file) divided so that one line is
supplied per argument to var(cmd). Compare this with the effect of
substitutes the contents of var(file) divided so that each line is
an element of the resulting array. Compare this with the effect of
`tt($)tt(LPAR()<)var(file)tt(RPAR())' alone, which divides the file
up by words, or the same inside double quotes, where the entire
contents of the file are passed as a single argument.
up by words, or the same inside double quotes, which makes the entire
content of the file a single string.
texinode(Command Substitution)(Arithmetic Expansion)(Parameter Expansion)(Expansion)
sect(Command Substitution)
cindex(command substitution)

View file

@ -614,7 +614,7 @@ Thus if `tt(/usr/local/bin)' is in the user's path, and he types
Commands explicitly beginning with `tt(/)', `tt(./)' or `tt(../)'
are not subject to the path search.
This also applies to the tt(.) builtin,
and searches for modules performed by the tt(zmodload) builtin.
and to searches for modules performed by the tt(zmodload) builtin.
)
pindex(POSIX_BUILTINS)
item(tt(POSIX_BUILTINS))(

View file

@ -175,6 +175,8 @@ sitem(tt(w))(True if the day of the week is equal to var(n) (Sunday = 0).)
sitem(tt(?))(True if the exit status of the last command was var(n).)
sitem(tt(#))(True if the effective uid of the current process is var(n).)
sitem(tt(g))(True if the effective gid of the current process is var(n).)
sitem(tt(l))(True if at least var(n) characters have already been
printed on the current line.)
sitem(tt(L))(True if the tt(SHLVL) parameter is at least var(n).)
sitem(tt(S))(True if the tt(SECONDS) parameter is at least var(n).)
sitem(tt(v))(True if the array tt(psvar) has at least var(n) elements.)
@ -185,25 +187,40 @@ endsitem()
xitem(tt(%<)var(string)tt(<))
xitem(tt(%>)var(string)tt(>))
item(tt(%[)var(xstring)tt(]))(
Specifies truncation behaviour.
Specifies truncation behaviour for the remainder of the prompt string.
The third, deprecated, form is equivalent to `tt(%)var(xstringx)',
i.e. var(x) may be `tt(<)' or `tt(>)'.
The numeric argument, which in the third form may appear immediately
after the `tt([)', specifies the maximum permitted length of
the various strings that can be displayed in the prompt. If this
integer is zero, or missing, truncation is disabled. Truncation is
initially disabled.
the various strings that can be displayed in the prompt.
The var(string) will be displayed in
place of the truncated portion of any string.
place of the truncated portion of any string; note this does not
undergo prompt expansion.
The forms with `tt(<)' truncate at the left of the string,
and the forms with `tt(>)' truncate at the right of the string.
For example, if the current directory is `tt(/home/pike)',
the prompt `tt(%8<..<%/)' will expand to `tt(..e/pike)'.
In this string, the terminating character (`tt(<)', `tt(>)' or `tt(])'),
or in fact any character, may be quoted by a preceding `tt(\)'.
% escapes are em(not) recognised.
or in fact any character, may be quoted by a preceding `tt(\)'; note
when using tt(print -P), however, that this must be doubled as the
string is also subject to standard tt(print) processing, in addition
to any backslashes removed by a double quoted string: the worst case
is therefore `tt(print -P "%<\\\\<<...")'.
If the var(string) is longer than the specified truncation length,
it will appear in full, completely replacing the truncated string.
The part of the prompt string to be truncated runs to the end of the
string, or to the end of the next enclosing group of the `tt(%LPAR())'
construct, or to the next truncation encountered at the same grouping
level (i.e. truncations inside a `tt(%LPAR())' are separate), which
ever comes first. In particular, a truncation with argument zero
(e.g. `tt(%<<)') marks the end of the range of the string to be
truncated while turning off truncation from there on. For example, the
prompt '%10<...<%~%<<%# ' will print a truncated representation of the
current directory, followed by a `tt(%)' or `tt(#)', followed by a
space. Without the `tt(%<<)', those two characters would be included
in the string to be truncated.
)
enditem()

View file

@ -5,8 +5,6 @@
# Usage: e.g.
# compctl -D -f + -U -Q -S '' -K multicomp
#
# Note that exactly matched directories are not expanded, e.g.
# s/zsh-2.4/s<TAB> will not expand to src/zsh-2.4old/src.
# Will expand glob patterns already in the word, but use complete-word,
# not TAB (expand-or-complete), or you will get ordinary glob expansion.
# Requires the -U option to compctl.
@ -42,32 +40,32 @@ while [[ -n "$pref" ]]; do
[[ "$pref" = /* ]] && sofar=(${sofar}/) && pref="${pref#/}"
head="${pref%%/*}"
pref="${pref#$head}"
if [[ -n "$pref" && -z $sofar[2] && -d "${sofar}$head" ]]; then
# Exactly matched directory: don't try to glob
reply=("${sofar}$head")
[[ -z "$pref" ]] && globdir=
# if path segment contains wildcards, don't add another.
if [[ "$head" = *[\[\(\*\?\$\~]* ]]; then
wild=$head
else
[[ -z "$pref" ]] && globdir=
# if path segment contains wildcards, don't add another.
if [[ "$head" = *[\[\(\*\?\$\~]* || -z "$head" ]]; then
wild=$head
else
# Simulate case-insensitive globbing for ASCII characters
wild="[${(j(][))${(s())head:l}}]*" # :gs/a/[a]/ etc.
# The following could all be one expansion, but for readability:
wild=$wild:gs/a/aA/:gs/b/bB/:gs/c/cC/:gs/d/dD/:gs/e/eE/:gs/f/fF/
wild=$wild:gs/g/gG/:gs/h/hH/:gs/i/iI/:gs/j/jJ/:gs/k/kK/:gs/l/lL/
wild=$wild:gs/m/mM/:gs/n/nN/:gs/o/oO/:gs/p/pP/:gs/q/qQ/:gs/r/rR/
wild=$wild:gs/s/sS/:gs/t/tT/:gs/u/uU/:gs/v/vV/:gs/w/wW/:gs/x/xX/
wild=$wild:gs/y/yY/:gs/z/zZ/:gs/-/_/:gs/_/-_/:gs/[]//
# Simulate case-insensitive globbing for ASCII characters
wild="[${(j(][))${(s())head:l}}]*" # :gs/a/[a]/ etc.
# The following could all be one expansion, but for readability:
wild=$wild:gs/a/aA/:gs/b/bB/:gs/c/cC/:gs/d/dD/:gs/e/eE/:gs/f/fF/
wild=$wild:gs/g/gG/:gs/h/hH/:gs/i/iI/:gs/j/jJ/:gs/k/kK/:gs/l/lL/
wild=$wild:gs/m/mM/:gs/n/nN/:gs/o/oO/:gs/p/pP/:gs/q/qQ/:gs/r/rR/
wild=$wild:gs/s/sS/:gs/t/tT/:gs/u/uU/:gs/v/vV/:gs/w/wW/:gs/x/xX/
wild=$wild:gs/y/yY/:gs/z/zZ/:gs/-/_/:gs/_/-_/:gs/[]//
# Expand on both sides of '.' (except when leading) as for '/'
wild="${${wild:gs/[.]/*.*/}#\*}"
fi
reply=(${sofar}"${wild}${globdir}")
reply=(${~reply})
# Expand on both sides of '.' (except when leading) as for '/'
wild="${${wild:gs/[.]/*.*/}#\*}"
fi
reply=(${sofar}"${wild}${globdir}")
reply=(${~reply})
[[ -z $reply[1] ]] && reply=() && break
[[ -n $pref ]] && sofar=($reply)
done
@ -77,4 +75,3 @@ done
[[ -n "$origtop" ]] && reply=("$origtop"${reply#$newtop})
# }

View file

@ -1,3 +1,3 @@
# get a random line from a file
integer z=$(wc -l <$1)
integer z="$(wc -l <$1)"
sed -n $[RANDOM%z+1]p $1

View file

@ -51,6 +51,10 @@ META-FAQ: FORCE
# ========== DEPENDENCIES FOR INSTALLING ==========
# install stripped
install-strip:
$(MAKE) install STRIPFLAGS="-s"
# install/uninstall everything
install: install.bin install.modules install.man install.info
uninstall: uninstall.bin uninstall.modules uninstall.man uninstall.info
@ -71,6 +75,10 @@ install.man uninstall.man:
install.info uninstall.info:
@cd Doc && $(MAKE) $(MAKEDEFS) $@
# install/uninstall just the html pages
install.html uninstall.html:
@cd Doc && $(MAKE) $(MAKEDEFS) $@
# ========== DEPENDENCIES FOR CLEANUP ==========
@@clean.mk@@

View file

@ -3,6 +3,7 @@
# c2z - environment conversion tool
# Contributed by Bart Schaefer
# (Tweaked a bit by Paul Falstad)
# (Tweaked again by Bart Schaefer)
#
# This is a quick script to convert csh aliases to zsh aliases/functions.
# It also converts the csh environment and local variables to zsh. c2z
@ -81,7 +82,13 @@ grep -v ! /tmp/cz$$.1 >/tmp/cz$$.3
sed -e "s/'/'"\\\\"''"/g \
-e 's/^\([^'"$T"']*\)'"$T"'\(.*\)$/alias -- \1='"'\2'/" \
/tmp/cz$$.3
sed -e 's/![:#]*/$/g' \
sed -e 's/>\(&*\)!/>\1|/g' \
-e 's/!\*:q/"$@"/g' \
-e 's/\(![:#]*[^'"$T"']*\):q/"\1"/g' \
-e 's/!\([-0-9][0-9]*\)\(:x\)*/$\2(fc -nl \1)/g' \
-e 's/\$:x(fc/$=(fc/g' \
-e 's/![:#]*\([^'"$T"']\)/$==\1/g' \
-e 's/\$=\(=[^'"$T"']*\):x/$\1/g' \
-e 's/\$cwd/$PWD/' \
-e 's/^\([^'"$T"']*\)'"$T"'\(.*\)$/\1 () { \2 }/' \
/tmp/cz$$.2
@ -92,6 +99,7 @@ exec < /tmp/cz$$.e
# Would be nice to deal with embedded newlines, e.g. in TERMCAP, but ...
sed -e '/^SHLVL/d' \
-e '/^PWD/d' \
-e '/^_=/d' \
-e "s/'/'"\\\\"''"/g \
-e "s/^\([A-Za-z0-9_]*=\)/export \1'/" \
-e "s/$/'/"

View file

@ -149,7 +149,7 @@ uninstall.bin: uninstall.bin-here
# install binary, creating install directory if necessary
install.bin-here: zsh install.bin-@L@
$(sdir_top)/mkinstalldirs $(bindir)
$(INSTALL_PROGRAM) zsh $(bindir)/zsh-$(VERSION)
$(INSTALL_PROGRAM) $(STRIPFLAGS) zsh $(bindir)/zsh-$(VERSION)
if test -f $(bindir)/zsh; then \
rm -f $(bindir)/zsh.old; \
ln $(bindir)/zsh $(bindir)/zsh.old; \

View file

@ -119,8 +119,9 @@ uninstall.modules: uninstall.modules-here
install.bin-here uninstall.bin-here:
install.modules-here:
$(sdir_top)/mkinstalldirs $(MODDIR)
modules='$(MODULES)'; for mod in $$modules; do \
modules='$(MODULES)'; \
if test -n "$$modules"; then $(sdir_top)/mkinstalldirs $(MODDIR); fi; \
for mod in $$modules; do \
$(INSTALL_PROGRAM) $$mod $(MODDIR)/$$mod; \
done

View file

@ -2945,7 +2945,7 @@ docompletion(char *s, int lst, int incmd)
ainfo = fainfo = NULL;
/* Make sure we have the completion list and compctl. */
if (makecomplist(s, incmd)) {
if (makecomplist(s, incmd, lst)) {
/* Error condition: feeeeeeeeeeeeep(). */
feep();
goto compend;
@ -3029,7 +3029,7 @@ static unsigned long ccont;
/**/
static int
makecomplist(char *s, int incmd)
makecomplist(char *s, int incmd, int lst)
{
struct cmlist ms;
Cmlist m = cmatcher;
@ -3062,7 +3062,7 @@ makecomplist(char *s, int incmd)
ccused = newlinklist();
ccstack = newlinklist();
makecomplistglobal(s, incmd);
makecomplistglobal(s, incmd, lst);
endcmgroup(NULL);
@ -3098,12 +3098,14 @@ makecomplist(char *s, int incmd)
/**/
static void
makecomplistglobal(char *os, int incmd)
makecomplistglobal(char *os, int incmd, int lst)
{
Compctl cc;
char *s;
if (inwhat == IN_ENV)
if (lst == COMP_WIDGET) {
cc = compwidget->u.cc;
} else if (inwhat == IN_ENV)
/* Default completion for parameter values. */
cc = &cc_default;
else if (inwhat == IN_MATH) {
@ -4413,7 +4415,8 @@ get_user_var(char *nam)
} else {
/* Otherwise it should be a parameter name. */
char **arr = NULL, *val;
if (!(arr = getaparam(nam)) && (val = getsparam(nam))) {
if (!(arr = getaparam(nam)) && !(arr = gethparam(nam)) &&
(val = getsparam(nam))) {
arr = (char **)ncalloc(2*sizeof(char *));
arr[0] = val;
arr[1] = NULL;
@ -5026,6 +5029,7 @@ do_single(Cmatch m)
else {
p = (char *) ncalloc(strlen(ppre) + strlen(str) +
strlen(psuf) + 1);
sprintf(p, "%s%s%s", ppre, str, psuf);
}
parsestr(p);
if (ic)

View file

@ -50,7 +50,7 @@ static struct builtin builtins[] =
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, "ALRUZafilrtux", 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),
@ -60,7 +60,7 @@ static struct builtin builtins[] =
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, "LRUZafilrtu", "x"),
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),
@ -78,7 +78,7 @@ static struct builtin builtins[] =
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, "ALRUZailrtu", NULL),
BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
@ -93,7 +93,7 @@ static struct builtin builtins[] =
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, "ALRUZafiltux", "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 +107,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, "ALRUZafilrtuxm", 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"),
@ -618,6 +618,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;
@ -704,7 +706,6 @@ bin_cd(char *nam, char **argv, char *ops, int func)
chdir(unmeta(pwd));
}
}
set_pwd_env();
return 0;
}
@ -946,7 +947,6 @@ cd_try_chdir(char *pfix, char *dest, int hard)
static void
cd_new_pwd(int func, LinkNode dir, int chaselinks)
{
Param pm;
List l;
char *new_pwd, *s;
int dirstacksize;
@ -981,13 +981,8 @@ cd_new_pwd(int func, LinkNode dir, int chaselinks)
zsfree(oldpwd);
oldpwd = pwd;
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) {
@ -1474,8 +1469,8 @@ 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;
char *optstr = "aiLRZlurtxU----A";
int on = 0, off = 0, roff, bit = PM_ARRAY;
int initon, initoff, of, i;
int returnval = 0, printflags = 0;
@ -1506,6 +1501,8 @@ bin_typeset(char *name, char **argv, char *ops, int func)
off |= PM_LOWER;
if (on & PM_LOWER)
off |= PM_UPPER;
if (on & PM_HASHED)
off |= PM_ARRAY;
on &= ~off;
/* Given no arguments, list whatever the options specify. */
@ -1548,8 +1545,15 @@ bin_typeset(char *name, char **argv, char *ops, int func)
if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) &&
!(pm->flags & PM_READONLY & ~off))
uniqarray((*pm->gets.afn) (pm));
if ((on & ~pm->flags) & PM_HASHED) {
char *nam = ztrdup(pm->nam);
unsetparam(nam);
pm = createparam(nam, on & ~PM_READONLY);
DPUTS(!pm, "BUG: parameter not created");
}
pm->flags = (pm->flags | on) & ~off;
if (PM_TYPE(pm->flags) != PM_ARRAY) {
if (PM_TYPE(pm->flags) != PM_ARRAY &&
PM_TYPE(pm->flags) != PM_HASHED) {
if ((on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | PM_INTEGER)) && auxlen)
pm->ct = auxlen;
/* did we just export this? */
@ -1598,7 +1602,8 @@ bin_typeset(char *name, char **argv, char *ops, int func)
(((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)))) {
!(bit = (((off & pm->flags) | (on & ~pm->flags)) &
(PM_INTEGER|PM_HASHED)))))) {
/* if no flags or values are given, just print this parameter */
if (!on && !roff && !asg->value) {
paramtab->printnode((HashNode) pm, 0);
@ -1623,7 +1628,8 @@ bin_typeset(char *name, char **argv, char *ops, int func)
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_TYPE(pm->flags) != PM_ARRAY &&
PM_TYPE(pm->flags) != PM_HASHED) {
if (pm->flags & PM_EXPORTED) {
if (!(pm->flags & PM_UNSET) && !pm->env && !asg->value)
pm->env = addenv(asg->name, getsparam(asg->name));

View file

@ -1004,11 +1004,12 @@ void
untokenize(char *s)
{
for (; *s; s++)
if (itok(*s))
if (itok(*s)) {
if (*s == Nularg)
chuck(s--);
else
*s = ztokens[*s - Pound];
}
}
/* Open a file for writing redicection */
@ -1923,22 +1924,8 @@ save_params(Cmd cmd, LinkList *restore_p, LinkList *remove_p)
} else if (!(pm->flags & PM_READONLY) &&
(unset(RESTRICTED) || !(pm->flags & PM_RESTRICTED))) {
Param tpm = (Param) alloc(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);
@ -1989,6 +1976,9 @@ restore_params(LinkList restorelist, LinkList removelist)
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);

View file

@ -355,21 +355,12 @@ scanner(Complist q)
insert(c->str, 0);
} else {
/* Do pattern matching on current path section. */
char *fn;
char *fn = pathbuf[pathbufcwd] ? unmeta(pathbuf + pathbufcwd) : ".";
int dirs = !!q->next;
DIR *lock;
DIR *lock = opendir(fn);
char *subdirs = NULL;
int subdirlen = 0;
fn = pathbuf[pathbufcwd] ? unmeta(pathbuf + pathbufcwd) : ".";
if (dirs) {
struct stat st;
stat(fn, &st);
/* a directory with subdirectories has link count greater than 2 */
if (!S_ISDIR(st.st_mode) || st.st_nlink == 2)
return;
}
lock = opendir(fn);
if (lock == NULL)
return;
while ((fn = zreaddir(lock, 1)) && !errflag) {
@ -594,7 +585,8 @@ parsecomp(int gflag)
pptr[1] && pptr[1] != Outpar && pptr[1] != Bar) ||
*pptr == Outpar) {
if (*pptr == '/' || !*pptr ||
(isset(EXTENDEDGLOB) && *pptr == Tilde &&
((*pptr == Bar ||
(isset(EXTENDEDGLOB) && *pptr == Tilde)) &&
(gflag & GF_TOPLEV)))
c->stat |= C_LAST;
return c;
@ -746,7 +738,8 @@ parsecomp(int gflag)
}
/* mark if last pattern component in path component or pattern */
if (*pptr == '/' || !*pptr ||
(isset(EXTENDEDGLOB) && *pptr == Tilde && (gflag & GF_TOPLEV)))
((*pptr == Bar ||
(isset(EXTENDEDGLOB) && *pptr == Tilde)) && (gflag & GF_TOPLEV)))
c->stat |= C_LAST;
c->str = dupstrpfx(cstr, pptr - cstr);
return c;

View file

@ -1061,93 +1061,6 @@ 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 */
/****************************************/

View file

@ -176,7 +176,7 @@ shingetline(void)
int
ingetc(void)
{
char lastc;
int lastc;
if (lexstop)
return ' ';

View file

@ -232,6 +232,11 @@ IPDEF9("manpath", &manpath, "MANPATH"),
IPDEF9("psvar", &psvar, "PSVAR"),
IPDEF9("watch", &watch, "WATCH"),
/*TEST BEGIN*/
#define IPDEF10(A) {NULL,A,PM_HASHED|PM_SPECIAL|PM_DONTIMPORT,BR((void *)0),SFN(hashsetfn),GFN(hashgetfn),stdunsetfn,0,NULL,NULL,NULL,0}
IPDEF10("testhash"),
/*TEST END*/
#ifdef DYNAMIC
IPDEF9F("module_path", &module_path, "MODULE_PATH", PM_RESTRICTED),
#endif
@ -247,7 +252,137 @@ static Param argvparam;
/**/
HashTable paramtab;
/**/
HashTable
newparamtable(int size, char const *name)
{
HashTable ht = newhashtable(size, name, NULL);
ht->hash = hasher;
ht->emptytable = emptyhashtable;
ht->filltable = NULL;
ht->addnode = addhashnode;
ht->getnode = gethashnode2;
ht->getnode2 = gethashnode2;
ht->removenode = removehashnode;
ht->disablenode = NULL;
ht->enablenode = NULL;
ht->freenode = freeparamnode;
ht->printnode = printparamnode;
return ht;
}
/* 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;
}
#define SCANPM_WANTVALS (1<<0)
#define SCANPM_WANTKEYS (1<<1)
#define SCANPM_WANTINDEX (1<<2)
static unsigned numparamvals;
/**/
static void
scancountparams(HashNode hn, int flags)
{
if (!(((Param)hn)->flags & PM_UNSET)) {
++numparamvals;
if ((flags & SCANPM_WANTKEYS) && (flags & SCANPM_WANTVALS))
++numparamvals;
}
}
static char **paramvals;
/**/
static void
scanparamvals(HashNode hn, int flags)
{
struct value v;
v.pm = (Param)hn;
if (!(v.pm->flags & PM_UNSET)) {
if (flags & SCANPM_WANTKEYS) {
paramvals[numparamvals++] = v.pm->nam;
if (!(flags & SCANPM_WANTVALS))
return;
}
v.isarr = (PM_TYPE(v.pm->flags) & (PM_ARRAY|PM_HASHED));
v.inv = (flags & SCANPM_WANTINDEX);
v.a = 0;
v.b = -1;
paramvals[numparamvals++] = getstrvalue(&v);
}
}
/**/
char **
paramvalarr(HashTable ht, unsigned flags)
{
MUSTUSEHEAP("paramvalarr");
numparamvals = 0;
if (ht)
scanhashtable(ht, 0, 0, 0, scancountparams, flags);
paramvals = (char **) alloc((numparamvals + 1) * sizeof(char *));
if (ht) {
numparamvals = 0;
scanhashtable(ht, 0, 0, 0, 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) {
unsigned flags = 0;
if (v->a)
flags |= SCANPM_WANTKEYS;
if (v->b > v->a)
flags |= SCANPM_WANTVALS;
v->arr = paramvalarr(v->pm->gets.hfn(v->pm), flags);
/* Can't take numeric slices of associative arrays */
v->a = 0;
v->b = -1;
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. */
@ -261,19 +396,7 @@ createparamtable(void)
char buf[50], *str, *iname;
int num_env;
paramtab = newhashtable(151, "paramtab", NULL);
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 = newparamtable(151, "paramtab");
/* Add the special parameters to the hash table */
for (ip = special_params; ip->nam; ip++)
@ -368,6 +491,36 @@ createparamtable(void)
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_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 *
@ -413,27 +566,51 @@ createparam(char *name, int flags)
pm = (Param) alloc(sizeof *pm);
pm->flags = flags;
if(!(pm->flags & PM_SPECIAL)) {
switch (PM_TYPE(flags)) {
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).
*/
PERMALLOC {
tpm->flags = pm->flags;
if (!toplevel)
tpm->flags &= ~PM_SPECIAL;
switch (PM_TYPE(pm->flags)) {
case PM_SCALAR:
pm->sets.cfn = strsetfn;
pm->gets.cfn = strgetfn;
tpm->u.str = ztrdup(pm->gets.cfn(pm));
break;
case PM_INTEGER:
pm->sets.ifn = intsetfn;
pm->gets.ifn = intgetfn;
tpm->u.val = pm->gets.ifn(pm);
break;
case PM_ARRAY:
pm->sets.afn = arrsetfn;
pm->gets.afn = arrgetfn;
tpm->u.arr = arrdup(pm->gets.afn(pm));
break;
default:
DPUTS(1, "BUG: tried to create param node without valid flag");
case PM_HASHED:
tpm->u.hash = copyparamtable(pm->gets.hfn(pm), pm->nam);
break;
}
pm->unsetfn = stdunsetfn;
}
return pm;
} LASTALLOC;
/*
* 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. */
@ -577,6 +754,23 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
singsub(&s);
if (!rev) {
if (PM_TYPE(v->pm->flags) == PM_HASHED) {
HashTable ht = v->pm->gets.hfn(v->pm);
if (!ht) {
ht = newparamtable(17, v->pm->nam);
v->pm->sets.hfn(v->pm, ht);
}
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 = 0;
v->a = 0;
*w = v->b = -1;
r = isset(KSHARRAYS) ? 1 : 0;
} else
if (!(r = mathevalarg(s, &s)) || (isset(KSHARRAYS) && r >= 0))
r++;
if (word && !v->isarr) {
@ -799,11 +993,12 @@ getvalue(char **pptr, int bracks)
s = t = *pptr;
garr = NULL;
if (idigit(*s))
if (idigit(*s)) {
if (bracks >= 0)
ppar = zstrtol(s, &s, 10);
else
ppar = *s++ - '0';
}
else if (iident(*s))
while (iident(*s))
s++;
@ -844,7 +1039,7 @@ getvalue(char **pptr, int bracks)
if (!pm || (pm->flags & PM_UNSET))
return NULL;
v = (Value) hcalloc(sizeof *v);
if (PM_TYPE(pm->flags) == PM_ARRAY)
if (PM_TYPE(pm->flags) & (PM_ARRAY|PM_HASHED))
v->isarr = isvarat ? -1 : 1;
v->pm = pm;
v->inv = 0;
@ -863,12 +1058,12 @@ 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;
@ -891,11 +1086,12 @@ getstrvalue(Value v)
}
switch(PM_TYPE(v->pm->flags)) {
case PM_HASHED:
case PM_ARRAY:
ss = getvaluearr(v);
if (v->isarr)
s = sepjoin(v->pm->gets.afn(v->pm), NULL);
s = sepjoin(ss, 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];
@ -913,8 +1109,9 @@ getstrvalue(Value v)
break;
}
if (v->a == 0 && v->b == -1)
if (v->a == 0 && v->b == -1) {
LASTALLOC_RETURN s;
}
if (v->a < 0)
v->a += strlen(s);
if (v->b < 0)
@ -946,7 +1143,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)
@ -993,6 +1190,7 @@ setstrvalue(Value v, char *val)
zsfree(val);
return;
}
v->pm->flags &= ~PM_UNSET;
switch (PM_TYPE(v->pm->flags)) {
case PM_SCALAR:
MUSTUSEHEAP("setstrvalue");
@ -1103,17 +1301,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);
@ -1187,6 +1393,20 @@ getaparam(char *s)
return NULL;
}
/* Retrieve an assoc array parameter as an array */
/**/
char **
gethparam(char *s)
{
Value v;
if (!idigit(*s) && (v = getvalue(&s, 0)) &&
PM_TYPE(v->pm->flags) == PM_HASHED)
return paramvalarr(v->pm->gets.hfn(v->pm), SCANPM_WANTVALS);
return NULL;
}
/**/
Param
setsparam(char *s, char *val)
@ -1248,7 +1468,7 @@ setaparam(char *s, char **val)
} else {
if (!(v = getvalue(&s, 1)))
createparam(t, PM_ARRAY);
else if (PM_TYPE(v->pm->flags) != PM_ARRAY &&
else if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) &&
!(v->pm->flags & PM_SPECIAL)) {
int uniq = v->pm->flags & PM_UNIQUE;
unsetparam(t);
@ -1338,7 +1558,7 @@ unsetparam_pm(Param pm, int altflag, int exp)
* 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 ((locallevel && locallevel >= pm->level) || (pm->flags & PM_SPECIAL))
return;
paramtab->removenode(paramtab, pm->nam); /* remove parameter node from table */
@ -1363,6 +1583,7 @@ 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;
}
@ -1429,6 +1650,70 @@ arrsetfn(Param pm, char **x)
pm->u.arr = x;
}
/* Function to get value of an association parameter */
/**/
static HashTable
hashgetfn(Param pm)
{
return pm->u.hash;
}
/* Flag to freeparamnode to unset the struct */
static int delunset;
/* Function to set value of an association parameter */
/**/
static void
hashsetfn(Param pm, HashTable x)
{
if (pm->u.hash && pm->u.hash != x) {
/* The parameters in the hash table need to be unset *
* before being deleted. */
int odelunset = delunset;
delunset = 1;
deletehashtable(pm->u.hash);
delunset = odelunset;
}
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;
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\n",
NULL, 0);
return;
}
ht = paramtab = newparamtable(17, pm->nam);
while (*aptr) {
/* The parameter name is ztrdup'd... */
v->pm = createparam(*aptr, PM_SCALAR|PM_UNSET);
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 *
* special parameters that cannot be set by the user. */
@ -2189,3 +2474,107 @@ scanendscope(HashNode hn, int flags)
if(pm->level > locallevel)
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);
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 ");
else if (p->flags & PM_ARRAY)
printf("array ");
else if (p->flags & PM_HASHED)
printf("association ");
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;
case PM_HASHED:
/* association */
putchar('(');
{
HashTable ht = p->gets.hfn(p);
if (ht)
scanhashtable(ht, 0, 0, 0, ht->printnode, 0);
}
printf(")\n");
break;
}
}

View file

@ -71,6 +71,10 @@ static int bufspc;
static char *bp;
/* Position of the start of the current line in the buffer */
static char *bufline;
/* bp1 is an auxilliary pointer into the buffer, which when non-NULL is *
* moved whenever the buffer is reallocated. It is used when data is *
* being temporarily held in the buffer. */
@ -81,11 +85,9 @@ static char *bp1;
static char *fm;
/* Current truncation string (metafied), the length at which truncation *
* occurs, and the direction in which it occurs. */
/* Non-zero if truncating the current segment of the buffer. */
static char *truncstr;
static int trunclen, truncatleft;
static int trunclen;
/* Current level of nesting of %{ / %} sequences. */
@ -95,10 +97,6 @@ static int dontcount;
static char *rstring, *Rstring;
/* If non-zero, Inpar, Outpar and Nularg can be added to the buffer. */
static int nonsp;
/* Perform prompt expansion on a string, putting the result in a *
* permanently-allocated string. If ns is non-zero, this string *
* may have embedded Inpar and Outpar, which indicate a toggling *
@ -130,9 +128,8 @@ promptexpand(char *s, int ns, char *rs, char *Rs)
rstring = rs;
Rstring = Rs;
nonsp = ns;
fm = s;
bp = buf = zalloc(bufspc = 256);
bp = bufline = buf = zalloc(bufspc = 256);
bp1 = NULL;
trunclen = 0;
putpromptchar(1, '\0');
@ -140,6 +137,15 @@ promptexpand(char *s, int ns, char *rs, char *Rs)
if(dontcount)
*bp++ = Outpar;
*bp = 0;
if (!ns) {
/* If zero, Inpar, Outpar and Nularg should be removed. */
for (bp = buf; *bp; bp++) {
if (*bp == Meta)
bp++;
else if (*bp == Inpar || *bp == Outpar || *bp == Nularg)
chuck(bp);
}
}
return buf;
}
@ -164,7 +170,7 @@ putpromptchar(int doprint, int endchar)
arg = zstrtol(fm, &fm, 10);
}
if (*fm == '(') {
int tc;
int tc, otrunclen;
if (idigit(*++fm)) {
arg = zstrtol(fm, &fm, 10);
@ -224,6 +230,12 @@ putpromptchar(int doprint, int endchar)
if (getegid() == arg)
test = 1;
break;
case 'l':
*bp = '\0';
countprompt(bufline, &t0, 0);
if (t0 >= arg)
test = 1;
break;
case 'L':
if (shlvl >= arg)
test = 1;
@ -249,10 +261,15 @@ putpromptchar(int doprint, int endchar)
if (!*fm || !(sep = *++fm))
return 0;
fm++;
/* Don't do the current truncation until we get back */
otrunclen = trunclen;
trunclen = 0;
if (!putpromptchar(test == 1 && doprint, sep) || !*++fm ||
!putpromptchar(test == 0 && doprint, ')')) {
trunclen = otrunclen;
return 0;
}
trunclen = otrunclen;
continue;
}
if (!doprint)
@ -377,72 +394,24 @@ putpromptchar(int doprint, int endchar)
tsetcap(TCUNDERLINEEND, 1);
break;
case '[':
if (idigit(*++fm))
trunclen = zstrtol(fm, &fm, 10);
else
trunclen = arg;
if (trunclen) {
truncatleft = *fm && *fm != ']' && *fm++ == '<';
bp1 = bp;
while (*fm && *fm != ']') {
if (*fm == '\\' && fm[1])
++fm;
addbufspc(1);
*bp++ = *fm++;
}
addbufspc(2);
if (bp1 == bp)
*bp++ = '<';
*bp = '\0';
zsfree(truncstr);
truncstr = ztrdup(bp = bp1);
bp1 = NULL;
} else {
while (*fm && *fm != ']') {
if (*fm == '\\' && fm[1])
fm++;
fm++;
}
}
if(!*fm)
return 0;
if (idigit(*++fm))
arg = zstrtol(fm, &fm, 10);
if (!prompttrunc(arg, ']', doprint, endchar))
return *fm;
break;
case '<':
case '>':
if((trunclen = arg)) {
char ch = *fm++;
truncatleft = ch == '<';
bp1 = bp;
while (*fm && *fm != ch) {
if (*fm == '\\' && fm[1])
++fm;
addbufspc(1);
*bp++ = *fm++;
}
addbufspc(1);
*bp = '\0';
zsfree(truncstr);
truncstr = ztrdup(bp = bp1);
bp1 = NULL;
} else {
char ch = *fm++;
while(*fm && *fm != ch) {
if (*fm == '\\' && fm[1])
fm++;
fm++;
}
}
if(!*fm)
return 0;
if (!prompttrunc(arg, *fm, doprint, endchar))
return *fm;
break;
case '{': /*}*/
if (!dontcount++ && nonsp) {
if (!dontcount++) {
addbufspc(1);
*bp++ = Inpar;
}
break;
case /*{*/ '}':
if (dontcount && !--dontcount && nonsp) {
if (dontcount && !--dontcount) {
addbufspc(1);
*bp++ = Outpar;
}
@ -569,7 +538,7 @@ putpromptchar(int doprint, int endchar)
break;
}
} else if(*fm == '!' && isset(PROMPTBANG)) {
if(doprint)
if(doprint) {
if(fm[1] == '!') {
fm++;
addbufspc(1);
@ -579,6 +548,7 @@ putpromptchar(int doprint, int endchar)
sprintf(bp, "%d", curhist);
bp += strlen(bp);
}
}
} else {
char c = *fm == Meta ? *++fm ^ 32 : *fm;
@ -604,6 +574,8 @@ pputc(char c)
c ^= 32;
}
*bp++ = c;
if (c == '\n' && !dontcount)
bufline = bp;
}
/* Make sure there is room for `need' more characters in the buffer. */
@ -627,46 +599,19 @@ addbufspc(int need)
}
/* stradd() adds a metafied string to the prompt, *
* in a visible representation, doing truncation. */
* in a visible representation. */
/**/
void
stradd(char *d)
{
/* dlen is the full length of the string we want to add */
int dlen = niceztrlen(d);
char *ps, *pd, *pc, *t;
int tlen, maxlen;
addbufspc(dlen);
char *ps, *pc;
addbufspc(niceztrlen(d));
/* This loop puts the nice representation of the string into the prompt *
* buffer. It might be modified later. Note that bp isn't changed. */
for(ps=d, pd=bp; *ps; ps++)
* buffer. */
for(ps=d; *ps; ps++)
for(pc=nicechar(*ps == Meta ? STOUC(*++ps)^32 : STOUC(*ps)); *pc; pc++)
*pd++ = *pc;
if(!trunclen || dlen <= trunclen) {
/* No truncation is needed, so update bp and return, *
* leaving the full string in the prompt. */
bp += dlen;
return;
}
/* We need to truncate. t points to the truncation string -- which is *
* inserted literally, without nice representation. tlen is its *
* length, and maxlen is the amout of the main string that we want to *
* keep. Note that if the truncation string is longer than the *
* truncation length (tlen > trunclen), the truncation string is used *
* in full. */
addbufspc(tlen = ztrlen(t = truncstr));
maxlen = tlen < trunclen ? trunclen - tlen : 0;
if(truncatleft) {
memmove(bp + strlen(t), bp + dlen - maxlen, maxlen);
while(*t)
*bp++ = *t++;
bp += maxlen;
} else {
bp += maxlen;
while(*t)
*bp++ = *t++;
}
*bp++ = *pc;
}
/* tsetcap(), among other things, can write a termcap string into the buffer. */
@ -684,12 +629,12 @@ tsetcap(int cap, int flag)
tputs(tcstr[cap], 1, putshout);
break;
case 1:
if (!dontcount && nonsp) {
if (!dontcount) {
addbufspc(1);
*bp++ = Inpar;
}
tputs(tcstr[cap], 1, putstr);
if (!dontcount && nonsp) {
if (!dontcount) {
int glitch = 0;
if (cap == TCSTANDOUTBEG || cap == TCSTANDOUTEND)
@ -764,3 +709,108 @@ countprompt(char *str, int *wp, int *hp)
if(hp)
*hp = h;
}
/**/
static int
prompttrunc(int arg, int truncchar, int doprint, int endchar)
{
if (arg) {
char ch = *fm, *ptr = bp, *truncstr;
int truncatleft = ch == '<';
/*
* If there is already a truncation active, return so that
* can be finished, backing up so that the new truncation
* can be started afterwards.
*/
if (trunclen) {
while (*--fm != '%')
;
fm--;
return 0;
}
trunclen = arg;
if (*fm != ']')
fm++;
while (*fm && *fm != truncchar) {
if (*fm == '\\' && fm[1])
++fm;
addbufspc(1);
*bp++ = *fm++;
}
if (!*fm)
return 0;
if (bp == ptr && truncchar == ']') {
addbufspc(1);
*bp++ = '<';
}
truncstr = ztrduppfx(ptr, bp - ptr);
bp = ptr;
fm++;
putpromptchar(doprint, endchar);
*bp = '\0';
if (bp - ptr > trunclen) {
/*
* We need to truncate. t points to the truncation string -- *
* which is inserted literally, without nice representation. *
* tlen is its length, and maxlen is the amount of the main *
* string that we want to keep. Note that if the truncation *
* string is longer than the truncation length (tlen > *
* trunclen), the truncation string is used in full. *
*/
char *t = truncstr;
int fullen = bp - ptr;
int tlen = ztrlen(t), maxlen;
if (tlen > fullen) {
addbufspc(tlen - fullen);
bp += tlen - fullen;
} else
bp -= fullen - trunclen;
maxlen = tlen < trunclen ? trunclen - tlen : 0;
if (truncatleft) {
if (maxlen)
memmove(ptr + strlen(t), ptr + fullen - maxlen,
maxlen);
while (*t)
*ptr++ = *t++;
} else {
ptr += maxlen;
while (*t)
*ptr++ = *t++;
}
}
zsfree(truncstr);
trunclen = 0;
/*
* We may have returned early from the previous putpromptchar *
* because we found another truncation following this one. *
* In that case we need to do the rest now. *
*/
if (!*fm)
return 0;
if (*fm != endchar) {
fm++;
/*
* With trunclen set to zero, we always reach endchar *
* (or the terminating NULL) this time round. *
*/
if (!putpromptchar(doprint, endchar))
return 0;
/* Now we have to trick it into matching endchar again */
fm--;
}
} else {
if (*fm != ']')
fm++;
while(*fm && *fm != truncchar) {
if (*fm == '\\' && fm[1])
fm++;
fm++;
}
if (trunclen || !*fm)
return 0;
}
return 1;
}

View file

@ -716,6 +716,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
int eval = 0;
int nojoin = 0;
char inbrace = 0; /* != 0 means ${...}, otherwise $... */
char hkeys = 0; /* 1 means get keys from associative array */
char hvals = 1; /* > hkeys get values of associative array */
*s++ = '\0';
if (!ialnum(*s) && *s != '#' && *s != Pound && *s != '-' &&
@ -732,12 +734,16 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
if (*s == Inbrace) {
inbrace = 1;
s++;
if (*s == '(' || *s == Inpar) {
if (*s == '!' && s[1] != Outbrace && emulation == EMULATE_KSH) {
hkeys = 1;
s++;
} else if (*s == '(' || *s == Inpar) {
char *t, sav;
int tt = 0;
long num;
int escapes = 0;
int klen;
#define UNTOK(C) (itok(C) ? ztokens[(C) - Pound] : (C))
#define UNTOK_AND_ESCAPE(X) {\
untokenize(X = dupstring(s + 1));\
if (escapes) {\
@ -851,7 +857,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
prenum = num;
else
postnum = num;
if (s[1] != sav)
if (UNTOK(s[1]) != UNTOK(sav))
break;
t = get_strarg(++s);
if (!*t)
@ -865,7 +871,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
*t = sav;
sav = *s;
s = t + 1;
if (*s != sav) {
if (UNTOK(*s) != UNTOK(sav)) {
s--;
break;
}
@ -886,6 +892,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
escapes = 1;
break;
case 'k':
hkeys = 1;
break;
case 'v':
hvals = 2;
break;
default:
flagerr:
zerr("error in flags", NULL, 0);
@ -986,9 +999,16 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
if (getindex(&s, v) || s == os)
break;
}
if ((isarr = v->isarr))
if ((isarr = v->isarr)) {
/* No way to reach here with v->inv != 0, so getvaluearr() *
* will definitely be called by getarrvalue(). Slicing of *
* associations isn't done, so use v->a and v->b for flags */
if (PM_TYPE(v->pm->flags) == PM_HASHED) {
v->a = hkeys;
v->b = hvals;
}
aval = getarrvalue(v);
else {
} else {
if (v->pm->flags & PM_ARRAY) {
int tmplen = arrlen(v->pm->gets.afn(v->pm));

View file

@ -450,6 +450,8 @@ getsimptext(Cmd cmd)
taddchr('(');
taddlist(v->arr);
taddstr(") ");
} else if (PM_TYPE(v->type) == PM_HASHED) {
/* XXX */
} else {
taddstr(v->str);
taddchr(' ');

View file

@ -510,8 +510,8 @@ adduserdir(char *s, char *t, int flags, int always)
if ((flags & ND_USERNAME) && nameddirtab->getnode2(nameddirtab, s))
return;
/* Never hash PWD, because it's never useful */
if (!strcmp(s, "PWD"))
/* Never hash PWD unless it was explicitly requested */
if (!always && !strcmp(s, "PWD"))
return;
/* Normal parameter assignments generate calls to this function, *

View file

@ -59,6 +59,7 @@ freeheap
getaparam
gethashnode
gethashnode2
gethparam
getiparam
getkeystring
getlinknode

View file

@ -538,6 +538,7 @@ 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 */
@ -813,6 +814,7 @@ struct param {
char **arr; /* value if declared array (PM_ARRAY) */
char *str; /* value if declared string (PM_SCALAR) */
long val; /* value if declared integer (PM_INTEGER) */
HashTable hash; /* value if declared assoc (PM_HASHED) */
} u;
/* pointer to function to set value of this parameter */
@ -820,6 +822,7 @@ struct param {
void (*cfn) _((Param, char *));
void (*ifn) _((Param, long));
void (*afn) _((Param, char **));
void (*hfn) _((Param, HashTable));
} sets;
/* pointer to function to get value of this parameter */
@ -827,6 +830,7 @@ struct param {
char *(*cfn) _((Param));
long (*ifn) _((Param));
char **(*afn) _((Param));
HashTable (*hfn) _((Param));
} gets;
/* pointer to function to unset this parameter */
@ -845,8 +849,9 @@ struct param {
#define PM_SCALAR 0 /* scalar */
#define PM_ARRAY (1<<0) /* array */
#define PM_INTEGER (1<<1) /* integer */
#define PM_HASHED (1<<15) /* association */
#define PM_TYPE(X) (X & (PM_SCALAR|PM_INTEGER|PM_ARRAY))
#define PM_TYPE(X) (X & (PM_SCALAR|PM_INTEGER|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 */

6
config.guess vendored
View file

@ -557,6 +557,12 @@ EOF
# says <Richard.M.Bartel@ccMail.Census.GOV>
echo i586-unisys-sysv4
exit 0 ;;
Power*:Rhapsody:*:*)
echo powerpc-apple-rhapsody${UNAME_RELEASE}
exit 0 ;;
*:Rhapsody:*:*)
echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
exit 0 ;;
esac
#echo '(No uname command or uname output not recognized.)' 1>&2

2
config.sub vendored
View file

@ -689,7 +689,7 @@ case $os in
| -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
| -cygwin32* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
| -linux* | -uxpv*)
| -linux* | -uxpv* | -rhapsody* )
# Remember, each alternative MUST END IN *, to match a version number.
;;
-sunos5*)

View file

@ -5,7 +5,7 @@ patches. (The version number built into the shell has not been changed.)
Zoli's AIX dynamic loading patch from 3933, slightly updated, without
some hunks which weren't needed on AIX 3.x so I don't know how to
update properly.
update them properly
My completion widgets patch
@ -18,24 +18,85 @@ My patch in 4477 to rename three functions to avoid clashes when
dynamic loading (particularly necessary on IRIX and AIX), including
the effect of Sven's additional fix in 4488
My patch 4513 for case-insensitive globbing via flags, plus fixlet 4552
Sven's magna opera patch-or 4510 and patch-match 4509 to add control
of alternative matches and arbitrary mapping between characters in the
command line and the matched string, plus all known related fixes
4526, 4527, 4534, 4555, 4557
Sven's magna opera patch-or 4510 and patch-match 4509 to add control of
alternative matches and arbitrary mapping between characters in the
command line and the matched, plus all known fixes 4526, 4527, 4534,
4555, 4557
My patch 4513 for case-insensitive globbing via flags, plus fixlet 4552
My ~PWD patch 4533
My suggestion for fixing the suffix on a yank in 4564
Bart's deltochar patch including new flags to allow commands not to
interrupt cumulative effects in 4570
interrupt cumulative effects in 4570 (new flag merged with compctl
widgets flags).
Bart's doc fiz 4574
Bart's doc fix 4574
Sven's latest word on the fixsuffix() horror in 4576, plus a
fixsuffix() added by hand in delcharorlist() which I've somehow missed
along the way
A fixsuffix() added by hand in delcharorlist() which I've somehow
missed along the way. The fixsuffix() horror is probably not yet
resolved; 4576 has side effects and hasn't been applied.
My latest version of lete2ctl, not posted
My latest version of lete2ctl, not posted but available at
http://www.ifh.de/~pws/computing/lete2ctl .
Bart's chpwd() fix 4589
Second edition
Added line in zle_tricky.c missed when patching by hand, spotted by
Bart. (Whitespace is still non-canonical.)
Fixed up my compctl widgets patch for use with Sven's rewrite, which I
hadn't done properly before.
Bart's function fixes, 4471
Bart's doc fixes, 4472
Bart's PWD and OLDPWD reshuffle, 4589
My test-line-length patch for prompts, 4591
Configure patch from Wilfredo Sanchez in 4594, with some extra
tabbification and without the setterm() hunk, since I've already renamed
that to zsetterm(), avoiding the conflict
My globbing fix for a bug which shows up in `case' constructs, 4595
Alternative version of the ~PWD patch (allow users to hash PWD
explicitly if that's what turns them on), 4596
Bart's experimental associative array patch, 4598, plus various
additions, 4599, 4602, 4608, 4641, 4653, 4654. No documentation yet;
if you want to play with this, so far:
% typeset -A hash # create associative array $hash
% hash[one]=eins hash[two]=zwei # assign elements
% hash=(one eins two zwei) # same, assign whole array (*)
% print $hash[one] # retrieve elements
eins
% print $hash # whole array looks like normal array
eins zwei
% print ${(k)hash} # flag to get keys
one two
% print ${(kv)hash} # flag to get keys and values (**)
one eins two zwei
Comparison of (*) and (**) will reveal how to copy an associative
array, but you always need to declare it with typeset -A or an
ordinary array will appear. There is a predefined special associative
array $testhash, for testing purposes only, which will eventually
disappear.
My rewrite of prompt truncation, 4601
Bart's params error message fix, 4606
My input fix for 8 bit characters, 4612
Bart's version of the *** fix, 4624
Bart's parameter substitution flag delimiter fix, 4644
My special parameter unset fix, 4662