1 |
/* $OpenBSD: tree.c,v 1.21 2015/09/01 13:12:31 tedu Exp $ */ |
2 |
|
3 |
/*- |
4 |
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, |
5 |
* 2011, 2012, 2013, 2015, 2016, 2017 |
6 |
* mirabilos <m@mirbsd.org> |
7 |
* |
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 |
|
26 |
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.95 2018/01/14 00:03:05 tg Exp $"); |
27 |
|
28 |
#define INDENT 8 |
29 |
|
30 |
static void ptree(struct op *, int, struct shf *); |
31 |
static void pioact(struct shf *, struct ioword *); |
32 |
static const char *wdvarput(struct shf *, const char *, int, int); |
33 |
static void vfptreef(struct shf *, int, const char *, va_list); |
34 |
static struct ioword **iocopy(struct ioword **, Area *); |
35 |
static void iofree(struct ioword **, Area *); |
36 |
|
37 |
/* "foo& ; bar" and "foo |& ; bar" are invalid */ |
38 |
static bool prevent_semicolon; |
39 |
|
40 |
static const char Telif_pT[] = "elif %T"; |
41 |
|
42 |
/* |
43 |
* print a command tree |
44 |
*/ |
45 |
static void |
46 |
ptree(struct op *t, int indent, struct shf *shf) |
47 |
{ |
48 |
const char **w; |
49 |
struct ioword **ioact; |
50 |
struct op *t1; |
51 |
int i; |
52 |
const char *ccp; |
53 |
|
54 |
Chain: |
55 |
if (t == NULL) |
56 |
return; |
57 |
switch (t->type) { |
58 |
case TCOM: |
59 |
prevent_semicolon = false; |
60 |
/* special-case 'var=<<EOF' (cf. exec.c:execute) */ |
61 |
if (t->args && |
62 |
/* we have zero arguments, i.e. no program to run */ |
63 |
t->args[0] == NULL && |
64 |
/* we have exactly one variable assignment */ |
65 |
t->vars[0] != NULL && t->vars[1] == NULL && |
66 |
/* we have exactly one I/O redirection */ |
67 |
t->ioact != NULL && t->ioact[0] != NULL && |
68 |
t->ioact[1] == NULL && |
69 |
/* of type "here document" (or "here string") */ |
70 |
(t->ioact[0]->ioflag & IOTYPE) == IOHERE && |
71 |
/* the variable assignment begins with a valid varname */ |
72 |
(ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] && |
73 |
/* and has no right-hand side (i.e. "varname=") */ |
74 |
ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) || |
75 |
/* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR && |
76 |
ccp[3] == '=' && ccp[4] == EOS))) { |
77 |
fptreef(shf, indent, Tf_S, t->vars[0]); |
78 |
break; |
79 |
} |
80 |
|
81 |
if (t->vars) { |
82 |
w = (const char **)t->vars; |
83 |
while (*w) |
84 |
fptreef(shf, indent, Tf_S_, *w++); |
85 |
} else |
86 |
shf_puts("#no-vars# ", shf); |
87 |
if (t->args) { |
88 |
w = t->args; |
89 |
if (*w && **w == CHAR) { |
90 |
char *cp = wdstrip(*w++, WDS_TPUTS); |
91 |
|
92 |
if (valid_alias_name(cp)) |
93 |
shf_putc('\\', shf); |
94 |
shf_puts(cp, shf); |
95 |
shf_putc(' ', shf); |
96 |
afree(cp, ATEMP); |
97 |
} |
98 |
while (*w) |
99 |
fptreef(shf, indent, Tf_S_, *w++); |
100 |
} else |
101 |
shf_puts("#no-args# ", shf); |
102 |
break; |
103 |
case TEXEC: |
104 |
t = t->left; |
105 |
goto Chain; |
106 |
case TPAREN: |
107 |
fptreef(shf, indent + 2, "( %T) ", t->left); |
108 |
break; |
109 |
case TPIPE: |
110 |
fptreef(shf, indent, "%T| ", t->left); |
111 |
t = t->right; |
112 |
goto Chain; |
113 |
case TLIST: |
114 |
fptreef(shf, indent, "%T%;", t->left); |
115 |
t = t->right; |
116 |
goto Chain; |
117 |
case TOR: |
118 |
case TAND: |
119 |
fptreef(shf, indent, "%T%s %T", |
120 |
t->left, (t->type == TOR) ? "||" : "&&", t->right); |
121 |
break; |
122 |
case TBANG: |
123 |
shf_puts("! ", shf); |
124 |
prevent_semicolon = false; |
125 |
t = t->right; |
126 |
goto Chain; |
127 |
case TDBRACKET: |
128 |
w = t->args; |
129 |
shf_puts("[[", shf); |
130 |
while (*w) |
131 |
fptreef(shf, indent, Tf__S, *w++); |
132 |
shf_puts(" ]] ", shf); |
133 |
break; |
134 |
case TSELECT: |
135 |
case TFOR: |
136 |
fptreef(shf, indent, "%s %s ", |
137 |
(t->type == TFOR) ? "for" : Tselect, t->str); |
138 |
if (t->vars != NULL) { |
139 |
shf_puts("in ", shf); |
140 |
w = (const char **)t->vars; |
141 |
while (*w) |
142 |
fptreef(shf, indent, Tf_S_, *w++); |
143 |
fptreef(shf, indent, Tft_end); |
144 |
} |
145 |
fptreef(shf, indent + INDENT, "do%N%T", t->left); |
146 |
fptreef(shf, indent, "%;done "); |
147 |
break; |
148 |
case TCASE: |
149 |
fptreef(shf, indent, "case %S in", t->str); |
150 |
for (t1 = t->left; t1 != NULL; t1 = t1->right) { |
151 |
fptreef(shf, indent, "%N("); |
152 |
w = (const char **)t1->vars; |
153 |
while (*w) { |
154 |
fptreef(shf, indent, "%S%c", *w, |
155 |
(w[1] != NULL) ? '|' : ')'); |
156 |
++w; |
157 |
} |
158 |
fptreef(shf, indent + INDENT, "%N%T%N;%c", t1->left, |
159 |
t1->u.charflag); |
160 |
} |
161 |
fptreef(shf, indent, "%Nesac "); |
162 |
break; |
163 |
case TELIF: |
164 |
internal_errorf(TELIF_unexpected); |
165 |
/* FALLTHROUGH */ |
166 |
case TIF: |
167 |
i = 2; |
168 |
t1 = t; |
169 |
goto process_TIF; |
170 |
do { |
171 |
t1 = t1->right; |
172 |
i = 0; |
173 |
fptreef(shf, indent, Tft_end); |
174 |
process_TIF: |
175 |
/* 5 == strlen("elif ") */ |
176 |
fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left); |
177 |
t1 = t1->right; |
178 |
if (t1->left != NULL) { |
179 |
fptreef(shf, indent, Tft_end); |
180 |
fptreef(shf, indent + INDENT, "%s%N%T", |
181 |
"then", t1->left); |
182 |
} |
183 |
} while (t1->right && t1->right->type == TELIF); |
184 |
if (t1->right != NULL) { |
185 |
fptreef(shf, indent, Tft_end); |
186 |
fptreef(shf, indent + INDENT, "%s%N%T", |
187 |
"else", t1->right); |
188 |
} |
189 |
fptreef(shf, indent, "%;fi "); |
190 |
break; |
191 |
case TWHILE: |
192 |
case TUNTIL: |
193 |
/* 6 == strlen("while "/"until ") */ |
194 |
fptreef(shf, indent + 6, Tf_s_T, |
195 |
(t->type == TWHILE) ? "while" : "until", |
196 |
t->left); |
197 |
fptreef(shf, indent, Tft_end); |
198 |
fptreef(shf, indent + INDENT, "do%N%T", t->right); |
199 |
fptreef(shf, indent, "%;done "); |
200 |
break; |
201 |
case TBRACE: |
202 |
fptreef(shf, indent + INDENT, "{%N%T", t->left); |
203 |
fptreef(shf, indent, "%;} "); |
204 |
break; |
205 |
case TCOPROC: |
206 |
fptreef(shf, indent, "%T|& ", t->left); |
207 |
prevent_semicolon = true; |
208 |
break; |
209 |
case TASYNC: |
210 |
fptreef(shf, indent, "%T& ", t->left); |
211 |
prevent_semicolon = true; |
212 |
break; |
213 |
case TFUNCT: |
214 |
fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left); |
215 |
break; |
216 |
case TTIME: |
217 |
fptreef(shf, indent, Tf_s_T, Ttime, t->left); |
218 |
break; |
219 |
default: |
220 |
shf_puts("<botch>", shf); |
221 |
prevent_semicolon = false; |
222 |
break; |
223 |
} |
224 |
if ((ioact = t->ioact) != NULL) { |
225 |
bool need_nl = false; |
226 |
|
227 |
while (*ioact != NULL) |
228 |
pioact(shf, *ioact++); |
229 |
/* Print here documents after everything else... */ |
230 |
ioact = t->ioact; |
231 |
while (*ioact != NULL) { |
232 |
struct ioword *iop = *ioact++; |
233 |
|
234 |
/* heredoc is NULL when tracing (set -x) */ |
235 |
if ((iop->ioflag & (IOTYPE | IOHERESTR)) == IOHERE && |
236 |
iop->heredoc) { |
237 |
shf_putc('\n', shf); |
238 |
shf_puts(iop->heredoc, shf); |
239 |
fptreef(shf, indent, Tf_s, |
240 |
evalstr(iop->delim, 0)); |
241 |
need_nl = true; |
242 |
} |
243 |
} |
244 |
/* |
245 |
* Last delimiter must be followed by a newline (this |
246 |
* often leads to an extra blank line, but it's not |
247 |
* worth worrying about) |
248 |
*/ |
249 |
if (need_nl) { |
250 |
shf_putc('\n', shf); |
251 |
prevent_semicolon = true; |
252 |
} |
253 |
} |
254 |
} |
255 |
|
256 |
static void |
257 |
pioact(struct shf *shf, struct ioword *iop) |
258 |
{ |
259 |
unsigned short flag = iop->ioflag; |
260 |
unsigned short type = flag & IOTYPE; |
261 |
short expected; |
262 |
|
263 |
expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 : |
264 |
(type == IOCAT || type == IOWRITE) ? 1 : |
265 |
(type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit : |
266 |
iop->unit + 1; |
267 |
if (iop->unit != expected) |
268 |
shf_fprintf(shf, Tf_d, (int)iop->unit); |
269 |
|
270 |
switch (type) { |
271 |
case IOREAD: |
272 |
shf_putc('<', shf); |
273 |
break; |
274 |
case IOHERE: |
275 |
shf_puts("<<", shf); |
276 |
if (flag & IOSKIP) |
277 |
shf_putc('-', shf); |
278 |
else if (flag & IOHERESTR) |
279 |
shf_putc('<', shf); |
280 |
break; |
281 |
case IOCAT: |
282 |
shf_puts(">>", shf); |
283 |
break; |
284 |
case IOWRITE: |
285 |
shf_putc('>', shf); |
286 |
if (flag & IOCLOB) |
287 |
shf_putc('|', shf); |
288 |
break; |
289 |
case IORDWR: |
290 |
shf_puts("<>", shf); |
291 |
break; |
292 |
case IODUP: |
293 |
shf_puts(flag & IORDUP ? "<&" : ">&", shf); |
294 |
break; |
295 |
} |
296 |
/* name/delim are NULL when printing syntax errors */ |
297 |
if (type == IOHERE) { |
298 |
if (iop->delim && !(iop->ioflag & IONDELIM)) |
299 |
wdvarput(shf, iop->delim, 0, WDS_TPUTS); |
300 |
} else if (iop->ioname) { |
301 |
if (flag & IONAMEXP) |
302 |
print_value_quoted(shf, iop->ioname); |
303 |
else |
304 |
wdvarput(shf, iop->ioname, 0, WDS_TPUTS); |
305 |
} |
306 |
shf_putc(' ', shf); |
307 |
prevent_semicolon = false; |
308 |
} |
309 |
|
310 |
/* variant of fputs for ptreef and wdstrip */ |
311 |
static const char * |
312 |
wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode) |
313 |
{ |
314 |
int c; |
315 |
const char *cs; |
316 |
|
317 |
/*- |
318 |
* problems: |
319 |
* `...` -> $(...) |
320 |
* 'foo' -> "foo" |
321 |
* x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS |
322 |
* x${foo:-'hi'} -> x${foo:-hi} |
323 |
* could change encoding to: |
324 |
* OQUOTE ["'] ... CQUOTE ["'] |
325 |
* COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case) |
326 |
*/ |
327 |
while (/* CONSTCOND */ 1) |
328 |
switch (*wp++) { |
329 |
case EOS: |
330 |
return (--wp); |
331 |
case ADELIM: |
332 |
if (ord(*wp) == ORD(/*{*/ '}')) { |
333 |
++wp; |
334 |
goto wdvarput_csubst; |
335 |
} |
336 |
/* FALLTHROUGH */ |
337 |
case CHAR: |
338 |
c = ord(*wp++); |
339 |
shf_putc(c, shf); |
340 |
break; |
341 |
case QCHAR: |
342 |
c = ord(*wp++); |
343 |
if (opmode & WDS_TPUTS) |
344 |
switch (c) { |
345 |
case ORD('\n'): |
346 |
if (quotelevel == 0) { |
347 |
c = ORD('\''); |
348 |
shf_putc(c, shf); |
349 |
shf_putc(ORD('\n'), shf); |
350 |
} |
351 |
break; |
352 |
default: |
353 |
if (quotelevel == 0) |
354 |
/* FALLTHROUGH */ |
355 |
case ORD('"'): |
356 |
case ORD('`'): |
357 |
case ORD('$'): |
358 |
case ORD('\\'): |
359 |
shf_putc(ORD('\\'), shf); |
360 |
break; |
361 |
} |
362 |
shf_putc(c, shf); |
363 |
break; |
364 |
case COMASUB: |
365 |
case COMSUB: |
366 |
shf_puts("$(", shf); |
367 |
cs = ")"; |
368 |
if (ord(*wp) == ORD('(' /*)*/)) |
369 |
shf_putc(' ', shf); |
370 |
pSUB: |
371 |
while ((c = *wp++) != 0) |
372 |
shf_putc(c, shf); |
373 |
shf_puts(cs, shf); |
374 |
break; |
375 |
case FUNASUB: |
376 |
case FUNSUB: |
377 |
c = ORD(' '); |
378 |
if (0) |
379 |
/* FALLTHROUGH */ |
380 |
case VALSUB: |
381 |
c = ORD('|'); |
382 |
shf_putc('$', shf); |
383 |
shf_putc('{', shf); |
384 |
shf_putc(c, shf); |
385 |
cs = ";}"; |
386 |
goto pSUB; |
387 |
case EXPRSUB: |
388 |
shf_puts("$((", shf); |
389 |
cs = "))"; |
390 |
goto pSUB; |
391 |
case OQUOTE: |
392 |
if (opmode & WDS_TPUTS) { |
393 |
quotelevel++; |
394 |
shf_putc('"', shf); |
395 |
} |
396 |
break; |
397 |
case CQUOTE: |
398 |
if (opmode & WDS_TPUTS) { |
399 |
if (quotelevel) |
400 |
quotelevel--; |
401 |
shf_putc('"', shf); |
402 |
} |
403 |
break; |
404 |
case OSUBST: |
405 |
shf_putc('$', shf); |
406 |
if (ord(*wp++) == ORD('{')) |
407 |
shf_putc('{', shf); |
408 |
while ((c = *wp++) != 0) |
409 |
shf_putc(c, shf); |
410 |
wp = wdvarput(shf, wp, 0, opmode); |
411 |
break; |
412 |
case CSUBST: |
413 |
if (ord(*wp++) == ORD('}')) { |
414 |
wdvarput_csubst: |
415 |
shf_putc('}', shf); |
416 |
} |
417 |
return (wp); |
418 |
case OPAT: |
419 |
shf_putchar(*wp++, shf); |
420 |
shf_putc('(', shf); |
421 |
break; |
422 |
case SPAT: |
423 |
c = ORD('|'); |
424 |
if (0) |
425 |
/* FALLTHROUGH */ |
426 |
case CPAT: |
427 |
c = ORD(/*(*/ ')'); |
428 |
shf_putc(c, shf); |
429 |
break; |
430 |
} |
431 |
} |
432 |
|
433 |
/* |
434 |
* this is the _only_ way to reliably handle |
435 |
* variable args with an ANSI compiler |
436 |
*/ |
437 |
/* VARARGS */ |
438 |
void |
439 |
fptreef(struct shf *shf, int indent, const char *fmt, ...) |
440 |
{ |
441 |
va_list va; |
442 |
|
443 |
va_start(va, fmt); |
444 |
vfptreef(shf, indent, fmt, va); |
445 |
va_end(va); |
446 |
} |
447 |
|
448 |
/* VARARGS */ |
449 |
char * |
450 |
snptreef(char *s, ssize_t n, const char *fmt, ...) |
451 |
{ |
452 |
va_list va; |
453 |
struct shf shf; |
454 |
|
455 |
shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf); |
456 |
|
457 |
va_start(va, fmt); |
458 |
vfptreef(&shf, 0, fmt, va); |
459 |
va_end(va); |
460 |
|
461 |
/* shf_sclose NUL terminates */ |
462 |
return (shf_sclose(&shf)); |
463 |
} |
464 |
|
465 |
static void |
466 |
vfptreef(struct shf *shf, int indent, const char *fmt, va_list va) |
467 |
{ |
468 |
int c; |
469 |
|
470 |
while ((c = ord(*fmt++))) { |
471 |
if (c == '%') { |
472 |
switch ((c = ord(*fmt++))) { |
473 |
case ORD('c'): |
474 |
/* character (octet, probably) */ |
475 |
shf_putchar(va_arg(va, int), shf); |
476 |
break; |
477 |
case ORD('s'): |
478 |
/* string */ |
479 |
shf_puts(va_arg(va, char *), shf); |
480 |
break; |
481 |
case ORD('S'): |
482 |
/* word */ |
483 |
wdvarput(shf, va_arg(va, char *), 0, WDS_TPUTS); |
484 |
break; |
485 |
case ORD('d'): |
486 |
/* signed decimal */ |
487 |
shf_fprintf(shf, Tf_d, va_arg(va, int)); |
488 |
break; |
489 |
case ORD('u'): |
490 |
/* unsigned decimal */ |
491 |
shf_fprintf(shf, "%u", va_arg(va, unsigned int)); |
492 |
break; |
493 |
case ORD('T'): |
494 |
/* format tree */ |
495 |
ptree(va_arg(va, struct op *), indent, shf); |
496 |
goto dont_trash_prevent_semicolon; |
497 |
case ORD(';'): |
498 |
/* newline or ; */ |
499 |
case ORD('N'): |
500 |
/* newline or space */ |
501 |
if (shf->flags & SHF_STRING) { |
502 |
if ((unsigned int)c == ORD(';') && |
503 |
!prevent_semicolon) |
504 |
shf_putc(';', shf); |
505 |
shf_putc(' ', shf); |
506 |
} else { |
507 |
int i; |
508 |
|
509 |
shf_putc('\n', shf); |
510 |
i = indent; |
511 |
while (i >= 8) { |
512 |
shf_putc('\t', shf); |
513 |
i -= 8; |
514 |
} |
515 |
while (i--) |
516 |
shf_putc(' ', shf); |
517 |
} |
518 |
break; |
519 |
case ORD('R'): |
520 |
/* I/O redirection */ |
521 |
pioact(shf, va_arg(va, struct ioword *)); |
522 |
break; |
523 |
default: |
524 |
shf_putc(c, shf); |
525 |
break; |
526 |
} |
527 |
} else |
528 |
shf_putc(c, shf); |
529 |
prevent_semicolon = false; |
530 |
dont_trash_prevent_semicolon: |
531 |
; |
532 |
} |
533 |
} |
534 |
|
535 |
/* |
536 |
* copy tree (for function definition) |
537 |
*/ |
538 |
struct op * |
539 |
tcopy(struct op *t, Area *ap) |
540 |
{ |
541 |
struct op *r; |
542 |
const char **tw; |
543 |
char **rw; |
544 |
|
545 |
if (t == NULL) |
546 |
return (NULL); |
547 |
|
548 |
r = alloc(sizeof(struct op), ap); |
549 |
|
550 |
r->type = t->type; |
551 |
r->u.evalflags = t->u.evalflags; |
552 |
|
553 |
if (t->type == TCASE) |
554 |
r->str = wdcopy(t->str, ap); |
555 |
else |
556 |
strdupx(r->str, t->str, ap); |
557 |
|
558 |
if (t->vars == NULL) |
559 |
r->vars = NULL; |
560 |
else { |
561 |
tw = (const char **)t->vars; |
562 |
while (*tw) |
563 |
++tw; |
564 |
rw = r->vars = alloc2(tw - (const char **)t->vars + 1, |
565 |
sizeof(*tw), ap); |
566 |
tw = (const char **)t->vars; |
567 |
while (*tw) |
568 |
*rw++ = wdcopy(*tw++, ap); |
569 |
*rw = NULL; |
570 |
} |
571 |
|
572 |
if (t->args == NULL) |
573 |
r->args = NULL; |
574 |
else { |
575 |
tw = t->args; |
576 |
while (*tw) |
577 |
++tw; |
578 |
r->args = (const char **)(rw = alloc2(tw - t->args + 1, |
579 |
sizeof(*tw), ap)); |
580 |
tw = t->args; |
581 |
while (*tw) |
582 |
*rw++ = wdcopy(*tw++, ap); |
583 |
*rw = NULL; |
584 |
} |
585 |
|
586 |
r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap); |
587 |
|
588 |
r->left = tcopy(t->left, ap); |
589 |
r->right = tcopy(t->right, ap); |
590 |
r->lineno = t->lineno; |
591 |
|
592 |
return (r); |
593 |
} |
594 |
|
595 |
char * |
596 |
wdcopy(const char *wp, Area *ap) |
597 |
{ |
598 |
size_t len; |
599 |
|
600 |
len = wdscan(wp, EOS) - wp; |
601 |
return (memcpy(alloc(len, ap), wp, len)); |
602 |
} |
603 |
|
604 |
/* return the position of prefix c in wp plus 1 */ |
605 |
const char * |
606 |
wdscan(const char *wp, int c) |
607 |
{ |
608 |
int nest = 0; |
609 |
|
610 |
while (/* CONSTCOND */ 1) |
611 |
switch (*wp++) { |
612 |
case EOS: |
613 |
return (wp); |
614 |
case ADELIM: |
615 |
if (c == ADELIM && nest == 0) |
616 |
return (wp + 1); |
617 |
if (ord(*wp) == ORD(/*{*/ '}')) |
618 |
goto wdscan_csubst; |
619 |
/* FALLTHROUGH */ |
620 |
case CHAR: |
621 |
case QCHAR: |
622 |
wp++; |
623 |
break; |
624 |
case COMASUB: |
625 |
case COMSUB: |
626 |
case FUNASUB: |
627 |
case FUNSUB: |
628 |
case VALSUB: |
629 |
case EXPRSUB: |
630 |
while (*wp++ != 0) |
631 |
; |
632 |
break; |
633 |
case OQUOTE: |
634 |
case CQUOTE: |
635 |
break; |
636 |
case OSUBST: |
637 |
nest++; |
638 |
while (*wp++ != '\0') |
639 |
; |
640 |
break; |
641 |
case CSUBST: |
642 |
wdscan_csubst: |
643 |
wp++; |
644 |
if (c == CSUBST && nest == 0) |
645 |
return (wp); |
646 |
nest--; |
647 |
break; |
648 |
case OPAT: |
649 |
nest++; |
650 |
wp++; |
651 |
break; |
652 |
case SPAT: |
653 |
case CPAT: |
654 |
if (c == wp[-1] && nest == 0) |
655 |
return (wp); |
656 |
if (wp[-1] == CPAT) |
657 |
nest--; |
658 |
break; |
659 |
default: |
660 |
internal_warningf( |
661 |
"wdscan: unknown char 0x%X (carrying on)", |
662 |
(unsigned char)wp[-1]); |
663 |
} |
664 |
} |
665 |
|
666 |
/* |
667 |
* return a copy of wp without any of the mark up characters and with |
668 |
* quote characters (" ' \) stripped. (string is allocated from ATEMP) |
669 |
*/ |
670 |
char * |
671 |
wdstrip(const char *wp, int opmode) |
672 |
{ |
673 |
struct shf shf; |
674 |
|
675 |
shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf); |
676 |
wdvarput(&shf, wp, 0, opmode); |
677 |
/* shf_sclose NUL terminates */ |
678 |
return (shf_sclose(&shf)); |
679 |
} |
680 |
|
681 |
static struct ioword ** |
682 |
iocopy(struct ioword **iow, Area *ap) |
683 |
{ |
684 |
struct ioword **ior; |
685 |
int i; |
686 |
|
687 |
ior = iow; |
688 |
while (*ior) |
689 |
++ior; |
690 |
ior = alloc2(ior - iow + 1, sizeof(struct ioword *), ap); |
691 |
|
692 |
for (i = 0; iow[i] != NULL; i++) { |
693 |
struct ioword *p, *q; |
694 |
|
695 |
p = iow[i]; |
696 |
q = alloc(sizeof(struct ioword), ap); |
697 |
ior[i] = q; |
698 |
*q = *p; |
699 |
if (p->ioname != NULL) |
700 |
q->ioname = wdcopy(p->ioname, ap); |
701 |
if (p->delim != NULL) |
702 |
q->delim = wdcopy(p->delim, ap); |
703 |
if (p->heredoc != NULL) |
704 |
strdupx(q->heredoc, p->heredoc, ap); |
705 |
} |
706 |
ior[i] = NULL; |
707 |
|
708 |
return (ior); |
709 |
} |
710 |
|
711 |
/* |
712 |
* free tree (for function definition) |
713 |
*/ |
714 |
void |
715 |
tfree(struct op *t, Area *ap) |
716 |
{ |
717 |
char **w; |
718 |
|
719 |
if (t == NULL) |
720 |
return; |
721 |
|
722 |
afree(t->str, ap); |
723 |
|
724 |
if (t->vars != NULL) { |
725 |
for (w = t->vars; *w != NULL; w++) |
726 |
afree(*w, ap); |
727 |
afree(t->vars, ap); |
728 |
} |
729 |
|
730 |
if (t->args != NULL) { |
731 |
/*XXX we assume the caller is right */ |
732 |
union mksh_ccphack cw; |
733 |
|
734 |
cw.ro = t->args; |
735 |
for (w = cw.rw; *w != NULL; w++) |
736 |
afree(*w, ap); |
737 |
afree(t->args, ap); |
738 |
} |
739 |
|
740 |
if (t->ioact != NULL) |
741 |
iofree(t->ioact, ap); |
742 |
|
743 |
tfree(t->left, ap); |
744 |
tfree(t->right, ap); |
745 |
|
746 |
afree(t, ap); |
747 |
} |
748 |
|
749 |
static void |
750 |
iofree(struct ioword **iow, Area *ap) |
751 |
{ |
752 |
struct ioword **iop; |
753 |
struct ioword *p; |
754 |
|
755 |
iop = iow; |
756 |
while ((p = *iop++) != NULL) { |
757 |
afree(p->ioname, ap); |
758 |
afree(p->delim, ap); |
759 |
afree(p->heredoc, ap); |
760 |
afree(p, ap); |
761 |
} |
762 |
afree(iow, ap); |
763 |
} |
764 |
|
765 |
void |
766 |
fpFUNCTf(struct shf *shf, int i, bool isksh, const char *k, struct op *v) |
767 |
{ |
768 |
if (isksh) |
769 |
fptreef(shf, i, "%s %s %T", Tfunction, k, v); |
770 |
else if (ktsearch(&keywords, k, hash(k))) |
771 |
fptreef(shf, i, "%s %s() %T", Tfunction, k, v); |
772 |
else |
773 |
fptreef(shf, i, "%s() %T", k, v); |
774 |
} |
775 |
|
776 |
|
777 |
/* for jobs.c */ |
778 |
void |
779 |
vistree(char *dst, size_t sz, struct op *t) |
780 |
{ |
781 |
unsigned int c; |
782 |
char *cp, *buf; |
783 |
size_t n; |
784 |
|
785 |
buf = alloc(sz + 16, ATEMP); |
786 |
snptreef(buf, sz + 16, Tf_T, t); |
787 |
cp = buf; |
788 |
vist_loop: |
789 |
if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) { |
790 |
if (c == 0 || n >= sz) |
791 |
/* NUL or not enough free space */ |
792 |
goto vist_out; |
793 |
/* copy multibyte char */ |
794 |
sz -= n; |
795 |
while (n--) |
796 |
*dst++ = *cp++; |
797 |
goto vist_loop; |
798 |
} |
799 |
if (--sz == 0 || (c = ord(*cp++)) == 0) |
800 |
/* NUL or not enough free space */ |
801 |
goto vist_out; |
802 |
if (ksh_isctrl(c)) { |
803 |
/* C0 or C1 control character or DEL */ |
804 |
if (--sz == 0) |
805 |
/* not enough free space for two chars */ |
806 |
goto vist_out; |
807 |
*dst++ = '^'; |
808 |
c = ksh_unctrl(c); |
809 |
} else if (UTFMODE && rtt2asc(c) > 0x7F) { |
810 |
/* better not try to display broken multibyte chars */ |
811 |
/* also go easy on the Unicode: no U+FFFD here */ |
812 |
c = ORD('?'); |
813 |
} |
814 |
*dst++ = c; |
815 |
goto vist_loop; |
816 |
|
817 |
vist_out: |
818 |
*dst = '\0'; |
819 |
afree(buf, ATEMP); |
820 |
} |
821 |
|
822 |
#ifdef DEBUG |
823 |
void |
824 |
dumpchar(struct shf *shf, int c) |
825 |
{ |
826 |
if (ksh_isctrl(c)) { |
827 |
/* C0 or C1 control character or DEL */ |
828 |
shf_putc('^', shf); |
829 |
c = ksh_unctrl(c); |
830 |
} |
831 |
shf_putc(c, shf); |
832 |
} |
833 |
|
834 |
/* see: wdvarput */ |
835 |
static const char * |
836 |
dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel) |
837 |
{ |
838 |
int c; |
839 |
|
840 |
while (/* CONSTCOND */ 1) { |
841 |
switch(*wp++) { |
842 |
case EOS: |
843 |
shf_puts("EOS", shf); |
844 |
return (--wp); |
845 |
case ADELIM: |
846 |
if (ord(*wp) == ORD(/*{*/ '}')) { |
847 |
shf_puts(/*{*/ "]ADELIM(})", shf); |
848 |
return (wp + 1); |
849 |
} |
850 |
shf_puts("ADELIM=", shf); |
851 |
if (0) |
852 |
/* FALLTHROUGH */ |
853 |
case CHAR: |
854 |
shf_puts("CHAR=", shf); |
855 |
dumpchar(shf, *wp++); |
856 |
break; |
857 |
case QCHAR: |
858 |
shf_puts("QCHAR<", shf); |
859 |
c = ord(*wp++); |
860 |
if (quotelevel == 0 || c == ORD('"') || |
861 |
c == ORD('\\') || ctype(c, C_DOLAR | C_GRAVE)) |
862 |
shf_putc('\\', shf); |
863 |
dumpchar(shf, c); |
864 |
goto closeandout; |
865 |
case COMASUB: |
866 |
shf_puts("COMASUB<", shf); |
867 |
goto dumpsub; |
868 |
case COMSUB: |
869 |
shf_puts("COMSUB<", shf); |
870 |
dumpsub: |
871 |
while ((c = *wp++) != 0) |
872 |
dumpchar(shf, c); |
873 |
closeandout: |
874 |
shf_putc('>', shf); |
875 |
break; |
876 |
case FUNASUB: |
877 |
shf_puts("FUNASUB<", shf); |
878 |
goto dumpsub; |
879 |
case FUNSUB: |
880 |
shf_puts("FUNSUB<", shf); |
881 |
goto dumpsub; |
882 |
case VALSUB: |
883 |
shf_puts("VALSUB<", shf); |
884 |
goto dumpsub; |
885 |
case EXPRSUB: |
886 |
shf_puts("EXPRSUB<", shf); |
887 |
goto dumpsub; |
888 |
case OQUOTE: |
889 |
shf_fprintf(shf, "OQUOTE{%d" /*}*/, ++quotelevel); |
890 |
break; |
891 |
case CQUOTE: |
892 |
shf_fprintf(shf, /*{*/ "%d}CQUOTE", quotelevel); |
893 |
if (quotelevel) |
894 |
quotelevel--; |
895 |
else |
896 |
shf_puts("(err)", shf); |
897 |
break; |
898 |
case OSUBST: |
899 |
shf_puts("OSUBST(", shf); |
900 |
dumpchar(shf, *wp++); |
901 |
shf_puts(")[", shf); |
902 |
while ((c = *wp++) != 0) |
903 |
dumpchar(shf, c); |
904 |
shf_putc('|', shf); |
905 |
wp = dumpwdvar_i(shf, wp, 0); |
906 |
break; |
907 |
case CSUBST: |
908 |
shf_puts("]CSUBST(", shf); |
909 |
dumpchar(shf, *wp++); |
910 |
shf_putc(')', shf); |
911 |
return (wp); |
912 |
case OPAT: |
913 |
shf_puts("OPAT=", shf); |
914 |
dumpchar(shf, *wp++); |
915 |
break; |
916 |
case SPAT: |
917 |
shf_puts("SPAT", shf); |
918 |
break; |
919 |
case CPAT: |
920 |
shf_puts("CPAT", shf); |
921 |
break; |
922 |
default: |
923 |
shf_fprintf(shf, "INVAL<%u>", (uint8_t)wp[-1]); |
924 |
break; |
925 |
} |
926 |
shf_putc(' ', shf); |
927 |
} |
928 |
} |
929 |
void |
930 |
dumpwdvar(struct shf *shf, const char *wp) |
931 |
{ |
932 |
dumpwdvar_i(shf, wp, 0); |
933 |
} |
934 |
|
935 |
void |
936 |
dumpioact(struct shf *shf, struct op *t) |
937 |
{ |
938 |
struct ioword **ioact, *iop; |
939 |
|
940 |
if ((ioact = t->ioact) == NULL) |
941 |
return; |
942 |
|
943 |
shf_puts("{IOACT", shf); |
944 |
while ((iop = *ioact++) != NULL) { |
945 |
unsigned short type = iop->ioflag & IOTYPE; |
946 |
#define DT(x) case x: shf_puts(#x, shf); break; |
947 |
#define DB(x) if (iop->ioflag & x) shf_puts("|" #x, shf); |
948 |
|
949 |
shf_putc(';', shf); |
950 |
switch (type) { |
951 |
DT(IOREAD) |
952 |
DT(IOWRITE) |
953 |
DT(IORDWR) |
954 |
DT(IOHERE) |
955 |
DT(IOCAT) |
956 |
DT(IODUP) |
957 |
default: |
958 |
shf_fprintf(shf, "unk%d", type); |
959 |
} |
960 |
DB(IOEVAL) |
961 |
DB(IOSKIP) |
962 |
DB(IOCLOB) |
963 |
DB(IORDUP) |
964 |
DB(IONAMEXP) |
965 |
DB(IOBASH) |
966 |
DB(IOHERESTR) |
967 |
DB(IONDELIM) |
968 |
shf_fprintf(shf, ",unit=%d", (int)iop->unit); |
969 |
if (iop->delim && !(iop->ioflag & IONDELIM)) { |
970 |
shf_puts(",delim<", shf); |
971 |
dumpwdvar(shf, iop->delim); |
972 |
shf_putc('>', shf); |
973 |
} |
974 |
if (iop->ioname) { |
975 |
if (iop->ioflag & IONAMEXP) { |
976 |
shf_puts(",name=", shf); |
977 |
print_value_quoted(shf, iop->ioname); |
978 |
} else { |
979 |
shf_puts(",name<", shf); |
980 |
dumpwdvar(shf, iop->ioname); |
981 |
shf_putc('>', shf); |
982 |
} |
983 |
} |
984 |
if (iop->heredoc) { |
985 |
shf_puts(",heredoc=", shf); |
986 |
print_value_quoted(shf, iop->heredoc); |
987 |
} |
988 |
#undef DT |
989 |
#undef DB |
990 |
} |
991 |
shf_putc('}', shf); |
992 |
} |
993 |
|
994 |
void |
995 |
dumptree(struct shf *shf, struct op *t) |
996 |
{ |
997 |
int i, j; |
998 |
const char **w, *name; |
999 |
struct op *t1; |
1000 |
static int nesting; |
1001 |
|
1002 |
for (i = 0; i < nesting; ++i) |
1003 |
shf_putc('\t', shf); |
1004 |
++nesting; |
1005 |
shf_puts("{tree:" /*}*/, shf); |
1006 |
if (t == NULL) { |
1007 |
name = "(null)"; |
1008 |
goto out; |
1009 |
} |
1010 |
dumpioact(shf, t); |
1011 |
switch (t->type) { |
1012 |
#define OPEN(x) case x: name = #x; shf_puts(" {" #x ":", shf); /*}*/ |
1013 |
|
1014 |
OPEN(TCOM) |
1015 |
if (t->vars) { |
1016 |
i = 0; |
1017 |
w = (const char **)t->vars; |
1018 |
while (*w) { |
1019 |
shf_putc('\n', shf); |
1020 |
for (j = 0; j < nesting; ++j) |
1021 |
shf_putc('\t', shf); |
1022 |
shf_fprintf(shf, " var%d<", i++); |
1023 |
dumpwdvar(shf, *w++); |
1024 |
shf_putc('>', shf); |
1025 |
} |
1026 |
} else |
1027 |
shf_puts(" #no-vars#", shf); |
1028 |
if (t->args) { |
1029 |
i = 0; |
1030 |
w = t->args; |
1031 |
while (*w) { |
1032 |
shf_putc('\n', shf); |
1033 |
for (j = 0; j < nesting; ++j) |
1034 |
shf_putc('\t', shf); |
1035 |
shf_fprintf(shf, " arg%d<", i++); |
1036 |
dumpwdvar(shf, *w++); |
1037 |
shf_putc('>', shf); |
1038 |
} |
1039 |
} else |
1040 |
shf_puts(" #no-args#", shf); |
1041 |
break; |
1042 |
OPEN(TEXEC) |
1043 |
dumpleftandout: |
1044 |
t = t->left; |
1045 |
dumpandout: |
1046 |
shf_putc('\n', shf); |
1047 |
dumptree(shf, t); |
1048 |
break; |
1049 |
OPEN(TPAREN) |
1050 |
goto dumpleftandout; |
1051 |
OPEN(TPIPE) |
1052 |
dumpleftmidrightandout: |
1053 |
shf_putc('\n', shf); |
1054 |
dumptree(shf, t->left); |
1055 |
/* middumprightandout: (unused) */ |
1056 |
shf_fprintf(shf, "/%s:", name); |
1057 |
dumprightandout: |
1058 |
t = t->right; |
1059 |
goto dumpandout; |
1060 |
OPEN(TLIST) |
1061 |
goto dumpleftmidrightandout; |
1062 |
OPEN(TOR) |
1063 |
goto dumpleftmidrightandout; |
1064 |
OPEN(TAND) |
1065 |
goto dumpleftmidrightandout; |
1066 |
OPEN(TBANG) |
1067 |
goto dumprightandout; |
1068 |
OPEN(TDBRACKET) |
1069 |
i = 0; |
1070 |
w = t->args; |
1071 |
while (*w) { |
1072 |
shf_putc('\n', shf); |
1073 |
for (j = 0; j < nesting; ++j) |
1074 |
shf_putc('\t', shf); |
1075 |
shf_fprintf(shf, " arg%d<", i++); |
1076 |
dumpwdvar(shf, *w++); |
1077 |
shf_putc('>', shf); |
1078 |
} |
1079 |
break; |
1080 |
OPEN(TFOR) |
1081 |
dumpfor: |
1082 |
shf_fprintf(shf, " str<%s>", t->str); |
1083 |
if (t->vars != NULL) { |
1084 |
i = 0; |
1085 |
w = (const char **)t->vars; |
1086 |
while (*w) { |
1087 |
shf_putc('\n', shf); |
1088 |
for (j = 0; j < nesting; ++j) |
1089 |
shf_putc('\t', shf); |
1090 |
shf_fprintf(shf, " var%d<", i++); |
1091 |
dumpwdvar(shf, *w++); |
1092 |
shf_putc('>', shf); |
1093 |
} |
1094 |
} |
1095 |
goto dumpleftandout; |
1096 |
OPEN(TSELECT) |
1097 |
goto dumpfor; |
1098 |
OPEN(TCASE) |
1099 |
shf_fprintf(shf, " str<%s>", t->str); |
1100 |
i = 0; |
1101 |
for (t1 = t->left; t1 != NULL; t1 = t1->right) { |
1102 |
shf_putc('\n', shf); |
1103 |
for (j = 0; j < nesting; ++j) |
1104 |
shf_putc('\t', shf); |
1105 |
shf_fprintf(shf, " sub%d[(", i); |
1106 |
w = (const char **)t1->vars; |
1107 |
while (*w) { |
1108 |
dumpwdvar(shf, *w); |
1109 |
if (w[1] != NULL) |
1110 |
shf_putc('|', shf); |
1111 |
++w; |
1112 |
} |
1113 |
shf_putc(')', shf); |
1114 |
dumpioact(shf, t); |
1115 |
shf_putc('\n', shf); |
1116 |
dumptree(shf, t1->left); |
1117 |
shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++); |
1118 |
} |
1119 |
break; |
1120 |
OPEN(TWHILE) |
1121 |
goto dumpleftmidrightandout; |
1122 |
OPEN(TUNTIL) |
1123 |
goto dumpleftmidrightandout; |
1124 |
OPEN(TBRACE) |
1125 |
goto dumpleftandout; |
1126 |
OPEN(TCOPROC) |
1127 |
goto dumpleftandout; |
1128 |
OPEN(TASYNC) |
1129 |
goto dumpleftandout; |
1130 |
OPEN(TFUNCT) |
1131 |
shf_fprintf(shf, " str<%s> ksh<%s>", t->str, |
1132 |
t->u.ksh_func ? Ttrue : Tfalse); |
1133 |
goto dumpleftandout; |
1134 |
OPEN(TTIME) |
1135 |
goto dumpleftandout; |
1136 |
OPEN(TIF) |
1137 |
dumpif: |
1138 |
shf_putc('\n', shf); |
1139 |
dumptree(shf, t->left); |
1140 |
t = t->right; |
1141 |
dumpioact(shf, t); |
1142 |
if (t->left != NULL) { |
1143 |
shf_puts(" /TTHEN:\n", shf); |
1144 |
dumptree(shf, t->left); |
1145 |
} |
1146 |
if (t->right && t->right->type == TELIF) { |
1147 |
shf_puts(" /TELIF:", shf); |
1148 |
t = t->right; |
1149 |
dumpioact(shf, t); |
1150 |
goto dumpif; |
1151 |
} |
1152 |
if (t->right != NULL) { |
1153 |
shf_puts(" /TELSE:\n", shf); |
1154 |
dumptree(shf, t->right); |
1155 |
} |
1156 |
break; |
1157 |
OPEN(TEOF) |
1158 |
dumpunexpected: |
1159 |
shf_puts(Tunexpected, shf); |
1160 |
break; |
1161 |
OPEN(TELIF) |
1162 |
goto dumpunexpected; |
1163 |
OPEN(TPAT) |
1164 |
goto dumpunexpected; |
1165 |
default: |
1166 |
name = "TINVALID"; |
1167 |
shf_fprintf(shf, "{T<%d>:" /*}*/, t->type); |
1168 |
goto dumpunexpected; |
1169 |
|
1170 |
#undef OPEN |
1171 |
} |
1172 |
out: |
1173 |
shf_fprintf(shf, /*{*/ " /%s}\n", name); |
1174 |
--nesting; |
1175 |
} |
1176 |
#endif |