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