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