]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - bin/csh/lex.c
This commit was generated by cvs2svn to compensate for changes in r55714,
[FreeBSD/FreeBSD.git] / bin / csh / lex.c
1 /*-
2  * Copyright (c) 1980, 1991, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. 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
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)lex.c       8.1 (Berkeley) 5/31/93";
37 #else
38 static const char rcsid[] =
39   "$FreeBSD$";
40 #endif
41 #endif /* not lint */
42
43 #include <sys/types.h>
44 #include <sys/ioctl.h>
45 #include <termios.h>
46 #include <errno.h>
47 #include <string.h>
48 #include <unistd.h>
49 #if __STDC__
50 # include <stdarg.h>
51 #else
52 # include <varargs.h>
53 #endif
54
55 #include "csh.h"
56 #include "extern.h"
57
58 /*
59  * These lexical routines read input and form lists of words.
60  * There is some involved processing here, because of the complications
61  * of input buffering, and especially because of history substitution.
62  */
63
64 static Char     *word __P((void));
65 static int       getC1 __P((int));
66 static void      getdol __P((void));
67 static void      getexcl __P((int));
68 static struct Hist
69                 *findev __P((Char *, bool));
70 static void      setexclp __P((Char *));
71 static int       bgetc __P((void));
72 static void      bfree __P((void));
73 static struct wordent
74                 *gethent __P((int));
75 static int       matchs __P((Char *, Char *));
76 static int       getsel __P((int *, int *, int));
77 static struct wordent
78                 *getsub __P((struct wordent *));
79 static Char     *subword __P((Char *, int, bool *));
80 static struct wordent
81                 *dosub __P((int, struct wordent *, bool));
82
83 /*
84  * Peekc is a peek character for getC, peekread for readc.
85  * There is a subtlety here in many places... history routines
86  * will read ahead and then insert stuff into the input stream.
87  * If they push back a character then they must push it behind
88  * the text substituted by the history substitution.  On the other
89  * hand in several places we need 2 peek characters.  To make this
90  * all work, the history routines read with getC, and make use both
91  * of ungetC and unreadc.  The key observation is that the state
92  * of getC at the call of a history reference is such that calls
93  * to getC from the history routines will always yield calls of
94  * readc, unless this peeking is involved.  That is to say that during
95  * getexcl the variables lap, exclp, and exclnxt are all zero.
96  *
97  * Getdol invokes history substitution, hence the extra peek, peekd,
98  * which it can ungetD to be before history substitutions.
99  */
100 static Char peekc = 0, peekd = 0;
101 static Char peekread = 0;
102
103 /* (Tail of) current word from ! subst */
104 static Char *exclp = NULL;
105
106 /* The rest of the ! subst words */
107 static struct wordent *exclnxt = NULL;
108
109 /* Count of remaining words in ! subst */
110 static int exclc = 0;
111
112 /* "Globp" for alias resubstitution */
113 Char *alvecp = NULL;
114 int aret = F_SEEK;
115
116 /*
117  * Labuf implements a general buffer for lookahead during lexical operations.
118  * Text which is to be placed in the input stream can be stuck here.
119  * We stick parsed ahead $ constructs during initial input,
120  * process id's from `$$', and modified variable values (from qualifiers
121  * during expansion in sh.dol.c) here.
122  */
123 static Char labuf[BUFSIZ];
124
125 /*
126  * Lex returns to its caller not only a wordlist (as a "var" parameter)
127  * but also whether a history substitution occurred.  This is used in
128  * the main (process) routine to determine whether to echo, and also
129  * when called by the alias routine to determine whether to keep the
130  * argument list.
131  */
132 static bool hadhist = 0;
133
134 /*
135  * Avoid alias expansion recursion via \!#
136  */
137 int     hleft;
138
139 static Char getCtmp;
140
141 #define getC(f)         ((getCtmp = peekc) ? (peekc = 0, getCtmp) : getC1(f))
142 #define ungetC(c)       peekc = c
143 #define ungetD(c)       peekd = c
144
145 int
146 lex(hp)
147     struct wordent *hp;
148 {
149     struct wordent *wdp;
150     int     c;
151
152     btell(&lineloc);
153     hp->next = hp->prev = hp;
154     hp->word = STRNULL;
155     hadhist = 0;
156     do
157         c = readc(0);
158     while (c == ' ' || c == '\t');
159     if (c == HISTSUB && intty)
160         /* ^lef^rit     from tty is short !:s^lef^rit */
161         getexcl(c);
162     else
163         unreadc(c);
164     wdp = hp;
165     /*
166      * The following loop is written so that the links needed by freelex will
167      * be ready and rarin to go even if it is interrupted.
168      */
169     do {
170         struct wordent *new;
171
172         new = (struct wordent *) xmalloc((size_t) sizeof(*wdp));
173         new->word = 0;
174         new->prev = wdp;
175         new->next = hp;
176         wdp->next = new;
177         wdp = new;
178         wdp->word = word();
179     } while (wdp->word[0] != '\n');
180     hp->prev = wdp;
181     return (hadhist);
182 }
183
184 void
185 prlex(fp, sp0)
186     FILE *fp;
187     struct wordent *sp0;
188 {
189     struct wordent *sp = sp0->next;
190
191     for (;;) {
192         (void) fprintf(fp, "%s", vis_str(sp->word));
193         sp = sp->next;
194         if (sp == sp0)
195             break;
196         if (sp->word[0] != '\n')
197             (void) fputc(' ', fp);
198     }
199 }
200
201 void
202 copylex(hp, fp)
203     struct wordent *hp;
204     struct wordent *fp;
205 {
206     struct wordent *wdp;
207
208     wdp = hp;
209     fp = fp->next;
210     do {
211         struct wordent *new;
212
213         new = (struct wordent *) xmalloc((size_t) sizeof(*wdp));
214         new->prev = wdp;
215         new->next = hp;
216         wdp->next = new;
217         wdp = new;
218         wdp->word = Strsave(fp->word);
219         fp = fp->next;
220     } while (wdp->word[0] != '\n');
221     hp->prev = wdp;
222 }
223
224 void
225 freelex(vp)
226     struct wordent *vp;
227 {
228     struct wordent *fp;
229
230     while (vp->next != vp) {
231         fp = vp->next;
232         vp->next = fp->next;
233         xfree((ptr_t) fp->word);
234         xfree((ptr_t) fp);
235     }
236     vp->prev = vp;
237 }
238
239 static Char *
240 word()
241 {
242     Char c, c1;
243     Char *wp;
244     Char    wbuf[BUFSIZ];
245     bool dolflg;
246     int i;
247
248     wp = wbuf;
249     i = BUFSIZ - 4;
250 loop:
251     while ((c = getC(DOALL)) == ' ' || c == '\t')
252         continue;
253     if (cmap(c, _META | _ESC))
254         switch (c) {
255         case '&':
256         case '|':
257         case '<':
258         case '>':
259             *wp++ = c;
260             c1 = getC(DOALL);
261             if (c1 == c)
262                 *wp++ = c1;
263             else
264                 ungetC(c1);
265             goto ret;
266
267         case '#':
268             if (intty)
269                 break;
270             c = 0;
271             do {
272                 c1 = c;
273                 c = getC(0);
274             } while (c != '\n');
275             if (c1 == '\\')
276                 goto loop;
277             /* fall into ... */
278
279         case ';':
280         case '(':
281         case ')':
282         case '\n':
283             *wp++ = c;
284             goto ret;
285
286         case '\\':
287             c = getC(0);
288             if (c == '\n') {
289                 if (onelflg == 1)
290                     onelflg = 2;
291                 goto loop;
292             }
293             if (c != HIST)
294                 *wp++ = '\\', --i;
295             c |= QUOTE;
296         }
297     c1 = 0;
298     dolflg = DOALL;
299     for (;;) {
300         if (c1) {
301             if (c == c1) {
302                 c1 = 0;
303                 dolflg = DOALL;
304             }
305             else if (c == '\\') {
306                 c = getC(0);
307                 if (c == HIST)
308                     c |= QUOTE;
309                 else {
310                     if (c == '\n')
311                         /*
312                          * if (c1 == '`') c = ' '; else
313                          */
314                         c |= QUOTE;
315                     ungetC(c);
316                     c = '\\';
317                 }
318             }
319             else if (c == '\n') {
320                 seterror(ERR_UNMATCHED, c1);
321                 ungetC(c);
322                 break;
323             }
324         }
325         else if (cmap(c, _META | _QF | _QB | _ESC)) {
326             if (c == '\\') {
327                 c = getC(0);
328                 if (c == '\n') {
329                     if (onelflg == 1)
330                         onelflg = 2;
331                     break;
332                 }
333                 if (c != HIST)
334                     *wp++ = '\\', --i;
335                 c |= QUOTE;
336             }
337             else if (cmap(c, _QF | _QB)) {      /* '"` */
338                 c1 = c;
339                 dolflg = c == '"' ? DOALL : DOEXCL;
340             }
341             else if (c != '#' || !intty) {
342                 ungetC(c);
343                 break;
344             }
345         }
346         if (--i > 0) {
347             *wp++ = c;
348             c = getC(dolflg);
349         }
350         else {
351             seterror(ERR_WTOOLONG);
352             wp = &wbuf[1];
353             break;
354         }
355     }
356 ret:
357     *wp = 0;
358     return (Strsave(wbuf));
359 }
360
361 static int
362 getC1(flag)
363     int flag;
364 {
365     Char c;
366
367     while (1) {
368         if ((c = peekc) != '\0') {
369             peekc = 0;
370             return (c);
371         }
372         if (lap) {
373             if ((c = *lap++) == 0)
374                 lap = 0;
375             else {
376                 if (cmap(c, _META | _QF | _QB))
377                     c |= QUOTE;
378                 return (c);
379             }
380         }
381         if ((c = peekd) != '\0') {
382             peekd = 0;
383             return (c);
384         }
385         if (exclp) {
386             if ((c = *exclp++) != '\0')
387                 return (c);
388             if (exclnxt && --exclc >= 0) {
389                 exclnxt = exclnxt->next;
390                 setexclp(exclnxt->word);
391                 return (' ');
392             }
393             exclp = 0;
394             exclnxt = 0;
395         }
396         if (exclnxt) {
397             exclnxt = exclnxt->next;
398             if (--exclc < 0)
399                 exclnxt = 0;
400             else
401                 setexclp(exclnxt->word);
402             continue;
403         }
404         c = readc(0);
405         if (c == '$' && (flag & DODOL)) {
406             getdol();
407             continue;
408         }
409         if (c == HIST && (flag & DOEXCL)) {
410             getexcl(0);
411             continue;
412         }
413         break;
414     }
415     return (c);
416 }
417
418 static void
419 getdol()
420 {
421     Char *np, *ep;
422     Char    name[4 * MAXVARLEN + 1];
423     int c;
424     int     sc;
425     bool    special = 0, toolong;
426
427     np = name, *np++ = '$';
428     c = sc = getC(DOEXCL);
429     if (any("\t \n", c)) {
430         ungetD(c);
431         ungetC('$' | QUOTE);
432         return;
433     }
434     if (c == '{')
435         *np++ = c, c = getC(DOEXCL);
436     if (c == '#' || c == '?')
437         special++, *np++ = c, c = getC(DOEXCL);
438     *np++ = c;
439     switch (c) {
440
441     case '<':
442     case '$':
443     case '!':
444         if (special)
445             seterror(ERR_SPDOLLT);
446         *np = 0;
447         addla(name);
448         return;
449
450     case '\n':
451         ungetD(c);
452         np--;
453         seterror(ERR_NEWLINE);
454         *np = 0;
455         addla(name);
456         return;
457
458     case '*':
459         if (special)
460             seterror(ERR_SPSTAR);
461         *np = 0;
462         addla(name);
463         return;
464
465     default:
466         toolong = 0;
467         if (Isdigit(c)) {
468 #ifdef notdef
469             /* let $?0 pass for now */
470             if (special) {
471                 seterror(ERR_DIGIT);
472                 *np = 0;
473                 addla(name);
474                 return;
475             }
476 #endif
477             /* we know that np < &name[4] */
478             ep = &np[MAXVARLEN];
479             while ((c = getC(DOEXCL)) != '\0'){
480                 if (!Isdigit(c))
481                     break;
482                 if (np < ep)
483                     *np++ = c;
484                 else
485                     toolong = 1;
486             }
487         }
488         else if (letter(c)) {
489             /* we know that np < &name[4] */
490             ep = &np[MAXVARLEN];
491             toolong = 0;
492             while ((c = getC(DOEXCL)) != '\0') {
493                 /* Bugfix for ${v123x} from Chris Torek, DAS DEC-90. */
494                 if (!letter(c) && !Isdigit(c))
495                     break;
496                 if (np < ep)
497                     *np++ = c;
498                 else
499                     toolong = 1;
500             }
501         }
502         else {
503             *np = 0;
504             seterror(ERR_VARILL);
505             addla(name);
506             return;
507         }
508         if (toolong) {
509             seterror(ERR_VARTOOLONG);
510             *np = 0;
511             addla(name);
512             return;
513         }
514         break;
515     }
516     if (c == '[') {
517         *np++ = c;
518         /*
519          * Name up to here is a max of MAXVARLEN + 8.
520          */
521         ep = &np[2 * MAXVARLEN + 8];
522         do {
523             /*
524              * Michael Greim: Allow $ expansion to take place in selector
525              * expressions. (limits the number of characters returned)
526              */
527             c = getC(DOEXCL | DODOL);
528             if (c == '\n') {
529                 ungetD(c);
530                 np--;
531                 seterror(ERR_NLINDEX);
532                 *np = 0;
533                 addla(name);
534                 return;
535             }
536             if (np < ep)
537                 *np++ = c;
538         } while (c != ']');
539         *np = '\0';
540         if (np >= ep) {
541             seterror(ERR_SELOVFL);
542             addla(name);
543             return;
544         }
545         c = getC(DOEXCL);
546     }
547     /*
548      * Name up to here is a max of 2 * MAXVARLEN + 8.
549      */
550     if (c == ':') {
551         /*
552          * if the :g modifier is followed by a newline, then error right away!
553          * -strike
554          */
555
556         int     gmodflag = 0, amodflag = 0;
557
558         do {
559             *np++ = c, c = getC(DOEXCL);
560             if (c == 'g' || c == 'a') {
561                 if (c == 'g')
562                     gmodflag++;
563                 else
564                     amodflag++;
565                 *np++ = c; c = getC(DOEXCL);
566             }
567             if ((c == 'g' && !gmodflag) || (c == 'a' && !amodflag)) {
568                 if (c == 'g')
569                     gmodflag++;
570                 else
571                     amodflag++;
572                 *np++ = c; c = getC(DOEXCL);
573             }
574             *np++ = c;
575             /* scan s// [eichin:19910926.0512EST] */
576             if (c == 's') {
577                 int delimcnt = 2;
578                 int delim = getC(0);
579                 *np++ = delim;
580
581                 if (!delim || letter(delim)
582                     || Isdigit(delim) || any(" \t\n", delim)) {
583                     seterror(ERR_BADSUBST);
584                     break;
585                 }
586                 while ((c = getC(0)) != (-1)) {
587                     *np++ = c;
588                     if(c == delim) delimcnt--;
589                     if(!delimcnt) break;
590                 }
591                 if(delimcnt) {
592                     seterror(ERR_BADSUBST);
593                     break;
594                 }
595                 c = 's';
596             }
597             if (!any("htrqxes", c)) {
598                 if ((amodflag || gmodflag) && c == '\n')
599                     stderror(ERR_VARSYN);       /* strike */
600                 seterror(ERR_VARMOD, c);
601                 *np = 0;
602                 addla(name);
603                 return;
604             }
605         }
606         while ((c = getC(DOEXCL)) == ':');
607         ungetD(c);
608     }
609     else
610         ungetD(c);
611     if (sc == '{') {
612         c = getC(DOEXCL);
613         if (c != '}') {
614             ungetD(c);
615             seterror(ERR_MISSING, '}');
616             *np = 0;
617             addla(name);
618             return;
619         }
620         *np++ = c;
621     }
622     *np = 0;
623     addla(name);
624     return;
625 }
626
627 void
628 addla(cp)
629     Char   *cp;
630 {
631     Char    buf[BUFSIZ];
632
633     if (Strlen(cp) + (lap ? Strlen(lap) : 0) >=
634         (sizeof(labuf) - 4) / sizeof(Char)) {
635         seterror(ERR_EXPOVFL);
636         return;
637     }
638     if (lap)
639         (void) Strcpy(buf, lap);
640     (void) Strcpy(labuf, cp);
641     if (lap)
642         (void) Strcat(labuf, buf);
643     lap = labuf;
644 }
645
646 static Char lhsb[32];
647 static Char slhs[32];
648 static Char rhsb[64];
649 static int quesarg;
650
651 static void
652 getexcl(sc)
653     int    sc;
654 {
655     struct wordent *hp, *ip;
656     int     left, right, dol;
657     int c;
658
659     if (sc == 0) {
660         sc = getC(0);
661         if (sc != '{') {
662             ungetC(sc);
663             sc = 0;
664         }
665     }
666     quesarg = -1;
667     lastev = eventno;
668     hp = gethent(sc);
669     if (hp == 0)
670         return;
671     hadhist = 1;
672     dol = 0;
673     if (hp == alhistp)
674         for (ip = hp->next->next; ip != alhistt; ip = ip->next)
675             dol++;
676     else
677         for (ip = hp->next->next; ip != hp->prev; ip = ip->next)
678             dol++;
679     left = 0, right = dol;
680     if (sc == HISTSUB) {
681         ungetC('s'), unreadc(HISTSUB), c = ':';
682         goto subst;
683     }
684     c = getC(0);
685     if (!any(":^$*-%", c))
686         goto subst;
687     left = right = -1;
688     if (c == ':') {
689         c = getC(0);
690         unreadc(c);
691         if (letter(c) || c == '&') {
692             c = ':';
693             left = 0, right = dol;
694             goto subst;
695         }
696     }
697     else
698         ungetC(c);
699     if (!getsel(&left, &right, dol))
700         return;
701     c = getC(0);
702     if (c == '*')
703         ungetC(c), c = '-';
704     if (c == '-') {
705         if (!getsel(&left, &right, dol))
706             return;
707         c = getC(0);
708     }
709 subst:
710     exclc = right - left + 1;
711     while (--left >= 0)
712         hp = hp->next;
713     if (sc == HISTSUB || c == ':') {
714         do {
715             hp = getsub(hp);
716             c = getC(0);
717         } while (c == ':');
718     }
719     unreadc(c);
720     if (sc == '{') {
721         c = getC(0);
722         if (c != '}')
723             seterror(ERR_BADBANG);
724     }
725     exclnxt = hp;
726 }
727
728 static struct wordent *
729 getsub(en)
730     struct wordent *en;
731 {
732     Char *cp;
733     int     delim;
734     int c;
735     int     sc;
736     bool global;
737     Char    orhsb[sizeof(rhsb) / sizeof(Char)];
738
739     do {
740         exclnxt = 0;
741         global = 0;
742         sc = c = getC(0);
743         if (c == 'g' || c == 'a') {
744             global |= (c == 'g') ? 1 : 2;
745             sc = c = getC(0);
746         }
747         if (((c =='g') && !(global & 1)) || ((c == 'a') && !(global & 2))) {
748             global |= (c == 'g') ? 1 : 2;
749             sc = c = getC(0);
750         }
751
752         switch (c) {
753         case 'p':
754             justpr++;
755             return (en);
756
757         case 'x':
758         case 'q':
759             global |= 1;
760
761             /* fall into ... */
762
763         case 'h':
764         case 'r':
765         case 't':
766         case 'e':
767             break;
768
769         case '&':
770             if (slhs[0] == 0) {
771                 seterror(ERR_NOSUBST);
772                 return (en);
773             }
774             (void) Strcpy(lhsb, slhs);
775             break;
776
777 #ifdef notdef
778         case '~':
779             if (lhsb[0] == 0)
780                 goto badlhs;
781             break;
782 #endif
783
784         case 's':
785             delim = getC(0);
786             if (letter(delim) || Isdigit(delim) || any(" \t\n", delim)) {
787                 unreadc(delim);
788                 lhsb[0] = 0;
789                 seterror(ERR_BADSUBST);
790                 return (en);
791             }
792             cp = lhsb;
793             for (;;) {
794                 c = getC(0);
795                 if (c == '\n') {
796                     unreadc(c);
797                     break;
798                 }
799                 if (c == delim)
800                     break;
801                 if (cp > &lhsb[sizeof(lhsb) / sizeof(Char) - 2]) {
802                     lhsb[0] = 0;
803                     seterror(ERR_BADSUBST);
804                     return (en);
805                 }
806                 if (c == '\\') {
807                     c = getC(0);
808                     if (c != delim && c != '\\')
809                         *cp++ = '\\';
810                 }
811                 *cp++ = c;
812             }
813             if (cp != lhsb)
814                 *cp++ = 0;
815             else if (lhsb[0] == 0) {
816                 seterror(ERR_LHS);
817                 return (en);
818             }
819             cp = rhsb;
820             (void) Strcpy(orhsb, cp);
821             for (;;) {
822                 c = getC(0);
823                 if (c == '\n') {
824                     unreadc(c);
825                     break;
826                 }
827                 if (c == delim)
828                     break;
829 #ifdef notdef
830                 if (c == '~') {
831                     if (&cp[Strlen(orhsb)] > &rhsb[sizeof(rhsb) /
832                                                    sizeof(Char) - 2])
833                         goto toorhs;
834                     (void) Strcpy(cp, orhsb);
835                     cp = Strend(cp);
836                     continue;
837                 }
838 #endif
839                 if (cp > &rhsb[sizeof(rhsb) / sizeof(Char) - 2]) {
840                     seterror(ERR_RHSLONG);
841                     return (en);
842                 }
843                 if (c == '\\') {
844                     c = getC(0);
845                     if (c != delim /* && c != '~' */ )
846                         *cp++ = '\\';
847                 }
848                 *cp++ = c;
849             }
850             *cp++ = 0;
851             break;
852
853         default:
854             if (c == '\n')
855                 unreadc(c);
856             seterror(ERR_BADBANGMOD, c);
857             return (en);
858         }
859         (void) Strcpy(slhs, lhsb);
860         if (exclc)
861             en = dosub(sc, en, global);
862     }
863     while ((c = getC(0)) == ':');
864     unreadc(c);
865     return (en);
866 }
867
868 static struct wordent *
869 dosub(sc, en, global)
870     int     sc;
871     struct wordent *en;
872     bool global;
873 {
874     struct wordent lexi;
875     bool    didsub = 0, didone = 0;
876     struct wordent *hp = &lexi;
877     struct wordent *wdp;
878     int i = exclc;
879
880     wdp = hp;
881     while (--i >= 0) {
882         struct wordent *new =
883                 (struct wordent *) xcalloc(1, sizeof *wdp);
884
885         new->word = 0;
886         new->prev = wdp;
887         new->next = hp;
888         wdp->next = new;
889         wdp = new;
890         en = en->next;
891         if (en->word) {
892             Char *tword, *otword;
893
894             if ((global & 1) || didsub == 0) {
895                 tword = subword(en->word, sc, &didone);
896                 if (didone)
897                     didsub = 1;
898                 if (global & 2) {
899                     while (didone && tword != STRNULL) {
900                         otword = tword;
901                         tword = subword(otword, sc, &didone);
902                         if (Strcmp(tword, otword) == 0) {
903                             xfree((ptr_t) otword);
904                             break;
905                         }
906                         else
907                             xfree((ptr_t) otword);
908                     }
909                 }
910             }
911             else
912                 tword = Strsave(en->word);
913             wdp->word = tword;
914         }
915     }
916     if (didsub == 0)
917         seterror(ERR_MODFAIL);
918     hp->prev = wdp;
919     return (&enthist(-1000, &lexi, 0)->Hlex);
920 }
921
922 static Char *
923 subword(cp, type, adid)
924     Char   *cp;
925     int     type;
926     bool   *adid;
927 {
928     Char    wbuf[BUFSIZ];
929     Char *wp, *mp, *np;
930     int i;
931
932     *adid = 0;
933     switch (type) {
934
935     case 'r':
936     case 'e':
937     case 'h':
938     case 't':
939     case 'q':
940     case 'x':
941         wp = domod(cp, type);
942         if (wp == 0)
943             return (Strsave(cp));
944         *adid = 1;
945         return (wp);
946
947     default:
948         wp = wbuf;
949         i = BUFSIZ - 4;
950         for (mp = cp; *mp; mp++)
951             if (matchs(mp, lhsb)) {
952                 for (np = cp; np < mp;)
953                     *wp++ = *np++, --i;
954                 for (np = rhsb; *np; np++)
955                     switch (*np) {
956
957                     case '\\':
958                         if (np[1] == '&')
959                             np++;
960                         /* fall into ... */
961
962                     default:
963                         if (--i < 0) {
964                             seterror(ERR_SUBOVFL);
965                             return (STRNULL);
966                         }
967                         *wp++ = *np;
968                         continue;
969
970                     case '&':
971                         i -= Strlen(lhsb);
972                         if (i < 0) {
973                             seterror(ERR_SUBOVFL);
974                             return (STRNULL);
975                         }
976                         *wp = 0;
977                         (void) Strcat(wp, lhsb);
978                         wp = Strend(wp);
979                         continue;
980                     }
981                 mp += Strlen(lhsb);
982                 i -= Strlen(mp);
983                 if (i < 0) {
984                     seterror(ERR_SUBOVFL);
985                     return (STRNULL);
986                 }
987                 *wp = 0;
988                 (void) Strcat(wp, mp);
989                 *adid = 1;
990                 return (Strsave(wbuf));
991             }
992         return (Strsave(cp));
993     }
994 }
995
996 Char   *
997 domod(cp, type)
998     Char   *cp;
999     int     type;
1000 {
1001     Char *wp, *xp;
1002     int c;
1003
1004     switch (type) {
1005
1006     case 'x':
1007     case 'q':
1008         wp = Strsave(cp);
1009         for (xp = wp; (c = *xp) != '\0'; xp++)
1010             if ((c != ' ' && c != '\t') || type == 'q')
1011                 *xp |= QUOTE;
1012         return (wp);
1013
1014     case 'h':
1015     case 't':
1016         if (!any(short2str(cp), '/'))
1017             return (type == 't' ? Strsave(cp) : 0);
1018         wp = Strend(cp);
1019         while (*--wp != '/')
1020             continue;
1021         if (type == 'h')
1022             xp = Strsave(cp), xp[wp - cp] = 0;
1023         else
1024             xp = Strsave(wp + 1);
1025         return (xp);
1026
1027     case 'e':
1028     case 'r':
1029         wp = Strend(cp);
1030         for (wp--; wp >= cp && *wp != '/'; wp--)
1031             if (*wp == '.') {
1032                 if (type == 'e')
1033                     xp = Strsave(wp + 1);
1034                 else
1035                     xp = Strsave(cp), xp[wp - cp] = 0;
1036                 return (xp);
1037             }
1038         return (Strsave(type == 'e' ? STRNULL : cp));
1039     default:
1040         break;
1041     }
1042     return (0);
1043 }
1044
1045 static int
1046 matchs(str, pat)
1047     Char *str, *pat;
1048 {
1049     while (*str && *pat && *str == *pat)
1050         str++, pat++;
1051     return (*pat == 0);
1052 }
1053
1054 static int
1055 getsel(al, ar, dol)
1056     int *al, *ar;
1057     int     dol;
1058 {
1059     int c = getC(0);
1060     int i;
1061     bool    first = *al < 0;
1062
1063     switch (c) {
1064
1065     case '%':
1066         if (quesarg == -1) {
1067             seterror(ERR_BADBANGARG);
1068             return (0);
1069         }
1070         if (*al < 0)
1071             *al = quesarg;
1072         *ar = quesarg;
1073         break;
1074
1075     case '-':
1076         if (*al < 0) {
1077             *al = 0;
1078             *ar = dol - 1;
1079             unreadc(c);
1080         }
1081         return (1);
1082
1083     case '^':
1084         if (*al < 0)
1085             *al = 1;
1086         *ar = 1;
1087         break;
1088
1089     case '$':
1090         if (*al < 0)
1091             *al = dol;
1092         *ar = dol;
1093         break;
1094
1095     case '*':
1096         if (*al < 0)
1097             *al = 1;
1098         *ar = dol;
1099         if (*ar < *al) {
1100             *ar = 0;
1101             *al = 1;
1102             return (1);
1103         }
1104         break;
1105
1106     default:
1107         if (Isdigit(c)) {
1108             i = 0;
1109             while (Isdigit(c)) {
1110                 i = i * 10 + c - '0';
1111                 c = getC(0);
1112             }
1113             if (i < 0)
1114                 i = dol + 1;
1115             if (*al < 0)
1116                 *al = i;
1117             *ar = i;
1118         }
1119         else if (*al < 0)
1120             *al = 0, *ar = dol;
1121         else
1122             *ar = dol - 1;
1123         unreadc(c);
1124         break;
1125     }
1126     if (first) {
1127         c = getC(0);
1128         unreadc(c);
1129         if (any("-$*", c))
1130             return (1);
1131     }
1132     if (*al > *ar || *ar > dol) {
1133         seterror(ERR_BADBANGARG);
1134         return (0);
1135     }
1136     return (1);
1137
1138 }
1139
1140 static struct wordent *
1141 gethent(sc)
1142     int     sc;
1143 {
1144     struct Hist *hp;
1145     Char *np;
1146     int c;
1147     int     event;
1148     bool    back = 0;
1149
1150     c = sc == HISTSUB ? HIST : getC(0);
1151     if (c == HIST) {
1152         if (alhistp)
1153             return (alhistp);
1154         event = eventno;
1155     }
1156     else
1157         switch (c) {
1158
1159         case ':':
1160         case '^':
1161         case '$':
1162         case '*':
1163         case '%':
1164             ungetC(c);
1165             if (lastev == eventno && alhistp)
1166                 return (alhistp);
1167             event = lastev;
1168             break;
1169
1170         case '#':               /* !# is command being typed in (mrh) */
1171             if (--hleft == 0) {
1172                 seterror(ERR_HISTLOOP);
1173                 return (0);
1174             }
1175             else
1176                 return (&paraml);
1177             /* NOTREACHED */
1178
1179         case '-':
1180             back = 1;
1181             c = getC(0);
1182             /* FALLTHROUGH */
1183
1184         default:
1185             if (any("(=~", c)) {
1186                 unreadc(c);
1187                 ungetC(HIST);
1188                 return (0);
1189             }
1190             np = lhsb;
1191             event = 0;
1192             while (!cmap(c, _ESC | _META | _QF | _QB) && !any("^$*-%{}:", c)) {
1193                 if (event != -1 && Isdigit(c))
1194                     event = event * 10 + c - '0';
1195                 else
1196                     event = -1;
1197                 if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
1198                     *np++ = c;
1199                 c = getC(0);
1200             }
1201             unreadc(c);
1202             if (np == lhsb) {
1203                 ungetC(HIST);
1204                 return (0);
1205             }
1206             *np++ = 0;
1207             if (event != -1) {
1208                 /*
1209                  * History had only digits
1210                  */
1211                 if (back)
1212                     event = eventno + (alhistp == 0) - (event ? event : 0);
1213                 break;
1214             }
1215             hp = findev(lhsb, 0);
1216             if (hp)
1217                 lastev = hp->Hnum;
1218             return (&hp->Hlex);
1219
1220         case '?':
1221             np = lhsb;
1222             for (;;) {
1223                 c = getC(0);
1224                 if (c == '\n') {
1225                     unreadc(c);
1226                     break;
1227                 }
1228                 if (c == '?')
1229                     break;
1230                 if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
1231                     *np++ = c;
1232             }
1233             if (np == lhsb) {
1234                 if (lhsb[0] == 0) {
1235                     seterror(ERR_NOSEARCH);
1236                     return (0);
1237                 }
1238             }
1239             else
1240                 *np++ = 0;
1241             hp = findev(lhsb, 1);
1242             if (hp)
1243                 lastev = hp->Hnum;
1244             return (&hp->Hlex);
1245         }
1246
1247     for (hp = Histlist.Hnext; hp; hp = hp->Hnext)
1248         if (hp->Hnum == event) {
1249             hp->Href = eventno;
1250             lastev = hp->Hnum;
1251             return (&hp->Hlex);
1252         }
1253     np = putn(event);
1254     seterror(ERR_NOEVENT, vis_str(np));
1255     return (0);
1256 }
1257
1258 static struct Hist *
1259 findev(cp, anyarg)
1260     Char   *cp;
1261     bool    anyarg;
1262 {
1263     struct Hist *hp;
1264
1265     for (hp = Histlist.Hnext; hp; hp = hp->Hnext) {
1266         Char   *dp;
1267         Char *p, *q;
1268         struct wordent *lp = hp->Hlex.next;
1269         int     argno = 0;
1270
1271         /*
1272          * The entries added by alias substitution don't have a newline but do
1273          * have a negative event number. Savehist() trims off these entries,
1274          * but it happens before alias expansion, too early to delete those
1275          * from the previous command.
1276          */
1277         if (hp->Hnum < 0)
1278             continue;
1279         if (lp->word[0] == '\n')
1280             continue;
1281         if (!anyarg) {
1282             p = cp;
1283             q = lp->word;
1284             do
1285                 if (!*p)
1286                     return (hp);
1287             while (*p++ == *q++);
1288             continue;
1289         }
1290         do {
1291             for (dp = lp->word; *dp; dp++) {
1292                 p = cp;
1293                 q = dp;
1294                 do
1295                     if (!*p) {
1296                         quesarg = argno;
1297                         return (hp);
1298                     }
1299                 while (*p++ == *q++);
1300             }
1301             lp = lp->next;
1302             argno++;
1303         } while (lp->word[0] != '\n');
1304     }
1305     seterror(ERR_NOEVENT, vis_str(cp));
1306     return (0);
1307 }
1308
1309
1310 static void
1311 setexclp(cp)
1312     Char *cp;
1313 {
1314     if (cp && cp[0] == '\n')
1315         return;
1316     exclp = cp;
1317 }
1318
1319 void
1320 unreadc(c)
1321     int    c;
1322 {
1323     peekread = c;
1324 }
1325
1326 int
1327 readc(wanteof)
1328     bool    wanteof;
1329 {
1330     int c;
1331     static  int sincereal;
1332
1333     aret = F_SEEK;
1334     if ((c = peekread) != '\0') {
1335         peekread = 0;
1336         return (c);
1337     }
1338 top:
1339     aret = F_SEEK;
1340     if (alvecp) {
1341         aret = A_SEEK;
1342         if ((c = *alvecp++) != '\0')
1343             return (c);
1344         if (alvec && *alvec) {
1345                 alvecp = *alvec++;
1346                 return (' ');
1347         }
1348         else {
1349             aret = F_SEEK;
1350             alvecp = NULL;
1351             return('\n');
1352         }
1353     }
1354     if (alvec) {
1355         if ((alvecp = *alvec) != '\0') {
1356             alvec++;
1357             goto top;
1358         }
1359         /* Infinite source! */
1360         return ('\n');
1361     }
1362     if (evalp) {
1363         aret = E_SEEK;
1364         if ((c = *evalp++) != '\0')
1365             return (c);
1366         if (evalvec && *evalvec) {
1367             evalp = *evalvec++;
1368             return (' ');
1369         }
1370         aret = F_SEEK;
1371         evalp = 0;
1372     }
1373     if (evalvec) {
1374         if (evalvec == (Char **) 1) {
1375             doneinp = 1;
1376             reset();
1377         }
1378         if ((evalp = *evalvec) != '\0') {
1379             evalvec++;
1380             goto top;
1381         }
1382         evalvec = (Char **) 1;
1383         return ('\n');
1384     }
1385     do {
1386         if (arginp == (Char *) 1 || onelflg == 1) {
1387             if (wanteof)
1388                 return (-1);
1389             exitstat();
1390         }
1391         if (arginp) {
1392             if ((c = *arginp++) == 0) {
1393                 arginp = (Char *) 1;
1394                 return ('\n');
1395             }
1396             return (c);
1397         }
1398 reread:
1399         c = bgetc();
1400         if (c < 0) {
1401             struct termios tty;
1402             if (wanteof)
1403                 return (-1);
1404             /* was isatty but raw with ignoreeof yields problems */
1405             if (tcgetattr(SHIN, &tty) == 0 && (tty.c_lflag & ICANON))
1406             {
1407                 /* was 'short' for FILEC */
1408                 int     ctpgrp;
1409
1410                 if (++sincereal > 25)
1411                     goto oops;
1412                 if (tpgrp != -1 &&
1413                     (ctpgrp = tcgetpgrp(FSHTTY)) != -1 &&
1414                     tpgrp != ctpgrp) {
1415                     (void) tcsetpgrp(FSHTTY, tpgrp);
1416                     (void) killpg((pid_t) ctpgrp, SIGHUP);
1417                     (void) fprintf(csherr, "Reset tty pgrp from %d to %d\n",
1418                                    ctpgrp, tpgrp);
1419                     goto reread;
1420                 }
1421                 if (adrof(STRignoreeof)) {
1422                     if (loginsh)
1423                         (void) fprintf(csherr,"\nUse \"logout\" to logout.\n");
1424                     else
1425                         (void) fprintf(csherr,"\nUse \"exit\" to leave csh.\n");
1426                     reset();
1427                 }
1428                 if (chkstop == 0)
1429                     panystop(1);
1430             }
1431     oops:
1432             doneinp = 1;
1433             reset();
1434         }
1435         sincereal = 0;
1436         if (c == '\n' && onelflg)
1437             onelflg--;
1438     } while (c == 0);
1439     return (c);
1440 }
1441
1442 static int
1443 bgetc()
1444 {
1445     int buf, off, c;
1446
1447 #ifdef FILEC
1448     int numleft = 0, roomleft;
1449     Char    ttyline[BUFSIZ];
1450 #endif
1451     char    tbuf[BUFSIZ + 1];
1452
1453     if (cantell) {
1454         if (fseekp < fbobp || fseekp > feobp) {
1455             fbobp = feobp = fseekp;
1456             (void) lseek(SHIN, fseekp, L_SET);
1457         }
1458         if (fseekp == feobp) {
1459             int     i;
1460
1461             fbobp = feobp;
1462             do
1463                 c = read(SHIN, tbuf, BUFSIZ);
1464             while (c < 0 && errno == EINTR);
1465             if (c <= 0)
1466                 return (-1);
1467             for (i = 0; i < c; i++)
1468                 fbuf[0][i] = (unsigned char) tbuf[i];
1469             feobp += c;
1470         }
1471         c = fbuf[0][fseekp - fbobp];
1472         fseekp++;
1473         return (c);
1474     }
1475
1476 again:
1477     buf = (int) fseekp / BUFSIZ;
1478     if (buf >= fblocks) {
1479         Char **nfbuf =
1480         (Char **) xcalloc((size_t) (fblocks + 2),
1481                           sizeof(Char **));
1482
1483         if (fbuf) {
1484             (void) blkcpy(nfbuf, fbuf);
1485             xfree((ptr_t) fbuf);
1486         }
1487         fbuf = nfbuf;
1488         fbuf[fblocks] = (Char *) xcalloc(BUFSIZ, sizeof(Char));
1489         fblocks++;
1490         if (!intty)
1491             goto again;
1492     }
1493     if (fseekp >= feobp) {
1494         buf = (int) feobp / BUFSIZ;
1495         off = (int) feobp % BUFSIZ;
1496         roomleft = BUFSIZ - off;
1497
1498 #ifdef FILEC
1499         roomleft = BUFSIZ - off;
1500         for (;;) {
1501             if (filec && intty) {
1502                 c = numleft ? numleft : tenex(ttyline, BUFSIZ);
1503                 if (c > roomleft) {
1504                     /* start with fresh buffer */
1505                     feobp = fseekp = fblocks * BUFSIZ;
1506                     numleft = c;
1507                     goto again;
1508                 }
1509                 if (c > 0)
1510                     memmove(fbuf[buf] + off, ttyline, c * sizeof(Char));
1511                 numleft = 0;
1512             }
1513             else {
1514 #endif
1515                 c = read(SHIN, tbuf, roomleft);
1516                 if (c > 0) {
1517                     int     i;
1518                     Char   *ptr = fbuf[buf] + off;
1519
1520                     for (i = 0; i < c; i++)
1521                         ptr[i] = (unsigned char) tbuf[i];
1522                 }
1523 #ifdef FILEC
1524             }
1525 #endif
1526             if (c >= 0)
1527                 break;
1528             if (errno == EWOULDBLOCK) {
1529                 int     off = 0;
1530
1531                 (void) ioctl(SHIN, FIONBIO, (ioctl_t) & off);
1532             }
1533             else if (errno != EINTR)
1534                 break;
1535         }
1536         if (c <= 0)
1537             return (-1);
1538         feobp += c;
1539 #ifndef FILEC
1540         goto again;
1541 #else
1542         if (filec && !intty)
1543             goto again;
1544 #endif
1545     }
1546     c = fbuf[buf][(int) fseekp % BUFSIZ];
1547     fseekp++;
1548     return (c);
1549 }
1550
1551 static void
1552 bfree()
1553 {
1554     int sb, i;
1555
1556     if (cantell)
1557         return;
1558     if (whyles)
1559         return;
1560     sb = (int) (fseekp - 1) / BUFSIZ;
1561     if (sb > 0) {
1562         for (i = 0; i < sb; i++)
1563             xfree((ptr_t) fbuf[i]);
1564         (void) blkcpy(fbuf, &fbuf[sb]);
1565         fseekp -= BUFSIZ * sb;
1566         feobp -= BUFSIZ * sb;
1567         fblocks -= sb;
1568     }
1569 }
1570
1571 void
1572 bseek(l)
1573     struct Ain   *l;
1574 {
1575     switch (aret = l->type) {
1576     case E_SEEK:
1577         evalvec = l->a_seek;
1578         evalp = l->c_seek;
1579         return;
1580     case A_SEEK:
1581         alvec = l->a_seek;
1582         alvecp = l->c_seek;
1583         return;
1584     case F_SEEK:
1585         fseekp = l->f_seek;
1586         return;
1587     default:
1588         (void) fprintf(csherr, "Bad seek type %d\n", aret);
1589         abort();
1590     }
1591 }
1592
1593 void
1594 btell(l)
1595     struct Ain *l;
1596 {
1597     switch (l->type = aret) {
1598     case E_SEEK:
1599         l->a_seek = evalvec;
1600         l->c_seek = evalp;
1601         return;
1602     case A_SEEK:
1603         l->a_seek = alvec;
1604         l->c_seek = alvecp;
1605         return;
1606     case F_SEEK:
1607         l->f_seek = fseekp;
1608         l->a_seek = NULL;
1609         return;
1610     default:
1611         (void) fprintf(csherr, "Bad seek type %d\n", aret);
1612         abort();
1613     }
1614 }
1615
1616 void
1617 btoeof()
1618 {
1619     (void) lseek(SHIN, (off_t) 0, L_XTND);
1620     aret = F_SEEK;
1621     fseekp = feobp;
1622     alvec = NULL;
1623     alvecp = NULL;
1624     evalvec = NULL;
1625     evalp = NULL;
1626     wfree();
1627     bfree();
1628 }
1629
1630 void
1631 settell()
1632 {
1633     cantell = 0;
1634     if (arginp || onelflg || intty)
1635         return;
1636     if (lseek(SHIN, (off_t) 0, L_INCR) < 0 || errno == ESPIPE)
1637         return;
1638     fbuf = (Char **) xcalloc(2, sizeof(Char **));
1639     fblocks = 1;
1640     fbuf[0] = (Char *) xcalloc(BUFSIZ, sizeof(Char));
1641     fseekp = fbobp = feobp = lseek(SHIN, (off_t) 0, L_INCR);
1642     cantell = 1;
1643 }