xref: /dragonfly/contrib/tcsh-6/sh.dol.c (revision 23e52a52)
1 /* $Header: /p/tcsh/cvsroot/tcsh/sh.dol.c,v 3.83 2011/01/25 20:10:46 christos Exp $ */
2 /*
3  * sh.dol.c: Variable substitutions
4  */
5 /*-
6  * Copyright (c) 1980, 1991 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 #include "sh.h"
34 
35 RCSID("$tcsh: sh.dol.c,v 3.83 2011/01/25 20:10:46 christos Exp $")
36 
37 /*
38  * C shell
39  */
40 
41 /*
42  * These routines perform variable substitution and quoting via ' and ".
43  * To this point these constructs have been preserved in the divided
44  * input words.  Here we expand variables and turn quoting via ' and " into
45  * QUOTE bits on characters (which prevent further interpretation).
46  * If the `:q' modifier was applied during history expansion, then
47  * some QUOTEing may have occurred already, so we dont "trim()" here.
48  */
49 
50 static eChar Dpeekc;		/* Peek for DgetC */
51 static eChar Dpeekrd;		/* Peek for Dreadc */
52 static Char *Dcp, *const *Dvp;	/* Input vector for Dreadc */
53 
54 #define	DEOF	CHAR_ERR
55 
56 #define	unDgetC(c)	Dpeekc = c
57 
58 #define QUOTES		(_QF|_QB|_ESC)	/* \ ' " ` */
59 
60 /*
61  * The following variables give the information about the current
62  * $ expansion, recording the current word position, the remaining
63  * words within this expansion, the count of remaining words, and the
64  * information about any : modifier which is being applied.
65  */
66 static Char *dolp;		/* Remaining chars from this word */
67 static Char **dolnxt;		/* Further words */
68 static int dolcnt;		/* Count of further words */
69 static struct Strbuf dolmod; /* = Strbuf_INIT; : modifier characters */
70 static int dolmcnt;		/* :gx -> INT_MAX, else 1 */
71 static int dol_flag_a;		/* :ax -> 1, else 0 */
72 
73 static	Char	 **Dfix2	(Char *const *);
74 static	int 	 Dpack		(struct Strbuf *);
75 static	int	 Dword		(struct blk_buf *);
76 static	void	 dolerror	(Char *);
77 static	eChar	 DgetC		(int);
78 static	void	 Dgetdol	(void);
79 static	void	 fixDolMod	(void);
80 static	void	 setDolp	(Char *);
81 static	void	 unDredc	(eChar);
82 static	eChar	 Dredc		(void);
83 static	void	 Dtestq		(Char);
84 
85 /*
86  * Fix up the $ expansions and quotations in the
87  * argument list to command t.
88  */
89 void
90 Dfix(struct command *t)
91 {
92     Char **pp;
93     Char *p;
94 
95     if (noexec)
96 	return;
97     /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */
98     for (pp = t->t_dcom; (p = *pp++) != NULL;) {
99 	for (; *p; p++) {
100 	    if (cmap(*p, _DOL | QUOTES)) {	/* $, \, ', ", ` */
101 		Char **expanded;
102 
103 		expanded = Dfix2(t->t_dcom);	/* found one */
104 		blkfree(t->t_dcom);
105 		t->t_dcom = expanded;
106 		return;
107 	    }
108 	}
109     }
110 }
111 
112 /*
113  * $ substitute one word, for i/o redirection
114  */
115 Char   *
116 Dfix1(Char *cp)
117 {
118     Char *Dv[2], **expanded;
119 
120     if (noexec)
121 	return (0);
122     Dv[0] = cp;
123     Dv[1] = NULL;
124     expanded = Dfix2(Dv);
125     if (expanded[0] == NULL || expanded[1] != NULL) {
126 	blkfree(expanded);
127 	setname(short2str(cp));
128 	stderror(ERR_NAME | ERR_AMBIG);
129     }
130     cp = Strsave(expanded[0]);
131     blkfree(expanded);
132     return (cp);
133 }
134 
135 /*
136  * Subroutine to do actual fixing after state initialization.
137  */
138 static Char **
139 Dfix2(Char *const *v)
140 {
141     struct blk_buf *bb = bb_alloc();
142     Char **vec;
143 
144     Dvp = v;
145     Dcp = STRNULL;		/* Setup input vector for Dreadc */
146     unDgetC(0);
147     unDredc(0);			/* Clear out any old peeks (at error) */
148     dolp = 0;
149     dolcnt = 0;			/* Clear out residual $ expands (...) */
150     cleanup_push(bb, bb_free);
151     while (Dword(bb))
152 	continue;
153     cleanup_ignore(bb);
154     cleanup_until(bb);
155     vec = bb_finish(bb);
156     xfree(bb);
157     return vec;
158 }
159 
160 /*
161  * Pack up more characters in this word
162  */
163 static int
164 Dpack(struct Strbuf *wbuf)
165 {
166     eChar c;
167 
168     for (;;) {
169 	c = DgetC(DODOL);
170 	if (c == '\\') {
171 	    c = DgetC(0);
172 	    if (c == DEOF) {
173 		unDredc(c);
174 		return 1;
175 	    }
176 	    if (c == '\n')
177 		c = ' ';
178 	    else
179 		c |= QUOTE;
180 	}
181 	if (c == DEOF) {
182 	    unDredc(c);
183 	    return 1;
184 	}
185 	if (cmap(c, _SP | _NL | _QF | _QB)) {	/* sp \t\n'"` */
186 	    unDgetC(c);
187 	    if (cmap(c, QUOTES))
188 		return 0;
189 	    return 1;
190 	}
191 	Strbuf_append1(wbuf, (Char) c);
192     }
193 }
194 
195 /*
196  * Get a word.  This routine is analogous to the routine
197  * word() in sh.lex.c for the main lexical input.  One difference
198  * here is that we don't get a newline to terminate our expansion.
199  * Rather, DgetC will return a DEOF when we hit the end-of-input.
200  */
201 static int
202 Dword(struct blk_buf *bb)
203 {
204     eChar c, c1;
205     struct Strbuf *wbuf = Strbuf_alloc();
206     int dolflg;
207     int    sofar = 0;
208     Char *str;
209 
210     cleanup_push(wbuf, Strbuf_free);
211     for (;;) {
212 	c = DgetC(DODOL);
213 	switch (c) {
214 
215 	case DEOF:
216 	    if (sofar == 0) {
217 		cleanup_until(wbuf);
218 		return (0);
219 	    }
220 	    /* finish this word and catch the code above the next time */
221 	    unDredc(c);
222 	    /*FALLTHROUGH*/
223 
224 	case '\n':
225 	    goto end;
226 
227 	case ' ':
228 	case '\t':
229 	    continue;
230 
231 	case '`':
232 	    /* We preserve ` quotations which are done yet later */
233 	    Strbuf_append1(wbuf, (Char) c);
234 	    /*FALLTHROUGH*/
235 	case '\'':
236 	case '"':
237 	    /*
238 	     * Note that DgetC never returns a QUOTES character from an
239 	     * expansion, so only true input quotes will get us here or out.
240 	     */
241 	    c1 = c;
242 	    dolflg = c1 == '"' ? DODOL : 0;
243 	    for (;;) {
244 		c = DgetC(dolflg);
245 		if (c == c1)
246 		    break;
247 		if (c == '\n' || c == DEOF) {
248 		    cleanup_until(bb);
249 		    stderror(ERR_UNMATCHED, (int)c1);
250 		}
251 		if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) {
252 		    if (wbuf->len != 0 && (wbuf->s[wbuf->len - 1] & TRIM) == '\\')
253 			wbuf->len--;
254 		}
255 		switch (c1) {
256 
257 		case '"':
258 		    /*
259 		     * Leave any `s alone for later. Other chars are all
260 		     * quoted, thus `...` can tell it was within "...".
261 		     */
262 		    Strbuf_append1(wbuf, c == '`' ? '`' : c | QUOTE);
263 		    break;
264 
265 		case '\'':
266 		    /* Prevent all further interpretation */
267 		    Strbuf_append1(wbuf, c | QUOTE);
268 		    break;
269 
270 		case '`':
271 		    /* Leave all text alone for later */
272 		    Strbuf_append1(wbuf, (Char) c);
273 		    break;
274 
275 		default:
276 		    break;
277 		}
278 	    }
279 	    if (c1 == '`')
280 		Strbuf_append1(wbuf, '`');
281 	    sofar = 1;
282 	    if (Dpack(wbuf) != 0)
283 		goto end;
284 	    continue;
285 
286 	case '\\':
287 	    c = DgetC(0);	/* No $ subst! */
288 	    if (c == '\n' || c == DEOF)
289 		continue;
290 	    c |= QUOTE;
291 	    break;
292 
293 	default:
294 	    break;
295 	}
296 	unDgetC(c);
297 	sofar = 1;
298 	if (Dpack(wbuf) != 0)
299 	    goto end;
300     }
301 
302  end:
303     cleanup_ignore(wbuf);
304     cleanup_until(wbuf);
305     str = Strbuf_finish(wbuf);
306     bb_append(bb, str);
307     xfree(wbuf);
308     return 1;
309 }
310 
311 
312 /*
313  * Get a character, performing $ substitution unless flag is 0.
314  * Any QUOTES character which is returned from a $ expansion is
315  * QUOTEd so that it will not be recognized above.
316  */
317 static eChar
318 DgetC(int flag)
319 {
320     eChar c;
321 
322 top:
323     if ((c = Dpeekc) != 0) {
324 	Dpeekc = 0;
325 	return (c);
326     }
327     if (lap < labuf.len) {
328 	c = labuf.s[lap++] & (QUOTE | TRIM);
329 quotspec:
330 	if (cmap(c, QUOTES))
331 	    return (c | QUOTE);
332 	return (c);
333     }
334     if (dolp) {
335 	if ((c = *dolp++ & (QUOTE | TRIM)) != 0)
336 	    goto quotspec;
337 	if (dolcnt > 0) {
338 	    setDolp(*dolnxt++);
339 	    --dolcnt;
340 	    return (' ');
341 	}
342 	dolp = 0;
343     }
344     if (dolcnt > 0) {
345 	setDolp(*dolnxt++);
346 	--dolcnt;
347 	goto top;
348     }
349     c = Dredc();
350     if (c == '$' && flag) {
351 	Dgetdol();
352 	goto top;
353     }
354     return (c);
355 }
356 
357 static Char *nulvec[] = { NULL };
358 static struct varent nulargv = {nulvec, STRargv, VAR_READWRITE,
359 				{ NULL, NULL, NULL }, 0 };
360 
361 static void
362 dolerror(Char *s)
363 {
364     setname(short2str(s));
365     stderror(ERR_NAME | ERR_RANGE);
366 }
367 
368 /*
369  * Handle the multitudinous $ expansion forms.
370  * Ugh.
371  */
372 static void
373 Dgetdol(void)
374 {
375     Char *np;
376     struct varent *vp = NULL;
377     struct Strbuf *name = Strbuf_alloc();
378     eChar   c, sc;
379     int     subscr = 0, lwb = 1, upb = 0;
380     int    dimen = 0, bitset = 0, length = 0;
381     static Char *dolbang = NULL;
382 
383     cleanup_push(name, Strbuf_free);
384     dolmod.len = dolmcnt = dol_flag_a = 0;
385     c = sc = DgetC(0);
386     if (c == DEOF) {
387       stderror(ERR_SYNTAX);
388       return;
389     }
390     if (c == '{')
391 	c = DgetC(0);		/* sc is { to take } later */
392     if ((c & TRIM) == '#')
393 	dimen++, c = DgetC(0);	/* $# takes dimension */
394     else if (c == '?')
395 	bitset++, c = DgetC(0);	/* $? tests existence */
396     else if (c == '%')
397 	length++, c = DgetC(0); /* $% returns length in chars */
398     switch (c) {
399 
400     case '!':
401 	if (dimen || bitset || length)
402 	    stderror(ERR_SYNTAX);
403 	if (backpid != 0) {
404 	    xfree(dolbang);
405 	    setDolp(dolbang = putn((tcsh_number_t)backpid));
406 	}
407 	cleanup_until(name);
408 	goto eatbrac;
409 
410     case '$':
411 	if (dimen || bitset || length)
412 	    stderror(ERR_SYNTAX);
413 	setDolp(doldol);
414 	cleanup_until(name);
415 	goto eatbrac;
416 
417     case '<'|QUOTE: {
418 	static struct Strbuf wbuf; /* = Strbuf_INIT; */
419 
420 	if (bitset)
421 	    stderror(ERR_NOTALLOWED, "$?<");
422 	if (dimen)
423 	    stderror(ERR_NOTALLOWED, "$#<");
424 	if (length)
425 	    stderror(ERR_NOTALLOWED, "$%<");
426 	wbuf.len = 0;
427 	{
428 	    char cbuf[MB_LEN_MAX];
429 	    size_t cbp = 0;
430 	    int old_pintr_disabled;
431 
432 	    for (;;) {
433 	        int len;
434 		ssize_t res;
435 		Char wc;
436 
437 		pintr_push_enable(&old_pintr_disabled);
438 		res = force_read(OLDSTD, cbuf + cbp, 1);
439 		cleanup_until(&old_pintr_disabled);
440 		if (res != 1)
441 		    break;
442 		cbp++;
443 		len = normal_mbtowc(&wc, cbuf, cbp);
444 		if (len == -1) {
445 		    reset_mbtowc();
446 		    if (cbp < MB_LEN_MAX)
447 		        continue; /* Maybe a partial character */
448 		    wc = (unsigned char)*cbuf | INVALID_BYTE;
449 		}
450 		if (len <= 0)
451 		    len = 1;
452 		if (cbp != (size_t)len)
453 		    memmove(cbuf, cbuf + len, cbp - len);
454 		cbp -= len;
455 		if (wc == '\n')
456 		    break;
457 		Strbuf_append1(&wbuf, wc);
458 	    }
459 	    while (cbp != 0) {
460 		int len;
461 		Char wc;
462 
463 		len = normal_mbtowc(&wc, cbuf, cbp);
464 		if (len == -1) {
465 		    reset_mbtowc();
466 		    wc = (unsigned char)*cbuf | INVALID_BYTE;
467 		}
468 		if (len <= 0)
469 		    len = 1;
470 		if (cbp != (size_t)len)
471 		    memmove(cbuf, cbuf + len, cbp - len);
472 		cbp -= len;
473 		if (wc == '\n')
474 		    break;
475 		Strbuf_append1(&wbuf, wc);
476 	    }
477 	    Strbuf_terminate(&wbuf);
478 	}
479 
480 	fixDolMod();
481 	setDolp(wbuf.s); /* Kept allocated until next $< expansion */
482 	cleanup_until(name);
483 	goto eatbrac;
484     }
485 
486     case '*':
487 	Strbuf_append(name, STRargv);
488 	Strbuf_terminate(name);
489 	vp = adrof(STRargv);
490 	subscr = -1;		/* Prevent eating [...] */
491 	break;
492 
493     case DEOF:
494     case '\n':
495 	np = dimen ? STRargv : (bitset ? STRstatus : NULL);
496 	if (np) {
497 	    bitset = 0;
498 	    Strbuf_append(name, np);
499 	    Strbuf_terminate(name);
500 	    vp = adrof(np);
501 	    subscr = -1;		/* Prevent eating [...] */
502 	    unDredc(c);
503 	    break;
504 	}
505 	else
506 	    stderror(ERR_SYNTAX);
507 	/*NOTREACHED*/
508 
509     default:
510 	if (Isdigit(c)) {
511 	    if (dimen)
512 		stderror(ERR_NOTALLOWED, "$#<num>");
513 	    subscr = 0;
514 	    do {
515 		subscr = subscr * 10 + c - '0';
516 		c = DgetC(0);
517 	    } while (c != DEOF && Isdigit(c));
518 	    unDredc(c);
519 	    if (subscr < 0)
520 		stderror(ERR_RANGE);
521 	    if (subscr == 0) {
522 		if (bitset) {
523 		    dolp = dolzero ? STR1 : STR0;
524 		    cleanup_until(name);
525 		    goto eatbrac;
526 		}
527 		if (ffile == 0)
528 		    stderror(ERR_DOLZERO);
529 		if (length) {
530 		    length = Strlen(ffile);
531 		    addla(putn((tcsh_number_t)length));
532 		}
533 		else {
534 		    fixDolMod();
535 		    setDolp(ffile);
536 		}
537 		cleanup_until(name);
538 		goto eatbrac;
539 	    }
540 #if 0
541 	    if (bitset)
542 		stderror(ERR_NOTALLOWED, "$?<num>");
543 	    if (length)
544 		stderror(ERR_NOTALLOWED, "$%<num>");
545 #endif
546 	    vp = adrof(STRargv);
547 	    if (vp == 0) {
548 		vp = &nulargv;
549 		cleanup_until(name);
550 		goto eatmod;
551 	    }
552 	    break;
553 	}
554 	if (c == DEOF || !alnum(c)) {
555 	    np = dimen ? STRargv : (bitset ? STRstatus : NULL);
556 	    if (np) {
557 		bitset = 0;
558 		Strbuf_append(name, np);
559 		Strbuf_terminate(name);
560 		vp = adrof(np);
561 		subscr = -1;		/* Prevent eating [...] */
562 		unDredc(c);
563 		break;
564 	    }
565 	    else
566 		stderror(ERR_VARALNUM);
567 	}
568 	for (;;) {
569 	    Strbuf_append1(name, (Char) c);
570 	    c = DgetC(0);
571 	    if (c == DEOF || !alnum(c))
572 		break;
573 	}
574 	Strbuf_terminate(name);
575 	unDredc(c);
576 	vp = adrof(name->s);
577     }
578     if (bitset) {
579 	dolp = (vp || getenv(short2str(name->s))) ? STR1 : STR0;
580 	cleanup_until(name);
581 	goto eatbrac;
582     }
583     if (vp == NULL || vp->vec == NULL) {
584 	np = str2short(getenv(short2str(name->s)));
585 	if (np) {
586 	    static Char *env_val; /* = NULL; */
587 
588 	    cleanup_until(name);
589 	    fixDolMod();
590 	    if (length) {
591 		    addla(putn((tcsh_number_t)Strlen(np)));
592 	    } else {
593 		    xfree(env_val);
594 		    env_val = Strsave(np);
595 		    setDolp(env_val);
596 	    }
597 	    goto eatbrac;
598 	}
599 	udvar(name->s);
600 	/* NOTREACHED */
601     }
602     cleanup_until(name);
603     c = DgetC(0);
604     upb = blklen(vp->vec);
605     if (dimen == 0 && subscr == 0 && c == '[') {
606 	name = Strbuf_alloc();
607 	cleanup_push(name, Strbuf_free);
608 	np = name->s;
609 	for (;;) {
610 	    c = DgetC(DODOL);	/* Allow $ expand within [ ] */
611 	    if (c == ']')
612 		break;
613 	    if (c == '\n' || c == DEOF)
614 		stderror(ERR_INCBR);
615 	    Strbuf_append1(name, (Char) c);
616 	}
617 	Strbuf_terminate(name);
618 	np = name->s;
619 	if (dolp || dolcnt)	/* $ exp must end before ] */
620 	    stderror(ERR_EXPORD);
621 	if (!*np)
622 	    stderror(ERR_SYNTAX);
623 	if (Isdigit(*np)) {
624 	    int     i;
625 
626 	    for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0')
627 		continue;
628 	    if (i < 0 || (i > upb && !any("-*", *np))) {
629 		cleanup_until(name);
630 		dolerror(vp->v_name);
631 		return;
632 	    }
633 	    lwb = i;
634 	    if (!*np)
635 		upb = lwb, np = STRstar;
636 	}
637 	if (*np == '*')
638 	    np++;
639 	else if (*np != '-')
640 	    stderror(ERR_MISSING, '-');
641 	else {
642 	    int i = upb;
643 
644 	    np++;
645 	    if (Isdigit(*np)) {
646 		i = 0;
647 		while (Isdigit(*np))
648 		    i = i * 10 + *np++ - '0';
649 		if (i < 0 || i > upb) {
650 		    cleanup_until(name);
651 		    dolerror(vp->v_name);
652 		    return;
653 		}
654 	    }
655 	    if (i < lwb)
656 		upb = lwb - 1;
657 	    else
658 		upb = i;
659 	}
660 	if (lwb == 0) {
661 	    if (upb != 0) {
662 		cleanup_until(name);
663 		dolerror(vp->v_name);
664 		return;
665 	    }
666 	    upb = -1;
667 	}
668 	if (*np)
669 	    stderror(ERR_SYNTAX);
670 	cleanup_until(name);
671     }
672     else {
673 	if (subscr > 0) {
674 	    if (subscr > upb)
675 		lwb = 1, upb = 0;
676 	    else
677 		lwb = upb = subscr;
678 	}
679 	unDredc(c);
680     }
681     if (dimen) {
682 	/* this is a kludge. It prevents Dgetdol() from */
683 	/* pushing erroneous ${#<error> values into the labuf. */
684 	if (sc == '{') {
685 	    c = Dredc();
686 	    if (c != '}')
687 		stderror(ERR_MISSING, '}');
688 	    unDredc(c);
689 	}
690 	addla(putn((tcsh_number_t)(upb - lwb + 1)));
691     }
692     else if (length) {
693 	int i;
694 
695 	for (i = lwb - 1, length = 0; i < upb; i++)
696 	    length += Strlen(vp->vec[i]);
697 #ifdef notdef
698 	/* We don't want that, since we can always compute it by adding $#xxx */
699 	length += i - 1;	/* Add the number of spaces in */
700 #endif
701 	addla(putn((tcsh_number_t)length));
702     }
703     else {
704 eatmod:
705 	fixDolMod();
706 	dolnxt = &vp->vec[lwb - 1];
707 	dolcnt = upb - lwb + 1;
708     }
709 eatbrac:
710     if (sc == '{') {
711 	c = Dredc();
712 	if (c != '}')
713 	    stderror(ERR_MISSING, '}');
714     }
715 }
716 
717 static void
718 fixDolMod(void)
719 {
720     eChar c;
721 
722     c = DgetC(0);
723     if (c == ':') {
724 	do {
725 	    c = DgetC(0), dolmcnt = 1, dol_flag_a = 0;
726 	    if (c == 'g' || c == 'a') {
727 		if (c == 'g')
728 		    dolmcnt = INT_MAX;
729 		else
730 		    dol_flag_a = 1;
731 		c = DgetC(0);
732 	    }
733 	    if ((c == 'g' && dolmcnt != INT_MAX) ||
734 		(c == 'a' && dol_flag_a == 0)) {
735 		if (c == 'g')
736 		    dolmcnt = INT_MAX;
737 		else
738 		    dol_flag_a = 1;
739 		c = DgetC(0);
740 	    }
741 
742 	    if (c == 's') {	/* [eichin:19910926.0755EST] */
743 		int delimcnt = 2;
744 		eChar delim = DgetC(0);
745 		Strbuf_append1(&dolmod, (Char) c);
746 		Strbuf_append1(&dolmod, (Char) delim);
747 
748 		if (delim == DEOF || !delim || letter(delim)
749 		    || Isdigit(delim) || any(" \t\n", delim)) {
750 		    seterror(ERR_BADSUBST);
751 		    break;
752 		}
753 		while ((c = DgetC(0)) != DEOF) {
754 		    Strbuf_append1(&dolmod, (Char) c);
755 		    if(c == delim) delimcnt--;
756 		    if(!delimcnt) break;
757 		}
758 		if(delimcnt) {
759 		    seterror(ERR_BADSUBST);
760 		    break;
761 		}
762 		continue;
763 	    }
764 	    if (!any("luhtrqxes", c))
765 		stderror(ERR_BADMOD, (int)c);
766 	    Strbuf_append1(&dolmod, (Char) c);
767 	    if (c == 'q')
768 		dolmcnt = INT_MAX;
769 	}
770 	while ((c = DgetC(0)) == ':');
771 	unDredc(c);
772     }
773     else
774 	unDredc(c);
775 }
776 
777 static void
778 setDolp(Char *cp)
779 {
780     Char *dp;
781     size_t i;
782 
783     if (dolmod.len == 0 || dolmcnt == 0) {
784 	dolp = cp;
785 	return;
786     }
787     cp = Strsave(cp);
788     for (i = 0; i < dolmod.len; i++) {
789 	int didmod = 0;
790 
791 	/* handle s// [eichin:19910926.0510EST] */
792 	if(dolmod.s[i] == 's') {
793 	    Char delim;
794 	    Char *lhsub, *rhsub, *np;
795 	    size_t lhlen = 0, rhlen = 0;
796 
797 	    delim = dolmod.s[++i];
798 	    if (!delim || letter(delim)
799 		|| Isdigit(delim) || any(" \t\n", delim)) {
800 		seterror(ERR_BADSUBST);
801 		break;
802 	    }
803 	    lhsub = &dolmod.s[++i];
804 	    while(dolmod.s[i] != delim && dolmod.s[++i]) {
805 		lhlen++;
806 	    }
807 	    dolmod.s[i] = 0;
808 	    rhsub = &dolmod.s[++i];
809 	    while(dolmod.s[i] != delim && dolmod.s[++i]) {
810 		rhlen++;
811 	    }
812 	    dolmod.s[i] = 0;
813 
814 	    strip(lhsub);
815 	    strip(rhsub);
816 	    strip(cp);
817 	    dp = cp;
818 	    do {
819 		dp = Strstr(dp, lhsub);
820 		if (dp) {
821 		    ptrdiff_t diff = dp - cp;
822 		    size_t len = (Strlen(cp) + 1 - lhlen + rhlen);
823 		    np = xmalloc(len * sizeof(Char));
824 		    (void) Strncpy(np, cp, diff);
825 		    (void) Strcpy(np + diff, rhsub);
826 		    (void) Strcpy(np + diff + rhlen, dp + lhlen);
827 
828 		    dp = np + diff + 1;
829 		    xfree(cp);
830 		    cp = np;
831 		    cp[--len] = '\0';
832 		    didmod = 1;
833 		    if (diff >= (ssize_t)len)
834 			break;
835 		} else {
836 		    /* should this do a seterror? */
837 		    break;
838 		}
839 	    }
840 	    while (dol_flag_a != 0);
841 	    /*
842 	     * restore dolmod for additional words
843 	     */
844 	    dolmod.s[i] = rhsub[-1] = (Char) delim;
845         } else {
846 
847 	    do {
848 		if ((dp = domod(cp, dolmod.s[i])) != NULL) {
849 		    didmod = 1;
850 		    if (Strcmp(cp, dp) == 0) {
851 			xfree(cp);
852 			cp = dp;
853 			break;
854 		    }
855 		    else {
856 			xfree(cp);
857 			cp = dp;
858 		    }
859 		}
860 		else
861 		    break;
862 	    }
863 	    while (dol_flag_a != 0);
864 	}
865 	if (didmod && dolmcnt != INT_MAX)
866 	    dolmcnt--;
867 #ifdef notdef
868 	else
869 	    break;
870 #endif
871     }
872 
873     addla(cp);
874 
875     dolp = STRNULL;
876     if (seterr)
877 	stderror(ERR_OLD);
878 }
879 
880 static void
881 unDredc(eChar c)
882 {
883 
884     Dpeekrd = c;
885 }
886 
887 static eChar
888 Dredc(void)
889 {
890     eChar c;
891 
892     if ((c = Dpeekrd) != 0) {
893 	Dpeekrd = 0;
894 	return (c);
895     }
896     if (Dcp && (c = *Dcp++))
897 	return (c & (QUOTE | TRIM));
898     if (*Dvp == 0) {
899 	Dcp = 0;
900 	return (DEOF);
901     }
902     Dcp = *Dvp++;
903     return (' ');
904 }
905 
906 static int gflag;
907 
908 static void
909 Dtestq(Char c)
910 {
911 
912     if (cmap(c, QUOTES))
913 	gflag = 1;
914 }
915 
916 static void
917 inheredoc_cleanup(void *dummy)
918 {
919     USE(dummy);
920     inheredoc = 0;
921 }
922 
923 /*
924  * Form a shell temporary file (in unit 0) from the words
925  * of the shell input up to EOF or a line the same as "term".
926  * Unit 0 should have been closed before this call.
927  */
928 void
929 heredoc(Char *term)
930 {
931     eChar  c;
932     Char   *Dv[2];
933     struct Strbuf lbuf = Strbuf_INIT, mbuf = Strbuf_INIT;
934     Char    obuf[BUFSIZE + 1];
935 #define OBUF_END (obuf + sizeof(obuf) / sizeof (*obuf) - 1)
936     Char *lbp, *obp, *mbp;
937     Char  **vp;
938     int    quoted;
939 #ifdef HAVE_MKSTEMP
940     char   *tmp = short2str(shtemp);
941     char   *dot = strrchr(tmp, '.');
942 
943     if (!dot)
944 	stderror(ERR_NAME | ERR_NOMATCH);
945     strcpy(dot, TMP_TEMPLATE);
946 
947     xclose(0);
948     if (mkstemp(tmp) == -1)
949 	stderror(ERR_SYSTEM, tmp, strerror(errno));
950 #else /* !HAVE_MKSTEMP */
951     char   *tmp;
952 # ifndef WINNT_NATIVE
953     struct timeval tv;
954 
955 again:
956 # endif /* WINNT_NATIVE */
957     tmp = short2str(shtemp);
958 # if O_CREAT == 0
959     if (xcreat(tmp, 0600) < 0)
960 	stderror(ERR_SYSTEM, tmp, strerror(errno));
961 # endif
962     xclose(0);
963     if (xopen(tmp, O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY|O_LARGEFILE, 0600) ==
964 	-1) {
965 	int oerrno = errno;
966 # ifndef WINNT_NATIVE
967 	if (errno == EEXIST) {
968 	    if (unlink(tmp) == -1) {
969 		(void) gettimeofday(&tv, NULL);
970 		xfree(shtemp);
971 		mbp = putn((((tcsh_number_t)tv.tv_sec) ^
972 		    ((tcsh_number_t)tv.tv_usec) ^
973 		    ((tcsh_number_t)getpid())) & 0x00ffffff);
974 		shtemp = Strspl(STRtmpsh, mbp);
975 		xfree(mbp);
976 	    }
977 	    goto again;
978 	}
979 # endif /* WINNT_NATIVE */
980 	(void) unlink(tmp);
981 	errno = oerrno;
982  	stderror(ERR_SYSTEM, tmp, strerror(errno));
983     }
984 #endif /* HAVE_MKSTEMP */
985     (void) unlink(tmp);		/* 0 0 inode! */
986     Dv[0] = term;
987     Dv[1] = NULL;
988     gflag = 0;
989     trim(Dv);
990     rscan(Dv, Dtestq);
991     quoted = gflag;
992     obp = obuf;
993     obuf[BUFSIZE] = 0;
994     inheredoc = 1;
995     cleanup_push(&inheredoc, inheredoc_cleanup);
996 #ifdef WINNT_NATIVE
997     __dup_stdin = 1;
998 #endif /* WINNT_NATIVE */
999     cleanup_push(&lbuf, Strbuf_cleanup);
1000     cleanup_push(&mbuf, Strbuf_cleanup);
1001     for (;;) {
1002 	Char **words;
1003 
1004 	/*
1005 	 * Read up a line
1006 	 */
1007 	lbuf.len = 0;
1008 	for (;;) {
1009 	    c = readc(1);	/* 1 -> Want EOF returns */
1010 	    if (c == CHAR_ERR || c == '\n')
1011 		break;
1012 	    if ((c &= TRIM) != 0)
1013 		Strbuf_append1(&lbuf, (Char) c);
1014 	}
1015 	Strbuf_terminate(&lbuf);
1016 
1017 	/* Catch EOF in the middle of a line. */
1018 	if (c == CHAR_ERR && lbuf.len != 0)
1019 	    c = '\n';
1020 
1021 	/*
1022 	 * Check for EOF or compare to terminator -- before expansion
1023 	 */
1024 	if (c == CHAR_ERR || eq(lbuf.s, term))
1025 	    break;
1026 
1027 	/*
1028 	 * If term was quoted or -n just pass it on
1029 	 */
1030 	if (quoted || noexec) {
1031 	    Strbuf_append1(&lbuf, '\n');
1032 	    Strbuf_terminate(&lbuf);
1033 	    for (lbp = lbuf.s; (c = *lbp++) != 0;) {
1034 		*obp++ = (Char) c;
1035 		if (obp == OBUF_END) {
1036 		    tmp = short2str(obuf);
1037 		    (void) xwrite(0, tmp, strlen (tmp));
1038 		    obp = obuf;
1039 		}
1040 	    }
1041 	    continue;
1042 	}
1043 
1044 	/*
1045 	 * Term wasn't quoted so variable and then command expand the input
1046 	 * line
1047 	 */
1048 	Dcp = lbuf.s;
1049 	Dvp = Dv + 1;
1050 	mbuf.len = 0;
1051 	for (;;) {
1052 	    c = DgetC(DODOL);
1053 	    if (c == DEOF)
1054 		break;
1055 	    if ((c &= TRIM) == 0)
1056 		continue;
1057 	    /* \ quotes \ $ ` here */
1058 	    if (c == '\\') {
1059 		c = DgetC(0);
1060 		if (!any("$\\`", c))
1061 		    unDgetC(c | QUOTE), c = '\\';
1062 		else
1063 		    c |= QUOTE;
1064 	    }
1065 	    Strbuf_append1(&mbuf, (Char) c);
1066 	}
1067 	Strbuf_terminate(&mbuf);
1068 
1069 	/*
1070 	 * If any ` in line do command substitution
1071 	 */
1072 	mbp = mbuf.s;
1073 	if (Strchr(mbp, '`') != NULL) {
1074 	    /*
1075 	     * 1 arg to dobackp causes substitution to be literal. Words are
1076 	     * broken only at newlines so that all blanks and tabs are
1077 	     * preserved.  Blank lines (null words) are not discarded.
1078 	     */
1079 	    words = dobackp(mbp, 1);
1080 	}
1081 	else
1082 	    /* Setup trivial vector similar to return of dobackp */
1083 	    Dv[0] = mbp, Dv[1] = NULL, words = Dv;
1084 
1085 	/*
1086 	 * Resurrect the words from the command substitution each separated by
1087 	 * a newline.  Note that the last newline of a command substitution
1088 	 * will have been discarded, but we put a newline after the last word
1089 	 * because this represents the newline after the last input line!
1090 	 */
1091 	for (vp= words; *vp; vp++) {
1092 	    for (mbp = *vp; *mbp; mbp++) {
1093 		*obp++ = *mbp & TRIM;
1094 		if (obp == OBUF_END) {
1095 		    tmp = short2str(obuf);
1096 		    (void) xwrite(0, tmp, strlen (tmp));
1097 		    obp = obuf;
1098 		}
1099 	    }
1100 	    *obp++ = '\n';
1101 	    if (obp == OBUF_END) {
1102 	        tmp = short2str(obuf);
1103 		(void) xwrite(0, tmp, strlen (tmp));
1104 		obp = obuf;
1105 	    }
1106 	}
1107 	if (words != Dv)
1108 	    blkfree(words);
1109     }
1110     *obp = 0;
1111     tmp = short2str(obuf);
1112     (void) xwrite(0, tmp, strlen (tmp));
1113     (void) lseek(0, (off_t) 0, L_SET);
1114     cleanup_until(&inheredoc);
1115 }
1116