summaryrefslogtreecommitdiff
path: root/Src
diff options
context:
space:
mode:
authorPeter Stephenson <pws@zsh.org>2015-06-18 14:54:41 +0100
committerPeter Stephenson <pws@zsh.org>2015-06-24 10:21:12 +0100
commit39b28980f38e83e15cdeb19a489b5659af97fe93 (patch)
treee68f09fc59fc7008ff732704cbabed7e3df5f188 /Src
parenta68d22eb00ea5c85422d70d1be7efa42acfda739 (diff)
downloadzsh-39b28980f38e83e15cdeb19a489b5659af97fe93.tar.gz
zsh-39b28980f38e83e15cdeb19a489b5659af97fe93.zip
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.
Diffstat (limited to 'Src')
-rw-r--r--Src/builtin.c309
-rw-r--r--Src/exec.c176
-rw-r--r--Src/hashtable.c7
-rw-r--r--Src/lex.c4
-rw-r--r--Src/options.c2
-rw-r--r--Src/parse.c131
-rw-r--r--Src/text.c57
-rw-r--r--Src/zsh.h87
8 files changed, 611 insertions, 162 deletions
diff --git a/Src/builtin.c b/Src/builtin.c
index 0edc07024..ba384068d 100644
--- a/Src/builtin.c
+++ b/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 ||
diff --git a/Src/exec.c b/Src/exec.c
index 35a101b9c..57e8f6376 100644
--- a/Src/exec.c
+++ b/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)
diff --git a/Src/hashtable.c b/Src/hashtable.c
index 2b5524999..90739a882 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -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}
diff --git a/Src/lex.c b/Src/lex.c
index 841fb0b86..d56f670d2 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -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;
}
diff --git a/Src/options.c b/Src/options.c
index 78f603de1..da3d8308a 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -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},
diff --git a/Src/parse.c b/Src/parse.c
index c932851d9..477f8a0ab 100644
--- a/Src/parse.c
+++ b/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;
}
diff --git a/Src/text.c b/Src/text.c
index 850879699..a72ab33e6 100644
--- a/Src/text.c
+++ b/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("(");
diff --git a/Src/zsh.h b/Src/zsh.h
index fb04929d9..ee0609483 100644
--- a/Src/zsh.h
+++ b/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;