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