From 39b28980f38e83e15cdeb19a489b5659af97fe93 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 18 Jun 2015 14:54:41 +0100 Subject: 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. --- Src/exec.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 158 insertions(+), 18 deletions(-) (limited to 'Src/exec.c') 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) -- cgit 1.4.1