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