Sync some manuals from bin & sbin with NetBSD-8
[minix.git] / bin / csh / lex.c
blob8c1bca8180c7f9ea00bcef88f4cfa71752cb7cf5
1 /* $NetBSD: lex.c,v 1.31 2013/08/06 05:42:43 christos Exp $ */
3 /*-
4 * Copyright (c) 1980, 1991, 1993
5 * The Regents of the University of California. All rights reserved.
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.
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.
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.31 2013/08/06 05:42:43 christos Exp $");
38 #endif
39 #endif /* not lint */
41 #include <sys/ioctl.h>
42 #include <sys/types.h>
44 #include <errno.h>
45 #include <stdarg.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <termios.h>
49 #include <unistd.h>
51 #include "csh.h"
52 #include "extern.h"
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.
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);
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.
89 * Getdol invokes history substitution, hence the extra peek, peekd,
90 * which it can ungetD to be before history substitutions.
92 static int peekc = 0, peekd = 0;
93 static int peekread = 0;
95 /* (Tail of) current word from ! subst */
96 static Char *exclp = NULL;
98 /* The rest of the ! subst words */
99 static struct wordent *exclnxt = NULL;
101 /* Count of remaining words in ! subst */
102 static int exclc = 0;
104 /* "Globp" for alias resubstitution */
105 Char **alvec, *alvecp;
106 int aret = F_SEEK;
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.
115 static Char labuf[BUFSIZE];
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.
124 static int hadhist = 0;
127 * Avoid alias expansion recursion via \!#
129 int hleft;
131 static int getCtmp;
133 #define getC(f) ((getCtmp = peekc) ? (peekc = 0, getCtmp) : getC1(f))
134 #define ungetC(c) peekc = c
135 #define ungetD(c) peekd = c
138 lex(struct wordent *hp)
140 struct wordent *wdp;
141 int c;
143 btell(&lineloc);
144 hp->next = hp->prev = hp;
145 hp->word = STRNULL;
146 hadhist = 0;
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;
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.
160 do {
161 struct wordent *new;
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);
175 void
176 prlex(FILE *fp, struct wordent *sp0)
178 struct wordent *sp;
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);
191 #ifdef EDIT
193 sprlex(char **s, struct wordent *sp0)
195 struct wordent *sp;
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;
210 free(os);
211 os = *s;
213 sp = sp->next;
214 if (sp == sp0)
215 break;
217 return 0;
219 #endif
221 void
222 copylex(struct wordent *hp, struct wordent *fp)
224 struct wordent *wdp;
226 wdp = hp;
227 fp = fp->next;
228 do {
229 struct wordent *new;
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;
242 void
243 freelex(struct wordent *vp)
245 struct wordent *fp;
247 while (vp->next != vp) {
248 fp = vp->next;
249 vp->next = fp->next;
250 xfree((ptr_t) fp->word);
251 xfree((ptr_t) fp);
253 vp->prev = vp;
256 static Char *
257 word(void)
259 Char wbuf[BUFSIZE], *wp;
260 int i, c, c1;
261 int dolflg;
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;
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 */
294 case ';':
295 case '(':
296 case ')':
297 case '\n':
298 *wp++ = (Char)c;
299 goto ret;
301 case '\\':
302 c = getC(0);
303 if (c == '\n') {
304 if (onelflg == 1)
305 onelflg = 2;
306 goto loop;
308 if (c != HIST)
309 *wp++ = '\\', --i;
310 c |= QUOTE;
311 break;
313 c1 = 0;
314 dolflg = DOALL;
315 for (;;) {
316 if (c1) {
317 if (c == c1) {
318 c1 = 0;
319 dolflg = DOALL;
321 else if (c == '\\') {
322 c = getC(0);
323 if (c == HIST)
324 c |= QUOTE;
325 else {
326 if (c == '\n')
328 * if (c1 == '`') c = ' '; else
330 c |= QUOTE;
331 ungetC(c);
332 c = '\\';
335 else if (c == '\n') {
336 seterror(ERR_UNMATCHED, c1);
337 ungetC(c);
338 break;
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;
349 if (c != HIST)
350 *wp++ = '\\', --i;
351 c |= QUOTE;
353 else if (cmap(c, _QF | _QB)) { /* '"` */
354 c1 = c;
355 dolflg = c == '"' ? DOALL : DOEXCL;
357 else if (c != '#' || !intty) {
358 ungetC(c);
359 break;
362 if (--i > 0) {
363 *wp++ = (Char)c;
364 c = getC(dolflg);
366 else {
367 seterror(ERR_WTOOLONG);
368 wp = &wbuf[1];
369 break;
372 ret:
373 *wp = 0;
374 return (Strsave(wbuf));
377 static int
378 getC1(int flag)
380 int c;
382 for (;;) {
383 if ((c = peekc) != '\0') {
384 peekc = 0;
385 return (c);
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);
396 if ((c = peekd) != '\0') {
397 peekd = 0;
398 return (c);
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 (' ');
408 exclp = 0;
409 exclnxt = 0;
411 if (exclnxt) {
412 exclnxt = exclnxt->next;
413 if (--exclc < 0)
414 exclnxt = 0;
415 else
416 setexclp(exclnxt->word);
417 continue;
419 c = readc(0);
420 if (c == '$' && (flag & DODOL)) {
421 getdol();
422 continue;
424 if (c == HIST && (flag & DOEXCL)) {
425 getexcl(0);
426 continue;
428 break;
430 return (c);
433 static void
434 getdol(void)
436 Char name[4*MAXVARLEN+1], *ep, *np;
437 int c, sc;
438 int special, toolong;
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;
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;
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;
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;
512 else {
513 *np = 0;
514 seterror(ERR_VARILL);
515 addla(name);
516 return;
518 if (toolong) {
519 seterror(ERR_VARTOOLONG);
520 *np = 0;
521 addla(name);
522 return;
524 break;
526 if (c == '[') {
527 *np++ = (Char)c;
529 * Name up to here is a max of MAXVARLEN + 8.
531 ep = &np[2 * MAXVARLEN + 8];
532 do {
534 * Michael Greim: Allow $ expansion to take place in selector
535 * expressions. (limits the number of characters returned)
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;
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;
555 c = getC(DOEXCL);
558 * Name up to here is a max of 2 * MAXVARLEN + 8.
560 if (c == ':') {
562 * if the :g modifier is followed by a newline, then error right away!
563 * -strike
565 int amodflag, gmodflag;
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);
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);
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;
592 if (!delim || letter(delim)
593 || Isdigit(delim) || any(" \t\n", delim)) {
594 seterror(ERR_BADSUBST);
595 break;
597 while ((c = getC(0)) != -1) {
598 *np++ = (Char)c;
599 if(c == delim) delimcnt--;
600 if(!delimcnt) break;
602 if(delimcnt) {
603 seterror(ERR_BADSUBST);
604 break;
606 c = 's';
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;
617 while ((c = getC(DOEXCL)) == ':');
618 ungetD(c);
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;
631 *np++ = (Char)c;
633 *np = 0;
634 addla(name);
635 return;
638 void
639 addla(Char *cp)
641 Char buf[BUFSIZE];
643 if (Strlen(cp) + (lap ? Strlen(lap) : 0) >=
644 (sizeof(labuf) - 4) / sizeof(Char)) {
645 seterror(ERR_EXPOVFL);
646 return;
648 if (lap)
649 (void)Strcpy(buf, lap);
650 (void)Strcpy(labuf, cp);
651 if (lap)
652 (void)Strcat(labuf, buf);
653 lap = labuf;
656 static Char lhsb[32];
657 static Char slhs[32];
658 static Char rhsb[64];
659 static int quesarg;
661 static void
662 getexcl(int sc)
664 struct wordent *hp, *ip;
665 int c, dol, left, right;
667 if (sc == 0) {
668 sc = getC(0);
669 if (sc != '{') {
670 ungetC(sc);
671 sc = 0;
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;
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;
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);
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 == ':');
727 unreadc(c);
728 if (sc == '{') {
729 c = getC(0);
730 if (c != '}')
731 seterror(ERR_BADBANG);
733 exclnxt = hp;
736 static struct wordent *
737 getsub(struct wordent *en)
739 Char orhsb[sizeof(rhsb) / sizeof(Char)];
740 Char *cp;
741 int c, delim, sc;
742 int global;
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);
752 if (((c =='g') && !(global & 1)) || ((c == 'a') && !(global & 2))) {
753 global |= (c == 'g') ? 1 : 2;
754 sc = c = getC(0);
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);
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);
791 cp = lhsb;
792 for (;;) {
793 c = getC(0);
794 if (c == '\n') {
795 unreadc(c);
796 break;
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);
805 if (c == '\\') {
806 c = getC(0);
807 if (c != delim && c != '\\')
808 *cp++ = '\\';
810 *cp++ = (Char)c;
812 if (cp != lhsb)
813 *cp++ = 0;
814 else if (lhsb[0] == 0) {
815 seterror(ERR_LHS);
816 return (en);
818 cp = rhsb;
819 (void)Strcpy(orhsb, cp);
820 for (;;) {
821 c = getC(0);
822 if (c == '\n') {
823 unreadc(c);
824 break;
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;
837 #endif
838 if (cp > &rhsb[sizeof(rhsb) / sizeof(Char) - 2]) {
839 seterror(ERR_RHSLONG);
840 return (en);
842 if (c == '\\') {
843 c = getC(0);
844 if (c != delim /* && c != '~' */ )
845 *cp++ = '\\';
847 *cp++ = (Char)c;
849 *cp++ = 0;
850 break;
851 default:
852 if (c == '\n')
853 unreadc(c);
854 seterror(ERR_BADBANGMOD, c);
855 return (en);
857 (void)Strcpy(slhs, lhsb);
858 if (exclc)
859 en = dosub(sc, en, global);
861 while ((c = getC(0)) == ':');
862 unreadc(c);
863 return (en);
866 static struct wordent *
867 dosub(int sc, struct wordent *en, int global)
869 struct wordent lexi, *hp, *wdp;
870 int i;
871 int didone, didsub;
873 didone = 0;
874 didsub = 0;
875 i = exclc;
876 hp = &lexi;
878 wdp = hp;
879 while (--i >= 0) {
880 struct wordent *new = (struct wordent *)xcalloc(1, sizeof *wdp);
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;
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 xfree((ptr_t) otword);
901 break;
903 else
904 xfree((ptr_t)otword);
908 else
909 tword = Strsave(en->word);
910 wdp->word = tword;
913 if (didsub == 0)
914 seterror(ERR_MODFAIL);
915 hp->prev = wdp;
916 return (&enthist(-1000, &lexi, 0)->Hlex);
919 static Char *
920 subword(Char *cp, int type, int *adid)
922 Char wbuf[BUFSIZE];
923 Char *mp, *np, *wp;
924 ssize_t i;
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);
957 *wp++ = *np;
958 continue;
959 case '&':
960 i -= (ssize_t)Strlen(lhsb);
961 if (i < 0) {
962 seterror(ERR_SUBOVFL);
963 return (STRNULL);
965 *wp = 0;
966 (void) Strcat(wp, lhsb);
967 wp = Strend(wp);
968 continue;
970 mp += Strlen(lhsb);
971 i -= (ssize_t)Strlen(mp);
972 if (i < 0) {
973 seterror(ERR_SUBOVFL);
974 return (STRNULL);
976 *wp = 0;
977 (void) Strcat(wp, mp);
978 *adid = 1;
979 return (Strsave(wbuf));
981 return (Strsave(cp));
985 Char *
986 domod(Char *cp, int type)
988 Char *wp, *xp;
989 int c;
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 case 'h':
1000 case 't':
1001 if (!any(short2str(cp), '/'))
1002 return (type == 't' ? Strsave(cp) : 0);
1003 wp = Strend(cp);
1004 while (*--wp != '/')
1005 continue;
1006 if (type == 'h')
1007 xp = Strsave(cp), xp[wp - cp] = 0;
1008 else
1009 xp = Strsave(wp + 1);
1010 return (xp);
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);
1022 return (Strsave(type == 'e' ? STRNULL : cp));
1023 default:
1024 break;
1026 return (0);
1029 static int
1030 matchs(Char *str, Char *pat)
1032 while (*str && *pat && *str == *pat)
1033 str++, pat++;
1034 return (*pat == 0);
1037 static int
1038 getsel(int *al, int *ar, int dol)
1040 int c, i;
1041 int first;
1043 c = getC(0);
1044 first = *al < 0;
1046 switch (c) {
1047 case '%':
1048 if (quesarg == -1) {
1049 seterror(ERR_BADBANGARG);
1050 return (0);
1052 if (*al < 0)
1053 *al = quesarg;
1054 *ar = quesarg;
1055 break;
1056 case '-':
1057 if (*al < 0) {
1058 *al = 0;
1059 *ar = dol - 1;
1060 unreadc(c);
1062 return (1);
1063 case '^':
1064 if (*al < 0)
1065 *al = 1;
1066 *ar = 1;
1067 break;
1068 case '$':
1069 if (*al < 0)
1070 *al = dol;
1071 *ar = dol;
1072 break;
1073 case '*':
1074 if (*al < 0)
1075 *al = 1;
1076 *ar = dol;
1077 if (*ar < *al) {
1078 *ar = 0;
1079 *al = 1;
1080 return (1);
1082 break;
1083 default:
1084 if (Isdigit(c)) {
1085 i = 0;
1086 while (Isdigit(c)) {
1087 i = i * 10 + c - '0';
1088 c = getC(0);
1090 if (i < 0)
1091 i = dol + 1;
1092 if (*al < 0)
1093 *al = i;
1094 *ar = i;
1096 else if (*al < 0)
1097 *al = 0, *ar = dol;
1098 else
1099 *ar = dol - 1;
1100 unreadc(c);
1101 break;
1103 if (first) {
1104 c = getC(0);
1105 unreadc(c);
1106 if (any("-$*", c))
1107 return (1);
1109 if (*al > *ar || *ar > dol) {
1110 seterror(ERR_BADBANGARG);
1111 return (0);
1113 return (1);
1117 static struct wordent *
1118 gethent(int sc)
1120 struct Hist *hp;
1121 Char *np;
1122 char *str;
1123 int c, event;
1124 int back;
1126 back = 0;
1127 c = sc == HISTSUB ? HIST : getC(0);
1128 if (c == HIST) {
1129 if (alhistp)
1130 return (alhistp);
1131 event = eventno;
1133 else
1134 switch (c) {
1135 case ':':
1136 case '^':
1137 case '$':
1138 case '*':
1139 case '%':
1140 ungetC(c);
1141 if (lastev == eventno && alhistp)
1142 return (alhistp);
1143 event = lastev;
1144 break;
1145 case '#': /* !# is command being typed in (mrh) */
1146 if (--hleft == 0) {
1147 seterror(ERR_HISTLOOP);
1148 return (0);
1150 else
1151 return (&paraml);
1152 /* NOTREACHED */
1153 case '-':
1154 back = 1;
1155 c = getC(0);
1156 /* FALLTHROUGH */
1157 default:
1158 if (any("(=~", c)) {
1159 unreadc(c);
1160 ungetC(HIST);
1161 return (0);
1163 np = lhsb;
1164 event = 0;
1165 while (!cmap(c, _ESC | _META | _QF | _QB) && !any("${}:", c)) {
1166 if (event != -1 && Isdigit(c))
1167 event = event * 10 + c - '0';
1168 else
1169 event = -1;
1170 if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
1171 *np++ = (Char)c;
1172 c = getC(0);
1174 unreadc(c);
1175 if (np == lhsb) {
1176 ungetC(HIST);
1177 return (0);
1179 *np++ = 0;
1180 if (event != -1) {
1182 * History had only digits
1184 if (back)
1185 event = eventno + (alhistp == 0) - (event ? event : 0);
1186 break;
1188 hp = findev(lhsb, 0);
1189 if (hp)
1190 lastev = hp->Hnum;
1191 return (&hp->Hlex);
1192 case '?':
1193 np = lhsb;
1194 for (;;) {
1195 c = getC(0);
1196 if (c == '\n') {
1197 unreadc(c);
1198 break;
1200 if (c == '?')
1201 break;
1202 if (np < &lhsb[sizeof(lhsb) / sizeof(Char) - 2])
1203 *np++ = (Char)c;
1205 if (np == lhsb) {
1206 if (lhsb[0] == 0) {
1207 seterror(ERR_NOSEARCH);
1208 return (0);
1211 else
1212 *np++ = 0;
1213 hp = findev(lhsb, 1);
1214 if (hp)
1215 lastev = hp->Hnum;
1216 return (&hp->Hlex);
1219 for (hp = Histlist.Hnext; hp; hp = hp->Hnext)
1220 if (hp->Hnum == event) {
1221 hp->Href = eventno;
1222 lastev = hp->Hnum;
1223 return (&hp->Hlex);
1225 np = putn(event);
1226 str = vis_str(np);
1227 xfree((ptr_t) np);
1228 seterror(ERR_NOEVENT, str);
1229 return (0);
1232 static struct Hist *
1233 findev(Char *cp, int anyarg)
1235 struct Hist *hp;
1237 for (hp = Histlist.Hnext; hp; hp = hp->Hnext) {
1238 Char *dp, *p, *q;
1239 struct wordent *lp;
1240 int argno;
1242 lp = hp->Hlex.next;
1243 argno = 0;
1246 * The entries added by alias substitution don't have a newline but do
1247 * have a negative event number. Savehist() trims off these entries,
1248 * but it happens before alias expansion, too early to delete those
1249 * from the previous command.
1251 if (hp->Hnum < 0)
1252 continue;
1253 if (lp->word[0] == '\n')
1254 continue;
1255 if (!anyarg) {
1256 p = cp;
1257 q = lp->word;
1259 if (!*p)
1260 return (hp);
1261 while (*p++ == *q++);
1262 continue;
1264 do {
1265 for (dp = lp->word; *dp; dp++) {
1266 p = cp;
1267 q = dp;
1269 if (!*p) {
1270 quesarg = argno;
1271 return (hp);
1273 while (*p++ == *q++);
1275 lp = lp->next;
1276 argno++;
1277 } while (lp->word[0] != '\n');
1279 seterror(ERR_NOEVENT, vis_str(cp));
1280 return (0);
1284 static void
1285 setexclp(Char *cp)
1287 if (cp && cp[0] == '\n')
1288 return;
1289 exclp = cp;
1292 void
1293 unreadc(int c)
1295 peekread = c;
1299 readc(int wanteof)
1301 static int sincereal;
1302 int c;
1304 aret = F_SEEK;
1305 if ((c = peekread) != '\0') {
1306 peekread = 0;
1307 return (c);
1309 top:
1310 aret = F_SEEK;
1311 if (alvecp) {
1312 aret = A_SEEK;
1313 if ((c = *alvecp++) != '\0')
1314 return (c);
1315 if (alvec && *alvec) {
1316 alvecp = *alvec++;
1317 return (' ');
1319 else {
1320 aret = F_SEEK;
1321 alvecp = NULL;
1322 return('\n');
1325 if (alvec) {
1326 if ((alvecp = *alvec) != '\0') {
1327 alvec++;
1328 goto top;
1330 /* Infinite source! */
1331 return ('\n');
1333 if (evalp) {
1334 aret = E_SEEK;
1335 if ((c = *evalp++) != '\0')
1336 return (c);
1337 if (evalvec && *evalvec) {
1338 evalp = *evalvec++;
1339 return (' ');
1341 aret = F_SEEK;
1342 evalp = 0;
1344 if (evalvec) {
1345 if (evalvec == (Char **) 1) {
1346 doneinp = 1;
1347 reset();
1349 if ((evalp = *evalvec) != '\0') {
1350 evalvec++;
1351 goto top;
1353 evalvec = (Char **) 1;
1354 return ('\n');
1356 do {
1357 if (arginp == (Char *) 1 || onelflg == 1) {
1358 if (wanteof)
1359 return (-1);
1360 exitstat();
1362 if (arginp) {
1363 if ((c = *arginp++) == 0) {
1364 arginp = (Char *) 1;
1365 return ('\n');
1367 return (c);
1369 reread:
1370 c = bgetc();
1371 if (c < 0) {
1372 struct termios tty;
1373 if (wanteof)
1374 return (-1);
1375 /* was isatty but raw with ignoreeof yields problems */
1376 if (tcgetattr(SHIN, &tty) == 0 && (tty.c_lflag & ICANON))
1378 /* was 'short' for FILEC */
1379 pid_t ctpgrp;
1381 if (++sincereal > 25)
1382 goto oops;
1383 if (tpgrp != -1 &&
1384 (ctpgrp = tcgetpgrp(FSHTTY)) != -1 &&
1385 tpgrp != ctpgrp) {
1386 (void)tcsetpgrp(FSHTTY, tpgrp);
1387 (void)kill(-ctpgrp, SIGHUP);
1388 (void)fprintf(csherr, "Reset tty pgrp from %ld to %ld\n",
1389 (long)ctpgrp, (long)tpgrp);
1390 goto reread;
1392 if (adrof(STRignoreeof)) {
1393 if (loginsh)
1394 (void)fprintf(csherr,"\nUse \"logout\" to logout.\n");
1395 else
1396 (void)fprintf(csherr,"\nUse \"exit\" to leave csh.\n");
1397 reset();
1399 if (chkstop == 0)
1400 panystop(1);
1402 oops:
1403 doneinp = 1;
1404 reset();
1406 sincereal = 0;
1407 if (c == '\n' && onelflg)
1408 onelflg--;
1409 } while (c == 0);
1410 return (c);
1413 static int
1414 bgetc(void)
1416 #ifdef FILEC
1417 char tbuf[BUFSIZE + 1];
1418 Char ttyline[BUFSIZE];
1419 int buf, off;
1420 ssize_t c, numleft, roomleft;
1422 numleft = 0;
1423 #else /* FILEC */
1424 char tbuf[BUFSIZE + 1];
1425 int c, buf, off;
1426 #endif /* !FILEC */
1428 if (cantell) {
1429 if (fseekp < fbobp || fseekp > feobp) {
1430 fbobp = feobp = fseekp;
1431 (void)lseek(SHIN, fseekp, SEEK_SET);
1433 if (fseekp == feobp) {
1434 int i;
1436 fbobp = feobp;
1438 c = read(SHIN, tbuf, BUFSIZE);
1439 while (c < 0 && errno == EINTR);
1440 if (c <= 0)
1441 return (-1);
1442 for (i = 0; i < c; i++)
1443 fbuf[0][i] = (unsigned char) tbuf[i];
1444 feobp += c;
1446 c = fbuf[0][fseekp - fbobp];
1447 fseekp++;
1448 return (int)(c);
1451 again:
1452 buf = (int) fseekp / BUFSIZE;
1453 if (buf >= fblocks) {
1454 Char **nfbuf;
1456 nfbuf = (Char **)xcalloc((size_t) (fblocks + 2), sizeof(char **));
1457 if (fbuf) {
1458 (void)blkcpy(nfbuf, fbuf);
1459 xfree((ptr_t) fbuf);
1461 fbuf = nfbuf;
1462 fbuf[fblocks] = (Char *)xcalloc(BUFSIZE, sizeof(Char));
1463 fblocks++;
1464 if (!intty)
1465 goto again;
1467 if (fseekp >= feobp) {
1468 buf = (int) feobp / BUFSIZE;
1469 off = (int) feobp % BUFSIZE;
1470 roomleft = BUFSIZE - off;
1472 #ifdef FILEC
1473 for (;;) {
1474 if ((editing || filec) && intty) {
1475 #ifdef EDIT
1476 if (editing) {
1477 const char *p;
1478 int d;
1479 if ((p = el_gets(el, &d)) != NULL) {
1480 size_t i;
1481 /* XXX: Truncation */
1482 numleft = d > BUFSIZE ? BUFSIZE : d;
1483 for (i = 0; *p && i < BUFSIZE; i++, p++)
1484 ttyline[i] = *p;
1485 ttyline[i - (i == BUFSIZE)] = '\0';
1488 #endif
1489 c = numleft ? numleft : tenex(ttyline, BUFSIZE);
1490 if (c > roomleft) {
1491 /* start with fresh buffer */
1492 feobp = fseekp = fblocks * BUFSIZE;
1493 numleft = c;
1494 goto again;
1496 if (c > 0)
1497 (void)memcpy(fbuf[buf] + off, ttyline,
1498 (size_t)c * sizeof(**fbuf));
1499 numleft = 0;
1501 else {
1502 #endif
1503 c = read(SHIN, tbuf, (size_t)roomleft);
1504 if (c > 0) {
1505 int i;
1506 Char *ptr = fbuf[buf] + off;
1508 for (i = 0; i < c; i++)
1509 ptr[i] = (unsigned char) tbuf[i];
1511 #ifdef FILEC
1513 #endif
1514 if (c >= 0)
1515 break;
1516 if (errno == EWOULDBLOCK) {
1517 int iooff = 0;
1519 (void)ioctl(SHIN, FIONBIO, (ioctl_t) & iooff);
1521 else if (errno != EINTR)
1522 break;
1523 #ifdef FILEC
1525 #endif
1526 if (c <= 0)
1527 return (-1);
1528 feobp += c;
1529 #ifndef FILEC
1530 goto again;
1531 #else
1532 if (filec && !intty)
1533 goto again;
1534 #endif
1536 c = fbuf[buf][(int)fseekp % BUFSIZE];
1537 fseekp++;
1538 return (int)(c);
1541 static void
1542 bfree(void)
1544 int i, sb;
1546 if (cantell)
1547 return;
1548 if (whyles)
1549 return;
1550 sb = (int)(fseekp - 1) / BUFSIZE;
1551 if (sb > 0) {
1552 for (i = 0; i < sb; i++)
1553 xfree((ptr_t) fbuf[i]);
1554 (void)blkcpy(fbuf, &fbuf[sb]);
1555 fseekp -= BUFSIZE * sb;
1556 feobp -= BUFSIZE * sb;
1557 fblocks -= sb;
1561 void
1562 bseek(struct Ain *l)
1564 switch (aret = l->type) {
1565 case A_SEEK:
1566 alvec = l->a_seek;
1567 alvecp = l->c_seek;
1568 return;
1569 case E_SEEK:
1570 evalvec = l->a_seek;
1571 evalp = l->c_seek;
1572 return;
1573 case F_SEEK:
1574 fseekp = l->f_seek;
1575 return;
1576 default:
1577 (void)fprintf(csherr, "Bad seek type %d\n", aret);
1578 abort();
1582 void
1583 btell(struct Ain *l)
1585 switch (l->type = aret) {
1586 case A_SEEK:
1587 l->a_seek = alvec;
1588 l->c_seek = alvecp;
1589 return;
1590 case E_SEEK:
1591 l->a_seek = evalvec;
1592 l->c_seek = evalp;
1593 return;
1594 case F_SEEK:
1595 l->f_seek = fseekp;
1596 l->a_seek = NULL;
1597 return;
1598 default:
1599 (void)fprintf(csherr, "Bad seek type %d\n", aret);
1600 abort();
1604 void
1605 btoeof(void)
1607 (void)lseek(SHIN, (off_t) 0, SEEK_END);
1608 aret = F_SEEK;
1609 fseekp = feobp;
1610 alvec = NULL;
1611 alvecp = NULL;
1612 evalvec = NULL;
1613 evalp = NULL;
1614 wfree();
1615 bfree();
1618 void
1619 settell(void)
1621 cantell = 0;
1622 if (arginp || onelflg || intty)
1623 return;
1624 if (lseek(SHIN, (off_t) 0, SEEK_CUR) < 0 || errno == ESPIPE)
1625 return;
1626 fbuf = (Char **)xcalloc(2, sizeof(Char **));
1627 fblocks = 1;
1628 fbuf[0] = (Char *)xcalloc(BUFSIZE, sizeof(Char));
1629 fseekp = fbobp = feobp = lseek(SHIN, (off_t) 0, SEEK_CUR);
1630 cantell = 1;