mirror of
https://github.com/zsh-users/zsh
synced 2024-10-06 07:59:22 +00:00
various posts: Implement assignment parsing for typeset.
Typeset assignments now work like raw assignments except for no "+=" and no GLOB_ASSIGN. Documented in typeset builtin doc and mentioned in release notes. Tests to ensure basic sanity. Enabled by default, can be turned off by "disable -r" with typeset family of commands.
This commit is contained in:
parent
a68d22eb00
commit
39b28980f3
11
ChangeLog
11
ChangeLog
|
@ -1,3 +1,14 @@
|
|||
2015-06-24 Peter Stephenson <p.stephenson@samsung.com>
|
||||
|
||||
* various culminating in 35586, c.f. commits on typeset-array
|
||||
branch: Config/version.mk, Doc/Zsh/builtins.yo,
|
||||
Doc/Zsh/grammar.yo, Doc/Zsh/options.yo, NEWS, README,
|
||||
Src/builtin.c, Src/exec.c, Src/hashtable.c, Src/lex.c,
|
||||
Src/options.c, Src/parse.c, Src/text.c, Src/zsh.h,
|
||||
Test/B02typeset.ztst, Test/D01prompt.ztst, Test/E01options.ztst:
|
||||
Implement assignment handling for typeset etc. when matched as
|
||||
reserved words. Document and test.
|
||||
|
||||
2015-06-23 Peter Stephenson <p.stephenson@samsung.com>
|
||||
|
||||
* 35573: Completion/compinit: turn off POSIX_BUILTINS
|
||||
|
|
|
@ -27,5 +27,5 @@
|
|||
# This must also serve as a shell script, so do not add spaces around the
|
||||
# `=' signs.
|
||||
|
||||
VERSION=5.0.8-dev-0
|
||||
VERSION_DATE='June 1, 2015'
|
||||
VERSION=5.0.8-dev-1
|
||||
VERSION_DATE='June 19, 2015'
|
||||
|
|
|
@ -1729,7 +1729,7 @@ xitem(tt(typeset )[ {tt(PLUS())|tt(-)}tt(AHUaghlmprtux) ] \
|
|||
[ {tt(PLUS())|tt(-)}tt(EFLRZi) [ var(n) ] ])
|
||||
xitem(SPACES()[ tt(+) | var(name)[tt(=)var(value)] ... ])
|
||||
xitem(tt(typeset )tt(-T) [ {tt(PLUS())|tt(-)}tt(Uglprux) ] [ {tt(PLUS())|tt(-)}tt(LRZ) [ var(n) ] ])
|
||||
xitem(SPACES()[ tt(+) | var(SCALAR)[tt(=)var(value)] var(array) [ var(sep) ] ])
|
||||
xitem(SPACES()[ tt(+) | var(SCALAR)[tt(=)var(value)] var(array)[tt(=)LPAR()var(value)RPAR()] [ var(sep) ] ])
|
||||
item(tt(typeset) tt(-f) [ {tt(PLUS())|tt(-)}tt(TUkmtuz) ] [ tt(+) | var(name) ... ])(
|
||||
Set or display attributes and values for shell parameters.
|
||||
|
||||
|
@ -1743,22 +1743,72 @@ ifnzman(noderef(Local Parameters))\
|
|||
retain their special attributes when made local.
|
||||
|
||||
For each var(name)tt(=)var(value) assignment, the parameter
|
||||
var(name) is set to var(value). Note that arrays currently cannot be
|
||||
assigned in tt(typeset) expressions, only scalars and integers. Unless
|
||||
the option tt(KSH_TYPESET) is set, normal expansion rules apply to
|
||||
assignment arguments, so var(value) may be split into separate words; if
|
||||
the option is set, assignments which can be recognised when expansion is
|
||||
performed are treated as single words. For example the command
|
||||
tt(typeset vbl=$(echo one two)) is treated as having one argument if
|
||||
tt(KSH_TYPESET) is set, but otherwise is treated as having the two arguments
|
||||
tt(vbl=one) and tt(two).
|
||||
var(name) is set to var(value). All forms of the command
|
||||
handle scalar assignment.
|
||||
|
||||
If any of the reserved words tt(declare), tt(export), tt(float),
|
||||
tt(integer), tt(local), tt(readonly) or tt(typeset) is matched when the
|
||||
line is parsed (N.B. not when it is executed) the shell will try to parse
|
||||
arguments as assignments, except that the `tt(+=)' syntax and the
|
||||
tt(GLOB_ASSIGN) option are not supported. This has two major differences
|
||||
from normal command line argument parsing: array assignment is possible,
|
||||
and scalar values after tt(=) are not split further into words even if
|
||||
expanded (regardless of the setting of the tt(KSH_TYPESET) option; this
|
||||
option is obsolete). Here is an example:
|
||||
|
||||
example(# Reserved word parsing
|
||||
typeset svar=$(echo one word) avar=(several words))
|
||||
|
||||
The above creates a scalar parameter tt(svar) and an array
|
||||
parameter tt(var) as if the assignments had been
|
||||
|
||||
example(svar="one word"
|
||||
avar=(several words))
|
||||
|
||||
On the other hand:
|
||||
|
||||
example(# Normal builtin interface
|
||||
builtin typeset svar=$(echo two words))
|
||||
|
||||
The tt(builtin) keyword causes the above to use the standard builtin
|
||||
interface to tt(typeset) in which argument parsing is perfomed in the same
|
||||
way as for other commands. This example creates a scalar tt(svar)
|
||||
containing the value tt(two) and another scalar parameter tt(words) with
|
||||
no value. An array value in this case would either cause an error or be
|
||||
treated as an obscure set of glob qualifiers.
|
||||
|
||||
Arbitrary arguments are allowed if they take the form of assignments
|
||||
after command line expansion; however, these only perform scalar
|
||||
assignment:
|
||||
|
||||
example(var='svar=val'
|
||||
typeset $var)
|
||||
|
||||
The above sets the scalar parameter tt(svar) to the value tt(val).
|
||||
Parentheses around the value within tt(var) would not cause array
|
||||
assignment as they will be treated as ordinary characters when tt($var)
|
||||
is substituted. Any non-trivial expansion in the name part of the
|
||||
assignment causes the argument to be treated in this fashion:
|
||||
|
||||
example(typeset {var1,var2,var3}=name)
|
||||
|
||||
The above syntax is valid, and has the expected effect of setting the
|
||||
three parameters to the same value, but the command line is parsed as
|
||||
a set of three normal command line arguments to tt(typeset) after
|
||||
expansion. Hence it is not possible to assign to multiple arrays by
|
||||
this means.
|
||||
|
||||
Note that each interface to any of the commands my be disabled
|
||||
separately. For example, `tt(disable -r typeset)' disables the reserved
|
||||
word interface to tt(typeset), exposing the builtin interface, while
|
||||
`tt(disable typeset)' disables the builtin.
|
||||
|
||||
If the shell option tt(TYPESET_SILENT) is not set, for each remaining
|
||||
var(name) that refers to a parameter that is set, the name and value of the
|
||||
parameter are printed in the form of an assignment. Nothing is printed for
|
||||
newly-created parameters, or when any attribute flags listed below are
|
||||
given along with the var(name). Using `tt(PLUS())' instead of minus to
|
||||
introduce an attribute turns it off.
|
||||
var(name) that refers to a parameter that is already set, the name and
|
||||
value of the parameter are printed in the form of an assignment.
|
||||
Nothing is printed for newly-created parameters, or when any attribute
|
||||
flags listed below are given along with the var(name). Using
|
||||
`tt(PLUS())' instead of minus to introduce an attribute turns it off.
|
||||
|
||||
If no var(name) is present, the names and values of all parameters are
|
||||
printed. In this case the attribute flags restrict the display to only
|
||||
|
@ -1829,7 +1879,7 @@ the current state, readonly specials (whose values cannot be
|
|||
changed) are not shown and assignments to arrays are shown before
|
||||
the tt(typeset) rendering the array readonly.
|
||||
)
|
||||
item(tt(-T) [ var(scalar)[tt(=)var(value)] var(array) [ var(sep) ] ])(
|
||||
item(tt(-T) [ var(scalar)[tt(=)var(value)] var(array)[tt(=)LPAR()var(value)...RPAR()] [ var(sep) ] ])(
|
||||
This flag has a different meaning when used with tt(-f); see below.
|
||||
Otherwise the tt(-T) option requires zero, two, or three arguments to be
|
||||
present. With no arguments, the list of parameters created in this
|
||||
|
@ -1839,10 +1889,13 @@ together in the manner of tt($PATH) and tt($path). The optional third
|
|||
argument is a single-character separator which will be used to join the
|
||||
elements of the array to form the scalar; if absent, a colon is used, as
|
||||
with tt($PATH). Only the first character of the separator is significant;
|
||||
any remaining characters are ignored.
|
||||
any remaining characters are ignored. Multibyte characters are not
|
||||
yet supported.
|
||||
|
||||
Only the scalar parameter may be assigned an initial value. Both the
|
||||
scalar and the array may otherwise be manipulated as normal. If one is
|
||||
Only one of the scalar and array parameters may be assigned an initial
|
||||
value (the restrictions on assignment forms described above also apply).
|
||||
|
||||
Both the scalar and the array may be manipulated as normal. If one is
|
||||
unset, the other will automatically be unset too. There is no way of
|
||||
untying the variables without unsetting them, nor of converting the type
|
||||
of one of them with another tt(typeset) command; tt(+T) does not work,
|
||||
|
|
|
@ -472,7 +472,8 @@ word of a command unless quoted or disabled using tt(disable -r):
|
|||
|
||||
tt(do done esac then elif else fi for case
|
||||
if while function repeat time until
|
||||
select coproc nocorrect foreach end ! [[ { })
|
||||
select coproc nocorrect foreach end ! [[ { }
|
||||
declare export float integer local readonly typeset)
|
||||
|
||||
Additionally, `tt(})' is recognized in any position if neither the
|
||||
tt(IGNORE_BRACES) option nor the tt(IGNORE_CLOSE_BRACES) option is set.
|
||||
|
|
|
@ -1928,7 +1928,13 @@ pindex(KSHTYPESET)
|
|||
pindex(NOKSHTYPESET)
|
||||
cindex(argument splitting, in typeset etc.)
|
||||
cindex(ksh, argument splitting in typeset)
|
||||
item(tt(KSH_TYPESET) <K>)(
|
||||
item(tt(KSH_TYPESET))(
|
||||
This option is now obsolete: a better appropximation to the behaviour of
|
||||
other shells is obtained with the reserved word interface to
|
||||
tt(declare), tt(export), tt(float), tt(integer), tt(local), tt(readonly)
|
||||
and tt(typeset). Note that the option is only applied when the reserved
|
||||
word interface is em(not) in use.
|
||||
|
||||
Alters the way arguments to the tt(typeset) family of commands, including
|
||||
tt(declare), tt(export), tt(float), tt(integer), tt(local) and
|
||||
tt(readonly), are processed. Without this option, zsh will perform normal
|
||||
|
|
19
NEWS
19
NEWS
|
@ -4,7 +4,21 @@ CHANGES FROM PREVIOUS VERSIONS OF ZSH
|
|||
|
||||
Note also the list of incompatibilities in the README file.
|
||||
|
||||
Changes from 5.0.7 to 5.0.8
|
||||
Changes from 5.0.8 to 5.0.9
|
||||
---------------------------
|
||||
|
||||
The builtins declare, export, local, readonly and typeset
|
||||
now have corresponding reserved words. When used in
|
||||
this form, the builtin syntax is extended so that assignments
|
||||
following the reserved word are treated similarly to
|
||||
assignments that appear at the start of the command line.
|
||||
For example,
|
||||
local scalar=`echo one word` array=(several words)
|
||||
creates a local "scalar" containing the text "one word"
|
||||
and an array "array" containing the words "several"
|
||||
"words".
|
||||
|
||||
Changes from 5.0.0 to 5.0.8
|
||||
---------------------------
|
||||
|
||||
- Global aliases can be created for syntactic tokens such as command
|
||||
|
@ -47,9 +61,6 @@ Changes from 5.0.7 to 5.0.8
|
|||
- Some rationalisations have been made to the zsh/db/gdbm module that
|
||||
should make it more useful and predictable in operation.
|
||||
|
||||
Changes from 5.0.0 to 5.0.7
|
||||
---------------------------
|
||||
|
||||
- Numeric constants encountered in mathematical expressions (but not other
|
||||
contexts) can contain underscores as separators that will be ignored on
|
||||
evaluation, as allowed in other scripting languages. For example,
|
||||
|
|
17
README
17
README
|
@ -30,6 +30,23 @@ Zsh is a shell with lots of features. For a list of some of these, see the
|
|||
file FEATURES, and for the latest changes see NEWS. For more
|
||||
details, see the documentation.
|
||||
|
||||
Incompatibilites between 5.0.8 and 5.0.9
|
||||
----------------------------------------
|
||||
|
||||
As noted in NEWS, the builtins declare, export, float, integer, local,
|
||||
readonly and typeset now have corresponding reserved words that provide
|
||||
true assignment semantics instead of an approximation by means of normal
|
||||
command line arguments. It is hoped that this additional consistency
|
||||
provides a more natural interface. However, compatbility with older
|
||||
versions of zsh can be obtained by turning off the reserved word
|
||||
interface, exposing the builtin interface:
|
||||
|
||||
disable -r declare export float integer local readonly typeset
|
||||
|
||||
This is also necessary in the unusual eventuality that the builtins are
|
||||
to be overridden by shell functions, since reserved words take
|
||||
precedence over functions.
|
||||
|
||||
Incompatibilites between 5.0.7 and 5.0.8
|
||||
----------------------------------------
|
||||
|
||||
|
|
309
Src/builtin.c
309
Src/builtin.c
|
@ -53,7 +53,7 @@ static struct builtin builtins[] =
|
|||
BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
|
||||
BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
|
||||
BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
|
||||
BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL),
|
||||
BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL),
|
||||
BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL),
|
||||
BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL),
|
||||
BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
|
||||
|
@ -62,7 +62,7 @@ static struct builtin builtins[] =
|
|||
BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL),
|
||||
BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
|
||||
BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
|
||||
BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"),
|
||||
BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, BIN_EXPORT, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"),
|
||||
BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
|
||||
/*
|
||||
* We used to behave as if the argument to -e was optional.
|
||||
|
@ -71,7 +71,7 @@ static struct builtin builtins[] =
|
|||
*/
|
||||
BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt:W", NULL),
|
||||
BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
|
||||
BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"),
|
||||
BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"),
|
||||
BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUx:z", NULL),
|
||||
BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
|
||||
BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
|
||||
|
@ -82,11 +82,11 @@ static struct builtin builtins[] =
|
|||
#endif
|
||||
|
||||
BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "adDEfiLmnpPrt:", "l"),
|
||||
BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lprtux", "i"),
|
||||
BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lprtux", "i"),
|
||||
BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
|
||||
BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL),
|
||||
BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
|
||||
BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL),
|
||||
BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL),
|
||||
BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
|
||||
BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
|
||||
|
||||
|
@ -106,7 +106,7 @@ static struct builtin builtins[] =
|
|||
BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
|
||||
BUILTIN("r", 0, bin_fc, 0, -1, BIN_R, "IlLnr", NULL),
|
||||
BUILTIN("read", 0, bin_read, 0, -1, 0, "cd:ek:%lnpqrst:%zu:AE", NULL),
|
||||
BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"),
|
||||
BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"),
|
||||
BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "df", "r"),
|
||||
BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
|
||||
BUILTIN("set", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_set, 0, -1, 0, NULL, NULL),
|
||||
|
@ -120,7 +120,7 @@ static struct builtin builtins[] =
|
|||
BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL),
|
||||
BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
|
||||
BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"),
|
||||
BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL),
|
||||
BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL),
|
||||
BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
|
||||
BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL),
|
||||
BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"),
|
||||
|
@ -246,7 +246,7 @@ new_optarg(Options ops)
|
|||
|
||||
/**/
|
||||
int
|
||||
execbuiltin(LinkList args, Builtin bn)
|
||||
execbuiltin(LinkList args, LinkList assigns, Builtin bn)
|
||||
{
|
||||
char *pp, *name, *optstr;
|
||||
int flags, sense, argc, execop, xtr = isset(XTRACE);
|
||||
|
@ -443,11 +443,44 @@ execbuiltin(LinkList args, Builtin bn)
|
|||
fputc(' ', xtrerr);
|
||||
quotedzputs(*fullargv++, xtrerr);
|
||||
}
|
||||
if (assigns) {
|
||||
LinkNode node;
|
||||
for (node = firstnode(assigns); node; incnode(node)) {
|
||||
Asgment asg = (Asgment)node;
|
||||
fputc(' ', xtrerr);
|
||||
quotedzputs(asg->name, xtrerr);
|
||||
if (asg->is_array) {
|
||||
LinkNode arrnode;
|
||||
fprintf(xtrerr, "=(");
|
||||
for (arrnode = firstnode(asg->value.array);
|
||||
arrnode;
|
||||
incnode(arrnode)) {
|
||||
fputc(' ', xtrerr);
|
||||
quotedzputs((char *)getdata(arrnode), xtrerr);
|
||||
}
|
||||
fprintf(xtrerr, " )");
|
||||
} else if (asg->value.scalar) {
|
||||
fputc('=', xtrerr);
|
||||
quotedzputs(asg->value.scalar, xtrerr);
|
||||
}
|
||||
}
|
||||
}
|
||||
fputc('\n', xtrerr);
|
||||
fflush(xtrerr);
|
||||
}
|
||||
/* call the handler function, and return its return value */
|
||||
return (*(bn->handlerfunc)) (name, argv, &ops, bn->funcid);
|
||||
if (flags & BINF_ASSIGN)
|
||||
{
|
||||
/*
|
||||
* Takes two sets of arguments.
|
||||
*/
|
||||
HandlerFuncAssign assignfunc = (HandlerFuncAssign)bn->handlerfunc;
|
||||
return (*(assignfunc)) (name, argv, assigns, &ops, bn->funcid);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (*(bn->handlerfunc)) (name, argv, &ops, bn->funcid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1452,12 +1485,13 @@ bin_fc(char *nam, char **argv, Options ops, int func)
|
|||
if (!asgf)
|
||||
asgf = asgl = a;
|
||||
else {
|
||||
asgl->next = a;
|
||||
asgl->node.next = &a->node;
|
||||
asgl = a;
|
||||
}
|
||||
a->name = *argv;
|
||||
a->value = s;
|
||||
a->next = NULL;
|
||||
a->is_array = 0;
|
||||
a->value.scalar = s;
|
||||
a->node.next = a->node.prev = NULL;
|
||||
argv++;
|
||||
}
|
||||
/* interpret and check first history line specifier */
|
||||
|
@ -1631,8 +1665,8 @@ fcsubs(char **sp, struct asgment *sub)
|
|||
/* loop through the linked list */
|
||||
while (sub) {
|
||||
oldstr = sub->name;
|
||||
newstr = sub->value;
|
||||
sub = sub->next;
|
||||
newstr = sub->value.scalar;
|
||||
sub = (Asgment)sub->node.next;
|
||||
oldpos = s;
|
||||
/* loop over occurences of oldstr in s, replacing them with newstr */
|
||||
while ((newpos = (char *)strstr(oldpos, oldstr))) {
|
||||
|
@ -1820,13 +1854,22 @@ fcedit(char *ename, char *fn)
|
|||
|
||||
/**/
|
||||
static Asgment
|
||||
getasg(char *s)
|
||||
getasg(char ***argvp, LinkList assigns)
|
||||
{
|
||||
char *s = **argvp;
|
||||
static struct asgment asg;
|
||||
|
||||
/* sanity check for valid argument */
|
||||
if (!s)
|
||||
if (!s) {
|
||||
if (assigns) {
|
||||
Asgment asgp = (Asgment)firstnode(assigns);
|
||||
if (!asgp)
|
||||
return NULL;
|
||||
(void)uremnode(assigns, &asgp->node);
|
||||
return asgp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* check if name is empty */
|
||||
if (*s == '=') {
|
||||
|
@ -1834,6 +1877,7 @@ getasg(char *s)
|
|||
return NULL;
|
||||
}
|
||||
asg.name = s;
|
||||
asg.is_array = 0;
|
||||
|
||||
/* search for `=' */
|
||||
for (; *s && *s != '='; s++);
|
||||
|
@ -1841,11 +1885,12 @@ getasg(char *s)
|
|||
/* found `=', so return with a value */
|
||||
if (*s) {
|
||||
*s = '\0';
|
||||
asg.value = s + 1;
|
||||
asg.value.scalar = s + 1;
|
||||
} else {
|
||||
/* didn't find `=', so we only have a name */
|
||||
asg.value = NULL;
|
||||
asg.value.scalar = NULL;
|
||||
}
|
||||
(*argvp)++;
|
||||
return &asg;
|
||||
}
|
||||
|
||||
|
@ -1927,7 +1972,7 @@ typeset_setwidth(const char * name, Param pm, Options ops, int on, int always)
|
|||
/**/
|
||||
static Param
|
||||
typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
||||
int on, int off, int roff, char *value, Param altpm,
|
||||
int on, int off, int roff, Asgment asg, Param altpm,
|
||||
Options ops, int joinchar)
|
||||
{
|
||||
int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly;
|
||||
|
@ -1975,7 +2020,24 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
|||
|
||||
/* attempting a type conversion, or making a tied colonarray? */
|
||||
tc = 0;
|
||||
if (usepm || newspecial != NS_NONE) {
|
||||
if (ASG_ARRAYP(asg) && PM_TYPE(on) == PM_SCALAR &&
|
||||
!(usepm && (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED))))
|
||||
on |= PM_ARRAY;
|
||||
if (usepm && ASG_ARRAYP(asg) && newspecial == NS_NONE &&
|
||||
PM_TYPE(pm->node.flags) != PM_ARRAY &&
|
||||
PM_TYPE(pm->node.flags) != PM_HASHED) {
|
||||
if (on & (PM_EFLOAT|PM_FFLOAT|PM_INTEGER)) {
|
||||
zerrnam(cname, "%s: can't assign array value to non-array", pname);
|
||||
return NULL;
|
||||
}
|
||||
if (pm->node.flags & PM_SPECIAL) {
|
||||
zerrnam(cname, "%s: can't assign array value to non-array special", pname);
|
||||
return NULL;
|
||||
}
|
||||
tc = 1;
|
||||
usepm = 0;
|
||||
}
|
||||
else if (usepm || newspecial != NS_NONE) {
|
||||
int chflags = ((off & pm->node.flags) | (on & ~pm->node.flags)) &
|
||||
(PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED|
|
||||
PM_ARRAY|PM_TIED|PM_AUTOLOAD);
|
||||
|
@ -2023,7 +2085,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
|||
tc = 0; /* but don't do a normal conversion */
|
||||
}
|
||||
} else if (!setsecondstype(pm, on, off)) {
|
||||
if (value && !(pm = setsparam(pname, ztrdup(value))))
|
||||
if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar))))
|
||||
return NULL;
|
||||
usepm = 1;
|
||||
err = 0;
|
||||
|
@ -2049,7 +2111,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
|||
* Stricter rules about retaining readonly attribute in this case.
|
||||
*/
|
||||
if ((on & PM_READONLY) && (!usepm || (pm->node.flags & PM_UNSET)) &&
|
||||
!value)
|
||||
!ASG_VALUEP(asg))
|
||||
on |= PM_UNSET;
|
||||
else if (usepm && (pm->node.flags & PM_READONLY) &&
|
||||
!(on & PM_READONLY)) {
|
||||
|
@ -2068,8 +2130,14 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
|||
* ii. we are creating a new local parameter
|
||||
*/
|
||||
if (usepm) {
|
||||
if (asg->is_array ?
|
||||
(asg->value.array && !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED))) :
|
||||
(asg->value.scalar && (PM_TYPE(pm->node.flags & (PM_ARRAY|PM_HASHED))))) {
|
||||
zerrnam(cname, "%s: inconsistent type for assignment", pname);
|
||||
return NULL;
|
||||
}
|
||||
on &= ~PM_LOCAL;
|
||||
if (!on && !roff && !value) {
|
||||
if (!on && !roff && !ASG_VALUEP(asg)) {
|
||||
if (OPT_ISSET(ops,'p'))
|
||||
paramtab->printnode(&pm->node, PRINT_TYPESET);
|
||||
else if (!OPT_ISSET(ops,'g') &&
|
||||
|
@ -2123,15 +2191,17 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
|||
}
|
||||
if (!(pm->node.flags & (PM_ARRAY|PM_HASHED))) {
|
||||
if (pm->node.flags & PM_EXPORTED) {
|
||||
if (!(pm->node.flags & PM_UNSET) && !pm->env && !value)
|
||||
if (!(pm->node.flags & PM_UNSET) && !pm->env && !ASG_VALUEP(asg))
|
||||
addenv(pm, getsparam(pname));
|
||||
} else if (pm->env && !(pm->node.flags & PM_HASHELEM))
|
||||
delenv(pm);
|
||||
if (value && !(pm = setsparam(pname, ztrdup(value))))
|
||||
DPUTS(ASG_ARRAYP(asg), "BUG: typeset got array value where scalar expected");
|
||||
if (asg->value.scalar && !(pm = setsparam(pname, ztrdup(asg->value.scalar))))
|
||||
return NULL;
|
||||
} else if (asg->value.array) {
|
||||
DPUTS(!ASG_ARRAYP(asg), "BUG: typeset got scalar value where array expected");
|
||||
if (!(pm = setaparam(pname, zlinklist2array(asg->value.array))))
|
||||
return NULL;
|
||||
} else if (value) {
|
||||
zwarnnam(cname, "can't assign new value for array %s", pname);
|
||||
return NULL;
|
||||
}
|
||||
pm->node.flags |= (on & PM_READONLY);
|
||||
if (OPT_ISSET(ops,'p'))
|
||||
|
@ -2139,6 +2209,13 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
|||
return pm;
|
||||
}
|
||||
|
||||
if (asg->is_array ?
|
||||
(asg->value.array && !(on & (PM_ARRAY|PM_HASHED))) :
|
||||
(asg->value.scalar && (on & (PM_ARRAY|PM_HASHED)))) {
|
||||
zerrnam(cname, "%s: inconsistent type for assignment", pname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We're here either because we're creating a new parameter,
|
||||
* or we're adding a parameter at a different local level,
|
||||
|
@ -2158,9 +2235,14 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
|||
/*
|
||||
* Try to carry over a value, but not when changing from,
|
||||
* to, or between non-scalar types.
|
||||
*
|
||||
* (We can do better now, but it does have user-visible
|
||||
* implications.)
|
||||
*/
|
||||
if (!value && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED)))
|
||||
value = dupstring(getsparam(pname));
|
||||
if (!ASG_VALUEP(asg) && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED))) {
|
||||
asg->value.scalar = dupstring(getsparam(pname));
|
||||
asg->is_array = 0;
|
||||
}
|
||||
/* pname may point to pm->nam which is about to disappear */
|
||||
pname = dupstring(pname);
|
||||
unsetparam_pm(pm, 0, 1);
|
||||
|
@ -2251,16 +2333,17 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
|||
return NULL;
|
||||
}
|
||||
}
|
||||
if (PM_TYPE(on) == PM_SCALAR) {
|
||||
if (PM_TYPE(on) == PM_SCALAR && !ASG_ARRAYP(asg)) {
|
||||
/*
|
||||
* This will either complain about bad identifiers, or will set
|
||||
* a hash element or array slice. This once worked by accident,
|
||||
* creating a stray parameter along the way via createparam(),
|
||||
* now called below in the isident() branch.
|
||||
*/
|
||||
if (!(pm = setsparam(pname, ztrdup(value ? value : ""))))
|
||||
if (!(pm = setsparam(pname, ztrdup(asg->value.scalar ? asg->value.scalar : ""))))
|
||||
return NULL;
|
||||
value = NULL;
|
||||
asg->value.scalar = NULL;
|
||||
asg->is_array = 0;
|
||||
keeplocal = 0;
|
||||
on = pm->node.flags;
|
||||
} else {
|
||||
|
@ -2331,10 +2414,17 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
|||
pm->level = keeplocal;
|
||||
else if (on & PM_LOCAL)
|
||||
pm->level = locallevel;
|
||||
if (value && !(pm->node.flags & (PM_ARRAY|PM_HASHED))) {
|
||||
if (ASG_VALUEP(asg)) {
|
||||
Param ipm = pm;
|
||||
if (!(pm = setsparam(pname, ztrdup(value))))
|
||||
return NULL;
|
||||
if (pm->node.flags & (PM_ARRAY|PM_HASHED)) {
|
||||
DPUTS(!ASG_ARRAYP(asg), "BUG: inconsistent scalar value for array");
|
||||
if (!(pm=setaparam(pname, zlinklist2array(asg->value.array))))
|
||||
return NULL;
|
||||
} else {
|
||||
DPUTS(ASG_ARRAYP(asg), "BUG: inconsistent array value for scalar");
|
||||
if (!(pm = setsparam(pname, ztrdup(asg->value.scalar))))
|
||||
return NULL;
|
||||
}
|
||||
if (pm != ipm) {
|
||||
DPUTS(ipm->node.flags != pm->node.flags,
|
||||
"BUG: parameter recreated with wrong flags");
|
||||
|
@ -2371,12 +2461,6 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
|||
}
|
||||
}
|
||||
pm->node.flags |= (on & PM_READONLY);
|
||||
if (value && (pm->node.flags & (PM_ARRAY|PM_HASHED))) {
|
||||
zerrnam(cname, "%s: can't assign initial value for array", pname);
|
||||
/* the only safe thing to do here seems to be unset the param */
|
||||
unsetparam_pm(pm, 0, 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (OPT_ISSET(ops,'p'))
|
||||
paramtab->printnode(&pm->node, PRINT_TYPESET);
|
||||
|
@ -2384,11 +2468,18 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
|
|||
return pm;
|
||||
}
|
||||
|
||||
/* declare, export, integer, local, readonly, typeset */
|
||||
/*
|
||||
* declare, export, float, integer, local, readonly, typeset
|
||||
*
|
||||
* Note the difference in interface from most builtins, covered by the
|
||||
* BINF_ASSIGN builtin flag. This is only made use of by builtins
|
||||
* called by reserved word, which only covers declare, local, readonly
|
||||
* and typeset. Otherwise assigns is NULL.
|
||||
*/
|
||||
|
||||
/**/
|
||||
int
|
||||
bin_typeset(char *name, char **argv, Options ops, int func)
|
||||
bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
|
||||
{
|
||||
Param pm;
|
||||
Asgment asg;
|
||||
|
@ -2397,6 +2488,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
int on = 0, off = 0, roff, bit = PM_ARRAY;
|
||||
int i;
|
||||
int returnval = 0, printflags = 0;
|
||||
int hasargs;
|
||||
|
||||
/* hash -f is really the builtin `functions' */
|
||||
if (OPT_ISSET(ops,'f'))
|
||||
|
@ -2449,7 +2541,8 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
/* Given no arguments, list whatever the options specify. */
|
||||
if (OPT_ISSET(ops,'p'))
|
||||
printflags |= PRINT_TYPESET;
|
||||
if (!*argv) {
|
||||
hasargs = *argv != NULL || (assigns && firstnode(assigns));
|
||||
if (!hasargs) {
|
||||
if (!OPT_ISSET(ops,'p')) {
|
||||
if (!(on|roff))
|
||||
printflags |= PRINT_TYPE;
|
||||
|
@ -2468,9 +2561,9 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
|
||||
if (on & PM_TIED) {
|
||||
Param apm;
|
||||
struct asgment asg0;
|
||||
char *oldval = NULL;
|
||||
int joinchar;
|
||||
struct asgment asg0, asg2;
|
||||
char *oldval = NULL, *joinstr;
|
||||
int joinchar, nargs;
|
||||
|
||||
if (OPT_ISSET(ops,'m')) {
|
||||
zwarnnam(name, "incompatible options for -T");
|
||||
|
@ -2478,34 +2571,41 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
return 1;
|
||||
}
|
||||
on &= ~off;
|
||||
if (!argv[1] || (argv[2] && argv[3])) {
|
||||
nargs = arrlen(argv) + (assigns ? countlinknodes(assigns) : 0);
|
||||
if (nargs < 2) {
|
||||
zwarnnam(name, "-T requires names of scalar and array");
|
||||
unqueue_signals();
|
||||
return 1;
|
||||
}
|
||||
if (nargs > 3) {
|
||||
zwarnnam(name, "too many arguments for -T");
|
||||
unqueue_signals();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Third argument, if given, is character used to join
|
||||
* the elements of the array in the scalar.
|
||||
*/
|
||||
if (!argv[2])
|
||||
joinchar = ':';
|
||||
else if (!*argv[2])
|
||||
joinchar = 0;
|
||||
else if (*argv[2] == Meta)
|
||||
joinchar = argv[2][1] ^ 32;
|
||||
else
|
||||
joinchar = *argv[2];
|
||||
|
||||
if (!(asg = getasg(argv[0]))) {
|
||||
if (!(asg = getasg(&argv, assigns))) {
|
||||
unqueue_signals();
|
||||
return 1;
|
||||
}
|
||||
asg0 = *asg;
|
||||
if (!(asg = getasg(argv[1]))) {
|
||||
if (ASG_ARRAYP(&asg0)) {
|
||||
unqueue_signals();
|
||||
zwarnnam(name, "first argument of tie must be scalar: %s",
|
||||
asg0.name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(asg = getasg(&argv, assigns))) {
|
||||
unqueue_signals();
|
||||
return 1;
|
||||
}
|
||||
if (!ASG_ARRAYP(asg) && asg->value.scalar) {
|
||||
unqueue_signals();
|
||||
zwarnnam(name, "second argument of tie must be array: %s",
|
||||
asg->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!strcmp(asg0.name, asg->name)) {
|
||||
unqueue_signals();
|
||||
zerrnam(name, "can't tie a variable to itself: %s", asg0.name);
|
||||
|
@ -2516,6 +2616,36 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
zerrnam(name, "can't tie array elements: %s", asg0.name);
|
||||
return 1;
|
||||
}
|
||||
if (ASG_VALUEP(asg) && ASG_VALUEP(&asg0)) {
|
||||
unqueue_signals();
|
||||
zerrnam(name, "only one tied parameter can have value: %s", asg0.name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Third argument, if given, is character used to join
|
||||
* the elements of the array in the scalar.
|
||||
*/
|
||||
if (*argv)
|
||||
joinstr = *argv;
|
||||
else if (assigns && firstnode(assigns)) {
|
||||
Asgment nextasg = (Asgment)firstnode(assigns);
|
||||
if (ASG_ARRAYP(nextasg) || ASG_VALUEP(nextasg)) {
|
||||
zwarnnam(name, "third argument of tie must be join character");
|
||||
unqueue_signals();
|
||||
return 1;
|
||||
}
|
||||
joinstr = nextasg->name;
|
||||
} else
|
||||
joinstr = NULL;
|
||||
if (!joinstr)
|
||||
joinchar = ':';
|
||||
else if (!*joinstr)
|
||||
joinchar = 0;
|
||||
else if (*joinstr == Meta)
|
||||
joinchar = joinstr[1] ^ 32;
|
||||
else
|
||||
joinchar = *joinstr;
|
||||
/*
|
||||
* Keep the old value of the scalar. We need to do this
|
||||
* here as if it is already tied to the same array it
|
||||
|
@ -2537,8 +2667,8 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
struct tieddata *tdp = (struct tieddata*)pm->u.data;
|
||||
/* Update join character */
|
||||
tdp->joinchar = joinchar;
|
||||
if (asg0.value)
|
||||
setsparam(asg0.name, ztrdup(asg0.value));
|
||||
if (asg0.value.scalar)
|
||||
setsparam(asg0.name, ztrdup(asg0.value.scalar));
|
||||
return 0;
|
||||
} else {
|
||||
zwarnnam(name, "can't tie already tied scalar: %s",
|
||||
|
@ -2546,7 +2676,8 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
}
|
||||
return 1;
|
||||
}
|
||||
if (!asg0.value && !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)))
|
||||
if (!asg0.value.scalar && !asg->value.array &&
|
||||
!(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)))
|
||||
oldval = ztrdup(getsparam(asg0.name));
|
||||
on |= (pm->node.flags & PM_EXPORTED);
|
||||
}
|
||||
|
@ -2554,12 +2685,18 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
* Create the tied array; this is normal except that
|
||||
* it has the PM_TIED flag set. Do it first because
|
||||
* we need the address.
|
||||
*
|
||||
* Don't attempt to set it yet, it's too early
|
||||
* to be exported properly.
|
||||
*/
|
||||
asg2.name = asg->name;
|
||||
asg2.is_array = 0;
|
||||
asg2.value.array = (LinkList)0;
|
||||
if (!(apm=typeset_single(name, asg->name,
|
||||
(Param)paramtab->getnode(paramtab,
|
||||
asg->name),
|
||||
func, (on | PM_ARRAY) & ~PM_EXPORTED,
|
||||
off, roff, asg->value, NULL, ops, 0))) {
|
||||
off, roff, &asg2, NULL, ops, 0))) {
|
||||
if (oldval)
|
||||
zsfree(oldval);
|
||||
unqueue_signals();
|
||||
|
@ -2572,7 +2709,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
if (!(pm=typeset_single(name, asg0.name,
|
||||
(Param)paramtab->getnode(paramtab,
|
||||
asg0.name),
|
||||
func, on, off, roff, asg0.value, apm,
|
||||
func, on, off, roff, &asg0, apm,
|
||||
ops, joinchar))) {
|
||||
if (oldval)
|
||||
zsfree(oldval);
|
||||
|
@ -2591,7 +2728,9 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
if (apm->ename)
|
||||
zsfree(apm->ename);
|
||||
apm->ename = ztrdup(asg0.name);
|
||||
if (oldval)
|
||||
if (asg->value.array)
|
||||
setaparam(asg->name, zlinklist2array(asg->value.array));
|
||||
else if (oldval)
|
||||
setsparam(asg0.name, oldval);
|
||||
unqueue_signals();
|
||||
|
||||
|
@ -2611,18 +2750,18 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
printflags |= PRINT_NAMEONLY;
|
||||
}
|
||||
|
||||
while ((asg = getasg(*argv++))) {
|
||||
while ((asg = getasg(&argv, assigns))) {
|
||||
LinkList pmlist = newlinklist();
|
||||
LinkNode pmnode;
|
||||
|
||||
tokenize(asg->name); /* expand argument */
|
||||
if (!(pprog = patcompile(asg->name, 0, NULL))) {
|
||||
untokenize(asg->name);
|
||||
zwarnnam(name, "bad pattern : %s", argv[-1]);
|
||||
zwarnnam(name, "bad pattern : %s", asg->name);
|
||||
returnval = 1;
|
||||
continue;
|
||||
}
|
||||
if (OPT_PLUS(ops,'m') && !asg->value) {
|
||||
if (OPT_PLUS(ops,'m') && !ASG_VALUEP(asg)) {
|
||||
scanmatchtable(paramtab, pprog, 1, on|roff, 0,
|
||||
paramtab->printnode, printflags);
|
||||
continue;
|
||||
|
@ -2648,7 +2787,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) {
|
||||
pm = (Param) getdata(pmnode);
|
||||
if (!typeset_single(name, pm->node.nam, pm, func, on, off, roff,
|
||||
asg->value, NULL, ops, 0))
|
||||
asg, NULL, ops, 0))
|
||||
returnval = 1;
|
||||
}
|
||||
}
|
||||
|
@ -2657,7 +2796,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
}
|
||||
|
||||
/* Take arguments literally. Don't glob */
|
||||
while ((asg = getasg(*argv++))) {
|
||||
while ((asg = getasg(&argv, assigns))) {
|
||||
HashNode hn = (paramtab == realparamtab ?
|
||||
gethashnode2(paramtab, asg->name) :
|
||||
paramtab->getnode(paramtab, asg->name));
|
||||
|
@ -2671,7 +2810,7 @@ bin_typeset(char *name, char **argv, Options ops, int func)
|
|||
continue;
|
||||
}
|
||||
if (!typeset_single(name, asg->name, (Param)hn,
|
||||
func, on, off, roff, asg->value, NULL,
|
||||
func, on, off, roff, asg, NULL,
|
||||
ops, 0))
|
||||
returnval = 1;
|
||||
}
|
||||
|
@ -3514,7 +3653,7 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func))
|
|||
}
|
||||
|
||||
queue_signals();
|
||||
for (;*argv;++argv) {
|
||||
while (*argv) {
|
||||
void *hn;
|
||||
if (OPT_ISSET(ops,'m')) {
|
||||
/* with the -m option, treat the argument as a glob pattern */
|
||||
|
@ -3529,12 +3668,12 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func))
|
|||
}
|
||||
continue;
|
||||
}
|
||||
if (!(asg = getasg(*argv))) {
|
||||
if (!(asg = getasg(&argv, NULL))) {
|
||||
zwarnnam(name, "bad assignment");
|
||||
returnval = 1;
|
||||
} else if (asg->value) {
|
||||
} else if (ASG_VALUEP(asg)) {
|
||||
if(isset(RESTRICTED)) {
|
||||
zwarnnam(name, "restricted: %s", asg->value);
|
||||
zwarnnam(name, "restricted: %s", asg->value.scalar);
|
||||
returnval = 1;
|
||||
} else {
|
||||
/* The argument is of the form foo=bar, *
|
||||
|
@ -3550,12 +3689,12 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func))
|
|||
} else {
|
||||
Nameddir nd = hn = zshcalloc(sizeof *nd);
|
||||
nd->node.flags = 0;
|
||||
nd->dir = ztrdup(asg->value);
|
||||
nd->dir = ztrdup(asg->value.scalar);
|
||||
}
|
||||
} else {
|
||||
Cmdnam cn = hn = zshcalloc(sizeof *cn);
|
||||
cn->node.flags = HASHED;
|
||||
cn->u.cmd = ztrdup(asg->value);
|
||||
cn->u.cmd = ztrdup(asg->value.scalar);
|
||||
}
|
||||
ht->addnode(ht, ztrdup(asg->name), hn);
|
||||
if(OPT_ISSET(ops,'v'))
|
||||
|
@ -3761,12 +3900,12 @@ bin_alias(char *name, char **argv, Options ops, UNUSED(int func))
|
|||
|
||||
/* Take arguments literally. Don't glob */
|
||||
queue_signals();
|
||||
while ((asg = getasg(*argv++))) {
|
||||
if (asg->value && !OPT_ISSET(ops,'L')) {
|
||||
while ((asg = getasg(&argv, NULL))) {
|
||||
if (asg->value.scalar && !OPT_ISSET(ops,'L')) {
|
||||
/* The argument is of the form foo=bar and we are not *
|
||||
* forcing a listing with -L, so define an alias */
|
||||
ht->addnode(ht, ztrdup(asg->name),
|
||||
createaliasnode(ztrdup(asg->value), flags1));
|
||||
createaliasnode(ztrdup(asg->value.scalar), flags1));
|
||||
} else if ((a = (Alias) ht->getnode(ht, asg->name))) {
|
||||
/* display alias if appropriate */
|
||||
if (!type_opts || ht == sufaliastab ||
|
||||
|
|
176
Src/exec.c
176
Src/exec.c
|
@ -2437,13 +2437,13 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
|||
char *text;
|
||||
int save[10];
|
||||
int fil, dfil, is_cursh, type, do_exec = 0, redir_err = 0, i, htok = 0;
|
||||
int nullexec = 0, assign = 0, forked = 0;
|
||||
int nullexec = 0, assign = 0, forked = 0, postassigns = 0;
|
||||
int is_shfunc = 0, is_builtin = 0, is_exec = 0, use_defpath = 0;
|
||||
/* Various flags to the command. */
|
||||
int cflags = 0, orig_cflags = 0, checked = 0, oautocont = -1;
|
||||
LinkList redir;
|
||||
wordcode code;
|
||||
Wordcode beg = state->pc, varspc;
|
||||
Wordcode beg = state->pc, varspc, assignspc = (Wordcode)0;
|
||||
FILE *oxtrerr = xtrerr, *newxtrerr = NULL;
|
||||
|
||||
doneps4 = 0;
|
||||
|
@ -2464,8 +2464,28 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
|||
/* It would be nice if we could use EC_DUPTOK instead of EC_DUP here.
|
||||
* But for that we would need to check/change all builtins so that
|
||||
* they don't modify their argument strings. */
|
||||
args = (type == WC_SIMPLE ?
|
||||
ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok) : NULL);
|
||||
switch (type) {
|
||||
case WC_SIMPLE:
|
||||
args = ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok);
|
||||
break;
|
||||
|
||||
case WC_TYPESET:
|
||||
args = ecgetlist(state, WC_TYPESET_ARGC(code), EC_DUP, &htok);
|
||||
postassigns = *state->pc++;
|
||||
assignspc = state->pc;
|
||||
for (i = 0; i < postassigns; i++) {
|
||||
code = *state->pc;
|
||||
DPUTS(wc_code(code) != WC_ASSIGN,
|
||||
"BUG: miscounted typeset assignments");
|
||||
state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ?
|
||||
3 : WC_ASSIGN_NUM(code) + 2);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
args = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If assignment but no command get the status from variable
|
||||
* assignment.
|
||||
|
@ -2488,7 +2508,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
|||
|
||||
/* If the command begins with `%', then assume it is a *
|
||||
* reference to a job in the job table. */
|
||||
if (type == WC_SIMPLE && args && nonempty(args) &&
|
||||
if ((type == WC_SIMPLE || type == WC_TYPESET) && args && nonempty(args) &&
|
||||
*(char *)peekfirst(args) == '%') {
|
||||
if (how & Z_DISOWN) {
|
||||
oautocont = opts[AUTOCONTINUE];
|
||||
|
@ -2517,20 +2537,32 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
|||
* command if it contains some tokens (e.g. x=ex; ${x}port), so this *
|
||||
* only works in simple cases. has_token() is called to make sure *
|
||||
* this really is a simple case. */
|
||||
if (type == WC_SIMPLE) {
|
||||
if (type == WC_SIMPLE || type == WC_TYPESET) {
|
||||
while (args && nonempty(args)) {
|
||||
char *cmdarg = (char *) peekfirst(args);
|
||||
checked = !has_token(cmdarg);
|
||||
if (!checked)
|
||||
break;
|
||||
if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) &&
|
||||
(hn = shfunctab->getnode(shfunctab, cmdarg))) {
|
||||
is_shfunc = 1;
|
||||
break;
|
||||
}
|
||||
if (!(hn = builtintab->getnode(builtintab, cmdarg))) {
|
||||
checked = !(cflags & BINF_BUILTIN);
|
||||
break;
|
||||
if (type == WC_TYPESET &&
|
||||
(hn = builtintab->getnode2(builtintab, cmdarg))) {
|
||||
/*
|
||||
* If reserved word for typeset command found (and so
|
||||
* enabled), use regardless of whether builtin is
|
||||
* enabled as we share the implementation.
|
||||
*
|
||||
* Reserved words take precedence over shell functions.
|
||||
*/
|
||||
checked = 1;
|
||||
} else {
|
||||
if (!(cflags & (BINF_BUILTIN | BINF_COMMAND)) &&
|
||||
(hn = shfunctab->getnode(shfunctab, cmdarg))) {
|
||||
is_shfunc = 1;
|
||||
break;
|
||||
}
|
||||
if (!(hn = builtintab->getnode(builtintab, cmdarg))) {
|
||||
checked = !(cflags & BINF_BUILTIN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
orig_cflags |= cflags;
|
||||
cflags &= ~BINF_BUILTIN & ~BINF_COMMAND;
|
||||
|
@ -2661,7 +2693,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
|||
if (args && htok)
|
||||
prefork(args, esprefork);
|
||||
|
||||
if (type == WC_SIMPLE) {
|
||||
if (type == WC_SIMPLE || type == WC_TYPESET) {
|
||||
int unglobbed = 0;
|
||||
|
||||
for (;;) {
|
||||
|
@ -2897,7 +2929,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
|||
return;
|
||||
}
|
||||
|
||||
if (type == WC_SIMPLE && !nullexec) {
|
||||
if ((type == WC_SIMPLE || type == WC_TYPESET) && !nullexec) {
|
||||
char *s;
|
||||
char trycd = (isset(AUTOCD) && isset(SHINSTDIN) &&
|
||||
(!redir || empty(redir)) && args && !empty(args) &&
|
||||
|
@ -3457,9 +3489,117 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
|||
execshfunc((Shfunc) hn, args);
|
||||
} else {
|
||||
/* It's a builtin */
|
||||
LinkList assigns = (LinkList)0;
|
||||
if (forked)
|
||||
closem(FDT_INTERNAL);
|
||||
lastval = execbuiltin(args, (Builtin) hn);
|
||||
if (postassigns) {
|
||||
Wordcode opc = state->pc;
|
||||
state->pc = assignspc;
|
||||
assigns = newlinklist();
|
||||
while (postassigns--) {
|
||||
wordcode ac = *state->pc++;
|
||||
char *name = ecgetstr(state, EC_DUPTOK, &htok);
|
||||
Asgment asg;
|
||||
local_list1(svl);
|
||||
|
||||
DPUTS(wc_code(ac) != WC_ASSIGN,
|
||||
"BUG: bad assignment list for typeset");
|
||||
if (htok) {
|
||||
init_list1(svl, name);
|
||||
if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR &&
|
||||
WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) {
|
||||
char *data;
|
||||
/*
|
||||
* Special case: this is a name only, so
|
||||
* it's not required to be a single
|
||||
* expansion. Furthermore, for
|
||||
* consistency with the builtin
|
||||
* interface, it may expand into
|
||||
* scalar assignments:
|
||||
* ass=(one=two three=four)
|
||||
* typeset a=b $ass
|
||||
*/
|
||||
/* Unused dummy value for name */
|
||||
(void)ecgetstr(state, EC_DUPTOK, &htok);
|
||||
prefork(&svl, PREFORK_TYPESET);
|
||||
if (errflag) {
|
||||
state->pc = opc;
|
||||
break;
|
||||
}
|
||||
globlist(&svl, 0);
|
||||
if (errflag) {
|
||||
state->pc = opc;
|
||||
break;
|
||||
}
|
||||
while ((data = ugetnode(&svl))) {
|
||||
char *ptr;
|
||||
asg = (Asgment)zhalloc(sizeof(struct asgment));
|
||||
asg->is_array = 0;
|
||||
if ((ptr = strchr(data, '='))) {
|
||||
*ptr++ = '\0';
|
||||
asg->name = data;
|
||||
asg->value.scalar = ptr;
|
||||
} else {
|
||||
asg->name = data;
|
||||
asg->value.scalar = NULL;
|
||||
}
|
||||
uaddlinknode(assigns, &asg->node);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
prefork(&svl, PREFORK_SINGLE);
|
||||
name = empty(&svl) ? "" :
|
||||
(char *)getdata(firstnode(&svl));
|
||||
}
|
||||
untokenize(name);
|
||||
asg = (Asgment)zhalloc(sizeof(struct asgment));
|
||||
asg->name = name;
|
||||
if (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR) {
|
||||
char *val = ecgetstr(state, EC_DUPTOK, &htok);
|
||||
asg->is_array = 0;
|
||||
if (WC_ASSIGN_TYPE2(ac) == WC_ASSIGN_INC) {
|
||||
/* Fake assignment, no value */
|
||||
asg->value.scalar = NULL;
|
||||
} else {
|
||||
if (htok) {
|
||||
init_list1(svl, val);
|
||||
prefork(&svl, PREFORK_SINGLE|PREFORK_ASSIGN);
|
||||
if (errflag) {
|
||||
state->pc = opc;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* No globassign for typeset
|
||||
* arguments, thank you
|
||||
*/
|
||||
val = empty(&svl) ? "" :
|
||||
(char *)getdata(firstnode(&svl));
|
||||
}
|
||||
untokenize(val);
|
||||
asg->value.scalar = val;
|
||||
}
|
||||
} else {
|
||||
asg->is_array = 1;
|
||||
asg->value.array =
|
||||
ecgetlist(state, WC_ASSIGN_NUM(ac),
|
||||
EC_DUPTOK, &htok);
|
||||
prefork(asg->value.array, PREFORK_ASSIGN);
|
||||
if (errflag) {
|
||||
state->pc = opc;
|
||||
break;
|
||||
}
|
||||
globlist(asg->value.array, 0);
|
||||
if (errflag) {
|
||||
state->pc = opc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uaddlinknode(assigns, &asg->node);
|
||||
}
|
||||
state->pc = opc;
|
||||
}
|
||||
lastval = execbuiltin(args, assigns, (Builtin) hn);
|
||||
fflush(stdout);
|
||||
if (save[1] == -2) {
|
||||
if (ferror(stdout)) {
|
||||
|
@ -3501,7 +3641,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
|
|||
if (!subsh && isset(RCS) && interact && !nohistsave)
|
||||
savehistfile(NULL, 1, HFILE_USE_OPTIONS);
|
||||
}
|
||||
if (type == WC_SIMPLE) {
|
||||
if (type == WC_SIMPLE || type == WC_TYPESET) {
|
||||
if (varspc) {
|
||||
int addflags = ADDVAR_EXPORT|ADDVAR_RESTRICT;
|
||||
if (forked)
|
||||
|
|
|
@ -1050,22 +1050,29 @@ static struct reswd reswds[] = {
|
|||
{{NULL, "}", 0}, OUTBRACE},
|
||||
{{NULL, "case", 0}, CASE},
|
||||
{{NULL, "coproc", 0}, COPROC},
|
||||
{{NULL, "declare", 0}, TYPESET},
|
||||
{{NULL, "do", 0}, DOLOOP},
|
||||
{{NULL, "done", 0}, DONE},
|
||||
{{NULL, "elif", 0}, ELIF},
|
||||
{{NULL, "else", 0}, ELSE},
|
||||
{{NULL, "end", 0}, ZEND},
|
||||
{{NULL, "esac", 0}, ESAC},
|
||||
{{NULL, "export", 0}, TYPESET},
|
||||
{{NULL, "fi", 0}, FI},
|
||||
{{NULL, "float", 0}, TYPESET},
|
||||
{{NULL, "for", 0}, FOR},
|
||||
{{NULL, "foreach", 0}, FOREACH},
|
||||
{{NULL, "function", 0}, FUNC},
|
||||
{{NULL, "if", 0}, IF},
|
||||
{{NULL, "integer", 0}, TYPESET},
|
||||
{{NULL, "local", 0}, TYPESET},
|
||||
{{NULL, "nocorrect", 0}, NOCORRECT},
|
||||
{{NULL, "readonly", 0}, TYPESET},
|
||||
{{NULL, "repeat", 0}, REPEAT},
|
||||
{{NULL, "select", 0}, SELECT},
|
||||
{{NULL, "then", 0}, THEN},
|
||||
{{NULL, "time", 0}, TIME},
|
||||
{{NULL, "typeset", 0}, TYPESET},
|
||||
{{NULL, "until", 0}, UNTIL},
|
||||
{{NULL, "while", 0}, WHILE},
|
||||
{{NULL, NULL, 0}, 0}
|
||||
|
|
|
@ -1182,7 +1182,7 @@ gettokstr(int c, int sub)
|
|||
c = Outpar;
|
||||
}
|
||||
} else if (peek != ENVSTRING &&
|
||||
incmdpos && !bct && !brct) {
|
||||
(incmdpos || intypeset) && !bct && !brct) {
|
||||
char *t = tokstr;
|
||||
if (idigit(*t))
|
||||
while (++t < lexbuf.ptr && idigit(*t));
|
||||
|
@ -1200,7 +1200,7 @@ gettokstr(int c, int sub)
|
|||
t++;
|
||||
if (t == lexbuf.ptr) {
|
||||
e = hgetc();
|
||||
if (e == '(' && incmdpos) {
|
||||
if (e == '(') {
|
||||
*lexbuf.ptr = '\0';
|
||||
return ENVARRAY;
|
||||
}
|
||||
|
|
|
@ -172,7 +172,7 @@ static struct optname optns[] = {
|
|||
{{NULL, "kshautoload", OPT_EMULATE|OPT_BOURNE}, KSHAUTOLOAD},
|
||||
{{NULL, "kshglob", OPT_EMULATE|OPT_KSH}, KSHGLOB},
|
||||
{{NULL, "kshoptionprint", OPT_EMULATE|OPT_KSH}, KSHOPTIONPRINT},
|
||||
{{NULL, "kshtypeset", OPT_EMULATE|OPT_KSH}, KSHTYPESET},
|
||||
{{NULL, "kshtypeset", 0}, KSHTYPESET},
|
||||
{{NULL, "kshzerosubscript", 0}, KSHZEROSUBSCRIPT},
|
||||
{{NULL, "listambiguous", OPT_ALL}, LISTAMBIGUOUS},
|
||||
{{NULL, "listbeep", OPT_ALL}, LISTBEEP},
|
||||
|
|
131
Src/parse.c
131
Src/parse.c
|
@ -63,6 +63,11 @@ int isnewlin;
|
|||
/**/
|
||||
int infor;
|
||||
|
||||
/* != 0 if parsing arguments of typeset etc. */
|
||||
|
||||
/**/
|
||||
int intypeset;
|
||||
|
||||
/* list of here-documents */
|
||||
|
||||
/**/
|
||||
|
@ -118,11 +123,20 @@ struct heredocs *hdocs;
|
|||
* WC_ASSIGN
|
||||
* - data contains type (scalar, array) and number of array-elements
|
||||
* - followed by name and value
|
||||
* Note variant for WC_TYPESET assignments: WC_ASSIGN_INC indicates
|
||||
* a name with no equals, not an =+ which isn't valid here.
|
||||
*
|
||||
* WC_SIMPLE
|
||||
* - data contains the number of arguments (plus command)
|
||||
* - followed by strings
|
||||
*
|
||||
* WC_TYPESET
|
||||
* Variant of WC_SIMPLE used when TYPESET reserved word found.
|
||||
* - data contains the number of string arguments (plus command)
|
||||
* - followed by strings
|
||||
* - followed by number of assignments
|
||||
* - followed by assignments if non-zero number.
|
||||
*
|
||||
* WC_SUBSH
|
||||
* - data unused
|
||||
* - followed by list
|
||||
|
@ -257,6 +271,7 @@ parse_context_save(struct parse_stack *ps, int toplevel)
|
|||
ps->incasepat = incasepat;
|
||||
ps->isnewlin = isnewlin;
|
||||
ps->infor = infor;
|
||||
ps->intypeset = intypeset;
|
||||
|
||||
ps->hdocs = hdocs;
|
||||
ps->eclen = eclen;
|
||||
|
@ -290,6 +305,7 @@ parse_context_restore(const struct parse_stack *ps, int toplevel)
|
|||
incasepat = ps->incasepat;
|
||||
isnewlin = ps->isnewlin;
|
||||
infor = ps->infor;
|
||||
intypeset = ps->intypeset;
|
||||
|
||||
hdocs = ps->hdocs;
|
||||
eclen = ps->eclen;
|
||||
|
@ -430,7 +446,7 @@ init_parse_status(void)
|
|||
* between the two it's a bit ambiguous. We clear them when
|
||||
* using the lexical analyser for strings as well as here.
|
||||
*/
|
||||
incasepat = incond = inredir = infor = 0;
|
||||
incasepat = incond = inredir = infor = intypeset = 0;
|
||||
incmdpos = 1;
|
||||
}
|
||||
|
||||
|
@ -992,6 +1008,7 @@ par_cmd(int *cmplx, int zsh_construct)
|
|||
incmdpos = 1;
|
||||
incasepat = 0;
|
||||
incond = 0;
|
||||
intypeset = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1709,7 +1726,8 @@ static int
|
|||
par_simple(int *cmplx, int nr)
|
||||
{
|
||||
int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0;
|
||||
int c = *cmplx, nrediradd, assignments = 0;
|
||||
int c = *cmplx, nrediradd, assignments = 0, ppost = 0, is_typeset = 0;
|
||||
wordcode postassigns = 0;
|
||||
|
||||
r = ecused;
|
||||
for (;;) {
|
||||
|
@ -1717,31 +1735,32 @@ par_simple(int *cmplx, int nr)
|
|||
*cmplx = c = 1;
|
||||
nocorrect = 1;
|
||||
} else if (tok == ENVSTRING) {
|
||||
char *p, *name, *str;
|
||||
char *ptr, *name, *str;
|
||||
|
||||
name = tokstr;
|
||||
for (p = tokstr; *p && *p != Inbrack && *p != '=' && *p != '+';
|
||||
p++);
|
||||
if (*p == Inbrack) skipparens(Inbrack, Outbrack, &p);
|
||||
if (*p == '+') {
|
||||
*p++ = '\0';
|
||||
for (ptr = tokstr;
|
||||
*ptr && *ptr != Inbrack && *ptr != '=' && *ptr != '+';
|
||||
ptr++);
|
||||
if (*ptr == Inbrack) skipparens(Inbrack, Outbrack, &ptr);
|
||||
if (*ptr == '+') {
|
||||
*ptr++ = '\0';
|
||||
ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0));
|
||||
} else
|
||||
ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0));
|
||||
|
||||
if (*p == '=') {
|
||||
*p = '\0';
|
||||
str = p + 1;
|
||||
|
||||
if (*ptr == '=') {
|
||||
*ptr = '\0';
|
||||
str = ptr + 1;
|
||||
} else
|
||||
equalsplit(tokstr, &str);
|
||||
for (p = str; *p; p++) {
|
||||
for (ptr = str; *ptr; ptr++) {
|
||||
/*
|
||||
* We can't treat this as "simple" if it contains
|
||||
* expansions that require process subsitution, since then
|
||||
* we need process handling.
|
||||
*/
|
||||
if (p[1] == Inpar &&
|
||||
(*p == Equals || *p == Inang || *p == OutangProc)) {
|
||||
if (ptr[1] == Inpar &&
|
||||
(*ptr == Equals || *ptr == Inang || *ptr == OutangProc)) {
|
||||
*cmplx = 1;
|
||||
break;
|
||||
}
|
||||
|
@ -1786,14 +1805,18 @@ par_simple(int *cmplx, int nr)
|
|||
p = ecadd(WCB_SIMPLE(0));
|
||||
|
||||
for (;;) {
|
||||
if (tok == STRING) {
|
||||
if (tok == STRING || tok == TYPESET) {
|
||||
int redir_var = 0;
|
||||
|
||||
*cmplx = 1;
|
||||
incmdpos = 0;
|
||||
|
||||
if (tok == TYPESET)
|
||||
intypeset = is_typeset = 1;
|
||||
|
||||
if (!isset(IGNOREBRACES) && *tokstr == Inbrace)
|
||||
{
|
||||
/* Look for redirs of the form {var}>file etc. */
|
||||
char *eptr = tokstr + strlen(tokstr) - 1;
|
||||
char *ptr = eptr;
|
||||
|
||||
|
@ -1824,8 +1847,21 @@ par_simple(int *cmplx, int nr)
|
|||
|
||||
if (!redir_var)
|
||||
{
|
||||
ecstr(tokstr);
|
||||
argc++;
|
||||
if (postassigns) {
|
||||
/*
|
||||
* We're in the variable part of a typeset,
|
||||
* but this doesn't have an assignment.
|
||||
* We'll parse it as if it does, but mark
|
||||
* it specially with WC_ASSIGN_INC.
|
||||
*/
|
||||
postassigns++;
|
||||
ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_INC, 0));
|
||||
ecstr(tokstr);
|
||||
ecstr(""); /* TBD can possibly optimise out */
|
||||
} else {
|
||||
ecstr(tokstr);
|
||||
argc++;
|
||||
}
|
||||
zshlex();
|
||||
}
|
||||
} else if (IS_REDIROP(tok)) {
|
||||
|
@ -1833,6 +1869,50 @@ par_simple(int *cmplx, int nr)
|
|||
nrediradd = par_redir(&r, NULL);
|
||||
p += nrediradd;
|
||||
sr += nrediradd;
|
||||
} else if (tok == ENVSTRING) {
|
||||
char *ptr, *name, *str;
|
||||
|
||||
if (!postassigns++)
|
||||
ppost = ecadd(0);
|
||||
|
||||
name = tokstr;
|
||||
for (ptr = tokstr; *ptr && *ptr != Inbrack && *ptr != '=' && *ptr != '+';
|
||||
ptr++);
|
||||
if (*ptr == Inbrack) skipparens(Inbrack, Outbrack, &ptr);
|
||||
ecadd(WCB_ASSIGN(WC_ASSIGN_SCALAR, WC_ASSIGN_NEW, 0));
|
||||
|
||||
if (*ptr == '=') {
|
||||
*ptr = '\0';
|
||||
str = ptr + 1;
|
||||
} else
|
||||
equalsplit(tokstr, &str);
|
||||
ecstr(name);
|
||||
ecstr(str);
|
||||
zshlex();
|
||||
} else if (tok == ENVARRAY) {
|
||||
int n, parr;
|
||||
|
||||
if (!postassigns++)
|
||||
ppost = ecadd(0);
|
||||
|
||||
parr = ecadd(0);
|
||||
ecstr(tokstr);
|
||||
cmdpush(CS_ARRAY);
|
||||
/*
|
||||
* Careful here: this must be the typeset case,
|
||||
* but we need to tell the lexer not to look
|
||||
* for assignments until we've finished the
|
||||
* present one.
|
||||
*/
|
||||
intypeset = 0;
|
||||
zshlex();
|
||||
n = par_nl_wordlist();
|
||||
ecbuf[parr] = WCB_ASSIGN(WC_ASSIGN_ARRAY, WC_ASSIGN_NEW, n);
|
||||
cmdpop();
|
||||
intypeset = 1;
|
||||
if (tok != OUTPAR)
|
||||
YYERROR(oecused);
|
||||
zshlex();
|
||||
} else if (tok == INOUTPAR) {
|
||||
zlong oldlineno = lineno;
|
||||
int onp, so, oecssub = ecssub;
|
||||
|
@ -1841,7 +1921,7 @@ par_simple(int *cmplx, int nr)
|
|||
if (!isset(MULTIFUNCDEF) && argc > 1)
|
||||
YYERROR(oecused);
|
||||
/* Error if preceding assignments */
|
||||
if (assignments)
|
||||
if (assignments || postassigns)
|
||||
YYERROR(oecused);
|
||||
|
||||
*cmplx = c;
|
||||
|
@ -1947,9 +2027,18 @@ par_simple(int *cmplx, int nr)
|
|||
return 0;
|
||||
}
|
||||
incmdpos = 1;
|
||||
intypeset = 0;
|
||||
|
||||
if (!isfunc)
|
||||
ecbuf[p] = WCB_SIMPLE(argc);
|
||||
if (!isfunc) {
|
||||
if (is_typeset) {
|
||||
ecbuf[p] = WCB_TYPESET(argc);
|
||||
if (postassigns)
|
||||
ecbuf[ppost] = postassigns;
|
||||
else
|
||||
ecadd(0);
|
||||
} else
|
||||
ecbuf[p] = WCB_SIMPLE(argc);
|
||||
}
|
||||
|
||||
return sr + 1;
|
||||
}
|
||||
|
|
57
Src/text.c
57
Src/text.c
|
@ -155,6 +155,46 @@ taddlist(Estate state, int num)
|
|||
}
|
||||
}
|
||||
|
||||
/* add an assignment */
|
||||
|
||||
static void
|
||||
taddassign(wordcode code, Estate state, int typeset)
|
||||
{
|
||||
/* name */
|
||||
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
||||
/* value... maybe */
|
||||
if (WC_ASSIGN_TYPE2(code) == WC_ASSIGN_INC) {
|
||||
if (typeset) {
|
||||
/* dummy assignment --- just var name */
|
||||
(void)ecgetstr(state, EC_NODUP, NULL);
|
||||
taddchr(' ');
|
||||
return;
|
||||
}
|
||||
taddchr('+');
|
||||
}
|
||||
taddchr('=');
|
||||
if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) {
|
||||
taddchr('(');
|
||||
taddlist(state, WC_ASSIGN_NUM(code));
|
||||
taddstr(") ");
|
||||
} else {
|
||||
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
||||
taddchr(' ');
|
||||
}
|
||||
}
|
||||
|
||||
/* add a number of assignments from typeset */
|
||||
|
||||
/**/
|
||||
static void
|
||||
taddassignlist(Estate state, wordcode count)
|
||||
{
|
||||
while (count--) {
|
||||
wordcode code = *state->pc++;
|
||||
taddassign(code, state, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* add a newline, or something equivalent, to the text buffer */
|
||||
|
||||
/**/
|
||||
|
@ -439,22 +479,17 @@ gettext2(Estate state)
|
|||
}
|
||||
break;
|
||||
case WC_ASSIGN:
|
||||
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
||||
if (WC_ASSIGN_TYPE2(code) == WC_ASSIGN_INC) taddchr('+');
|
||||
taddchr('=');
|
||||
if (WC_ASSIGN_TYPE(code) == WC_ASSIGN_ARRAY) {
|
||||
taddchr('(');
|
||||
taddlist(state, WC_ASSIGN_NUM(code));
|
||||
taddstr(") ");
|
||||
} else {
|
||||
taddstr(ecgetstr(state, EC_NODUP, NULL));
|
||||
taddchr(' ');
|
||||
}
|
||||
taddassign(code, state, 0);
|
||||
break;
|
||||
case WC_SIMPLE:
|
||||
taddlist(state, WC_SIMPLE_ARGC(code));
|
||||
stack = 1;
|
||||
break;
|
||||
case WC_TYPESET:
|
||||
taddlist(state, WC_TYPESET_ARGC(code));
|
||||
taddassignlist(state, *state->pc++);
|
||||
stack = 1;
|
||||
break;
|
||||
case WC_SUBSH:
|
||||
if (!s) {
|
||||
taddstr("(");
|
||||
|
|
87
Src/zsh.h
87
Src/zsh.h
|
@ -336,7 +336,8 @@ enum lextok {
|
|||
THEN, /* then */
|
||||
TIME, /* time */ /* 60 */
|
||||
UNTIL, /* until */
|
||||
WHILE /* while */
|
||||
WHILE, /* while */
|
||||
TYPESET /* typeset or similar */
|
||||
};
|
||||
|
||||
/* Redirection types. If you modify this, you may also have to modify *
|
||||
|
@ -671,14 +672,6 @@ struct multio {
|
|||
int fds[MULTIOUNIT]; /* list of src/dests redirected to/from this fd */
|
||||
};
|
||||
|
||||
/* structure for foo=bar assignments */
|
||||
|
||||
struct asgment {
|
||||
struct asgment *next;
|
||||
char *name;
|
||||
char *value;
|
||||
};
|
||||
|
||||
/* lvalue for variable assignment/expansion */
|
||||
|
||||
struct value {
|
||||
|
@ -789,23 +782,24 @@ struct eccstr {
|
|||
#define WC_REDIR 4
|
||||
#define WC_ASSIGN 5
|
||||
#define WC_SIMPLE 6
|
||||
#define WC_SUBSH 7
|
||||
#define WC_CURSH 8
|
||||
#define WC_TIMED 9
|
||||
#define WC_FUNCDEF 10
|
||||
#define WC_FOR 11
|
||||
#define WC_SELECT 12
|
||||
#define WC_WHILE 13
|
||||
#define WC_REPEAT 14
|
||||
#define WC_CASE 15
|
||||
#define WC_IF 16
|
||||
#define WC_COND 17
|
||||
#define WC_ARITH 18
|
||||
#define WC_AUTOFN 19
|
||||
#define WC_TRY 20
|
||||
#define WC_TYPESET 7
|
||||
#define WC_SUBSH 8
|
||||
#define WC_CURSH 9
|
||||
#define WC_TIMED 10
|
||||
#define WC_FUNCDEF 11
|
||||
#define WC_FOR 12
|
||||
#define WC_SELECT 13
|
||||
#define WC_WHILE 14
|
||||
#define WC_REPEAT 15
|
||||
#define WC_CASE 16
|
||||
#define WC_IF 17
|
||||
#define WC_COND 18
|
||||
#define WC_ARITH 19
|
||||
#define WC_AUTOFN 20
|
||||
#define WC_TRY 21
|
||||
|
||||
/* increment as necessary */
|
||||
#define WC_COUNT 21
|
||||
#define WC_COUNT 22
|
||||
|
||||
#define WCB_END() wc_bld(WC_END, 0)
|
||||
|
||||
|
@ -849,6 +843,12 @@ struct eccstr {
|
|||
#define WC_ASSIGN_SCALAR 0
|
||||
#define WC_ASSIGN_ARRAY 1
|
||||
#define WC_ASSIGN_NEW 0
|
||||
/*
|
||||
* In normal assignment, this indicate += to append.
|
||||
* In assignment following a typeset, where that's not allowed,
|
||||
* we overload this to indicate a variable without an
|
||||
* assignment.
|
||||
*/
|
||||
#define WC_ASSIGN_INC 1
|
||||
#define WC_ASSIGN_NUM(C) (wc_data(C) >> 2)
|
||||
#define WCB_ASSIGN(T,A,N) wc_bld(WC_ASSIGN, ((T) | ((A) << 1) | ((N) << 2)))
|
||||
|
@ -856,6 +856,9 @@ struct eccstr {
|
|||
#define WC_SIMPLE_ARGC(C) wc_data(C)
|
||||
#define WCB_SIMPLE(N) wc_bld(WC_SIMPLE, (N))
|
||||
|
||||
#define WC_TYPESET_ARGC(C) wc_data(C)
|
||||
#define WCB_TYPESET(N) wc_bld(WC_TYPESET, (N))
|
||||
|
||||
#define WC_SUBSH_SKIP(C) wc_data(C)
|
||||
#define WCB_SUBSH(O) wc_bld(WC_SUBSH, (O))
|
||||
|
||||
|
@ -1140,6 +1143,34 @@ struct alias {
|
|||
/* is this an alias for suffix handling? */
|
||||
#define ALIAS_SUFFIX (1<<2)
|
||||
|
||||
/* structure for foo=bar assignments */
|
||||
|
||||
struct asgment {
|
||||
struct linknode node;
|
||||
char *name;
|
||||
int is_array;
|
||||
union {
|
||||
char *scalar;
|
||||
LinkList array;
|
||||
} value;
|
||||
};
|
||||
|
||||
/*
|
||||
* Assignment is array?
|
||||
*/
|
||||
#define ASG_ARRAYP(asg) ((asg)->is_array)
|
||||
|
||||
/*
|
||||
* Assignment has value?
|
||||
* We need to arrange for each of the values
|
||||
* to be the same type or the compiler will
|
||||
* get fed up.
|
||||
*/
|
||||
|
||||
#define ASG_VALUEP(asg) (ASG_ARRAYP(asg) ? \
|
||||
((asg)->value.array != (LinkList)0) : \
|
||||
((asg)->value.scalar != (char *)0))
|
||||
|
||||
/* node in command path hash table (cmdnamtab) */
|
||||
|
||||
struct cmdnam {
|
||||
|
@ -1268,6 +1299,7 @@ struct options {
|
|||
*/
|
||||
|
||||
typedef int (*HandlerFunc) _((char *, char **, Options, int));
|
||||
typedef int (*HandlerFuncAssign) _((char *, char **, LinkList, Options, int));
|
||||
#define NULLBINCMD ((HandlerFunc) 0)
|
||||
|
||||
struct builtin {
|
||||
|
@ -1311,6 +1343,12 @@ struct builtin {
|
|||
* does not terminate options.
|
||||
*/
|
||||
#define BINF_HANDLES_OPTS (1<<18)
|
||||
/*
|
||||
* Handles the assignement interface. The argv list actually contains
|
||||
* two nested litsts, the first of normal arguments, and the second of
|
||||
* assignment structures.
|
||||
*/
|
||||
#define BINF_ASSIGN (1<<19)
|
||||
|
||||
struct module {
|
||||
struct hashnode node;
|
||||
|
@ -2779,6 +2817,7 @@ struct parse_stack {
|
|||
int incasepat;
|
||||
int isnewlin;
|
||||
int infor;
|
||||
int intypeset;
|
||||
|
||||
int eclen, ecused, ecnpats;
|
||||
Wordcode ecbuf;
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
%prep
|
||||
|
||||
mkdir typeset.tmp && cd typeset.tmp
|
||||
|
||||
setopt noglob
|
||||
|
||||
scalar=scalar
|
||||
|
@ -231,7 +233,7 @@
|
|||
|
||||
typeset -T THIS will not work
|
||||
1:Tied array syntax
|
||||
?(eval):typeset:1: -T requires names of scalar and array
|
||||
?(eval):typeset:1: too many arguments for -T
|
||||
|
||||
local array[2]=x
|
||||
1:Illegal local array element assignment
|
||||
|
@ -508,3 +510,144 @@
|
|||
>a2=(three four)
|
||||
>typeset -r r1=yes
|
||||
>typeset -r r2=no
|
||||
|
||||
one=hidden two=hidden three=hidden four=hidden five=hidden
|
||||
fn() {
|
||||
local bleugh="four=vier"
|
||||
typeset -R10 one=eins two=(zwei dio) three $bleugh five=(cinq cinque)
|
||||
three=drei
|
||||
print -l $one $two $three $four $five
|
||||
}
|
||||
fn
|
||||
print -l $one $two $three $four $five
|
||||
0:typeset reserved word interface: basic
|
||||
> eins
|
||||
>zwei
|
||||
>dio
|
||||
> drei
|
||||
> vier
|
||||
>cinq
|
||||
>cinque
|
||||
>hidden
|
||||
>hidden
|
||||
>hidden
|
||||
>hidden
|
||||
>hidden
|
||||
|
||||
(
|
||||
setopt glob
|
||||
mkdir -p arrayglob
|
||||
touch arrayglob/{one,two,three,four,five,six,seven}
|
||||
fn() {
|
||||
typeset array=(arrayglob/[tf]*)
|
||||
print -l ${array:t}
|
||||
#
|
||||
typeset {first,second,third}=the_same_value array=(
|
||||
extends
|
||||
over
|
||||
multiple
|
||||
lines
|
||||
)
|
||||
print -l $first $second $third "$array"
|
||||
#
|
||||
integer i=$(echo 1 + 2 + 3 + 4)
|
||||
print $i
|
||||
#
|
||||
# only noted by accident this was broken..
|
||||
# we need to turn off special recognition
|
||||
# of assignments within assignments...
|
||||
typeset careful=( i=1 j=2 k=3 )
|
||||
print -l $careful
|
||||
}
|
||||
fn
|
||||
)
|
||||
0:typeset reserved word, more complicated cases
|
||||
>five
|
||||
>four
|
||||
>three
|
||||
>two
|
||||
>the_same_value
|
||||
>the_same_value
|
||||
>the_same_value
|
||||
>extends over multiple lines
|
||||
>10
|
||||
>i=1
|
||||
>j=2
|
||||
>k=3
|
||||
|
||||
(
|
||||
# reserved word is recognised at parsing.
|
||||
# yes, this is documented.
|
||||
# anyway, that means we need to
|
||||
# re-eval the function...
|
||||
fn='
|
||||
fn() {
|
||||
typeset foo=`echo one word=two`
|
||||
print $foo
|
||||
print $word
|
||||
}
|
||||
'
|
||||
print reserved
|
||||
eval $fn; fn
|
||||
print builtin
|
||||
disable -r typeset
|
||||
eval $fn; fn
|
||||
enable -r typeset
|
||||
disable typeset
|
||||
print reserved
|
||||
eval $fn; fn
|
||||
)
|
||||
0:reserved word and builtin interfaces
|
||||
>reserved
|
||||
>one word=two
|
||||
>
|
||||
>builtin
|
||||
>one
|
||||
>two
|
||||
>reserved
|
||||
>one word=two
|
||||
>
|
||||
|
||||
fn() {
|
||||
emulate -L zsh
|
||||
setopt typeset_silent
|
||||
local k
|
||||
typeset -A hash=(k1 v1 k2 v2)
|
||||
typeset foo=word array=(more than one word)
|
||||
for k in ${(ko)hash}; do
|
||||
print $k $hash[$k]
|
||||
done
|
||||
print -l $foo $array
|
||||
typeset -A hash
|
||||
typeset foo array
|
||||
for k in ${(ko)hash}; do
|
||||
print $k $hash[$k]
|
||||
done
|
||||
print -l $foo $array
|
||||
typeset hash=(k3 v3 k4 v4) array=(odd number here)
|
||||
for k in ${(ko)hash}; do
|
||||
print $k $hash[$k]
|
||||
done
|
||||
print -l $array
|
||||
}
|
||||
fn
|
||||
0:typeset preserves existing variable types
|
||||
>k1 v1
|
||||
>k2 v2
|
||||
>word
|
||||
>more
|
||||
>than
|
||||
>one
|
||||
>word
|
||||
>k1 v1
|
||||
>k2 v2
|
||||
>word
|
||||
>more
|
||||
>than
|
||||
>one
|
||||
>word
|
||||
>k3 v3
|
||||
>k4 v4
|
||||
>odd
|
||||
>number
|
||||
>here
|
||||
|
|
|
@ -199,5 +199,5 @@
|
|||
?+zsh_directory_name:4> [[ d == n ]]
|
||||
?+zsh_directory_name:12> [[ <parent>/very_long_directory_name == (#b)(*)/very_long_directory_name ]]
|
||||
?+zsh_directory_name:14> return 0
|
||||
?+fn:7> local 'd=~[<parent>:l]'
|
||||
?+fn:7> local d='~[<parent>:l]'
|
||||
?+fn:8> print '~[<parent>:l]'
|
||||
|
|
|
@ -570,6 +570,15 @@
|
|||
>unset
|
||||
>globassign
|
||||
|
||||
# This test is now somewhat artificial as
|
||||
# KSH_TYPESET only applies to the builtin
|
||||
# interface. Tests to the more standard
|
||||
# reserved word interface appear elsewhere.
|
||||
(
|
||||
# reserved words are handled during parsing,
|
||||
# hence eval...
|
||||
disable -r typeset
|
||||
eval '
|
||||
setopt kshtypeset
|
||||
ktvars=(ktv1 ktv2)
|
||||
typeset ktfoo=`echo arg1 arg2` $ktvars
|
||||
|
@ -580,6 +589,8 @@
|
|||
print $noktfoo
|
||||
print $+noktarg1 $+noktarg2
|
||||
unset ktfoo ktv1 ktv2 noktfoo noktarg2
|
||||
'
|
||||
)
|
||||
0:KSH_TYPESET option
|
||||
>1 1 0
|
||||
>arg1 arg2
|
||||
|
|
Loading…
Reference in a new issue