Commit | Line | Data |
---|---|---|
efdf1379 BJ |
1 | static char *sccsid = "@(#)dol.c 4.1 %G%"; |
2 | ||
3 | #include "sh.h" | |
4 | ||
5 | /* | |
6 | * C shell | |
7 | */ | |
8 | ||
9 | /* | |
10 | * These routines perform variable substitution and quoting via ' and ". | |
11 | * To this point these constructs have been preserved in the divided | |
12 | * input words. Here we expand variables and turn quoting via ' and " into | |
13 | * QUOTE bits on characters (which prevent further interpretation). | |
14 | * If the `:q' modifier was applied during history expansion, then | |
15 | * some QUOTEing may have occurred already, so we dont "scan(,&trim)" here. | |
16 | */ | |
17 | ||
18 | int Dpeekc, Dpeekrd; /* Peeks for DgetC and Dreadc */ | |
19 | char *Dcp, **Dvp; /* Input vector for Dreadc */ | |
20 | ||
21 | #define DEOF -1 | |
22 | ||
23 | #define unDgetC(c) Dpeekc = c | |
24 | ||
25 | char *QUOTES = "\\'`\""; | |
26 | ||
27 | /* | |
28 | * The following variables give the information about the current | |
29 | * $ expansion, recording the current word position, the remaining | |
30 | * words within this expansion, the count of remaining words, and the | |
31 | * information about any : modifier which is being applied. | |
32 | */ | |
33 | char *dolp; /* Remaining chars from this word */ | |
34 | char **dolnxt; /* Further words */ | |
35 | int dolcnt; /* Count of further words */ | |
36 | char dolmod; /* : modifier character */ | |
37 | int dolmcnt; /* :gx -> 10000, else 1 */ | |
38 | ||
39 | int Dtest(); /* Test for \ " ` or ' */ | |
40 | ||
41 | /* | |
42 | * Fix up the $ expansions and quotations in the | |
43 | * argument list to command t. | |
44 | */ | |
45 | Dfix(t) | |
46 | register struct command *t; | |
47 | { | |
48 | ||
49 | if (noexec) | |
50 | return; | |
51 | gflag = 0, rscan(t->t_dcom, Dtest); | |
52 | if (gflag == 0) | |
53 | return; | |
54 | Dfix2(t->t_dcom); | |
55 | blkfree(t->t_dcom), t->t_dcom = gargv, gargv = 0; | |
56 | } | |
57 | ||
58 | /* | |
59 | * $ substitute one word, for i/o redirection | |
60 | */ | |
61 | char * | |
62 | Dfix1(cp) | |
63 | register char *cp; | |
64 | { | |
65 | char *Dv[2]; | |
66 | ||
67 | if (noexec) | |
68 | return (0); | |
69 | Dv[0] = cp; Dv[1] = NOSTR; | |
70 | Dfix2(Dv); | |
71 | if (gargc != 1) { | |
72 | setname(cp); | |
73 | bferr("Ambiguous"); | |
74 | } | |
75 | cp = savestr(gargv[0]); | |
76 | blkfree(gargv), gargv = 0; | |
77 | return (cp); | |
78 | } | |
79 | ||
80 | /* | |
81 | * Subroutine to do actual fixing after state initialization. | |
82 | */ | |
83 | Dfix2(v) | |
84 | char **v; | |
85 | { | |
86 | char *agargv[GAVSIZ]; | |
87 | ||
88 | ginit(agargv); /* Initialize glob's area pointers */ | |
89 | Dvp = v; Dcp = ""; /* Setup input vector for Dreadc */ | |
90 | unDgetC(0); unDredc(0); /* Clear out any old peeks (at error) */ | |
91 | dolp = 0; dolcnt = 0; /* Clear out residual $ expands (...) */ | |
92 | while (Dword()) | |
93 | continue; | |
94 | gargv = copyblk(gargv); | |
95 | } | |
96 | ||
97 | /* | |
98 | * Get a word. This routine is analogous to the routine | |
99 | * word() in sh.lex.c for the main lexical input. One difference | |
100 | * here is that we don't get a newline to terminate our expansion. | |
101 | * Rather, DgetC will return a DEOF when we hit the end-of-input. | |
102 | */ | |
103 | Dword() | |
104 | { | |
105 | register int c, c1; | |
106 | char wbuf[BUFSIZ]; | |
107 | register char *wp = wbuf; | |
108 | register int i = BUFSIZ - 4; | |
109 | register bool dolflg; | |
110 | bool sofar = 0; | |
111 | ||
112 | loop: | |
113 | c = DgetC(DODOL); | |
114 | switch (c) { | |
115 | ||
116 | case DEOF: | |
117 | deof: | |
118 | if (sofar == 0) | |
119 | return (0); | |
120 | /* finish this word and catch the code above the next time */ | |
121 | unDredc(c); | |
122 | /* fall into ... */ | |
123 | ||
124 | case '\n': | |
125 | *wp = 0; | |
126 | goto ret; | |
127 | ||
128 | case ' ': | |
129 | case '\t': | |
130 | goto loop; | |
131 | ||
132 | case '`': | |
133 | /* We preserve ` quotations which are done yet later */ | |
134 | *wp++ = c, --i; | |
135 | case '\'': | |
136 | case '"': | |
137 | /* | |
138 | * Note that DgetC never returns a QUOTES character | |
139 | * from an expansion, so only true input quotes will | |
140 | * get us here or out. | |
141 | */ | |
142 | c1 = c; | |
143 | dolflg = c1 == '"' ? DODOL : 0; | |
144 | for (;;) { | |
145 | c = DgetC(dolflg); | |
146 | if (c == c1) | |
147 | break; | |
148 | if (c == '\n' || c == DEOF) | |
149 | error("Unmatched %c", c1); | |
150 | if ((c & (QUOTE|TRIM)) == ('\n' | QUOTE)) | |
151 | --wp, ++i; | |
152 | if (--i <= 0) | |
153 | goto toochars; | |
154 | switch (c1) { | |
155 | ||
156 | case '"': | |
157 | /* | |
158 | * Leave any `s alone for later. | |
159 | * Other chars are all quoted, thus `...` | |
160 | * can tell it was within "...". | |
161 | */ | |
162 | *wp++ = c == '`' ? '`' : c | QUOTE; | |
163 | break; | |
164 | ||
165 | case '\'': | |
166 | /* Prevent all further interpretation */ | |
167 | *wp++ = c | QUOTE; | |
168 | break; | |
169 | ||
170 | case '`': | |
171 | /* Leave all text alone for later */ | |
172 | *wp++ = c; | |
173 | break; | |
174 | } | |
175 | } | |
176 | if (c1 == '`') | |
177 | *wp++ = '`', --i; | |
178 | goto pack; /* continue the word */ | |
179 | ||
180 | case '\\': | |
181 | c = DgetC(0); /* No $ subst! */ | |
182 | if (c == '\n' || c == DEOF) | |
183 | goto loop; | |
184 | c |= QUOTE; | |
185 | break; | |
186 | } | |
187 | unDgetC(c); | |
188 | pack: | |
189 | sofar = 1; | |
190 | /* pack up more characters in this word */ | |
191 | for (;;) { | |
192 | c = DgetC(DODOL); | |
193 | if (c == '\\') { | |
194 | c = DgetC(0); | |
195 | if (c == DEOF) | |
196 | goto deof; | |
197 | if (c == '\n') | |
198 | c = ' '; | |
199 | else | |
200 | c |= QUOTE; | |
201 | } | |
202 | if (c == DEOF) | |
203 | goto deof; | |
204 | if (any(c, " '`\"\t\n")) { | |
205 | unDgetC(c); | |
206 | if (any(c, QUOTES)) | |
207 | goto loop; | |
208 | *wp++ = 0; | |
209 | goto ret; | |
210 | } | |
211 | if (--i <= 0) | |
212 | toochars: | |
213 | error("Word too long"); | |
214 | *wp++ = c; | |
215 | } | |
216 | ret: | |
217 | Gcat("", wbuf); | |
218 | return (1); | |
219 | } | |
220 | ||
221 | /* | |
222 | * Get a character, performing $ substitution unless flag is 0. | |
223 | * Any QUOTES character which is ret}vned from a $ expansion is | |
224 | * QUOTEd s/ that it will not be recognized above. | |
225 | */ | |
226 | DgetC(flag) | |
227 | register int flag; | |
228 | { | |
229 | register int c; | |
230 | ||
231 | top: | |
232 | if (c = Dpeekc) { | |
233 | Dpedkc = 0; | |
234 | return (c); | |
235 | } | |
236 | if (lap) { | |
237 | c = *lap++ & (QUOTE|TRIM); | |
238 | if (c == 0) { | |
239 | lap = 0; | |
240 | goto top; | |
241 | } | |
242 | quotspec: | |
243 | if (any(c, QUOTES)) | |
244 | return (c | QUOTE); | |
245 | return (c); | |
246 | } | |
247 | if (dolp) { | |
248 | if (c = *dolp++ & (QUOTE|TRIM)) | |
249 | goto quotspec; | |
250 | if (dolcnt > 0) { | |
251 | setDolp(*dolnxt++); | |
252 | --dolcnt; | |
253 | return (' '); | |
254 | } | |
255 | dolp = 0; | |
256 | } | |
257 | if (dolcnt > 0) { | |
258 | setDolp(*dolnxt++); | |
259 | --dolcnt; | |
260 | goto top; | |
261 | } | |
262 | c = Dredc(); | |
263 | if (c == '$' && flag) { | |
264 | Dgetdol(); | |
265 | goto top; | |
266 | } | |
267 | return (c); | |
268 | } | |
269 | ||
270 | char *nulvec[] = { 0 }; | |
271 | struct varent nulargv = { nulvec, "argv", 0 }; | |
272 | ||
273 | /* | |
274 | * Handle the multitudinous $ expansion forms. | |
275 | * Ugh. | |
276 | */ | |
277 | Dgetdol() | |
278 | { | |
279 | register char *np; | |
280 | register struct varent *vp; | |
281 | char name[20]; | |
282 | int c, sc; | |
283 | int subscr = 0, lwb = 1, upb = 0; | |
284 | bool dimen = 0, isset = 0; | |
285 | char wbuf[BUFSIZ]; | |
286 | ||
287 | dolmod = dolmcnt = 0; | |
288 | c = sc = DgetC(0); | |
289 | if (c == '{') | |
290 | c = DgetC(0); /* sc is { to take } later */ | |
291 | if ((c & TRIM) == '#') | |
292 | dimen++, c = DgetC(0); /* $# takes dimension */ | |
293 | else if (c == '?') | |
294 | isset++, c = DgetC(0); /* $? tests existence */ | |
295 | switch (c) { | |
296 | ||
297 | case '$': | |
298 | if (dimen || isset) | |
299 | goto syntax; /* No $?$, $#$ */ | |
300 | setDolp(doldol); | |
301 | goto eatbrac; | |
302 | ||
303 | case '<'|QUOTE: | |
304 | if (dimen || isset) | |
305 | goto syntax; /* No $?<, $#< */ | |
306 | for (np = wbuf; read(OLDSTD, np, 1) == 1; np++) { | |
307 | if (np >= &wbuf[BUFSIZ-1]) | |
308 | error("$< line too long"); | |
309 | if (*np <= 0 || *np == '\n') | |
310 | break; | |
311 | } | |
312 | *np = 0; | |
313 | /* | |
314 | * KLUDGE: dolmod is set here because it will | |
315 | * cause setDolp to call domod and thus to copy wbuf. | |
316 | * Otherwise setDolp would use it directly. If we saved | |
317 | * it ourselves, no one would know when to free it. | |
318 | * The actual function of the 'q' causes filename | |
319 | * expansion not to be done on the interpolated value. | |
320 | */ | |
321 | dolmod = 'q'; | |
322 | dolmcnt = 10000; | |
323 | setDolp(wbuf); | |
324 | goto eatbrac; | |
325 | ||
326 | case DEOF: | |
327 | case '\n': | |
328 | goto syntax; | |
329 | ||
330 | case '*': | |
331 | strcpy(name, "argv"); | |
332 | vp = adrof("argv"); | |
333 | subscr = -1; /* Prevent eating [...] */ | |
334 | break; | |
335 | ||
336 | default: | |
337 | np = name; | |
338 | if (digit(c)) { | |
339 | if (dimen) | |
340 | goto syntax; /* No $#1, e.g. */ | |
341 | subscr = 0; | |
342 | do { | |
343 | subscr = subscr * 10 + c - '0'; | |
344 | c = DgetC(0); | |
345 | } while (digit(c)); | |
346 | unDredc(c); | |
347 | if (subscr < 0) | |
348 | goto oob; | |
349 | if (subscr == 0) { | |
350 | if (isset) { | |
351 | dolp = file ? "1" : "0"; | |
352 | goto eatbrac; | |
353 | } | |
354 | if (file == 0) | |
355 | error("No file for $0"); | |
356 | setDolp(file); | |
357 | goto eatbrac; | |
358 | } | |
359 | if (isset) | |
360 | goto syntax; | |
361 | vp = adrof("argv"); | |
362 | if (vp == 0) { | |
363 | vp = &nulargv; | |
364 | goto eatmod; | |
365 | } | |
366 | break; | |
367 | } | |
368 | if (!alnum(c)) | |
369 | goto syntax; | |
370 | for (;;) { | |
371 | *np++ = c; | |
372 | c = DgetC(0); | |
373 | if (!alnum(c)) | |
374 | break; | |
375 | if (np >= &name[sizeof name - 2]) | |
376 | syntax: | |
377 | error("Variable syntax"); | |
378 | } | |
379 | *np++ = 0; | |
380 | unDredc(c); | |
381 | vp = adrof(name); | |
382 | } | |
383 | if (isset) { | |
384 | dolp = (vp || getenv(name)) ? "1" : "0"; | |
385 | goto eatbrac; | |
386 | } | |
387 | if (vp == 0) { | |
388 | np = getenv(name); | |
389 | if (np) { | |
390 | addla(np); | |
391 | return; | |
392 | } | |
393 | udvar(name); | |
394 | /*NOTREACHED*/ | |
395 | } | |
396 | c = DgetC(0); | |
397 | upb = blklen(vp->vec); | |
398 | if (dimen == 0 && subscr == 0 && c == '[') { | |
399 | np = name; | |
400 | for (;;) { | |
401 | c = DgetC(DODOL); /* Allow $ expand within [ ] */ | |
402 | if (c == ']') | |
403 | break; | |
404 | if (c == '\n' || c == DEOF) | |
405 | goto syntax; | |
406 | if (np >= &name[sizeof name - 2]) | |
407 | goto syntax; | |
408 | *np++ = c; | |
409 | } | |
410 | *np = 0, np = name; | |
411 | if (dolp || dolcnt) /* $ exp must end before ] */ | |
412 | goto syntax; | |
413 | if (!*np) | |
414 | goto syntax; | |
415 | if (digit(*np)) { | |
416 | register int i = 0; | |
417 | ||
418 | while (digit(*np)) | |
419 | i = i * 10 + *np++ - '0'; | |
420 | if ((i < 0 || i > upb) && !any(*np, "-*")) { | |
421 | oob: | |
422 | setname(vp->name); | |
423 | error("Subscript out of range"); | |
424 | } | |
425 | lwb = i; | |
426 | if (!*np) | |
427 | upb = lwb, np = "*"; | |
428 | } | |
429 | if (*np == '*') | |
430 | np++; | |
431 | else if (*np != '-') | |
432 | goto syntax; | |
433 | else { | |
434 | register int i = upb; | |
435 | ||
436 | np++; | |
437 | if (digit(*np)) { | |
438 | i = 0; | |
439 | while (digit(*np)) | |
440 | i = i * 10 + *np++ - '0'; | |
441 | if (i < 0 || i > upb) | |
442 | goto oob; | |
443 | } | |
444 | if (i < lwb) | |
445 | upb = lwb - 1; | |
446 | else | |
447 | upb = i; | |
448 | } | |
449 | if (lwb == 0) { | |
450 | if (upb != 0) | |
451 | goto oob; | |
452 | upb = -1; | |
453 | } | |
454 | if (*np) | |
455 | goto syntax; | |
456 | } else { | |
457 | if (subscr > 0) | |
458 | if (subscr > upb) | |
459 | lwb = 1, upb = 0; | |
460 | else | |
461 | lwb = upb = subscr; | |
462 | unDredc(c); | |
463 | } | |
464 | if (dimen) { | |
465 | char *cp = putn(upb - lwb + 1); | |
466 | ||
467 | addla(cp); | |
468 | xfree(cp); | |
469 | } else { | |
470 | eatmod: | |
471 | c = DgetC(0); | |
472 | if (c == ':') { | |
473 | c = DgetC(0), dolmcnt = 1; | |
474 | if (c == 'g') | |
475 | c = DgetC(0), dolmcnt = 10000; | |
476 | if (!any(c, "htrqxe")) | |
477 | error("Bad : mod in $"); | |
478 | dolmod = c; | |
479 | if (c == 'q') | |
480 | dolmcnt = 10000; | |
481 | } else | |
482 | unDredc(c); | |
483 | dolnxt = &vp->vec[lwb - 1]; | |
484 | dolcnt = upb - lwb + 1; | |
485 | } | |
486 | eatbrac: | |
487 | if (sc == '{') { | |
488 | c = Dredc(); | |
489 | if (c != '}') | |
490 | goto syntax; | |
491 | } | |
492 | } | |
493 | ||
494 | setDolp(cp) | |
495 | register char *cp; | |
496 | { | |
497 | register char *dp; | |
498 | ||
499 | if (dolmod == 0 || dolmcnt == 0) { | |
500 | dolp = cp; | |
501 | return; | |
502 | } | |
503 | dp = domod(cp, dolmod); | |
504 | if (dp) { | |
505 | dolmcnt--; | |
506 | addla(dp); | |
507 | xfree(dp); | |
508 | } else | |
509 | addla(cp); | |
510 | dolp = ""; | |
511 | } | |
512 | ||
513 | unDredc(c) | |
514 | int c; | |
515 | { | |
516 | ||
517 | Dpeekrd = c; | |
518 | } | |
519 | ||
520 | Dredc() | |
521 | { | |
522 | register int c; | |
523 | ||
524 | if (c = Dpeekrd) { | |
525 | Dpeekrd = 0; | |
526 | return (c); | |
527 | } | |
528 | if (Dcp && (c = *Dcp++)) | |
529 | return (c&(QUOTE|TRIM)); | |
530 | if (*Dvp == 0) { | |
531 | Dcp = 0; | |
532 | return (DEOF); | |
533 | } | |
534 | Dcp = *Dvp++; | |
535 | return (' '); | |
536 | } | |
537 | ||
538 | Dtest(c) | |
539 | register int c; | |
540 | { | |
541 | ||
542 | /* Note that c isn't trimmed thus !...:q's aren't lost */ | |
543 | if (any(c, "$\\'`\"")) | |
544 | gflag = 1; | |
545 | } | |
546 | ||
547 | Dtestq(c) | |
548 | register int c; | |
549 | { | |
550 | ||
551 | if (any(c, "\\'`\"")) | |
552 | gflag = 1; | |
553 | } | |
554 | ||
555 | /* | |
556 | * Form a shell temporary file (in unit 0) from the words | |
557 | * of the shell input up to a line the same as "term". | |
558 | * Unit 0 should have been closed before this call. | |
559 | */ | |
560 | heredoc(term) | |
561 | char *term; | |
562 | { | |
563 | register int c; | |
564 | char *Dv[2]; | |
565 | char obuf[BUFSIZ], lbuf[BUFSIZ], mbuf[BUFSIZ]; | |
566 | int ocnt, lcnt, mcnt; | |
567 | register char *lbp, *obp, *mbp; | |
568 | char **vp; | |
569 | bool quoted; | |
570 | ||
571 | if (creat(shtemp, 0600) < 0) | |
572 | Perror(shtemp); | |
573 | close(0); | |
574 | if (open(shtemp, 2) < 0) { | |
575 | int oerrno = errno; | |
576 | ||
577 | unlink(shtemp); | |
578 | errno = oerrno; | |
579 | Perror(shtemp); | |
580 | } | |
581 | unlink(shtemp); /* 0 0 inode! */ | |
582 | Dv[0] = term; Dv[1] = NOSTR; gflag = 0; | |
583 | scan(Dv, trim); rscan(Dv, Dtestq); quoted = gflag; | |
584 | ocnt = BUFSIZ; obp = obuf; | |
585 | for (;;) { | |
586 | /* | |
587 | * Read up a line | |
588 | */ | |
589 | lbp = lbuf; lcnt = BUFSIZ - 4; | |
590 | for (;;) { | |
591 | c = readc(1); /* 1 -> Want EOF returns */ | |
592 | if (c < 0) { | |
593 | setname(term); | |
594 | bferr("<< terminator not found"); | |
595 | } | |
596 | if (c == '\n') | |
597 | break; | |
598 | if (c &= TRIM) { | |
599 | *lbp++ = c; | |
600 | if (--lcnt < 0) { | |
601 | setname("<<"); | |
602 | error("Line overflow"); | |
603 | } | |
604 | } | |
605 | } | |
606 | *lbp = 0; | |
607 | ||
608 | /* | |
609 | * Compare to terminator -- before expansion | |
610 | */ | |
611 | if (eq(lbuf, term)) { | |
612 | write(0, obuf, BUFSIZ - ocnt); | |
613 | lseek(0, 0l, 0); | |
614 | return; | |
615 | } | |
616 | ||
617 | /* | |
618 | * If term was quoted or -n just pass it on | |
619 | */ | |
620 | if (quoted || noexec) { | |
621 | *lbp++ = '\n'; *lbp = 0; | |
622 | for (lbp = lbuf; c = *lbp++;) { | |
623 | *obp++ = c; | |
624 | if (--ocnt == 0) { | |
625 | write(0, obuf, BUFSIZ); | |
626 | obp = obuf; ocnt = BUFSIZ; | |
627 | } | |
628 | } | |
629 | continue; | |
630 | } | |
631 | ||
632 | /* | |
633 | * Term wasn't quoted so variable and then command | |
634 | * expand the input line | |
635 | */ | |
636 | Dcp = lbuf; Dvp = Dv + 1; mbp = mbuf; mcnt = BUFSIZ - 4; | |
637 | for (;;) { | |
638 | c = DgetC(DODOL); | |
639 | if (c == DEOF) | |
640 | break; | |
641 | if ((c &= TRIM) == 0) | |
642 | continue; | |
643 | /* \ quotes \ $ ` here */ | |
644 | if (c =='\\') { | |
645 | c = DgetC(0); | |
646 | if (!any(c, "$\\`")) | |
647 | unDgetC(c | QUOTE), c = '\\'; | |
648 | else | |
649 | c |= QUOTE; | |
650 | } | |
651 | *mbp++ = c; | |
652 | if (--mcnt == 0) { | |
653 | setname("<<"); | |
654 | bferr("Line overflow"); | |
655 | } | |
656 | } | |
657 | *mbp++ = 0; | |
658 | ||
659 | /* | |
660 | * If any ` in line do command substitution | |
661 | */ | |
662 | mbp = mbuf; | |
663 | if (any('`', mbp)) { | |
664 | /* | |
665 | * 1 arg to dobackp causes substitution to be literal. | |
666 | * Words are broken only at newlines so that all blanks | |
667 | * and tabs are preserved. Blank lines (null words) | |
668 | * are not discarded. | |
669 | */ | |
670 | vp = dobackp(mbuf, 1); | |
671 | } else | |
672 | /* Setup trivial vector similar to return of dobackp */ | |
673 | Dv[0] = mbp, Dv[1] = NOSTR, vp = Dv; | |
674 | ||
675 | /* | |
676 | * Resurrect the words from the command substitution | |
677 | * each separated by a newline. Note that the last | |
678 | * newline of a command substitution will have been | |
679 | * discarded, but we put a newline after the last word | |
680 | * because this represents the newline after the last | |
681 | * input line! | |
682 | */ | |
683 | for (; *vp; vp++) { | |
684 | for (mbp = *vp; *mbp; mbp++) { | |
685 | *obp++ = *mbp & TRIM; | |
686 | if (--ocnt == 0) { | |
687 | write(0, obuf, BUFSIZ); | |
688 | obp = obuf; ocnt = BUFSIZ; | |
689 | } | |
690 | } | |
691 | *obp++ = '\n'; | |
692 | if (--ocnt == 0) { | |
693 | write(0, obuf, BUFSIZ); | |
694 | obp = obuf; ocnt = BUFSIZ; | |
695 | } | |
696 | } | |
697 | if (pargv) | |
698 | blkfree(pargv), pargv = 0; | |
699 | } | |
700 | } |