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