1*ca3ae29fSczarkoff /* $OpenBSD: lex.c,v 1.68 2016/03/04 09:37:23 czarkoff Exp $ */ 27cb960a2Sdownsj 37cb960a2Sdownsj /* 47cb960a2Sdownsj * lexical analysis and source input 57cb960a2Sdownsj */ 67cb960a2Sdownsj 77cb960a2Sdownsj #include <ctype.h> 84a010e0cStb #include <errno.h> 9b608f594Smmcc #include <libgen.h> 104a010e0cStb #include <stdio.h> 1156018212Smmcc #include <string.h> 124a010e0cStb #include <unistd.h> 137cb960a2Sdownsj 14b608f594Smmcc #include "sh.h" 153b015934Smillert 161bbe2e09Smillert /* 171bbe2e09Smillert * states while lexing word 181bbe2e09Smillert */ 19091f90deSnicm #define SINVALID -1 /* invalid state */ 201bbe2e09Smillert #define SBASE 0 /* outside any lexical constructs */ 211bbe2e09Smillert #define SWORD 1 /* implicit quoting for substitute() */ 221bbe2e09Smillert #define SLETPAREN 2 /* inside (( )), implicit quoting */ 231bbe2e09Smillert #define SSQUOTE 3 /* inside '' */ 241bbe2e09Smillert #define SDQUOTE 4 /* inside "" */ 251bbe2e09Smillert #define SBRACE 5 /* inside ${} */ 261bbe2e09Smillert #define SCSPAREN 6 /* inside $() */ 271bbe2e09Smillert #define SBQUOTE 7 /* inside `` */ 281bbe2e09Smillert #define SASPAREN 8 /* inside $(( )) */ 291bbe2e09Smillert #define SHEREDELIM 9 /* parsing <<,<<- delimiter */ 301bbe2e09Smillert #define SHEREDQUOTE 10 /* parsing " in <<,<<- delimiter */ 311bbe2e09Smillert #define SPATTERN 11 /* parsing *(...|...) pattern (*+?@!) */ 321bbe2e09Smillert #define STBRACE 12 /* parsing ${..[#%]..} */ 331bbe2e09Smillert #define SBRACEQ 13 /* inside "${}" */ 341bbe2e09Smillert 353b015934Smillert /* Structure to keep track of the lexing state and the various pieces of info 363b015934Smillert * needed for each particular state. 373b015934Smillert */ 383b015934Smillert typedef struct lex_state Lex_state; 393b015934Smillert struct lex_state { 403b015934Smillert int ls_state; 413b015934Smillert union { 423b015934Smillert /* $(...) */ 433b015934Smillert struct scsparen_info { 443b015934Smillert int nparen; /* count open parenthesis */ 453b015934Smillert int csstate; /* XXX remove */ 463b015934Smillert #define ls_scsparen ls_info.u_scsparen 473b015934Smillert } u_scsparen; 483b015934Smillert 493b015934Smillert /* $((...)) */ 503b015934Smillert struct sasparen_info { 513b015934Smillert int nparen; /* count open parenthesis */ 523b015934Smillert int start; /* marks start of $(( in output str */ 533b015934Smillert #define ls_sasparen ls_info.u_sasparen 543b015934Smillert } u_sasparen; 553b015934Smillert 563b015934Smillert /* ((...)) */ 573b015934Smillert struct sletparen_info { 583b015934Smillert int nparen; /* count open parenthesis */ 593b015934Smillert #define ls_sletparen ls_info.u_sletparen 603b015934Smillert } u_sletparen; 613b015934Smillert 623b015934Smillert /* `...` */ 633b015934Smillert struct sbquote_info { 643b015934Smillert int indquotes; /* true if in double quotes: "`...`" */ 653b015934Smillert #define ls_sbquote ls_info.u_sbquote 663b015934Smillert } u_sbquote; 673b015934Smillert 683b015934Smillert Lex_state *base; /* used to point to next state block */ 693b015934Smillert } ls_info; 703b015934Smillert }; 713b015934Smillert 723b015934Smillert typedef struct State_info State_info; 733b015934Smillert struct State_info { 743b015934Smillert Lex_state *base; 753b015934Smillert Lex_state *end; 763b015934Smillert }; 773b015934Smillert 783b015934Smillert 79c5d5393cSotto static void readhere(struct ioword *); 8069b9f96bSmillert static int getsc__(void); 81c5d5393cSotto static void getsc_line(Source *); 8269b9f96bSmillert static int getsc_bn(void); 83c5d5393cSotto static char *get_brace_var(XString *, char *); 84c5d5393cSotto static int arraysub(char **); 85c5d5393cSotto static const char *ungetsc(int); 8669b9f96bSmillert static void gethere(void); 87c5d5393cSotto static Lex_state *push_state_(State_info *, Lex_state *); 88c5d5393cSotto static Lex_state *pop_state_(State_info *, Lex_state *); 89e7b1290aSotto static char *special_prompt_expand(char *); 90c5d5393cSotto static int dopprompt(const char *, int, const char **, int); 91a3427febSderaadt int promptlen(const char *cp, const char **spp); 927cb960a2Sdownsj 93e55c1b2cSdownsj static int backslash_skip; 94e55c1b2cSdownsj static int ignore_backslash_newline; 95e55c1b2cSdownsj 9618ad1d01Snicm Source *source; /* yyparse/yylex source */ 9718ad1d01Snicm YYSTYPE yylval; /* result from yylex */ 9818ad1d01Snicm struct ioword *heres[HERES], **herep; 9918ad1d01Snicm char ident[IDENT+1]; 10018ad1d01Snicm 10118ad1d01Snicm char **history; /* saved commands */ 10218ad1d01Snicm char **histptr; /* last history item */ 10318ad1d01Snicm int histsize; /* history size */ 10418ad1d01Snicm 105e55c1b2cSdownsj /* optimized getsc_bn() */ 106e55c1b2cSdownsj #define getsc() (*source->str != '\0' && *source->str != '\\' \ 107e55c1b2cSdownsj && !backslash_skip ? *source->str++ : getsc_bn()) 108e55c1b2cSdownsj /* optimized getsc__() */ 109e55c1b2cSdownsj #define getsc_() ((*source->str != '\0') ? *source->str++ : getsc__()) 1107cb960a2Sdownsj 1113b015934Smillert #define STATE_BSIZE 32 1123b015934Smillert 1133b015934Smillert #define PUSH_STATE(s) do { \ 1143b015934Smillert if (++statep == state_info.end) \ 1153b015934Smillert statep = push_state_(&state_info, statep); \ 1163b015934Smillert state = statep->ls_state = (s); \ 1173b015934Smillert } while (0) 1183b015934Smillert 1193b015934Smillert #define POP_STATE() do { \ 1203b015934Smillert if (--statep == state_info.base) \ 1213b015934Smillert statep = pop_state_(&state_info, statep); \ 1223b015934Smillert state = statep->ls_state; \ 1233b015934Smillert } while (0) 1243b015934Smillert 1253b015934Smillert 1267cb960a2Sdownsj 1277cb960a2Sdownsj /* 1287cb960a2Sdownsj * Lexical analyzer 1297cb960a2Sdownsj * 1307cb960a2Sdownsj * tokens are not regular expressions, they are LL(1). 1317cb960a2Sdownsj * for example, "${var:-${PWD}}", and "$(size $(whence ksh))". 1327cb960a2Sdownsj * hence the state stack. 1337cb960a2Sdownsj */ 1347cb960a2Sdownsj 1357cb960a2Sdownsj int 136c5d5393cSotto yylex(int cf) 1377cb960a2Sdownsj { 1383b015934Smillert Lex_state states[STATE_BSIZE], *statep; 1393b015934Smillert State_info state_info; 1407894b443Smillert int c, state; 1417cb960a2Sdownsj XString ws; /* expandable output word */ 1427894b443Smillert char *wp; /* output word pointer */ 1433b015934Smillert char *sp, *dp; 1443b015934Smillert int c2; 1457cb960a2Sdownsj 1467cb960a2Sdownsj 1477cb960a2Sdownsj Again: 148091f90deSnicm states[0].ls_state = SINVALID; 14922a9cf94Sguenther states[0].ls_info.base = NULL; 1503b015934Smillert statep = &states[1]; 1513b015934Smillert state_info.base = states; 1523b015934Smillert state_info.end = &states[STATE_BSIZE]; 1533b015934Smillert 1547cb960a2Sdownsj Xinit(ws, wp, 64, ATEMP); 1557cb960a2Sdownsj 156e55c1b2cSdownsj backslash_skip = 0; 157e55c1b2cSdownsj ignore_backslash_newline = 0; 158e55c1b2cSdownsj 1597cb960a2Sdownsj if (cf&ONEWORD) 1603b015934Smillert state = SWORD; 1617cb960a2Sdownsj else if (cf&LETEXPR) { 1627cb960a2Sdownsj *wp++ = OQUOTE; /* enclose arguments in (double) quotes */ 1633b015934Smillert state = SLETPAREN; 1643b015934Smillert statep->ls_sletparen.nparen = 0; 16594e42df6Smillert } else { /* normal lexing */ 1663b015934Smillert state = (cf & HEREDELIM) ? SHEREDELIM : SBASE; 1677cb960a2Sdownsj while ((c = getsc()) == ' ' || c == '\t') 1687cb960a2Sdownsj ; 169e55c1b2cSdownsj if (c == '#') { 170e55c1b2cSdownsj ignore_backslash_newline++; 1717cb960a2Sdownsj while ((c = getsc()) != '\0' && c != '\n') 1727cb960a2Sdownsj ; 173e55c1b2cSdownsj ignore_backslash_newline--; 174e55c1b2cSdownsj } 1757cb960a2Sdownsj ungetsc(c); 1767cb960a2Sdownsj } 1777cb960a2Sdownsj if (source->flags & SF_ALIAS) { /* trailing ' ' in alias definition */ 1787cb960a2Sdownsj source->flags &= ~SF_ALIAS; 1797cb960a2Sdownsj /* In POSIX mode, a trailing space only counts if we are 1807cb960a2Sdownsj * parsing a simple command 1817cb960a2Sdownsj */ 1827cb960a2Sdownsj if (!Flag(FPOSIX) || (cf & CMDWORD)) 1837cb960a2Sdownsj cf |= ALIAS; 1847cb960a2Sdownsj } 1857cb960a2Sdownsj 1863b015934Smillert /* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */ 1873b015934Smillert statep->ls_state = state; 1883b015934Smillert 1897cb960a2Sdownsj /* collect non-special or quoted characters to form word */ 1907a8124d8Sderaadt while (!((c = getsc()) == 0 || 1917a8124d8Sderaadt ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) { 1927cb960a2Sdownsj Xcheck(ws, wp); 1937cb960a2Sdownsj switch (state) { 1947cb960a2Sdownsj case SBASE: 1957a8124d8Sderaadt if (Flag(FCSHHISTORY) && (source->flags & SF_TTY) && 1967a8124d8Sderaadt c == '!') { 197f740f764Sderaadt char **replace = NULL; 1981c2ba45aStedu int get, i; 199bc7ffb06Snicm char match[200] = { 0 }, *str = match; 2001c2ba45aStedu size_t mlen; 201f740f764Sderaadt 202f740f764Sderaadt c2 = getsc(); 2030b1768a7Sotto if (c2 == '\0' || c2 == ' ' || c2 == '\t') 204f740f764Sderaadt ; 205f740f764Sderaadt else if (c2 == '!') 206f740f764Sderaadt replace = hist_get_newest(0); 207f740f764Sderaadt else if (isdigit(c2) || c2 == '-' || 208f740f764Sderaadt isalpha(c2)) { 2091c2ba45aStedu get = !isalpha(c2); 210f740f764Sderaadt 211f740f764Sderaadt *str++ = c2; 212f740f764Sderaadt do { 213f740f764Sderaadt if ((c2 = getsc()) == '\0') 214f740f764Sderaadt break; 215f740f764Sderaadt if (c2 == '\t' || c2 == ' ' || 216f740f764Sderaadt c2 == '\n') { 217f740f764Sderaadt ungetsc(c2); 218f740f764Sderaadt break; 219f740f764Sderaadt } 220f740f764Sderaadt *str++ = c2; 221f740f764Sderaadt } while (str < &match[sizeof(match)-1]); 222f740f764Sderaadt *str = '\0'; 223f740f764Sderaadt 224f740f764Sderaadt if (get) { 225f740f764Sderaadt int h = findhistrel(match); 226f740f764Sderaadt if (h >= 0) 227f740f764Sderaadt replace = &history[h]; 228f740f764Sderaadt } else { 2290e7d3a01Smillert int h = findhist(-1, 0, match, true); 230f740f764Sderaadt if (h >= 0) 231f740f764Sderaadt replace = &history[h]; 232f740f764Sderaadt } 233f740f764Sderaadt } 234f740f764Sderaadt 235f740f764Sderaadt /* 236f740f764Sderaadt * XXX ksh history buffer saves un-expanded 237f740f764Sderaadt * commands. Until the history buffer code is 238f740f764Sderaadt * changed to contain expanded commands, we 239f740f764Sderaadt * ignore the bad commands (spinning sucks) 240f740f764Sderaadt */ 241f740f764Sderaadt if (replace && **replace == '!') 242f740f764Sderaadt ungetsc(c2); 243f740f764Sderaadt else if (replace) { 244f740f764Sderaadt Source *s; 245f740f764Sderaadt 246f740f764Sderaadt /* do not strdup replacement via alloc */ 2477a8124d8Sderaadt s = pushs(SREREAD, source->areap); 248f740f764Sderaadt s->start = s->str = *replace; 249f740f764Sderaadt s->next = source; 2507d849734Smillert s->u.freeme = NULL; 251f740f764Sderaadt source = s; 252f740f764Sderaadt continue; 253bc7ffb06Snicm } else if (*match != '\0') { 2541c2ba45aStedu /* restore what followed the '!' */ 2551c2ba45aStedu mlen = strlen(match); 2561c2ba45aStedu for (i = mlen-1; i >= 0; i--) 2571c2ba45aStedu ungetsc(match[i]); 258bc7ffb06Snicm } else 259bc7ffb06Snicm ungetsc(c2); 260f740f764Sderaadt } 2617cb960a2Sdownsj if (c == '[' && (cf & (VARASN|ARRAYVAR))) { 2627cb960a2Sdownsj *wp = EOS; /* temporary */ 2637a8124d8Sderaadt if (is_wdvarname(Xstring(ws, wp), false)) { 2647cb960a2Sdownsj char *p, *tmp; 2657cb960a2Sdownsj 2667cb960a2Sdownsj if (arraysub(&tmp)) { 2677cb960a2Sdownsj *wp++ = CHAR; 2687cb960a2Sdownsj *wp++ = c; 2697cb960a2Sdownsj for (p = tmp; *p; ) { 2707cb960a2Sdownsj Xcheck(ws, wp); 2717cb960a2Sdownsj *wp++ = CHAR; 2727cb960a2Sdownsj *wp++ = *p++; 2737cb960a2Sdownsj } 2747cb960a2Sdownsj afree(tmp, ATEMP); 2757cb960a2Sdownsj break; 2767cb960a2Sdownsj } else { 2777cb960a2Sdownsj Source *s; 2787cb960a2Sdownsj 2797cb960a2Sdownsj s = pushs(SREREAD, 2807cb960a2Sdownsj source->areap); 2817cb960a2Sdownsj s->start = s->str 2827cb960a2Sdownsj = s->u.freeme = tmp; 2837cb960a2Sdownsj s->next = source; 2847cb960a2Sdownsj source = s; 2857cb960a2Sdownsj } 2867cb960a2Sdownsj } 2877cb960a2Sdownsj *wp++ = CHAR; 2887cb960a2Sdownsj *wp++ = c; 2897cb960a2Sdownsj break; 2907cb960a2Sdownsj } 2917eab881bSjaredy /* FALLTHROUGH */ 2927cb960a2Sdownsj Sbase1: /* includes *(...|...) pattern (*+?@!) */ 2937a8124d8Sderaadt if (c == '*' || c == '@' || c == '+' || c == '?' || 2947a8124d8Sderaadt c == '!') { 2957cb960a2Sdownsj c2 = getsc(); 2967cb960a2Sdownsj if (c2 == '(' /*)*/ ) { 2977cb960a2Sdownsj *wp++ = OPAT; 2987cb960a2Sdownsj *wp++ = c; 2993b015934Smillert PUSH_STATE(SPATTERN); 3007cb960a2Sdownsj break; 3017cb960a2Sdownsj } 3027cb960a2Sdownsj ungetsc(c2); 3037cb960a2Sdownsj } 3047eab881bSjaredy /* FALLTHROUGH */ 3057cb960a2Sdownsj Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */ 3067cb960a2Sdownsj switch (c) { 3077cb960a2Sdownsj case '\\': 3087cb960a2Sdownsj c = getsc(); 309e55c1b2cSdownsj if (c) /* trailing \ is lost */ 3107cb960a2Sdownsj *wp++ = QCHAR, *wp++ = c; 3117cb960a2Sdownsj break; 3127cb960a2Sdownsj case '\'': 31322a9cf94Sguenther if ((cf & HEREDOC) || state == SBRACEQ) { 31422a9cf94Sguenther *wp++ = CHAR, *wp++ = c; 31522a9cf94Sguenther break; 31622a9cf94Sguenther } 3177cb960a2Sdownsj *wp++ = OQUOTE; 318e55c1b2cSdownsj ignore_backslash_newline++; 3193b015934Smillert PUSH_STATE(SSQUOTE); 3207cb960a2Sdownsj break; 3217cb960a2Sdownsj case '"': 3227cb960a2Sdownsj *wp++ = OQUOTE; 3233b015934Smillert PUSH_STATE(SDQUOTE); 3247cb960a2Sdownsj break; 3257cb960a2Sdownsj default: 3267cb960a2Sdownsj goto Subst; 3277cb960a2Sdownsj } 3287cb960a2Sdownsj break; 3297cb960a2Sdownsj 3307cb960a2Sdownsj Subst: 3317cb960a2Sdownsj switch (c) { 3327cb960a2Sdownsj case '\\': 3337cb960a2Sdownsj c = getsc(); 3347cb960a2Sdownsj switch (c) { 33557f623feSotto case '\\': 3367cb960a2Sdownsj case '$': case '`': 3377cb960a2Sdownsj *wp++ = QCHAR, *wp++ = c; 3387cb960a2Sdownsj break; 33957f623feSotto case '"': 34057f623feSotto if ((cf & HEREDOC) == 0) { 34157f623feSotto *wp++ = QCHAR, *wp++ = c; 34257f623feSotto break; 34357f623feSotto } 34425d8d262Smoritz /* FALLTHROUGH */ 3457cb960a2Sdownsj default: 346d3ecfddbSstsp if (cf & UNESCAPE) { 347d3ecfddbSstsp *wp++ = QCHAR, *wp++ = c; 348d3ecfddbSstsp break; 349d3ecfddbSstsp } 3507cb960a2Sdownsj Xcheck(ws, wp); 351e55c1b2cSdownsj if (c) { /* trailing \ is lost */ 3527cb960a2Sdownsj *wp++ = CHAR, *wp++ = '\\'; 3537cb960a2Sdownsj *wp++ = CHAR, *wp++ = c; 354e55c1b2cSdownsj } 3557cb960a2Sdownsj break; 3567cb960a2Sdownsj } 3577cb960a2Sdownsj break; 3587cb960a2Sdownsj case '$': 3597cb960a2Sdownsj c = getsc(); 3607cb960a2Sdownsj if (c == '(') /*)*/ { 3617cb960a2Sdownsj c = getsc(); 3627cb960a2Sdownsj if (c == '(') /*)*/ { 3633b015934Smillert PUSH_STATE(SASPAREN); 3643b015934Smillert statep->ls_sasparen.nparen = 2; 3653b015934Smillert statep->ls_sasparen.start = 3663b015934Smillert Xsavepos(ws, wp); 3677cb960a2Sdownsj *wp++ = EXPRSUB; 3687cb960a2Sdownsj } else { 3697cb960a2Sdownsj ungetsc(c); 3703b015934Smillert PUSH_STATE(SCSPAREN); 3713b015934Smillert statep->ls_scsparen.nparen = 1; 3723b015934Smillert statep->ls_scsparen.csstate = 0; 3737cb960a2Sdownsj *wp++ = COMSUB; 3747cb960a2Sdownsj } 3757cb960a2Sdownsj } else if (c == '{') /*}*/ { 3767cb960a2Sdownsj *wp++ = OSUBST; 3773b015934Smillert *wp++ = '{'; /*}*/ 3787cb960a2Sdownsj wp = get_brace_var(&ws, wp); 379e55c1b2cSdownsj c = getsc(); 3803b015934Smillert /* allow :# and :% (ksh88 compat) */ 3813b015934Smillert if (c == ':') { 3823b015934Smillert *wp++ = CHAR, *wp++ = c; 3833b015934Smillert c = getsc(); 3843b015934Smillert } 3853b015934Smillert /* If this is a trim operation, 3863b015934Smillert * treat (,|,) specially in STBRACE. 3873b015934Smillert */ 3887cb960a2Sdownsj if (c == '#' || c == '%') { 3893b015934Smillert ungetsc(c); 3903b015934Smillert PUSH_STATE(STBRACE); 3917cb960a2Sdownsj } else { 3927cb960a2Sdownsj ungetsc(c); 39322a9cf94Sguenther if (state == SDQUOTE || 39422a9cf94Sguenther state == SBRACEQ) 39522a9cf94Sguenther PUSH_STATE(SBRACEQ); 39622a9cf94Sguenther else 3973b015934Smillert PUSH_STATE(SBRACE); 3987cb960a2Sdownsj } 3997cb960a2Sdownsj } else if (ctype(c, C_ALPHA)) { 4007cb960a2Sdownsj *wp++ = OSUBST; 4013b015934Smillert *wp++ = 'X'; 4027cb960a2Sdownsj do { 4037cb960a2Sdownsj Xcheck(ws, wp); 4047cb960a2Sdownsj *wp++ = c; 4057cb960a2Sdownsj c = getsc(); 406bbf05626Smmcc } while (ctype(c, C_ALPHA) || digit(c)); 4077cb960a2Sdownsj *wp++ = '\0'; 4087cb960a2Sdownsj *wp++ = CSUBST; 4093b015934Smillert *wp++ = 'X'; 4107cb960a2Sdownsj ungetsc(c); 411bbf05626Smmcc } else if (ctype(c, C_VAR1) || digit(c)) { 4127cb960a2Sdownsj Xcheck(ws, wp); 4137cb960a2Sdownsj *wp++ = OSUBST; 4143b015934Smillert *wp++ = 'X'; 4157cb960a2Sdownsj *wp++ = c; 4167cb960a2Sdownsj *wp++ = '\0'; 4177cb960a2Sdownsj *wp++ = CSUBST; 4183b015934Smillert *wp++ = 'X'; 4197cb960a2Sdownsj } else { 4207cb960a2Sdownsj *wp++ = CHAR, *wp++ = '$'; 4217cb960a2Sdownsj ungetsc(c); 4227cb960a2Sdownsj } 4237cb960a2Sdownsj break; 4247cb960a2Sdownsj case '`': 4253b015934Smillert PUSH_STATE(SBQUOTE); 4267cb960a2Sdownsj *wp++ = COMSUB; 4277cb960a2Sdownsj /* Need to know if we are inside double quotes 4287cb960a2Sdownsj * since sh/at&t-ksh translate the \" to " in 4297cb960a2Sdownsj * "`..\"..`". 4303b015934Smillert * This is not done in posix mode (section 4313b015934Smillert * 3.2.3, Double Quotes: "The backquote shall 4323b015934Smillert * retain its special meaning introducing the 4333b015934Smillert * other form of command substitution (see 4343b015934Smillert * 3.6.3). The portion of the quoted string 4353b015934Smillert * from the initial backquote and the 4363b015934Smillert * characters up to the next backquote that 4373b015934Smillert * is not preceded by a backslash (having 4383b015934Smillert * escape characters removed) defines that 4393b015934Smillert * command whose output replaces `...` when 4403b015934Smillert * the word is expanded." 4413b015934Smillert * Section 3.6.3, Command Substitution: 4423b015934Smillert * "Within the backquoted style of command 4433b015934Smillert * substitution, backslash shall retain its 4443b015934Smillert * literal meaning, except when followed by 4453b015934Smillert * $ ` \."). 4467cb960a2Sdownsj */ 4473b015934Smillert statep->ls_sbquote.indquotes = 0; 4483b015934Smillert if (!Flag(FPOSIX)) { 4493b015934Smillert Lex_state *s = statep; 4503b015934Smillert Lex_state *base = state_info.base; 4513b015934Smillert while (1) { 4523b015934Smillert for (; s != base; s--) { 4533b015934Smillert if (s->ls_state == SDQUOTE) { 4543b015934Smillert statep->ls_sbquote.indquotes = 1; 4553b015934Smillert break; 4563b015934Smillert } 4573b015934Smillert } 4583b015934Smillert if (s != base) 4593b015934Smillert break; 4603b015934Smillert if (!(s = s->ls_info.base)) 4613b015934Smillert break; 4623b015934Smillert base = s-- - STATE_BSIZE; 4633b015934Smillert } 4643b015934Smillert } 4657cb960a2Sdownsj break; 4667cb960a2Sdownsj default: 4677cb960a2Sdownsj *wp++ = CHAR, *wp++ = c; 4687cb960a2Sdownsj } 4697cb960a2Sdownsj break; 4707cb960a2Sdownsj 4717cb960a2Sdownsj case SSQUOTE: 4727cb960a2Sdownsj if (c == '\'') { 4733b015934Smillert POP_STATE(); 47422a9cf94Sguenther if (state == SBRACEQ) { 47522a9cf94Sguenther *wp++ = CHAR, *wp++ = c; 47622a9cf94Sguenther break; 47722a9cf94Sguenther } 4787cb960a2Sdownsj *wp++ = CQUOTE; 479e55c1b2cSdownsj ignore_backslash_newline--; 4807cb960a2Sdownsj } else 4817cb960a2Sdownsj *wp++ = QCHAR, *wp++ = c; 4827cb960a2Sdownsj break; 4837cb960a2Sdownsj 4847cb960a2Sdownsj case SDQUOTE: 4857cb960a2Sdownsj if (c == '"') { 4863b015934Smillert POP_STATE(); 4877cb960a2Sdownsj *wp++ = CQUOTE; 4887cb960a2Sdownsj } else 4897cb960a2Sdownsj goto Subst; 4907cb960a2Sdownsj break; 4917cb960a2Sdownsj 4923b015934Smillert case SCSPAREN: /* $( .. ) */ 4937cb960a2Sdownsj /* todo: deal with $(...) quoting properly 4947cb960a2Sdownsj * kludge to partly fake quoting inside $(..): doesn't 4957cb960a2Sdownsj * really work because nested $(..) or ${..} inside 4967cb960a2Sdownsj * double quotes aren't dealt with. 4977cb960a2Sdownsj */ 4983b015934Smillert switch (statep->ls_scsparen.csstate) { 4997cb960a2Sdownsj case 0: /* normal */ 5007cb960a2Sdownsj switch (c) { 5017cb960a2Sdownsj case '(': 5023b015934Smillert statep->ls_scsparen.nparen++; 5037cb960a2Sdownsj break; 5047cb960a2Sdownsj case ')': 5053b015934Smillert statep->ls_scsparen.nparen--; 5067cb960a2Sdownsj break; 5077cb960a2Sdownsj case '\\': 5083b015934Smillert statep->ls_scsparen.csstate = 1; 5097cb960a2Sdownsj break; 5107cb960a2Sdownsj case '"': 5113b015934Smillert statep->ls_scsparen.csstate = 2; 5127cb960a2Sdownsj break; 5137cb960a2Sdownsj case '\'': 5143b015934Smillert statep->ls_scsparen.csstate = 4; 515e55c1b2cSdownsj ignore_backslash_newline++; 5167cb960a2Sdownsj break; 5177cb960a2Sdownsj } 5187cb960a2Sdownsj break; 5197cb960a2Sdownsj 5207cb960a2Sdownsj case 1: /* backslash in normal mode */ 5217cb960a2Sdownsj case 3: /* backslash in double quotes */ 5223b015934Smillert --statep->ls_scsparen.csstate; 5237cb960a2Sdownsj break; 5247cb960a2Sdownsj 5257cb960a2Sdownsj case 2: /* double quotes */ 5267cb960a2Sdownsj if (c == '"') 5273b015934Smillert statep->ls_scsparen.csstate = 0; 5287cb960a2Sdownsj else if (c == '\\') 5293b015934Smillert statep->ls_scsparen.csstate = 3; 5307cb960a2Sdownsj break; 5317cb960a2Sdownsj 5327cb960a2Sdownsj case 4: /* single quotes */ 533e55c1b2cSdownsj if (c == '\'') { 5343b015934Smillert statep->ls_scsparen.csstate = 0; 535e55c1b2cSdownsj ignore_backslash_newline--; 536e55c1b2cSdownsj } 5377cb960a2Sdownsj break; 5387cb960a2Sdownsj } 5393b015934Smillert if (statep->ls_scsparen.nparen == 0) { 5403b015934Smillert POP_STATE(); 5417cb960a2Sdownsj *wp++ = 0; /* end of COMSUB */ 5427cb960a2Sdownsj } else 5437cb960a2Sdownsj *wp++ = c; 5447cb960a2Sdownsj break; 5457cb960a2Sdownsj 5463b015934Smillert case SASPAREN: /* $(( .. )) */ 5477cb960a2Sdownsj /* todo: deal with $((...); (...)) properly */ 5487cb960a2Sdownsj /* XXX should nest using existing state machine 5497cb960a2Sdownsj * (embed "..", $(...), etc.) */ 5507cb960a2Sdownsj if (c == '(') 5513b015934Smillert statep->ls_sasparen.nparen++; 5527cb960a2Sdownsj else if (c == ')') { 5533b015934Smillert statep->ls_sasparen.nparen--; 5543b015934Smillert if (statep->ls_sasparen.nparen == 1) { 5557cb960a2Sdownsj /*(*/ 5567cb960a2Sdownsj if ((c2 = getsc()) == ')') { 5573b015934Smillert POP_STATE(); 5587cb960a2Sdownsj *wp++ = 0; /* end of EXPRSUB */ 5597cb960a2Sdownsj break; 5607cb960a2Sdownsj } else { 5613b015934Smillert char *s; 5623b015934Smillert 5637cb960a2Sdownsj ungetsc(c2); 5647cb960a2Sdownsj /* mismatched parenthesis - 5657cb960a2Sdownsj * assume we were really 5667cb960a2Sdownsj * parsing a $(..) expression 5677cb960a2Sdownsj */ 5683b015934Smillert s = Xrestpos(ws, wp, 5693b015934Smillert statep->ls_sasparen.start); 5703b015934Smillert memmove(s + 1, s, wp - s); 5713b015934Smillert *s++ = COMSUB; 5723b015934Smillert *s = '('; /*)*/ 5737cb960a2Sdownsj wp++; 5743b015934Smillert statep->ls_scsparen.nparen = 1; 5753b015934Smillert statep->ls_scsparen.csstate = 0; 5767a8124d8Sderaadt state = statep->ls_state = 5777a8124d8Sderaadt SCSPAREN; 5787cb960a2Sdownsj } 5797cb960a2Sdownsj } 5807cb960a2Sdownsj } 5817cb960a2Sdownsj *wp++ = c; 5827cb960a2Sdownsj break; 5837cb960a2Sdownsj 58422a9cf94Sguenther case SBRACEQ: 585*ca3ae29fSczarkoff /*{*/ 586*ca3ae29fSczarkoff if (c == '}') { 587*ca3ae29fSczarkoff POP_STATE(); 588*ca3ae29fSczarkoff *wp++ = CSUBST; 589*ca3ae29fSczarkoff *wp++ = /*{*/ '}'; 590*ca3ae29fSczarkoff } else 591*ca3ae29fSczarkoff goto Sbase2; 592*ca3ae29fSczarkoff break; 593*ca3ae29fSczarkoff 5947cb960a2Sdownsj case SBRACE: 5957cb960a2Sdownsj /*{*/ 5967cb960a2Sdownsj if (c == '}') { 5973b015934Smillert POP_STATE(); 5987cb960a2Sdownsj *wp++ = CSUBST; 5993b015934Smillert *wp++ = /*{*/ '}'; 6007cb960a2Sdownsj } else 6017cb960a2Sdownsj goto Sbase1; 6027cb960a2Sdownsj break; 6037cb960a2Sdownsj 6047cb960a2Sdownsj case STBRACE: 6053b015934Smillert /* Same as SBRACE, except (,|,) treated specially */ 6067cb960a2Sdownsj /*{*/ 6077cb960a2Sdownsj if (c == '}') { 6083b015934Smillert POP_STATE(); 6097cb960a2Sdownsj *wp++ = CSUBST; 6103b015934Smillert *wp++ = /*{*/ '}'; 6117cb960a2Sdownsj } else if (c == '|') { 6127cb960a2Sdownsj *wp++ = SPAT; 6133b015934Smillert } else if (c == '(') { 6143b015934Smillert *wp++ = OPAT; 6153b015934Smillert *wp++ = ' '; /* simile for @ */ 6163b015934Smillert PUSH_STATE(SPATTERN); 6177cb960a2Sdownsj } else 6187cb960a2Sdownsj goto Sbase1; 6197cb960a2Sdownsj break; 6207cb960a2Sdownsj 6217cb960a2Sdownsj case SBQUOTE: 6227cb960a2Sdownsj if (c == '`') { 6237cb960a2Sdownsj *wp++ = 0; 6243b015934Smillert POP_STATE(); 6257cb960a2Sdownsj } else if (c == '\\') { 6267cb960a2Sdownsj switch (c = getsc()) { 6277cb960a2Sdownsj case '\\': 6287cb960a2Sdownsj case '$': case '`': 6297cb960a2Sdownsj *wp++ = c; 6307cb960a2Sdownsj break; 6317cb960a2Sdownsj case '"': 6323b015934Smillert if (statep->ls_sbquote.indquotes) { 6337cb960a2Sdownsj *wp++ = c; 6347cb960a2Sdownsj break; 6357cb960a2Sdownsj } 6367eab881bSjaredy /* FALLTHROUGH */ 6377cb960a2Sdownsj default: 638e55c1b2cSdownsj if (c) { /* trailing \ is lost */ 6397cb960a2Sdownsj *wp++ = '\\'; 6407cb960a2Sdownsj *wp++ = c; 641e55c1b2cSdownsj } 6427cb960a2Sdownsj break; 6437cb960a2Sdownsj } 6447cb960a2Sdownsj } else 6457cb960a2Sdownsj *wp++ = c; 6467cb960a2Sdownsj break; 6477cb960a2Sdownsj 6487cb960a2Sdownsj case SWORD: /* ONEWORD */ 6497cb960a2Sdownsj goto Subst; 6507cb960a2Sdownsj 6513b015934Smillert case SLETPAREN: /* LETEXPR: (( ... )) */ 6527cb960a2Sdownsj /*(*/ 6537cb960a2Sdownsj if (c == ')') { 6543b015934Smillert if (statep->ls_sletparen.nparen > 0) 6553b015934Smillert --statep->ls_sletparen.nparen; 6567cb960a2Sdownsj /*(*/ 6577cb960a2Sdownsj else if ((c2 = getsc()) == ')') { 6587cb960a2Sdownsj c = 0; 6597cb960a2Sdownsj *wp++ = CQUOTE; 6607cb960a2Sdownsj goto Done; 6617cb960a2Sdownsj } else 6627cb960a2Sdownsj ungetsc(c2); 6637cb960a2Sdownsj } else if (c == '(') 6647cb960a2Sdownsj /* parenthesis inside quotes and backslashes 6657cb960a2Sdownsj * are lost, but at&t ksh doesn't count them 6667cb960a2Sdownsj * either 6677cb960a2Sdownsj */ 6683b015934Smillert ++statep->ls_sletparen.nparen; 6697cb960a2Sdownsj goto Sbase2; 6707cb960a2Sdownsj 6717cb960a2Sdownsj case SHEREDELIM: /* <<,<<- delimiter */ 6727cb960a2Sdownsj /* XXX chuck this state (and the next) - use 6737cb960a2Sdownsj * the existing states ($ and \`..` should be 6747cb960a2Sdownsj * stripped of their specialness after the 6757cb960a2Sdownsj * fact). 6767cb960a2Sdownsj */ 6777cb960a2Sdownsj /* here delimiters need a special case since 6787cb960a2Sdownsj * $ and `..` are not to be treated specially 6797cb960a2Sdownsj */ 6807cb960a2Sdownsj if (c == '\\') { 6817cb960a2Sdownsj c = getsc(); 682e55c1b2cSdownsj if (c) { /* trailing \ is lost */ 6837cb960a2Sdownsj *wp++ = QCHAR; 6847cb960a2Sdownsj *wp++ = c; 6857cb960a2Sdownsj } 6867cb960a2Sdownsj } else if (c == '\'') { 6873b015934Smillert PUSH_STATE(SSQUOTE); 6887cb960a2Sdownsj *wp++ = OQUOTE; 689e55c1b2cSdownsj ignore_backslash_newline++; 6907cb960a2Sdownsj } else if (c == '"') { 6913b015934Smillert state = statep->ls_state = SHEREDQUOTE; 6927cb960a2Sdownsj *wp++ = OQUOTE; 6937cb960a2Sdownsj } else { 6947cb960a2Sdownsj *wp++ = CHAR; 6957cb960a2Sdownsj *wp++ = c; 6967cb960a2Sdownsj } 6977cb960a2Sdownsj break; 6987cb960a2Sdownsj 6997cb960a2Sdownsj case SHEREDQUOTE: /* " in <<,<<- delimiter */ 7007cb960a2Sdownsj if (c == '"') { 7017cb960a2Sdownsj *wp++ = CQUOTE; 7023b015934Smillert state = statep->ls_state = SHEREDELIM; 7037cb960a2Sdownsj } else { 704e55c1b2cSdownsj if (c == '\\') { 705e55c1b2cSdownsj switch (c = getsc()) { 706e55c1b2cSdownsj case '\\': case '"': 707e55c1b2cSdownsj case '$': case '`': 7087cb960a2Sdownsj break; 709e55c1b2cSdownsj default: 710e55c1b2cSdownsj if (c) { /* trailing \ lost */ 711e55c1b2cSdownsj *wp++ = CHAR; 712e55c1b2cSdownsj *wp++ = '\\'; 713e55c1b2cSdownsj } 714e55c1b2cSdownsj break; 715e55c1b2cSdownsj } 716e55c1b2cSdownsj } 7177cb960a2Sdownsj *wp++ = CHAR; 7187cb960a2Sdownsj *wp++ = c; 7197cb960a2Sdownsj } 7207cb960a2Sdownsj break; 7217cb960a2Sdownsj 7227cb960a2Sdownsj case SPATTERN: /* in *(...|...) pattern (*+?@!) */ 7237cb960a2Sdownsj if ( /*(*/ c == ')') { 7247cb960a2Sdownsj *wp++ = CPAT; 7253b015934Smillert POP_STATE(); 7263b015934Smillert } else if (c == '|') { 7277cb960a2Sdownsj *wp++ = SPAT; 7283b015934Smillert } else if (c == '(') { 7293b015934Smillert *wp++ = OPAT; 7303b015934Smillert *wp++ = ' '; /* simile for @ */ 7313b015934Smillert PUSH_STATE(SPATTERN); 7323b015934Smillert } else 7337cb960a2Sdownsj goto Sbase1; 7347cb960a2Sdownsj break; 7357cb960a2Sdownsj } 7367cb960a2Sdownsj } 7377cb960a2Sdownsj Done: 7387cb960a2Sdownsj Xcheck(ws, wp); 7393b015934Smillert if (statep != &states[1]) 7403b015934Smillert /* XXX figure out what is missing */ 7417cb960a2Sdownsj yyerror("no closing quote\n"); 7427cb960a2Sdownsj 7437cb960a2Sdownsj /* This done to avoid tests for SHEREDELIM wherever SBASE tested */ 7447cb960a2Sdownsj if (state == SHEREDELIM) 7457cb960a2Sdownsj state = SBASE; 7467cb960a2Sdownsj 7473b015934Smillert dp = Xstring(ws, wp); 7487a8124d8Sderaadt if ((c == '<' || c == '>') && state == SBASE && 7497a8124d8Sderaadt ((c2 = Xlength(ws, wp)) == 0 || 7507a8124d8Sderaadt (c2 == 2 && dp[0] == CHAR && digit(dp[1])))) { 7518c046d24Snicm struct ioword *iop = alloc(sizeof(*iop), ATEMP); 7527cb960a2Sdownsj 7533b015934Smillert if (c2 == 2) 7543b015934Smillert iop->unit = dp[1] - '0'; 7557cb960a2Sdownsj else 7563b015934Smillert iop->unit = c == '>'; /* 0 for <, 1 for > */ 7577cb960a2Sdownsj 7587cb960a2Sdownsj c2 = getsc(); 7597cb960a2Sdownsj /* <<, >>, <> are ok, >< is not */ 7607cb960a2Sdownsj if (c == c2 || (c == '<' && c2 == '>')) { 7617cb960a2Sdownsj iop->flag = c == c2 ? 7627cb960a2Sdownsj (c == '>' ? IOCAT : IOHERE) : IORDWR; 76318bbba6bSmillert if (iop->flag == IOHERE) { 764e55c1b2cSdownsj if ((c2 = getsc()) == '-') 7657cb960a2Sdownsj iop->flag |= IOSKIP; 7667cb960a2Sdownsj else 7677cb960a2Sdownsj ungetsc(c2); 76818bbba6bSmillert } 7697cb960a2Sdownsj } else if (c2 == '&') 7707cb960a2Sdownsj iop->flag = IODUP | (c == '<' ? IORDUP : 0); 7717cb960a2Sdownsj else { 7727cb960a2Sdownsj iop->flag = c == '>' ? IOWRITE : IOREAD; 7737cb960a2Sdownsj if (c == '>' && c2 == '|') 7747cb960a2Sdownsj iop->flag |= IOCLOB; 7757cb960a2Sdownsj else 7767cb960a2Sdownsj ungetsc(c2); 7777cb960a2Sdownsj } 7787cb960a2Sdownsj 779355ffa75Stedu iop->name = NULL; 780355ffa75Stedu iop->delim = NULL; 781355ffa75Stedu iop->heredoc = NULL; 7823b015934Smillert Xfree(ws, wp); /* free word */ 7837cb960a2Sdownsj yylval.iop = iop; 7847cb960a2Sdownsj return REDIR; 7857cb960a2Sdownsj } 7863b015934Smillert 7873b015934Smillert if (wp == dp && state == SBASE) { 7883b015934Smillert Xfree(ws, wp); /* free word */ 7893b015934Smillert /* no word, process LEX1 character */ 7903b015934Smillert switch (c) { 7913b015934Smillert default: 7923b015934Smillert return c; 7933b015934Smillert 7943b015934Smillert case '|': 7953b015934Smillert case '&': 7963b015934Smillert case ';': 7973b015934Smillert if ((c2 = getsc()) == c) 7983b015934Smillert c = (c == ';') ? BREAK : 7993b015934Smillert (c == '|') ? LOGOR : 8003b015934Smillert (c == '&') ? LOGAND : 8013b015934Smillert YYERRCODE; 8023b015934Smillert else if (c == '|' && c2 == '&') 8033b015934Smillert c = COPROC; 8043b015934Smillert else 8053b015934Smillert ungetsc(c2); 8063b015934Smillert return c; 8073b015934Smillert 8087cb960a2Sdownsj case '\n': 8097cb960a2Sdownsj gethere(); 8107cb960a2Sdownsj if (cf & CONTIN) 8117cb960a2Sdownsj goto Again; 8127cb960a2Sdownsj return c; 8137cb960a2Sdownsj 8147cb960a2Sdownsj case '(': /*)*/ 815b69beb66Sgrr if (!Flag(FSH)) { 8167cb960a2Sdownsj if ((c2 = getsc()) == '(') /*)*/ 8173b015934Smillert /* XXX need to handle ((...); (...)) */ 8187cb960a2Sdownsj c = MDPAREN; 8197cb960a2Sdownsj else 8207cb960a2Sdownsj ungetsc(c2); 821b69beb66Sgrr } 8227cb960a2Sdownsj return c; 8237cb960a2Sdownsj /*(*/ 8247cb960a2Sdownsj case ')': 8257cb960a2Sdownsj return c; 8267cb960a2Sdownsj } 8277cb960a2Sdownsj } 8287cb960a2Sdownsj 8297cb960a2Sdownsj *wp++ = EOS; /* terminate word */ 8307cb960a2Sdownsj yylval.cp = Xclose(ws, wp); 83194e42df6Smillert if (state == SWORD || state == SLETPAREN) /* ONEWORD? */ 8327cb960a2Sdownsj return LWORD; 8337cb960a2Sdownsj ungetsc(c); /* unget terminator */ 8347cb960a2Sdownsj 8357cb960a2Sdownsj /* copy word to unprefixed string ident */ 8367cb960a2Sdownsj for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; ) 8377cb960a2Sdownsj *dp++ = *sp++; 83812e7fb2dSjmc /* Make sure the ident array stays '\0' padded */ 8397cb960a2Sdownsj memset(dp, 0, (ident+IDENT) - dp + 1); 8407cb960a2Sdownsj if (c != EOS) 8417cb960a2Sdownsj *ident = '\0'; /* word is not unquoted */ 8427cb960a2Sdownsj 8437cb960a2Sdownsj if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) { 8447cb960a2Sdownsj struct tbl *p; 8457cb960a2Sdownsj int h = hash(ident); 8467cb960a2Sdownsj 8477cb960a2Sdownsj /* { */ 8486df3ee40Sotto if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) && 8497a8124d8Sderaadt (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) { 8507cb960a2Sdownsj afree(yylval.cp, ATEMP); 8517cb960a2Sdownsj return p->val.i; 8527cb960a2Sdownsj } 8536df3ee40Sotto if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) && 8547a8124d8Sderaadt (p->flag & ISSET)) { 8557894b443Smillert Source *s; 8567cb960a2Sdownsj 8577cb960a2Sdownsj for (s = source; s->type == SALIAS; s = s->next) 8587cb960a2Sdownsj if (s->u.tblp == p) 8597cb960a2Sdownsj return LWORD; 8607cb960a2Sdownsj /* push alias expansion */ 8617cb960a2Sdownsj s = pushs(SALIAS, source->areap); 8627cb960a2Sdownsj s->start = s->str = p->val.s; 8637cb960a2Sdownsj s->u.tblp = p; 8647cb960a2Sdownsj s->next = source; 8657cb960a2Sdownsj source = s; 8667cb960a2Sdownsj afree(yylval.cp, ATEMP); 8677cb960a2Sdownsj goto Again; 8687cb960a2Sdownsj } 8697cb960a2Sdownsj } 8707cb960a2Sdownsj 8717cb960a2Sdownsj return LWORD; 8727cb960a2Sdownsj } 8737cb960a2Sdownsj 8747cb960a2Sdownsj static void 875c5d5393cSotto gethere(void) 8767cb960a2Sdownsj { 8777894b443Smillert struct ioword **p; 8787cb960a2Sdownsj 8797cb960a2Sdownsj for (p = heres; p < herep; p++) 8807cb960a2Sdownsj readhere(*p); 8817cb960a2Sdownsj herep = heres; 8827cb960a2Sdownsj } 8837cb960a2Sdownsj 8847cb960a2Sdownsj /* 8857cb960a2Sdownsj * read "<<word" text into temp file 8867cb960a2Sdownsj */ 8877cb960a2Sdownsj 8887cb960a2Sdownsj static void 889c5d5393cSotto readhere(struct ioword *iop) 8907cb960a2Sdownsj { 8917894b443Smillert int c; 8927cb960a2Sdownsj char *volatile eof; 8937cb960a2Sdownsj char *eofp; 894e55c1b2cSdownsj int skiptabs; 895f00c5086Smillert XString xs; 896f00c5086Smillert char *xp; 897f00c5086Smillert int xpos; 8987cb960a2Sdownsj 8997cb960a2Sdownsj eof = evalstr(iop->delim, 0); 9007cb960a2Sdownsj 901e55c1b2cSdownsj if (!(iop->flag & IOEVAL)) 902e55c1b2cSdownsj ignore_backslash_newline++; 903e55c1b2cSdownsj 904f00c5086Smillert Xinit(xs, xp, 256, ATEMP); 905f00c5086Smillert 9067cb960a2Sdownsj for (;;) { 9077cb960a2Sdownsj eofp = eof; 9087cb960a2Sdownsj skiptabs = iop->flag & IOSKIP; 909f00c5086Smillert xpos = Xsavepos(xs, xp); 910e55c1b2cSdownsj while ((c = getsc()) != 0) { 9117cb960a2Sdownsj if (skiptabs) { 9127cb960a2Sdownsj if (c == '\t') 9137cb960a2Sdownsj continue; 9147cb960a2Sdownsj skiptabs = 0; 9157cb960a2Sdownsj } 9167cb960a2Sdownsj if (c != *eofp) 9177cb960a2Sdownsj break; 918f00c5086Smillert Xcheck(xs, xp); 919f00c5086Smillert Xput(xs, xp, c); 9207cb960a2Sdownsj eofp++; 9217cb960a2Sdownsj } 9227cb960a2Sdownsj /* Allow EOF here so commands with out trailing newlines 9237cb960a2Sdownsj * will work (eg, ksh -c '...', $(...), etc). 9247cb960a2Sdownsj */ 925f00c5086Smillert if (*eofp == '\0' && (c == 0 || c == '\n')) { 926f00c5086Smillert xp = Xrestpos(xs, xp, xpos); 9277cb960a2Sdownsj break; 928f00c5086Smillert } 9297cb960a2Sdownsj ungetsc(c); 930e55c1b2cSdownsj while ((c = getsc()) != '\n') { 9317cb960a2Sdownsj if (c == 0) 9327cb960a2Sdownsj yyerror("here document `%s' unclosed\n", eof); 933f00c5086Smillert Xcheck(xs, xp); 934f00c5086Smillert Xput(xs, xp, c); 9357cb960a2Sdownsj } 936f00c5086Smillert Xcheck(xs, xp); 937f00c5086Smillert Xput(xs, xp, c); 9387cb960a2Sdownsj } 939f00c5086Smillert Xput(xs, xp, '\0'); 940f00c5086Smillert iop->heredoc = Xclose(xs, xp); 941f00c5086Smillert 942e55c1b2cSdownsj if (!(iop->flag & IOEVAL)) 943e55c1b2cSdownsj ignore_backslash_newline--; 9447cb960a2Sdownsj } 9457cb960a2Sdownsj 9467cb960a2Sdownsj void 9477cb960a2Sdownsj yyerror(const char *fmt, ...) 9487cb960a2Sdownsj { 9497cb960a2Sdownsj va_list va; 9507cb960a2Sdownsj 9517cb960a2Sdownsj /* pop aliases and re-reads */ 9527cb960a2Sdownsj while (source->type == SALIAS || source->type == SREREAD) 9537cb960a2Sdownsj source = source->next; 9547cb960a2Sdownsj source->str = null; /* zap pending input */ 9557cb960a2Sdownsj 9560e7d3a01Smillert error_prefix(true); 95769b9f96bSmillert va_start(va, fmt); 9587cb960a2Sdownsj shf_vfprintf(shl_out, fmt, va); 9597cb960a2Sdownsj va_end(va); 96063ca93eaSmillert errorf(NULL); 9617cb960a2Sdownsj } 9627cb960a2Sdownsj 9637cb960a2Sdownsj /* 9647cb960a2Sdownsj * input for yylex with alias expansion 9657cb960a2Sdownsj */ 9667cb960a2Sdownsj 9677cb960a2Sdownsj Source * 968c5d5393cSotto pushs(int type, Area *areap) 9697cb960a2Sdownsj { 9707894b443Smillert Source *s; 9717cb960a2Sdownsj 9728c046d24Snicm s = alloc(sizeof(Source), areap); 9737cb960a2Sdownsj s->type = type; 9747cb960a2Sdownsj s->str = null; 9757cb960a2Sdownsj s->start = NULL; 9767cb960a2Sdownsj s->line = 0; 977e7b1290aSotto s->cmd_offset = 0; 9787cb960a2Sdownsj s->errline = 0; 9797cb960a2Sdownsj s->file = NULL; 9807cb960a2Sdownsj s->flags = 0; 9817cb960a2Sdownsj s->next = NULL; 9827cb960a2Sdownsj s->areap = areap; 9837cb960a2Sdownsj if (type == SFILE || type == SSTDIN) { 9847cb960a2Sdownsj char *dummy; 9857cb960a2Sdownsj Xinit(s->xs, dummy, 256, s->areap); 9867cb960a2Sdownsj } else 9877cb960a2Sdownsj memset(&s->xs, 0, sizeof(s->xs)); 9887cb960a2Sdownsj return s; 9897cb960a2Sdownsj } 9907cb960a2Sdownsj 9917cb960a2Sdownsj static int 992c5d5393cSotto getsc__(void) 9937cb960a2Sdownsj { 9947894b443Smillert Source *s = source; 9957894b443Smillert int c; 9967cb960a2Sdownsj 9977cb960a2Sdownsj while ((c = *s->str++) == 0) { 9987cb960a2Sdownsj s->str = NULL; /* return 0 for EOF by default */ 9997cb960a2Sdownsj switch (s->type) { 10007cb960a2Sdownsj case SEOF: 10017cb960a2Sdownsj s->str = null; 10027cb960a2Sdownsj return 0; 10037cb960a2Sdownsj 10047cb960a2Sdownsj case SSTDIN: 10057cb960a2Sdownsj case SFILE: 10067cb960a2Sdownsj getsc_line(s); 10077cb960a2Sdownsj break; 10087cb960a2Sdownsj 10097cb960a2Sdownsj case SWSTR: 10107cb960a2Sdownsj break; 10117cb960a2Sdownsj 10127cb960a2Sdownsj case SSTRING: 10137cb960a2Sdownsj break; 10147cb960a2Sdownsj 10157cb960a2Sdownsj case SWORDS: 10167cb960a2Sdownsj s->start = s->str = *s->u.strv++; 10177cb960a2Sdownsj s->type = SWORDSEP; 10187cb960a2Sdownsj break; 10197cb960a2Sdownsj 10207cb960a2Sdownsj case SWORDSEP: 10217cb960a2Sdownsj if (*s->u.strv == NULL) { 102296085982Snicm s->start = s->str = "\n"; 10237cb960a2Sdownsj s->type = SEOF; 10247cb960a2Sdownsj } else { 102596085982Snicm s->start = s->str = " "; 10267cb960a2Sdownsj s->type = SWORDS; 10277cb960a2Sdownsj } 10287cb960a2Sdownsj break; 10297cb960a2Sdownsj 10307cb960a2Sdownsj case SALIAS: 10317cb960a2Sdownsj if (s->flags & SF_ALIASEND) { 10327cb960a2Sdownsj /* pass on an unused SF_ALIAS flag */ 10337cb960a2Sdownsj source = s->next; 10347cb960a2Sdownsj source->flags |= s->flags & SF_ALIAS; 10357cb960a2Sdownsj s = source; 10367a8124d8Sderaadt } else if (*s->u.tblp->val.s && 1037e569fc7cSderaadt isspace((unsigned char)strchr(s->u.tblp->val.s, 0)[-1])) { 10387cb960a2Sdownsj source = s = s->next; /* pop source stack */ 1039e55c1b2cSdownsj /* Note that this alias ended with a space, 1040e55c1b2cSdownsj * enabling alias expansion on the following 1041e55c1b2cSdownsj * word. 1042e55c1b2cSdownsj */ 10437cb960a2Sdownsj s->flags |= SF_ALIAS; 10447cb960a2Sdownsj } else { 1045e55c1b2cSdownsj /* At this point, we need to keep the current 1046e55c1b2cSdownsj * alias in the source list so recursive 1047e55c1b2cSdownsj * aliases can be detected and we also need 1048e55c1b2cSdownsj * to return the next character. Do this 1049e55c1b2cSdownsj * by temporarily popping the alias to get 1050e55c1b2cSdownsj * the next character and then put it back 1051e55c1b2cSdownsj * in the source list with the SF_ALIASEND 1052e55c1b2cSdownsj * flag set. 10537cb960a2Sdownsj */ 1054e55c1b2cSdownsj source = s->next; /* pop source stack */ 1055e55c1b2cSdownsj source->flags |= s->flags & SF_ALIAS; 1056e55c1b2cSdownsj c = getsc__(); 1057e55c1b2cSdownsj if (c) { 10587cb960a2Sdownsj s->flags |= SF_ALIASEND; 1059e55c1b2cSdownsj s->ugbuf[0] = c; s->ugbuf[1] = '\0'; 1060e55c1b2cSdownsj s->start = s->str = s->ugbuf; 1061e55c1b2cSdownsj s->next = source; 1062e55c1b2cSdownsj source = s; 1063e55c1b2cSdownsj } else { 1064e55c1b2cSdownsj s = source; 1065e55c1b2cSdownsj /* avoid reading eof twice */ 1066e55c1b2cSdownsj s->str = NULL; 1067e7bc3c65Sdownsj break; 1068e55c1b2cSdownsj } 10697cb960a2Sdownsj } 10707cb960a2Sdownsj continue; 10717cb960a2Sdownsj 10727cb960a2Sdownsj case SREREAD: 1073e55c1b2cSdownsj if (s->start != s->ugbuf) /* yuck */ 10747cb960a2Sdownsj afree(s->u.freeme, ATEMP); 10757cb960a2Sdownsj source = s = s->next; 10767cb960a2Sdownsj continue; 10777cb960a2Sdownsj } 10787cb960a2Sdownsj if (s->str == NULL) { 10797cb960a2Sdownsj s->type = SEOF; 10807cb960a2Sdownsj s->start = s->str = null; 10817cb960a2Sdownsj return '\0'; 10827cb960a2Sdownsj } 10837cb960a2Sdownsj if (s->flags & SF_ECHO) { 10847cb960a2Sdownsj shf_puts(s->str, shl_out); 10857cb960a2Sdownsj shf_flush(shl_out); 10867cb960a2Sdownsj } 10877cb960a2Sdownsj } 10887cb960a2Sdownsj return c; 10897cb960a2Sdownsj } 10907cb960a2Sdownsj 10917cb960a2Sdownsj static void 1092c5d5393cSotto getsc_line(Source *s) 10937cb960a2Sdownsj { 10947cb960a2Sdownsj char *xp = Xstring(s->xs, xp); 10957cb960a2Sdownsj int interactive = Flag(FTALKING) && s->type == SSTDIN; 10967cb960a2Sdownsj int have_tty = interactive && (s->flags & SF_TTY); 10977cb960a2Sdownsj 10987cb960a2Sdownsj /* Done here to ensure nothing odd happens when a timeout occurs */ 10997cb960a2Sdownsj XcheckN(s->xs, xp, LINE); 11007cb960a2Sdownsj *xp = '\0'; 11017cb960a2Sdownsj s->start = s->str = xp; 11027cb960a2Sdownsj 11037cb960a2Sdownsj if (have_tty && ksh_tmout) { 11047cb960a2Sdownsj ksh_tmout_state = TMOUT_READING; 11057cb960a2Sdownsj alarm(ksh_tmout); 11067cb960a2Sdownsj } 11077cb960a2Sdownsj #ifdef EDIT 11087cb960a2Sdownsj if (have_tty && (0 11097cb960a2Sdownsj # ifdef VI 11107cb960a2Sdownsj || Flag(FVI) 11117cb960a2Sdownsj # endif /* VI */ 11127cb960a2Sdownsj # ifdef EMACS 11137cb960a2Sdownsj || Flag(FEMACS) || Flag(FGMACS) 11147cb960a2Sdownsj # endif /* EMACS */ 11157a8124d8Sderaadt )) { 11167cb960a2Sdownsj int nread; 11177cb960a2Sdownsj 11187cb960a2Sdownsj nread = x_read(xp, LINE); 11197cb960a2Sdownsj if (nread < 0) /* read error */ 11207cb960a2Sdownsj nread = 0; 11217cb960a2Sdownsj xp[nread] = '\0'; 11227cb960a2Sdownsj xp += nread; 11237cb960a2Sdownsj } 11247cb960a2Sdownsj else 11257cb960a2Sdownsj #endif /* EDIT */ 11267cb960a2Sdownsj { 11277cb960a2Sdownsj if (interactive) { 11287cb960a2Sdownsj pprompt(prompt, 0); 11297cb960a2Sdownsj } else 11307cb960a2Sdownsj s->line++; 11317cb960a2Sdownsj 11327cb960a2Sdownsj while (1) { 11337cb960a2Sdownsj char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf); 11347cb960a2Sdownsj 11357a8124d8Sderaadt if (!p && shf_error(s->u.shf) && 11365ef49dcfSmmcc s->u.shf->errno_ == EINTR) { 11377cb960a2Sdownsj shf_clearerr(s->u.shf); 11387cb960a2Sdownsj if (trap) 11397cb960a2Sdownsj runtraps(0); 11407cb960a2Sdownsj continue; 11417cb960a2Sdownsj } 11427cb960a2Sdownsj if (!p || (xp = p, xp[-1] == '\n')) 11437cb960a2Sdownsj break; 11447cb960a2Sdownsj /* double buffer size */ 11457cb960a2Sdownsj xp++; /* move past null so doubling works... */ 11467cb960a2Sdownsj XcheckN(s->xs, xp, Xlength(s->xs, xp)); 11477cb960a2Sdownsj xp--; /* ...and move back again */ 11487cb960a2Sdownsj } 11497cb960a2Sdownsj /* flush any unwanted input so other programs/builtins 11507cb960a2Sdownsj * can read it. Not very optimal, but less error prone 11517cb960a2Sdownsj * than flushing else where, dealing with redirections, 11527cb960a2Sdownsj * etc.. 11537cb960a2Sdownsj * todo: reduce size of shf buffer (~128?) if SSTDIN 11547cb960a2Sdownsj */ 11557cb960a2Sdownsj if (s->type == SSTDIN) 11567cb960a2Sdownsj shf_flush(s->u.shf); 11577cb960a2Sdownsj } 11587cb960a2Sdownsj /* XXX: temporary kludge to restore source after a 11597cb960a2Sdownsj * trap may have been executed. 11607cb960a2Sdownsj */ 11617cb960a2Sdownsj source = s; 116294e42df6Smillert if (have_tty && ksh_tmout) { 11637cb960a2Sdownsj ksh_tmout_state = TMOUT_EXECUTING; 11647cb960a2Sdownsj alarm(0); 11657cb960a2Sdownsj } 11667cb960a2Sdownsj s->start = s->str = Xstring(s->xs, xp); 11677cb960a2Sdownsj strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp)); 11687cb960a2Sdownsj /* Note: if input is all nulls, this is not eof */ 11697cb960a2Sdownsj if (Xlength(s->xs, xp) == 0) { /* EOF */ 11707cb960a2Sdownsj if (s->type == SFILE) 11717cb960a2Sdownsj shf_fdclose(s->u.shf); 11727cb960a2Sdownsj s->str = NULL; 11737cb960a2Sdownsj } else if (interactive) { 11747cb960a2Sdownsj #ifdef HISTORY 11757cb960a2Sdownsj char *p = Xstring(s->xs, xp); 11767cb960a2Sdownsj if (cur_prompt == PS1) 11777cb960a2Sdownsj while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS)) 11787cb960a2Sdownsj p++; 11797cb960a2Sdownsj if (*p) { 11807cb960a2Sdownsj s->line++; 11817cb960a2Sdownsj histsave(s->line, s->str, 1); 11827cb960a2Sdownsj } 11837cb960a2Sdownsj #endif /* HISTORY */ 11847cb960a2Sdownsj } 11857cb960a2Sdownsj if (interactive) 1186f7654a50Snicm set_prompt(PS2, NULL); 11877cb960a2Sdownsj } 11887cb960a2Sdownsj 1189e7b1290aSotto static char * 1190e7b1290aSotto special_prompt_expand(char *str) 1191e7b1290aSotto { 1192e7b1290aSotto char *p = str; 1193e7b1290aSotto 1194e7b1290aSotto while ((p = strstr(p, "\\$")) != NULL) { 11956aa3ef4bSdrahn *(p+1) = 'p'; 1196e7b1290aSotto } 1197e7b1290aSotto return str; 1198e7b1290aSotto } 1199e7b1290aSotto 12007cb960a2Sdownsj void 1201c5d5393cSotto set_prompt(int to, Source *s) 12027cb960a2Sdownsj { 12034300ac9bSmillert char *ps1; 12047cb960a2Sdownsj Area *saved_atemp; 1205fc804817Smillert 120694e42df6Smillert cur_prompt = to; 120794e42df6Smillert 120894e42df6Smillert switch (to) { 120994e42df6Smillert case PS1: /* command */ 12104300ac9bSmillert ps1 = str_save(str_val(global("PS1")), ATEMP); 12114300ac9bSmillert saved_atemp = ATEMP; /* ps1 is freed by substitute() */ 12127cb960a2Sdownsj newenv(E_ERRH); 12135ae5b57eStedu if (sigsetjmp(genv->jbuf, 0)) { 12147cb960a2Sdownsj prompt = safe_prompt; 1215dcacb757Sdownsj /* Don't print an error - assume it has already 1216dcacb757Sdownsj * been printed. Reason is we may have forked 1217dcacb757Sdownsj * to run a command and the child may be 1218dcacb757Sdownsj * unwinding its stack through this code as it 1219dcacb757Sdownsj * exits. 1220dcacb757Sdownsj */ 1221e7b1290aSotto } else { 1222e7b1290aSotto /* expand \$ before other substitutions are done */ 1223e7b1290aSotto char *tmp = special_prompt_expand(ps1); 1224e7b1290aSotto prompt = str_save(substitute(tmp, 0), saved_atemp); 1225e7b1290aSotto } 12260ee3f80eSotto quitenv(NULL); 12277cb960a2Sdownsj break; 12287cb960a2Sdownsj case PS2: /* command continuation */ 12297cb960a2Sdownsj prompt = str_val(global("PS2")); 12307cb960a2Sdownsj break; 12317cb960a2Sdownsj } 12327cb960a2Sdownsj } 12337cb960a2Sdownsj 1234eb6bc482Sderaadt static int 1235c5d5393cSotto dopprompt(const char *sp, int ntruncate, const char **spp, int doprint) 12367cb960a2Sdownsj { 1237eb6bc482Sderaadt char strbuf[1024], tmpbuf[1024], *p, *str, nbuf[32], delimiter = '\0'; 1238a1344090Sderaadt int len, c, n, totlen = 0, indelimit = 0, counting = 1, delimitthis; 123941c26999Sotto const char *cp = sp; 1240eb6bc482Sderaadt struct tm *tm; 1241eb6bc482Sderaadt time_t t; 1242eb6bc482Sderaadt 1243eb6bc482Sderaadt if (*cp && cp[1] == '\r') { 1244eb6bc482Sderaadt delimiter = *cp; 1245eb6bc482Sderaadt cp += 2; 1246eb6bc482Sderaadt } 12477cb960a2Sdownsj 12487cb960a2Sdownsj while (*cp != 0) { 1249a1344090Sderaadt delimitthis = 0; 1250eb6bc482Sderaadt if (indelimit && *cp != delimiter) 1251eb6bc482Sderaadt ; 1252eb6bc482Sderaadt else if (*cp == '\n' || *cp == '\r') { 1253eb6bc482Sderaadt totlen = 0; 1254eb6bc482Sderaadt sp = cp + 1; 1255a1344090Sderaadt } else if (*cp == '\t') { 1256a1344090Sderaadt if (counting) 1257eb6bc482Sderaadt totlen = (totlen | 7) + 1; 1258a1344090Sderaadt } else if (*cp == delimiter) { 1259eb6bc482Sderaadt indelimit = !indelimit; 1260a1344090Sderaadt delimitthis = 1; 1261a1344090Sderaadt } 1262eb6bc482Sderaadt 1263eb6bc482Sderaadt if (*cp == '\\') { 1264eb6bc482Sderaadt cp++; 1265eb6bc482Sderaadt if (!*cp) 1266eb6bc482Sderaadt break; 1267eb6bc482Sderaadt if (Flag(FSH)) 1268eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "\\%c", *cp); 1269eb6bc482Sderaadt else switch (*cp) { 1270eb6bc482Sderaadt case 'a': /* '\' 'a' bell */ 1271eb6bc482Sderaadt strbuf[0] = '\007'; 1272eb6bc482Sderaadt strbuf[1] = '\0'; 1273eb6bc482Sderaadt break; 1274eb6bc482Sderaadt case 'd': /* '\' 'd' Dow Mon DD */ 1275eb6bc482Sderaadt time(&t); 1276eb6bc482Sderaadt tm = localtime(&t); 1277eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%a %b %d", tm); 1278eb6bc482Sderaadt break; 1279eb6bc482Sderaadt case 'D': /* '\' 'D' '{' strftime format '}' */ 1280eb6bc482Sderaadt p = strchr(cp + 2, '}'); 1281eb6bc482Sderaadt if (cp[1] != '{' || p == NULL) { 1282eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, 1283eb6bc482Sderaadt "\\%c", *cp); 1284eb6bc482Sderaadt break; 1285eb6bc482Sderaadt } 1286eb6bc482Sderaadt strlcpy(tmpbuf, cp + 2, sizeof tmpbuf); 1287eb6bc482Sderaadt p = strchr(tmpbuf, '}'); 1288eb6bc482Sderaadt if (p) 1289eb6bc482Sderaadt *p = '\0'; 1290eb6bc482Sderaadt time(&t); 1291eb6bc482Sderaadt tm = localtime(&t); 1292eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, tmpbuf, tm); 1293eb6bc482Sderaadt cp = strchr(cp + 2, '}'); 1294eb6bc482Sderaadt break; 1295eb6bc482Sderaadt case 'e': /* '\' 'e' escape */ 1296eb6bc482Sderaadt strbuf[0] = '\033'; 1297eb6bc482Sderaadt strbuf[1] = '\0'; 1298eb6bc482Sderaadt break; 1299eb6bc482Sderaadt case 'h': /* '\' 'h' shortened hostname */ 1300eb6bc482Sderaadt gethostname(strbuf, sizeof strbuf); 1301eb6bc482Sderaadt p = strchr(strbuf, '.'); 1302eb6bc482Sderaadt if (p) 1303eb6bc482Sderaadt *p = '\0'; 1304eb6bc482Sderaadt break; 1305eb6bc482Sderaadt case 'H': /* '\' 'H' full hostname */ 1306eb6bc482Sderaadt gethostname(strbuf, sizeof strbuf); 1307eb6bc482Sderaadt break; 1308eb6bc482Sderaadt case 'j': /* '\' 'j' number of jobs */ 1309eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%d", 1310eb6bc482Sderaadt j_njobs()); 1311eb6bc482Sderaadt break; 1312eb6bc482Sderaadt case 'l': /* '\' 'l' basename of tty */ 1313eb6bc482Sderaadt p = ttyname(0); 1314eb6bc482Sderaadt if (p) 1315eb6bc482Sderaadt p = basename(p); 1316eb6bc482Sderaadt if (p) 1317eb6bc482Sderaadt strlcpy(strbuf, p, sizeof strbuf); 1318eb6bc482Sderaadt break; 1319eb6bc482Sderaadt case 'n': /* '\' 'n' newline */ 1320eb6bc482Sderaadt strbuf[0] = '\n'; 1321eb6bc482Sderaadt strbuf[1] = '\0'; 13225c4a5744Sderaadt totlen = 0; /* reset for prompt re-print */ 13235c4a5744Sderaadt sp = cp + 1; 1324eb6bc482Sderaadt break; 13256aa3ef4bSdrahn case 'p': /* '\' '$' $ or # */ 13266aa3ef4bSdrahn strbuf[0] = ksheuid ? '$' : '#'; 13276aa3ef4bSdrahn strbuf[1] = '\0'; 13286aa3ef4bSdrahn break; 1329eb6bc482Sderaadt case 'r': /* '\' 'r' return */ 1330eb6bc482Sderaadt strbuf[0] = '\r'; 1331eb6bc482Sderaadt strbuf[1] = '\0'; 13325c4a5744Sderaadt totlen = 0; /* reset for prompt re-print */ 13335c4a5744Sderaadt sp = cp + 1; 1334eb6bc482Sderaadt break; 1335eb6bc482Sderaadt case 's': /* '\' 's' basename $0 */ 1336eb6bc482Sderaadt strlcpy(strbuf, kshname, sizeof strbuf); 1337eb6bc482Sderaadt break; 1338eb6bc482Sderaadt case 't': /* '\' 't' 24 hour HH:MM:SS */ 1339eb6bc482Sderaadt time(&t); 1340eb6bc482Sderaadt tm = localtime(&t); 1341eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%T", tm); 1342eb6bc482Sderaadt break; 1343eb6bc482Sderaadt case 'T': /* '\' 'T' 12 hour HH:MM:SS */ 1344eb6bc482Sderaadt time(&t); 1345eb6bc482Sderaadt tm = localtime(&t); 1346eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%l:%M:%S", tm); 1347eb6bc482Sderaadt break; 1348eb6bc482Sderaadt case '@': /* '\' '@' 12 hour am/pm format */ 1349eb6bc482Sderaadt time(&t); 1350eb6bc482Sderaadt tm = localtime(&t); 1351eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%r", tm); 1352eb6bc482Sderaadt break; 1353eb6bc482Sderaadt case 'A': /* '\' 'A' 24 hour HH:MM */ 1354eb6bc482Sderaadt time(&t); 1355eb6bc482Sderaadt tm = localtime(&t); 1356eb6bc482Sderaadt strftime(strbuf, sizeof strbuf, "%R", tm); 1357eb6bc482Sderaadt break; 1358eb6bc482Sderaadt case 'u': /* '\' 'u' username */ 1359f351fb1cSotto strlcpy(strbuf, username, sizeof strbuf); 1360eb6bc482Sderaadt break; 1361eb6bc482Sderaadt case 'v': /* '\' 'v' version (short) */ 1362eb6bc482Sderaadt p = strchr(ksh_version, ' '); 1363eb6bc482Sderaadt if (p) 1364eb6bc482Sderaadt p = strchr(p + 1, ' '); 1365eb6bc482Sderaadt if (p) { 1366eb6bc482Sderaadt p++; 1367eb6bc482Sderaadt strlcpy(strbuf, p, sizeof strbuf); 1368eb6bc482Sderaadt p = strchr(strbuf, ' '); 1369eb6bc482Sderaadt if (p) 1370eb6bc482Sderaadt *p = '\0'; 1371eb6bc482Sderaadt } 1372eb6bc482Sderaadt break; 1373eb6bc482Sderaadt case 'V': /* '\' 'V' version (long) */ 1374eb6bc482Sderaadt strlcpy(strbuf, ksh_version, sizeof strbuf); 1375eb6bc482Sderaadt break; 1376eb6bc482Sderaadt case 'w': /* '\' 'w' cwd */ 1377eb6bc482Sderaadt p = str_val(global("PWD")); 1378ad55f791Sderaadt n = strlen(str_val(global("HOME"))); 1379ad55f791Sderaadt if (strcmp(p, "/") == 0) { 1380ad55f791Sderaadt strlcpy(strbuf, p, sizeof strbuf); 1381ad55f791Sderaadt } else if (strcmp(p, str_val(global("HOME"))) == 0) { 1382eb6bc482Sderaadt strbuf[0] = '~'; 1383eb6bc482Sderaadt strbuf[1] = '\0'; 1384ad55f791Sderaadt } else if (strncmp(p, str_val(global("HOME")), n) 1385ad55f791Sderaadt == 0 && p[n] == '/') { 1386ad55f791Sderaadt snprintf(strbuf, sizeof strbuf, "~/%s", 1387ad55f791Sderaadt str_val(global("PWD")) + n + 1); 1388eb6bc482Sderaadt } else 1389eb6bc482Sderaadt strlcpy(strbuf, p, sizeof strbuf); 1390eb6bc482Sderaadt break; 1391eb6bc482Sderaadt case 'W': /* '\' 'W' basename(cwd) */ 1392eb6bc482Sderaadt p = str_val(global("PWD")); 139330e9b9efSokan if (strcmp(p, str_val(global("HOME"))) == 0) { 139430e9b9efSokan strbuf[0] = '~'; 139530e9b9efSokan strbuf[1] = '\0'; 139630e9b9efSokan } else 1397eb6bc482Sderaadt strlcpy(strbuf, basename(p), sizeof strbuf); 1398eb6bc482Sderaadt break; 1399e7b1290aSotto case '!': /* '\' '!' history line number */ 1400eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%d", 1401eb6bc482Sderaadt source->line + 1); 1402eb6bc482Sderaadt break; 1403e7b1290aSotto case '#': /* '\' '#' command line number */ 1404eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%d", 1405e7b1290aSotto source->line - source->cmd_offset + 1); 1406eb6bc482Sderaadt break; 1407eb6bc482Sderaadt case '0': /* '\' '#' '#' ' #' octal numeric handling */ 1408eb6bc482Sderaadt case '1': 1409eb6bc482Sderaadt case '2': 1410eb6bc482Sderaadt case '3': 1411eb6bc482Sderaadt case '4': 1412eb6bc482Sderaadt case '5': 1413eb6bc482Sderaadt case '6': 1414eb6bc482Sderaadt case '7': 1415eb6bc482Sderaadt if ((cp[1] > '7' || cp[1] < '0') || 1416eb6bc482Sderaadt (cp[2] > '7' || cp[2] < '0')) { 1417eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, 1418eb6bc482Sderaadt "\\%c", *cp); 1419eb6bc482Sderaadt break; 1420eb6bc482Sderaadt } 1421064356ecSzhuk n = (cp[0] - '0') * 8 * 8 + (cp[1] - '0') * 8 + 1422064356ecSzhuk (cp[2] - '0'); 1423eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%c", n); 1424eb6bc482Sderaadt cp += 2; 1425eb6bc482Sderaadt break; 1426eb6bc482Sderaadt case '\\': /* '\' '\' */ 1427eb6bc482Sderaadt strbuf[0] = '\\'; 1428eb6bc482Sderaadt strbuf[1] = '\0'; 1429eb6bc482Sderaadt break; 1430a1344090Sderaadt case '[': /* '\' '[' .... stop counting */ 1431a1344090Sderaadt strbuf[0] = '\0'; 1432a1344090Sderaadt counting = 0; 1433eb6bc482Sderaadt break; 1434a1344090Sderaadt case ']': /* '\' ']' restart counting */ 1435a1344090Sderaadt strbuf[0] = '\0'; 1436a1344090Sderaadt counting = 1; 1437eb6bc482Sderaadt break; 1438eb6bc482Sderaadt 1439eb6bc482Sderaadt default: 1440eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "\\%c", *cp); 1441eb6bc482Sderaadt break; 1442eb6bc482Sderaadt } 1443eb6bc482Sderaadt cp++; 1444eb6bc482Sderaadt 1445eb6bc482Sderaadt str = strbuf; 1446eb6bc482Sderaadt len = strlen(str); 1447eb6bc482Sderaadt if (ntruncate) { 1448eb6bc482Sderaadt if (ntruncate >= len) { 1449eb6bc482Sderaadt ntruncate -= len; 1450eb6bc482Sderaadt continue; 1451eb6bc482Sderaadt } 1452eb6bc482Sderaadt str += ntruncate; 1453eb6bc482Sderaadt len -= ntruncate; 1454eb6bc482Sderaadt ntruncate = 0; 1455eb6bc482Sderaadt } 1456eb6bc482Sderaadt if (doprint) 1457eb6bc482Sderaadt shf_write(str, len, shl_out); 1458a1344090Sderaadt if (counting && !indelimit && !delimitthis) 1459eb6bc482Sderaadt totlen += len; 1460eb6bc482Sderaadt continue; 1461eb6bc482Sderaadt } else if (*cp != '!') 14627cb960a2Sdownsj c = *cp++; 14637cb960a2Sdownsj else if (*++cp == '!') 14647cb960a2Sdownsj c = *cp++; 14657cb960a2Sdownsj else { 14667cb960a2Sdownsj char *p; 14677cb960a2Sdownsj 14687cb960a2Sdownsj shf_snprintf(p = nbuf, sizeof(nbuf), "%d", 14697cb960a2Sdownsj source->line + 1); 14707cb960a2Sdownsj len = strlen(nbuf); 14717cb960a2Sdownsj if (ntruncate) { 14727cb960a2Sdownsj if (ntruncate >= len) { 14737cb960a2Sdownsj ntruncate -= len; 14747cb960a2Sdownsj continue; 14757cb960a2Sdownsj } 14767cb960a2Sdownsj p += ntruncate; 14777cb960a2Sdownsj len -= ntruncate; 14787cb960a2Sdownsj ntruncate = 0; 14797cb960a2Sdownsj } 1480eb6bc482Sderaadt if (doprint) 14817cb960a2Sdownsj shf_write(p, len, shl_out); 1482a1344090Sderaadt if (counting && !indelimit && !delimitthis) 1483eb6bc482Sderaadt totlen += len; 14847cb960a2Sdownsj continue; 14857cb960a2Sdownsj } 1486638b9e76Sbeck if (counting && ntruncate) 14877cb960a2Sdownsj --ntruncate; 1488eb6bc482Sderaadt else if (doprint) { 14897cb960a2Sdownsj shf_putc(c, shl_out); 14907cb960a2Sdownsj } 1491a1344090Sderaadt if (counting && !indelimit && !delimitthis) 1492eb6bc482Sderaadt totlen++; 1493eb6bc482Sderaadt } 1494eb6bc482Sderaadt if (doprint) 14957cb960a2Sdownsj shf_flush(shl_out); 1496eb6bc482Sderaadt if (spp) 1497eb6bc482Sderaadt *spp = sp; 1498eb6bc482Sderaadt return (totlen); 1499eb6bc482Sderaadt } 1500eb6bc482Sderaadt 1501eb6bc482Sderaadt void 1502c5d5393cSotto pprompt(const char *cp, int ntruncate) 1503eb6bc482Sderaadt { 1504eb6bc482Sderaadt dopprompt(cp, ntruncate, NULL, 1); 1505eb6bc482Sderaadt } 1506eb6bc482Sderaadt 1507eb6bc482Sderaadt int 1508c5d5393cSotto promptlen(const char *cp, const char **spp) 1509eb6bc482Sderaadt { 1510eb6bc482Sderaadt return dopprompt(cp, 0, spp, 0); 15117cb960a2Sdownsj } 15127cb960a2Sdownsj 15137cb960a2Sdownsj /* Read the variable part of a ${...} expression (ie, up to but not including 15147cb960a2Sdownsj * the :[-+?=#%] or close-brace. 15157cb960a2Sdownsj */ 15167cb960a2Sdownsj static char * 1517c5d5393cSotto get_brace_var(XString *wsp, char *wp) 15187cb960a2Sdownsj { 15197cb960a2Sdownsj enum parse_state { 15207cb960a2Sdownsj PS_INITIAL, PS_SAW_HASH, PS_IDENT, 15217cb960a2Sdownsj PS_NUMBER, PS_VAR1, PS_END 15227cb960a2Sdownsj } 15237cb960a2Sdownsj state; 15247cb960a2Sdownsj char c; 15257cb960a2Sdownsj 15267cb960a2Sdownsj state = PS_INITIAL; 15277cb960a2Sdownsj while (1) { 1528e55c1b2cSdownsj c = getsc(); 15297cb960a2Sdownsj /* State machine to figure out where the variable part ends. */ 15307cb960a2Sdownsj switch (state) { 15317cb960a2Sdownsj case PS_INITIAL: 15327cb960a2Sdownsj if (c == '#') { 15337cb960a2Sdownsj state = PS_SAW_HASH; 15347cb960a2Sdownsj break; 15357cb960a2Sdownsj } 15367eab881bSjaredy /* FALLTHROUGH */ 15377cb960a2Sdownsj case PS_SAW_HASH: 15387cb960a2Sdownsj if (letter(c)) 15397cb960a2Sdownsj state = PS_IDENT; 15407cb960a2Sdownsj else if (digit(c)) 15417cb960a2Sdownsj state = PS_NUMBER; 15427cb960a2Sdownsj else if (ctype(c, C_VAR1)) 15437cb960a2Sdownsj state = PS_VAR1; 15447cb960a2Sdownsj else 15457cb960a2Sdownsj state = PS_END; 15467cb960a2Sdownsj break; 15477cb960a2Sdownsj case PS_IDENT: 15487cb960a2Sdownsj if (!letnum(c)) { 15497cb960a2Sdownsj state = PS_END; 15507cb960a2Sdownsj if (c == '[') { 15517cb960a2Sdownsj char *tmp, *p; 15527cb960a2Sdownsj 15537cb960a2Sdownsj if (!arraysub(&tmp)) 15547cb960a2Sdownsj yyerror("missing ]\n"); 15557cb960a2Sdownsj *wp++ = c; 15567cb960a2Sdownsj for (p = tmp; *p; ) { 15577cb960a2Sdownsj Xcheck(*wsp, wp); 15587cb960a2Sdownsj *wp++ = *p++; 15597cb960a2Sdownsj } 15607cb960a2Sdownsj afree(tmp, ATEMP); 1561e55c1b2cSdownsj c = getsc(); /* the ] */ 15627cb960a2Sdownsj } 15637cb960a2Sdownsj } 15647cb960a2Sdownsj break; 15657cb960a2Sdownsj case PS_NUMBER: 15667cb960a2Sdownsj if (!digit(c)) 15677cb960a2Sdownsj state = PS_END; 15687cb960a2Sdownsj break; 15697cb960a2Sdownsj case PS_VAR1: 15707cb960a2Sdownsj state = PS_END; 15717cb960a2Sdownsj break; 15727cb960a2Sdownsj case PS_END: /* keep gcc happy */ 15737cb960a2Sdownsj break; 15747cb960a2Sdownsj } 15757cb960a2Sdownsj if (state == PS_END) { 15767cb960a2Sdownsj *wp++ = '\0'; /* end of variable part */ 15777cb960a2Sdownsj ungetsc(c); 15787cb960a2Sdownsj break; 15797cb960a2Sdownsj } 15807cb960a2Sdownsj Xcheck(*wsp, wp); 15817cb960a2Sdownsj *wp++ = c; 15827cb960a2Sdownsj } 15837cb960a2Sdownsj return wp; 15847cb960a2Sdownsj } 15857cb960a2Sdownsj 15867cb960a2Sdownsj /* 15877cb960a2Sdownsj * Save an array subscript - returns true if matching bracket found, false 15887cb960a2Sdownsj * if eof or newline was found. 15897cb960a2Sdownsj * (Returned string double null terminated) 15907cb960a2Sdownsj */ 15917cb960a2Sdownsj static int 1592c5d5393cSotto arraysub(char **strp) 15937cb960a2Sdownsj { 15947cb960a2Sdownsj XString ws; 15957cb960a2Sdownsj char *wp; 15967cb960a2Sdownsj char c; 15977cb960a2Sdownsj int depth = 1; /* we are just past the initial [ */ 15987cb960a2Sdownsj 15997cb960a2Sdownsj Xinit(ws, wp, 32, ATEMP); 16007cb960a2Sdownsj 16017cb960a2Sdownsj do { 1602e55c1b2cSdownsj c = getsc(); 16037cb960a2Sdownsj Xcheck(ws, wp); 16047cb960a2Sdownsj *wp++ = c; 16057cb960a2Sdownsj if (c == '[') 16067cb960a2Sdownsj depth++; 16077cb960a2Sdownsj else if (c == ']') 16087cb960a2Sdownsj depth--; 16097cb960a2Sdownsj } while (depth > 0 && c && c != '\n'); 16107cb960a2Sdownsj 16117cb960a2Sdownsj *wp++ = '\0'; 16127cb960a2Sdownsj *strp = Xclose(ws, wp); 16137cb960a2Sdownsj 16147cb960a2Sdownsj return depth == 0 ? 1 : 0; 16157cb960a2Sdownsj } 16167cb960a2Sdownsj 16177cb960a2Sdownsj /* Unget a char: handles case when we are already at the start of the buffer */ 16187cb960a2Sdownsj static const char * 1619c5d5393cSotto ungetsc(int c) 16207cb960a2Sdownsj { 1621e55c1b2cSdownsj if (backslash_skip) 1622e55c1b2cSdownsj backslash_skip--; 16237cb960a2Sdownsj /* Don't unget eof... */ 16247cb960a2Sdownsj if (source->str == null && c == '\0') 16257cb960a2Sdownsj return source->str; 16267cb960a2Sdownsj if (source->str > source->start) 16277cb960a2Sdownsj source->str--; 16287cb960a2Sdownsj else { 16297cb960a2Sdownsj Source *s; 16307cb960a2Sdownsj 16317cb960a2Sdownsj s = pushs(SREREAD, source->areap); 1632e55c1b2cSdownsj s->ugbuf[0] = c; s->ugbuf[1] = '\0'; 1633e55c1b2cSdownsj s->start = s->str = s->ugbuf; 16347cb960a2Sdownsj s->next = source; 16357cb960a2Sdownsj source = s; 16367cb960a2Sdownsj } 16377cb960a2Sdownsj return source->str; 16387cb960a2Sdownsj } 16397cb960a2Sdownsj 1640e55c1b2cSdownsj 16417cb960a2Sdownsj /* Called to get a char that isn't a \newline sequence. */ 16427cb960a2Sdownsj static int 164369b9f96bSmillert getsc_bn(void) 16447cb960a2Sdownsj { 1645e55c1b2cSdownsj int c, c2; 1646e55c1b2cSdownsj 1647e55c1b2cSdownsj if (ignore_backslash_newline) 1648e55c1b2cSdownsj return getsc_(); 1649e55c1b2cSdownsj 1650e55c1b2cSdownsj if (backslash_skip == 1) { 1651e55c1b2cSdownsj backslash_skip = 2; 1652e55c1b2cSdownsj return getsc_(); 1653e55c1b2cSdownsj } 1654e55c1b2cSdownsj 1655e55c1b2cSdownsj backslash_skip = 0; 16567cb960a2Sdownsj 16577cb960a2Sdownsj while (1) { 16587cb960a2Sdownsj c = getsc_(); 1659e55c1b2cSdownsj if (c == '\\') { 1660e55c1b2cSdownsj if ((c2 = getsc_()) == '\n') 16617cb960a2Sdownsj /* ignore the \newline; get the next char... */ 1662e55c1b2cSdownsj continue; 1663e55c1b2cSdownsj ungetsc(c2); 1664e55c1b2cSdownsj backslash_skip = 1; 1665e55c1b2cSdownsj } 1666e55c1b2cSdownsj return c; 16677cb960a2Sdownsj } 16687cb960a2Sdownsj } 16693b015934Smillert 16703b015934Smillert static Lex_state * 1671c5d5393cSotto push_state_(State_info *si, Lex_state *old_end) 16723b015934Smillert { 1673d67c3782Smmcc Lex_state *new = areallocarray(NULL, STATE_BSIZE, 1674d67c3782Smmcc sizeof(Lex_state), ATEMP); 16753b015934Smillert 16763b015934Smillert new[0].ls_info.base = old_end; 16773b015934Smillert si->base = &new[0]; 16783b015934Smillert si->end = &new[STATE_BSIZE]; 16793b015934Smillert return &new[1]; 16803b015934Smillert } 16813b015934Smillert 16823b015934Smillert static Lex_state * 1683c5d5393cSotto pop_state_(State_info *si, Lex_state *old_end) 16843b015934Smillert { 16853b015934Smillert Lex_state *old_base = si->base; 16863b015934Smillert 16873b015934Smillert si->base = old_end->ls_info.base - STATE_BSIZE; 16883b015934Smillert si->end = old_end->ls_info.base; 16893b015934Smillert 16903b015934Smillert afree(old_base, ATEMP); 16913b015934Smillert 16927b425235Smillert return si->base + STATE_BSIZE - 1; 16933b015934Smillert } 1694