int shcomexec(const char **wp) { struct tbl *tp; tp = ktsearch(&builtins, *wp, hash(*wp)); return (call_builtin(tp, wp, "shcomexec")); }
/* Test if name is a special parameter */ static int special(const char *name) { struct tbl *tp; tp = ktsearch(&specials, name, hash(name)); return (tp && (tp->flag & ISSET) ? tp->type : V_NONE); }
/* Make a variable non-special */ static void unspecial(const char *name) { struct tbl *tp; tp = ktsearch(&specials, name, hash(name)); if (tp) ktdelete(tp); }
/* * define function. Returns 1 if function is being undefined (t == 0) and * function did not exist, returns 0 otherwise. */ int define(const char *name, struct op *t) { uint32_t nhash; struct tbl *tp; bool was_set = false; nhash = hash(name); if (t != NULL && !tobool(t->u.ksh_func)) { /* drop same-name aliases for POSIX functions */ if ((tp = ktsearch(&aliases, name, nhash))) ktdelete(tp); } while (/* CONSTCOND */ 1) { tp = findfunc(name, nhash, true); /* because findfunc:create=true */ mkssert(tp != NULL); if (tp->flag & ISSET) was_set = true; /* * If this function is currently being executed, we zap * this table entry so findfunc() won't see it */ if (tp->flag & FINUSE) { tp->name[0] = '\0'; /* ensure it won't be found */ tp->flag &= ~DEFINED; tp->flag |= FDELETE; } else break; } if (tp->flag & ALLOC) { tp->flag &= ~(ISSET|ALLOC); tfree(tp->val.t, tp->areap); } if (t == NULL) { /* undefine */ ktdelete(tp); return (was_set ? 0 : 1); } tp->val.t = tcopy(t->left, tp->areap); tp->flag |= (ISSET|ALLOC); if (t->u.ksh_func) tp->flag |= FKSH; return (0); }
/* common code for several functions below and c_typeset() */ struct block * varsearch(struct block *l, struct tbl **vpp, const char *vn, uint32_t h) { register struct tbl *vp; if (l) { varsearch_loop: if ((vp = ktsearch(&l->vars, vn, h)) != NULL) goto varsearch_out; if (l->next != NULL) { l = l->next; goto varsearch_loop; } } vp = NULL; varsearch_out: *vpp = vp; return (l); }
/* * Search for local variable, if not found create locally. */ struct tbl * local(const char *n, bool copy) { struct block *l = e->loc; struct tbl *vp; unsigned int h; bool array; int val; /* Check to see if this is an array */ n = array_index_calc(n, &array, &val); h = hash(n); if (!letter(*n)) { vp = &vtemp; vp->flag = DEFINED|RDONLY; vp->type = 0; vp->areap = ATEMP; return vp; } vp = ktenter(&l->vars, n, h); if (copy && !(vp->flag & DEFINED)) { struct block *ll = l; struct tbl *vq = (struct tbl *) 0; while ((ll = ll->next) && !(vq = ktsearch(&ll->vars, n, h))) ; if (vq) { vp->flag |= vq->flag & (EXPORT | INTEGER | RDONLY | LJUST | RJUST | ZEROFIL | LCASEV | UCASEV_AL | INT_U | INT_L); if (vq->flag & INTEGER) vp->type = vq->type; vp->u2.field = vq->u2.field; } } if (array) vp = arraysearch(vp, val); vp->flag |= DEFINED; if (special(n)) vp->flag |= SPECIAL; return vp; }
/* * Search function tables for a function. If create set, a table entry * is created if none is found. */ struct tbl * findfunc(const char *name, uint32_t h, bool create) { struct block *l; struct tbl *tp = NULL; for (l = e->loc; l; l = l->next) { tp = ktsearch(&l->funs, name, h); if (tp) break; if (!l->next && create) { tp = ktenter(&l->funs, name, h); tp->flag = DEFINED; tp->type = CFUNC; tp->val.t = NULL; break; } } return (tp); }
/* * find command * either function, hashed command, or built-in (in that order) */ struct tbl * findcom(const char *name, int flags) { static struct tbl temp; uint32_t h = hash(name); struct tbl *tp = NULL, *tbi; /* insert if not found */ unsigned char insert = Flag(FTRACKALL); /* for function autoloading */ char *fpath; union mksh_cchack npath; if (vstrchr(name, '/')) { insert = 0; /* prevent FPATH search below */ flags &= ~FC_FUNC; goto Search; } tbi = (flags & FC_BI) ? ktsearch(&builtins, name, h) : NULL; /* * POSIX says special builtins first, then functions, then * POSIX regular builtins, then search path... */ if ((flags & FC_SPECBI) && tbi && (tbi->flag & SPEC_BI)) tp = tbi; if (!tp && (flags & FC_FUNC)) { tp = findfunc(name, h, false); if (tp && !(tp->flag & ISSET)) { if ((fpath = str_val(global("FPATH"))) == null) { tp->u.fpath = NULL; tp->u2.errnov = ENOENT; } else tp->u.fpath = search_path(name, fpath, R_OK, &tp->u2.errnov); } } if (!tp && (flags & FC_REGBI) && tbi && (tbi->flag & REG_BI)) tp = tbi; if (!tp && (flags & FC_UNREGBI) && tbi) tp = tbi; if (!tp && (flags & FC_PATH) && !(flags & FC_DEFPATH)) { tp = ktsearch(&taliases, name, h); if (tp && (tp->flag & ISSET) && ksh_access(tp->val.s, X_OK) != 0) { if (tp->flag & ALLOC) { tp->flag &= ~ALLOC; afree(tp->val.s, APERM); } tp->flag &= ~ISSET; } } Search: if ((!tp || (tp->type == CTALIAS && !(tp->flag&ISSET))) && (flags & FC_PATH)) { if (!tp) { if (insert && !(flags & FC_DEFPATH)) { tp = ktenter(&taliases, name, h); tp->type = CTALIAS; } else { tp = &temp; tp->type = CEXEC; } /* make ~ISSET */ tp->flag = DEFINED; } npath.ro = search_path(name, (flags & FC_DEFPATH) ? def_path : path, X_OK, &tp->u2.errnov); if (npath.ro) { strdupx(tp->val.s, npath.ro, APERM); if (npath.ro != name) afree(npath.rw, ATEMP); tp->flag |= ISSET|ALLOC; } else if ((flags & FC_FUNC) && (fpath = str_val(global("FPATH"))) != null && (npath.ro = search_path(name, fpath, R_OK, &tp->u2.errnov)) != NULL) { /* * An undocumented feature of AT&T ksh is that * it searches FPATH if a command is not found, * even if the command hasn't been set up as an * autoloaded function (ie, no typeset -uf). */ tp = &temp; tp->type = CFUNC; /* make ~ISSET */ tp->flag = DEFINED; tp->u.fpath = npath.ro; } } return (tp); }
/* * Search for variable, if not found create globally. */ struct tbl * global(const char *n) { struct block *l = e->loc; struct tbl *vp; int c; unsigned int h; bool array; int val; /* Check to see if this is an array */ n = array_index_calc(n, &array, &val); h = hash(n); c = n[0]; if (!letter(c)) { if (array) errorf("bad substitution"); vp = &vtemp; vp->flag = DEFINED; vp->type = 0; vp->areap = ATEMP; *vp->name = c; if (digit(c)) { for (c = 0; digit(*n); n++) c = c*10 + *n-'0'; if (c <= l->argc) /* setstr can't fail here */ setstr(vp, l->argv[c], KSH_RETURN_ERROR); vp->flag |= RDONLY; return vp; } vp->flag |= RDONLY; if (n[1] != '\0') return vp; vp->flag |= ISSET|INTEGER; switch (c) { case '$': vp->val.i = kshpid; break; case '!': /* If no job, expand to nothing */ if ((vp->val.i = j_async()) == 0) vp->flag &= ~(ISSET|INTEGER); break; case '?': vp->val.i = exstat; break; case '#': vp->val.i = l->argc; break; case '-': vp->flag &= ~INTEGER; vp->val.s = getoptions(); break; default: vp->flag &= ~(ISSET|INTEGER); } return vp; } for (l = e->loc; ; l = l->next) { vp = ktsearch(&l->vars, n, h); if (vp != NULL) { if (array) return arraysearch(vp, val); else return vp; } if (l->next == NULL) break; } vp = ktenter(&l->vars, n, h); if (array) vp = arraysearch(vp, val); vp->flag |= DEFINED; if (special(n)) vp->flag |= SPECIAL; return vp; }
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 ((c = getsc()) == ' ' || c == '\t') ; if (c == '#') { ignore_backslash_newline++; while ((c = getsc()) != '\0' && c != '\n') ; 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 == /*{*/ '}' && c == /*{*/ '}') /* possibly end ${ :;} */ break; Xcheck(ws, wp); switch (state) { case SADELIM: if (c == '(') statep->nparen++; else if (c == ')') statep->nparen--; else if (statep->nparen == 0 && (c == /*{*/ '}' || c == (int)statep->ls_adelim.delimiter)) { *wp++ = ADELIM; *wp++ = c; if (c == /*{*/ '}' || --statep->ls_adelim.num == 0) POP_STATE(); if (c == /*{*/ '}') POP_STATE(); break; } /* FALLTHROUGH */ case SBASE: if (c == '[' && (cf & (VARASN|ARRAYVAR))) { /* 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 (c == '*' || c == '@' || c == '+' || c == '?' || c == '!') { c2 = getsc(); if (c2 == '(' /*)*/ ) { *wp++ = OPAT; *wp++ = c; PUSH_STATE(SPATTERN); break; } ungetsc(c2); } /* FALLTHROUGH */ Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */ switch (c) { case '\\': getsc_qchar: if ((c = getsc())) { /* trailing \ is lost */ *wp++ = QCHAR; *wp++ = c; } break; case '\'': open_ssquote_unless_heredoc: if ((cf & HEREDOC)) goto store_char; *wp++ = OQUOTE; ignore_backslash_newline++; PUSH_STATE(SSQUOTE); break; case '"': open_sdquote: *wp++ = OQUOTE; PUSH_STATE(SDQUOTE); break; case '$': /* * processing of dollar sign belongs into * Subst, except for those which can open * a string: $'…' and $"…" */ subst_dollar_ex: c = getsc(); switch (c) { case '"': goto open_sdquote; case '\'': goto open_sequote; default: goto SubstS; } default: goto Subst; } break; Subst: switch (c) { case '\\': c = getsc(); switch (c) { case '"': if ((cf & HEREDOC)) goto heredocquote; /* FALLTHROUGH */ case '\\': case '$': case '`': 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 '$': c = getsc(); SubstS: if (c == '(') /*)*/ { c = getsc(); if (c == '(') /*)*/ { *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 == '{') /*}*/ { if ((c = getsc()) == '|') { /* * 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 == ':') { *wp++ = CHAR; *wp++ = c; c = getsc(); if (c == ':') { *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 (ksh_isdigit(c) || c == '('/*)*/ || c == ' ' || /*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 == '/') { *wp++ = CHAR; *wp++ = c; if ((c = getsc()) == '/') { *wp++ = ADELIM; *wp++ = c; } else ungetsc(c); PUSH_STATE(SBRACE); PUSH_STATE(SADELIM); statep->ls_adelim.delimiter = '/'; statep->ls_adelim.num = 1; statep->nparen = 0; break; } /* * If this is a trim operation, * treat (,|,) specially in STBRACE. */ if (ctype(c, C_SUBOP2)) { 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 (ksh_isalphx(c)) { *wp++ = OSUBST; *wp++ = 'X'; do { Xcheck(ws, wp); *wp++ = c; c = getsc(); } while (ksh_isalnux(c)); *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 '`': subst_gravis: PUSH_STATE(SBQUOTE); *wp++ = COMSUB; /* * We need to know whether we are within double * quotes, since most shells translate \" to " * within "…`…\"…`…". This is not done in POSIX * mode (§2.2.3 Double-Quotes: “The backquote * shall retain its special meaning introducing * the other form of command substitution (see * Command Substitution). The portion of the * quoted string from the initial backquote and * the characters up to the next backquote that * is not preceded by a <backslash>, having * escape characters removed, defines that * command whose output replaces "`...`" when * the word is expanded.”; §2.6.3 Command * Substitution: “Within the backquoted style * of command substitution, <backslash> shall * retain its literal meaning, except when * followed by: '$', '`', or <backslash>. The * search for the matching backquote shall be * satisfied by the first unquoted non-escaped * backquote; during this search, if a * non-escaped backquote is encountered[…], * undefined results occur.”). */ statep->ls_bool = false; if (Flag(FPOSIX)) break; 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 == '\'') { POP_STATE(); *wp++ = CQUOTE; ignore_backslash_newline--; } else if (c == '\\') { if ((c2 = unbksl(true, s_get, s_put)) == -1) c2 = s_get(); 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 == '\'') { 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 == '"') { POP_STATE(); *wp++ = CQUOTE; } else goto Subst; break; /* $(( ... )) */ case SASPAREN: if (c == '(') statep->nparen++; else if (c == ')') { statep->nparen--; if (statep->nparen == 1) { /* end of EXPRSUB */ POP_SRETRACE(); if ((c2 = getsc()) == /*(*/ ')') { 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 == '\\') { /* * 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()) == '"' || c == '\\' || c == '$' || c == '`' || c == /*{*/'}') goto store_qchar; goto heredocquote; } goto common_SQBRACE; case SBRACE: if (c == '\'') goto open_ssquote_unless_heredoc; else if (c == '\\') goto getsc_qchar; common_SQBRACE: if (c == '"') goto open_sdquote; else if (c == '$') goto subst_dollar_ex; else if (c == '`') goto subst_gravis; else if (c != /*{*/ '}') goto store_char; POP_STATE(); *wp++ = CSUBST; *wp++ = /*{*/ '}'; break; /* Same as SBASE, except (,|,) treated specially */ case STBRACEKORN: if (c == '|') *wp++ = SPAT; else if (c == '(') { *wp++ = OPAT; /* simile for @ */ *wp++ = ' '; PUSH_STATE(SPATTERN); } else /* FALLTHROUGH */ case STBRACEBOURNE: if (c == /*{*/ '}') { POP_STATE(); *wp++ = CSUBST; *wp++ = /*{*/ '}'; } else goto Sbase1; break; case SBQUOTE: if (c == '`') { *wp++ = 0; POP_STATE(); } else if (c == '\\') { switch (c = getsc()) { case 0: /* trailing \ is lost */ break; case '$': case '`': case '\\': *wp++ = c; break; case '"': 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 == /*(*/ ')') { if (statep->nparen > 0) --statep->nparen; else if ((c2 = getsc()) == /*(*/ ')') { c = 0; *wp++ = CQUOTE; goto Done; } else { Source *s; ungetsc(c2); /* * 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; return ('('/*)*/); } } else if (c == '(') /* * 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 '\\': if ((c = getsc())) { /* trailing \ is lost */ *wp++ = QCHAR; *wp++ = c; } break; case '\'': goto open_ssquote_unless_heredoc; case '$': if ((c2 = getsc()) == '\'') { open_sequote: *wp++ = OQUOTE; ignore_backslash_newline++; PUSH_STATE(SEQUOTE); statep->ls_bool = false; break; } else if (c2 == '"') { /* FALLTHROUGH */ case '"': PUSH_SRETRACE(SHEREDQUOTE); break; } ungetsc(c2); /* FALLTHROUGH */ default: *wp++ = CHAR; *wp++ = c; } break; /* " in << or <<- delimiter */ case SHEREDQUOTE: if (c != '"') 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 '\\': case '"': case '$': case '`': 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 == /*(*/ ')') { *wp++ = CPAT; POP_STATE(); } else if (c == '|') { *wp++ = SPAT; } else if (c == '(') { *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\n"); /* This done to avoid tests for SHEREDELIM wherever SBASE tested */ if (state == SHEREDELIM) state = SBASE; dp = Xstring(ws, wp); if (state == SBASE && ( #ifndef MKSH_LEGACY_MODE (c == '&' && !Flag(FSH) && !Flag(FPOSIX)) || #endif c == '<' || c == '>')) { struct ioword *iop = alloc(sizeof(struct ioword), ATEMP); if (Xlength(ws, wp) == 0) iop->unit = c == '<' ? 0 : 1; else for (iop->unit = 0, c2 = 0; c2 < Xlength(ws, wp); c2 += 2) { if (dp[c2] != CHAR) goto no_iop; if (!ksh_isdigit(dp[c2 + 1])) goto no_iop; iop->unit = iop->unit * 10 + ksh_numdig(dp[c2 + 1]); if (iop->unit >= FDBASE) goto no_iop; } if (c == '&') { if ((c2 = getsc()) != '>') { ungetsc(c2); goto no_iop; } c = c2; iop->ioflag = IOBASH; } else iop->ioflag = 0; c2 = getsc(); /* <<, >>, <> are ok, >< is not */ if (c == c2 || (c == '<' && c2 == '>')) { iop->ioflag |= c == c2 ? (c == '>' ? IOCAT : IOHERE) : IORDWR; if (iop->ioflag == IOHERE) { if ((c2 = getsc()) == '-') iop->ioflag |= IOSKIP; else if (c2 == '<') iop->ioflag |= IOHERESTR; else ungetsc(c2); } } else if (c2 == '&') iop->ioflag |= IODUP | (c == '<' ? IORDUP : 0); else { iop->ioflag |= c == '>' ? IOWRITE : IOREAD; if (c == '>' && c2 == '|') 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 == '|') || (c == '&') || (c == ';') || (c == '('/*)*/)) { if ((c2 = getsc()) == c) c = (c == ';') ? BREAK : (c == '|') ? LOGOR : (c == '&') ? LOGAND : /* c == '(' ) */ MDPAREN; else if (c == '|' && c2 == '&') c = COPROC; else if (c == ';' && c2 == '|') c = BRKEV; else if (c == ';' && c2 == '&') c = BRKFT; else ungetsc(c2); #ifndef MKSH_SMALL if (c == BREAK) { if ((c2 = getsc()) == '&') c = BRKEV; else ungetsc(c2); } #endif } else if (c == '\n') { if (cf & HEREDELIM) ungetsc(c); else { gethere(); if (cf & CONTIN) goto Again; } } 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 */ dp = ident; /* make sure the ident array stays NUL padded */ memset(dp, 0, (ident + IDENT) - dp + 1); if (!(cf & (KEYWORD | ALIAS))) return (LWORD); if (*ident != '\0') { 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 == /*{*/ '}')) { 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 (*cp == ' ' || *cp == '\t') /* * 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->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 (cf & ALIAS) { /* retain typeset et al. even when quoted */ if (assign_command((dp = wdstrip(yylval.cp, 0)))) strlcpy(ident, dp, sizeof(ident)); afree(dp, ATEMP); } return (LWORD); }