about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:10:10 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:10:10 +0000
commit2a5a899a55fd2bce10efd01c75a4bec5285aa46c (patch)
tree4744bc2f1a6b86fc1b12870be94edf96fdab4879
parent9003d99d16c46b5679da7fcf1f2a41adef495ff9 (diff)
downloadzsh-2a5a899a55fd2bce10efd01c75a4bec5285aa46c.tar.gz
zsh-2a5a899a55fd2bce10efd01c75a4bec5285aa46c.tar.xz
zsh-2a5a899a55fd2bce10efd01c75a4bec5285aa46c.zip
zsh-3.1.5-pws-4 zsh-3.1.5-pws-4
-rw-r--r--Config/version.mk4
-rw-r--r--Doc/Zsh/arith.yo2
-rw-r--r--Doc/Zsh/builtins.yo79
-rw-r--r--Doc/Zsh/compat.yo6
-rw-r--r--Doc/Zsh/compctl.yo5
-rw-r--r--Doc/Zsh/expn.yo159
-rw-r--r--Doc/Zsh/grammar.yo6
-rw-r--r--Doc/Zsh/options.yo12
-rw-r--r--Doc/Zsh/params.yo48
-rw-r--r--Doc/Zsh/redirect.yo4
-rwxr-xr-xFunctions/multicomp7
-rwxr-xr-xMisc/lete2ctl49
-rw-r--r--Src/Builtins/rlimits.c15
-rw-r--r--Src/Builtins/sched.c14
-rw-r--r--Src/Modules/cap.c15
-rw-r--r--Src/Modules/clone.c15
-rw-r--r--Src/Modules/example.c47
-rw-r--r--Src/Modules/files.c15
-rw-r--r--Src/Modules/stat.c14
-rw-r--r--Src/Modules/zftp.c48
-rw-r--r--Src/Zle/comp.h5
-rw-r--r--Src/Zle/comp1.c16
-rw-r--r--Src/Zle/compctl.c23
-rw-r--r--Src/Zle/deltochar.c15
-rw-r--r--Src/Zle/zle_main.c35
-rw-r--r--Src/Zle/zle_tricky.c18
-rw-r--r--Src/builtin.c6
-rw-r--r--Src/cond.c58
-rw-r--r--Src/exec.c22
-rw-r--r--Src/glob.c2
-rw-r--r--Src/init.c5
-rw-r--r--Src/makepro.awk2
-rw-r--r--Src/mkbltnmlst.sh2
-rw-r--r--Src/modentry.c24
-rw-r--r--Src/module.c257
-rw-r--r--Src/params.c78
-rw-r--r--Src/parse.c6
-rw-r--r--Src/signals.c4
-rw-r--r--Src/subst.c28
-rw-r--r--Src/utils.c22
-rw-r--r--Src/zsh.export3
-rw-r--r--Src/zsh.h65
-rw-r--r--Util/zsh-development-guide291
-rw-r--r--acconfig.h3
-rw-r--r--aczsh.m481
-rw-r--r--configure.in37
-rw-r--r--patchlist.txt44
47 files changed, 1373 insertions, 343 deletions
diff --git a/Config/version.mk b/Config/version.mk
index 5b8c88350..309c19772 100644
--- a/Config/version.mk
+++ b/Config/version.mk
@@ -27,5 +27,5 @@
 # This must also serve as a shell script, so do not add spaces around the
 # `=' signs.
 
-VERSION=3.1.5-pws-3
-VERSION_DATE='December 12, 1998'
+VERSION=3.1.5-pws-4
+VERSION_DATE='December 17, 1998'
diff --git a/Doc/Zsh/arith.yo b/Doc/Zsh/arith.yo
index 1560c81d3..f08deb372 100644
--- a/Doc/Zsh/arith.yo
+++ b/Doc/Zsh/arith.yo
@@ -53,7 +53,7 @@ gives the ascii value of this character and an expression of the form
 of the parameter var(foo).
 
 Named parameters and subscripted arrays can be referenced by name within an
-arithmetic expression without using the parameter substitution syntax.
+arithmetic expression without using the parameter expansion syntax.
 
 An internal integer representation of a named parameter
 can be specified with the tt(integer) builtin.
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 4eae549e5..2df70cd98 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -47,7 +47,7 @@ cindex(aliases, listing)
 item(tt(alias) [ tt(-gmrL) ] [ var(name)[tt(=)var(value)] ... ])(
 For each var(name) with a corresponding var(value), define an alias
 with that value.  A trailing space in var(value) causes the next word
-to be checked for alias substitution.  If the tt(-g) flag is present,
+to be checked for alias expansion.  If the tt(-g) flag is present,
 define a global alias; global aliases are expanded even if they do not
 occur in command position.
 
@@ -879,23 +879,50 @@ findex(typeset)
 cindex(parameters, setting)
 cindex(parameters, declaring)
 item(tt(typeset) [ {tt(PLUS())|tt(-)}tt(ALRUZafilrtuxm) [var(n)]] [ var(name)[tt(=)var(value)] ... ])(
-Set attributes and values for shell parameters.
-When invoked inside a function a new parameter is created which will be
-unset when the function completes.  The new parameter will not be
-exported unless tt(ALL_EXPORT) is set, in which case the parameter will be
-exported provided no parameter of that name already exists.
-The following attributes are valid:
+Set or display attributes and values for shell parameters.
+
+A parameter is created for each var(name) that does not already refer
+to one.  When inside a function, a new parameter is created for every
+var(name) (even those that already exist), and is unset again when the
+function completes.  See
+ifzman(`Local Parameters' in zmanref(zshparam))\
+ifnzman(noderef(Local Parameters))\
+.  Local parameters are not exported unless tt(ALL_EXPORT) is set, in
+which case the parameter is exported em(only) when var(name) does not
+already appear in the environment.
+
+For each nofill(var(name)tt(=)var(value)) assignment, the parameter
+var(name) set to var(value).  Note that arrays currently cannot be
+assigned in tt(typeset) expressions; scalars and integers only.
+
+For each remaining var(name) that refers to a parameter that is set,
+the name and value of the parameter are printed in the form of an
+assignment.  Nothing is printed for newly-created parameters.
+
+If no var(name) is present, the names and values of all parameters are
+printed.  In this case the attribute flags restrict the the display to
+only those parameters that have the specified attributes.  Using
+`tt(PLUS())' rather than `tt(-)' to introduce the flag causes the
+attribute to be turned off, and suppresses printing of the names and
+values.  If only the tt(-m) flag is given the arguments are taken as
+patterns (should be quoted) and all parameters (or functions with the
+tt(-f) flag) with matching names are printed.
+
+The following attribute flags may be specified:
 
 startitem()
 item(tt(-A))(
-Declare var(name) to be an em(A)ssociation parameter (also known as a hash).
+The names refer to associative array parameters; see
+ifzman(`Array Parameters' in zmanref(zshparam))\
+ifnzman(noderef(Array Parameters))\
+.
 )
 item(tt(-L))(
 Left justify and remove leading blanks from var(value).
 If var(n) is nonzero, it defines the width of the field;
 otherwise it is determined by the width of the value of the first
 assignment.
-When the parameter is printed, it is filled on the right with
+When the parameter is expanded, it is filled on the right with
 blanks or truncated if necessary to fit the field.
 Leading zeros are removed if the tt(-Z) flag is also set.
 )
@@ -904,13 +931,13 @@ Right justify and fill with leading blanks.  If var(n) is nonzero
 if defines the width of the field;
 otherwise it is determined by the width of the value of the first
 assignment.
-When the parameter is printed, the field is left filled with
+When the parameter is expanded, the field is left filled with
 blanks or truncated from the end.
 )
 item(tt(-U))(
-For arrays keep only the first element of each duplications. It can also be
-set for colon separated special parameters like tt(PATH) or tt(FIGNORE),
-etc.
+For arrays (but not for associative arrays), keep only the first
+occurrence of each duplicated value.  This may also be set for
+colon-separated special parameters like tt(PATH) or tt(FIGNORE), etc.
 )
 item(tt(-Z))(
 Right justify and fill with leading zeros if the first non-blank
@@ -920,8 +947,9 @@ otherwise it is determined by the width of the value of the
 first assignment.
 )
 item(tt(-a))(
-On its own, this option produces a list of all array parameters.
-If any non-options are provided, the tt(typeset) command is silently ignored.
+The names refer to array parameters.  For historical reasons, scalar
+parameters are created even when this flag is specified, but the
+output is restricted to arrays (including associative arrays).
 )
 item(tt(-f))(
 The names refer to functions rather than parameters.  No assignments
@@ -933,12 +961,13 @@ function definition when the function is first referenced; see
 noderef(Functions).
 )
 item(tt(-i))(
-Use an internal integer representation.  If var(n) is nonzero
-it defines the output arithmetic base, otherwise it is determined by the first
-assignment.
+Use an internal integer representation.  If var(n) is nonzero it
+defines the output arithmetic base, otherwise it is determined by the
+first assignment.
 )
 item(tt(-l))(
-Convert to lower case.
+Convert the result to lower case whenever the parameter is expanded.
+The value is em(not) converted when assigned.
 )
 item(tt(-r))(
 The given var(name)s are marked readonly.
@@ -947,22 +976,14 @@ item(tt(-t))(
 Tags the named parameters.  Tags have no special meaning to the shell.
 )
 item(tt(-u))(
-Convert to upper case.
+Convert the result to upper case whenever the parameter is expanded.
+The value is em(not) converted when assigned.
 )
 item(tt(-x))(
 Mark for automatic export to the environment of subsequently
 executed commands.
 )
 enditem()
-
-Using `tt(PLUS())' rather than `tt(-)' causes these flags to be turned off.
-If no arguments are given but flags are specified,
-a list of named parameters which have these flags set is printed.
-Using `tt(PLUS())' instead of `tt(-)' keeps their values from being printed.
-If no arguments or options are given, the names and attributes
-of all parameters are printed. If only the tt(-m) flag is given the
-arguments are taken as patterns (should be quoted) and all parameters
-or functions (with the tt(-f) flag) with matching names are printed.
 )
 findex(ulimit)
 cindex(resource limits)
diff --git a/Doc/Zsh/compat.yo b/Doc/Zsh/compat.yo
index 529bd4557..a1ebd5411 100644
--- a/Doc/Zsh/compat.yo
+++ b/Doc/Zsh/compat.yo
@@ -32,9 +32,9 @@ The usual zsh startup/shutdown scripts are not executed.  Login shells
 source tt(/etc/profile) followed by tt($HOME/.profile).  If the
 tt(ENV) environment variable is set on invocation, tt($ENV) is sourced
 after the profile scripts.  The value of tt(ENV) is subjected to
-parameter expansion, command substitution, and arithmetic expansion before
-being interpreted as a pathname.  Note that the tt(PRIVILEGED) option
-also affects the execution of startup files.
+parameter expansion, command substitution, and arithmetic expansion
+before being interpreted as a pathname.  Note that the tt(PRIVILEGED)
+option also affects the execution of startup files.
 
 The following options are set if the shell is invoked as tt(sh) or
 tt(ksh):
diff --git a/Doc/Zsh/compctl.yo b/Doc/Zsh/compctl.yo
index 721d415a6..37296b716 100644
--- a/Doc/Zsh/compctl.yo
+++ b/Doc/Zsh/compctl.yo
@@ -348,8 +348,9 @@ words are used as prefixes.
 item(tt(-q))(
 If used with a suffix as specified by the tt(-S) option, this
 causes the suffix to be removed if the next character typed is a blank
-or does not insert anything (the same rule as used for the
-tt(AUTO_REMOVE_SLASH) option).  The option is most useful for list
+or does not insert anything or if the suffix consists of only one character
+and the next character typed is the same character (the same rule as used
+for the tt(AUTO_REMOVE_SLASH) option).  The option is most useful for list
 separators (comma, colon, etc.).
 )
 item(tt(-l) var(cmd))(
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 5341ec61e..32cc27f4e 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -5,15 +5,15 @@ sect(Description)
 The types of expansions performed are
 
 startlist()
-list(em(history expansion))
-list(em(alias expansion))
-list(em(process substitution))
-list(em(parameter expansion))
-list(em(command substitution))
-list(em(arithmetic expansion))
-list(em(brace expansion))
-list(em(filename expansion))
-list(em(filename generation))
+list(em(History Expansion))
+list(em(Alias Expansion))
+list(em(Process Substitution))
+list(em(Parameter Expansion))
+list(em(Command Substitution))
+list(em(Arithmetic Expansion))
+list(em(Brace Expansion))
+list(em(Filename Expansion))
+list(em(Filename Generation))
 endlist()
 
 Expansion is done in the above specified order in five steps.  The
@@ -29,8 +29,8 @@ em(filename expansion) followed by em(filename generation).
 
 If the tt(SH_FILE_EXPANSION) option is set, the order of expansion is modified
 for compatibility with bf(sh) and bf(ksh).  em(Filename expansion)
-is performed immediately after em(alias substitution),
-preceding the set of five substitutions mentioned above.
+is performed immediately after em(alias expansion),
+preceding the set of five expansions mentioned above.
 startmenu()
 menu(History Expansion)
 menu(Process Substitution)
@@ -52,7 +52,7 @@ corrections and the repetition of complicated commands or arguments.
 Command lines are saved in the history list, the size of which
 is controlled by the tt(HISTSIZE)
 vindex(HISTSIZE, use of)
-variable.  The most recent command is retained in any case.
+parameter.  The most recent command is retained in any case.
 A history expansion begins with the first character of the
 tt(histchars) parameter which is `tt(!)'
 by default and may occur anywhere on the command line; history
@@ -175,8 +175,8 @@ Convert the words to all uppercase.
 )
 item(tt(f))(
 (This and the following
-tt(F), tt(w) and tt(W) modifier only work with parameter and
-filename expansion.)
+tt(F), tt(w) and tt(W) modifier only work with parameter expansion and
+filename generation.)
 Repeats the immediately (without a colon) following modifier until the
 resulting word doesn't change any more.
 )
@@ -200,13 +200,13 @@ item(tt(s/)var(l)tt(/)var(r)[tt(/)])(
 Substitute var(r) for var(l) as described below.
 Unless preceded immediately by a tt(g), with no colon between,
 the substitution is done only for the
-first string that matches var(l).  For arrays and filename
-expansion, this applies to each word of the expanded text.
+first string that matches var(l).  For arrays and for filename
+generation, this applies to each word of the expanded text.
 )
 item(tt(&))(
 Repeat the previous tt(s) substitution.  Like tt(s), may be preceded
 immediately by a tt(g).  In variable expansion the tt(&) must appear
-inside braces, and in filename expansion it must be quoted with a
+inside braces, and in filename generation it must be quoted with a
 backslash.
 )
 enditem()
@@ -309,25 +309,27 @@ zmanref(zshparam)
 ifnzman(\
 noderef(Parameters)
 )\
-for a description of parameters.
+for a description of parameters, including arrays, associative arrays,
+and subscript notation to access individual array elements.
+
 In the expansions discussed below that require a pattern, the form of
 the pattern is the same as that used for filename generation;
 see noderef(Filename Generation).  Note that this pattern, along with
-the replacement text of a substitution, is itself subject to
-parameter, command and arithmetic substitution.  In addition to the
-following operations, the file modifiers described in
+the replacement text of any substitutions, are themselves subject to
+parameter expansion, command substitution, and arithmetic expansion.
+In addition to the following operations, the file modifiers described in
 noderef(Modifiers) in noderef(History Expansion) can be
 applied:  for example, tt(${i:s/foo/bar/}) performs string
-substitution on the value of parameter tt($i).
+substitution on the expansion of parameter tt($i).
 
 startitem()
 item(tt(${)var(name)tt(}))(
 The value, if any, of the parameter var(name) is substituted.
-The braces are required if var(name) is followed by
+The braces are required if the expansion is to be followed by
 a letter, digit, or underscore that is not to be interpreted
-as part of its name.
+as part of var(name).
 
-If var(name) is an array parameter, then the values of each
+If var(name) is an array parameter, then the value of each
 element of var(name) is substituted, one element per word.
 Otherwise, the expansion results in one word only; no field
 splitting is done on the result unless the tt(SH_WORD_SPLIT)
@@ -342,10 +344,12 @@ If var(name) is set and is non-null then substitute its
 value; otherwise substitute var(word). If var(name) is
 missing, substitute var(word).
 )
-item(tt(${)var(name)tt(:=)var(word)tt(}))(
-If var(name) is unset or is null then
-set it to var(word); the value of the parameter is then
-substituted.
+xitem(tt(${)var(name)tt(:=)var(word)tt(}))
+item(tt(${)var(name)tt(::=)var(word)tt(}))(
+In the first form, if var(name) is unset or is null then
+set it to var(word); in the second form, unconditionally
+set var(name) to var(word).  In both forms, the value of
+the parameter is then substituted.
 )
 item(tt(${)var(name)tt(:?)var(word)tt(}))(
 If var(name) is set and is non-null, then substitute
@@ -360,13 +364,14 @@ enditem()
 
 If the colon is omitted from one of the above expressions
 containing a colon, then the shell only checks whether
-var(name) is set or not, not whether it is null.
+var(name) is set, not whether its value is null.
+
+In the following expressions, when var(name) is an array and
+the substitution is not quoted, or if the tt((@)) flag or the
+`var(name)tt([@])' syntax is used, matching and replacement is
+performed on each array element separately.
 
 startitem()
-item(tt(${)var(name)tt(::=)var(word)tt(}))(
-Set var(name) to var(word); the value of the parameter is then
-substituted.
-)
 xitem(tt(${)var(name)tt(#)var(pattern)tt(}))
 item(tt(${)var(name)tt(##)var(pattern)tt(}))(
 If the var(pattern) matches the beginning of the value of
@@ -375,9 +380,7 @@ the matched portion deleted; otherwise, just
 substitute the value of var(name).  In the first
 form, the smallest matching pattern is preferred;
 in the second form, the largest matching pattern is
-preferred. If var(name) is an array and the substitution
-is not quoted or the tt((@)) flag or the `var(name)tt([@])' syntax
-is used, matching is performed on each array elements separately.
+preferred.
 )
 xitem(tt(${)var(name)tt(%)var(pattern)tt(}))
 item(tt(${)var(name)tt(%%)var(pattern)tt(}))(
@@ -387,43 +390,38 @@ the matched portion deleted; otherwise, just
 substitute the value of var(name).  In the first
 form, the smallest matching pattern is preferred;
 in the second form, the largest matching pattern is
-preferred. If var(name) is an array and the substitution
-is not quoted or the tt((@)) flag or the `var(name)tt([@])' syntax
-is used, matching is performed on each array elements separately.
+preferred.
 )
 item(tt(${)var(name)tt(:#)var(pattern)tt(}))(
 If the var(pattern) matches the value of var(name), then substitute
 the empty string; otherwise, just substitute the value of var(name).
-If var(name) is an array and the substitution
-is not quoted or the tt((@)) flag or the `var(name)tt([@])' syntax
-is used, matching is performed on each array elements separately, and
-the matched array elements are removed (use the tt((M)) flag to
+If var(name) is an array
+the matching array elements are removed (use the tt((M)) flag to
 remove the non-matched elements).
 )
 xitem(tt(${)var(name)tt(/)var(pattern)tt(/)var(repl)tt(}))
 item(tt(${)var(name)tt(//)var(pattern)tt(/)var(repl)tt(}))(
-Substitute the longest possible match of var(pattern) in the value of
-variable var(name) with the string var(repl).  The first form
-substitutes just the first occurrence, the second all occurrences.
+Replace by string var(repl), the longest possible match of
+var(pattern) in the expansion of parameter var(name).  The first form
+replaces just the first occurrence, the second form all occurrences.
 The var(pattern) may begin with a var(#), in which case the
 var(pattern) must match at the start of the string, or var(%), in
 which case it must match at the end of the string.  The var(repl) may
 be an empty string, in which case the final tt(/) may also be omitted.
 To quote the final tt(/) in other cases it should be preceded by two
 backslashes (i.e., a quoted backslash); this is not necessary if the
-tt(/) occurs inside a substituted paramter.  Substitution of an array
-is as described for tt(#) and tt(%) above.
+tt(/) occurs inside a substituted parameter.
 
 The first tt(/) may be preceded by a tt(:), in which case the match
 will only succeed if it matches the entire word.  Note also the
-effect of the tt(I) and tt(S) parameter expansion flags below:  the
-flags tt(M), tt(R), tt(B), tt(E) and tt(N) are not useful, however.
+effect of the tt(I) and tt(S) parameter expansion flags below; however,
+the flags tt(M), tt(R), tt(B), tt(E) and tt(N) are not useful.
 
 For example,
 
-nofill(tt(foo="twinkle twinkle little star" sub="t*e" rep="spy"))
-nofill(tt(print ${foo//${~sub}/$rep}))
-nofill(tt(print ${(S)foo//${~sub}/$rep}))
+nofill(tt(foo="twinkle twinkle little star" sub="t*e" rep="spy")
+tt(print ${foo//${~sub}/$rep})
+tt(print ${(S)foo//${~sub}/$rep}))
 
 Here, the tt(~) ensures that the text of tt($sub) is treated as a
 pattern rather than a plain string.  In the first case, the longest
@@ -436,6 +434,8 @@ If var(spec) is one of the above substitutions, substitute
 the length in characters of the result instead of
 the result itself.  If var(spec) is an array expression,
 substitute the number of elements of the result.
+Note that tt(^), tt(=), and tt(~), below, must appear
+to the left of tt(#) when these forms are combined.
 )
 item(tt(${^)var(spec)tt(}))(
 pindex(RC_EXPAND_PARAM, use of)
@@ -465,24 +465,27 @@ cindex(sh, field splitting style)
 Turn on the tt(SH_WORD_SPLIT) option for the
 evaluation of var(spec); if the `tt(=)' is doubled, turn it off.
 vindex(IFS, use of)
-When this option is set, parameter values are split into
-separate words using tt(IFS) as a delimiter
-before substitution.
+When this option is set, parameter expansions are split into
+separate words before substitution, using tt(IFS) as a delimiter.
 This is done by default in most other shells.
+
+Note that splitting is applied to var(word) in the assignment forms
+of var(spec) em(before) the assignment to var(name) is performed.
+This affects the result of array assignments with the tt(A) flag.
 )
 item(tt(${~)var(spec)tt(}))(
 pindex(GLOB_SUBST)
 Turn on the tt(GLOB_SUBST) option for the evaluation of
 var(spec); if the `tt(~)' is doubled, turn it off.  When this option is
 set, any pattern characters resulting
-from the substitution become eligible for file expansion and filename
+from parameter expansion are eligible for filename expansion and filename
 generation.
 )
 enditem()
 
 If a tt(${)...tt(}) type parameter expression or a
 tt($LPAR())...tt(RPAR()) type command substitution is used in place of
-var(name) above, it is substituted first and the result is used as if
+var(name) above, it is expanded first and the result is used as if
 it were the value of var(name).  Thus it is
 possible to perform nested operations:  tt(${${foo#head}%tail})
 substitues the value of tt($foo) with both tt(head) and tt(tail)
@@ -491,7 +494,7 @@ combination with the flags described next; see the example below.
 subsect(Parameter Expansion Flags)
 cindex(parameter expansion flags)
 cindex(flags, parameter expansion)
-cindex(expansion, parameter, flags)
+cindex(substitution, parameter, flags)
 If the opening brace is directly followed by an opening parenthesis,
 the string up to the matching closing parenthesis will be taken as a
 list of flags.  Where arguments are valid, any character, or the
@@ -501,9 +504,14 @@ in place of the colon as delimiters.  The following flags are supported:
 
 startitem()
 item(tt(A))(
-Create an array parameter with
+Create an array parameter with tt(${)...tt(=)...tt(}),
 tt(${)...tt(:=)...tt(}) or tt(${)...tt(::=)...tt(}).
-Assignment is made before sorting or padding.
+If this flag is repeated (as in tt(AA)), create an associative
+array parameter.  Assignment is made before sorting or padding.
+The var(name) part may be a subscripted range for ordinary
+arrays; the var(word) part em(must) be converted to an array, for
+example by using tt(${(AA)=)...tt(}) to activate word splitting,
+when creating an associative array.
 )
 item(tt(@))(
 In double quotes, array elements are put into separate words.
@@ -531,7 +539,9 @@ item(tt(U))(
 Convert all letters in the result to upper case.
 )
 item(tt(C))(
-Capitalize the resulting words.
+Capitalize the resulting words.  `Words' in this case refers to sequences
+of alphanumeric characters separated by non-alphanumerics, em(not) to words
+that result from field splitting.
 )
 item(tt(c))(
 With tt(${#)var(name)tt(}), count the total number of characters in an array,
@@ -545,9 +555,22 @@ item(tt(W))(
 Similar to tt(w) with the difference that empty words between
 repeated delimiters are also counted.
 )
+item(tt(k))(
+If var(name) refers to an associative array, substitute the em(keys)
+(element names) rather than the values of the elements.  Used with
+subscripts (including ordinary arrays), force indices or keys to be
+substituted even if the subscript form refers to values.  However,
+this flag may not be combined with subscript ranges.
+)
+item(tt(v))(
+Used with tt(k), substitute (as two consecutive words) both the key
+and the value of each associative array element.  Used with subscripts,
+force values to be substituted even if the subscript form refers to
+indices or keys.
+)
 item(tt(p))(
 Recognize the same escape sequences as the tt(print) builtin
-in string arguments to subsequent flags.
+in string arguments to any of the flags described below.
 )
 item(tt(l:)var(expr)tt(::)var(string1)tt(::)var(string2)tt(:))(
 Pad the resulting words on the left.  Each word will be truncated if
@@ -579,9 +602,13 @@ item(tt(f))(
 Split the result of the expansion to lines. This is a shorthand
 for `tt(ps:\n:)'.
 )
+enditem()
+
+The following flags are meaningful with the tt(${)...tt(#)...tt(}),
+tt(${)...tt(%)...tt(}), or tt(${)...tt(/)...tt(}) forms.
+
+startitem()
 item(tt(S))(
-(This and all remaining flags are used with the tt(${)...tt(#)...tt(}) or
-tt(${)...tt(%)...tt(}) forms.)
 Search substrings as well as beginnings or ends; with tt(#) start
 from the beginning and with tt(%) start from the end of the string.
 With substitution via tt(${)...tt(/)...tt(}) or
diff --git a/Doc/Zsh/grammar.yo b/Doc/Zsh/grammar.yo
index c78aed4b4..51783dc70 100644
--- a/Doc/Zsh/grammar.yo
+++ b/Doc/Zsh/grammar.yo
@@ -307,9 +307,9 @@ cindex(aliases, global)
 An alias is defined using the tt(alias) builtin; global aliases
 may be defined using the tt(-g) option to that builtin.
 
-Alias substitution is done on the shell input before any
-other substitution except history substitution.  Therefore,
-if an alias is defined for the word tt(foo), alias substitution
+Alias expansion is done on the shell input before any
+other expansion except history expansion.  Therefore,
+if an alias is defined for the word tt(foo), alias expansion
 may be avoided by quoting part of the word, e.g. tt(\foo).
 But there is nothing to prevent an alias being defined
 for tt(\foo) as well.
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 3af59560d..6b6093623 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -139,7 +139,7 @@ pindex(BANG_HIST)
 cindex(history, enable substitution)
 cindex(enable history substitution)
 item(tt(BANG_HIST) (tt(PLUS()K)))(
-Perform textual history substitution, bf(csh)-style,
+Perform textual history expansion, bf(csh)-style,
 treating the character `tt(!)' specially.
 )
 pindex(BARE_GLOB_QUAL)
@@ -263,9 +263,9 @@ in a command have no matches.
 Overrides tt(NULL_GLOB).
 )
 pindex(EQUALS)
-cindex(filename substitution, =)
+cindex(filename expansion, =)
 item(tt(EQUALS))(
-Perform tt(=) filename substitution.
+Perform tt(=) filename expansion.
 (See noderef(Filename Expansion).)
 )
 pindex(ERR_EXIT)
@@ -344,7 +344,7 @@ Do not require a leading `tt(.)' in a filename to be matched explicitly.
 )
 pindex(GLOB_SUBST)
 item(tt(GLOB_SUBST))(
-Treat any characters resulting from parameter substitution as being
+Treat any characters resulting from parameter expansion as being
 eligible for file expansion and filename generation, and any
 characters resulting from command substitution as being eligible for
 filename generation.
@@ -412,9 +412,9 @@ being added to the history list.
 pindex(HIST_VERIFY)
 cindex(history, verifying substitution)
 item(tt(HIST_VERIFY))(
-Whenever the user enters a line with history substitution,
+Whenever the user enters a line with history expansion,
 don't execute the line directly; instead, perform
-history substitution and reload the line into the editing buffer.
+history expansion and reload the line into the editing buffer.
 )
 pindex(HUP)
 cindex(jobs, HUP)
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 4c25a18ef..29899b379 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -7,7 +7,8 @@ A name may be any sequence of alphanumeric
 characters and underscores, or the single characters
 `tt(*)', `tt(@)', `tt(#)', `tt(?)', `tt(-)', `tt($)', or `tt(!)'.
 The value may be a em(scalar) (a string),
-an integer, or an array.
+an integer, an array (indexed numerically), or an em(associative)
+array (an unordered set of name-value pairs, indexed by name).
 To assign a scalar or integer value to a parameter,
 use the tt(typeset) builtin.
 findex(typeset, use of)
@@ -17,10 +18,12 @@ The value of a parameter may also be assigned by writing:
 
 nofill(var(name)tt(=)var(value))
 
-If the integer attribute, tt(-i), is set for var(name),
-the var(value) is subject to arithmetic evaluation.
+If the integer attribute, tt(-i), is set for var(name), the var(value)
+is subject to arithmetic evaluation.  See noderef(Array Parameters)
+for additional forms of assignment.
 
-In the parameter lists, the mark `<S>' indicates that the parameter is special.
+In the parameter lists that follow, the mark `<S>' indicates that the
+parameter is special.
 Special parameters cannot have their type changed, and they stay special even
 if unset.  `<Z>' indicates that the parameter does not exist when the shell
 initialises in tt(sh) or tt(ksh) emulation mode.
@@ -55,6 +58,15 @@ The value of an array parameter may be assigned by writing:
 
 nofill(var(name)tt(=LPAR())var(value) ...tt(RPAR()))
 
+If no parameter var(name) exists, an ordinary array parameter is created.
+Associative arrays must be declared first, by `tt(typeset -A) var(name)'.
+When var(name) refers to an associative array, the parenthesized list is
+interpreted as alternating keys and values:
+
+nofill(var(name)tt(=LPAR())var(key) var(value) ...tt(RPAR()))
+
+Every var(key) must have a var(value) in this case.
+
 Individual elements of an array may be selected using a
 subscript.  A subscript of the form `tt([)var(exp)tt(])'
 selects the single element var(exp), where var(exp) is
@@ -64,6 +76,9 @@ The elements are numbered beginning with 1 unless the
 tt(KSH_ARRAYS) option is set when they are numbered from zero.
 pindex(KSH_ARRAYS, use of)
 
+The same subscripting syntax is used for associative arrays,
+except that no arithmetic expansion is applied to var(EXP).
+
 A subscript of the form `tt([*])' or `tt([@])' evaluates to all
 elements of an array; there is no difference between the two
 except when they appear within double quotes.
@@ -73,6 +88,7 @@ except when they appear within double quotes.
 A subscript of the form `tt([)var(exp1)tt(,)var(exp2)tt(])'
 selects all elements in the range var(exp1) to var(exp2),
 inclusive.
+(Associative arrays are unordered, and so do not support ranges.)
 If one of the subscripts evaluates to a negative number,
 say tt(-)var(n), then the var(n)th element from the end
 of the array is used.  Thus `tt($foo[-3])' is the third element
@@ -90,7 +106,9 @@ option is set, the braced form is the only one that will
 work, the subscript otherwise not being treated specially.
 
 If a subscript is used on the left side of an assignment the selected
-range is replaced by the expression on the right side.
+element or range is replaced by the expression on the right side.  An
+array (but not an associative array) may be created by assignment to a
+range or element.
 
 If the opening bracket or the comma is directly followed by an opening
 parentheses the string up to the matching closing one is considered to
@@ -123,21 +141,27 @@ result is the first matching array element, substring or word (if the
 parameter is an array, if it is a scalar, or if it is a scalar and the
 `tt(w)' flag is given, respectively); note that this is like giving a
 number: `tt($foo[(r))var(??)tt(,3])' and `tt($foo[(r))var(??)tt(,(r)f*])' work.
+If the parameter is an associative array, only the value part of each pair
+is compared to the pattern.
 )
 item(tt(R))(
-like `tt(r)', but gives the last match.
+like `tt(r)', but gives the last match.  For associative arrays, gives
+all possible matches.
 )
 item(tt(i))(
 like `tt(r)', but gives the index of the match instead; this may not
-be combined with a second argument.
+be combined with a second argument.  For associative arrays, the key
+part of each pair is compared to the pattern, and the first matching
+key found is used.
 )
 item(tt(I))(
-like `tt(i), but gives the index of the last match.
+like `tt(i), but gives the index of the last match, or all possible
+matching keys in an associative array.
 )
 item(tt(n:)var(expr)tt(:))(
 if combined with `tt(r)', `tt(R)', `tt(i)' or `tt(I)', makes them give
 the var(n)th or var(n)th last match (if var(expr) evaluates to
-var(n)).
+var(n)).  This flag is ignored when the array is associative.
 )
 enditem()
 texinode(Positional Parameters)(Parameters Set By The Shell)(Array Parameters)(Parameters)
@@ -417,7 +441,7 @@ vindex(histchars)
 item(tt(histchars) <S>)(
 Three characters used by the shell's history and lexical analysis
 mechanism.  The first character signals the start of a history
-substitution (default `tt(!)').  The second character signals the
+expansion (default `tt(!)').  The second character signals the
 start of a quick history substitution (default `tt(^)').  The third
 character is the comment character (default `tt(#)').
 )
@@ -442,7 +466,7 @@ vindex(IFS)
 item(tt(IFS) <S>)(
 Internal field separators (by default space, tab, newline and NUL), that
 are used to separate words which result from
-command or parameter substitution and words read by
+command or parameter expansion and words read by
 the tt(read) builtin.  Any characters from the set space, tab and
 newline that appear in the IFS are called em(IFS white space).
 One or more IFS white space characters or one non-IFS white space
@@ -518,7 +542,7 @@ An array (colon-separated list) of filenames to check for
 new mail.  Each filename can be followed by a `tt(?)' and a
 message that will be printed.  The message will undergo
 parameter expansion, command substitution and arithmetic
-substitution with the variable tt($_) defined as the name
+expansion with the variable tt($_) defined as the name
 of the file that has changed.  The default message is
 `tt(You have new mail)'.  If an element is a directory
 instead of a file the shell will recursively check every
diff --git a/Doc/Zsh/redirect.yo b/Doc/Zsh/redirect.yo
index 8955e67ac..71f03f4cf 100644
--- a/Doc/Zsh/redirect.yo
+++ b/Doc/Zsh/redirect.yo
@@ -16,7 +16,7 @@ input/output specifications.
 
 The following may appear anywhere in a simple command
 or may precede or follow a complex command.
-Substitution occurs before var(word) or var(digit)
+Expansion occurs before var(word) or var(digit)
 is used except as noted below.
 If the result of substitution on var(word)
 produces more than one filename,
@@ -57,7 +57,7 @@ exist, even if tt(CLOBBER) is unset.
 item(tt(<<)[tt(-)] var(word))(
 The shell input is read up to a line that is the same as
 var(word), or to an end-of-file.
-No parameter substitution, command substitution or
+No parameter expansion, command substitution or
 filename generation is performed on var(word).
 The resulting document, called a
 em(here-document), becomes the standard input.
diff --git a/Functions/multicomp b/Functions/multicomp
index bef4e179f..c28558d95 100755
--- a/Functions/multicomp
+++ b/Functions/multicomp
@@ -45,13 +45,8 @@ while [[ -n "$pref" ]]; do
   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.
+    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/
diff --git a/Misc/lete2ctl b/Misc/lete2ctl
index ca00b8aee..273ea4674 100755
--- a/Misc/lete2ctl
+++ b/Misc/lete2ctl
@@ -9,6 +9,7 @@
 # Runs as a filter.  Should ignore anything which isn't a "complete".
 # It expects each "complete" statement to be the first thing on a line.
 # All the examples in the tcsh manual give sensible results.
+# Author:  Peter Stephenson <pws@ibmth.df.unipi.it>
 #
 # Option:
 # -x (exact): only applies in the case of command disambiguation (is
@@ -38,6 +39,11 @@
 # (5) Make sure all command names with wildcards are processed together --
 #     they need to be lumped into one "compctl -C" or "compctl -D"
 #     statement for zsh.
+# (6) Group completion (complete's g flag) is not built into zsh, so
+#     you need perl to be available to generate the groups.  If this
+#     script is useful, I assume that's not a problem.
+# (7) I don't know what `completing completions' means, so the X
+#     flag to complete is not handled.
 
 # Handle options
 if (@ARGV) {
@@ -113,6 +119,13 @@ sub gettype {
 # Nothing (n) can be handled by returning nothing.  (C.f. King Lear, I.i.)
     if ($c =~ /[abcjuv]/) {
 	$ret = "-$c";
+    } elsif ($c eq 'C') {
+	if (defined($glob)) {
+	    $ret = "-W $glob -/g '*(.*)'";
+	    undef($glob);
+	} else {
+	    $ret = '-c';
+	}
     } elsif ($c eq 'S') {
 	$ret = '-k signals';
     } elsif ($c eq 'd') {
@@ -121,18 +134,42 @@ sub gettype {
 	} else {
 	    $ret = '-/';
 	}
+    } elsif ($c eq 'D') {
+	if (defined($glob)) {
+	    $ret = "-W $glob -/";
+	    undef($glob);
+	} else {
+	    $ret = '-/';
+	}
     } elsif ($c eq 'e') {
 	$ret = '-E';
     } elsif ($c eq 'f' && !$glob) {
 	$ret = '-f';
+    } elsif ($c eq 'F') {
+	if (defined($glob)) {
+	    $ret = "-W $glob -f";
+	    undef($glob);
+	} else {
+	    $ret = '-f';
+	}
+    } elsif ($c eq 'g') {
+	$ret = "-s '\$(perl -e '\\''while ((\$name) = getgrent)\n" .
+	    "{ print \$name, \"\\n\"; }'\\'')'";
     } elsif ($c eq 'l') {
 	$ret = q!-k "(`limit | awk '{print $1}'`)"!;
     } elsif ($c eq 'p') {
-	$ret = "-W $glob -f", undef($glob) if defined($glob);
+        $ret = "-W $glob -f", undef($glob) if defined($glob);
     } elsif ($c eq 's') {
-	$ret = '-p';
+        $ret = '-p';
     } elsif ($c eq 't') {
 	$qual = '.';
+    } elsif ($c eq 'T') {
+        if (defined($glob)) {
+            $ret = "-W $glob -g '*(.)'";
+            undef($glob);
+        } else {
+            $ret = "-g '*(.)'";
+        }
     } elsif ($c eq 'x') {
 	$glob =~ s/'/'\\''/g;
 	$ret = "-X '$glob'";
@@ -190,7 +227,7 @@ $" = " - ";
 
 while (<>) {
     if (/^\s*complete\s/) {
-	undef(@stuff); 
+	undef(@stuff);
 	$default = '';
 	$_ = $';
 	while (/\\$/) {
@@ -211,7 +248,7 @@ while (<>) {
 	    # Loop over remaining arguments to "complete".
 	    $sep = substr($word,1,1);
 	    $sep =~ s/(\W)/\\$1/g;
-	    @split = split(/$sep/,$word);
+	    @split = split(/$sep/,$word,4);
 	    for ($i = 0; $i < 3; $i++) {
 		while ($split[$i] =~ /\\$/) {
 		    substr($split[$i],-1,1) = "";
@@ -225,7 +262,9 @@ while (<>) {
 		 # The "complete" catch-all:  treat this as compctl\'s
 		 # default (requiring no pattern matching).
 		$default .= &gettype($type) . ' ';
-		defined($suffix) && ($defsuf .= $suffix);
+		defined($suffix) &&
+		    (defined($defsuf) ? ($defsuf .= $suffix)
+		     : ($defsuf = $suffix));
 	    } else {
 		$pat = &getpat($pat,$arg);
 		$type = &gettype($type);
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 20b8d663d..31dcb71b1 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -576,6 +576,13 @@ static struct builtin bintab[] = {
 
 /**/
 int
+setup_rlimits(Module m)
+{
+    return 0;
+}
+
+/**/
+int
 boot_rlimits(Module m)
 {
     return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
@@ -590,4 +597,12 @@ cleanup_rlimits(Module m)
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     return 0;
 }
+
+/**/
+int
+finish_rlimits(Module m)
+{
+    return 0;
+}
+
 #endif
diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c
index b4914899e..94661ccef 100644
--- a/Src/Builtins/sched.c
+++ b/Src/Builtins/sched.c
@@ -185,6 +185,13 @@ static struct builtin bintab[] = {
 
 /**/
 int
+setup_sched(Module m)
+{
+    return 0;
+}
+
+/**/
+int
 boot_sched(Module m)
 {
     if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
@@ -211,4 +218,11 @@ cleanup_sched(Module m)
     return 0;
 }
 
+/**/
+int
+finish_sched(Module m)
+{
+    return 0;
+}
+
 #endif
diff --git a/Src/Modules/cap.c b/Src/Modules/cap.c
index 008b6932d..dfeca86ad 100644
--- a/Src/Modules/cap.c
+++ b/Src/Modules/cap.c
@@ -124,6 +124,13 @@ static struct builtin bintab[] = {
 
 /**/
 int
+setup_cap(Module m)
+{
+    return 0;
+}
+
+/**/
+int
 boot_cap(Module m)
 {
     return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
@@ -138,4 +145,12 @@ cleanup_cap(Module m)
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     return 0;
 }
+
+/**/
+int
+finish_cap(Module m)
+{
+    return 0;
+}
+
 #endif
diff --git a/Src/Modules/clone.c b/Src/Modules/clone.c
index 11387fc90..e2cfea8d9 100644
--- a/Src/Modules/clone.c
+++ b/Src/Modules/clone.c
@@ -98,6 +98,13 @@ static struct builtin bintab[] = {
 
 /**/
 int
+setup_clone(Module m)
+{
+    return 0;
+}
+
+/**/
+int
 boot_clone(Module m)
 {
     return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
@@ -112,4 +119,12 @@ cleanup_clone(Module m)
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     return 0;
 }
+
+/**/
+int
+finish_clone(Module m)
+{
+    return 0;
+}
+
 #endif
diff --git a/Src/Modules/example.c b/Src/Modules/example.c
index a71806c3a..95545172f 100644
--- a/Src/Modules/example.c
+++ b/Src/Modules/example.c
@@ -51,16 +51,14 @@ bin_example(char *nam, char **args, char *ops, int func)
 
 /**/
 static int
-cond_p_len(Conddef c, char **a)
+cond_p_len(char **a, int id)
 {
-    char *s1 = a[0], *s2 = a[1];
-
-    singsub(&s1);
-    untokenize(s1);
-    if (s2) {
-	singsub(&s2);
-	untokenize(s2);
-	return strlen(s1) == matheval(s2);
+    char *s1 = cond_str(a, 0);
+
+    if (a[1]) {
+	long v = cond_val(a, 1);
+
+	return strlen(s1) == v;
     } else {
 	return !s1[0];
     }
@@ -68,14 +66,10 @@ cond_p_len(Conddef c, char **a)
 
 /**/
 static int
-cond_i_ex(Conddef c, char **a)
+cond_i_ex(char **a, int id)
 {
-    char *s1 = a[0], *s2 = a[1];
+    char *s1 = cond_str(a, 0), *s2 = cond_str(a, 1);
 
-    singsub(&s1);
-    untokenize(s1);
-    singsub(&s2);
-    untokenize(s2);
     return !strcmp("example", dyncat(s1, s2));
 }
 
@@ -105,8 +99,8 @@ static struct builtin bintab[] = {
 };
 
 static struct conddef cotab[] = {
-    CONDDEF("len", 0, 1, 2, cond_p_len),
-    CONDDEF("ex", CONDF_INFIX, 0, 0, cond_i_ex),
+    CONDDEF("len", 0, cond_p_len, 1, 2, 0),
+    CONDDEF("ex", CONDF_INFIX, cond_i_ex, 0, 0, 0),
 };
 
 static struct funcwrap wrapper[] = {
@@ -115,6 +109,15 @@ static struct funcwrap wrapper[] = {
 
 /**/
 int
+setup_example(Module m)
+{
+    printf("The example module has now been set up.\n");
+    fflush(stdout);
+    return 0;
+}
+
+/**/
+int
 boot_example(Module m)
 {
     return !(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
@@ -133,4 +136,14 @@ cleanup_example(Module m)
     deletewrapper(m, wrapper);
     return 0;
 }
+
+/**/
+int
+finish_example(Module m)
+{
+    printf("Thank you for using the example module.  Have a nice day.\n");
+    fflush(stdout);
+    return 0;
+}
+
 #endif
diff --git a/Src/Modules/files.c b/Src/Modules/files.c
index 6127c5524..f52c54338 100644
--- a/Src/Modules/files.c
+++ b/Src/Modules/files.c
@@ -511,6 +511,13 @@ static struct builtin bintab[] = {
 
 /**/
 int
+setup_files(Module m)
+{
+    return 0;
+}
+
+/**/
+int
 boot_files(Module m)
 {
     return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
@@ -525,4 +532,12 @@ cleanup_files(Module m)
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     return 0;
 }
+
+/**/
+int
+finish_files(Module m)
+{
+    return 0;
+}
+
 #endif
diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c
index 769b42b1a..5c56be5c6 100644
--- a/Src/Modules/stat.c
+++ b/Src/Modules/stat.c
@@ -571,6 +571,13 @@ static struct builtin bintab[] = {
 
 /**/
 int
+setup_stat(Module m)
+{
+    return 0;
+}
+
+/**/
+int
 boot_stat(Module m)
 {
     return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
@@ -586,4 +593,11 @@ cleanup_stat(Module m)
     return 0;
 }
 
+/**/
+int
+finish_stat(Module m)
+{
+    return 0;
+}
+
 #endif
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index ca0843419..4bcd80c7f 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -62,9 +62,9 @@
 /* it's a TELNET based protocol, but don't think I like doing this */
 #include <arpa/telnet.h>
 
-/* bet there are machines which have neither INADDR_NONE nor in_addr_t. */
+/* pinch the definition from <netinet/in.h> for deficient headers */
 #ifndef INADDR_NONE
-#define INADDR_NONE (in_addr_t)-1
+#define INADDR_NONE 0xffffffff
 #endif
 
 /*
@@ -1354,7 +1354,11 @@ zfsenddata(char *name, int recv, int progress, long startat)
 	 * We do this here in case we needed to wait for a RETR
 	 * command to tell us how many bytes are coming.
 	 */
+	int osc = sfcontext;
+
+	sfcontext = SFC_HOOK;
 	doshfunc("zftp_progress", l, NULL, 0, 1);
+	sfcontext = osc;
 	/* Now add in the bit of the file we've got/sent already */
 	sofar = last_sofar = startat;
     }
@@ -1482,8 +1486,12 @@ zfsenddata(char *name, int recv, int progress, long startat)
 	    break;
 	if (!ret && sofar != last_sofar && progress &&
 	    (l = getshfunc("zftp_progress")) != &dummy_list) {
+	    int osc = sfcontext;
+
 	    zfsetparam("ZFTP_COUNT", &sofar, ZFPM_READONLY|ZFPM_INTEGER);
+	    sfcontext = SFC_HOOK;
 	    doshfunc("zftp_progress", l, NULL, 0, 1);
+	    sfcontext = osc;
 	    last_sofar = sofar;
 	}
     }
@@ -1650,7 +1658,7 @@ zftp_open(char *name, char **args, int flags)
 	zfsetparam("ZFTP_HOST", ztrdup(zhostp->h_name), ZFPM_READONLY);
     }
 
-    zsock.sin_port = ntohs(zservp->s_port);
+    zsock.sin_port = zservp->s_port;
     zcfd = zfmovefd(socket(zsock.sin_family, SOCK_STREAM, 0));
     if (zcfd < 0) {
 	zwarnnam(name, "socket failed: %e", NULL, errno);
@@ -2102,9 +2110,13 @@ zfgetcwd(void)
      * front end.  By putting it here, and in close when ZFTP_PWD is unset,
      * we at least cover the bases.
      */
-    if ((l = getshfunc("zftp_chpwd")) != &dummy_list)
-	doshfunc("zftp_chpwd", l, NULL, 0, 1);
+    if ((l = getshfunc("zftp_chpwd")) != &dummy_list) {
+	int osc = sfcontext;
 
+	sfcontext = SFC_HOOK;
+	doshfunc("zftp_chpwd", l, NULL, 0, 1);
+	sfcontext = osc;
+    }
     return 0;
 }
 
@@ -2303,9 +2315,13 @@ zftp_getput(char *name, char **args, int flags)
 	zsfree(ln);
 	if (progress && (l = getshfunc("zftp_progress")) != &dummy_list) {
 	    /* progress to finish: ZFTP_TRANSFER set to GF or PF */
+	    int osc = sfcontext;
+
 	    zfsetparam("ZFTP_TRANSFER", ztrdup(recv ? "GF" : "PF"),
 		       ZFPM_READONLY);
+	    sfcontext = SFC_HOOK;
 	    doshfunc("zftp_progress", l, NULL, 0, 1);
+	    sfcontext = osc;
 	}
 	if (rest) {
 	    zsfree(rest);
@@ -2428,9 +2444,13 @@ zftp_close(char *name, char **args, int flags)
 	zfunsetparam(*aptr);
 
     /* Now ZFTP_PWD is unset.  It's up to zftp_chpwd to notice. */
-    if ((l = getshfunc("zftp_chpwd")) != &dummy_list)
-	doshfunc("zftp_chpwd", l, NULL, 0, 1);
+    if ((l = getshfunc("zftp_chpwd")) != &dummy_list) {
+	int osc = sfcontext;
 
+	sfcontext = SFC_HOOK;
+	doshfunc("zftp_chpwd", l, NULL, 0, 1);
+	sfcontext = osc;
+    }
     /* tidy up status variables, because mess is bad */
     zfclosing = zfdrrrring = 0;
 
@@ -2558,6 +2578,13 @@ bin_zftp(char *name, char **args, char *ops, int func)
 
 /**/
 int
+setup_zftp(Module m)
+{
+    return 0;
+}
+
+/**/
+int
 boot_zftp(Module m)
 {
     int ret;
@@ -2593,4 +2620,11 @@ cleanup_zftp(Module m)
     return 0;
 }
 
+/**/
+int
+finish_zftp(Module m)
+{
+    return 0;
+}
+
 #endif
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 84f8c3c98..afd55b7f1 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -98,7 +98,7 @@ struct compcond {
 struct compctl {
     int refc;			/* reference count                         */
     Compctl next;		/* next compctl for -x                     */
-    unsigned long mask, mask2;		/* mask of things to complete (CC_*)       */
+    unsigned long mask, mask2;	/* masks of things to complete (CC_*)      */
     char *keyvar;		/* for -k (variable)                       */
     char *glob;			/* for -g (globbing)                       */
     char *str;			/* for -s (expansion)                      */
@@ -110,7 +110,7 @@ struct compctl {
     char *withd;		/* for -w (with directory                  */
     char *hpat;			/* for -H (history pattern)                */
     int hnum;			/* for -H (number of events to search)     */
-    char *gname;
+    char *gname;		/* for -J and -V (group name)              */
     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)     */
@@ -169,7 +169,6 @@ 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. */
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index 5ffce0da2..a0c013901 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -430,7 +430,7 @@ quotename(const char *s, char **e, char *te, int *pl)
 
 /**/
 int
-boot_comp1(Module m)
+setup_comp1(Module m)
 {
     compctlreadptr = compctlread;
     clwords = (char **) zcalloc((clwsize = 16) * sizeof(char *));
@@ -446,12 +446,26 @@ boot_comp1(Module m)
     return 0;
 }
 
+/**/
+int
+boot_comp1(Module m)
+{
+    return 0;
+}
+
 #ifdef MODULE
 
 /**/
 int
 cleanup_comp1(Module m)
 {
+    return 0;
+}
+
+/**/
+int
+finish_comp1(Module m)
+{
     deletehashtable(compctltab);
     zfree(clwords, clwsize * sizeof(char *));
     compctlreadptr = fallback_compctlread;
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 7104bfc6e..4d192fef8 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1638,26 +1638,41 @@ static struct builtin bintab[] = {
 
 /**/
 int
-boot_compctl(Module m)
+setup_compctl(Module m)
 {
-    if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
-	return 1;
     compctltab->printnode = printcompctlp;
     printcompctlptr = printcompctl;
     compctl_widgetptr = compctl_widget;
     return 0;
 }
 
+/**/
+int
+boot_compctl(Module m)
+{
+    if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
+	return 1;
+    return 0;
+}
+
 #ifdef MODULE
 
 /**/
 int
 cleanup_compctl(Module m)
 {
-    compctltab->printnode = NULL;
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
+}
+
+/**/
+int
+finish_compctl(Module m)
+{
+    compctltab->printnode = NULL;
     printcompctlptr = NULL;
     compctl_widgetptr = NULL;
     return 0;
 }
+
 #endif
diff --git a/Src/Zle/deltochar.c b/Src/Zle/deltochar.c
index 8869eb147..66a301119 100644
--- a/Src/Zle/deltochar.c
+++ b/Src/Zle/deltochar.c
@@ -73,6 +73,13 @@ deltochar(void)
 
 /**/
 int
+setup_deltochar(Module m)
+{
+    return 0;
+}
+
+/**/
+int
 boot_deltochar(Module m)
 {
     w_deletetochar = addzlefunction("delete-to-char", deltochar,
@@ -93,4 +100,12 @@ cleanup_deltochar(Module m)
     deletezlefunction(w_deletetochar);
     return 0;
 }
+
+/**/
+int
+finish_deltochar(Module m)
+{
+    return 0;
+}
+
 #endif
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 57b75cd39..515405a0d 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -603,11 +603,15 @@ execzlefunc(Thingy func)
 	    zsfree(msg);
 	    feep();
 	} else {
-	  startparamscope();
-	  makezleparams();
-	  doshfunc(w->u.fnnam, l, NULL, 0, 1);
-	  endparamscope();
-	  lastcmd = 0;
+	    int osc = sfcontext;
+
+	    startparamscope();
+	    makezleparams();
+	    sfcontext = SFC_WIDGET;
+	    doshfunc(w->u.fnnam, l, NULL, 0, 1);
+	    sfcontext = osc;
+	    endparamscope();
+	    lastcmd = 0;
 	}
     }
 }
@@ -856,7 +860,7 @@ static struct builtin bintab[] = {
 
 /**/
 int
-boot_zle(Module m)
+setup_zle(Module m)
 {
     /* Set up editor entry points */
     trashzleptr = trashzle;
@@ -875,6 +879,13 @@ boot_zle(Module m)
     /* initialise the keymap system */
     init_keymaps();
 
+    return 0;
+}
+
+/**/
+int
+boot_zle(Module m)
+{
     addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     return 0;
 }
@@ -885,15 +896,21 @@ boot_zle(Module m)
 int
 cleanup_zle(Module m)
 {
-    int i;
-
     if(zleactive) {
 	zerrnam(m->nam, "can't unload the zle module while zle is active",
 	    NULL, 0);
 	return 1;
     }
-
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
+}
+
+/**/
+int
+finish_zle(Module m)
+{
+    int i;
+
     cleanup_keymaps();
     deletehashtable(thingytab);
 
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index cbc744601..8c976449e 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -3801,15 +3801,23 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 
 	/* Completion after `~', maketildelist adds the usernames *
 	 * and named directories.                                 */
-	if (ic == Tilde)
+	if (ic == Tilde) {
+	    char *oi = ipre;
+
+	    ipre = (ipre ? dyncat("~", ipre) : "~");
 	    maketildelist();
-	else if (ic == Equals) {
+	    ipre = oi;
+	} else if (ic == Equals) {
 	    /* Completion after `=', get the command names from *
 	     * the cmdnamtab and aliases from aliastab.         */
+	    char *oi = ipre;
+
+	    ipre = (ipre ? dyncat("=", ipre) : "=");
 	    if (isset(HASHLISTALL))
 		cmdnamtab->filltable(cmdnamtab);
 	    dumphashtable(cmdnamtab, -7);
 	    dumphashtable(aliastab, -2);
+	    ipre = oi;
 	} else {
 	    /* Normal file completion... */
 	    if (ispattern & 1) {
@@ -4082,6 +4090,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	if ((list = getshfunc(cc->func)) != &dummy_list) {
 	    /* We have it, so build a argument list. */
 	    LinkList args = newlinklist();
+	    int osc = sfcontext;
 
 	    addlinknode(args, cc->func);
 
@@ -4099,8 +4108,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 
 	    /* This flag allows us to use read -l and -c. */
 	    incompctlfunc = 1;
+	    sfcontext = SFC_COMPLETE;
 	    /* Call the function. */
 	    doshfunc(cc->func, list, args, 0, 1);
+	    sfcontext = osc;
 	    incompctlfunc = 0;
 	    /* And get the result from the reply parameter. */
 	    if ((r = get_user_var("reply")))
@@ -4246,6 +4257,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    LinkList args = newlinklist();
 	    LinkNode ln;
 	    Cmatch m;
+	    int osc = sfcontext;
 
 	    addlinknode(args, cc->ylist);
 	    for (ln = firstnode(matches); ln; ln = nextnode(ln)) {
@@ -4263,7 +4275,9 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 
 	    /* No harm in allowing read -l and -c here, too */
 	    incompctlfunc = 1;
+	    sfcontext = SFC_COMPLETE;
 	    doshfunc(cc->ylist, list, args, 0, 1);
+	    sfcontext = osc;
 	    incompctlfunc = 0;
 	    uv = "reply";
 	}
diff --git a/Src/builtin.c b/Src/builtin.c
index 7e77bc190..6c41ce2fd 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -992,9 +992,13 @@ cd_new_pwd(int func, LinkNode dir, int chaselinks)
 
     /* execute the chpwd function */
     if ((l = getshfunc("chpwd")) != &dummy_list) {
+	int osc = sfcontext;
+
 	fflush(stdout);
 	fflush(stderr);
+	sfcontext = SFC_HOOK;
 	doshfunc("chpwd", l, NULL, 0, 1);
+	sfcontext = osc;
     }
 
     dirstacksize = getiparam("DIRSTACKSIZE");
@@ -1469,7 +1473,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
     Param pm;
     Asgment asg;
     Comp com;
-    char *optstr = "aiLRZlurtxU----A";
+    char *optstr = "aiALRZlurtxU";
     int on = 0, off = 0, roff, bit = PM_ARRAY;
     int initon, initoff, of, i;
     int returnval = 0, printflags = 0;
diff --git a/Src/cond.c b/Src/cond.c
index ed91f72f3..906e81938 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -48,7 +48,8 @@ evalcond(Cond c)
 	{
 	    Conddef cd;
 
-	    if ((cd = getconddef((c->type == COND_MODI), (char *) c->left, 1))) {
+	    if ((cd = getconddef((c->type == COND_MODI),
+				 ((char *) c->left) + 1, 1))) {
 		if (c->type == COND_MOD) {
 		    int l = arrlen((char **) c->right);
 
@@ -57,10 +58,24 @@ evalcond(Cond c)
 			return 0;
 		    }
 		}
-		return cd->handler(cd, (char **) c->right);
+		return cd->handler((char **) c->right, cd->condid);
+	    }
+	    else {
+		char **a = (char **) c->right, *s = a[0];
+
+		if (s && s[0] == '-' &&
+		    (cd = getconddef(0, s + 1, 1))) {
+		    int l = arrlen(a);
+
+		    if (l < cd->min || (cd->max >= 0 && l > cd->max)) {
+			zerr("unrecognized condition: `-%s'", (char *) c->left, 0);
+			return 0;
+		    }
+		    a[0] = (char *) c->left;
+		    return cd->handler(a, cd->condid);
+		} else
+		    zerr("unrecognized condition: `-%s'", (char *) c->left, 0);
 	    }
-	    else
-		zerr("unrecognized condition: `-%s'", (char *) c->left, 0);
 	    return 0;
 	}
     }
@@ -244,3 +259,38 @@ optison(char *s)
     else
 	return isset(i);
 }
+
+/**/
+char *
+cond_str(char **args, int num)
+{
+    char *s = args[num];
+
+    singsub(&s);
+    untokenize(s);
+
+    return s;
+}
+
+/**/
+long
+cond_val(char **args, int num)
+{
+    char *s = args[num];
+
+    singsub(&s);
+    untokenize(s);
+
+    return matheval(s);
+}
+
+/**/
+int
+cond_match(char **args, int num, char *str)
+{
+    char *s = args[num];
+
+    singsub(&s);
+
+    return matchpat(str, s);
+}
diff --git a/Src/exec.c b/Src/exec.c
index a2d74a9f4..911559a02 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -113,7 +113,12 @@ pid_t cmdoutpid;
  
 /**/
 int cmdoutval;
- 
+
+/* The context in which a shell function is called, see SFC_* in zsh.h. */ 
+
+/**/
+int sfcontext;
+
 /* Stack to save some variables before executing a signal handler function */
 
 /**/
@@ -2771,19 +2776,14 @@ runshfunc(List list, FuncWrap wrap, char *name)
     char *ou;
 
     while (wrap) {
-	wrap->module->flags |= MOD_WRAPPER;
-	wrap->count++;
+	wrap->module->wrapper++;
 	cont = wrap->handler(list, wrap->next, name);
-	wrap->count--;
-	if (!wrap->count) {
-	    wrap->module->flags &= ~MOD_WRAPPER;
+	wrap->module->wrapper--;
 #ifdef DYNAMIC
-	    if (wrap->module->flags & MOD_UNLOAD) {
-		wrap->module->flags &= ~MOD_UNLOAD;
-		unload_module(wrap->module, NULL);
-	    }
+	if (!wrap->module->wrapper &&
+	    (wrap->module->flags & MOD_UNLOAD))
+	    unload_module(wrap->module, NULL);
 #endif
-	}
 	if (!cont)
 	    return;
 	wrap = wrap->next;
diff --git a/Src/glob.c b/Src/glob.c
index 194d535a4..9c1d08aa4 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -1947,7 +1947,7 @@ getmatch(char **sp, char *pat, int fl, int n, char *replstr)
 {
     Comp c;
     char *s = *sp, *t, *start, sav;
-    int i, j, l = strlen(*sp), matched;
+    int i, l = strlen(*sp), matched;
 
     MUSTUSEHEAP("getmatch");	/* presumably covered by prefork() test */
     repllist = NULL;
diff --git a/Src/init.c b/Src/init.c
index decc7617e..0c874eead 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -111,13 +111,17 @@ loop(int toplevel, int justonce)
 	    if (toplevel && (prelist = getshfunc("preexec")) != &dummy_list) {
 		Histent he = gethistent(curhist);
 		LinkList args;
+		int osc = sfcontext;
+
 		PERMALLOC {
 		    args = newlinklist();
 		    addlinknode(args, "preexec");
 		    if (he && he->text)
 			addlinknode(args, he->text);
 		} LASTALLOC;
+		sfcontext = SFC_HOOK;
 		doshfunc("preexec", prelist, args, 0, 1);
+		sfcontext = osc;
 		freelinklist(args, (FreeFunc) NULL);
 		errflag = 0;
 	    }
@@ -613,6 +617,7 @@ setupvals(void)
     breaks = loops = 0;
     lastmailcheck = time(NULL);
     locallevel = sourcelevel = 0;
+    sfcontext = SFC_DIRECT;
     trapreturn = 0;
     noerrexit = -1;
     nohistsave = 1;
diff --git a/Src/makepro.awk b/Src/makepro.awk
index b5d2f3dc4..86117fcc1 100644
--- a/Src/makepro.awk
+++ b/Src/makepro.awk
@@ -104,7 +104,7 @@ BEGIN {
 	gsub(/@!/, ",", dcltor)
 
 	# If this is a module boot/cleanup function, conditionally rename it.
-	if(" " dtype " " ~ / int / && dcltor ~ / *@\+(boot|cleanup)_[_0-9A-Za-z]+@- *_\(\( *Module +[_0-9A-Za-z]+ *\)\) */) {
+	if(" " dtype " " ~ / int / && dcltor ~ / *@\+(boot|cleanup|setup|finish)_[_0-9A-Za-z]+@- *_\(\( *Module +[_0-9A-Za-z]+ *\)\) */) {
 	    modtype = dnam
 	    sub(/_.*$/, "", modtype)
 	    output = output "# if defined(DYNAMIC_NAME_CLASH_OK) && defined(MODULE)\n"
diff --git a/Src/mkbltnmlst.sh b/Src/mkbltnmlst.sh
index 4a90ecd20..2f988ec8f 100644
--- a/Src/mkbltnmlst.sh
+++ b/Src/mkbltnmlst.sh
@@ -55,6 +55,6 @@ for bin_mod in $bin_mods; do
 		exit 1 ;;
 	esac
     done
-    echo "    mod.nam = \"$bin_mod\"; boot_$bin_mod(&mod);"
+    echo "    mod.nam = \"$bin_mod\"; setup_$bin_mod(&mod); boot_$bin_mod(&mod);"
     done_mods="$done_mods$bin_mod "
 done
diff --git a/Src/modentry.c b/Src/modentry.c
index 63c4b825d..f177e0a6a 100644
--- a/Src/modentry.c
+++ b/Src/modentry.c
@@ -1,15 +1,35 @@
 #include "zsh.mdh"
 
+int setup_ _((Module));
 int boot_ _((Module));
 int cleanup_ _((Module));
+int finish_ _((Module));
 int modentry _((int boot, Module m));
 
 /**/
 int
 modentry(int boot, Module m)
 {
-    if (boot)
+    switch (boot) {
+    case 0:
+	return setup_(m);
+	break;
+
+    case 1:
 	return boot_(m);
-    else
+	break;
+
+    case 2:
 	return cleanup_(m);
+	break;
+
+    case 3:
+	return finish_(m);
+	break;
+
+    default:
+	zerr("bad call to modentry", NULL, 0);
+	return 1;
+	break;
+    }
 }
diff --git a/Src/module.c b/Src/module.c
index 8ed4f1d3b..5780eb134 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -36,6 +36,13 @@
 
 /**/
 int
+setup_zsh(Module m)
+{
+    return 0;
+}
+
+/**/
+int
 boot_zsh(Module m)
 {
     return 0;
@@ -114,7 +121,6 @@ addwrapper(Module m, FuncWrap w)
     w->next = NULL;
     w->flags |= WRAPF_ADDED;
     w->module = m;
-    w->count = 0;
 
     return 0;
 }
@@ -256,24 +262,58 @@ load_and_bind(const char *fn)
 #ifdef HAVE_DLFCN_H
 # include <dlfcn.h>
 #else
-# include <sys/types.h>
-# include <nlist.h>
-# include <link.h>
+# ifdef HAVE_DL_H
+#  include <dl.h>
+#  define RTLD_LAZY BIND_DEFERRED
+#  define RTLD_GLOBAL DYNAMIC_PATH
+# else
+#  include <sys/types.h>
+#  include <nlist.h>
+#  include <link.h>
+# endif
 #endif
-#ifndef HAVE_DLCLOSE
-# define dlclose(X) ((X), 0)
+
+#ifdef HPUXDYNAMIC
+# define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0)
+# define dlclose(handle) shl_unload((shl_t)(handle))
+
+/**/
+static
+void *
+hpux_dlsym(void *handle, char *name)
+{
+    void *sym_addr;
+    if (!shl_findsym((shl_t *)&handle, name, TYPE_UNDEFINED, &sym_addr))
+	return sym_addr;
+    return NULL;
+}
+
+# define dlsym(handle,name) hpux_dlsym(handle,name)
+# define dlerror() 0
+#else
+# ifndef HAVE_DLCLOSE
+#  define dlclose(X) ((X), 0)
+# endif
 #endif
 
 #ifdef DLSYM_NEEDS_UNDERSCORE
+# define STR_SETUP     "_setup_"
+# define STR_SETUP_S   "_setup_%s"
 # define STR_BOOT      "_boot_"
 # define STR_BOOT_S    "_boot_%s"
 # define STR_CLEANUP   "_cleanup_"
 # define STR_CLEANUP_S "_cleanup_%s"
+# define STR_FINISH    "_finish_"
+# define STR_FINISH_S  "_finish_%s"
 #else /* !DLSYM_NEEDS_UNDERSCORE */
+# define STR_SETUP     "setup_"
+# define STR_SETUP_S   "setup_%s"
 # define STR_BOOT      "boot_"
 # define STR_BOOT_S    "boot_%s"
 # define STR_CLEANUP   "cleanup_"
 # define STR_CLEANUP_S "cleanup_%s"
+# define STR_FINISH    "finish_"
+# define STR_FINISH_S  "finish_%s"
 #endif /* !DLSYM_NEEDS_UNDERSCORE */
 
 #endif /* !AIXDYNAMIC */
@@ -357,6 +397,13 @@ find_module(const char *name)
 
 /**/
 static int
+setup_module(Module m)
+{
+    return ((int (*)_((int,Module))) m->handle)(0, m);
+}
+
+/**/
+static int
 init_module(Module m)
 {
     return ((int (*)_((int,Module))) m->handle)(1, m);
@@ -366,14 +413,20 @@ init_module(Module m)
 static int
 cleanup_module(Module m)
 {
-    return ((int (*)_((int,Module))) m->handle)(0, m);
+    return ((int (*)_((int,Module))) m->handle)(2, m);
 }
 
-#else
-
 /**/
 static int
-init_module(Module m)
+finish_module(Module m)
+{
+    return ((int (*)_((int,Module))) m->handle)(3, m);
+}
+
+#else
+
+static Module_func
+module_func(Module m, char *name, char *name_s)
 {
     char *s, *t;
 #ifndef DYNAMIC_NAME_CLASH_OK
@@ -389,13 +442,34 @@ init_module(Module m)
     if ((t = strrchr(s, '.')))
 	*t = '\0';
 #ifdef DYNAMIC_NAME_CLASH_OK
-    fn = (Module_func) dlsym(m->handle, STR_BOOT);
+    fn = (Module_func) dlsym(m->handle, name);
 #else /* !DYNAMIC_NAME_CLASH_OK */
     if (strlen(s) + 6 > PATH_MAX)
-	return 1;
-    sprintf(buf, STR_BOOT_S, s);
+	return NULL;
+    sprintf(buf, name_s, s);
     fn = (Module_func) dlsym(m->handle, buf);
 #endif /* !DYNAMIC_NAME_CLASH_OK */
+    return fn;
+}
+
+/**/
+static int
+setup_module(Module m)
+{
+    Module_func fn = module_func(m, STR_SETUP, STR_SETUP_S);
+
+    if (fn)
+	return fn(m);
+    zwarnnam(m->nam, "no setup function", NULL, 0);
+    return 1;
+}
+
+/**/
+static int
+init_module(Module m)
+{
+    Module_func fn = module_func(m, STR_BOOT, STR_BOOT_S);
+
     if(fn)
 	return fn(m);
     zwarnnam(m->nam, "no boot function", NULL, 0);
@@ -406,33 +480,34 @@ init_module(Module m)
 static int
 cleanup_module(Module m)
 {
-    char *s, *t;
-#ifndef DYNAMIC_NAME_CLASH_OK
-    char buf[PATH_MAX + 1];
-#endif
-    Module_func fn;
+    Module_func fn = module_func(m, STR_CLEANUP, STR_CLEANUP_S);
 
-    s = strrchr(m->nam, '/');
-    if (s)
-	s = dupstring(++s);
-    else
-	s = m->nam;
-    if ((t = strrchr(s, '.')))
-	*t = '\0';
-#ifdef DYNAMIC_NAME_CLASH_OK
-    fn = (Module_func) dlsym(m->handle, STR_CLEANUP);
-#else /* !DYNAMIC_NAME_CLASH_OK */
-    if (strlen(s) + 9 > PATH_MAX)
-	return 1;
-    sprintf(buf, STR_CLEANUP_S, s);
-    fn = (Module_func) dlsym(m->handle, buf);
-#endif /* !DYNAMIC_NAME_CLASH_OK */
     if(fn)
 	return fn(m);
     zwarnnam(m->nam, "no cleanup function", NULL, 0);
     return 1;
 }
 
+/* Note that this function does more than just calling finish_foo(), *
+ * it really unloads the module. */
+
+/**/
+static int
+finish_module(Module m)
+{
+    Module_func fn = module_func(m, STR_FINISH, STR_FINISH_S);
+    int r;
+
+    if (fn)
+	r = fn(m);
+    else {
+	zwarnnam(m->nam, "no finish function", NULL, 0);
+	r = 1;
+    }
+    dlclose(m->handle);
+    return r;
+}
+
 #endif /* !AIXDYNAMIC */
 
 /**/
@@ -449,8 +524,8 @@ load_module(char const *name)
 	m = zcalloc(sizeof(*m));
 	m->nam = ztrdup(name);
 	m->handle = handle;
-	if (init_module(m)) {
-	    dlclose(handle);
+	if (setup_module(m) || init_module(m)) {
+	    finish_module(m);
 	    zsfree(m->nam);
 	    zfree(m, sizeof(*m));
 	    return NULL;
@@ -459,25 +534,35 @@ load_module(char const *name)
 	    addlinknode(modules, m);
 	} LASTALLOC;
 	return m;
-    }
+    } 
     m = (Module) getdata(node);
-    if (m->handle)
+    if (m->flags & MOD_UNLOAD)
+	m->flags &= ~MOD_UNLOAD;
+    else if (m->handle)
 	return m;
     if (m->flags & MOD_BUSY) {
 	zerr("circular dependencies for module %s", name, 0);
 	return NULL;
     }
     m->flags |= MOD_BUSY;
-    for (n = firstnode(m->deps); n; incnode(n))
-	if (!load_module((char *) getdata(n))) {
-	    m->flags &= ~MOD_BUSY;
+    if (m->deps)
+	for (n = firstnode(m->deps); n; incnode(n))
+	    if (!load_module((char *) getdata(n))) {
+		m->flags &= ~MOD_BUSY;
+		return NULL;
+	    }
+    m->flags &= ~MOD_BUSY;
+    if (!m->handle) {
+	if (!(m->handle = do_load_module(name)))
+	    return NULL;
+	if (setup_module(m)) {
+	    finish_module(m->handle);
+	    m->handle = NULL;
 	    return NULL;
 	}
-    m->flags &= ~MOD_BUSY;
-    if (!(m->handle = do_load_module(name)))
-	return NULL;
+    }
     if (init_module(m)) {
-	dlclose(m->handle);
+	finish_module(m->handle);
 	m->handle = NULL;
 	return NULL;
     }
@@ -756,12 +841,53 @@ bin_zmodload_cond(char *nam, char **args, char *ops)
 int
 unload_module(Module m, LinkNode node)
 {
-    if (m->handle && cleanup_module(m))
+    if (m->handle && !(m->flags & MOD_UNLOAD) && cleanup_module(m))
 	return 1;
     else {
+	int del = (m->flags & MOD_UNLOAD);
+
+	if (m->wrapper) {
+	    m->flags |= MOD_UNLOAD;
+	    return 0;
+	}
+	m->flags &= ~MOD_UNLOAD;
 	if (m->handle)
-	    dlclose(m->handle);
+	    finish_module(m);
 	m->handle = NULL;
+	if (del && m->deps) {
+	    /* The module was unloaded delayed, unload all modules *
+	     * on which it depended. */
+	    LinkNode n;
+
+	    for (n = firstnode(m->deps); n; incnode(n)) {
+		LinkNode dn = find_module((char *) getdata(n));
+		Module dm;
+
+		if (dn && (dm = (Module) getdata(dn)) &&
+		    (dm->flags & MOD_UNLOAD)) {
+		    /* See if this is the only module depending on it. */
+
+		    LinkNode an;
+		    Module am;
+		    int du = 1;
+
+		    for (an = firstnode(modules); du && an; incnode(an)) {
+			am = (Module) getdata(an);
+			if (am != m && am->handle && am->deps) {
+			    LinkNode sn;
+
+			    for (sn = firstnode(am->deps); du && sn;
+				 incnode(sn)) {
+				if (!strcmp((char *) getdata(sn), dm->nam))
+				    du = 0;
+			    }
+			}
+		    }
+		    if (du)
+			unload_module(dm, NULL);
+		}
+	    }
+	}
 	if(!m->deps) {
 	    if (!node) {
 		for (node = firstnode(modules); node; incnode(node))
@@ -791,24 +917,29 @@ bin_zmodload_load(char *nam, char **args, char *ops)
 	    node = find_module(*args);
 	    if (node) {
 		LinkNode mn, dn;
+		int del = 0;
 
 		for (mn = firstnode(modules); mn; incnode(mn)) {
 		    m = (Module) getdata(mn);
 		    if (m->deps && m->handle)
 			for (dn = firstnode(m->deps); dn; incnode(dn))
 			    if (!strcmp((char *) getdata(dn), *args)) {
-				zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", *args, 0);
-				ret = 1;
-				goto cont;
+				if (m->flags & MOD_UNLOAD)
+				    del = 1;
+				else {
+				    zwarnnam(nam, "module %s is in use by another module and cannot be unloaded", *args, 0);
+				    ret = 1;
+				    goto cont;
+				}
 			    }
 		}
 		m = (Module) getdata(node);
-		if (!(m->flags & MOD_WRAPPER)) {
-		    if (unload_module(m, node))
-			ret = 1;
-		}
-		else
-		    m->flags |= MOD_UNLOAD;
+		if (del)
+		    m->wrapper++;
+		if (unload_module(m, node))
+		    ret = 1;
+		if (del)
+		    m->wrapper--;
 	    } else if (!ops['i']) {
 		zwarnnam(nam, "no such module %s", *args, 0);
 		ret = 1;
@@ -820,7 +951,7 @@ bin_zmodload_load(char *nam, char **args, char *ops)
 	/* list modules */
 	for (node = firstnode(modules); node; incnode(node)) {
 	    m = (Module) getdata(node);
-	    if (m->handle) {
+	    if (m->handle && !(m->flags & MOD_UNLOAD)) {
 		if(ops['L']) {
 		    printf("zmodload ");
 		    if(m->nam[0] == '-')
@@ -835,8 +966,11 @@ bin_zmodload_load(char *nam, char **args, char *ops)
     } else {
 	/* load modules */
 	for (; *args; args++) {
+	    Module m;
+
 	    node = find_module(*args);
-	    if (node && ((Module) getdata(node))->handle) {
+	    if (node && (m = ((Module) getdata(node)))->handle &&
+		!(m->flags & MOD_UNLOAD)) {
 		if (!ops['i']) {
 		    zwarnnam(nam, "module %s already loaded.", *args, 0);
 		    ret = 1;
@@ -894,8 +1028,6 @@ getconddef(int inf, char *name, int autol)
     return p;
 }
 
-#ifdef DYNAMIC
-
 /* This adds the given condition definition. The return value is zero on *
  * success and 1 on failure. If there is a matching definition for an    *
  * autoloaded condition, it is removed. */
@@ -909,10 +1041,11 @@ addconddef(Conddef c)
     if (p) {
 	if (!p->module || (p->flags & CONDF_ADDED))
 	    return 1;
-
+#ifdef DYNAMIC
 	/* There is an autoload definition. */
 
 	deleteconddef(p);
+#endif
     }
     c->next = condtab;
     condtab = c;
@@ -942,6 +1075,8 @@ addconddefs(char const *nam, Conddef c, int size)
     return hadf ? hads : 1;
 }
 
+#ifdef DYNAMIC
+
 /* This adds a definition for autoloading a module for a condition. */
 
 /**/
@@ -1014,4 +1149,4 @@ deleteconddefs(char const *nam, Conddef c, int size)
     return hadf ? hads : 1;
 }
 
-#endif /* DYNAMIC */
+#endif
diff --git a/Src/params.c b/Src/params.c
index 54699476c..89a1ed6a1 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -301,14 +301,6 @@ copyparamtable(HashTable ht, char *name)
     return nht;
 }
 
-#define SCANPM_WANTVALS   (1<<0)
-#define SCANPM_WANTKEYS   (1<<1)
-#define SCANPM_WANTINDEX  (1<<2)	/* Useful only if nested arrays */
-#define SCANPM_MATCHKEY   (1<<3)
-#define SCANPM_MATCHVAL   (1<<4)
-#define SCANPM_MATCHMANY  (1<<5)
-#define SCANPM_ISVAR_AT   ((-1)<<15)	/* Only sign bit is significant */
-
 static unsigned numparamvals;
 
 /**/
@@ -641,6 +633,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] == '('))
@@ -650,6 +643,7 @@ isident(char *s)
      * definitely not a valid identifier.              */
     if (*ss != '[')
 	return 0;
+
     noeval = 1;
     (void)mathevalarg(++ss, &ss);
     if (*ss == ',')
@@ -658,6 +652,19 @@ 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;
+
+    /* Require balanced [ ] pairs */
+    if (skipparens('[', ']', &ss))
+	return 0;
+    return !*ss;
+#endif
 }
 
 static char **garr;
@@ -755,6 +762,8 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
 	if (ind) {
 	    v->isarr |= SCANPM_WANTKEYS;
 	    v->isarr &= ~SCANPM_WANTVALS;
+	} else if (rev) {
+	    v->isarr |= SCANPM_WANTVALS;
 	}
 	if (!down)
 	    v->isarr &= ~SCANPM_MATCHMANY;
@@ -788,8 +797,9 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
 		v->pm = createparam(s, PM_SCALAR|PM_UNSET);
 		paramtab = tht;
 	    }
-	    v->isarr = 0;
+	    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
@@ -979,9 +989,11 @@ getindex(char **pptr, Value v)
 	    }
 	    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)
@@ -1546,35 +1558,37 @@ setaparam(char *s, char **val)
 
 /**/
 Param
-sethparam(char *s, char **kvarr)
+sethparam(char *s, char **val)
 {
     Value v;
-    Param pm;
-    char *t;
+    char *t = s;
 
     if (!isident(s)) {
 	zerr("not an identifier: %s", s, 0);
-	freearray(kvarr);
+	freearray(val);
+	errflag = 1;
+	return NULL;
+    }
+    if (strchr(s, '[')) {
+	freearray(val);
+	zerr("attempt to set slice of associative array", NULL, 0);
 	errflag = 1;
 	return NULL;
+    } else {
+	if (!(v = getvalue(&s, 1)))
+	    createparam(t, PM_HASHED);
+	else if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) &&
+		 !(v->pm->flags & PM_SPECIAL)) {
+	    unsetparam(t);
+	    createparam(t, PM_HASHED);
+	    v = NULL;
+	}
     }
-    t=ztrdup(s); /* Is this a memory leak? */
-    /* Why does getvalue(s, 1) set s to empty string? */
-    if ((v = getvalue(&t, 1)))
-	if (v->pm->flags & PM_SPECIAL) {
-	    zerr("not overriding a special: %s", s, 0);
-	    freearray(kvarr);
-	    errflag = 1;
+    if (!v)
+	if (!(v = getvalue(&t, 1)))
 	    return NULL;
-	} else
-	    unsetparam(s);
-
-    pm = createparam(s, PM_HASHED);
-    DPUTS(!pm, "BUG: parameter not created");
-
-    arrhashsetfn(pm, kvarr);
-
-    return pm;
+    setarrvalue(v, val);
+    return v->pm;
 }
 
 /**/
diff --git a/Src/parse.c b/Src/parse.c
index 9024a834e..eb8398b1a 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -1339,7 +1339,7 @@ par_cond_double(char *a, char *b)
 
 	n->ntype = NT_SET(N_COND, NT_STR, NT_STR | NT_ARR, 0, 0);
 	n->type = COND_MOD;
-	n->left = (void *) (a + 1);
+	n->left = (void *) a;
 	d[0] = b;
 	d[1] = NULL;
 	n->right = (void *) arrdup(d);
@@ -1386,7 +1386,7 @@ par_cond_triple(char *a, char *b, char *c)
 
 	    n->ntype = NT_SET(N_COND, NT_STR, NT_STR | NT_ARR, 0, 0);
 	    n->type = COND_MODI;
-	    n->left = (void *) (b + 1);
+	    n->left = (void *) b;
 	    d[0] = a;
 	    d[1] = c;
 	    d[2] = NULL;
@@ -1397,7 +1397,7 @@ par_cond_triple(char *a, char *b, char *c)
 
 	n->ntype = NT_SET(N_COND, NT_STR, NT_STR | NT_ARR, 0, 0);
 	n->type = COND_MOD;
-	n->left = (void *) (a + 1);
+	n->left = (void *) a;
 	d[0] = b;
 	d[1] = c;
 	d[2] = NULL;
diff --git a/Src/signals.c b/Src/signals.c
index e637a8ca9..65bac0f52 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -703,6 +703,8 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
     execsave();
     breaks = 0;
     if (*sigtr & ZSIG_FUNC) {
+	int osc = sfcontext;
+
 	PERMALLOC {
 	    args = newlinklist();
 	    name = (char *) zalloc(5 + strlen(sigs[sig]));
@@ -712,7 +714,9 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
 	    addlinknode(args, num);
 	} LASTALLOC;
 	trapreturn = -1;
+	sfcontext = SFC_SIGNAL;
 	doshfunc(name, sigfn, args, 0, 1);
+	sfcontext = osc;
 	freelinklist(args, (FreeFunc) NULL);
 	zsfree(name);
     } else HEAPALLOC {
diff --git a/Src/subst.c b/Src/subst.c
index 77f0249e2..cc7e87d3d 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -720,8 +720,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 = 0;		/* > hkeys get values of associative array */
+    char hkeys = 0;
+    char hvals = 0;
 
     *s++ = '\0';
     if (!ialnum(*s) && *s != '#' && *s != Pound && *s != '-' &&
@@ -739,7 +739,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	inbrace = 1;
 	s++;
 	if (*s == '!' && s[1] != Outbrace && emulation == EMULATE_KSH) {
-	    hkeys = 1;
+	    hkeys = SCANPM_WANTKEYS;
 	    s++;
 	} else if (*s == '(' || *s == Inpar) {
 	    char *t, sav;
@@ -762,7 +762,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		case Outpar:
 		    break;
 		case 'A':
-		    arrasg = 1;
+		    ++arrasg;
 		    break;
 		case '@':
 		    nojoin = 1;
@@ -897,10 +897,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		    break;
 
 		case 'k':
-		    hkeys = 1;
+		    hkeys = SCANPM_WANTKEYS;
 		    break;
 		case 'v':
-		    hvals = 2;
+		    hvals = SCANPM_WANTVALS;
 		    break;
 
 		default:
@@ -979,9 +979,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	*s = sav;
 	v = (Value) NULL;
     } else {
-	/* 2 == SCANPM_WANTKEYS, 1 == SCANPM_WANTVALS, see params.c */
 	if (!(v = fetchvalue(&s, (unset(KSHARRAYS) || inbrace) ? 1 : -1,
-			     (hkeys ? 2 : 0) + ((hvals > hkeys) ? 1 : 0))))
+			     hkeys|hvals)))
 	    vunset = 1;
     }
     while (v || ((inbrace || (unset(KSHARRAYS) && vunset)) && isbrack(*s))) {
@@ -1010,7 +1009,11 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	if ((isarr = v->isarr)) {
 	    /* No way to get here with v->inv != 0, so getvaluearr() *
 	     * is called by getarrvalue(); needn't test PM_HASHED.   */
-	    aval = getarrvalue(v);
+	    if (v->isarr == SCANPM_WANTINDEX) {
+		isarr = v->isarr = 0;
+		val = dupstring(v->pm->nam);
+	    } else
+		aval = getarrvalue(v);
 	} else {
 	    if (v->pm->flags & PM_ARRAY) {
 		int tmplen = arrlen(v->pm->gets.afn(v->pm));
@@ -1260,7 +1263,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 			*p++ = ztrdup(*t++);
 		    }
 		    *p++ = NULL;
-		    setaparam(idbeg, a);
+		    if (arrasg > 1) {
+			Param pm = sethparam(idbeg, a);
+			if (pm)
+			    aval = paramvalarr(pm->gets.hfn(pm), hkeys|hvals);
+		    } else
+			setaparam(idbeg, a);
 		} else {
 		    untokenize(val);
 		    setsparam(idbeg, ztrdup(val));
diff --git a/Src/utils.c b/Src/utils.c
index af0247ebf..90da15368 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -427,7 +427,7 @@ get_username(void)
 	    cached_username = ztrdup("");
     }
 #else /* !HAVE_GETPWUID */
-    cached_uid = current_uid;
+    cached_uid = getuid();
 #endif /* !HAVE_GETPWUID */
     return cached_username;
 }
@@ -510,16 +510,13 @@ adduserdir(char *s, char *t, int flags, int always)
     if ((flags & ND_USERNAME) && nameddirtab->getnode2(nameddirtab, s))
 	return;
 
-    /* Never hash PWD unless it was explicitly requested */
-    if (!always && !strcmp(s, "PWD"))
-	return;
-
     /* Normal parameter assignments generate calls to this function, *
      * with always==0.  Unless the AUTO_NAME_DIRS option is set, we  *
      * don't let such assignments actually create directory names.   *
      * Instead, a reference to the parameter as a directory name can *
-     * cause the actual creation of the hash table entry.            */
-    if (!always && unset(AUTONAMEDIRS) &&
+     * cause the actual creation of the hash table entry. Never hash *
+     * PWD unless it was explicitly requested (or already hashed).   */
+    if (!always && (unset(AUTONAMEDIRS) || !strcmp(s, "PWD")) &&
 	    !nameddirtab->getnode2(nameddirtab, s))
 	return;
 
@@ -633,8 +630,13 @@ preprompt(void)
 
     /* If a shell function named "precmd" exists, *
      * then execute it.                           */
-    if ((list = getshfunc("precmd")) != &dummy_list)
+    if ((list = getshfunc("precmd")) != &dummy_list) {
+	int osc = sfcontext;
+
+	sfcontext = SFC_HOOK;
 	doshfunc("precmd", list, NULL, 0, 1);
+	sfcontext = osc;
+    }
     if (errflag)
 	return;
 
@@ -643,7 +645,11 @@ preprompt(void)
      * executed "periodic", then execute it now.                    */
     if (period && (time(NULL) > lastperiodic + period) &&
 	(list = getshfunc("periodic")) != &dummy_list) {
+	int osc = sfcontext;
+
+	sfcontext = SFC_HOOK;
 	doshfunc("periodic", list, NULL, 0, 1);
+	sfcontext = osc;
 	lastperiodic = time(NULL);
     }
     if (errflag)
diff --git a/Src/zsh.export b/Src/zsh.export
index c51699269..d8b0ddf19 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -23,6 +23,8 @@ closem
 cmdnamtab
 columns
 compctlreadptr
+cond_str
+cond_val
 coprocin
 coprocout
 countlinknodes
@@ -173,6 +175,7 @@ sethparam
 setlimits
 setsparam
 settyinfo
+sfcontext
 shfunctab
 shingetline
 shout
diff --git a/Src/zsh.h b/Src/zsh.h
index 6a962d8bf..a31a7469b 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -460,23 +460,24 @@ struct cond {
 #define COND_MOD   16
 #define COND_MODI  17
 
-typedef int (*CondHandler) _((Conddef, char **));
+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          */
-    CondHandler handler;	/* handler function                   */
+    int condid;			/* for overloading handler functions  */
     char *module;		/* module to autoload                 */
 };
 
 #define CONDF_INFIX  1
 #define CONDF_ADDED  2
 
-#define CONDDEF(name, flags, min, max, handler) \
-    { NULL, name, flags, min, max, handler, NULL }
+#define CONDDEF(name, flags, handler, min, max, condid) \
+    { NULL, name, flags, handler, min, max, condid, NULL }
 
 struct forcmd {			/* for/select */
 /* Cmd->args contains list of words to loop thru */
@@ -772,6 +773,14 @@ struct shfunc {
     List funcdef;		/* function definition    */
 };
 
+/* Shell function context types. */
+
+#define SFC_DIRECT   0		/* called directly from the user */
+#define SFC_SIGNAL   1		/* signal handler */
+#define SFC_HOOK     2		/* one of the special functions */
+#define SFC_WIDGET   3		/* user defined widget */
+#define SFC_COMPLETE 4		/* called from completion code */
+
 /* node in list of function call wrappers */
 
 typedef int (*WrapFunc) _((List, FuncWrap, char *));
@@ -781,13 +790,12 @@ struct funcwrap {
     int flags;
     WrapFunc handler;
     Module module;
-    int count;
 };
 
 #define WRAPF_ADDED 1
 
 #define WRAPDEF(func) \
-    { NULL, 0, func, NULL, 0 }
+    { NULL, 0, func, NULL }
 
 /* node in builtin command hash table (builtintab) */
 
@@ -836,11 +844,11 @@ struct module {
     int flags;
     void *handle;
     LinkList deps;
+    int wrapper;
 };
 
 #define MOD_BUSY    (1<<0)
-#define MOD_WRAPPER (1<<1)
-#define MOD_UNLOAD  (1<<2)
+#define MOD_UNLOAD  (1<<1)
 
 /* node used in parameter hash table (paramtab) */
 
@@ -890,28 +898,37 @@ 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_HASHED	(1<<2)	/* association                                */
 
 #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 */
-#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<<3)	/* left justify and remove leading blanks     */
+#define PM_RIGHT_B	(1<<4)	/* right justify and fill with leading blanks */
+#define PM_RIGHT_Z	(1<<5)	/* right justify and fill with leading zeros  */
+#define PM_LOWER	(1<<6)	/* 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<<7)	/* all upper case                             */
+#define PM_UNDEFINED	(1<<7)	/* undefined (autoloaded) shell function      */
+
+#define PM_READONLY	(1<<8)	/* readonly                                   */
+#define PM_TAGGED	(1<<9)	/* tagged                                     */
+#define PM_EXPORTED	(1<<10)	/* exported                                   */
+#define PM_UNIQUE	(1<<11)	/* remove duplicates                          */
+#define PM_SPECIAL	(1<<12) /* special builtin parameter                  */
+#define PM_DONTIMPORT	(1<<13)	/* do not import this variable                */
+#define PM_RESTRICTED	(1<<14) /* cannot be changed in restricted mode       */
+#define PM_UNSET	(1<<15)	/* has null value                             */
+
+/* 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_ISVAR_AT   ((-1)<<15)	/* Only sign bit is significant */
 
 /*
  * Flags for doing matches inside parameter substitutions, i.e.
diff --git a/Util/zsh-development-guide b/Util/zsh-development-guide
index d574d8af0..73ac120ab 100644
--- a/Util/zsh-development-guide
+++ b/Util/zsh-development-guide
@@ -112,6 +112,297 @@ C coding style
   groups of statements in the interests of clarity.  There should never
   be two consecutive blank lines.
 
+Modules
+-------
+
+Modules are described by a file named `foo.mdd' for a module
+`foo'. This file is actually a shell script that will sourced when zsh 
+is build. To describe the module it can/should set the following shell 
+variables:
+
+  - moddeps       modules on which this module depends (default none)
+  - nozshdep      non-empty indicates no dependence on the `zsh' pseudo-module
+  - alwayslink    if non-empty, always link the module into the executable
+  - autobins      builtins defined by the module, for autoloading
+  - objects       .o files making up this module (*must* be defined)
+  - proto         .pro files for this module (default generated from $objects)
+  - headers       extra headers for this module (default none)
+  - hdrdeps       extra headers on which the .mdh depends (default none)
+  - otherincs     extra headers that are included indirectly (default none)
+
+Be sure to put the values in quotes. For further enlightenment have a
+look at the `mkmakemod.sh' script in the Src directory of the
+distribution.
+
+Modules have to define four functions which will automatically called
+by the zsh core. The first one, named `setup_foo' for a module named
+`foo', should set up any data needed in the module, at least any data
+other modules may be interested in. The second one, named `boot_foo',
+should register all builtins, conditional codes, and function wrappers
+(i.e. anything that will be visible to the user) and will be called
+after the `setup'-function. 
+The third one, named `cleanup_foo' for module `foo' is called when the
+user tries to unload a module and should de-register the builtins
+etc. The last function, `finish_foo' is called when the module is
+actually unloaded and should finalize all the data initialized in the 
+`setup'-function. Since the last two functions are only executed when
+the module is used as an dynamically loaded module you can surround
+it with `#ifdef MODULE' and `#endif'.
+In short, the `cleanup'-function should undo what the `boot'-function
+did, and the `finish'-function should undo what the `setup'-function
+did.
+All of these functions should return zero if they succeeded and
+non-zero otherwise.
+
+Builtins are described in a table, for example:
+
+  static struct builtin bintab[] = {
+    BUILTIN("example", 0, bin_example, 0, -1, 0, "flags", NULL),
+  };
+
+Here `BUILTIN(...)' is a macro that simplifies the description. Its
+arguments are:
+  - the name of the builtin as a string
+  - optional flags (see BINF_* in zsh.h)
+  - the C-function implementing the builtin
+  - the minimum number of arguments the builtin needs
+  - the maximum number of arguments the builtin can handle or -1 if
+    the builtin can get any number of arguments
+  - an integer that is passed to the handler function and can be used
+    to distinguish builtins if the same C-function is used to
+    implement multiple builtins
+  - the options the builtin accepts, given as a string containing the
+    option characters (the above example makes the builtin accept the
+    options `f', `l', `a', `g', and `s')
+  - and finally a optional string containing option characters that
+    will always be reported as set when calling the C-function (this,
+    too, can be used when using one C-function to implement multiple
+    builtins)
+
+The definition of the handler function looks like:
+
+  /**/
+  static int
+  bin_example(char *nam, char **args, char *ops, int func)
+  {
+    ...
+  }
+
+The special comment /**/ is used by the zsh Makefile to generate the
+`*.pro' files. The arguments of the function are the number under
+which this function was invoked (the name of the builtin, but for
+functions that implement more than one builtin this information is
+needed). The second argument is the array of arguments *excluding* the 
+options that were defined in the struct and which are handled by the
+calling code. These options are given as the third argument. It is an
+array of 256 characters in which the n'th element is non-zero if the
+option with ASCII-value n was set (i.e. you can easily test if an
+option was used by `if (ops['f'])' etc.). The last argument is the
+integer value from the table (the sixth argument to `BUILTIN(...)').
+The integer return value by the function is the value returned by the
+builtin in shell level.
+
+To register builtins in zsh and thereby making them visible to the
+user the function `addbuiltins()' is used:
+
+  /**/
+  int
+  boot_example(Module m)
+  {
+    int ret;
+
+    ret = addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    ...
+  }
+
+The arguments are the name of the module (taken from the argument in
+the example), the table of definitions and the number of entries in
+this table.
+The return value is 1 if everything went fine, 2 if at least one
+builtin couldn't be defined, and 0 if none of the builtin could be
+defined.
+
+To de-register builtins use the function `deletebuiltins()':
+
+  /**/
+  int
+  cleanup_example(Module m)
+  {
+    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    ...
+  }
+
+The arguments and the return value are the same as for `addbuiltins()'
+
+The definition of condition codes in modules is equally simple. First
+we need a table with the descriptions:
+
+  static struct conddef cotab[] = {
+    CONDDEF("len", 0, cond_p_len, 1, 2, 0),
+    CONDDEF("ex", CONDF_INFIX, cond_i_ex, 0, 0, 0),
+  };
+
+Again a macro is used, with the following arguments:
+
+  - the name of the condition code without the leading hyphen
+    (i.e. the example makes the condition codes `-len' and `-ex'
+    usable in `[[...]]' constructs)
+  - an optional flag which for now can only be CONDF_INFIX; if this is 
+    given, an infix operator is created (i.e. the above makes
+    `[[ -len str ]]' and `[[ s1 -ex s2 ]]' available)
+  - the C-function implementing the conditional
+  - for non-infix condition codes the next two arguments give the
+    minimum and maximum number of string the conditional can handle
+    (i.e. `-len' can get one or two strings); as with builtins giving
+    -1 as the maximum number means that the conditional accepts any
+    number of strings
+  - finally as the last argument an integer that is passed to the
+    handler function that can be used to distinguish different
+    condition codes if the same C-function implements more than one of 
+    them
+
+The definition for the function looks like:
+
+  /**/
+  static int
+  cond_p_len(char **a, int id)
+  {
+    ...
+  }
+
+The first argument is an array containing the strings (NULL-terminated
+like the array of arguments for builtins), the second argument is the
+integer value stored in the table (the last argument to `CONDDEF(...)').
+The value returned by the function should be non-zero if the condition 
+is true and zero otherwise.
+
+Note that no preprocessing is done on the strings. This means that
+no substitutions are performed on them and that they will be
+tokenized. There are three helper functions available:
+
+  - char *cond_str(args, num)
+    The first argument is the array of strings the handler function
+    got as an argument and the second one is an index into this array.
+    The return value is the num'th string from the array with
+    substitutions performed and untokenized.
+  - long cond_val(args, num)
+    The arguments are the same as for cond_str(). The return value is
+    the result of the mathematical evaluation of the num'th string
+    form the array.
+  - int cond_match(args, num, str)
+    Again, the first two arguments are the same as for the other
+    functions. The third argument is any string. The result of the
+    function is non-zero if the the num'th string from the array taken 
+    as a glob pattern matches the given string.
+
+Registering and de-resgitering condition codes with the shell is
+almost exactly the same as for builtins, using the functions
+`addconddefs()' and `deleteconddefs()' instead:
+
+  /**/
+  int
+  boot_example(Module m)
+  {
+    int ret;
+
+    ret = addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
+    ...
+  }
+
+  /**/
+  int
+  cleanup_example(Module m)
+  {
+    deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
+    ...
+  }
+
+Arguments and return values are the same as for the functions for
+builtins.
+
+Finally, modules can define wrapper functions. These functions are
+called whenever a shell function is to be executed.
+
+The definition is simple:
+
+  static struct funcwrap wrapper[] = {
+    WRAPDEF(ex_wrapper),
+  };
+
+The macro `WRAPDEF(...)' gets the C-function as its only argument.
+This function should be defined like:
+
+  /**/
+  static int
+  ex_wrapper(List list, FuncWrap w, char *name)
+  {
+    ...
+    runshfunc(list, w, name);
+    ...
+    return 0;
+  }
+
+The first two arguments should only be used to pass them to
+`runshfunc()' which will execute the shell function. The last argument 
+is the name of the function to be executed. The arguments passed to
+the function can be accessed vie the global variable `pparams' (a
+NULL-terminated array of strings).
+The return value of the wrapper function should be zero if it calls
+`runshfunc()' itself and non-zero otherwise. This can be used for
+wrapper functions that only need to run under certain conditions or
+that don't need to clean anything up after the shell function has
+finished:
+
+  /**/
+  static int
+  ex_wrapper(List list, FuncWrap w, char *name)
+  {
+    if (wrapper_need_to_run) {
+      ...
+      runshfunc(list, w, name);
+      ...
+      return 0;
+    }
+    return 1;
+  }
+
+Inside these wrapper functions the global variable `sfcontext' will be 
+set to a vlue indicating the circumstances under which the shell
+function was called. It can have any of the following values:
+
+  - SFC_DIRECT:   the function was invoked directly by the user
+  - SFC_SIGNAL:   the function was invoked as a signal handler
+  - SFC_HOOK:     the function was automatically invoked as one of the
+                  special functions known by the shell (like `chpwd')
+  - SFC_WIDGET:   the function was called from the zsh line editor as a
+                  user-defined widget
+  - SFC_COMPLETE: the function was called from the completion code
+                  (e.g. with `compctl -K func')
+
+If a module invokes a shell function (e.g. as a hook function), the
+value of this variable should only be changed temporarily and restored
+to its previous value after the shell function has finished.
+
+There is a problem when the user tries to unload a module that has
+defined wrappers from a shell function. In this case the module can't
+be unloaded immediately since the wrapper function is still on the
+call stack. The zsh code delays unloading modules until all wrappers
+from them have finished. To hide this from the user, the module's
+cleanup function is run immediatly so that all builtins, condition
+codes, and wrapper function defined by the module are
+de-registered. But if there is some module-global state that has to be 
+finalized (e.g. some memory that has to be freed) and that is used by
+the wrapper functions finalizing this data in the cleanup function
+won't work.
+This is why ther are two functions each for the initialization and
+finalization of modules. The `boot'- and `cleanup'-functions are run
+whenever the user calls `zmodload' or `zmodload -u' and should only
+register or de-register the module's interface that is visible to the
+user. Anything else should be done in the `setup'- and
+`finish'-functions. Otherwise modules that other modules depend upon
+may destroy their state too early and wrapper functions in the latter
+modules may stop working since the state they use is already destroyed.
+
 Documentation
 -------------
 
diff --git a/acconfig.h b/acconfig.h
index 417e651a2..77110b457 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -222,3 +222,6 @@
 
 /* Define to 1 if you want to use dynamically loaded modules on AIX */
 #undef AIXDYNAMIC
+
+/* Define to 1 if you want to use dynamically loaded modules on HPUX 10 */
+#undef HPUXDYNAMIC
diff --git a/aczsh.m4 b/aczsh.m4
index 8d86a4817..dfc40ce6f 100644
--- a/aczsh.m4
+++ b/aczsh.m4
@@ -56,6 +56,18 @@ $DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&5 2>&5 &&
 $CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&5 2>&5 &&
 $DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&5 2>&5; then
     AC_TRY_RUN([
+#ifdef HPUXDYNAMIC
+#include <dl.h>
+#define RTLD_LAZY BIND_DEFERRED
+#define RTLD_GLOBAL DYNAMIC_PATH
+
+char *zsh_gl_sym_addr ;
+
+#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0)
+#define dlclose(handle) shl_unload((shl_t)(handle))
+#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr)
+#define dlerror() 0
+#else
 #ifdef HAVE_DLFCN_H
 #include <dlfcn.h>
 #else
@@ -63,6 +75,7 @@ $DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&5 2>&5; then
 #include <nlist.h>
 #include <link.h>
 #endif
+#endif
 #ifndef RTLD_LAZY
 #define RTLD_LAZY 1
 #endif
@@ -116,6 +129,18 @@ $DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&5 2>&5 &&
 $CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&5 2>&5 &&
 $DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&5 2>&5; then
     AC_TRY_RUN([
+#ifdef HPUXDYNAMIC
+#include <dl.h>
+#define RTLD_LAZY BIND_DEFERRED
+#define RTLD_GLOBAL DYNAMIC_PATH
+
+char *zsh_gl_sym_addr ;
+
+#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0)
+#define dlclose(handle) shl_unload((shl_t)(handle))
+#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr)
+#define dlerror() 0
+#else
 #ifdef HAVE_DLFCN_H
 #include <dlfcn.h>
 #else
@@ -123,6 +148,7 @@ $DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&5 2>&5; then
 #include <nlist.h>
 #include <link.h>
 #endif
+#endif
 #ifndef RTLD_LAZY
 #define RTLD_LAZY 1
 #endif
@@ -130,6 +156,7 @@ $DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&5 2>&5; then
 #define RTLD_GLOBAL 0
 #endif
 
+
 main()
 {
     void *handle1, *handle2;
@@ -177,6 +204,18 @@ $DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&5 2>&5 &&
 $CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&5 2>&5 &&
 $DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&5 2>&5; then
     AC_TRY_RUN([
+#ifdef HPUXDYNAMIC
+#include <dl.h>
+#define RTLD_LAZY BIND_DEFERRED
+#define RTLD_GLOBAL DYNAMIC_PATH
+
+char *zsh_gl_sym_addr ;
+
+#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0)
+#define dlclose(handle) shl_unload((shl_t)(handle))
+#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr)
+#define dlerror() 0
+#else
 #ifdef HAVE_DLFCN_H
 #include <dlfcn.h>
 #else
@@ -184,6 +223,7 @@ $DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&5 2>&5; then
 #include <nlist.h>
 #include <link.h>
 #endif
+#endif
 #ifndef RTLD_LAZY
 #define RTLD_LAZY 1
 #endif
@@ -234,6 +274,18 @@ $DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&5 2>&5; then
     save_ldflags=$LDFLAGS
     LDFLAGS="$LDFLAGS $EXTRA_LDFLAGS"
     AC_TRY_RUN([
+#ifdef HPUXDYNAMIC
+#include <dl.h>
+#define RTLD_LAZY BIND_DEFERRED
+#define RTLD_GLOBAL DYNAMIC_PATH
+
+char *zsh_gl_sym_addr ;
+
+#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0)
+#define dlclose(handle) shl_unload((shl_t)(handle))
+#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr)
+#define dlerror() 0
+#else
 #ifdef HAVE_DLFCN_H
 #include <dlfcn.h>
 #else
@@ -241,6 +293,7 @@ $DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS conftest1.o $LIBS 1>&5 2>&5; then
 #include <nlist.h>
 #include <link.h>
 #endif
+#endif
 #ifndef RTLD_LAZY
 #define RTLD_LAZY 1
 #endif
@@ -294,6 +347,18 @@ elif
     save_ldflags=$LDFLAGS
     LDFLAGS="$LDFLAGS $EXTRA_LDFLAGS -s"
     AC_TRY_RUN([
+#ifdef HPUXDYNAMIC
+#include <dl.h>
+#define RTLD_LAZY BIND_DEFERRED
+#define RTLD_GLOBAL DYNAMIC_PATH
+
+char *zsh_gl_sym_addr ;
+
+#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0)
+#define dlclose(handle) shl_unload((shl_t)(handle))
+#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr)
+#define dlerror() 0
+#else
 #ifdef HAVE_DLFCN_H
 #include <dlfcn.h>
 #else
@@ -301,6 +366,7 @@ elif
 #include <nlist.h>
 #include <link.h>
 #endif
+#endif
 #ifndef RTLD_LAZY
 #define RTLD_LAZY 1
 #endif
@@ -348,6 +414,18 @@ echo 'int fred () { return 42; }' > conftest1.c
 if $CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&5 2>&5 &&
 $DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS -s conftest1.o $LIBS 1>&5 2>&5; then
     AC_TRY_RUN([
+#ifdef HPUXDYNAMIC
+#include <dl.h>
+#define RTLD_LAZY BIND_DEFERRED
+#define RTLD_GLOBAL DYNAMIC_PATH
+
+char *zsh_gl_sym_addr ;
+
+#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0)
+#define dlclose(handle) shl_unload((shl_t)(handle))
+#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr)
+#define dlerror() 0
+#else
 #ifdef HAVE_DLFCN_H
 #include <dlfcn.h>
 #else
@@ -355,6 +433,7 @@ $DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS -s conftest1.o $LIBS 1>&5 2>&5; t
 #include <nlist.h>
 #include <link.h>
 #endif
+#endif
 #ifndef RTLD_LAZY
 #define RTLD_LAZY 1
 #endif
@@ -389,7 +468,7 @@ dnl
 
 AC_DEFUN(zsh_PATH_UTMP,
 [AC_CACHE_CHECK([for $1 file], [zsh_cv_path_$1],
-[for dir in /etc /usr/etc /var/adm /usr/adm /var/run ./conftest; do
+[for dir in /etc /usr/etc /var/adm /usr/adm /var/run /var/log ./conftest; do
   zsh_cv_path_$1=${dir}/$1
   test -f $zsh_cv_path_$1 && break
   zsh_cv_path_$1=no
diff --git a/configure.in b/configure.in
index 6985f24e4..4fc45fe7c 100644
--- a/configure.in
+++ b/configure.in
@@ -45,6 +45,9 @@ AC_DEFINE_UNQUOTED(OSTYPE,   "$host_os")
 dnl -----------------------------
 dnl CHECKING COMMAND LINE OPTIONS
 dnl -----------------------------
+dnl Handle --program-prefix, --program-suffix, etc.
+AC_ARG_PROGRAM
+
 dnl Do you want to debug zsh?
 undefine([zsh-debug])dnl
 AC_ARG_ENABLE(zsh-debug,
@@ -211,7 +214,7 @@ if test -n "$auto_cflags"; then
 fi
 if test -n "$auto_ldflags"; then
   case "${enable_zsh_debug}$host_os" in
-    yesaix*) ;;  # AIX ld does not accept -g
+    yesaix*|yeshpux*) ;;  # AIX ld does not accept -g
     yes*)    LDFLAGS=-g ;;
     *)       LDFLAGS=-s ;;
   esac
@@ -340,6 +343,7 @@ AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \
 		 utmp.h utmpx.h sys/types.h pwd.h grp.h)
 if test $dynamic = yes; then
   AC_CHECK_HEADERS(dlfcn.h)
+  AC_CHECK_HEADERS(dl.h)
 fi
 
 dnl Some SCO systems cannot include both sys/time.h and sys/select.h
@@ -619,7 +623,8 @@ AC_CHECK_FUNCS(memcpy memmove \
 	      getgrgid getgrnam getpwent getpwnam getpwuid)
 
 if test $dynamic = yes; then
-  AC_CHECK_FUNCS(dlopen dlerror dlsym dlclose load loadquery loadbind unload)
+  AC_CHECK_FUNCS(dlopen dlerror dlsym dlclose load loadquery loadbind unload \
+		shl_load shl_unload shl_findsym)
 fi
 
 
@@ -902,6 +907,7 @@ dnl dynamic loading
 dnl ---------------
 L=N
 aixdynamic=no
+hpuxdynamic=no
 if test "$ac_cv_func_dlopen"  != yes ||
    test "$ac_cv_func_dlsym"   != yes ||
    test "$ac_cv_func_dlerror" != yes; then
@@ -909,7 +915,19 @@ if test "$ac_cv_func_dlopen"  != yes ||
      test "$ac_cv_func_unload"    != yes ||
      test "$ac_cv_func_loadbind"  != yes ||
      test "$ac_cv_func_loadquery" != yes; then
-    dynamic=no
+    if test "$ac_cv_func_shl_load" != yes ||
+       test "$ac_cv_func_shl_unload" != yes ||
+       test "$ac_cv_func_shl_findsym" != yes; then
+      dynamic=no
+    elif test "x$dynamic" = xyes; then
+      hpuxdynamic=yes
+      DL_EXT="${DL_EXT=sl}"
+      dnl autoheader won't allow us to define anything which isn't
+      dnl going into a header, and we can't undefine anything, so
+      dnl just define this anyway and rely on the later tests to
+      dnl define DYNAMIC or not.
+      AC_DEFINE(HPUXDYNAMIC)dnl
+    fi
   elif test "x$dynamic" = xyes; then
     aixdynamic=yes
   fi
@@ -990,6 +1008,18 @@ EOM
     $DLLD -o conftest.$DL_EXT $LDFLAGS $DLLDFLAGS conftest.o 1>&5 2>&5 &&
     AC_TRY_RUN([
 #include <stdio.h>
+#ifdef HPUXDYNAMIC
+#include <dl.h>
+#define RTLD_LAZY BIND_DEFERRED
+#define RTLD_GLOBAL DYNAMIC_PATH
+
+char *zsh_gl_sym_addr ;
+
+#define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0)
+#define dlclose(handle) shl_unload((shl_t)(handle))
+#define dlsym(handle,name) (zsh_gl_sym_addr=0,shl_findsym((shl_t *)&(handle),name,TYPE_UNDEFINED,&zsh_gl_sym_addr), (void *)zsh_gl_sym_addr)
+#define dlerror() 0
+#else
 #ifdef HAVE_DLFCN_H
 #include <dlfcn.h>
 #else
@@ -997,6 +1027,7 @@ EOM
 #include <nlist.h>
 #include <link.h>
 #endif
+#endif
 #ifndef RTLD_LAZY
 #define RTLD_LAZY 1
 #endif
diff --git a/patchlist.txt b/patchlist.txt
index 923919098..2179665ca 100644
--- a/patchlist.txt
+++ b/patchlist.txt
@@ -169,3 +169,47 @@ Bart's associative array patches for implentation of subscripting flags,
 4763, plus fix 4766; typeset output 4764
 
 Sven's completion listing fix, 4767
+
+  Fourth edition
+
+Compilation fix for static linking, 4779
+
+Phil's patch for wtmp in /var/log on Linux, which someone else sent
+before... except it needs to be applied to aczsh.m4 and propagated
+from there, 4783
+
+Phil's removal of now useless j in glob.c, 4784
+
+Bart's collection in 4788:  put back some missing patches.
+
+Param's patches from Bart in 4789, 4794, 4795: fix sethparam() and move
+flags; make sure setsparam() and sethparam() are consistent with
+existing parameters; allow assoc array assignment with
+${(AA)=assoc::=key1 value1 key2 value2}
+
+Return to not hashing PWD from Bart in 4791
+
+Handle --program-suffix and --program-prefix (but not --target, so I
+removed the comment) from Bart in 4792
+
+Compilation with no HAVE_GETPWUID, 4801
+
+INADDR_NONE in zftp, 4805
+
+Sven's unloading modules, 4806, 4815, 4820, plus my AIX (and
+DYNAMIC_NAME_CLASH_OK) fix, 4822, then Sven's 4830
+
+Parameter's documentation changes by Bart, 4817
+
+Network order fix for zftp from Sven, 4821
+
+My patch (with Gene Cohler's suggestions) for dynamical loading under
+HPUX 10, 4824, plus fixes, 4833, 4843
+
+Bart's random assoc array fixes, 4826, 4836, plus Sven's 4831
+
+Sven's ignored character fix, 4828
+
+More Sven condition patches, 4837, 4842
+
+Final (???) isident() fix from Sven, 4845