void exec_cmd(s_cmd_node *cmd) { assert(cmd); if (DEBUG_EXEC) cmd_debugger(cmd); //FIXME: expansions here if (!cmd->argv) { if (cmd->prefix) exec_prefix(cmd->prefix, 0); return; } if (func_exist(shell->func, cmd->argv[0])) exec_function(cmd->argv); else if (is_a_builtin(cmd->argv[0])) shell->status = get_builtin(cmd->argv[0])(cmd->argv); else shell->status = exec_program(cmd); }
static void handle_builtin(int argc, const char **argv) { const char *cmd; struct cmd_struct *builtin; strip_extension(argv); cmd = argv[0]; /* Turn "git cmd --help" into "git help cmd" */ if (argc > 1 && !strcmp(argv[1], "--help")) { argv[1] = argv[0]; argv[0] = cmd = "help"; } builtin = get_builtin(cmd); if (builtin) exit(run_builtin(builtin, argc, argv)); }
int is_builtin(const char *s) { return !!get_builtin(s); }
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); }
init_builtins () { long rule, alt; equal = get_builtin ("equal"); if (meta_uniq_flag) *(REPR (equal)) = 'f'; notequal = get_builtin ("notequal"); if (meta_uniq_flag) *(REPR (notequal)) = 'f'; endofsentence = get_builtin ("endofsentence"); evalmeta = get_builtin ("evalmeta"); explintersect = get_builtin ("explintersect_"); falseip = get_builtin ("falseip_"); getip = get_builtin ("getip_"); initmeta = get_builtin ("initmeta"); initmint = get_builtin ("initmint_"); intersect = get_builtin ("intersect_"); metaterminal = get_builtin ("metaterminal"); metaterminal2 = get_builtin ("metaterminal2_"); nestaralt = get_builtin ("nestaralt_"); nestarset = get_builtin ("nestarset"); detnestarset = get_builtin ("detnestarset"); detprefix = get_builtin ("detprefix"); detprefix2 = get_builtin ("detprefix2_"); equalsempty = get_builtin ("equalsempty"); nlcr = get_builtin ("nlcr"); pair = get_builtin ("pair"); repair = get_builtin ("repair"); resetinputptr = get_builtin ("resetinputptr_"); restoreip = get_builtin ("restoreip_"); setinputptrto = get_builtin ("setinputptrto_"); skip = get_builtin ("skip_"); tltraditional = get_builtin ("tltraditional_"); tltraditionalterm = get_builtin ("tltraditionalterm_"); transformlattice = get_builtin ("transformlattice_"); transformlatticeterm = get_builtin ("transformlatticeterm_"); unpair = get_builtin ("unpair"); where = get_builtin ("where"); getlist_ = get_builtin ("getlist_"); add_to = get_builtin ("addto"); assign = get_builtin ("assign"); meta_empty = get_meta_builtin ("empty"); }