/* * Process here documents by providing the content, either as * result (globally allocated) string or in a temp file; if * unquoted, the string is expanded first. */ static int hereinval(struct ioword *iop, int sub, char **resbuf, struct shf *shf) { const char * volatile ccp = iop->heredoc; struct source *s, *osource; osource = source; newenv(E_ERRH); if (kshsetjmp(e->jbuf)) { source = osource; quitenv(shf); /* special to iosetup(): don't print error */ return (-2); } if (iop->ioflag & IOHERESTR) { ccp = evalstr(iop->delim, DOHERESTR | DOSCALAR | DOHEREDOC); } else if (sub) { /* do substitutions on the content of heredoc */ s = pushs(SSTRING, ATEMP); s->start = s->str = ccp; source = s; if (yylex(sub) != LWORD) internal_errorf("%s: %s", "herein", "yylex"); source = osource; ccp = evalstr(yylval.cp, DOSCALAR | DOHEREDOC); } if (resbuf == NULL) shf_puts(ccp, shf); else strdupx(*resbuf, ccp, APERM); quitenv(NULL); return (0); }
static int stringcomp(char *s, char *t, int op) { int p; char *l, *r; l = evalstr(s); r = evalstr(t); p = strcmp(l, r); switch (op) { case EQ: return (p == 0); case NE: return (p != 0); case LT: return (p < 0); case GT: return (p > 0); case LE: return (p <= 0); case GE: return (p >= 0); } return (0); }
static void readhere(struct ioword *iop) { int c; char *volatile eof; char *eofp; int skiptabs; XString xs; char *xp; int xpos; eof = evalstr(iop->delim, 0); if (!(iop->flag & IOEVAL)) ignore_backslash_newline++; Xinit(xs, xp, 256, ATEMP); for (;;) { eofp = eof; skiptabs = iop->flag & IOSKIP; xpos = Xsavepos(xs, xp); while ((c = getsc()) != 0) { if (skiptabs) { if (c == '\t') continue; skiptabs = 0; } if (c != *eofp) break; Xcheck(xs, xp); Xput(xs, xp, c); eofp++; } /* Allow EOF here so commands with out trailing newlines * will work (eg, ksh -c '...', $(...), etc). */ if (*eofp == '\0' && (c == 0 || c == '\n')) { xp = Xrestpos(xs, xp, xpos); break; } ungetsc(c); while ((c = getsc()) != '\n') { if (c == 0) yyerror("here document `%s' unclosed\n", eof); Xcheck(xs, xp); Xput(xs, xp, c); } Xcheck(xs, xp); Xput(xs, xp, c); } Xput(xs, xp, '\0'); iop->heredoc = Xclose(xs, xp); if (!(iop->flag & IOEVAL)) ignore_backslash_newline--; }
static const char * dbteste_getopnd(Test_env *te, Test_op op, bool do_eval) { const char *s = *te->pos.wp; if (!s) return (NULL); te->pos.wp++; if (!do_eval) return (null); if (op == TO_STEQL || op == TO_STNEQ) s = evalstr(s, DOTILDE | DOPAT); else s = evalstr(s, DOTILDE); return (s); }
static int stringcomp(char *s, char *t, int op) { int n, p; char *l, *r; l = evalstr(s); r = evalstr(t); /* we'll do a compare based on the length of the shortest string */ n = min(strlen(l), strlen(r)); p = strncmp(l, r, n); switch (op) { case EQ: return (p == 0); case NE: return (p != 0); case LT: return (p < 0); case GT: return (p > 0); case LE: return (p <= 0); case GE: return (p >= 0); } return (0); }
static const char * dbteste_getopnd(Test_env *te, Test_op op, bool do_eval) { const char *s = *te->pos.wp; int flags = DOTILDE | DOSCALAR; if (!s) return (NULL); te->pos.wp++; if (!do_eval) return (null); if (op == TO_STEQL || op == TO_STNEQ) flags |= DOPAT; return (evalstr(s, flags)); }
static int comexec(struct op *t, struct tbl * volatile tp, const char **ap, volatile int flags, volatile int *xerrok) { int i; volatile int rv = 0; const char *cp; const char **lastp; /* Must be static (XXX but why?) */ static struct op texec; int type_flags; bool keepasn_ok; int fcflags = FC_BI|FC_FUNC|FC_PATH; bool bourne_function_call = false; struct block *l_expand, *l_assign; /* * snag the last argument for $_ XXX not the same as AT&T ksh, * which only seems to set $_ after a newline (but not in * functions/dot scripts, but in interactive and script) - * perhaps save last arg here and set it in shell()?. */ if (Flag(FTALKING) && *(lastp = ap)) { while (*++lastp) ; /* setstr() can't fail here */ setstr(typeset("_", LOCAL, 0, INTEGER, 0), *--lastp, KSH_RETURN_ERROR); } /** * Deal with the shell builtins builtin, exec and command since * they can be followed by other commands. This must be done before * we know if we should create a local block which must be done * before we can do a path search (in case the assignments change * PATH). * Odd cases: * FOO=bar exec >/dev/null FOO is kept but not exported * FOO=bar exec foobar FOO is exported * FOO=bar command exec >/dev/null FOO is neither kept nor exported * FOO=bar command FOO is neither kept nor exported * PATH=... foobar use new PATH in foobar search */ keepasn_ok = true; while (tp && tp->type == CSHELL) { /* undo effects of command */ fcflags = FC_BI|FC_FUNC|FC_PATH; if (tp->val.f == c_builtin) { if ((cp = *++ap) == NULL || (!strcmp(cp, "--") && (cp = *++ap) == NULL)) { tp = NULL; break; } if ((tp = findcom(cp, FC_BI)) == NULL) errorf("%s: %s: %s", Tbuiltin, cp, "not a builtin"); continue; } else if (tp->val.f == c_exec) { if (ap[1] == NULL) break; ap++; flags |= XEXEC; } else if (tp->val.f == c_command) { int optc, saw_p = 0; /* * Ugly dealing with options in two places (here * and in c_command(), but such is life) */ ksh_getopt_reset(&builtin_opt, 0); while ((optc = ksh_getopt(ap, &builtin_opt, ":p")) == 'p') saw_p = 1; if (optc != EOF) /* command -vV or something */ break; /* don't look for functions */ fcflags = FC_BI|FC_PATH; if (saw_p) { if (Flag(FRESTRICTED)) { warningf(true, "%s: %s", "command -p", "restricted"); rv = 1; goto Leave; } fcflags |= FC_DEFPATH; } ap += builtin_opt.optind; /* * POSIX says special builtins lose their status * if accessed using command. */ keepasn_ok = false; if (!ap[0]) { /* ensure command with no args exits with 0 */ subst_exstat = 0; break; } #ifndef MKSH_NO_EXTERNAL_CAT } else if (tp->val.f == c_cat) { /* * if we have any flags, do not use the builtin * in theory, we could allow -u, but that would * mean to use ksh_getopt here and possibly ad- * ded complexity and more code and isn't worth * additional hassle (and the builtin must call * ksh_getopt already but can't come back here) */ if (ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' && /* argument, begins with -, is not - or -- */ (ap[1][1] != '-' || ap[1][2] != '\0')) /* don't look for builtins or functions */ fcflags = FC_PATH; else /* go on, use the builtin */ break; #endif #if !defined(MKSH_SMALL) } else if (tp->val.f == c_trap) { t->u.evalflags &= ~DOTCOMEXEC; break; #endif } else break; tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC)); } #if !defined(MKSH_SMALL) if (t->u.evalflags & DOTCOMEXEC) flags |= XEXEC; #endif l_expand = e->loc; if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN)))) type_flags = 0; else { /* create new variable/function block */ newblock(); /* ksh functions don't keep assignments, POSIX functions do. */ if (keepasn_ok && tp && tp->type == CFUNC && !(tp->flag & FKSH)) { bourne_function_call = true; type_flags = EXPORT; } else type_flags = LOCAL|LOCAL_COPY|EXPORT; } l_assign = e->loc; if (Flag(FEXPORT)) type_flags |= EXPORT; for (i = 0; t->vars[i]; i++) { /* do NOT lookup in the new var/fn block just created */ e->loc = l_expand; cp = evalstr(t->vars[i], DOASNTILDE); e->loc = l_assign; /* but assign in there as usual */ if (Flag(FXTRACE)) { if (i == 0) shf_puts(substitute(str_val(global("PS4")), 0), shl_out); shf_fprintf(shl_out, "%s%c", cp, t->vars[i + 1] ? ' ' : '\n'); if (!t->vars[i + 1]) shf_flush(shl_out); } typeset(cp, type_flags, 0, 0, 0); if (bourne_function_call && !(type_flags & EXPORT)) typeset(cp, LOCAL|LOCAL_COPY|EXPORT, 0, 0, 0); } if ((cp = *ap) == NULL) { rv = subst_exstat; goto Leave; } else if (!tp) { if (Flag(FRESTRICTED) && vstrchr(cp, '/')) { warningf(true, "%s: %s", cp, "restricted"); rv = 1; goto Leave; } tp = findcom(cp, fcflags); } switch (tp->type) { /* shell built-in */ case CSHELL: rv = call_builtin(tp, (const char **)ap, null); if (!keepasn_ok && tp->val.f == c_shift) { l_expand->argc = l_assign->argc; l_expand->argv = l_assign->argv; } break; /* function call */ case CFUNC: { volatile unsigned char old_xflag; volatile uint32_t old_inuse; const char * volatile old_kshname; if (!(tp->flag & ISSET)) { struct tbl *ftp; if (!tp->u.fpath) { rv = (tp->u2.errnov == ENOENT) ? 127 : 126; warningf(true, "%s: %s %s: %s", cp, "can't find", "function definition file", cstrerror(tp->u2.errnov)); break; } if (include(tp->u.fpath, 0, NULL, false) < 0) { rv = errno; warningf(true, "%s: %s %s %s: %s", cp, "can't open", "function definition file", tp->u.fpath, cstrerror(rv)); rv = 127; break; } if (!(ftp = findfunc(cp, hash(cp), false)) || !(ftp->flag & ISSET)) { warningf(true, "%s: %s %s", cp, "function not defined by", tp->u.fpath); rv = 127; break; } tp = ftp; } /* * ksh functions set $0 to function name, POSIX * functions leave $0 unchanged. */ old_kshname = kshname; if (tp->flag & FKSH) kshname = ap[0]; else ap[0] = kshname; e->loc->argv = ap; for (i = 0; *ap++ != NULL; i++) ; e->loc->argc = i - 1; /* * ksh-style functions handle getopts sanely, * Bourne/POSIX functions are insane... */ if (tp->flag & FKSH) { e->loc->flags |= BF_DOGETOPTS; e->loc->getopts_state = user_opt; getopts_reset(1); } old_xflag = Flag(FXTRACE); Flag(FXTRACE) |= tp->flag & TRACE ? 1 : 0; old_inuse = tp->flag & FINUSE; tp->flag |= FINUSE; e->type = E_FUNC; if (!(i = kshsetjmp(e->jbuf))) { execute(tp->val.t, flags & XERROK, NULL); i = LRETURN; } kshname = old_kshname; Flag(FXTRACE) = old_xflag; tp->flag = (tp->flag & ~FINUSE) | old_inuse; /* * Were we deleted while executing? If so, free the * execution tree. TODO: Unfortunately, the table entry * is never re-used until the lookup table is expanded. */ if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) { if (tp->flag & ALLOC) { tp->flag &= ~ALLOC; tfree(tp->val.t, tp->areap); } tp->flag = 0; } switch (i) { case LRETURN: case LERROR: rv = exstat & 0xFF; break; case LINTR: case LEXIT: case LLEAVE: case LSHELL: quitenv(NULL); unwind(i); /* NOTREACHED */ default: quitenv(NULL); internal_errorf("%s %d", "CFUNC", i); } break; } /* executable command */ case CEXEC: /* tracked alias */ case CTALIAS: if (!(tp->flag&ISSET)) { if (tp->u2.errnov == ENOENT) { rv = 127; warningf(true, "%s: %s", cp, "not found"); } else { rv = 126; warningf(true, "%s: %s: %s", cp, "can't execute", cstrerror(tp->u2.errnov)); } break; } /* set $_ to programme's full path */ /* setstr() can't fail here */ setstr(typeset("_", LOCAL|EXPORT, 0, INTEGER, 0), tp->val.s, KSH_RETURN_ERROR); if (flags&XEXEC) { j_exit(); if (!(flags&XBGND) #ifndef MKSH_UNEMPLOYED || Flag(FMONITOR) #endif ) { setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG); setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG); } } /* to fork we set up a TEXEC node and call execute */ texec.type = TEXEC; /* for tprint */ texec.left = t; texec.str = tp->val.s; texec.args = ap; rv = exchild(&texec, flags, xerrok, -1); break; } Leave: if (flags & XEXEC) { exstat = rv & 0xFF; unwind(LLEAVE); } return (rv); }
/* * execute command tree */ int execute(struct op * volatile t, /* if XEXEC don't fork */ volatile int flags, volatile int * volatile xerrok) { int i; volatile int rv = 0, dummy = 0; int pv[2]; const char ** volatile ap = NULL; char ** volatile up; const char *s, *ccp; struct ioword **iowp; struct tbl *tp = NULL; char *cp; if (t == NULL) return (0); /* Caller doesn't care if XERROK should propagate. */ if (xerrok == NULL) xerrok = &dummy; if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE) /* run in sub-process */ return (exchild(t, flags & ~XTIME, xerrok, -1)); newenv(E_EXEC); if (trap) runtraps(0); /* we want to run an executable, do some variance checks */ if (t->type == TCOM) { /* check if this is 'var=<<EOF' */ if ( /* we have zero arguments, i.e. no programme to run */ t->args[0] == NULL && /* we have exactly one variable assignment */ t->vars[0] != NULL && t->vars[1] == NULL && /* we have exactly one I/O redirection */ t->ioact != NULL && t->ioact[0] != NULL && t->ioact[1] == NULL && /* of type "here document" (or "here string") */ (t->ioact[0]->flag & IOTYPE) == IOHERE && /* the variable assignment begins with a valid varname */ (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] && /* and has no right-hand side (i.e. "varname=") */ ccp[0] == CHAR && ccp[1] == '=' && ccp[2] == EOS && /* plus we can have a here document content */ herein(t->ioact[0], &cp) == 0 && cp && *cp) { char *sp = cp, *dp; size_t n = ccp - t->vars[0] + 2, z; /* drop redirection (will be garbage collected) */ t->ioact = NULL; /* set variable to its expanded value */ z = strlen(cp) + 1; if (notoktomul(z, 2) || notoktoadd(z * 2, n)) internal_errorf(Toomem, (unsigned long)-1); dp = alloc(z * 2 + n, ATEMP); memcpy(dp, t->vars[0], n); t->vars[0] = dp; dp += n; while (*sp) { *dp++ = QCHAR; *dp++ = *sp++; } *dp = EOS; /* free the expanded value */ afree(cp, APERM); } /* * Clear subst_exstat before argument expansion. Used by * null commands (see comexec() and c_eval()) and by c_set(). */ subst_exstat = 0; /* for $LINENO */ current_lineno = t->lineno; /* * POSIX says expand command words first, then redirections, * and assignments last.. */ up = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE); if (flags & XTIME) /* Allow option parsing (bizarre, but POSIX) */ timex_hook(t, &up); ap = (const char **)up; if (Flag(FXTRACE) && ap[0]) { shf_puts(substitute(str_val(global("PS4")), 0), shl_out); for (i = 0; ap[i]; i++) shf_fprintf(shl_out, "%s%c", ap[i], ap[i + 1] ? ' ' : '\n'); shf_flush(shl_out); } if (ap[0]) tp = findcom(ap[0], FC_BI|FC_FUNC); } flags &= ~XTIME; if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) { e->savefd = alloc2(NUFILE, sizeof(short), ATEMP); /* initialise to not redirected */ memset(e->savefd, 0, NUFILE * sizeof(short)); } /* mark for replacement later (unless TPIPE) */ vp_pipest->flag |= INT_L; /* do redirection, to be restored in quitenv() */ if (t->ioact != NULL) for (iowp = t->ioact; *iowp != NULL; iowp++) { if (iosetup(*iowp, tp) < 0) { exstat = rv = 1; /* * Redirection failures for special commands * cause (non-interactive) shell to exit. */ if (tp && tp->type == CSHELL && (tp->flag & SPEC_BI)) errorfz(); /* Deal with FERREXIT, quitenv(), etc. */ goto Break; } } switch (t->type) { case TCOM: rv = comexec(t, tp, (const char **)ap, flags, xerrok); break; case TPAREN: rv = execute(t->left, flags | XFORK, xerrok); break; case TPIPE: flags |= XFORK; flags &= ~XEXEC; e->savefd[0] = savefd(0); e->savefd[1] = savefd(1); while (t->type == TPIPE) { openpipe(pv); /* stdout of curr */ ksh_dup2(pv[1], 1, false); /** * Let exchild() close pv[0] in child * (if this isn't done, commands like * (: ; cat /etc/termcap) | sleep 1 * will hang forever). */ exchild(t->left, flags | XPIPEO | XCCLOSE, NULL, pv[0]); /* stdin of next */ ksh_dup2(pv[0], 0, false); closepipe(pv); flags |= XPIPEI; t = t->right; } /* stdout of last */ restfd(1, e->savefd[1]); /* no need to re-restore this */ e->savefd[1] = 0; /* Let exchild() close 0 in parent, after fork, before wait */ i = exchild(t, flags | XPCLOSE | XPIPEST, xerrok, 0); if (!(flags&XBGND) && !(flags&XXCOM)) rv = i; break; case TLIST: while (t->type == TLIST) { execute(t->left, flags & XERROK, NULL); t = t->right; } rv = execute(t, flags & XERROK, xerrok); break; case TCOPROC: { #ifndef MKSH_NOPROSPECTOFWORK sigset_t omask; /* * Block sigchild as we are using things changed in the * signal handler */ sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); e->type = E_ERRH; if ((i = kshsetjmp(e->jbuf))) { sigprocmask(SIG_SETMASK, &omask, NULL); quitenv(NULL); unwind(i); /* NOTREACHED */ } #endif /* Already have a (live) co-process? */ if (coproc.job && coproc.write >= 0) errorf("coprocess already exists"); /* Can we re-use the existing co-process pipe? */ coproc_cleanup(true); /* do this before opening pipes, in case these fail */ e->savefd[0] = savefd(0); e->savefd[1] = savefd(1); openpipe(pv); if (pv[0] != 0) { ksh_dup2(pv[0], 0, false); close(pv[0]); } coproc.write = pv[1]; coproc.job = NULL; if (coproc.readw >= 0) ksh_dup2(coproc.readw, 1, false); else { openpipe(pv); coproc.read = pv[0]; ksh_dup2(pv[1], 1, false); /* closed before first read */ coproc.readw = pv[1]; coproc.njobs = 0; /* create new coprocess id */ ++coproc.id; } #ifndef MKSH_NOPROSPECTOFWORK sigprocmask(SIG_SETMASK, &omask, NULL); /* no more need for error handler */ e->type = E_EXEC; #endif /* * exchild() closes coproc.* in child after fork, * will also increment coproc.njobs when the * job is actually created. */ flags &= ~XEXEC; exchild(t->left, flags | XBGND | XFORK | XCOPROC | XCCLOSE, NULL, coproc.readw); break; } case TASYNC: /* * XXX non-optimal, I think - "(foo &)", forks for (), * forks again for async... parent should optimise * this to "foo &"... */ rv = execute(t->left, (flags&~XEXEC)|XBGND|XFORK, xerrok); break; case TOR: case TAND: rv = execute(t->left, XERROK, xerrok); if ((rv == 0) == (t->type == TAND)) rv = execute(t->right, XERROK, xerrok); flags |= XERROK; if (xerrok) *xerrok = 1; break; case TBANG: rv = !execute(t->right, XERROK, xerrok); flags |= XERROK; if (xerrok) *xerrok = 1; break; case TDBRACKET: { Test_env te; te.flags = TEF_DBRACKET; te.pos.wp = t->args; te.isa = dbteste_isa; te.getopnd = dbteste_getopnd; te.eval = test_eval; te.error = dbteste_error; rv = test_parse(&te); break; } case TFOR: case TSELECT: { volatile bool is_first = true; ap = (t->vars == NULL) ? e->loc->argv + 1 : (const char **)eval((const char **)t->vars, DOBLANK | DOGLOB | DOTILDE); e->type = E_LOOP; while ((i = kshsetjmp(e->jbuf))) { if ((e->flags&EF_BRKCONT_PASS) || (i != LBREAK && i != LCONTIN)) { quitenv(NULL); unwind(i); } else if (i == LBREAK) { rv = 0; goto Break; } } /* in case of a continue */ rv = 0; if (t->type == TFOR) { while (*ap != NULL) { setstr(global(t->str), *ap++, KSH_UNWIND_ERROR); rv = execute(t->left, flags & XERROK, xerrok); } } else { /* TSELECT */ for (;;) { if (!(ccp = do_selectargs(ap, is_first))) { rv = 1; break; } is_first = false; setstr(global(t->str), ccp, KSH_UNWIND_ERROR); execute(t->left, flags & XERROK, xerrok); } } break; } case TWHILE: case TUNTIL: e->type = E_LOOP; while ((i = kshsetjmp(e->jbuf))) { if ((e->flags&EF_BRKCONT_PASS) || (i != LBREAK && i != LCONTIN)) { quitenv(NULL); unwind(i); } else if (i == LBREAK) { rv = 0; goto Break; } } /* in case of a continue */ rv = 0; while ((execute(t->left, XERROK, NULL) == 0) == (t->type == TWHILE)) rv = execute(t->right, flags & XERROK, xerrok); break; case TIF: case TELIF: if (t->right == NULL) /* should be error */ break; rv = execute(t->left, XERROK, NULL) == 0 ? execute(t->right->left, flags & XERROK, xerrok) : execute(t->right->right, flags & XERROK, xerrok); break; case TCASE: i = 0; ccp = evalstr(t->str, DOTILDE); for (t = t->left; t != NULL && t->type == TPAT; t = t->right) { for (ap = (const char **)t->vars; *ap; ap++) { if (i || ((s = evalstr(*ap, DOTILDE|DOPAT)) && gmatchx(ccp, s, false))) { rv = execute(t->left, flags & XERROK, xerrok); i = 0; switch (t->u.charflag) { case '&': i = 1; /* FALLTHROUGH */ case '|': goto TCASE_next; } goto TCASE_out; } } i = 0; TCASE_next: /* empty */; } TCASE_out: break; case TBRACE: rv = execute(t->left, flags & XERROK, xerrok); break; case TFUNCT: rv = define(t->str, t); break; case TTIME: /* * Clear XEXEC so nested execute() call doesn't exit * (allows "ls -l | time grep foo"). */ rv = timex(t, flags & ~XEXEC, xerrok); break; case TEXEC: /* an eval'd TCOM */ s = t->args[0]; up = makenv(); restoresigs(); cleanup_proc_env(); { union mksh_ccphack cargs; cargs.ro = t->args; execve(t->str, cargs.rw, up); rv = errno; } if (rv == ENOEXEC) scriptexec(t, (const char **)up); else errorf("%s: %s", s, cstrerror(rv)); } Break: exstat = rv & 0xFF; if (vp_pipest->flag & INT_L) { unset(vp_pipest, 1); vp_pipest->flag = DEFINED | ISSET | INTEGER | RDONLY | ARRAY | INT_U; vp_pipest->val.i = rv; } /* restores IO */ quitenv(NULL); if ((flags&XEXEC)) /* exit child */ unwind(LEXIT); if (rv != 0 && !(flags & XERROK) && (xerrok == NULL || !*xerrok)) { if (Flag(FERREXIT) & 0x80) { /* inside eval */ Flag(FERREXIT) = 0; } else { trapsig(ksh_SIGERR); if (Flag(FERREXIT)) unwind(LERROR); } } return (rv); }
/* * print a command tree */ static void ptree(struct op *t, int indent, struct shf *shf) { const char **w; struct ioword **ioact; struct op *t1; int i; Chain: if (t == NULL) return; switch (t->type) { case TCOM: prevent_semicolon = false; /* * special-case 'var=<<EOF' (rough; see * exec.c:execute() for full code) */ if ( /* we have zero arguments, i.e. no programme to run */ t->args[0] == NULL && /* we have exactly one variable assignment */ t->vars[0] != NULL && t->vars[1] == NULL && /* we have exactly one I/O redirection */ t->ioact != NULL && t->ioact[0] != NULL && t->ioact[1] == NULL && /* of type "here document" (or "here string") */ (t->ioact[0]->flag & IOTYPE) == IOHERE) { fptreef(shf, indent, "%S", t->vars[0]); break; } if (t->vars) { w = (const char **)t->vars; while (*w) fptreef(shf, indent, "%S ", *w++); } else shf_puts("#no-vars# ", shf); if (t->args) { w = t->args; while (*w) fptreef(shf, indent, "%S ", *w++); } else shf_puts("#no-args# ", shf); break; case TEXEC: t = t->left; goto Chain; case TPAREN: fptreef(shf, indent + 2, "( %T) ", t->left); break; case TPIPE: fptreef(shf, indent, "%T| ", t->left); t = t->right; goto Chain; case TLIST: fptreef(shf, indent, "%T%;", t->left); t = t->right; goto Chain; case TOR: case TAND: fptreef(shf, indent, "%T%s %T", t->left, (t->type == TOR) ? "||" : "&&", t->right); break; case TBANG: shf_puts("! ", shf); prevent_semicolon = false; t = t->right; goto Chain; case TDBRACKET: w = t->args; shf_puts("[[", shf); while (*w) fptreef(shf, indent, " %S", *w++); shf_puts(" ]] ", shf); break; case TSELECT: case TFOR: fptreef(shf, indent, "%s %s ", (t->type == TFOR) ? "for" : Tselect, t->str); if (t->vars != NULL) { shf_puts("in ", shf); w = (const char **)t->vars; while (*w) fptreef(shf, indent, "%S ", *w++); fptreef(shf, indent, "%;"); } fptreef(shf, indent + INDENT, "do%N%T", t->left); fptreef(shf, indent, "%;done "); break; case TCASE: fptreef(shf, indent, "case %S in", t->str); for (t1 = t->left; t1 != NULL; t1 = t1->right) { fptreef(shf, indent, "%N("); w = (const char **)t1->vars; while (*w) { fptreef(shf, indent, "%S%c", *w, (w[1] != NULL) ? '|' : ')'); ++w; } fptreef(shf, indent + INDENT, "%N%T%N;%c", t1->left, t1->u.charflag); } fptreef(shf, indent, "%Nesac "); break; #ifdef DEBUG case TELIF: internal_errorf("TELIF in tree.c:ptree() unexpected"); /* FALLTHROUGH */ #endif case TIF: i = 2; t1 = t; goto process_TIF; do { t1 = t1->right; i = 0; fptreef(shf, indent, "%;"); process_TIF: /* 5 == strlen("elif ") */ fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left); t1 = t1->right; if (t1->left != NULL) { fptreef(shf, indent, "%;"); fptreef(shf, indent + INDENT, "%s%N%T", "then", t1->left); } } while (t1->right && t1->right->type == TELIF); if (t1->right != NULL) { fptreef(shf, indent, "%;"); fptreef(shf, indent + INDENT, "%s%N%T", "else", t1->right); } fptreef(shf, indent, "%;fi "); break; case TWHILE: case TUNTIL: /* 6 == strlen("while "/"until ") */ fptreef(shf, indent + 6, "%s %T", (t->type == TWHILE) ? "while" : "until", t->left); fptreef(shf, indent, "%;"); fptreef(shf, indent + INDENT, "do%N%T", t->right); fptreef(shf, indent, "%;done "); break; case TBRACE: fptreef(shf, indent + INDENT, "{%N%T", t->left); fptreef(shf, indent, "%;} "); break; case TCOPROC: fptreef(shf, indent, "%T|& ", t->left); prevent_semicolon = true; break; case TASYNC: fptreef(shf, indent, "%T& ", t->left); prevent_semicolon = true; break; case TFUNCT: fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left); break; case TTIME: fptreef(shf, indent, "%s %T", "time", t->left); break; default: shf_puts("<botch>", shf); prevent_semicolon = false; break; } if ((ioact = t->ioact) != NULL) { bool need_nl = false; while (*ioact != NULL) pioact(shf, *ioact++); /* Print here documents after everything else... */ ioact = t->ioact; while (*ioact != NULL) { struct ioword *iop = *ioact++; /* heredoc is NULL when tracing (set -x) */ if ((iop->flag & (IOTYPE | IOHERESTR)) == IOHERE && iop->heredoc) { shf_putc('\n', shf); shf_puts(iop->heredoc, shf); fptreef(shf, indent, "%s", iop->flag & IONDELIM ? "<<" : evalstr(iop->delim, 0)); need_nl = true; } } /* * Last delimiter must be followed by a newline (this * often leads to an extra blank line, but it's not * worth worrying about) */ if (need_nl) { shf_putc('\n', shf); prevent_semicolon = true; } } }
static void ptree(struct op *t, int indent, struct shf *shf) { char **w; struct ioword **ioact; struct op *t1; Chain: if (t == NULL) return; switch (t->type) { case TCOM: if (t->vars) for (w = t->vars; *w != NULL; ) fptreef(shf, indent, "%S ", *w++); else fptreef(shf, indent, "#no-vars# "); if (t->args) for (w = t->args; *w != NULL; ) fptreef(shf, indent, "%S ", *w++); else fptreef(shf, indent, "#no-args# "); break; case TEXEC: #if 0 /* ?not useful - can't be called? */ /* Print original vars */ if (t->left->vars) for (w = t->left->vars; *w != NULL; ) fptreef(shf, indent, "%S ", *w++); else fptreef(shf, indent, "#no-vars# "); /* Print expanded vars */ if (t->args) for (w = t->args; *w != NULL; ) fptreef(shf, indent, "%s ", *w++); else fptreef(shf, indent, "#no-args# "); /* Print original io */ t = t->left; #else t = t->left; goto Chain; #endif case TPAREN: fptreef(shf, indent + 2, "( %T) ", t->left); break; case TPIPE: fptreef(shf, indent, "%T| ", t->left); t = t->right; goto Chain; case TLIST: fptreef(shf, indent, "%T%;", t->left); t = t->right; goto Chain; case TOR: case TAND: fptreef(shf, indent, "%T%s %T", t->left, (t->type==TOR) ? "||" : "&&", t->right); break; case TBANG: fptreef(shf, indent, "! "); t = t->right; goto Chain; case TDBRACKET: { int i; fptreef(shf, indent, "[["); for (i = 0; t->args[i]; i++) fptreef(shf, indent, " %S", t->args[i]); fptreef(shf, indent, " ]] "); break; } case TSELECT: fptreef(shf, indent, "select %s ", t->str); /* FALLTHROUGH */ case TFOR: if (t->type == TFOR) fptreef(shf, indent, "for %s ", t->str); if (t->vars != NULL) { fptreef(shf, indent, "in "); for (w = t->vars; *w; ) fptreef(shf, indent, "%S ", *w++); fptreef(shf, indent, "%;"); } fptreef(shf, indent + INDENT, "do%N%T", t->left); fptreef(shf, indent, "%;done "); break; case TCASE: fptreef(shf, indent, "case %S in", t->str); for (t1 = t->left; t1 != NULL; t1 = t1->right) { fptreef(shf, indent, "%N("); for (w = t1->vars; *w != NULL; w++) fptreef(shf, indent, "%S%c", *w, (w[1] != NULL) ? '|' : ')'); fptreef(shf, indent + INDENT, "%;%T%N;;", t1->left); } fptreef(shf, indent, "%Nesac "); break; case TIF: case TELIF: /* 3 == strlen("if ") */ fptreef(shf, indent + 3, "if %T", t->left); for (;;) { t = t->right; if (t->left != NULL) { fptreef(shf, indent, "%;"); fptreef(shf, indent + INDENT, "then%N%T", t->left); } if (t->right == NULL || t->right->type != TELIF) break; t = t->right; fptreef(shf, indent, "%;"); /* 5 == strlen("elif ") */ fptreef(shf, indent + 5, "elif %T", t->left); } if (t->right != NULL) { fptreef(shf, indent, "%;"); fptreef(shf, indent + INDENT, "else%;%T", t->right); } fptreef(shf, indent, "%;fi "); break; case TWHILE: case TUNTIL: /* 6 == strlen("while"/"until") */ fptreef(shf, indent + 6, "%s %T", (t->type==TWHILE) ? "while" : "until", t->left); fptreef(shf, indent, "%;do"); fptreef(shf, indent + INDENT, "%;%T", t->right); fptreef(shf, indent, "%;done "); break; case TBRACE: fptreef(shf, indent + INDENT, "{%;%T", t->left); fptreef(shf, indent, "%;} "); break; case TCOPROC: fptreef(shf, indent, "%T|& ", t->left); break; case TASYNC: fptreef(shf, indent, "%T& ", t->left); break; case TFUNCT: fptreef(shf, indent, t->u.ksh_func ? "function %s %T" : "%s() %T", t->str, t->left); break; case TTIME: fptreef(shf, indent, "time %T", t->left); break; default: fptreef(shf, indent, "<botch>"); break; } if ((ioact = t->ioact) != NULL) { int need_nl = 0; while (*ioact != NULL) pioact(shf, indent, *ioact++); /* Print here documents after everything else... */ for (ioact = t->ioact; *ioact != NULL; ) { struct ioword *iop = *ioact++; /* heredoc is 0 when tracing (set -x) */ if ((iop->flag & IOTYPE) == IOHERE && iop->heredoc) { tputc('\n', shf); shf_puts(iop->heredoc, shf); fptreef(shf, indent, "%s", evalstr(iop->delim, 0)); need_nl = 1; } } /* Last delimiter must be followed by a newline (this often * leads to an extra blank line, but its not worth worrying * about) */ if (need_nl) tputc('\n', shf); } }
static void readhere(struct ioword *iop) { int c; const char *eof, *eofp; XString xs; char *xp; size_t xpos; eof = evalstr(iop->delim, 0); if (!(iop->ioflag & IOEVAL)) ignore_backslash_newline++; Xinit(xs, xp, 256, ATEMP); heredoc_read_line: /* beginning of line */ eofp = eof; xpos = Xsavepos(xs, xp); if (iop->ioflag & IOSKIP) { /* skip over leading tabs */ while ((c = getsc()) == '\t') ; /* nothing */ goto heredoc_parse_char; } heredoc_read_char: c = getsc(); heredoc_parse_char: /* compare with here document marker */ if (!*eofp) { /* end of here document marker, what to do? */ switch (c) { case /*(*/ ')': if (!subshell_nesting_type) /*- * not allowed outside $(...) or (...) * => mismatch */ break; /* allow $(...) or (...) to close here */ ungetsc(/*(*/ ')'); /* FALLTHROUGH */ case 0: /* * Allow EOF here to commands without trailing * newlines (mksh -c '...') will work as well. */ case '\n': /* Newline terminates here document marker */ goto heredoc_found_terminator; } } else if (c == *eofp++) /* store; then read and compare next character */ goto heredoc_store_and_loop; /* nope, mismatch; read until end of line */ while (c != '\n') { if (!c) /* oops, reached EOF */ yyerror("%s '%s' unclosed\n", "here document", eof); /* store character */ Xcheck(xs, xp); Xput(xs, xp, c); /* read next character */ c = getsc(); } /* we read a newline as last character */ heredoc_store_and_loop: /* store character */ Xcheck(xs, xp); Xput(xs, xp, c); if (c == '\n') goto heredoc_read_line; goto heredoc_read_char; heredoc_found_terminator: /* jump back to saved beginning of line */ xp = Xrestpos(xs, xp, xpos); /* terminate, close and store */ Xput(xs, xp, '\0'); iop->heredoc = Xclose(xs, xp); if (!(iop->ioflag & IOEVAL)) ignore_backslash_newline--; }
int yylex(int cf) { Lex_state states[STATE_BSIZE], *statep, *s2, *base; State_info state_info; int c, c2, state; size_t cz; XString ws; /* expandable output word */ char *wp; /* output word pointer */ char *sp, *dp; Again: states[0].type = SINVALID; states[0].ls_base = NULL; statep = &states[1]; state_info.base = states; state_info.end = &state_info.base[STATE_BSIZE]; Xinit(ws, wp, 64, ATEMP); backslash_skip = 0; ignore_backslash_newline = 0; if (cf & ONEWORD) state = SWORD; else if (cf & LETEXPR) { /* enclose arguments in (double) quotes */ *wp++ = OQUOTE; state = SLETPAREN; statep->nparen = 0; } else { /* normal lexing */ state = (cf & HEREDELIM) ? SHEREDELIM : SBASE; while (ctype((c = getsc()), C_BLANK)) ; if (c == '#') { ignore_backslash_newline++; while (!ctype((c = getsc()), C_NUL | C_LF)) ; ignore_backslash_newline--; } ungetsc(c); } if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */ source->flags &= ~SF_ALIAS; /* POSIX: trailing space only counts if parsing simple cmd */ if (!Flag(FPOSIX) || (cf & CMDWORD)) cf |= ALIAS; } /* Initial state: one of SWORD SLETPAREN SHEREDELIM SBASE */ statep->type = state; /* collect non-special or quoted characters to form word */ while (!((c = getsc()) == 0 || ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) { if (state == SBASE && subshell_nesting_type == ord(/*{*/ '}') && c == ord(/*{*/ '}')) /* possibly end ${ :;} */ break; Xcheck(ws, wp); switch (state) { case SADELIM: if (c == ord('(')) statep->nparen++; else if (c == ord(')')) statep->nparen--; else if (statep->nparen == 0 && (c == ord(/*{*/ '}') || c == (int)statep->ls_adelim.delimiter)) { *wp++ = ADELIM; *wp++ = c; if (c == ord(/*{*/ '}') || --statep->ls_adelim.num == 0) POP_STATE(); if (c == ord(/*{*/ '}')) POP_STATE(); break; } /* FALLTHROUGH */ case SBASE: if (c == ord('[') && (cf & CMDASN)) { /* temporary */ *wp = EOS; if (is_wdvarname(Xstring(ws, wp), false)) { char *p, *tmp; if (arraysub(&tmp)) { *wp++ = CHAR; *wp++ = c; for (p = tmp; *p; ) { Xcheck(ws, wp); *wp++ = CHAR; *wp++ = *p++; } afree(tmp, ATEMP); break; } else { Source *s; s = pushs(SREREAD, source->areap); s->start = s->str = s->u.freeme = tmp; s->next = source; source = s; } } *wp++ = CHAR; *wp++ = c; break; } /* FALLTHROUGH */ Sbase1: /* includes *(...|...) pattern (*+?@!) */ if (ctype(c, C_PATMO)) { c2 = getsc(); if (c2 == ord('(' /*)*/)) { *wp++ = OPAT; *wp++ = c; PUSH_STATE(SPATTERN); break; } ungetsc(c2); } /* FALLTHROUGH */ Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */ switch (c) { case ord('\\'): getsc_qchar: if ((c = getsc())) { /* trailing \ is lost */ *wp++ = QCHAR; *wp++ = c; } break; case ord('\''): open_ssquote_unless_heredoc: if ((cf & HEREDOC)) goto store_char; *wp++ = OQUOTE; ignore_backslash_newline++; PUSH_STATE(SSQUOTE); break; case ord('"'): open_sdquote: *wp++ = OQUOTE; PUSH_STATE(SDQUOTE); break; case ord('$'): /* * processing of dollar sign belongs into * Subst, except for those which can open * a string: $'…' and $"…" */ subst_dollar_ex: c = getsc(); switch (c) { case ord('"'): goto open_sdquote; case ord('\''): goto open_sequote; default: goto SubstS; } default: goto Subst; } break; Subst: switch (c) { case ord('\\'): c = getsc(); switch (c) { case ord('"'): if ((cf & HEREDOC)) goto heredocquote; /* FALLTHROUGH */ case ord('\\'): case ord('$'): case ord('`'): store_qchar: *wp++ = QCHAR; *wp++ = c; break; default: heredocquote: Xcheck(ws, wp); if (c) { /* trailing \ is lost */ *wp++ = CHAR; *wp++ = '\\'; *wp++ = CHAR; *wp++ = c; } break; } break; case ord('$'): c = getsc(); SubstS: if (c == ord('(' /*)*/)) { c = getsc(); if (c == ord('(' /*)*/)) { *wp++ = EXPRSUB; PUSH_SRETRACE(SASPAREN); statep->nparen = 2; *retrace_info->xp++ = '('; } else { ungetsc(c); subst_command: c = COMSUB; subst_command2: sp = yyrecursive(c); cz = strlen(sp) + 1; XcheckN(ws, wp, cz); *wp++ = c; memcpy(wp, sp, cz); wp += cz; } } else if (c == ord('{' /*}*/)) { if ((c = getsc()) == ord('|')) { /* * non-subenvironment * value substitution */ c = VALSUB; goto subst_command2; } else if (ctype(c, C_IFSWS)) { /* * non-subenvironment * "command" substitution */ c = FUNSUB; goto subst_command2; } ungetsc(c); *wp++ = OSUBST; *wp++ = '{' /*}*/; wp = get_brace_var(&ws, wp); c = getsc(); /* allow :# and :% (ksh88 compat) */ if (c == ord(':')) { *wp++ = CHAR; *wp++ = c; c = getsc(); if (c == ord(':')) { *wp++ = CHAR; *wp++ = '0'; *wp++ = ADELIM; *wp++ = ':'; PUSH_STATE(SBRACE); PUSH_STATE(SADELIM); statep->ls_adelim.delimiter = ':'; statep->ls_adelim.num = 1; statep->nparen = 0; break; } else if (ctype(c, C_DIGIT | C_DOLAR | C_SPC) || /*XXX what else? */ c == '(' /*)*/) { /* substring subst. */ if (c != ' ') { *wp++ = CHAR; *wp++ = ' '; } ungetsc(c); PUSH_STATE(SBRACE); PUSH_STATE(SADELIM); statep->ls_adelim.delimiter = ':'; statep->ls_adelim.num = 2; statep->nparen = 0; break; } } else if (c == '/') { c2 = ADELIM; parse_adelim_slash: *wp++ = CHAR; *wp++ = c; if ((c = getsc()) == ord('/')) { *wp++ = c2; *wp++ = c; } else ungetsc(c); PUSH_STATE(SBRACE); PUSH_STATE(SADELIM); statep->ls_adelim.delimiter = '/'; statep->ls_adelim.num = 1; statep->nparen = 0; break; } else if (c == '@') { c2 = getsc(); ungetsc(c2); if (c2 == ord('/')) { c2 = CHAR; goto parse_adelim_slash; } } /* * If this is a trim operation, * treat (,|,) specially in STBRACE. */ if (ctype(c, C_SUB2)) { ungetsc(c); if (Flag(FSH)) PUSH_STATE(STBRACEBOURNE); else PUSH_STATE(STBRACEKORN); } else { ungetsc(c); if (state == SDQUOTE || state == SQBRACE) PUSH_STATE(SQBRACE); else PUSH_STATE(SBRACE); } } else if (ctype(c, C_ALPHX)) { *wp++ = OSUBST; *wp++ = 'X'; do { Xcheck(ws, wp); *wp++ = c; c = getsc(); } while (ctype(c, C_ALNUX)); *wp++ = '\0'; *wp++ = CSUBST; *wp++ = 'X'; ungetsc(c); } else if (ctype(c, C_VAR1 | C_DIGIT)) { Xcheck(ws, wp); *wp++ = OSUBST; *wp++ = 'X'; *wp++ = c; *wp++ = '\0'; *wp++ = CSUBST; *wp++ = 'X'; } else { *wp++ = CHAR; *wp++ = '$'; ungetsc(c); } break; case ord('`'): subst_gravis: PUSH_STATE(SBQUOTE); *wp++ = COMASUB; /* * We need to know whether we are within double * quotes in order to translate \" to " within * "…`…\"…`…" because, unlike for COMSUBs, the * outer double quoteing changes the backslash * meaning for the inside. For more details: * http://austingroupbugs.net/view.php?id=1015 */ statep->ls_bool = false; s2 = statep; base = state_info.base; while (/* CONSTCOND */ 1) { for (; s2 != base; s2--) { if (s2->type == SDQUOTE) { statep->ls_bool = true; break; } } if (s2 != base) break; if (!(s2 = s2->ls_base)) break; base = s2-- - STATE_BSIZE; } break; case QCHAR: if (cf & LQCHAR) { *wp++ = QCHAR; *wp++ = getsc(); break; } /* FALLTHROUGH */ default: store_char: *wp++ = CHAR; *wp++ = c; } break; case SEQUOTE: if (c == ord('\'')) { POP_STATE(); *wp++ = CQUOTE; ignore_backslash_newline--; } else if (c == ord('\\')) { if ((c2 = unbksl(true, getsc_i, ungetsc)) == -1) c2 = getsc(); if (c2 == 0) statep->ls_bool = true; if (!statep->ls_bool) { char ts[4]; if ((unsigned int)c2 < 0x100) { *wp++ = QCHAR; *wp++ = c2; } else { cz = utf_wctomb(ts, c2 - 0x100); ts[cz] = 0; cz = 0; do { *wp++ = QCHAR; *wp++ = ts[cz]; } while (ts[++cz]); } } } else if (!statep->ls_bool) { *wp++ = QCHAR; *wp++ = c; } break; case SSQUOTE: if (c == ord('\'')) { POP_STATE(); if ((cf & HEREDOC) || state == SQBRACE) goto store_char; *wp++ = CQUOTE; ignore_backslash_newline--; } else { *wp++ = QCHAR; *wp++ = c; } break; case SDQUOTE: if (c == ord('"')) { POP_STATE(); *wp++ = CQUOTE; } else goto Subst; break; /* $(( ... )) */ case SASPAREN: if (c == ord('(')) statep->nparen++; else if (c == ord(')')) { statep->nparen--; if (statep->nparen == 1) { /* end of EXPRSUB */ POP_SRETRACE(); if ((c2 = getsc()) == ord(/*(*/ ')')) { cz = strlen(sp) - 2; XcheckN(ws, wp, cz); memcpy(wp, sp + 1, cz); wp += cz; afree(sp, ATEMP); *wp++ = '\0'; break; } else { Source *s; ungetsc(c2); /* * mismatched parenthesis - * assume we were really * parsing a $(...) expression */ --wp; s = pushs(SREREAD, source->areap); s->start = s->str = s->u.freeme = sp; s->next = source; source = s; goto subst_command; } } } /* reuse existing state machine */ goto Sbase2; case SQBRACE: if (c == ord('\\')) { /* * perform POSIX "quote removal" if the back- * slash is "special", i.e. same cases as the * {case '\\':} in Subst: plus closing brace; * in mksh code "quote removal" on '\c' means * write QCHAR+c, otherwise CHAR+\+CHAR+c are * emitted (in heredocquote:) */ if ((c = getsc()) == ord('"') || c == ord('\\') || ctype(c, C_DOLAR | C_GRAVE) || c == ord(/*{*/ '}')) goto store_qchar; goto heredocquote; } goto common_SQBRACE; case SBRACE: if (c == ord('\'')) goto open_ssquote_unless_heredoc; else if (c == ord('\\')) goto getsc_qchar; common_SQBRACE: if (c == ord('"')) goto open_sdquote; else if (c == ord('$')) goto subst_dollar_ex; else if (c == ord('`')) goto subst_gravis; else if (c != ord(/*{*/ '}')) goto store_char; POP_STATE(); *wp++ = CSUBST; *wp++ = /*{*/ '}'; break; /* Same as SBASE, except (,|,) treated specially */ case STBRACEKORN: if (c == ord('|')) *wp++ = SPAT; else if (c == ord('(')) { *wp++ = OPAT; /* simile for @ */ *wp++ = ' '; PUSH_STATE(SPATTERN); } else /* FALLTHROUGH */ case STBRACEBOURNE: if (c == ord(/*{*/ '}')) { POP_STATE(); *wp++ = CSUBST; *wp++ = /*{*/ '}'; } else goto Sbase1; break; case SBQUOTE: if (c == ord('`')) { *wp++ = 0; POP_STATE(); } else if (c == ord('\\')) { switch (c = getsc()) { case 0: /* trailing \ is lost */ break; case ord('$'): case ord('`'): case ord('\\'): *wp++ = c; break; case ord('"'): if (statep->ls_bool) { *wp++ = c; break; } /* FALLTHROUGH */ default: *wp++ = '\\'; *wp++ = c; break; } } else *wp++ = c; break; /* ONEWORD */ case SWORD: goto Subst; /* LETEXPR: (( ... )) */ case SLETPAREN: if (c == ord(/*(*/ ')')) { if (statep->nparen > 0) --statep->nparen; else if ((c2 = getsc()) == ord(/*(*/ ')')) { c = 0; *wp++ = CQUOTE; goto Done; } else { Source *s; ungetsc(c2); ungetsc(c); /* * mismatched parenthesis - * assume we were really * parsing a (...) expression */ *wp = EOS; sp = Xstring(ws, wp); dp = wdstrip(sp + 1, WDS_TPUTS); s = pushs(SREREAD, source->areap); s->start = s->str = s->u.freeme = dp; s->next = source; source = s; ungetsc('(' /*)*/); return (ord('(' /*)*/)); } } else if (c == ord('(')) /* * parentheses inside quotes and * backslashes are lost, but AT&T ksh * doesn't count them either */ ++statep->nparen; goto Sbase2; /* << or <<- delimiter */ case SHEREDELIM: /* * here delimiters need a special case since * $ and `...` are not to be treated specially */ switch (c) { case ord('\\'): if ((c = getsc())) { /* trailing \ is lost */ *wp++ = QCHAR; *wp++ = c; } break; case ord('\''): goto open_ssquote_unless_heredoc; case ord('$'): if ((c2 = getsc()) == ord('\'')) { open_sequote: *wp++ = OQUOTE; ignore_backslash_newline++; PUSH_STATE(SEQUOTE); statep->ls_bool = false; break; } else if (c2 == ord('"')) { /* FALLTHROUGH */ case ord('"'): PUSH_SRETRACE(SHEREDQUOTE); break; } ungetsc(c2); /* FALLTHROUGH */ default: *wp++ = CHAR; *wp++ = c; } break; /* " in << or <<- delimiter */ case SHEREDQUOTE: if (c != ord('"')) goto Subst; POP_SRETRACE(); dp = strnul(sp) - 1; /* remove the trailing double quote */ *dp = '\0'; /* store the quoted string */ *wp++ = OQUOTE; XcheckN(ws, wp, (dp - sp) * 2); dp = sp; while ((c = *dp++)) { if (c == '\\') { switch ((c = *dp++)) { case ord('\\'): case ord('"'): case ord('$'): case ord('`'): break; default: *wp++ = CHAR; *wp++ = '\\'; break; } } *wp++ = CHAR; *wp++ = c; } afree(sp, ATEMP); *wp++ = CQUOTE; state = statep->type = SHEREDELIM; break; /* in *(...|...) pattern (*+?@!) */ case SPATTERN: if (c == ord(/*(*/ ')')) { *wp++ = CPAT; POP_STATE(); } else if (c == ord('|')) { *wp++ = SPAT; } else if (c == ord('(')) { *wp++ = OPAT; /* simile for @ */ *wp++ = ' '; PUSH_STATE(SPATTERN); } else goto Sbase1; break; } } Done: Xcheck(ws, wp); if (statep != &states[1]) /* XXX figure out what is missing */ yyerror("no closing quote"); /* This done to avoid tests for SHEREDELIM wherever SBASE tested */ if (state == SHEREDELIM) state = SBASE; dp = Xstring(ws, wp); if (state == SBASE && ( (c == '&' && !Flag(FSH) && !Flag(FPOSIX)) || ctype(c, C_ANGLE)) && ((c2 = Xlength(ws, wp)) == 0 || (c2 == 2 && dp[0] == CHAR && ctype(dp[1], C_DIGIT)))) { struct ioword *iop = alloc(sizeof(struct ioword), ATEMP); iop->unit = c2 == 2 ? ksh_numdig(dp[1]) : c == '<' ? 0 : 1; if (c == '&') { if ((c2 = getsc()) != ord('>')) { ungetsc(c2); goto no_iop; } c = c2; iop->ioflag = IOBASH; } else iop->ioflag = 0; c2 = getsc(); /* <<, >>, <> are ok, >< is not */ if (c == c2 || (c == ord('<') && c2 == ord('>'))) { iop->ioflag |= c == c2 ? (c == ord('>') ? IOCAT : IOHERE) : IORDWR; if (iop->ioflag == IOHERE) { if ((c2 = getsc()) == ord('-')) iop->ioflag |= IOSKIP; else if (c2 == ord('<')) iop->ioflag |= IOHERESTR; else ungetsc(c2); } } else if (c2 == ord('&')) iop->ioflag |= IODUP | (c == ord('<') ? IORDUP : 0); else { iop->ioflag |= c == ord('>') ? IOWRITE : IOREAD; if (c == ord('>') && c2 == ord('|')) iop->ioflag |= IOCLOB; else ungetsc(c2); } iop->ioname = NULL; iop->delim = NULL; iop->heredoc = NULL; /* free word */ Xfree(ws, wp); yylval.iop = iop; return (REDIR); no_iop: afree(iop, ATEMP); } if (wp == dp && state == SBASE) { /* free word */ Xfree(ws, wp); /* no word, process LEX1 character */ if ((c == ord('|')) || (c == ord('&')) || (c == ord(';')) || (c == ord('(' /*)*/))) { if ((c2 = getsc()) == c) c = (c == ord(';')) ? BREAK : (c == ord('|')) ? LOGOR : (c == ord('&')) ? LOGAND : /* c == ord('(' )) */ MDPAREN; else if (c == ord('|') && c2 == ord('&')) c = COPROC; else if (c == ord(';') && c2 == ord('|')) c = BRKEV; else if (c == ord(';') && c2 == ord('&')) c = BRKFT; else ungetsc(c2); #ifndef MKSH_SMALL if (c == BREAK) { if ((c2 = getsc()) == ord('&')) c = BRKEV; else ungetsc(c2); } #endif } else if (c == ord('\n')) { if (cf & HEREDELIM) ungetsc(c); else { gethere(); if (cf & CONTIN) goto Again; } } else if (c == '\0' && !(cf & HEREDELIM)) { struct ioword **p = heres; while (p < herep) if ((*p)->ioflag & IOHERESTR) ++p; else /* ksh -c 'cat <<EOF' can cause this */ yyerror(Tf_heredoc, evalstr((*p)->delim, 0)); } return (c); } /* terminate word */ *wp++ = EOS; yylval.cp = Xclose(ws, wp); if (state == SWORD || state == SLETPAREN /* XXX ONEWORD? */) return (LWORD); /* unget terminator */ ungetsc(c); /* * note: the alias-vs-function code below depends on several * interna: starting from here, source->str is not modified; * the way getsc() and ungetsc() operate; etc. */ /* copy word to unprefixed string ident */ sp = yylval.cp; dp = ident; while ((dp - ident) < IDENT && (c = *sp++) == CHAR) *dp++ = *sp++; if (c != EOS) /* word is not unquoted, or space ran out */ dp = ident; /* make sure the ident array stays NUL padded */ memset(dp, 0, (ident + IDENT) - dp + 1); if (*ident != '\0' && (cf & (KEYWORD | ALIAS))) { struct tbl *p; uint32_t h = hash(ident); if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) && (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == ord(/*{*/ '}'))) { afree(yylval.cp, ATEMP); return (p->val.i); } if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) && (p->flag & ISSET)) { /* * this still points to the same character as the * ungetsc'd terminator from above */ const char *cp = source->str; /* prefer POSIX but not Korn functions over aliases */ while (ctype(*cp, C_BLANK)) /* * this is like getsc() without skipping * over Source boundaries (including not * parsing ungetsc'd characters that got * pushed into an SREREAD) which is what * we want here anyway: find out whether * the alias name is followed by a POSIX * function definition */ ++cp; /* prefer functions over aliases */ if (cp[0] != '(' || cp[1] != ')') { Source *s = source; while (s && (s->flags & SF_HASALIAS)) if (s->u.tblp == p) return (LWORD); else s = s->next; /* push alias expansion */ s = pushs(SALIAS, source->areap); s->start = s->str = p->val.s; s->u.tblp = p; s->flags |= SF_HASALIAS; s->line = source->line; s->next = source; if (source->type == SEOF) { /* prevent infinite recursion at EOS */ source->u.tblp = p; source->flags |= SF_HASALIAS; } source = s; afree(yylval.cp, ATEMP); goto Again; } } } else if (*ident == '\0') { /* retain typeset et al. even when quoted */ struct tbl *tt = get_builtin((dp = wdstrip(yylval.cp, 0))); uint32_t flag = tt ? tt->flag : 0; if (flag & (DECL_UTIL | DECL_FWDR)) strlcpy(ident, dp, sizeof(ident)); afree(dp, ATEMP); } return (LWORD); }
/* * print a command tree */ static void ptree(struct op *t, int indent, struct shf *shf) { const char **w; struct ioword **ioact; struct op *t1; int i; const char *ccp; Chain: if (t == NULL) return; switch (t->type) { case TCOM: prevent_semicolon = false; /* special-case 'var=<<EOF' (cf. exec.c:execute) */ if (t->args && /* we have zero arguments, i.e. no program to run */ t->args[0] == NULL && /* we have exactly one variable assignment */ t->vars[0] != NULL && t->vars[1] == NULL && /* we have exactly one I/O redirection */ t->ioact != NULL && t->ioact[0] != NULL && t->ioact[1] == NULL && /* of type "here document" (or "here string") */ (t->ioact[0]->ioflag & IOTYPE) == IOHERE && /* the variable assignment begins with a valid varname */ (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] && /* and has no right-hand side (i.e. "varname=") */ ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) || /* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR && ccp[3] == '=' && ccp[4] == EOS))) { fptreef(shf, indent, Tf_S, t->vars[0]); break; } if (t->vars) { w = (const char **)t->vars; while (*w) fptreef(shf, indent, Tf_S_, *w++); } else shf_puts("#no-vars# ", shf); if (t->args) { w = t->args; if (*w && **w == CHAR) { char *cp = wdstrip(*w++, WDS_TPUTS); if (valid_alias_name(cp)) shf_putc('\\', shf); shf_puts(cp, shf); shf_putc(' ', shf); afree(cp, ATEMP); } while (*w) fptreef(shf, indent, Tf_S_, *w++); } else shf_puts("#no-args# ", shf); break; case TEXEC: t = t->left; goto Chain; case TPAREN: fptreef(shf, indent + 2, "( %T) ", t->left); break; case TPIPE: fptreef(shf, indent, "%T| ", t->left); t = t->right; goto Chain; case TLIST: fptreef(shf, indent, "%T%;", t->left); t = t->right; goto Chain; case TOR: case TAND: fptreef(shf, indent, "%T%s %T", t->left, (t->type == TOR) ? "||" : "&&", t->right); break; case TBANG: shf_puts("! ", shf); prevent_semicolon = false; t = t->right; goto Chain; case TDBRACKET: w = t->args; shf_puts("[[", shf); while (*w) fptreef(shf, indent, Tf__S, *w++); shf_puts(" ]] ", shf); break; case TSELECT: case TFOR: fptreef(shf, indent, "%s %s ", (t->type == TFOR) ? "for" : Tselect, t->str); if (t->vars != NULL) { shf_puts("in ", shf); w = (const char **)t->vars; while (*w) fptreef(shf, indent, Tf_S_, *w++); fptreef(shf, indent, Tft_end); } fptreef(shf, indent + INDENT, "do%N%T", t->left); fptreef(shf, indent, "%;done "); break; case TCASE: fptreef(shf, indent, "case %S in", t->str); for (t1 = t->left; t1 != NULL; t1 = t1->right) { fptreef(shf, indent, "%N("); w = (const char **)t1->vars; while (*w) { fptreef(shf, indent, "%S%c", *w, (w[1] != NULL) ? '|' : ')'); ++w; } fptreef(shf, indent + INDENT, "%N%T%N;%c", t1->left, t1->u.charflag); } fptreef(shf, indent, "%Nesac "); break; case TELIF: internal_errorf(TELIF_unexpected); /* FALLTHROUGH */ case TIF: i = 2; t1 = t; goto process_TIF; do { t1 = t1->right; i = 0; fptreef(shf, indent, Tft_end); process_TIF: /* 5 == strlen("elif ") */ fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left); t1 = t1->right; if (t1->left != NULL) { fptreef(shf, indent, Tft_end); fptreef(shf, indent + INDENT, "%s%N%T", "then", t1->left); } } while (t1->right && t1->right->type == TELIF); if (t1->right != NULL) { fptreef(shf, indent, Tft_end); fptreef(shf, indent + INDENT, "%s%N%T", "else", t1->right); } fptreef(shf, indent, "%;fi "); break; case TWHILE: case TUNTIL: /* 6 == strlen("while "/"until ") */ fptreef(shf, indent + 6, Tf_s_T, (t->type == TWHILE) ? "while" : "until", t->left); fptreef(shf, indent, Tft_end); fptreef(shf, indent + INDENT, "do%N%T", t->right); fptreef(shf, indent, "%;done "); break; case TBRACE: fptreef(shf, indent + INDENT, "{%N%T", t->left); fptreef(shf, indent, "%;} "); break; case TCOPROC: fptreef(shf, indent, "%T|& ", t->left); prevent_semicolon = true; break; case TASYNC: fptreef(shf, indent, "%T& ", t->left); prevent_semicolon = true; break; case TFUNCT: fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left); break; case TTIME: fptreef(shf, indent, Tf_s_T, Ttime, t->left); break; default: shf_puts("<botch>", shf); prevent_semicolon = false; break; } if ((ioact = t->ioact) != NULL) { bool need_nl = false; while (*ioact != NULL) pioact(shf, *ioact++); /* Print here documents after everything else... */ ioact = t->ioact; while (*ioact != NULL) { struct ioword *iop = *ioact++; /* heredoc is NULL when tracing (set -x) */ if ((iop->ioflag & (IOTYPE | IOHERESTR)) == IOHERE && iop->heredoc) { shf_putc('\n', shf); shf_puts(iop->heredoc, shf); fptreef(shf, indent, Tf_s, evalstr(iop->delim, 0)); need_nl = true; } } /* * Last delimiter must be followed by a newline (this * often leads to an extra blank line, but it's not * worth worrying about) */ if (need_nl) { shf_putc('\n', shf); prevent_semicolon = true; } } }
static int x_command_glob(int flags, const char *str, int slen, char ***wordsp) { char *toglob; char *pat; char *fpath; int nwords; XPtrV w; struct block *l; if (slen < 0) return 0; toglob = add_glob(str, slen); /* Convert "foo*" (toglob) to a pattern for future use */ pat = evalstr(toglob, DOPAT|DOTILDE); afree(toglob, ATEMP); XPinit(w, 32); glob_table(pat, &w, &keywords); glob_table(pat, &w, &aliases); glob_table(pat, &w, &builtins); for (l = e->loc; l; l = l->next) glob_table(pat, &w, &l->funs); glob_path(flags, pat, &w, path); if ((fpath = str_val(global("FPATH"))) != null) glob_path(flags, pat, &w, fpath); nwords = XPsize(w); if (!nwords) { *wordsp = NULL; XPfree(w); return 0; } /* Sort entries */ if (flags & XCF_FULLPATH) { /* Sort by basename, then path order */ struct path_order_info *info; struct path_order_info *last_info = NULL; char **words = (char **) XPptrv(w); int path_order = 0; int i; info = areallocarray(NULL, nwords, sizeof(struct path_order_info), ATEMP); for (i = 0; i < nwords; i++) { info[i].word = words[i]; info[i].base = x_basename(words[i], NULL); if (!last_info || info[i].base != last_info->base || strncmp(words[i], last_info->word, info[i].base) != 0) { last_info = &info[i]; path_order++; } info[i].path_order = path_order; } qsort(info, nwords, sizeof(struct path_order_info), path_order_cmp); for (i = 0; i < nwords; i++) words[i] = info[i].word; afree(info, ATEMP); } else { /* Sort and remove duplicate entries */ char **words = (char **) XPptrv(w); int i, j; qsortp(XPptrv(w), (size_t) nwords, xstrcmp); for (i = j = 0; i < nwords - 1; i++) { if (strcmp(words[i], words[i + 1])) words[j++] = words[i]; else afree(words[i], ATEMP); } words[j++] = words[i]; nwords = j; w.cur = (void **) &words[j]; } XPput(w, NULL); *wordsp = (char **) XPclose(w); return nwords; }
static int comexec(struct op *t, struct tbl * volatile tp, const char **ap, volatile int flags, volatile int *xerrok) { int i; volatile int rv = 0; const char *cp; const char **lastp; /* Must be static (XXX but why?) */ static struct op texec; int type_flags; bool resetspec; int fcflags = FC_BI|FC_FUNC|FC_PATH; struct block *l_expand, *l_assign; int optc; const char *exec_argv0 = NULL; bool exec_clrenv = false; /* snag the last argument for $_ */ if (Flag(FTALKING) && *(lastp = ap)) { /* * XXX not the same as AT&T ksh, which only seems to set $_ * after a newline (but not in functions/dot scripts, but in * interactive and script) - perhaps save last arg here and * set it in shell()?. */ while (*++lastp) ; /* setstr() can't fail here */ setstr(typeset("_", LOCAL, 0, INTEGER, 0), *--lastp, KSH_RETURN_ERROR); } /** * Deal with the shell builtins builtin, exec and command since * they can be followed by other commands. This must be done before * we know if we should create a local block which must be done * before we can do a path search (in case the assignments change * PATH). * Odd cases: * FOO=bar exec >/dev/null FOO is kept but not exported * FOO=bar exec foobar FOO is exported * FOO=bar command exec >/dev/null FOO is neither kept nor exported * FOO=bar command FOO is neither kept nor exported * PATH=... foobar use new PATH in foobar search */ resetspec = false; while (tp && tp->type == CSHELL) { /* undo effects of command */ fcflags = FC_BI|FC_FUNC|FC_PATH; if (tp->val.f == c_builtin) { if ((cp = *++ap) == NULL || (!strcmp(cp, "--") && (cp = *++ap) == NULL)) { tp = NULL; break; } if ((tp = findcom(cp, FC_BI)) == NULL) errorf("%s: %s: %s", Tbuiltin, cp, "not a builtin"); if (tp->type == CSHELL && (tp->val.f == c_cat #ifdef MKSH_PRINTF_BUILTIN || tp->val.f == c_printf #endif )) break; continue; } else if (tp->val.f == c_exec) { if (ap[1] == NULL) break; ksh_getopt_reset(&builtin_opt, GF_ERROR); while ((optc = ksh_getopt(ap, &builtin_opt, "a:c")) != -1) switch (optc) { case 'a': exec_argv0 = builtin_opt.optarg; break; case 'c': exec_clrenv = true; /* ensure we can actually do this */ resetspec = true; break; default: rv = 2; goto Leave; } ap += builtin_opt.optind; flags |= XEXEC; } else if (tp->val.f == c_command) { bool saw_p = false; /* * Ugly dealing with options in two places (here * and in c_command(), but such is life) */ ksh_getopt_reset(&builtin_opt, 0); while ((optc = ksh_getopt(ap, &builtin_opt, ":p")) == 'p') saw_p = true; if (optc != -1) /* command -vV or something */ break; /* don't look for functions */ fcflags = FC_BI|FC_PATH; if (saw_p) { if (Flag(FRESTRICTED)) { warningf(true, "%s: %s", "command -p", "restricted"); rv = 1; goto Leave; } fcflags |= FC_DEFPATH; } ap += builtin_opt.optind; /* * POSIX says special builtins lose their status * if accessed using command. */ resetspec = true; if (!ap[0]) { /* ensure command with no args exits with 0 */ subst_exstat = 0; break; } } else if (tp->val.f == c_cat) { /* if we have any flags, do not use the builtin */ if (ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' && /* argument, begins with -, is not - or -- */ (ap[1][1] != '-' || ap[1][2] != '\0')) { struct tbl *ext_cat; ext_cat = findcom(Tcat, FC_PATH | FC_FUNC); if (ext_cat && (ext_cat->type != CTALIAS || (ext_cat->flag & ISSET))) tp = ext_cat; } break; #ifdef MKSH_PRINTF_BUILTIN } else if (tp->val.f == c_printf) { struct tbl *ext_printf; ext_printf = findcom(Tprintf, FC_PATH | FC_FUNC); if (ext_printf && (ext_printf->type != CTALIAS || (ext_printf->flag & ISSET))) tp = ext_printf; break; #endif } else if (tp->val.f == c_trap) { t->u.evalflags &= ~DOTCOMEXEC; break; } else break; tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC)); } if (t->u.evalflags & DOTCOMEXEC) flags |= XEXEC; l_expand = e->loc; if (!resetspec && (!ap[0] || (tp && (tp->flag & KEEPASN)))) type_flags = 0; else { /* create new variable/function block */ newblock(); /* ksh functions don't keep assignments, POSIX functions do. */ if (!resetspec && tp && tp->type == CFUNC && !(tp->flag & FKSH)) type_flags = EXPORT; else type_flags = LOCAL|LOCAL_COPY|EXPORT; } l_assign = e->loc; if (exec_clrenv) l_assign->flags |= BF_STOPENV; if (Flag(FEXPORT)) type_flags |= EXPORT; if (Flag(FXTRACE)) change_xtrace(2, false); for (i = 0; t->vars[i]; i++) { /* do NOT lookup in the new var/fn block just created */ e->loc = l_expand; cp = evalstr(t->vars[i], DOASNTILDE | DOSCALAR); e->loc = l_assign; if (Flag(FXTRACE)) { const char *ccp; ccp = skip_varname(cp, true); if (*ccp == '+') ++ccp; if (*ccp == '=') ++ccp; shf_write(cp, ccp - cp, shl_xtrace); print_value_quoted(shl_xtrace, ccp); shf_putc(' ', shl_xtrace); } /* but assign in there as usual */ typeset(cp, type_flags, 0, 0, 0); } if (Flag(FXTRACE)) { change_xtrace(2, false); if (ap[rv = 0]) { xtrace_ap_loop: print_value_quoted(shl_xtrace, ap[rv]); if (ap[++rv]) { shf_putc(' ', shl_xtrace); goto xtrace_ap_loop; } } change_xtrace(1, false); } if ((cp = *ap) == NULL) { rv = subst_exstat; goto Leave; } else if (!tp) { if (Flag(FRESTRICTED) && vstrchr(cp, '/')) { warningf(true, "%s: %s", cp, "restricted"); rv = 1; goto Leave; } tp = findcom(cp, fcflags); } switch (tp->type) { /* shell built-in */ case CSHELL: do_call_builtin: rv = call_builtin(tp, (const char **)ap, null, resetspec); if (resetspec && tp->val.f == c_shift) { l_expand->argc = l_assign->argc; l_expand->argv = l_assign->argv; } break; /* function call */ case CFUNC: { volatile uint32_t old_inuse; const char * volatile old_kshname; volatile uint8_t old_flags[FNFLAGS]; if (!(tp->flag & ISSET)) { struct tbl *ftp; if (!tp->u.fpath) { rv = (tp->u2.errnov == ENOENT) ? 127 : 126; warningf(true, "%s: %s %s: %s", cp, "can't find", "function definition file", cstrerror(tp->u2.errnov)); break; } if (include(tp->u.fpath, 0, NULL, false) < 0) { if (!strcmp(cp, Tcat)) { no_cat_in_FPATH: tp = findcom(Tcat, FC_BI); goto do_call_builtin; } #ifdef MKSH_PRINTF_BUILTIN if (!strcmp(cp, Tprintf)) { no_printf_in_FPATH: tp = findcom(Tprintf, FC_BI); goto do_call_builtin; } #endif warningf(true, "%s: %s %s %s: %s", cp, "can't open", "function definition file", tp->u.fpath, cstrerror(errno)); rv = 127; break; } if (!(ftp = findfunc(cp, hash(cp), false)) || !(ftp->flag & ISSET)) { if (!strcmp(cp, Tcat)) goto no_cat_in_FPATH; #ifdef MKSH_PRINTF_BUILTIN if (!strcmp(cp, Tprintf)) goto no_printf_in_FPATH; #endif warningf(true, "%s: %s %s", cp, "function not defined by", tp->u.fpath); rv = 127; break; } tp = ftp; } /* * ksh functions set $0 to function name, POSIX * functions leave $0 unchanged. */ old_kshname = kshname; if (tp->flag & FKSH) kshname = ap[0]; else ap[0] = kshname; e->loc->argv = ap; for (i = 0; *ap++ != NULL; i++) ; e->loc->argc = i - 1; /* * ksh-style functions handle getopts sanely, * Bourne/POSIX functions are insane... */ if (tp->flag & FKSH) { e->loc->flags |= BF_DOGETOPTS; e->loc->getopts_state = user_opt; getopts_reset(1); } for (type_flags = 0; type_flags < FNFLAGS; ++type_flags) old_flags[type_flags] = shell_flags[type_flags]; change_xtrace((Flag(FXTRACEREC) ? Flag(FXTRACE) : 0) | ((tp->flag & TRACE) ? 1 : 0), false); old_inuse = tp->flag & FINUSE; tp->flag |= FINUSE; e->type = E_FUNC; if (!(i = kshsetjmp(e->jbuf))) { execute(tp->val.t, flags & XERROK, NULL); i = LRETURN; } kshname = old_kshname; change_xtrace(old_flags[(int)FXTRACE], false); #ifndef MKSH_LEGACY_MODE if (tp->flag & FKSH) { /* Korn style functions restore Flags on return */ old_flags[(int)FXTRACE] = Flag(FXTRACE); for (type_flags = 0; type_flags < FNFLAGS; ++type_flags) shell_flags[type_flags] = old_flags[type_flags]; } #endif tp->flag = (tp->flag & ~FINUSE) | old_inuse; /* * Were we deleted while executing? If so, free the * execution tree. */ if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) { if (tp->flag & ALLOC) { tp->flag &= ~ALLOC; tfree(tp->val.t, tp->areap); } tp->flag = 0; } switch (i) { case LRETURN: case LERROR: rv = exstat & 0xFF; break; case LINTR: case LEXIT: case LLEAVE: case LSHELL: quitenv(NULL); unwind(i); /* NOTREACHED */ default: quitenv(NULL); internal_errorf("%s %d", "CFUNC", i); } break; } /* executable command */ case CEXEC: /* tracked alias */ case CTALIAS: if (!(tp->flag&ISSET)) { if (tp->u2.errnov == ENOENT) { rv = 127; warningf(true, "%s: %s", cp, "not found"); } else { rv = 126; warningf(true, "%s: %s: %s", cp, "can't execute", cstrerror(tp->u2.errnov)); } break; } /* set $_ to program's full path */ /* setstr() can't fail here */ setstr(typeset("_", LOCAL | EXPORT, 0, INTEGER, 0), tp->val.s, KSH_RETURN_ERROR); /* to fork, we set up a TEXEC node and call execute */ texec.type = TEXEC; /* for vistree/dumptree */ texec.left = t; texec.str = tp->val.s; texec.args = ap; /* in this case we do not fork, of course */ if (flags & XEXEC) { if (exec_argv0) texec.args[0] = exec_argv0; j_exit(); if (!(flags & XBGND) #ifndef MKSH_UNEMPLOYED || Flag(FMONITOR) #endif ) { setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG); setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG); } } rv = exchild(&texec, flags, xerrok, -1); break; } Leave: if (flags & XEXEC) { exstat = rv & 0xFF; unwind(LLEAVE); } return (rv); }