summaryrefslogtreecommitdiff
path: root/Src/exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/exec.c')
-rw-r--r--Src/exec.c176
1 files changed, 158 insertions, 18 deletions
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)