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