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