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 (¶ml);
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