sh.dol.c revision 100616
1/* $Header: /src/pub/tcsh/sh.dol.c,v 3.50 2002/06/25 19:02:11 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("$Id: sh.dol.c,v 3.50 2002/06/25 19:02:11 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 int Dpeekc, Dpeekrd;	/* Peeks for DgetC and Dreadc */
51static Char *Dcp, **Dvp;	/* Input vector for Dreadc */
52
53#define	DEOF	-1
54
55#define	unDgetC(c)	Dpeekc = c
56
57#define QUOTES		(_QF|_QB|_ESC)	/* \ ' " ` */
58
59/*
60 * The following variables give the information about the current
61 * $ expansion, recording the current word position, the remaining
62 * words within this expansion, the count of remaining words, and the
63 * information about any : modifier which is being applied.
64 */
65#define MAXWLEN (BUFSIZE - 4)
66#ifndef COMPAT
67#define MAXMOD MAXWLEN		/* This cannot overflow	*/
68#endif /* COMPAT */
69static Char *dolp;		/* Remaining chars from this word */
70static Char **dolnxt;		/* Further words */
71static int dolcnt;		/* Count of further words */
72#ifdef COMPAT
73static Char dolmod;		/* : modifier character */
74#else
75static Char dolmod[MAXMOD];	/* : modifier character */
76static int dolnmod;		/* Number of modifiers */
77#endif /* COMPAT */
78static int dolmcnt;		/* :gx -> 10000, else 1 */
79static int dolwcnt;		/* :ax -> 10000, else 1 */
80
81static	void	 Dfix2		__P((Char **));
82static	Char 	*Dpack		__P((Char *, Char *));
83static	int	 Dword		__P((void));
84static	void	 dolerror	__P((Char *));
85static	int	 DgetC		__P((int));
86static	void	 Dgetdol	__P((void));
87static	void	 fixDolMod	__P((void));
88static	void	 setDolp	__P((Char *));
89static	void	 unDredc	__P((int));
90static	int	 Dredc		__P((void));
91static	void	 Dtestq		__P((int));
92
93/*
94 * Fix up the $ expansions and quotations in the
95 * argument list to command t.
96 */
97void
98Dfix(t)
99    register struct command *t;
100{
101    register Char **pp;
102    register Char *p;
103
104    if (noexec)
105	return;
106    /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */
107    for (pp = t->t_dcom; (p = *pp++) != NULL;) {
108	for (; *p; p++) {
109#ifdef DSPMBYTE
110	    if (Ismbyte1(*p) && *(p + 1))
111		p ++;
112	    else
113#endif /* DSPMBYTE */
114	    if (cmap(*p, _DOL | QUOTES)) {	/* $, \, ', ", ` */
115		Dfix2(t->t_dcom);	/* found one */
116		blkfree(t->t_dcom);
117		t->t_dcom = gargv;
118		gargv = 0;
119		return;
120	    }
121	}
122    }
123}
124
125/*
126 * $ substitute one word, for i/o redirection
127 */
128Char   *
129Dfix1(cp)
130    register Char *cp;
131{
132    Char   *Dv[2];
133
134    if (noexec)
135	return (0);
136    Dv[0] = cp;
137    Dv[1] = NULL;
138    Dfix2(Dv);
139    if (gargc != 1) {
140	setname(short2str(cp));
141	stderror(ERR_NAME | ERR_AMBIG);
142    }
143    cp = Strsave(gargv[0]);
144    blkfree(gargv), gargv = 0;
145    return (cp);
146}
147
148/*
149 * Subroutine to do actual fixing after state initialization.
150 */
151static void
152Dfix2(v)
153    Char  **v;
154{
155    ginit();			/* Initialize glob's area pointers */
156    Dvp = v;
157    Dcp = STRNULL;		/* Setup input vector for Dreadc */
158    unDgetC(0);
159    unDredc(0);			/* Clear out any old peeks (at error) */
160    dolp = 0;
161    dolcnt = 0;			/* Clear out residual $ expands (...) */
162    while (Dword())
163	continue;
164}
165
166/*
167 * Pack up more characters in this word
168 */
169static Char *
170Dpack(wbuf, wp)
171    Char   *wbuf, *wp;
172{
173    register int c;
174    register int i = MAXWLEN - (int) (wp - wbuf);
175#if defined(DSPMBYTE)
176    int mbytepos = 1;
177#endif /* DSPMBYTE */
178
179    for (;;) {
180	c = DgetC(DODOL);
181#if defined(DSPMBYTE)
182	if (mbytepos == 1 && Ismbyte1(c)) {
183	    /* An MB1 byte that may be followed by a MB2 byte */
184	    mbytepos = 2;
185	}
186	else {
187	    /* check if MB1 byte followed by an MB2 byte */
188	    if (mbytepos == 2 && Ismbyte2(c)) {
189		/* MB1 + MB2 make the character */
190		mbytepos = 1; /* reset */
191		goto mbyteskip;
192	    }
193	    mbytepos = 1; /* reset */
194	    /* wasn't followed, so the two bytes make two characters */
195	}
196#endif /* DSPMBYTE */
197	if (c == '\\') {
198	    c = DgetC(0);
199	    if (c == DEOF) {
200		unDredc(c);
201		*wp = 0;
202		Gcat(STRNULL, wbuf);
203		return (NULL);
204	    }
205	    if (c == '\n')
206		c = ' ';
207	    else
208		c |= QUOTE;
209	}
210	if (c == DEOF) {
211	    unDredc(c);
212	    *wp = 0;
213	    Gcat(STRNULL, wbuf);
214	    return (NULL);
215	}
216	if (cmap(c, _SP | _NL | _QF | _QB)) {	/* sp \t\n'"` */
217	    unDgetC(c);
218	    if (cmap(c, QUOTES))
219		return (wp);
220	    *wp++ = 0;
221	    Gcat(STRNULL, wbuf);
222	    return (NULL);
223	}
224#if defined(DSPMBYTE)
225mbyteskip:
226#endif /* DSPMBYTE */
227	if (--i <= 0)
228	    stderror(ERR_WTOOLONG);
229	*wp++ = (Char) c;
230    }
231}
232
233/*
234 * Get a word.  This routine is analogous to the routine
235 * word() in sh.lex.c for the main lexical input.  One difference
236 * here is that we don't get a newline to terminate our expansion.
237 * Rather, DgetC will return a DEOF when we hit the end-of-input.
238 */
239static int
240Dword()
241{
242    register int c, c1;
243    Char    wbuf[BUFSIZE];
244    register Char *wp = wbuf;
245    register int i = MAXWLEN;
246    register bool dolflg;
247    bool    sofar = 0, done = 0;
248
249    while (!done) {
250	done = 1;
251	c = DgetC(DODOL);
252	switch (c) {
253
254	case DEOF:
255	    if (sofar == 0)
256		return (0);
257	    /* finish this word and catch the code above the next time */
258	    unDredc(c);
259	    /*FALLTHROUGH*/
260
261	case '\n':
262	    *wp = 0;
263	    Gcat(STRNULL, wbuf);
264	    return (1);
265
266	case ' ':
267	case '\t':
268	    done = 0;
269	    break;
270
271	case '`':
272	    /* We preserve ` quotations which are done yet later */
273	    *wp++ = (Char) c, --i;
274	    /*FALLTHROUGH*/
275	case '\'':
276	case '"':
277	    /*
278	     * Note that DgetC never returns a QUOTES character from an
279	     * expansion, so only true input quotes will get us here or out.
280	     */
281	    c1 = c;
282	    dolflg = c1 == '"' ? DODOL : 0;
283	    for (;;) {
284		c = DgetC(dolflg);
285		if (c == c1)
286		    break;
287		if (c == '\n' || c == DEOF)
288		    stderror(ERR_UNMATCHED, c1);
289		if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) {
290		    if ((wp[-1] & TRIM) == '\\')
291			--wp;
292		    ++i;
293		}
294		if (--i <= 0)
295		    stderror(ERR_WTOOLONG);
296		switch (c1) {
297
298		case '"':
299		    /*
300		     * Leave any `s alone for later. Other chars are all
301		     * quoted, thus `...` can tell it was within "...".
302		     */
303		    *wp++ = c == '`' ? '`' : c | QUOTE;
304		    break;
305
306		case '\'':
307		    /* Prevent all further interpretation */
308		    *wp++ = c | QUOTE;
309		    break;
310
311		case '`':
312		    /* Leave all text alone for later */
313		    *wp++ = (Char) c;
314		    break;
315
316		default:
317		    break;
318		}
319	    }
320	    if (c1 == '`')
321		*wp++ = '`' /* i--; eliminated */;
322	    sofar = 1;
323	    if ((wp = Dpack(wbuf, wp)) == NULL)
324		return (1);
325	    else {
326#ifdef masscomp
327    /*
328     * Avoid a nasty message from the RTU 4.1A & RTU 5.0 compiler concerning
329     * the "overuse of registers". According to the compiler release notes,
330     * incorrect code may be produced unless the offending expression is
331     * rewritten. Therefore, we can't just ignore it, DAS DEC-90.
332     */
333		i = MAXWLEN;
334		i -= (int) (wp - wbuf);
335#else /* !masscomp */
336		i = MAXWLEN - (int) (wp - wbuf);
337#endif /* masscomp */
338		done = 0;
339	    }
340	    break;
341
342	case '\\':
343	    c = DgetC(0);	/* No $ subst! */
344	    if (c == '\n' || c == DEOF) {
345		done = 0;
346		break;
347	    }
348	    c |= QUOTE;
349	    break;
350
351	default:
352	    break;
353	}
354	if (done) {
355	    unDgetC(c);
356	    sofar = 1;
357	    if ((wp = Dpack(wbuf, wp)) == NULL)
358		return (1);
359	    else {
360#ifdef masscomp
361    /*
362     * Avoid a nasty message from the RTU 4.1A & RTU 5.0 compiler concerning
363     * the "overuse of registers". According to the compiler release notes,
364     * incorrect code may be produced unless the offending expression is
365     * rewritten. Therefore, we can't just ignore it, DAS DEC-90.
366     */
367		i = MAXWLEN;
368		i -= (int) (wp - wbuf);
369#else /* !masscomp */
370		i = MAXWLEN - (int) (wp - wbuf);
371#endif /* masscomp */
372		done = 0;
373	    }
374	}
375    }
376    /* Really NOTREACHED */
377    return (0);
378}
379
380
381/*
382 * Get a character, performing $ substitution unless flag is 0.
383 * Any QUOTES character which is returned from a $ expansion is
384 * QUOTEd so that it will not be recognized above.
385 */
386static int
387DgetC(flag)
388    register int flag;
389{
390    register int c;
391
392top:
393    if ((c = Dpeekc) != 0) {
394	Dpeekc = 0;
395	return (c);
396    }
397    if (lap) {
398	c = *lap++ & (QUOTE | TRIM);
399	if (c == 0) {
400	    lap = 0;
401	    goto top;
402	}
403quotspec:
404	if (cmap(c, QUOTES))
405	    return (c | QUOTE);
406	return (c);
407    }
408    if (dolp) {
409	if ((c = *dolp++ & (QUOTE | TRIM)) != 0)
410	    goto quotspec;
411	if (dolcnt > 0) {
412	    setDolp(*dolnxt++);
413	    --dolcnt;
414	    return (' ');
415	}
416	dolp = 0;
417    }
418    if (dolcnt > 0) {
419	setDolp(*dolnxt++);
420	--dolcnt;
421	goto top;
422    }
423    c = Dredc();
424    if (c == '$' && flag) {
425	Dgetdol();
426	goto top;
427    }
428    return (c);
429}
430
431static Char *nulvec[] = { NULL };
432static struct varent nulargv = {nulvec, STRargv, VAR_READWRITE,
433				{ NULL, NULL, NULL }, 0 };
434
435static void
436dolerror(s)
437    Char   *s;
438{
439    setname(short2str(s));
440    stderror(ERR_NAME | ERR_RANGE);
441}
442
443/*
444 * Handle the multitudinous $ expansion forms.
445 * Ugh.
446 */
447static void
448Dgetdol()
449{
450    register Char *np;
451    register struct varent *vp = NULL;
452    Char    name[4 * MAXVARLEN + 1];
453    int     c, sc;
454    int     subscr = 0, lwb = 1, upb = 0;
455    bool    dimen = 0, bitset = 0, length = 0;
456    char    tnp;
457    Char    wbuf[BUFSIZE];
458    static Char *dolbang = NULL;
459
460#ifdef COMPAT
461    dolmod = dolmcnt = dolwcnt = 0;
462#else
463    dolnmod = dolmcnt = dolwcnt = 0;
464#endif /* COMPAT */
465    c = sc = DgetC(0);
466    if (c == '{')
467	c = DgetC(0);		/* sc is { to take } later */
468    if ((c & TRIM) == '#')
469	dimen++, c = DgetC(0);	/* $# takes dimension */
470    else if (c == '?')
471	bitset++, c = DgetC(0);	/* $? tests existence */
472    else if (c == '%')
473	length++, c = DgetC(0); /* $% returns length in chars */
474    switch (c) {
475
476    case '!':
477	if (dimen || bitset || length)
478	    stderror(ERR_SYNTAX);
479	if (backpid != 0) {
480	    if (dolbang)
481		xfree((ptr_t) dolbang);
482	    setDolp(dolbang = putn(backpid));
483	}
484	goto eatbrac;
485
486    case '$':
487	if (dimen || bitset || length)
488	    stderror(ERR_SYNTAX);
489	setDolp(doldol);
490	goto eatbrac;
491
492#ifdef COHERENT
493    /* Coherent compiler doesn't allow case-labels that are not
494       constant-expressions */
495#ifdef SHORT_STRINGS
496    case 0100074:
497#else /* !SHORT_STRINGS */
498    case 0274:
499#endif
500#else /* !COHERENT */
501    case '<'|QUOTE:
502#endif
503	if (bitset)
504	    stderror(ERR_NOTALLOWED, "$?<");
505	if (dimen)
506	    stderror(ERR_NOTALLOWED, "$#<");
507	if (length)
508	    stderror(ERR_NOTALLOWED, "$%<");
509	{
510#ifdef BSDSIGS
511	    sigmask_t omask = sigsetmask(sigblock(0) & ~sigmask(SIGINT));
512#else /* !BSDSIGS */
513	    (void) sigrelse(SIGINT);
514#endif /* BSDSIGS */
515	    for (np = wbuf; force_read(OLDSTD, &tnp, 1) == 1; np++) {
516		*np = (unsigned char) tnp;
517		if (np >= &wbuf[BUFSIZE - 1])
518		    stderror(ERR_LTOOLONG);
519		if (tnp == '\n')
520		    break;
521	    }
522	    *np = 0;
523#ifdef BSDSIGS
524	    (void) sigsetmask(omask);
525#else /* !BSDSIGS */
526	    (void) sighold(SIGINT);
527#endif /* BSDSIGS */
528	}
529
530#ifdef COMPAT
531	/*
532	 * KLUDGE: dolmod is set here because it will cause setDolp to call
533	 * domod and thus to copy wbuf. Otherwise setDolp would use it
534	 * directly. If we saved it ourselves, no one would know when to free
535	 * it. The actual function of the 'q' causes filename expansion not to
536	 * be done on the interpolated value.
537	 */
538	/*
539	 * If we do that, then other modifiers don't work.
540	 * in addition, let the user specify :q if wanted
541	 * [christos]
542	 */
543/*old*/	dolmod = 'q';
544/*new*/	dolmod[dolnmod++] = 'q';
545	dolmcnt = 10000;
546#endif /* COMPAT */
547
548	fixDolMod();
549	setDolp(wbuf);
550	goto eatbrac;
551
552    case '*':
553	(void) Strcpy(name, STRargv);
554	vp = adrof(STRargv);
555	subscr = -1;		/* Prevent eating [...] */
556	break;
557
558    case DEOF:
559    case '\n':
560	np = dimen ? STRargv : (bitset ? STRstatus : NULL);
561	if (np) {
562	    bitset = 0;
563	    (void) Strcpy(name, np);
564	    vp = adrof(np);
565	    subscr = -1;		/* Prevent eating [...] */
566	    unDredc(c);
567	    break;
568	}
569	else
570	    stderror(ERR_SYNTAX);
571	/*NOTREACHED*/
572
573    default:
574	np = name;
575	if (Isdigit(c)) {
576	    if (dimen)
577		stderror(ERR_NOTALLOWED, "$#<num>");
578	    subscr = 0;
579	    do {
580		subscr = subscr * 10 + c - '0';
581		c = DgetC(0);
582	    } while (Isdigit(c));
583	    unDredc(c);
584	    if (subscr < 0)
585		stderror(ERR_RANGE);
586	    if (subscr == 0) {
587		if (bitset) {
588		    dolp = dolzero ? STR1 : STR0;
589		    goto eatbrac;
590		}
591		if (ffile == 0)
592		    stderror(ERR_DOLZERO);
593		if (length) {
594		    Char *cp;
595		    length = Strlen(ffile);
596		    cp = putn(length);
597		    addla(cp);
598		    xfree((ptr_t) cp);
599		}
600		else {
601		    fixDolMod();
602		    setDolp(ffile);
603		}
604		goto eatbrac;
605	    }
606#if 0
607	    if (bitset)
608		stderror(ERR_NOTALLOWED, "$?<num>");
609	    if (length)
610		stderror(ERR_NOTALLOWED, "$%<num>");
611#endif
612	    vp = adrof(STRargv);
613	    if (vp == 0) {
614		vp = &nulargv;
615		goto eatmod;
616	    }
617	    break;
618	}
619	if (!alnum(c)) {
620	    np = dimen ? STRargv : (bitset ? STRstatus : NULL);
621	    if (np) {
622		bitset = 0;
623		(void) Strcpy(name, np);
624		vp = adrof(np);
625		subscr = -1;		/* Prevent eating [...] */
626		unDredc(c);
627		break;
628	    }
629	    else
630		stderror(ERR_VARALNUM);
631	}
632	for (;;) {
633	    *np++ = (Char) c;
634	    c = DgetC(0);
635	    if (!alnum(c))
636		break;
637	    if (np >= &name[MAXVARLEN])
638		stderror(ERR_VARTOOLONG);
639	}
640	*np++ = 0;
641	unDredc(c);
642	vp = adrof(name);
643    }
644    if (bitset) {
645	dolp = (vp || getenv(short2str(name))) ? STR1 : STR0;
646	goto eatbrac;
647    }
648    if (vp == NULL || vp->vec == NULL) {
649	np = str2short(getenv(short2str(name)));
650	if (np) {
651	    fixDolMod();
652	    setDolp(np);
653	    goto eatbrac;
654	}
655	udvar(name);
656	/* NOTREACHED */
657    }
658    c = DgetC(0);
659    upb = blklen(vp->vec);
660    if (dimen == 0 && subscr == 0 && c == '[') {
661	np = name;
662	for (;;) {
663	    c = DgetC(DODOL);	/* Allow $ expand within [ ] */
664	    if (c == ']')
665		break;
666	    if (c == '\n' || c == DEOF)
667		stderror(ERR_INCBR);
668	    if (np >= &name[sizeof(name) / sizeof(Char) - 2])
669		stderror(ERR_VARTOOLONG);
670	    *np++ = (Char) c;
671	}
672	*np = 0, np = name;
673	if (dolp || dolcnt)	/* $ exp must end before ] */
674	    stderror(ERR_EXPORD);
675	if (!*np)
676	    stderror(ERR_SYNTAX);
677	if (Isdigit(*np)) {
678	    int     i;
679
680	    for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0')
681		continue;
682	    if ((i < 0 || i > upb) && !any("-*", *np)) {
683		dolerror(vp->v_name);
684		return;
685	    }
686	    lwb = i;
687	    if (!*np)
688		upb = lwb, np = STRstar;
689	}
690	if (*np == '*')
691	    np++;
692	else if (*np != '-')
693	    stderror(ERR_MISSING, '-');
694	else {
695	    register int i = upb;
696
697	    np++;
698	    if (Isdigit(*np)) {
699		i = 0;
700		while (Isdigit(*np))
701		    i = i * 10 + *np++ - '0';
702		if (i < 0 || i > upb) {
703		    dolerror(vp->v_name);
704		    return;
705		}
706	    }
707	    if (i < lwb)
708		upb = lwb - 1;
709	    else
710		upb = i;
711	}
712	if (lwb == 0) {
713	    if (upb != 0) {
714		dolerror(vp->v_name);
715		return;
716	    }
717	    upb = -1;
718	}
719	if (*np)
720	    stderror(ERR_SYNTAX);
721    }
722    else {
723	if (subscr > 0) {
724	    if (subscr > upb)
725		lwb = 1, upb = 0;
726	    else
727		lwb = upb = subscr;
728	}
729	unDredc(c);
730    }
731    if (dimen) {
732	Char   *cp = putn(upb - lwb + 1);
733
734	/* this is a kludge. It prevents Dgetdol() from */
735	/* pushing erroneous ${#<error> values into the labuf. */
736	if (sc == '{') {
737	    c = Dredc();
738	    if (c != '}')
739	    {
740		xfree((ptr_t) cp);
741		stderror(ERR_MISSING, '}');
742	        return;
743	    }
744	    unDredc(c);
745	}
746	addla(cp);
747	xfree((ptr_t) cp);
748    }
749    else if (length) {
750	int i;
751	Char   *cp;
752	for (i = lwb - 1, length = 0; i < upb; i++)
753	    length += Strlen(vp->vec[i]);
754#ifdef notdef
755	/* We don't want that, since we can always compute it by adding $#xxx */
756	length += i - 1;	/* Add the number of spaces in */
757#endif
758	cp = putn(length);
759	addla(cp);
760	xfree((ptr_t) cp);
761    }
762    else {
763eatmod:
764	fixDolMod();
765	dolnxt = &vp->vec[lwb - 1];
766	dolcnt = upb - lwb + 1;
767    }
768eatbrac:
769    if (sc == '{') {
770	c = Dredc();
771	if (c != '}')
772	    stderror(ERR_MISSING, '}');
773    }
774}
775
776static void
777fixDolMod()
778{
779    register int c;
780
781    c = DgetC(0);
782    if (c == ':') {
783#ifndef COMPAT
784	do {
785#endif /* COMPAT */
786	    c = DgetC(0), dolmcnt = 1, dolwcnt = 1;
787	    if (c == 'g' || c == 'a') {
788		if (c == 'g')
789		    dolmcnt = 10000;
790		else
791		    dolwcnt = 10000;
792		c = DgetC(0);
793	    }
794	    if ((c == 'g' && dolmcnt != 10000) ||
795		(c == 'a' && dolwcnt != 10000)) {
796		if (c == 'g')
797		    dolmcnt = 10000;
798		else
799		    dolwcnt = 10000;
800		c = DgetC(0);
801	    }
802
803	    if (c == 's') {	/* [eichin:19910926.0755EST] */
804		int delimcnt = 2;
805		int delim = DgetC(0);
806		dolmod[dolnmod++] = (Char) c;
807		dolmod[dolnmod++] = (Char) delim;
808
809		if (!delim || letter(delim)
810		    || Isdigit(delim) || any(" \t\n", delim)) {
811		    seterror(ERR_BADSUBST);
812		    break;
813		}
814		while ((c = DgetC(0)) != (-1)) {
815		    dolmod[dolnmod++] = (Char) c;
816		    if(c == delim) delimcnt--;
817		    if(!delimcnt) break;
818		}
819		if(delimcnt) {
820		    seterror(ERR_BADSUBST);
821		    break;
822		}
823		continue;
824	    }
825	    if (!any("luhtrqxes", c))
826		stderror(ERR_BADMOD, c);
827#ifndef COMPAT
828	    dolmod[dolnmod++] = (Char) c;
829#else
830	    dolmod = (Char) c;
831#endif /* COMPAT */
832	    if (c == 'q')
833		dolmcnt = 10000;
834#ifndef COMPAT
835	}
836	while ((c = DgetC(0)) == ':');
837	unDredc(c);
838#endif /* COMPAT */
839    }
840    else
841	unDredc(c);
842}
843
844static void
845setDolp(cp)
846    register Char *cp;
847{
848    register Char *dp;
849#ifndef COMPAT
850    int i;
851#endif /* COMPAT */
852
853#ifdef COMPAT
854    if (dolmod == 0 || dolmcnt == 0) {
855#else
856    if (dolnmod == 0 || dolmcnt == 0) {
857#endif /* COMPAT */
858	dolp = cp;
859	return;
860    }
861#ifdef COMPAT
862    dp = domod(cp, dolmod);
863#else
864    dp = cp = Strsave(cp);
865    for (i = 0; i < dolnmod; i++) {
866	/* handle s// [eichin:19910926.0510EST] */
867	if(dolmod[i] == 's') {
868	    int delim;
869	    Char *lhsub, *rhsub, *np;
870	    size_t lhlen = 0, rhlen = 0;
871	    int didmod = 0;
872
873	    delim = dolmod[++i];
874	    if (!delim || letter(delim)
875		|| Isdigit(delim) || any(" \t\n", delim)) {
876		seterror(ERR_BADSUBST);
877		break;
878	    }
879	    lhsub = &dolmod[++i];
880	    while(dolmod[i] != delim && dolmod[++i]) {
881		lhlen++;
882	    }
883	    dolmod[i] = 0;
884	    rhsub = &dolmod[++i];
885	    while(dolmod[i] != delim && dolmod[++i]) {
886		rhlen++;
887	    }
888	    dolmod[i] = 0;
889
890	    do {
891		strip(lhsub);
892		strip(cp);
893		dp = Strstr(cp, lhsub);
894		if (dp) {
895		    np = (Char *) xmalloc((size_t)
896					  ((Strlen(cp) + 1 - lhlen + rhlen) *
897					  sizeof(Char)));
898		    (void) Strncpy(np, cp, (size_t) (dp - cp));
899		    (void) Strcpy(np + (dp - cp), rhsub);
900		    (void) Strcpy(np + (dp - cp) + rhlen, dp + lhlen);
901
902		    xfree((ptr_t) cp);
903		    dp = cp = np;
904		    didmod = 1;
905		} else {
906		    /* should this do a seterror? */
907		    break;
908		}
909	    }
910	    while (dolwcnt == 10000);
911	    /*
912	     * restore dolmod for additional words
913	     */
914	    dolmod[i] = rhsub[-1] = (Char) delim;
915	    if (didmod)
916		dolmcnt--;
917#ifdef notdef
918	    else
919		break;
920#endif
921        } else {
922	    int didmod = 0;
923
924	    do {
925		if ((dp = domod(cp, dolmod[i])) != NULL) {
926		    didmod = 1;
927		    if (Strcmp(cp, dp) == 0) {
928			xfree((ptr_t) cp);
929			cp = dp;
930			break;
931		    }
932		    else {
933			xfree((ptr_t) cp);
934			cp = dp;
935		    }
936		}
937		else
938		    break;
939	    }
940	    while (dolwcnt == 10000);
941	    dp = cp;
942	    if (didmod)
943		dolmcnt--;
944#ifdef notdef
945	    else
946		break;
947#endif
948	}
949    }
950#endif /* COMPAT */
951
952    if (dp) {
953#ifdef COMPAT
954	dolmcnt--;
955#endif /* COMPAT */
956	addla(dp);
957	xfree((ptr_t) dp);
958    }
959#ifndef COMPAT
960    else
961	addla(cp);
962#endif /* COMPAT */
963
964    dolp = STRNULL;
965    if (seterr)
966	stderror(ERR_OLD);
967}
968
969static void
970unDredc(c)
971    int     c;
972{
973
974    Dpeekrd = c;
975}
976
977static int
978Dredc()
979{
980    register int c;
981
982    if ((c = Dpeekrd) != 0) {
983	Dpeekrd = 0;
984	return (c);
985    }
986    if (Dcp && (c = *Dcp++))
987	return (c & (QUOTE | TRIM));
988    if (*Dvp == 0) {
989	Dcp = 0;
990	return (DEOF);
991    }
992    Dcp = *Dvp++;
993    return (' ');
994}
995
996static void
997Dtestq(c)
998    register int c;
999{
1000
1001    if (cmap(c, QUOTES))
1002	gflag = 1;
1003}
1004
1005/*
1006 * Form a shell temporary file (in unit 0) from the words
1007 * of the shell input up to EOF or a line the same as "term".
1008 * Unit 0 should have been closed before this call.
1009 */
1010void
1011heredoc(term)
1012    Char   *term;
1013{
1014    int c;
1015    Char   *Dv[2];
1016    Char    obuf[BUFSIZE], lbuf[BUFSIZE], mbuf[BUFSIZE];
1017    int     ocnt, lcnt, mcnt;
1018    register Char *lbp, *obp, *mbp;
1019    Char  **vp;
1020    bool    quoted;
1021    char   *tmp;
1022#ifndef WINNT_NATIVE
1023    struct timeval tv;
1024
1025again:
1026#endif /* WINNT_NATIVE */
1027    tmp = short2str(shtemp);
1028#ifndef O_CREAT
1029# define O_CREAT 0
1030    if (creat(tmp, 0600) < 0)
1031	stderror(ERR_SYSTEM, tmp, strerror(errno));
1032#endif
1033    (void) close(0);
1034#ifndef O_TEMPORARY
1035# define O_TEMPORARY 0
1036#endif
1037#ifndef O_EXCL
1038# define O_EXCL 0
1039#endif
1040    if (open(tmp, O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY, 0600) == -1) {
1041	int oerrno = errno;
1042#ifndef WINNT_NATIVE
1043	if (errno == EEXIST) {
1044	    if (unlink(tmp) == -1) {
1045		(void) gettimeofday(&tv, NULL);
1046		shtemp = Strspl(STRtmpsh, putn((((int)tv.tv_sec) ^
1047		    ((int)tv.tv_usec) ^ ((int)getpid())) & 0x00ffffff));
1048	    }
1049	    goto again;
1050	}
1051#endif /* WINNT_NATIVE */
1052	(void) unlink(tmp);
1053	errno = oerrno;
1054 	stderror(ERR_SYSTEM, tmp, strerror(errno));
1055    }
1056    (void) unlink(tmp);		/* 0 0 inode! */
1057    Dv[0] = term;
1058    Dv[1] = NULL;
1059    gflag = 0;
1060    trim(Dv);
1061    rscan(Dv, Dtestq);
1062    quoted = gflag;
1063    ocnt = BUFSIZE;
1064    obp = obuf;
1065    inheredoc = 1;
1066#ifdef WINNT_NATIVE
1067    __dup_stdin = 1;
1068#endif /* WINNT_NATIVE */
1069    for (;;) {
1070	/*
1071	 * Read up a line
1072	 */
1073	lbp = lbuf;
1074	lcnt = BUFSIZE - 4;
1075	for (;;) {
1076	    c = readc(1);	/* 1 -> Want EOF returns */
1077	    if (c < 0 || c == '\n')
1078		break;
1079	    if ((c &= TRIM) != 0) {
1080		*lbp++ = (Char) c;
1081		if (--lcnt < 0) {
1082		    setname("<<");
1083		    stderror(ERR_NAME | ERR_OVERFLOW);
1084		}
1085	    }
1086	}
1087	*lbp = 0;
1088
1089	/*
1090	 * Check for EOF or compare to terminator -- before expansion
1091	 */
1092	if (c < 0 || eq(lbuf, term)) {
1093	    (void) write(0, short2str(obuf), (size_t) (BUFSIZE - ocnt));
1094	    (void) lseek(0, (off_t) 0, L_SET);
1095	    inheredoc = 0;
1096	    return;
1097	}
1098
1099	/*
1100	 * If term was quoted or -n just pass it on
1101	 */
1102	if (quoted || noexec) {
1103	    *lbp++ = '\n';
1104	    *lbp = 0;
1105	    for (lbp = lbuf; (c = *lbp++) != 0;) {
1106		*obp++ = (Char) c;
1107		if (--ocnt == 0) {
1108		    (void) write(0, short2str(obuf), BUFSIZE);
1109		    obp = obuf;
1110		    ocnt = BUFSIZE;
1111		}
1112	    }
1113	    continue;
1114	}
1115
1116	/*
1117	 * Term wasn't quoted so variable and then command expand the input
1118	 * line
1119	 */
1120	Dcp = lbuf;
1121	Dvp = Dv + 1;
1122	mbp = mbuf;
1123	mcnt = BUFSIZE - 4;
1124	for (;;) {
1125	    c = DgetC(DODOL);
1126	    if (c == DEOF)
1127		break;
1128	    if ((c &= TRIM) == 0)
1129		continue;
1130	    /* \ quotes \ $ ` here */
1131	    if (c == '\\') {
1132		c = DgetC(0);
1133		if (!any("$\\`", c))
1134		    unDgetC(c | QUOTE), c = '\\';
1135		else
1136		    c |= QUOTE;
1137	    }
1138	    *mbp++ = (Char) c;
1139	    if (--mcnt == 0) {
1140		setname("<<");
1141		stderror(ERR_NAME | ERR_OVERFLOW);
1142	    }
1143	}
1144	*mbp++ = 0;
1145
1146	/*
1147	 * If any ` in line do command substitution
1148	 */
1149	mbp = mbuf;
1150	if (Strchr(mbp, '`') != NULL) {
1151	    /*
1152	     * 1 arg to dobackp causes substitution to be literal. Words are
1153	     * broken only at newlines so that all blanks and tabs are
1154	     * preserved.  Blank lines (null words) are not discarded.
1155	     */
1156	    vp = dobackp(mbuf, 1);
1157	}
1158	else
1159	    /* Setup trivial vector similar to return of dobackp */
1160	    Dv[0] = mbp, Dv[1] = NULL, vp = Dv;
1161
1162	/*
1163	 * Resurrect the words from the command substitution each separated by
1164	 * a newline.  Note that the last newline of a command substitution
1165	 * will have been discarded, but we put a newline after the last word
1166	 * because this represents the newline after the last input line!
1167	 */
1168	for (; *vp; vp++) {
1169	    for (mbp = *vp; *mbp; mbp++) {
1170		*obp++ = *mbp & TRIM;
1171		if (--ocnt == 0) {
1172		    (void) write(0, short2str(obuf), BUFSIZE);
1173		    obp = obuf;
1174		    ocnt = BUFSIZE;
1175		}
1176	    }
1177	    *obp++ = '\n';
1178	    if (--ocnt == 0) {
1179		(void) write(0, short2str(obuf), BUFSIZE);
1180		obp = obuf;
1181		ocnt = BUFSIZE;
1182	    }
1183	}
1184	if (pargv)
1185	    blkfree(pargv), pargv = 0;
1186    }
1187}
1188