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