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