1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
23 /* All Rights Reserved */
24
25
26 /*
27 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
29 */
30
31 /* from OpenSolaris "bfs.c 1.14 05/06/08 SMI" */
32
33 /*
34 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
35 */
36 #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
37 #define USED __attribute__ ((used))
38 #elif defined __GNUC__
39 #define USED __attribute__ ((unused))
40 #else
41 #define USED
42 #endif
43 static const char sccsid[] USED = "@(#)bfs.c 1.15 (gritter) 7/2/05";
44
45 #include <setjmp.h>
46 #include <signal.h>
47 #include <stdlib.h>
48 #include <regexpr.h>
49 #include <limits.h>
50 #include <sys/types.h>
51 #include <unistd.h>
52 #include <sys/stat.h>
53 #include <locale.h>
54 #include <stdio.h>
55 #include <string.h>
56 #include <sys/wait.h>
57 #include <fcntl.h>
58 #include <inttypes.h>
59 #include <wchar.h>
60 #include <limits.h>
61 #include <errno.h>
62 #include "sigset.h"
63 static jmp_buf env;
64
65 #define BRKTYP long
66 #define BRKSIZ 8192
67 #define BRKTWO 4
68 #define BFSTRU -1L
69 #define BFSBUF 512
70
71 struct Comd {
72 int Cnumadr;
73 long Cadr[2];
74 char Csep;
75 char Cop;
76 };
77
78 static long Dot, Dollar;
79 static long markarray[26], *mark;
80 static int fstack[15] = {1, -1};
81 static int infildes = 0;
82 static int outfildes = 1;
83 static char *internal, *intptr;
84 static size_t internalsize;
85 static char *comdlist;
86 static size_t comdlistsize;
87 static char charbuf = '\n';
88 static int peeked;
89 static char *currex;
90 static size_t currexsize;
91 static long trunc = BFSTRU;
92 static int crunch = -1;
93 static off_t *segblk, prevblk;
94 static short *segoff, prevoff;
95 static int txtfd;
96 static int oldfd = 0;
97 static int flag4 = 0;
98 static int flag3 = 0;
99 static int flag2 = 0;
100 static int flag1 = 0;
101 static int flag = 0;
102 static int lprev = 1;
103 static int status[1];
104 static BRKTYP *lincnt;
105 static char *perbuf;
106 static char *glbuf;
107 static char tty, *bigfile;
108 static char *fle;
109 static size_t flesize;
110 static char prompt = 1;
111 static char verbose = 1; /* 1=print # of bytes read in; 0=silent. */
112 static char *varray[10]; /* Holds xv cmd parameters. */
113 static size_t varraysize[10];
114 static long long outcnt;
115 static char strtmp[PATH_MAX+32];
116 static int mb_cur_max;
117 static int errcnt;
118
119 static void reset(int);
120 static void begin(struct Comd *p);
121 static int bigopen(const char *file);
122 static void sizeprt(long long blk, int off);
123 static long bigread(long l, char **rec, size_t *recsize);
124 static int gcomd(struct Comd *p, int k);
125 static int fcomd(struct Comd *p);
126 static void ecomd(void);
127 static int kcomd(struct Comd *p);
128 static int xncomd(struct Comd *p);
129 static int pcomd(struct Comd *p);
130 static int qcomd(void);
131 static int xcomds(struct Comd *p);
132 static int xbcomd(struct Comd *p);
133 static int xccomd(struct Comd *p);
134 static int xfcomd(struct Comd *p);
135 static int xocomd(struct Comd *p);
136 static int xtcomd(struct Comd *p);
137 static int xvcomd(void);
138 static int wcomd(struct Comd *p);
139 static int nlcomd(struct Comd *p);
140 static int eqcomd(struct Comd *p);
141 static int colcomd(struct Comd *p);
142 static int excomd(void);
143 static int xcomdlist(struct Comd *p);
144 static int defaults(struct Comd *p, int prt, int max,
145 long def1, long def2, int setdot, int errsok);
146 static int getcomd(struct Comd *p, int prt);
147 static int getadrs(struct Comd *p, int prt);
148 static int getadr(struct Comd *p, int prt);
149 static int getnumb(struct Comd *p, int prt);
150 static int rdnumb(int prt);
151 static int getrel(struct Comd *p, int prt);
152 static int getmark(struct Comd *p, int prt);
153 static int getrex(struct Comd *p, int prt, char c);
154 static int hunt(int prt, char rex[], long start,
155 int down, int wrap, int errsok);
156 static int jump(int prt, char label[]);
157 static int getstr(int prt, char **buf, size_t *size,
158 char brk, char ignr, int nonl);
159 static int regerr(int c);
160 static int err(int prt, const char msg[]);
161 static char mygetc(void);
162 static int readc(int f, char *c);
163 static int percent(char **line, size_t *linesize);
164 static int newfile(int prt, char f[]);
165 static void push(int s[], int d);
166 static int pop(int s[]);
167 static int peekc(void);
168 static void eat(void);
169 static int more(void);
170 static void quit(void);
171 static void out(char *ln, long length);
172 static char *untab(const char *l);
173 static int patoi(const char *b);
174 static int equal(const char *a, const char *b);
175 static void intcat(const char *);
176 static void *grow(void *ptr, size_t size, const char *msg);
177
178 int
main(int argc,char * argv[])179 main(int argc, char *argv[])
180 {
181 struct Comd comdstruct, *p;
182 setlocale(LC_CTYPE, "");
183 mb_cur_max = MB_CUR_MAX;
184 if (argc < 2 || argc > 3) {
185 err(1, "arg count");
186 quit();
187 }
188 mark = markarray-'a';
189 if (argc == 3) {
190 verbose = 0;
191 }
192 setbuf(stdout, 0);
193 if (bigopen(bigfile = argv[argc-1]))
194 quit();
195 tty = isatty(0);
196 p = &comdstruct;
197 /* Look for 0 or more non-'%' char followed by a '%' */
198 perbuf = compile("[^%]*%", NULL, NULL);
199 if (regerrno)
200 regerr(regerrno);
201 setjmp(env);
202 sigset(SIGINT, reset);
203 err(0, "");
204 printf("\n");
205 flag = 0;
206 prompt = 0;
207 /*CONSTCOND*/ for (;;)
208 begin(p);
209 /*NOTREACHED*/
210 return 0;
211 }
212
213 static void
reset(int signo)214 reset(int signo) /* for longjmp on signal */
215 {
216 sigrelse(signo);
217 longjmp(env, 1);
218 }
219
220 static void
begin(struct Comd * p)221 begin(struct Comd *p)
222 {
223 char *line = NULL;
224 size_t linesize = 0;
225 strtagn:
226 if (flag == 0)
227 eat();
228 if (infildes != 100) {
229 if (infildes == 0 && prompt)
230 printf("*");
231 flag3 = 1;
232 if (getstr(1, &line, &linesize, 0, 0, 0))
233 exit(1);
234 flag3 = 0;
235 if (percent(&line, &linesize) < 0)
236 goto strtagn;
237 newfile(1, "");
238 }
239 if (!(getcomd(p, 1) < 0)) {
240 switch (p->Cop) {
241 case 'e':
242 if (!flag)
243 ecomd();
244 else
245 err(0, "");
246 break;
247
248 case 'f':
249 fcomd(p);
250 break;
251
252 case 'g':
253 if (flag == 0)
254 gcomd(p, 1);
255 else
256 err(0, "");
257 break;
258
259 case 'k':
260 kcomd(p);
261 break;
262
263 case 'p':
264 pcomd(p);
265 break;
266
267 case 'q':
268 qcomd();
269 break;
270
271 case 'v':
272 if (flag == 0)
273 gcomd(p, 0);
274 else
275 err(0, "");
276 break;
277
278 case 'x':
279 if (!flag)
280 xcomds(p);
281 else
282 err(0, "");
283 break;
284
285 case 'w':
286 wcomd(p);
287 break;
288
289 case '\n':
290 nlcomd(p);
291 break;
292
293 case '=':
294 eqcomd(p);
295 break;
296
297 case ':':
298 colcomd(p);
299 break;
300
301 case '!':
302 excomd();
303 break;
304
305 case 'P':
306 prompt = !prompt;
307 break;
308
309 default:
310 if (flag)
311 err(0, "");
312 else
313 err(1, "bad command");
314 break;
315 }
316 }
317 free(line);
318 }
319
320 static int
bigopen(const char * file)321 bigopen(const char *file)
322 {
323 long l, off, cnt;
324 off_t blk;
325 long s;
326 int newline, n;
327 char block[512];
328 size_t totsiz;
329 const char toomany[] = "too many lines";
330 if ((txtfd = open(file, O_RDONLY)) < 0)
331 return (err(1, "can't open"));
332 blk = -1;
333 newline = 1;
334 l = cnt = s = 0;
335 off = 512;
336 totsiz = 0;
337 lincnt = grow(lincnt, BRKSIZ * sizeof *lincnt, toomany);
338 segblk = grow(segblk, 512 * sizeof *segblk, toomany);
339 segoff = grow(segoff, 512 * sizeof *segblk, toomany);
340 totsiz += BRKSIZ;
341 while ((n = read(txtfd, block, 512)) > 0) {
342 blk++;
343 for (off = 0; off < n; off++) {
344 if (newline) {
345 newline = 0;
346 if (l > 0 && !(l&07777)) {
347 totsiz += BRKSIZ;
348 lincnt = grow(lincnt,
349 totsiz * sizeof *lincnt,
350 toomany);
351 }
352 lincnt[l] = cnt;
353 cnt = 0;
354 if (l == LONG_MAX)
355 return err(1, toomany);
356 if (!(l++ & 077)) {
357 if (!(l-1 & 077777)) {
358 if (s >= LONG_MAX - 512)
359 return err(1, toomany);
360 segblk = grow(segblk,
361 (s + 512) *
362 sizeof *segblk,
363 toomany);
364 segoff = grow(segoff,
365 (s + 512) *
366 sizeof *segoff,
367 toomany);
368 }
369 segblk[s] = blk;
370 segoff[s++] = off;
371 }
372 }
373 if (block[off] == '\n') newline = 1;
374 if (cnt == LONG_MAX)
375 return err(1, "line too long");
376 cnt++;
377 }
378 }
379 if (n < 0) {
380 errcnt = 1;
381 puts("i/o error");
382 }
383 if (!(l&07777)) {
384 totsiz += BRKTWO;
385 lincnt = grow(lincnt, totsiz * sizeof *lincnt, toomany);
386 }
387 lincnt[Dot = Dollar = l] = cnt;
388 sizeprt(blk, off);
389 return (0);
390 }
391
392 static void
sizeprt(long long blk,int off)393 sizeprt(long long blk, int off)
394 {
395 if (verbose)
396 printf("%lld", 512*blk+off);
397 }
398
399 static off_t saveblk = -1;
400
401 static long
bigread(long l,char ** rec,size_t * recsize)402 bigread(long l, char **rec, size_t *recsize)
403 {
404 long i;
405 char *b;
406 long r;
407 int off;
408 static char savetxt[512];
409 static int rd;
410
411 if ((i = l-lprev) == 1) prevoff += lincnt[lprev];
412 else if (i >= 0 && i <= 32)
413 for (i = lprev; i < l; i++) prevoff += lincnt[i];
414 else if (i < 0 && i >= -32)
415 for (i = lprev-1; i >= l; i--) prevoff -= lincnt[i];
416 else {
417 prevblk = segblk[i = (l-1)>>6];
418 prevoff = segoff[i];
419 for (i = (i<<6)+1; i < l; i++) prevoff += lincnt[i];
420 }
421
422 prevblk += prevoff>>9;
423 prevoff &= 0777;
424 lprev = l;
425 if (prevblk != saveblk) {
426 lseek(txtfd, (saveblk = prevblk)<<9, 0);
427 rd = read(txtfd, savetxt, 512);
428 }
429 r = 0;
430 off = prevoff;
431 while (rd > 0) {
432 for (b = savetxt+off; b < savetxt+rd; b++) {
433 if (r+1 >= *recsize) {
434 char *nrec;
435 nrec = realloc(*rec, *recsize += BFSBUF);
436 if (nrec == NULL) {
437 write(2, "Line too long--"
438 "output truncated\n", 32);
439 goto out;
440 }
441 *rec = nrec;
442 }
443 if (*b == '\n') {
444 out: (*rec)[r] = '\0';
445 return r;
446 }
447 (*rec)[r++] = *b;
448 }
449 if ((i = read(txtfd, savetxt, 512)) == 0) {
450 puts("'\\n' appended");
451 goto out;
452 }
453 rd = i;
454 off = 0;
455 saveblk++;
456 }
457 if (rd < 0) {
458 puts("i/o error");
459 saveblk = -1;
460 }
461 if (*rec == 0)
462 *rec = grow(*rec, *recsize = 1, "no space");
463 goto out;
464 }
465
466 static void
ecomd(void)467 ecomd(void)
468 {
469 int i = 0;
470 while (peekc() == ' ')
471 mygetc();
472 do {
473 if (i >= flesize)
474 fle = grow(fle, flesize += 80, "file name too long");
475 } while ((fle[i++] = mygetc()) != '\n');
476 fle[--i] = '\0';
477 /* Without this, ~20 "e" cmds gave "can't open" msg. */
478 close(txtfd);
479 free(lincnt);
480 lincnt = NULL;
481 /* Reset parameters. */
482 lprev = 1;
483 prevblk = 0;
484 prevoff = 0;
485 saveblk = -1;
486 if (bigopen(bigfile = fle))
487 quit();
488 printf("\n");
489 }
490
491 static int
fcomd(struct Comd * p)492 fcomd(struct Comd *p)
493 {
494 if (more() || defaults(p, 1, 0, 0, 0, 0, 0))
495 return (-1);
496 printf("%s\n", bigfile);
497 return (0);
498 }
499
500 static int
gcomd(struct Comd * p,int k)501 gcomd(struct Comd *p, int k)
502 {
503 char d;
504 long i, end;
505 char *line = NULL;
506 size_t linesize = 0;
507 if (defaults(p, 1, 2, 1, Dollar, 0, 0))
508 return (-1);
509 if ((d = mygetc()) == '\n')
510 return (err(1, "syntax"));
511 if (peekc() == d)
512 mygetc();
513 else
514 if (getstr(1, &currex, &currexsize, d, 0, 1))
515 return (-1);
516 glbuf = compile(currex, NULL, NULL);
517 if (regerrno) {
518 regerr(regerrno);
519 return (-1);
520 }
521
522 if (getstr(1, &comdlist, &comdlistsize, 0, 0, 0)) {
523 free(glbuf);
524 return (-1);
525 }
526 i = p->Cadr[0];
527 end = p->Cadr[1];
528 while (i <= end) {
529 bigread(i, &line, &linesize);
530 if (!(step(line, glbuf))) {
531 if (!k) {
532 Dot = i;
533 if (xcomdlist(p)) {
534 free(glbuf);
535 free(line);
536 return (err(1, "bad comd list"));
537 }
538 }
539 i++;
540 } else {
541 if (k) {
542 Dot = i;
543 if (xcomdlist(p)) {
544 free(glbuf);
545 free(line);
546 return (err(1, "bad comd list"));
547 }
548 }
549 i++;
550 }
551 }
552 free(line);
553 free(glbuf);
554 return (0);
555 }
556
557 static int
kcomd(struct Comd * p)558 kcomd(struct Comd *p)
559 {
560 int c;
561 if ((c = peekc()) < 'a' || c > 'z')
562 return (err(1, "bad mark"));
563 mygetc();
564 if (more() || defaults(p, 1, 1, Dot, 0, 1, 0))
565 return (-1);
566 mark[c] = Dot = p->Cadr[0];
567 return (0);
568 }
569
570 static int
xncomd(struct Comd * p)571 xncomd(struct Comd *p)
572 {
573 int c;
574 if (more() || defaults(p, 1, 0, 0, 0, 0, 0))
575 return (-1);
576
577 for (c = 'a'; c <= 'z'; c++)
578 if (mark[c])
579 printf("%c\n", c);
580
581 return (0);
582 }
583
584 static int
pcomd(struct Comd * p)585 pcomd(struct Comd *p)
586 {
587 long i, n;
588 char *line = NULL;
589 size_t linesize = 0;
590 if (more() || defaults(p, 1, 2, Dot, Dot, 1, 0))
591 return (-1);
592 for (i = p->Cadr[0]; i <= p->Cadr[1] && i > 0; i++) {
593 n = bigread(i, &line, &linesize);
594 out(line, n);
595 }
596 free(line);
597 return (0);
598 }
599
600 static int
qcomd(void)601 qcomd(void)
602 {
603 if (more())
604 return (-1);
605 quit();
606 return (0);
607 }
608
609 static int
xcomds(struct Comd * p)610 xcomds(struct Comd *p)
611 {
612 switch (mygetc()) {
613 case 'b': return (xbcomd(p));
614 case 'c': return (xccomd(p));
615 case 'f': return (xfcomd(p));
616 case 'n': return (xncomd(p));
617 case 'o': return (xocomd(p));
618 case 't': return (xtcomd(p));
619 case 'v': return (xvcomd());
620 default: return (err(1, "bad command"));
621 }
622 }
623
624 #define Return free(str); return
625 static int
xbcomd(struct Comd * p)626 xbcomd(struct Comd *p)
627 {
628 int fail, n = 0;
629 char d;
630 char *str = NULL;
631 size_t strsize = 0;
632
633 fail = 0;
634 if (defaults(p, 0, 2, Dot, Dot, 0, 1))
635 fail = 1;
636 else {
637 if ((d = mygetc()) == '\n') {
638 Return (err(1, "syntax"));
639 }
640 if (d == 'z') {
641 if (status[0] != 0) {
642 Return (0);
643 }
644 mygetc();
645 if (getstr(1, &str, &strsize, 0, 0, 0)) {
646 Return (-1);
647 }
648 Return (jump(1, str));
649 }
650 if (d == 'n') {
651 if (status[0] == 0) {
652 Return (0);
653 }
654 mygetc();
655 if (getstr(1, &str, &strsize, 0, 0, 0)) {
656 Return (-1);
657 }
658 Return (jump(1, str));
659 }
660 if (getstr(1, &str, &strsize, d, ' ', 0)) {
661 Return (-1);
662 }
663 if ((n = hunt(0, str, p->Cadr[0]-1, 1, 0, 1)) < 0)
664 fail = 1;
665 if (getstr(1, &str, &strsize, 0, 0, 0)) {
666 Return (-1);
667 }
668 if (more()) {
669 Return (err(1, "syntax"));
670 }
671 }
672 if (!fail) {
673 Dot = n;
674 Return (jump(1, str));
675 }
676 Return (0);
677 }
678 #undef Return
679
680 static int
xccomd(struct Comd * p)681 xccomd(struct Comd *p)
682 {
683 char *arg = NULL;
684 size_t argsize = 0;
685 if (getstr(1, &arg, &argsize, 0, ' ', 0) ||
686 defaults(p, 1, 0, 0, 0, 0, 0)) {
687 free(arg);
688 return (-1);
689 }
690 if (equal(arg, ""))
691 crunch = -crunch;
692 else if (equal(arg, "0"))
693 crunch = -1;
694 else if (equal(arg, "1"))
695 crunch = 1;
696 else {
697 free(arg);
698 return (err(1, "syntax"));
699 }
700
701 free(arg);
702 return (0);
703 }
704
705 static int
xfcomd(struct Comd * p)706 xfcomd(struct Comd *p)
707 {
708 char fl[100];
709 char *f;
710 if (defaults(p, 1, 0, 0, 0, 0, 0))
711 return (-1);
712
713 while (peekc() == ' ')
714 mygetc();
715 for (f = fl; (*f = mygetc()) != '\n'; f++);
716 if (f == fl)
717 return (err(1, "no file"));
718 *f = '\0';
719
720 return (newfile(1, fl));
721 }
722
723 #define Return free(arg); return
724 static int
xocomd(struct Comd * p)725 xocomd(struct Comd *p)
726 {
727 int fd;
728 char *arg = NULL;
729 size_t argsize = 0;
730
731 if (getstr(1, &arg, &argsize, 0, ' ', 0) ||
732 defaults(p, 1, 0, 0, 0, 0, 0)) {
733 Return (-1);
734 }
735
736 if (!arg[0]) {
737 if (outfildes == 1) {
738 Return (err(1, "no diversion"));
739 }
740 close(outfildes);
741 outfildes = 1;
742 } else {
743 if (outfildes != 1) {
744 Return (err(1, "already diverted"));
745 }
746 if ((fd = open(arg, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
747 Return (err(1, "can't create"));
748 }
749 outfildes = fd;
750 }
751 Return (0);
752 }
753 #undef Return
754
755 static int
xtcomd(struct Comd * p)756 xtcomd(struct Comd *p)
757 {
758 int t;
759
760 while (peekc() == ' ')
761 mygetc();
762 if ((t = rdnumb(1)) < 0 || more() || defaults(p, 1, 0, 0, 0, 0, 0))
763 return (-1);
764
765 trunc = t;
766 return (0);
767 }
768
769 static int
xvcomd(void)770 xvcomd(void)
771 {
772 int c;
773 int i;
774 int temp0 = -1, temp1 = -1, temp2 = -1;
775 int fildes[2];
776
777 if ((c = peekc()) < '0' || c > '9')
778 return (err(1, "digit required"));
779 mygetc();
780 c -= '0';
781 while (peekc() == ' ')
782 mygetc();
783 if (peekc() == '\\')
784 mygetc();
785 else if (peekc() == '!') {
786 if (pipe(fildes) < 0) {
787 printf("Try again");
788 return (-1);
789 }
790 temp0 = dup(0);
791 temp1 = dup(1);
792 temp2 = infildes;
793 close(0);
794 dup(fildes[0]);
795 close(1);
796 dup(fildes[1]);
797 close(fildes[0]);
798 close(fildes[1]);
799 mygetc();
800 flag4 = 1;
801 excomd();
802 close(1);
803 infildes = 0;
804 }
805 i = 0;
806 do {
807 if (i >= varraysize[c])
808 varray[c] = grow(varray[c], varraysize[c] += 100,
809 "command too long");
810 } while ((varray[c][i++] = mygetc()) != '\n');
811 varray[c][i-1] = '\0';
812 if (flag4) {
813 infildes = temp2;
814 close(0);
815 dup(temp0);
816 close(temp0);
817 dup(temp1);
818 close(temp1);
819 flag4 = 0;
820 charbuf = ' ';
821 }
822 return (0);
823 }
824
825 #define Return free(arg); free(line); return
826 static int
wcomd(struct Comd * p)827 wcomd(struct Comd *p)
828 {
829 long i, n;
830 int fd, savefd;
831 int savecrunch, savetrunc;
832 char *arg = NULL, *line = NULL;
833 size_t argsize = 0, linesize = 0;
834
835 if (getstr(1, &arg, &argsize, 0, ' ', 0) ||
836 defaults(p, 1, 2, 1, Dollar, 1, 0)) {
837 Return (-1);
838 }
839 if (!arg[0]) {
840 Return (err(1, "no file name"));
841 }
842 if (equal(arg, bigfile)) {
843 Return (err(1, "no change indicated"));
844 }
845 if ((fd = open(arg, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
846 Return (err(1, "can't create"));
847 }
848
849 savefd = outfildes;
850 savetrunc = trunc;
851 savecrunch = crunch;
852 outfildes = fd;
853 trunc = BFSTRU;
854 crunch = -1;
855
856 outcnt = 0;
857 for (i = p->Cadr[0]; i <= p->Cadr[1] && i > 0; i++) {
858 n = bigread(i, &line, &linesize);
859 out(line, n);
860 }
861 if (verbose)
862 printf("%lld\n", outcnt);
863 close(fd);
864
865 outfildes = savefd;
866 trunc = savetrunc;
867 crunch = savecrunch;
868 Return (0);
869 }
870 #undef Return
871
872 static int
nlcomd(struct Comd * p)873 nlcomd(struct Comd *p)
874 {
875 if (defaults(p, 1, 2, Dot+1, Dot+1, 1, 0)) {
876 mygetc();
877 return (-1);
878 }
879 return (pcomd(p));
880 }
881
882 static int
eqcomd(struct Comd * p)883 eqcomd(struct Comd *p)
884 {
885 if (more() || defaults(p, 1, 1, Dollar, 0, 0, 0))
886 return (-1);
887 printf("%ld\n", p->Cadr[0]);
888 return (0);
889 }
890
891 static int
colcomd(struct Comd * p)892 colcomd(struct Comd *p)
893 {
894 return (defaults(p, 1, 0, 0, 0, 0, 0));
895 }
896
897 static int
xcomdlist(struct Comd * p)898 xcomdlist(struct Comd *p)
899 {
900 flag = 1;
901 flag2 = 1;
902 newfile(1, "");
903 while (flag2)
904 begin(p);
905 if (flag == 0)
906 return (1);
907 flag = 0;
908 return (0);
909 }
910
911 static int
excomd(void)912 excomd(void)
913 {
914 pid_t i;
915 int j;
916 if (infildes != 100)
917 charbuf = '\n';
918 while ((i = fork()) < (pid_t)0)
919 sleep(10);
920 if (i == (pid_t)0) {
921 /* Guarantees child can be intr. */
922 sigset(SIGINT, SIG_DFL);
923 if (infildes == 100 || flag4) {
924 execl(SHELL, "sh", "-c", intptr, NULL);
925 _exit(0);
926 }
927 if (infildes != 0) {
928 close(0);
929 dup(infildes);
930 }
931 for (j = 3; j < 15; j++) close(j);
932 execl(SHELL, "sh", "-t", NULL);
933 _exit(0);
934 }
935 sigset(SIGINT, SIG_IGN);
936 while (wait(status) != i);
937 status[0] = status[0] >> 8;
938
939 sigset(SIGINT, reset); /* Restore signal to previous status */
940
941 if (((infildes == 0) || ((infildes == 100) &&
942 (fstack[fstack[0]] == 0)))&& verbose && (!flag4))
943 printf("!\n");
944 return (0);
945 }
946
947 static int
defaults(struct Comd * p,int prt,int max,long def1,long def2,int setdot,int errsok)948 defaults(struct Comd *p, int prt, int max,
949 long def1, long def2, int setdot, int errsok)
950 {
951 if (!def1)
952 def1 = Dot;
953 if (!def2)
954 def2 = def1;
955 if (p->Cnumadr >= max)
956 return (errsok?-1:err(prt, "adr count"));
957 if (p->Cnumadr < 0) {
958 p->Cadr[++p->Cnumadr] = def1;
959 p->Cadr[++p->Cnumadr] = def2;
960 } else if (p->Cnumadr < 1)
961 p->Cadr[++p->Cnumadr] = p->Cadr[0];
962 if (p->Cadr[0] < 1 || p->Cadr[0] > Dollar ||
963 p->Cadr[1] < 1 || p->Cadr[1] > Dollar)
964 return (errsok?-1:err(prt, "range"));
965 if (p->Cadr[0] > p->Cadr[1])
966 return (errsok?-1:err(prt, "adr1 > adr2"));
967 if (setdot)
968 Dot = p->Cadr[1];
969 return (0);
970 }
971
972 static int
getcomd(struct Comd * p,int prt)973 getcomd(struct Comd *p, int prt)
974 {
975 int r;
976 int c;
977
978 p->Cnumadr = -1;
979 p->Csep = ' ';
980 switch (c = peekc()) {
981 case ',':
982 case ';': p->Cop = mygetc();
983 return (0);
984 }
985
986 if ((r = getadrs(p, prt)) < 0)
987 return (r);
988
989 if ((c = peekc()) < 0)
990 return (err(prt, "syntax"));
991 if (c == '\n')
992 p->Cop = '\n';
993 else
994 p->Cop = mygetc();
995
996 return (0);
997 }
998
999 static int
getadrs(struct Comd * p,int prt)1000 getadrs(struct Comd *p, int prt)
1001 {
1002 int r;
1003 char c;
1004
1005 if ((r = getadr(p, prt)) < 0)
1006 return (r);
1007
1008 switch (c = peekc()) {
1009 case ';':
1010 Dot = p->Cadr[0];
1011 mygetc();
1012 p->Csep = c;
1013 return (getadr(p, prt));
1014 case ',':
1015 mygetc();
1016 p->Csep = c;
1017 return (getadr(p, prt));
1018 }
1019
1020 return (0);
1021 }
1022
1023 static int
getadr(struct Comd * p,int prt)1024 getadr(struct Comd *p, int prt)
1025 {
1026 int r;
1027 char c;
1028
1029 r = 0;
1030 while (peekc() == ' ')
1031 mygetc(); /* Ignore leading spaces */
1032 switch (c = peekc()) {
1033 case '\n':
1034 case ',':
1035 case ';': return (0);
1036 case '\'': mygetc();
1037 r = getmark(p, prt);
1038 break;
1039
1040 case '0':
1041 case '1':
1042 case '2':
1043 case '3':
1044 case '4':
1045 case '5':
1046 case '6':
1047 case '7':
1048 case '8':
1049 case '9': r = getnumb(p, prt);
1050 break;
1051 case '.': mygetc();
1052 p->Cadr[++p->Cnumadr] = Dot;
1053 break;
1054 case '+':
1055 case '-': p->Cadr[++p->Cnumadr] = Dot;
1056 break;
1057 case '$': mygetc();
1058 p->Cadr[++p->Cnumadr] = Dollar;
1059 break;
1060 case '^': mygetc();
1061 p->Cadr[++p->Cnumadr] = Dot - 1;
1062 break;
1063 case '/':
1064 case '?':
1065 case '>':
1066 case '<': mygetc();
1067 r = getrex(p, prt, c);
1068 break;
1069 default: return (0);
1070 }
1071
1072 if (r == 0)
1073 r = getrel(p, prt);
1074 return (r);
1075 }
1076
1077 static int
getnumb(struct Comd * p,int prt)1078 getnumb(struct Comd *p, int prt)
1079 {
1080 long i;
1081
1082 if ((i = rdnumb(prt)) < 0)
1083 return (-1);
1084 p->Cadr[++p->Cnumadr] = i;
1085 return (0);
1086 }
1087
1088 static int
rdnumb(int prt)1089 rdnumb(int prt)
1090 {
1091 char num[20], *n;
1092 int i;
1093
1094 n = num;
1095 while ((*n = peekc()) >= '0' && *n <= '9') {
1096 n++;
1097 mygetc();
1098 }
1099
1100 *n = '\0';
1101 if ((i = patoi(num)) >= 0)
1102 return (i);
1103 return (err(prt, "bad num"));
1104 }
1105
1106 static int
getrel(struct Comd * p,int prt)1107 getrel(struct Comd *p, int prt)
1108 {
1109 int op, n;
1110 char c;
1111 int j;
1112
1113 n = 0;
1114 op = 1;
1115 while ((c = peekc()) == '+' || c == '-') {
1116 if (c == '+')
1117 n++;
1118 else
1119 n--;
1120 mygetc();
1121 }
1122 j = n;
1123 if (n < 0)
1124 op = -1;
1125 if (c == '\n')
1126 p->Cadr[p->Cnumadr] += n;
1127 else {
1128 if ((n = rdnumb(0)) > 0 && p->Cnumadr >= 0) {
1129 p->Cadr[p->Cnumadr] += op*n;
1130 getrel(p, prt);
1131 } else {
1132 p->Cadr[p->Cnumadr] += j;
1133 }
1134 }
1135 return (0);
1136 }
1137
1138 static int
getmark(struct Comd * p,int prt)1139 getmark(struct Comd *p, int prt)
1140 {
1141 int c;
1142
1143 if ((c = peekc()) < 'a' || c > 'z')
1144 return (err(prt, "bad mark"));
1145 mygetc();
1146
1147 if (!mark[c])
1148 return (err(prt, "undefined mark"));
1149 p->Cadr[++p->Cnumadr] = mark[c];
1150 return (0);
1151 }
1152
1153 static int
getrex(struct Comd * p,int prt,char c)1154 getrex(struct Comd *p, int prt, char c)
1155 {
1156 int down = -1, wrap = -1;
1157 long start;
1158
1159 if (peekc() == c)
1160 mygetc();
1161 else if (getstr(prt, &currex, &currexsize, c, 0, 1))
1162 return (-1);
1163
1164 switch (c) {
1165 case '/': down = 1; wrap = 1; break;
1166 case '?': down = 0; wrap = 1; break;
1167 case '>': down = 1; wrap = 0; break;
1168 case '<': down = 0; wrap = 0; break;
1169 }
1170
1171 if (p->Csep == ';')
1172 start = p->Cadr[0];
1173 else
1174 start = Dot;
1175
1176 if ((p->Cadr[++p->Cnumadr] = hunt(prt, currex, start, down, wrap, 0))
1177 < 0)
1178 return (-1);
1179 return (0);
1180 }
1181
1182 static int
hunt(int prt,char rex[],long start,int down,int wrap,int errsok)1183 hunt(int prt, char rex[], long start, int down, int wrap, int errsok)
1184 {
1185 long i, end1, incr;
1186 long start1, start2;
1187 char *line = NULL;
1188 size_t linesize = 0;
1189 char *rebuf;
1190
1191 if (down) {
1192 start1 = start + 1;
1193 end1 = Dollar;
1194 start2 = 1;
1195 incr = 1;
1196 } else {
1197 start1 = start - 1;
1198 end1 = 1;
1199 start2 = Dollar;
1200 incr = -1;
1201 }
1202
1203 rebuf = compile(rex, NULL, NULL);
1204 if (regerrno)
1205 regerr(regerrno);
1206
1207 for (i = start1; i != end1+incr; i += incr) {
1208 bigread(i, &line, &linesize);
1209 if (step(line, rebuf)) {
1210 free(rebuf);
1211 free(line);
1212 return (i);
1213 }
1214 }
1215
1216 if (!wrap) {
1217 free(rebuf);
1218 free(line);
1219 return (errsok?-1:err(prt, "not found"));
1220 }
1221
1222 for (i = start2; i != start1; i += incr) {
1223 bigread(i, &line, &linesize);
1224 if (step(line, rebuf)) {
1225 free(rebuf);
1226 free(line);
1227 return (i);
1228 }
1229 }
1230
1231 free(rebuf);
1232 free(line);
1233 return (errsok?-1:err(prt, "not found"));
1234 }
1235
1236 static int
jump(int prt,char label[])1237 jump(int prt, char label[])
1238 {
1239 size_t n;
1240 char *line = NULL;
1241 size_t linesize = 0;
1242 char *rebuf;
1243
1244 if (infildes == 0 && tty)
1245 return (err(prt, "jump on tty"));
1246 if (infildes == 100)
1247 intptr = internal;
1248 else
1249 lseek(infildes, 0L, 0);
1250
1251 snprintf(strtmp, sizeof strtmp, "^: *%s$", label);
1252 rebuf = compile(strtmp, NULL, NULL);
1253 if (regerrno) {
1254 regerr(regerrno);
1255 return (-1);
1256 }
1257
1258 n = 0;
1259 for (;;) {
1260 if (n >= linesize)
1261 line = grow(line, linesize += 256, "line too long");
1262 if (readc(infildes, &line[n]) == 0)
1263 break;
1264 if (line[n] == '\n') {
1265 line[n] = '\0';
1266 if (step(line, rebuf)) {
1267 charbuf = '\n';
1268 free(rebuf);
1269 free(line);
1270 return (peeked = 0);
1271 }
1272 n = 0;
1273 } else
1274 n++;
1275 }
1276
1277 free(line);
1278 free(rebuf);
1279 return (err(prt, "label not found"));
1280 }
1281
1282 #define check(t) \
1283 if ((t) - 1 >= *size) { \
1284 *buf = grow(*buf, *size += 100, "command too long"); \
1285 }
1286 static int
getstr(int prt,char ** buf,size_t * size,char brk,char ignr,int nonl)1287 getstr(int prt, char **buf, size_t *size, char brk, char ignr, int nonl)
1288 {
1289 char c, prevc;
1290 size_t n;
1291
1292 if (*buf == NULL)
1293 *buf = grow(*buf, *size = 100, "command too long");
1294 prevc = 0;
1295 for (n = 0; c = peekc(); prevc = c) {
1296 if (c == '\n') {
1297 if (prevc == '\\' && (!flag3)) {
1298 check(n-1);
1299 (*buf)[n-1] = mygetc();
1300 }
1301 else if (prevc == '\\' && flag3) {
1302 check(n);
1303 (*buf)[n++] = mygetc();
1304 } else if (nonl)
1305 break;
1306 else {
1307 check(n);
1308 return ((*buf)[n] = '\0');
1309 }
1310 } else {
1311 mygetc();
1312 if (c == brk) {
1313 if (prevc == '\\') {
1314 check(n-1);
1315 (*buf)[n-1] = c;
1316 }
1317 else {
1318 check(n);
1319 return ((*buf)[n] = '\0');
1320 }
1321 } else if (n != 0 || c != ignr) {
1322 check(n);
1323 (*buf)[n++] = c;
1324 }
1325 }
1326 }
1327 return (err(prt, "syntax"));
1328 }
1329
1330 static int
regerr(int c)1331 regerr(int c)
1332 {
1333 if (prompt) {
1334 switch (c) {
1335 case 11: printf("Range endpoint too large.\n");
1336 break;
1337 case 16: printf("Bad number.\n");
1338 break;
1339 case 25: printf("``\\digit'' out of range.\n");
1340 break;
1341 case 41: printf("No remembered search string.\n");
1342 break;
1343 case 42: printf("() imbalance.\n");
1344 break;
1345 case 43: printf("Too many (.\n");
1346 break;
1347 case 44: printf("More than 2 numbers given in { }.\n");
1348 break;
1349 case 45: printf("} expected after \\.\n");
1350 break;
1351 case 46: printf("First number exceeds second in { }.\n");
1352 break;
1353 case 49: printf("[] imbalance.\n");
1354 break;
1355 case 50: printf("Regular expression overflow.\n");
1356 break;
1357 case 67: printf("Illegal byte sequence.\n");
1358 break;
1359 default: printf("RE error.\n");
1360 break;
1361 }
1362 } else {
1363 printf("?\n");
1364 }
1365 return (-1);
1366 }
1367
1368 static int
err(int prt,const char msg[])1369 err(int prt, const char msg[])
1370 {
1371 if (prt) (prompt? printf("%s\n", msg): printf("?\n"));
1372 if (infildes != 0) {
1373 infildes = pop(fstack);
1374 charbuf = '\n';
1375 peeked = 0;
1376 flag3 = 0;
1377 flag2 = 0;
1378 flag = 0;
1379 }
1380 return (-1);
1381 }
1382
1383 static char
mygetc(void)1384 mygetc(void)
1385 {
1386 if (!peeked) {
1387 while ((!(infildes == oldfd && flag)) && (!flag1) &&
1388 (!readc(infildes, &charbuf))) {
1389 if (infildes == 100 && (!flag)) flag1 = 1;
1390 if ((infildes = pop(fstack)) == -1) quit();
1391 if ((!flag1) && infildes == 0 && flag3 && prompt)
1392 printf("*");
1393 }
1394 if (infildes == oldfd && flag) flag2 = 0;
1395 flag1 = 0;
1396 } else peeked = 0;
1397 return (charbuf);
1398 }
1399
1400 static int
readc(int f,char * c)1401 readc(int f, char *c)
1402 {
1403 if (f == 100) {
1404 if (!(*c = *intptr++)) {
1405 intptr--;
1406 charbuf = '\n';
1407 return (0);
1408 }
1409 } else if (read(f, c, 1) != 1) {
1410 close(f);
1411 charbuf = '\n';
1412 return (0);
1413 }
1414 return (1);
1415 }
1416
1417 static int
percent(char ** line,size_t * linesize)1418 percent(char **line, size_t *linesize)
1419 {
1420 char *var;
1421 char *front, *per, c[2], *olp, p[2], *fr = NULL;
1422 int i, j;
1423
1424 per = p;
1425 var = c;
1426 j = 0;
1427 while (!j) {
1428 fr = grow(fr, strlen(*line) + 1, "command too long");
1429 front = fr;
1430 j = 1;
1431 olp = *line;
1432 intptr = internal;
1433 while (step(olp, perbuf)) {
1434 while (loc1 < loc2) *front++ = *loc1++;
1435 *(--front) = '\0';
1436 front = fr;
1437 *per++ = '%';
1438 *per = '\0';
1439 per = p;
1440 *var = *loc2;
1441 if ((i = 1 + strlen(front)) >= 2 && fr[i-2] == '\\') {
1442 intcat(front);
1443 --intptr;
1444 intcat(per);
1445 } else {
1446 if (!(*var >= '0' && *var <= '9')) {
1447 free(fr);
1448 return (err(1, "usage: %digit"));
1449 }
1450 intcat(front);
1451 intcat(varray[*var-'0']);
1452 j = 0;
1453 loc2++; /* Compensate for removing --lp above */
1454 }
1455 olp = loc2;
1456 }
1457 intcat(olp);
1458 if (!j) {
1459 intptr = internal;
1460 i = 0;
1461 do {
1462 if (i >= *linesize)
1463 *line = grow(*line, *linesize += 100,
1464 "command too long");
1465 (*line)[i] = intptr[i];
1466 } while (intptr[i++]);
1467 }
1468 }
1469 free(fr);
1470 return (0);
1471 }
1472
1473 static int
newfile(int prt,char f[])1474 newfile(int prt, char f[])
1475 {
1476 int fd;
1477
1478 if (!*f) {
1479 if (flag != 0) {
1480 oldfd = infildes;
1481 intptr = comdlist;
1482 } else intptr = internal;
1483 fd = 100;
1484 } else if ((fd = open(f, O_RDONLY)) < 0) {
1485 snprintf(strtmp, sizeof strtmp, "cannot open %s", f);
1486 return (err(prt, strtmp));
1487 }
1488
1489 push(fstack, infildes);
1490 if (flag4) oldfd = fd;
1491 infildes = fd;
1492 return (peeked = 0);
1493 }
1494
1495 static void
push(int s[],int d)1496 push(int s[], int d)
1497 {
1498 s[++s[0]] = d;
1499 }
1500
1501 static int
pop(int s[])1502 pop(int s[])
1503 {
1504 return (s[s[0]--]);
1505 }
1506
1507 static int
peekc(void)1508 peekc(void)
1509 {
1510 int c;
1511
1512 c = mygetc();
1513 peeked = 1;
1514
1515 return (c);
1516 }
1517
1518 static void
eat(void)1519 eat(void)
1520 {
1521 if (charbuf != '\n')
1522 while (mygetc() != '\n');
1523 peeked = 0;
1524 }
1525
1526 static int
more(void)1527 more(void)
1528 {
1529 if (mygetc() != '\n')
1530 return (err(1, "syntax"));
1531 return (0);
1532 }
1533
1534 static void
quit(void)1535 quit(void)
1536 {
1537 exit(errcnt);
1538 }
1539
1540 static void
out(char * ln,long length)1541 out(char *ln, long length)
1542 {
1543 char *rp, *wp, prev;
1544 int w, width;
1545 char *oldrp;
1546 wchar_t cl;
1547 int p;
1548 long lim;
1549 if (crunch > 0) {
1550
1551 ln = untab(ln);
1552 rp = wp = ln - 1;
1553 prev = ' ';
1554
1555 while (*++rp) {
1556 if (prev != ' ' || *rp != ' ')
1557 *++wp = *rp;
1558 prev = *rp;
1559 }
1560 *++wp = '\n';
1561 lim = wp - ln;
1562 *++wp = '\0';
1563
1564 if (*ln == '\n')
1565 return;
1566 } else
1567 ln[lim = length] = '\n';
1568
1569 if (trunc < 0)
1570 /*EMPTY*/;
1571 else if (mb_cur_max <= 1) {
1572 if (lim > trunc)
1573 ln[lim = trunc] = '\n';
1574 } else {
1575 if (lim > trunc) {
1576 w = 0;
1577 oldrp = rp = ln;
1578 while (rp < &ln[lim]) {
1579 p = mbtowc(&cl, rp, mb_cur_max);
1580 if (p == -1) {
1581 width = p = 1;
1582 } else {
1583 width = wcwidth(cl);
1584 if (width < 0)
1585 width = 1;
1586 }
1587 if ((w += width) > trunc)
1588 break;
1589 rp += p;
1590 }
1591 *rp = '\n';
1592 lim = rp - oldrp;
1593 }
1594 }
1595
1596 w = 0;
1597 while (w < lim+1) {
1598 if ((p = write(outfildes, &ln[w], lim+1)) < 0) {
1599 if (errno == EAGAIN)
1600 continue;
1601 puts("i/o error");
1602 errcnt = 1;
1603 break;
1604 }
1605 w += p;
1606 }
1607 outcnt += w;
1608 }
1609
1610 static char *
untab(const char * l)1611 untab(const char *l)
1612 {
1613 static char *line;
1614 static size_t linesize;
1615 const char *s;
1616 long q;
1617
1618 free(line);
1619 line = NULL;
1620 linesize = 0;
1621 s = l;
1622 q = 0;
1623 do {
1624 if (q + 9 >= linesize)
1625 line = grow(line, linesize += 256, "line too long");
1626 if (*s == '\t')
1627 do
1628 line[q++] = ' ';
1629 while (q%8);
1630 else line[q++] = *s;
1631 } while (*s++);
1632 return (line);
1633 }
1634
1635 /*
1636 * Function to convert ascii string to integer. Converts
1637 * positive numbers only. Returns -1 if non-numeric
1638 * character encountered.
1639 */
1640
1641 static int
patoi(const char * b)1642 patoi(const char *b)
1643 {
1644 int i;
1645 const char *a;
1646
1647 a = b;
1648 i = 0;
1649 while (*a >= '0' && *a <= '9') i = 10 * i + *a++ - '0';
1650
1651 if (*a)
1652 return (-1);
1653 return (i);
1654 }
1655
1656 /*
1657 * Compares 2 strings. Returns 1 if equal, 0 if not.
1658 */
1659
1660 static int
equal(const char * a,const char * b)1661 equal(const char *a, const char *b)
1662 {
1663 const char *x, *y;
1664
1665 x = a;
1666 y = b;
1667 while (*x == *y++)
1668 if (*x++ == 0)
1669 return (1);
1670 return (0);
1671 }
1672
1673 static void
intcat(const char * cp)1674 intcat(const char *cp)
1675 {
1676 size_t diff;
1677
1678 if (cp == NULL)
1679 return;
1680 do {
1681 if ((diff = intptr - internal) >= internalsize) {
1682 internal = grow(internal, internalsize += 100,
1683 "command too long");
1684 intptr = &internal[diff];
1685 }
1686 } while (*intptr++ = *cp++);
1687 intptr--;
1688 }
1689
1690 static void *
grow(void * ptr,size_t size,const char * msg)1691 grow(void *ptr, size_t size, const char *msg)
1692 {
1693 if ((ptr = realloc(ptr, size)) == NULL) {
1694 err(1, msg);
1695 _exit(077);
1696 }
1697 return ptr;
1698 }
1699