mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-09-25 05:31:19 +02:00
zsh-3.1.5-pws-3
This commit is contained in:
parent
f13624e0f8
commit
9003d99d16
41 changed files with 5315 additions and 427 deletions
|
@ -27,5 +27,5 @@
|
|||
# This must also serve as a shell script, so do not add spaces around the
|
||||
# `=' signs.
|
||||
|
||||
VERSION=3.1.5
|
||||
VERSION_DATE='October 29, 1998'
|
||||
VERSION=3.1.5-pws-3
|
||||
VERSION_DATE='December 12, 1998'
|
||||
|
|
|
@ -512,7 +512,7 @@ sitem([var(mm)tt(:)]var(ss))(minutes and seconds)
|
|||
endsitem()
|
||||
)
|
||||
findex(local)
|
||||
item(tt(local) [ {tt(PLUS())|tt(-)}tt(LRZilrtu) [var(n)]] [ var(name)[tt(=)var(value)] ] ...)(
|
||||
item(tt(local) [ {tt(PLUS())|tt(-)}tt(ALRUZailrtu) [var(n)]] [ var(name)[tt(=)var(value)] ] ...)(
|
||||
Same as tt(typeset), except that the options tt(-x) and
|
||||
tt(-f) are not permitted.
|
||||
)
|
||||
|
@ -878,7 +878,7 @@ Equivalent to tt(whence -v).
|
|||
findex(typeset)
|
||||
cindex(parameters, setting)
|
||||
cindex(parameters, declaring)
|
||||
item(tt(typeset) [ {tt(PLUS())|tt(-)}tt(LRUZfilrtuxm) [var(n)]] [ var(name)[tt(=)var(value)] ... ])(
|
||||
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
|
||||
|
@ -887,6 +887,9 @@ exported provided no parameter of that name already exists.
|
|||
The following attributes are valid:
|
||||
|
||||
startitem()
|
||||
item(tt(-A))(
|
||||
Declare var(name) to be an em(A)ssociation parameter (also known as a hash).
|
||||
)
|
||||
item(tt(-L))(
|
||||
Left justify and remove leading blanks from var(value).
|
||||
If var(n) is nonzero, it defines the width of the field;
|
||||
|
@ -916,6 +919,10 @@ 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.
|
||||
)
|
||||
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.
|
||||
)
|
||||
item(tt(-f))(
|
||||
The names refer to functions rather than parameters. No assignments
|
||||
can be made, and the only other valid flags are tt(-t)
|
||||
|
@ -1115,7 +1122,10 @@ xitem(tt(zmodload) tt(-u) [ tt(-i) ] var(name) ...)
|
|||
xitem(tt(zmodload) tt(-d) [ tt(-L) ] [ var(name) [ var(dep) ... ] ])
|
||||
xitem(tt(zmodload) tt(-du) var(name) [ var(dep) ... ])
|
||||
xitem(tt(zmodload) tt(-a) [ tt(-iL) ] [ var(name) [ var(builtin) ... ] ])
|
||||
item(tt(zmodload) tt(-au) [ tt(-i) ] var(builtin) ...)(
|
||||
xitem(tt(zmodload) tt(-au) [ tt(-i) ] var(builtin) ...)
|
||||
xitem(tt(zmodload) tt(-c) [ tt(-iI) ] [ var(name) [ var(cond) ... ] ])
|
||||
xitem(tt(zmodload) tt(-cu) [ tt(-iI) ] var(cond) ...)
|
||||
item(tt(zmodload) tt(-c) [ tt(-IL) ])(
|
||||
tt(zmodload) performs operations relating to zsh's loadable modules.
|
||||
This feature is not available on all operating systems,
|
||||
or on all installations on a particular operating system.
|
||||
|
@ -1182,5 +1192,14 @@ together with the tt(-u) option it removes builtins defined with
|
|||
tt(zmodload -a). This is only possible if the builtin is not yet
|
||||
loaded. tt(-i) suppresses the error if the builtin is already
|
||||
removed (or never existed).
|
||||
|
||||
The tt(-c) option is used to define autoloaded condition codes. The
|
||||
var(cond) strings give the names of the conditions defined by the
|
||||
module. The optional tt(-I) option is used to define infix condition
|
||||
names. Without this option prefix condition names are defined.
|
||||
|
||||
Together with the tt(-u) option definitions for autoloaded conditions
|
||||
are removed. If given no condition names all defined names are listed
|
||||
(as a series of tt(zmodload) commands if the tt(-L) option is given).
|
||||
)
|
||||
enditem()
|
||||
|
|
|
@ -312,8 +312,10 @@ noderef(Parameters)
|
|||
for a description of parameters.
|
||||
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). In addition to the following
|
||||
operations, the file modifiers described in
|
||||
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
|
||||
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).
|
||||
|
@ -398,6 +400,37 @@ is used, matching is performed on each array elements separately, and
|
|||
the matched 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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}))
|
||||
|
||||
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
|
||||
match for tt(t*e) is substituted and the result is `tt(spy star)',
|
||||
while in the second case, the shortest matches are taken and the
|
||||
result is `tt(spy spy lispy star)'.
|
||||
)
|
||||
item(tt(${#)var(spec)tt(}))(
|
||||
If var(spec) is one of the above substitutions, substitute
|
||||
the length in characters of the result instead of
|
||||
|
@ -549,10 +582,21 @@ for `tt(ps:\n:)'.
|
|||
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.
|
||||
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
|
||||
tt(${)...tt(//)...tt(}), specifies that the shortest instead of the
|
||||
longest match should be replaced.
|
||||
)
|
||||
item(tt(I:)var(expr)tt(:))(
|
||||
Search the var(expr)th match (where var(expr) evaluates to a number).
|
||||
This only applies when searching for substrings, either with the tt(S)
|
||||
flag, or with tt(${)...tt(/)...tt(}) (only the var(expr)th match is
|
||||
substituted) or tt(${)...tt(//)...tt(}) (all matches from the
|
||||
var(expr)th on are substituted). The var(expr)th match is counted
|
||||
such that there is either one or zero matches from each starting
|
||||
position in the string, although for global subsitution matches
|
||||
overlapping previous replacements are ignored.
|
||||
)
|
||||
item(tt(M))(
|
||||
Include the matched portion in the result.
|
||||
|
|
|
@ -114,6 +114,7 @@ menu(The example Module)
|
|||
menu(The files Module)
|
||||
menu(The sched Module)
|
||||
menu(The stat Module)
|
||||
menu(The zftp Module)
|
||||
menu(The zle Module)
|
||||
endmenu()
|
||||
texinode(The Z Shell Guide)(Introduction)(Top)(Top)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
texinode(The stat Module)(The zle Module)(The sched Module)(Zsh Modules)
|
||||
texinode(The stat Module)(The zftp Module)(The sched Module)(Zsh Modules)
|
||||
sect(The stat Module)
|
||||
The tt(stat) module makes available one builtin command:
|
||||
|
||||
|
@ -6,7 +6,8 @@ startitem()
|
|||
findex(stat)
|
||||
cindex(files, listing)
|
||||
cindex(files, examining)
|
||||
item(tt(stat) [ tt(-gnNlLtTrs) ] [ tt(-f) var(fd) ] [ tt(-A) var(array) ] \
|
||||
item(tt(stat) [ tt(-gnNlLtTrs) ] [ tt(-f) var(fd) ] \
|
||||
[ tt(-H) var(hash) ] [ tt(-A) var(array) ] \
|
||||
[ tt(-F) var(fmt) ] [ tt(PLUS())var(element) ] [ var(file) ... ])(
|
||||
The command acts as a front end to the tt(stat) system call (see
|
||||
manref(stat)(2)).
|
||||
|
@ -91,6 +92,11 @@ appropriate array element and in the latter case the file name
|
|||
appears as a separate array element preceding all the others.
|
||||
Other formatting options are respected.
|
||||
)
|
||||
item(tt(-H) var(hash))(
|
||||
Similar to tt(-A), but instead assign the values to var(hash). The keys
|
||||
are the elements listed above. If the tt(-n) option is provided then the
|
||||
name of the file is included in the hash with key tt(name).
|
||||
)
|
||||
item(tt(-f) var(fd))(
|
||||
Use the file on file descriptor var(fd) instead of
|
||||
named files; no list of file names is allowed in this case.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
texinode(The zle Module)()(The stat Module)(Zsh Modules)
|
||||
texinode(The zle Module)()(The zftp Module)(Zsh Modules)
|
||||
sect(The zle Module)
|
||||
The tt(zle) module contains the Zsh Line Editor. See
|
||||
ifzman(zmanref(zshzle))\
|
||||
|
|
|
@ -36,6 +36,9 @@ A builtin that provides a timed execution facility within the shell.
|
|||
item(tt(stat))(
|
||||
A builtin command interface to the tt(stat) system call.
|
||||
)
|
||||
item(tt(zftp))(
|
||||
A builtin FTP client.
|
||||
)
|
||||
item(tt(zle))(
|
||||
The Zsh Line Editor, including the tt(bindkey) and tt(vared) builtins.
|
||||
)
|
||||
|
@ -50,6 +53,7 @@ menu(The example Module)
|
|||
menu(The files Module)
|
||||
menu(The sched Module)
|
||||
menu(The stat Module)
|
||||
menu(The zftp Module)
|
||||
menu(The zle Module)
|
||||
endmenu()
|
||||
includefile(Zsh/mod_cap.yo)
|
||||
|
@ -61,4 +65,5 @@ includefile(Zsh/mod_example.yo)
|
|||
includefile(Zsh/mod_files.yo)
|
||||
includefile(Zsh/mod_sched.yo)
|
||||
includefile(Zsh/mod_stat.yo)
|
||||
includefile(Zsh/mod_zftp.yo)
|
||||
includefile(Zsh/mod_zle.yo)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# second argument of cd and pushd.
|
||||
|
||||
emulate -R zsh # Requires zsh 3.0-pre4 or later
|
||||
setopt localoptions
|
||||
setopt localoptions extendedglob
|
||||
local from
|
||||
|
||||
read -Ac from
|
||||
|
|
|
@ -112,5 +112,6 @@ t fooxx ((#i)FOOX)x
|
|||
f fooxx ((#i)FOOX)X
|
||||
f BAR (bar|(#i)foo)
|
||||
t FOO (bar|(#i)foo)
|
||||
t Modules (#i)*m*
|
||||
EOT
|
||||
print "$failed tests failed."
|
||||
|
|
|
@ -96,5 +96,6 @@ t fooxx @((#i)FOOX)x
|
|||
f fooxx @((#i)FOOX)X
|
||||
f BAR @(bar|(#i)foo)
|
||||
t FOO @(bar|(#i)foo)
|
||||
t Modules (#i)*m*
|
||||
EOT
|
||||
print "$failed tests failed."
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
# 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
|
||||
|
@ -39,11 +38,6 @@
|
|||
# (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) {
|
||||
|
@ -119,13 +113,6 @@ 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') {
|
||||
|
@ -134,42 +121,18 @@ 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'";
|
||||
|
@ -227,7 +190,7 @@ $" = " - ";
|
|||
|
||||
while (<>) {
|
||||
if (/^\s*complete\s/) {
|
||||
undef(@stuff);
|
||||
undef(@stuff);
|
||||
$default = '';
|
||||
$_ = $';
|
||||
while (/\\$/) {
|
||||
|
@ -248,7 +211,7 @@ while (<>) {
|
|||
# Loop over remaining arguments to "complete".
|
||||
$sep = substr($word,1,1);
|
||||
$sep =~ s/(\W)/\\$1/g;
|
||||
@split = split(/$sep/,$word,4);
|
||||
@split = split(/$sep/,$word);
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
while ($split[$i] =~ /\\$/) {
|
||||
substr($split[$i],-1,1) = "";
|
||||
|
@ -262,9 +225,7 @@ while (<>) {
|
|||
# The "complete" catch-all: treat this as compctl\'s
|
||||
# default (requiring no pattern matching).
|
||||
$default .= &gettype($type) . ' ';
|
||||
defined($suffix) &&
|
||||
(defined($defsuf) ? ($defsuf .= $suffix)
|
||||
: ($defsuf = $suffix));
|
||||
defined($suffix) && ($defsuf .= $suffix);
|
||||
} else {
|
||||
$pat = &getpat($pat,$arg);
|
||||
$type = &gettype($type);
|
||||
|
|
1281
Misc/zftp-functions
Normal file
1281
Misc/zftp-functions
Normal file
File diff suppressed because it is too large
Load diff
|
@ -49,6 +49,53 @@ bin_example(char *nam, char **args, char *ops, int func)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**/
|
||||
static int
|
||||
cond_p_len(Conddef c, char **a)
|
||||
{
|
||||
char *s1 = a[0], *s2 = a[1];
|
||||
|
||||
singsub(&s1);
|
||||
untokenize(s1);
|
||||
if (s2) {
|
||||
singsub(&s2);
|
||||
untokenize(s2);
|
||||
return strlen(s1) == matheval(s2);
|
||||
} else {
|
||||
return !s1[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**/
|
||||
static int
|
||||
cond_i_ex(Conddef c, char **a)
|
||||
{
|
||||
char *s1 = a[0], *s2 = a[1];
|
||||
|
||||
singsub(&s1);
|
||||
untokenize(s1);
|
||||
singsub(&s2);
|
||||
untokenize(s2);
|
||||
return !strcmp("example", dyncat(s1, s2));
|
||||
}
|
||||
|
||||
/**/
|
||||
static int
|
||||
ex_wrapper(List list, FuncWrap w, char *name)
|
||||
{
|
||||
if (strncmp(name, "example", 7))
|
||||
return 1;
|
||||
else {
|
||||
int ogd = opts[GLOBDOTS];
|
||||
|
||||
opts[GLOBDOTS] = 1;
|
||||
runshfunc(list, w, name);
|
||||
opts[GLOBDOTS] = ogd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* boot_example is executed when the module is loaded.
|
||||
*/
|
||||
|
@ -57,11 +104,22 @@ static struct builtin bintab[] = {
|
|||
BUILTIN("example", 0, bin_example, 0, -1, 0, "flags", NULL),
|
||||
};
|
||||
|
||||
static struct conddef cotab[] = {
|
||||
CONDDEF("len", 0, 1, 2, cond_p_len),
|
||||
CONDDEF("ex", CONDF_INFIX, 0, 0, cond_i_ex),
|
||||
};
|
||||
|
||||
static struct funcwrap wrapper[] = {
|
||||
WRAPDEF(ex_wrapper),
|
||||
};
|
||||
|
||||
/**/
|
||||
int
|
||||
boot_example(Module m)
|
||||
{
|
||||
return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
|
||||
return !(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
|
||||
addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) |
|
||||
!addwrapper(m, wrapper));
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
|
@ -71,6 +129,8 @@ int
|
|||
cleanup_example(Module m)
|
||||
{
|
||||
deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
|
||||
deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
|
||||
deletewrapper(m, wrapper);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -34,11 +34,13 @@ enum statnum { ST_DEV, ST_INO, ST_MODE, ST_NLINK, ST_UID, ST_GID,
|
|||
ST_RDEV, ST_SIZE, ST_ATIM, ST_MTIM, ST_CTIM,
|
||||
ST_BLKSIZE, ST_BLOCKS, ST_READLINK, ST_COUNT };
|
||||
enum statflags { STF_NAME = 1, STF_FILE = 2, STF_STRING = 4, STF_RAW = 8,
|
||||
STF_PICK = 16, STF_ARRAY = 32, STF_GMT = 64 };
|
||||
STF_PICK = 16, STF_ARRAY = 32, STF_GMT = 64,
|
||||
STF_HASH = 128 };
|
||||
static char *statelts[] = { "device", "inode", "mode", "nlink",
|
||||
"uid", "gid", "rdev", "size", "atime",
|
||||
"mtime", "ctime", "blksize", "blocks",
|
||||
"link", NULL };
|
||||
#define HNAMEKEY "name"
|
||||
|
||||
/**/
|
||||
static void
|
||||
|
@ -287,6 +289,8 @@ statprint(struct stat *sbuf, char *outbuf, char *fname, int iwhich, int flags)
|
|||
* file names are returned as a separate array element, type names as
|
||||
* prefix to element. Note the formatting deliberately contains
|
||||
* fewer frills when -A is used.
|
||||
* -H hash: as for -A array, but returns a hash with the keys being those
|
||||
* from stat -l
|
||||
* -F fmt: specify a $TIME-like format for printing times; the default
|
||||
* is the (CTIME-like) "%a %b %e %k:%M:%S". This option implies
|
||||
* -s as it is not useful for numerical times.
|
||||
|
@ -305,6 +309,7 @@ static int
|
|||
bin_stat(char *name, char **args, char *ops, int func)
|
||||
{
|
||||
char **aptr, *arrnam = NULL, **array = NULL, **arrptr = NULL;
|
||||
char *hashnam = NULL, **hash = NULL, **hashptr = NULL;
|
||||
int len, iwhich = -1, ret = 0, flags = 0, arrsize = 0, fd = 0;
|
||||
struct stat statbuf;
|
||||
int found = 0, nargs;
|
||||
|
@ -352,6 +357,16 @@ bin_stat(char *name, char **args, char *ops, int func)
|
|||
}
|
||||
flags |= STF_ARRAY;
|
||||
break;
|
||||
} else if (*arg == 'H') {
|
||||
if (arg[1]) {
|
||||
hashnam = arg+1;
|
||||
} else if (!(hashnam = *++args)) {
|
||||
zerrnam(name, "missing parameter name\n",
|
||||
NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
flags |= STF_HASH;
|
||||
break;
|
||||
} else if (*arg == 'f') {
|
||||
char *sfd;
|
||||
ops['f'] = 1;
|
||||
|
@ -385,6 +400,15 @@ bin_stat(char *name, char **args, char *ops, int func)
|
|||
}
|
||||
}
|
||||
|
||||
if ((flags & STF_ARRAY) && (flags & STF_HASH)) {
|
||||
/* We don't implement setting multiple variables at once */
|
||||
zwarnnam(name, "both array and hash requested", NULL, 0);
|
||||
return 1;
|
||||
/* Alternate method would be to make -H blank arrnam etc etc *
|
||||
* and so get 'silent loss' of earlier choice, which would *
|
||||
* be similar to stat -A foo -A bar filename */
|
||||
}
|
||||
|
||||
if (ops['l']) {
|
||||
/* list types and return: can also list to array */
|
||||
if (arrnam) {
|
||||
|
@ -435,7 +459,7 @@ bin_stat(char *name, char **args, char *ops, int func)
|
|||
if (ops['g'])
|
||||
flags |= STF_GMT;
|
||||
|
||||
if (!arrnam) {
|
||||
if (!(arrnam || hashnam)) {
|
||||
if (nargs > 1)
|
||||
flags |= STF_FILE;
|
||||
if (!(flags & STF_PICK))
|
||||
|
@ -444,9 +468,20 @@ bin_stat(char *name, char **args, char *ops, int func)
|
|||
|
||||
if (ops['N'] || ops['f'])
|
||||
flags &= ~STF_FILE;
|
||||
if (ops['T'])
|
||||
if (ops['T'] || ops['H'])
|
||||
flags &= ~STF_NAME;
|
||||
|
||||
if (hashnam) {
|
||||
if (nargs > 1) {
|
||||
zwarnnam(name, "only one file allowed with -H", NULL, 0);
|
||||
return 1;
|
||||
}
|
||||
arrsize = (flags & STF_PICK) ? 1 : ST_COUNT;
|
||||
if (flags & STF_FILE)
|
||||
arrsize++;
|
||||
hashptr = hash = (char **)zcalloc((arrsize+1)*2*sizeof(char *));
|
||||
}
|
||||
|
||||
if (arrnam) {
|
||||
arrsize = (flags & STF_PICK) ? 1 : ST_COUNT;
|
||||
if (flags & STF_FILE)
|
||||
|
@ -473,13 +508,20 @@ bin_stat(char *name, char **args, char *ops, int func)
|
|||
if (flags & STF_FILE)
|
||||
if (arrnam)
|
||||
*arrptr++ = ztrdup(*args);
|
||||
else
|
||||
else if (hashnam) {
|
||||
*hashptr++ = ztrdup(HNAMEKEY);
|
||||
*hashptr++ = ztrdup(*args);
|
||||
} else
|
||||
printf("%s%s", *args, (flags & STF_PICK) ? " " : ":\n");
|
||||
if (iwhich > -1) {
|
||||
statprint(&statbuf, outbuf, *args, iwhich, flags);
|
||||
if (arrnam)
|
||||
*arrptr++ = ztrdup(outbuf);
|
||||
else
|
||||
else if (hashnam) {
|
||||
/* STF_NAME explicitly turned off for ops['H'] above */
|
||||
*hashptr++ = ztrdup(statelts[iwhich]);
|
||||
*hashptr++ = ztrdup(outbuf);
|
||||
} else
|
||||
printf("%s\n", outbuf);
|
||||
} else {
|
||||
int i;
|
||||
|
@ -487,28 +529,39 @@ bin_stat(char *name, char **args, char *ops, int func)
|
|||
statprint(&statbuf, outbuf, *args, i, flags);
|
||||
if (arrnam)
|
||||
*arrptr++= ztrdup(outbuf);
|
||||
else
|
||||
else if (hashnam) {
|
||||
/* STF_NAME explicitly turned off for ops['H'] above */
|
||||
*hashptr++ = ztrdup(statelts[i]);
|
||||
*hashptr++ = ztrdup(outbuf);
|
||||
} else
|
||||
printf("%s\n", outbuf);
|
||||
}
|
||||
}
|
||||
if (ops['f'])
|
||||
break;
|
||||
|
||||
if (!arrnam && args[1] && !(flags & STF_PICK))
|
||||
if (!arrnam && !hashnam && args[1] && !(flags & STF_PICK))
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
if (arrnam)
|
||||
if (ret) {
|
||||
for (aptr = array; *aptr; aptr++)
|
||||
zsfree(*aptr);
|
||||
zfree(array, arrsize+1);
|
||||
} else {
|
||||
if (ret)
|
||||
freearray(array);
|
||||
else {
|
||||
setaparam(arrnam, array);
|
||||
if (errflag)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (hashnam)
|
||||
if (ret)
|
||||
freearray(hash);
|
||||
else {
|
||||
sethparam(hashnam, hash);
|
||||
if (errflag)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
2596
Src/Modules/zftp.c
Normal file
2596
Src/Modules/zftp.c
Normal file
File diff suppressed because it is too large
Load diff
3
Src/Modules/zftp.mdd
Normal file
3
Src/Modules/zftp.mdd
Normal file
|
@ -0,0 +1,3 @@
|
|||
autobins="zftp"
|
||||
|
||||
objects="zftp.o"
|
|
@ -44,7 +44,7 @@ Cmlist cmatcher;
|
|||
/* pointers to functions required by zle */
|
||||
|
||||
/**/
|
||||
void (*printcompctlptr) _((char *, Compctl, int));
|
||||
void (*printcompctlptr) _((char *, Compctl, int, int));
|
||||
|
||||
/**/
|
||||
Compctl (*compctl_widgetptr) _((char *, char **));
|
||||
|
|
|
@ -605,7 +605,7 @@ execzlefunc(Thingy func)
|
|||
} else {
|
||||
startparamscope();
|
||||
makezleparams();
|
||||
doshfunc(l, NULL, 0, 1);
|
||||
doshfunc(w->u.fnnam, l, NULL, 0, 1);
|
||||
endparamscope();
|
||||
lastcmd = 0;
|
||||
}
|
||||
|
|
|
@ -72,6 +72,9 @@ makezleparams(void)
|
|||
|
||||
for(zp = zleparams; zp->name; zp++) {
|
||||
Param pm = createparam(zp->name, zp->type | PM_SPECIAL);
|
||||
if (!pm)
|
||||
pm = (Param) paramtab->getnode(paramtab, zp->name);
|
||||
DPUTS(!pm, "param not set in makezleparams");
|
||||
|
||||
pm->level = locallevel;
|
||||
pm->u.data = zp->data;
|
||||
|
|
|
@ -394,7 +394,7 @@ scanlistwidgets(HashNode hn, int list)
|
|||
quotedzputs(t->nam, stdout);
|
||||
if (w->flags & WIDGET_COMP) {
|
||||
if (printcompctlptr && w->u.cc)
|
||||
printcompctlptr(NULL, w->u.cc, PRINT_LIST);
|
||||
printcompctlptr(NULL, w->u.cc, PRINT_LIST, 0);
|
||||
} else if(strcmp(t->nam, w->u.fnnam)) {
|
||||
fputc(' ', stdout);
|
||||
quotedzputs(w->u.fnnam, stdout);
|
||||
|
@ -404,7 +404,7 @@ scanlistwidgets(HashNode hn, int list)
|
|||
if (w->flags & WIDGET_COMP) {
|
||||
fputs(" -C", stdout);
|
||||
if (printcompctlptr && w->u.cc)
|
||||
printcompctlptr(NULL, w->u.cc, PRINT_TYPE);
|
||||
printcompctlptr(NULL, w->u.cc, PRINT_TYPE, 0);
|
||||
} else if(strcmp(t->nam, w->u.fnnam)) {
|
||||
fputs(" (", stdout);
|
||||
nicezputs(w->u.fnnam, stdout);
|
||||
|
|
|
@ -2730,18 +2730,16 @@ maketildelist(void)
|
|||
|
||||
/**/
|
||||
static int
|
||||
getcpat(char *wrd, int cpatindex, char *cpat, int class)
|
||||
getcpat(char *str, int cpatindex, char *cpat, int class)
|
||||
{
|
||||
char *str, *s, *t, *p;
|
||||
char *s, *t, *p;
|
||||
int d = 0;
|
||||
|
||||
if (!wrd || !*wrd)
|
||||
if (!str || !*str)
|
||||
return -1;
|
||||
|
||||
cpat = rembslash(cpat);
|
||||
|
||||
str = ztrdup(wrd);
|
||||
untokenize(str);
|
||||
if (!cpatindex)
|
||||
cpatindex++, d = 0;
|
||||
else if ((d = (cpatindex < 0)))
|
||||
|
@ -2752,23 +2750,14 @@ getcpat(char *wrd, int cpatindex, char *cpat, int class)
|
|||
d ? s-- : s++) {
|
||||
for (t = s, p = cpat; *t && *p; p++) {
|
||||
if (class) {
|
||||
if (*p == *s && !--cpatindex) {
|
||||
zsfree(str);
|
||||
if (*p == *s && !--cpatindex)
|
||||
return (int)(s - str + 1);
|
||||
}
|
||||
} else if (*t++ != *p)
|
||||
break;
|
||||
}
|
||||
if (!class && !*p && !--cpatindex) {
|
||||
zsfree(str);
|
||||
t += wrd - str;
|
||||
for (d = 0; --t >= wrd;)
|
||||
if (! INULL(*t))
|
||||
d++;
|
||||
return d;
|
||||
}
|
||||
if (!class && !*p && !--cpatindex)
|
||||
return t - str;
|
||||
}
|
||||
zsfree(str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -3336,8 +3325,9 @@ makecomplistext(Compctl occ, char *os, int incmd)
|
|||
break;
|
||||
case CCT_CURSUF:
|
||||
case CCT_CURPRE:
|
||||
s = ztrdup(clwpos < clwnum ? clwords[clwpos] : "");
|
||||
s = ztrdup(clwpos < clwnum ? os : "");
|
||||
untokenize(s);
|
||||
if (isset(COMPLETEINWORD)) s[offs] = '\0';
|
||||
sc = rembslash(cc->u.s.s[i]);
|
||||
a = strlen(sc);
|
||||
if (!strncmp(s, sc, a)) {
|
||||
|
@ -3347,10 +3337,13 @@ makecomplistext(Compctl occ, char *os, int incmd)
|
|||
break;
|
||||
case CCT_CURSUB:
|
||||
case CCT_CURSUBC:
|
||||
if (clwpos < 0 || clwpos > clwnum)
|
||||
if (clwpos < 0 || clwpos >= clwnum)
|
||||
t = 0;
|
||||
else {
|
||||
a = getcpat(clwords[clwpos],
|
||||
s = ztrdup(os);
|
||||
untokenize(s);
|
||||
if (isset(COMPLETEINWORD)) s[offs] = '\0';
|
||||
a = getcpat(s,
|
||||
cc->u.s.p[i],
|
||||
cc->u.s.s[i],
|
||||
cc->type == CCT_CURSUBC);
|
||||
|
@ -4107,7 +4100,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
|
|||
/* This flag allows us to use read -l and -c. */
|
||||
incompctlfunc = 1;
|
||||
/* Call the function. */
|
||||
doshfunc(list, args, 0, 1);
|
||||
doshfunc(cc->func, list, args, 0, 1);
|
||||
incompctlfunc = 0;
|
||||
/* And get the result from the reply parameter. */
|
||||
if ((r = get_user_var("reply")))
|
||||
|
@ -4270,7 +4263,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
|
|||
|
||||
/* No harm in allowing read -l and -c here, too */
|
||||
incompctlfunc = 1;
|
||||
doshfunc(list, args, 0, 1);
|
||||
doshfunc(cc->ylist, list, args, 0, 1);
|
||||
incompctlfunc = 0;
|
||||
uv = "reply";
|
||||
}
|
||||
|
@ -4912,7 +4905,7 @@ do_ambiguous(void)
|
|||
* if it is needed. */
|
||||
if (isset(LISTBEEP))
|
||||
feep();
|
||||
if (isset(AUTOLIST) && !amenu && !showinglist)
|
||||
if (isset(AUTOLIST) && !amenu && !showinglist && smatches >= 2)
|
||||
showinglist = -2;
|
||||
if (am)
|
||||
lastambig = 1;
|
||||
|
@ -5233,14 +5226,10 @@ listmatches(void)
|
|||
Cmgroup g;
|
||||
Cmatch *p, m;
|
||||
Cexpl *e;
|
||||
int nlines = 0, ncols, colsz, ngr = 0, nlist = 0, longest = 1, pnl = 0;
|
||||
int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0;
|
||||
int of = isset(LISTTYPES), opl = 0;
|
||||
int listmax = getiparam("LISTMAX");
|
||||
|
||||
if (smatches < 2) {
|
||||
showinglist = 0;
|
||||
return;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
/* Sanity check */
|
||||
if(!validlist) {
|
||||
|
@ -5310,16 +5299,13 @@ listmatches(void)
|
|||
e++;
|
||||
}
|
||||
}
|
||||
if (g->lcount)
|
||||
ngr++;
|
||||
}
|
||||
longest += 2 + of;
|
||||
if ((ncols = (columns + 1) / longest)) {
|
||||
colsz = (nlist + ncols - 1) / ncols;
|
||||
nlines += ngr - 1 + colsz + (nlist == 0);
|
||||
for (g = amatches; g; g = g->next)
|
||||
nlines += (g->lcount + ncols - 1) / ncols;
|
||||
} else {
|
||||
ncols = 1;
|
||||
colsz = 1;
|
||||
opl = 1;
|
||||
for (g = amatches; g; g = g->next) {
|
||||
char **pp = g->ylist;
|
||||
|
@ -5396,12 +5382,11 @@ listmatches(void)
|
|||
}
|
||||
}
|
||||
else {
|
||||
int n = g->lcount, nl = (n + ncols - 1) / ncols, i, a;
|
||||
int nc = (opl ? 1 : (n + colsz - 1) / colsz);
|
||||
int n = g->lcount, nl = (n + ncols - 1) / ncols, nc = nl, i, a;
|
||||
char **pq;
|
||||
|
||||
while (n && nl--) {
|
||||
i = nc;
|
||||
i = ncols;
|
||||
pq = pp;
|
||||
while (n && i--) {
|
||||
if (pq - g->ylist >= g->lcount)
|
||||
|
@ -5412,7 +5397,7 @@ listmatches(void)
|
|||
while (a--)
|
||||
putc(' ', shout);
|
||||
}
|
||||
pq += colsz;
|
||||
pq += nc;
|
||||
n--;
|
||||
}
|
||||
if (n)
|
||||
|
@ -5422,8 +5407,7 @@ listmatches(void)
|
|||
}
|
||||
}
|
||||
else if (g->lcount) {
|
||||
int n = g->lcount, nl = (n + ncols - 1) / ncols, i, j, a;
|
||||
int nc = (opl ? 1 : (n + colsz - 1) / colsz);
|
||||
int n = g->lcount, nl = (n + ncols - 1) / ncols, nc = nl, i, j, a;
|
||||
Cmatch *q;
|
||||
|
||||
if (n && pnl) {
|
||||
|
@ -5431,7 +5415,7 @@ listmatches(void)
|
|||
pnl = 0;
|
||||
}
|
||||
for (p = skipnolist(g->matches); n && nl--;) {
|
||||
i = nc;
|
||||
i = ncols;
|
||||
q = p;
|
||||
while (n && i--) {
|
||||
if (!(m = *q))
|
||||
|
@ -5460,7 +5444,7 @@ listmatches(void)
|
|||
while (a--)
|
||||
putc(' ', shout);
|
||||
if (--n)
|
||||
for (j = colsz; j && *q; j--)
|
||||
for (j = nc; j && *q; j--)
|
||||
q = skipnolist(q + 1);
|
||||
}
|
||||
if (n) {
|
||||
|
|
|
@ -567,7 +567,9 @@ vioperswapcase(void)
|
|||
}
|
||||
/* go back to the first line of the range */
|
||||
cs = oldcs;
|
||||
#if 0
|
||||
vifirstnonblank();
|
||||
#endif
|
||||
}
|
||||
vichgflag = 0;
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ viforwardword(void)
|
|||
cs++;
|
||||
if (wordflag && !n)
|
||||
return;
|
||||
while (cs != ll && iblank(line[cs]))
|
||||
while (cs != ll && (iblank(line[cs]) || line[cs] == '\n'))
|
||||
cs++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ static struct builtin builtins[] =
|
|||
BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsw", "c"),
|
||||
|
||||
#ifdef DYNAMIC
|
||||
BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "Laudi", NULL),
|
||||
BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "LaudicI", NULL),
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -994,7 +994,7 @@ cd_new_pwd(int func, LinkNode dir, int chaselinks)
|
|||
if ((l = getshfunc("chpwd")) != &dummy_list) {
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
doshfunc(l, NULL, 0, 1);
|
||||
doshfunc("chpwd", l, NULL, 0, 1);
|
||||
}
|
||||
|
||||
dirstacksize = getiparam("DIRSTACKSIZE");
|
||||
|
|
20
Src/cond.c
20
Src/cond.c
|
@ -43,6 +43,26 @@ evalcond(Cond c)
|
|||
return evalcond(c->left) && evalcond(c->right);
|
||||
case COND_OR:
|
||||
return evalcond(c->left) || evalcond(c->right);
|
||||
case COND_MOD:
|
||||
case COND_MODI:
|
||||
{
|
||||
Conddef cd;
|
||||
|
||||
if ((cd = getconddef((c->type == COND_MODI), (char *) c->left, 1))) {
|
||||
if (c->type == COND_MOD) {
|
||||
int l = arrlen((char **) c->right);
|
||||
|
||||
if (l < cd->min || (cd->max >= 0 && l > cd->max)) {
|
||||
zerr("unrecognized condition: `-%s'", (char *) c->left, 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return cd->handler(cd, (char **) c->right);
|
||||
}
|
||||
else
|
||||
zerr("unrecognized condition: `-%s'", (char *) c->left, 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
singsub((char **)&c->left);
|
||||
untokenize(c->left);
|
||||
|
|
52
Src/exec.c
52
Src/exec.c
|
@ -2602,7 +2602,7 @@ execshfunc(Cmd cmd, Shfunc shf)
|
|||
deletejob(jobtab + thisjob);
|
||||
}
|
||||
|
||||
doshfunc(shf->funcdef, cmd->args, shf->flags, 0);
|
||||
doshfunc(shf->nam, shf->funcdef, cmd->args, shf->flags, 0);
|
||||
|
||||
if (!list_pipe)
|
||||
deletefilelist(last_file_list);
|
||||
|
@ -2650,14 +2650,13 @@ execautofn(Cmd cmd)
|
|||
|
||||
/**/
|
||||
void
|
||||
doshfunc(List list, LinkList doshargs, int flags, int noreturnval)
|
||||
doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
|
||||
/* If noreturnval is nonzero, then reset the current return *
|
||||
* value (lastval) to its value before the shell function *
|
||||
* was executed. */
|
||||
{
|
||||
char **tab, **x, *oargv0 = NULL;
|
||||
int xexittr, newexittr, oldzoptind, oldlastval;
|
||||
char *ou;
|
||||
void *xexitfn, *newexitfn;
|
||||
char saveopts[OPT_SIZE];
|
||||
int obreaks = breaks;
|
||||
|
@ -2705,14 +2704,7 @@ doshfunc(List list, LinkList doshargs, int flags, int noreturnval)
|
|||
argzero = ztrdup(argzero);
|
||||
}
|
||||
}
|
||||
startparamscope();
|
||||
ou = underscore;
|
||||
underscore = ztrdup(underscore);
|
||||
execlist(dupstruct(list), 1, 0);
|
||||
zsfree(underscore);
|
||||
underscore = ou;
|
||||
endparamscope();
|
||||
|
||||
runshfunc(list, wrappers, name);
|
||||
if (retflag) {
|
||||
retflag = 0;
|
||||
breaks = obreaks;
|
||||
|
@ -2767,6 +2759,44 @@ doshfunc(List list, LinkList doshargs, int flags, int noreturnval)
|
|||
} LASTALLOC;
|
||||
}
|
||||
|
||||
/* This finally executes a shell function and any function wrappers *
|
||||
* defined by modules. This works by calling the wrapper function which *
|
||||
* in turn has to call back this function with the arguments it gets. */
|
||||
|
||||
/**/
|
||||
void
|
||||
runshfunc(List list, FuncWrap wrap, char *name)
|
||||
{
|
||||
int cont;
|
||||
char *ou;
|
||||
|
||||
while (wrap) {
|
||||
wrap->module->flags |= MOD_WRAPPER;
|
||||
wrap->count++;
|
||||
cont = wrap->handler(list, wrap->next, name);
|
||||
wrap->count--;
|
||||
if (!wrap->count) {
|
||||
wrap->module->flags &= ~MOD_WRAPPER;
|
||||
#ifdef DYNAMIC
|
||||
if (wrap->module->flags & MOD_UNLOAD) {
|
||||
wrap->module->flags &= ~MOD_UNLOAD;
|
||||
unload_module(wrap->module, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (!cont)
|
||||
return;
|
||||
wrap = wrap->next;
|
||||
}
|
||||
startparamscope();
|
||||
ou = underscore;
|
||||
underscore = ztrdup(underscore);
|
||||
execlist(dupstruct(list), 1, 0);
|
||||
zsfree(underscore);
|
||||
underscore = ou;
|
||||
endparamscope();
|
||||
}
|
||||
|
||||
/* Search fpath for an undefined function. Finds the file, and returns the *
|
||||
* list of its contents. */
|
||||
|
||||
|
|
434
Src/glob.c
434
Src/glob.c
|
@ -129,6 +129,8 @@ struct comp {
|
|||
static char *pptr; /* current place in string being matched */
|
||||
static Comp tail;
|
||||
static int first; /* are leading dots special? */
|
||||
static int longest; /* always match longest piece of path. */
|
||||
static int inclosure; /* see comment in doesmatch() */
|
||||
|
||||
/* Add a component to pathbuf: This keeps track of how *
|
||||
* far we are into a file name, since each path component *
|
||||
|
@ -1806,41 +1808,62 @@ matchpat(char *a, char *b)
|
|||
/* do the ${foo%%bar}, ${foo#bar} stuff */
|
||||
/* please do not laugh at this code. */
|
||||
|
||||
struct repldata {
|
||||
int b, e; /* beginning and end of chunk to replace */
|
||||
};
|
||||
typedef struct repldata *Repldata;
|
||||
|
||||
/*
|
||||
* List of bits of matches to concatenate with replacement string.
|
||||
* The data is a struct repldata. It is not used in cases like
|
||||
* ${...//#foo/bar} even though SUB_GLOBAL is set, since the match
|
||||
* is anchored. It goes on the heap.
|
||||
*/
|
||||
|
||||
static LinkList repllist;
|
||||
|
||||
/* Having found a match in getmatch, decide what part of string
|
||||
* to return. The matched part starts b characters into string s
|
||||
* and finishes e characters in: 0 <= b <= e <= strlen(s)
|
||||
* (yes, empty matches should work).
|
||||
* Bits 3 and higher in fl are used: the flags are
|
||||
* 8: Result is matched portion.
|
||||
* 16: Result is unmatched portion.
|
||||
* (N.B. this should be set for standard ${foo#bar} etc. matches.)
|
||||
* 32: Result is numeric position of start of matched portion.
|
||||
* 64: Result is numeric position of end of matched portion.
|
||||
* 128: Result is length of matched portion.
|
||||
* fl is a set of the SUB_* matches defined in zsh.h from SUB_MATCH onwards;
|
||||
* the lower parts are ignored.
|
||||
* replstr is the replacement string for a substitution
|
||||
*/
|
||||
|
||||
/**/
|
||||
static char *
|
||||
get_match_ret(char *s, int b, int e, int fl)
|
||||
get_match_ret(char *s, int b, int e, int fl, char *replstr)
|
||||
{
|
||||
char buf[80], *r, *p, *rr;
|
||||
int ll = 0, l = strlen(s), bl = 0, t = 0, i;
|
||||
|
||||
if (fl & 8) /* matched portion */
|
||||
if (replstr) {
|
||||
if ((fl & SUB_GLOBAL) && repllist) {
|
||||
/* We are replacing the chunk, just add this to the list */
|
||||
Repldata rd = (Repldata) halloc(sizeof(*rd));
|
||||
rd->b = b;
|
||||
rd->e = e;
|
||||
addlinknode(repllist, rd);
|
||||
return s;
|
||||
}
|
||||
ll += strlen(replstr);
|
||||
}
|
||||
if (fl & SUB_MATCH) /* matched portion */
|
||||
ll += 1 + (e - b);
|
||||
if (fl & 16) /* unmatched portion */
|
||||
if (fl & SUB_REST) /* unmatched portion */
|
||||
ll += 1 + (l - (e - b));
|
||||
if (fl & 32) {
|
||||
if (fl & SUB_BIND) {
|
||||
/* position of start of matched portion */
|
||||
sprintf(buf, "%d ", b + 1);
|
||||
ll += (bl = strlen(buf));
|
||||
}
|
||||
if (fl & 64) {
|
||||
if (fl & SUB_EIND) {
|
||||
/* position of end of matched portion */
|
||||
sprintf(buf + bl, "%d ", e + 1);
|
||||
ll += (bl = strlen(buf));
|
||||
}
|
||||
if (fl & 128) {
|
||||
if (fl & SUB_LEN) {
|
||||
/* length of matched portion */
|
||||
sprintf(buf + bl, "%d ", e - b);
|
||||
ll += (bl = strlen(buf));
|
||||
|
@ -1850,13 +1873,13 @@ get_match_ret(char *s, int b, int e, int fl)
|
|||
|
||||
rr = r = (char *)ncalloc(ll);
|
||||
|
||||
if (fl & 8) {
|
||||
if (fl & SUB_MATCH) {
|
||||
/* copy matched portion to new buffer */
|
||||
for (i = b, p = s + b; i < e; i++)
|
||||
*rr++ = *p++;
|
||||
t = 1;
|
||||
}
|
||||
if (fl & 16) {
|
||||
if (fl & SUB_REST) {
|
||||
/* Copy unmatched portion to buffer. If both portions *
|
||||
* requested, put a space in between (why?) */
|
||||
if (t)
|
||||
|
@ -1864,6 +1887,9 @@ get_match_ret(char *s, int b, int e, int fl)
|
|||
/* there may be unmatched bits at both beginning and end of string */
|
||||
for (i = 0, p = s; i < b; i++)
|
||||
*rr++ = *p++;
|
||||
if (replstr)
|
||||
for (p = replstr; *p; )
|
||||
*rr++ = *p++;
|
||||
for (i = e, p = s + e; i < l; i++)
|
||||
*rr++ = *p++;
|
||||
t = 1;
|
||||
|
@ -1879,64 +1905,102 @@ get_match_ret(char *s, int b, int e, int fl)
|
|||
return r;
|
||||
}
|
||||
|
||||
/* It is called from paramsubst to get the match for ${foo#bar} etc.
|
||||
* Bits of fl determines the required action:
|
||||
* bit 0: match the end instead of the beginning (% or %%)
|
||||
* bit 1: % or # was doubled so get the longest match
|
||||
* bit 2: substring match
|
||||
* bit 3: include the matched portion
|
||||
* bit 4: include the unmatched portion
|
||||
* bit 5: the index of the beginning
|
||||
* bit 6: the index of the end
|
||||
* bit 7: the length of the match
|
||||
* bit 8: match the complete string
|
||||
/*
|
||||
* Run the pattern so that we always get the longest possible match.
|
||||
* This eliminates a loop where we gradually shorten the target string
|
||||
* to find same. We also need to check pptr (the point successfully
|
||||
* reached along the target string) explicitly.
|
||||
*
|
||||
* For this to work, we need the full hairy closure code, so
|
||||
* set inclosure.
|
||||
*/
|
||||
|
||||
/**/
|
||||
static int
|
||||
dolongestmatch(char *str, Comp c, int fist)
|
||||
{
|
||||
int ret;
|
||||
longest = 1;
|
||||
inclosure++;
|
||||
ret = domatch(str, c, fist);
|
||||
inclosure--;
|
||||
longest = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called from paramsubst to get the match for ${foo#bar} etc.
|
||||
* fl is a set of the SUB_* flags defined in zsh.h
|
||||
* *sp points to the string we have to modify. The n'th match will be
|
||||
* returned in *sp. ncalloc is used to get memory for the result string.
|
||||
* replstr is the replacement string from a ${.../orig/repl}, in
|
||||
* which case pat is the original.
|
||||
*
|
||||
* n is now ignored unless we are looking for a substring, in
|
||||
* which case the n'th match from the start is counted such that
|
||||
* there is no more than one match from each position.
|
||||
*/
|
||||
|
||||
/**/
|
||||
int
|
||||
getmatch(char **sp, char *pat, int fl, int n)
|
||||
getmatch(char **sp, char *pat, int fl, int n, char *replstr)
|
||||
{
|
||||
Comp c;
|
||||
char *s = *sp, *t, sav;
|
||||
int i, j, l = strlen(*sp);
|
||||
char *s = *sp, *t, *start, sav;
|
||||
int i, j, l = strlen(*sp), matched;
|
||||
|
||||
MUSTUSEHEAP("getmatch"); /* presumably covered by prefork() test */
|
||||
repllist = NULL;
|
||||
c = parsereg(pat);
|
||||
if (!c) {
|
||||
zerr("bad pattern: %s", pat, 0);
|
||||
return 1;
|
||||
}
|
||||
if (fl & 256) {
|
||||
if (fl & SUB_ALL) {
|
||||
i = domatch(s, c, 0);
|
||||
*sp = get_match_ret(*sp, 0, domatch(s, c, 0) ? l : 0, fl);
|
||||
if (! **sp && (((fl & 8) && !i) || ((fl & 16) && i)))
|
||||
*sp = get_match_ret(*sp, 0, i ? l : 0, fl, i ? replstr : 0);
|
||||
if (! **sp && (((fl & SUB_MATCH) && !i) || ((fl & SUB_REST) && i)))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
switch (fl & 7) {
|
||||
switch (fl & (SUB_END|SUB_LONG|SUB_SUBSTR)) {
|
||||
case 0:
|
||||
/* Smallest possible match at head of string: *
|
||||
* start adding characters until we get a match. */
|
||||
for (i = 0, t = s; i <= l; i++, t++) {
|
||||
sav = *t;
|
||||
*t = '\0';
|
||||
if (domatch(s, c, 0) && !--n) {
|
||||
case SUB_LONG:
|
||||
/*
|
||||
* Largest/smallest possible match at head of string.
|
||||
* First get the longest match.
|
||||
*/
|
||||
if (dolongestmatch(s, c, 0)) {
|
||||
char *mpos = pptr;
|
||||
while (!(fl & SUB_LONG) && pptr > s) {
|
||||
/*
|
||||
* If we want the shortest, keep backing up to the
|
||||
* previous character and find the longest up to there.
|
||||
* That way we can usually reach the shortest in only
|
||||
* a few attempts.
|
||||
*/
|
||||
t = (pptr > s + 1 && pptr[-2] == Meta) ? pptr - 2 : pptr -1;
|
||||
sav = *t;
|
||||
*t = '\0';
|
||||
if (!dolongestmatch(s, c, 0)) {
|
||||
*t = sav;
|
||||
break;
|
||||
}
|
||||
mpos = pptr;
|
||||
*t = sav;
|
||||
*sp = get_match_ret(*sp, 0, i, fl);
|
||||
return 1;
|
||||
}
|
||||
if ((*t = sav) == Meta)
|
||||
i++, t++;
|
||||
*sp = get_match_ret(*sp, 0, mpos-s, fl, replstr);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case SUB_END:
|
||||
/* Smallest possible match at tail of string: *
|
||||
* move back down string until we get a match. */
|
||||
* move back down string until we get a match. *
|
||||
* There's no optimization here. */
|
||||
for (t = s + l; t >= s; t--) {
|
||||
if (domatch(t, c, 0) && !--n) {
|
||||
*sp = get_match_ret(*sp, t - s, l, fl);
|
||||
if (domatch(t, c, 0)) {
|
||||
*sp = get_match_ret(*sp, t - s, l, fl, replstr);
|
||||
return 1;
|
||||
}
|
||||
if (t > s+1 && t[-2] == Meta)
|
||||
|
@ -1944,29 +2008,13 @@ getmatch(char **sp, char *pat, int fl, int n)
|
|||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* Largest possible match at head of string: *
|
||||
* delete characters from end until we get a match. */
|
||||
for (t = s + l; t > s; t--) {
|
||||
sav = *t;
|
||||
*t = '\0';
|
||||
if (domatch(s, c, 0) && !--n) {
|
||||
*t = sav;
|
||||
*sp = get_match_ret(*sp, 0, t - s, fl);
|
||||
return 1;
|
||||
}
|
||||
*t = sav;
|
||||
if (t >= s+2 && t[-2] == Meta)
|
||||
t--;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case (SUB_END|SUB_LONG):
|
||||
/* Largest possible match at tail of string: *
|
||||
* move forward along string until we get a match. */
|
||||
* move forward along string until we get a match. *
|
||||
* Again there's no optimisation. */
|
||||
for (i = 0, t = s; i < l; i++, t++) {
|
||||
if (domatch(t, c, 0) && !--n) {
|
||||
*sp = get_match_ret(*sp, i, l, fl);
|
||||
if (domatch(t, c, 0)) {
|
||||
*sp = get_match_ret(*sp, i, l, fl, replstr);
|
||||
return 1;
|
||||
}
|
||||
if (*t == Meta)
|
||||
|
@ -1974,110 +2022,147 @@ getmatch(char **sp, char *pat, int fl, int n)
|
|||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case SUB_SUBSTR:
|
||||
/* Smallest at start, but matching substrings. */
|
||||
if (domatch(s + l, c, 0) && !--n) {
|
||||
*sp = get_match_ret(*sp, 0, 0, fl);
|
||||
if (!(fl & SUB_GLOBAL) && domatch(s + l, c, 0) && !--n) {
|
||||
*sp = get_match_ret(*sp, 0, 0, fl, replstr);
|
||||
return 1;
|
||||
}
|
||||
for (i = 1; i <= l; i++) {
|
||||
for (t = s, j = i; j <= l; j++, t++) {
|
||||
sav = s[j];
|
||||
s[j] = '\0';
|
||||
if (domatch(t, c, 0) && !--n) {
|
||||
s[j] = sav;
|
||||
*sp = get_match_ret(*sp, t - s, j, fl);
|
||||
return 1;
|
||||
} /* fall through */
|
||||
case (SUB_SUBSTR|SUB_LONG):
|
||||
/* longest or smallest at start with substrings */
|
||||
start = s;
|
||||
if (fl & SUB_GLOBAL)
|
||||
repllist = newlinklist();
|
||||
do {
|
||||
/* loop over all matches for global substitution */
|
||||
matched = 0;
|
||||
for (t = start; t < s + l; t++) {
|
||||
/* Find the longest match from this position. */
|
||||
if (dolongestmatch(t, c, 0) && pptr > t) {
|
||||
char *mpos = pptr;
|
||||
while (!(fl & SUB_LONG) && pptr > t) {
|
||||
/* Now reduce to find the smallest match */
|
||||
char *p = (pptr > t + 1 && pptr[-2] == Meta) ?
|
||||
pptr - 2 : pptr - 1;
|
||||
sav = *p;
|
||||
*p = '\0';
|
||||
if (!dolongestmatch(t, c, 0)) {
|
||||
*p = sav;
|
||||
break;
|
||||
}
|
||||
mpos = pptr;
|
||||
*p = sav;
|
||||
}
|
||||
if (!--n || (n <= 0 && (fl & SUB_GLOBAL)))
|
||||
*sp = get_match_ret(*sp, t-s, mpos-s, fl, replstr);
|
||||
if (!(fl & SUB_GLOBAL)) {
|
||||
if (n) {
|
||||
/*
|
||||
* Looking for a later match: in this case,
|
||||
* we can continue looking for matches from
|
||||
* the next character, even if it overlaps
|
||||
* with what we just found.
|
||||
*/
|
||||
continue;
|
||||
} else
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* For a global match, we need to skip the stuff
|
||||
* which is already marked for replacement.
|
||||
*/
|
||||
matched = 1;
|
||||
start = mpos;
|
||||
break;
|
||||
}
|
||||
if ((s[j] = sav) == Meta)
|
||||
j++;
|
||||
if (*t == Meta)
|
||||
t++;
|
||||
}
|
||||
if (s[i] == Meta)
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
/* Smallest at end, matching substrings */
|
||||
if (domatch(s + l, c, 0) && !--n) {
|
||||
*sp = get_match_ret(*sp, l, l, fl);
|
||||
return 1;
|
||||
}
|
||||
for (i = l; i--;) {
|
||||
if (i && s[i-1] == Meta)
|
||||
i--;
|
||||
for (t = s + l, j = i; j >= 0; j--, t--) {
|
||||
sav = *t;
|
||||
*t = '\0';
|
||||
if (domatch(s + j, c, 0) && !--n) {
|
||||
*t = sav;
|
||||
*sp = get_match_ret(*sp, j, t - s, fl);
|
||||
return 1;
|
||||
}
|
||||
*t = sav;
|
||||
if (t >= s+2 && t[-2] == Meta)
|
||||
t--;
|
||||
if (j >= 2 && s[j-2] == Meta)
|
||||
j--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 6:
|
||||
/* Largest at start, matching substrings. */
|
||||
for (i = l; i; i--) {
|
||||
for (t = s, j = i; j <= l; j++, t++) {
|
||||
sav = s[j];
|
||||
s[j] = '\0';
|
||||
if (domatch(t, c, 0) && !--n) {
|
||||
s[j] = sav;
|
||||
*sp = get_match_ret(*sp, t - s, j, fl);
|
||||
return 1;
|
||||
}
|
||||
if ((s[j] = sav) == Meta)
|
||||
j++;
|
||||
if (*t == Meta)
|
||||
t++;
|
||||
}
|
||||
if (i >= 2 && s[i-2] == Meta)
|
||||
i--;
|
||||
}
|
||||
if (domatch(s + l, c, 0) && !--n) {
|
||||
*sp = get_match_ret(*sp, 0, 0, fl);
|
||||
} while (matched);
|
||||
/*
|
||||
* check if we can match a blank string, if so do it
|
||||
* at the start. Goodness knows if this is a good idea
|
||||
* with global substitution, so it doesn't happen.
|
||||
*/
|
||||
if ((fl & (SUB_LONG|SUB_GLOBAL)) == SUB_LONG &&
|
||||
domatch(s + l, c, 0) && !--n) {
|
||||
*sp = get_match_ret(*sp, 0, 0, fl, replstr);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 7:
|
||||
/* Largest at end, matching substrings. */
|
||||
for (i = 0; i < l; i++) {
|
||||
for (t = s + l, j = i; j >= 0; j--, t--) {
|
||||
sav = *t;
|
||||
*t = '\0';
|
||||
if (domatch(s + j, c, 0) && !--n) {
|
||||
*t = sav;
|
||||
*sp = get_match_ret(*sp, j, t - s, fl);
|
||||
return 1;
|
||||
}
|
||||
*t = sav;
|
||||
if (t >= s+2 && t[-2] == Meta)
|
||||
t--;
|
||||
if (j >= 2 && s[j-2] == Meta)
|
||||
j--;
|
||||
}
|
||||
if (s[i] == Meta)
|
||||
i++;
|
||||
}
|
||||
case (SUB_END|SUB_SUBSTR):
|
||||
/* Shortest at end with substrings */
|
||||
if (domatch(s + l, c, 0) && !--n) {
|
||||
*sp = get_match_ret(*sp, l, l, fl);
|
||||
*sp = get_match_ret(*sp, l, l, fl, replstr);
|
||||
return 1;
|
||||
} /* fall through */
|
||||
case (SUB_END|SUB_LONG|SUB_SUBSTR):
|
||||
/* Longest/shortest at end, matching substrings. */
|
||||
for (t = s + l - 1; t >= s; t--) {
|
||||
if (t > s && t[-1] == Meta)
|
||||
t--;
|
||||
if (dolongestmatch(t, c, 0) && pptr > t && !--n) {
|
||||
/* Found the longest match */
|
||||
char *mpos = pptr;
|
||||
while (!(fl & SUB_LONG) && pptr > t) {
|
||||
/* Look for the shortest match */
|
||||
char *p = (pptr > t+1 && pptr[-2] == Meta) ?
|
||||
pptr-2 : pptr-1;
|
||||
sav = *p;
|
||||
*p = '\0';
|
||||
if (!dolongestmatch(t, c, 0) || pptr == t) {
|
||||
*p = sav;
|
||||
break;
|
||||
}
|
||||
*p = sav;
|
||||
mpos = pptr;
|
||||
}
|
||||
*sp = get_match_ret(*sp, t-s, mpos-s, fl, replstr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if ((fl & SUB_LONG) && domatch(s + l, c, 0) && !--n) {
|
||||
*sp = get_match_ret(*sp, l, l, fl, replstr);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* munge the whole string */
|
||||
*sp = get_match_ret(*sp, 0, 0, fl);
|
||||
|
||||
if (repllist && nonempty(repllist)) {
|
||||
/* Put all the bits of a global search and replace together. */
|
||||
LinkNode nd;
|
||||
Repldata rd;
|
||||
int rlen;
|
||||
int lleft = 0; /* size of returned string */
|
||||
|
||||
i = 0; /* start of last chunk we got from *sp */
|
||||
rlen = strlen(replstr);
|
||||
for (nd = firstnode(repllist); nd; incnode(nd)) {
|
||||
rd = (Repldata) getdata(nd);
|
||||
lleft += rd->b - i; /* previous chunk of *sp */
|
||||
lleft += rlen; /* the replaced bit */
|
||||
i = rd->e; /* start of next chunk of *sp */
|
||||
}
|
||||
lleft += l - i; /* final chunk from *sp */
|
||||
start = t = halloc(lleft+1);
|
||||
i = 0;
|
||||
for (nd = firstnode(repllist); nd; incnode(nd)) {
|
||||
rd = (Repldata) getdata(nd);
|
||||
memcpy(t, s + i, rd->b - i);
|
||||
t += rd->b - i;
|
||||
memcpy(t, replstr, rlen);
|
||||
t += rlen;
|
||||
i = rd->e;
|
||||
}
|
||||
memcpy(t, s + i, l - i);
|
||||
start[lleft] = '\0';
|
||||
*sp = start;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* munge the whole string: no match, so no replstr */
|
||||
*sp = get_match_ret(*sp, 0, 0, fl, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -2109,9 +2194,15 @@ static int
|
|||
excluded(Comp c, char *eptr)
|
||||
{
|
||||
char *saves = pptr;
|
||||
int savei = first, ret;
|
||||
int savei = first, savel = longest, ret;
|
||||
|
||||
first = 0;
|
||||
/*
|
||||
* Even if we've been told always to match the longest string,
|
||||
* i.e. not anchored to the end, we want to match the full string
|
||||
* we've already matched when we're trying to exclude it.
|
||||
*/
|
||||
longest = 0;
|
||||
if (PATHADDP(c) && pathpos) {
|
||||
VARARR(char, buf, pathpos + strlen(eptr) + 1);
|
||||
|
||||
|
@ -2128,6 +2219,7 @@ excluded(Comp c, char *eptr)
|
|||
|
||||
pptr = saves;
|
||||
first = savei;
|
||||
longest = savel;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -2138,8 +2230,6 @@ struct gclose {
|
|||
};
|
||||
typedef struct gclose *Gclose;
|
||||
|
||||
static int inclosure; /* see comment in doesmatch() */
|
||||
|
||||
/* Add a list of matches that fit the closure. trystring is a string of
|
||||
* the same length as the target string; a non-zero in that string
|
||||
* indicates that we have already tried to match the patterns following
|
||||
|
@ -2182,6 +2272,15 @@ addclosures(Comp c, LinkList closlist, int *pdone, char *trystring)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Match characters with case-insensitivity.
|
||||
* Note CHARMATCH(x,y) != CHARMATCH(y,x)
|
||||
*/
|
||||
#define CHARMATCH(x, y) \
|
||||
(x == y || (((c->stat & C_IGNCASE) ? (tulower(x) == tulower(y)) : \
|
||||
(c->stat & C_LCMATCHUC) ? (islower(y) && tuupper(y) == x) : 0)))
|
||||
|
||||
|
||||
/* see if current string in pptr matches c */
|
||||
|
||||
/**/
|
||||
|
@ -2219,7 +2318,7 @@ doesmatch(Comp c)
|
|||
for (; *pptr; pptr++) {
|
||||
if (*pptr == Meta)
|
||||
pptr++;
|
||||
else if (*pptr == looka)
|
||||
else if (CHARMATCH(*pptr, looka))
|
||||
break;
|
||||
}
|
||||
if (!*(saves = pptr))
|
||||
|
@ -2233,7 +2332,7 @@ doesmatch(Comp c)
|
|||
for (done = 0; ; done++) {
|
||||
saves = pptr;
|
||||
if ((done || ONEHASHP(c) || OPTIONALP(c)) &&
|
||||
((!c->next && (!LASTP(c) || !*pptr)) ||
|
||||
((!c->next && (!LASTP(c) || !*pptr || longest)) ||
|
||||
(c->next && doesmatch(c->next))))
|
||||
return 1;
|
||||
if (done && OPTIONALP(c))
|
||||
|
@ -2267,7 +2366,7 @@ doesmatch(Comp c)
|
|||
break;
|
||||
saves = pptr;
|
||||
/* do we really want this LASTP here?? */
|
||||
if ((!c->next && (!LASTP(c) || !*pptr)) ||
|
||||
if ((!c->next && (!LASTP(c) || !*pptr || longest)) ||
|
||||
(c->next && doesmatch(c->next))) {
|
||||
retflag = 1;
|
||||
break;
|
||||
|
@ -2453,7 +2552,7 @@ matchonce(Comp c)
|
|||
if (!ret)
|
||||
break;
|
||||
if ((ret = ret2 &&
|
||||
((!c->next && (!LASTP(c) || !*pptr))
|
||||
((!c->next && (!LASTP(c) || !*pptr || longest))
|
||||
|| (c->next && doesmatch(c->next)))) ||
|
||||
(!c->next && LASTP(c)))
|
||||
break;
|
||||
|
@ -2485,7 +2584,7 @@ matchonce(Comp c)
|
|||
if (CLOSUREP(c))
|
||||
return 1;
|
||||
if (!c->next) /* no more patterns left */
|
||||
return (!LASTP(c) || !*pptr);
|
||||
return (!LASTP(c) || !*pptr || longest);
|
||||
/* optimisation when next pattern is not a closure */
|
||||
if (!CLOSUREP(c->next)) {
|
||||
c = c->next;
|
||||
|
@ -2589,10 +2688,7 @@ matchonce(Comp c)
|
|||
}
|
||||
continue;
|
||||
}
|
||||
if (*pptr == *pat ||
|
||||
(((c->stat & C_IGNCASE) ? (tulower(*pat) == tulower(*pptr)) :
|
||||
(c->stat & C_LCMATCHUC) ?
|
||||
(islower(*pat) && tuupper(*pat) == *pptr) : 0))) {
|
||||
if (CHARMATCH(*pptr, *pat)) {
|
||||
/* just plain old characters */
|
||||
pptr++;
|
||||
pat++;
|
||||
|
|
|
@ -414,9 +414,10 @@ scanmatchtable(HashTable ht, Comp com, int flags1, int flags2, ScanFunc scanfunc
|
|||
HashNode hn = st.u.u;
|
||||
st.u.u = st.u.u->next;
|
||||
if ((hn->flags & flags1) + !flags1 && !(hn->flags & flags2) &&
|
||||
domatch(hn->nam, com, 0))
|
||||
domatch(hn->nam, com, 0)) {
|
||||
scanfunc(hn, scanflags);
|
||||
match++;
|
||||
}
|
||||
}
|
||||
|
||||
ht->scan = NULL;
|
||||
|
|
|
@ -117,7 +117,7 @@ loop(int toplevel, int justonce)
|
|||
if (he && he->text)
|
||||
addlinknode(args, he->text);
|
||||
} LASTALLOC;
|
||||
doshfunc(prelist, args, 0, 1);
|
||||
doshfunc("preexec", prelist, args, 0, 1);
|
||||
freelinklist(args, (FreeFunc) NULL);
|
||||
errflag = 0;
|
||||
}
|
||||
|
@ -592,6 +592,9 @@ setupvals(void)
|
|||
createnameddirtable(); /* create hash table for named directories */
|
||||
createparamtable(); /* create paramater hash table */
|
||||
|
||||
condtab = NULL;
|
||||
wrappers = NULL;
|
||||
|
||||
#ifdef TIOCGWINSZ
|
||||
adjustwinsize();
|
||||
#else
|
||||
|
|
|
@ -244,7 +244,7 @@ halloc(size_t size)
|
|||
size = (size + H_ISIZE - 1) & ~(H_ISIZE - 1);
|
||||
|
||||
#if defined(ZSH_MEM) && defined(ZSH_MEM_DEBUG)
|
||||
h_m[size < 1024 ? (size / H_ISIZE) : 1024]++;
|
||||
h_m[size < (1024 * H_ISIZE) ? (size / H_ISIZE) : 1024]++;
|
||||
#endif
|
||||
|
||||
/* find a heap with enough free space */
|
||||
|
@ -319,6 +319,9 @@ hrealloc(char *p, size_t old, size_t new)
|
|||
if (new > old) {
|
||||
char *ptr = (char *) halloc(new);
|
||||
memcpy(ptr, p, old);
|
||||
#ifdef ZSH_MEM_DEBUG
|
||||
memset(p, 0xff, old);
|
||||
#endif
|
||||
return ptr;
|
||||
} else
|
||||
return new ? p : NULL;
|
||||
|
@ -1004,8 +1007,9 @@ zfree(void *p, int sz)
|
|||
long n = (m_lfree->len - M_MIN) & ~(m_pgsz - 1);
|
||||
|
||||
m_lfree->len -= n;
|
||||
if (brk(m_high -= n) == -1)
|
||||
if (brk(m_high -= n) == -1) {
|
||||
DPUTS(1, "MEM: allocation error at brk.");
|
||||
}
|
||||
|
||||
#ifdef ZSH_MEM_DEBUG
|
||||
m_b += n;
|
||||
|
|
328
Src/module.c
328
Src/module.c
|
@ -90,6 +90,35 @@ addbuiltins(char const *nam, Builtin binl, int size)
|
|||
return hadf ? hads : 1;
|
||||
}
|
||||
|
||||
/* The list of function wrappers defined. */
|
||||
|
||||
/**/
|
||||
FuncWrap wrappers;
|
||||
|
||||
/* This adds a definition for a wrapper. Return value is one in case of *
|
||||
* error and zero if all went fine. */
|
||||
|
||||
/**/
|
||||
int
|
||||
addwrapper(Module m, FuncWrap w)
|
||||
{
|
||||
FuncWrap p, q;
|
||||
|
||||
if (w->flags & WRAPF_ADDED)
|
||||
return 1;
|
||||
for (p = wrappers, q = NULL; p; q = p, p = p->next);
|
||||
if (q)
|
||||
q->next = w;
|
||||
else
|
||||
wrappers = w;
|
||||
w->next = NULL;
|
||||
w->flags |= WRAPF_ADDED;
|
||||
w->module = m;
|
||||
w->count = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DYNAMIC
|
||||
|
||||
/* $module_path ($MODULE_PATH) */
|
||||
|
@ -161,6 +190,31 @@ deletebuiltins(char const *nam, Builtin binl, int size)
|
|||
return hadf ? hads : 1;
|
||||
}
|
||||
|
||||
/* This removes the given wrapper definition from the list. Returned is *
|
||||
* one in case of error and zero otherwise. */
|
||||
|
||||
/**/
|
||||
int
|
||||
deletewrapper(Module m, FuncWrap w)
|
||||
{
|
||||
FuncWrap p, q;
|
||||
|
||||
if (w->flags & WRAPF_ADDED) {
|
||||
for (p = wrappers, q = NULL; p && p != w; q = p, p = p->next);
|
||||
|
||||
if (p) {
|
||||
if (q)
|
||||
q->next = p->next;
|
||||
else
|
||||
wrappers = p->next;
|
||||
p->flags &= ~WRAPF_ADDED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef AIXDYNAMIC
|
||||
|
||||
#include <sys/ldr.h>
|
||||
|
@ -498,6 +552,8 @@ bin_zmodload(char *nam, char **args, char *ops, int func)
|
|||
return bin_zmodload_dep(nam, args, ops);
|
||||
else if(ops['a'])
|
||||
return bin_zmodload_auto(nam, args, ops);
|
||||
else if (ops['c'] || ops['C'])
|
||||
return bin_zmodload_cond(nam, args, ops);
|
||||
else
|
||||
return bin_zmodload_load(nam, args, ops);
|
||||
}
|
||||
|
@ -630,6 +686,98 @@ bin_zmodload_auto(char *nam, char **args, char *ops)
|
|||
}
|
||||
}
|
||||
|
||||
/**/
|
||||
static int
|
||||
bin_zmodload_cond(char *nam, char **args, char *ops)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (ops['u']) {
|
||||
/* remove autoloaded conditions */
|
||||
for (; *args; args++) {
|
||||
Conddef cd = getconddef(ops['I'], *args, 0);
|
||||
|
||||
if (!cd) {
|
||||
if (!ops['i']) {
|
||||
zwarnnam(nam, "%s: no such condition", *args, 0);
|
||||
ret = 1;
|
||||
}
|
||||
} else if (cd->flags & CONDF_ADDED) {
|
||||
zwarnnam(nam, "%s: condition is already defined", *args, 0);
|
||||
ret = 1;
|
||||
} else
|
||||
deleteconddef(cd);
|
||||
}
|
||||
return ret;
|
||||
} else if (!*args) {
|
||||
/* list autoloaded conditions */
|
||||
Conddef p;
|
||||
|
||||
for (p = condtab; p; p = p->next) {
|
||||
if (p->module) {
|
||||
if (ops['L']) {
|
||||
fputs("zmodload -c", stdout);
|
||||
if (p->flags & CONDF_INFIX)
|
||||
putchar('I');
|
||||
printf(" %s %s\n", p->module, p->name);
|
||||
} else {
|
||||
fputs("post ", stdout);
|
||||
if (p->flags & CONDF_INFIX)
|
||||
fputs("infix ", stdout);
|
||||
printf("%s (%s)\n",p->name, p->module);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
/* add autoloaded conditions */
|
||||
char *modnam;
|
||||
|
||||
modnam = *args++;
|
||||
if(isset(RESTRICTED) && strchr(modnam, '/')) {
|
||||
zwarnnam(nam, "%s: restricted", modnam, 0);
|
||||
return 1;
|
||||
}
|
||||
do {
|
||||
char *cnam = *args ? *args++ : modnam;
|
||||
if (strchr(cnam, '/')) {
|
||||
zwarnnam(nam, "%s: `/' is illegal in a condition", cnam, 0);
|
||||
ret = 1;
|
||||
} else if (add_autocond(cnam, ops['I'], modnam) && !ops['i']) {
|
||||
zwarnnam(nam, "failed to add condition %s", cnam, 0);
|
||||
ret = 1;
|
||||
}
|
||||
} while(*args);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/**/
|
||||
int
|
||||
unload_module(Module m, LinkNode node)
|
||||
{
|
||||
if (m->handle && cleanup_module(m))
|
||||
return 1;
|
||||
else {
|
||||
if (m->handle)
|
||||
dlclose(m->handle);
|
||||
m->handle = NULL;
|
||||
if(!m->deps) {
|
||||
if (!node) {
|
||||
for (node = firstnode(modules); node; incnode(node))
|
||||
if (m == (Module) getdata(node))
|
||||
break;
|
||||
if (!node)
|
||||
return 1;
|
||||
}
|
||||
remnode(modules, node);
|
||||
zsfree(m->nam);
|
||||
zfree(m, sizeof(*m));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**/
|
||||
static int
|
||||
bin_zmodload_load(char *nam, char **args, char *ops)
|
||||
|
@ -654,20 +802,13 @@ bin_zmodload_load(char *nam, char **args, char *ops)
|
|||
goto cont;
|
||||
}
|
||||
}
|
||||
|
||||
m = (Module) getdata(node);
|
||||
if (m->handle && cleanup_module(m))
|
||||
ret = 1;
|
||||
else {
|
||||
if (m->handle)
|
||||
dlclose(m->handle);
|
||||
m->handle = NULL;
|
||||
if(!m->deps) {
|
||||
remnode(modules, node);
|
||||
zsfree(m->nam);
|
||||
zfree(m, sizeof(*m));
|
||||
}
|
||||
if (!(m->flags & MOD_WRAPPER)) {
|
||||
if (unload_module(m, node))
|
||||
ret = 1;
|
||||
}
|
||||
else
|
||||
m->flags |= MOD_UNLOAD;
|
||||
} else if (!ops['i']) {
|
||||
zwarnnam(nam, "no such module %s", *args, 0);
|
||||
ret = 1;
|
||||
|
@ -711,3 +852,166 @@ bin_zmodload_load(char *nam, char **args, char *ops)
|
|||
}
|
||||
|
||||
#endif /* DYNAMIC */
|
||||
|
||||
/* The list of module-defined conditions. */
|
||||
|
||||
/**/
|
||||
Conddef condtab;
|
||||
|
||||
/* This gets a condition definition with the given name. The first *
|
||||
* argument says if we have to look for an infix condition. The last *
|
||||
* argument is non-zero if we should autoload modules if needed. */
|
||||
|
||||
/**/
|
||||
Conddef
|
||||
getconddef(int inf, char *name, int autol)
|
||||
{
|
||||
Conddef p;
|
||||
#ifdef DYNAMIC
|
||||
int f = 1;
|
||||
#endif
|
||||
|
||||
do {
|
||||
for (p = condtab; p; p = p->next) {
|
||||
if ((!!inf == !!(p->flags & CONDF_INFIX)) &&
|
||||
!strcmp(name, p->name))
|
||||
break;
|
||||
}
|
||||
#ifdef DYNAMIC
|
||||
if (autol && p && p->module) {
|
||||
/* This is a definition for an autoloaded condition, load the *
|
||||
* module if we haven't tried that already. */
|
||||
if (f) {
|
||||
load_module(p->module);
|
||||
f = 0;
|
||||
p = NULL;
|
||||
} else
|
||||
break;
|
||||
} else
|
||||
#endif
|
||||
break;
|
||||
} while (!p);
|
||||
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. */
|
||||
|
||||
/**/
|
||||
int
|
||||
addconddef(Conddef c)
|
||||
{
|
||||
Conddef p = getconddef((c->flags & CONDF_INFIX), c->name, 0);
|
||||
|
||||
if (p) {
|
||||
if (!p->module || (p->flags & CONDF_ADDED))
|
||||
return 1;
|
||||
|
||||
/* There is an autoload definition. */
|
||||
|
||||
deleteconddef(p);
|
||||
}
|
||||
c->next = condtab;
|
||||
condtab = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This adds multiple condition definitions. This is like addbuiltins(). */
|
||||
|
||||
/**/
|
||||
int
|
||||
addconddefs(char const *nam, Conddef c, int size)
|
||||
{
|
||||
int hads = 0, hadf = 0;
|
||||
|
||||
while (size--) {
|
||||
if (c->flags & CONDF_ADDED)
|
||||
continue;
|
||||
if (addconddef(c)) {
|
||||
zwarnnam(nam, "name clash when adding condition `%s'", c->name, 0);
|
||||
hadf = 1;
|
||||
} else {
|
||||
c->flags |= CONDF_ADDED;
|
||||
hads = 2;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
return hadf ? hads : 1;
|
||||
}
|
||||
|
||||
/* This adds a definition for autoloading a module for a condition. */
|
||||
|
||||
/**/
|
||||
int
|
||||
add_autocond(char *nam, int inf, char *module)
|
||||
{
|
||||
Conddef c = zalloc(sizeof(*c));
|
||||
|
||||
c->name = ztrdup(nam);
|
||||
c->flags = (inf ? CONDF_INFIX : 0);
|
||||
c->module = ztrdup(module);
|
||||
|
||||
if (addconddef(c)) {
|
||||
zsfree(c->name);
|
||||
zsfree(c->module);
|
||||
zfree(c, sizeof(*c));
|
||||
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This removes the given condition definition from the list(s). If this *
|
||||
* is a definition for a autoloaded condition, the memory is freed. */
|
||||
|
||||
/**/
|
||||
int
|
||||
deleteconddef(Conddef c)
|
||||
{
|
||||
Conddef p, q;
|
||||
|
||||
for (p = condtab, q = NULL; p && p != c; q = p, p = p->next);
|
||||
|
||||
if (p) {
|
||||
if (q)
|
||||
q->next = p->next;
|
||||
else
|
||||
condtab = p->next;
|
||||
|
||||
if (p->module) {
|
||||
/* autoloaded, free it */
|
||||
zsfree(p->name);
|
||||
zsfree(p->module);
|
||||
zfree(p, sizeof(*p));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* This removes multiple condition definitions (like deletebuiltins()). */
|
||||
|
||||
/**/
|
||||
int
|
||||
deleteconddefs(char const *nam, Conddef c, int size)
|
||||
{
|
||||
int hads = 0, hadf = 0;
|
||||
|
||||
while (size--) {
|
||||
if (!(c->flags & CONDF_ADDED))
|
||||
continue;
|
||||
if (deleteconddef(c)) {
|
||||
zwarnnam(nam, "condition `%s' already deleted", c->name, 0);
|
||||
hadf = 1;
|
||||
} else
|
||||
hads = 2;
|
||||
c->flags &= ~CONDF_ADDED;
|
||||
c++;
|
||||
}
|
||||
return hadf ? hads : 1;
|
||||
}
|
||||
|
||||
#endif /* DYNAMIC */
|
||||
|
|
189
Src/params.c
189
Src/params.c
|
@ -303,7 +303,11 @@ copyparamtable(HashTable ht, char *name)
|
|||
|
||||
#define SCANPM_WANTVALS (1<<0)
|
||||
#define SCANPM_WANTKEYS (1<<1)
|
||||
#define SCANPM_WANTINDEX (1<<2)
|
||||
#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;
|
||||
|
||||
|
@ -311,13 +315,12 @@ static unsigned numparamvals;
|
|||
static void
|
||||
scancountparams(HashNode hn, int flags)
|
||||
{
|
||||
if (!(((Param)hn)->flags & PM_UNSET)) {
|
||||
++numparamvals;
|
||||
if ((flags & SCANPM_WANTKEYS) && (flags & SCANPM_WANTVALS))
|
||||
++numparamvals;
|
||||
if ((flags & SCANPM_WANTKEYS) && (flags & SCANPM_WANTVALS))
|
||||
++numparamvals;
|
||||
}
|
||||
}
|
||||
|
||||
static Comp scancomp;
|
||||
static char **paramvals;
|
||||
|
||||
/**/
|
||||
|
@ -325,33 +328,45 @@ static void
|
|||
scanparamvals(HashNode hn, int flags)
|
||||
{
|
||||
struct value v;
|
||||
if (numparamvals && (flags & (SCANPM_MATCHVAL|SCANPM_MATCHKEY)) &&
|
||||
!(flags & SCANPM_MATCHMANY))
|
||||
return;
|
||||
v.pm = (Param)hn;
|
||||
if (!(v.pm->flags & PM_UNSET)) {
|
||||
if (flags & SCANPM_WANTKEYS) {
|
||||
paramvals[numparamvals++] = v.pm->nam;
|
||||
if (!(flags & SCANPM_WANTVALS))
|
||||
return;
|
||||
}
|
||||
v.isarr = (PM_TYPE(v.pm->flags) & (PM_ARRAY|PM_HASHED));
|
||||
v.inv = (flags & SCANPM_WANTINDEX);
|
||||
v.a = 0;
|
||||
v.b = -1;
|
||||
paramvals[numparamvals++] = getstrvalue(&v);
|
||||
if ((flags & SCANPM_MATCHKEY) && !domatch(v.pm->nam, scancomp, 0)) {
|
||||
return;
|
||||
}
|
||||
if (flags & SCANPM_WANTKEYS) {
|
||||
paramvals[numparamvals++] = v.pm->nam;
|
||||
if (!(flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)))
|
||||
return;
|
||||
}
|
||||
v.isarr = (PM_TYPE(v.pm->flags) & (PM_ARRAY|PM_HASHED));
|
||||
v.inv = 0;
|
||||
v.a = 0;
|
||||
v.b = -1;
|
||||
paramvals[numparamvals] = getstrvalue(&v);
|
||||
if (flags & SCANPM_MATCHVAL) {
|
||||
if (domatch(paramvals[numparamvals], scancomp, 0)) {
|
||||
numparamvals += ((flags & SCANPM_WANTVALS) ? 1 :
|
||||
!(flags & SCANPM_WANTKEYS));
|
||||
} else if (flags & SCANPM_WANTKEYS)
|
||||
--numparamvals; /* Value didn't match, discard key */
|
||||
} else
|
||||
++numparamvals;
|
||||
}
|
||||
|
||||
/**/
|
||||
char **
|
||||
paramvalarr(HashTable ht, unsigned flags)
|
||||
paramvalarr(HashTable ht, int flags)
|
||||
{
|
||||
MUSTUSEHEAP("paramvalarr");
|
||||
numparamvals = 0;
|
||||
if (ht)
|
||||
scanhashtable(ht, 0, 0, 0, scancountparams, flags);
|
||||
scanhashtable(ht, 0, 0, PM_UNSET, scancountparams, flags);
|
||||
paramvals = (char **) alloc((numparamvals + 1) * sizeof(char *));
|
||||
if (ht) {
|
||||
numparamvals = 0;
|
||||
scanhashtable(ht, 0, 0, 0, scanparamvals, flags);
|
||||
scanhashtable(ht, 0, 0, PM_UNSET, scanparamvals, flags);
|
||||
}
|
||||
paramvals[numparamvals] = 0;
|
||||
return paramvals;
|
||||
|
@ -369,15 +384,10 @@ getvaluearr(Value v)
|
|||
else if (PM_TYPE(v->pm->flags) == PM_ARRAY)
|
||||
return v->arr = v->pm->gets.afn(v->pm);
|
||||
else if (PM_TYPE(v->pm->flags) == PM_HASHED) {
|
||||
unsigned flags = 0;
|
||||
if (v->a)
|
||||
flags |= SCANPM_WANTKEYS;
|
||||
if (v->b > v->a)
|
||||
flags |= SCANPM_WANTVALS;
|
||||
v->arr = paramvalarr(v->pm->gets.hfn(v->pm), flags);
|
||||
v->arr = paramvalarr(v->pm->gets.hfn(v->pm), v->isarr);
|
||||
/* Can't take numeric slices of associative arrays */
|
||||
v->a = 0;
|
||||
v->b = -1;
|
||||
v->b = numparamvals;
|
||||
return v->arr;
|
||||
} else
|
||||
return NULL;
|
||||
|
@ -737,7 +747,19 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
|
|||
down = !down;
|
||||
num = -num;
|
||||
}
|
||||
*inv = ind;
|
||||
if (v->isarr & SCANPM_WANTKEYS)
|
||||
*inv = (ind || !(v->isarr & SCANPM_WANTVALS));
|
||||
else if (v->isarr & SCANPM_WANTVALS)
|
||||
*inv = 0;
|
||||
else {
|
||||
if (ind) {
|
||||
v->isarr |= SCANPM_WANTKEYS;
|
||||
v->isarr &= ~SCANPM_WANTVALS;
|
||||
}
|
||||
if (!down)
|
||||
v->isarr &= ~SCANPM_MATCHMANY;
|
||||
*inv = ind;
|
||||
}
|
||||
|
||||
for (t=s, i=0; *t && ((*t != ']' && *t != Outbrack && *t != ',') || i); t++)
|
||||
if (*t == '[' || *t == Inbrack)
|
||||
|
@ -829,7 +851,21 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
|
|||
|
||||
if ((c = parsereg(s))) {
|
||||
if (v->isarr) {
|
||||
ta = getarrvalue(v);
|
||||
if (PM_TYPE(v->pm->flags) == PM_HASHED) {
|
||||
scancomp = c;
|
||||
if (ind)
|
||||
v->isarr |= SCANPM_MATCHKEY;
|
||||
else
|
||||
v->isarr |= SCANPM_MATCHVAL;
|
||||
if (down)
|
||||
v->isarr |= SCANPM_MATCHMANY;
|
||||
if ((ta = getvaluearr(v)) && *ta) {
|
||||
*inv = v->inv;
|
||||
*w = v->b;
|
||||
return 1;
|
||||
}
|
||||
} else
|
||||
ta = getarrvalue(v);
|
||||
if (!ta || !*ta)
|
||||
return 0;
|
||||
if (down)
|
||||
|
@ -920,8 +956,8 @@ getindex(char **pptr, Value v)
|
|||
if (*tbrack == Outbrack)
|
||||
*tbrack = ']';
|
||||
if ((s[0] == '*' || s[0] == '@') && s[1] == ']') {
|
||||
if (v->isarr)
|
||||
v->isarr = (s[0] == '*') ? 1 : -1;
|
||||
if (v->isarr && s[0] == '@')
|
||||
v->isarr |= SCANPM_ISVAR_AT;
|
||||
v->a = 0;
|
||||
v->b = -1;
|
||||
s += 2;
|
||||
|
@ -941,7 +977,7 @@ getindex(char **pptr, Value v)
|
|||
} else
|
||||
a = -ztrlen(t + a + strlen(t));
|
||||
}
|
||||
if (a > 0 && isset(KSHARRAYS))
|
||||
if (a > 0 && (isset(KSHARRAYS) || (v->pm->flags & PM_HASHED)))
|
||||
a--;
|
||||
v->inv = 1;
|
||||
v->isarr = 0;
|
||||
|
@ -984,6 +1020,13 @@ getindex(char **pptr, Value v)
|
|||
/**/
|
||||
Value
|
||||
getvalue(char **pptr, int bracks)
|
||||
{
|
||||
return fetchvalue(pptr, bracks, 0);
|
||||
}
|
||||
|
||||
/**/
|
||||
Value
|
||||
fetchvalue(char **pptr, int bracks, int flags)
|
||||
{
|
||||
char *s, *t;
|
||||
char sav;
|
||||
|
@ -1039,8 +1082,16 @@ getvalue(char **pptr, int bracks)
|
|||
if (!pm || (pm->flags & PM_UNSET))
|
||||
return NULL;
|
||||
v = (Value) hcalloc(sizeof *v);
|
||||
if (PM_TYPE(pm->flags) & (PM_ARRAY|PM_HASHED))
|
||||
v->isarr = isvarat ? -1 : 1;
|
||||
if (PM_TYPE(pm->flags) & (PM_ARRAY|PM_HASHED)) {
|
||||
/* Overload v->isarr as the flag bits for hashed arrays. */
|
||||
v->isarr = flags | (isvarat ? SCANPM_ISVAR_AT : 0);
|
||||
/* If no flags were passed, we need something to represent *
|
||||
* `true' yet differ from an explicit WANTVALS. This is a *
|
||||
* bit of a hack, but makes some sense: When no subscript *
|
||||
* is provided, all values are substituted. */
|
||||
if (!v->isarr)
|
||||
v->isarr = SCANPM_MATCHMANY;
|
||||
}
|
||||
v->pm = pm;
|
||||
v->inv = 0;
|
||||
v->a = 0;
|
||||
|
@ -1079,7 +1130,7 @@ getstrvalue(Value v)
|
|||
if (!v)
|
||||
return hcalloc(1);
|
||||
HEAPALLOC {
|
||||
if (v->inv) {
|
||||
if (v->inv && !(v->pm->flags & PM_HASHED)) {
|
||||
sprintf(buf, "%d", v->a);
|
||||
s = dupstring(buf);
|
||||
LASTALLOC_RETURN s;
|
||||
|
@ -1087,6 +1138,13 @@ getstrvalue(Value v)
|
|||
|
||||
switch(PM_TYPE(v->pm->flags)) {
|
||||
case PM_HASHED:
|
||||
/* (!v->isarr) should be impossible unless emulating ksh */
|
||||
if (!v->isarr && emulation == EMULATE_KSH) {
|
||||
s = dupstring("[0]");
|
||||
if (getindex(&s, v) == 0)
|
||||
s = getstrvalue(v);
|
||||
LASTALLOC_RETURN s;
|
||||
} /* else fall through */
|
||||
case PM_ARRAY:
|
||||
ss = getvaluearr(v);
|
||||
if (v->isarr)
|
||||
|
@ -1486,6 +1544,39 @@ setaparam(char *s, char **val)
|
|||
return v->pm;
|
||||
}
|
||||
|
||||
/**/
|
||||
Param
|
||||
sethparam(char *s, char **kvarr)
|
||||
{
|
||||
Value v;
|
||||
Param pm;
|
||||
char *t;
|
||||
|
||||
if (!isident(s)) {
|
||||
zerr("not an identifier: %s", s, 0);
|
||||
freearray(kvarr);
|
||||
errflag = 1;
|
||||
return 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;
|
||||
return NULL;
|
||||
} else
|
||||
unsetparam(s);
|
||||
|
||||
pm = createparam(s, PM_HASHED);
|
||||
DPUTS(!pm, "BUG: parameter not created");
|
||||
|
||||
arrhashsetfn(pm, kvarr);
|
||||
|
||||
return pm;
|
||||
}
|
||||
|
||||
/**/
|
||||
Param
|
||||
setiparam(char *s, long val)
|
||||
|
@ -2538,24 +2629,28 @@ printparamnode(HashNode hn, int printflags)
|
|||
return;
|
||||
}
|
||||
|
||||
quotedzputs(p->nam, stdout);
|
||||
if (printflags & PRINT_KV_PAIR)
|
||||
putchar(' ');
|
||||
else
|
||||
putchar('=');
|
||||
|
||||
/* How the value is displayed depends *
|
||||
* on the type of the parameter */
|
||||
quotedzputs(p->nam, stdout);
|
||||
putchar('=');
|
||||
switch (PM_TYPE(p->flags)) {
|
||||
case PM_SCALAR:
|
||||
/* string: simple output */
|
||||
if (p->gets.cfn && (t = p->gets.cfn(p)))
|
||||
quotedzputs(t, stdout);
|
||||
putchar('\n');
|
||||
break;
|
||||
case PM_INTEGER:
|
||||
/* integer */
|
||||
printf("%ld\n", p->gets.ifn(p));
|
||||
printf("%ld", p->gets.ifn(p));
|
||||
break;
|
||||
case PM_ARRAY:
|
||||
/* array */
|
||||
putchar('(');
|
||||
if (!(printflags & PRINT_KV_PAIR))
|
||||
putchar('(');
|
||||
u = p->gets.afn(p);
|
||||
if(*u) {
|
||||
quotedzputs(*u++, stdout);
|
||||
|
@ -2564,17 +2659,25 @@ printparamnode(HashNode hn, int printflags)
|
|||
quotedzputs(*u++, stdout);
|
||||
}
|
||||
}
|
||||
printf(")\n");
|
||||
if (!(printflags & PRINT_KV_PAIR))
|
||||
putchar(')');
|
||||
break;
|
||||
case PM_HASHED:
|
||||
/* association */
|
||||
putchar('(');
|
||||
if (!(printflags & PRINT_KV_PAIR))
|
||||
putchar('(');
|
||||
{
|
||||
HashTable ht = p->gets.hfn(p);
|
||||
if (ht)
|
||||
scanhashtable(ht, 0, 0, 0, ht->printnode, 0);
|
||||
scanhashtable(ht, 0, 0, PM_UNSET,
|
||||
ht->printnode, PRINT_KV_PAIR);
|
||||
}
|
||||
printf(")\n");
|
||||
if (!(printflags & PRINT_KV_PAIR))
|
||||
putchar(')');
|
||||
break;
|
||||
}
|
||||
if (printflags & PRINT_KV_PAIR)
|
||||
putchar(' ');
|
||||
else
|
||||
putchar('\n');
|
||||
}
|
||||
|
|
91
Src/parse.c
91
Src/parse.c
|
@ -114,7 +114,7 @@ par_event(void)
|
|||
}
|
||||
if (tok == ENDINPUT)
|
||||
return NULL;
|
||||
if ((sl = par_sublist()))
|
||||
if ((sl = par_sublist())) {
|
||||
if (tok == ENDINPUT) {
|
||||
l = (List) make_list();
|
||||
l->type = Z_SYNC;
|
||||
|
@ -137,6 +137,7 @@ par_event(void)
|
|||
yylex();
|
||||
} else
|
||||
l = NULL;
|
||||
}
|
||||
if (!l) {
|
||||
if (errflag) {
|
||||
yyerror();
|
||||
|
@ -181,7 +182,7 @@ par_list(void)
|
|||
|
||||
while (tok == SEPER)
|
||||
yylex();
|
||||
if ((sl = par_sublist()))
|
||||
if ((sl = par_sublist())) {
|
||||
if (tok == SEPER || tok == AMPER || tok == AMPERBANG) {
|
||||
l = (List) make_list();
|
||||
l->left = sl;
|
||||
|
@ -197,6 +198,7 @@ par_list(void)
|
|||
l->left = sl;
|
||||
l->type = Z_SYNC;
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
|
@ -1139,13 +1141,14 @@ par_cond_2(void)
|
|||
condlex();
|
||||
return c;
|
||||
}
|
||||
if (tok != STRING)
|
||||
if (tok != STRING) {
|
||||
if (tok && tok != LEXERR && condlex == testlex) {
|
||||
s1 = tokstr;
|
||||
condlex();
|
||||
return par_cond_double("-n", s1);
|
||||
} else
|
||||
YYERROR;
|
||||
}
|
||||
s1 = tokstr;
|
||||
if (condlex == testlex)
|
||||
dble = (*s1 == '-' && strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1
|
||||
|
@ -1165,7 +1168,7 @@ par_cond_2(void)
|
|||
c->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0);
|
||||
return c;
|
||||
}
|
||||
if (tok != STRING)
|
||||
if (tok != STRING) {
|
||||
if (tok != LEXERR && condlex == testlex) {
|
||||
if (!dble)
|
||||
return par_cond_double("-n", s1);
|
||||
|
@ -1173,6 +1176,7 @@ par_cond_2(void)
|
|||
return par_cond_double(s1, "1");
|
||||
} else
|
||||
YYERROR;
|
||||
}
|
||||
s2 = tokstr;
|
||||
incond++; /* parentheses do globbing */
|
||||
condlex();
|
||||
|
@ -1180,7 +1184,19 @@ par_cond_2(void)
|
|||
if (tok == STRING && !dble) {
|
||||
s3 = tokstr;
|
||||
condlex();
|
||||
return par_cond_triple(s1, s2, s3);
|
||||
if (tok == STRING) {
|
||||
LinkList l = newlinklist();
|
||||
|
||||
addlinknode(l, s2);
|
||||
addlinknode(l, s3);
|
||||
|
||||
while (tok == STRING) {
|
||||
addlinknode(l, tokstr);
|
||||
condlex();
|
||||
}
|
||||
return par_cond_multi(s1, l);
|
||||
} else
|
||||
return par_cond_triple(s1, s2, s3);
|
||||
} else
|
||||
return par_cond_double(s1, s2);
|
||||
}
|
||||
|
@ -1312,11 +1328,22 @@ par_cond_double(char *a, char *b)
|
|||
{
|
||||
Cond n = (Cond) make_cond();
|
||||
|
||||
if (a[0] != '-' || !a[1] || a[2])
|
||||
COND_ERROR("parse error: condition expected: %s", a);
|
||||
n->left = (void *) b;
|
||||
n->type = a[1];
|
||||
n->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0);
|
||||
n->left = (void *) b;
|
||||
if (a[0] != '-' || !a[1])
|
||||
COND_ERROR("parse error: condition expected: %s", a);
|
||||
else if (!a[2] && strspn(a+1, "abcdefgknoprstuwxzhLONGS") == 1)
|
||||
n->type = a[1];
|
||||
else {
|
||||
char *d[2];
|
||||
|
||||
n->ntype = NT_SET(N_COND, NT_STR, NT_STR | NT_ARR, 0, 0);
|
||||
n->type = COND_MOD;
|
||||
n->left = (void *) (a + 1);
|
||||
d[0] = b;
|
||||
d[1] = NULL;
|
||||
n->right = (void *) arrdup(d);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -1343,6 +1370,9 @@ par_cond_triple(char *a, char *b, char *c)
|
|||
Cond n = (Cond) make_cond();
|
||||
int t0;
|
||||
|
||||
n->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0);
|
||||
n->left = (void *) a;
|
||||
n->right = (void *) c;
|
||||
if ((b[0] == Equals || b[0] == '=') &&
|
||||
(!b[1] || ((b[1] == Equals || b[1] == '=') && !b[2])))
|
||||
n->type = COND_STREQ;
|
||||
|
@ -1351,13 +1381,46 @@ par_cond_triple(char *a, char *b, char *c)
|
|||
else if (b[0] == '-') {
|
||||
if ((t0 = get_cond_num(b + 1)) > -1)
|
||||
n->type = t0 + COND_NT;
|
||||
else
|
||||
COND_ERROR("unrecognized condition: %s", b);
|
||||
else {
|
||||
char *d[3];
|
||||
|
||||
n->ntype = NT_SET(N_COND, NT_STR, NT_STR | NT_ARR, 0, 0);
|
||||
n->type = COND_MODI;
|
||||
n->left = (void *) (b + 1);
|
||||
d[0] = a;
|
||||
d[1] = c;
|
||||
d[2] = NULL;
|
||||
n->right = (void *) arrdup(d);
|
||||
}
|
||||
} else if (a[0] == '-' && a[1]) {
|
||||
char *d[3];
|
||||
|
||||
n->ntype = NT_SET(N_COND, NT_STR, NT_STR | NT_ARR, 0, 0);
|
||||
n->type = COND_MOD;
|
||||
n->left = (void *) (a + 1);
|
||||
d[0] = b;
|
||||
d[1] = c;
|
||||
d[2] = NULL;
|
||||
n->right = (void *) arrdup(d);
|
||||
} else
|
||||
COND_ERROR("condition expected: %s", b);
|
||||
n->left = (void *) a;
|
||||
n->right = (void *) c;
|
||||
n->ntype = NT_SET(N_COND, NT_STR, NT_STR, 0, 0);
|
||||
return n;
|
||||
}
|
||||
|
||||
/**/
|
||||
static Cond
|
||||
par_cond_multi(char *a, LinkList l)
|
||||
{
|
||||
Cond n = (Cond) make_cond();
|
||||
|
||||
n->ntype = NT_SET(N_COND, NT_STR, NT_STR | NT_ARR, 0, 0);
|
||||
if (a[0] != '-' || !a[1])
|
||||
COND_ERROR("condition expected: %s", a);
|
||||
else {
|
||||
n->type = COND_MOD;
|
||||
n->left = (void *) a;
|
||||
n->right = (void *) listarr(l);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
|
|
@ -712,7 +712,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
|
|||
addlinknode(args, num);
|
||||
} LASTALLOC;
|
||||
trapreturn = -1;
|
||||
doshfunc(sigfn, args, 0, 1);
|
||||
doshfunc(name, sigfn, args, 0, 1);
|
||||
freelinklist(args, (FreeFunc) NULL);
|
||||
zsfree(name);
|
||||
} else HEAPALLOC {
|
||||
|
|
117
Src/subst.c
117
Src/subst.c
|
@ -99,7 +99,7 @@ stringsubst(LinkList list, LinkNode node, int ssub)
|
|||
char *str = str3;
|
||||
|
||||
while (!errflag && *str) {
|
||||
if ((qt = *str == Qstring) || *str == String)
|
||||
if ((qt = *str == Qstring) || *str == String) {
|
||||
if (str[1] == Inpar) {
|
||||
str++;
|
||||
goto comsub;
|
||||
|
@ -125,7 +125,7 @@ stringsubst(LinkList list, LinkNode node, int ssub)
|
|||
str3 = (char *)getdata(node);
|
||||
continue;
|
||||
}
|
||||
else if ((qt = *str == Qtick) || *str == Tick)
|
||||
} else if ((qt = *str == Qtick) || *str == Tick)
|
||||
comsub: {
|
||||
LinkList pl;
|
||||
char *s, *str2 = str;
|
||||
|
@ -135,8 +135,12 @@ stringsubst(LinkList list, LinkNode node, int ssub)
|
|||
if (*str == Inpar) {
|
||||
endchar = Outpar;
|
||||
str[-1] = '\0';
|
||||
#ifdef DEBUG
|
||||
if (skipparens(Inpar, Outpar, &str))
|
||||
DPUTS(1, "BUG: parse error in command substitution");
|
||||
dputs("BUG: parse error in command substitution");
|
||||
#else
|
||||
skipparens(Inpar, Outpar, &str);
|
||||
#endif
|
||||
str--;
|
||||
} else {
|
||||
endchar = *str;
|
||||
|
@ -298,7 +302,7 @@ filesub(char **namptr, int assign)
|
|||
if (!assign)
|
||||
return;
|
||||
|
||||
if (assign < 3)
|
||||
if (assign < 3) {
|
||||
if ((*namptr)[1] && (sub = strchr(*namptr + 1, Equals))) {
|
||||
if (assign == 1)
|
||||
for (ptr = *namptr; ptr != sub; ptr++)
|
||||
|
@ -311,6 +315,7 @@ filesub(char **namptr, int assign)
|
|||
}
|
||||
} else
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = *namptr;
|
||||
while ((sub = strchr(ptr, ':'))) {
|
||||
|
@ -691,7 +696,6 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
|||
char *aptr = *str;
|
||||
char *s = aptr, *fstr, *idbeg, *idend, *ostr = (char *) getdata(n);
|
||||
int colf; /* != 0 means we found a colon after the name */
|
||||
int doub = 0; /* != 0 means we have %%, not %, or ##, not # */
|
||||
int isarr = 0;
|
||||
int plan9 = isset(RCEXPANDPARAM);
|
||||
int globsubst = isset(GLOBSUBST);
|
||||
|
@ -705,11 +709,11 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
|||
Value v;
|
||||
int flags = 0;
|
||||
int flnum = 0;
|
||||
int substr = 0;
|
||||
int sortit = 0, casind = 0;
|
||||
int casmod = 0;
|
||||
char *sep = NULL, *spsep = NULL;
|
||||
char *premul = NULL, *postmul = NULL, *preone = NULL, *postone = NULL;
|
||||
char *replstr = NULL; /* replacement string for /orig/repl */
|
||||
long prenum = 0, postnum = 0;
|
||||
int copied = 0;
|
||||
int arrasg = 0;
|
||||
|
@ -717,7 +721,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
|||
int nojoin = 0;
|
||||
char inbrace = 0; /* != 0 means ${...}, otherwise $... */
|
||||
char hkeys = 0; /* 1 means get keys from associative array */
|
||||
char hvals = 1; /* > hkeys get values of associative array */
|
||||
char hvals = 0; /* > hkeys get values of associative array */
|
||||
|
||||
*s++ = '\0';
|
||||
if (!ialnum(*s) && *s != '#' && *s != Pound && *s != '-' &&
|
||||
|
@ -764,22 +768,22 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
|||
nojoin = 1;
|
||||
break;
|
||||
case 'M':
|
||||
flags |= 8;
|
||||
flags |= SUB_MATCH;
|
||||
break;
|
||||
case 'R':
|
||||
flags |= 16;
|
||||
flags |= SUB_REST;
|
||||
break;
|
||||
case 'B':
|
||||
flags |= 32;
|
||||
flags |= SUB_BIND;
|
||||
break;
|
||||
case 'E':
|
||||
flags |= 64;
|
||||
flags |= SUB_EIND;
|
||||
break;
|
||||
case 'N':
|
||||
flags |= 128;
|
||||
flags |= SUB_LEN;
|
||||
break;
|
||||
case 'S':
|
||||
substr = 1;
|
||||
flags |= SUB_SUBSTR;
|
||||
break;
|
||||
case 'I':
|
||||
flnum = get_intarg(&s);
|
||||
|
@ -940,7 +944,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
|||
s++;
|
||||
} else
|
||||
globsubst = 1;
|
||||
} else if (*s == '+')
|
||||
} else if (*s == '+') {
|
||||
if (iident(s[1]))
|
||||
chkset = 1, s++;
|
||||
else if (!inbrace) {
|
||||
|
@ -951,7 +955,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
|||
zerr("bad substitution", NULL, 0);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
} else
|
||||
break;
|
||||
}
|
||||
globsubst = globsubst && !qt;
|
||||
|
@ -974,8 +978,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
|||
copied = 1;
|
||||
*s = sav;
|
||||
v = (Value) NULL;
|
||||
} else if (!(v = getvalue(&s, (unset(KSHARRAYS) || inbrace) ? 1 : -1)))
|
||||
vunset = 1;
|
||||
} 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))))
|
||||
vunset = 1;
|
||||
}
|
||||
while (v || ((inbrace || (unset(KSHARRAYS) && vunset)) && isbrack(*s))) {
|
||||
if (!v) {
|
||||
Param pm;
|
||||
|
@ -1000,13 +1008,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
|||
break;
|
||||
}
|
||||
if ((isarr = v->isarr)) {
|
||||
/* No way to reach here with v->inv != 0, so getvaluearr() *
|
||||
* will definitely be called by getarrvalue(). Slicing of *
|
||||
* associations isn't done, so use v->a and v->b for flags */
|
||||
if (PM_TYPE(v->pm->flags) == PM_HASHED) {
|
||||
v->a = hkeys;
|
||||
v->b = hvals;
|
||||
}
|
||||
/* No way to get here with v->inv != 0, so getvaluearr() *
|
||||
* is called by getarrvalue(); needn't test PM_HASHED. */
|
||||
aval = getarrvalue(v);
|
||||
} else {
|
||||
if (v->pm->flags & PM_ARRAY) {
|
||||
|
@ -1124,23 +1127,72 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
|||
*s == '=' || *s == Equals ||
|
||||
*s == '%' ||
|
||||
*s == '#' || *s == Pound ||
|
||||
*s == '?' || *s == Quest)) {
|
||||
*s == '?' || *s == Quest ||
|
||||
*s == '/')) {
|
||||
|
||||
if (!flnum)
|
||||
flnum++;
|
||||
if (*s == '%')
|
||||
flags |= 1;
|
||||
flags |= SUB_END;
|
||||
|
||||
/* Check for ${..%%..} or ${..##..} */
|
||||
if ((*s == '%' || *s == '#' || *s == Pound) && *s == s[1]) {
|
||||
s++;
|
||||
doub = 1;
|
||||
/* we have %%, not %, or ##, not # */
|
||||
flags |= SUB_LONG;
|
||||
}
|
||||
s++;
|
||||
if (s[-1] == '/') {
|
||||
char *ptr;
|
||||
/*
|
||||
* previous flags are irrelevant, except for (S) which
|
||||
* indicates shortest substring; else look for longest.
|
||||
*/
|
||||
flags = (flags & SUB_SUBSTR) ? 0 : SUB_LONG;
|
||||
if (*s == '/') {
|
||||
/* doubled, so replace all occurrences */
|
||||
flags |= SUB_GLOBAL;
|
||||
s++;
|
||||
}
|
||||
/* Check for anchored substitution */
|
||||
if (*s == '%') {
|
||||
/* anchor at tail */
|
||||
flags |= SUB_END;
|
||||
s++;
|
||||
} else if (*s == '#' || *s == Pound) {
|
||||
/* anchor at head: this is the `normal' case in getmatch */
|
||||
s++;
|
||||
} else
|
||||
flags |= SUB_SUBSTR;
|
||||
/*
|
||||
* Find the / marking the end of the search pattern.
|
||||
* If there isn't one, we're just going to delete that,
|
||||
* i.e. replace it with an empty string.
|
||||
*
|
||||
* This allows quotation of the slash with '\\/'. Why
|
||||
* two? Well, for a non-quoted string we can check for
|
||||
* Bnull+/, which is what you get from `\/', but inside
|
||||
* double quotes the Bnull isn't there, so it's not
|
||||
* consistent.
|
||||
*/
|
||||
for (ptr = s; *ptr && *ptr != '/'; ptr++)
|
||||
if (*ptr == '\\' && ptr[1] == '/')
|
||||
chuck(ptr);
|
||||
replstr = (*ptr && ptr[1]) ? ptr+1 : "";
|
||||
singsub(&replstr);
|
||||
untokenize(replstr);
|
||||
*ptr = '\0';
|
||||
}
|
||||
|
||||
flags |= (doub << 1) | (substr << 2) | (colf << 8);
|
||||
if (!(flags & 0xf8))
|
||||
flags |= 16;
|
||||
if (colf)
|
||||
flags |= SUB_ALL;
|
||||
/*
|
||||
* With no special flags, i.e. just a # or % or whatever,
|
||||
* the matched portion is removed and we keep the rest.
|
||||
* We also want the rest when we're doing a substitution.
|
||||
*/
|
||||
if (!(flags & (SUB_MATCH|SUB_REST|SUB_BIND|SUB_EIND|SUB_LEN)))
|
||||
flags |= SUB_REST;
|
||||
|
||||
if (colf && !vunset)
|
||||
vunset = (isarr) ? !*aval : !*val || (*val == Nularg && !val[1]);
|
||||
|
@ -1234,6 +1286,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
|||
case '%':
|
||||
case '#':
|
||||
case Pound:
|
||||
case '/':
|
||||
if (qt)
|
||||
if (parse_subst_string(s)) {
|
||||
zerr("parse error in ${...%c...} substitution",
|
||||
|
@ -1247,14 +1300,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
|
|||
char **pp = aval = (char **)ncalloc(sizeof(char *) * (arrlen(aval) + 1));
|
||||
|
||||
while ((*pp = *ap++)) {
|
||||
if (getmatch(pp, s, flags, flnum))
|
||||
if (getmatch(pp, s, flags, flnum, replstr))
|
||||
pp++;
|
||||
}
|
||||
copied = 1;
|
||||
} else {
|
||||
if (vunset)
|
||||
val = dupstring("");
|
||||
getmatch(&val, s, flags, flnum);
|
||||
getmatch(&val, s, flags, flnum, replstr);
|
||||
copied = 1;
|
||||
}
|
||||
break;
|
||||
|
|
21
Src/text.c
21
Src/text.c
|
@ -410,6 +410,27 @@ getcond(Cond nm, int addpar)
|
|||
taddstr(" || ");
|
||||
getcond(nm->right, _Cond(nm->right)->type == COND_AND);
|
||||
break;
|
||||
case COND_MOD:
|
||||
{
|
||||
/* Module defined prefix condition. */
|
||||
char **p = (char **) nm->right;
|
||||
|
||||
taddstr("-");
|
||||
taddstr(nm->left);
|
||||
for (; *p; p++) {
|
||||
taddstr(" ");
|
||||
taddstr(*p);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case COND_MODI:
|
||||
/* Module defined infix condition. */
|
||||
taddstr(((char **) nm->right)[0]);
|
||||
taddstr(" -");
|
||||
taddstr(nm->left);
|
||||
taddstr(" ");
|
||||
taddstr(((char **) nm->right)[1]);
|
||||
break;
|
||||
default:
|
||||
if (nm->type <= COND_GE) {
|
||||
/* Binary test: `a = b' etc. */
|
||||
|
|
47
Src/utils.c
47
Src/utils.c
|
@ -634,7 +634,7 @@ preprompt(void)
|
|||
/* If a shell function named "precmd" exists, *
|
||||
* then execute it. */
|
||||
if ((list = getshfunc("precmd")) != &dummy_list)
|
||||
doshfunc(list, NULL, 0, 1);
|
||||
doshfunc("precmd", list, NULL, 0, 1);
|
||||
if (errflag)
|
||||
return;
|
||||
|
||||
|
@ -643,7 +643,7 @@ preprompt(void)
|
|||
* executed "periodic", then execute it now. */
|
||||
if (period && (time(NULL) > lastperiodic + period) &&
|
||||
(list = getshfunc("periodic")) != &dummy_list) {
|
||||
doshfunc(list, NULL, 0, 1);
|
||||
doshfunc("periodic", list, NULL, 0, 1);
|
||||
lastperiodic = time(NULL);
|
||||
}
|
||||
if (errflag)
|
||||
|
@ -732,7 +732,7 @@ checkmailpath(char **s)
|
|||
}
|
||||
} else {
|
||||
if (st.st_size && st.st_atime <= st.st_mtime &&
|
||||
st.st_mtime > lastmailcheck)
|
||||
st.st_mtime > lastmailcheck) {
|
||||
if (!u) {
|
||||
fprintf(shout, "You have new mail.\n");
|
||||
fflush(shout);
|
||||
|
@ -751,6 +751,7 @@ checkmailpath(char **s)
|
|||
underscore = usav;
|
||||
} LASTALLOC;
|
||||
}
|
||||
}
|
||||
if (isset(MAILWARNING) && st.st_atime > st.st_mtime &&
|
||||
st.st_atime > lastmailcheck && st.st_size) {
|
||||
fprintf(shout, "The mail in %s has been read.\n", unmeta(*s));
|
||||
|
@ -1066,14 +1067,14 @@ zstrtol(const char *s, char **t, int base)
|
|||
else if (*s == '+')
|
||||
s++;
|
||||
|
||||
if (!base)
|
||||
if (!base) {
|
||||
if (*s != '0')
|
||||
base = 10;
|
||||
else if (*++s == 'x' || *s == 'X')
|
||||
base = 16, s++;
|
||||
else
|
||||
base = 8;
|
||||
|
||||
}
|
||||
if (base <= 10)
|
||||
for (; *s >= '0' && *s < ('0' + base); s++)
|
||||
ret = ret * base + *s - '0';
|
||||
|
@ -2137,22 +2138,24 @@ dupstruct2(void *a)
|
|||
n = dupstring(on);
|
||||
break;
|
||||
case NT_LIST | NT_NODE:
|
||||
if (heap)
|
||||
if (heap) {
|
||||
if (useheap)
|
||||
n = duplist(on, (VFunc) dupstruct2);
|
||||
else
|
||||
n = list2arr(on, (VFunc) dupstruct2);
|
||||
}
|
||||
else if (useheap)
|
||||
n = arr2list(on, (VFunc) dupstruct2);
|
||||
else
|
||||
n = duparray(on, (VFunc) dupstruct2);
|
||||
break;
|
||||
case NT_LIST | NT_STR:
|
||||
if (heap)
|
||||
if (heap) {
|
||||
if (useheap)
|
||||
n = duplist(on, (VFunc) dupstring);
|
||||
else
|
||||
n = list2arr(on, (VFunc) ztrdup);
|
||||
}
|
||||
else if (useheap)
|
||||
n = arr2list(on, (VFunc) dupstring);
|
||||
else
|
||||
|
@ -2378,11 +2381,12 @@ inittyptab(void)
|
|||
for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(Nularg); t0++)
|
||||
typtab[t0] |= ITOK | IMETA;
|
||||
for (s = ifs ? ifs : DEFAULT_IFS; *s; s++) {
|
||||
if (inblank(*s))
|
||||
if (inblank(*s)) {
|
||||
if (s[1] == *s)
|
||||
s++;
|
||||
else
|
||||
typtab[STOUC(*s)] |= IWSEP;
|
||||
}
|
||||
typtab[STOUC(*s == Meta ? *++s ^ 32 : *s)] |= ISEP;
|
||||
}
|
||||
for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++)
|
||||
|
@ -2405,6 +2409,21 @@ arrdup(char **s)
|
|||
return y;
|
||||
}
|
||||
|
||||
/**/
|
||||
char **
|
||||
listarr(LinkList l)
|
||||
{
|
||||
char **x, **y;
|
||||
LinkNode n;
|
||||
|
||||
x = y = (char **) ncalloc((countlinknodes(l) + 1) * sizeof(char *));
|
||||
|
||||
for (n = firstnode(l); n; incnode(n))
|
||||
*x++ = dupstring((char *) getdata(n));
|
||||
*x = NULL;
|
||||
return y;
|
||||
}
|
||||
|
||||
/**/
|
||||
static char *
|
||||
spname(char *oldname)
|
||||
|
@ -3009,11 +3028,12 @@ niceztrdup(char const *s)
|
|||
char *p = buf, *n, *ret;
|
||||
|
||||
while ((c = *s++)) {
|
||||
if (itok(c))
|
||||
if (itok(c)) {
|
||||
if (c <= Comma)
|
||||
c = ztokens[c - Pound];
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if (c == Meta)
|
||||
c = *s++ ^ 32;
|
||||
n = nicechar(c);
|
||||
|
@ -3034,11 +3054,12 @@ nicezputs(char const *s, FILE *stream)
|
|||
int c;
|
||||
|
||||
while ((c = *s++)) {
|
||||
if (itok(c))
|
||||
if (itok(c)) {
|
||||
if (c <= Comma)
|
||||
c = ztokens[c - Pound];
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if (c == Meta)
|
||||
c = *s++ ^ 32;
|
||||
if(fputs(nicechar(c), stream) < 0)
|
||||
|
@ -3057,11 +3078,12 @@ niceztrlen(char const *s)
|
|||
int c;
|
||||
|
||||
while ((c = *s++)) {
|
||||
if (itok(c))
|
||||
if (itok(c)) {
|
||||
if (c <= Comma)
|
||||
c = ztokens[c - Pound];
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if (c == Meta)
|
||||
c = *s++ ^ 32;
|
||||
l += strlen(nicechar(STOUC(c)));
|
||||
|
@ -3328,13 +3350,14 @@ getkeystring(char *s, int *len, int fromwhere, int *misc)
|
|||
}
|
||||
default:
|
||||
if ((idigit(*s) && *s < '8') || *s == 'x') {
|
||||
if (!fromwhere)
|
||||
if (!fromwhere) {
|
||||
if (*s == '0')
|
||||
s++;
|
||||
else if (*s != 'x') {
|
||||
*t++ = '\\', s--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (s[1] && s[2] && s[3]) {
|
||||
svchar = s[3];
|
||||
s[3] = '\0';
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#!
|
||||
SHTTY
|
||||
addbuiltins
|
||||
addconddefs
|
||||
addedx
|
||||
addhashnode
|
||||
addwrapper
|
||||
aliastab
|
||||
alloc_stackp
|
||||
appstr
|
||||
|
@ -30,7 +32,9 @@ ctxtlex
|
|||
curhist
|
||||
current_limits
|
||||
deletebuiltins
|
||||
deleteconddefs
|
||||
deletehashtable
|
||||
deletewrapper
|
||||
domatch
|
||||
doshfunc
|
||||
dputs
|
||||
|
@ -65,6 +69,7 @@ getkeystring
|
|||
getlinknode
|
||||
getshfunc
|
||||
getsparam
|
||||
gettempname
|
||||
glob_pre
|
||||
glob_suf
|
||||
global_heapalloc
|
||||
|
@ -82,6 +87,7 @@ hgetc
|
|||
hgetline
|
||||
histentarr
|
||||
histentct
|
||||
holdintr
|
||||
hptr
|
||||
hrealloc
|
||||
inbufct
|
||||
|
@ -94,6 +100,7 @@ inpop
|
|||
inpush
|
||||
inredir
|
||||
insertlinknode
|
||||
install_handler
|
||||
intr
|
||||
inwhat
|
||||
isfirstln
|
||||
|
@ -108,6 +115,7 @@ limits
|
|||
line
|
||||
lines
|
||||
locallevel
|
||||
matheval
|
||||
metadiffer
|
||||
metafy
|
||||
metalen
|
||||
|
@ -124,6 +132,7 @@ niceztrdup
|
|||
niceztrlen
|
||||
noaliases
|
||||
noerrs
|
||||
noholdintr
|
||||
noop_function
|
||||
noop_function_int
|
||||
optiontab
|
||||
|
@ -157,8 +166,10 @@ resetneeded
|
|||
restoredir
|
||||
reswdtab
|
||||
retflag
|
||||
runshfunc
|
||||
scanhashtable
|
||||
setaparam
|
||||
sethparam
|
||||
setlimits
|
||||
setsparam
|
||||
settyinfo
|
||||
|
@ -166,6 +177,8 @@ shfunctab
|
|||
shingetline
|
||||
shout
|
||||
shttyinfo
|
||||
sigfuncs
|
||||
sigtrapped
|
||||
singsub
|
||||
skipparens
|
||||
spaceinlineptr
|
||||
|
@ -184,6 +197,7 @@ struncpy
|
|||
tclen
|
||||
tcstr
|
||||
termflags
|
||||
thisjob
|
||||
tgoto
|
||||
tok
|
||||
tokenize
|
||||
|
@ -201,6 +215,7 @@ ugetnode
|
|||
uinsertlinknode
|
||||
unmeta
|
||||
unmetafy
|
||||
unsetparam_pm
|
||||
untokenize
|
||||
uremnode
|
||||
useheap
|
||||
|
@ -210,6 +225,7 @@ zalloc
|
|||
zbeep
|
||||
zcalloc
|
||||
zchdir
|
||||
zclose
|
||||
zerr
|
||||
zerrnam
|
||||
zexit
|
||||
|
@ -222,6 +238,7 @@ zleparse
|
|||
zlereadptr
|
||||
zputs
|
||||
zreaddir
|
||||
zrealloc
|
||||
zsetlimit
|
||||
zsfree
|
||||
zshcs
|
||||
|
|
69
Src/zsh.h
69
Src/zsh.h
|
@ -233,6 +233,7 @@ typedef struct alias *Alias;
|
|||
typedef struct param *Param;
|
||||
typedef struct cmdnam *Cmdnam;
|
||||
typedef struct shfunc *Shfunc;
|
||||
typedef struct funcwrap *FuncWrap;
|
||||
typedef struct builtin *Builtin;
|
||||
typedef struct nameddir *Nameddir;
|
||||
typedef struct module *Module;
|
||||
|
@ -242,6 +243,7 @@ typedef struct job *Job;
|
|||
typedef struct value *Value;
|
||||
typedef struct varasg *Varasg;
|
||||
typedef struct cond *Cond;
|
||||
typedef struct conddef *Conddef;
|
||||
typedef struct cmd *Cmd;
|
||||
typedef struct pline *Pline;
|
||||
typedef struct sublist *Sublist;
|
||||
|
@ -455,6 +457,26 @@ struct cond {
|
|||
#define COND_GT 13
|
||||
#define COND_LE 14
|
||||
#define COND_GE 15
|
||||
#define COND_MOD 16
|
||||
#define COND_MODI 17
|
||||
|
||||
typedef int (*CondHandler) _((Conddef, char **));
|
||||
|
||||
struct conddef {
|
||||
Conddef next; /* next in list */
|
||||
char *name; /* the condition name */
|
||||
int flags; /* see CONDF_* below */
|
||||
int min; /* minimum number of strings */
|
||||
int max; /* maximum number of strings */
|
||||
CondHandler handler; /* handler function */
|
||||
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 }
|
||||
|
||||
struct forcmd { /* for/select */
|
||||
/* Cmd->args contains list of words to loop thru */
|
||||
|
@ -750,6 +772,23 @@ struct shfunc {
|
|||
List funcdef; /* function definition */
|
||||
};
|
||||
|
||||
/* node in list of function call wrappers */
|
||||
|
||||
typedef int (*WrapFunc) _((List, FuncWrap, char *));
|
||||
|
||||
struct funcwrap {
|
||||
FuncWrap next;
|
||||
int flags;
|
||||
WrapFunc handler;
|
||||
Module module;
|
||||
int count;
|
||||
};
|
||||
|
||||
#define WRAPF_ADDED 1
|
||||
|
||||
#define WRAPDEF(func) \
|
||||
{ NULL, 0, func, NULL, 0 }
|
||||
|
||||
/* node in builtin command hash table (builtintab) */
|
||||
|
||||
typedef int (*HandlerFunc) _((char *, char **, char *, int));
|
||||
|
@ -800,6 +839,8 @@ struct module {
|
|||
};
|
||||
|
||||
#define MOD_BUSY (1<<0)
|
||||
#define MOD_WRAPPER (1<<1)
|
||||
#define MOD_UNLOAD (1<<2)
|
||||
|
||||
/* node used in parameter hash table (paramtab) */
|
||||
|
||||
|
@ -872,6 +913,23 @@ struct param {
|
|||
#define PM_RESTRICTED (1<<13) /* cannot be changed in restricted mode */
|
||||
#define PM_UNSET (1<<14) /* has null value */
|
||||
|
||||
/*
|
||||
* Flags for doing matches inside parameter substitutions, i.e.
|
||||
* ${...#...} and friends. This could be an enum, but so
|
||||
* could a lot of other things.
|
||||
*/
|
||||
|
||||
#define SUB_END 0x0001 /* match end instead of begining, % or %% */
|
||||
#define SUB_LONG 0x0002 /* % or # doubled, get longest match */
|
||||
#define SUB_SUBSTR 0x0004 /* match a substring */
|
||||
#define SUB_MATCH 0x0008 /* include the matched portion */
|
||||
#define SUB_REST 0x0010 /* include the unmatched portion */
|
||||
#define SUB_BIND 0x0020 /* index of beginning of string */
|
||||
#define SUB_EIND 0x0040 /* index of end of string */
|
||||
#define SUB_LEN 0x0080 /* length of match */
|
||||
#define SUB_ALL 0x0100 /* match complete string */
|
||||
#define SUB_GLOBAL 0x0200 /* global substitution ${..//all/these} */
|
||||
|
||||
/* node for named directory hash table (nameddirtab) */
|
||||
|
||||
struct nameddir {
|
||||
|
@ -891,13 +949,14 @@ struct nameddir {
|
|||
#define PRINT_NAMEONLY (1<<0)
|
||||
#define PRINT_TYPE (1<<1)
|
||||
#define PRINT_LIST (1<<2)
|
||||
#define PRINT_KV_PAIR (1<<3)
|
||||
|
||||
/* flags for printing for the whence builtin */
|
||||
#define PRINT_WHENCE_CSH (1<<3)
|
||||
#define PRINT_WHENCE_VERBOSE (1<<4)
|
||||
#define PRINT_WHENCE_SIMPLE (1<<5)
|
||||
#define PRINT_WHENCE_FUNCDEF (1<<6)
|
||||
#define PRINT_WHENCE_WORD (1<<7)
|
||||
#define PRINT_WHENCE_CSH (1<<4)
|
||||
#define PRINT_WHENCE_VERBOSE (1<<5)
|
||||
#define PRINT_WHENCE_SIMPLE (1<<6)
|
||||
#define PRINT_WHENCE_FUNCDEF (1<<7)
|
||||
#define PRINT_WHENCE_WORD (1<<8)
|
||||
|
||||
/***********************************/
|
||||
/* Definitions for history control */
|
||||
|
|
|
@ -428,6 +428,8 @@ fi
|
|||
|
||||
AC_CHECK_LIB(cap, cap_get_proc)
|
||||
|
||||
AC_CHECK_LIB(socket, socket)
|
||||
|
||||
dnl ---------------------
|
||||
dnl CHECK TERMCAP LIBRARY
|
||||
dnl ---------------------
|
||||
|
|
|
@ -47,7 +47,8 @@ Bart's chpwd() fix 4589
|
|||
Second edition
|
||||
|
||||
Added line in zle_tricky.c missed when patching by hand, spotted by
|
||||
Bart. (Whitespace is still non-canonical.)
|
||||
Bart. (Whitespace is still non-canonical in the completion code where
|
||||
I have merged patches by hand.)
|
||||
|
||||
Fixed up my compctl widgets patch for use with Sven's rewrite, which I
|
||||
hadn't done properly before.
|
||||
|
@ -58,7 +59,9 @@ Bart's doc fixes, 4472
|
|||
|
||||
Bart's PWD and OLDPWD reshuffle, 4589
|
||||
|
||||
My test-line-length patch for prompts, 4591
|
||||
My test-line-length patch for prompts, 4591 (`%(40l.yes.no)' outputs
|
||||
`yes' if at least 40 characters have already appeared on the line,
|
||||
`no' otherwise.)
|
||||
|
||||
Configure patch from Wilfredo Sanchez in 4594, with some extra
|
||||
tabbification and without the setterm() hunk, since I've already renamed
|
||||
|
@ -84,12 +87,16 @@ one two
|
|||
% print ${(kv)hash} # flag to get keys and values (**)
|
||||
one eins two zwei
|
||||
Comparison of (*) and (**) will reveal how to copy an associative
|
||||
array, but you always need to declare it with typeset -A or an
|
||||
ordinary array will appear. There is a predefined special associative
|
||||
array $testhash, for testing purposes only, which will eventually
|
||||
disappear.
|
||||
array, `hash2=(${(kv}hash})', but you always need to `typeset -A
|
||||
hash2' first or an ordinary array will appear. There is a predefined
|
||||
special associative array $testhash, for testing purposes only, which
|
||||
will eventually disappear.
|
||||
|
||||
My rewrite of prompt truncation, 4601
|
||||
My rewrite of prompt truncation, 4601 --- note this introduces a
|
||||
slight incompatibility in that the string to be truncated now runs by
|
||||
default to the end of the string, instead of only covering individual
|
||||
%-substitutions. If necessary, stick in an extra '%>>' to turn
|
||||
truncation off at the point you want.
|
||||
|
||||
Bart's params error message fix, 4606
|
||||
|
||||
|
@ -100,3 +107,65 @@ Bart's version of the *** fix, 4624
|
|||
Bart's parameter substitution flag delimiter fix, 4644
|
||||
|
||||
My special parameter unset fix, 4662
|
||||
|
||||
Third edition
|
||||
|
||||
I've taken the plunge and changed $ZSH_VERSION, the current one is now
|
||||
3.1.5.pws-1 . It seemed rational to have something incremental at the
|
||||
end for testing, so I abandoned using the date.
|
||||
|
||||
4482 (cdmatch2)and 4641 (${assoc[0]}) now applied; 4641 was supposed
|
||||
to be there before.
|
||||
|
||||
nroff manual pages deleted, you now need yodl.
|
||||
|
||||
deleted modules-bltin by hand, which `make distclean' somehow missed.
|
||||
Caused problems when building a statically linked shell.
|
||||
|
||||
Bart's scanmatchtable fix, 4674
|
||||
|
||||
Commented out vifirstnonblank() in vioperswapcase(), pending any
|
||||
better patch for it.
|
||||
|
||||
Bart's viforwardword fix, 4678
|
||||
|
||||
My case-independent globbing fix, 4693
|
||||
|
||||
Sven's zle_tricky.c, 4697
|
||||
|
||||
Sven's patch to ignore completions if the cursor is not in a part to
|
||||
be completed, 4698, plus addition, 4707
|
||||
|
||||
I have not added Sven's zerr() patch, 4699, in case it has side
|
||||
effects, but I haven't heard anything on the subject and I haven't
|
||||
looked at it.
|
||||
|
||||
Sven's pennockite heap memory patch, 4700
|
||||
|
||||
Sven's condition module patch, 4716, and addition, 4732, and the
|
||||
function wrapper patch, 4734, and additions, 4742, 4769: the module.c
|
||||
bits of these have been moved around a little to avoid clashes with
|
||||
the AIXDYNAMIC stuff. The wrapper stuff is still not finished, but
|
||||
doesn't currently impinge on use of the shell.
|
||||
|
||||
Phil Pennock's patch to use associative arrays in stat, 4727
|
||||
|
||||
Unposted fix for use of printcompctlptr in completion widgets:
|
||||
printcompctl() had acquired another argument.
|
||||
|
||||
My bash-like ${foo/orig/new} patch, 4736, and the version to do
|
||||
shortest matching together with optimizations of all pattern matching
|
||||
in variable strings, 4754.
|
||||
|
||||
Phil's patch for typeset -a docs, 4737
|
||||
|
||||
Nobody wanted my fix for `FOO=x eval external', so it's not there.
|
||||
|
||||
zftp, 4761
|
||||
|
||||
Bart's fix for conddef without dynamical modules, 4762
|
||||
|
||||
Bart's associative array patches for implentation of subscripting flags,
|
||||
4763, plus fix 4766; typeset output 4764
|
||||
|
||||
Sven's completion listing fix, 4767
|
||||
|
|
Loading…
Reference in a new issue