xref: /plan9-contrib/sys/src/cmd/ed.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 /*
2  * Editor
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <bio.h>
7 #include <regexp.h>
8 
9 enum
10 {
11 	FNSIZE	= 128,		/* file name */
12 	LBSIZE	= 4096,		/* max line size */
13 	BLKSIZE	= 4096,		/* block size in temp file */
14 	NBLK	= 4095,		/* max size of temp file */
15 	ESIZE	= 256,		/* max size of reg exp */
16 	GBSIZE	= 256,		/* max size of global command */
17 	MAXSUB	= 9,		/* max number of sub reg exp */
18 	ESCFLG	= 0xFFFF,	/* escape Rune - user defined code */
19 	EOF	= -1,
20 };
21 
22 void	(*oldhup)(int);
23 void	(*oldquit)(int);
24 int*	addr1;
25 int*	addr2;
26 int	anymarks;
27 int	col;
28 long	count;
29 int*	dol;
30 int*	dot;
31 int	fchange;
32 char	file[FNSIZE];
33 Rune	genbuf[LBSIZE];
34 int	given;
35 Rune*	globp;
36 int	iblock;
37 int	ichanged;
38 int	io;
39 Biobuf	iobuf;
40 int	lastc;
41 char	line[70];
42 Rune*	linebp;
43 Rune	linebuf[LBSIZE];
44 int	listf;
45 int	listn;
46 Rune*	loc1;
47 Rune*	loc2;
48 int	names[26];
49 int	nleft;
50 int	oblock;
51 int	oflag;
52 Reprog	*pattern;
53 int	peekc;
54 int	pflag;
55 int	rescuing;
56 Rune	rhsbuf[LBSIZE/2];
57 char	savedfile[FNSIZE];
58 jmp_buf	savej;
59 int	subnewa;
60 int	subolda;
61 Resub	subexp[MAXSUB];
62 char*	tfname;
63 int	tline;
64 int	wrapp;
65 int*	zero;
66 
67 char	Q[]	= "";
68 char	T[]	= "TMP";
69 char	WRERR[]	= "WRITE ERROR";
70 int	bpagesize = 20;
71 char	hex[]	= "0123456789abcdef";
72 char*	linp	= line;
73 ulong	nlall = 128;
74 int	tfile	= -1;
75 int	vflag	= 1;
76 
77 void	add(int);
78 int*	address(void);
79 int	append(int(*)(void), int*);
80 void	browse(void);
81 void	callunix(void);
82 void	commands(void);
83 void	compile(int);
84 int	compsub(void);
85 void	dosub(void);
86 void	error(char*);
87 int	match(int*);
88 void	exfile(int);
89 void	filename(int);
90 Rune*	getblock(int, int);
91 int	getchr(void);
92 int	getcopy(void);
93 int	getfile(void);
94 Rune*	getline(int);
95 int	getnum(void);
96 int	getsub(void);
97 int	gettty(void);
98 void	global(int);
99 void	init(void);
100 void	join(void);
101 void	move(int);
102 void	newline(void);
103 void	nonzero(void);
104 void	notifyf(void*, char*);
105 Rune*	place(Rune*, Rune*, Rune*);
106 void	printcom(void);
107 void	putchr(int);
108 void	putd(void);
109 void	putfile(void);
110 int	putline(void);
111 void	putshst(Rune*);
112 void	putst(char*);
113 void	quit(void);
114 void	rdelete(int*, int*);
115 void	regerror(char *);
116 void	reverse(int*, int*);
117 void	setnoaddr(void);
118 void	setwide(void);
119 void	squeeze(int);
120 void	substitute(int);
121 
122 void
123 main(int argc, char *argv[])
124 {
125 	char *p1, *p2;
126 
127 	notify(notifyf);
128 	ARGBEGIN {
129 	case 'o':
130 		oflag = 1;
131 		vflag = 0;
132 		break;
133 	} ARGEND
134 
135 	USED(argc);
136 	if(*argv && (strcmp(*argv, "-") == 0)) {
137 		argv++;
138 		vflag = 0;
139 	}
140 	if(oflag) {
141 		p1 = "/fd/1";
142 		p2 = savedfile;
143 		while(*p2++ = *p1++)
144 			;
145 		globp = L"a";
146 	} else
147 	if(*argv) {
148 		p1 = *argv;
149 		p2 = savedfile;
150 		while(*p2++ = *p1++)
151 			if(p2 >= &savedfile[sizeof(savedfile)])
152 				p2--;
153 		globp = L"r";
154 	}
155 	zero = malloc((nlall+5)*sizeof(int*));
156 	tfname = mktemp("/tmp/eXXXXX");
157 	init();
158 	setjmp(savej);
159 	commands();
160 	quit();
161 }
162 
163 void
164 commands(void)
165 {
166 	int *a1, c, temp;
167 	char lastsep;
168 	Dir d;
169 
170 	for(;;) {
171 		if(pflag) {
172 			pflag = 0;
173 			addr1 = addr2 = dot;
174 			printcom();
175 		}
176 		c = '\n';
177 		for(addr1 = 0;;) {
178 			lastsep = c;
179 			a1 = address();
180 			c = getchr();
181 			if(c != ',' && c != ';')
182 				break;
183 			if(lastsep == ',')
184 				error(Q);
185 			if(a1 == 0) {
186 				a1 = zero+1;
187 				if(a1 > dol)
188 					a1--;
189 			}
190 			addr1 = a1;
191 			if(c == ';')
192 				dot = a1;
193 		}
194 		if(lastsep != '\n' && a1 == 0)
195 			a1 = dol;
196 		if((addr2=a1) == 0) {
197 			given = 0;
198 			addr2 = dot;
199 		} else
200 			given = 1;
201 		if(addr1 == 0)
202 			addr1 = addr2;
203 		switch(c) {
204 
205 		case 'a':
206 			add(0);
207 			continue;
208 
209 		case 'b':
210 			nonzero();
211 			browse();
212 			continue;
213 
214 		case 'c':
215 			nonzero();
216 			newline();
217 			rdelete(addr1, addr2);
218 			append(gettty, addr1-1);
219 			continue;
220 
221 		case 'd':
222 			nonzero();
223 			newline();
224 			rdelete(addr1, addr2);
225 			continue;
226 
227 		case 'E':
228 			fchange = 0;
229 			c = 'e';
230 		case 'e':
231 			setnoaddr();
232 			if(vflag && fchange) {
233 				fchange = 0;
234 				error(Q);
235 			}
236 			filename(c);
237 			init();
238 			addr2 = zero;
239 			goto caseread;
240 
241 		case 'f':
242 			setnoaddr();
243 			filename(c);
244 			putst(savedfile);
245 			continue;
246 
247 		case 'g':
248 			global(1);
249 			continue;
250 
251 		case 'i':
252 			add(-1);
253 			continue;
254 
255 
256 		case 'j':
257 			if(!given)
258 				addr2++;
259 			newline();
260 			join();
261 			continue;
262 
263 		case 'k':
264 			nonzero();
265 			c = getchr();
266 			if(c < 'a' || c > 'z')
267 				error(Q);
268 			newline();
269 			names[c-'a'] = *addr2 & ~01;
270 			anymarks |= 01;
271 			continue;
272 
273 		case 'm':
274 			move(0);
275 			continue;
276 
277 		case 'n':
278 			listn++;
279 			newline();
280 			printcom();
281 			continue;
282 
283 		case '\n':
284 			if(a1==0) {
285 				a1 = dot+1;
286 				addr2 = a1;
287 				addr1 = a1;
288 			}
289 			if(lastsep==';')
290 				addr1 = a1;
291 			printcom();
292 			continue;
293 
294 		case 'l':
295 			listf++;
296 		case 'p':
297 		case 'P':
298 			newline();
299 			printcom();
300 			continue;
301 
302 		case 'Q':
303 			fchange = 0;
304 		case 'q':
305 			setnoaddr();
306 			newline();
307 			quit();
308 
309 		case 'r':
310 			filename(c);
311 		caseread:
312 			if((io=open(file, OREAD)) < 0) {
313 				lastc = '\n';
314 				error(file);
315 			}
316 			if(dirfstat(io, &d) >= 0){
317 				if(d.mode & CHAPPEND)
318 					print("warning: %s is append only\n", file);
319 			}
320 			Binit(&iobuf, io, OREAD);
321 			setwide();
322 			squeeze(0);
323 			c = zero != dol;
324 			append(getfile, addr2);
325 			exfile(OREAD);
326 
327 			fchange = c;
328 			continue;
329 
330 		case 's':
331 			nonzero();
332 			substitute(globp != 0);
333 			continue;
334 
335 		case 't':
336 			move(1);
337 			continue;
338 
339 		case 'u':
340 			nonzero();
341 			newline();
342 			if((*addr2&~01) != subnewa)
343 				error(Q);
344 			*addr2 = subolda;
345 			dot = addr2;
346 			continue;
347 
348 		case 'v':
349 			global(0);
350 			continue;
351 
352 		case 'W':
353 			wrapp++;
354 		case 'w':
355 			setwide();
356 			squeeze(dol>zero);
357 			temp = getchr();
358 			if(temp != 'q' && temp != 'Q') {
359 				peekc = temp;
360 				temp = 0;
361 			}
362 			filename(c);
363 			if(!wrapp ||
364 			  ((io = open(file, OWRITE)) == -1) ||
365 			  ((seek(io, 0L, 2)) == -1))
366 				if((io = create(file, OWRITE, 0666)) < 0)
367 					error(file);
368 			Binit(&iobuf, io, OWRITE);
369 			wrapp = 0;
370 			if(dol > zero)
371 				putfile();
372 			exfile(OWRITE);
373 			if(addr1<=zero+1 && addr2==dol)
374 				fchange = 0;
375 			if(temp == 'Q')
376 				fchange = 0;
377 			if(temp)
378 				quit();
379 			continue;
380 
381 		case '=':
382 			setwide();
383 			squeeze(0);
384 			newline();
385 			count = addr2 - zero;
386 			putd();
387 			putchr(L'\n');
388 			continue;
389 
390 		case '!':
391 			callunix();
392 			continue;
393 
394 		case EOF:
395 			return;
396 
397 		}
398 		error(Q);
399 	}
400 }
401 
402 void
403 printcom(void)
404 {
405 	int *a1;
406 
407 	nonzero();
408 	a1 = addr1;
409 	do {
410 		if(listn) {
411 			count = a1-zero;
412 			putd();
413 			putchr(L'\t');
414 		}
415 		putshst(getline(*a1++));
416 	} while(a1 <= addr2);
417 	dot = addr2;
418 	listf = 0;
419 	listn = 0;
420 	pflag = 0;
421 }
422 
423 int*
424 address(void)
425 {
426 	int sign, *a, opcnt, nextopand, *b, c;
427 
428 	nextopand = -1;
429 	sign = 1;
430 	opcnt = 0;
431 	a = dot;
432 	do {
433 		do {
434 			c = getchr();
435 		} while(c == ' ' || c == '\t');
436 		if(c >= '0' && c <= '9') {
437 			peekc = c;
438 			if(!opcnt)
439 				a = zero;
440 			a += sign*getnum();
441 		} else
442 		switch(c) {
443 		case '$':
444 			a = dol;
445 		case '.':
446 			if(opcnt)
447 				error(Q);
448 			break;
449 		case '\'':
450 			c = getchr();
451 			if(opcnt || c < 'a' || c > 'z')
452 				error(Q);
453 			a = zero;
454 			do {
455 				a++;
456 			} while(a <= dol && names[c-'a'] != (*a & ~01));
457 			break;
458 		case '?':
459 			sign = -sign;
460 		case '/':
461 			compile(c);
462 			b = a;
463 			for(;;) {
464 				a += sign;
465 				if(a <= zero)
466 					a = dol;
467 				if(a > dol)
468 					a = zero;
469 				if(match(a))
470 					break;
471 				if(a == b)
472 					error(Q);
473 			}
474 			break;
475 		default:
476 			if(nextopand == opcnt) {
477 				a += sign;
478 				if(a < zero || dol < a)
479 					continue;       /* error(Q); */
480 			}
481 			if(c != '+' && c != '-' && c != '^') {
482 				peekc = c;
483 				if(opcnt == 0)
484 					a = 0;
485 				return a;
486 			}
487 			sign = 1;
488 			if(c != '+')
489 				sign = -sign;
490 			nextopand = ++opcnt;
491 			continue;
492 		}
493 		sign = 1;
494 		opcnt++;
495 	} while(zero <= a && a <= dol);
496 	error(Q);
497 	return 0;
498 }
499 
500 int
501 getnum(void)
502 {
503 	int r, c;
504 
505 	r = 0;
506 	for(;;) {
507 		c = getchr();
508 		if(c < '0' || c > '9')
509 			break;
510 		r = r*10 + (c-'0');
511 	}
512 	peekc = c;
513 	return r;
514 }
515 
516 void
517 setwide(void)
518 {
519 	if(!given) {
520 		addr1 = zero + (dol>zero);
521 		addr2 = dol;
522 	}
523 }
524 
525 void
526 setnoaddr(void)
527 {
528 	if(given)
529 		error(Q);
530 }
531 
532 void
533 nonzero(void)
534 {
535 	squeeze(1);
536 }
537 
538 void
539 squeeze(int i)
540 {
541 	if(addr1 < zero+i || addr2 > dol || addr1 > addr2)
542 		error(Q);
543 }
544 
545 void
546 newline(void)
547 {
548 	int c;
549 
550 	c = getchr();
551 	if(c == '\n' || c == EOF)
552 		return;
553 	if(c == 'p' || c == 'l' || c == 'n') {
554 		pflag++;
555 		if(c == 'l')
556 			listf++;
557 		else
558 		if(c == 'n')
559 			listn++;
560 		c = getchr();
561 		if(c == '\n')
562 			return;
563 	}
564 	error(Q);
565 }
566 
567 void
568 filename(int comm)
569 {
570 	char *p1, *p2;
571 	Rune rune;
572 	int c;
573 
574 	count = 0;
575 	c = getchr();
576 	if(c == '\n' || c == EOF) {
577 		p1 = savedfile;
578 		if(*p1 == 0 && comm != 'f')
579 			error(Q);
580 		p2 = file;
581 		while(*p2++ = *p1++)
582 			;
583 		return;
584 	}
585 	if(c != ' ')
586 		error(Q);
587 	while((c=getchr()) == ' ')
588 		;
589 	if(c == '\n')
590 		error(Q);
591 	p1 = file;
592 	do {
593 		if(p1 >= &file[sizeof(file)-6] || c == ' ' || c == EOF)
594 			error(Q);
595 		rune = c;
596 		p1 += runetochar(p1, &rune);
597 	} while((c=getchr()) != '\n');
598 	*p1 = 0;
599 	if(savedfile[0] == 0 || comm == 'e' || comm == 'f') {
600 		p1 = savedfile;
601 		p2 = file;
602 		while(*p1++ = *p2++)
603 			;
604 	}
605 }
606 
607 void
608 exfile(int om)
609 {
610 
611 	if(om == OWRITE)
612 		if(Bflush(&iobuf) < 0)
613 			error(Q);
614 	close(io);
615 	io = -1;
616 	if(vflag) {
617 		putd();
618 		putchr(L'\n');
619 	}
620 }
621 
622 void
623 error1(char *s)
624 {
625 	int c;
626 
627 	wrapp = 0;
628 	listf = 0;
629 	listn = 0;
630 	count = 0;
631 	seek(0, 0, 2);
632 	pflag = 0;
633 	if(globp)
634 		lastc = '\n';
635 	globp = 0;
636 	peekc = lastc;
637 	if(lastc)
638 		for(;;) {
639 			c = getchr();
640 			if(c == '\n' || c == EOF)
641 				break;
642 		}
643 	if(io > 0) {
644 		close(io);
645 		io = -1;
646 	}
647 	putchr(L'?');
648 	putst(s);
649 }
650 
651 void
652 error(char *s)
653 {
654 	error1(s);
655 	longjmp(savej, 1);
656 }
657 
658 void
659 rescue(void)
660 {
661 	rescuing = 1;
662 	if(dol > zero) {
663 		addr1 = zero+1;
664 		addr2 = dol;
665 		io = create("ed.hup", OWRITE, 0666);
666 		if(io > 0)
667 			putfile();
668 	}
669 	fchange = 0;
670 	quit();
671 }
672 
673 void
674 notifyf(void *a, char *s)
675 {
676 	if(strcmp(s, "interrupt") == 0){
677 		if(rescuing)
678 			noted(NCONT);
679 		putchr(L'\n');
680 		lastc = '\n';
681 		error1(Q);
682 		notejmp(a, savej, 0);
683 	}
684 	if(strcmp(s, "hangup") == 0){
685 		if(rescuing)
686 			noted(NDFLT);
687 		rescue();
688 	}
689 	fprint(2, "ed: note: %s\n", s);
690 	abort();
691 }
692 
693 int
694 getchr(void)
695 {
696 	char s[UTFmax];
697 	int i;
698 	Rune r;
699 
700 	if(lastc = peekc) {
701 		peekc = 0;
702 		return lastc;
703 	}
704 	if(globp) {
705 		if((lastc=*globp++) != 0)
706 			return lastc;
707 		globp = 0;
708 		return EOF;
709 	}
710 	for(i=0;;) {
711 		if(read(0, s+i, 1) <= 0)
712 			return lastc = EOF;
713 		i++;
714 		if(fullrune(s, i))
715 			break;
716 
717 	}
718 	chartorune(&r, s);
719 	lastc = r;
720 	return lastc;
721 }
722 
723 int
724 gety(void)
725 {
726 	int c;
727 	Rune *gf, *p;
728 
729 	p = linebuf;
730 	gf = globp;
731 	for(;;) {
732 		c = getchr();
733 		if(c == '\n') {
734 			*p = 0;
735 			return 0;
736 		}
737 		if(c == EOF) {
738 			if(gf)
739 				peekc = c;
740 			return c;
741 		}
742 		if(c == 0)
743 			continue;
744 		*p++ = c;
745 		if(p >= &linebuf[LBSIZE-2])
746 			error(Q);
747 	}
748 	return 0;
749 }
750 
751 int
752 gettty(void)
753 {
754 	int rc;
755 
756 	rc = gety();
757 	if(rc)
758 		return rc;
759 	if(linebuf[0] == '.' && linebuf[1] == 0)
760 		return EOF;
761 	return 0;
762 }
763 
764 int
765 getfile(void)
766 {
767 	int c;
768 	Rune *lp;
769 
770 	lp = linebuf;
771 	do {
772 		c = Bgetrune(&iobuf);
773 		if(c < 0) {
774 			if(lp > linebuf) {
775 				putst("'\\n' appended");
776 				c = '\n';
777 			} else
778 				return EOF;
779 		}
780 		if(lp >= &linebuf[LBSIZE]) {
781 			lastc = '\n';
782 			error(Q);
783 		}
784 		*lp++ = c;
785 		count++;
786 	} while(c != '\n');
787 	lp[-1] = 0;
788 	return 0;
789 }
790 
791 void
792 putfile(void)
793 {
794 	int *a1;
795 	Rune *lp;
796 	long c;
797 
798 	a1 = addr1;
799 	do {
800 		lp = getline(*a1++);
801 		for(;;) {
802 			count++;
803 			c = *lp++;
804 			if(c == 0) {
805 				if(Bputrune(&iobuf, '\n') < 0)
806 					error(Q);
807 				break;
808 			}
809 			if(Bputrune(&iobuf, c) < 0)
810 				error(Q);
811 		}
812 	} while(a1 <= addr2);
813 	if(Bflush(&iobuf) < 0)
814 		error(Q);
815 }
816 
817 int
818 append(int (*f)(void), int *a)
819 {
820 	int *a1, *a2, *rdot, nline, tl;
821 
822 	nline = 0;
823 	dot = a;
824 	while((*f)() == 0) {
825 		if((dol-zero) >= nlall) {
826 			nlall += 512;
827 			a1 = realloc(zero, (nlall+5)*sizeof(int*));
828 			if(a1 == 0) {
829 				error("MEM?");
830 				rescue();
831 			}
832 			tl = a1 - zero;	/* relocate pointers */
833 			zero += tl;
834 			addr1 += tl;
835 			addr2 += tl;
836 			dol += tl;
837 			dot += tl;
838 		}
839 		tl = putline();
840 		nline++;
841 		a1 = ++dol;
842 		a2 = a1+1;
843 		rdot = ++dot;
844 		while(a1 > rdot)
845 			*--a2 = *--a1;
846 		*rdot = tl;
847 	}
848 	return nline;
849 }
850 
851 void
852 add(int i)
853 {
854 	if(i && (given || dol > zero)) {
855 		addr1--;
856 		addr2--;
857 	}
858 	squeeze(0);
859 	newline();
860 	append(gettty, addr2);
861 }
862 
863 void
864 browse(void)
865 {
866 	int forward, n;
867 	static bformat, bnum; /* 0 */
868 
869 	forward = 1;
870 	peekc = getchr();
871 	if(peekc != '\n'){
872 		if(peekc == '-' || peekc == '+') {
873 			if(peekc == '-')
874 				forward = 0;
875 			getchr();
876 		}
877 		n = getnum();
878 		if(n > 0)
879 			bpagesize = n;
880 	}
881 	newline();
882 	if(pflag) {
883 		bformat = listf;
884 		bnum = listn;
885 	} else {
886 		listf = bformat;
887 		listn = bnum;
888 	}
889 	if(forward) {
890 		addr1 = addr2;
891 		addr2 += bpagesize;
892 		if(addr2 > dol)
893 			addr2 = dol;
894 	} else {
895 		addr1 = addr2-bpagesize;
896 		if(addr1 <= zero)
897 			addr1 = zero+1;
898 	}
899 	printcom();
900 }
901 
902 void
903 callunix(void)
904 {
905 	int c;
906 	Rune rune;
907 	Waitmsg w;
908 	char buf[512];
909 	char *p;
910 
911 	setnoaddr();
912 	p = buf;
913 	while((c=getchr()) != EOF && c != '\n')
914 		if(p < &buf[sizeof(buf) - 6]) {
915 			rune = c;
916 			p += runetochar(p, &rune);
917 		}
918 	*p = 0;
919 	if(fork() == 0) {
920 		execl("/bin/rc", "rc", "-c", buf, 0);
921 		exits("execl failed");
922 	}
923 	wait(&w);
924 	if(vflag)
925 		putst("!");
926 }
927 
928 void
929 quit(void)
930 {
931 	if(vflag && fchange && dol!=zero) {
932 		fchange = 0;
933 		error(Q);
934 	}
935 	remove(tfname);
936 	exits(0);
937 }
938 
939 void
940 onquit(int sig)
941 {
942 	USED(sig);
943 	quit();
944 }
945 
946 void
947 rdelete(int *ad1, int *ad2)
948 {
949 	int *a1, *a2, *a3;
950 
951 	a1 = ad1;
952 	a2 = ad2+1;
953 	a3 = dol;
954 	dol -= a2 - a1;
955 	do {
956 		*a1++ = *a2++;
957 	} while(a2 <= a3);
958 	a1 = ad1;
959 	if(a1 > dol)
960 		a1 = dol;
961 	dot = a1;
962 	fchange = 1;
963 }
964 
965 void
966 gdelete(void)
967 {
968 	int *a1, *a2, *a3;
969 
970 	a3 = dol;
971 	for(a1=zero; (*a1&01)==0; a1++)
972 		if(a1>=a3)
973 			return;
974 	for(a2=a1+1; a2<=a3;) {
975 		if(*a2 & 01) {
976 			a2++;
977 			dot = a1;
978 		} else
979 			*a1++ = *a2++;
980 	}
981 	dol = a1-1;
982 	if(dot > dol)
983 		dot = dol;
984 	fchange = 1;
985 }
986 
987 Rune*
988 getline(int tl)
989 {
990 	Rune *lp, *bp;
991 	int nl;
992 
993 	lp = linebuf;
994 	bp = getblock(tl, OREAD);
995 	nl = nleft;
996 	tl &= ~((BLKSIZE/2) - 1);
997 	while(*lp++ = *bp++) {
998 		nl -= sizeof(Rune);
999 		if(nl == 0) {
1000 			bp = getblock(tl += BLKSIZE/2, OREAD);
1001 			nl = nleft;
1002 		}
1003 	}
1004 	return linebuf;
1005 }
1006 
1007 int
1008 putline(void)
1009 {
1010 	Rune *lp, *bp;
1011 	int nl, tl;
1012 
1013 	fchange = 1;
1014 	lp = linebuf;
1015 	tl = tline;
1016 	bp = getblock(tl, OWRITE);
1017 	nl = nleft;
1018 	tl &= ~((BLKSIZE/2)-1);
1019 	while(*bp = *lp++) {
1020 		if(*bp++ == '\n') {
1021 			bp[-1] = 0;
1022 			linebp = lp;
1023 			break;
1024 		}
1025 		nl -= sizeof(Rune);
1026 		if(nl == 0) {
1027 			tl += BLKSIZE/2;
1028 			bp = getblock(tl, OWRITE);
1029 			nl = nleft;
1030 		}
1031 	}
1032 	nl = tline;
1033 	tline += ((lp-linebuf) + 03) & 077776;
1034 	return nl;
1035 }
1036 
1037 void
1038 blkio(int b, uchar *buf, long (*iofcn)(int, void *, long))
1039 {
1040 	seek(tfile, b*BLKSIZE, 0);
1041 	if((*iofcn)(tfile, buf, BLKSIZE) != BLKSIZE) {
1042 		error(T);
1043 	}
1044 }
1045 
1046 Rune*
1047 getblock(int atl, int iof)
1048 {
1049 	int bno, off;
1050 
1051 	static uchar ibuff[BLKSIZE];
1052 	static uchar obuff[BLKSIZE];
1053 
1054 	bno = atl / (BLKSIZE/2);
1055 	off = (atl<<1) & (BLKSIZE-1) & ~03;
1056 	if(bno >= NBLK) {
1057 		lastc = '\n';
1058 		error(T);
1059 	}
1060 	nleft = BLKSIZE - off;
1061 	if(bno == iblock) {
1062 		ichanged |= iof;
1063 		return (Rune*)(ibuff+off);
1064 	}
1065 	if(bno == oblock)
1066 		return (Rune*)(obuff+off);
1067 	if(iof == OREAD) {
1068 		if(ichanged)
1069 			blkio(iblock, ibuff, write);
1070 		ichanged = 0;
1071 		iblock = bno;
1072 		blkio(bno, ibuff, read);
1073 		return (Rune*)(ibuff+off);
1074 	}
1075 	if(oblock >= 0)
1076 		blkio(oblock, obuff, write);
1077 	oblock = bno;
1078 	return (Rune*)(obuff+off);
1079 }
1080 
1081 void
1082 init(void)
1083 {
1084 	int *markp;
1085 
1086 	close(tfile);
1087 	tline = 2;
1088 	for(markp = names; markp < &names[26]; )
1089 		*markp++ = 0;
1090 	subnewa = 0;
1091 	anymarks = 0;
1092 	iblock = -1;
1093 	oblock = -1;
1094 	ichanged = 0;
1095 	if((tfile = create(tfname, ORDWR, 0600)) < 0){
1096 		error1(T);
1097 		exits(0);
1098 	}
1099 	dot = dol = zero;
1100 }
1101 
1102 void
1103 global(int k)
1104 {
1105 	Rune *gp, globuf[GBSIZE];
1106 	int c, *a1;
1107 
1108 	if(globp)
1109 		error(Q);
1110 	setwide();
1111 	squeeze(dol > zero);
1112 	c = getchr();
1113 	if(c == '\n')
1114 		error(Q);
1115 	compile(c);
1116 	gp = globuf;
1117 	while((c=getchr()) != '\n') {
1118 		if(c == EOF)
1119 			error(Q);
1120 		if(c == '\\') {
1121 			c = getchr();
1122 			if(c != '\n')
1123 				*gp++ = '\\';
1124 		}
1125 		*gp++ = c;
1126 		if(gp >= &globuf[GBSIZE-2])
1127 			error(Q);
1128 	}
1129 	if(gp == globuf)
1130 		*gp++ = 'p';
1131 	*gp++ = '\n';
1132 	*gp = 0;
1133 	for(a1=zero; a1<=dol; a1++) {
1134 		*a1 &= ~01;
1135 		if(a1 >= addr1 && a1 <= addr2 && match(a1) == k)
1136 			*a1 |= 01;
1137 	}
1138 
1139 	/*
1140 	 * Special case: g/.../d (avoid n^2 algorithm)
1141 	 */
1142 	if(globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == 0) {
1143 		gdelete();
1144 		return;
1145 	}
1146 	for(a1=zero; a1<=dol; a1++) {
1147 		if(*a1 & 01) {
1148 			*a1 &= ~01;
1149 			dot = a1;
1150 			globp = globuf;
1151 			commands();
1152 			a1 = zero;
1153 		}
1154 	}
1155 }
1156 
1157 void
1158 join(void)
1159 {
1160 	Rune *gp, *lp;
1161 	int *a1;
1162 
1163 	nonzero();
1164 	gp = genbuf;
1165 	for(a1=addr1; a1<=addr2; a1++) {
1166 		lp = getline(*a1);
1167 		while(*gp = *lp++)
1168 			if(gp++ >= &genbuf[LBSIZE-2])
1169 				error(Q);
1170 	}
1171 	lp = linebuf;
1172 	gp = genbuf;
1173 	while(*lp++ = *gp++)
1174 		;
1175 	*addr1 = putline();
1176 	if(addr1 < addr2)
1177 		rdelete(addr1+1, addr2);
1178 	dot = addr1;
1179 }
1180 
1181 void
1182 substitute(int inglob)
1183 {
1184 	int *mp, *a1, nl, gsubf, n;
1185 
1186 	n = getnum();	/* OK even if n==0 */
1187 	gsubf = compsub();
1188 	for(a1 = addr1; a1 <= addr2; a1++) {
1189 		if(match(a1)){
1190 			int *ozero;
1191 			int m = n;
1192 
1193 			do {
1194 				int span = loc2-loc1;
1195 
1196 				if(--m <= 0) {
1197 					dosub();
1198 					if(!gsubf)
1199 						break;
1200 					if(span == 0) {	/* null RE match */
1201 						if(*loc2 == 0)
1202 							break;
1203 						loc2++;
1204 					}
1205 				}
1206 			} while(match(0));
1207 			if(m <= 0) {
1208 				inglob |= 01;
1209 				subnewa = putline();
1210 				*a1 &= ~01;
1211 				if(anymarks) {
1212 					for(mp=names; mp<&names[26]; mp++)
1213 						if(*mp == *a1)
1214 							*mp = subnewa;
1215 				}
1216 				subolda = *a1;
1217 				*a1 = subnewa;
1218 				ozero = zero;
1219 				nl = append(getsub, a1);
1220 				addr2 += nl;
1221 				nl += zero-ozero;
1222 				a1 += nl;
1223 			}
1224 		}
1225 	}
1226 	if(inglob == 0)
1227 		error(Q);
1228 }
1229 
1230 int
1231 compsub(void)
1232 {
1233 	int seof, c;
1234 	Rune *p;
1235 
1236 	seof = getchr();
1237 	if(seof == '\n' || seof == ' ')
1238 		error(Q);
1239 	compile(seof);
1240 	p = rhsbuf;
1241 	for(;;) {
1242 		c = getchr();
1243 		if(c == '\\') {
1244 			c = getchr();
1245 			*p++ = ESCFLG;
1246 			if(p >= &rhsbuf[LBSIZE/2])
1247 				error(Q);
1248 		} else
1249 		if(c == '\n' && (!globp || !globp[0])) {
1250 			peekc = c;
1251 			pflag++;
1252 			break;
1253 		} else
1254 		if(c == seof)
1255 			break;
1256 		*p++ = c;
1257 		if(p >= &rhsbuf[LBSIZE/2])
1258 			error(Q);
1259 	}
1260 	*p = 0;
1261 	peekc = getchr();
1262 	if(peekc == 'g') {
1263 		peekc = 0;
1264 		newline();
1265 		return 1;
1266 	}
1267 	newline();
1268 	return 0;
1269 }
1270 
1271 int
1272 getsub(void)
1273 {
1274 	Rune *p1, *p2;
1275 
1276 	p1 = linebuf;
1277 	if((p2 = linebp) == 0)
1278 		return EOF;
1279 	while(*p1++ = *p2++)
1280 		;
1281 	linebp = 0;
1282 	return 0;
1283 }
1284 
1285 void
1286 dosub(void)
1287 {
1288 	Rune *lp, *sp, *rp;
1289 	int c, n;
1290 
1291 	lp = linebuf;
1292 	sp = genbuf;
1293 	rp = rhsbuf;
1294 	while(lp < loc1)
1295 		*sp++ = *lp++;
1296 	while(c = *rp++) {
1297 		if(c == '&'){
1298 			sp = place(sp, loc1, loc2);
1299 			continue;
1300 		}
1301 		if(c == ESCFLG && (c = *rp++) >= '1' && c < MAXSUB+'0') {
1302 			n = c-'0';
1303 			if(subexp[n].rsp && subexp[n].rep) {
1304 				sp = place(sp, subexp[n].rsp, subexp[n].rep);
1305 				continue;
1306 			}
1307 			error(Q);
1308 		}
1309 		*sp++ = c;
1310 		if(sp >= &genbuf[LBSIZE])
1311 			error(Q);
1312 	}
1313 	lp = loc2;
1314 	loc2 = sp - genbuf + linebuf;
1315 	while(*sp++ = *lp++)
1316 		if(sp >= &genbuf[LBSIZE])
1317 			error(Q);
1318 	lp = linebuf;
1319 	sp = genbuf;
1320 	while(*lp++ = *sp++)
1321 		;
1322 }
1323 
1324 Rune*
1325 place(Rune *sp, Rune *l1, Rune *l2)
1326 {
1327 
1328 	while(l1 < l2) {
1329 		*sp++ = *l1++;
1330 		if(sp >= &genbuf[LBSIZE])
1331 			error(Q);
1332 	}
1333 	return sp;
1334 }
1335 
1336 void
1337 move(int cflag)
1338 {
1339 	int *adt, *ad1, *ad2;
1340 
1341 	nonzero();
1342 	if((adt = address())==0)	/* address() guarantees addr is in range */
1343 		error(Q);
1344 	newline();
1345 	if(cflag) {
1346 		int *ozero, delta;
1347 		ad1 = dol;
1348 		ozero = zero;
1349 		append(getcopy, ad1++);
1350 		ad2 = dol;
1351 		delta = zero - ozero;
1352 		ad1 += delta;
1353 		adt += delta;
1354 	} else {
1355 		ad2 = addr2;
1356 		for(ad1 = addr1; ad1 <= ad2;)
1357 			*ad1++ &= ~01;
1358 		ad1 = addr1;
1359 	}
1360 	ad2++;
1361 	if(adt<ad1) {
1362 		dot = adt + (ad2-ad1);
1363 		if((++adt)==ad1)
1364 			return;
1365 		reverse(adt, ad1);
1366 		reverse(ad1, ad2);
1367 		reverse(adt, ad2);
1368 	} else
1369 	if(adt >= ad2) {
1370 		dot = adt++;
1371 		reverse(ad1, ad2);
1372 		reverse(ad2, adt);
1373 		reverse(ad1, adt);
1374 	} else
1375 		error(Q);
1376 	fchange = 1;
1377 }
1378 
1379 void
1380 reverse(int *a1, int *a2)
1381 {
1382 	int t;
1383 
1384 	for(;;) {
1385 		t = *--a2;
1386 		if(a2 <= a1)
1387 			return;
1388 		*a2 = *a1;
1389 		*a1++ = t;
1390 	}
1391 }
1392 
1393 int
1394 getcopy(void)
1395 {
1396 	if(addr1 > addr2)
1397 		return EOF;
1398 	getline(*addr1++);
1399 	return 0;
1400 }
1401 
1402 void
1403 compile(int eof)
1404 {
1405 	Rune c;
1406 	char *ep;
1407 	char expbuf[ESIZE];
1408 
1409 	if((c = getchr()) == '\n') {
1410 		peekc = c;
1411 		c = eof;
1412 	}
1413 	if(c == eof) {
1414 		if(!pattern)
1415 			error(Q);
1416 		return;
1417 	}
1418 	if(pattern) {
1419 		free(pattern);
1420 		pattern = 0;
1421 	}
1422 	ep = expbuf;
1423 	do {
1424 		if(c == '\\') {
1425 			if(ep >= expbuf+sizeof(expbuf)) {
1426 				error(Q);
1427 				return;
1428 			}
1429 			ep += runetochar(ep, &c);
1430 			if((c = getchr()) == '\n') {
1431 				error(Q);
1432 				return;
1433 			}
1434 		}
1435 		if(ep >= expbuf+sizeof(expbuf)) {
1436 			error(Q);
1437 			return;
1438 		}
1439 		ep += runetochar(ep, &c);
1440 	} while((c = getchr()) != eof && c != '\n');
1441 	if(c == '\n')
1442 		peekc = c;
1443 	*ep = 0;
1444 	pattern = regcomp(expbuf);
1445 }
1446 
1447 int
1448 match(int *addr)
1449 {
1450 	if(!pattern)
1451 		return 0;
1452 	if(addr){
1453 		if(addr == zero)
1454 			return 0;
1455 		subexp[0].rsp = getline(*addr);
1456 	} else
1457 		subexp[0].rsp = loc2;
1458 	subexp[0].rep = 0;
1459 	if(rregexec(pattern, linebuf, subexp, MAXSUB)) {
1460 		loc1 = subexp[0].rsp;
1461 		loc2 = subexp[0].rep;
1462 		return 1;
1463 	}
1464 	loc1 = loc2 = 0;
1465 	return 0;
1466 
1467 }
1468 
1469 void
1470 putd(void)
1471 {
1472 	int r;
1473 
1474 	r = count%10;
1475 	count /= 10;
1476 	if(count)
1477 		putd();
1478 	putchr(r + L'0');
1479 }
1480 
1481 void
1482 putst(char *sp)
1483 {
1484 	Rune r;
1485 
1486 	col = 0;
1487 	for(;;) {
1488 		sp += chartorune(&r, sp);
1489 		if(r == 0)
1490 			break;
1491 		putchr(r);
1492 	}
1493 	putchr(L'\n');
1494 }
1495 
1496 void
1497 putshst(Rune *sp)
1498 {
1499 	col = 0;
1500 	while(*sp)
1501 		putchr(*sp++);
1502 	putchr(L'\n');
1503 }
1504 
1505 void
1506 putchr(int ac)
1507 {
1508 	char *lp;
1509 	int c;
1510 	Rune rune;
1511 
1512 	lp = linp;
1513 	c = ac;
1514 	if(listf) {
1515 		if(c == '\n') {
1516 			if(linp != line && linp[-1] == ' ') {
1517 				*lp++ = '\\';
1518 				*lp++ = 'n';
1519 			}
1520 		} else {
1521 			if(col > (72-6-2)) {
1522 				col = 8;
1523 				*lp++ = '\\';
1524 				*lp++ = '\n';
1525 				*lp++ = '\t';
1526 			}
1527 			col++;
1528 			if(c=='\b' || c=='\t' || c=='\\') {
1529 				*lp++ = '\\';
1530 				if(c == '\b')
1531 					c = 'b';
1532 				else
1533 				if(c == '\t')
1534 					c = 't';
1535 				col++;
1536 			} else
1537 			if(c<' ' || c>='\177') {
1538 				*lp++ = '\\';
1539 				*lp++ = 'x';
1540 				*lp++ =  hex[c>>12];
1541 				*lp++ =  hex[c>>8&0xF];
1542 				*lp++ =  hex[c>>4&0xF];
1543 				c     =  hex[c&0xF];
1544 				col += 5;
1545 			}
1546 		}
1547 	}
1548 
1549 	rune = c;
1550 	lp += runetochar(lp, &rune);
1551 
1552 	if(c == '\n' || lp >= &line[sizeof(line)-5]) {
1553 		linp = line;
1554 		write(oflag? 2: 1, line, lp-line);
1555 		return;
1556 	}
1557 	linp = lp;
1558 }
1559 
1560 char*
1561 mktemp(char *as)
1562 {
1563 	char *s;
1564 	unsigned pid;
1565 	int i;
1566 
1567 	pid = getpid();
1568 	s = as;
1569 	while(*s++)
1570 		;
1571 	s--;
1572 	while(*--s == 'X') {
1573 		*s = pid % 10 + '0';
1574 		pid /= 10;
1575 	}
1576 	s++;
1577 	i = 'a';
1578 	while(access(as, 0) != -1) {
1579 		if(i == 'z')
1580 			return "/";
1581 		*s = i++;
1582 	}
1583 	return as;
1584 }
1585 
1586 void
1587 regerror(char *s)
1588 {
1589 	USED(s);
1590 	error(Q);
1591 }
1592