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