1*8c046d24Snicm /* $OpenBSD: lex.c,v 1.53 2015/09/17 14:21:33 nicm Exp $ */ 27cb960a2Sdownsj 37cb960a2Sdownsj /* 47cb960a2Sdownsj * lexical analysis and source input 57cb960a2Sdownsj */ 67cb960a2Sdownsj 77cb960a2Sdownsj #include "sh.h" 8eb6bc482Sderaadt #include <libgen.h> 97cb960a2Sdownsj #include <ctype.h> 107cb960a2Sdownsj 113b015934Smillert 123b015934Smillert /* Structure to keep track of the lexing state and the various pieces of info 133b015934Smillert * needed for each particular state. 143b015934Smillert */ 153b015934Smillert typedef struct lex_state Lex_state; 163b015934Smillert struct lex_state { 173b015934Smillert int ls_state; 183b015934Smillert union { 193b015934Smillert /* $(...) */ 203b015934Smillert struct scsparen_info { 213b015934Smillert int nparen; /* count open parenthesis */ 223b015934Smillert int csstate; /* XXX remove */ 233b015934Smillert #define ls_scsparen ls_info.u_scsparen 243b015934Smillert } u_scsparen; 253b015934Smillert 263b015934Smillert /* $((...)) */ 273b015934Smillert struct sasparen_info { 283b015934Smillert int nparen; /* count open parenthesis */ 293b015934Smillert int start; /* marks start of $(( in output str */ 303b015934Smillert #define ls_sasparen ls_info.u_sasparen 313b015934Smillert } u_sasparen; 323b015934Smillert 333b015934Smillert /* ((...)) */ 343b015934Smillert struct sletparen_info { 353b015934Smillert int nparen; /* count open parenthesis */ 363b015934Smillert #define ls_sletparen ls_info.u_sletparen 373b015934Smillert } u_sletparen; 383b015934Smillert 393b015934Smillert /* `...` */ 403b015934Smillert struct sbquote_info { 413b015934Smillert int indquotes; /* true if in double quotes: "`...`" */ 423b015934Smillert #define ls_sbquote ls_info.u_sbquote 433b015934Smillert } u_sbquote; 443b015934Smillert 453b015934Smillert Lex_state *base; /* used to point to next state block */ 463b015934Smillert } ls_info; 473b015934Smillert }; 483b015934Smillert 493b015934Smillert typedef struct State_info State_info; 503b015934Smillert struct State_info { 513b015934Smillert Lex_state *base; 523b015934Smillert Lex_state *end; 533b015934Smillert }; 543b015934Smillert 553b015934Smillert 56c5d5393cSotto static void readhere(struct ioword *); 5769b9f96bSmillert static int getsc__(void); 58c5d5393cSotto static void getsc_line(Source *); 5969b9f96bSmillert static int getsc_bn(void); 60c5d5393cSotto static char *get_brace_var(XString *, char *); 61c5d5393cSotto static int arraysub(char **); 62c5d5393cSotto static const char *ungetsc(int); 6369b9f96bSmillert static void gethere(void); 64c5d5393cSotto static Lex_state *push_state_(State_info *, Lex_state *); 65c5d5393cSotto static Lex_state *pop_state_(State_info *, Lex_state *); 66e7b1290aSotto static char *special_prompt_expand(char *); 67c5d5393cSotto static int dopprompt(const char *, int, const char **, int); 68a3427febSderaadt int promptlen(const char *cp, const char **spp); 697cb960a2Sdownsj 70e55c1b2cSdownsj static int backslash_skip; 71e55c1b2cSdownsj static int ignore_backslash_newline; 72e55c1b2cSdownsj 73e55c1b2cSdownsj /* optimized getsc_bn() */ 74e55c1b2cSdownsj #define getsc() (*source->str != '\0' && *source->str != '\\' \ 75e55c1b2cSdownsj && !backslash_skip ? *source->str++ : getsc_bn()) 76e55c1b2cSdownsj /* optimized getsc__() */ 77e55c1b2cSdownsj #define getsc_() ((*source->str != '\0') ? *source->str++ : getsc__()) 787cb960a2Sdownsj 793b015934Smillert #define STATE_BSIZE 32 803b015934Smillert 813b015934Smillert #define PUSH_STATE(s) do { \ 823b015934Smillert if (++statep == state_info.end) \ 833b015934Smillert statep = push_state_(&state_info, statep); \ 843b015934Smillert state = statep->ls_state = (s); \ 853b015934Smillert } while (0) 863b015934Smillert 873b015934Smillert #define POP_STATE() do { \ 883b015934Smillert if (--statep == state_info.base) \ 893b015934Smillert statep = pop_state_(&state_info, statep); \ 903b015934Smillert state = statep->ls_state; \ 913b015934Smillert } while (0) 923b015934Smillert 933b015934Smillert 947cb960a2Sdownsj 957cb960a2Sdownsj /* 967cb960a2Sdownsj * Lexical analyzer 977cb960a2Sdownsj * 987cb960a2Sdownsj * tokens are not regular expressions, they are LL(1). 997cb960a2Sdownsj * for example, "${var:-${PWD}}", and "$(size $(whence ksh))". 1007cb960a2Sdownsj * hence the state stack. 1017cb960a2Sdownsj */ 1027cb960a2Sdownsj 1037cb960a2Sdownsj int 104c5d5393cSotto yylex(int cf) 1057cb960a2Sdownsj { 1063b015934Smillert Lex_state states[STATE_BSIZE], *statep; 1073b015934Smillert State_info state_info; 1087894b443Smillert int c, state; 1097cb960a2Sdownsj XString ws; /* expandable output word */ 1107894b443Smillert char *wp; /* output word pointer */ 1113b015934Smillert char *sp, *dp; 1123b015934Smillert int c2; 1137cb960a2Sdownsj 1147cb960a2Sdownsj 1157cb960a2Sdownsj Again: 1163b015934Smillert states[0].ls_state = -1; 11722a9cf94Sguenther states[0].ls_info.base = NULL; 1183b015934Smillert statep = &states[1]; 1193b015934Smillert state_info.base = states; 1203b015934Smillert state_info.end = &states[STATE_BSIZE]; 1213b015934Smillert 1227cb960a2Sdownsj Xinit(ws, wp, 64, ATEMP); 1237cb960a2Sdownsj 124e55c1b2cSdownsj backslash_skip = 0; 125e55c1b2cSdownsj ignore_backslash_newline = 0; 126e55c1b2cSdownsj 1277cb960a2Sdownsj if (cf&ONEWORD) 1283b015934Smillert state = SWORD; 1297cb960a2Sdownsj else if (cf&LETEXPR) { 1307cb960a2Sdownsj *wp++ = OQUOTE; /* enclose arguments in (double) quotes */ 1313b015934Smillert state = SLETPAREN; 1323b015934Smillert statep->ls_sletparen.nparen = 0; 13394e42df6Smillert } else { /* normal lexing */ 1343b015934Smillert state = (cf & HEREDELIM) ? SHEREDELIM : SBASE; 1357cb960a2Sdownsj while ((c = getsc()) == ' ' || c == '\t') 1367cb960a2Sdownsj ; 137e55c1b2cSdownsj if (c == '#') { 138e55c1b2cSdownsj ignore_backslash_newline++; 1397cb960a2Sdownsj while ((c = getsc()) != '\0' && c != '\n') 1407cb960a2Sdownsj ; 141e55c1b2cSdownsj ignore_backslash_newline--; 142e55c1b2cSdownsj } 1437cb960a2Sdownsj ungetsc(c); 1447cb960a2Sdownsj } 1457cb960a2Sdownsj if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */ 1467cb960a2Sdownsj source->flags &= ~SF_ALIAS; 1477cb960a2Sdownsj /* In POSIX mode, a trailing space only counts if we are 1487cb960a2Sdownsj * parsing a simple command 1497cb960a2Sdownsj */ 1507cb960a2Sdownsj if (!Flag(FPOSIX) || (cf & CMDWORD)) 1517cb960a2Sdownsj cf |= ALIAS; 1527cb960a2Sdownsj } 1537cb960a2Sdownsj 1543b015934Smillert /* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */ 1553b015934Smillert statep->ls_state = state; 1563b015934Smillert 1577cb960a2Sdownsj /* collect non-special or quoted characters to form word */ 1587a8124d8Sderaadt while (!((c = getsc()) == 0 || 1597a8124d8Sderaadt ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) { 1607cb960a2Sdownsj Xcheck(ws, wp); 1617cb960a2Sdownsj switch (state) { 1627cb960a2Sdownsj case SBASE: 1637a8124d8Sderaadt if (Flag(FCSHHISTORY) && (source->flags & SF_TTY) && 1647a8124d8Sderaadt c == '!') { 165f740f764Sderaadt char **replace = NULL; 166f740f764Sderaadt 167f740f764Sderaadt c2 = getsc(); 1680b1768a7Sotto if (c2 == '\0' || c2 == ' ' || c2 == '\t') 169f740f764Sderaadt ; 170f740f764Sderaadt else if (c2 == '!') 171f740f764Sderaadt replace = hist_get_newest(0); 172f740f764Sderaadt else if (isdigit(c2) || c2 == '-' || 173f740f764Sderaadt isalpha(c2)) { 174f740f764Sderaadt int get = !isalpha(c2); 175f740f764Sderaadt char match[200], *str = match; 176f740f764Sderaadt 177f740f764Sderaadt *str++ = c2; 178f740f764Sderaadt do { 179f740f764Sderaadt if ((c2 = getsc()) == '\0') 180f740f764Sderaadt break; 181f740f764Sderaadt if (c2 == '\t' || c2 == ' ' || 182f740f764Sderaadt c2 == '\n') { 183f740f764Sderaadt ungetsc(c2); 184f740f764Sderaadt break; 185f740f764Sderaadt } 186f740f764Sderaadt *str++ = c2; 187f740f764Sderaadt } while (str < &match[sizeof(match)-1]); 188f740f764Sderaadt *str = '\0'; 189f740f764Sderaadt 190f740f764Sderaadt if (get) { 191f740f764Sderaadt int h = findhistrel(match); 192f740f764Sderaadt if (h >= 0) 193f740f764Sderaadt replace = &history[h]; 194f740f764Sderaadt } else { 1950e7d3a01Smillert int h = findhist(-1, 0, match, true); 196f740f764Sderaadt if (h >= 0) 197f740f764Sderaadt replace = &history[h]; 198f740f764Sderaadt } 199f740f764Sderaadt } 200f740f764Sderaadt 201f740f764Sderaadt /* 202f740f764Sderaadt * XXX ksh history buffer saves un-expanded 203f740f764Sderaadt * commands. Until the history buffer code is 204f740f764Sderaadt * changed to contain expanded commands, we 205f740f764Sderaadt * ignore the bad commands (spinning sucks) 206f740f764Sderaadt */ 207f740f764Sderaadt if (replace && **replace == '!') 208f740f764Sderaadt ungetsc(c2); 209f740f764Sderaadt else if (replace) { 210f740f764Sderaadt Source *s; 211f740f764Sderaadt 212f740f764Sderaadt /* do not strdup replacement via alloc */ 2137a8124d8Sderaadt s = pushs(SREREAD, source->areap); 214f740f764Sderaadt s->start = s->str = *replace; 215f740f764Sderaadt s->next = source; 2167d849734Smillert s->u.freeme = NULL; 217f740f764Sderaadt source = s; 218f740f764Sderaadt continue; 219f740f764Sderaadt } else 220f740f764Sderaadt ungetsc(c2); 221f740f764Sderaadt } 2227cb960a2Sdownsj if (c == '[' && (cf & (VARASN|ARRAYVAR))) { 2237cb960a2Sdownsj *wp = EOS; /* temporary */ 2247a8124d8Sderaadt if (is_wdvarname(Xstring(ws, wp), false)) { 2257cb960a2Sdownsj char *p, *tmp; 2267cb960a2Sdownsj 2277cb960a2Sdownsj if (arraysub(&tmp)) { 2287cb960a2Sdownsj *wp++ = CHAR; 2297cb960a2Sdownsj *wp++ = c; 2307cb960a2Sdownsj for (p = tmp; *p; ) { 2317cb960a2Sdownsj Xcheck(ws, wp); 2327cb960a2Sdownsj *wp++ = CHAR; 2337cb960a2Sdownsj *wp++ = *p++; 2347cb960a2Sdownsj } 2357cb960a2Sdownsj afree(tmp, ATEMP); 2367cb960a2Sdownsj break; 2377cb960a2Sdownsj } else { 2387cb960a2Sdownsj Source *s; 2397cb960a2Sdownsj 2407cb960a2Sdownsj s = pushs(SREREAD, 2417cb960a2Sdownsj source->areap); 2427cb960a2Sdownsj s->start = s->str 2437cb960a2Sdownsj = s->u.freeme = tmp; 2447cb960a2Sdownsj s->next = source; 2457cb960a2Sdownsj source = s; 2467cb960a2Sdownsj } 2477cb960a2Sdownsj } 2487cb960a2Sdownsj *wp++ = CHAR; 2497cb960a2Sdownsj *wp++ = c; 2507cb960a2Sdownsj break; 2517cb960a2Sdownsj } 2527eab881bSjaredy /* FALLTHROUGH */ 2537cb960a2Sdownsj Sbase1: /* includes *(...|...) pattern (*+?@!) */ 2547a8124d8Sderaadt if (c == '*' || c == '@' || c == '+' || c == '?' || 2557a8124d8Sderaadt c == '!') { 2567cb960a2Sdownsj c2 = getsc(); 2577cb960a2Sdownsj if (c2 == '(' /*)*/ ) { 2587cb960a2Sdownsj *wp++ = OPAT; 2597cb960a2Sdownsj *wp++ = c; 2603b015934Smillert PUSH_STATE(SPATTERN); 2617cb960a2Sdownsj break; 2627cb960a2Sdownsj } 2637cb960a2Sdownsj ungetsc(c2); 2647cb960a2Sdownsj } 2657eab881bSjaredy /* FALLTHROUGH */ 2667cb960a2Sdownsj Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */ 2677cb960a2Sdownsj switch (c) { 2687cb960a2Sdownsj case '\\': 2697cb960a2Sdownsj c = getsc(); 270e55c1b2cSdownsj if (c) /* trailing \ is lost */ 2717cb960a2Sdownsj *wp++ = QCHAR, *wp++ = c; 2727cb960a2Sdownsj break; 2737cb960a2Sdownsj case '\'': 27422a9cf94Sguenther if ((cf & HEREDOC) || state == SBRACEQ) { 27522a9cf94Sguenther *wp++ = CHAR, *wp++ = c; 27622a9cf94Sguenther break; 27722a9cf94Sguenther } 2787cb960a2Sdownsj *wp++ = OQUOTE; 279e55c1b2cSdownsj ignore_backslash_newline++; 2803b015934Smillert PUSH_STATE(SSQUOTE); 2817cb960a2Sdownsj break; 2827cb960a2Sdownsj case '"': 2837cb960a2Sdownsj *wp++ = OQUOTE; 2843b015934Smillert PUSH_STATE(SDQUOTE); 2857cb960a2Sdownsj break; 2867cb960a2Sdownsj default: 2877cb960a2Sdownsj goto Subst; 2887cb960a2Sdownsj } 2897cb960a2Sdownsj break; 2907cb960a2Sdownsj 2917cb960a2Sdownsj Subst: 2927cb960a2Sdownsj switch (c) { 2937cb960a2Sdownsj case '\\': 2947cb960a2Sdownsj c = getsc(); 2957cb960a2Sdownsj switch (c) { 29657f623feSotto case '\\': 2977cb960a2Sdownsj case '$': case '`': 2987cb960a2Sdownsj *wp++ = QCHAR, *wp++ = c; 2997cb960a2Sdownsj break; 30057f623feSotto case '"': 30157f623feSotto if ((cf & HEREDOC) == 0) { 30257f623feSotto *wp++ = QCHAR, *wp++ = c; 30357f623feSotto break; 30457f623feSotto } 30525d8d262Smoritz /* FALLTHROUGH */ 3067cb960a2Sdownsj default: 307d3ecfddbSstsp if (cf & UNESCAPE) { 308d3ecfddbSstsp *wp++ = QCHAR, *wp++ = c; 309d3ecfddbSstsp break; 310d3ecfddbSstsp } 3117cb960a2Sdownsj Xcheck(ws, wp); 312e55c1b2cSdownsj if (c) { /* trailing \ is lost */ 3137cb960a2Sdownsj *wp++ = CHAR, *wp++ = '\\'; 3147cb960a2Sdownsj *wp++ = CHAR, *wp++ = c; 315e55c1b2cSdownsj } 3167cb960a2Sdownsj break; 3177cb960a2Sdownsj } 3187cb960a2Sdownsj break; 3197cb960a2Sdownsj case '$': 3207cb960a2Sdownsj c = getsc(); 3217cb960a2Sdownsj if (c == '(') /*)*/ { 3227cb960a2Sdownsj c = getsc(); 3237cb960a2Sdownsj if (c == '(') /*)*/ { 3243b015934Smillert PUSH_STATE(SASPAREN); 3253b015934Smillert statep->ls_sasparen.nparen = 2; 3263b015934Smillert statep->ls_sasparen.start = 3273b015934Smillert Xsavepos(ws, wp); 3287cb960a2Sdownsj *wp++ = EXPRSUB; 3297cb960a2Sdownsj } else { 3307cb960a2Sdownsj ungetsc(c); 3313b015934Smillert PUSH_STATE(SCSPAREN); 3323b015934Smillert statep->ls_scsparen.nparen = 1; 3333b015934Smillert statep->ls_scsparen.csstate = 0; 3347cb960a2Sdownsj *wp++ = COMSUB; 3357cb960a2Sdownsj } 3367cb960a2Sdownsj } else if (c == '{') /*}*/ { 3377cb960a2Sdownsj *wp++ = OSUBST; 3383b015934Smillert *wp++ = '{'; /*}*/ 3397cb960a2Sdownsj wp = get_brace_var(&ws, wp); 340e55c1b2cSdownsj c = getsc(); 3413b015934Smillert /* allow :# and :% (ksh88 compat) */ 3423b015934Smillert if (c == ':') { 3433b015934Smillert *wp++ = CHAR, *wp++ = c; 3443b015934Smillert c = getsc(); 3453b015934Smillert } 3463b015934Smillert /* If this is a trim operation, 3473b015934Smillert * treat (,|,) specially in STBRACE. 3483b015934Smillert */ 3497cb960a2Sdownsj if (c == '#' || c == '%') { 3503b015934Smillert ungetsc(c); 3513b015934Smillert PUSH_STATE(STBRACE); 3527cb960a2Sdownsj } else { 3537cb960a2Sdownsj ungetsc(c); 35422a9cf94Sguenther if (state == SDQUOTE || 35522a9cf94Sguenther state == SBRACEQ) 35622a9cf94Sguenther PUSH_STATE(SBRACEQ); 35722a9cf94Sguenther else 3583b015934Smillert PUSH_STATE(SBRACE); 3597cb960a2Sdownsj } 3607cb960a2Sdownsj } else if (ctype(c, C_ALPHA)) { 3617cb960a2Sdownsj *wp++ = OSUBST; 3623b015934Smillert *wp++ = 'X'; 3637cb960a2Sdownsj do { 3647cb960a2Sdownsj Xcheck(ws, wp); 3657cb960a2Sdownsj *wp++ = c; 3667cb960a2Sdownsj c = getsc(); 3677cb960a2Sdownsj } while (ctype(c, C_ALPHA|C_DIGIT)); 3687cb960a2Sdownsj *wp++ = '\0'; 3697cb960a2Sdownsj *wp++ = CSUBST; 3703b015934Smillert *wp++ = 'X'; 3717cb960a2Sdownsj ungetsc(c); 3727cb960a2Sdownsj } else if (ctype(c, C_DIGIT|C_VAR1)) { 3737cb960a2Sdownsj Xcheck(ws, wp); 3747cb960a2Sdownsj *wp++ = OSUBST; 3753b015934Smillert *wp++ = 'X'; 3767cb960a2Sdownsj *wp++ = c; 3777cb960a2Sdownsj *wp++ = '\0'; 3787cb960a2Sdownsj *wp++ = CSUBST; 3793b015934Smillert *wp++ = 'X'; 3807cb960a2Sdownsj } else { 3817cb960a2Sdownsj *wp++ = CHAR, *wp++ = '$'; 3827cb960a2Sdownsj ungetsc(c); 3837cb960a2Sdownsj } 3847cb960a2Sdownsj break; 3857cb960a2Sdownsj case '`': 3863b015934Smillert PUSH_STATE(SBQUOTE); 3877cb960a2Sdownsj *wp++ = COMSUB; 3887cb960a2Sdownsj /* Need to know if we are inside double quotes 3897cb960a2Sdownsj * since sh/at&t-ksh translate the \" to " in 3907cb960a2Sdownsj * "`..\"..`". 3913b015934Smillert * This is not done in posix mode (section 3923b015934Smillert * 3.2.3, Double Quotes: "The backquote shall 3933b015934Smillert * retain its special meaning introducing the 3943b015934Smillert * other form of command substitution (see 3953b015934Smillert * 3.6.3). The portion of the quoted string 3963b015934Smillert * from the initial backquote and the 3973b015934Smillert * characters up to the next backquote that 3983b015934Smillert * is not preceded by a backslash (having 3993b015934Smillert * escape characters removed) defines that 4003b015934Smillert * command whose output replaces `...` when 4013b015934Smillert * the word is expanded." 4023b015934Smillert * Section 3.6.3, Command Substitution: 4033b015934Smillert * "Within the backquoted style of command 4043b015934Smillert * substitution, backslash shall retain its 4053b015934Smillert * literal meaning, except when followed by 4063b015934Smillert * $ ` \."). 4077cb960a2Sdownsj */ 4083b015934Smillert statep->ls_sbquote.indquotes = 0; 4093b015934Smillert if (!Flag(FPOSIX)) { 4103b015934Smillert Lex_state *s = statep; 4113b015934Smillert Lex_state *base = state_info.base; 4123b015934Smillert while (1) { 4133b015934Smillert for (; s != base; s--) { 4143b015934Smillert if (s->ls_state == SDQUOTE) { 4153b015934Smillert statep->ls_sbquote.indquotes = 1; 4163b015934Smillert break; 4173b015934Smillert } 4183b015934Smillert } 4193b015934Smillert if (s != base) 4203b015934Smillert break; 4213b015934Smillert if (!(s = s->ls_info.base)) 4223b015934Smillert break; 4233b015934Smillert base = s-- - STATE_BSIZE; 4243b015934Smillert } 4253b015934Smillert } 4267cb960a2Sdownsj break; 4277cb960a2Sdownsj default: 4287cb960a2Sdownsj *wp++ = CHAR, *wp++ = c; 4297cb960a2Sdownsj } 4307cb960a2Sdownsj break; 4317cb960a2Sdownsj 4327cb960a2Sdownsj case SSQUOTE: 4337cb960a2Sdownsj if (c == '\'') { 4343b015934Smillert POP_STATE(); 43522a9cf94Sguenther if (state == SBRACEQ) { 43622a9cf94Sguenther *wp++ = CHAR, *wp++ = c; 43722a9cf94Sguenther break; 43822a9cf94Sguenther } 4397cb960a2Sdownsj *wp++ = CQUOTE; 440e55c1b2cSdownsj ignore_backslash_newline--; 4417cb960a2Sdownsj } else 4427cb960a2Sdownsj *wp++ = QCHAR, *wp++ = c; 4437cb960a2Sdownsj break; 4447cb960a2Sdownsj 4457cb960a2Sdownsj case SDQUOTE: 4467cb960a2Sdownsj if (c == '"') { 4473b015934Smillert POP_STATE(); 4487cb960a2Sdownsj *wp++ = CQUOTE; 4497cb960a2Sdownsj } else 4507cb960a2Sdownsj goto Subst; 4517cb960a2Sdownsj break; 4527cb960a2Sdownsj 4533b015934Smillert case SCSPAREN: /* $( .. ) */ 4547cb960a2Sdownsj /* todo: deal with $(...) quoting properly 4557cb960a2Sdownsj * kludge to partly fake quoting inside $(..): doesn't 4567cb960a2Sdownsj * really work because nested $(..) or ${..} inside 4577cb960a2Sdownsj * double quotes aren't dealt with. 4587cb960a2Sdownsj */ 4593b015934Smillert switch (statep->ls_scsparen.csstate) { 4607cb960a2Sdownsj case 0: /* normal */ 4617cb960a2Sdownsj switch (c) { 4627cb960a2Sdownsj case '(': 4633b015934Smillert statep->ls_scsparen.nparen++; 4647cb960a2Sdownsj break; 4657cb960a2Sdownsj case ')': 4663b015934Smillert statep->ls_scsparen.nparen--; 4677cb960a2Sdownsj break; 4687cb960a2Sdownsj case '\\': 4693b015934Smillert statep->ls_scsparen.csstate = 1; 4707cb960a2Sdownsj break; 4717cb960a2Sdownsj case '"': 4723b015934Smillert statep->ls_scsparen.csstate = 2; 4737cb960a2Sdownsj break; 4747cb960a2Sdownsj case '\'': 4753b015934Smillert statep->ls_scsparen.csstate = 4; 476e55c1b2cSdownsj ignore_backslash_newline++; 4777cb960a2Sdownsj break; 4787cb960a2Sdownsj } 4797cb960a2Sdownsj break; 4807cb960a2Sdownsj 4817cb960a2Sdownsj case 1: /* backslash in normal mode */ 4827cb960a2Sdownsj case 3: /* backslash in double quotes */ 4833b015934Smillert --statep->ls_scsparen.csstate; 4847cb960a2Sdownsj break; 4857cb960a2Sdownsj 4867cb960a2Sdownsj case 2: /* double quotes */ 4877cb960a2Sdownsj if (c == '"') 4883b015934Smillert statep->ls_scsparen.csstate = 0; 4897cb960a2Sdownsj else if (c == '\\') 4903b015934Smillert statep->ls_scsparen.csstate = 3; 4917cb960a2Sdownsj break; 4927cb960a2Sdownsj 4937cb960a2Sdownsj case 4: /* single quotes */ 494e55c1b2cSdownsj if (c == '\'') { 4953b015934Smillert statep->ls_scsparen.csstate = 0; 496e55c1b2cSdownsj ignore_backslash_newline--; 497e55c1b2cSdownsj } 4987cb960a2Sdownsj break; 4997cb960a2Sdownsj } 5003b015934Smillert if (statep->ls_scsparen.nparen == 0) { 5013b015934Smillert POP_STATE(); 5027cb960a2Sdownsj *wp++ = 0; /* end of COMSUB */ 5037cb960a2Sdownsj } else 5047cb960a2Sdownsj *wp++ = c; 5057cb960a2Sdownsj break; 5067cb960a2Sdownsj 5073b015934Smillert case SASPAREN: /* $(( .. )) */ 5087cb960a2Sdownsj /* todo: deal with $((...); (...)) properly */ 5097cb960a2Sdownsj /* XXX should nest using existing state machine 5107cb960a2Sdownsj * (embed "..", $(...), etc.) */ 5117cb960a2Sdownsj if (c == '(') 5123b015934Smillert statep->ls_sasparen.nparen++; 5137cb960a2Sdownsj else if (c == ')') { 5143b015934Smillert statep->ls_sasparen.nparen--; 5153b015934Smillert if (statep->ls_sasparen.nparen == 1) { 5167cb960a2Sdownsj /*(*/ 5177cb960a2Sdownsj if ((c2 = getsc()) == ')') { 5183b015934Smillert POP_STATE(); 5197cb960a2Sdownsj *wp++ = 0; /* end of EXPRSUB */ 5207cb960a2Sdownsj break; 5217cb960a2Sdownsj } else { 5223b015934Smillert char *s; 5233b015934Smillert 5247cb960a2Sdownsj ungetsc(c2); 5257cb960a2Sdownsj /* mismatched parenthesis - 5267cb960a2Sdownsj * assume we were really 5277cb960a2Sdownsj * parsing a $(..) expression 5287cb960a2Sdownsj */ 5293b015934Smillert s = Xrestpos(ws, wp, 5303b015934Smillert statep->ls_sasparen.start); 5313b015934Smillert memmove(s + 1, s, wp - s); 5323b015934Smillert *s++ = COMSUB; 5333b015934Smillert *s = '('; /*)*/ 5347cb960a2Sdownsj wp++; 5353b015934Smillert statep->ls_scsparen.nparen = 1; 5363b015934Smillert statep->ls_scsparen.csstate = 0; 5377a8124d8Sderaadt state = statep->ls_state = 5387a8124d8Sderaadt SCSPAREN; 5397cb960a2Sdownsj } 5407cb960a2Sdownsj } 5417cb960a2Sdownsj } 5427cb960a2Sdownsj *wp++ = c; 5437cb960a2Sdownsj break; 5447cb960a2Sdownsj 54522a9cf94Sguenther case SBRACEQ: 5467cb960a2Sdownsj case SBRACE: 5477cb960a2Sdownsj /*{*/ 5487cb960a2Sdownsj if (c == '}') { 5493b015934Smillert POP_STATE(); 5507cb960a2Sdownsj *wp++ = CSUBST; 5513b015934Smillert *wp++ = /*{*/ '}'; 5527cb960a2Sdownsj } else 5537cb960a2Sdownsj goto Sbase1; 5547cb960a2Sdownsj break; 5557cb960a2Sdownsj 5567cb960a2Sdownsj case STBRACE: 5573b015934Smillert /* Same as SBRACE, except (,|,) treated specially */ 5587cb960a2Sdownsj /*{*/ 5597cb960a2Sdownsj if (c == '}') { 5603b015934Smillert POP_STATE(); 5617cb960a2Sdownsj *wp++ = CSUBST; 5623b015934Smillert *wp++ = /*{*/ '}'; 5637cb960a2Sdownsj } else if (c == '|') { 5647cb960a2Sdownsj *wp++ = SPAT; 5653b015934Smillert } else if (c == '(') { 5663b015934Smillert *wp++ = OPAT; 5673b015934Smillert *wp++ = ' '; /* simile for @ */ 5683b015934Smillert PUSH_STATE(SPATTERN); 5697cb960a2Sdownsj } else 5707cb960a2Sdownsj goto Sbase1; 5717cb960a2Sdownsj break; 5727cb960a2Sdownsj 5737cb960a2Sdownsj case SBQUOTE: 5747cb960a2Sdownsj if (c == '`') { 5757cb960a2Sdownsj *wp++ = 0; 5763b015934Smillert POP_STATE(); 5777cb960a2Sdownsj } else if (c == '\\') { 5787cb960a2Sdownsj switch (c = getsc()) { 5797cb960a2Sdownsj case '\\': 5807cb960a2Sdownsj case '$': case '`': 5817cb960a2Sdownsj *wp++ = c; 5827cb960a2Sdownsj break; 5837cb960a2Sdownsj case '"': 5843b015934Smillert if (statep->ls_sbquote.indquotes) { 5857cb960a2Sdownsj *wp++ = c; 5867cb960a2Sdownsj break; 5877cb960a2Sdownsj } 5887eab881bSjaredy /* FALLTHROUGH */ 5897cb960a2Sdownsj default: 590e55c1b2cSdownsj if (c) { /* trailing \ is lost */ 5917cb960a2Sdownsj *wp++ = '\\'; 5927cb960a2Sdownsj *wp++ = c; 593e55c1b2cSdownsj } 5947cb960a2Sdownsj break; 5957cb960a2Sdownsj } 5967cb960a2Sdownsj } else 5977cb960a2Sdownsj *wp++ = c; 5987cb960a2Sdownsj break; 5997cb960a2Sdownsj 6007cb960a2Sdownsj case SWORD: /* ONEWORD */ 6017cb960a2Sdownsj goto Subst; 6027cb960a2Sdownsj 6033b015934Smillert case SLETPAREN: /* LETEXPR: (( ... )) */ 6047cb960a2Sdownsj /*(*/ 6057cb960a2Sdownsj if (c == ')') { 6063b015934Smillert if (statep->ls_sletparen.nparen > 0) 6073b015934Smillert --statep->ls_sletparen.nparen; 6087cb960a2Sdownsj /*(*/ 6097cb960a2Sdownsj else if ((c2 = getsc()) == ')') { 6107cb960a2Sdownsj c = 0; 6117cb960a2Sdownsj *wp++ = CQUOTE; 6127cb960a2Sdownsj goto Done; 6137cb960a2Sdownsj } else 6147cb960a2Sdownsj ungetsc(c2); 6157cb960a2Sdownsj } else if (c == '(') 6167cb960a2Sdownsj /* parenthesis inside quotes and backslashes 6177cb960a2Sdownsj * are lost, but at&t ksh doesn't count them 6187cb960a2Sdownsj * either 6197cb960a2Sdownsj */ 6203b015934Smillert ++statep->ls_sletparen.nparen; 6217cb960a2Sdownsj goto Sbase2; 6227cb960a2Sdownsj 6237cb960a2Sdownsj case SHEREDELIM: /* <<,<<- delimiter */ 6247cb960a2Sdownsj /* XXX chuck this state (and the next) - use 6257cb960a2Sdownsj * the existing states ($ and \`..` should be 6267cb960a2Sdownsj * stripped of their specialness after the 6277cb960a2Sdownsj * fact). 6287cb960a2Sdownsj */ 6297cb960a2Sdownsj /* here delimiters need a special case since 6307cb960a2Sdownsj * $ and `..` are not to be treated specially 6317cb960a2Sdownsj */ 6327cb960a2Sdownsj if (c == '\\') { 6337cb960a2Sdownsj c = getsc(); 634e55c1b2cSdownsj if (c) { /* trailing \ is lost */ 6357cb960a2Sdownsj *wp++ = QCHAR; 6367cb960a2Sdownsj *wp++ = c; 6377cb960a2Sdownsj } 6387cb960a2Sdownsj } else if (c == '\'') { 6393b015934Smillert PUSH_STATE(SSQUOTE); 6407cb960a2Sdownsj *wp++ = OQUOTE; 641e55c1b2cSdownsj ignore_backslash_newline++; 6427cb960a2Sdownsj } else if (c == '"') { 6433b015934Smillert state = statep->ls_state = SHEREDQUOTE; 6447cb960a2Sdownsj *wp++ = OQUOTE; 6457cb960a2Sdownsj } else { 6467cb960a2Sdownsj *wp++ = CHAR; 6477cb960a2Sdownsj *wp++ = c; 6487cb960a2Sdownsj } 6497cb960a2Sdownsj break; 6507cb960a2Sdownsj 6517cb960a2Sdownsj case SHEREDQUOTE: /* " in <<,<<- delimiter */ 6527cb960a2Sdownsj if (c == '"') { 6537cb960a2Sdownsj *wp++ = CQUOTE; 6543b015934Smillert state = statep->ls_state = SHEREDELIM; 6557cb960a2Sdownsj } else { 656e55c1b2cSdownsj if (c == '\\') { 657e55c1b2cSdownsj switch (c = getsc()) { 658e55c1b2cSdownsj case '\\': case '"': 659e55c1b2cSdownsj case '$': case '`': 6607cb960a2Sdownsj break; 661e55c1b2cSdownsj default: 662e55c1b2cSdownsj if (c) { /* trailing \ lost */ 663e55c1b2cSdownsj *wp++ = CHAR; 664e55c1b2cSdownsj *wp++ = '\\'; 665e55c1b2cSdownsj } 666e55c1b2cSdownsj break; 667e55c1b2cSdownsj } 668e55c1b2cSdownsj } 6697cb960a2Sdownsj *wp++ = CHAR; 6707cb960a2Sdownsj *wp++ = c; 6717cb960a2Sdownsj } 6727cb960a2Sdownsj break; 6737cb960a2Sdownsj 6747cb960a2Sdownsj case SPATTERN: /* in *(...|...) pattern (*+?@!) */ 6757cb960a2Sdownsj if ( /*(*/ c == ')') { 6767cb960a2Sdownsj *wp++ = CPAT; 6773b015934Smillert POP_STATE(); 6783b015934Smillert } else if (c == '|') { 6797cb960a2Sdownsj *wp++ = SPAT; 6803b015934Smillert } else if (c == '(') { 6813b015934Smillert *wp++ = OPAT; 6823b015934Smillert *wp++ = ' '; /* simile for @ */ 6833b015934Smillert PUSH_STATE(SPATTERN); 6843b015934Smillert } else 6857cb960a2Sdownsj goto Sbase1; 6867cb960a2Sdownsj break; 6877cb960a2Sdownsj } 6887cb960a2Sdownsj } 6897cb960a2Sdownsj Done: 6907cb960a2Sdownsj Xcheck(ws, wp); 6913b015934Smillert if (statep != &states[1]) 6923b015934Smillert /* XXX figure out what is missing */ 6937cb960a2Sdownsj yyerror("no closing quote\n"); 6947cb960a2Sdownsj 6957cb960a2Sdownsj /* This done to avoid tests for SHEREDELIM wherever SBASE tested */ 6967cb960a2Sdownsj if (state == SHEREDELIM) 6977cb960a2Sdownsj state = SBASE; 6987cb960a2Sdownsj 6993b015934Smillert dp = Xstring(ws, wp); 7007a8124d8Sderaadt if ((c == '<' || c == '>') && state == SBASE && 7017a8124d8Sderaadt ((c2 = Xlength(ws, wp)) == 0 || 7027a8124d8Sderaadt (c2 == 2 && dp[0] == CHAR && digit(dp[1])))) { 703*8c046d24Snicm struct ioword *iop = alloc(sizeof(*iop), ATEMP); 7047cb960a2Sdownsj 7053b015934Smillert if (c2 == 2) 7063b015934Smillert iop->unit = dp[1] - '0'; 7077cb960a2Sdownsj else 7083b015934Smillert iop->unit = c == '>'; /* 0 for <, 1 for > */ 7097cb960a2Sdownsj 7107cb960a2Sdownsj c2 = getsc(); 7117cb960a2Sdownsj /* <<, >>, <> are ok, >< is not */ 7127cb960a2Sdownsj if (c == c2 || (c == '<' && c2 == '>')) { 7137cb960a2Sdownsj iop->flag = c == c2 ? 7147cb960a2Sdownsj (c == '>' ? IOCAT : IOHERE) : IORDWR; 71518bbba6bSmillert if (iop->flag == IOHERE) { 716e55c1b2cSdownsj if ((c2 = getsc()) == '-') 7177cb960a2Sdownsj iop->flag |= IOSKIP; 7187cb960a2Sdownsj else 7197cb960a2Sdownsj ungetsc(c2); 72018bbba6bSmillert } 7217cb960a2Sdownsj } else if (c2 == '&') 7227cb960a2Sdownsj iop->flag = IODUP | (c == '<' ? IORDUP : 0); 7237cb960a2Sdownsj else { 7247cb960a2Sdownsj iop->flag = c == '>' ? IOWRITE : IOREAD; 7257cb960a2Sdownsj if (c == '>' && c2 == '|') 7267cb960a2Sdownsj iop->flag |= IOCLOB; 7277cb960a2Sdownsj else 7287cb960a2Sdownsj ungetsc(c2); 7297cb960a2Sdownsj } 7307cb960a2Sdownsj 731355ffa75Stedu iop->name = NULL; 732355ffa75Stedu iop->delim = NULL; 733355ffa75Stedu iop->heredoc = NULL; 7343b015934Smillert Xfree(ws, wp); /* free word */ 7357cb960a2Sdownsj yylval.iop = iop; 7367cb960a2Sdownsj return REDIR; 7377cb960a2Sdownsj } 7383b015934Smillert 7393b015934Smillert if (wp == dp && state == SBASE) { 7403b015934Smillert Xfree(ws, wp); /* free word */ 7413b015934Smillert /* no word, process LEX1 character */ 7423b015934Smillert switch (c) { 7433b015934Smillert default: 7443b015934Smillert return c; 7453b015934Smillert 7463b015934Smillert case '|': 7473b015934Smillert case '&': 7483b015934Smillert case ';': 7493b015934Smillert if ((c2 = getsc()) == c) 7503b015934Smillert c = (c == ';') ? BREAK : 7513b015934Smillert (c == '|') ? LOGOR : 7523b015934Smillert (c == '&') ? LOGAND : 7533b015934Smillert YYERRCODE; 7543b015934Smillert else if (c == '|' && c2 == '&') 7553b015934Smillert c = COPROC; 7563b015934Smillert else 7573b015934Smillert ungetsc(c2); 7583b015934Smillert return c; 7593b015934Smillert 7607cb960a2Sdownsj case '\n': 7617cb960a2Sdownsj gethere(); 7627cb960a2Sdownsj if (cf & CONTIN) 7637cb960a2Sdownsj goto Again; 7647cb960a2Sdownsj return c; 7657cb960a2Sdownsj 7667cb960a2Sdownsj case '(': /*)*/ 767b69beb66Sgrr if (!Flag(FSH)) { 7687cb960a2Sdownsj if ((c2 = getsc()) == '(') /*)*/ 7693b015934Smillert /* XXX need to handle ((...); (...)) */ 7707cb960a2Sdownsj c = MDPAREN; 7717cb960a2Sdownsj else 7727cb960a2Sdownsj ungetsc(c2); 773b69beb66Sgrr } 7747cb960a2Sdownsj return c; 7757cb960a2Sdownsj /*(*/ 7767cb960a2Sdownsj case ')': 7777cb960a2Sdownsj return c; 7787cb960a2Sdownsj } 7797cb960a2Sdownsj } 7807cb960a2Sdownsj 7817cb960a2Sdownsj *wp++ = EOS; /* terminate word */ 7827cb960a2Sdownsj yylval.cp = Xclose(ws, wp); 78394e42df6Smillert if (state == SWORD || state == SLETPAREN) /* ONEWORD? */ 7847cb960a2Sdownsj return LWORD; 7857cb960a2Sdownsj ungetsc(c); /* unget terminator */ 7867cb960a2Sdownsj 7877cb960a2Sdownsj /* copy word to unprefixed string ident */ 7887cb960a2Sdownsj for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; ) 7897cb960a2Sdownsj *dp++ = *sp++; 79012e7fb2dSjmc /* Make sure the ident array stays '\0' padded */ 7917cb960a2Sdownsj memset(dp, 0, (ident+IDENT) - dp + 1); 7927cb960a2Sdownsj if (c != EOS) 7937cb960a2Sdownsj *ident = '\0'; /* word is not unquoted */ 7947cb960a2Sdownsj 7957cb960a2Sdownsj if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) { 7967cb960a2Sdownsj struct tbl *p; 7977cb960a2Sdownsj int h = hash(ident); 7987cb960a2Sdownsj 7997cb960a2Sdownsj /* { */ 8006df3ee40Sotto if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) && 8017a8124d8Sderaadt (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) { 8027cb960a2Sdownsj afree(yylval.cp, ATEMP); 8037cb960a2Sdownsj return p->val.i; 8047cb960a2Sdownsj } 8056df3ee40Sotto if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) && 8067a8124d8Sderaadt (p->flag & ISSET)) { 8077894b443Smillert Source *s; 8087cb960a2Sdownsj 8097cb960a2Sdownsj for (s = source; s->type == SALIAS; s = s->next) 8107cb960a2Sdownsj if (s->u.tblp == p) 8117cb960a2Sdownsj return LWORD; 8127cb960a2Sdownsj /* push alias expansion */ 8137cb960a2Sdownsj s = pushs(SALIAS, source->areap); 8147cb960a2Sdownsj s->start = s->str = p->val.s; 8157cb960a2Sdownsj s->u.tblp = p; 8167cb960a2Sdownsj s->next = source; 8177cb960a2Sdownsj source = s; 8187cb960a2Sdownsj afree(yylval.cp, ATEMP); 8197cb960a2Sdownsj goto Again; 8207cb960a2Sdownsj } 8217cb960a2Sdownsj } 8227cb960a2Sdownsj 8237cb960a2Sdownsj return LWORD; 8247cb960a2Sdownsj } 8257cb960a2Sdownsj 8267cb960a2Sdownsj static void 827c5d5393cSotto gethere(void) 8287cb960a2Sdownsj { 8297894b443Smillert struct ioword **p; 8307cb960a2Sdownsj 8317cb960a2Sdownsj for (p = heres; p < herep; p++) 8327cb960a2Sdownsj readhere(*p); 8337cb960a2Sdownsj herep = heres; 8347cb960a2Sdownsj } 8357cb960a2Sdownsj 8367cb960a2Sdownsj /* 8377cb960a2Sdownsj * read "<<word" text into temp file 8387cb960a2Sdownsj */ 8397cb960a2Sdownsj 8407cb960a2Sdownsj static void 841c5d5393cSotto readhere(struct ioword *iop) 8427cb960a2Sdownsj { 8437894b443Smillert int c; 8447cb960a2Sdownsj char *volatile eof; 8457cb960a2Sdownsj char *eofp; 846e55c1b2cSdownsj int skiptabs; 847f00c5086Smillert XString xs; 848f00c5086Smillert char *xp; 849f00c5086Smillert int xpos; 8507cb960a2Sdownsj 8517cb960a2Sdownsj eof = evalstr(iop->delim, 0); 8527cb960a2Sdownsj 853e55c1b2cSdownsj if (!(iop->flag & IOEVAL)) 854e55c1b2cSdownsj ignore_backslash_newline++; 855e55c1b2cSdownsj 856f00c5086Smillert Xinit(xs, xp, 256, ATEMP); 857f00c5086Smillert 8587cb960a2Sdownsj for (;;) { 8597cb960a2Sdownsj eofp = eof; 8607cb960a2Sdownsj skiptabs = iop->flag & IOSKIP; 861f00c5086Smillert xpos = Xsavepos(xs, xp); 862e55c1b2cSdownsj while ((c = getsc()) != 0) { 8637cb960a2Sdownsj if (skiptabs) { 8647cb960a2Sdownsj if (c == '\t') 8657cb960a2Sdownsj continue; 8667cb960a2Sdownsj skiptabs = 0; 8677cb960a2Sdownsj } 8687cb960a2Sdownsj if (c != *eofp) 8697cb960a2Sdownsj break; 870f00c5086Smillert Xcheck(xs, xp); 871f00c5086Smillert Xput(xs, xp, c); 8727cb960a2Sdownsj eofp++; 8737cb960a2Sdownsj } 8747cb960a2Sdownsj /* Allow EOF here so commands with out trailing newlines 8757cb960a2Sdownsj * will work (eg, ksh -c '...', $(...), etc). 8767cb960a2Sdownsj */ 877f00c5086Smillert if (*eofp == '\0' && (c == 0 || c == '\n')) { 878f00c5086Smillert xp = Xrestpos(xs, xp, xpos); 8797cb960a2Sdownsj break; 880f00c5086Smillert } 8817cb960a2Sdownsj ungetsc(c); 882e55c1b2cSdownsj while ((c = getsc()) != '\n') { 8837cb960a2Sdownsj if (c == 0) 8847cb960a2Sdownsj yyerror("here document `%s' unclosed\n", eof); 885f00c5086Smillert Xcheck(xs, xp); 886f00c5086Smillert Xput(xs, xp, c); 8877cb960a2Sdownsj } 888f00c5086Smillert Xcheck(xs, xp); 889f00c5086Smillert Xput(xs, xp, c); 8907cb960a2Sdownsj } 891f00c5086Smillert Xput(xs, xp, '\0'); 892f00c5086Smillert iop->heredoc = Xclose(xs, xp); 893f00c5086Smillert 894e55c1b2cSdownsj if (!(iop->flag & IOEVAL)) 895e55c1b2cSdownsj ignore_backslash_newline--; 8967cb960a2Sdownsj } 8977cb960a2Sdownsj 8987cb960a2Sdownsj void 8997cb960a2Sdownsj yyerror(const char *fmt, ...) 9007cb960a2Sdownsj { 9017cb960a2Sdownsj va_list va; 9027cb960a2Sdownsj 9037cb960a2Sdownsj /* pop aliases and re-reads */ 9047cb960a2Sdownsj while (source->type == SALIAS || source->type == SREREAD) 9057cb960a2Sdownsj source = source->next; 9067cb960a2Sdownsj source->str = null; /* zap pending input */ 9077cb960a2Sdownsj 9080e7d3a01Smillert error_prefix(true); 90969b9f96bSmillert va_start(va, fmt); 9107cb960a2Sdownsj shf_vfprintf(shl_out, fmt, va); 9117cb960a2Sdownsj va_end(va); 9127cb960a2Sdownsj errorf(null); 9137cb960a2Sdownsj } 9147cb960a2Sdownsj 9157cb960a2Sdownsj /* 9167cb960a2Sdownsj * input for yylex with alias expansion 9177cb960a2Sdownsj */ 9187cb960a2Sdownsj 9197cb960a2Sdownsj Source * 920c5d5393cSotto pushs(int type, Area *areap) 9217cb960a2Sdownsj { 9227894b443Smillert Source *s; 9237cb960a2Sdownsj 924*8c046d24Snicm s = alloc(sizeof(Source), areap); 9257cb960a2Sdownsj s->type = type; 9267cb960a2Sdownsj s->str = null; 9277cb960a2Sdownsj s->start = NULL; 9287cb960a2Sdownsj s->line = 0; 929e7b1290aSotto s->cmd_offset = 0; 9307cb960a2Sdownsj s->errline = 0; 9317cb960a2Sdownsj s->file = NULL; 9327cb960a2Sdownsj s->flags = 0; 9337cb960a2Sdownsj s->next = NULL; 9347cb960a2Sdownsj s->areap = areap; 9357cb960a2Sdownsj if (type == SFILE || type == SSTDIN) { 9367cb960a2Sdownsj char *dummy; 9377cb960a2Sdownsj Xinit(s->xs, dummy, 256, s->areap); 9387cb960a2Sdownsj } else 9397cb960a2Sdownsj memset(&s->xs, 0, sizeof(s->xs)); 9407cb960a2Sdownsj return s; 9417cb960a2Sdownsj } 9427cb960a2Sdownsj 9437cb960a2Sdownsj static int 944c5d5393cSotto getsc__(void) 9457cb960a2Sdownsj { 9467894b443Smillert Source *s = source; 9477894b443Smillert int c; 9487cb960a2Sdownsj 9497cb960a2Sdownsj while ((c = *s->str++) == 0) { 9507cb960a2Sdownsj s->str = NULL; /* return 0 for EOF by default */ 9517cb960a2Sdownsj switch (s->type) { 9527cb960a2Sdownsj case SEOF: 9537cb960a2Sdownsj s->str = null; 9547cb960a2Sdownsj return 0; 9557cb960a2Sdownsj 9567cb960a2Sdownsj case SSTDIN: 9577cb960a2Sdownsj case SFILE: 9587cb960a2Sdownsj getsc_line(s); 9597cb960a2Sdownsj break; 9607cb960a2Sdownsj 9617cb960a2Sdownsj case SWSTR: 9627cb960a2Sdownsj break; 9637cb960a2Sdownsj 9647cb960a2Sdownsj case SSTRING: 9657cb960a2Sdownsj break; 9667cb960a2Sdownsj 9677cb960a2Sdownsj case SWORDS: 9687cb960a2Sdownsj s->start = s->str = *s->u.strv++; 9697cb960a2Sdownsj s->type = SWORDSEP; 9707cb960a2Sdownsj break; 9717cb960a2Sdownsj 9727cb960a2Sdownsj case SWORDSEP: 9737cb960a2Sdownsj if (*s->u.strv == NULL) { 97496085982Snicm s->start = s->str = "\n"; 9757cb960a2Sdownsj s->type = SEOF; 9767cb960a2Sdownsj } else { 97796085982Snicm s->start = s->str = " "; 9787cb960a2Sdownsj s->type = SWORDS; 9797cb960a2Sdownsj } 9807cb960a2Sdownsj break; 9817cb960a2Sdownsj 9827cb960a2Sdownsj case SALIAS: 9837cb960a2Sdownsj if (s->flags & SF_ALIASEND) { 9847cb960a2Sdownsj /* pass on an unused SF_ALIAS flag */ 9857cb960a2Sdownsj source = s->next; 9867cb960a2Sdownsj source->flags |= s->flags & SF_ALIAS; 9877cb960a2Sdownsj s = source; 9887a8124d8Sderaadt } else if (*s->u.tblp->val.s && 989e569fc7cSderaadt isspace((unsigned char)strchr(s->u.tblp->val.s, 0)[-1])) { 9907cb960a2Sdownsj source = s = s->next; /* pop source stack */ 991e55c1b2cSdownsj /* Note that this alias ended with a space, 992e55c1b2cSdownsj * enabling alias expansion on the following 993e55c1b2cSdownsj * word. 994e55c1b2cSdownsj */ 9957cb960a2Sdownsj s->flags |= SF_ALIAS; 9967cb960a2Sdownsj } else { 997e55c1b2cSdownsj /* At this point, we need to keep the current 998e55c1b2cSdownsj * alias in the source list so recursive 999e55c1b2cSdownsj * aliases can be detected and we also need 1000e55c1b2cSdownsj * to return the next character. Do this 1001e55c1b2cSdownsj * by temporarily popping the alias to get 1002e55c1b2cSdownsj * the next character and then put it back 1003e55c1b2cSdownsj * in the source list with the SF_ALIASEND 1004e55c1b2cSdownsj * flag set. 10057cb960a2Sdownsj */ 1006e55c1b2cSdownsj source = s->next; /* pop source stack */ 1007e55c1b2cSdownsj source->flags |= s->flags & SF_ALIAS; 1008e55c1b2cSdownsj c = getsc__(); 1009e55c1b2cSdownsj if (c) { 10107cb960a2Sdownsj s->flags |= SF_ALIASEND; 1011e55c1b2cSdownsj s->ugbuf[0] = c; s->ugbuf[1] = '\0'; 1012e55c1b2cSdownsj s->start = s->str = s->ugbuf; 1013e55c1b2cSdownsj s->next = source; 1014e55c1b2cSdownsj source = s; 1015e55c1b2cSdownsj } else { 1016e55c1b2cSdownsj s = source; 1017e55c1b2cSdownsj /* avoid reading eof twice */ 1018e55c1b2cSdownsj s->str = NULL; 1019e7bc3c65Sdownsj break; 1020e55c1b2cSdownsj } 10217cb960a2Sdownsj } 10227cb960a2Sdownsj continue; 10237cb960a2Sdownsj 10247cb960a2Sdownsj case SREREAD: 1025e55c1b2cSdownsj if (s->start != s->ugbuf) /* yuck */ 10267cb960a2Sdownsj afree(s->u.freeme, ATEMP); 10277cb960a2Sdownsj source = s = s->next; 10287cb960a2Sdownsj continue; 10297cb960a2Sdownsj } 10307cb960a2Sdownsj if (s->str == NULL) { 10317cb960a2Sdownsj s->type = SEOF; 10327cb960a2Sdownsj s->start = s->str = null; 10337cb960a2Sdownsj return '\0'; 10347cb960a2Sdownsj } 10357cb960a2Sdownsj if (s->flags & SF_ECHO) { 10367cb960a2Sdownsj shf_puts(s->str, shl_out); 10377cb960a2Sdownsj shf_flush(shl_out); 10387cb960a2Sdownsj } 10397cb960a2Sdownsj } 10407cb960a2Sdownsj return c; 10417cb960a2Sdownsj } 10427cb960a2Sdownsj 10437cb960a2Sdownsj static void 1044c5d5393cSotto getsc_line(Source *s) 10457cb960a2Sdownsj { 10467cb960a2Sdownsj char *xp = Xstring(s->xs, xp); 10477cb960a2Sdownsj int interactive = Flag(FTALKING) && s->type == SSTDIN; 10487cb960a2Sdownsj int have_tty = interactive && (s->flags & SF_TTY); 10497cb960a2Sdownsj 10507cb960a2Sdownsj /* Done here to ensure nothing odd happens when a timeout occurs */ 10517cb960a2Sdownsj XcheckN(s->xs, xp, LINE); 10527cb960a2Sdownsj *xp = '\0'; 10537cb960a2Sdownsj s->start = s->str = xp; 10547cb960a2Sdownsj 10557cb960a2Sdownsj if (have_tty && ksh_tmout) { 10567cb960a2Sdownsj ksh_tmout_state = TMOUT_READING; 10577cb960a2Sdownsj alarm(ksh_tmout); 10587cb960a2Sdownsj } 10597cb960a2Sdownsj #ifdef EDIT 10607cb960a2Sdownsj if (have_tty && (0 10617cb960a2Sdownsj # ifdef VI 10627cb960a2Sdownsj || Flag(FVI) 10637cb960a2Sdownsj # endif /* VI */ 10647cb960a2Sdownsj # ifdef EMACS 10657cb960a2Sdownsj || Flag(FEMACS) || Flag(FGMACS) 10667cb960a2Sdownsj # endif /* EMACS */ 10677a8124d8Sderaadt )) { 10687cb960a2Sdownsj int nread; 10697cb960a2Sdownsj 10707cb960a2Sdownsj nread = x_read(xp, LINE); 10717cb960a2Sdownsj if (nread < 0) /* read error */ 10727cb960a2Sdownsj nread = 0; 10737cb960a2Sdownsj xp[nread] = '\0'; 10747cb960a2Sdownsj xp += nread; 10757cb960a2Sdownsj } 10767cb960a2Sdownsj else 10777cb960a2Sdownsj #endif /* EDIT */ 10787cb960a2Sdownsj { 10797cb960a2Sdownsj if (interactive) { 10807cb960a2Sdownsj pprompt(prompt, 0); 10817cb960a2Sdownsj } else 10827cb960a2Sdownsj s->line++; 10837cb960a2Sdownsj 10847cb960a2Sdownsj while (1) { 10857cb960a2Sdownsj char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf); 10867cb960a2Sdownsj 10877a8124d8Sderaadt if (!p && shf_error(s->u.shf) && 10887a8124d8Sderaadt shf_errno(s->u.shf) == EINTR) { 10897cb960a2Sdownsj shf_clearerr(s->u.shf); 10907cb960a2Sdownsj if (trap) 10917cb960a2Sdownsj runtraps(0); 10927cb960a2Sdownsj continue; 10937cb960a2Sdownsj } 10947cb960a2Sdownsj if (!p || (xp = p, xp[-1] == '\n')) 10957cb960a2Sdownsj break; 10967cb960a2Sdownsj /* double buffer size */ 10977cb960a2Sdownsj xp++; /* move past null so doubling works... */ 10987cb960a2Sdownsj XcheckN(s->xs, xp, Xlength(s->xs, xp)); 10997cb960a2Sdownsj xp--; /* ...and move back again */ 11007cb960a2Sdownsj } 11017cb960a2Sdownsj /* flush any unwanted input so other programs/builtins 11027cb960a2Sdownsj * can read it. Not very optimal, but less error prone 11037cb960a2Sdownsj * than flushing else where, dealing with redirections, 11047cb960a2Sdownsj * etc.. 11057cb960a2Sdownsj * todo: reduce size of shf buffer (~128?) if SSTDIN 11067cb960a2Sdownsj */ 11077cb960a2Sdownsj if (s->type == SSTDIN) 11087cb960a2Sdownsj shf_flush(s->u.shf); 11097cb960a2Sdownsj } 11107cb960a2Sdownsj /* XXX: temporary kludge to restore source after a 11117cb960a2Sdownsj * trap may have been executed. 11127cb960a2Sdownsj */ 11137cb960a2Sdownsj source = s; 111494e42df6Smillert if (have_tty && ksh_tmout) { 11157cb960a2Sdownsj ksh_tmout_state = TMOUT_EXECUTING; 11167cb960a2Sdownsj alarm(0); 11177cb960a2Sdownsj } 11187cb960a2Sdownsj s->start = s->str = Xstring(s->xs, xp); 11197cb960a2Sdownsj strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp)); 11207cb960a2Sdownsj /* Note: if input is all nulls, this is not eof */ 11217cb960a2Sdownsj if (Xlength(s->xs, xp) == 0) { /* EOF */ 11227cb960a2Sdownsj if (s->type == SFILE) 11237cb960a2Sdownsj shf_fdclose(s->u.shf); 11247cb960a2Sdownsj s->str = NULL; 11257cb960a2Sdownsj } else if (interactive) { 11267cb960a2Sdownsj #ifdef HISTORY 11277cb960a2Sdownsj char *p = Xstring(s->xs, xp); 11287cb960a2Sdownsj if (cur_prompt == PS1) 11297cb960a2Sdownsj while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS)) 11307cb960a2Sdownsj p++; 11317cb960a2Sdownsj if (*p) { 11327cb960a2Sdownsj s->line++; 11337cb960a2Sdownsj histsave(s->line, s->str, 1); 11347cb960a2Sdownsj } 11357cb960a2Sdownsj #endif /* HISTORY */ 11367cb960a2Sdownsj } 11377cb960a2Sdownsj if (interactive) 11384c69a2c1Skstailey set_prompt(PS2, (Source *) 0); 11397cb960a2Sdownsj } 11407cb960a2Sdownsj 1141e7b1290aSotto static char * 1142e7b1290aSotto special_prompt_expand(char *str) 1143e7b1290aSotto { 1144e7b1290aSotto char *p = str; 1145e7b1290aSotto 1146e7b1290aSotto while ((p = strstr(p, "\\$")) != NULL) { 11476aa3ef4bSdrahn *(p+1) = 'p'; 1148e7b1290aSotto } 1149e7b1290aSotto return str; 1150e7b1290aSotto } 1151e7b1290aSotto 11527cb960a2Sdownsj void 1153c5d5393cSotto set_prompt(int to, Source *s) 11547cb960a2Sdownsj { 11554300ac9bSmillert char *ps1; 11567cb960a2Sdownsj Area *saved_atemp; 1157fc804817Smillert 115894e42df6Smillert cur_prompt = to; 115994e42df6Smillert 116094e42df6Smillert switch (to) { 116194e42df6Smillert case PS1: /* command */ 11624300ac9bSmillert ps1 = str_save(str_val(global("PS1")), ATEMP); 11634300ac9bSmillert saved_atemp = ATEMP; /* ps1 is freed by substitute() */ 11647cb960a2Sdownsj newenv(E_ERRH); 116569b9f96bSmillert if (sigsetjmp(e->jbuf, 0)) { 11667cb960a2Sdownsj prompt = safe_prompt; 1167dcacb757Sdownsj /* Don't print an error - assume it has already 1168dcacb757Sdownsj * been printed. Reason is we may have forked 1169dcacb757Sdownsj * to run a command and the child may be 1170dcacb757Sdownsj * unwinding its stack through this code as it 1171dcacb757Sdownsj * exits. 1172dcacb757Sdownsj */ 1173e7b1290aSotto } else { 1174e7b1290aSotto /* expand \$ before other substitutions are done */ 1175e7b1290aSotto char *tmp = special_prompt_expand(ps1); 1176e7b1290aSotto prompt = str_save(substitute(tmp, 0), saved_atemp); 1177e7b1290aSotto } 11780ee3f80eSotto quitenv(NULL); 11797cb960a2Sdownsj break; 11807cb960a2Sdownsj case PS2: /* command continuation */ 11817cb960a2Sdownsj prompt = str_val(global("PS2")); 11827cb960a2Sdownsj break; 11837cb960a2Sdownsj } 11847cb960a2Sdownsj } 11857cb960a2Sdownsj 1186eb6bc482Sderaadt static int 1187c5d5393cSotto dopprompt(const char *sp, int ntruncate, const char **spp, int doprint) 11887cb960a2Sdownsj { 1189eb6bc482Sderaadt char strbuf[1024], tmpbuf[1024], *p, *str, nbuf[32], delimiter = '\0'; 1190a1344090Sderaadt int len, c, n, totlen = 0, indelimit = 0, counting = 1, delimitthis; 119141c26999Sotto const char *cp = sp; 1192eb6bc482Sderaadt struct tm *tm; 1193eb6bc482Sderaadt time_t t; 1194eb6bc482Sderaadt 1195eb6bc482Sderaadt if (*cp && cp[1] == '\r') { 1196eb6bc482Sderaadt delimiter = *cp; 1197eb6bc482Sderaadt cp += 2; 1198eb6bc482Sderaadt } 11997cb960a2Sdownsj 12007cb960a2Sdownsj while (*cp != 0) { 1201a1344090Sderaadt delimitthis = 0; 1202eb6bc482Sderaadt if (indelimit && *cp != delimiter) 1203eb6bc482Sderaadt ; 1204eb6bc482Sderaadt else if (*cp == '\n' || *cp == '\r') { 1205eb6bc482Sderaadt totlen = 0; 1206eb6bc482Sderaadt sp = cp + 1; 1207a1344090Sderaadt } else if (*cp == '\t') { 1208a1344090Sderaadt if (counting) 1209eb6bc482Sderaadt totlen = (totlen | 7) + 1; 1210a1344090Sderaadt } else if (*cp == delimiter) { 1211eb6bc482Sderaadt indelimit = !indelimit; 1212a1344090Sderaadt delimitthis = 1; 1213a1344090Sderaadt } 1214eb6bc482Sderaadt 1215eb6bc482Sderaadt if (*cp == '\\') { 1216eb6bc482Sderaadt cp++; 1217eb6bc482Sderaadt if (!*cp) 1218eb6bc482Sderaadt break; 1219eb6bc482Sderaadt if (Flag(FSH)) 1220eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "\\%c", *cp); 1221eb6bc482Sderaadt else switch (*cp) { 1222eb6bc482Sderaadt case 'a': /* '\' 'a' bell */ 1223eb6bc482Sderaadt strbuf[0] = '\007'; 1224eb6bc482Sderaadt strbuf[1] = '\0'; 1225eb6bc482Sderaadt break; 1226eb6bc482Sderaadt case 'd': /* '\' 'd' Dow Mon DD */ 1227eb6bc482Sderaadt time(&t); 1228eb6bc482Sderaadt tm = localtime(&t); 1229eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%a %b %d", tm); 1230eb6bc482Sderaadt break; 1231eb6bc482Sderaadt case 'D': /* '\' 'D' '{' strftime format '}' */ 1232eb6bc482Sderaadt p = strchr(cp + 2, '}'); 1233eb6bc482Sderaadt if (cp[1] != '{' || p == NULL) { 1234eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, 1235eb6bc482Sderaadt "\\%c", *cp); 1236eb6bc482Sderaadt break; 1237eb6bc482Sderaadt } 1238eb6bc482Sderaadt strlcpy(tmpbuf, cp + 2, sizeof tmpbuf); 1239eb6bc482Sderaadt p = strchr(tmpbuf, '}'); 1240eb6bc482Sderaadt if (p) 1241eb6bc482Sderaadt *p = '\0'; 1242eb6bc482Sderaadt time(&t); 1243eb6bc482Sderaadt tm = localtime(&t); 1244eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, tmpbuf, tm); 1245eb6bc482Sderaadt cp = strchr(cp + 2, '}'); 1246eb6bc482Sderaadt break; 1247eb6bc482Sderaadt case 'e': /* '\' 'e' escape */ 1248eb6bc482Sderaadt strbuf[0] = '\033'; 1249eb6bc482Sderaadt strbuf[1] = '\0'; 1250eb6bc482Sderaadt break; 1251eb6bc482Sderaadt case 'h': /* '\' 'h' shortened hostname */ 1252eb6bc482Sderaadt gethostname(strbuf, sizeof strbuf); 1253eb6bc482Sderaadt p = strchr(strbuf, '.'); 1254eb6bc482Sderaadt if (p) 1255eb6bc482Sderaadt *p = '\0'; 1256eb6bc482Sderaadt break; 1257eb6bc482Sderaadt case 'H': /* '\' 'H' full hostname */ 1258eb6bc482Sderaadt gethostname(strbuf, sizeof strbuf); 1259eb6bc482Sderaadt break; 1260eb6bc482Sderaadt case 'j': /* '\' 'j' number of jobs */ 1261eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%d", 1262eb6bc482Sderaadt j_njobs()); 1263eb6bc482Sderaadt break; 1264eb6bc482Sderaadt case 'l': /* '\' 'l' basename of tty */ 1265eb6bc482Sderaadt p = ttyname(0); 1266eb6bc482Sderaadt if (p) 1267eb6bc482Sderaadt p = basename(p); 1268eb6bc482Sderaadt if (p) 1269eb6bc482Sderaadt strlcpy(strbuf, p, sizeof strbuf); 1270eb6bc482Sderaadt break; 1271eb6bc482Sderaadt case 'n': /* '\' 'n' newline */ 1272eb6bc482Sderaadt strbuf[0] = '\n'; 1273eb6bc482Sderaadt strbuf[1] = '\0'; 12745c4a5744Sderaadt totlen = 0; /* reset for prompt re-print */ 12755c4a5744Sderaadt sp = cp + 1; 1276eb6bc482Sderaadt break; 12776aa3ef4bSdrahn case 'p': /* '\' '$' $ or # */ 12786aa3ef4bSdrahn strbuf[0] = ksheuid ? '$' : '#'; 12796aa3ef4bSdrahn strbuf[1] = '\0'; 12806aa3ef4bSdrahn break; 1281eb6bc482Sderaadt case 'r': /* '\' 'r' return */ 1282eb6bc482Sderaadt strbuf[0] = '\r'; 1283eb6bc482Sderaadt strbuf[1] = '\0'; 12845c4a5744Sderaadt totlen = 0; /* reset for prompt re-print */ 12855c4a5744Sderaadt sp = cp + 1; 1286eb6bc482Sderaadt break; 1287eb6bc482Sderaadt case 's': /* '\' 's' basename $0 */ 1288eb6bc482Sderaadt strlcpy(strbuf, kshname, sizeof strbuf); 1289eb6bc482Sderaadt break; 1290eb6bc482Sderaadt case 't': /* '\' 't' 24 hour HH:MM:SS */ 1291eb6bc482Sderaadt time(&t); 1292eb6bc482Sderaadt tm = localtime(&t); 1293eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%T", tm); 1294eb6bc482Sderaadt break; 1295eb6bc482Sderaadt case 'T': /* '\' 'T' 12 hour HH:MM:SS */ 1296eb6bc482Sderaadt time(&t); 1297eb6bc482Sderaadt tm = localtime(&t); 1298eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%l:%M:%S", tm); 1299eb6bc482Sderaadt break; 1300eb6bc482Sderaadt case '@': /* '\' '@' 12 hour am/pm format */ 1301eb6bc482Sderaadt time(&t); 1302eb6bc482Sderaadt tm = localtime(&t); 1303eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%r", tm); 1304eb6bc482Sderaadt break; 1305eb6bc482Sderaadt case 'A': /* '\' 'A' 24 hour HH:MM */ 1306eb6bc482Sderaadt time(&t); 1307eb6bc482Sderaadt tm = localtime(&t); 1308eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%R", tm); 1309eb6bc482Sderaadt break; 1310eb6bc482Sderaadt case 'u': /* '\' 'u' username */ 1311f351fb1cSotto strlcpy(strbuf, username, sizeof strbuf); 1312eb6bc482Sderaadt break; 1313eb6bc482Sderaadt case 'v': /* '\' 'v' version (short) */ 1314eb6bc482Sderaadt p = strchr(ksh_version, ' '); 1315eb6bc482Sderaadt if (p) 1316eb6bc482Sderaadt p = strchr(p + 1, ' '); 1317eb6bc482Sderaadt if (p) { 1318eb6bc482Sderaadt p++; 1319eb6bc482Sderaadt strlcpy(strbuf, p, sizeof strbuf); 1320eb6bc482Sderaadt p = strchr(strbuf, ' '); 1321eb6bc482Sderaadt if (p) 1322eb6bc482Sderaadt *p = '\0'; 1323eb6bc482Sderaadt } 1324eb6bc482Sderaadt break; 1325eb6bc482Sderaadt case 'V': /* '\' 'V' version (long) */ 1326eb6bc482Sderaadt strlcpy(strbuf, ksh_version, sizeof strbuf); 1327eb6bc482Sderaadt break; 1328eb6bc482Sderaadt case 'w': /* '\' 'w' cwd */ 1329eb6bc482Sderaadt p = str_val(global("PWD")); 1330ad55f791Sderaadt n = strlen(str_val(global("HOME"))); 1331ad55f791Sderaadt if (strcmp(p, "/") == 0) { 1332ad55f791Sderaadt strlcpy(strbuf, p, sizeof strbuf); 1333ad55f791Sderaadt } else if (strcmp(p, str_val(global("HOME"))) == 0) { 1334eb6bc482Sderaadt strbuf[0] = '~'; 1335eb6bc482Sderaadt strbuf[1] = '\0'; 1336ad55f791Sderaadt } else if (strncmp(p, str_val(global("HOME")), n) 1337ad55f791Sderaadt == 0 && p[n] == '/') { 1338ad55f791Sderaadt snprintf(strbuf, sizeof strbuf, "~/%s", 1339ad55f791Sderaadt str_val(global("PWD")) + n + 1); 1340eb6bc482Sderaadt } else 1341eb6bc482Sderaadt strlcpy(strbuf, p, sizeof strbuf); 1342eb6bc482Sderaadt break; 1343eb6bc482Sderaadt case 'W': /* '\' 'W' basename(cwd) */ 1344eb6bc482Sderaadt p = str_val(global("PWD")); 134530e9b9efSokan if (strcmp(p, str_val(global("HOME"))) == 0) { 134630e9b9efSokan strbuf[0] = '~'; 134730e9b9efSokan strbuf[1] = '\0'; 134830e9b9efSokan } else 1349eb6bc482Sderaadt strlcpy(strbuf, basename(p), sizeof strbuf); 1350eb6bc482Sderaadt break; 1351e7b1290aSotto case '!': /* '\' '!' history line number */ 1352eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%d", 1353eb6bc482Sderaadt source->line + 1); 1354eb6bc482Sderaadt break; 1355e7b1290aSotto case '#': /* '\' '#' command line number */ 1356eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%d", 1357e7b1290aSotto source->line - source->cmd_offset + 1); 1358eb6bc482Sderaadt break; 1359eb6bc482Sderaadt case '0': /* '\' '#' '#' ' #' octal numeric handling */ 1360eb6bc482Sderaadt case '1': 1361eb6bc482Sderaadt case '2': 1362eb6bc482Sderaadt case '3': 1363eb6bc482Sderaadt case '4': 1364eb6bc482Sderaadt case '5': 1365eb6bc482Sderaadt case '6': 1366eb6bc482Sderaadt case '7': 1367eb6bc482Sderaadt if ((cp[1] > '7' || cp[1] < '0') || 1368eb6bc482Sderaadt (cp[2] > '7' || cp[2] < '0')) { 1369eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, 1370eb6bc482Sderaadt "\\%c", *cp); 1371eb6bc482Sderaadt break; 1372eb6bc482Sderaadt } 1373064356ecSzhuk n = (cp[0] - '0') * 8 * 8 + (cp[1] - '0') * 8 + 1374064356ecSzhuk (cp[2] - '0'); 1375eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%c", n); 1376eb6bc482Sderaadt cp += 2; 1377eb6bc482Sderaadt break; 1378eb6bc482Sderaadt case '\\': /* '\' '\' */ 1379eb6bc482Sderaadt strbuf[0] = '\\'; 1380eb6bc482Sderaadt strbuf[1] = '\0'; 1381eb6bc482Sderaadt break; 1382a1344090Sderaadt case '[': /* '\' '[' .... stop counting */ 1383a1344090Sderaadt strbuf[0] = '\0'; 1384a1344090Sderaadt counting = 0; 1385eb6bc482Sderaadt break; 1386a1344090Sderaadt case ']': /* '\' ']' restart counting */ 1387a1344090Sderaadt strbuf[0] = '\0'; 1388a1344090Sderaadt counting = 1; 1389eb6bc482Sderaadt break; 1390eb6bc482Sderaadt 1391eb6bc482Sderaadt default: 1392eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "\\%c", *cp); 1393eb6bc482Sderaadt break; 1394eb6bc482Sderaadt } 1395eb6bc482Sderaadt cp++; 1396eb6bc482Sderaadt 1397eb6bc482Sderaadt str = strbuf; 1398eb6bc482Sderaadt len = strlen(str); 1399eb6bc482Sderaadt if (ntruncate) { 1400eb6bc482Sderaadt if (ntruncate >= len) { 1401eb6bc482Sderaadt ntruncate -= len; 1402eb6bc482Sderaadt continue; 1403eb6bc482Sderaadt } 1404eb6bc482Sderaadt str += ntruncate; 1405eb6bc482Sderaadt len -= ntruncate; 1406eb6bc482Sderaadt ntruncate = 0; 1407eb6bc482Sderaadt } 1408eb6bc482Sderaadt if (doprint) 1409eb6bc482Sderaadt shf_write(str, len, shl_out); 1410a1344090Sderaadt if (counting && !indelimit && !delimitthis) 1411eb6bc482Sderaadt totlen += len; 1412eb6bc482Sderaadt continue; 1413eb6bc482Sderaadt } else if (*cp != '!') 14147cb960a2Sdownsj c = *cp++; 14157cb960a2Sdownsj else if (*++cp == '!') 14167cb960a2Sdownsj c = *cp++; 14177cb960a2Sdownsj else { 14187cb960a2Sdownsj char *p; 14197cb960a2Sdownsj 14207cb960a2Sdownsj shf_snprintf(p = nbuf, sizeof(nbuf), "%d", 14217cb960a2Sdownsj source->line + 1); 14227cb960a2Sdownsj len = strlen(nbuf); 14237cb960a2Sdownsj if (ntruncate) { 14247cb960a2Sdownsj if (ntruncate >= len) { 14257cb960a2Sdownsj ntruncate -= len; 14267cb960a2Sdownsj continue; 14277cb960a2Sdownsj } 14287cb960a2Sdownsj p += ntruncate; 14297cb960a2Sdownsj len -= ntruncate; 14307cb960a2Sdownsj ntruncate = 0; 14317cb960a2Sdownsj } 1432eb6bc482Sderaadt if (doprint) 14337cb960a2Sdownsj shf_write(p, len, shl_out); 1434a1344090Sderaadt if (counting && !indelimit && !delimitthis) 1435eb6bc482Sderaadt totlen += len; 14367cb960a2Sdownsj continue; 14377cb960a2Sdownsj } 1438638b9e76Sbeck if (counting && ntruncate) 14397cb960a2Sdownsj --ntruncate; 1440eb6bc482Sderaadt else if (doprint) { 14417cb960a2Sdownsj shf_putc(c, shl_out); 14427cb960a2Sdownsj } 1443a1344090Sderaadt if (counting && !indelimit && !delimitthis) 1444eb6bc482Sderaadt totlen++; 1445eb6bc482Sderaadt } 1446eb6bc482Sderaadt if (doprint) 14477cb960a2Sdownsj shf_flush(shl_out); 1448eb6bc482Sderaadt if (spp) 1449eb6bc482Sderaadt *spp = sp; 1450eb6bc482Sderaadt return (totlen); 1451eb6bc482Sderaadt } 1452eb6bc482Sderaadt 1453eb6bc482Sderaadt void 1454c5d5393cSotto pprompt(const char *cp, int ntruncate) 1455eb6bc482Sderaadt { 1456eb6bc482Sderaadt dopprompt(cp, ntruncate, NULL, 1); 1457eb6bc482Sderaadt } 1458eb6bc482Sderaadt 1459eb6bc482Sderaadt int 1460c5d5393cSotto promptlen(const char *cp, const char **spp) 1461eb6bc482Sderaadt { 1462eb6bc482Sderaadt return dopprompt(cp, 0, spp, 0); 14637cb960a2Sdownsj } 14647cb960a2Sdownsj 14657cb960a2Sdownsj /* Read the variable part of a ${...} expression (ie, up to but not including 14667cb960a2Sdownsj * the :[-+?=#%] or close-brace. 14677cb960a2Sdownsj */ 14687cb960a2Sdownsj static char * 1469c5d5393cSotto get_brace_var(XString *wsp, char *wp) 14707cb960a2Sdownsj { 14717cb960a2Sdownsj enum parse_state { 14727cb960a2Sdownsj PS_INITIAL, PS_SAW_HASH, PS_IDENT, 14737cb960a2Sdownsj PS_NUMBER, PS_VAR1, PS_END 14747cb960a2Sdownsj } 14757cb960a2Sdownsj state; 14767cb960a2Sdownsj char c; 14777cb960a2Sdownsj 14787cb960a2Sdownsj state = PS_INITIAL; 14797cb960a2Sdownsj while (1) { 1480e55c1b2cSdownsj c = getsc(); 14817cb960a2Sdownsj /* State machine to figure out where the variable part ends. */ 14827cb960a2Sdownsj switch (state) { 14837cb960a2Sdownsj case PS_INITIAL: 14847cb960a2Sdownsj if (c == '#') { 14857cb960a2Sdownsj state = PS_SAW_HASH; 14867cb960a2Sdownsj break; 14877cb960a2Sdownsj } 14887eab881bSjaredy /* FALLTHROUGH */ 14897cb960a2Sdownsj case PS_SAW_HASH: 14907cb960a2Sdownsj if (letter(c)) 14917cb960a2Sdownsj state = PS_IDENT; 14927cb960a2Sdownsj else if (digit(c)) 14937cb960a2Sdownsj state = PS_NUMBER; 14947cb960a2Sdownsj else if (ctype(c, C_VAR1)) 14957cb960a2Sdownsj state = PS_VAR1; 14967cb960a2Sdownsj else 14977cb960a2Sdownsj state = PS_END; 14987cb960a2Sdownsj break; 14997cb960a2Sdownsj case PS_IDENT: 15007cb960a2Sdownsj if (!letnum(c)) { 15017cb960a2Sdownsj state = PS_END; 15027cb960a2Sdownsj if (c == '[') { 15037cb960a2Sdownsj char *tmp, *p; 15047cb960a2Sdownsj 15057cb960a2Sdownsj if (!arraysub(&tmp)) 15067cb960a2Sdownsj yyerror("missing ]\n"); 15077cb960a2Sdownsj *wp++ = c; 15087cb960a2Sdownsj for (p = tmp; *p; ) { 15097cb960a2Sdownsj Xcheck(*wsp, wp); 15107cb960a2Sdownsj *wp++ = *p++; 15117cb960a2Sdownsj } 15127cb960a2Sdownsj afree(tmp, ATEMP); 1513e55c1b2cSdownsj c = getsc(); /* the ] */ 15147cb960a2Sdownsj } 15157cb960a2Sdownsj } 15167cb960a2Sdownsj break; 15177cb960a2Sdownsj case PS_NUMBER: 15187cb960a2Sdownsj if (!digit(c)) 15197cb960a2Sdownsj state = PS_END; 15207cb960a2Sdownsj break; 15217cb960a2Sdownsj case PS_VAR1: 15227cb960a2Sdownsj state = PS_END; 15237cb960a2Sdownsj break; 15247cb960a2Sdownsj case PS_END: /* keep gcc happy */ 15257cb960a2Sdownsj break; 15267cb960a2Sdownsj } 15277cb960a2Sdownsj if (state == PS_END) { 15287cb960a2Sdownsj *wp++ = '\0'; /* end of variable part */ 15297cb960a2Sdownsj ungetsc(c); 15307cb960a2Sdownsj break; 15317cb960a2Sdownsj } 15327cb960a2Sdownsj Xcheck(*wsp, wp); 15337cb960a2Sdownsj *wp++ = c; 15347cb960a2Sdownsj } 15357cb960a2Sdownsj return wp; 15367cb960a2Sdownsj } 15377cb960a2Sdownsj 15387cb960a2Sdownsj /* 15397cb960a2Sdownsj * Save an array subscript - returns true if matching bracket found, false 15407cb960a2Sdownsj * if eof or newline was found. 15417cb960a2Sdownsj * (Returned string double null terminated) 15427cb960a2Sdownsj */ 15437cb960a2Sdownsj static int 1544c5d5393cSotto arraysub(char **strp) 15457cb960a2Sdownsj { 15467cb960a2Sdownsj XString ws; 15477cb960a2Sdownsj char *wp; 15487cb960a2Sdownsj char c; 15497cb960a2Sdownsj int depth = 1; /* we are just past the initial [ */ 15507cb960a2Sdownsj 15517cb960a2Sdownsj Xinit(ws, wp, 32, ATEMP); 15527cb960a2Sdownsj 15537cb960a2Sdownsj do { 1554e55c1b2cSdownsj c = getsc(); 15557cb960a2Sdownsj Xcheck(ws, wp); 15567cb960a2Sdownsj *wp++ = c; 15577cb960a2Sdownsj if (c == '[') 15587cb960a2Sdownsj depth++; 15597cb960a2Sdownsj else if (c == ']') 15607cb960a2Sdownsj depth--; 15617cb960a2Sdownsj } while (depth > 0 && c && c != '\n'); 15627cb960a2Sdownsj 15637cb960a2Sdownsj *wp++ = '\0'; 15647cb960a2Sdownsj *strp = Xclose(ws, wp); 15657cb960a2Sdownsj 15667cb960a2Sdownsj return depth == 0 ? 1 : 0; 15677cb960a2Sdownsj } 15687cb960a2Sdownsj 15697cb960a2Sdownsj /* Unget a char: handles case when we are already at the start of the buffer */ 15707cb960a2Sdownsj static const char * 1571c5d5393cSotto ungetsc(int c) 15727cb960a2Sdownsj { 1573e55c1b2cSdownsj if (backslash_skip) 1574e55c1b2cSdownsj backslash_skip--; 15757cb960a2Sdownsj /* Don't unget eof... */ 15767cb960a2Sdownsj if (source->str == null && c == '\0') 15777cb960a2Sdownsj return source->str; 15787cb960a2Sdownsj if (source->str > source->start) 15797cb960a2Sdownsj source->str--; 15807cb960a2Sdownsj else { 15817cb960a2Sdownsj Source *s; 15827cb960a2Sdownsj 15837cb960a2Sdownsj s = pushs(SREREAD, source->areap); 1584e55c1b2cSdownsj s->ugbuf[0] = c; s->ugbuf[1] = '\0'; 1585e55c1b2cSdownsj s->start = s->str = s->ugbuf; 15867cb960a2Sdownsj s->next = source; 15877cb960a2Sdownsj source = s; 15887cb960a2Sdownsj } 15897cb960a2Sdownsj return source->str; 15907cb960a2Sdownsj } 15917cb960a2Sdownsj 1592e55c1b2cSdownsj 15937cb960a2Sdownsj /* Called to get a char that isn't a \newline sequence. */ 15947cb960a2Sdownsj static int 159569b9f96bSmillert getsc_bn(void) 15967cb960a2Sdownsj { 1597e55c1b2cSdownsj int c, c2; 1598e55c1b2cSdownsj 1599e55c1b2cSdownsj if (ignore_backslash_newline) 1600e55c1b2cSdownsj return getsc_(); 1601e55c1b2cSdownsj 1602e55c1b2cSdownsj if (backslash_skip == 1) { 1603e55c1b2cSdownsj backslash_skip = 2; 1604e55c1b2cSdownsj return getsc_(); 1605e55c1b2cSdownsj } 1606e55c1b2cSdownsj 1607e55c1b2cSdownsj backslash_skip = 0; 16087cb960a2Sdownsj 16097cb960a2Sdownsj while (1) { 16107cb960a2Sdownsj c = getsc_(); 1611e55c1b2cSdownsj if (c == '\\') { 1612e55c1b2cSdownsj if ((c2 = getsc_()) == '\n') 16137cb960a2Sdownsj /* ignore the \newline; get the next char... */ 1614e55c1b2cSdownsj continue; 1615e55c1b2cSdownsj ungetsc(c2); 1616e55c1b2cSdownsj backslash_skip = 1; 1617e55c1b2cSdownsj } 1618e55c1b2cSdownsj return c; 16197cb960a2Sdownsj } 16207cb960a2Sdownsj } 16213b015934Smillert 16223b015934Smillert static Lex_state * 1623c5d5393cSotto push_state_(State_info *si, Lex_state *old_end) 16243b015934Smillert { 16253b015934Smillert Lex_state *new = alloc(sizeof(Lex_state) * STATE_BSIZE, ATEMP); 16263b015934Smillert 16273b015934Smillert new[0].ls_info.base = old_end; 16283b015934Smillert si->base = &new[0]; 16293b015934Smillert si->end = &new[STATE_BSIZE]; 16303b015934Smillert return &new[1]; 16313b015934Smillert } 16323b015934Smillert 16333b015934Smillert static Lex_state * 1634c5d5393cSotto pop_state_(State_info *si, Lex_state *old_end) 16353b015934Smillert { 16363b015934Smillert Lex_state *old_base = si->base; 16373b015934Smillert 16383b015934Smillert si->base = old_end->ls_info.base - STATE_BSIZE; 16393b015934Smillert si->end = old_end->ls_info.base; 16403b015934Smillert 16413b015934Smillert afree(old_base, ATEMP); 16423b015934Smillert 16437b425235Smillert return si->base + STATE_BSIZE - 1; 16443b015934Smillert } 1645