9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

ed.c (22554B)


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