From a29746ee48c84b40add764f3cbd62d995179f72a Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 18 Jun 2015 14:54:41 +0100 Subject: First compiling attempt at typeset array handling. Completely untested and undebugged. --- Src/builtin.c | 234 +++++++++++++++++++++++++++++++++++++------------------- Src/exec.c | 102 +++++++++++++++++++++--- Src/hashtable.c | 4 + Src/lex.c | 4 +- Src/parse.c | 122 +++++++++++++++++++++++------ Src/text.c | 57 +++++++++++--- Src/zsh.h | 57 +++++++++++--- 7 files changed, 449 insertions(+), 131 deletions(-) diff --git a/Src/builtin.c b/Src/builtin.c index 0edc07024..bd5d614ff 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); @@ -447,7 +447,18 @@ execbuiltin(LinkList args, Builtin bn) 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 +1463,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 +1643,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 +1832,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 +1855,7 @@ getasg(char *s) return NULL; } asg.name = s; + asg.is_array = 0; /* search for `=' */ for (; *s && *s != '='; s++); @@ -1841,11 +1863,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 +1950,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 +1998,23 @@ 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)) + 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 +2062,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 +2088,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)) { @@ -2069,7 +2108,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), */ if (usepm) { 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 +2162,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')) @@ -2158,9 +2199,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 +2297,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,9 +2378,10 @@ 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) && !(pm->node.flags & (PM_ARRAY|PM_HASHED))) { Param ipm = pm; - if (!(pm = setsparam(pname, ztrdup(value)))) + 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, @@ -2371,11 +2419,10 @@ 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 (ASG_VALUEP(asg) && (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; } if (OPT_ISSET(ops,'p')) @@ -2384,11 +2431,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; @@ -2469,7 +2523,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) if (on & PM_TIED) { Param apm; struct asgment asg0; - char *oldval = NULL; + char *oldval = NULL, *joinstr; int joinchar; if (OPT_ISSET(ops,'m')) { @@ -2484,28 +2538,29 @@ bin_typeset(char *name, char **argv, Options ops, int func) 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 scalar: %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 +2571,31 @@ bin_typeset(char *name, char **argv, Options ops, int func) zerrnam(name, "can't tie array elements: %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) { + 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 +2617,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 +2626,7 @@ 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 && !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED))) oldval = ztrdup(getsparam(asg0.name)); on |= (pm->node.flags & PM_EXPORTED); } @@ -2559,7 +2639,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) (Param)paramtab->getnode(paramtab, asg->name), func, (on | PM_ARRAY) & ~PM_EXPORTED, - off, roff, asg->value, NULL, ops, 0))) { + off, roff, asg, NULL, ops, 0))) { if (oldval) zsfree(oldval); unqueue_signals(); @@ -2572,7 +2652,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); @@ -2611,7 +2691,7 @@ 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; @@ -2622,7 +2702,7 @@ bin_typeset(char *name, char **argv, Options ops, int func) 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 +2728,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 +2737,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 +2751,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 +3594,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 +3609,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 +3630,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 +3841,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..74059bfdc 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,7 +2537,7 @@ 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); @@ -2661,7 +2681,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 +2917,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 +3477,71 @@ 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--) { + Asgment asg = (Asgment)zhalloc(sizeof(struct asgment)); + wordcode ac = *state->pc++; + int htok; + char *name = ecgetstr(state, EC_DUPTOK, &htok); + local_list1(svl); + + DPUTS(wc_code(ac) != WC_ASSIGN, + "BUG: bad assignment list for typeset"); + if (htok) + untokenize(name); + 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 +3583,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..14c65e647 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -1050,6 +1050,7 @@ 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}, @@ -1061,11 +1062,14 @@ static struct reswd reswds[] = { {{NULL, "foreach", 0}, FOREACH}, {{NULL, "function", 0}, FUNC}, {{NULL, "if", 0}, IF}, + {{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/parse.c b/Src/parse.c index c932851d9..b0273e254 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,22 @@ 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 trailing assignments are + * needed. N.B.: if they are not, we use WC_SIMPLE even + * if this is a TYPESET keyword. + * - data contains the number of string arguments (plus command) + * - followed by strings + * - followed by number of assignments + * - followed by assignments + * * WC_SUBSH * - data unused * - followed by list @@ -257,6 +273,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 +307,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 +448,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 +1010,7 @@ par_cmd(int *cmplx, int zsh_construct) incmdpos = 1; incasepat = 0; incond = 0; + intypeset = 0; return 1; } @@ -1709,7 +1728,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; + wordcode postassigns = 0; r = ecused; for (;;) { @@ -1717,31 +1737,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 +1807,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 = 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,15 +1849,62 @@ par_simple(int *cmplx, int nr) if (!redir_var) { - ecstr(tokstr); - argc++; - zshlex(); + 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)) { *cmplx = c = 1; 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); + } else if (tok == ENVARRAY) { + int n, parr; + + if (!postassigns++) + ppost = ecadd(0); + + parr = ecadd(0); + ecstr(tokstr); + cmdpush(CS_ARRAY); + zshlex(); + n = par_nl_wordlist(); + ecbuf[parr] = WCB_ASSIGN(WC_ASSIGN_ARRAY, WC_ASSIGN_NEW, n); + cmdpop(); + if (tok != OUTPAR) + YYERROR(oecused); } else if (tok == INOUTPAR) { zlong oldlineno = lineno; int onp, so, oecssub = ecssub; @@ -1841,7 +1913,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 +2019,15 @@ par_simple(int *cmplx, int nr) return 0; } incmdpos = 1; + intypeset = 0; - if (!isfunc) - ecbuf[p] = WCB_SIMPLE(argc); + if (!isfunc) { + if (postassigns) { + ecbuf[p] = WCB_TYPESET(argc); + ecbuf[ppost] = postassigns; + } 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..0d43d37a7 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 { @@ -803,6 +796,7 @@ struct eccstr { #define WC_ARITH 18 #define WC_AUTOFN 19 #define WC_TRY 20 +#define WC_TYPESET 21 /* increment as necessary */ #define WC_COUNT 21 @@ -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; -- cgit 1.4.1