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