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