1*55449a4bSflorian /* $OpenBSD: lex.c,v 1.80 2024/04/28 16:43:15 florian 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 */
1034c503bcdSmillert uint32_t 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
yylex(int cf)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 */
38851c2de25Santon 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 * "`..\"..`".
4307cb960a2Sdownsj */
4313b015934Smillert statep->ls_sbquote.indquotes = 0;
4323b015934Smillert Lex_state *s = statep;
4333b015934Smillert Lex_state *base = state_info.base;
4343b015934Smillert while (1) {
4353b015934Smillert for (; s != base; s--) {
4363b015934Smillert if (s->ls_state == SDQUOTE) {
4373b015934Smillert statep->ls_sbquote.indquotes = 1;
4383b015934Smillert break;
4393b015934Smillert }
4403b015934Smillert }
4413b015934Smillert if (s != base)
4423b015934Smillert break;
4433b015934Smillert if (!(s = s->ls_info.base))
4443b015934Smillert break;
4453b015934Smillert base = s-- - STATE_BSIZE;
4463b015934Smillert }
4477cb960a2Sdownsj break;
4487cb960a2Sdownsj default:
4497cb960a2Sdownsj *wp++ = CHAR, *wp++ = c;
4507cb960a2Sdownsj }
4517cb960a2Sdownsj break;
4527cb960a2Sdownsj
4537cb960a2Sdownsj case SSQUOTE:
4547cb960a2Sdownsj if (c == '\'') {
4553b015934Smillert POP_STATE();
45622a9cf94Sguenther if (state == SBRACEQ) {
45722a9cf94Sguenther *wp++ = CHAR, *wp++ = c;
45822a9cf94Sguenther break;
45922a9cf94Sguenther }
4607cb960a2Sdownsj *wp++ = CQUOTE;
461e55c1b2cSdownsj ignore_backslash_newline--;
4627cb960a2Sdownsj } else
4637cb960a2Sdownsj *wp++ = QCHAR, *wp++ = c;
4647cb960a2Sdownsj break;
4657cb960a2Sdownsj
4667cb960a2Sdownsj case SDQUOTE:
4677cb960a2Sdownsj if (c == '"') {
4683b015934Smillert POP_STATE();
4697cb960a2Sdownsj *wp++ = CQUOTE;
4707cb960a2Sdownsj } else
4717cb960a2Sdownsj goto Subst;
4727cb960a2Sdownsj break;
4737cb960a2Sdownsj
4743b015934Smillert case SCSPAREN: /* $( .. ) */
4757cb960a2Sdownsj /* todo: deal with $(...) quoting properly
4767cb960a2Sdownsj * kludge to partly fake quoting inside $(..): doesn't
4777cb960a2Sdownsj * really work because nested $(..) or ${..} inside
4787cb960a2Sdownsj * double quotes aren't dealt with.
4797cb960a2Sdownsj */
4803b015934Smillert switch (statep->ls_scsparen.csstate) {
4817cb960a2Sdownsj case 0: /* normal */
4827cb960a2Sdownsj switch (c) {
4837cb960a2Sdownsj case '(':
4843b015934Smillert statep->ls_scsparen.nparen++;
4857cb960a2Sdownsj break;
4867cb960a2Sdownsj case ')':
4873b015934Smillert statep->ls_scsparen.nparen--;
4887cb960a2Sdownsj break;
4897cb960a2Sdownsj case '\\':
4903b015934Smillert statep->ls_scsparen.csstate = 1;
4917cb960a2Sdownsj break;
4927cb960a2Sdownsj case '"':
4933b015934Smillert statep->ls_scsparen.csstate = 2;
4947cb960a2Sdownsj break;
4957cb960a2Sdownsj case '\'':
4963b015934Smillert statep->ls_scsparen.csstate = 4;
497e55c1b2cSdownsj ignore_backslash_newline++;
4987cb960a2Sdownsj break;
4997cb960a2Sdownsj }
5007cb960a2Sdownsj break;
5017cb960a2Sdownsj
5027cb960a2Sdownsj case 1: /* backslash in normal mode */
5037cb960a2Sdownsj case 3: /* backslash in double quotes */
5043b015934Smillert --statep->ls_scsparen.csstate;
5057cb960a2Sdownsj break;
5067cb960a2Sdownsj
5077cb960a2Sdownsj case 2: /* double quotes */
5087cb960a2Sdownsj if (c == '"')
5093b015934Smillert statep->ls_scsparen.csstate = 0;
5107cb960a2Sdownsj else if (c == '\\')
5113b015934Smillert statep->ls_scsparen.csstate = 3;
5127cb960a2Sdownsj break;
5137cb960a2Sdownsj
5147cb960a2Sdownsj case 4: /* single quotes */
515e55c1b2cSdownsj if (c == '\'') {
5163b015934Smillert statep->ls_scsparen.csstate = 0;
517e55c1b2cSdownsj ignore_backslash_newline--;
518e55c1b2cSdownsj }
5197cb960a2Sdownsj break;
5207cb960a2Sdownsj }
5213b015934Smillert if (statep->ls_scsparen.nparen == 0) {
5223b015934Smillert POP_STATE();
5237cb960a2Sdownsj *wp++ = 0; /* end of COMSUB */
5247cb960a2Sdownsj } else
5257cb960a2Sdownsj *wp++ = c;
5267cb960a2Sdownsj break;
5277cb960a2Sdownsj
5283b015934Smillert case SASPAREN: /* $(( .. )) */
5297cb960a2Sdownsj /* todo: deal with $((...); (...)) properly */
5307cb960a2Sdownsj /* XXX should nest using existing state machine
5317cb960a2Sdownsj * (embed "..", $(...), etc.) */
5327cb960a2Sdownsj if (c == '(')
5333b015934Smillert statep->ls_sasparen.nparen++;
5347cb960a2Sdownsj else if (c == ')') {
5353b015934Smillert statep->ls_sasparen.nparen--;
5363b015934Smillert if (statep->ls_sasparen.nparen == 1) {
5377cb960a2Sdownsj /*(*/
5387cb960a2Sdownsj if ((c2 = getsc()) == ')') {
5393b015934Smillert POP_STATE();
5407cb960a2Sdownsj *wp++ = 0; /* end of EXPRSUB */
5417cb960a2Sdownsj break;
5427cb960a2Sdownsj } else {
5433b015934Smillert char *s;
5443b015934Smillert
5457cb960a2Sdownsj ungetsc(c2);
5467cb960a2Sdownsj /* mismatched parenthesis -
5477cb960a2Sdownsj * assume we were really
5487cb960a2Sdownsj * parsing a $(..) expression
5497cb960a2Sdownsj */
5503b015934Smillert s = Xrestpos(ws, wp,
5513b015934Smillert statep->ls_sasparen.start);
5523b015934Smillert memmove(s + 1, s, wp - s);
5533b015934Smillert *s++ = COMSUB;
5543b015934Smillert *s = '('; /*)*/
5557cb960a2Sdownsj wp++;
5563b015934Smillert statep->ls_scsparen.nparen = 1;
5573b015934Smillert statep->ls_scsparen.csstate = 0;
5587a8124d8Sderaadt state = statep->ls_state =
5597a8124d8Sderaadt SCSPAREN;
5607cb960a2Sdownsj }
5617cb960a2Sdownsj }
5627cb960a2Sdownsj }
5637cb960a2Sdownsj *wp++ = c;
5647cb960a2Sdownsj break;
5657cb960a2Sdownsj
56622a9cf94Sguenther case SBRACEQ:
567ca3ae29fSczarkoff /*{*/
568ca3ae29fSczarkoff if (c == '}') {
569ca3ae29fSczarkoff POP_STATE();
570ca3ae29fSczarkoff *wp++ = CSUBST;
571ca3ae29fSczarkoff *wp++ = /*{*/ '}';
572ca3ae29fSczarkoff } else
573ca3ae29fSczarkoff goto Sbase2;
574ca3ae29fSczarkoff break;
575ca3ae29fSczarkoff
5767cb960a2Sdownsj case SBRACE:
5777cb960a2Sdownsj /*{*/
5787cb960a2Sdownsj if (c == '}') {
5793b015934Smillert POP_STATE();
5807cb960a2Sdownsj *wp++ = CSUBST;
5813b015934Smillert *wp++ = /*{*/ '}';
5827cb960a2Sdownsj } else
5837cb960a2Sdownsj goto Sbase1;
5847cb960a2Sdownsj break;
5857cb960a2Sdownsj
5867cb960a2Sdownsj case STBRACE:
5873b015934Smillert /* Same as SBRACE, except (,|,) treated specially */
5887cb960a2Sdownsj /*{*/
5897cb960a2Sdownsj if (c == '}') {
5903b015934Smillert POP_STATE();
5917cb960a2Sdownsj *wp++ = CSUBST;
5923b015934Smillert *wp++ = /*{*/ '}';
5937cb960a2Sdownsj } else if (c == '|') {
5947cb960a2Sdownsj *wp++ = SPAT;
5953b015934Smillert } else if (c == '(') {
5963b015934Smillert *wp++ = OPAT;
5973b015934Smillert *wp++ = ' '; /* simile for @ */
5983b015934Smillert PUSH_STATE(SPATTERN);
5997cb960a2Sdownsj } else
6007cb960a2Sdownsj goto Sbase1;
6017cb960a2Sdownsj break;
6027cb960a2Sdownsj
6037cb960a2Sdownsj case SBQUOTE:
6047cb960a2Sdownsj if (c == '`') {
6057cb960a2Sdownsj *wp++ = 0;
6063b015934Smillert POP_STATE();
6077cb960a2Sdownsj } else if (c == '\\') {
6087cb960a2Sdownsj switch (c = getsc()) {
6097cb960a2Sdownsj case '\\':
6107cb960a2Sdownsj case '$': case '`':
6117cb960a2Sdownsj *wp++ = c;
6127cb960a2Sdownsj break;
6137cb960a2Sdownsj case '"':
6143b015934Smillert if (statep->ls_sbquote.indquotes) {
6157cb960a2Sdownsj *wp++ = c;
6167cb960a2Sdownsj break;
6177cb960a2Sdownsj }
6187eab881bSjaredy /* FALLTHROUGH */
6197cb960a2Sdownsj default:
620e55c1b2cSdownsj if (c) { /* trailing \ is lost */
6217cb960a2Sdownsj *wp++ = '\\';
6227cb960a2Sdownsj *wp++ = c;
623e55c1b2cSdownsj }
6247cb960a2Sdownsj break;
6257cb960a2Sdownsj }
6267cb960a2Sdownsj } else
6277cb960a2Sdownsj *wp++ = c;
6287cb960a2Sdownsj break;
6297cb960a2Sdownsj
6307cb960a2Sdownsj case SWORD: /* ONEWORD */
6317cb960a2Sdownsj goto Subst;
6327cb960a2Sdownsj
6333b015934Smillert case SLETPAREN: /* LETEXPR: (( ... )) */
6347cb960a2Sdownsj /*(*/
6357cb960a2Sdownsj if (c == ')') {
6363b015934Smillert if (statep->ls_sletparen.nparen > 0)
6373b015934Smillert --statep->ls_sletparen.nparen;
6387cb960a2Sdownsj /*(*/
6397cb960a2Sdownsj else if ((c2 = getsc()) == ')') {
6407cb960a2Sdownsj c = 0;
6417cb960a2Sdownsj *wp++ = CQUOTE;
6427cb960a2Sdownsj goto Done;
6437cb960a2Sdownsj } else
6447cb960a2Sdownsj ungetsc(c2);
6457cb960a2Sdownsj } else if (c == '(')
6467cb960a2Sdownsj /* parenthesis inside quotes and backslashes
6477cb960a2Sdownsj * are lost, but at&t ksh doesn't count them
6487cb960a2Sdownsj * either
6497cb960a2Sdownsj */
6503b015934Smillert ++statep->ls_sletparen.nparen;
6517cb960a2Sdownsj goto Sbase2;
6527cb960a2Sdownsj
6537cb960a2Sdownsj case SHEREDELIM: /* <<,<<- delimiter */
6547cb960a2Sdownsj /* XXX chuck this state (and the next) - use
6557cb960a2Sdownsj * the existing states ($ and \`..` should be
6567cb960a2Sdownsj * stripped of their specialness after the
6577cb960a2Sdownsj * fact).
6587cb960a2Sdownsj */
6597cb960a2Sdownsj /* here delimiters need a special case since
6607cb960a2Sdownsj * $ and `..` are not to be treated specially
6617cb960a2Sdownsj */
6627cb960a2Sdownsj if (c == '\\') {
6637cb960a2Sdownsj c = getsc();
664e55c1b2cSdownsj if (c) { /* trailing \ is lost */
6657cb960a2Sdownsj *wp++ = QCHAR;
6667cb960a2Sdownsj *wp++ = c;
6677cb960a2Sdownsj }
6687cb960a2Sdownsj } else if (c == '\'') {
6693b015934Smillert PUSH_STATE(SSQUOTE);
6707cb960a2Sdownsj *wp++ = OQUOTE;
671e55c1b2cSdownsj ignore_backslash_newline++;
6727cb960a2Sdownsj } else if (c == '"') {
6733b015934Smillert state = statep->ls_state = SHEREDQUOTE;
6747cb960a2Sdownsj *wp++ = OQUOTE;
6757cb960a2Sdownsj } else {
6767cb960a2Sdownsj *wp++ = CHAR;
6777cb960a2Sdownsj *wp++ = c;
6787cb960a2Sdownsj }
6797cb960a2Sdownsj break;
6807cb960a2Sdownsj
6817cb960a2Sdownsj case SHEREDQUOTE: /* " in <<,<<- delimiter */
6827cb960a2Sdownsj if (c == '"') {
6837cb960a2Sdownsj *wp++ = CQUOTE;
6843b015934Smillert state = statep->ls_state = SHEREDELIM;
6857cb960a2Sdownsj } else {
686e55c1b2cSdownsj if (c == '\\') {
687e55c1b2cSdownsj switch (c = getsc()) {
688e55c1b2cSdownsj case '\\': case '"':
689e55c1b2cSdownsj case '$': case '`':
6907cb960a2Sdownsj break;
691e55c1b2cSdownsj default:
692e55c1b2cSdownsj if (c) { /* trailing \ lost */
693e55c1b2cSdownsj *wp++ = CHAR;
694e55c1b2cSdownsj *wp++ = '\\';
695e55c1b2cSdownsj }
696e55c1b2cSdownsj break;
697e55c1b2cSdownsj }
698e55c1b2cSdownsj }
6997cb960a2Sdownsj *wp++ = CHAR;
7007cb960a2Sdownsj *wp++ = c;
7017cb960a2Sdownsj }
7027cb960a2Sdownsj break;
7037cb960a2Sdownsj
7047cb960a2Sdownsj case SPATTERN: /* in *(...|...) pattern (*+?@!) */
7057cb960a2Sdownsj if ( /*(*/ c == ')') {
7067cb960a2Sdownsj *wp++ = CPAT;
7073b015934Smillert POP_STATE();
7083b015934Smillert } else if (c == '|') {
7097cb960a2Sdownsj *wp++ = SPAT;
7103b015934Smillert } else if (c == '(') {
7113b015934Smillert *wp++ = OPAT;
7123b015934Smillert *wp++ = ' '; /* simile for @ */
7133b015934Smillert PUSH_STATE(SPATTERN);
7143b015934Smillert } else
7157cb960a2Sdownsj goto Sbase1;
7167cb960a2Sdownsj break;
7177cb960a2Sdownsj }
7187cb960a2Sdownsj }
7197cb960a2Sdownsj Done:
7207cb960a2Sdownsj Xcheck(ws, wp);
7213b015934Smillert if (statep != &states[1])
7223b015934Smillert /* XXX figure out what is missing */
7237cb960a2Sdownsj yyerror("no closing quote\n");
7247cb960a2Sdownsj
7257cb960a2Sdownsj /* This done to avoid tests for SHEREDELIM wherever SBASE tested */
7267cb960a2Sdownsj if (state == SHEREDELIM)
7277cb960a2Sdownsj state = SBASE;
7287cb960a2Sdownsj
7293b015934Smillert dp = Xstring(ws, wp);
7307a8124d8Sderaadt if ((c == '<' || c == '>') && state == SBASE &&
7317a8124d8Sderaadt ((c2 = Xlength(ws, wp)) == 0 ||
7327a8124d8Sderaadt (c2 == 2 && dp[0] == CHAR && digit(dp[1])))) {
7338c046d24Snicm struct ioword *iop = alloc(sizeof(*iop), ATEMP);
7347cb960a2Sdownsj
7353b015934Smillert if (c2 == 2)
7363b015934Smillert iop->unit = dp[1] - '0';
7377cb960a2Sdownsj else
7383b015934Smillert iop->unit = c == '>'; /* 0 for <, 1 for > */
7397cb960a2Sdownsj
7407cb960a2Sdownsj c2 = getsc();
7417cb960a2Sdownsj /* <<, >>, <> are ok, >< is not */
7427cb960a2Sdownsj if (c == c2 || (c == '<' && c2 == '>')) {
7437cb960a2Sdownsj iop->flag = c == c2 ?
7447cb960a2Sdownsj (c == '>' ? IOCAT : IOHERE) : IORDWR;
74518bbba6bSmillert if (iop->flag == IOHERE) {
746e55c1b2cSdownsj if ((c2 = getsc()) == '-')
7477cb960a2Sdownsj iop->flag |= IOSKIP;
7487cb960a2Sdownsj else
7497cb960a2Sdownsj ungetsc(c2);
75018bbba6bSmillert }
7517cb960a2Sdownsj } else if (c2 == '&')
7527cb960a2Sdownsj iop->flag = IODUP | (c == '<' ? IORDUP : 0);
7537cb960a2Sdownsj else {
7547cb960a2Sdownsj iop->flag = c == '>' ? IOWRITE : IOREAD;
7557cb960a2Sdownsj if (c == '>' && c2 == '|')
7567cb960a2Sdownsj iop->flag |= IOCLOB;
7577cb960a2Sdownsj else
7587cb960a2Sdownsj ungetsc(c2);
7597cb960a2Sdownsj }
7607cb960a2Sdownsj
761355ffa75Stedu iop->name = NULL;
762355ffa75Stedu iop->delim = NULL;
763355ffa75Stedu iop->heredoc = NULL;
7643b015934Smillert Xfree(ws, wp); /* free word */
7657cb960a2Sdownsj yylval.iop = iop;
7667cb960a2Sdownsj return REDIR;
7677cb960a2Sdownsj }
7683b015934Smillert
7693b015934Smillert if (wp == dp && state == SBASE) {
7703b015934Smillert Xfree(ws, wp); /* free word */
7713b015934Smillert /* no word, process LEX1 character */
7723b015934Smillert switch (c) {
7733b015934Smillert default:
7743b015934Smillert return c;
7753b015934Smillert
7763b015934Smillert case '|':
7773b015934Smillert case '&':
7783b015934Smillert case ';':
7793b015934Smillert if ((c2 = getsc()) == c)
7803b015934Smillert c = (c == ';') ? BREAK :
7813b015934Smillert (c == '|') ? LOGOR :
7823b015934Smillert (c == '&') ? LOGAND :
7833b015934Smillert YYERRCODE;
7843b015934Smillert else if (c == '|' && c2 == '&')
7853b015934Smillert c = COPROC;
7863b015934Smillert else
7873b015934Smillert ungetsc(c2);
7883b015934Smillert return c;
7893b015934Smillert
7907cb960a2Sdownsj case '\n':
7917cb960a2Sdownsj gethere();
7927cb960a2Sdownsj if (cf & CONTIN)
7937cb960a2Sdownsj goto Again;
7947cb960a2Sdownsj return c;
7957cb960a2Sdownsj
7967cb960a2Sdownsj case '(': /*)*/
797b69beb66Sgrr if (!Flag(FSH)) {
7987cb960a2Sdownsj if ((c2 = getsc()) == '(') /*)*/
7993b015934Smillert /* XXX need to handle ((...); (...)) */
8007cb960a2Sdownsj c = MDPAREN;
8017cb960a2Sdownsj else
8027cb960a2Sdownsj ungetsc(c2);
803b69beb66Sgrr }
8047cb960a2Sdownsj return c;
8057cb960a2Sdownsj /*(*/
8067cb960a2Sdownsj case ')':
8077cb960a2Sdownsj return c;
8087cb960a2Sdownsj }
8097cb960a2Sdownsj }
8107cb960a2Sdownsj
8117cb960a2Sdownsj *wp++ = EOS; /* terminate word */
8127cb960a2Sdownsj yylval.cp = Xclose(ws, wp);
81394e42df6Smillert if (state == SWORD || state == SLETPAREN) /* ONEWORD? */
8147cb960a2Sdownsj return LWORD;
8157cb960a2Sdownsj ungetsc(c); /* unget terminator */
8167cb960a2Sdownsj
8177cb960a2Sdownsj /* copy word to unprefixed string ident */
8187cb960a2Sdownsj for (sp = yylval.cp, dp = ident; dp < ident+IDENT && (c = *sp++) == CHAR; )
8197cb960a2Sdownsj *dp++ = *sp++;
82012e7fb2dSjmc /* Make sure the ident array stays '\0' padded */
8217cb960a2Sdownsj memset(dp, 0, (ident+IDENT) - dp + 1);
8227cb960a2Sdownsj if (c != EOS)
8237cb960a2Sdownsj *ident = '\0'; /* word is not unquoted */
8247cb960a2Sdownsj
8257cb960a2Sdownsj if (*ident != '\0' && (cf&(KEYWORD|ALIAS))) {
8267cb960a2Sdownsj struct tbl *p;
8277cb960a2Sdownsj int h = hash(ident);
8287cb960a2Sdownsj
8297cb960a2Sdownsj /* { */
8306df3ee40Sotto if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) &&
8317a8124d8Sderaadt (!(cf & ESACONLY) || p->val.i == ESAC || p->val.i == '}')) {
8327cb960a2Sdownsj afree(yylval.cp, ATEMP);
8337cb960a2Sdownsj return p->val.i;
8347cb960a2Sdownsj }
8356df3ee40Sotto if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) &&
8367a8124d8Sderaadt (p->flag & ISSET)) {
8377894b443Smillert Source *s;
8387cb960a2Sdownsj
8397cb960a2Sdownsj for (s = source; s->type == SALIAS; s = s->next)
8407cb960a2Sdownsj if (s->u.tblp == p)
8417cb960a2Sdownsj return LWORD;
8427cb960a2Sdownsj /* push alias expansion */
8437cb960a2Sdownsj s = pushs(SALIAS, source->areap);
8447cb960a2Sdownsj s->start = s->str = p->val.s;
8457cb960a2Sdownsj s->u.tblp = p;
8467cb960a2Sdownsj s->next = source;
8477cb960a2Sdownsj source = s;
8487cb960a2Sdownsj afree(yylval.cp, ATEMP);
8497cb960a2Sdownsj goto Again;
8507cb960a2Sdownsj }
8517cb960a2Sdownsj }
8527cb960a2Sdownsj
8537cb960a2Sdownsj return LWORD;
8547cb960a2Sdownsj }
8557cb960a2Sdownsj
8567cb960a2Sdownsj static void
gethere(void)857c5d5393cSotto gethere(void)
8587cb960a2Sdownsj {
8597894b443Smillert struct ioword **p;
8607cb960a2Sdownsj
8617cb960a2Sdownsj for (p = heres; p < herep; p++)
8627cb960a2Sdownsj readhere(*p);
8637cb960a2Sdownsj herep = heres;
8647cb960a2Sdownsj }
8657cb960a2Sdownsj
8667cb960a2Sdownsj /*
8677cb960a2Sdownsj * read "<<word" text into temp file
8687cb960a2Sdownsj */
8697cb960a2Sdownsj
8707cb960a2Sdownsj static void
readhere(struct ioword * iop)871c5d5393cSotto readhere(struct ioword *iop)
8727cb960a2Sdownsj {
8737894b443Smillert int c;
8747cb960a2Sdownsj char *volatile eof;
8757cb960a2Sdownsj char *eofp;
876e55c1b2cSdownsj int skiptabs;
877f00c5086Smillert XString xs;
878f00c5086Smillert char *xp;
879f00c5086Smillert int xpos;
8807cb960a2Sdownsj
8817cb960a2Sdownsj eof = evalstr(iop->delim, 0);
8827cb960a2Sdownsj
883e55c1b2cSdownsj if (!(iop->flag & IOEVAL))
884e55c1b2cSdownsj ignore_backslash_newline++;
885e55c1b2cSdownsj
886f00c5086Smillert Xinit(xs, xp, 256, ATEMP);
887f00c5086Smillert
8887cb960a2Sdownsj for (;;) {
8897cb960a2Sdownsj eofp = eof;
8907cb960a2Sdownsj skiptabs = iop->flag & IOSKIP;
891f00c5086Smillert xpos = Xsavepos(xs, xp);
892e55c1b2cSdownsj while ((c = getsc()) != 0) {
8937cb960a2Sdownsj if (skiptabs) {
8947cb960a2Sdownsj if (c == '\t')
8957cb960a2Sdownsj continue;
8967cb960a2Sdownsj skiptabs = 0;
8977cb960a2Sdownsj }
8987cb960a2Sdownsj if (c != *eofp)
8997cb960a2Sdownsj break;
900f00c5086Smillert Xcheck(xs, xp);
901f00c5086Smillert Xput(xs, xp, c);
9027cb960a2Sdownsj eofp++;
9037cb960a2Sdownsj }
9047cb960a2Sdownsj /* Allow EOF here so commands with out trailing newlines
9057cb960a2Sdownsj * will work (eg, ksh -c '...', $(...), etc).
9067cb960a2Sdownsj */
907f00c5086Smillert if (*eofp == '\0' && (c == 0 || c == '\n')) {
908f00c5086Smillert xp = Xrestpos(xs, xp, xpos);
9097cb960a2Sdownsj break;
910f00c5086Smillert }
9117cb960a2Sdownsj ungetsc(c);
912e55c1b2cSdownsj while ((c = getsc()) != '\n') {
9137cb960a2Sdownsj if (c == 0)
9147cb960a2Sdownsj yyerror("here document `%s' unclosed\n", eof);
915f00c5086Smillert Xcheck(xs, xp);
916f00c5086Smillert Xput(xs, xp, c);
9177cb960a2Sdownsj }
918f00c5086Smillert Xcheck(xs, xp);
919f00c5086Smillert Xput(xs, xp, c);
9207cb960a2Sdownsj }
921f00c5086Smillert Xput(xs, xp, '\0');
922f00c5086Smillert iop->heredoc = Xclose(xs, xp);
923f00c5086Smillert
924e55c1b2cSdownsj if (!(iop->flag & IOEVAL))
925e55c1b2cSdownsj ignore_backslash_newline--;
9267cb960a2Sdownsj }
9277cb960a2Sdownsj
9287cb960a2Sdownsj void
yyerror(const char * fmt,...)9297cb960a2Sdownsj yyerror(const char *fmt, ...)
9307cb960a2Sdownsj {
9317cb960a2Sdownsj va_list va;
9327cb960a2Sdownsj
9337cb960a2Sdownsj /* pop aliases and re-reads */
9347cb960a2Sdownsj while (source->type == SALIAS || source->type == SREREAD)
9357cb960a2Sdownsj source = source->next;
9367cb960a2Sdownsj source->str = null; /* zap pending input */
9377cb960a2Sdownsj
9380e7d3a01Smillert error_prefix(true);
93969b9f96bSmillert va_start(va, fmt);
9407cb960a2Sdownsj shf_vfprintf(shl_out, fmt, va);
9417cb960a2Sdownsj va_end(va);
94263ca93eaSmillert errorf(NULL);
9437cb960a2Sdownsj }
9447cb960a2Sdownsj
9457cb960a2Sdownsj /*
9467cb960a2Sdownsj * input for yylex with alias expansion
9477cb960a2Sdownsj */
9487cb960a2Sdownsj
9497cb960a2Sdownsj Source *
pushs(int type,Area * areap)950c5d5393cSotto pushs(int type, Area *areap)
9517cb960a2Sdownsj {
9527894b443Smillert Source *s;
9537cb960a2Sdownsj
9548c046d24Snicm s = alloc(sizeof(Source), areap);
9557cb960a2Sdownsj s->type = type;
9567cb960a2Sdownsj s->str = null;
9577cb960a2Sdownsj s->start = NULL;
9587cb960a2Sdownsj s->line = 0;
959e7b1290aSotto s->cmd_offset = 0;
9607cb960a2Sdownsj s->errline = 0;
9617cb960a2Sdownsj s->file = NULL;
9627cb960a2Sdownsj s->flags = 0;
9637cb960a2Sdownsj s->next = NULL;
9647cb960a2Sdownsj s->areap = areap;
9657cb960a2Sdownsj if (type == SFILE || type == SSTDIN) {
9667cb960a2Sdownsj char *dummy;
9677cb960a2Sdownsj Xinit(s->xs, dummy, 256, s->areap);
9687cb960a2Sdownsj } else
9697cb960a2Sdownsj memset(&s->xs, 0, sizeof(s->xs));
9707cb960a2Sdownsj return s;
9717cb960a2Sdownsj }
9727cb960a2Sdownsj
9737cb960a2Sdownsj static int
getsc__(void)974c5d5393cSotto getsc__(void)
9757cb960a2Sdownsj {
9767894b443Smillert Source *s = source;
9777894b443Smillert int c;
9787cb960a2Sdownsj
9797cb960a2Sdownsj while ((c = *s->str++) == 0) {
9807cb960a2Sdownsj s->str = NULL; /* return 0 for EOF by default */
9817cb960a2Sdownsj switch (s->type) {
9827cb960a2Sdownsj case SEOF:
9837cb960a2Sdownsj s->str = null;
9847cb960a2Sdownsj return 0;
9857cb960a2Sdownsj
9867cb960a2Sdownsj case SSTDIN:
9877cb960a2Sdownsj case SFILE:
9887cb960a2Sdownsj getsc_line(s);
9897cb960a2Sdownsj break;
9907cb960a2Sdownsj
9917cb960a2Sdownsj case SWSTR:
9927cb960a2Sdownsj break;
9937cb960a2Sdownsj
9947cb960a2Sdownsj case SSTRING:
9957cb960a2Sdownsj break;
9967cb960a2Sdownsj
9977cb960a2Sdownsj case SWORDS:
9987cb960a2Sdownsj s->start = s->str = *s->u.strv++;
9997cb960a2Sdownsj s->type = SWORDSEP;
10007cb960a2Sdownsj break;
10017cb960a2Sdownsj
10027cb960a2Sdownsj case SWORDSEP:
10037cb960a2Sdownsj if (*s->u.strv == NULL) {
100496085982Snicm s->start = s->str = "\n";
10057cb960a2Sdownsj s->type = SEOF;
10067cb960a2Sdownsj } else {
100796085982Snicm s->start = s->str = " ";
10087cb960a2Sdownsj s->type = SWORDS;
10097cb960a2Sdownsj }
10107cb960a2Sdownsj break;
10117cb960a2Sdownsj
10127cb960a2Sdownsj case SALIAS:
10137cb960a2Sdownsj if (s->flags & SF_ALIASEND) {
10147cb960a2Sdownsj /* pass on an unused SF_ALIAS flag */
10157cb960a2Sdownsj source = s->next;
10167cb960a2Sdownsj source->flags |= s->flags & SF_ALIAS;
10177cb960a2Sdownsj s = source;
10187a8124d8Sderaadt } else if (*s->u.tblp->val.s &&
1019e569fc7cSderaadt isspace((unsigned char)strchr(s->u.tblp->val.s, 0)[-1])) {
10207cb960a2Sdownsj source = s = s->next; /* pop source stack */
1021e55c1b2cSdownsj /* Note that this alias ended with a space,
1022e55c1b2cSdownsj * enabling alias expansion on the following
1023e55c1b2cSdownsj * word.
1024e55c1b2cSdownsj */
10257cb960a2Sdownsj s->flags |= SF_ALIAS;
10267cb960a2Sdownsj } else {
1027e55c1b2cSdownsj /* At this point, we need to keep the current
1028e55c1b2cSdownsj * alias in the source list so recursive
1029e55c1b2cSdownsj * aliases can be detected and we also need
1030e55c1b2cSdownsj * to return the next character. Do this
1031e55c1b2cSdownsj * by temporarily popping the alias to get
1032e55c1b2cSdownsj * the next character and then put it back
1033e55c1b2cSdownsj * in the source list with the SF_ALIASEND
1034e55c1b2cSdownsj * flag set.
10357cb960a2Sdownsj */
1036e55c1b2cSdownsj source = s->next; /* pop source stack */
1037e55c1b2cSdownsj source->flags |= s->flags & SF_ALIAS;
1038e55c1b2cSdownsj c = getsc__();
1039e55c1b2cSdownsj if (c) {
10407cb960a2Sdownsj s->flags |= SF_ALIASEND;
1041e55c1b2cSdownsj s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1042e55c1b2cSdownsj s->start = s->str = s->ugbuf;
1043e55c1b2cSdownsj s->next = source;
1044e55c1b2cSdownsj source = s;
1045e55c1b2cSdownsj } else {
1046e55c1b2cSdownsj s = source;
1047e55c1b2cSdownsj /* avoid reading eof twice */
1048e55c1b2cSdownsj s->str = NULL;
1049e7bc3c65Sdownsj break;
1050e55c1b2cSdownsj }
10517cb960a2Sdownsj }
10527cb960a2Sdownsj continue;
10537cb960a2Sdownsj
10547cb960a2Sdownsj case SREREAD:
1055e55c1b2cSdownsj if (s->start != s->ugbuf) /* yuck */
10567cb960a2Sdownsj afree(s->u.freeme, ATEMP);
10577cb960a2Sdownsj source = s = s->next;
10587cb960a2Sdownsj continue;
10597cb960a2Sdownsj }
10607cb960a2Sdownsj if (s->str == NULL) {
10617cb960a2Sdownsj s->type = SEOF;
10627cb960a2Sdownsj s->start = s->str = null;
10637cb960a2Sdownsj return '\0';
10647cb960a2Sdownsj }
10657cb960a2Sdownsj if (s->flags & SF_ECHO) {
10667cb960a2Sdownsj shf_puts(s->str, shl_out);
10677cb960a2Sdownsj shf_flush(shl_out);
10687cb960a2Sdownsj }
10697cb960a2Sdownsj }
10707cb960a2Sdownsj return c;
10717cb960a2Sdownsj }
10727cb960a2Sdownsj
10737cb960a2Sdownsj static void
getsc_line(Source * s)1074c5d5393cSotto getsc_line(Source *s)
10757cb960a2Sdownsj {
10767cb960a2Sdownsj char *xp = Xstring(s->xs, xp);
10777cb960a2Sdownsj int interactive = Flag(FTALKING) && s->type == SSTDIN;
10787cb960a2Sdownsj int have_tty = interactive && (s->flags & SF_TTY);
10797cb960a2Sdownsj
10807cb960a2Sdownsj /* Done here to ensure nothing odd happens when a timeout occurs */
10817cb960a2Sdownsj XcheckN(s->xs, xp, LINE);
10827cb960a2Sdownsj *xp = '\0';
10837cb960a2Sdownsj s->start = s->str = xp;
10847cb960a2Sdownsj
10857cb960a2Sdownsj if (have_tty && ksh_tmout) {
10867cb960a2Sdownsj ksh_tmout_state = TMOUT_READING;
10877cb960a2Sdownsj alarm(ksh_tmout);
10887cb960a2Sdownsj }
10897cb960a2Sdownsj if (have_tty && (0
10907cb960a2Sdownsj #ifdef VI
10917cb960a2Sdownsj || Flag(FVI)
10927cb960a2Sdownsj #endif /* VI */
10937cb960a2Sdownsj #ifdef EMACS
10947cb960a2Sdownsj || Flag(FEMACS) || Flag(FGMACS)
10957cb960a2Sdownsj #endif /* EMACS */
10967a8124d8Sderaadt )) {
10977cb960a2Sdownsj int nread;
10987cb960a2Sdownsj
10997cb960a2Sdownsj nread = x_read(xp, LINE);
11007cb960a2Sdownsj if (nread < 0) /* read error */
11017cb960a2Sdownsj nread = 0;
11027cb960a2Sdownsj xp[nread] = '\0';
11037cb960a2Sdownsj xp += nread;
11043dc0c5b1Sjca } else {
11057cb960a2Sdownsj if (interactive) {
11067cb960a2Sdownsj pprompt(prompt, 0);
11077cb960a2Sdownsj } else
11087cb960a2Sdownsj s->line++;
11097cb960a2Sdownsj
11107cb960a2Sdownsj while (1) {
11117cb960a2Sdownsj char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf);
11127cb960a2Sdownsj
11137a8124d8Sderaadt if (!p && shf_error(s->u.shf) &&
11145ef49dcfSmmcc s->u.shf->errno_ == EINTR) {
11157cb960a2Sdownsj shf_clearerr(s->u.shf);
11167cb960a2Sdownsj if (trap)
11177cb960a2Sdownsj runtraps(0);
11187cb960a2Sdownsj continue;
11197cb960a2Sdownsj }
11207cb960a2Sdownsj if (!p || (xp = p, xp[-1] == '\n'))
11217cb960a2Sdownsj break;
11227cb960a2Sdownsj /* double buffer size */
11237cb960a2Sdownsj xp++; /* move past null so doubling works... */
11247cb960a2Sdownsj XcheckN(s->xs, xp, Xlength(s->xs, xp));
11257cb960a2Sdownsj xp--; /* ...and move back again */
11267cb960a2Sdownsj }
11277cb960a2Sdownsj /* flush any unwanted input so other programs/builtins
11287cb960a2Sdownsj * can read it. Not very optimal, but less error prone
11297cb960a2Sdownsj * than flushing else where, dealing with redirections,
11307cb960a2Sdownsj * etc..
11317cb960a2Sdownsj * todo: reduce size of shf buffer (~128?) if SSTDIN
11327cb960a2Sdownsj */
11337cb960a2Sdownsj if (s->type == SSTDIN)
11347cb960a2Sdownsj shf_flush(s->u.shf);
11357cb960a2Sdownsj }
11367cb960a2Sdownsj /* XXX: temporary kludge to restore source after a
11377cb960a2Sdownsj * trap may have been executed.
11387cb960a2Sdownsj */
11397cb960a2Sdownsj source = s;
114094e42df6Smillert if (have_tty && ksh_tmout) {
11417cb960a2Sdownsj ksh_tmout_state = TMOUT_EXECUTING;
11427cb960a2Sdownsj alarm(0);
11437cb960a2Sdownsj }
11447cb960a2Sdownsj s->start = s->str = Xstring(s->xs, xp);
11457cb960a2Sdownsj strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp));
11467cb960a2Sdownsj /* Note: if input is all nulls, this is not eof */
11477cb960a2Sdownsj if (Xlength(s->xs, xp) == 0) { /* EOF */
11487cb960a2Sdownsj if (s->type == SFILE)
11497cb960a2Sdownsj shf_fdclose(s->u.shf);
11507cb960a2Sdownsj s->str = NULL;
11517cb960a2Sdownsj } else if (interactive) {
11527cb960a2Sdownsj char *p = Xstring(s->xs, xp);
11537cb960a2Sdownsj if (cur_prompt == PS1)
11547cb960a2Sdownsj while (*p && ctype(*p, C_IFS) && ctype(*p, C_IFSWS))
11557cb960a2Sdownsj p++;
11567cb960a2Sdownsj if (*p) {
11577cb960a2Sdownsj s->line++;
11587cb960a2Sdownsj histsave(s->line, s->str, 1);
11597cb960a2Sdownsj }
11607cb960a2Sdownsj }
11617cb960a2Sdownsj if (interactive)
1162311eb4a5Stb set_prompt(PS2);
11637cb960a2Sdownsj }
11647cb960a2Sdownsj
1165e7b1290aSotto static char *
special_prompt_expand(char * str)1166e7b1290aSotto special_prompt_expand(char *str)
1167e7b1290aSotto {
1168e7b1290aSotto char *p = str;
1169e7b1290aSotto
1170e7b1290aSotto while ((p = strstr(p, "\\$")) != NULL) {
11716aa3ef4bSdrahn *(p+1) = 'p';
1172e7b1290aSotto }
1173e7b1290aSotto return str;
1174e7b1290aSotto }
1175e7b1290aSotto
11767cb960a2Sdownsj void
set_prompt(int to)1177311eb4a5Stb set_prompt(int to)
11787cb960a2Sdownsj {
11794300ac9bSmillert char *ps1;
11807cb960a2Sdownsj Area *saved_atemp;
1181fc804817Smillert
118294e42df6Smillert cur_prompt = to;
118394e42df6Smillert
118494e42df6Smillert switch (to) {
118594e42df6Smillert case PS1: /* command */
11864300ac9bSmillert ps1 = str_save(str_val(global("PS1")), ATEMP);
11874300ac9bSmillert saved_atemp = ATEMP; /* ps1 is freed by substitute() */
11887cb960a2Sdownsj newenv(E_ERRH);
11895ae5b57eStedu if (sigsetjmp(genv->jbuf, 0)) {
11907cb960a2Sdownsj prompt = safe_prompt;
1191dcacb757Sdownsj /* Don't print an error - assume it has already
1192dcacb757Sdownsj * been printed. Reason is we may have forked
1193dcacb757Sdownsj * to run a command and the child may be
1194dcacb757Sdownsj * unwinding its stack through this code as it
1195dcacb757Sdownsj * exits.
1196dcacb757Sdownsj */
1197e7b1290aSotto } else {
1198e7b1290aSotto /* expand \$ before other substitutions are done */
1199e7b1290aSotto char *tmp = special_prompt_expand(ps1);
1200e7b1290aSotto prompt = str_save(substitute(tmp, 0), saved_atemp);
1201e7b1290aSotto }
12020ee3f80eSotto quitenv(NULL);
12037cb960a2Sdownsj break;
12047cb960a2Sdownsj case PS2: /* command continuation */
12057cb960a2Sdownsj prompt = str_val(global("PS2"));
12067cb960a2Sdownsj break;
12077cb960a2Sdownsj }
12087cb960a2Sdownsj }
12097cb960a2Sdownsj
1210eb6bc482Sderaadt static int
dopprompt(const char * sp,int ntruncate,const char ** spp,int doprint)1211c5d5393cSotto dopprompt(const char *sp, int ntruncate, const char **spp, int doprint)
12127cb960a2Sdownsj {
1213eb6bc482Sderaadt char strbuf[1024], tmpbuf[1024], *p, *str, nbuf[32], delimiter = '\0';
1214a1344090Sderaadt int len, c, n, totlen = 0, indelimit = 0, counting = 1, delimitthis;
121541c26999Sotto const char *cp = sp;
1216eb6bc482Sderaadt struct tm *tm;
1217eb6bc482Sderaadt time_t t;
1218eb6bc482Sderaadt
1219eb6bc482Sderaadt if (*cp && cp[1] == '\r') {
1220eb6bc482Sderaadt delimiter = *cp;
1221eb6bc482Sderaadt cp += 2;
1222eb6bc482Sderaadt }
12237cb960a2Sdownsj
12247cb960a2Sdownsj while (*cp != 0) {
1225a1344090Sderaadt delimitthis = 0;
1226eb6bc482Sderaadt if (indelimit && *cp != delimiter)
1227eb6bc482Sderaadt ;
1228eb6bc482Sderaadt else if (*cp == '\n' || *cp == '\r') {
1229eb6bc482Sderaadt totlen = 0;
1230eb6bc482Sderaadt sp = cp + 1;
1231a1344090Sderaadt } else if (*cp == '\t') {
1232a1344090Sderaadt if (counting)
1233eb6bc482Sderaadt totlen = (totlen | 7) + 1;
1234a1344090Sderaadt } else if (*cp == delimiter) {
1235eb6bc482Sderaadt indelimit = !indelimit;
1236a1344090Sderaadt delimitthis = 1;
1237a1344090Sderaadt }
1238eb6bc482Sderaadt
1239eb6bc482Sderaadt if (*cp == '\\') {
1240eb6bc482Sderaadt cp++;
1241eb6bc482Sderaadt if (!*cp)
1242eb6bc482Sderaadt break;
12430a9a54a3Stb /* Expand \h and \$ for both, sh(1) and ksh(1) */
12440a9a54a3Stb if (Flag(FSH) && !(*cp == 'h' || *cp == 'p'))
1245eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "\\%c", *cp);
1246eb6bc482Sderaadt else switch (*cp) {
1247eb6bc482Sderaadt case 'a': /* '\' 'a' bell */
1248eb6bc482Sderaadt strbuf[0] = '\007';
1249eb6bc482Sderaadt strbuf[1] = '\0';
1250eb6bc482Sderaadt break;
1251eb6bc482Sderaadt case 'd': /* '\' 'd' Dow Mon DD */
1252eb6bc482Sderaadt time(&t);
1253eb6bc482Sderaadt tm = localtime(&t);
1254*55449a4bSflorian if (tm)
1255*55449a4bSflorian strftime(strbuf, sizeof strbuf,
1256*55449a4bSflorian "%a %b %d", tm);
1257*55449a4bSflorian else
1258*55449a4bSflorian strbuf[0] = '\0';
1259eb6bc482Sderaadt break;
1260eb6bc482Sderaadt case 'D': /* '\' 'D' '{' strftime format '}' */
1261eb6bc482Sderaadt p = strchr(cp + 2, '}');
1262eb6bc482Sderaadt if (cp[1] != '{' || p == NULL) {
1263eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf,
1264eb6bc482Sderaadt "\\%c", *cp);
1265eb6bc482Sderaadt break;
1266eb6bc482Sderaadt }
1267eb6bc482Sderaadt strlcpy(tmpbuf, cp + 2, sizeof tmpbuf);
1268eb6bc482Sderaadt p = strchr(tmpbuf, '}');
1269eb6bc482Sderaadt if (p)
1270eb6bc482Sderaadt *p = '\0';
1271eb6bc482Sderaadt time(&t);
1272eb6bc482Sderaadt tm = localtime(&t);
1273*55449a4bSflorian if (tm)
1274*55449a4bSflorian strftime(strbuf, sizeof strbuf, tmpbuf,
1275*55449a4bSflorian tm);
1276*55449a4bSflorian else
1277*55449a4bSflorian strbuf[0] = '\0';
1278eb6bc482Sderaadt cp = strchr(cp + 2, '}');
1279eb6bc482Sderaadt break;
1280eb6bc482Sderaadt case 'e': /* '\' 'e' escape */
1281eb6bc482Sderaadt strbuf[0] = '\033';
1282eb6bc482Sderaadt strbuf[1] = '\0';
1283eb6bc482Sderaadt break;
1284eb6bc482Sderaadt case 'h': /* '\' 'h' shortened hostname */
1285eb6bc482Sderaadt gethostname(strbuf, sizeof strbuf);
1286eb6bc482Sderaadt p = strchr(strbuf, '.');
1287eb6bc482Sderaadt if (p)
1288eb6bc482Sderaadt *p = '\0';
1289eb6bc482Sderaadt break;
1290eb6bc482Sderaadt case 'H': /* '\' 'H' full hostname */
1291eb6bc482Sderaadt gethostname(strbuf, sizeof strbuf);
1292eb6bc482Sderaadt break;
1293eb6bc482Sderaadt case 'j': /* '\' 'j' number of jobs */
1294eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%d",
1295eb6bc482Sderaadt j_njobs());
1296eb6bc482Sderaadt break;
1297eb6bc482Sderaadt case 'l': /* '\' 'l' basename of tty */
1298eb6bc482Sderaadt p = ttyname(0);
1299eb6bc482Sderaadt if (p)
1300eb6bc482Sderaadt p = basename(p);
1301eb6bc482Sderaadt if (p)
1302eb6bc482Sderaadt strlcpy(strbuf, p, sizeof strbuf);
1303eb6bc482Sderaadt break;
1304eb6bc482Sderaadt case 'n': /* '\' 'n' newline */
1305eb6bc482Sderaadt strbuf[0] = '\n';
1306eb6bc482Sderaadt strbuf[1] = '\0';
13075c4a5744Sderaadt totlen = 0; /* reset for prompt re-print */
13085c4a5744Sderaadt sp = cp + 1;
1309eb6bc482Sderaadt break;
13106aa3ef4bSdrahn case 'p': /* '\' '$' $ or # */
13116aa3ef4bSdrahn strbuf[0] = ksheuid ? '$' : '#';
13126aa3ef4bSdrahn strbuf[1] = '\0';
13136aa3ef4bSdrahn break;
1314eb6bc482Sderaadt case 'r': /* '\' 'r' return */
1315eb6bc482Sderaadt strbuf[0] = '\r';
1316eb6bc482Sderaadt strbuf[1] = '\0';
13175c4a5744Sderaadt totlen = 0; /* reset for prompt re-print */
13185c4a5744Sderaadt sp = cp + 1;
1319eb6bc482Sderaadt break;
1320eb6bc482Sderaadt case 's': /* '\' 's' basename $0 */
1321eb6bc482Sderaadt strlcpy(strbuf, kshname, sizeof strbuf);
1322eb6bc482Sderaadt break;
1323eb6bc482Sderaadt case 't': /* '\' 't' 24 hour HH:MM:SS */
1324eb6bc482Sderaadt time(&t);
1325eb6bc482Sderaadt tm = localtime(&t);
1326*55449a4bSflorian if (tm)
1327*55449a4bSflorian strftime(strbuf, sizeof strbuf, "%T",
1328*55449a4bSflorian tm);
1329*55449a4bSflorian else
1330*55449a4bSflorian strbuf[0] = '\0';
1331eb6bc482Sderaadt break;
1332eb6bc482Sderaadt case 'T': /* '\' 'T' 12 hour HH:MM:SS */
1333eb6bc482Sderaadt time(&t);
1334eb6bc482Sderaadt tm = localtime(&t);
1335*55449a4bSflorian if (tm)
1336*55449a4bSflorian strftime(strbuf, sizeof strbuf,
1337*55449a4bSflorian "%l:%M:%S", tm);
1338*55449a4bSflorian else
1339*55449a4bSflorian strbuf[0] = '\0';
1340eb6bc482Sderaadt break;
1341eb6bc482Sderaadt case '@': /* '\' '@' 12 hour am/pm format */
1342eb6bc482Sderaadt time(&t);
1343eb6bc482Sderaadt tm = localtime(&t);
1344*55449a4bSflorian if (tm)
1345*55449a4bSflorian strftime(strbuf, sizeof strbuf, "%r",
1346*55449a4bSflorian tm);
1347*55449a4bSflorian else
1348*55449a4bSflorian strbuf[0] = '\0';
1349eb6bc482Sderaadt break;
1350eb6bc482Sderaadt case 'A': /* '\' 'A' 24 hour HH:MM */
1351eb6bc482Sderaadt time(&t);
1352eb6bc482Sderaadt tm = localtime(&t);
1353*55449a4bSflorian if (tm)
1354*55449a4bSflorian strftime(strbuf, sizeof strbuf, "%R",
1355*55449a4bSflorian tm);
1356*55449a4bSflorian else
1357*55449a4bSflorian strbuf[0] = '\0';
1358eb6bc482Sderaadt break;
1359eb6bc482Sderaadt case 'u': /* '\' 'u' username */
1360f351fb1cSotto strlcpy(strbuf, username, sizeof strbuf);
1361eb6bc482Sderaadt break;
13627fada9fdSkn #ifndef SMALL
1363eb6bc482Sderaadt case 'v': /* '\' 'v' version (short) */
1364eb6bc482Sderaadt p = strchr(ksh_version, ' ');
1365eb6bc482Sderaadt if (p)
1366eb6bc482Sderaadt p = strchr(p + 1, ' ');
1367eb6bc482Sderaadt if (p) {
1368eb6bc482Sderaadt p++;
1369eb6bc482Sderaadt strlcpy(strbuf, p, sizeof strbuf);
1370eb6bc482Sderaadt p = strchr(strbuf, ' ');
1371eb6bc482Sderaadt if (p)
1372eb6bc482Sderaadt *p = '\0';
1373eb6bc482Sderaadt }
1374eb6bc482Sderaadt break;
1375eb6bc482Sderaadt case 'V': /* '\' 'V' version (long) */
1376eb6bc482Sderaadt strlcpy(strbuf, ksh_version, sizeof strbuf);
1377eb6bc482Sderaadt break;
13787fada9fdSkn #endif /* SMALL */
1379eb6bc482Sderaadt case 'w': /* '\' 'w' cwd */
1380eb6bc482Sderaadt p = str_val(global("PWD"));
1381ad55f791Sderaadt n = strlen(str_val(global("HOME")));
1382ad55f791Sderaadt if (strcmp(p, "/") == 0) {
1383ad55f791Sderaadt strlcpy(strbuf, p, sizeof strbuf);
1384ad55f791Sderaadt } else if (strcmp(p, str_val(global("HOME"))) == 0) {
1385eb6bc482Sderaadt strbuf[0] = '~';
1386eb6bc482Sderaadt strbuf[1] = '\0';
1387ad55f791Sderaadt } else if (strncmp(p, str_val(global("HOME")), n)
1388ad55f791Sderaadt == 0 && p[n] == '/') {
1389ad55f791Sderaadt snprintf(strbuf, sizeof strbuf, "~/%s",
1390ad55f791Sderaadt str_val(global("PWD")) + n + 1);
1391eb6bc482Sderaadt } else
1392eb6bc482Sderaadt strlcpy(strbuf, p, sizeof strbuf);
1393eb6bc482Sderaadt break;
1394eb6bc482Sderaadt case 'W': /* '\' 'W' basename(cwd) */
1395eb6bc482Sderaadt p = str_val(global("PWD"));
139630e9b9efSokan if (strcmp(p, str_val(global("HOME"))) == 0) {
139730e9b9efSokan strbuf[0] = '~';
139830e9b9efSokan strbuf[1] = '\0';
139930e9b9efSokan } else
1400eb6bc482Sderaadt strlcpy(strbuf, basename(p), sizeof strbuf);
1401eb6bc482Sderaadt break;
1402e7b1290aSotto case '!': /* '\' '!' history line number */
1403eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%d",
1404eb6bc482Sderaadt source->line + 1);
1405eb6bc482Sderaadt break;
1406e7b1290aSotto case '#': /* '\' '#' command line number */
1407eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%d",
1408e7b1290aSotto source->line - source->cmd_offset + 1);
1409eb6bc482Sderaadt break;
1410eb6bc482Sderaadt case '0': /* '\' '#' '#' ' #' octal numeric handling */
1411eb6bc482Sderaadt case '1':
1412eb6bc482Sderaadt case '2':
1413eb6bc482Sderaadt case '3':
1414eb6bc482Sderaadt case '4':
1415eb6bc482Sderaadt case '5':
1416eb6bc482Sderaadt case '6':
1417eb6bc482Sderaadt case '7':
1418eb6bc482Sderaadt if ((cp[1] > '7' || cp[1] < '0') ||
1419eb6bc482Sderaadt (cp[2] > '7' || cp[2] < '0')) {
1420eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf,
1421eb6bc482Sderaadt "\\%c", *cp);
1422eb6bc482Sderaadt break;
1423eb6bc482Sderaadt }
1424064356ecSzhuk n = (cp[0] - '0') * 8 * 8 + (cp[1] - '0') * 8 +
1425064356ecSzhuk (cp[2] - '0');
1426eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "%c", n);
1427eb6bc482Sderaadt cp += 2;
1428eb6bc482Sderaadt break;
1429eb6bc482Sderaadt case '\\': /* '\' '\' */
1430eb6bc482Sderaadt strbuf[0] = '\\';
1431eb6bc482Sderaadt strbuf[1] = '\0';
1432eb6bc482Sderaadt break;
1433a1344090Sderaadt case '[': /* '\' '[' .... stop counting */
1434a1344090Sderaadt strbuf[0] = '\0';
1435a1344090Sderaadt counting = 0;
1436eb6bc482Sderaadt break;
1437a1344090Sderaadt case ']': /* '\' ']' restart counting */
1438a1344090Sderaadt strbuf[0] = '\0';
1439a1344090Sderaadt counting = 1;
1440eb6bc482Sderaadt break;
1441eb6bc482Sderaadt
1442eb6bc482Sderaadt default:
1443eb6bc482Sderaadt snprintf(strbuf, sizeof strbuf, "\\%c", *cp);
1444eb6bc482Sderaadt break;
1445eb6bc482Sderaadt }
1446eb6bc482Sderaadt cp++;
1447eb6bc482Sderaadt
1448eb6bc482Sderaadt str = strbuf;
1449eb6bc482Sderaadt len = strlen(str);
1450eb6bc482Sderaadt if (ntruncate) {
1451eb6bc482Sderaadt if (ntruncate >= len) {
1452eb6bc482Sderaadt ntruncate -= len;
1453eb6bc482Sderaadt continue;
1454eb6bc482Sderaadt }
1455eb6bc482Sderaadt str += ntruncate;
1456eb6bc482Sderaadt len -= ntruncate;
1457eb6bc482Sderaadt ntruncate = 0;
1458eb6bc482Sderaadt }
1459eb6bc482Sderaadt if (doprint)
1460eb6bc482Sderaadt shf_write(str, len, shl_out);
1461a1344090Sderaadt if (counting && !indelimit && !delimitthis)
1462eb6bc482Sderaadt totlen += len;
1463eb6bc482Sderaadt continue;
1464eb6bc482Sderaadt } else if (*cp != '!')
14657cb960a2Sdownsj c = *cp++;
14667cb960a2Sdownsj else if (*++cp == '!')
14677cb960a2Sdownsj c = *cp++;
14687cb960a2Sdownsj else {
14697cb960a2Sdownsj shf_snprintf(p = nbuf, sizeof(nbuf), "%d",
14707cb960a2Sdownsj source->line + 1);
14717cb960a2Sdownsj len = strlen(nbuf);
14727cb960a2Sdownsj if (ntruncate) {
14737cb960a2Sdownsj if (ntruncate >= len) {
14747cb960a2Sdownsj ntruncate -= len;
14757cb960a2Sdownsj continue;
14767cb960a2Sdownsj }
14777cb960a2Sdownsj p += ntruncate;
14787cb960a2Sdownsj len -= ntruncate;
14797cb960a2Sdownsj ntruncate = 0;
14807cb960a2Sdownsj }
1481eb6bc482Sderaadt if (doprint)
14827cb960a2Sdownsj shf_write(p, len, shl_out);
1483a1344090Sderaadt if (counting && !indelimit && !delimitthis)
1484eb6bc482Sderaadt totlen += len;
14857cb960a2Sdownsj continue;
14867cb960a2Sdownsj }
1487638b9e76Sbeck if (counting && ntruncate)
14887cb960a2Sdownsj --ntruncate;
1489eb6bc482Sderaadt else if (doprint) {
14907cb960a2Sdownsj shf_putc(c, shl_out);
14917cb960a2Sdownsj }
1492a1344090Sderaadt if (counting && !indelimit && !delimitthis)
1493eb6bc482Sderaadt totlen++;
1494eb6bc482Sderaadt }
1495eb6bc482Sderaadt if (doprint)
14967cb960a2Sdownsj shf_flush(shl_out);
1497eb6bc482Sderaadt if (spp)
1498eb6bc482Sderaadt *spp = sp;
1499eb6bc482Sderaadt return (totlen);
1500eb6bc482Sderaadt }
1501eb6bc482Sderaadt
1502eb6bc482Sderaadt void
pprompt(const char * cp,int ntruncate)1503c5d5393cSotto pprompt(const char *cp, int ntruncate)
1504eb6bc482Sderaadt {
1505eb6bc482Sderaadt dopprompt(cp, ntruncate, NULL, 1);
1506eb6bc482Sderaadt }
1507eb6bc482Sderaadt
1508eb6bc482Sderaadt int
promptlen(const char * cp,const char ** spp)1509c5d5393cSotto promptlen(const char *cp, const char **spp)
1510eb6bc482Sderaadt {
1511eb6bc482Sderaadt return dopprompt(cp, 0, spp, 0);
15127cb960a2Sdownsj }
15137cb960a2Sdownsj
15147cb960a2Sdownsj /* Read the variable part of a ${...} expression (ie, up to but not including
15157cb960a2Sdownsj * the :[-+?=#%] or close-brace.
15167cb960a2Sdownsj */
15177cb960a2Sdownsj static char *
get_brace_var(XString * wsp,char * wp)1518c5d5393cSotto get_brace_var(XString *wsp, char *wp)
15197cb960a2Sdownsj {
15207cb960a2Sdownsj enum parse_state {
15217cb960a2Sdownsj PS_INITIAL, PS_SAW_HASH, PS_IDENT,
15227cb960a2Sdownsj PS_NUMBER, PS_VAR1, PS_END
15237cb960a2Sdownsj }
15247cb960a2Sdownsj state;
15257cb960a2Sdownsj char c;
15267cb960a2Sdownsj
15277cb960a2Sdownsj state = PS_INITIAL;
15287cb960a2Sdownsj while (1) {
1529e55c1b2cSdownsj c = getsc();
15307cb960a2Sdownsj /* State machine to figure out where the variable part ends. */
15317cb960a2Sdownsj switch (state) {
15327cb960a2Sdownsj case PS_INITIAL:
15337cb960a2Sdownsj if (c == '#') {
15347cb960a2Sdownsj state = PS_SAW_HASH;
15357cb960a2Sdownsj break;
15367cb960a2Sdownsj }
15377eab881bSjaredy /* FALLTHROUGH */
15387cb960a2Sdownsj case PS_SAW_HASH:
15397cb960a2Sdownsj if (letter(c))
15407cb960a2Sdownsj state = PS_IDENT;
15417cb960a2Sdownsj else if (digit(c))
15427cb960a2Sdownsj state = PS_NUMBER;
15437cb960a2Sdownsj else if (ctype(c, C_VAR1))
15447cb960a2Sdownsj state = PS_VAR1;
15457cb960a2Sdownsj else
15467cb960a2Sdownsj state = PS_END;
15477cb960a2Sdownsj break;
15487cb960a2Sdownsj case PS_IDENT:
15497cb960a2Sdownsj if (!letnum(c)) {
15507cb960a2Sdownsj state = PS_END;
15517cb960a2Sdownsj if (c == '[') {
15527cb960a2Sdownsj char *tmp, *p;
15537cb960a2Sdownsj
15547cb960a2Sdownsj if (!arraysub(&tmp))
15557cb960a2Sdownsj yyerror("missing ]\n");
15567cb960a2Sdownsj *wp++ = c;
15577cb960a2Sdownsj for (p = tmp; *p; ) {
15587cb960a2Sdownsj Xcheck(*wsp, wp);
15597cb960a2Sdownsj *wp++ = *p++;
15607cb960a2Sdownsj }
15617cb960a2Sdownsj afree(tmp, ATEMP);
1562e55c1b2cSdownsj c = getsc(); /* the ] */
15637cb960a2Sdownsj }
15647cb960a2Sdownsj }
15657cb960a2Sdownsj break;
15667cb960a2Sdownsj case PS_NUMBER:
15677cb960a2Sdownsj if (!digit(c))
15687cb960a2Sdownsj state = PS_END;
15697cb960a2Sdownsj break;
15707cb960a2Sdownsj case PS_VAR1:
15717cb960a2Sdownsj state = PS_END;
15727cb960a2Sdownsj break;
15737cb960a2Sdownsj case PS_END: /* keep gcc happy */
15747cb960a2Sdownsj break;
15757cb960a2Sdownsj }
15767cb960a2Sdownsj if (state == PS_END) {
15777cb960a2Sdownsj *wp++ = '\0'; /* end of variable part */
15787cb960a2Sdownsj ungetsc(c);
15797cb960a2Sdownsj break;
15807cb960a2Sdownsj }
15817cb960a2Sdownsj Xcheck(*wsp, wp);
15827cb960a2Sdownsj *wp++ = c;
15837cb960a2Sdownsj }
15847cb960a2Sdownsj return wp;
15857cb960a2Sdownsj }
15867cb960a2Sdownsj
15877cb960a2Sdownsj /*
15887cb960a2Sdownsj * Save an array subscript - returns true if matching bracket found, false
15897cb960a2Sdownsj * if eof or newline was found.
15907cb960a2Sdownsj * (Returned string double null terminated)
15917cb960a2Sdownsj */
15927cb960a2Sdownsj static int
arraysub(char ** strp)1593c5d5393cSotto arraysub(char **strp)
15947cb960a2Sdownsj {
15957cb960a2Sdownsj XString ws;
15967cb960a2Sdownsj char *wp;
15977cb960a2Sdownsj char c;
15987cb960a2Sdownsj int depth = 1; /* we are just past the initial [ */
15997cb960a2Sdownsj
16007cb960a2Sdownsj Xinit(ws, wp, 32, ATEMP);
16017cb960a2Sdownsj
16027cb960a2Sdownsj do {
1603e55c1b2cSdownsj c = getsc();
16047cb960a2Sdownsj Xcheck(ws, wp);
16057cb960a2Sdownsj *wp++ = c;
16067cb960a2Sdownsj if (c == '[')
16077cb960a2Sdownsj depth++;
16087cb960a2Sdownsj else if (c == ']')
16097cb960a2Sdownsj depth--;
16107cb960a2Sdownsj } while (depth > 0 && c && c != '\n');
16117cb960a2Sdownsj
16127cb960a2Sdownsj *wp++ = '\0';
16137cb960a2Sdownsj *strp = Xclose(ws, wp);
16147cb960a2Sdownsj
16157cb960a2Sdownsj return depth == 0 ? 1 : 0;
16167cb960a2Sdownsj }
16177cb960a2Sdownsj
16187cb960a2Sdownsj /* Unget a char: handles case when we are already at the start of the buffer */
16197cb960a2Sdownsj static const char *
ungetsc(int c)1620c5d5393cSotto ungetsc(int c)
16217cb960a2Sdownsj {
1622e55c1b2cSdownsj if (backslash_skip)
1623e55c1b2cSdownsj backslash_skip--;
16247cb960a2Sdownsj /* Don't unget eof... */
16257cb960a2Sdownsj if (source->str == null && c == '\0')
16267cb960a2Sdownsj return source->str;
16277cb960a2Sdownsj if (source->str > source->start)
16287cb960a2Sdownsj source->str--;
16297cb960a2Sdownsj else {
16307cb960a2Sdownsj Source *s;
16317cb960a2Sdownsj
16327cb960a2Sdownsj s = pushs(SREREAD, source->areap);
1633e55c1b2cSdownsj s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1634e55c1b2cSdownsj s->start = s->str = s->ugbuf;
16357cb960a2Sdownsj s->next = source;
16367cb960a2Sdownsj source = s;
16377cb960a2Sdownsj }
16387cb960a2Sdownsj return source->str;
16397cb960a2Sdownsj }
16407cb960a2Sdownsj
1641e55c1b2cSdownsj
16427cb960a2Sdownsj /* Called to get a char that isn't a \newline sequence. */
16437cb960a2Sdownsj static int
getsc_bn(void)164469b9f96bSmillert getsc_bn(void)
16457cb960a2Sdownsj {
1646e55c1b2cSdownsj int c, c2;
1647e55c1b2cSdownsj
1648e55c1b2cSdownsj if (ignore_backslash_newline)
1649e55c1b2cSdownsj return getsc_();
1650e55c1b2cSdownsj
1651e55c1b2cSdownsj if (backslash_skip == 1) {
1652e55c1b2cSdownsj backslash_skip = 2;
1653e55c1b2cSdownsj return getsc_();
1654e55c1b2cSdownsj }
1655e55c1b2cSdownsj
1656e55c1b2cSdownsj backslash_skip = 0;
16577cb960a2Sdownsj
16587cb960a2Sdownsj while (1) {
16597cb960a2Sdownsj c = getsc_();
1660e55c1b2cSdownsj if (c == '\\') {
1661e55c1b2cSdownsj if ((c2 = getsc_()) == '\n')
16627cb960a2Sdownsj /* ignore the \newline; get the next char... */
1663e55c1b2cSdownsj continue;
1664e55c1b2cSdownsj ungetsc(c2);
1665e55c1b2cSdownsj backslash_skip = 1;
1666e55c1b2cSdownsj }
1667e55c1b2cSdownsj return c;
16687cb960a2Sdownsj }
16697cb960a2Sdownsj }
16703b015934Smillert
16713b015934Smillert static Lex_state *
push_state_(State_info * si,Lex_state * old_end)1672c5d5393cSotto push_state_(State_info *si, Lex_state *old_end)
16733b015934Smillert {
1674d67c3782Smmcc Lex_state *new = areallocarray(NULL, STATE_BSIZE,
1675d67c3782Smmcc sizeof(Lex_state), ATEMP);
16763b015934Smillert
16773b015934Smillert new[0].ls_info.base = old_end;
16783b015934Smillert si->base = &new[0];
16793b015934Smillert si->end = &new[STATE_BSIZE];
16803b015934Smillert return &new[1];
16813b015934Smillert }
16823b015934Smillert
16833b015934Smillert static Lex_state *
pop_state_(State_info * si,Lex_state * old_end)1684c5d5393cSotto pop_state_(State_info *si, Lex_state *old_end)
16853b015934Smillert {
16863b015934Smillert Lex_state *old_base = si->base;
16873b015934Smillert
16883b015934Smillert si->base = old_end->ls_info.base - STATE_BSIZE;
16893b015934Smillert si->end = old_end->ls_info.base;
16903b015934Smillert
16913b015934Smillert afree(old_base, ATEMP);
16923b015934Smillert
16937b425235Smillert return si->base + STATE_BSIZE - 1;
16943b015934Smillert }
1695