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