include fixes
[unix-history] / usr / src / bin / csh / dol.c
CommitLineData
fe7a2c87 1static char *sccsid = "@(#)dol.c 4.4 %G%";
efdf1379
BJ
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
18int Dpeekc, Dpeekrd; /* Peeks for DgetC and Dreadc */
19char *Dcp, **Dvp; /* Input vector for Dreadc */
20
21#define DEOF -1
22
23#define unDgetC(c) Dpeekc = c
24
25char *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 */
33char *dolp; /* Remaining chars from this word */
34char **dolnxt; /* Further words */
35int dolcnt; /* Count of further words */
36char dolmod; /* : modifier character */
37int dolmcnt; /* :gx -> 10000, else 1 */
38
39int Dtest(); /* Test for \ " ` or ' */
40
41/*
42 * Fix up the $ expansions and quotations in the
43 * argument list to command t.
44 */
45Dfix(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 */
61char *
62Dfix1(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 */
83Dfix2(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 */
103Dword()
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
112loop:
113 c = DgetC(DODOL);
114 switch (c) {
115
116 case DEOF:
117deof:
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);
188pack:
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)
212toochars:
213 error("Word too long");
214 *wp++ = c;
215 }
216ret:
217 Gcat("", wbuf);
218 return (1);
219}
220
221/*
222 * Get a character, performing $ substitution unless flag is 0.
fe7a2c87
SL
223 * Any QUOTES character which is returned from a $ expansion is
224 * QUOTEd so that it will not be recognized above.
efdf1379
BJ
225 */
226DgetC(flag)
227 register int flag;
228{
229 register int c;
230
231top:
232 if (c = Dpeekc) {
fe7a2c87 233 Dpeekc = 0;
efdf1379
BJ
234 return (c);
235 }
236 if (lap) {
237 c = *lap++ & (QUOTE|TRIM);
238 if (c == 0) {
239 lap = 0;
240 goto top;
241 }
242quotspec:
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
270char *nulvec[] = { 0 };
271struct varent nulargv = { nulvec, "argv", 0 };
272
273/*
274 * Handle the multitudinous $ expansion forms.
275 * Ugh.
276 */
277Dgetdol()
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;
3d9f6a35 284 bool dimen = 0, bitset = 0;
efdf1379
BJ
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 == '?')
3d9f6a35 294 bitset++, c = DgetC(0); /* $? tests existence */
efdf1379
BJ
295 switch (c) {
296
297 case '$':
3d9f6a35 298 if (dimen || bitset)
efdf1379
BJ
299 goto syntax; /* No $?$, $#$ */
300 setDolp(doldol);
301 goto eatbrac;
302
303 case '<'|QUOTE:
3d9f6a35 304 if (dimen || bitset)
efdf1379
BJ
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) {
3d9f6a35 350 if (bitset) {
efdf1379
BJ
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 }
3d9f6a35 359 if (bitset)
efdf1379
BJ
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])
376syntax:
377 error("Variable syntax");
378 }
379 *np++ = 0;
380 unDredc(c);
381 vp = adrof(name);
382 }
3d9f6a35 383 if (bitset) {
efdf1379
BJ
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);
b14dd860 391 goto eatbrac;
efdf1379
BJ
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, "-*")) {
421oob:
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 {
470eatmod:
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 }
486eatbrac:
487 if (sc == '{') {
488 c = Dredc();
489 if (c != '}')
490 goto syntax;
491 }
492}
493
494setDolp(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
513unDredc(c)
514 int c;
515{
516
517 Dpeekrd = c;
518}
519
520Dredc()
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
538Dtest(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
547Dtestq(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 */
560heredoc(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}