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