1*1c2ba45aStedu /* $OpenBSD: lex.c,v 1.56 2015/09/30 14:33:41 tedu 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; 166*1c2ba45aStedu int get, i; 167*1c2ba45aStedu char match[200], *str = match; 168*1c2ba45aStedu size_t mlen; 169f740f764Sderaadt 170f740f764Sderaadt c2 = getsc(); 1710b1768a7Sotto if (c2 == '\0' || c2 == ' ' || c2 == '\t') 172f740f764Sderaadt ; 173f740f764Sderaadt else if (c2 == '!') 174f740f764Sderaadt replace = hist_get_newest(0); 175f740f764Sderaadt else if (isdigit(c2) || c2 == '-' || 176f740f764Sderaadt isalpha(c2)) { 177*1c2ba45aStedu get = !isalpha(c2); 178f740f764Sderaadt 179f740f764Sderaadt *str++ = c2; 180f740f764Sderaadt do { 181f740f764Sderaadt if ((c2 = getsc()) == '\0') 182f740f764Sderaadt break; 183f740f764Sderaadt if (c2 == '\t' || c2 == ' ' || 184f740f764Sderaadt c2 == '\n') { 185f740f764Sderaadt ungetsc(c2); 186f740f764Sderaadt break; 187f740f764Sderaadt } 188f740f764Sderaadt *str++ = c2; 189f740f764Sderaadt } while (str < &match[sizeof(match)-1]); 190f740f764Sderaadt *str = '\0'; 191f740f764Sderaadt 192f740f764Sderaadt if (get) { 193f740f764Sderaadt int h = findhistrel(match); 194f740f764Sderaadt if (h >= 0) 195f740f764Sderaadt replace = &history[h]; 196f740f764Sderaadt } else { 1970e7d3a01Smillert int h = findhist(-1, 0, match, true); 198f740f764Sderaadt if (h >= 0) 199f740f764Sderaadt replace = &history[h]; 200f740f764Sderaadt } 201f740f764Sderaadt } 202f740f764Sderaadt 203f740f764Sderaadt /* 204f740f764Sderaadt * XXX ksh history buffer saves un-expanded 205f740f764Sderaadt * commands. Until the history buffer code is 206f740f764Sderaadt * changed to contain expanded commands, we 207f740f764Sderaadt * ignore the bad commands (spinning sucks) 208f740f764Sderaadt */ 209f740f764Sderaadt if (replace && **replace == '!') 210f740f764Sderaadt ungetsc(c2); 211f740f764Sderaadt else if (replace) { 212f740f764Sderaadt Source *s; 213f740f764Sderaadt 214f740f764Sderaadt /* do not strdup replacement via alloc */ 2157a8124d8Sderaadt s = pushs(SREREAD, source->areap); 216f740f764Sderaadt s->start = s->str = *replace; 217f740f764Sderaadt s->next = source; 2187d849734Smillert s->u.freeme = NULL; 219f740f764Sderaadt source = s; 220f740f764Sderaadt continue; 221*1c2ba45aStedu } else { 222*1c2ba45aStedu /* restore what followed the '!' */ 223*1c2ba45aStedu mlen = strlen(match); 224*1c2ba45aStedu for (i = mlen-1; i >= 0; i--) 225*1c2ba45aStedu ungetsc(match[i]); 226*1c2ba45aStedu } 227f740f764Sderaadt } 2287cb960a2Sdownsj if (c == '[' && (cf & (VARASN|ARRAYVAR))) { 2297cb960a2Sdownsj *wp = EOS; /* temporary */ 2307a8124d8Sderaadt if (is_wdvarname(Xstring(ws, wp), false)) { 2317cb960a2Sdownsj char *p, *tmp; 2327cb960a2Sdownsj 2337cb960a2Sdownsj if (arraysub(&tmp)) { 2347cb960a2Sdownsj *wp++ = CHAR; 2357cb960a2Sdownsj *wp++ = c; 2367cb960a2Sdownsj for (p = tmp; *p; ) { 2377cb960a2Sdownsj Xcheck(ws, wp); 2387cb960a2Sdownsj *wp++ = CHAR; 2397cb960a2Sdownsj *wp++ = *p++; 2407cb960a2Sdownsj } 2417cb960a2Sdownsj afree(tmp, ATEMP); 2427cb960a2Sdownsj break; 2437cb960a2Sdownsj } else { 2447cb960a2Sdownsj Source *s; 2457cb960a2Sdownsj 2467cb960a2Sdownsj s = pushs(SREREAD, 2477cb960a2Sdownsj source->areap); 2487cb960a2Sdownsj s->start = s->str 2497cb960a2Sdownsj = s->u.freeme = tmp; 2507cb960a2Sdownsj s->next = source; 2517cb960a2Sdownsj source = s; 2527cb960a2Sdownsj } 2537cb960a2Sdownsj } 2547cb960a2Sdownsj *wp++ = CHAR; 2557cb960a2Sdownsj *wp++ = c; 2567cb960a2Sdownsj break; 2577cb960a2Sdownsj } 2587eab881bSjaredy /* FALLTHROUGH */ 2597cb960a2Sdownsj Sbase1: /* includes *(...|...) pattern (*+?@!) */ 2607a8124d8Sderaadt if (c == '*' || c == '@' || c == '+' || c == '?' || 2617a8124d8Sderaadt c == '!') { 2627cb960a2Sdownsj c2 = getsc(); 2637cb960a2Sdownsj if (c2 == '(' /*)*/ ) { 2647cb960a2Sdownsj *wp++ = OPAT; 2657cb960a2Sdownsj *wp++ = c; 2663b015934Smillert PUSH_STATE(SPATTERN); 2677cb960a2Sdownsj break; 2687cb960a2Sdownsj } 2697cb960a2Sdownsj ungetsc(c2); 2707cb960a2Sdownsj } 2717eab881bSjaredy /* FALLTHROUGH */ 2727cb960a2Sdownsj Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */ 2737cb960a2Sdownsj switch (c) { 2747cb960a2Sdownsj case '\\': 2757cb960a2Sdownsj c = getsc(); 276e55c1b2cSdownsj if (c) /* trailing \ is lost */ 2777cb960a2Sdownsj *wp++ = QCHAR, *wp++ = c; 2787cb960a2Sdownsj break; 2797cb960a2Sdownsj case '\'': 28022a9cf94Sguenther if ((cf & HEREDOC) || state == SBRACEQ) { 28122a9cf94Sguenther *wp++ = CHAR, *wp++ = c; 28222a9cf94Sguenther break; 28322a9cf94Sguenther } 2847cb960a2Sdownsj *wp++ = OQUOTE; 285e55c1b2cSdownsj ignore_backslash_newline++; 2863b015934Smillert PUSH_STATE(SSQUOTE); 2877cb960a2Sdownsj break; 2887cb960a2Sdownsj case '"': 2897cb960a2Sdownsj *wp++ = OQUOTE; 2903b015934Smillert PUSH_STATE(SDQUOTE); 2917cb960a2Sdownsj break; 2927cb960a2Sdownsj default: 2937cb960a2Sdownsj goto Subst; 2947cb960a2Sdownsj } 2957cb960a2Sdownsj break; 2967cb960a2Sdownsj 2977cb960a2Sdownsj Subst: 2987cb960a2Sdownsj switch (c) { 2997cb960a2Sdownsj case '\\': 3007cb960a2Sdownsj c = getsc(); 3017cb960a2Sdownsj switch (c) { 30257f623feSotto case '\\': 3037cb960a2Sdownsj case '$': case '`': 3047cb960a2Sdownsj *wp++ = QCHAR, *wp++ = c; 3057cb960a2Sdownsj break; 30657f623feSotto case '"': 30757f623feSotto if ((cf & HEREDOC) == 0) { 30857f623feSotto *wp++ = QCHAR, *wp++ = c; 30957f623feSotto break; 31057f623feSotto } 31125d8d262Smoritz /* FALLTHROUGH */ 3127cb960a2Sdownsj default: 313d3ecfddbSstsp if (cf & UNESCAPE) { 314d3ecfddbSstsp *wp++ = QCHAR, *wp++ = c; 315d3ecfddbSstsp break; 316d3ecfddbSstsp } 3177cb960a2Sdownsj Xcheck(ws, wp); 318e55c1b2cSdownsj if (c) { /* trailing \ is lost */ 3197cb960a2Sdownsj *wp++ = CHAR, *wp++ = '\\'; 3207cb960a2Sdownsj *wp++ = CHAR, *wp++ = c; 321e55c1b2cSdownsj } 3227cb960a2Sdownsj break; 3237cb960a2Sdownsj } 3247cb960a2Sdownsj break; 3257cb960a2Sdownsj case '$': 3267cb960a2Sdownsj c = getsc(); 3277cb960a2Sdownsj if (c == '(') /*)*/ { 3287cb960a2Sdownsj c = getsc(); 3297cb960a2Sdownsj if (c == '(') /*)*/ { 3303b015934Smillert PUSH_STATE(SASPAREN); 3313b015934Smillert statep->ls_sasparen.nparen = 2; 3323b015934Smillert statep->ls_sasparen.start = 3333b015934Smillert Xsavepos(ws, wp); 3347cb960a2Sdownsj *wp++ = EXPRSUB; 3357cb960a2Sdownsj } else { 3367cb960a2Sdownsj ungetsc(c); 3373b015934Smillert PUSH_STATE(SCSPAREN); 3383b015934Smillert statep->ls_scsparen.nparen = 1; 3393b015934Smillert statep->ls_scsparen.csstate = 0; 3407cb960a2Sdownsj *wp++ = COMSUB; 3417cb960a2Sdownsj } 3427cb960a2Sdownsj } else if (c == '{') /*}*/ { 3437cb960a2Sdownsj *wp++ = OSUBST; 3443b015934Smillert *wp++ = '{'; /*}*/ 3457cb960a2Sdownsj wp = get_brace_var(&ws, wp); 346e55c1b2cSdownsj c = getsc(); 3473b015934Smillert /* allow :# and :% (ksh88 compat) */ 3483b015934Smillert if (c == ':') { 3493b015934Smillert *wp++ = CHAR, *wp++ = c; 3503b015934Smillert c = getsc(); 3513b015934Smillert } 3523b015934Smillert /* If this is a trim operation, 3533b015934Smillert * treat (,|,) specially in STBRACE. 3543b015934Smillert */ 3557cb960a2Sdownsj if (c == '#' || c == '%') { 3563b015934Smillert ungetsc(c); 3573b015934Smillert PUSH_STATE(STBRACE); 3587cb960a2Sdownsj } else { 3597cb960a2Sdownsj ungetsc(c); 36022a9cf94Sguenther if (state == SDQUOTE || 36122a9cf94Sguenther state == SBRACEQ) 36222a9cf94Sguenther PUSH_STATE(SBRACEQ); 36322a9cf94Sguenther else 3643b015934Smillert PUSH_STATE(SBRACE); 3657cb960a2Sdownsj } 3667cb960a2Sdownsj } else if (ctype(c, C_ALPHA)) { 3677cb960a2Sdownsj *wp++ = OSUBST; 3683b015934Smillert *wp++ = 'X'; 3697cb960a2Sdownsj do { 3707cb960a2Sdownsj Xcheck(ws, wp); 3717cb960a2Sdownsj *wp++ = c; 3727cb960a2Sdownsj c = getsc(); 3737cb960a2Sdownsj } while (ctype(c, C_ALPHA|C_DIGIT)); 3747cb960a2Sdownsj *wp++ = '\0'; 3757cb960a2Sdownsj *wp++ = CSUBST; 3763b015934Smillert *wp++ = 'X'; 3777cb960a2Sdownsj ungetsc(c); 3787cb960a2Sdownsj } else if (ctype(c, C_DIGIT|C_VAR1)) { 3797cb960a2Sdownsj Xcheck(ws, wp); 3807cb960a2Sdownsj *wp++ = OSUBST; 3813b015934Smillert *wp++ = 'X'; 3827cb960a2Sdownsj *wp++ = c; 3837cb960a2Sdownsj *wp++ = '\0'; 3847cb960a2Sdownsj *wp++ = CSUBST; 3853b015934Smillert *wp++ = 'X'; 3867cb960a2Sdownsj } else { 3877cb960a2Sdownsj *wp++ = CHAR, *wp++ = '$'; 3887cb960a2Sdownsj ungetsc(c); 3897cb960a2Sdownsj } 3907cb960a2Sdownsj break; 3917cb960a2Sdownsj case '`': 3923b015934Smillert PUSH_STATE(SBQUOTE); 3937cb960a2Sdownsj *wp++ = COMSUB; 3947cb960a2Sdownsj /* Need to know if we are inside double quotes 3957cb960a2Sdownsj * since sh/at&t-ksh translate the \" to " in 3967cb960a2Sdownsj * "`..\"..`". 3973b015934Smillert * This is not done in posix mode (section 3983b015934Smillert * 3.2.3, Double Quotes: "The backquote shall 3993b015934Smillert * retain its special meaning introducing the 4003b015934Smillert * other form of command substitution (see 4013b015934Smillert * 3.6.3). The portion of the quoted string 4023b015934Smillert * from the initial backquote and the 4033b015934Smillert * characters up to the next backquote that 4043b015934Smillert * is not preceded by a backslash (having 4053b015934Smillert * escape characters removed) defines that 4063b015934Smillert * command whose output replaces `...` when 4073b015934Smillert * the word is expanded." 4083b015934Smillert * Section 3.6.3, Command Substitution: 4093b015934Smillert * "Within the backquoted style of command 4103b015934Smillert * substitution, backslash shall retain its 4113b015934Smillert * literal meaning, except when followed by 4123b015934Smillert * $ ` \."). 4137cb960a2Sdownsj */ 4143b015934Smillert statep->ls_sbquote.indquotes = 0; 4153b015934Smillert if (!Flag(FPOSIX)) { 4163b015934Smillert Lex_state *s = statep; 4173b015934Smillert Lex_state *base = state_info.base; 4183b015934Smillert while (1) { 4193b015934Smillert for (; s != base; s--) { 4203b015934Smillert if (s->ls_state == SDQUOTE) { 4213b015934Smillert statep->ls_sbquote.indquotes = 1; 4223b015934Smillert break; 4233b015934Smillert } 4243b015934Smillert } 4253b015934Smillert if (s != base) 4263b015934Smillert break; 4273b015934Smillert if (!(s = s->ls_info.base)) 4283b015934Smillert break; 4293b015934Smillert base = s-- - STATE_BSIZE; 4303b015934Smillert } 4313b015934Smillert } 4327cb960a2Sdownsj break; 4337cb960a2Sdownsj default: 4347cb960a2Sdownsj *wp++ = CHAR, *wp++ = c; 4357cb960a2Sdownsj } 4367cb960a2Sdownsj break; 4377cb960a2Sdownsj 4387cb960a2Sdownsj case SSQUOTE: 4397cb960a2Sdownsj if (c == '\'') { 4403b015934Smillert POP_STATE(); 44122a9cf94Sguenther if (state == SBRACEQ) { 44222a9cf94Sguenther *wp++ = CHAR, *wp++ = c; 44322a9cf94Sguenther break; 44422a9cf94Sguenther } 4457cb960a2Sdownsj *wp++ = CQUOTE; 446e55c1b2cSdownsj ignore_backslash_newline--; 4477cb960a2Sdownsj } else 4487cb960a2Sdownsj *wp++ = QCHAR, *wp++ = c; 4497cb960a2Sdownsj break; 4507cb960a2Sdownsj 4517cb960a2Sdownsj case SDQUOTE: 4527cb960a2Sdownsj if (c == '"') { 4533b015934Smillert POP_STATE(); 4547cb960a2Sdownsj *wp++ = CQUOTE; 4557cb960a2Sdownsj } else 4567cb960a2Sdownsj goto Subst; 4577cb960a2Sdownsj break; 4587cb960a2Sdownsj 4593b015934Smillert case SCSPAREN: /* $( .. ) */ 4607cb960a2Sdownsj /* todo: deal with $(...) quoting properly 4617cb960a2Sdownsj * kludge to partly fake quoting inside $(..): doesn't 4627cb960a2Sdownsj * really work because nested $(..) or ${..} inside 4637cb960a2Sdownsj * double quotes aren't dealt with. 4647cb960a2Sdownsj */ 4653b015934Smillert switch (statep->ls_scsparen.csstate) { 4667cb960a2Sdownsj case 0: /* normal */ 4677cb960a2Sdownsj switch (c) { 4687cb960a2Sdownsj case '(': 4693b015934Smillert statep->ls_scsparen.nparen++; 4707cb960a2Sdownsj break; 4717cb960a2Sdownsj case ')': 4723b015934Smillert statep->ls_scsparen.nparen--; 4737cb960a2Sdownsj break; 4747cb960a2Sdownsj case '\\': 4753b015934Smillert statep->ls_scsparen.csstate = 1; 4767cb960a2Sdownsj break; 4777cb960a2Sdownsj case '"': 4783b015934Smillert statep->ls_scsparen.csstate = 2; 4797cb960a2Sdownsj break; 4807cb960a2Sdownsj case '\'': 4813b015934Smillert statep->ls_scsparen.csstate = 4; 482e55c1b2cSdownsj ignore_backslash_newline++; 4837cb960a2Sdownsj break; 4847cb960a2Sdownsj } 4857cb960a2Sdownsj break; 4867cb960a2Sdownsj 4877cb960a2Sdownsj case 1: /* backslash in normal mode */ 4887cb960a2Sdownsj case 3: /* backslash in double quotes */ 4893b015934Smillert --statep->ls_scsparen.csstate; 4907cb960a2Sdownsj break; 4917cb960a2Sdownsj 4927cb960a2Sdownsj case 2: /* double quotes */ 4937cb960a2Sdownsj if (c == '"') 4943b015934Smillert statep->ls_scsparen.csstate = 0; 4957cb960a2Sdownsj else if (c == '\\') 4963b015934Smillert statep->ls_scsparen.csstate = 3; 4977cb960a2Sdownsj break; 4987cb960a2Sdownsj 4997cb960a2Sdownsj case 4: /* single quotes */ 500e55c1b2cSdownsj if (c == '\'') { 5013b015934Smillert statep->ls_scsparen.csstate = 0; 502e55c1b2cSdownsj ignore_backslash_newline--; 503e55c1b2cSdownsj } 5047cb960a2Sdownsj break; 5057cb960a2Sdownsj } 5063b015934Smillert if (statep->ls_scsparen.nparen == 0) { 5073b015934Smillert POP_STATE(); 5087cb960a2Sdownsj *wp++ = 0; /* end of COMSUB */ 5097cb960a2Sdownsj } else 5107cb960a2Sdownsj *wp++ = c; 5117cb960a2Sdownsj break; 5127cb960a2Sdownsj 5133b015934Smillert case SASPAREN: /* $(( .. )) */ 5147cb960a2Sdownsj /* todo: deal with $((...); (...)) properly */ 5157cb960a2Sdownsj /* XXX should nest using existing state machine 5167cb960a2Sdownsj * (embed "..", $(...), etc.) */ 5177cb960a2Sdownsj if (c == '(') 5183b015934Smillert statep->ls_sasparen.nparen++; 5197cb960a2Sdownsj else if (c == ')') { 5203b015934Smillert statep->ls_sasparen.nparen--; 5213b015934Smillert if (statep->ls_sasparen.nparen == 1) { 5227cb960a2Sdownsj /*(*/ 5237cb960a2Sdownsj if ((c2 = getsc()) == ')') { 5243b015934Smillert POP_STATE(); 5257cb960a2Sdownsj *wp++ = 0; /* end of EXPRSUB */ 5267cb960a2Sdownsj break; 5277cb960a2Sdownsj } else { 5283b015934Smillert char *s; 5293b015934Smillert 5307cb960a2Sdownsj ungetsc(c2); 5317cb960a2Sdownsj /* mismatched parenthesis - 5327cb960a2Sdownsj * assume we were really 5337cb960a2Sdownsj * parsing a $(..) expression 5347cb960a2Sdownsj */ 5353b015934Smillert s = Xrestpos(ws, wp, 5363b015934Smillert statep->ls_sasparen.start); 5373b015934Smillert memmove(s + 1, s, wp - s); 5383b015934Smillert *s++ = COMSUB; 5393b015934Smillert *s = '('; /*)*/ 5407cb960a2Sdownsj wp++; 5413b015934Smillert statep->ls_scsparen.nparen = 1; 5423b015934Smillert statep->ls_scsparen.csstate = 0; 5437a8124d8Sderaadt state = statep->ls_state = 5447a8124d8Sderaadt SCSPAREN; 5457cb960a2Sdownsj } 5467cb960a2Sdownsj } 5477cb960a2Sdownsj } 5487cb960a2Sdownsj *wp++ = c; 5497cb960a2Sdownsj break; 5507cb960a2Sdownsj 55122a9cf94Sguenther case SBRACEQ: 5527cb960a2Sdownsj case SBRACE: 5537cb960a2Sdownsj /*{*/ 5547cb960a2Sdownsj if (c == '}') { 5553b015934Smillert POP_STATE(); 5567cb960a2Sdownsj *wp++ = CSUBST; 5573b015934Smillert *wp++ = /*{*/ '}'; 5587cb960a2Sdownsj } else 5597cb960a2Sdownsj goto Sbase1; 5607cb960a2Sdownsj break; 5617cb960a2Sdownsj 5627cb960a2Sdownsj case STBRACE: 5633b015934Smillert /* Same as SBRACE, except (,|,) treated specially */ 5647cb960a2Sdownsj /*{*/ 5657cb960a2Sdownsj if (c == '}') { 5663b015934Smillert POP_STATE(); 5677cb960a2Sdownsj *wp++ = CSUBST; 5683b015934Smillert *wp++ = /*{*/ '}'; 5697cb960a2Sdownsj } else if (c == '|') { 5707cb960a2Sdownsj *wp++ = SPAT; 5713b015934Smillert } else if (c == '(') { 5723b015934Smillert *wp++ = OPAT; 5733b015934Smillert *wp++ = ' '; /* simile for @ */ 5743b015934Smillert PUSH_STATE(SPATTERN); 5757cb960a2Sdownsj } else 5767cb960a2Sdownsj goto Sbase1; 5777cb960a2Sdownsj break; 5787cb960a2Sdownsj 5797cb960a2Sdownsj case SBQUOTE: 5807cb960a2Sdownsj if (c == '`') { 5817cb960a2Sdownsj *wp++ = 0; 5823b015934Smillert POP_STATE(); 5837cb960a2Sdownsj } else if (c == '\\') { 5847cb960a2Sdownsj switch (c = getsc()) { 5857cb960a2Sdownsj case '\\': 5867cb960a2Sdownsj case '$': case '`': 5877cb960a2Sdownsj *wp++ = c; 5887cb960a2Sdownsj break; 5897cb960a2Sdownsj case '"': 5903b015934Smillert if (statep->ls_sbquote.indquotes) { 5917cb960a2Sdownsj *wp++ = c; 5927cb960a2Sdownsj break; 5937cb960a2Sdownsj } 5947eab881bSjaredy /* FALLTHROUGH */ 5957cb960a2Sdownsj default: 596e55c1b2cSdownsj if (c) { /* trailing \ is lost */ 5977cb960a2Sdownsj *wp++ = '\\'; 5987cb960a2Sdownsj *wp++ = c; 599e55c1b2cSdownsj } 6007cb960a2Sdownsj break; 6017cb960a2Sdownsj } 6027cb960a2Sdownsj } else 6037cb960a2Sdownsj *wp++ = c; 6047cb960a2Sdownsj break; 6057cb960a2Sdownsj 6067cb960a2Sdownsj case SWORD: /* ONEWORD */ 6077cb960a2Sdownsj goto Subst; 6087cb960a2Sdownsj 6093b015934Smillert case SLETPAREN: /* LETEXPR: (( ... )) */ 6107cb960a2Sdownsj /*(*/ 6117cb960a2Sdownsj if (c == ')') { 6123b015934Smillert if (statep->ls_sletparen.nparen > 0) 6133b015934Smillert --statep->ls_sletparen.nparen; 6147cb960a2Sdownsj /*(*/ 6157cb960a2Sdownsj else if ((c2 = getsc()) == ')') { 6167cb960a2Sdownsj c = 0; 6177cb960a2Sdownsj *wp++ = CQUOTE; 6187cb960a2Sdownsj goto Done; 6197cb960a2Sdownsj } else 6207cb960a2Sdownsj ungetsc(c2); 6217cb960a2Sdownsj } else if (c == '(') 6227cb960a2Sdownsj /* parenthesis inside quotes and backslashes 6237cb960a2Sdownsj * are lost, but at&t ksh doesn't count them 6247cb960a2Sdownsj * either 6257cb960a2Sdownsj */ 6263b015934Smillert ++statep->ls_sletparen.nparen; 6277cb960a2Sdownsj goto Sbase2; 6287cb960a2Sdownsj 6297cb960a2Sdownsj case SHEREDELIM: /* <<,<<- delimiter */ 6307cb960a2Sdownsj /* XXX chuck this state (and the next) - use 6317cb960a2Sdownsj * the existing states ($ and \`..` should be 6327cb960a2Sdownsj * stripped of their specialness after the 6337cb960a2Sdownsj * fact). 6347cb960a2Sdownsj */ 6357cb960a2Sdownsj /* here delimiters need a special case since 6367cb960a2Sdownsj * $ and `..` are not to be treated specially 6377cb960a2Sdownsj */ 6387cb960a2Sdownsj if (c == '\\') { 6397cb960a2Sdownsj c = getsc(); 640e55c1b2cSdownsj if (c) { /* trailing \ is lost */ 6417cb960a2Sdownsj *wp++ = QCHAR; 6427cb960a2Sdownsj *wp++ = c; 6437cb960a2Sdownsj } 6447cb960a2Sdownsj } else if (c == '\'') { 6453b015934Smillert PUSH_STATE(SSQUOTE); 6467cb960a2Sdownsj *wp++ = OQUOTE; 647e55c1b2cSdownsj ignore_backslash_newline++; 6487cb960a2Sdownsj } else if (c == '"') { 6493b015934Smillert state = statep->ls_state = SHEREDQUOTE; 6507cb960a2Sdownsj *wp++ = OQUOTE; 6517cb960a2Sdownsj } else { 6527cb960a2Sdownsj *wp++ = CHAR; 6537cb960a2Sdownsj *wp++ = c; 6547cb960a2Sdownsj } 6557cb960a2Sdownsj break; 6567cb960a2Sdownsj 6577cb960a2Sdownsj case SHEREDQUOTE: /* " in <<,<<- delimiter */ 6587cb960a2Sdownsj if (c == '"') { 6597cb960a2Sdownsj *wp++ = CQUOTE; 6603b015934Smillert state = statep->ls_state = SHEREDELIM; 6617cb960a2Sdownsj } else { 662e55c1b2cSdownsj if (c == '\\') { 663e55c1b2cSdownsj switch (c = getsc()) { 664e55c1b2cSdownsj case '\\': case '"': 665e55c1b2cSdownsj case '$': case '`': 6667cb960a2Sdownsj break; 667e55c1b2cSdownsj default: 668e55c1b2cSdownsj if (c) { /* trailing \ lost */ 669e55c1b2cSdownsj *wp++ = CHAR; 670e55c1b2cSdownsj *wp++ = '\\'; 671e55c1b2cSdownsj } 672e55c1b2cSdownsj break; 673e55c1b2cSdownsj } 674e55c1b2cSdownsj } 6757cb960a2Sdownsj *wp++ = CHAR; 6767cb960a2Sdownsj *wp++ = c; 6777cb960a2Sdownsj } 6787cb960a2Sdownsj break; 6797cb960a2Sdownsj 6807cb960a2Sdownsj case SPATTERN: /* in *(...|...) pattern (*+?@!) */ 6817cb960a2Sdownsj if ( /*(*/ c == ')') { 6827cb960a2Sdownsj *wp++ = CPAT; 6833b015934Smillert POP_STATE(); 6843b015934Smillert } else if (c == '|') { 6857cb960a2Sdownsj *wp++ = SPAT; 6863b015934Smillert } else if (c == '(') { 6873b015934Smillert *wp++ = OPAT; 6883b015934Smillert *wp++ = ' '; /* simile for @ */ 6893b015934Smillert PUSH_STATE(SPATTERN); 6903b015934Smillert } else 6917cb960a2Sdownsj goto Sbase1; 6927cb960a2Sdownsj break; 6937cb960a2Sdownsj } 6947cb960a2Sdownsj } 6957cb960a2Sdownsj Done: 6967cb960a2Sdownsj Xcheck(ws, wp); 6973b015934Smillert if (statep != &states[1]) 6983b015934Smillert /* XXX figure out what is missing */ 6997cb960a2Sdownsj yyerror("no closing quote\n"); 7007cb960a2Sdownsj 7017cb960a2Sdownsj /* This done to avoid tests for SHEREDELIM wherever SBASE tested */ 7027cb960a2Sdownsj if (state == SHEREDELIM) 7037cb960a2Sdownsj state = SBASE; 7047cb960a2Sdownsj 7053b015934Smillert dp = Xstring(ws, wp); 7067a8124d8Sderaadt if ((c == '<' || c == '>') && state == SBASE && 7077a8124d8Sderaadt ((c2 = Xlength(ws, wp)) == 0 || 7087a8124d8Sderaadt (c2 == 2 && dp[0] == CHAR && digit(dp[1])))) { 7098c046d24Snicm struct ioword *iop = alloc(sizeof(*iop), ATEMP); 7107cb960a2Sdownsj 7113b015934Smillert if (c2 == 2) 7123b015934Smillert iop->unit = dp[1] - '0'; 7137cb960a2Sdownsj else 7143b015934Smillert iop->unit = c == '>'; /* 0 for <, 1 for > */ 7157cb960a2Sdownsj 7167cb960a2Sdownsj c2 = getsc(); 7177cb960a2Sdownsj /* <<, >>, <> are ok, >< is not */ 7187cb960a2Sdownsj if (c == c2 || (c == '<' && c2 == '>')) { 7197cb960a2Sdownsj iop->flag = c == c2 ? 7207cb960a2Sdownsj (c == '>' ? IOCAT : IOHERE) : IORDWR; 72118bbba6bSmillert if (iop->flag == IOHERE) { 722e55c1b2cSdownsj if ((c2 = getsc()) == '-') 7237cb960a2Sdownsj iop->flag |= IOSKIP; 7247cb960a2Sdownsj else 7257cb960a2Sdownsj ungetsc(c2); 72618bbba6bSmillert } 7277cb960a2Sdownsj } else if (c2 == '&') 7287cb960a2Sdownsj iop->flag = IODUP | (c == '<' ? IORDUP : 0); 7297cb960a2Sdownsj else { 7307cb960a2Sdownsj iop->flag = c == '>' ? IOWRITE : IOREAD; 7317cb960a2Sdownsj if (c == '>' && c2 == '|') 7327cb960a2Sdownsj iop->flag |= IOCLOB; 7337cb960a2Sdownsj else 7347cb960a2Sdownsj ungetsc(c2); 7357cb960a2Sdownsj } 7367cb960a2Sdownsj 737355ffa75Stedu iop->name = NULL; 738355ffa75Stedu iop->delim = NULL; 739355ffa75Stedu iop->heredoc = NULL; 7403b015934Smillert Xfree(ws, wp); /* free word */ 7417cb960a2Sdownsj yylval.iop = iop; 7427cb960a2Sdownsj return REDIR; 7437cb960a2Sdownsj } 7443b015934Smillert 7453b015934Smillert if (wp == dp && state == SBASE) { 7463b015934Smillert Xfree(ws, wp); /* free word */ 7473b015934Smillert /* no word, process LEX1 character */ 7483b015934Smillert switch (c) { 7493b015934Smillert default: 7503b015934Smillert return c; 7513b015934Smillert 7523b015934Smillert case '|': 7533b015934Smillert case '&': 7543b015934Smillert case ';': 7553b015934Smillert if ((c2 = getsc()) == c) 7563b015934Smillert c = (c == ';') ? BREAK : 7573b015934Smillert (c == '|') ? LOGOR : 7583b015934Smillert (c == '&') ? LOGAND : 7593b015934Smillert YYERRCODE; 7603b015934Smillert else if (c == '|' && c2 == '&') 7613b015934Smillert c = COPROC; 7623b015934Smillert else 7633b015934Smillert ungetsc(c2); 7643b015934Smillert return c; 7653b015934Smillert 7667cb960a2Sdownsj case '\n': 7677cb960a2Sdownsj gethere(); 7687cb960a2Sdownsj if (cf & CONTIN) 7697cb960a2Sdownsj goto Again; 7707cb960a2Sdownsj return c; 7717cb960a2Sdownsj 7727cb960a2Sdownsj case '(': /*)*/ 773b69beb66Sgrr if (!Flag(FSH)) { 7747cb960a2Sdownsj if ((c2 = getsc()) == '(') /*)*/ 7753b015934Smillert /* XXX need to handle ((...); (...)) */ 7767cb960a2Sdownsj c = MDPAREN; 7777cb960a2Sdownsj else 7787cb960a2Sdownsj ungetsc(c2); 779b69beb66Sgrr } 7807cb960a2Sdownsj return c; 7817cb960a2Sdownsj /*(*/ 7827cb960a2Sdownsj case ')': 7837cb960a2Sdownsj return c; 7847cb960a2Sdownsj } 7857cb960a2Sdownsj } 7867cb960a2Sdownsj 7877cb960a2Sdownsj *wp++ = EOS; /* terminate word */ 7887cb960a2Sdownsj yylval.cp = Xclose(ws, wp); 78994e42df6Smillert if (state == SWORD || state == SLETPAREN) /* ONEWORD? */ 7907cb960a2Sdownsj return LWORD; 7917cb960a2Sdownsj ungetsc(c); /* unget terminator */ 7927cb960a2Sdownsj 7937cb960a2Sdownsj /* copy word to unprefixed string ident */ 7947cb960a2Sdownsj for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; ) 7957cb960a2Sdownsj *dp++ = *sp++; 79612e7fb2dSjmc /* Make sure the ident array stays '\0' padded */ 7977cb960a2Sdownsj memset(dp, 0, (ident+IDENT) - dp + 1); 7987cb960a2Sdownsj if (c != EOS) 7997cb960a2Sdownsj *ident = '\0'; /* word is not unquoted */ 8007cb960a2Sdownsj 8017cb960a2Sdownsj if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) { 8027cb960a2Sdownsj struct tbl *p; 8037cb960a2Sdownsj int h = hash(ident); 8047cb960a2Sdownsj 8057cb960a2Sdownsj /* { */ 8066df3ee40Sotto if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) && 8077a8124d8Sderaadt (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) { 8087cb960a2Sdownsj afree(yylval.cp, ATEMP); 8097cb960a2Sdownsj return p->val.i; 8107cb960a2Sdownsj } 8116df3ee40Sotto if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) && 8127a8124d8Sderaadt (p->flag & ISSET)) { 8137894b443Smillert Source *s; 8147cb960a2Sdownsj 8157cb960a2Sdownsj for (s = source; s->type == SALIAS; s = s->next) 8167cb960a2Sdownsj if (s->u.tblp == p) 8177cb960a2Sdownsj return LWORD; 8187cb960a2Sdownsj /* push alias expansion */ 8197cb960a2Sdownsj s = pushs(SALIAS, source->areap); 8207cb960a2Sdownsj s->start = s->str = p->val.s; 8217cb960a2Sdownsj s->u.tblp = p; 8227cb960a2Sdownsj s->next = source; 8237cb960a2Sdownsj source = s; 8247cb960a2Sdownsj afree(yylval.cp, ATEMP); 8257cb960a2Sdownsj goto Again; 8267cb960a2Sdownsj } 8277cb960a2Sdownsj } 8287cb960a2Sdownsj 8297cb960a2Sdownsj return LWORD; 8307cb960a2Sdownsj } 8317cb960a2Sdownsj 8327cb960a2Sdownsj static void 833c5d5393cSotto gethere(void) 8347cb960a2Sdownsj { 8357894b443Smillert struct ioword **p; 8367cb960a2Sdownsj 8377cb960a2Sdownsj for (p = heres; p < herep; p++) 8387cb960a2Sdownsj readhere(*p); 8397cb960a2Sdownsj herep = heres; 8407cb960a2Sdownsj } 8417cb960a2Sdownsj 8427cb960a2Sdownsj /* 8437cb960a2Sdownsj * read "<<word" text into temp file 8447cb960a2Sdownsj */ 8457cb960a2Sdownsj 8467cb960a2Sdownsj static void 847c5d5393cSotto readhere(struct ioword *iop) 8487cb960a2Sdownsj { 8497894b443Smillert int c; 8507cb960a2Sdownsj char *volatile eof; 8517cb960a2Sdownsj char *eofp; 852e55c1b2cSdownsj int skiptabs; 853f00c5086Smillert XString xs; 854f00c5086Smillert char *xp; 855f00c5086Smillert int xpos; 8567cb960a2Sdownsj 8577cb960a2Sdownsj eof = evalstr(iop->delim, 0); 8587cb960a2Sdownsj 859e55c1b2cSdownsj if (!(iop->flag & IOEVAL)) 860e55c1b2cSdownsj ignore_backslash_newline++; 861e55c1b2cSdownsj 862f00c5086Smillert Xinit(xs, xp, 256, ATEMP); 863f00c5086Smillert 8647cb960a2Sdownsj for (;;) { 8657cb960a2Sdownsj eofp = eof; 8667cb960a2Sdownsj skiptabs = iop->flag & IOSKIP; 867f00c5086Smillert xpos = Xsavepos(xs, xp); 868e55c1b2cSdownsj while ((c = getsc()) != 0) { 8697cb960a2Sdownsj if (skiptabs) { 8707cb960a2Sdownsj if (c == '\t') 8717cb960a2Sdownsj continue; 8727cb960a2Sdownsj skiptabs = 0; 8737cb960a2Sdownsj } 8747cb960a2Sdownsj if (c != *eofp) 8757cb960a2Sdownsj break; 876f00c5086Smillert Xcheck(xs, xp); 877f00c5086Smillert Xput(xs, xp, c); 8787cb960a2Sdownsj eofp++; 8797cb960a2Sdownsj } 8807cb960a2Sdownsj /* Allow EOF here so commands with out trailing newlines 8817cb960a2Sdownsj * will work (eg, ksh -c '...', $(...), etc). 8827cb960a2Sdownsj */ 883f00c5086Smillert if (*eofp == '\0' && (c == 0 || c == '\n')) { 884f00c5086Smillert xp = Xrestpos(xs, xp, xpos); 8857cb960a2Sdownsj break; 886f00c5086Smillert } 8877cb960a2Sdownsj ungetsc(c); 888e55c1b2cSdownsj while ((c = getsc()) != '\n') { 8897cb960a2Sdownsj if (c == 0) 8907cb960a2Sdownsj yyerror("here document `%s' unclosed\n", eof); 891f00c5086Smillert Xcheck(xs, xp); 892f00c5086Smillert Xput(xs, xp, c); 8937cb960a2Sdownsj } 894f00c5086Smillert Xcheck(xs, xp); 895f00c5086Smillert Xput(xs, xp, c); 8967cb960a2Sdownsj } 897f00c5086Smillert Xput(xs, xp, '\0'); 898f00c5086Smillert iop->heredoc = Xclose(xs, xp); 899f00c5086Smillert 900e55c1b2cSdownsj if (!(iop->flag & IOEVAL)) 901e55c1b2cSdownsj ignore_backslash_newline--; 9027cb960a2Sdownsj } 9037cb960a2Sdownsj 9047cb960a2Sdownsj void 9057cb960a2Sdownsj yyerror(const char *fmt, ...) 9067cb960a2Sdownsj { 9077cb960a2Sdownsj va_list va; 9087cb960a2Sdownsj 9097cb960a2Sdownsj /* pop aliases and re-reads */ 9107cb960a2Sdownsj while (source->type == SALIAS || source->type == SREREAD) 9117cb960a2Sdownsj source = source->next; 9127cb960a2Sdownsj source->str = null; /* zap pending input */ 9137cb960a2Sdownsj 9140e7d3a01Smillert error_prefix(true); 91569b9f96bSmillert va_start(va, fmt); 9167cb960a2Sdownsj shf_vfprintf(shl_out, fmt, va); 9177cb960a2Sdownsj va_end(va); 91863ca93eaSmillert errorf(NULL); 9197cb960a2Sdownsj } 9207cb960a2Sdownsj 9217cb960a2Sdownsj /* 9227cb960a2Sdownsj * input for yylex with alias expansion 9237cb960a2Sdownsj */ 9247cb960a2Sdownsj 9257cb960a2Sdownsj Source * 926c5d5393cSotto pushs(int type, Area *areap) 9277cb960a2Sdownsj { 9287894b443Smillert Source *s; 9297cb960a2Sdownsj 9308c046d24Snicm s = alloc(sizeof(Source), areap); 9317cb960a2Sdownsj s->type = type; 9327cb960a2Sdownsj s->str = null; 9337cb960a2Sdownsj s->start = NULL; 9347cb960a2Sdownsj s->line = 0; 935e7b1290aSotto s->cmd_offset = 0; 9367cb960a2Sdownsj s->errline = 0; 9377cb960a2Sdownsj s->file = NULL; 9387cb960a2Sdownsj s->flags = 0; 9397cb960a2Sdownsj s->next = NULL; 9407cb960a2Sdownsj s->areap = areap; 9417cb960a2Sdownsj if (type == SFILE || type == SSTDIN) { 9427cb960a2Sdownsj char *dummy; 9437cb960a2Sdownsj Xinit(s->xs, dummy, 256, s->areap); 9447cb960a2Sdownsj } else 9457cb960a2Sdownsj memset(&s->xs, 0, sizeof(s->xs)); 9467cb960a2Sdownsj return s; 9477cb960a2Sdownsj } 9487cb960a2Sdownsj 9497cb960a2Sdownsj static int 950c5d5393cSotto getsc__(void) 9517cb960a2Sdownsj { 9527894b443Smillert Source *s = source; 9537894b443Smillert int c; 9547cb960a2Sdownsj 9557cb960a2Sdownsj while ((c = *s->str++) == 0) { 9567cb960a2Sdownsj s->str = NULL; /* return 0 for EOF by default */ 9577cb960a2Sdownsj switch (s->type) { 9587cb960a2Sdownsj case SEOF: 9597cb960a2Sdownsj s->str = null; 9607cb960a2Sdownsj return 0; 9617cb960a2Sdownsj 9627cb960a2Sdownsj case SSTDIN: 9637cb960a2Sdownsj case SFILE: 9647cb960a2Sdownsj getsc_line(s); 9657cb960a2Sdownsj break; 9667cb960a2Sdownsj 9677cb960a2Sdownsj case SWSTR: 9687cb960a2Sdownsj break; 9697cb960a2Sdownsj 9707cb960a2Sdownsj case SSTRING: 9717cb960a2Sdownsj break; 9727cb960a2Sdownsj 9737cb960a2Sdownsj case SWORDS: 9747cb960a2Sdownsj s->start = s->str = *s->u.strv++; 9757cb960a2Sdownsj s->type = SWORDSEP; 9767cb960a2Sdownsj break; 9777cb960a2Sdownsj 9787cb960a2Sdownsj case SWORDSEP: 9797cb960a2Sdownsj if (*s->u.strv == NULL) { 98096085982Snicm s->start = s->str = "\n"; 9817cb960a2Sdownsj s->type = SEOF; 9827cb960a2Sdownsj } else { 98396085982Snicm s->start = s->str = " "; 9847cb960a2Sdownsj s->type = SWORDS; 9857cb960a2Sdownsj } 9867cb960a2Sdownsj break; 9877cb960a2Sdownsj 9887cb960a2Sdownsj case SALIAS: 9897cb960a2Sdownsj if (s->flags & SF_ALIASEND) { 9907cb960a2Sdownsj /* pass on an unused SF_ALIAS flag */ 9917cb960a2Sdownsj source = s->next; 9927cb960a2Sdownsj source->flags |= s->flags & SF_ALIAS; 9937cb960a2Sdownsj s = source; 9947a8124d8Sderaadt } else if (*s->u.tblp->val.s && 995e569fc7cSderaadt isspace((unsigned char)strchr(s->u.tblp->val.s, 0)[-1])) { 9967cb960a2Sdownsj source = s = s->next; /* pop source stack */ 997e55c1b2cSdownsj /* Note that this alias ended with a space, 998e55c1b2cSdownsj * enabling alias expansion on the following 999e55c1b2cSdownsj * word. 1000e55c1b2cSdownsj */ 10017cb960a2Sdownsj s->flags |= SF_ALIAS; 10027cb960a2Sdownsj } else { 1003e55c1b2cSdownsj /* At this point, we need to keep the current 1004e55c1b2cSdownsj * alias in the source list so recursive 1005e55c1b2cSdownsj * aliases can be detected and we also need 1006e55c1b2cSdownsj * to return the next character. Do this 1007e55c1b2cSdownsj * by temporarily popping the alias to get 1008e55c1b2cSdownsj * the next character and then put it back 1009e55c1b2cSdownsj * in the source list with the SF_ALIASEND 1010e55c1b2cSdownsj * flag set. 10117cb960a2Sdownsj */ 1012e55c1b2cSdownsj source = s->next; /* pop source stack */ 1013e55c1b2cSdownsj source->flags |= s->flags & SF_ALIAS; 1014e55c1b2cSdownsj c = getsc__(); 1015e55c1b2cSdownsj if (c) { 10167cb960a2Sdownsj s->flags |= SF_ALIASEND; 1017e55c1b2cSdownsj s->ugbuf[0] = c; s->ugbuf[1] = '\0'; 1018e55c1b2cSdownsj s->start = s->str = s->ugbuf; 1019e55c1b2cSdownsj s->next = source; 1020e55c1b2cSdownsj source = s; 1021e55c1b2cSdownsj } else { 1022e55c1b2cSdownsj s = source; 1023e55c1b2cSdownsj /* avoid reading eof twice */ 1024e55c1b2cSdownsj s->str = NULL; 1025e7bc3c65Sdownsj break; 1026e55c1b2cSdownsj } 10277cb960a2Sdownsj } 10287cb960a2Sdownsj continue; 10297cb960a2Sdownsj 10307cb960a2Sdownsj case SREREAD: 1031e55c1b2cSdownsj if (s->start != s->ugbuf) /* yuck */ 10327cb960a2Sdownsj afree(s->u.freeme, ATEMP); 10337cb960a2Sdownsj source = s = s->next; 10347cb960a2Sdownsj continue; 10357cb960a2Sdownsj } 10367cb960a2Sdownsj if (s->str == NULL) { 10377cb960a2Sdownsj s->type = SEOF; 10387cb960a2Sdownsj s->start = s->str = null; 10397cb960a2Sdownsj return '\0'; 10407cb960a2Sdownsj } 10417cb960a2Sdownsj if (s->flags & SF_ECHO) { 10427cb960a2Sdownsj shf_puts(s->str, shl_out); 10437cb960a2Sdownsj shf_flush(shl_out); 10447cb960a2Sdownsj } 10457cb960a2Sdownsj } 10467cb960a2Sdownsj return c; 10477cb960a2Sdownsj } 10487cb960a2Sdownsj 10497cb960a2Sdownsj static void 1050c5d5393cSotto getsc_line(Source *s) 10517cb960a2Sdownsj { 10527cb960a2Sdownsj char *xp = Xstring(s->xs, xp); 10537cb960a2Sdownsj int interactive = Flag(FTALKING) && s->type == SSTDIN; 10547cb960a2Sdownsj int have_tty = interactive && (s->flags & SF_TTY); 10557cb960a2Sdownsj 10567cb960a2Sdownsj /* Done here to ensure nothing odd happens when a timeout occurs */ 10577cb960a2Sdownsj XcheckN(s->xs, xp, LINE); 10587cb960a2Sdownsj *xp = '\0'; 10597cb960a2Sdownsj s->start = s->str = xp; 10607cb960a2Sdownsj 10617cb960a2Sdownsj if (have_tty && ksh_tmout) { 10627cb960a2Sdownsj ksh_tmout_state = TMOUT_READING; 10637cb960a2Sdownsj alarm(ksh_tmout); 10647cb960a2Sdownsj } 10657cb960a2Sdownsj #ifdef EDIT 10667cb960a2Sdownsj if (have_tty && (0 10677cb960a2Sdownsj # ifdef VI 10687cb960a2Sdownsj || Flag(FVI) 10697cb960a2Sdownsj # endif /* VI */ 10707cb960a2Sdownsj # ifdef EMACS 10717cb960a2Sdownsj || Flag(FEMACS) || Flag(FGMACS) 10727cb960a2Sdownsj # endif /* EMACS */ 10737a8124d8Sderaadt )) { 10747cb960a2Sdownsj int nread; 10757cb960a2Sdownsj 10767cb960a2Sdownsj nread = x_read(xp, LINE); 10777cb960a2Sdownsj if (nread < 0) /* read error */ 10787cb960a2Sdownsj nread = 0; 10797cb960a2Sdownsj xp[nread] = '\0'; 10807cb960a2Sdownsj xp += nread; 10817cb960a2Sdownsj } 10827cb960a2Sdownsj else 10837cb960a2Sdownsj #endif /* EDIT */ 10847cb960a2Sdownsj { 10857cb960a2Sdownsj if (interactive) { 10867cb960a2Sdownsj pprompt(prompt, 0); 10877cb960a2Sdownsj } else 10887cb960a2Sdownsj s->line++; 10897cb960a2Sdownsj 10907cb960a2Sdownsj while (1) { 10917cb960a2Sdownsj char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf); 10927cb960a2Sdownsj 10937a8124d8Sderaadt if (!p && shf_error(s->u.shf) && 10947a8124d8Sderaadt shf_errno(s->u.shf) == EINTR) { 10957cb960a2Sdownsj shf_clearerr(s->u.shf); 10967cb960a2Sdownsj if (trap) 10977cb960a2Sdownsj runtraps(0); 10987cb960a2Sdownsj continue; 10997cb960a2Sdownsj } 11007cb960a2Sdownsj if (!p || (xp = p, xp[-1] == '\n')) 11017cb960a2Sdownsj break; 11027cb960a2Sdownsj /* double buffer size */ 11037cb960a2Sdownsj xp++; /* move past null so doubling works... */ 11047cb960a2Sdownsj XcheckN(s->xs, xp, Xlength(s->xs, xp)); 11057cb960a2Sdownsj xp--; /* ...and move back again */ 11067cb960a2Sdownsj } 11077cb960a2Sdownsj /* flush any unwanted input so other programs/builtins 11087cb960a2Sdownsj * can read it. Not very optimal, but less error prone 11097cb960a2Sdownsj * than flushing else where, dealing with redirections, 11107cb960a2Sdownsj * etc.. 11117cb960a2Sdownsj * todo: reduce size of shf buffer (~128?) if SSTDIN 11127cb960a2Sdownsj */ 11137cb960a2Sdownsj if (s->type == SSTDIN) 11147cb960a2Sdownsj shf_flush(s->u.shf); 11157cb960a2Sdownsj } 11167cb960a2Sdownsj /* XXX: temporary kludge to restore source after a 11177cb960a2Sdownsj * trap may have been executed. 11187cb960a2Sdownsj */ 11197cb960a2Sdownsj source = s; 112094e42df6Smillert if (have_tty && ksh_tmout) { 11217cb960a2Sdownsj ksh_tmout_state = TMOUT_EXECUTING; 11227cb960a2Sdownsj alarm(0); 11237cb960a2Sdownsj } 11247cb960a2Sdownsj s->start = s->str = Xstring(s->xs, xp); 11257cb960a2Sdownsj strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp)); 11267cb960a2Sdownsj /* Note: if input is all nulls, this is not eof */ 11277cb960a2Sdownsj if (Xlength(s->xs, xp) == 0) { /* EOF */ 11287cb960a2Sdownsj if (s->type == SFILE) 11297cb960a2Sdownsj shf_fdclose(s->u.shf); 11307cb960a2Sdownsj s->str = NULL; 11317cb960a2Sdownsj } else if (interactive) { 11327cb960a2Sdownsj #ifdef HISTORY 11337cb960a2Sdownsj char *p = Xstring(s->xs, xp); 11347cb960a2Sdownsj if (cur_prompt == PS1) 11357cb960a2Sdownsj while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS)) 11367cb960a2Sdownsj p++; 11377cb960a2Sdownsj if (*p) { 11387cb960a2Sdownsj s->line++; 11397cb960a2Sdownsj histsave(s->line, s->str, 1); 11407cb960a2Sdownsj } 11417cb960a2Sdownsj #endif /* HISTORY */ 11427cb960a2Sdownsj } 11437cb960a2Sdownsj if (interactive) 1144f7654a50Snicm set_prompt(PS2, NULL); 11457cb960a2Sdownsj } 11467cb960a2Sdownsj 1147e7b1290aSotto static char * 1148e7b1290aSotto special_prompt_expand(char *str) 1149e7b1290aSotto { 1150e7b1290aSotto char *p = str; 1151e7b1290aSotto 1152e7b1290aSotto while ((p = strstr(p, "\\$")) != NULL) { 11536aa3ef4bSdrahn *(p+1) = 'p'; 1154e7b1290aSotto } 1155e7b1290aSotto return str; 1156e7b1290aSotto } 1157e7b1290aSotto 11587cb960a2Sdownsj void 1159c5d5393cSotto set_prompt(int to, Source *s) 11607cb960a2Sdownsj { 11614300ac9bSmillert char *ps1; 11627cb960a2Sdownsj Area *saved_atemp; 1163fc804817Smillert 116494e42df6Smillert cur_prompt = to; 116594e42df6Smillert 116694e42df6Smillert switch (to) { 116794e42df6Smillert case PS1: /* command */ 11684300ac9bSmillert ps1 = str_save(str_val(global("PS1")), ATEMP); 11694300ac9bSmillert saved_atemp = ATEMP; /* ps1 is freed by substitute() */ 11707cb960a2Sdownsj newenv(E_ERRH); 117169b9f96bSmillert if (sigsetjmp(e->jbuf, 0)) { 11727cb960a2Sdownsj prompt = safe_prompt; 1173dcacb757Sdownsj /* Don't print an error - assume it has already 1174dcacb757Sdownsj * been printed. Reason is we may have forked 1175dcacb757Sdownsj * to run a command and the child may be 1176dcacb757Sdownsj * unwinding its stack through this code as it 1177dcacb757Sdownsj * exits. 1178dcacb757Sdownsj */ 1179e7b1290aSotto } else { 1180e7b1290aSotto /* expand \$ before other substitutions are done */ 1181e7b1290aSotto char *tmp = special_prompt_expand(ps1); 1182e7b1290aSotto prompt = str_save(substitute(tmp, 0), saved_atemp); 1183e7b1290aSotto } 11840ee3f80eSotto quitenv(NULL); 11857cb960a2Sdownsj break; 11867cb960a2Sdownsj case PS2: /* command continuation */ 11877cb960a2Sdownsj prompt = str_val(global("PS2")); 11887cb960a2Sdownsj break; 11897cb960a2Sdownsj } 11907cb960a2Sdownsj } 11917cb960a2Sdownsj 1192eb6bc482Sderaadt static int 1193c5d5393cSotto dopprompt(const char *sp, int ntruncate, const char **spp, int doprint) 11947cb960a2Sdownsj { 1195eb6bc482Sderaadt char strbuf[1024], tmpbuf[1024], *p, *str, nbuf[32], delimiter = '\0'; 1196a1344090Sderaadt int len, c, n, totlen = 0, indelimit = 0, counting = 1, delimitthis; 119741c26999Sotto const char *cp = sp; 1198eb6bc482Sderaadt struct tm *tm; 1199eb6bc482Sderaadt time_t t; 1200eb6bc482Sderaadt 1201eb6bc482Sderaadt if (*cp && cp[1] == '\r') { 1202eb6bc482Sderaadt delimiter = *cp; 1203eb6bc482Sderaadt cp += 2; 1204eb6bc482Sderaadt } 12057cb960a2Sdownsj 12067cb960a2Sdownsj while (*cp != 0) { 1207a1344090Sderaadt delimitthis = 0; 1208eb6bc482Sderaadt if (indelimit && *cp != delimiter) 1209eb6bc482Sderaadt ; 1210eb6bc482Sderaadt else if (*cp == '\n' || *cp == '\r') { 1211eb6bc482Sderaadt totlen = 0; 1212eb6bc482Sderaadt sp = cp + 1; 1213a1344090Sderaadt } else if (*cp == '\t') { 1214a1344090Sderaadt if (counting) 1215eb6bc482Sderaadt totlen = (totlen | 7) + 1; 1216a1344090Sderaadt } else if (*cp == delimiter) { 1217eb6bc482Sderaadt indelimit = !indelimit; 1218a1344090Sderaadt delimitthis = 1; 1219a1344090Sderaadt } 1220eb6bc482Sderaadt 1221eb6bc482Sderaadt if (*cp == '\\') { 1222eb6bc482Sderaadt cp++; 1223eb6bc482Sderaadt if (!*cp) 1224eb6bc482Sderaadt break; 1225eb6bc482Sderaadt if (Flag(FSH)) 1226eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "\\%c", *cp); 1227eb6bc482Sderaadt else switch (*cp) { 1228eb6bc482Sderaadt case 'a': /* '\' 'a' bell */ 1229eb6bc482Sderaadt strbuf[0] = '\007'; 1230eb6bc482Sderaadt strbuf[1] = '\0'; 1231eb6bc482Sderaadt break; 1232eb6bc482Sderaadt case 'd': /* '\' 'd' Dow Mon DD */ 1233eb6bc482Sderaadt time(&t); 1234eb6bc482Sderaadt tm = localtime(&t); 1235eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%a %b %d", tm); 1236eb6bc482Sderaadt break; 1237eb6bc482Sderaadt case 'D': /* '\' 'D' '{' strftime format '}' */ 1238eb6bc482Sderaadt p = strchr(cp + 2, '}'); 1239eb6bc482Sderaadt if (cp[1] != '{' || p == NULL) { 1240eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, 1241eb6bc482Sderaadt "\\%c", *cp); 1242eb6bc482Sderaadt break; 1243eb6bc482Sderaadt } 1244eb6bc482Sderaadt strlcpy(tmpbuf, cp + 2, sizeof tmpbuf); 1245eb6bc482Sderaadt p = strchr(tmpbuf, '}'); 1246eb6bc482Sderaadt if (p) 1247eb6bc482Sderaadt *p = '\0'; 1248eb6bc482Sderaadt time(&t); 1249eb6bc482Sderaadt tm = localtime(&t); 1250eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, tmpbuf, tm); 1251eb6bc482Sderaadt cp = strchr(cp + 2, '}'); 1252eb6bc482Sderaadt break; 1253eb6bc482Sderaadt case 'e': /* '\' 'e' escape */ 1254eb6bc482Sderaadt strbuf[0] = '\033'; 1255eb6bc482Sderaadt strbuf[1] = '\0'; 1256eb6bc482Sderaadt break; 1257eb6bc482Sderaadt case 'h': /* '\' 'h' shortened hostname */ 1258eb6bc482Sderaadt gethostname(strbuf, sizeof strbuf); 1259eb6bc482Sderaadt p = strchr(strbuf, '.'); 1260eb6bc482Sderaadt if (p) 1261eb6bc482Sderaadt *p = '\0'; 1262eb6bc482Sderaadt break; 1263eb6bc482Sderaadt case 'H': /* '\' 'H' full hostname */ 1264eb6bc482Sderaadt gethostname(strbuf, sizeof strbuf); 1265eb6bc482Sderaadt break; 1266eb6bc482Sderaadt case 'j': /* '\' 'j' number of jobs */ 1267eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%d", 1268eb6bc482Sderaadt j_njobs()); 1269eb6bc482Sderaadt break; 1270eb6bc482Sderaadt case 'l': /* '\' 'l' basename of tty */ 1271eb6bc482Sderaadt p = ttyname(0); 1272eb6bc482Sderaadt if (p) 1273eb6bc482Sderaadt p = basename(p); 1274eb6bc482Sderaadt if (p) 1275eb6bc482Sderaadt strlcpy(strbuf, p, sizeof strbuf); 1276eb6bc482Sderaadt break; 1277eb6bc482Sderaadt case 'n': /* '\' 'n' newline */ 1278eb6bc482Sderaadt strbuf[0] = '\n'; 1279eb6bc482Sderaadt strbuf[1] = '\0'; 12805c4a5744Sderaadt totlen = 0; /* reset for prompt re-print */ 12815c4a5744Sderaadt sp = cp + 1; 1282eb6bc482Sderaadt break; 12836aa3ef4bSdrahn case 'p': /* '\' '$' $ or # */ 12846aa3ef4bSdrahn strbuf[0] = ksheuid ? '$' : '#'; 12856aa3ef4bSdrahn strbuf[1] = '\0'; 12866aa3ef4bSdrahn break; 1287eb6bc482Sderaadt case 'r': /* '\' 'r' return */ 1288eb6bc482Sderaadt strbuf[0] = '\r'; 1289eb6bc482Sderaadt strbuf[1] = '\0'; 12905c4a5744Sderaadt totlen = 0; /* reset for prompt re-print */ 12915c4a5744Sderaadt sp = cp + 1; 1292eb6bc482Sderaadt break; 1293eb6bc482Sderaadt case 's': /* '\' 's' basename $0 */ 1294eb6bc482Sderaadt strlcpy(strbuf, kshname, sizeof strbuf); 1295eb6bc482Sderaadt break; 1296eb6bc482Sderaadt case 't': /* '\' 't' 24 hour HH:MM:SS */ 1297eb6bc482Sderaadt time(&t); 1298eb6bc482Sderaadt tm = localtime(&t); 1299eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%T", tm); 1300eb6bc482Sderaadt break; 1301eb6bc482Sderaadt case 'T': /* '\' 'T' 12 hour HH:MM:SS */ 1302eb6bc482Sderaadt time(&t); 1303eb6bc482Sderaadt tm = localtime(&t); 1304eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%l:%M:%S", tm); 1305eb6bc482Sderaadt break; 1306eb6bc482Sderaadt case '@': /* '\' '@' 12 hour am/pm format */ 1307eb6bc482Sderaadt time(&t); 1308eb6bc482Sderaadt tm = localtime(&t); 1309eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%r", tm); 1310eb6bc482Sderaadt break; 1311eb6bc482Sderaadt case 'A': /* '\' 'A' 24 hour HH:MM */ 1312eb6bc482Sderaadt time(&t); 1313eb6bc482Sderaadt tm = localtime(&t); 1314eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%R", tm); 1315eb6bc482Sderaadt break; 1316eb6bc482Sderaadt case 'u': /* '\' 'u' username */ 1317f351fb1cSotto strlcpy(strbuf, username, sizeof strbuf); 1318eb6bc482Sderaadt break; 1319eb6bc482Sderaadt case 'v': /* '\' 'v' version (short) */ 1320eb6bc482Sderaadt p = strchr(ksh_version, ' '); 1321eb6bc482Sderaadt if (p) 1322eb6bc482Sderaadt p = strchr(p + 1, ' '); 1323eb6bc482Sderaadt if (p) { 1324eb6bc482Sderaadt p++; 1325eb6bc482Sderaadt strlcpy(strbuf, p, sizeof strbuf); 1326eb6bc482Sderaadt p = strchr(strbuf, ' '); 1327eb6bc482Sderaadt if (p) 1328eb6bc482Sderaadt *p = '\0'; 1329eb6bc482Sderaadt } 1330eb6bc482Sderaadt break; 1331eb6bc482Sderaadt case 'V': /* '\' 'V' version (long) */ 1332eb6bc482Sderaadt strlcpy(strbuf, ksh_version, sizeof strbuf); 1333eb6bc482Sderaadt break; 1334eb6bc482Sderaadt case 'w': /* '\' 'w' cwd */ 1335eb6bc482Sderaadt p = str_val(global("PWD")); 1336ad55f791Sderaadt n = strlen(str_val(global("HOME"))); 1337ad55f791Sderaadt if (strcmp(p, "/") == 0) { 1338ad55f791Sderaadt strlcpy(strbuf, p, sizeof strbuf); 1339ad55f791Sderaadt } else if (strcmp(p, str_val(global("HOME"))) == 0) { 1340eb6bc482Sderaadt strbuf[0] = '~'; 1341eb6bc482Sderaadt strbuf[1] = '\0'; 1342ad55f791Sderaadt } else if (strncmp(p, str_val(global("HOME")), n) 1343ad55f791Sderaadt == 0 && p[n] == '/') { 1344ad55f791Sderaadt snprintf(strbuf, sizeof strbuf, "~/%s", 1345ad55f791Sderaadt str_val(global("PWD")) + n + 1); 1346eb6bc482Sderaadt } else 1347eb6bc482Sderaadt strlcpy(strbuf, p, sizeof strbuf); 1348eb6bc482Sderaadt break; 1349eb6bc482Sderaadt case 'W': /* '\' 'W' basename(cwd) */ 1350eb6bc482Sderaadt p = str_val(global("PWD")); 135130e9b9efSokan if (strcmp(p, str_val(global("HOME"))) == 0) { 135230e9b9efSokan strbuf[0] = '~'; 135330e9b9efSokan strbuf[1] = '\0'; 135430e9b9efSokan } else 1355eb6bc482Sderaadt strlcpy(strbuf, basename(p), sizeof strbuf); 1356eb6bc482Sderaadt break; 1357e7b1290aSotto case '!': /* '\' '!' history line number */ 1358eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%d", 1359eb6bc482Sderaadt source->line + 1); 1360eb6bc482Sderaadt break; 1361e7b1290aSotto case '#': /* '\' '#' command line number */ 1362eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%d", 1363e7b1290aSotto source->line - source->cmd_offset + 1); 1364eb6bc482Sderaadt break; 1365eb6bc482Sderaadt case '0': /* '\' '#' '#' ' #' octal numeric handling */ 1366eb6bc482Sderaadt case '1': 1367eb6bc482Sderaadt case '2': 1368eb6bc482Sderaadt case '3': 1369eb6bc482Sderaadt case '4': 1370eb6bc482Sderaadt case '5': 1371eb6bc482Sderaadt case '6': 1372eb6bc482Sderaadt case '7': 1373eb6bc482Sderaadt if ((cp[1] > '7' || cp[1] < '0') || 1374eb6bc482Sderaadt (cp[2] > '7' || cp[2] < '0')) { 1375eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, 1376eb6bc482Sderaadt "\\%c", *cp); 1377eb6bc482Sderaadt break; 1378eb6bc482Sderaadt } 1379064356ecSzhuk n = (cp[0] - '0') * 8 * 8 + (cp[1] - '0') * 8 + 1380064356ecSzhuk (cp[2] - '0'); 1381eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%c", n); 1382eb6bc482Sderaadt cp += 2; 1383eb6bc482Sderaadt break; 1384eb6bc482Sderaadt case '\\': /* '\' '\' */ 1385eb6bc482Sderaadt strbuf[0] = '\\'; 1386eb6bc482Sderaadt strbuf[1] = '\0'; 1387eb6bc482Sderaadt break; 1388a1344090Sderaadt case '[': /* '\' '[' .... stop counting */ 1389a1344090Sderaadt strbuf[0] = '\0'; 1390a1344090Sderaadt counting = 0; 1391eb6bc482Sderaadt break; 1392a1344090Sderaadt case ']': /* '\' ']' restart counting */ 1393a1344090Sderaadt strbuf[0] = '\0'; 1394a1344090Sderaadt counting = 1; 1395eb6bc482Sderaadt break; 1396eb6bc482Sderaadt 1397eb6bc482Sderaadt default: 1398eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "\\%c", *cp); 1399eb6bc482Sderaadt break; 1400eb6bc482Sderaadt } 1401eb6bc482Sderaadt cp++; 1402eb6bc482Sderaadt 1403eb6bc482Sderaadt str = strbuf; 1404eb6bc482Sderaadt len = strlen(str); 1405eb6bc482Sderaadt if (ntruncate) { 1406eb6bc482Sderaadt if (ntruncate >= len) { 1407eb6bc482Sderaadt ntruncate -= len; 1408eb6bc482Sderaadt continue; 1409eb6bc482Sderaadt } 1410eb6bc482Sderaadt str += ntruncate; 1411eb6bc482Sderaadt len -= ntruncate; 1412eb6bc482Sderaadt ntruncate = 0; 1413eb6bc482Sderaadt } 1414eb6bc482Sderaadt if (doprint) 1415eb6bc482Sderaadt shf_write(str, len, shl_out); 1416a1344090Sderaadt if (counting && !indelimit && !delimitthis) 1417eb6bc482Sderaadt totlen += len; 1418eb6bc482Sderaadt continue; 1419eb6bc482Sderaadt } else if (*cp != '!') 14207cb960a2Sdownsj c = *cp++; 14217cb960a2Sdownsj else if (*++cp == '!') 14227cb960a2Sdownsj c = *cp++; 14237cb960a2Sdownsj else { 14247cb960a2Sdownsj char *p; 14257cb960a2Sdownsj 14267cb960a2Sdownsj shf_snprintf(p = nbuf, sizeof(nbuf), "%d", 14277cb960a2Sdownsj source->line + 1); 14287cb960a2Sdownsj len = strlen(nbuf); 14297cb960a2Sdownsj if (ntruncate) { 14307cb960a2Sdownsj if (ntruncate >= len) { 14317cb960a2Sdownsj ntruncate -= len; 14327cb960a2Sdownsj continue; 14337cb960a2Sdownsj } 14347cb960a2Sdownsj p += ntruncate; 14357cb960a2Sdownsj len -= ntruncate; 14367cb960a2Sdownsj ntruncate = 0; 14377cb960a2Sdownsj } 1438eb6bc482Sderaadt if (doprint) 14397cb960a2Sdownsj shf_write(p, len, shl_out); 1440a1344090Sderaadt if (counting && !indelimit && !delimitthis) 1441eb6bc482Sderaadt totlen += len; 14427cb960a2Sdownsj continue; 14437cb960a2Sdownsj } 1444638b9e76Sbeck if (counting && ntruncate) 14457cb960a2Sdownsj --ntruncate; 1446eb6bc482Sderaadt else if (doprint) { 14477cb960a2Sdownsj shf_putc(c, shl_out); 14487cb960a2Sdownsj } 1449a1344090Sderaadt if (counting && !indelimit && !delimitthis) 1450eb6bc482Sderaadt totlen++; 1451eb6bc482Sderaadt } 1452eb6bc482Sderaadt if (doprint) 14537cb960a2Sdownsj shf_flush(shl_out); 1454eb6bc482Sderaadt if (spp) 1455eb6bc482Sderaadt *spp = sp; 1456eb6bc482Sderaadt return (totlen); 1457eb6bc482Sderaadt } 1458eb6bc482Sderaadt 1459eb6bc482Sderaadt void 1460c5d5393cSotto pprompt(const char *cp, int ntruncate) 1461eb6bc482Sderaadt { 1462eb6bc482Sderaadt dopprompt(cp, ntruncate, NULL, 1); 1463eb6bc482Sderaadt } 1464eb6bc482Sderaadt 1465eb6bc482Sderaadt int 1466c5d5393cSotto promptlen(const char *cp, const char **spp) 1467eb6bc482Sderaadt { 1468eb6bc482Sderaadt return dopprompt(cp, 0, spp, 0); 14697cb960a2Sdownsj } 14707cb960a2Sdownsj 14717cb960a2Sdownsj /* Read the variable part of a ${...} expression (ie, up to but not including 14727cb960a2Sdownsj * the :[-+?=#%] or close-brace. 14737cb960a2Sdownsj */ 14747cb960a2Sdownsj static char * 1475c5d5393cSotto get_brace_var(XString *wsp, char *wp) 14767cb960a2Sdownsj { 14777cb960a2Sdownsj enum parse_state { 14787cb960a2Sdownsj PS_INITIAL, PS_SAW_HASH, PS_IDENT, 14797cb960a2Sdownsj PS_NUMBER, PS_VAR1, PS_END 14807cb960a2Sdownsj } 14817cb960a2Sdownsj state; 14827cb960a2Sdownsj char c; 14837cb960a2Sdownsj 14847cb960a2Sdownsj state = PS_INITIAL; 14857cb960a2Sdownsj while (1) { 1486e55c1b2cSdownsj c = getsc(); 14877cb960a2Sdownsj /* State machine to figure out where the variable part ends. */ 14887cb960a2Sdownsj switch (state) { 14897cb960a2Sdownsj case PS_INITIAL: 14907cb960a2Sdownsj if (c == '#') { 14917cb960a2Sdownsj state = PS_SAW_HASH; 14927cb960a2Sdownsj break; 14937cb960a2Sdownsj } 14947eab881bSjaredy /* FALLTHROUGH */ 14957cb960a2Sdownsj case PS_SAW_HASH: 14967cb960a2Sdownsj if (letter(c)) 14977cb960a2Sdownsj state = PS_IDENT; 14987cb960a2Sdownsj else if (digit(c)) 14997cb960a2Sdownsj state = PS_NUMBER; 15007cb960a2Sdownsj else if (ctype(c, C_VAR1)) 15017cb960a2Sdownsj state = PS_VAR1; 15027cb960a2Sdownsj else 15037cb960a2Sdownsj state = PS_END; 15047cb960a2Sdownsj break; 15057cb960a2Sdownsj case PS_IDENT: 15067cb960a2Sdownsj if (!letnum(c)) { 15077cb960a2Sdownsj state = PS_END; 15087cb960a2Sdownsj if (c == '[') { 15097cb960a2Sdownsj char *tmp, *p; 15107cb960a2Sdownsj 15117cb960a2Sdownsj if (!arraysub(&tmp)) 15127cb960a2Sdownsj yyerror("missing ]\n"); 15137cb960a2Sdownsj *wp++ = c; 15147cb960a2Sdownsj for (p = tmp; *p; ) { 15157cb960a2Sdownsj Xcheck(*wsp, wp); 15167cb960a2Sdownsj *wp++ = *p++; 15177cb960a2Sdownsj } 15187cb960a2Sdownsj afree(tmp, ATEMP); 1519e55c1b2cSdownsj c = getsc(); /* the ] */ 15207cb960a2Sdownsj } 15217cb960a2Sdownsj } 15227cb960a2Sdownsj break; 15237cb960a2Sdownsj case PS_NUMBER: 15247cb960a2Sdownsj if (!digit(c)) 15257cb960a2Sdownsj state = PS_END; 15267cb960a2Sdownsj break; 15277cb960a2Sdownsj case PS_VAR1: 15287cb960a2Sdownsj state = PS_END; 15297cb960a2Sdownsj break; 15307cb960a2Sdownsj case PS_END: /* keep gcc happy */ 15317cb960a2Sdownsj break; 15327cb960a2Sdownsj } 15337cb960a2Sdownsj if (state == PS_END) { 15347cb960a2Sdownsj *wp++ = '\0'; /* end of variable part */ 15357cb960a2Sdownsj ungetsc(c); 15367cb960a2Sdownsj break; 15377cb960a2Sdownsj } 15387cb960a2Sdownsj Xcheck(*wsp, wp); 15397cb960a2Sdownsj *wp++ = c; 15407cb960a2Sdownsj } 15417cb960a2Sdownsj return wp; 15427cb960a2Sdownsj } 15437cb960a2Sdownsj 15447cb960a2Sdownsj /* 15457cb960a2Sdownsj * Save an array subscript - returns true if matching bracket found, false 15467cb960a2Sdownsj * if eof or newline was found. 15477cb960a2Sdownsj * (Returned string double null terminated) 15487cb960a2Sdownsj */ 15497cb960a2Sdownsj static int 1550c5d5393cSotto arraysub(char **strp) 15517cb960a2Sdownsj { 15527cb960a2Sdownsj XString ws; 15537cb960a2Sdownsj char *wp; 15547cb960a2Sdownsj char c; 15557cb960a2Sdownsj int depth = 1; /* we are just past the initial [ */ 15567cb960a2Sdownsj 15577cb960a2Sdownsj Xinit(ws, wp, 32, ATEMP); 15587cb960a2Sdownsj 15597cb960a2Sdownsj do { 1560e55c1b2cSdownsj c = getsc(); 15617cb960a2Sdownsj Xcheck(ws, wp); 15627cb960a2Sdownsj *wp++ = c; 15637cb960a2Sdownsj if (c == '[') 15647cb960a2Sdownsj depth++; 15657cb960a2Sdownsj else if (c == ']') 15667cb960a2Sdownsj depth--; 15677cb960a2Sdownsj } while (depth > 0 && c && c != '\n'); 15687cb960a2Sdownsj 15697cb960a2Sdownsj *wp++ = '\0'; 15707cb960a2Sdownsj *strp = Xclose(ws, wp); 15717cb960a2Sdownsj 15727cb960a2Sdownsj return depth == 0 ? 1 : 0; 15737cb960a2Sdownsj } 15747cb960a2Sdownsj 15757cb960a2Sdownsj /* Unget a char: handles case when we are already at the start of the buffer */ 15767cb960a2Sdownsj static const char * 1577c5d5393cSotto ungetsc(int c) 15787cb960a2Sdownsj { 1579e55c1b2cSdownsj if (backslash_skip) 1580e55c1b2cSdownsj backslash_skip--; 15817cb960a2Sdownsj /* Don't unget eof... */ 15827cb960a2Sdownsj if (source->str == null && c == '\0') 15837cb960a2Sdownsj return source->str; 15847cb960a2Sdownsj if (source->str > source->start) 15857cb960a2Sdownsj source->str--; 15867cb960a2Sdownsj else { 15877cb960a2Sdownsj Source *s; 15887cb960a2Sdownsj 15897cb960a2Sdownsj s = pushs(SREREAD, source->areap); 1590e55c1b2cSdownsj s->ugbuf[0] = c; s->ugbuf[1] = '\0'; 1591e55c1b2cSdownsj s->start = s->str = s->ugbuf; 15927cb960a2Sdownsj s->next = source; 15937cb960a2Sdownsj source = s; 15947cb960a2Sdownsj } 15957cb960a2Sdownsj return source->str; 15967cb960a2Sdownsj } 15977cb960a2Sdownsj 1598e55c1b2cSdownsj 15997cb960a2Sdownsj /* Called to get a char that isn't a \newline sequence. */ 16007cb960a2Sdownsj static int 160169b9f96bSmillert getsc_bn(void) 16027cb960a2Sdownsj { 1603e55c1b2cSdownsj int c, c2; 1604e55c1b2cSdownsj 1605e55c1b2cSdownsj if (ignore_backslash_newline) 1606e55c1b2cSdownsj return getsc_(); 1607e55c1b2cSdownsj 1608e55c1b2cSdownsj if (backslash_skip == 1) { 1609e55c1b2cSdownsj backslash_skip = 2; 1610e55c1b2cSdownsj return getsc_(); 1611e55c1b2cSdownsj } 1612e55c1b2cSdownsj 1613e55c1b2cSdownsj backslash_skip = 0; 16147cb960a2Sdownsj 16157cb960a2Sdownsj while (1) { 16167cb960a2Sdownsj c = getsc_(); 1617e55c1b2cSdownsj if (c == '\\') { 1618e55c1b2cSdownsj if ((c2 = getsc_()) == '\n') 16197cb960a2Sdownsj /* ignore the \newline; get the next char... */ 1620e55c1b2cSdownsj continue; 1621e55c1b2cSdownsj ungetsc(c2); 1622e55c1b2cSdownsj backslash_skip = 1; 1623e55c1b2cSdownsj } 1624e55c1b2cSdownsj return c; 16257cb960a2Sdownsj } 16267cb960a2Sdownsj } 16273b015934Smillert 16283b015934Smillert static Lex_state * 1629c5d5393cSotto push_state_(State_info *si, Lex_state *old_end) 16303b015934Smillert { 16313b015934Smillert Lex_state *new = alloc(sizeof(Lex_state) * STATE_BSIZE, ATEMP); 16323b015934Smillert 16333b015934Smillert new[0].ls_info.base = old_end; 16343b015934Smillert si->base = &new[0]; 16353b015934Smillert si->end = &new[STATE_BSIZE]; 16363b015934Smillert return &new[1]; 16373b015934Smillert } 16383b015934Smillert 16393b015934Smillert static Lex_state * 1640c5d5393cSotto pop_state_(State_info *si, Lex_state *old_end) 16413b015934Smillert { 16423b015934Smillert Lex_state *old_base = si->base; 16433b015934Smillert 16443b015934Smillert si->base = old_end->ls_info.base - STATE_BSIZE; 16453b015934Smillert si->end = old_end->ls_info.base; 16463b015934Smillert 16473b015934Smillert afree(old_base, ATEMP); 16483b015934Smillert 16497b425235Smillert return si->base + STATE_BSIZE - 1; 16503b015934Smillert } 1651