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