xref: /freebsd/contrib/tcsh/sh.dol.c (revision b9f654b163bce26de79705e77b872427c9f2afa1)
1 /* $Header: /p/tcsh/cvsroot/tcsh/sh.dol.c,v 3.87 2014/08/13 23:39:34 amold 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.87 2014/08/13 23:39:34 amold 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 		    xfree(cp);
829 		    dp = cp = np;
830 		    cp[--len] = '\0';
831 		    didmod = 1;
832 		    if (diff >= (ssize_t)len)
833 			break;
834 		} else {
835 		    /* should this do a seterror? */
836 		    break;
837 		}
838 	    }
839 	    while (dol_flag_a != 0);
840 	    /*
841 	     * restore dolmod for additional words
842 	     */
843 	    dolmod.s[i] = rhsub[-1] = (Char) delim;
844         } else {
845 
846 	    do {
847 		if ((dp = domod(cp, dolmod.s[i])) != NULL) {
848 		    didmod = 1;
849 		    if (Strcmp(cp, dp) == 0) {
850 			xfree(cp);
851 			cp = dp;
852 			break;
853 		    }
854 		    else {
855 			xfree(cp);
856 			cp = dp;
857 		    }
858 		}
859 		else
860 		    break;
861 	    }
862 	    while (dol_flag_a != 0);
863 	}
864 	if (didmod && dolmcnt != INT_MAX)
865 	    dolmcnt--;
866 #ifdef notdef
867 	else
868 	    break;
869 #endif
870     }
871 
872     addla(cp);
873 
874     dolp = STRNULL;
875     if (seterr)
876 	stderror(ERR_OLD);
877 }
878 
879 static void
880 unDredc(eChar c)
881 {
882 
883     Dpeekrd = c;
884 }
885 
886 static eChar
887 Dredc(void)
888 {
889     eChar c;
890 
891     if ((c = Dpeekrd) != 0) {
892 	Dpeekrd = 0;
893 	return (c);
894     }
895     if (Dcp && (c = *Dcp++))
896 	return (c & (QUOTE | TRIM));
897     if (*Dvp == 0) {
898 	Dcp = 0;
899 	return (DEOF);
900     }
901     Dcp = *Dvp++;
902     return (' ');
903 }
904 
905 static int gflag;
906 
907 static void
908 Dtestq(Char c)
909 {
910 
911     if (cmap(c, QUOTES))
912 	gflag = 1;
913 }
914 
915 static void
916 inheredoc_cleanup(void *dummy)
917 {
918     USE(dummy);
919     inheredoc = 0;
920 }
921 
922 Char *
923 randsuf(void) {
924 #ifndef WINNT_NATIVE
925 	struct timeval tv;
926 	(void) gettimeofday(&tv, NULL);
927 	return putn((((tcsh_number_t)tv.tv_sec) ^
928 	    ((tcsh_number_t)tv.tv_usec) ^
929 	    ((tcsh_number_t)getpid())) & 0x00ffffff);
930 #else
931     return putn(getpid());
932 #endif
933 }
934 
935 /*
936  * Form a shell temporary file (in unit 0) from the words
937  * of the shell input up to EOF or a line the same as "term".
938  * Unit 0 should have been closed before this call.
939  */
940 void
941 heredoc(Char *term)
942 {
943     eChar  c;
944     Char   *Dv[2];
945     struct Strbuf lbuf = Strbuf_INIT, mbuf = Strbuf_INIT;
946     Char    obuf[BUFSIZE + 1];
947 #define OBUF_END (obuf + sizeof(obuf) / sizeof (*obuf) - 1)
948     Char *lbp, *obp, *mbp;
949     Char  **vp;
950     int    quoted;
951 #ifdef HAVE_MKSTEMP
952     char   *tmp = short2str(shtemp);
953     char   *dot = strrchr(tmp, '.');
954 
955     if (!dot)
956 	stderror(ERR_NAME | ERR_NOMATCH);
957     strcpy(dot, TMP_TEMPLATE);
958 
959     xclose(0);
960     if (mkstemp(tmp) == -1)
961 	stderror(ERR_SYSTEM, tmp, strerror(errno));
962 #else /* !HAVE_MKSTEMP */
963     char   *tmp;
964 # ifndef WINNT_NATIVE
965 
966 again:
967 # endif /* WINNT_NATIVE */
968     tmp = short2str(shtemp);
969 # if O_CREAT == 0
970     if (xcreat(tmp, 0600) < 0)
971 	stderror(ERR_SYSTEM, tmp, strerror(errno));
972 # endif
973     xclose(0);
974     if (xopen(tmp, O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY|O_LARGEFILE, 0600) ==
975 	-1) {
976 	int oerrno = errno;
977 # ifndef WINNT_NATIVE
978 	if (errno == EEXIST) {
979 	    if (unlink(tmp) == -1) {
980 		xfree(shtemp);
981 		mbp = randsuf();
982 		shtemp = Strspl(STRtmpsh, mbp);
983 		xfree(mbp);
984 	    }
985 	    goto again;
986 	}
987 # endif /* WINNT_NATIVE */
988 	(void) unlink(tmp);
989 	errno = oerrno;
990  	stderror(ERR_SYSTEM, tmp, strerror(errno));
991     }
992 #endif /* HAVE_MKSTEMP */
993     (void) unlink(tmp);		/* 0 0 inode! */
994     Dv[0] = term;
995     Dv[1] = NULL;
996     gflag = 0;
997     trim(Dv);
998     rscan(Dv, Dtestq);
999     quoted = gflag;
1000     obp = obuf;
1001     obuf[BUFSIZE] = 0;
1002     inheredoc = 1;
1003     cleanup_push(&inheredoc, inheredoc_cleanup);
1004 #ifdef WINNT_NATIVE
1005     __dup_stdin = 1;
1006 #endif /* WINNT_NATIVE */
1007     cleanup_push(&lbuf, Strbuf_cleanup);
1008     cleanup_push(&mbuf, Strbuf_cleanup);
1009     for (;;) {
1010 	Char **words;
1011 
1012 	/*
1013 	 * Read up a line
1014 	 */
1015 	lbuf.len = 0;
1016 	for (;;) {
1017 	    c = readc(1);	/* 1 -> Want EOF returns */
1018 	    if (c == CHAR_ERR || c == '\n')
1019 		break;
1020 	    if ((c &= TRIM) != 0)
1021 		Strbuf_append1(&lbuf, (Char) c);
1022 	}
1023 	Strbuf_terminate(&lbuf);
1024 
1025 	/* Catch EOF in the middle of a line. */
1026 	if (c == CHAR_ERR && lbuf.len != 0)
1027 	    c = '\n';
1028 
1029 	/*
1030 	 * Check for EOF or compare to terminator -- before expansion
1031 	 */
1032 	if (c == CHAR_ERR || eq(lbuf.s, term))
1033 	    break;
1034 
1035 	/*
1036 	 * If term was quoted or -n just pass it on
1037 	 */
1038 	if (quoted || noexec) {
1039 	    Strbuf_append1(&lbuf, '\n');
1040 	    Strbuf_terminate(&lbuf);
1041 	    for (lbp = lbuf.s; (c = *lbp++) != 0;) {
1042 		*obp++ = (Char) c;
1043 		if (obp == OBUF_END) {
1044 		    tmp = short2str(obuf);
1045 		    (void) xwrite(0, tmp, strlen (tmp));
1046 		    obp = obuf;
1047 		}
1048 	    }
1049 	    continue;
1050 	}
1051 
1052 	/*
1053 	 * Term wasn't quoted so variable and then command expand the input
1054 	 * line
1055 	 */
1056 	Dcp = lbuf.s;
1057 	Dvp = Dv + 1;
1058 	mbuf.len = 0;
1059 	for (;;) {
1060 	    c = DgetC(DODOL);
1061 	    if (c == DEOF)
1062 		break;
1063 	    if ((c &= TRIM) == 0)
1064 		continue;
1065 	    /* \ quotes \ $ ` here */
1066 	    if (c == '\\') {
1067 		c = DgetC(0);
1068 		if (!any("$\\`", c))
1069 		    unDgetC(c | QUOTE), c = '\\';
1070 		else
1071 		    c |= QUOTE;
1072 	    }
1073 	    Strbuf_append1(&mbuf, (Char) c);
1074 	}
1075 	Strbuf_terminate(&mbuf);
1076 
1077 	/*
1078 	 * If any ` in line do command substitution
1079 	 */
1080 	mbp = mbuf.s;
1081 	if (Strchr(mbp, '`') != NULL) {
1082 	    /*
1083 	     * 1 arg to dobackp causes substitution to be literal. Words are
1084 	     * broken only at newlines so that all blanks and tabs are
1085 	     * preserved.  Blank lines (null words) are not discarded.
1086 	     */
1087 	    words = dobackp(mbp, 1);
1088 	}
1089 	else
1090 	    /* Setup trivial vector similar to return of dobackp */
1091 	    Dv[0] = mbp, Dv[1] = NULL, words = Dv;
1092 
1093 	/*
1094 	 * Resurrect the words from the command substitution each separated by
1095 	 * a newline.  Note that the last newline of a command substitution
1096 	 * will have been discarded, but we put a newline after the last word
1097 	 * because this represents the newline after the last input line!
1098 	 */
1099 	for (vp= words; *vp; vp++) {
1100 	    for (mbp = *vp; *mbp; mbp++) {
1101 		*obp++ = *mbp & TRIM;
1102 		if (obp == OBUF_END) {
1103 		    tmp = short2str(obuf);
1104 		    (void) xwrite(0, tmp, strlen (tmp));
1105 		    obp = obuf;
1106 		}
1107 	    }
1108 	    *obp++ = '\n';
1109 	    if (obp == OBUF_END) {
1110 	        tmp = short2str(obuf);
1111 		(void) xwrite(0, tmp, strlen (tmp));
1112 		obp = obuf;
1113 	    }
1114 	}
1115 	if (words != Dv)
1116 	    blkfree(words);
1117     }
1118     *obp = 0;
1119     tmp = short2str(obuf);
1120     (void) xwrite(0, tmp, strlen (tmp));
1121     (void) lseek(0, (off_t) 0, L_SET);
1122     cleanup_until(&inheredoc);
1123 }
1124