/* Return a pointer to the first character past any legal variable name */ const char * skip_wdvarname(const char *s, /* skip array de-reference? */ bool aok) { if (s[0] == CHAR && ksh_isalphx(s[1])) { do { s += 2; } while (s[0] == CHAR && ksh_isalnux(s[1])); if (aok && s[0] == CHAR && s[1] == '[') { /* skip possible array de-reference */ const char *p = s; char c; int depth = 0; while (/* CONSTCOND */ 1) { if (p[0] != CHAR) break; c = p[1]; p += 2; if (c == '[') depth++; else if (c == ']' && --depth == 0) { s = p; break; } } } } return (s); }
/* * Return a pointer to the first char past a legal variable name * (returns the argument if there is no legal name, returns a pointer to * the terminating NUL if whole string is legal). */ const char * skip_varname(const char *s, bool aok) { size_t alen; if (s && ksh_isalphx(*s)) { while (*++s && ksh_isalnux(*s)) ; if (aok && *s == '[' && (alen = array_ref_len(s))) s += alen; } return (s); }
/* * Used to calculate an array index for global()/local(). Sets *arrayp * to true if this is an array, sets *valp to the array index, returns * the basename of the array. May only be called from global()/local() * and must be their first callee. */ static const char * array_index_calc(const char *n, bool *arrayp, uint32_t *valp) { const char *p; size_t len; char *ap = NULL; *arrayp = false; redo_from_ref: p = skip_varname(n, false); if (innermost_refflag == SRF_NOP && (p != n) && ksh_isalphx(n[0])) { struct tbl *vp; char *vn; strndupx(vn, n, p - n, ATEMP); /* check if this is a reference */ varsearch(e->loc, &vp, vn, hash(vn)); afree(vn, ATEMP); if (vp && (vp->flag & (DEFINED | ASSOC | ARRAY)) == (DEFINED | ASSOC)) { char *cp; /* gotcha! */ cp = shf_smprintf("%s%s", str_val(vp), p); afree(ap, ATEMP); n = ap = cp; goto redo_from_ref; } } innermost_refflag = SRF_NOP; if (p != n && *p == '[' && (len = array_ref_len(p))) { char *sub, *tmp; mksh_ari_t rval; /* calculate the value of the subscript */ *arrayp = true; strndupx(tmp, p + 1, len - 2, ATEMP); sub = substitute(tmp, 0); afree(tmp, ATEMP); strndupx(n, n, p - n, ATEMP); evaluate(sub, &rval, KSH_UNWIND_ERROR, true); *valp = (uint32_t)rval; afree(sub, ATEMP); } return (n); }
/* * 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; bool array; uint32_t h, val; /* * check to see if this is an array; * dereference namerefs; must come first */ n = array_index_calc(n, &array, &val); h = hash(n); if (!ksh_isalphx(*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 tbl *vq; varsearch(l->next, &vq, n, h); if (vq != NULL) { 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 for variable, if not found create globally. */ struct tbl * global(const char *n) { struct block *l = e->loc; struct tbl *vp; int c; bool array; uint32_t h, val; /* * check to see if this is an array; * dereference namerefs; must come first */ n = array_index_calc(n, &array, &val); h = hash(n); c = (unsigned char)n[0]; if (!ksh_isalphx(c)) { if (array) errorf("bad substitution"); vp = &vtemp; vp->flag = DEFINED; vp->type = 0; vp->areap = ATEMP; *vp->name = c; if (ksh_isdigit(c)) { if (getn(n, &c) && (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 & 0xFF; break; case '#': vp->val.i = l->argc; break; case '-': vp->flag &= ~INTEGER; vp->val.s = getoptions(); break; default: vp->flag &= ~(ISSET|INTEGER); } return (vp); } l = varsearch(e->loc, &vp, n, h); if (vp != NULL) return (array ? arraysearch(vp, val) : vp); 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); }
/* * Read the variable part of a ${...} expression (i.e. up to but not * including the :[-+?=#%] or close-brace). */ static char * get_brace_var(XString *wsp, char *wp) { char c; enum parse_state { PS_INITIAL, PS_SAW_HASH, PS_IDENT, PS_NUMBER, PS_VAR1 } state = PS_INITIAL; while (/* CONSTCOND */ 1) { c = getsc(); /* State machine to figure out where the variable part ends. */ switch (state) { case PS_INITIAL: if (c == '#' || c == '!' || c == '%') { state = PS_SAW_HASH; break; } /* FALLTHROUGH */ case PS_SAW_HASH: if (ksh_isalphx(c)) state = PS_IDENT; else if (ksh_isdigit(c)) state = PS_NUMBER; else if (c == '#') { if (state == PS_SAW_HASH) { char c2; c2 = getsc(); ungetsc(c2); if (c2 != /*{*/ '}') { ungetsc(c); goto out; } } state = PS_VAR1; } else if (ctype(c, C_VAR1)) state = PS_VAR1; else goto out; break; case PS_IDENT: if (!ksh_isalnux(c)) { if (c == '[') { char *tmp, *p; if (!arraysub(&tmp)) yyerror("missing ]\n"); *wp++ = c; for (p = tmp; *p; ) { Xcheck(*wsp, wp); *wp++ = *p++; } afree(tmp, ATEMP); /* the ] */ c = getsc(); } goto out; } break; case PS_NUMBER: if (!ksh_isdigit(c)) goto out; break; case PS_VAR1: goto out; } Xcheck(*wsp, wp); *wp++ = c; } out: /* end of variable part */ *wp++ = '\0'; ungetsc(c); return (wp); }