blob: 78c2ee798808424dfb7cd06f107bbfac1b90445c [file] [log] [blame]
Elliott Hughesfc0307d2016-02-02 15:26:47 -08001/* $OpenBSD: lex.c,v 1.51 2015/09/10 22:48:58 nicm Exp $ */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002
3/*-
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00004 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
Elliott Hughesa3c3f962017-04-12 16:52:30 -07005 * 2011, 2012, 2013, 2014, 2015, 2016, 2017
Elliott Hughesfc0307d2016-02-02 15:26:47 -08006 * mirabilos <m@mirbsd.org>
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07007 *
8 * Provided that these terms and disclaimer and all copyright notices
9 * are retained or reproduced in an accompanying document, permission
10 * is granted to deal in this work without restriction, including un-
11 * limited rights to use, publicly perform, distribute, sell, modify,
12 * merge, give away, or sublicence.
13 *
14 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
15 * the utmost extent permitted by applicable law, neither express nor
16 * implied; without malicious intent or gross negligence. In no event
17 * may a licensor, author or contributor be held liable for indirect,
18 * direct, other damage, loss, or other issues arising in any way out
19 * of dealing in the work, even if advised of the possibility of such
20 * damage or existence of a defect, except proven that it results out
21 * of said person's immediate fault when using the work as intended.
22 */
23
24#include "sh.h"
25
Elliott Hughesa3c3f962017-04-12 16:52:30 -070026__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.234 2017/04/06 01:59:55 tg Exp $");
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070027
28/*
29 * states while lexing word
30 */
31#define SBASE 0 /* outside any lexical constructs */
32#define SWORD 1 /* implicit quoting for substitute() */
33#define SLETPAREN 2 /* inside (( )), implicit quoting */
34#define SSQUOTE 3 /* inside '' */
35#define SDQUOTE 4 /* inside "" */
36#define SEQUOTE 5 /* inside $'' */
37#define SBRACE 6 /* inside ${} */
38#define SQBRACE 7 /* inside "${}" */
Geremy Condra03ebf062011-10-12 18:17:24 -070039#define SBQUOTE 8 /* inside `` */
40#define SASPAREN 9 /* inside $(( )) */
Elliott Hughesfc0307d2016-02-02 15:26:47 -080041#define SHEREDELIM 10 /* parsing << or <<- delimiter */
42#define SHEREDQUOTE 11 /* parsing " in << or <<- delimiter */
Geremy Condra03ebf062011-10-12 18:17:24 -070043#define SPATTERN 12 /* parsing *(...|...) pattern (*+?@!) */
44#define SADELIM 13 /* like SBASE, looking for delimiter */
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +000045#define STBRACEKORN 14 /* parsing ${...[#%]...} !FSH */
46#define STBRACEBOURNE 15 /* parsing ${...[#%]...} FSH */
Geremy Condra03ebf062011-10-12 18:17:24 -070047#define SINVALID 255 /* invalid state */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070048
Geremy Condra03ebf062011-10-12 18:17:24 -070049struct sretrace_info {
50 struct sretrace_info *next;
51 XString xs;
52 char *xp;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070053};
54
Geremy Condra03ebf062011-10-12 18:17:24 -070055/*
56 * Structure to keep track of the lexing state and the various pieces of info
57 * needed for each particular state.
58 */
59typedef struct lex_state {
60 union {
61 /* point to the next state block */
62 struct lex_state *base;
63 /* marks start of state output in output string */
Elliott Hughesfc0307d2016-02-02 15:26:47 -080064 size_t start;
Geremy Condra03ebf062011-10-12 18:17:24 -070065 /* SBQUOTE: true if in double quotes: "`...`" */
66 /* SEQUOTE: got NUL, ignore rest of string */
67 bool abool;
68 /* SADELIM information */
69 struct {
70 /* character to search for */
71 unsigned char delimiter;
72 /* max. number of delimiters */
73 unsigned char num;
74 } adelim;
75 } u;
76 /* count open parentheses */
77 short nparen;
78 /* type of this state */
79 uint8_t type;
80} Lex_state;
81#define ls_base u.base
82#define ls_start u.start
83#define ls_bool u.abool
84#define ls_adelim u.adelim
85
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070086typedef struct {
87 Lex_state *base;
88 Lex_state *end;
89} State_info;
90
91static void readhere(struct ioword *);
Geremy Condra03ebf062011-10-12 18:17:24 -070092static void ungetsc(int);
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +000093static void ungetsc_i(int);
Geremy Condra03ebf062011-10-12 18:17:24 -070094static int getsc_uu(void);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070095static void getsc_line(Source *);
96static int getsc_bn(void);
Elliott Hughesfc0307d2016-02-02 15:26:47 -080097static int getsc_i(void);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070098static char *get_brace_var(XString *, char *);
Geremy Condra03ebf062011-10-12 18:17:24 -070099static bool arraysub(char **);
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800100static void gethere(void);
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000101static Lex_state *push_state_i(State_info *, Lex_state *);
102static Lex_state *pop_state_i(State_info *, Lex_state *);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700103
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700104static int backslash_skip;
105static int ignore_backslash_newline;
106
107/* optimised getsc_bn() */
Geremy Condra03ebf062011-10-12 18:17:24 -0700108#define o_getsc() (*source->str != '\0' && *source->str != '\\' && \
109 !backslash_skip ? *source->str++ : getsc_bn())
110/* optimised getsc_uu() */
111#define o_getsc_u() ((*source->str != '\0') ? *source->str++ : getsc_uu())
112
113/* retrace helper */
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800114#define o_getsc_r(carg) \
Geremy Condra03ebf062011-10-12 18:17:24 -0700115 int cev = (carg); \
116 struct sretrace_info *rp = retrace_info; \
117 \
118 while (rp) { \
119 Xcheck(rp->xs, rp->xp); \
120 *rp->xp++ = cev; \
121 rp = rp->next; \
122 } \
123 \
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800124 return (cev);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700125
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800126/* callback */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700127static int
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800128getsc_i(void)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700129{
Geremy Condra03ebf062011-10-12 18:17:24 -0700130 o_getsc_r(o_getsc());
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700131}
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800132
133#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
134#define getsc getsc_i
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700135#else
Geremy Condra03ebf062011-10-12 18:17:24 -0700136static int getsc_r(int);
137
138static int
139getsc_r(int c)
140{
141 o_getsc_r(c);
142}
143
144#define getsc() getsc_r(o_getsc())
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700145#endif
146
Geremy Condra03ebf062011-10-12 18:17:24 -0700147#define STATE_BSIZE 8
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700148
149#define PUSH_STATE(s) do { \
150 if (++statep == state_info.end) \
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000151 statep = push_state_i(&state_info, statep); \
Geremy Condra03ebf062011-10-12 18:17:24 -0700152 state = statep->type = (s); \
153} while (/* CONSTCOND */ 0)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700154
155#define POP_STATE() do { \
156 if (--statep == state_info.base) \
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000157 statep = pop_state_i(&state_info, statep); \
Geremy Condra03ebf062011-10-12 18:17:24 -0700158 state = statep->type; \
159} while (/* CONSTCOND */ 0)
160
Elliott Hughes737fdce2014-08-07 12:59:26 -0700161#define PUSH_SRETRACE(s) do { \
Geremy Condra03ebf062011-10-12 18:17:24 -0700162 struct sretrace_info *ri; \
163 \
Elliott Hughes737fdce2014-08-07 12:59:26 -0700164 PUSH_STATE(s); \
Geremy Condra03ebf062011-10-12 18:17:24 -0700165 statep->ls_start = Xsavepos(ws, wp); \
166 ri = alloc(sizeof(struct sretrace_info), ATEMP); \
167 Xinit(ri->xs, ri->xp, 64, ATEMP); \
168 ri->next = retrace_info; \
169 retrace_info = ri; \
170} while (/* CONSTCOND */ 0)
171
172#define POP_SRETRACE() do { \
173 wp = Xrestpos(ws, wp, statep->ls_start); \
174 *retrace_info->xp = '\0'; \
175 sp = Xstring(retrace_info->xs, retrace_info->xp); \
176 dp = (void *)retrace_info; \
177 retrace_info = retrace_info->next; \
178 afree(dp, ATEMP); \
Elliott Hughes737fdce2014-08-07 12:59:26 -0700179 POP_STATE(); \
Geremy Condra03ebf062011-10-12 18:17:24 -0700180} while (/* CONSTCOND */ 0)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700181
182/**
183 * Lexical analyser
184 *
185 * tokens are not regular expressions, they are LL(1).
186 * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
Geremy Condra03ebf062011-10-12 18:17:24 -0700187 * hence the state stack. Note "$(...)" are now parsed recursively.
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700188 */
189
190int
191yylex(int cf)
192{
193 Lex_state states[STATE_BSIZE], *statep, *s2, *base;
194 State_info state_info;
195 int c, c2, state;
Geremy Condra03ebf062011-10-12 18:17:24 -0700196 size_t cz;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700197 XString ws; /* expandable output word */
198 char *wp; /* output word pointer */
199 char *sp, *dp;
200
201 Again:
Geremy Condra03ebf062011-10-12 18:17:24 -0700202 states[0].type = SINVALID;
203 states[0].ls_base = NULL;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700204 statep = &states[1];
205 state_info.base = states;
206 state_info.end = &state_info.base[STATE_BSIZE];
207
208 Xinit(ws, wp, 64, ATEMP);
209
210 backslash_skip = 0;
211 ignore_backslash_newline = 0;
212
Geremy Condra03ebf062011-10-12 18:17:24 -0700213 if (cf & ONEWORD)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700214 state = SWORD;
Geremy Condra03ebf062011-10-12 18:17:24 -0700215 else if (cf & LETEXPR) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700216 /* enclose arguments in (double) quotes */
217 *wp++ = OQUOTE;
218 state = SLETPAREN;
Geremy Condra03ebf062011-10-12 18:17:24 -0700219 statep->nparen = 0;
220 } else {
221 /* normal lexing */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700222 state = (cf & HEREDELIM) ? SHEREDELIM : SBASE;
223 while ((c = getsc()) == ' ' || c == '\t')
224 ;
225 if (c == '#') {
226 ignore_backslash_newline++;
227 while ((c = getsc()) != '\0' && c != '\n')
228 ;
229 ignore_backslash_newline--;
230 }
231 ungetsc(c);
232 }
Geremy Condra03ebf062011-10-12 18:17:24 -0700233 if (source->flags & SF_ALIAS) {
234 /* trailing ' ' in alias definition */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700235 source->flags &= ~SF_ALIAS;
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800236 /* POSIX: trailing space only counts if parsing simple cmd */
237 if (!Flag(FPOSIX) || (cf & CMDWORD))
238 cf |= ALIAS;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700239 }
240
Geremy Condra03ebf062011-10-12 18:17:24 -0700241 /* Initial state: one of SWORD SLETPAREN SHEREDELIM SBASE */
242 statep->type = state;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700243
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700244 /* collect non-special or quoted characters to form word */
245 while (!((c = getsc()) == 0 ||
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000246 ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) {
247 if (state == SBASE &&
248 subshell_nesting_type == /*{*/ '}' &&
249 c == /*{*/ '}')
250 /* possibly end ${ :;} */
251 break;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700252 Xcheck(ws, wp);
253 switch (state) {
254 case SADELIM:
255 if (c == '(')
Geremy Condra03ebf062011-10-12 18:17:24 -0700256 statep->nparen++;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700257 else if (c == ')')
Geremy Condra03ebf062011-10-12 18:17:24 -0700258 statep->nparen--;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000259 else if (statep->nparen == 0 && (c == /*{*/ '}' ||
260 c == (int)statep->ls_adelim.delimiter)) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700261 *wp++ = ADELIM;
262 *wp++ = c;
Geremy Condra03ebf062011-10-12 18:17:24 -0700263 if (c == /*{*/ '}' || --statep->ls_adelim.num == 0)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700264 POP_STATE();
265 if (c == /*{*/ '}')
266 POP_STATE();
267 break;
268 }
269 /* FALLTHROUGH */
270 case SBASE:
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800271 if (c == '[' && (cf & CMDASN)) {
Geremy Condra03ebf062011-10-12 18:17:24 -0700272 /* temporary */
273 *wp = EOS;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700274 if (is_wdvarname(Xstring(ws, wp), false)) {
275 char *p, *tmp;
276
277 if (arraysub(&tmp)) {
278 *wp++ = CHAR;
279 *wp++ = c;
280 for (p = tmp; *p; ) {
281 Xcheck(ws, wp);
282 *wp++ = CHAR;
283 *wp++ = *p++;
284 }
285 afree(tmp, ATEMP);
286 break;
287 } else {
288 Source *s;
289
290 s = pushs(SREREAD,
291 source->areap);
292 s->start = s->str =
293 s->u.freeme = tmp;
294 s->next = source;
295 source = s;
296 }
297 }
298 *wp++ = CHAR;
299 *wp++ = c;
300 break;
301 }
302 /* FALLTHROUGH */
303 Sbase1: /* includes *(...|...) pattern (*+?@!) */
304 if (c == '*' || c == '@' || c == '+' || c == '?' ||
305 c == '!') {
306 c2 = getsc();
307 if (c2 == '(' /*)*/ ) {
308 *wp++ = OPAT;
309 *wp++ = c;
310 PUSH_STATE(SPATTERN);
311 break;
312 }
313 ungetsc(c2);
314 }
315 /* FALLTHROUGH */
316 Sbase2: /* doesn't include *(...|...) pattern (*+?@!) */
317 switch (c) {
318 case '\\':
319 getsc_qchar:
320 if ((c = getsc())) {
321 /* trailing \ is lost */
322 *wp++ = QCHAR;
323 *wp++ = c;
324 }
325 break;
326 case '\'':
Thorsten Glaser811a5752013-07-25 14:24:45 +0000327 open_ssquote_unless_heredoc:
328 if ((cf & HEREDOC))
329 goto store_char;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700330 *wp++ = OQUOTE;
331 ignore_backslash_newline++;
332 PUSH_STATE(SSQUOTE);
333 break;
334 case '"':
335 open_sdquote:
336 *wp++ = OQUOTE;
337 PUSH_STATE(SDQUOTE);
338 break;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000339 case '$':
340 /*
341 * processing of dollar sign belongs into
342 * Subst, except for those which can open
343 * a string: $'…' and $"…"
344 */
345 subst_dollar_ex:
346 c = getsc();
347 switch (c) {
348 case '"':
349 goto open_sdquote;
350 case '\'':
351 goto open_sequote;
352 default:
353 goto SubstS;
354 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700355 default:
356 goto Subst;
357 }
358 break;
359
360 Subst:
361 switch (c) {
362 case '\\':
363 c = getsc();
364 switch (c) {
365 case '"':
366 if ((cf & HEREDOC))
367 goto heredocquote;
368 /* FALLTHROUGH */
369 case '\\':
370 case '$': case '`':
371 store_qchar:
372 *wp++ = QCHAR;
373 *wp++ = c;
374 break;
375 default:
376 heredocquote:
377 Xcheck(ws, wp);
378 if (c) {
379 /* trailing \ is lost */
380 *wp++ = CHAR;
381 *wp++ = '\\';
382 *wp++ = CHAR;
383 *wp++ = c;
384 }
385 break;
386 }
387 break;
388 case '$':
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700389 c = getsc();
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000390 SubstS:
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700391 if (c == '(') /*)*/ {
392 c = getsc();
393 if (c == '(') /*)*/ {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700394 *wp++ = EXPRSUB;
Elliott Hughes737fdce2014-08-07 12:59:26 -0700395 PUSH_SRETRACE(SASPAREN);
Geremy Condra03ebf062011-10-12 18:17:24 -0700396 statep->nparen = 2;
Geremy Condra03ebf062011-10-12 18:17:24 -0700397 *retrace_info->xp++ = '(';
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700398 } else {
399 ungetsc(c);
Geremy Condra03ebf062011-10-12 18:17:24 -0700400 subst_command:
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000401 c = COMSUB;
402 subst_command2:
403 sp = yyrecursive(c);
Geremy Condra03ebf062011-10-12 18:17:24 -0700404 cz = strlen(sp) + 1;
405 XcheckN(ws, wp, cz);
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000406 *wp++ = c;
Geremy Condra03ebf062011-10-12 18:17:24 -0700407 memcpy(wp, sp, cz);
408 wp += cz;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700409 }
410 } else if (c == '{') /*}*/ {
Thorsten Glaser811a5752013-07-25 14:24:45 +0000411 if ((c = getsc()) == '|') {
412 /*
413 * non-subenvironment
414 * value substitution
415 */
416 c = VALSUB;
417 goto subst_command2;
418 } else if (ctype(c, C_IFSWS)) {
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000419 /*
420 * non-subenvironment
421 * "command" substitution
422 */
423 c = FUNSUB;
424 goto subst_command2;
425 }
426 ungetsc(c);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700427 *wp++ = OSUBST;
428 *wp++ = '{'; /*}*/
429 wp = get_brace_var(&ws, wp);
430 c = getsc();
431 /* allow :# and :% (ksh88 compat) */
432 if (c == ':') {
433 *wp++ = CHAR;
434 *wp++ = c;
435 c = getsc();
436 if (c == ':') {
437 *wp++ = CHAR;
438 *wp++ = '0';
439 *wp++ = ADELIM;
440 *wp++ = ':';
441 PUSH_STATE(SBRACE);
442 PUSH_STATE(SADELIM);
Geremy Condra03ebf062011-10-12 18:17:24 -0700443 statep->ls_adelim.delimiter = ':';
444 statep->ls_adelim.num = 1;
445 statep->nparen = 0;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700446 break;
447 } else if (ksh_isdigit(c) ||
448 c == '('/*)*/ || c == ' ' ||
Geremy Condra03ebf062011-10-12 18:17:24 -0700449 /*XXX what else? */
450 c == '$') {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700451 /* substring subst. */
452 if (c != ' ') {
453 *wp++ = CHAR;
454 *wp++ = ' ';
455 }
456 ungetsc(c);
457 PUSH_STATE(SBRACE);
458 PUSH_STATE(SADELIM);
Geremy Condra03ebf062011-10-12 18:17:24 -0700459 statep->ls_adelim.delimiter = ':';
460 statep->ls_adelim.num = 2;
461 statep->nparen = 0;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700462 break;
463 }
464 } else if (c == '/') {
Elliott Hughes77740fc2016-08-12 15:06:53 -0700465 c2 = ADELIM;
466 parse_adelim_slash:
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700467 *wp++ = CHAR;
468 *wp++ = c;
469 if ((c = getsc()) == '/') {
Elliott Hughes77740fc2016-08-12 15:06:53 -0700470 *wp++ = c2;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700471 *wp++ = c;
472 } else
473 ungetsc(c);
474 PUSH_STATE(SBRACE);
475 PUSH_STATE(SADELIM);
Geremy Condra03ebf062011-10-12 18:17:24 -0700476 statep->ls_adelim.delimiter = '/';
477 statep->ls_adelim.num = 1;
478 statep->nparen = 0;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700479 break;
Elliott Hughes77740fc2016-08-12 15:06:53 -0700480 } else if (c == '@') {
481 c2 = getsc();
482 ungetsc(c2);
483 if (c2 == '/') {
484 c2 = CHAR;
485 goto parse_adelim_slash;
486 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700487 }
Geremy Condra03ebf062011-10-12 18:17:24 -0700488 /*
489 * If this is a trim operation,
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700490 * treat (,|,) specially in STBRACE.
491 */
Elliott Hughesa3c3f962017-04-12 16:52:30 -0700492 if (ksh_issubop2(c)) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700493 ungetsc(c);
Geremy Condra03ebf062011-10-12 18:17:24 -0700494 if (Flag(FSH))
495 PUSH_STATE(STBRACEBOURNE);
496 else
497 PUSH_STATE(STBRACEKORN);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700498 } else {
499 ungetsc(c);
Thorsten Glaser811a5752013-07-25 14:24:45 +0000500 if (state == SDQUOTE ||
501 state == SQBRACE)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700502 PUSH_STATE(SQBRACE);
503 else
504 PUSH_STATE(SBRACE);
505 }
506 } else if (ksh_isalphx(c)) {
507 *wp++ = OSUBST;
508 *wp++ = 'X';
509 do {
510 Xcheck(ws, wp);
511 *wp++ = c;
512 c = getsc();
513 } while (ksh_isalnux(c));
514 *wp++ = '\0';
515 *wp++ = CSUBST;
516 *wp++ = 'X';
517 ungetsc(c);
518 } else if (ctype(c, C_VAR1 | C_DIGIT)) {
519 Xcheck(ws, wp);
520 *wp++ = OSUBST;
521 *wp++ = 'X';
522 *wp++ = c;
523 *wp++ = '\0';
524 *wp++ = CSUBST;
525 *wp++ = 'X';
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700526 } else {
527 *wp++ = CHAR;
528 *wp++ = '$';
529 ungetsc(c);
530 }
531 break;
532 case '`':
533 subst_gravis:
534 PUSH_STATE(SBQUOTE);
Elliott Hughesa3c3f962017-04-12 16:52:30 -0700535 *wp++ = COMASUB;
Geremy Condra03ebf062011-10-12 18:17:24 -0700536 /*
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800537 * We need to know whether we are within double
Elliott Hughes77740fc2016-08-12 15:06:53 -0700538 * quotes in order to translate \" to " within
539 * "…`…\"…`…" because, unlike for COMSUBs, the
540 * outer double quoteing changes the backslash
541 * meaning for the inside. For more details:
542 * http://austingroupbugs.net/view.php?id=1015
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700543 */
Geremy Condra03ebf062011-10-12 18:17:24 -0700544 statep->ls_bool = false;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700545 s2 = statep;
546 base = state_info.base;
Geremy Condra03ebf062011-10-12 18:17:24 -0700547 while (/* CONSTCOND */ 1) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700548 for (; s2 != base; s2--) {
Geremy Condra03ebf062011-10-12 18:17:24 -0700549 if (s2->type == SDQUOTE) {
550 statep->ls_bool = true;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700551 break;
552 }
553 }
554 if (s2 != base)
555 break;
Geremy Condra03ebf062011-10-12 18:17:24 -0700556 if (!(s2 = s2->ls_base))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700557 break;
558 base = s2-- - STATE_BSIZE;
559 }
560 break;
561 case QCHAR:
562 if (cf & LQCHAR) {
563 *wp++ = QCHAR;
564 *wp++ = getsc();
565 break;
566 }
567 /* FALLTHROUGH */
568 default:
569 store_char:
570 *wp++ = CHAR;
571 *wp++ = c;
572 }
573 break;
574
575 case SEQUOTE:
576 if (c == '\'') {
577 POP_STATE();
578 *wp++ = CQUOTE;
579 ignore_backslash_newline--;
580 } else if (c == '\\') {
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800581 if ((c2 = unbksl(true, getsc_i, ungetsc)) == -1)
582 c2 = getsc();
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700583 if (c2 == 0)
Geremy Condra03ebf062011-10-12 18:17:24 -0700584 statep->ls_bool = true;
585 if (!statep->ls_bool) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700586 char ts[4];
587
588 if ((unsigned int)c2 < 0x100) {
589 *wp++ = QCHAR;
590 *wp++ = c2;
591 } else {
Geremy Condra03ebf062011-10-12 18:17:24 -0700592 cz = utf_wctomb(ts, c2 - 0x100);
593 ts[cz] = 0;
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800594 cz = 0;
595 do {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700596 *wp++ = QCHAR;
Geremy Condra03ebf062011-10-12 18:17:24 -0700597 *wp++ = ts[cz];
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800598 } while (ts[++cz]);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700599 }
600 }
Geremy Condra03ebf062011-10-12 18:17:24 -0700601 } else if (!statep->ls_bool) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700602 *wp++ = QCHAR;
603 *wp++ = c;
604 }
605 break;
606
607 case SSQUOTE:
608 if (c == '\'') {
609 POP_STATE();
Thorsten Glaser811a5752013-07-25 14:24:45 +0000610 if ((cf & HEREDOC) || state == SQBRACE)
611 goto store_char;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700612 *wp++ = CQUOTE;
613 ignore_backslash_newline--;
614 } else {
615 *wp++ = QCHAR;
616 *wp++ = c;
617 }
618 break;
619
620 case SDQUOTE:
621 if (c == '"') {
622 POP_STATE();
623 *wp++ = CQUOTE;
624 } else
625 goto Subst;
626 break;
627
Geremy Condra03ebf062011-10-12 18:17:24 -0700628 /* $(( ... )) */
629 case SASPAREN:
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700630 if (c == '(')
Geremy Condra03ebf062011-10-12 18:17:24 -0700631 statep->nparen++;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700632 else if (c == ')') {
Geremy Condra03ebf062011-10-12 18:17:24 -0700633 statep->nparen--;
634 if (statep->nparen == 1) {
635 /* end of EXPRSUB */
636 POP_SRETRACE();
Geremy Condra03ebf062011-10-12 18:17:24 -0700637
638 if ((c2 = getsc()) == /*(*/ ')') {
639 cz = strlen(sp) - 2;
640 XcheckN(ws, wp, cz);
641 memcpy(wp, sp + 1, cz);
642 wp += cz;
643 afree(sp, ATEMP);
644 *wp++ = '\0';
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700645 break;
646 } else {
Geremy Condra03ebf062011-10-12 18:17:24 -0700647 Source *s;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700648
649 ungetsc(c2);
Geremy Condra03ebf062011-10-12 18:17:24 -0700650 /*
651 * mismatched parenthesis -
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700652 * assume we were really
653 * parsing a $(...) expression
654 */
Geremy Condra03ebf062011-10-12 18:17:24 -0700655 --wp;
656 s = pushs(SREREAD,
657 source->areap);
658 s->start = s->str =
659 s->u.freeme = sp;
660 s->next = source;
661 source = s;
662 goto subst_command;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700663 }
664 }
665 }
Geremy Condra03ebf062011-10-12 18:17:24 -0700666 /* reuse existing state machine */
667 goto Sbase2;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700668
669 case SQBRACE:
670 if (c == '\\') {
671 /*
672 * perform POSIX "quote removal" if the back-
673 * slash is "special", i.e. same cases as the
674 * {case '\\':} in Subst: plus closing brace;
675 * in mksh code "quote removal" on '\c' means
676 * write QCHAR+c, otherwise CHAR+\+CHAR+c are
677 * emitted (in heredocquote:)
678 */
679 if ((c = getsc()) == '"' || c == '\\' ||
680 c == '$' || c == '`' || c == /*{*/'}')
681 goto store_qchar;
682 goto heredocquote;
683 }
684 goto common_SQBRACE;
685
686 case SBRACE:
687 if (c == '\'')
Thorsten Glaser811a5752013-07-25 14:24:45 +0000688 goto open_ssquote_unless_heredoc;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700689 else if (c == '\\')
690 goto getsc_qchar;
691 common_SQBRACE:
692 if (c == '"')
693 goto open_sdquote;
694 else if (c == '$')
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000695 goto subst_dollar_ex;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700696 else if (c == '`')
697 goto subst_gravis;
698 else if (c != /*{*/ '}')
699 goto store_char;
700 POP_STATE();
701 *wp++ = CSUBST;
702 *wp++ = /*{*/ '}';
703 break;
704
Geremy Condra03ebf062011-10-12 18:17:24 -0700705 /* Same as SBASE, except (,|,) treated specially */
706 case STBRACEKORN:
707 if (c == '|')
708 *wp++ = SPAT;
709 else if (c == '(') {
710 *wp++ = OPAT;
711 /* simile for @ */
712 *wp++ = ' ';
713 PUSH_STATE(SPATTERN);
714 } else /* FALLTHROUGH */
715 case STBRACEBOURNE:
716 if (c == /*{*/ '}') {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700717 POP_STATE();
718 *wp++ = CSUBST;
719 *wp++ = /*{*/ '}';
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700720 } else
721 goto Sbase1;
722 break;
723
724 case SBQUOTE:
725 if (c == '`') {
726 *wp++ = 0;
727 POP_STATE();
728 } else if (c == '\\') {
729 switch (c = getsc()) {
Geremy Condra03ebf062011-10-12 18:17:24 -0700730 case 0:
731 /* trailing \ is lost */
732 break;
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800733 case '$':
734 case '`':
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700735 case '\\':
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700736 *wp++ = c;
737 break;
738 case '"':
Geremy Condra03ebf062011-10-12 18:17:24 -0700739 if (statep->ls_bool) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700740 *wp++ = c;
741 break;
742 }
743 /* FALLTHROUGH */
744 default:
Geremy Condra03ebf062011-10-12 18:17:24 -0700745 *wp++ = '\\';
746 *wp++ = c;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700747 break;
748 }
749 } else
750 *wp++ = c;
751 break;
752
Geremy Condra03ebf062011-10-12 18:17:24 -0700753 /* ONEWORD */
754 case SWORD:
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700755 goto Subst;
756
Geremy Condra03ebf062011-10-12 18:17:24 -0700757 /* LETEXPR: (( ... )) */
758 case SLETPAREN:
759 if (c == /*(*/ ')') {
760 if (statep->nparen > 0)
761 --statep->nparen;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700762 else if ((c2 = getsc()) == /*(*/ ')') {
763 c = 0;
764 *wp++ = CQUOTE;
765 goto Done;
766 } else {
767 Source *s;
768
769 ungetsc(c2);
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800770 ungetsc(c);
Geremy Condra03ebf062011-10-12 18:17:24 -0700771 /*
772 * mismatched parenthesis -
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700773 * assume we were really
Geremy Condra03ebf062011-10-12 18:17:24 -0700774 * parsing a (...) expression
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700775 */
776 *wp = EOS;
777 sp = Xstring(ws, wp);
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800778 dp = wdstrip(sp + 1, WDS_TPUTS);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700779 s = pushs(SREREAD, source->areap);
780 s->start = s->str = s->u.freeme = dp;
781 s->next = source;
782 source = s;
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800783 ungetsc('('/*)*/);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700784 return ('('/*)*/);
785 }
786 } else if (c == '(')
Geremy Condra03ebf062011-10-12 18:17:24 -0700787 /*
788 * parentheses inside quotes and
789 * backslashes are lost, but AT&T ksh
790 * doesn't count them either
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700791 */
Geremy Condra03ebf062011-10-12 18:17:24 -0700792 ++statep->nparen;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700793 goto Sbase2;
794
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800795 /* << or <<- delimiter */
Geremy Condra03ebf062011-10-12 18:17:24 -0700796 case SHEREDELIM:
797 /*
Geremy Condra03ebf062011-10-12 18:17:24 -0700798 * here delimiters need a special case since
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700799 * $ and `...` are not to be treated specially
800 */
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000801 switch (c) {
802 case '\\':
803 if ((c = getsc())) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700804 /* trailing \ is lost */
805 *wp++ = QCHAR;
806 *wp++ = c;
807 }
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000808 break;
809 case '\'':
Thorsten Glaser811a5752013-07-25 14:24:45 +0000810 goto open_ssquote_unless_heredoc;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000811 case '$':
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700812 if ((c2 = getsc()) == '\'') {
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000813 open_sequote:
814 *wp++ = OQUOTE;
815 ignore_backslash_newline++;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700816 PUSH_STATE(SEQUOTE);
Geremy Condra03ebf062011-10-12 18:17:24 -0700817 statep->ls_bool = false;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000818 break;
819 } else if (c2 == '"') {
820 /* FALLTHROUGH */
821 case '"':
Elliott Hughes737fdce2014-08-07 12:59:26 -0700822 PUSH_SRETRACE(SHEREDQUOTE);
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000823 break;
824 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700825 ungetsc(c2);
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000826 /* FALLTHROUGH */
827 default:
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700828 *wp++ = CHAR;
829 *wp++ = c;
830 }
831 break;
832
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800833 /* " in << or <<- delimiter */
Geremy Condra03ebf062011-10-12 18:17:24 -0700834 case SHEREDQUOTE:
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000835 if (c != '"')
836 goto Subst;
837 POP_SRETRACE();
838 dp = strnul(sp) - 1;
839 /* remove the trailing double quote */
840 *dp = '\0';
841 /* store the quoted string */
842 *wp++ = OQUOTE;
Elliott Hughes50012062015-03-10 22:22:24 -0700843 XcheckN(ws, wp, (dp - sp) * 2);
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000844 dp = sp;
845 while ((c = *dp++)) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700846 if (c == '\\') {
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000847 switch ((c = *dp++)) {
Geremy Condra03ebf062011-10-12 18:17:24 -0700848 case '\\':
849 case '"':
850 case '$':
851 case '`':
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700852 break;
853 default:
Geremy Condra03ebf062011-10-12 18:17:24 -0700854 *wp++ = CHAR;
855 *wp++ = '\\';
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700856 break;
857 }
858 }
859 *wp++ = CHAR;
860 *wp++ = c;
861 }
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000862 afree(sp, ATEMP);
863 *wp++ = CQUOTE;
864 state = statep->type = SHEREDELIM;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700865 break;
866
Geremy Condra03ebf062011-10-12 18:17:24 -0700867 /* in *(...|...) pattern (*+?@!) */
868 case SPATTERN:
869 if (c == /*(*/ ')') {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700870 *wp++ = CPAT;
871 POP_STATE();
872 } else if (c == '|') {
873 *wp++ = SPAT;
874 } else if (c == '(') {
875 *wp++ = OPAT;
Geremy Condra03ebf062011-10-12 18:17:24 -0700876 /* simile for @ */
877 *wp++ = ' ';
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700878 PUSH_STATE(SPATTERN);
879 } else
880 goto Sbase1;
881 break;
882 }
883 }
884 Done:
885 Xcheck(ws, wp);
886 if (statep != &states[1])
887 /* XXX figure out what is missing */
Elliott Hughesa3c3f962017-04-12 16:52:30 -0700888 yyerror("no closing quote");
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700889
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700890 /* This done to avoid tests for SHEREDELIM wherever SBASE tested */
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000891 if (state == SHEREDELIM)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700892 state = SBASE;
893
894 dp = Xstring(ws, wp);
Thorsten Glaser811a5752013-07-25 14:24:45 +0000895 if (state == SBASE && (
Thorsten Glaser811a5752013-07-25 14:24:45 +0000896 (c == '&' && !Flag(FSH) && !Flag(FPOSIX)) ||
Elliott Hughes77740fc2016-08-12 15:06:53 -0700897 c == '<' || c == '>') && ((c2 = Xlength(ws, wp)) == 0 ||
898 (c2 == 2 && dp[0] == CHAR && ksh_isdigit(dp[1])))) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700899 struct ioword *iop = alloc(sizeof(struct ioword), ATEMP);
900
Elliott Hughes77740fc2016-08-12 15:06:53 -0700901 iop->unit = c2 == 2 ? ksh_numdig(dp[1]) : c == '<' ? 0 : 1;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700902
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700903 if (c == '&') {
904 if ((c2 = getsc()) != '>') {
905 ungetsc(c2);
906 goto no_iop;
907 }
908 c = c2;
Elliott Hughesb27ce952015-04-21 13:39:18 -0700909 iop->ioflag = IOBASH;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700910 } else
Elliott Hughesb27ce952015-04-21 13:39:18 -0700911 iop->ioflag = 0;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700912
913 c2 = getsc();
914 /* <<, >>, <> are ok, >< is not */
915 if (c == c2 || (c == '<' && c2 == '>')) {
Elliott Hughesb27ce952015-04-21 13:39:18 -0700916 iop->ioflag |= c == c2 ?
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700917 (c == '>' ? IOCAT : IOHERE) : IORDWR;
Elliott Hughesb27ce952015-04-21 13:39:18 -0700918 if (iop->ioflag == IOHERE) {
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800919 if ((c2 = getsc()) == '-')
Elliott Hughesb27ce952015-04-21 13:39:18 -0700920 iop->ioflag |= IOSKIP;
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800921 else if (c2 == '<')
Elliott Hughesb27ce952015-04-21 13:39:18 -0700922 iop->ioflag |= IOHERESTR;
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800923 else
924 ungetsc(c2);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700925 }
926 } else if (c2 == '&')
Elliott Hughesb27ce952015-04-21 13:39:18 -0700927 iop->ioflag |= IODUP | (c == '<' ? IORDUP : 0);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700928 else {
Elliott Hughesb27ce952015-04-21 13:39:18 -0700929 iop->ioflag |= c == '>' ? IOWRITE : IOREAD;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700930 if (c == '>' && c2 == '|')
Elliott Hughesb27ce952015-04-21 13:39:18 -0700931 iop->ioflag |= IOCLOB;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700932 else
933 ungetsc(c2);
934 }
935
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800936 iop->ioname = NULL;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700937 iop->delim = NULL;
938 iop->heredoc = NULL;
Geremy Condra03ebf062011-10-12 18:17:24 -0700939 /* free word */
940 Xfree(ws, wp);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700941 yylval.iop = iop;
942 return (REDIR);
943 no_iop:
Geremy Condra03ebf062011-10-12 18:17:24 -0700944 afree(iop, ATEMP);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700945 }
946
947 if (wp == dp && state == SBASE) {
Geremy Condra03ebf062011-10-12 18:17:24 -0700948 /* free word */
949 Xfree(ws, wp);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700950 /* no word, process LEX1 character */
951 if ((c == '|') || (c == '&') || (c == ';') || (c == '('/*)*/)) {
952 if ((c2 = getsc()) == c)
953 c = (c == ';') ? BREAK :
954 (c == '|') ? LOGOR :
955 (c == '&') ? LOGAND :
956 /* c == '(' ) */ MDPAREN;
957 else if (c == '|' && c2 == '&')
958 c = COPROC;
Geremy Condra03ebf062011-10-12 18:17:24 -0700959 else if (c == ';' && c2 == '|')
960 c = BRKEV;
961 else if (c == ';' && c2 == '&')
962 c = BRKFT;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700963 else
964 ungetsc(c2);
Geremy Condra03ebf062011-10-12 18:17:24 -0700965#ifndef MKSH_SMALL
966 if (c == BREAK) {
967 if ((c2 = getsc()) == '&')
968 c = BRKEV;
969 else
970 ungetsc(c2);
971 }
972#endif
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700973 } else if (c == '\n') {
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800974 if (cf & HEREDELIM)
975 ungetsc(c);
976 else {
977 gethere();
978 if (cf & CONTIN)
979 goto Again;
980 }
Elliott Hughes77740fc2016-08-12 15:06:53 -0700981 } else if (c == '\0' && !(cf & HEREDELIM)) {
982 struct ioword **p = heres;
983
984 while (p < herep)
985 if ((*p)->ioflag & IOHERESTR)
986 ++p;
987 else
988 /* ksh -c 'cat <<EOF' can cause this */
989 yyerror(Tf_heredoc,
990 evalstr((*p)->delim, 0));
Elliott Hughesfc0307d2016-02-02 15:26:47 -0800991 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700992 return (c);
993 }
994
Geremy Condra03ebf062011-10-12 18:17:24 -0700995 /* terminate word */
996 *wp++ = EOS;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700997 yylval.cp = Xclose(ws, wp);
998 if (state == SWORD || state == SLETPAREN
Geremy Condra03ebf062011-10-12 18:17:24 -0700999 /* XXX ONEWORD? */)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001000 return (LWORD);
1001
1002 /* unget terminator */
1003 ungetsc(c);
1004
1005 /*
1006 * note: the alias-vs-function code below depends on several
1007 * interna: starting from here, source->str is not modified;
1008 * the way getsc() and ungetsc() operate; etc.
1009 */
1010
1011 /* copy word to unprefixed string ident */
1012 sp = yylval.cp;
1013 dp = ident;
Elliott Hughesfc0307d2016-02-02 15:26:47 -08001014 while ((dp - ident) < IDENT && (c = *sp++) == CHAR)
1015 *dp++ = *sp++;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001016 if (c != EOS)
Elliott Hughesa3c3f962017-04-12 16:52:30 -07001017 /* word is not unquoted, or space ran out */
Elliott Hughesb27ce952015-04-21 13:39:18 -07001018 dp = ident;
1019 /* make sure the ident array stays NUL padded */
1020 memset(dp, 0, (ident + IDENT) - dp + 1);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001021
Elliott Hughesa3c3f962017-04-12 16:52:30 -07001022 if (*ident != '\0' && (cf & (KEYWORD | ALIAS))) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001023 struct tbl *p;
1024 uint32_t h = hash(ident);
1025
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001026 if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) &&
Geremy Condra03ebf062011-10-12 18:17:24 -07001027 (!(cf & ESACONLY) || p->val.i == ESAC ||
1028 p->val.i == /*{*/ '}')) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001029 afree(yylval.cp, ATEMP);
1030 return (p->val.i);
1031 }
1032 if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) &&
1033 (p->flag & ISSET)) {
1034 /*
1035 * this still points to the same character as the
1036 * ungetsc'd terminator from above
1037 */
1038 const char *cp = source->str;
1039
1040 /* prefer POSIX but not Korn functions over aliases */
1041 while (*cp == ' ' || *cp == '\t')
1042 /*
1043 * this is like getsc() without skipping
1044 * over Source boundaries (including not
1045 * parsing ungetsc'd characters that got
1046 * pushed into an SREREAD) which is what
1047 * we want here anyway: find out whether
1048 * the alias name is followed by a POSIX
Elliott Hughesb27ce952015-04-21 13:39:18 -07001049 * function definition
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001050 */
1051 ++cp;
1052 /* prefer functions over aliases */
Geremy Condra03ebf062011-10-12 18:17:24 -07001053 if (cp[0] != '(' || cp[1] != ')') {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001054 Source *s = source;
1055
1056 while (s && (s->flags & SF_HASALIAS))
1057 if (s->u.tblp == p)
1058 return (LWORD);
1059 else
1060 s = s->next;
1061 /* push alias expansion */
1062 s = pushs(SALIAS, source->areap);
1063 s->start = s->str = p->val.s;
1064 s->u.tblp = p;
1065 s->flags |= SF_HASALIAS;
Elliott Hughesa3c3f962017-04-12 16:52:30 -07001066 s->line = source->line;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001067 s->next = source;
1068 if (source->type == SEOF) {
1069 /* prevent infinite recursion at EOS */
1070 source->u.tblp = p;
1071 source->flags |= SF_HASALIAS;
1072 }
1073 source = s;
1074 afree(yylval.cp, ATEMP);
1075 goto Again;
1076 }
1077 }
Elliott Hughesa3c3f962017-04-12 16:52:30 -07001078 } else if (*ident == '\0') {
Elliott Hughesb27ce952015-04-21 13:39:18 -07001079 /* retain typeset et al. even when quoted */
Elliott Hughesa3c3f962017-04-12 16:52:30 -07001080 struct tbl *tt = get_builtin((dp = wdstrip(yylval.cp, 0)));
1081 uint32_t flag = tt ? tt->flag : 0;
1082
1083 if (flag & (DECL_UTIL | DECL_FWDR))
Elliott Hughesb27ce952015-04-21 13:39:18 -07001084 strlcpy(ident, dp, sizeof(ident));
1085 afree(dp, ATEMP);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001086 }
1087
1088 return (LWORD);
1089}
1090
1091static void
Elliott Hughesfc0307d2016-02-02 15:26:47 -08001092gethere(void)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001093{
1094 struct ioword **p;
1095
1096 for (p = heres; p < herep; p++)
Elliott Hughesfc0307d2016-02-02 15:26:47 -08001097 if (!((*p)->ioflag & IOHERESTR))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001098 readhere(*p);
1099 herep = heres;
1100}
1101
1102/*
1103 * read "<<word" text into temp file
1104 */
1105
1106static void
1107readhere(struct ioword *iop)
1108{
1109 int c;
Geremy Condra03ebf062011-10-12 18:17:24 -07001110 const char *eof, *eofp;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001111 XString xs;
1112 char *xp;
Elliott Hughesfc0307d2016-02-02 15:26:47 -08001113 size_t xpos;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001114
Elliott Hughesfc0307d2016-02-02 15:26:47 -08001115 eof = evalstr(iop->delim, 0);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001116
Elliott Hughesb27ce952015-04-21 13:39:18 -07001117 if (!(iop->ioflag & IOEVAL))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001118 ignore_backslash_newline++;
1119
1120 Xinit(xs, xp, 256, ATEMP);
1121
Geremy Condra03ebf062011-10-12 18:17:24 -07001122 heredoc_read_line:
1123 /* beginning of line */
1124 eofp = eof;
1125 xpos = Xsavepos(xs, xp);
Elliott Hughesb27ce952015-04-21 13:39:18 -07001126 if (iop->ioflag & IOSKIP) {
Geremy Condra03ebf062011-10-12 18:17:24 -07001127 /* skip over leading tabs */
1128 while ((c = getsc()) == '\t')
Elliott Hughesb27ce952015-04-21 13:39:18 -07001129 ; /* nothing */
Geremy Condra03ebf062011-10-12 18:17:24 -07001130 goto heredoc_parse_char;
1131 }
1132 heredoc_read_char:
1133 c = getsc();
1134 heredoc_parse_char:
1135 /* compare with here document marker */
1136 if (!*eofp) {
1137 /* end of here document marker, what to do? */
1138 switch (c) {
1139 case /*(*/ ')':
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001140 if (!subshell_nesting_type)
Geremy Condra03ebf062011-10-12 18:17:24 -07001141 /*-
1142 * not allowed outside $(...) or (...)
1143 * => mismatch
1144 */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001145 break;
Geremy Condra03ebf062011-10-12 18:17:24 -07001146 /* allow $(...) or (...) to close here */
1147 ungetsc(/*(*/ ')');
1148 /* FALLTHROUGH */
1149 case 0:
1150 /*
1151 * Allow EOF here to commands without trailing
1152 * newlines (mksh -c '...') will work as well.
1153 */
1154 case '\n':
1155 /* Newline terminates here document marker */
1156 goto heredoc_found_terminator;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001157 }
Geremy Condra03ebf062011-10-12 18:17:24 -07001158 } else if (c == *eofp++)
1159 /* store; then read and compare next character */
1160 goto heredoc_store_and_loop;
1161 /* nope, mismatch; read until end of line */
1162 while (c != '\n') {
1163 if (!c)
1164 /* oops, reached EOF */
Elliott Hughes77740fc2016-08-12 15:06:53 -07001165 yyerror(Tf_heredoc, eof);
Geremy Condra03ebf062011-10-12 18:17:24 -07001166 /* store character */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001167 Xcheck(xs, xp);
1168 Xput(xs, xp, c);
Geremy Condra03ebf062011-10-12 18:17:24 -07001169 /* read next character */
1170 c = getsc();
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001171 }
Geremy Condra03ebf062011-10-12 18:17:24 -07001172 /* we read a newline as last character */
1173 heredoc_store_and_loop:
1174 /* store character */
1175 Xcheck(xs, xp);
1176 Xput(xs, xp, c);
1177 if (c == '\n')
1178 goto heredoc_read_line;
1179 goto heredoc_read_char;
1180
1181 heredoc_found_terminator:
1182 /* jump back to saved beginning of line */
1183 xp = Xrestpos(xs, xp, xpos);
1184 /* terminate, close and store */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001185 Xput(xs, xp, '\0');
1186 iop->heredoc = Xclose(xs, xp);
1187
Elliott Hughesb27ce952015-04-21 13:39:18 -07001188 if (!(iop->ioflag & IOEVAL))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001189 ignore_backslash_newline--;
1190}
1191
1192void
1193yyerror(const char *fmt, ...)
1194{
1195 va_list va;
1196
1197 /* pop aliases and re-reads */
1198 while (source->type == SALIAS || source->type == SREREAD)
1199 source = source->next;
Geremy Condra03ebf062011-10-12 18:17:24 -07001200 /* zap pending input */
1201 source->str = null;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001202
1203 error_prefix(true);
1204 va_start(va, fmt);
1205 shf_vfprintf(shl_out, fmt, va);
Elliott Hughesa3c3f962017-04-12 16:52:30 -07001206 shf_putc('\n', shl_out);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001207 va_end(va);
1208 errorfz();
1209}
1210
1211/*
1212 * input for yylex with alias expansion
1213 */
1214
1215Source *
1216pushs(int type, Area *areap)
1217{
1218 Source *s;
1219
1220 s = alloc(sizeof(Source), areap);
1221 memset(s, 0, sizeof(Source));
1222 s->type = type;
1223 s->str = null;
1224 s->areap = areap;
1225 if (type == SFILE || type == SSTDIN)
1226 XinitN(s->xs, 256, s->areap);
1227 return (s);
1228}
1229
1230static int
Geremy Condra03ebf062011-10-12 18:17:24 -07001231getsc_uu(void)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001232{
1233 Source *s = source;
1234 int c;
1235
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001236 while ((c = *s->str++) == 0) {
Geremy Condra03ebf062011-10-12 18:17:24 -07001237 /* return 0 for EOF by default */
1238 s->str = NULL;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001239 switch (s->type) {
1240 case SEOF:
1241 s->str = null;
1242 return (0);
1243
1244 case SSTDIN:
1245 case SFILE:
1246 getsc_line(s);
1247 break;
1248
1249 case SWSTR:
1250 break;
1251
1252 case SSTRING:
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001253 case SSTRINGCMDLINE:
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001254 break;
1255
1256 case SWORDS:
1257 s->start = s->str = *s->u.strv++;
1258 s->type = SWORDSEP;
1259 break;
1260
1261 case SWORDSEP:
1262 if (*s->u.strv == NULL) {
1263 s->start = s->str = "\n";
1264 s->type = SEOF;
1265 } else {
Elliott Hughes77740fc2016-08-12 15:06:53 -07001266 s->start = s->str = T1space;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001267 s->type = SWORDS;
1268 }
1269 break;
1270
1271 case SALIAS:
1272 if (s->flags & SF_ALIASEND) {
1273 /* pass on an unused SF_ALIAS flag */
1274 source = s->next;
1275 source->flags |= s->flags & SF_ALIAS;
1276 s = source;
1277 } else if (*s->u.tblp->val.s &&
1278 (c = strnul(s->u.tblp->val.s)[-1], ksh_isspace(c))) {
Geremy Condra03ebf062011-10-12 18:17:24 -07001279 /* pop source stack */
1280 source = s = s->next;
1281 /*
1282 * Note that this alias ended with a
1283 * space, enabling alias expansion on
1284 * the following word.
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001285 */
1286 s->flags |= SF_ALIAS;
1287 } else {
Geremy Condra03ebf062011-10-12 18:17:24 -07001288 /*
1289 * At this point, we need to keep the current
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001290 * alias in the source list so recursive
Geremy Condra03ebf062011-10-12 18:17:24 -07001291 * aliases can be detected and we also need to
1292 * return the next character. Do this by
1293 * temporarily popping the alias to get the
1294 * next character and then put it back in the
1295 * source list with the SF_ALIASEND flag set.
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001296 */
Geremy Condra03ebf062011-10-12 18:17:24 -07001297 /* pop source stack */
1298 source = s->next;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001299 source->flags |= s->flags & SF_ALIAS;
Geremy Condra03ebf062011-10-12 18:17:24 -07001300 c = getsc_uu();
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001301 if (c) {
1302 s->flags |= SF_ALIASEND;
1303 s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1304 s->start = s->str = s->ugbuf;
1305 s->next = source;
1306 source = s;
1307 } else {
1308 s = source;
Geremy Condra03ebf062011-10-12 18:17:24 -07001309 /* avoid reading EOF twice */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001310 s->str = NULL;
1311 break;
1312 }
1313 }
1314 continue;
1315
1316 case SREREAD:
Geremy Condra03ebf062011-10-12 18:17:24 -07001317 if (s->start != s->ugbuf)
1318 /* yuck */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001319 afree(s->u.freeme, ATEMP);
1320 source = s = s->next;
1321 continue;
1322 }
1323 if (s->str == NULL) {
1324 s->type = SEOF;
1325 s->start = s->str = null;
1326 return ('\0');
1327 }
1328 if (s->flags & SF_ECHO) {
1329 shf_puts(s->str, shl_out);
1330 shf_flush(shl_out);
1331 }
1332 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001333 return (c);
1334}
1335
1336static void
1337getsc_line(Source *s)
1338{
1339 char *xp = Xstring(s->xs, xp), *cp;
1340 bool interactive = Flag(FTALKING) && s->type == SSTDIN;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001341 bool have_tty = tobool(interactive && (s->flags & SF_TTY));
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001342
1343 /* Done here to ensure nothing odd happens when a timeout occurs */
1344 XcheckN(s->xs, xp, LINE);
1345 *xp = '\0';
1346 s->start = s->str = xp;
1347
1348 if (have_tty && ksh_tmout) {
1349 ksh_tmout_state = TMOUT_READING;
1350 alarm(ksh_tmout);
1351 }
Elliott Hughes77740fc2016-08-12 15:06:53 -07001352 if (interactive) {
1353 if (cur_prompt == PS1)
1354 histsave(&s->line, NULL, HIST_FLUSH, true);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001355 change_winsz();
Elliott Hughes77740fc2016-08-12 15:06:53 -07001356 }
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001357#ifndef MKSH_NO_CMDLINE_EDITING
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001358 if (have_tty && (
1359#if !MKSH_S_NOVI
1360 Flag(FVI) ||
1361#endif
1362 Flag(FEMACS) || Flag(FGMACS))) {
1363 int nread;
1364
Thorsten Glaser811a5752013-07-25 14:24:45 +00001365 nread = x_read(xp);
Geremy Condra03ebf062011-10-12 18:17:24 -07001366 if (nread < 0)
1367 /* read error */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001368 nread = 0;
1369 xp[nread] = '\0';
1370 xp += nread;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001371 } else
1372#endif
1373 {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001374 if (interactive)
1375 pprompt(prompt, 0);
1376 else
1377 s->line++;
1378
Geremy Condra03ebf062011-10-12 18:17:24 -07001379 while (/* CONSTCOND */ 1) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001380 char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf);
1381
1382 if (!p && shf_error(s->u.shf) &&
1383 shf_errno(s->u.shf) == EINTR) {
1384 shf_clearerr(s->u.shf);
1385 if (trap)
1386 runtraps(0);
1387 continue;
1388 }
1389 if (!p || (xp = p, xp[-1] == '\n'))
1390 break;
1391 /* double buffer size */
Geremy Condra03ebf062011-10-12 18:17:24 -07001392 /* move past NUL so doubling works... */
1393 xp++;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001394 XcheckN(s->xs, xp, Xlength(s->xs, xp));
Geremy Condra03ebf062011-10-12 18:17:24 -07001395 /* ...and move back again */
1396 xp--;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001397 }
Geremy Condra03ebf062011-10-12 18:17:24 -07001398 /*
1399 * flush any unwanted input so other programs/builtins
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001400 * can read it. Not very optimal, but less error prone
1401 * than flushing else where, dealing with redirections,
1402 * etc.
Geremy Condra03ebf062011-10-12 18:17:24 -07001403 * TODO: reduce size of shf buffer (~128?) if SSTDIN
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001404 */
1405 if (s->type == SSTDIN)
1406 shf_flush(s->u.shf);
1407 }
Geremy Condra03ebf062011-10-12 18:17:24 -07001408 /*
1409 * XXX: temporary kludge to restore source after a
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001410 * trap may have been executed.
1411 */
1412 source = s;
1413 if (have_tty && ksh_tmout) {
1414 ksh_tmout_state = TMOUT_EXECUTING;
1415 alarm(0);
1416 }
1417 cp = Xstring(s->xs, xp);
Elliott Hughes737fdce2014-08-07 12:59:26 -07001418 rndpush(cp);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001419 s->start = s->str = cp;
1420 strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp));
1421 /* Note: if input is all nulls, this is not eof */
1422 if (Xlength(s->xs, xp) == 0) {
1423 /* EOF */
1424 if (s->type == SFILE)
1425 shf_fdclose(s->u.shf);
1426 s->str = NULL;
Elliott Hughes96b43632015-07-17 11:39:41 -07001427 } else if (interactive && *s->str) {
1428 if (cur_prompt != PS1)
1429 histsave(&s->line, s->str, HIST_APPEND, true);
1430 else if (!ctype(*s->str, C_IFS | C_IFSWS))
1431 histsave(&s->line, s->str, HIST_QUEUE, true);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001432#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
Elliott Hughes96b43632015-07-17 11:39:41 -07001433 else
1434 goto check_for_sole_return;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001435 } else if (interactive && cur_prompt == PS1) {
Elliott Hughes96b43632015-07-17 11:39:41 -07001436 check_for_sole_return:
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001437 cp = Xstring(s->xs, xp);
1438 while (*cp && ctype(*cp, C_IFSWS))
1439 ++cp;
Elliott Hughes96b43632015-07-17 11:39:41 -07001440 if (!*cp) {
1441 histsave(&s->line, NULL, HIST_FLUSH, true);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001442 histsync();
Elliott Hughes96b43632015-07-17 11:39:41 -07001443 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001444#endif
1445 }
1446 if (interactive)
1447 set_prompt(PS2, NULL);
1448}
1449
1450void
1451set_prompt(int to, Source *s)
1452{
Elliott Hughesb27ce952015-04-21 13:39:18 -07001453 cur_prompt = (uint8_t)to;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001454
1455 switch (to) {
Geremy Condra03ebf062011-10-12 18:17:24 -07001456 /* command */
1457 case PS1:
1458 /*
1459 * Substitute ! and !! here, before substitutions are done
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001460 * so ! in expanded variables are not expanded.
1461 * NOTE: this is not what AT&T ksh does (it does it after
1462 * substitutions, POSIX doesn't say which is to be done.
1463 */
1464 {
1465 struct shf *shf;
1466 char * volatile ps1;
1467 Area *saved_atemp;
Elliott Hughes96b43632015-07-17 11:39:41 -07001468 int saved_lineno;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001469
1470 ps1 = str_val(global("PS1"));
1471 shf = shf_sopen(NULL, strlen(ps1) * 2,
1472 SHF_WR | SHF_DYNAMIC, NULL);
1473 while (*ps1)
1474 if (*ps1 != '!' || *++ps1 == '!')
1475 shf_putchar(*ps1++, shf);
1476 else
Elliott Hughes77740fc2016-08-12 15:06:53 -07001477 shf_fprintf(shf, Tf_lu, s ?
Elliott Hughesb27ce952015-04-21 13:39:18 -07001478 (unsigned long)s->line + 1 : 0UL);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001479 ps1 = shf_sclose(shf);
Elliott Hughes96b43632015-07-17 11:39:41 -07001480 saved_lineno = current_lineno;
1481 if (s)
1482 current_lineno = s->line + 1;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001483 saved_atemp = ATEMP;
1484 newenv(E_ERRH);
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001485 if (kshsetjmp(e->jbuf)) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001486 prompt = safe_prompt;
Geremy Condra03ebf062011-10-12 18:17:24 -07001487 /*
1488 * Don't print an error - assume it has already
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001489 * been printed. Reason is we may have forked
1490 * to run a command and the child may be
1491 * unwinding its stack through this code as it
1492 * exits.
1493 */
1494 } else {
1495 char *cp = substitute(ps1, 0);
1496 strdupx(prompt, cp, saved_atemp);
1497 }
Elliott Hughes96b43632015-07-17 11:39:41 -07001498 current_lineno = saved_lineno;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001499 quitenv(NULL);
1500 }
1501 break;
Geremy Condra03ebf062011-10-12 18:17:24 -07001502 /* command continuation */
1503 case PS2:
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001504 prompt = str_val(global("PS2"));
1505 break;
1506 }
1507}
1508
Thorsten Glaser811a5752013-07-25 14:24:45 +00001509int
1510pprompt(const char *cp, int ntruncate)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001511{
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001512 char delimiter = 0;
Elliott Hughes737fdce2014-08-07 12:59:26 -07001513 bool doprint = (ntruncate != -1);
1514 bool indelimit = false;
1515 int columns = 0, lines = 0;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001516
Geremy Condra03ebf062011-10-12 18:17:24 -07001517 /*
1518 * Undocumented AT&T ksh feature:
1519 * If the second char in the prompt string is \r then the first
1520 * char is taken to be a non-printing delimiter and any chars
1521 * between two instances of the delimiter are not considered to
1522 * be part of the prompt length
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001523 */
1524 if (*cp && cp[1] == '\r') {
1525 delimiter = *cp;
1526 cp += 2;
1527 }
1528 for (; *cp; cp++) {
1529 if (indelimit && *cp != delimiter)
1530 ;
1531 else if (*cp == '\n' || *cp == '\r') {
1532 lines += columns / x_cols + ((*cp == '\n') ? 1 : 0);
1533 columns = 0;
1534 } else if (*cp == '\t') {
1535 columns = (columns | 7) + 1;
1536 } else if (*cp == '\b') {
1537 if (columns > 0)
1538 columns--;
1539 } else if (*cp == delimiter)
1540 indelimit = !indelimit;
1541 else if (UTFMODE && ((unsigned char)*cp > 0x7F)) {
1542 const char *cp2;
1543 columns += utf_widthadj(cp, &cp2);
Elliott Hughes737fdce2014-08-07 12:59:26 -07001544 if (doprint && (indelimit ||
1545 (ntruncate < (x_cols * lines + columns))))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001546 shf_write(cp, cp2 - cp, shl_out);
1547 cp = cp2 - /* loop increment */ 1;
1548 continue;
1549 } else
1550 columns++;
Elliott Hughes737fdce2014-08-07 12:59:26 -07001551 if (doprint && (*cp != delimiter) &&
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001552 (indelimit || (ntruncate < (x_cols * lines + columns))))
1553 shf_putc(*cp, shl_out);
1554 }
Elliott Hughes737fdce2014-08-07 12:59:26 -07001555 if (doprint)
1556 shf_flush(shl_out);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001557 return (x_cols * lines + columns);
1558}
1559
Geremy Condra03ebf062011-10-12 18:17:24 -07001560/*
1561 * Read the variable part of a ${...} expression (i.e. up to but not
1562 * including the :[-+?=#%] or close-brace).
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001563 */
1564static char *
1565get_brace_var(XString *wsp, char *wp)
1566{
Geremy Condra03ebf062011-10-12 18:17:24 -07001567 char c;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001568 enum parse_state {
Elliott Hughes77740fc2016-08-12 15:06:53 -07001569 PS_INITIAL, PS_SAW_PERCENT, PS_SAW_HASH, PS_SAW_BANG,
1570 PS_IDENT, PS_NUMBER, PS_VAR1
Geremy Condra03ebf062011-10-12 18:17:24 -07001571 } state = PS_INITIAL;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001572
Geremy Condra03ebf062011-10-12 18:17:24 -07001573 while (/* CONSTCOND */ 1) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001574 c = getsc();
1575 /* State machine to figure out where the variable part ends. */
1576 switch (state) {
Elliott Hughes77740fc2016-08-12 15:06:53 -07001577 case PS_SAW_HASH:
1578 if (ctype(c, C_VAR1)) {
1579 char c2;
1580
1581 c2 = getsc();
1582 ungetsc(c2);
1583 if (c2 != /*{*/ '}') {
1584 ungetsc(c);
1585 goto out;
1586 }
1587 }
1588 goto ps_common;
1589 case PS_SAW_BANG:
1590 switch (c) {
1591 case '@':
1592 case '#':
1593 case '-':
1594 case '?':
1595 goto out;
1596 }
1597 goto ps_common;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001598 case PS_INITIAL:
Elliott Hughes77740fc2016-08-12 15:06:53 -07001599 switch (c) {
1600 case '%':
1601 state = PS_SAW_PERCENT;
1602 goto next;
1603 case '#':
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001604 state = PS_SAW_HASH;
Elliott Hughes77740fc2016-08-12 15:06:53 -07001605 goto next;
1606 case '!':
1607 state = PS_SAW_BANG;
1608 goto next;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001609 }
1610 /* FALLTHROUGH */
Elliott Hughes77740fc2016-08-12 15:06:53 -07001611 case PS_SAW_PERCENT:
1612 ps_common:
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001613 if (ksh_isalphx(c))
1614 state = PS_IDENT;
1615 else if (ksh_isdigit(c))
1616 state = PS_NUMBER;
Elliott Hughes77740fc2016-08-12 15:06:53 -07001617 else if (ctype(c, C_VAR1))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001618 state = PS_VAR1;
1619 else
1620 goto out;
1621 break;
1622 case PS_IDENT:
1623 if (!ksh_isalnux(c)) {
1624 if (c == '[') {
1625 char *tmp, *p;
1626
1627 if (!arraysub(&tmp))
Elliott Hughesa3c3f962017-04-12 16:52:30 -07001628 yyerror("missing ]");
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001629 *wp++ = c;
1630 for (p = tmp; *p; ) {
1631 Xcheck(*wsp, wp);
1632 *wp++ = *p++;
1633 }
1634 afree(tmp, ATEMP);
Geremy Condra03ebf062011-10-12 18:17:24 -07001635 /* the ] */
1636 c = getsc();
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001637 }
1638 goto out;
1639 }
Elliott Hughes77740fc2016-08-12 15:06:53 -07001640 next:
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001641 break;
1642 case PS_NUMBER:
1643 if (!ksh_isdigit(c))
1644 goto out;
1645 break;
1646 case PS_VAR1:
1647 goto out;
1648 }
1649 Xcheck(*wsp, wp);
1650 *wp++ = c;
1651 }
1652 out:
Geremy Condra03ebf062011-10-12 18:17:24 -07001653 /* end of variable part */
1654 *wp++ = '\0';
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001655 ungetsc(c);
1656 return (wp);
1657}
1658
1659/*
1660 * Save an array subscript - returns true if matching bracket found, false
1661 * if eof or newline was found.
1662 * (Returned string double null terminated)
1663 */
Geremy Condra03ebf062011-10-12 18:17:24 -07001664static bool
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001665arraysub(char **strp)
1666{
1667 XString ws;
Geremy Condra03ebf062011-10-12 18:17:24 -07001668 char *wp, c;
1669 /* we are just past the initial [ */
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001670 unsigned int depth = 1;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001671
1672 Xinit(ws, wp, 32, ATEMP);
1673
1674 do {
1675 c = getsc();
1676 Xcheck(ws, wp);
1677 *wp++ = c;
1678 if (c == '[')
1679 depth++;
1680 else if (c == ']')
1681 depth--;
1682 } while (depth > 0 && c && c != '\n');
1683
1684 *wp++ = '\0';
1685 *strp = Xclose(ws, wp);
1686
Geremy Condra03ebf062011-10-12 18:17:24 -07001687 return (tobool(depth == 0));
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001688}
1689
1690/* Unget a char: handles case when we are already at the start of the buffer */
Geremy Condra03ebf062011-10-12 18:17:24 -07001691static void
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001692ungetsc(int c)
1693{
Geremy Condra03ebf062011-10-12 18:17:24 -07001694 struct sretrace_info *rp = retrace_info;
1695
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001696 if (backslash_skip)
1697 backslash_skip--;
Geremy Condra03ebf062011-10-12 18:17:24 -07001698 /* Don't unget EOF... */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001699 if (source->str == null && c == '\0')
Geremy Condra03ebf062011-10-12 18:17:24 -07001700 return;
1701 while (rp) {
1702 if (Xlength(rp->xs, rp->xp))
1703 rp->xp--;
1704 rp = rp->next;
1705 }
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001706 ungetsc_i(c);
Geremy Condra03ebf062011-10-12 18:17:24 -07001707}
1708static void
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001709ungetsc_i(int c)
Geremy Condra03ebf062011-10-12 18:17:24 -07001710{
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001711 if (source->str > source->start)
1712 source->str--;
1713 else {
1714 Source *s;
1715
1716 s = pushs(SREREAD, source->areap);
1717 s->ugbuf[0] = c; s->ugbuf[1] = '\0';
1718 s->start = s->str = s->ugbuf;
1719 s->next = source;
1720 source = s;
1721 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001722}
1723
1724
1725/* Called to get a char that isn't a \newline sequence. */
1726static int
1727getsc_bn(void)
1728{
1729 int c, c2;
1730
1731 if (ignore_backslash_newline)
Geremy Condra03ebf062011-10-12 18:17:24 -07001732 return (o_getsc_u());
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001733
1734 if (backslash_skip == 1) {
1735 backslash_skip = 2;
Geremy Condra03ebf062011-10-12 18:17:24 -07001736 return (o_getsc_u());
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001737 }
1738
1739 backslash_skip = 0;
1740
Geremy Condra03ebf062011-10-12 18:17:24 -07001741 while (/* CONSTCOND */ 1) {
1742 c = o_getsc_u();
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001743 if (c == '\\') {
Geremy Condra03ebf062011-10-12 18:17:24 -07001744 if ((c2 = o_getsc_u()) == '\n')
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001745 /* ignore the \newline; get the next char... */
1746 continue;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001747 ungetsc_i(c2);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001748 backslash_skip = 1;
1749 }
1750 return (c);
1751 }
1752}
1753
Geremy Condra03ebf062011-10-12 18:17:24 -07001754void
1755yyskiputf8bom(void)
1756{
1757 int c;
1758
1759 if ((unsigned char)(c = o_getsc_u()) != 0xEF) {
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001760 ungetsc_i(c);
Geremy Condra03ebf062011-10-12 18:17:24 -07001761 return;
1762 }
1763 if ((unsigned char)(c = o_getsc_u()) != 0xBB) {
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001764 ungetsc_i(c);
1765 ungetsc_i(0xEF);
Geremy Condra03ebf062011-10-12 18:17:24 -07001766 return;
1767 }
1768 if ((unsigned char)(c = o_getsc_u()) != 0xBF) {
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001769 ungetsc_i(c);
1770 ungetsc_i(0xBB);
1771 ungetsc_i(0xEF);
Geremy Condra03ebf062011-10-12 18:17:24 -07001772 return;
1773 }
1774 UTFMODE |= 8;
1775}
1776
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001777static Lex_state *
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001778push_state_i(State_info *si, Lex_state *old_end)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001779{
Geremy Condra03ebf062011-10-12 18:17:24 -07001780 Lex_state *news = alloc2(STATE_BSIZE, sizeof(Lex_state), ATEMP);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001781
Geremy Condra03ebf062011-10-12 18:17:24 -07001782 news[0].ls_base = old_end;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001783 si->base = &news[0];
1784 si->end = &news[STATE_BSIZE];
1785 return (&news[1]);
1786}
1787
1788static Lex_state *
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001789pop_state_i(State_info *si, Lex_state *old_end)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001790{
1791 Lex_state *old_base = si->base;
1792
Geremy Condra03ebf062011-10-12 18:17:24 -07001793 si->base = old_end->ls_base - STATE_BSIZE;
1794 si->end = old_end->ls_base;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001795
1796 afree(old_base, ATEMP);
1797
1798 return (si->base + STATE_BSIZE - 1);
1799}