heirloom-ed

view ed.c @ 4:4165f1b57d18

Become SUSv3 compatible and thus remove own regexp code The Heirloom tools can be compiled to comply to several standards. This version does not need this flexibility. We can omit the regexp code and use the system's, by using the SU3 variant of ed. This is the latest of the supported standards.
author markus schnalke <meillo@marmaro.de>
date Mon, 13 Apr 2015 17:26:51 +0200
parents ac52712b2b5e
children
line source
1 /*
2 * Editor
3 */
5 /*
6 * Changes by Gunnar Ritter, Freiburg i. Br., Germany, July 2003.
7 */
8 /* from Unix 32V /usr/src/cmd/ed.c */
9 /*
10 * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * Redistributions of source code and documentation must retain the
16 * above copyright notice, this list of conditions and the following
17 * disclaimer.
18 * Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed or owned by Caldera
24 * International, Inc.
25 * Neither the name of Caldera International, Inc. nor the names of
26 * other contributors may be used to endorse or promote products
27 * derived from this software without specific prior written permission.
28 *
29 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
30 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
31 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
34 * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
37 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
38 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
39 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
40 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 */
43 #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
44 #define USED __attribute__ ((used))
45 #elif defined __GNUC__
46 #define USED __attribute__ ((unused))
47 #else
48 #define USED
49 #endif
50 static const char sccsid[] USED = "@(#)ed_su3.sl 1.99 (gritter) 7/27/06";
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/wait.h>
55 #include <sys/stat.h>
56 #include <fcntl.h>
57 #include <unistd.h>
58 #include <time.h>
59 #include <string.h>
60 #include <stdlib.h>
61 #include <signal.h>
62 #include "sigset.h"
63 #include <setjmp.h>
64 #include <libgen.h>
65 #include <inttypes.h>
66 #include <locale.h>
67 #include <wchar.h>
68 #include <ctype.h>
69 #include <wctype.h>
70 #include <limits.h>
71 static int FNSIZE;
72 static int LBSIZE;
73 static int RHSIZE;
74 #define ESIZE 2048
75 static int GBSIZE;
76 #undef EOF
77 #define EOF -1
78 #define puts(s) xxputs(s)
79 #define getline(t, n) xxgetline(t, n)
81 #if (LONG_MAX > 017777777777L)
82 #define MAXCNT 0777777777777777777777L /* 2^63-1 */
83 #else
84 #define MAXCNT 017777777777L /* 2^31-1 */
85 #endif
86 #define BLKMSK (MAXCNT>>8) /* was 0377 */
88 #define READ 0
89 #define WRITE 1
90 #define EXIST 2
92 static int peekc;
93 static int lastc;
94 static char *savedfile;
95 static char *file;
96 static struct stat fstbuf;
97 static char *linebuf;
98 static char *rhsbuf;
99 static char expbuf[ESIZE + 4];
100 static long *zero;
101 static long *undzero;
102 static long *dot;
103 static long *unddot;
104 static long *dol;
105 static long *unddol;
106 static long *addr1;
107 static long *addr2;
108 static char *genbuf;
109 static long count;
110 static char *linebp;
111 static int ninbuf;
112 static int io;
113 static int ioeof;
114 static int pflag;
115 static char *wrtemp;
116 static uid_t myuid;
117 static void (*oldhup)(int);
118 static void (*oldquit)(int);
119 static void (*oldpipe)(int);
120 static int vflag = 1;
121 static int listf;
122 static int numbf;
123 static char *globp;
124 static int tfile = -1;
125 static long tline;
126 static char tfname[64];
127 static char ibuff[512];
128 static int iblock = -1;
129 static char obuff[512];
130 static int oblock = -1;
131 static int ichanged;
132 static int nleft;
133 static long *names;
134 static long *undnames;
135 static int anymarks;
136 static int subnewa;
137 static int fchange;
138 static int wrapp;
139 static unsigned nlall = 128;
140 static const char *progname;
141 static const char *prompt = "*";
142 static int Pflag;
143 static int prhelp;
144 static const char *prvmsg;
145 static int lastsig;
146 static int pipid = -1;
147 static int readop;
148 static int status;
149 static int mb_cur_max;
150 static int needsub;
151 static int insub;
152 static int maxlength;
153 static int rspec;
154 static int Nflag;
155 static int bcount = 22;
156 static int ocount = 11;
158 static jmp_buf savej;
160 static void usage(char);
161 static void commands(void);
162 static long *address(void);
163 static void setdot(void);
164 static void setall(void);
165 static void setnoaddr(void);
166 static void nonzero(void);
167 static void newline(void);
168 static void filename(int);
169 static void exfile(void);
170 static void onintr(int);
171 static void onhup(int);
172 static void onpipe(int);
173 static void error(const char *);
174 static void error2(const char *, const char *);
175 static void errput(const char *, const char *);
176 static int getchr(void);
177 static int gettty(void);
178 static long getnum(void);
179 static int getfile(void);
180 static void putfile(void);
181 static int append(int (*)(void), long *);
182 static void callunix(void);
183 static char *readcmd(void);
184 static void quit(int);
185 static void delete(void);
186 static void rdelete(long *, long *);
187 static void gdelete(void);
188 static char *getline(long, int);
189 static int putline(void);
190 static char *getblock(long, long);
191 static void blkio(long, char *, int);
192 static void init(void);
193 static void global(int, int);
194 static void globrd(char **, int);
195 static void join(void);
196 static void substitute(int);
197 static int compsub(void);
198 static int getsub(void);
199 static int dosub(int);
200 static int place(int, const char *, const char *);
201 static void move(int);
202 static void reverse(long *, long *);
203 static int getcopy(void);
204 static int execute(int, long *, int);
205 static void cmplerr(int);
206 static void doprnt(long *, long *);
207 static void putd(long);
208 static void puts(const char *);
209 static void nlputs(const char *);
210 static void list(const char *);
211 static int lstchr(int);
212 static void putstr(const char *);
213 static void putchr(int);
214 static void checkpoint(void);
215 static void undo(void);
216 static int maketf(int);
217 static int creatf(const char *);
218 static int sopen(const char *, int);
219 static void sclose(int);
220 static void growlb(const char *);
221 static void growrhs(const char *);
222 static void growfn(const char *);
223 static void help(void);
225 #define INIT
226 #define GETC() getchr()
227 #define UNGETC(c) (peekc = c)
228 #define PEEKC() (peekc = getchr())
229 #define RETURN(c) return c
230 #define ERROR(c) cmplerr(c)
231 static wint_t GETWC(char *);
233 #include <regex.h>
235 #define NBRA 9
237 static char *braslist[NBRA];
238 static char *braelist[NBRA];
239 static char *loc1, *loc2, *locs;
240 static int nbra;
241 static int circf;
242 static int nodelim;
244 static char *compile(char *, char *, const char *, int);
245 static int step(const char *, const char *);
247 int
248 main(int argc, char **argv)
249 {
250 register int i;
251 void (*oldintr)(int);
253 progname = basename(argv[0]);
254 setlocale(LC_COLLATE, "");
255 setlocale(LC_CTYPE, "");
256 mb_cur_max = MB_CUR_MAX;
257 myuid = getuid();
258 oldquit = sigset(SIGQUIT, SIG_IGN);
259 oldhup = sigset(SIGHUP, SIG_IGN);
260 oldintr = sigset(SIGINT, SIG_IGN);
261 if (sigset(SIGTERM, SIG_IGN) != SIG_IGN)
262 sigset(SIGTERM, quit);
263 oldpipe = sigset(SIGPIPE, onpipe);
264 argv++;
265 if (argc > 1 && **argv=='-') {
266 if ((*argv)[1]=='\0') {
267 vflag = 0;
268 } else {
269 usage((*argv)[1]);
270 }
271 argv++;
272 argc--;
273 }
275 growfn("no space");
276 if (argc>1) {
277 i = -1;
278 do
279 if (++i >= FNSIZE)
280 growfn("maximum of characters in "
281 "file names reached");
282 while (savedfile[i] = (*argv)[i]);
283 globp = "e";
284 }
285 names = malloc(26*sizeof *names);
286 undnames = malloc(26*sizeof *undnames);
287 zero = malloc(nlall*sizeof *zero);
288 if ((undzero = malloc(nlall*sizeof *undzero)) == NULL)
289 puts("no memory for undo");
290 growlb("no space");
291 growrhs("no space");
292 init();
293 if (oldintr != SIG_IGN)
294 sigset(SIGINT, onintr);
295 if (oldhup != SIG_IGN)
296 sigset(SIGHUP, onhup);
297 setjmp(savej);
298 if (lastsig) {
299 sigrelse(lastsig);
300 lastsig = 0;
301 }
302 commands();
303 quit(0);
304 /*NOTREACHED*/
305 return 0;
306 }
308 static void
309 usage(char c)
310 {
311 if (c) {
312 write(2, progname, strlen(progname));
313 write(2, ": illegal option -- ", 20);
314 write(2, &c, 1);
315 write(2, "\n", 1);
316 }
317 write(2, "usage: ", 7);
318 write(2, progname, strlen(progname));
319 write(2, " [-] [file]\n", 12);
320 exit(2);
321 }
323 static void
324 commands(void)
325 {
326 register long *a1;
327 register int c;
328 int n;
330 for (;;) {
331 if (pflag) {
332 pflag = 0;
333 addr1 = addr2 = dot;
334 goto print;
335 }
336 if (Pflag && globp == NULL)
337 write(1, prompt, strlen(prompt));
338 addr1 = 0;
339 addr2 = 0;
340 switch (c = getchr()) {
341 case ',':
342 case ';':
343 addr2 = c == ',' ? zero+1 : dot;
344 if (((peekc = getchr()) < '0' || peekc > '9') &&
345 peekc != ' ' && peekc != '\t' &&
346 peekc != '+' && peekc != '-' &&
347 peekc != '^' && peekc != '?' &&
348 peekc != '/' && peekc != '$' &&
349 peekc != '.' && peekc != '\'') {
350 addr1 = addr2;
351 a1 = dol;
352 goto loop;
353 }
354 break;
355 default:
356 peekc = c;
357 }
358 do {
359 addr1 = addr2;
360 if ((a1 = address())==0) {
361 c = getchr();
362 break;
363 }
364 loop: addr2 = a1;
365 if ((c=getchr()) == ';') {
366 c = ',';
367 dot = a1;
368 }
369 } while (c==',');
370 if (addr1==0)
371 addr1 = addr2;
372 switch(c) {
374 case 'a':
375 setdot();
376 newline();
377 checkpoint();
378 append(gettty, addr2);
379 continue;
381 case 'c':
382 if (addr1 == zero && addr1+1 <= dol) {
383 if (addr1 == addr2)
384 addr2++;
385 addr1++;
386 }
387 delete();
388 append(gettty, addr1-1);
389 if (dot == addr1-1 && addr1 <= dol)
390 dot = addr1;
391 continue;
393 case 'd':
394 delete();
395 continue;
397 case 'E':
398 fchange = 0;
399 c = 'e';
400 case 'e':
401 setnoaddr();
402 if (vflag && fchange) {
403 fchange = 0;
404 error("warning: expecting `w'");
405 }
406 filename(c);
407 init();
408 addr2 = zero;
409 goto caseread;
411 case 'f':
412 setnoaddr();
413 filename(c);
414 puts(savedfile);
415 continue;
417 case 'g':
418 global(1, 0);
419 continue;
421 case 'G':
422 global(1, 1);
423 continue;
425 case 'H':
426 prhelp = !prhelp;
427 /*FALLTHRU*/
429 case 'h':
430 if ((peekc = getchr()) == 'e') {
431 peekc = 0;
432 if (getchr() != 'l' || getchr() != 'p' ||
433 getchr() != '\n')
434 error("illegal suffix");
435 setnoaddr();
436 help();
437 continue;
438 }
439 newline();
440 setnoaddr();
441 if (prvmsg)
442 puts(prvmsg);
443 continue;
445 case 'i':
446 setdot();
447 if (addr1 == zero) {
448 if (addr1 == addr2)
449 addr2++;
450 addr1++;
451 if (dol != zero)
452 nonzero();
453 } else
454 nonzero();
455 newline();
456 checkpoint();
457 append(gettty, addr2-1);
458 if (dot == addr2-1)
459 dot++;
460 continue;
463 case 'j':
464 if (addr2==0) {
465 addr1 = dot;
466 addr2 = dot+1;
467 }
468 setdot();
469 newline();
470 nonzero();
471 checkpoint();
472 if (addr1 != addr2)
473 join();
474 continue;
476 case 'k':
477 if ((c = getchr()) < 'a' || c > 'z')
478 error("mark not lower case");
479 newline();
480 setdot();
481 nonzero();
482 names[c-'a'] = *addr2 & ~01;
483 anymarks |= 01;
484 continue;
486 case 'm':
487 move(0);
488 continue;
490 case '\n':
491 if (addr2==0)
492 addr2 = dot+1;
493 addr1 = addr2;
494 goto print;
496 case 'n':
497 numbf = 1;
498 newline();
499 goto print;
501 case 'N':
502 newline();
503 setnoaddr();
504 Nflag = !Nflag;
505 continue;
507 case 'b':
508 case 'o':
509 n = getnum();
510 newline();
511 setdot();
512 nonzero();
513 if (n >= 0) {
514 if (c == 'b')
515 bcount = n;
516 else
517 ocount = n;
518 }
519 if (c == 'b') {
520 a1 = addr2+bcount > dol ? dol : addr2 + bcount;
521 doprnt(addr1, a1);
522 dot = a1;
523 } else {
524 a1 = addr2+ocount > dol ? dol : addr2 + ocount;
525 doprnt(addr2-ocount<zero+1?zero+1:addr2-ocount, a1);
526 dot = addr2;
527 }
528 continue;
530 case 'l':
531 listf++;
532 case 'p':
533 newline();
534 print:
535 setdot();
536 nonzero();
537 doprnt(addr1, addr2);
538 dot = addr2;
539 continue;
541 case 'P':
542 setnoaddr();
543 newline();
544 Pflag = !Pflag;
545 continue;
547 case 'Q':
548 fchange = 0;
549 case 'q':
550 setnoaddr();
551 newline();
552 quit(0);
554 case 'r':
555 filename(c);
556 caseread:
557 if ((io = sopen(file, READ)) < 0) {
558 lastc = '\n';
559 error2("cannot open input file", file);
560 }
561 ioeof = 0;
562 setall();
563 ninbuf = 0;
564 if (c == 'r')
565 checkpoint();
566 n = zero != dol;
567 rspec = (c == 'e' || !n) && file[0] != '!';
568 append(getfile, addr2);
569 rspec = 0;
570 exfile();
571 fchange = n;
572 continue;
574 case 's':
575 setdot();
576 nonzero();
577 substitute(globp!=0);
578 continue;
580 case 't':
581 move(1);
582 continue;
584 case 'u':
585 setdot();
586 newline();
587 if (unddot == NULL)
588 error("nothing to undo");
589 undo();
590 continue;
592 case 'v':
593 global(0, 0);
594 continue;
596 case 'V':
597 global(0, 1);
598 continue;
600 case 'W':
601 wrapp++;
602 case 'w':
603 write:
604 setall();
605 if (zero != dol)
606 nonzero();
607 filename(c);
608 if(!wrapp ||
609 ((io = open(file,O_WRONLY|O_APPEND)) == -1) ||
610 ((lseek(io, 0, SEEK_END)) == -1)) {
611 struct stat st;
612 if (lstat(file, &st) == 0 &&
613 (st.st_mode&S_IFMT) == S_IFREG &&
614 st.st_nlink == 1 &&
615 (myuid==0 || myuid==st.st_uid)) {
616 char *cp, *tp;
617 int nio;
618 if ((io = sopen(file, EXIST)) < 0)
619 error("cannot create output file");
620 if ((wrtemp = malloc(strlen(file)+8)) == NULL)
621 error("out of memory");
622 for (cp = file, tp = wrtemp; *cp; cp++)
623 *tp++ = *cp;
624 while (tp > wrtemp && tp[-1] != '/')
625 tp--;
626 for (cp = "\7XXXXXX"; *cp; cp++)
627 *tp++ = *cp;
628 *tp = '\0';
629 if ((nio = mkstemp(wrtemp)) < 0) {
630 free(wrtemp);
631 wrtemp = NULL;
632 ftruncate(io, 0);
633 } else {
634 close(io);
635 io = nio;
636 }
637 } else {
638 if ((io = sopen(file, WRITE)) < 0)
639 error("cannot create output file");
640 }
641 }
642 if (zero != dol) {
643 ioeof = 0;
644 wrapp = 0;
645 putfile();
646 }
647 exfile();
648 if (addr1==zero+1 && addr2==dol || addr1==addr2 && dol==zero)
649 fchange = 0;
650 if (c == 'z')
651 quit(0);
652 continue;
654 case 'z':
655 if ((peekc=getchr()) != '\n')
656 error("illegal suffix");
657 setnoaddr();
658 goto write;
660 case '=':
661 setall();
662 newline();
663 putd((addr2-zero)&MAXCNT);
664 putchr('\n');
665 continue;
667 case '!':
668 callunix();
669 continue;
671 case EOF:
672 return;
674 }
675 error("unknown command");
676 }
677 }
679 static long *
680 address(void)
681 {
682 register long *a1;
683 register int minus, c;
684 int n, relerr;
686 minus = 0;
687 a1 = 0;
688 for (;;) {
689 c = getchr();
690 if ('0'<=c && c<='9') {
691 n = 0;
692 do {
693 n *= 10;
694 n += c - '0';
695 } while ((c = getchr())>='0' && c<='9');
696 peekc = c;
697 if (a1==0)
698 a1 = zero;
699 if (minus<0)
700 n = -n;
701 a1 += n;
702 minus = 0;
703 continue;
704 }
705 relerr = 0;
706 if (a1 || minus)
707 relerr++;
708 switch(c) {
709 case ' ':
710 case '\t':
711 continue;
713 case '+':
714 minus++;
715 if (a1==0)
716 a1 = dot;
717 continue;
719 case '-':
720 case '^':
721 minus--;
722 if (a1==0)
723 a1 = dot;
724 continue;
726 case '?':
727 case '/':
728 compile(NULL, expbuf, &expbuf[ESIZE], c);
729 a1 = dot;
730 for (;;) {
731 if (c=='/') {
732 a1++;
733 if (a1 > dol)
734 a1 = zero;
735 } else {
736 a1--;
737 if (a1 < zero)
738 a1 = dol;
739 }
740 if (execute(0, a1, 0))
741 break;
742 if (a1==dot)
743 error("search string not found");
744 }
745 break;
747 case '$':
748 a1 = dol;
749 break;
751 case '.':
752 a1 = dot;
753 break;
755 case '\'':
756 if ((c = getchr()) < 'a' || c > 'z')
757 error("mark not lower case");
758 for (a1=zero; a1<=dol; a1++)
759 if (names[c-'a'] == (*a1 & ~01))
760 break;
761 break;
763 default:
764 peekc = c;
765 if (a1==0)
766 return(0);
767 a1 += minus;
768 if (a1<zero || a1>dol)
769 error("line out of range");
770 return(a1);
771 }
772 if (relerr)
773 error("bad number");
774 }
775 }
777 static void
778 setdot(void)
779 {
780 if (addr2 == 0)
781 addr1 = addr2 = dot;
782 if (addr1 > addr2)
783 error("bad range");
784 }
786 static void
787 setall(void)
788 {
789 if (addr2==0) {
790 addr1 = zero+1;
791 addr2 = dol;
792 if (dol==zero)
793 addr1 = zero;
794 }
795 setdot();
796 }
798 static void
799 setnoaddr(void)
800 {
801 if (addr2)
802 error("Illegal address count");
803 }
805 static void
806 nonzero(void)
807 {
808 if (addr1<=zero || addr2>dol)
809 error("line out of range");
810 }
812 static void
813 newline(void)
814 {
815 register int c;
817 if ((c = getchr()) == '\n')
818 return;
819 if (c=='p' || c=='l' || c=='n') {
820 pflag++;
821 if (c=='l')
822 listf++;
823 else if (c=='n')
824 numbf = 1;
825 if (getchr() == '\n')
826 return;
827 }
828 error("illegal suffix");
829 }
831 static void
832 filename(int comm)
833 {
834 register char *p1, *p2;
835 register int c, i;
837 count = 0;
838 c = getchr();
839 if (c=='\n' || c==EOF) {
840 p1 = savedfile;
841 if (*p1==0 && comm!='f')
842 error("illegal or missing filename");
843 p2 = file;
844 while (*p2++ = *p1++)
845 ;
846 return;
847 }
848 if (c!=' ')
849 error("no space after command");
850 while ((c = getchr()) == ' ')
851 ;
852 if (c=='\n')
853 error("illegal or missing filename");
854 i = 0;
855 do {
856 if (i >= FNSIZE)
857 growfn("maximum of characters in file names reached");
858 file[i++] = c;
859 if (c==' ' && file[0] != '!' || c==EOF)
860 error("illegal or missing filename");
861 } while ((c = getchr()) != '\n');
862 file[i++] = 0;
863 if ((savedfile[0]==0 || comm=='e' || comm=='f') && file[0] != '!') {
864 p1 = savedfile;
865 p2 = file;
866 while (*p1++ = *p2++)
867 ;
868 }
869 }
871 static void
872 exfile(void)
873 {
874 sclose(io);
875 io = -1;
876 if (wrtemp) {
877 extern int rename(const char *, const char *);
878 if (rename(wrtemp, file) < 0)
879 error("cannot create output file");
880 if (myuid == 0)
881 chown(file, fstbuf.st_uid, fstbuf.st_gid);
882 chmod(file, fstbuf.st_mode & 07777);
883 free(wrtemp);
884 wrtemp = NULL;
885 }
886 if (vflag) {
887 putd(count);
888 putchr('\n');
889 }
890 }
892 static void
893 onintr(int signo)
894 {
895 lastsig = signo;
896 putchr('\n');
897 lastc = '\n';
898 if (readop) {
899 puts("\007read may be incomplete - beware!\007");
900 fchange = 0;
901 }
902 error("interrupt");
903 }
905 static void
906 onhup(int signo)
907 {
908 if (dol > zero && fchange) {
909 addr1 = zero+1;
910 addr2 = dol;
911 io = creat("ed.hup", 0666);
912 if (io < 0) {
913 char *home = getenv("HOME");
914 if (home) {
915 char *fn = malloc(strlen(home) + 10);
916 if (fn) {
917 strcpy(fn, home);
918 strcat(fn, "/ed.hup");
919 io = creat(fn, 0666);
920 }
921 }
922 }
923 if (io >= 0)
924 putfile();
925 }
926 fchange = 0;
927 status = 0200 | signo;
928 quit(0);
929 }
931 static void
932 onpipe(int signo)
933 {
934 lastsig = signo;
935 error("write or open on pipe failed");
936 }
938 static void
939 error(const char *s)
940 {
941 error2(s, NULL);
942 }
944 static void
945 error2(const char *s, const char *fn)
946 {
947 register int c;
949 wrapp = 0;
950 listf = 0;
951 numbf = 0;
952 errput(s, fn);
953 count = 0;
954 if (lseek(0, 0, SEEK_END) > 0)
955 status = 2;
956 pflag = 0;
957 if (globp)
958 lastc = '\n';
959 globp = 0;
960 peekc = lastc;
961 if(lastc)
962 while ((c = getchr()) != '\n' && c != EOF)
963 ;
964 if (io > 0) {
965 sclose(io);
966 io = -1;
967 }
968 if (wrtemp) {
969 unlink(wrtemp);
970 free(wrtemp);
971 wrtemp = NULL;
972 }
973 longjmp(savej, 1);
974 }
976 static void
977 errput(const char *s, const char *fn)
978 {
979 prvmsg = s;
980 if (fn) {
981 putchr('?');
982 puts(fn);
983 } else
984 puts("?");
985 if (prhelp)
986 puts(s);
987 }
989 static int
990 getchr(void)
991 {
992 char c;
993 if (lastc=peekc) {
994 peekc = 0;
995 return(lastc);
996 }
997 if (globp) {
998 if ((lastc = *globp++) != 0)
999 return(lastc);
1000 globp = 0;
1001 return(EOF);
1003 if (read(0, &c, 1) <= 0)
1004 return(lastc = EOF);
1005 lastc = c;
1006 return(lastc);
1009 static int
1010 gettty(void)
1012 register int c, i;
1013 register char *gf;
1015 i = 0;
1016 gf = globp;
1017 while ((c = getchr()) != '\n') {
1018 if (c==EOF) {
1019 if (gf)
1020 peekc = c;
1021 return(c);
1023 if (c == 0)
1024 continue;
1025 if (i >= LBSIZE)
1026 growlb("line too long");
1027 linebuf[i++] = c;
1029 if (i >= LBSIZE-2)
1030 growlb("line too long");
1031 linebuf[i++] = 0;
1032 if (linebuf[0]=='.' && linebuf[1]==0)
1033 return(EOF);
1034 return(0);
1037 static long
1038 getnum(void)
1040 char scount[20];
1041 int i;
1043 i = 0;
1044 while ((peekc=getchr()) >= '0' && peekc <= '9' && i < sizeof scount) {
1045 scount[i++] = peekc;
1046 peekc = 0;
1048 scount[i] = '\0';
1049 return i ? atol(scount) : -1;
1052 static int
1053 getfile(void)
1055 register int c, i, j;
1056 static int nextj;
1058 i = 0;
1059 j = nextj;
1060 do {
1061 if (--ninbuf < 0) {
1062 if (ioeof || (ninbuf=read(io, genbuf, LBSIZE)-1) < 0) {
1063 if (ioeof == 0 && ninbuf < -1) {
1064 puts("input error");
1065 status = 1;
1067 if (i > 0) {
1068 puts("'\\n' appended");
1069 c = '\n';
1070 ioeof = 1;
1071 goto wrc;
1073 return(EOF);
1075 j = 0;
1077 c = genbuf[j++]&0377;
1078 wrc: if (i >= LBSIZE) {
1079 lastc = '\n';
1080 growlb("line too long");
1082 linebuf[i++] = c ? c : '\n';
1083 count++;
1084 } while (c != '\n');
1085 linebuf[--i] = 0;
1086 nextj = j;
1087 if (maxlength && i > maxlength) {
1088 putstr("line too long: lno = ");
1089 putd((dot - zero+1)&MAXCNT);
1090 putchr('\n');
1092 return(0);
1095 static void
1096 putfile(void)
1098 long *a1;
1099 int n;
1100 register char *fp, *lp;
1101 register int nib;
1103 nib = 512;
1104 fp = genbuf;
1105 a1 = addr1;
1106 do {
1107 lp = getline(*a1++, 0);
1108 if (maxlength) {
1109 for (n = 0; lp[n]; n++);
1110 if (n > maxlength) {
1111 putstr("line too long: lno = ");
1112 putd((a1-1 - zero)&MAXCNT);
1113 putchr('\n');
1116 for (;;) {
1117 if (--nib < 0) {
1118 n = fp-genbuf;
1119 if(write(io, genbuf, n) != n)
1120 error("write error");
1121 nib = 511;
1122 fp = genbuf;
1124 count++;
1125 if ((*fp++ = *lp++) == 0) {
1126 fp[-1] = '\n';
1127 break;
1128 } else if (fp[-1] == '\n')
1129 fp[-1] = '\0';
1131 } while (a1 <= addr2);
1132 n = fp-genbuf;
1133 if(write(io, genbuf, n) != n)
1134 error("write error");
1137 static int
1138 append(int (*f)(void), long *a)
1140 register long *a1, *a2, *rdot;
1141 int nline, tl;
1143 nline = 0;
1144 dot = a;
1145 while ((*f)() == 0) {
1146 if ((dol-zero)+1 >= nlall) {
1147 long *ozero = zero;
1148 nlall += 512;
1149 if ((zero = realloc(zero, nlall*sizeof *zero))==NULL) {
1150 lastc = '\n';
1151 zero = ozero;
1152 error("out of memory for append");
1154 dot += zero - ozero;
1155 dol += zero - ozero;
1156 addr1 += zero - ozero;
1157 addr2 += zero - ozero;
1158 if (unddot) {
1159 unddot += zero - ozero;
1160 unddol += zero - ozero;
1162 if (undzero) {
1163 ozero = undzero;
1164 if ((undzero = realloc(undzero,
1165 nlall*sizeof *undzero)) == 0) {
1166 puts("no memory for undo");
1167 free(ozero);
1171 tl = putline();
1172 nline++;
1173 a1 = ++dol;
1174 a2 = a1+1;
1175 rdot = ++dot;
1176 while (a1 > rdot)
1177 *--a2 = *--a1;
1178 *rdot = tl;
1180 return(nline);
1183 static void
1184 callunix(void)
1186 char *line;
1187 void (*savint)(int);
1188 pid_t pid, rpid;
1189 int retcode;
1191 setnoaddr();
1192 line = readcmd();
1193 if ((pid = fork()) == 0) {
1194 sigset(SIGHUP, oldhup);
1195 sigset(SIGQUIT, oldquit);
1196 sigset(SIGPIPE, oldpipe);
1197 execl(SHELL, "sh", "-c", line, NULL);
1198 _exit(0100);
1199 } else if (pid < 0)
1200 error("fork failed - try again");
1201 savint = sigset(SIGINT, SIG_IGN);
1202 while ((rpid = wait(&retcode)) != pid && rpid != -1)
1204 sigset(SIGINT, savint);
1205 if (vflag)
1206 puts("!");
1209 #define cmadd(c) ((i>=cmsize ? \
1210 ((line=realloc(line,cmsize+=128)) == 0 ? \
1211 (error("line too long"),0) : 0, 0) \
1212 : 0), line[i++]=(c))
1214 static char *
1215 readcmd(void)
1217 static char *line, *prev;
1218 static int cmsize, pvsize;
1219 char *pp;
1220 int c, mod = 0, i;
1222 i = 0;
1223 if ((c = getchr()) == '!') {
1224 for (pp = prev; *pp; pp++)
1225 line[i++] = *pp;
1226 mod = 1;
1227 c = getchr();
1229 while (c != '\n' && c != EOF) {
1230 if (c == '\\') {
1231 c = getchr();
1232 if (c != '%')
1233 cmadd('\\');
1234 cmadd(c);
1235 } else if (c == '%') {
1236 for (pp = savedfile; *pp; pp++)
1237 cmadd(*pp);
1238 mod = 1;
1239 } else
1240 cmadd(c);
1241 c = getchr();
1243 cmadd('\0');
1244 if (pvsize < cmsize && (prev = realloc(prev, pvsize=cmsize)) == 0)
1245 error("line too long");
1246 strcpy(prev, line);
1247 if (mod)
1248 nlputs(line);
1249 return line;
1252 static void
1253 quit(int signo)
1255 lastsig = signo;
1256 if (vflag && fchange) {
1257 fchange = 0;
1258 error("warning: expecting `w'");
1260 if (wrtemp)
1261 unlink(wrtemp);
1262 unlink(tfname);
1263 exit(status);
1266 static void
1267 delete(void)
1269 setdot();
1270 newline();
1271 nonzero();
1272 checkpoint();
1273 rdelete(addr1, addr2);
1276 static void
1277 rdelete(long *ad1, long *ad2)
1279 register long *a1, *a2, *a3;
1281 a1 = ad1;
1282 a2 = ad2+1;
1283 a3 = dol;
1284 dol -= a2 - a1;
1285 do {
1286 *a1++ = *a2++;
1287 } while (a2 <= a3);
1288 a1 = ad1;
1289 if (a1 > dol)
1290 a1 = dol;
1291 dot = a1;
1292 fchange = 1;
1295 static void
1296 gdelete(void)
1298 register long *a1, *a2, *a3;
1300 a3 = dol;
1301 for (a1=zero+1; (*a1&01)==0; a1++)
1302 if (a1>=a3)
1303 return;
1304 for (a2=a1+1; a2<=a3;) {
1305 if (*a2&01) {
1306 a2++;
1307 dot = a1;
1308 } else
1309 *a1++ = *a2++;
1311 dol = a1-1;
1312 if (dot>dol)
1313 dot = dol;
1314 fchange = 1;
1317 static char *
1318 getline(long tl, int nulterm)
1320 register char *bp, *lp;
1321 register long nl;
1323 lp = linebuf;
1324 bp = getblock(tl, READ);
1325 nl = nleft;
1326 tl &= ~0377;
1327 while (*lp++ = *bp++) {
1328 if (lp[-1] == '\n' && nulterm) {
1329 lp[-1] = '\0';
1330 break;
1332 if (--nl == 0) {
1333 bp = getblock(tl+=0400, READ);
1334 nl = nleft;
1337 return(linebuf);
1340 static int
1341 putline(void)
1343 register char *bp, *lp;
1344 register long nl;
1345 long tl;
1347 fchange = 1;
1348 lp = linebuf;
1349 tl = tline;
1350 bp = getblock(tl, WRITE);
1351 nl = nleft;
1352 tl &= ~0377;
1353 while (*bp = *lp++) {
1354 if (*bp++ == '\n' && insub) {
1355 *--bp = 0;
1356 linebp = lp;
1357 break;
1359 if (--nl == 0) {
1360 bp = getblock(tl+=0400, WRITE);
1361 nl = nleft;
1364 nl = tline;
1365 tline += (((lp-linebuf)+03)>>1)&(MAXCNT-1);
1366 return(nl);
1369 static char *
1370 getblock(long atl, long iof)
1372 register long bno, off;
1374 bno = (atl>>8)&BLKMSK;
1375 off = (atl<<1)&0774;
1376 if (bno >= BLKMSK) {
1377 lastc = '\n';
1378 error("temp file too big");
1380 nleft = 512 - off;
1381 if (bno==iblock) {
1382 ichanged |= iof;
1383 return(ibuff+off);
1385 if (bno==oblock)
1386 return(obuff+off);
1387 if (iof==READ) {
1388 if (ichanged)
1389 blkio(iblock, ibuff, 1);
1390 ichanged = 0;
1391 iblock = bno;
1392 blkio(bno, ibuff, 0);
1393 return(ibuff+off);
1395 if (oblock>=0)
1396 blkio(oblock, obuff, 1);
1397 oblock = bno;
1398 return(obuff+off);
1401 static void
1402 blkio(long b, char *buf, int wr)
1404 lseek(tfile, b<<9, SEEK_SET);
1405 if ((wr ? write(tfile, buf, 512) : read (tfile, buf, 512)) != 512) {
1406 status = 1;
1407 error("I/O error on temp file");
1411 static void
1412 init(void)
1414 register long *markp;
1416 tline = 2;
1417 for (markp = names; markp < &names[26]; markp++)
1418 *markp = 0;
1419 for (markp = undnames; markp < &undnames[26]; markp++)
1420 *markp = 0;
1421 subnewa = 0;
1422 anymarks = 0;
1423 iblock = -1;
1424 oblock = -1;
1425 ichanged = 0;
1426 tfile = maketf(tfile);
1427 dot = dol = zero;
1428 unddot = NULL;
1431 static void
1432 global(int k, int ia)
1434 register int c;
1435 register long *a1;
1436 static char *globuf;
1437 char mb[MB_LEN_MAX+1];
1438 int spflag = 0;
1440 if (globp)
1441 error("multiple globals not allowed");
1442 setall();
1443 nonzero();
1444 if ((c=GETWC(mb))=='\n')
1445 error("incomplete global expression");
1446 compile(NULL, expbuf, &expbuf[ESIZE], c);
1447 if (!ia) {
1448 globrd(&globuf, EOF);
1449 if (globuf[0] == '\n')
1450 globuf[0] = 'p', globuf[1] = '\n', globuf[2] = '\0';
1451 } else {
1452 newline();
1453 spflag = pflag;
1454 pflag = 0;
1456 checkpoint();
1457 for (a1=zero; a1<=dol; a1++) {
1458 *a1 &= ~01;
1459 if (a1>=addr1 && a1<=addr2 && execute(0, a1, 0)==k)
1460 *a1 |= 01;
1462 /*
1463 * Special case: g/.../d (avoid n^2 algorithm)
1464 */
1465 if (!ia && globuf[0]=='d' && globuf[1]=='\n' && globuf[2]=='\0') {
1466 gdelete();
1467 return;
1469 for (a1=zero; a1<=dol; a1++) {
1470 if (*a1 & 01) {
1471 *a1 &= ~01;
1472 dot = a1;
1473 if (ia) {
1474 puts(getline(*a1, 0));
1475 if ((c = getchr()) == EOF)
1476 error("command expected");
1477 if (c == 'a' || c == 'c' || c == 'i')
1478 error("a, i, or c not allowed in G");
1479 else if (c == '&') {
1480 if ((c = getchr()) != '\n')
1481 error("end of line expected");
1482 if (globuf == 0 || *globuf == 0)
1483 error("no remembered command");
1484 } else if (c == '\n') {
1485 a1 = zero;
1486 continue;
1487 } else
1488 globrd(&globuf, c);
1490 globp = globuf;
1491 commands();
1492 globp = NULL;
1493 a1 = zero;
1496 if (ia)
1497 pflag = spflag;
1500 static void
1501 globrd(char **globuf, register int c)
1503 register int i;
1505 if (*globuf == 0 && (*globuf = malloc(GBSIZE=256)) == 0)
1506 error("global too long");
1507 i = 0;
1508 if (c != EOF)
1509 (*globuf)[i++] = c;
1510 while ((c = getchr()) != '\n') {
1511 if (c==EOF)
1512 error("incomplete global expression");
1513 if (c=='\\') {
1514 c = getchr();
1515 if (c!='\n')
1516 (*globuf)[i++] = '\\';
1518 (*globuf)[i++] = c;
1519 if (i>=GBSIZE-4 && (*globuf=realloc(*globuf,GBSIZE+=256)) == 0)
1520 error("global too long");
1522 (*globuf)[i++] = '\n';
1523 (*globuf)[i++] = 0;
1526 static void
1527 join(void)
1529 register int i, j;
1530 register long *a1;
1532 j = 0;
1533 for (a1=addr1; a1<=addr2; a1++) {
1534 i = getline(*a1, 0) - linebuf;
1535 while (genbuf[j] = linebuf[i++])
1536 if (j++ >= LBSIZE-2)
1537 growlb("line too long");
1539 i = 0;
1540 j = 0;
1541 while (linebuf[i++] = genbuf[j++])
1543 *addr1 = putline();
1544 if (addr1<addr2)
1545 rdelete(addr1+1, addr2);
1546 dot = addr1;
1549 static void
1550 substitute(int inglob)
1552 register long *markp;
1553 register long *a1;
1554 intptr_t nl;
1555 int gsubf;
1557 checkpoint();
1558 gsubf = compsub();
1559 insub = 1;
1560 for (a1 = addr1; a1 <= addr2; a1++) {
1561 long *ozero;
1562 if (execute(0, a1, 1)==0)
1563 continue;
1564 inglob |= dosub(gsubf < 2);
1565 if (gsubf) {
1566 int i = 1;
1568 while (*loc2) {
1569 if (execute(1, NULL, 1)==0)
1570 break;
1571 inglob |= dosub(gsubf == -1 || ++i == gsubf);
1574 subnewa = putline();
1575 *a1 &= ~01;
1576 if (anymarks) {
1577 for (markp = names; markp < &names[26]; markp++)
1578 if (*markp == *a1)
1579 *markp = subnewa;
1581 *a1 = subnewa;
1582 ozero = zero;
1583 nl = append(getsub, a1);
1584 nl += zero-ozero;
1585 a1 += nl;
1586 addr2 += nl;
1588 insub = 0;
1589 if (inglob==0)
1590 error("no match");
1593 static int
1594 compsub(void)
1596 register int seof, c, i;
1597 static char *oldrhs;
1598 static int orhssz;
1599 char mb[MB_LEN_MAX+1];
1601 if ((seof = GETWC(mb)) == '\n' || seof == ' ')
1602 error("illegal or missing delimiter");
1603 nodelim = 0;
1604 compile(NULL, expbuf, &expbuf[ESIZE], seof);
1605 i = 0;
1606 for (;;) {
1607 c = GETWC(mb);
1608 if (c=='\\') {
1609 if (i >= RHSIZE-2)
1610 growrhs("replacement string too long");
1611 rhsbuf[i++] = c;
1612 c = GETWC(mb);
1613 } else if (c=='\n') {
1614 if (globp && *globp) {
1615 if (i >= RHSIZE-2)
1616 growrhs("replacement string too long");
1617 rhsbuf[i++] = '\\';
1619 else if (nodelim)
1620 error("illegal or missing delimiter");
1621 else {
1622 peekc = c;
1623 pflag++;
1624 break;
1626 } else if (c==seof)
1627 break;
1628 for (c = 0; c==0 || mb[c]; c++) {
1629 if (i >= RHSIZE-2)
1630 growrhs("replacement string too long");
1631 rhsbuf[i++] = mb[c];
1634 rhsbuf[i++] = 0;
1635 if (rhsbuf[0] == '%' && rhsbuf[1] == 0) {
1636 if (orhssz == 0)
1637 error("no remembered replacement string");
1638 strcpy(rhsbuf, oldrhs);
1639 } else {
1640 if (orhssz < RHSIZE &&
1641 (oldrhs = realloc(oldrhs, orhssz=RHSIZE)) == 0)
1642 error("replacement string too long");
1643 strcpy(oldrhs, rhsbuf);
1645 if ((peekc = getchr()) == 'g') {
1646 peekc = 0;
1647 newline();
1648 return(-1);
1649 } else if (peekc >= '0' && peekc <= '9') {
1650 c = getnum();
1651 if (c < 1 || c > LBSIZE)
1652 error("invalid count");
1653 newline();
1654 return c;
1656 newline();
1657 return(0);
1660 static int
1661 getsub(void)
1663 register char *p1, *p2;
1665 p1 = linebuf;
1666 if ((p2 = linebp) == 0)
1667 return(EOF);
1668 while (*p1++ = *p2++)
1670 linebp = 0;
1671 return(0);
1674 static int
1675 dosub(int really)
1677 register char *lp, *sp;
1678 register int i, j, k;
1679 int c;
1681 if (!really)
1682 goto copy;
1683 i = 0;
1684 j = 0;
1685 k = 0;
1686 while (&linebuf[i] < loc1)
1687 genbuf[j++] = linebuf[i++];
1688 while (c = rhsbuf[k++]&0377) {
1689 if (c=='&') {
1690 j = place(j, loc1, loc2);
1691 continue;
1692 } else if (c == '\\') {
1693 c = rhsbuf[k++]&0377;
1694 if (c >='1' && c < nbra+'1') {
1695 j = place(j, braslist[c-'1'], braelist[c-'1']);
1696 continue;
1699 if (j >= LBSIZE)
1700 growlb("line too long");
1701 genbuf[j++] = c;
1703 i = loc2 - linebuf;
1704 loc2 = j + linebuf;
1705 if (loc1 == &linebuf[i]) {
1706 int n;
1707 wchar_t wc;
1708 if (mb_cur_max > 1 && (n = mbtowc(&wc, loc2, mb_cur_max)) > 0)
1709 loc2 += n;
1710 else
1711 loc2++;
1713 while (genbuf[j++] = linebuf[i++])
1714 if (j >= LBSIZE)
1715 growlb("line too long");
1716 if (really) {
1717 lp = linebuf;
1718 sp = genbuf;
1719 } else {
1720 copy: sp = linebuf;
1721 lp = genbuf;
1723 while (*lp++ = *sp++)
1725 return really;
1728 static int
1729 place(register int j, register const char *l1, register const char *l2)
1732 while (l1 < l2) {
1733 genbuf[j++] = *l1++;
1734 if (j >= LBSIZE)
1735 growlb("line too long");
1737 return(j);
1740 static void
1741 move(int cflag)
1743 register long *adt, *ad1, *ad2;
1745 setdot();
1746 nonzero();
1747 if ((adt = address())==0)
1748 error("illegal move destination");
1749 newline();
1750 checkpoint();
1751 if (cflag) {
1752 long *ozero;
1753 intptr_t delta;
1754 ad1 = dol;
1755 ozero = zero;
1756 append(getcopy, ad1++);
1757 ad2 = dol;
1758 delta = zero - ozero;
1759 ad1 += delta;
1760 adt += delta;
1761 } else {
1762 ad2 = addr2;
1763 for (ad1 = addr1; ad1 <= ad2;)
1764 *ad1++ &= ~01;
1765 ad1 = addr1;
1767 ad2++;
1768 if (adt<ad1) {
1769 dot = adt + (ad2-ad1);
1770 if ((++adt)==ad1)
1771 return;
1772 reverse(adt, ad1);
1773 reverse(ad1, ad2);
1774 reverse(adt, ad2);
1775 } else if (adt >= ad2) {
1776 dot = adt++;
1777 reverse(ad1, ad2);
1778 reverse(ad2, adt);
1779 reverse(ad1, adt);
1780 } else
1781 error("illegal move destination");
1782 fchange = 1;
1785 static void
1786 reverse(register long *a1, register long *a2)
1788 register int t;
1790 for (;;) {
1791 t = *--a2;
1792 if (a2 <= a1)
1793 return;
1794 *a2 = *a1;
1795 *a1++ = t;
1799 static int
1800 getcopy(void)
1802 if (addr1 > addr2)
1803 return(EOF);
1804 getline(*addr1++, 0);
1805 return(0);
1808 static int
1809 execute(int gf, long *addr, int subst)
1811 register char *p1, *p2, c;
1813 for (c=0; c<NBRA; c++) {
1814 braslist[c&0377] = 0;
1815 braelist[c&0377] = 0;
1817 if (gf) {
1818 if (circf)
1819 return(0);
1820 p1 = linebuf;
1821 p2 = genbuf;
1822 while (*p1++ = *p2++)
1824 locs = p1 = loc2;
1825 } else {
1826 if (addr==zero)
1827 return(0);
1828 p1 = getline(*addr, 1);
1829 locs = 0;
1831 needsub = subst;
1832 return step(p1, expbuf);
1835 static void
1836 cmplerr(int c)
1838 const char *msg;
1840 switch (c) {
1841 case 11:
1842 msg = "Range endpoint too large";
1843 break;
1844 case 16:
1845 msg = "bad number";
1846 break;
1847 case 25:
1848 msg = "`\\digit' out of range";
1849 break;
1850 case 36:
1851 msg = "illegal or missing delimiter";
1852 break;
1853 case 41:
1854 msg = "no remembered search string";
1855 break;
1856 case 42:
1857 msg = "'\\( \\)' imbalance";
1858 break;
1859 case 43:
1860 msg = "Too many `\\(' s";
1861 break;
1862 case 44:
1863 msg = "more than 2 numbers given";
1864 break;
1865 case 45:
1866 msg = "'\\}' expected";
1867 break;
1868 case 46:
1869 msg = "first number exceeds second";
1870 break;
1871 case 49:
1872 msg = "'[ ]' imbalance";
1873 break;
1874 case 50:
1875 msg = "regular expression overflow";
1876 break;
1877 case 67:
1878 msg = "illegal byte sequence";
1879 break;
1880 default:
1881 msg = "regular expression error";
1882 break;
1884 error(msg);
1887 static void
1888 doprnt(long *bot, long *top)
1890 long *a1;
1892 a1 = bot;
1893 do {
1894 if (numbf ^ Nflag) {
1895 putd(a1-zero);
1896 putchr('\t');
1898 nlputs(getline(*a1++, 0));
1899 } while (a1 <= top);
1900 pflag = 0;
1901 listf = 0;
1902 numbf = 0;
1905 static void
1906 putd(long c)
1908 register int r;
1910 r = c%10;
1911 c /= 10;
1912 if (c)
1913 putd(c);
1914 putchr(r + '0');
1917 static void
1918 nlputs(register const char *sp)
1920 if (listf)
1921 list(sp);
1922 else
1923 puts(sp);
1926 static void
1927 puts(register const char *sp)
1929 while (*sp) {
1930 if (*sp != '\n')
1931 putchr(*sp++ & 0377);
1932 else
1933 sp++, putchr('\0');
1935 putchr('\n');
1938 static void
1939 list(const char *lp)
1941 int col, n;
1942 wchar_t c;
1944 col = numbf ^ Nflag ? 8 : 0;
1945 while (*lp) {
1946 if (mb_cur_max > 1 && *lp&0200)
1947 n = mbtowc(&c, lp, mb_cur_max);
1948 else {
1949 n = 1;
1950 c = *lp&0377;
1952 if (col+1 >= 72) {
1953 col = 0;
1954 putchr('\\');
1955 putchr('\n');
1957 if (n<0 || c == '\\' ||
1958 !(mb_cur_max>1 ? iswprint(c) : isprint(c))) {
1959 if (n<0)
1960 n = 1;
1961 while (n--)
1962 col += lstchr(*lp++&0377);
1963 } else if (mb_cur_max>1) {
1964 col += wcwidth(c);
1965 while (n--)
1966 putchr(*lp++&0377);
1967 } else {
1968 putchr(*lp++&0377);
1969 col++;
1972 putchr('$');
1973 putchr('\n');
1976 static int
1977 lstchr(int c)
1979 int cad = 1, d;
1981 if (c == '\n')
1982 c = '\0';
1983 if (c == '\\') {
1984 putchr('\\');
1985 putchr('\\');
1986 cad = 2;
1987 } else if (c == '\a') {
1988 putchr('\\');
1989 putchr('a');
1990 cad = 2;
1991 } else if (c == '\b') {
1992 putchr('\\');
1993 putchr('b');
1994 cad = 2;
1995 } else if (c == '\f') {
1996 putchr('\\');
1997 putchr('f');
1998 cad = 2;
1999 } else if (c == '\r') {
2000 putchr('\\');
2001 putchr('r');
2002 cad = 2;
2003 } else if (c == '\t') {
2004 putchr('\\');
2005 putchr('t');
2006 cad = 2;
2007 } else if (c == '\v') {
2008 putchr('\\');
2009 putchr('v');
2010 cad = 2;
2011 } else {
2012 putchr('\\');
2013 putchr(((c&~077)>>6)+'0');
2014 c &= 077;
2015 d = c & 07;
2016 putchr(c > d ? ((c-d)>>3)+'0' : '0');
2017 putchr(d+'0');
2018 cad = 4;
2020 return cad;
2023 static void
2024 putstr(const char *s)
2026 while (*s)
2027 putchr(*s++);
2030 static char line[70];
2031 static char *linp = line;
2033 static void
2034 putchr(int ac)
2036 register char *lp;
2037 register int c;
2039 lp = linp;
2040 c = ac;
2041 *lp++ = c;
2042 if(c == '\n' || lp >= &line[64]) {
2043 linp = line;
2044 write(1, line, lp-line);
2045 return;
2047 linp = lp;
2050 static void
2051 checkpoint(void)
2053 long *a1, *a2;
2055 if (undzero && globp == NULL) {
2056 for (a1 = zero+1, a2 = undzero+1; a1 <= dol; a1++, a2++)
2057 *a2 = *a1;
2058 unddot = &undzero[dot-zero];
2059 unddol = &undzero[dol-zero];
2060 for (a1 = names, a2 = undnames; a1 < &names[26]; a1++, a2++)
2061 *a2 = *a1;
2065 #define swap(a, b) (t = a, a = b, b = t)
2067 static void
2068 undo(void)
2070 long *t;
2072 if (undzero == NULL)
2073 error("no undo information saved");
2074 swap(zero, undzero);
2075 swap(dot, unddot);
2076 swap(dol, unddol);
2077 swap(names, undnames);
2080 static int
2081 maketf(int fd)
2083 char *tmpdir;
2085 if (fd == -1) {
2086 if ((tmpdir = getenv("TMPDIR")) == NULL ||
2087 (fd = creatf(tmpdir)) < 0)
2088 if ((fd = creatf("/var/tmp")) < 0 &&
2089 (fd = creatf("/tmp")) < 0)
2090 error("cannot create temporary file");
2091 } else
2092 ftruncate(fd, 0); /* blkio() will seek to 0 anyway */
2093 return fd;
2096 static int
2097 creatf(const char *tmpdir)
2099 if (strlen(tmpdir) >= sizeof tfname - 9)
2100 return -1;
2101 strcpy(tfname, tmpdir);
2102 strcat(tfname, "/eXXXXXX");
2103 return mkstemp(tfname);
2106 static int
2107 sopen(const char *fn, int rdwr)
2109 int pf[2], fd = -1;
2111 if (fn[0] == '!') {
2112 fn++;
2113 if (pipe(pf) < 0)
2114 error("write or open on pipe failed");
2115 switch (pipid = fork()) {
2116 case 0:
2117 if (rdwr == READ)
2118 dup2(pf[1], 1);
2119 else
2120 dup2(pf[0], 0);
2121 close(pf[0]);
2122 close(pf[1]);
2123 sigset(SIGHUP, oldhup);
2124 sigset(SIGQUIT, oldquit);
2125 sigset(SIGPIPE, oldpipe);
2126 execl(SHELL, "sh", "-c", fn, NULL);
2127 _exit(0100);
2128 default:
2129 close(pf[rdwr == READ ? 1 : 0]);
2130 fd = pf[rdwr == READ ? 0 : 1];
2131 break;
2132 case -1:
2133 error("fork failed - try again");
2135 } else if (rdwr == READ)
2136 fd = open(fn, O_RDONLY);
2137 else if (rdwr == EXIST)
2138 fd = open(fn, O_WRONLY);
2139 else /*if (rdwr == WRITE)*/
2140 fd = creat(fn, 0666);
2141 if (fd >= 0 && rdwr == READ)
2142 readop = 1;
2143 if (fd >= 0)
2144 fstat(fd, &fstbuf);
2145 return fd;
2148 static void
2149 sclose(int fd)
2151 int status;
2153 close(fd);
2154 if (pipid >= 0) {
2155 while (wait(&status) != pipid);
2156 pipid = -1;
2158 readop = 0;
2161 static wint_t
2162 GETWC(char *mb)
2164 int c, n;
2166 n = 1;
2167 mb[0] = c = GETC();
2168 mb[1] = '\0';
2169 if (mb_cur_max > 1 && c&0200 && c != EOF) {
2170 int m;
2171 wchar_t wc;
2173 while ((m = mbtowc(&wc, mb, mb_cur_max)) < 0 && n<mb_cur_max) {
2174 mb[n++] = c = GETC();
2175 mb[n] = '\0';
2176 if (c == '\n' || c == EOF)
2177 break;
2179 if (m != n)
2180 ERROR(67);
2181 return wc;
2182 } else
2183 return c;
2186 static void
2187 growlb(const char *msg)
2189 char *olb = linebuf;
2190 int i;
2192 LBSIZE += 512;
2193 if ((linebuf = realloc(linebuf, LBSIZE)) == NULL ||
2194 (genbuf = realloc(genbuf, LBSIZE)) == NULL)
2195 error(msg);
2196 if (linebuf != olb) {
2197 loc1 += linebuf - olb;
2198 loc2 += linebuf - olb;
2199 for (i = 0; i < NBRA; i++) {
2200 if (braslist[i])
2201 braslist[i] += linebuf - olb;
2202 if (braelist[i])
2203 braelist[i] += linebuf - olb;
2208 static void
2209 growrhs(const char *msg)
2211 RHSIZE += 256;
2212 if ((rhsbuf = realloc(rhsbuf, RHSIZE)) == NULL)
2213 error(msg);
2216 static void
2217 growfn(const char *msg)
2219 FNSIZE += 64;
2220 if ((savedfile = realloc(savedfile, FNSIZE)) == NULL ||
2221 (file = realloc(file, FNSIZE)) == NULL)
2222 error(msg);
2223 if (FNSIZE == 64)
2224 file[0] = savedfile[0] = 0;
2227 union ptrstore {
2228 void *vp;
2229 char bp[sizeof (void *)];
2230 };
2232 static void *
2233 fetchptr(const char *bp)
2235 union ptrstore u;
2236 int i;
2238 for (i = 0; i < sizeof (void *); i++)
2239 u.bp[i] = bp[i];
2240 return u.vp;
2243 static void
2244 storeptr(void *vp, char *bp)
2246 union ptrstore u;
2247 int i;
2249 u.vp = vp;
2250 for (i = 0; i < sizeof (void *); i++)
2251 bp[i] = u.bp[i];
2254 #define add(c) ((i>=LBSIZE ? (growlb("regular expression overflow"),0) : 0), \
2255 genbuf[i++] = (c))
2257 #define copy(s) { \
2258 int m; \
2259 for (m = 0; m==0 || s[m]; m++) \
2260 add(s[m]); \
2263 static char *
2264 compile(char *unused, char *ep, const char *endbuf, int seof)
2266 INIT
2267 int c, d, i;
2268 regex_t *rp;
2269 char *op;
2270 char mb[MB_LEN_MAX+1];
2272 op = ep;
2273 ep += 2;
2274 if ((rp = fetchptr(ep)) == NULL) {
2275 if ((rp = calloc(1, sizeof *rp)) == NULL)
2276 ERROR(50);
2277 storeptr(rp, ep);
2279 ep += sizeof (void *);
2280 i = 0;
2281 nbra = 0;
2282 do {
2283 if ((c = GETWC(mb)) == seof)
2284 add('\0');
2285 else if (c == '\\') {
2286 copy(mb);
2287 c = GETWC(mb);
2288 if (c == '(')
2289 nbra++;
2290 goto normchar;
2291 } else if (c == '[') {
2292 add(c);
2293 d = EOF;
2294 do {
2295 c = GETWC(mb);
2296 if (c == EOF || c == '\n')
2297 ERROR(49);
2298 copy(mb);
2299 if (d=='[' && (c==':' || c=='.' || c=='=')) {
2300 d = c;
2301 do {
2302 c = GETWC(mb);
2303 if (c == EOF || c == '\n')
2304 ERROR(49);
2305 copy(mb);
2306 } while (c != d || PEEKC() != ']');
2307 c = GETWC(mb);
2308 copy(mb);
2309 c = EOF;
2311 d = c;
2312 } while (c != ']');
2313 } else {
2314 if (c == EOF || c == '\n') {
2315 if (c == '\n')
2316 UNGETC(c);
2317 mb[0] = c = '\0';
2319 if (c == '\0')
2320 nodelim = 1;
2321 normchar: copy(mb);
2323 } while (genbuf[i-1] != '\0');
2324 if (genbuf[0]) {
2325 int reflags = 0;
2327 #ifdef REG_ANGLES
2328 reflags |= REG_ANGLES;
2329 #endif
2330 #ifdef REG_AVOIDNULL
2331 reflags |= REG_AVOIDNULL;
2332 #endif
2333 if (op[0])
2334 regfree(rp);
2335 op[0] = 0;
2336 switch (regcomp(rp, genbuf, reflags)) {
2337 case 0:
2338 break;
2339 case REG_ESUBREG:
2340 ERROR(25);
2341 /*NOTREACHED*/
2342 case REG_EBRACK:
2343 ERROR(49);
2344 /*NOTREACHED*/
2345 case REG_EPAREN:
2346 ERROR(42);
2347 /*NOTREACHED*/
2348 case REG_BADBR:
2349 case REG_EBRACE:
2350 ERROR(45);
2351 /*NOTREACHED*/
2352 case REG_ERANGE:
2353 ERROR(11);
2354 /*NOTREACHED*/
2355 case REG_ESPACE:
2356 ERROR(50);
2357 /*NOTREACHED*/
2358 default:
2359 ERROR(-1);
2361 op[0] = 1;
2362 circf = op[1] = genbuf[0] == '^';
2363 } else if (op[0]) {
2364 circf = op[1];
2365 } else
2366 ERROR(41);
2367 return ep + sizeof (void *);
2370 static int
2371 step(const char *lp, const char *ep)
2373 regex_t *rp;
2374 regmatch_t bralist[NBRA+1];
2375 int eflag = 0;
2376 int res;
2377 int i;
2379 rp = fetchptr(&ep[2]);
2380 if (ep[0] == 0)
2381 return 0;
2382 if (locs)
2383 eflag |= REG_NOTBOL;
2384 if ((res = regexec(rp, lp, needsub? NBRA+1 : 0, bralist, eflag)) == 0 &&
2385 needsub) {
2386 loc1 = (char *)lp + bralist[0].rm_so;
2387 loc2 = (char *)lp + bralist[0].rm_eo;
2388 for (i = 1; i <= NBRA; i++) {
2389 if (bralist[i].rm_so != -1) {
2390 braslist[i-1] = (char *)lp + bralist[i].rm_so;
2391 braelist[i-1] = (char *)lp + bralist[i].rm_eo;
2392 } else
2393 braslist[i-1] = braelist[i-1] = NULL;
2396 return res == 0;
2399 static void
2400 help(void)
2402 const char *desc[] = {
2403 "(.)a append up to .",
2404 "(.)b[n] browse n lines",
2405 "(.,.)c change up to .",
2406 "(.,.)d delete lines",
2407 "e [file] edit file",
2408 "E [file] force edit",
2409 "f [file] print or set file",
2410 "(1,$)g/RE/cmd global cmd",
2411 "(1,$)G/RE/ interactive global",
2412 "h print last error",
2413 "H toggle error messages",
2414 "help print this screen",
2415 "(.)i insert up to .",
2416 "(.,.+1)j join lines",
2417 "(.)kx mark line with x",
2418 "(.,.)l list lines",
2419 "(.,.)ma move lines to a",
2420 "(.,.)n number lines",
2421 "N revert n and p",
2422 "(.)o[n] show n lines of context",
2423 "(.,.)p print lines",
2424 "P toggle prompt",
2425 "q quit",
2426 "Q force quit",
2427 "($)r read file",
2428 "(.,.)s/RE/repl/ search and replace",
2429 "(.,.)s/RE/rp/g replace all occurrences",
2430 "(.,.)s/RE/rp/n replace n-th occurrence",
2431 "(.,.)ta transfer lines to a",
2432 "u undo last change",
2433 "(1,$)v/RE/cmd reverse global",
2434 "(1,$)V/RE/ reverse i/a global",
2435 "(1,$)w [file] write file",
2436 "(1,$)W [file] append to file",
2437 "z write buffer and quit",
2438 "($)= print line number",
2439 "!command execute shell command",
2440 "(.+1)<newline> print one line",
2441 "/RE find RE forwards",
2442 "?RE find RE backwards",
2443 "1 first line",
2444 ". current line",
2445 "$ last line",
2446 ", 1,$",
2447 "; .,$",
2448 NULL
2449 };
2450 char line[100];
2451 int c, half, i, k;
2453 half = (sizeof desc / sizeof *desc) / 2;
2454 for (i = 0; i < half && desc[i]; i++) {
2455 c = 0;
2456 for (k = 0; desc[i][k]; k++)
2457 line[c++] = desc[i][k];
2458 if (desc[i+half]) {
2459 while (c < 40)
2460 line[c++] = ' ';
2461 for (k = 0; desc[i+half][k]; k++)
2462 line[c++] = desc[i+half][k];
2464 line[c] = 0;
2465 puts(line);