hbase

heirloom base
git clone git://git.2f30.org/hbase
Log | Files | Refs | README

ed.c (50977B)


      1 /*
      2  * Editor
      3  */
      4 
      5 /*
      6  * Changes by Gunnar Ritter, Freiburg i. Br., Germany, July 2003.
      7  */
      8 /*	from Unix 32V /usr/src/cmd/ed.c	*/
      9 /*
     10  * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
     11  *
     12  * Redistribution and use in source and binary forms, with or without
     13  * modification, are permitted provided that the following conditions
     14  * are met:
     15  *   Redistributions of source code and documentation must retain the
     16  *    above copyright notice, this list of conditions and the following
     17  *    disclaimer.
     18  *   Redistributions in binary form must reproduce the above copyright
     19  *    notice, this list of conditions and the following disclaimer in the
     20  *    documentation and/or other materials provided with the distribution.
     21  *   All advertising materials mentioning features or use of this software
     22  *    must display the following acknowledgement:
     23  *      This product includes software developed or owned by Caldera
     24  *      International, Inc.
     25  *   Neither the name of Caldera International, Inc. nor the names of
     26  *    other contributors may be used to endorse or promote products
     27  *    derived from this software without specific prior written permission.
     28  *
     29  * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
     30  * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
     31  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     32  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     33  * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
     34  * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
     35  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     36  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
     37  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     38  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
     39  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     40  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     41  */
     42 
     43 #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
     44 #define	USED	__attribute__ ((used))
     45 #elif defined __GNUC__
     46 #define	USED	__attribute__ ((unused))
     47 #else
     48 #define	USED
     49 #endif
     50 #if defined (SU3)
     51 static const char sccsid[] USED = "@(#)ed_su3.sl	1.99 (gritter) 7/27/06";
     52 #elif defined (SUS)
     53 static const char sccsid[] USED = "@(#)ed_sus.sl	1.99 (gritter) 7/27/06";
     54 #elif defined (S42)
     55 static const char sccsid[] USED = "@(#)ed_s42.sl	1.99 (gritter) 7/27/06";
     56 #else	/* !SU3, !SUS, !S42 */
     57 static const char sccsid[] USED = "@(#)ed.sl	1.99 (gritter) 7/27/06";
     58 #endif	/* !SU3, !SUS, !S42 */
     59 
     60 #include <sys/types.h>
     61 #include <sys/stat.h>
     62 #include <sys/wait.h>
     63 #include <sys/stat.h>
     64 #include <fcntl.h>
     65 #include <unistd.h>
     66 #include <time.h>
     67 #include <string.h>
     68 #include <stdlib.h>
     69 #include <signal.h>
     70 #include "sigset.h"
     71 #include <termios.h>
     72 #include <setjmp.h>
     73 #include <libgen.h>
     74 #include <inttypes.h>
     75 #include <locale.h>
     76 #include <wchar.h>
     77 #include <ctype.h>
     78 #include <wctype.h>
     79 #include <limits.h>
     80 #include <termios.h>
     81 static int	FNSIZE;
     82 static int	LBSIZE;
     83 static int	RHSIZE;
     84 #define	ESIZE	2048
     85 static int	GBSIZE;
     86 #undef	EOF
     87 #define	EOF	-1
     88 #define	puts(s)		xxputs(s)
     89 #define	getline(t, n)	xxgetline(t, n)
     90 
     91 #if (LONG_MAX > 017777777777L)
     92 #define	MAXCNT	0777777777777777777777L		/* 2^63-1 */
     93 #else
     94 #define	MAXCNT	017777777777L			/* 2^31-1 */
     95 #endif
     96 #define	BLKMSK	(MAXCNT>>8)			/* was 0377 */
     97 
     98 #define	READ	0
     99 #define	WRITE	1
    100 #define	EXIST	2
    101 
    102 struct	tabulator {
    103 	struct tabulator	*t_nxt;	/* next list element */
    104 	const char	*t_str;		/* tabulator string */
    105 	int	t_tab;			/* tab stop position */
    106 	int	t_rep;			/* repetitive tab count */
    107 };
    108 
    109 static int	peekc;
    110 static int	lastc;
    111 static char	*savedfile;
    112 static char	*file;
    113 static struct stat	fstbuf;
    114 static char	*linebuf;
    115 static char	*rhsbuf;
    116 static char	expbuf[ESIZE + 4];
    117 static long	*zero;
    118 static long	*undzero;
    119 static long	*dot;
    120 static long	*unddot;
    121 static long	*dol;
    122 static long	*unddol;
    123 static long	*addr1;
    124 static long	*addr2;
    125 static char	*genbuf;
    126 static long	count;
    127 static char	*linebp;
    128 static int	ninbuf;
    129 static int	io;
    130 static int	ioeof;
    131 static int	pflag;
    132 static char	*wrtemp;
    133 static uid_t	myuid;
    134 static void	(*oldhup)(int);
    135 static void	(*oldquit)(int);
    136 static void	(*oldpipe)(int);
    137 static int	vflag	= 1;
    138 static int	listf;
    139 static int	numbf;
    140 static char	*globp;
    141 static int	tfile	= -1;
    142 static long	tline;
    143 static char	tfname[64];
    144 static char	ibuff[512];
    145 static int	iblock	= -1;
    146 static char	obuff[512];
    147 static int	oblock	= -1;
    148 static int	ichanged;
    149 static int	nleft;
    150 static long	*names;
    151 static long	*undnames;
    152 static int	anymarks;
    153 static int	subnewa;
    154 static int	fchange;
    155 static int	wrapp;
    156 static unsigned nlall = 128;
    157 static const char	*progname;
    158 static const char	*prompt = "*";
    159 static int	Pflag;
    160 static int	prhelp;
    161 static const char	*prvmsg;
    162 static int	lastsig;
    163 static int	pipid = -1;
    164 static int	readop;
    165 static int	status;
    166 static int	mb_cur_max;
    167 static int	needsub;
    168 static int	insub;
    169 static struct tabulator	*tabstops;
    170 static int	maxlength;
    171 static int	rspec;
    172 static int	Nflag;
    173 static int	bcount = 22;
    174 static int	ocount = 11;
    175 
    176 static jmp_buf	savej;
    177 
    178 static void	usage(char, int);
    179 static void	commands(void);
    180 static long	*address(void);
    181 static void	setdot(void);
    182 static void	setall(void);
    183 static void	setnoaddr(void);
    184 static void	nonzero(void);
    185 static void	newline(void);
    186 static void	filename(int);
    187 static void	exfile(void);
    188 static void	onintr(int);
    189 static void	onhup(int);
    190 static void	onpipe(int);
    191 static void	error(const char *);
    192 static void	error2(const char *, const char *);
    193 static void	errput(const char *, const char *);
    194 static int	getchr(void);
    195 static int	gettty(void);
    196 static long	getnum(void);
    197 static int	getfile(void);
    198 static void	putfile(void);
    199 static int	append(int (*)(void), long *);
    200 static void	callunix(void);
    201 static char	*readcmd(void);
    202 static void	quit(int);
    203 static void	delete(void);
    204 static void	rdelete(long *, long *);
    205 static void	gdelete(void);
    206 static char	*getline(long, int);
    207 static int	putline(void);
    208 static char	*getblock(long, long);
    209 static void	blkio(long, char *, int);
    210 static void	init(void);
    211 static void	global(int, int);
    212 static void	globrd(char **, int);
    213 static void	join(void);
    214 static void	substitute(int);
    215 static int	compsub(void);
    216 static int	getsub(void);
    217 static int	dosub(int);
    218 static int	place(int, const char *, const char *);
    219 static void	move(int);
    220 static void	reverse(long *, long *);
    221 static int	getcopy(void);
    222 static int	execute(int, long *, int);
    223 static void	cmplerr(int);
    224 static void	doprnt(long *, long *);
    225 static void	putd(long);
    226 static void	puts(const char *);
    227 static void	nlputs(const char *);
    228 static void	list(const char *);
    229 static int	lstchr(int);
    230 static void	putstr(const char *);
    231 static void	putchr(int);
    232 static void	checkpoint(void);
    233 static void	undo(void);
    234 static int	maketf(int);
    235 static int	creatf(const char *);
    236 static int	sopen(const char *, int);
    237 static void	sclose(int);
    238 static void	fspec(const char *);
    239 static const char	*ftok(const char **);
    240 static struct tabulator	*tabstring(const char *);
    241 static void	freetabs(void);
    242 static void	expand(const char *);
    243 static void	growlb(const char *);
    244 static void	growrhs(const char *);
    245 static void	growfn(const char *);
    246 static void	help(void);
    247 
    248 #define	INIT
    249 #define	GETC()		getchr()
    250 #define	UNGETC(c)	(peekc = c)
    251 #define	PEEKC()		(peekc = getchr())
    252 #define	RETURN(c)	return c
    253 #define	ERROR(c)	cmplerr(c)
    254 static wint_t	GETWC(char *);
    255 
    256 #if defined (SUS) || defined (S42) || defined (SU3)
    257 
    258 #include <regex.h>
    259 
    260 #define	NBRA	9
    261 
    262 static char	*braslist[NBRA];
    263 static char	*braelist[NBRA];
    264 static char	*loc1, *loc2, *locs;
    265 static int	nbra;
    266 static int	circf;
    267 static int	nodelim;
    268 
    269 static char	*compile(char *, char *, const char *, int);
    270 static int	step(const char *, const char *);
    271 
    272 #else	/* !SUS, !S42, !SU3 */
    273 
    274 #include <regexp.h>
    275 
    276 #endif	/* !SUS, !S42, !SU3 */
    277 
    278 int
    279 main(int argc, char **argv)
    280 {
    281 	register int i;
    282 	void (*oldintr)(int);
    283 
    284 	progname = basename(argv[0]);
    285 #if defined (SUS) || defined (S42) || defined (SU3)
    286 	setlocale(LC_COLLATE, "");
    287 #endif
    288 	setlocale(LC_CTYPE, "");
    289 	mb_cur_max = MB_CUR_MAX;
    290 	myuid = getuid();
    291 	oldquit = sigset(SIGQUIT, SIG_IGN);
    292 	oldhup = sigset(SIGHUP, SIG_IGN);
    293 	oldintr = sigset(SIGINT, SIG_IGN);
    294 	if (sigset(SIGTERM, SIG_IGN) != SIG_IGN)
    295 		sigset(SIGTERM, quit);
    296 	oldpipe = sigset(SIGPIPE, onpipe);
    297 	argv++;
    298 	while (argc > 1 && **argv=='-') {
    299 		if ((*argv)[1] == '\0') {
    300 			vflag = 0;
    301 			goto next;
    302 		} else if ((*argv)[1] == '-' && (*argv)[2] == '\0') {
    303 			argv++;
    304 			argc--;
    305 			break;
    306 		}
    307 	letter:	switch((*argv)[1]) {
    308 
    309 		case 's':
    310 			vflag = 0;
    311 			break;
    312 
    313 		case 'q':
    314 			sigset(SIGQUIT, SIG_DFL);
    315 			vflag = 1;
    316 			break;
    317 
    318 		case 'p':
    319 			if ((*argv)[2])
    320 				prompt = &(*argv)[2];
    321 			else if (argv[1]) {
    322 				prompt = argv[1];
    323 				argv++;
    324 				argc--;
    325 			} else
    326 				usage((*argv)[1], 1);
    327 			Pflag = 1;
    328 			goto next;
    329 
    330 		default:
    331 			usage((*argv)[1], 0);
    332 		}
    333 		if ((*argv)[2]) {
    334 			(*argv)++;
    335 			goto letter;
    336 		}
    337 	next:	argv++;
    338 		argc--;
    339 	}
    340 
    341 	growfn("no space");
    342 	if (argc>1) {
    343 		i = -1;
    344 		do
    345 			if (++i >= FNSIZE)
    346 				growfn("maximum of characters in "
    347 						"file names reached");
    348 		while (savedfile[i] = (*argv)[i]);
    349 		globp = "e";
    350 	}
    351 	names = malloc(26*sizeof *names);
    352 	undnames = malloc(26*sizeof *undnames);
    353 	zero = malloc(nlall*sizeof *zero);
    354 	if ((undzero = malloc(nlall*sizeof *undzero)) == NULL)
    355 		puts("no memory for undo");
    356 	growlb("no space");
    357 	growrhs("no space");
    358 	init();
    359 	if (oldintr != SIG_IGN)
    360 		sigset(SIGINT, onintr);
    361 	if (oldhup != SIG_IGN)
    362 		sigset(SIGHUP, onhup);
    363 	setjmp(savej);
    364 	if (lastsig) {
    365 		sigrelse(lastsig);
    366 		lastsig = 0;
    367 	}
    368 	commands();
    369 	quit(0);
    370 	/*NOTREACHED*/
    371 	return 0;
    372 }
    373 
    374 static void
    375 usage(char c, int misarg)
    376 {
    377 	if (c) {
    378 		write(2, progname, strlen(progname));
    379 		if (misarg)
    380 			write(2, ": option requires an argument -- ", 33);
    381 		else
    382 			write(2, ": illegal option -- ", 20);
    383 		write(2, &c, 1);
    384 		write(2, "\n", 1);
    385 	}
    386 	write(2, "usage: ", 7);
    387 	write(2, progname, strlen(progname));
    388 	write(2, " [- | -s] [-p string] [file]\n", 29);
    389 	exit(2);
    390 }
    391 
    392 static void
    393 commands(void)
    394 {
    395 	register long *a1;
    396 	register int c;
    397 	int	n;
    398 
    399 	for (;;) {
    400 	if (pflag) {
    401 		pflag = 0;
    402 		addr1 = addr2 = dot;
    403 		goto print;
    404 	}
    405 	if (Pflag && globp == NULL)
    406 		write(1, prompt, strlen(prompt));
    407 	addr1 = 0;
    408 	addr2 = 0;
    409 	switch (c = getchr()) {
    410 	case ',':
    411 	case ';':
    412 		addr2 = c == ',' ? zero+1 : dot;
    413 		if (((peekc = getchr()) < '0' || peekc > '9') &&
    414 				peekc != ' ' && peekc != '\t' &&
    415 				peekc != '+' && peekc != '-' &&
    416 				peekc != '^' && peekc != '?' &&
    417 				peekc != '/' && peekc != '$' &&
    418 				peekc != '.' && peekc != '\'') {
    419 			addr1 = addr2;
    420 			a1 = dol;
    421 			goto loop;
    422 		}
    423 		break;
    424 	default:
    425 		peekc = c;
    426 	}
    427 	do {
    428 		addr1 = addr2;
    429 		if ((a1 = address())==0) {
    430 			c = getchr();
    431 			break;
    432 		}
    433 	loop:	addr2 = a1;
    434 		if ((c=getchr()) == ';') {
    435 			c = ',';
    436 			dot = a1;
    437 		}
    438 	} while (c==',');
    439 	if (addr1==0)
    440 		addr1 = addr2;
    441 	switch(c) {
    442 
    443 	case 'a':
    444 		setdot();
    445 		newline();
    446 		checkpoint();
    447 		append(gettty, addr2);
    448 		continue;
    449 
    450 	case 'c':
    451 #if defined (SU3)
    452 		if (addr1 == zero && addr1+1 <= dol) {
    453 			if (addr1 == addr2)
    454 				addr2++;
    455 			addr1++;
    456 		}
    457 #endif	/* SU3 */
    458 		delete();
    459 		append(gettty, addr1-1);
    460 #if defined (SUS) || defined (SU3)
    461 		if (dot == addr1-1 && addr1 <= dol)
    462 			dot = addr1;
    463 #endif	/* SUS || SU3 */
    464 		continue;
    465 
    466 	case 'd':
    467 		delete();
    468 		continue;
    469 
    470 	case 'E':
    471 		fchange = 0;
    472 		c = 'e';
    473 	case 'e':
    474 		setnoaddr();
    475 		if (vflag && fchange) {
    476 			fchange = 0;
    477 			error("warning: expecting `w'");
    478 		}
    479 		filename(c);
    480 		init();
    481 		addr2 = zero;
    482 		goto caseread;
    483 
    484 	case 'f':
    485 		setnoaddr();
    486 		filename(c);
    487 		puts(savedfile);
    488 		continue;
    489 
    490 	case 'g':
    491 		global(1, 0);
    492 		continue;
    493 
    494 	case 'G':
    495 		global(1, 1);
    496 		continue;
    497 
    498 	case 'H':
    499 		prhelp = !prhelp;
    500 		/*FALLTHRU*/
    501 
    502 	case 'h':
    503 		if ((peekc = getchr()) == 'e') {
    504 			peekc = 0;
    505 			if (getchr() != 'l' || getchr() != 'p' ||
    506 					getchr() != '\n')
    507 				error("illegal suffix");
    508 			setnoaddr();
    509 			help();
    510 			continue;
    511 		}
    512 		newline();
    513 		setnoaddr();
    514 		if (prvmsg)
    515 			puts(prvmsg);
    516 		continue;
    517 
    518 	case 'i':
    519 		setdot();
    520 #if defined (SU3)
    521 		if (addr1 == zero) {
    522 			if (addr1 == addr2)
    523 				addr2++;
    524 			addr1++;
    525 			if (dol != zero)
    526 				nonzero();
    527 		} else
    528 #endif	/* SU3 */
    529 			nonzero();
    530 		newline();
    531 		checkpoint();
    532 		append(gettty, addr2-1);
    533 		if (dot == addr2-1)
    534 			dot++;
    535 		continue;
    536 
    537 
    538 	case 'j':
    539 		if (addr2==0) {
    540 			addr1 = dot;
    541 			addr2 = dot+1;
    542 		}
    543 		setdot();
    544 		newline();
    545 		nonzero();
    546 		checkpoint();
    547 		if (addr1 != addr2)
    548 			join();
    549 		continue;
    550 
    551 	case 'k':
    552 		if ((c = getchr()) < 'a' || c > 'z')
    553 			error("mark not lower case");
    554 		newline();
    555 		setdot();
    556 		nonzero();
    557 		names[c-'a'] = *addr2 & ~01;
    558 		anymarks |= 01;
    559 		continue;
    560 
    561 	case 'm':
    562 		move(0);
    563 		continue;
    564 
    565 	case '\n':
    566 		if (addr2==0)
    567 			addr2 = dot+1;
    568 		addr1 = addr2;
    569 		goto print;
    570 
    571 	case 'n':
    572 		numbf = 1;
    573 		newline();
    574 		goto print;
    575 
    576 	case 'N':
    577 		newline();
    578 		setnoaddr();
    579 		Nflag = !Nflag;
    580 		continue;
    581 
    582 	case 'b':
    583 	case 'o':
    584 		n = getnum();
    585 		newline();
    586 		setdot();
    587 		nonzero();
    588 		if (n >= 0) {
    589 			if (c == 'b')
    590 				bcount = n;
    591 			else
    592 				ocount = n;
    593 		}
    594 		if (c == 'b') {
    595 			a1 = addr2+bcount > dol ? dol : addr2 + bcount;
    596 			doprnt(addr1, a1);
    597 			dot = a1;
    598 		} else {
    599 			a1 = addr2+ocount > dol ? dol : addr2 + ocount;
    600 			doprnt(addr2-ocount<zero+1?zero+1:addr2-ocount, a1);
    601 			dot = addr2;
    602 		}
    603 		continue;
    604 
    605 	case 'l':
    606 		listf++;
    607 	case 'p':
    608 		newline();
    609 	print:
    610 		setdot();
    611 		nonzero();
    612 		doprnt(addr1, addr2);
    613 		dot = addr2;
    614 		continue;
    615 
    616 	case 'P':
    617 		setnoaddr();
    618 		newline();
    619 		Pflag = !Pflag;
    620 		continue;
    621 
    622 	case 'Q':
    623 		fchange = 0;
    624 	case 'q':
    625 		setnoaddr();
    626 		newline();
    627 		quit(0);
    628 
    629 	case 'r':
    630 		filename(c);
    631 	caseread:
    632 		if ((io = sopen(file, READ)) < 0) {
    633 			lastc = '\n';
    634 			error2("cannot open input file", file);
    635 		}
    636 		ioeof = 0;
    637 		setall();
    638 		ninbuf = 0;
    639 		if (c == 'r')
    640 			checkpoint();
    641 		n = zero != dol;
    642 		rspec = (c == 'e' || !n) && file[0] != '!';
    643 		append(getfile, addr2);
    644 		rspec = 0;
    645 		exfile();
    646 		fchange = n;
    647 		continue;
    648 
    649 	case 's':
    650 		setdot();
    651 		nonzero();
    652 		substitute(globp!=0);
    653 		continue;
    654 
    655 	case 't':
    656 		move(1);
    657 		continue;
    658 
    659 	case 'u':
    660 		setdot();
    661 		newline();
    662 		if (unddot == NULL)
    663 			error("nothing to undo");
    664 		undo();
    665 		continue;
    666 
    667 	case 'v':
    668 		global(0, 0);
    669 		continue;
    670 
    671 	case 'V':
    672 		global(0, 1);
    673 		continue;
    674 
    675 	case 'W':
    676 		wrapp++;
    677 	case 'w':
    678 	write:
    679 		setall();
    680 		if (zero != dol)
    681 			nonzero();
    682 		filename(c);
    683 		if(!wrapp ||
    684 		  ((io = open(file,O_WRONLY|O_APPEND)) == -1) ||
    685 		  ((lseek(io, 0, SEEK_END)) == -1)) {
    686 			struct stat	st;
    687 			if (lstat(file, &st) == 0 &&
    688 					(st.st_mode&S_IFMT) == S_IFREG &&
    689 					st.st_nlink == 1 &&
    690 					(myuid==0 || myuid==st.st_uid)) {
    691 				char	*cp, *tp;
    692 				int	nio;
    693 				if ((io = sopen(file, EXIST)) < 0)
    694 					error("cannot create output file");
    695 				if ((wrtemp = malloc(strlen(file)+8)) == NULL)
    696 					error("out of memory");
    697 				for (cp = file, tp = wrtemp; *cp; cp++)
    698 					*tp++ = *cp;
    699 				while (tp > wrtemp && tp[-1] != '/')
    700 					tp--;
    701 				for (cp = "\7XXXXXX"; *cp; cp++)
    702 					*tp++ = *cp;
    703 				*tp = '\0';
    704 				if ((nio = mkstemp(wrtemp)) < 0) {
    705 					free(wrtemp);
    706 					wrtemp = NULL;
    707 					ftruncate(io, 0);
    708 				} else {
    709 					close(io);
    710 					io = nio;
    711 				}
    712 			} else {
    713 				if ((io = sopen(file, WRITE)) < 0)
    714 					error("cannot create output file");
    715 			}
    716 		}
    717 		if (zero != dol) {
    718 			ioeof = 0;
    719 			wrapp = 0;
    720 			putfile();
    721 		}
    722 		exfile();
    723 		if (addr1==zero+1 && addr2==dol || addr1==addr2 && dol==zero)
    724 			fchange = 0;
    725 		if (c == 'z')
    726 			quit(0);
    727 		continue;
    728 
    729 	case 'z':
    730 		if ((peekc=getchr()) != '\n')
    731 			error("illegal suffix");
    732 		setnoaddr();
    733 		goto write;
    734 
    735 	case '=':
    736 		setall();
    737 		newline();
    738 		putd((addr2-zero)&MAXCNT);
    739 		putchr('\n');
    740 		continue;
    741 
    742 	case '!':
    743 		callunix();
    744 		continue;
    745 
    746 	case EOF:
    747 		return;
    748 
    749 	}
    750 	error("unknown command");
    751 	}
    752 }
    753 
    754 static long *
    755 address(void)
    756 {
    757 	register long *a1;
    758 	register int minus, c;
    759 	int n, relerr;
    760 
    761 	minus = 0;
    762 	a1 = 0;
    763 	for (;;) {
    764 		c = getchr();
    765 		if ('0'<=c && c<='9') {
    766 			n = 0;
    767 			do {
    768 				n *= 10;
    769 				n += c - '0';
    770 			} while ((c = getchr())>='0' && c<='9');
    771 			peekc = c;
    772 			if (a1==0)
    773 				a1 = zero;
    774 			if (minus<0)
    775 				n = -n;
    776 			a1 += n;
    777 			minus = 0;
    778 			continue;
    779 		}
    780 		relerr = 0;
    781 		if (a1 || minus)
    782 			relerr++;
    783 		switch(c) {
    784 		case ' ':
    785 		case '\t':
    786 			continue;
    787 	
    788 		case '+':
    789 			minus++;
    790 			if (a1==0)
    791 				a1 = dot;
    792 			continue;
    793 
    794 		case '-':
    795 		case '^':
    796 			minus--;
    797 			if (a1==0)
    798 				a1 = dot;
    799 			continue;
    800 	
    801 		case '?':
    802 		case '/':
    803 			compile(NULL, expbuf, &expbuf[ESIZE], c);
    804 			a1 = dot;
    805 			for (;;) {
    806 				if (c=='/') {
    807 					a1++;
    808 					if (a1 > dol)
    809 						a1 = zero;
    810 				} else {
    811 					a1--;
    812 					if (a1 < zero)
    813 						a1 = dol;
    814 				}
    815 				if (execute(0, a1, 0))
    816 					break;
    817 				if (a1==dot)
    818 					error("search string not found");
    819 			}
    820 			break;
    821 	
    822 		case '$':
    823 			a1 = dol;
    824 			break;
    825 	
    826 		case '.':
    827 			a1 = dot;
    828 			break;
    829 
    830 		case '\'':
    831 			if ((c = getchr()) < 'a' || c > 'z')
    832 				error("mark not lower case");
    833 			for (a1=zero; a1<=dol; a1++)
    834 				if (names[c-'a'] == (*a1 & ~01))
    835 					break;
    836 			break;
    837 	
    838 		default:
    839 			peekc = c;
    840 			if (a1==0)
    841 				return(0);
    842 			a1 += minus;
    843 			if (a1<zero || a1>dol)
    844 				error("line out of range");
    845 			return(a1);
    846 		}
    847 		if (relerr)
    848 			error("bad number");
    849 	}
    850 }
    851 
    852 static void
    853 setdot(void)
    854 {
    855 	if (addr2 == 0)
    856 		addr1 = addr2 = dot;
    857 	if (addr1 > addr2)
    858 		error("bad range");
    859 }
    860 
    861 static void
    862 setall(void)
    863 {
    864 	if (addr2==0) {
    865 		addr1 = zero+1;
    866 		addr2 = dol;
    867 		if (dol==zero)
    868 			addr1 = zero;
    869 	}
    870 	setdot();
    871 }
    872 
    873 static void
    874 setnoaddr(void)
    875 {
    876 	if (addr2)
    877 		error("Illegal address count");
    878 }
    879 
    880 static void
    881 nonzero(void)
    882 {
    883 	if (addr1<=zero || addr2>dol)
    884 		error("line out of range");
    885 }
    886 
    887 static void
    888 newline(void)
    889 {
    890 	register int c;
    891 
    892 	if ((c = getchr()) == '\n')
    893 		return;
    894 	if (c=='p' || c=='l' || c=='n') {
    895 		pflag++;
    896 		if (c=='l')
    897 			listf++;
    898 		else if (c=='n')
    899 			numbf = 1;
    900 		if (getchr() == '\n')
    901 			return;
    902 	}
    903 	error("illegal suffix");
    904 }
    905 
    906 static void
    907 filename(int comm)
    908 {
    909 	register char *p1, *p2;
    910 	register int c, i;
    911 
    912 	count = 0;
    913 	c = getchr();
    914 	if (c=='\n' || c==EOF) {
    915 		p1 = savedfile;
    916 		if (*p1==0 && comm!='f')
    917 			error("illegal or missing filename");
    918 		p2 = file;
    919 		while (*p2++ = *p1++)
    920 			;
    921 		return;
    922 	}
    923 	if (c!=' ')
    924 		error("no space after command");
    925 	while ((c = getchr()) == ' ')
    926 		;
    927 	if (c=='\n')
    928 		error("illegal or missing filename");
    929 	i = 0;
    930 	do {
    931 		if (i >= FNSIZE)
    932 			growfn("maximum of characters in file names reached");
    933 		file[i++] = c;
    934 		if (c==' ' && file[0] != '!' || c==EOF)
    935 			error("illegal or missing filename");
    936 	} while ((c = getchr()) != '\n');
    937 	file[i++] = 0;
    938 	if ((savedfile[0]==0 || comm=='e' || comm=='f') && file[0] != '!') {
    939 		p1 = savedfile;
    940 		p2 = file;
    941 		while (*p1++ = *p2++)
    942 			;
    943 	}
    944 }
    945 
    946 static void
    947 exfile(void)
    948 {
    949 	sclose(io);
    950 	io = -1;
    951 	if (wrtemp) {
    952 		extern int	rename(const char *, const char *);
    953 		if (rename(wrtemp, file) < 0)
    954 			error("cannot create output file");
    955 		if (myuid == 0)
    956 			chown(file, fstbuf.st_uid, fstbuf.st_gid);
    957 		chmod(file, fstbuf.st_mode & 07777);
    958 		free(wrtemp);
    959 		wrtemp = NULL;
    960 	}
    961 	if (vflag) {
    962 		putd(count);
    963 		putchr('\n');
    964 	}
    965 }
    966 
    967 static void
    968 onintr(int signo)
    969 {
    970 	lastsig = signo;
    971 	putchr('\n');
    972 	lastc = '\n';
    973 	if (readop) {
    974 		puts("\007read may be incomplete - beware!\007");
    975 		fchange = 0;
    976 	}
    977 	error("interrupt");
    978 }
    979 
    980 static void
    981 onhup(int signo)
    982 {
    983 	if (dol > zero && fchange) {
    984 		addr1 = zero+1;
    985 		addr2 = dol;
    986 		io = creat("ed.hup", 0666);
    987 		if (io < 0) {
    988 			char	*home = getenv("HOME");
    989 			if (home) {
    990 				char	*fn = malloc(strlen(home) + 10);
    991 				if (fn) {
    992 					strcpy(fn, home);
    993 					strcat(fn, "/ed.hup");
    994 					io = creat(fn, 0666);
    995 				}
    996 			}
    997 		}
    998 		if (io >= 0)
    999 			putfile();
   1000 	}
   1001 	fchange = 0;
   1002 	status = 0200 | signo;
   1003 	quit(0);
   1004 }
   1005 
   1006 static void
   1007 onpipe(int signo)
   1008 {
   1009 	lastsig = signo;
   1010 	error("write or open on pipe failed");
   1011 }
   1012 
   1013 static void
   1014 error(const char *s)
   1015 {
   1016 	error2(s, NULL);
   1017 }
   1018 
   1019 static void
   1020 error2(const char *s, const char *fn)
   1021 {
   1022 	register int c;
   1023 
   1024 	wrapp = 0;
   1025 	listf = 0;
   1026 	numbf = 0;
   1027 	errput(s, fn);
   1028 	count = 0;
   1029 	if (lseek(0, 0, SEEK_END) > 0)
   1030 		status = 2;
   1031 	pflag = 0;
   1032 	if (globp)
   1033 		lastc = '\n';
   1034 	globp = 0;
   1035 	peekc = lastc;
   1036 	if(lastc)
   1037 		while ((c = getchr()) != '\n' && c != EOF)
   1038 			;
   1039 	if (io > 0) {
   1040 		sclose(io);
   1041 		io = -1;
   1042 	}
   1043 	if (wrtemp) {
   1044 		unlink(wrtemp);
   1045 		free(wrtemp);
   1046 		wrtemp = NULL;
   1047 	}
   1048 	longjmp(savej, 1);
   1049 }
   1050 
   1051 static void
   1052 errput(const char *s, const char *fn)
   1053 {
   1054 	prvmsg = s;
   1055 	if (fn) {
   1056 		putchr('?');
   1057 		puts(fn);
   1058 	} else
   1059 		puts("?");
   1060 	if (prhelp)
   1061 		puts(s);
   1062 }
   1063 
   1064 static int
   1065 getchr(void)
   1066 {
   1067 	char c;
   1068 	if (lastc=peekc) {
   1069 		peekc = 0;
   1070 		return(lastc);
   1071 	}
   1072 	if (globp) {
   1073 		if ((lastc = *globp++) != 0)
   1074 			return(lastc);
   1075 		globp = 0;
   1076 		return(EOF);
   1077 	}
   1078 	if (read(0, &c, 1) <= 0)
   1079 		return(lastc = EOF);
   1080 	lastc = c;
   1081 	return(lastc);
   1082 }
   1083 
   1084 static int
   1085 gettty(void)
   1086 {
   1087 	register int c, i;
   1088 	register char *gf;
   1089 
   1090 	i = 0;
   1091 	gf = globp;
   1092 	while ((c = getchr()) != '\n') {
   1093 		if (c==EOF) {
   1094 			if (gf)
   1095 				peekc = c;
   1096 			return(c);
   1097 		}
   1098 		if (c == 0)
   1099 			continue;
   1100 		if (i >= LBSIZE)
   1101 			growlb("line too long");
   1102 		linebuf[i++] = c;
   1103 	}
   1104 	if (i >= LBSIZE-2)
   1105 		growlb("line too long");
   1106 	linebuf[i++] = 0;
   1107 	if (linebuf[0]=='.' && linebuf[1]==0)
   1108 		return(EOF);
   1109 #if !defined (SUS) && !defined (SU3)
   1110 	if (linebuf[0]=='\\' && linebuf[1]=='.' && linebuf[2]==0)
   1111 		linebuf[0]='.', linebuf[1]=0;
   1112 #endif
   1113 	return(0);
   1114 }
   1115 
   1116 static long
   1117 getnum(void)
   1118 {
   1119 	char	scount[20];
   1120 	int	i;
   1121 
   1122 	i = 0;
   1123 	while ((peekc=getchr()) >= '0' && peekc <= '9' && i < sizeof scount) {
   1124 		scount[i++] = peekc;
   1125 		peekc = 0;
   1126 	}
   1127 	scount[i] = '\0';
   1128 	return i ? atol(scount) : -1;
   1129 }
   1130 
   1131 static int
   1132 getfile(void)
   1133 {
   1134 	register int c, i, j;
   1135 	static int	nextj;
   1136 
   1137 	i = 0;
   1138 	j = nextj;
   1139 	do {
   1140 		if (--ninbuf < 0) {
   1141 			if (ioeof || (ninbuf=read(io, genbuf, LBSIZE)-1) < 0) {
   1142 				if (ioeof == 0 && ninbuf < -1) {
   1143 					puts("input error");
   1144 					status = 1;
   1145 				}
   1146 				if (i > 0) {
   1147 					puts("'\\n' appended");
   1148 					c = '\n';
   1149 					ioeof = 1;
   1150 					goto wrc;
   1151 				}
   1152 				return(EOF);
   1153 			}
   1154 			j = 0;
   1155 		}
   1156 		c = genbuf[j++]&0377;
   1157 	wrc:	if (i >= LBSIZE) {
   1158 			lastc = '\n';
   1159 			growlb("line too long");
   1160 		}
   1161 		linebuf[i++] = c ? c : '\n';
   1162 		count++;
   1163 	} while (c != '\n');
   1164 	linebuf[--i] = 0;
   1165 	nextj = j;
   1166 	if (rspec && dot == zero)
   1167 		fspec(linebuf);
   1168 	if (maxlength && i > maxlength) {
   1169 		putstr("line too long: lno = ");
   1170 		putd((dot - zero+1)&MAXCNT);
   1171 		putchr('\n');
   1172 	}
   1173 	return(0);
   1174 }
   1175 
   1176 static void
   1177 putfile(void)
   1178 {
   1179 	long *a1;
   1180 	int n;
   1181 	register char *fp, *lp;
   1182 	register int nib;
   1183 
   1184 	nib = 512;
   1185 	fp = genbuf;
   1186 	a1 = addr1;
   1187 	do {
   1188 		lp = getline(*a1++, 0);
   1189 		if (maxlength) {
   1190 			for (n = 0; lp[n]; n++);
   1191 			if (n > maxlength) {
   1192 				putstr("line too long: lno = ");
   1193 				putd((a1-1 - zero)&MAXCNT);
   1194 				putchr('\n');
   1195 			}
   1196 		}
   1197 		for (;;) {
   1198 			if (--nib < 0) {
   1199 				n = fp-genbuf;
   1200 				if(write(io, genbuf, n) != n)
   1201 					error("write error");
   1202 				nib = 511;
   1203 				fp = genbuf;
   1204 			}
   1205 			count++;
   1206 			if ((*fp++ = *lp++) == 0) {
   1207 				fp[-1] = '\n';
   1208 				break;
   1209 			} else if (fp[-1] == '\n')
   1210 				fp[-1] = '\0';
   1211 		}
   1212 	} while (a1 <= addr2);
   1213 	n = fp-genbuf;
   1214 	if(write(io, genbuf, n) != n)
   1215 		error("write error");
   1216 }
   1217 
   1218 static int
   1219 append(int (*f)(void), long *a)
   1220 {
   1221 	register long *a1, *a2, *rdot;
   1222 	int nline, tl;
   1223 
   1224 	nline = 0;
   1225 	dot = a;
   1226 	while ((*f)() == 0) {
   1227 		if ((dol-zero)+1 >= nlall) {
   1228 			long *ozero = zero;
   1229 			nlall += 512;
   1230 			if ((zero = realloc(zero, nlall*sizeof *zero))==NULL) {
   1231 				lastc = '\n';
   1232 				zero = ozero;
   1233 				error("out of memory for append");
   1234 			}
   1235 			dot += zero - ozero;
   1236 			dol += zero - ozero;
   1237 			addr1 += zero - ozero;
   1238 			addr2 += zero - ozero;
   1239 			if (unddot) {
   1240 				unddot += zero - ozero;
   1241 				unddol += zero - ozero;
   1242 			}
   1243 			if (undzero) {
   1244 				ozero = undzero;
   1245 				if ((undzero = realloc(undzero,
   1246 						nlall*sizeof *undzero)) == 0) {
   1247 					puts("no memory for undo");
   1248 					free(ozero);
   1249 				}
   1250 			}
   1251 		}
   1252 		tl = putline();
   1253 		nline++;
   1254 		a1 = ++dol;
   1255 		a2 = a1+1;
   1256 		rdot = ++dot;
   1257 		while (a1 > rdot)
   1258 			*--a2 = *--a1;
   1259 		*rdot = tl;
   1260 	}
   1261 	return(nline);
   1262 }
   1263 
   1264 static void
   1265 callunix(void)
   1266 {
   1267 	char	*line;
   1268 	void (*savint)(int);
   1269 	pid_t pid, rpid;
   1270 	int retcode;
   1271 
   1272 	setnoaddr();
   1273 	line = readcmd();
   1274 	if ((pid = fork()) == 0) {
   1275 		sigset(SIGHUP, oldhup);
   1276 		sigset(SIGQUIT, oldquit);
   1277 		sigset(SIGPIPE, oldpipe);
   1278 		execl(SHELL, "sh", "-c", line, NULL);
   1279 		_exit(0100);
   1280 	} else if (pid < 0)
   1281 		error("fork failed - try again");
   1282 	savint = sigset(SIGINT, SIG_IGN);
   1283 	while ((rpid = wait(&retcode)) != pid && rpid != -1)
   1284 		;
   1285 	sigset(SIGINT, savint);
   1286 	if (vflag)
   1287 		puts("!");
   1288 }
   1289 
   1290 #define	cmadd(c)	((i>=cmsize ? \
   1291 		((line=realloc(line,cmsize+=128)) == 0 ? \
   1292 			(error("line too long"),0) : 0, 0) \
   1293 		: 0), line[i++]=(c))
   1294 
   1295 static char *
   1296 readcmd(void)
   1297 {
   1298 	static char	*line, *prev;
   1299 	static int	cmsize, pvsize;
   1300 	char	*pp;
   1301 	int	c, mod = 0, i;
   1302 
   1303 	i = 0;
   1304 	if ((c = getchr()) == '!') {
   1305 		for (pp = prev; *pp; pp++)
   1306 			line[i++] = *pp;
   1307 		mod = 1;
   1308 		c = getchr();
   1309 	}
   1310 	while (c != '\n' && c != EOF) {
   1311 		if (c == '\\') {
   1312 			c = getchr();
   1313 			if (c != '%')
   1314 				cmadd('\\');
   1315 			cmadd(c);
   1316 		} else if (c == '%') {
   1317 			for (pp = savedfile; *pp; pp++)
   1318 				cmadd(*pp);
   1319 			mod = 1;
   1320 		} else
   1321 			cmadd(c);
   1322 		c = getchr();
   1323 	}
   1324 	cmadd('\0');
   1325 	if (pvsize < cmsize && (prev = realloc(prev, pvsize=cmsize)) == 0)
   1326 		error("line too long");
   1327 	strcpy(prev, line);
   1328 	if (mod)
   1329 		nlputs(line);
   1330 	return line;
   1331 }
   1332 
   1333 static void
   1334 quit(int signo)
   1335 {
   1336 	lastsig = signo;
   1337 	if (vflag && fchange) {
   1338 		fchange = 0;
   1339 		error("warning: expecting `w'");
   1340 	}
   1341 	if (wrtemp)
   1342 		unlink(wrtemp);
   1343 	unlink(tfname);
   1344 	exit(status);
   1345 }
   1346 
   1347 static void
   1348 delete(void)
   1349 {
   1350 	setdot();
   1351 	newline();
   1352 	nonzero();
   1353 	checkpoint();
   1354 	rdelete(addr1, addr2);
   1355 }
   1356 
   1357 static void
   1358 rdelete(long *ad1, long *ad2)
   1359 {
   1360 	register long *a1, *a2, *a3;
   1361 
   1362 	a1 = ad1;
   1363 	a2 = ad2+1;
   1364 	a3 = dol;
   1365 	dol -= a2 - a1;
   1366 	do {
   1367 		*a1++ = *a2++;
   1368 	} while (a2 <= a3);
   1369 	a1 = ad1;
   1370 	if (a1 > dol)
   1371 		a1 = dol;
   1372 	dot = a1;
   1373 	fchange = 1;
   1374 }
   1375 
   1376 static void
   1377 gdelete(void)
   1378 {
   1379 	register long *a1, *a2, *a3;
   1380 
   1381 	a3 = dol;
   1382 	for (a1=zero+1; (*a1&01)==0; a1++)
   1383 		if (a1>=a3)
   1384 			return;
   1385 	for (a2=a1+1; a2<=a3;) {
   1386 		if (*a2&01) {
   1387 			a2++;
   1388 			dot = a1;
   1389 		} else
   1390 			*a1++ = *a2++;
   1391 	}
   1392 	dol = a1-1;
   1393 	if (dot>dol)
   1394 		dot = dol;
   1395 	fchange = 1;
   1396 }
   1397 
   1398 static char *
   1399 getline(long tl, int nulterm)
   1400 {
   1401 	register char *bp, *lp;
   1402 	register long nl;
   1403 
   1404 	lp = linebuf;
   1405 	bp = getblock(tl, READ);
   1406 	nl = nleft;
   1407 	tl &= ~0377;
   1408 	while (*lp++ = *bp++) {
   1409 		if (lp[-1] == '\n' && nulterm) {
   1410 			lp[-1] = '\0';
   1411 			break;
   1412 		}
   1413 		if (--nl == 0) {
   1414 			bp = getblock(tl+=0400, READ);
   1415 			nl = nleft;
   1416 		}
   1417 	}
   1418 	return(linebuf);
   1419 }
   1420 
   1421 static int
   1422 putline(void)
   1423 {
   1424 	register char *bp, *lp;
   1425 	register long nl;
   1426 	long tl;
   1427 
   1428 	fchange = 1;
   1429 	lp = linebuf;
   1430 	tl = tline;
   1431 	bp = getblock(tl, WRITE);
   1432 	nl = nleft;
   1433 	tl &= ~0377;
   1434 	while (*bp = *lp++) {
   1435 		if (*bp++ == '\n' && insub) {
   1436 			*--bp = 0;
   1437 			linebp = lp;
   1438 			break;
   1439 		}
   1440 		if (--nl == 0) {
   1441 			bp = getblock(tl+=0400, WRITE);
   1442 			nl = nleft;
   1443 		}
   1444 	}
   1445 	nl = tline;
   1446 	tline += (((lp-linebuf)+03)>>1)&(MAXCNT-1);
   1447 	return(nl);
   1448 }
   1449 
   1450 static char *
   1451 getblock(long atl, long iof)
   1452 {
   1453 	register long bno, off;
   1454 	
   1455 	bno = (atl>>8)&BLKMSK;
   1456 	off = (atl<<1)&0774;
   1457 	if (bno >= BLKMSK) {
   1458 		lastc = '\n';
   1459 		error("temp file too big");
   1460 	}
   1461 	nleft = 512 - off;
   1462 	if (bno==iblock) {
   1463 		ichanged |= iof;
   1464 		return(ibuff+off);
   1465 	}
   1466 	if (bno==oblock)
   1467 		return(obuff+off);
   1468 	if (iof==READ) {
   1469 		if (ichanged)
   1470 			blkio(iblock, ibuff, 1);
   1471 		ichanged = 0;
   1472 		iblock = bno;
   1473 		blkio(bno, ibuff, 0);
   1474 		return(ibuff+off);
   1475 	}
   1476 	if (oblock>=0)
   1477 		blkio(oblock, obuff, 1);
   1478 	oblock = bno;
   1479 	return(obuff+off);
   1480 }
   1481 
   1482 static void
   1483 blkio(long b, char *buf, int wr)
   1484 {
   1485 	lseek(tfile, b<<9, SEEK_SET);
   1486 	if ((wr ? write(tfile, buf, 512) : read (tfile, buf, 512)) != 512) {
   1487 		status = 1;
   1488 		error("I/O error on temp file");
   1489 	}
   1490 }
   1491 
   1492 static void
   1493 init(void)
   1494 {
   1495 	register long *markp;
   1496 
   1497 	tline = 2;
   1498 	for (markp = names; markp < &names[26]; markp++)
   1499 		*markp = 0;
   1500 	for (markp = undnames; markp < &undnames[26]; markp++)
   1501 		*markp = 0;
   1502 	subnewa = 0;
   1503 	anymarks = 0;
   1504 	iblock = -1;
   1505 	oblock = -1;
   1506 	ichanged = 0;
   1507 	tfile = maketf(tfile);
   1508 	dot = dol = zero;
   1509 	unddot = NULL;
   1510 }
   1511 
   1512 static void
   1513 global(int k, int ia)
   1514 {
   1515 	register int c;
   1516 	register long *a1;
   1517 	static char *globuf;
   1518 	char	mb[MB_LEN_MAX+1];
   1519 	int	spflag = 0;
   1520 
   1521 	if (globp)
   1522 		error("multiple globals not allowed");
   1523 	setall();
   1524 	nonzero();
   1525 	if ((c=GETWC(mb))=='\n')
   1526 		error("incomplete global expression");
   1527 	compile(NULL, expbuf, &expbuf[ESIZE], c);
   1528 	if (!ia) {
   1529 		globrd(&globuf, EOF);
   1530 		if (globuf[0] == '\n')
   1531 			globuf[0] = 'p', globuf[1] = '\n', globuf[2] = '\0';
   1532 	} else {
   1533 		newline();
   1534 		spflag = pflag;
   1535 		pflag = 0;
   1536 	}
   1537 	checkpoint();
   1538 	for (a1=zero; a1<=dol; a1++) {
   1539 		*a1 &= ~01;
   1540 		if (a1>=addr1 && a1<=addr2 && execute(0, a1, 0)==k)
   1541 			*a1 |= 01;
   1542 	}
   1543 	/*
   1544 	 * Special case: g/.../d (avoid n^2 algorithm)
   1545 	 */
   1546 	if (!ia && globuf[0]=='d' && globuf[1]=='\n' && globuf[2]=='\0') {
   1547 		gdelete();
   1548 		return;
   1549 	}
   1550 	for (a1=zero; a1<=dol; a1++) {
   1551 		if (*a1 & 01) {
   1552 			*a1 &= ~01;
   1553 			dot = a1;
   1554 			if (ia) {
   1555 				puts(getline(*a1, 0));
   1556 				if ((c = getchr()) == EOF)
   1557 					error("command expected");
   1558 				if (c == 'a' || c == 'c' || c == 'i')
   1559 					error("a, i, or c not allowed in G");
   1560 				else if (c == '&') {
   1561 					if ((c = getchr()) != '\n')
   1562 						error("end of line expected");
   1563 					if (globuf == 0 || *globuf == 0)
   1564 						error("no remembered command");
   1565 				} else if (c == '\n') {
   1566 					a1 = zero;
   1567 					continue;
   1568 				} else
   1569 					globrd(&globuf, c);
   1570 			}
   1571 			globp = globuf;
   1572 			commands();
   1573 			globp = NULL;
   1574 			a1 = zero;
   1575 		}
   1576 	}
   1577 	if (ia)
   1578 		pflag = spflag;
   1579 }
   1580 
   1581 static void
   1582 globrd(char **globuf, register int c)
   1583 {
   1584 	register int i;
   1585 
   1586 	if (*globuf == 0 && (*globuf = malloc(GBSIZE=256)) == 0)
   1587 		error("global too long");
   1588 	i = 0;
   1589 	if (c != EOF)
   1590 		(*globuf)[i++] = c;
   1591 	while ((c = getchr()) != '\n') {
   1592 		if (c==EOF)
   1593 			error("incomplete global expression");
   1594 		if (c=='\\') {
   1595 			c = getchr();
   1596 			if (c!='\n')
   1597 				(*globuf)[i++] = '\\';
   1598 		}
   1599 		(*globuf)[i++] = c;
   1600 		if (i>=GBSIZE-4 && (*globuf=realloc(*globuf,GBSIZE+=256)) == 0)
   1601 			error("global too long");
   1602 	}
   1603 	(*globuf)[i++] = '\n';
   1604 	(*globuf)[i++] = 0;
   1605 }
   1606 
   1607 static void
   1608 join(void)
   1609 {
   1610 	register int i, j;
   1611 	register long *a1;
   1612 
   1613 	j = 0;
   1614 	for (a1=addr1; a1<=addr2; a1++) {
   1615 		i = getline(*a1, 0) - linebuf;
   1616 		while (genbuf[j] = linebuf[i++])
   1617 			if (j++ >= LBSIZE-2)
   1618 				growlb("line too long");
   1619 	}
   1620 	i = 0;
   1621 	j = 0;
   1622 	while (linebuf[i++] = genbuf[j++])
   1623 		;
   1624 	*addr1 = putline();
   1625 	if (addr1<addr2)
   1626 		rdelete(addr1+1, addr2);
   1627 	dot = addr1;
   1628 }
   1629 
   1630 static void
   1631 substitute(int inglob)
   1632 {
   1633 	register long *markp;
   1634 	register long *a1;
   1635 	intptr_t nl;
   1636 	int gsubf;
   1637 
   1638 	checkpoint();
   1639 	gsubf = compsub();
   1640 	insub = 1;
   1641 	for (a1 = addr1; a1 <= addr2; a1++) {
   1642 		long *ozero;
   1643 		if (execute(0, a1, 1)==0)
   1644 			continue;
   1645 		inglob |= dosub(gsubf < 2);
   1646 		if (gsubf) {
   1647 			int	i = 1;
   1648 
   1649 			while (*loc2) {
   1650 				if (execute(1, NULL, 1)==0)
   1651 					break;
   1652 				inglob |= dosub(gsubf == -1 || ++i == gsubf);
   1653 			}
   1654 		}
   1655 		subnewa = putline();
   1656 		*a1 &= ~01;
   1657 		if (anymarks) {
   1658 			for (markp = names; markp < &names[26]; markp++)
   1659 				if (*markp == *a1)
   1660 					*markp = subnewa;
   1661 		}
   1662 		*a1 = subnewa;
   1663 		ozero = zero;
   1664 		nl = append(getsub, a1);
   1665 		nl += zero-ozero;
   1666 		a1 += nl;
   1667 		addr2 += nl;
   1668 	}
   1669 	insub = 0;
   1670 	if (inglob==0)
   1671 		error("no match");
   1672 }
   1673 
   1674 static int
   1675 compsub(void)
   1676 {
   1677 	register int seof, c, i;
   1678 	static char	*oldrhs;
   1679 	static int	orhssz;
   1680 	char	mb[MB_LEN_MAX+1];
   1681 
   1682 	if ((seof = GETWC(mb)) == '\n' || seof == ' ')
   1683 		error("illegal or missing delimiter");
   1684 	nodelim = 0;
   1685 	compile(NULL, expbuf, &expbuf[ESIZE], seof);
   1686 	i = 0;
   1687 	for (;;) {
   1688 		c = GETWC(mb);
   1689 		if (c=='\\') {
   1690 			if (i >= RHSIZE-2)
   1691 				growrhs("replacement string too long");
   1692 			rhsbuf[i++] = c;
   1693 			c = GETWC(mb);
   1694 		} else if (c=='\n') {
   1695 			if (globp && *globp) {
   1696 				if (i >= RHSIZE-2)
   1697 					growrhs("replacement string too long");
   1698 				rhsbuf[i++] = '\\';
   1699 			}
   1700 			else if (nodelim)
   1701 				error("illegal or missing delimiter");
   1702 			else {
   1703 				peekc = c;
   1704 				pflag++;
   1705 				break;
   1706 			}
   1707 		} else if (c==seof)
   1708 			break;
   1709 		for (c = 0; c==0 || mb[c]; c++) {
   1710 			if (i >= RHSIZE-2)
   1711 				growrhs("replacement string too long");
   1712 			rhsbuf[i++] = mb[c];
   1713 		}
   1714 	}
   1715 	rhsbuf[i++] = 0;
   1716 	if (rhsbuf[0] == '%' && rhsbuf[1] == 0) {
   1717 		if (orhssz == 0)
   1718 			error("no remembered replacement string");
   1719 		strcpy(rhsbuf, oldrhs);
   1720 	} else {
   1721 		if (orhssz < RHSIZE &&
   1722 				(oldrhs = realloc(oldrhs, orhssz=RHSIZE)) == 0)
   1723 			error("replacement string too long");
   1724 		strcpy(oldrhs, rhsbuf);
   1725 	}
   1726 	if ((peekc = getchr()) == 'g') {
   1727 		peekc = 0;
   1728 		newline();
   1729 		return(-1);
   1730 	} else if (peekc >= '0' && peekc <= '9') {
   1731 		c = getnum();
   1732 		if (c < 1 || c > LBSIZE)
   1733 			error("invalid count");
   1734 		newline();
   1735 		return c;
   1736 	}
   1737 	newline();
   1738 	return(0);
   1739 }
   1740 
   1741 static int
   1742 getsub(void)
   1743 {
   1744 	register char *p1, *p2;
   1745 
   1746 	p1 = linebuf;
   1747 	if ((p2 = linebp) == 0)
   1748 		return(EOF);
   1749 	while (*p1++ = *p2++)
   1750 		;
   1751 	linebp = 0;
   1752 	return(0);
   1753 }
   1754 
   1755 static int
   1756 dosub(int really)
   1757 {
   1758 	register char *lp, *sp;
   1759 	register int i, j, k;
   1760 	int c;
   1761 
   1762 	if (!really)
   1763 		goto copy;
   1764 	i = 0;
   1765 	j = 0;
   1766 	k = 0;
   1767 	while (&linebuf[i] < loc1)
   1768 		genbuf[j++] = linebuf[i++];
   1769 	while (c = rhsbuf[k++]&0377) {
   1770 		if (c=='&') {
   1771 			j = place(j, loc1, loc2);
   1772 			continue;
   1773 		} else if (c == '\\') {
   1774 			c = rhsbuf[k++]&0377;
   1775 			if (c >='1' && c < nbra+'1') {
   1776 				j = place(j, braslist[c-'1'], braelist[c-'1']);
   1777 				continue;
   1778 			}
   1779 		}
   1780 		if (j >= LBSIZE)
   1781 			growlb("line too long");
   1782 		genbuf[j++] = c;
   1783 	}
   1784 	i = loc2 - linebuf;
   1785 	loc2 = j + linebuf;
   1786 #if defined (SUS) || defined (SU3) || defined (S42)
   1787 	if (loc1 == &linebuf[i]) {
   1788 		int	n;
   1789 		wchar_t	wc;
   1790 		if (mb_cur_max > 1 && (n = mbtowc(&wc, loc2, mb_cur_max)) > 0)
   1791 			loc2 += n;
   1792 		else
   1793 			loc2++;
   1794 	}
   1795 #endif	/* SUS || SU3 || S42 */
   1796 	while (genbuf[j++] = linebuf[i++])
   1797 		if (j >= LBSIZE)
   1798 			growlb("line too long");
   1799 	if (really) {
   1800 		lp = linebuf;
   1801 		sp = genbuf;
   1802 	} else {
   1803 	copy:	sp = linebuf;
   1804 		lp = genbuf;
   1805 	}
   1806 	while (*lp++ = *sp++)
   1807 		;
   1808 	return really;
   1809 }
   1810 
   1811 static int
   1812 place(register int j, register const char *l1, register const char *l2)
   1813 {
   1814 
   1815 	while (l1 < l2) {
   1816 		genbuf[j++] = *l1++;
   1817 		if (j >= LBSIZE)
   1818 			growlb("line too long");
   1819 	}
   1820 	return(j);
   1821 }
   1822 
   1823 static void
   1824 move(int cflag)
   1825 {
   1826 	register long *adt, *ad1, *ad2;
   1827 
   1828 	setdot();
   1829 	nonzero();
   1830 	if ((adt = address())==0)
   1831 		error("illegal move destination");
   1832 	newline();
   1833 	checkpoint();
   1834 	if (cflag) {
   1835 		long *ozero;
   1836 		intptr_t delta;
   1837 		ad1 = dol;
   1838 		ozero = zero;
   1839 		append(getcopy, ad1++);
   1840 		ad2 = dol;
   1841 		delta = zero - ozero;
   1842 		ad1 += delta;
   1843 		adt += delta;
   1844 	} else {
   1845 		ad2 = addr2;
   1846 		for (ad1 = addr1; ad1 <= ad2;)
   1847 			*ad1++ &= ~01;
   1848 		ad1 = addr1;
   1849 	}
   1850 	ad2++;
   1851 	if (adt<ad1) {
   1852 		dot = adt + (ad2-ad1);
   1853 		if ((++adt)==ad1)
   1854 			return;
   1855 		reverse(adt, ad1);
   1856 		reverse(ad1, ad2);
   1857 		reverse(adt, ad2);
   1858 	} else if (adt >= ad2) {
   1859 		dot = adt++;
   1860 		reverse(ad1, ad2);
   1861 		reverse(ad2, adt);
   1862 		reverse(ad1, adt);
   1863 	} else
   1864 		error("illegal move destination");
   1865 	fchange = 1;
   1866 }
   1867 
   1868 static void
   1869 reverse(register long *a1, register long *a2)
   1870 {
   1871 	register int t;
   1872 
   1873 	for (;;) {
   1874 		t = *--a2;
   1875 		if (a2 <= a1)
   1876 			return;
   1877 		*a2 = *a1;
   1878 		*a1++ = t;
   1879 	}
   1880 }
   1881 
   1882 static int
   1883 getcopy(void)
   1884 {
   1885 	if (addr1 > addr2)
   1886 		return(EOF);
   1887 	getline(*addr1++, 0);
   1888 	return(0);
   1889 }
   1890 
   1891 static int
   1892 execute(int gf, long *addr, int subst)
   1893 {
   1894 	register char *p1, *p2, c;
   1895 
   1896 	for (c=0; c<NBRA; c++) {
   1897 		braslist[c&0377] = 0;
   1898 		braelist[c&0377] = 0;
   1899 	}
   1900 	if (gf) {
   1901 		if (circf)
   1902 			return(0);
   1903 		p1 = linebuf;
   1904 		p2 = genbuf;
   1905 		while (*p1++ = *p2++)
   1906 			;
   1907 		locs = p1 = loc2;
   1908 	} else {
   1909 		if (addr==zero)
   1910 			return(0);
   1911 		p1 = getline(*addr, 1);
   1912 		locs = 0;
   1913 	}
   1914 	needsub = subst;
   1915 	return step(p1, expbuf);
   1916 }
   1917 
   1918 static void
   1919 cmplerr(int c)
   1920 {
   1921 	const char	*msg;
   1922 
   1923 #if !defined (SUS) && !defined (S42) && !defined (SU3)
   1924 	expbuf[0] = 0;
   1925 #endif
   1926 	switch (c) {
   1927 	case 11:
   1928 		msg = "Range endpoint too large";
   1929 		break;
   1930 	case 16:
   1931 		msg = "bad number";
   1932 		break;
   1933 	case 25:
   1934 		msg = "`\\digit' out of range";
   1935 		break;
   1936 	case 36:
   1937 		msg = "illegal or missing delimiter";
   1938 		break;
   1939 	case 41:
   1940 		msg = "no remembered search string";
   1941 		break;
   1942 	case 42:
   1943 		msg = "'\\( \\)' imbalance";
   1944 		break;
   1945 	case 43:
   1946 		msg = "Too many `\\(' s";
   1947 		break;
   1948 	case 44:
   1949 		msg = "more than 2 numbers given";
   1950 		break;
   1951 	case 45:
   1952 		msg = "'\\}' expected";
   1953 		break;
   1954 	case 46:
   1955 		msg = "first number exceeds second";
   1956 		break;
   1957 	case 49:
   1958 		msg = "'[ ]' imbalance";
   1959 		break;
   1960 	case 50:
   1961 		msg = "regular expression overflow";
   1962 		break;
   1963 	case 67:
   1964 		msg = "illegal byte sequence";
   1965 		break;
   1966 	default:
   1967 		msg = "regular expression error";
   1968 		break;
   1969 	}
   1970 	error(msg);
   1971 }
   1972 
   1973 static void
   1974 doprnt(long *bot, long *top)
   1975 {
   1976 	long	*a1;
   1977 
   1978 	a1 = bot;
   1979 	do {
   1980 		if (numbf ^ Nflag) {
   1981 			putd(a1-zero);
   1982 			putchr('\t');
   1983 		}
   1984 		nlputs(getline(*a1++, 0));
   1985 	} while (a1 <= top);
   1986 	pflag = 0;
   1987 	listf = 0;
   1988 	numbf = 0;
   1989 }
   1990 
   1991 static void
   1992 putd(long c)
   1993 {
   1994 	register int r;
   1995 
   1996 	r = c%10;
   1997 	c /= 10;
   1998 	if (c)
   1999 		putd(c);
   2000 	putchr(r + '0');
   2001 }
   2002 
   2003 static void
   2004 nlputs(register const char *sp)
   2005 {
   2006 	if (listf)
   2007 		list(sp);
   2008 	else if (tabstops)
   2009 		expand(sp);
   2010 	else
   2011 		puts(sp);
   2012 }
   2013 
   2014 static void
   2015 puts(register const char *sp)
   2016 {
   2017 	while (*sp) {
   2018 		if (*sp != '\n')
   2019 			putchr(*sp++ & 0377);
   2020 		else
   2021 			sp++, putchr('\0');
   2022 	}
   2023 	putchr('\n');
   2024 }
   2025 
   2026 static void
   2027 list(const char *lp)
   2028 {
   2029 	int col, n;
   2030 	wchar_t c;
   2031 
   2032 	col = numbf ^ Nflag ? 8 : 0;
   2033 	while (*lp) {
   2034 		if (mb_cur_max > 1 && *lp&0200)
   2035 			n = mbtowc(&c, lp, mb_cur_max);
   2036 		else {
   2037 			n = 1;
   2038 			c = *lp&0377;
   2039 		}
   2040 		if (col+1 >= 72) {
   2041 			col = 0;
   2042 			putchr('\\');
   2043 			putchr('\n');
   2044 		}
   2045 		if (n<0 ||
   2046 #if defined (SUS) || defined (S42) || defined (SU3)
   2047 				c == '\\' ||
   2048 #endif	/* SUS || S42 || SU3 */
   2049 				!(mb_cur_max>1 ? iswprint(c) : isprint(c))) {
   2050 			if (n<0)
   2051 				n = 1;
   2052 			while (n--)
   2053 				col += lstchr(*lp++&0377);
   2054 		} else if (mb_cur_max>1) {
   2055 			col += wcwidth(c);
   2056 			while (n--)
   2057 				putchr(*lp++&0377);
   2058 		} else {
   2059 			putchr(*lp++&0377);
   2060 			col++;
   2061 		}
   2062 	}
   2063 #if defined (SUS) || defined (S42) || defined (SU3)
   2064 	putchr('$');
   2065 #endif
   2066 	putchr('\n');
   2067 }
   2068 
   2069 static int
   2070 lstchr(int c)
   2071 {
   2072 	int	cad = 1, d;
   2073 
   2074 #if !defined (SUS) && !defined (S42) && !defined (SU3)
   2075 	if (c=='\t') {
   2076 		c = '>';
   2077 		goto esc;
   2078 	}
   2079 	if (c=='\b') {
   2080 		c = '<';
   2081 	esc:
   2082 		putchr('-');
   2083 		putchr('\b');
   2084 		putchr(c);
   2085 	} else if (c == '\n') {
   2086 		putchr('\\');
   2087 		putchr('0');
   2088 		putchr('0');
   2089 		putchr('0');
   2090 		cad = 4;
   2091 #else	/* !SUS, !S42, !SU3 */
   2092 	if (c == '\n')
   2093 		c = '\0';
   2094 	if (c == '\\') {
   2095 		putchr('\\');
   2096 		putchr('\\');
   2097 		cad = 2;
   2098 	} else if (c == '\a') {
   2099 		putchr('\\');
   2100 		putchr('a');
   2101 		cad = 2;
   2102 	} else if (c == '\b') {
   2103 		putchr('\\');
   2104 		putchr('b');
   2105 		cad = 2;
   2106 	} else if (c == '\f') {
   2107 		putchr('\\');
   2108 		putchr('f');
   2109 		cad = 2;
   2110 	} else if (c == '\r') {
   2111 		putchr('\\');
   2112 		putchr('r');
   2113 		cad = 2;
   2114 	} else if (c == '\t') {
   2115 		putchr('\\');
   2116 		putchr('t');
   2117 		cad = 2;
   2118 	} else if (c == '\v') {
   2119 		putchr('\\');
   2120 		putchr('v');
   2121 		cad = 2;
   2122 #endif	/* !SUS, !S42, !SU3 */
   2123 	} else {
   2124 		putchr('\\');
   2125 		putchr(((c&~077)>>6)+'0');
   2126 		c &= 077;
   2127 		d = c & 07;
   2128 		putchr(c > d ? ((c-d)>>3)+'0' : '0');
   2129 		putchr(d+'0');
   2130 		cad = 4;
   2131 	}
   2132 	return cad;
   2133 }
   2134 
   2135 static void
   2136 putstr(const char *s)
   2137 {
   2138 	while (*s)
   2139 		putchr(*s++);
   2140 }
   2141 
   2142 static char	line[70];
   2143 static char	*linp	= line;
   2144 
   2145 static void
   2146 putchr(int ac)
   2147 {
   2148 	register char *lp;
   2149 	register int c;
   2150 
   2151 	lp = linp;
   2152 	c = ac;
   2153 	*lp++ = c;
   2154 	if(c == '\n' || lp >= &line[64]) {
   2155 		linp = line;
   2156 		write(1, line, lp-line);
   2157 		return;
   2158 	}
   2159 	linp = lp;
   2160 }
   2161 
   2162 static void
   2163 checkpoint(void)
   2164 {
   2165 	long	*a1, *a2;
   2166 
   2167 	if (undzero && globp == NULL) {
   2168 		for (a1 = zero+1, a2 = undzero+1; a1 <= dol; a1++, a2++)
   2169 			*a2 = *a1;
   2170 		unddot = &undzero[dot-zero];
   2171 		unddol = &undzero[dol-zero];
   2172 		for (a1 = names, a2 = undnames; a1 < &names[26]; a1++, a2++)
   2173 			*a2 = *a1;
   2174 	}
   2175 }
   2176 
   2177 #define	swap(a, b)	(t = a, a = b, b = t)
   2178 
   2179 static void
   2180 undo(void)
   2181 {
   2182 	long	*t;
   2183 
   2184 	if (undzero == NULL)
   2185 		error("no undo information saved");
   2186 	swap(zero, undzero);
   2187 	swap(dot, unddot);
   2188 	swap(dol, unddol);
   2189 	swap(names, undnames);
   2190 }
   2191 
   2192 static int
   2193 maketf(int fd)
   2194 {
   2195 	char	*tmpdir;
   2196 
   2197 	if (fd == -1) {
   2198 		if ((tmpdir = getenv("TMPDIR")) == NULL ||
   2199 				(fd = creatf(tmpdir)) < 0)
   2200 			if ((fd = creatf("/var/tmp")) < 0 &&
   2201 					(fd = creatf("/tmp")) < 0)
   2202 				error("cannot create temporary file");
   2203 	} else
   2204 		ftruncate(fd, 0);	/* blkio() will seek to 0 anyway */
   2205 	return fd;
   2206 }
   2207 
   2208 static int
   2209 creatf(const char *tmpdir)
   2210 {
   2211 	if (strlen(tmpdir) >= sizeof tfname - 9)
   2212 		return -1;
   2213 	strcpy(tfname, tmpdir);
   2214 	strcat(tfname, "/eXXXXXX");
   2215 	return mkstemp(tfname);
   2216 }
   2217 
   2218 static int
   2219 sopen(const char *fn, int rdwr)
   2220 {
   2221 	int	pf[2], fd = -1;
   2222 
   2223 	if (fn[0] == '!') {
   2224 		fn++;
   2225 		if (pipe(pf) < 0)
   2226 			error("write or open on pipe failed");
   2227 		switch (pipid = fork()) {
   2228 		case 0:
   2229 			if (rdwr == READ)
   2230 				dup2(pf[1], 1);
   2231 			else
   2232 				dup2(pf[0], 0);
   2233 			close(pf[0]);
   2234 			close(pf[1]);
   2235 			sigset(SIGHUP, oldhup);
   2236 			sigset(SIGQUIT, oldquit);
   2237 			sigset(SIGPIPE, oldpipe);
   2238 			execl(SHELL, "sh", "-c", fn, NULL);
   2239 			_exit(0100);
   2240 		default:
   2241 			close(pf[rdwr == READ ? 1 : 0]);
   2242 			fd = pf[rdwr == READ ? 0 : 1];
   2243 			break;
   2244 		case -1:
   2245 			error("fork failed - try again");
   2246 		}
   2247 	} else if (rdwr == READ)
   2248 		fd = open(fn, O_RDONLY);
   2249 	else if (rdwr == EXIST)
   2250 		fd = open(fn, O_WRONLY);
   2251 	else /*if (rdwr == WRITE)*/
   2252 		fd = creat(fn, 0666);
   2253 	if (fd >= 0 && rdwr == READ)
   2254 		readop = 1;
   2255 	if (fd >= 0)
   2256 		fstat(fd, &fstbuf);
   2257 	return fd;
   2258 }
   2259 
   2260 static void
   2261 sclose(int fd)
   2262 {
   2263 	int	status;
   2264 
   2265 	close(fd);
   2266 	if (pipid >= 0) {
   2267 		while (wait(&status) != pipid);
   2268 		pipid = -1;
   2269 	}
   2270 	readop = 0;
   2271 }
   2272 
   2273 static void
   2274 fspec(const char *lp)
   2275 {
   2276 	struct termios	ts;
   2277 	const char	*cp;
   2278 
   2279 	freetabs();
   2280 	maxlength = 0;
   2281 	if (tcgetattr(1, &ts) < 0
   2282 #ifdef	TAB3
   2283 			|| (ts.c_oflag&TAB3) == 0
   2284 #endif
   2285 			)
   2286 		return;
   2287 	while (lp[0]) {
   2288 		if (lp[0] == '<' && lp[1] == ':')
   2289 			break;
   2290 		lp++;
   2291 	}
   2292 	if (lp[0]) {
   2293 		lp += 2;
   2294 		while ((cp = ftok(&lp)) != NULL) {
   2295 			switch (*cp) {
   2296 			case 't':
   2297 				freetabs();
   2298 				if ((tabstops = tabstring(&cp[1])) == NULL)
   2299 					goto err;
   2300 				break;
   2301 			case 's':
   2302 				maxlength = atoi(&cp[1]);
   2303 				break;
   2304 			case 'm':
   2305 			case 'd':
   2306 			case 'e':
   2307 				break;
   2308 			case ':':
   2309 				if (cp[1] == '>') {
   2310 					if (tabstops == NULL)
   2311 						if ((tabstops = tabstring("0"))
   2312 								== NULL)
   2313 							goto err;
   2314 					return;
   2315 				}
   2316 				/*FALLTHRU*/
   2317 			default:
   2318 			err:	freetabs();
   2319 				maxlength = 0;
   2320 				errput("PWB spec problem", NULL);
   2321 				return;
   2322 			}
   2323 		}
   2324 	}
   2325 }
   2326 
   2327 static const char *
   2328 ftok(const char **lp)
   2329 {
   2330 	const char	*cp;
   2331 
   2332 	while (**lp && **lp != ':' && (**lp == ' ' || **lp == '\t'))
   2333 		(*lp)++;
   2334 	cp = *lp;
   2335 	while (**lp && **lp != ':' && **lp != ' ' && **lp != '\t')
   2336 		(*lp)++;
   2337 	return cp;
   2338 }
   2339 
   2340 static struct tabulator *
   2341 repetitive(int repetition)
   2342 {
   2343 	struct tabulator	*tp, *tabspec;
   2344 	int	col, i;
   2345 
   2346 	if ((tp = tabspec = calloc(1, sizeof *tp)) == NULL)
   2347 		return NULL;
   2348 	tp->t_rep = repetition;
   2349 	if (repetition > 0) {
   2350 		 for (col = 1+repetition, i = 0; i < 22; col += repetition) {
   2351 			if ((tp->t_nxt = calloc(1, sizeof *tp)) == NULL)
   2352 				return NULL;
   2353 			tp = tp->t_nxt;
   2354 			tp->t_tab = col;
   2355 		}
   2356 	}
   2357 	return tabspec;
   2358 }
   2359 
   2360 #define	blank(c)	((c) == ' ' || (c) == '\t')
   2361 
   2362 static struct tabulator *
   2363 tablist(const char *s)
   2364 {
   2365 	struct tabulator	*tp, *tabspec;
   2366 	char	*x;
   2367 	int	prev = 0, val;
   2368 
   2369 	if ((tp = tabspec = calloc(1, sizeof *tp)) == NULL)
   2370 		return NULL;
   2371 	for (;;) {
   2372 		while (*s == ',')
   2373 			s++;
   2374 		if (*s == '\0' || blank(*s) || *s == ':')
   2375 			break;
   2376 		val = strtol(s, &x, 10);
   2377 		if (*s == '+')
   2378 			val += prev;
   2379 		prev = val;
   2380 		if (*s == '-' || (*x != ',' && !blank(*x) && *x != ':' &&
   2381 					*x != '\0'))
   2382 			return NULL;
   2383 		s = x;
   2384 		if ((tp->t_nxt = calloc(1, sizeof *tp)) == NULL)
   2385 			return NULL;
   2386 		tp = tp->t_nxt;
   2387 		tp->t_tab = val;
   2388 	}
   2389 	return tabspec;
   2390 }
   2391 
   2392 static struct tabulator *
   2393 tabstring(const char *s)
   2394 {
   2395 	const struct {
   2396 		const char	*c_nam;
   2397 		const char	*c_str;
   2398 	} canned[] = {
   2399 		{ "a",	"1,10,16,36,72" },
   2400 		{ "a2",	"1,10,16,40,72" },
   2401 		{ "c",	"1,8,12,16,20,55" },
   2402 		{ "c2",	"1,6,10,14,49" },
   2403 		{ "c3",	"1,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62,67" },
   2404 		{ "f",	"1,7,11,15,19,23" },
   2405 		{ "p",	"1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61" },
   2406 		{ "s",	"1,10,55" },
   2407 		{ "u",	"1,12,20,44" },
   2408 		{ 0,	0 }
   2409 	};
   2410 
   2411 	int	i, j;
   2412 
   2413 	if (s[0] == '-') {
   2414 		if (s[1] >= '0' && s[1] <= '9' && ((i = atoi(&s[1])) != 0))
   2415 			return repetitive(i);
   2416 		for (i = 0; canned[i].c_nam; i++) {
   2417 			for (j = 0; canned[i].c_nam[j]; j++)
   2418 				if (s[j+1] != canned[i].c_nam[j])
   2419 					break;
   2420 			if ((s[j+1]=='\0' || s[j+1]==':' || blank(s[j+1])) &&
   2421 					canned[i].c_nam[j] == '\0')
   2422 				return tablist(canned[i].c_str);
   2423 		}
   2424 		return NULL;
   2425 	} else
   2426 		return tablist(s);
   2427 }
   2428 
   2429 static void
   2430 freetabs(void)
   2431 {
   2432 	struct tabulator	*tp;
   2433 
   2434 	tp = tabstops;
   2435 	while (tp) {
   2436 		tabstops = tp->t_nxt;
   2437 		free(tp);
   2438 		tp = tabstops;
   2439 	}
   2440 }
   2441 
   2442 static void
   2443 expand(const char *s)
   2444 {
   2445 	struct tabulator	*tp = tabstops;
   2446 	int	col = 0, n = 1, m, tabcnt = 0, nspc;
   2447 	wchar_t	wc;
   2448 
   2449 	while (*s) {
   2450 		nspc = 0;
   2451 		switch (*s) {
   2452 		case '\n':
   2453 			putchr('\0');
   2454 			s++;
   2455 			continue;
   2456 		case '\t':
   2457 			if (tp) {
   2458 				if (tp->t_rep) {
   2459 					if (col % tp->t_rep == 0) {
   2460 						nspc++;
   2461 						col++;
   2462 					}
   2463 					while (col % tp->t_rep) {
   2464 						nspc++;
   2465 						col++;
   2466 					}
   2467 					break;
   2468 				}
   2469 				while (tp && (col>tp->t_tab || tp->t_tab == 0))
   2470 					tp = tp->t_nxt;
   2471 				if (tp && col == tp->t_tab) {
   2472 					nspc++;
   2473 					col++;
   2474 					tp = tp->t_nxt;
   2475 				}
   2476 				if (tp) {
   2477 					while (col < tp->t_tab) {
   2478 						nspc++;
   2479 						col++;
   2480 					}
   2481 					tp = tp->t_nxt;
   2482 					break;
   2483 				}
   2484 			}
   2485 			tabcnt = 1;
   2486 			nspc++;
   2487 			break;
   2488 		default:
   2489 			if (mb_cur_max>1 && (n=mbtowc(&wc, s, mb_cur_max))>0) {
   2490 				if ((m = wcwidth(wc)) > 0)
   2491 					col += m;
   2492 			} else {
   2493 				col++;
   2494 				n = 1;
   2495 			}
   2496 		}
   2497 		if (maxlength && col > maxlength) {
   2498 			putstr("\ntoo long");
   2499 			break;
   2500 		}
   2501 		if (nspc) {
   2502 			while (nspc--)
   2503 				putchr(' ');
   2504 			s++;
   2505 		} else
   2506 			while (n--)
   2507 				putchr(*s++);
   2508 	}
   2509 	if (tabcnt)
   2510 		putstr("\ntab count");
   2511 	putchr('\n');
   2512 }
   2513 
   2514 static wint_t
   2515 GETWC(char *mb)
   2516 {
   2517 	int	c, n;
   2518 
   2519 	n = 1;
   2520 	mb[0] = c = GETC();
   2521 	mb[1] = '\0';
   2522 	if (mb_cur_max > 1 && c&0200 && c != EOF) {
   2523 		int	m;
   2524 		wchar_t	wc;
   2525 
   2526 		while ((m = mbtowc(&wc, mb, mb_cur_max)) < 0 && n<mb_cur_max) {
   2527 			mb[n++] = c = GETC();
   2528 			mb[n] = '\0';
   2529 			if (c == '\n' || c == EOF)
   2530 				break;
   2531 		}
   2532 		if (m != n)
   2533 			ERROR(67);
   2534 		return wc;
   2535 	} else
   2536 		return c;
   2537 }
   2538 
   2539 static void
   2540 growlb(const char *msg)
   2541 {
   2542 	char	*olb = linebuf;
   2543 	int	i;
   2544 
   2545 	LBSIZE += 512;
   2546 	if ((linebuf = realloc(linebuf, LBSIZE)) == NULL ||
   2547 			(genbuf = realloc(genbuf, LBSIZE)) == NULL)
   2548 		error(msg);
   2549 	if (linebuf != olb) {
   2550 		loc1 += linebuf - olb;
   2551 		loc2 += linebuf - olb;
   2552 		for (i = 0; i < NBRA; i++) {
   2553 			if (braslist[i])
   2554 				braslist[i] += linebuf - olb;
   2555 			if (braelist[i])
   2556 				braelist[i] += linebuf - olb;
   2557 		}
   2558 	}
   2559 }
   2560 
   2561 static void
   2562 growrhs(const char *msg)
   2563 {
   2564 	RHSIZE += 256;
   2565 	if ((rhsbuf = realloc(rhsbuf, RHSIZE)) == NULL)
   2566 		error(msg);
   2567 }
   2568 
   2569 static void
   2570 growfn(const char *msg)
   2571 {
   2572 	FNSIZE += 64;
   2573 	if ((savedfile = realloc(savedfile, FNSIZE)) == NULL ||
   2574 			(file = realloc(file, FNSIZE)) == NULL)
   2575 		error(msg);
   2576 	if (FNSIZE == 64)
   2577 		file[0] = savedfile[0] = 0;
   2578 }
   2579 
   2580 #if defined (SUS) || defined (S42) || defined (SU3)
   2581 union	ptrstore {
   2582 	void	*vp;
   2583 	char	bp[sizeof (void *)];
   2584 };
   2585 
   2586 static void *
   2587 fetchptr(const char *bp)
   2588 {
   2589 	union ptrstore	u;
   2590 	int	i;
   2591 
   2592 	for (i = 0; i < sizeof (void *); i++)
   2593 		u.bp[i] = bp[i];
   2594 	return u.vp;
   2595 }
   2596 
   2597 static void
   2598 storeptr(void *vp, char *bp)
   2599 {
   2600 	union ptrstore	u;
   2601 	int	i;
   2602 
   2603 	u.vp = vp;
   2604 	for (i = 0; i < sizeof (void *); i++)
   2605 		bp[i] = u.bp[i];
   2606 }
   2607 
   2608 #define	add(c)	((i>=LBSIZE ? (growlb("regular expression overflow"),0) : 0), \
   2609 		genbuf[i++] = (c))
   2610 
   2611 #define	copy(s)	{ \
   2612 	int	m; \
   2613 	for (m = 0; m==0 || s[m]; m++) \
   2614 		add(s[m]); \
   2615 }
   2616 
   2617 static char *
   2618 compile(char *unused, char *ep, const char *endbuf, int seof)
   2619 {
   2620 	INIT
   2621 	int	c, d, i;
   2622 	regex_t	*rp;
   2623 	char	*op;
   2624 	char	mb[MB_LEN_MAX+1];
   2625 
   2626 	op = ep;
   2627 	ep += 2;
   2628 	if ((rp = fetchptr(ep)) == NULL) {
   2629 		if ((rp = calloc(1, sizeof *rp)) == NULL)
   2630 			ERROR(50);
   2631 		storeptr(rp, ep);
   2632 	}
   2633 	ep += sizeof (void *);
   2634 	i = 0;
   2635 	nbra = 0;
   2636 	do {
   2637 		if ((c = GETWC(mb)) == seof)
   2638 			add('\0');
   2639 		else if (c == '\\') {
   2640 			copy(mb);
   2641 			c = GETWC(mb);
   2642 			if (c == '(')
   2643 				nbra++;
   2644 			goto normchar;
   2645 		} else if (c == '[') {
   2646 			add(c);
   2647 			d = EOF;
   2648 			do {
   2649 				c = GETWC(mb);
   2650 				if (c == EOF || c == '\n')
   2651 					ERROR(49);
   2652 				copy(mb);
   2653 				if (d=='[' && (c==':' || c=='.' || c=='=')) {
   2654 					d = c;
   2655 					do {
   2656 						c = GETWC(mb);
   2657 						if (c == EOF || c == '\n')
   2658 							ERROR(49);
   2659 						copy(mb);
   2660 					} while (c != d || PEEKC() != ']');
   2661 					c = GETWC(mb);
   2662 					copy(mb);
   2663 					c = EOF;
   2664 				}
   2665 				d = c;
   2666 			} while (c != ']');
   2667 		} else {
   2668 			if (c == EOF || c == '\n') {
   2669 				if (c == '\n')
   2670 					UNGETC(c);
   2671 				mb[0] = c = '\0';
   2672 			}
   2673 			if (c == '\0')
   2674 				nodelim = 1;
   2675 	normchar:	copy(mb);
   2676 		}
   2677 	} while (genbuf[i-1] != '\0');
   2678 	if (genbuf[0]) {
   2679 		int	reflags = 0;
   2680 
   2681 #ifdef	REG_ANGLES
   2682 		reflags |= REG_ANGLES;
   2683 #endif
   2684 #if defined (SU3) && defined (REG_AVOIDNULL)
   2685 		reflags |= REG_AVOIDNULL;
   2686 #endif
   2687 		if (op[0])
   2688 			regfree(rp);
   2689 		op[0] = 0;
   2690 		switch (regcomp(rp, genbuf, reflags)) {
   2691 		case 0:
   2692 			break;
   2693 		case REG_ESUBREG:
   2694 			ERROR(25);
   2695 			/*NOTREACHED*/
   2696 		case REG_EBRACK:
   2697 			ERROR(49);
   2698 			/*NOTREACHED*/
   2699 		case REG_EPAREN:
   2700 			ERROR(42);
   2701 			/*NOTREACHED*/
   2702 		case REG_BADBR:
   2703 		case REG_EBRACE:
   2704 			ERROR(45);
   2705 			/*NOTREACHED*/
   2706 		case REG_ERANGE:
   2707 			ERROR(11);
   2708 			/*NOTREACHED*/
   2709 		case REG_ESPACE:
   2710 			ERROR(50);
   2711 			/*NOTREACHED*/
   2712 		default:
   2713 			ERROR(-1);
   2714 		}
   2715 		op[0] = 1;
   2716 		circf = op[1] = genbuf[0] == '^';
   2717 	} else if (op[0]) {
   2718 		circf = op[1];
   2719 	} else
   2720 		ERROR(41);
   2721 	return ep + sizeof (void *);
   2722 }
   2723 
   2724 static int
   2725 step(const char *lp, const char *ep)
   2726 {
   2727 	regex_t	*rp;
   2728 	regmatch_t	bralist[NBRA+1];
   2729 	int	eflag = 0;
   2730 	int	res;
   2731 	int	i;
   2732 
   2733 	rp = fetchptr(&ep[2]);
   2734 	if (ep[0] == 0)
   2735 		return 0;
   2736 	if (locs)
   2737 		eflag |= REG_NOTBOL;
   2738 	if ((res = regexec(rp, lp, needsub? NBRA+1 : 0, bralist, eflag)) == 0 &&
   2739 			needsub) {
   2740 		loc1 = (char *)lp + bralist[0].rm_so;
   2741 		loc2 = (char *)lp + bralist[0].rm_eo;
   2742 		for (i = 1; i <= NBRA; i++) {
   2743 			if (bralist[i].rm_so != -1) {
   2744 				braslist[i-1] = (char *)lp + bralist[i].rm_so;
   2745 				braelist[i-1] = (char *)lp + bralist[i].rm_eo;
   2746 			} else
   2747 				braslist[i-1] = braelist[i-1] = NULL;
   2748 		}
   2749 	}
   2750 	return res == 0;
   2751 }
   2752 #endif	/* SUS || S42 || SU3 */
   2753 
   2754 static void
   2755 help(void)
   2756 {
   2757 	const char	*desc[] = {
   2758 		"(.)a            append up to .",
   2759 		"(.)b[n]         browse n lines",
   2760 		"(.,.)c          change up to .",
   2761 		"(.,.)d          delete lines",
   2762 		"e [file]        edit file",
   2763 		"E [file]        force edit",
   2764 		"f [file]        print or set file",
   2765 		"(1,$)g/RE/cmd   global cmd",
   2766 		"(1,$)G/RE/      interactive global",
   2767 		"h               print last error",
   2768 		"H               toggle error messages",
   2769 		"help            print this screen",
   2770 		"(.)i            insert up to .",
   2771 		"(.,.+1)j        join lines",
   2772 		"(.)kx           mark line with x",
   2773 		"(.,.)l          list lines",
   2774 		"(.,.)ma         move lines to a",
   2775 		"(.,.)n          number lines",
   2776 		"N               revert n and p",
   2777 		"(.)o[n]         show n lines of context",
   2778 		"(.,.)p          print lines",
   2779 		"P               toggle prompt",
   2780 		"q               quit",
   2781 		"Q               force quit",
   2782 		"($)r            read file",
   2783 		"(.,.)s/RE/repl/ search and replace",
   2784 		"(.,.)s/RE/rp/g  replace all occurrences",
   2785 		"(.,.)s/RE/rp/n  replace n-th occurrence",
   2786 		"(.,.)ta         transfer lines to a",
   2787 		"u               undo last change",
   2788 		"(1,$)v/RE/cmd   reverse global",
   2789 		"(1,$)V/RE/      reverse i/a global",
   2790 		"(1,$)w [file]   write file",
   2791 		"(1,$)W [file]   append to file",
   2792 		"z               write buffer and quit",
   2793 		"($)=            print line number",
   2794 		"!command        execute shell command",
   2795 		"(.+1)<newline>  print one line",
   2796 		"/RE             find RE forwards",
   2797 		"?RE             find RE backwards",
   2798 		"1               first line",
   2799 		".               current line",
   2800 		"$               last line",
   2801 		",               1,$",
   2802 		";               .,$",
   2803 		NULL
   2804 	};
   2805 	char	line[100];
   2806 	int	c, half, i, k;
   2807 
   2808 	half = (sizeof desc / sizeof *desc) / 2;
   2809 	for (i = 0; i < half && desc[i]; i++) {
   2810 		c = 0;
   2811 		for (k = 0; desc[i][k]; k++)
   2812 			line[c++] = desc[i][k];
   2813 		if (desc[i+half]) {
   2814 			while (c < 40)
   2815 				line[c++] = ' ';
   2816 			for (k = 0; desc[i+half][k]; k++)
   2817 				line[c++] = desc[i+half][k];
   2818 		}
   2819 		line[c] = 0;
   2820 		puts(line);
   2821 	}
   2822 }