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