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