view usr/src/cmd/ed/ed.c @ 4:1a15d5aaf794

synchronized with onnv_86 (6202) in onnv-gate
author Koji Uno <koji.uno@sun.com>
date Mon, 31 Aug 2009 14:38:03 +0900
parents c9caec207d52
children
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Editor
 */

#include	<crypt.h>
#include	<libgen.h>
#include	<wait.h>
#include	<string.h>
#include	<sys/types.h>
#include	<locale.h>
#include	<regexpr.h>
#include	<regex.h>
#include	<errno.h>

static const 	char	*msgtab[] =
{
	"write or open on pipe failed",			/*  0 */
	"warning: expecting `w'",			/*  1 */
	"mark not lower case ascii",			/*  2 */
	"Cannot open input file",			/*  3 */
	"PWB spec problem",				/*  4 */
	"nothing to undo",				/*  5 */
	"restricted shell",				/*  6 */
	"cannot create output file",			/*  7 */
	"filesystem out of space!",			/*  8 */
	"cannot open file",				/*  9 */
	"cannot link",					/* 10 */
	"Range endpoint too large",			/* 11 */
	"unknown command",				/* 12 */
	"search string not found",			/* 13 */
	"-",						/* 14 */
	"line out of range",				/* 15 */
	"bad number",					/* 16 */
	"bad range",					/* 17 */
	"Illegal address count",			/* 18 */
	"incomplete global expression",			/* 19 */
	"illegal suffix",				/* 20 */
	"illegal or missing filename",			/* 21 */
	"no space after command",			/* 22 */
	"fork failed - try again",			/* 23 */
	"maximum of 64 characters in file names",	/* 24 */
	"`\\digit' out of range",			/* 25 */
	"interrupt",					/* 26 */
	"line too long",				/* 27 */
	"illegal character in input file",		/* 28 */
	"write error",					/* 29 */
	"out of memory for append",			/* 30 */
	"temp file too big",				/* 31 */
	"I/O error on temp file",			/* 32 */
	"multiple globals not allowed",			/* 33 */
	"global too long",				/* 34 */
	"no match",					/* 35 */
	"illegal or missing delimiter",			/* 36 */
	"-",						/* 37 */
	"replacement string too long",			/* 38 */
	"illegal move destination",			/* 39 */
	"-",						/* 40 */
	"no remembered search string",			/* 41 */
	"'\\( \\)' imbalance",				/* 42 */
	"Too many `\\(' s",				/* 43 */
	"more than 2 numbers given",			/* 44 */
	"'\\}' expected",				/* 45 */
	"first number exceeds second",			/* 46 */
	"incomplete substitute",			/* 47 */
	"newline unexpected",				/* 48 */
	"'[ ]' imbalance",				/* 49 */
	"regular expression overflow",			/* 50 */
	"regular expression error",			/* 51 */
	"command expected",				/* 52 */
	"a, i, or c not allowed in G",			/* 53 */
	"end of line expected",				/* 54 */
	"no remembered replacement string",		/* 55 */
	"no remembered command",			/* 56 */
	"illegal redirection",				/* 57 */
	"possible concurrent update",			/* 58 */
	"-",						/* 59 */
	"the x command has become X (upper case)",	/* 60 */
	"Warning: 'w' may destroy input file "
	"(due to `illegal char' read earlier)",
							/* 61 */
	"Caution: 'q' may lose data in buffer;"
	" 'w' may destroy input file",
							/* 62 */
	"Encryption of string failed",			/* 63 */
	"Encryption facility not available",		/* 64 */
	"Cannot encrypt temporary file",		/* 65 */
	"Enter key:",					/* 66 */
	"Illegal byte sequence",			/* 67 */
	"File does not exist",				/* 68 */
	"tempnam failed",				/* 69 */
	"Cannot open temporary file",			/* 70 */
	0
};

#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <unistd.h>
#include <termio.h>
#include <ctype.h>
#include <setjmp.h>
#include <fcntl.h>
#include <wchar.h>	/* I18N */
#include <wctype.h>	/* I18N */
#include <widec.h>	/* I18N */

#define	FTYPE(A)	(A.st_mode)
#define	FMODE(A)	(A.st_mode)
#define	IDENTICAL(A, B)	(A.st_dev == B.st_dev && A.st_ino == B.st_ino)
#define	ISBLK(A)	((A.st_mode & S_IFMT) == S_IFBLK)
#define	ISCHR(A)	((A.st_mode & S_IFMT) == S_IFCHR)
#define	ISDIR(A)	((A.st_mode & S_IFMT) == S_IFDIR)
#define	ISFIFO(A)	((A.st_mode & S_IFMT) == S_IFIFO)
#define	ISREG(A)	((A.st_mode & S_IFMT) == S_IFREG)

#define	PUTM()	if (xcode >= 0) puts(gettext(msgtab[xcode]))
#define	UNGETC(c)	(peekc = c)
#define	FNSIZE	PATH_MAX
#define	LBSIZE	LINE_MAX

/* size of substitution replacement pattern buffer */
#define	RHSIZE	(LINE_MAX*2)

#define	KSIZE	8

#define	READ	0
#define	WRITE	1

extern  char	*optarg;	/* Value of argument */
extern  int	optind;		/* Indicator of argument */
extern	int __xpg4;	/* defined in xpg4.c; 0 if not xpg4-compiled program */

struct  Fspec   {
	char    Ftabs[22];
	char    Fdel;
	unsigned char   Flim;
	char    Fmov;
	char    Ffill;
};
static struct  Fspec   fss;

static char	*fsp;
static int	fsprtn;
static char	line[70];
static char	*linp = line;
static int	sig;
static int	Xqt = 0;
static int	lastc;
static char	savedfile[FNSIZE];
static char	file[FNSIZE];
static char	funny[FNSIZE];
static int	funlink = 0;
static char	linebuf[LBSIZE];
static char	*tstring = linebuf;

static char *expbuf;

static char	rhsbuf[RHSIZE];
struct	lin	{
	long cur;
	long sav;
};
typedef struct lin *LINE;
static LINE	zero;
static LINE	dot;
static LINE	dol;
static LINE	endcore;
static LINE	fendcore;
static LINE	addr1;
static LINE	addr2;
static LINE	savdol, savdot;
static int	globflg;
static int	initflg;
static char	genbuf[LBSIZE];
static long	count;
static int	numpass;	/* Number of passes thru dosub(). */
static int	gsubf;		/* Occurrence value. LBSIZE-1=all. */
static int	ocerr1;	/* Allows lines NOT changed by dosub() to NOT be put */
			/* out. Retains last line changed as current line. */
static int	ocerr2;	/* Flags if ANY line changed by substitute(). 0=nc. */
static char	*nextip;
static char	*linebp;
static int	ninbuf;
static int	peekc;
static int	io;
static void	(*oldhup)(), (*oldintr)();
static void	(*oldquit)(), (*oldpipe)();
static void	quit(int);
static int	vflag = 1;
static int	xflag;
static int	xtflag;
static int	kflag;
static int	crflag;
		/* Flag for determining if file being read is encrypted */
static int	hflag;
static int	xcode = -1;
static char	crbuf[LBSIZE];
static	int	perm[2];
static int	tperm[2];
static int	permflag;
static int	tpermflag;
static int	col;
static char	*globp;
static int	tfile = -1;
static int	tline;
static char	*tfname;
extern char	*locs;
static char	ibuff[LBSIZE];
static int	iblock = -1;
static char	obuff[LBSIZE];
static int	oblock = -1;
static int	ichanged;
static int	nleft;
static long	savnames[26], names[26];
static int	anymarks;
static long	subnewa;
static int	fchange;
static int	nline;
static int	fflg, shflg;
static char	prompt[16] = "*";
static int	rflg;
static int	readflg;
static int 	eflg;
static int 	qflg = 0;
static int 	ncflg;
static int 	listn;
static int 	listf;
static int 	pflag;
static int 	flag28 = 0; /* Prevents write after a partial read */
static int 	save28 = 0; /* Flag whether buffer empty at start of read */
static long 	savtime;
static char	*name = "SHELL";
static char	*rshell = "/usr/lib/rsh";
static	char	*shpath; /* pointer to correct shell for execution */
			/* of execlp() */
static char	*val;
static char	*home;
static int	nodelim;

int	makekey(int *);
int	_mbftowc(char *, wchar_t *, int (*)(), int *);
static int	error(int code);
static void	tlist(struct Fspec *);
static void	tstd(struct Fspec *);
static void	gdelete(void);
static void	delete(void);
static void	exfile(void);
static void	filename(int comm);
static void	newline(void);
static int	gettty(void);
static void	commands(void);
static void	undo(void);
static void	save(void);
static void	strcopy(char *source, char *dest);
static int	strequal(char **scan1, char *str);
static int	stdtab(char *, char *);
static int	lenchk(char *, struct Fspec *);
static void	clear(struct Fspec *);
static int	expnd(char *, char *, int *, struct Fspec *);
static void	tincr(int, struct Fspec *);
static void	targ(struct Fspec *);
static int	numb(void);
static int	fspec(char *, struct Fspec *, int);
static void	red(char *);
static void	newtime(void);
static void	chktime(void);
static void	getime(void);
static void	mkfunny(void);
static int	eopen(char *, int);
static void	eclose(int f);
static void	globaln(int);
static char	*getkey(const char *);
static int	execute(int, LINE);
static void	error1(int);
static int	getcopy(void);
static void	move(int);
static void	dosub(void);
static int	getsub(void);
static int	compsub(void);
static void	substitute(int);
static void	join(void);
static void	global(int);
static void	init(void);
static void	rdelete(LINE, LINE);
static void	append(int (*)(void), LINE);
static int	getfile(void);
static void	putfile(void);
static void	onpipe(int);
static void	onhup(int);
static void	onintr(int);
static void	setdot(void);
static void	setall(void);
static void	setnoaddr(void);
static void	nonzero(void);
static void	setzeroasone(void);
static long	putline(void);
static LINE	address(void);
static char	*getline(long);
static char	*getblock(long, long);
static char	*place(char *, char *, char *);
static void	comple(wchar_t);
static void	putchr(unsigned char);
static void	putwchr(wchar_t);
static int	getchr(void);
static void	unixcom(void);
static void	blkio(int, char *, ssize_t (*)());
static void	reverse(LINE, LINE);
static void	putd();
static wchar_t	get_wchr(void);

static struct stat	Fl, Tf;
#ifndef RESEARCH
static struct statvfs	U;
static int	Short = 0;
static mode_t	oldmask; /* No umask while writing */
#endif
static jmp_buf	savej;

#ifdef	NULLS
int	nulls;	/* Null count */
#endif
static long	ccount;

static int	errcnt = 0;


static void
onpipe(int sig)
{
	(int)error(0);
}

int
main(int argc, char **argv)
{
	char *p1, *p2;
	int c;

	(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
#define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
#endif
	(void) textdomain(TEXT_DOMAIN);

	oldquit = signal(SIGQUIT, SIG_IGN);
	oldhup = signal(SIGHUP, SIG_IGN);
	oldintr = signal(SIGINT, SIG_IGN);
	oldpipe = signal(SIGPIPE, onpipe);
	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
		signal(SIGTERM, quit);
	p1 = *argv;
	while (*p1++);
	while (--p1 >= *argv)
		if (*p1 == '/')
			break;
	*argv = p1 + 1;
	/* if SHELL set in environment and is /usr/lib/rsh, set rflg */
	if ((val = getenv(name)) != NULL)
		if (strcmp(val, rshell) == 0)
			rflg++;
	if (**argv == 'r')
		rflg++;
	home = getenv("HOME");
	while (1) {
		while ((c = getopt(argc, argv, "sp:qxC")) != EOF) {
			switch (c) {

			case 's':
				vflag = 0;
				break;

			case 'p':
				strncpy(prompt, optarg, sizeof (prompt)-1);
				shflg = 1;
				break;

			case 'q':
				signal(SIGQUIT, SIG_DFL);
				vflag = 1;
				break;

			case 'x':
				crflag = -1;
				xflag = 1;
				break;

			case 'C':
				crflag = 1;
				xflag = 1;
				break;

			case '?':
				(void) fprintf(stderr, gettext(
		"Usage:   ed [- | -s] [-p string] [-x] [-C] [file]\n"
		"	  red [- | -s] [-p string] [-x] [-C] [file]\n"));
				exit(2);
			}
		}
		if (argv[optind] && strcmp(argv[optind], "-") == 0 &&
			strcmp(argv[optind-1], "--") != 0) {
			vflag = 0;
			optind++;
			continue;
		}
		break;
	}
	argc = argc - optind;
	argv = &argv[optind];

	if (xflag) {
		if (permflag)
			crypt_close(perm);
		permflag = 1;
		if ((kflag = run_setkey(&perm[0], getkey(msgtab[66])))
			== -1) {
			puts(gettext(msgtab[64]));
			xflag = 0;
			kflag = 0;
		}
		if (kflag == 0)
			crflag = 0;
	}

	if (argc > 0) {
		p1 = *argv;
		if (strlen(p1) >= (size_t)FNSIZE) {
			puts(gettext("file name too long"));
			if (kflag)
				crypt_close(perm);
			exit(2);
		}
		p2 = savedfile;
		while (*p2++ = *p1++);
		globp = "e";
		fflg++;
	} else 	/* editing with no file so set savtime to 0 */
		savtime = 0;
	eflg++;
	if ((tfname = tempnam("", "ea")) == NULL) {
		puts(gettext(msgtab[69]));
		exit(2);
	}

	fendcore = (LINE)sbrk(0);
	init();
	if (oldintr != SIG_IGN)
		signal(SIGINT, onintr);
	if (oldhup != SIG_IGN)
		signal(SIGHUP, onhup);
	setjmp(savej);
	commands();
	quit(sig);
	return (0);
}

static void
commands(void)
{
	LINE a1;
	int c;
	char *p1, *p2;
	int fsave, m, n;

	for (;;) {
	nodelim = 0;
	if (pflag) {
		pflag = 0;
		addr1 = addr2 = dot;
		goto print;
	}
	if (shflg && globp == 0)
		write(1, gettext(prompt), strlen(gettext(prompt)));
	addr1 = 0;
	addr2 = 0;
	if ((c = getchr()) == ',') {
		addr1 = zero + 1;
		addr2 = dol;
#ifdef XPG6
	/* XPG4 - it was an error if the second address was */
	/* input and the first address was ommitted	*/
	/* Parse second address	*/
		if ((a1 = address()) != 0) {
			addr2 = a1;
		}
#endif
		c = getchr();
		goto swch;
	} else if (c == ';') {
		addr1 = dot;
		addr2 = dol;
#ifdef XPG6
	/* XPG4 - it was an error if the second address was */
	/* input and the first address was ommitted	*/
	/* Parse second address	*/
		if ((a1 = address()) != 0) {
			addr2 = a1;
		}
#endif
		c = getchr();
		goto swch;
	} else
		peekc = c;
	do {
		addr1 = addr2;
		if ((a1 = address()) == 0) {
			c = getchr();
			break;
		}
		addr2 = a1;
		if ((c = getchr()) == ';') {
			c = ',';
			dot = a1;
		}
	} while (c == ',');
	if (addr1 == 0)
		addr1 = addr2;
swch:
	switch (c) {

	case 'a':
		setdot();
		newline();
		if (!globflg) save();
		append(gettty, addr2);
		continue;

	case 'c':
#ifdef XPG6
		setzeroasone();
#endif
		delete();
		append(gettty, addr1-1);

		/* XPG4 - If no new lines are inserted, then the current */
		/* line becomes the line after the lines deleted. */

		if (((linebuf[0] != '.') || (dot == (addr1-1))) &&
			(addr2 <= dol))
			dot = addr1;
		continue;

	case 'd':
		delete();
		continue;

	case 'E':
		fchange = 0;
		c = 'e';
	case 'e':
		fflg++;
		setnoaddr();
		if (vflag && fchange) {
			fchange = 0;
			(void) error(1);
		}
		filename(c);
		eflg++;
		init();
		addr2 = zero;
		goto caseread;

	case 'f':
		setnoaddr();
		filename(c);
		if (!ncflg)  /* there is a filename */
			getime();
		else
			ncflg--;
		puts(savedfile);
		continue;

	case 'g':
		global(1);
		continue;
	case 'G':
		globaln(1);
		continue;

	case 'h':
		newline();
		setnoaddr();
		PUTM();
		continue;

	case 'H':
		newline();
		setnoaddr();
		if (!hflag) {
			hflag = 1;
			PUTM();
		}
		else
			hflag = 0;
		continue;

	case 'i':
#ifdef XPG6
		setzeroasone();
#endif
		setdot();
		nonzero();
		newline();
		if (!globflg) save();
		append(gettty, addr2-1);
		if (dot == addr2-1)
			dot += 1;
		continue;

	case 'j':
		if (addr2 == 0) {
			addr1 = dot;
			addr2 = dot+1;
		}
		setdot();
		newline();
		nonzero();
		if (!globflg) save();
		join();
		continue;

	case 'k':
		if ((c = getchr()) < 'a' || c > 'z')
			(void) error(2);
		newline();
		setdot();
		nonzero();
		names[c-'a'] = addr2->cur & ~01;
		anymarks |= 01;
		continue;

	case 'm':
		move(0);
		continue;

	case '\n':
		if (addr2 == 0)
			addr2 = dot+1;
		addr1 = addr2;
		goto print;

	case 'n':
		listn++;
		newline();
		goto print;

	case 'l':
		listf++;
	case 'p':
		newline();
	print:
		setdot();
		nonzero();
		a1 = addr1;
		do {
			if (listn) {
				count = a1 - zero;
				putd();
				putchr('\t');
			}
			puts(getline((a1++)->cur));
		}
		while (a1 <= addr2);
		dot = addr2;
		pflag = 0;
		listn = 0;
		listf = 0;
		continue;

	case 'Q':
		fchange = 0;
	case 'q':
		setnoaddr();
		newline();
		quit(sig);

	case 'r':
		filename(c);
	caseread:
		readflg = 1;
		save28 = (dol != fendcore);
		if (crflag == 2 || crflag == -2)
			crflag = -1; /* restore crflag for next file */
		errno = 0;
		if ((io = eopen(file, O_RDONLY)) < 0) {
			lastc = '\n';
			/* if first entering editor and file does not exist */
			/* set saved access time to 0 */
			if (eflg) {
				savtime = 0;
				eflg  = 0;
				if (c == 'e' && vflag == 0)
					qflg = 1;
			}
			if (errno == ENOENT) {
				(void) error(68);
			} else {
				(void) error(3);
			}
		}
		/* get last mod time of file */
		/* eflg - entered editor with ed or e  */
		if (eflg) {
			eflg = 0;
			getime();
		}
		setall();
		ninbuf = 0;
		n = zero != dol;
#ifdef NULLS
		nulls = 0;
#endif
		if (!globflg && (c == 'r')) save();
		append(getfile, addr2);
		exfile();
		readflg = 0;
		fchange = n;
		continue;

	case 's':
		setdot();
		nonzero();
		if (!globflg) save();
		substitute(globp != 0);
		continue;

	case 't':
		move(1);
		continue;

	case 'u':
		setdot();
		newline();
		if (!initflg)
			undo();
		else
			(void) error(5);
		fchange = 1;
		continue;

	case 'v':
		global(0);
		continue;
	case 'V':
		globaln(0);
		continue;

	case 'W':
	case 'w':
		if (flag28) {
			flag28 = 0;
			fchange = 0;
			(void) error(61);
		}
		setall();

		/* on NULL-RE condition do not generate error */

		if ((linebuf[0] != '.') && (zero != dol) &&
			(addr1 <= zero || addr2 > dol))
			(void) error(15);
		filename(c);
		if (Xqt) {
			io = eopen(file, O_WRONLY);
			n = 1;	/* set n so newtime will not execute */
		} else {
			struct stat lFl;
			fstat(tfile, &Tf);
			if (stat(file, &Fl) < 0) {
				if ((io = creat(file, S_IRUSR|S_IWUSR|S_IRGRP
					|S_IWGRP|S_IROTH|S_IWOTH)) < 0)
					(void) error(7);
				fstat(io, &Fl);
				Fl.st_mtime = 0;
				lFl = Fl;
				close(io);
			} else {
#ifndef	RESEARCH
				oldmask = umask(0);
				/*
				 * Must determine if file is
				 * a symbolic link
				 */
				lstat(file, &lFl);
#endif
			}
#ifndef RESEARCH
			/*
			 * Determine if there are enough free blocks on system
			 */
			if (!Short && statvfs(file, &U) == 0 &&
			    U.f_bfree < ((Tf.st_size/U.f_frsize) + 100)) {
				Short = 1;
				(void) error(8);
			}
			Short = 0;
#endif
			p1 = savedfile;		/* The current filename */
			p2 = file;
			m = strcmp(p1, p2);
			if (c == 'w' && Fl.st_nlink == 1 && ISREG(lFl)) {
				if (close(open(file, O_WRONLY)) < 0)
					(void) error(9);
				if (!(n = m))
					chktime();
				mkfunny();
				/*
				 * If funlink equals one it means that
				 * funny points to a valid file which must
				 * be unlinked when interrupted.
				 */

				funlink = 1;
				if ((io = creat(funny, FMODE(Fl))) >= 0) {
					chown(funny, Fl.st_uid, Fl.st_gid);
					chmod(funny, FMODE(Fl));
					putfile();
					exfile();

					if (rename(funny, file))
						(void) error(10);
					funlink = 0;
					/* if filenames are the same */
					if (!n)
						newtime();
					/* check if entire buffer was written */
					fsave = fchange;
		fchange = (((addr1 == zero) || (addr1 == (zero + 1))) &&
			(addr2 == dol)) ? 0 : 1;
			if (fchange == 1 && m != 0) fchange = fsave;
					continue;
				}
			} else
				n = 1;	/* set n so newtime will not execute */
			if ((io = open(file,
				(c == 'w') ? O_WRONLY|O_CREAT|O_TRUNC
				: O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR
				|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0)
				(void) error(7);
		}
		putfile();
		exfile();
		if (!n) newtime();
		fsave = fchange;
		fchange = (((addr1 == zero) || (addr1 == (zero + 1))) &&
			(addr2 == dol)) ? 0 : 1;
	/* Leave fchange alone if partial write was to another file */
		if (fchange == 1 && m != 0) fchange = fsave;
		continue;

	case 'C':
		crflag = 1;
			/*
			 * C is same as X, but always assume input files are
			 * ciphertext
			 */
		goto encrypt;

	case 'X':
		crflag = -1;
encrypt:
		setnoaddr();
		newline();
		xflag = 1;
		if (permflag)
			(void) crypt_close(perm);
		permflag = 1;
		if ((kflag = run_setkey(&perm[0], getkey(msgtab[66])))
			== -1) {
			xflag = 0;
			kflag = 0;
			crflag = 0;
			(void) error(64);
		}
		if (kflag == 0)
			crflag = 0;
		continue;

	case '=':
		setall();
		newline();
		count = (addr2-zero)&077777;
		putd();
		putchr('\n');
		continue;

	case '!':
		unixcom();
		continue;

	case EOF:
		return;

	case 'P':
		setnoaddr();
		newline();
		if (shflg)
			shflg = 0;
		else
			shflg++;
		continue;
	}
	if (c == 'x')
		(void) error(60);
	else
		(void) error(12);
	}
}

LINE
address(void)
{
	int minus, c;
	LINE a1;
	int n, relerr, retval;

	minus = 0;
	a1 = 0;
	for (;;) {
		c = getchr();
		if ('0' <= c && c <= '9') {
			n = 0;
			do {
				n *= 10;
				n += c - '0';
			} while ((c = getchr()) >= '0' && c <= '9');
			peekc = c;
			if (a1 == 0)
				a1 = zero;
			if (minus < 0)
				n = -n;
			a1 += n;
			minus = 0;
			continue;
		}
		relerr = 0;
		if (a1 || minus)
			relerr++;
		switch (c) {
		case ' ':
		case '\t':
			continue;

		case '+':
			minus++;
			if (a1 == 0)
				a1 = dot;
			continue;

		case '-':
		case '^':
			minus--;
			if (a1 == 0)
				a1 = dot;
			continue;

		case '?':
		case '/':
			comple(c);
			a1 = dot;
			for (;;) {
				if (c == '/') {
					a1++;
					if (a1 > dol)
						a1 = zero;
				} else {
					a1--;
					if (a1 < zero)
						a1 = dol;
				}

				if (execute(0, a1))
					break;
				if (a1 == dot)
					(void) error(13);
			}
			break;

		case '$':
			a1 = dol;
			break;

		case '.':
			a1 = dot;
			break;

		case '\'':
			if ((c = getchr()) < 'a' || c > 'z')
				(void) error(2);
			for (a1 = zero; a1 <= dol; a1++)
				if (names[c-'a'] == (a1->cur & ~01))
					break;
			break;

		default:
			peekc = c;
			if (a1 == 0)
				return (0);
			a1 += minus;

			/* on NULL-RE condition do not generate error */

			if ((linebuf[0] != '.') && (a1 < zero || a1 > dol))
				(void) error(15);
			return (a1);
		}
		if (relerr)
			(void) error(16);
	}
}

static void
setdot(void)
{
	if (addr2 == 0)
		addr1 = addr2 = dot;
	if (addr1 > addr2)
		(void) error(17);
}

static void
setall(void)
{
	if (addr2 == 0) {
		addr1 = zero+1;
		addr2 = dol;
		if (dol == zero)
			addr1 = zero;
	}
	setdot();
}

static void
setnoaddr(void)
{
	if (addr2)
		(void) error(18);
}

static void
nonzero(void)
{
	/* on NULL-RE condition do not generate error */

	if ((linebuf[0] != '.') && (addr1 <= zero || addr2 > dol))
		(void) error(15);
}

static void
setzeroasone(void)
{
/* for the c and i commands 0 equal to 1 address */
	if (addr1 == zero) {
		addr1 = zero+1;
	}
	if (addr2 == zero) {
		addr2 = zero+1;
	}
}


static void
newline(void)
{
	int c;

	if ((c = getchr()) == '\n')
		return;
	if (c == 'p' || c == 'l' || c == 'n') {
		pflag++;
		if (c == 'l') listf++;
		if (c == 'n') listn++;
		if ((c = getchr()) == '\n')
			return;
	}
	(void) error(20);
}

static void
filename(int comm)
{
	char *p1, *p2;
	int c;
	int i = 0;

	count = 0;
	c = getchr();
	if (c == '\n' || c == EOF) {
		p1 = savedfile;
		if (*p1 == 0 && comm != 'f')
			(void) error(21);
		/* ncflg set means do not get mod time of file */
		/* since no filename followed f */
		if (comm == 'f')
			ncflg++;
		p2 = file;
		while (*p2++ = *p1++);
		red(savedfile);
		return;
	}
	if (c != ' ')
		(void) error(22);
	while ((c = getchr()) == ' ');
	if (c == '!')
		++Xqt, c = getchr();
	if (c == '\n')
		(void) error(21);
	p1 = file;
	do {
		if (++i >= FNSIZE)
			(void) error(24);
		*p1++ = c;
		if (c == EOF || (c == ' ' && !Xqt))
			(void) error(21);
	} while ((c = getchr()) != '\n');
	*p1++ = 0;
	if (Xqt)
		if (comm == 'f') {
			--Xqt;
			(void) error(57);
		}
		else
			return;
	if (savedfile[0] == 0 || comm == 'e' || comm == 'f') {
		p1 = savedfile;
		p2 = file;
		while (*p1++ = *p2++);
	}
	red(file);
}


static void
exfile(void)
{
#ifdef NULLS
	int c;
#endif

#ifndef RESEARCH
	if (oldmask) {
		umask(oldmask);
		oldmask = 0;
	}
#endif
	eclose(io);
	io = -1;
	if (vflag) {
		putd();
		putchr('\n');
#ifdef NULLS
		if (nulls) {
			c = count;
			count = nulls;
			nulls = 0;
			putd();
			puts(gettext(" nulls replaced by '\\0'"));
			count = c;
		}
#endif
	}
}

static void
onintr(int sig)
{
	signal(SIGINT, onintr);
	putchr('\n');
	lastc = '\n';
	globflg = 0;
	if (funlink) unlink(funny); /* remove tmp file */
	/* if interrupted a read, only part of file may be in buffer */
	if (readflg) {
		sprintf(tstring, "\007read may be incomplete - beware!\007");
		puts(gettext(tstring));
		fchange = 0;
	}
	(void) error(26);
}

static void
onhup(int sig)
{
	signal(SIGINT, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	/*
	 * if there are lines in file and file was not written
	 * since last update, save in ed.hup, or $HOME/ed.hup
	 */
	if (dol > zero && fchange == 1) {
		addr1 = zero+1;
		addr2 = dol;
		io = creat("ed.hup",
			S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
		if (io < 0 && home) {
			char	*fn;

			fn = (char *)calloc(strlen(home) + 8, sizeof (char));
			if (fn) {
				strcpy(fn, home);
				strcat(fn, "/ed.hup");
				io = creat(fn, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
						|S_IROTH|S_IWOTH);
				free(fn);
			}
		}
		if (io > 0)
			putfile();
	}
	fchange = 0;
	++errcnt;
	quit(sig);
}

static int
error(int code)
{
	int c;

	if (code == 28 && save28 == 0) {
		fchange = 0;
		flag28++;
	}
	readflg = 0;
	++errcnt;
	listf = listn = 0;
	pflag = 0;
#ifndef RESEARCH
	if (oldmask) {
		umask(oldmask);
		oldmask = 0;
	}
#endif
#ifdef NULLS	/* Not really nulls, but close enough */
	/* This is a bug because of buffering */
	if (code == 28) /* illegal char. */
		putd();
#endif
	/* Cant open file or file does not exist */
	if ((code == 3) || (code == 68)) {
		if (qflg == 0) {
			putchr('?');
			puts(file);
		}
		else
			qflg = 0;
	}
	else
	{
		putchr('?');
		putchr('\n');
	}
	count = 0;
	lseek(0, (long)0, 2);
	if (globp)
		lastc = '\n';
	globp = 0;
	peekc = lastc;
	if (lastc)
		while ((c = getchr()) != '\n' && c != EOF);
	if (io) {
		eclose(io);
		io = -1;
	}
	xcode = code;
	if (hflag)
		PUTM();
	if (code == 4)
		return (0);	/* Non-fatal error. */
	longjmp(savej, 1);
	/* NOTREACHED */
}

static int
getchr(void)
{
	char c;
	if (lastc = peekc) {
		peekc = 0;
		return (lastc);
	}
	if (globp) {
		if ((lastc = (unsigned char)*globp++) != 0)
			return (lastc);
		globp = 0;
		return (EOF);
	}
	if (read(0, &c, 1) <= 0)
		return (lastc = EOF);
	lastc = (unsigned char)c;
	return (lastc);
}

static int
gettty(void)
{
	int c;
	char *gf;
	char *p;

	p = linebuf;
	gf = globp;
	while ((c = getchr()) != '\n') {
		if (c == EOF) {
			if (gf)
				peekc = c;
			return (c);
		}
		if (c == 0)
			continue;
		*p++ = c;

		if (p > &linebuf[LBSIZE-1])
			(void) error(27);
	}
	*p++ = 0;
	if (linebuf[0] == '.' && linebuf[1] == 0)
		return (EOF);

	/*
	 * POSIX.2/XPG4 explicitly says no to this:
	 *
	 * in Solaris backslash followed by special character "." is
	 * special character "." itself; (so terminating input mode can be
	 * "\.\n").
	 *
	 * however, POSIX2/XPG4 says, input mode is terminated by
	 * entering line consisting of only 2 characters: ".\n"
	 *
	 * if (linebuf[0]=='\\' && linebuf[1]=='.' && linebuf[2]==0) {
	 *	linebuf[0] = '.';
	 *	linebuf[1] = 0;
	 * }
	 */
	return (0);
}

static int
getfile(void)
{
	char c;
	char *lp, *fp;

	lp = linebuf;
	fp = nextip;
	do {
		if (--ninbuf < 0) {
			if ((ninbuf = read(io, genbuf, LBSIZE)-1) < 0)
				if (lp > linebuf) {
					puts(gettext("'\\n' appended"));
					*genbuf = '\n';
				}
				else
					return (EOF);
			if (crflag == -1) {
				if (isencrypt(genbuf, ninbuf + 1))
					crflag = 2;
				else
					crflag = -2;
			}
			fp = genbuf;
			if (crflag > 0)
			if (run_crypt(count, genbuf, ninbuf+1, perm) == -1)
				(void) error(63);
		}
		if (lp >= &linebuf[LBSIZE]) {
			lastc = '\n';
			(void) error(27);
		}
		if ((*lp++ = c = *fp++) == 0) {
#ifdef NULLS
			lp[-1] = '\\';
			*lp++ = '0';
			nulls++;
#else
			lp--;
			continue;
#endif
		}
		count++;
	} while (c != '\n');
	*--lp = 0;
	nextip = fp;
	if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) {
		write(1, gettext("line too long: lno = "),
			strlen(gettext("line too long: lno = ")));
		ccount = count;
		count = (++dot-zero)&077777;
		dot--;
		putd();
		count = ccount;
		putchr('\n');
	}
	return (0);
}

static void
putfile(void)
{
	int n;
	LINE a1;
	char *fp, *lp;
	int nib;

	nib = LBSIZE;
	fp = genbuf;
	a1 = addr1;
	do {
		lp = getline(a1++->cur);
		if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) {
			write(1, gettext("line too long: lno = "),
				strlen(gettext("line too long: lno = ")));
			ccount = count;
			count = (a1-zero-1)&077777;
			putd();
			count = ccount;
			putchr('\n');
		}
		for (;;) {
			if (--nib < 0) {
				n = fp-genbuf;
				if (kflag)
				if (run_crypt(count-n, genbuf, n, perm) == -1)
					(void) error(63);
				if (write(io, genbuf, n) != n)
					(void) error(29);
				nib = LBSIZE - 1;
				fp = genbuf;
			}
			if (dol->cur == 0L)break; /* Allow write of null file */
			count++;
			if ((*fp++ = *lp++) == 0) {
				fp[-1] = '\n';
				break;
			}
		}
	} while (a1 <= addr2);
	n = fp-genbuf;
	if (kflag)
		if (run_crypt(count-n, genbuf, n, perm) == -1)
			(void) error(63);
	if (write(io, genbuf, n) != n)
		(void) error(29);
}

static void
append(int (*f)(void), LINE a)
{
	LINE a1, a2, rdot;
	long tl;

	nline = 0;
	dot = a;
	while ((*f)() == 0) {
		if (dol >= endcore) {
			if ((int)sbrk(512 * sizeof (struct lin)) == -1) {
				lastc = '\n';
				(void) error(30);
			}
			endcore += 512;
		}
		tl = putline();
		nline++;
		a1 = ++dol;
		a2 = a1+1;
		rdot = ++dot;
		while (a1 > rdot)
			(--a2)->cur = (--a1)->cur;
		rdot->cur = tl;
	}
}

static void
unixcom(void)
{
	void (*savint)();
	pid_t	pid, rpid;
	int retcode;
	static char savcmd[LBSIZE];	/* last command */
	char curcmd[LBSIZE];		/* current command */
	char *psavcmd, *pcurcmd, *psavedfile;
	int endflg = 1, shflg = 0;
	wchar_t	c;
	int	len;

	setnoaddr();
	if (rflg)
		(void) error(6);
	pcurcmd = curcmd;
	/* read command til end */

	/*
	 * a '!' found in beginning of command is replaced with the saved
	 * command.  a '%' found in command is replaced with the current
	 * filename
	 */

	c = getchr();
	if (c == '!') {
		if (savcmd[0] == 0)
			(void) error(56);
		else {
			psavcmd = savcmd;
			while (*pcurcmd++ = *psavcmd++);
			--pcurcmd;
			shflg = 1;
		}
	} else
		UNGETC(c);  /* put c back */
	while (endflg == 1) {
		while ((c = get_wchr()) != '\n' && c != '%' && c != '\\') {
			if ((len = wctomb(pcurcmd, c)) <= 0) {
				*pcurcmd = (unsigned char)c;
				len = 1;
			}
			pcurcmd += len;
		}

		if (c == '%') {
			if (savedfile[0] == 0)
				(void) error(21);
			else {
				psavedfile = savedfile;
				while (pcurcmd < curcmd + LBSIZE &&
					(*pcurcmd++ = *psavedfile++));
				--pcurcmd;
				shflg = 1;
			}
		} else if (c == '\\') {
			c = get_wchr();
			if (c != '%')
				*pcurcmd++ = '\\';
			if ((len = wctomb(pcurcmd, c)) <= 0) {
				*pcurcmd = (unsigned char)c;
				len = 1;
			}
			pcurcmd += len;
		}
		else
			/* end of command hit */
			endflg = 0;
	}
	*pcurcmd++ = 0;
	if (shflg == 1)
		puts(curcmd);
	/* save command */
	strcpy(savcmd, curcmd);

	if ((pid = fork()) == 0) {
		signal(SIGHUP, oldhup);
		signal(SIGQUIT, oldquit);
		close(tfile);
		if (__xpg4 == 0) {	/* not XPG4 */
			shpath = "/usr/bin/sh";
		} else {
			/* XPG4 */
			shpath = "/usr/xpg4/bin/sh";
		}
		execlp((const char *)shpath, "sh", "-c", curcmd, (char *)0);
		exit(0100);
	}
	savint = signal(SIGINT, SIG_IGN);
	while ((rpid = wait(&retcode)) != pid && rpid != (pid_t)-1);
	signal(SIGINT, savint);
	if (vflag) puts("!");
}

static void
quit(int sig)
{
	if (vflag && fchange) {
		fchange = 0;
		if (flag28) {
			flag28 = 0;
			(void) error(62);
		}

		/*
		 * For case where user reads in BOTH a good
		 * file & a bad file
		 */
		(void) error(1);
	}
	unlink(tfname);
	if (kflag)
		crypt_close(perm);
	if (xtflag)
		crypt_close(tperm);
	exit(errcnt? 2: 0);
}

static void
delete(void)
{
	setdot();
	newline();
	nonzero();
	if (!globflg) save();
	rdelete(addr1, addr2);
}

static void
rdelete(LINE ad1, LINE ad2)
{
	LINE a1, a2, a3;

	a1 = ad1;
	a2 = ad2+1;
	a3 = dol;
	dol -= a2 - a1;
	do
		(a1++)->cur = (a2++)->cur;
	while (a2 <= a3);
	a1 = ad1;
	if (a1 > dol)
		a1 = dol;
	dot = a1;
	fchange = 1;
}

static void
gdelete(void)
{
	LINE a1, a2, a3;

	a3 = dol;
	for (a1 = zero+1; (a1->cur&01) == 0; a1++)
		if (a1 >= a3)
			return;
	for (a2 = a1 + 1; a2 <= a3; ) {
		if (a2->cur & 01) {
			a2++;
			dot = a1;
		} else
			(a1++)->cur = (a2++)->cur;
	}
	dol = a1-1;
	if (dot > dol)
		dot = dol;
	fchange = 1;
}

static char *
getline(long tl)
{
	char *bp, *lp;
	int nl;

	lp = linebuf;
	bp = getblock(tl, READ);
	nl = nleft;
	tl &= ~0377;
	while (*lp++ = *bp++)
		if (--nl == 0) {
			bp = getblock(tl += 0400, READ);
			nl = nleft;
		}
	return (linebuf);
}

static long
putline(void)
{
	char *bp, *lp;
	int nl;
	long tl;

	fchange = 1;
	lp = linebuf;
	tl = tline;
	bp = getblock(tl, WRITE);
	nl = nleft;
	tl &= ~0377;
	while (*bp = *lp++) {
		if (*bp++ == '\n') {
			*--bp = 0;
			linebp = lp;
			break;
		}
		if (--nl == 0) {
			bp = getblock(tl += 0400, WRITE);
			nl = nleft;
		}
	}
	nl = tline;
	tline += (((lp-linebuf)+03)>>1)&077776;
	return (nl);
}

static char *
getblock(long atl, long iof)
{
	int bno, off;
	char *p1, *p2;
	int n;

	bno = atl >> 8;
	off = (atl<<1)&0774;

	/* bno is limited to 16 bits */
	if (bno >= 65535) {
		lastc = '\n';
		(void) error(31);
	}
	nleft = 512 - off;
	if (bno == iblock) {
		ichanged |= iof;
		return (ibuff+off);
	}
	if (bno == oblock)
		return (obuff+off);
	if (iof == READ) {
		if (ichanged) {
			if (xtflag)
				if (run_crypt(0L, ibuff, 512, tperm) == -1)
					(void) error(63);
			blkio(iblock, ibuff, write);
		}
		ichanged = 0;
		iblock = bno;
		blkio(bno, ibuff, read);
		if (xtflag)
			if (run_crypt(0L, ibuff, 512, tperm) == -1)
				(void) error(63);
		return (ibuff+off);
	}
	if (oblock >= 0) {
		if (xtflag) {
			p1 = obuff;
			p2 = crbuf;
			n = 512;
			while (n--)
				*p2++ = *p1++;
			if (run_crypt(0L, crbuf, 512, tperm) == -1)
				(void) error(63);
			blkio(oblock, crbuf, write);
		} else
			blkio(oblock, obuff, write);
	}
	oblock = bno;
	return (obuff+off);
}

static void
blkio(int b, char *buf, ssize_t (*iofcn)())
{
	lseek(tfile, (long)b<<9, 0);
	if ((*iofcn)(tfile, buf, 512) != 512) {
		if (dol != zero)
			(void) error(32); /* Bypass this if writing null file */
	}
}

static void
init(void)
{
	long *markp;
	mode_t omask;

	if (tfile != -1) {
		(void) close(tfile);
		(void) unlink(tfname);
	}

	tline = 2;
	for (markp = names; markp < &names[26]; )
		*markp++ = 0L;
	subnewa = 0L;
	anymarks = 0;
	iblock = -1;
	oblock = -1;
	ichanged = 0;
	initflg = 1;
	omask = umask(0);

	if ((tfile = open(tfname, O_CREAT|O_EXCL|O_RDWR,
	    S_IRUSR|S_IWUSR)) < 0) {
		puts(gettext(msgtab[70]));
		exit(2);
	}

	umask(omask);
	if (xflag) {
		xtflag = 1;
		if (tpermflag)
			(void) crypt_close(tperm);
		tpermflag = 1;
		if (makekey(tperm)) {
			xtflag = 0;
			puts(gettext(msgtab[65]));
		}
	}
	brk((char *)fendcore);
	dot = zero = dol = savdot = savdol = fendcore;
	flag28 = save28 = 0;
	endcore = fendcore - sizeof (struct lin);
}

static void
global(int k)
{
	char *gp;
	wchar_t l;
	char multic[MB_LEN_MAX];
	wchar_t c;
	LINE a1;
	char globuf[LBSIZE];
	int n;
	int	len;

	if (globp)
		(void) error(33);
	setall();
	nonzero();
	if ((n = _mbftowc(multic, &l, getchr, &peekc)) <= 0)
		(void) error(67);
	if (l == '\n')
		(void) error(19);
	save();
	comple(l);
	gp = globuf;
	while ((c = get_wchr()) != '\n') {
		if (c == EOF)
			(void) error(19);

		/* '\\' has special meaning only if preceding a '\n' */
		if (c == '\\') {
			c = get_wchr();
			if (c != '\n')
				*gp++ = '\\';
		}
		if ((gp + (unsigned int)MB_CUR_MAX) >= &globuf[LBSIZE-1])
			(void) error(34);
		if ((len = wctomb(gp, c)) <= 0) {
			*gp = (unsigned char)c;
			len = 1;
		}
		gp += len;
	}
	if (gp == globuf)
		*gp++ = 'p';
	*gp++ = '\n';
	*gp++ = 0;
	for (a1 = zero; a1 <= dol; a1++) {
		a1->cur &= ~01;
		if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
			a1->cur |= 01;
	}
	/*
	 * Special case: g/.../d (avoid n^2 algorithm)
	 */
	if (globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == '\0') {
		gdelete();
		return;
	}
	for (a1 = zero; a1 <= dol; a1++) {
		if (a1->cur & 01) {
			a1->cur &= ~01;
			dot = a1;
			globp = globuf;
			globflg = 1;
			commands();
			globflg = 0;
			a1 = zero;
		}
	}
}

static void
join(void)
{
	char *gp, *lp;
	LINE a1;

	if (addr1 == addr2)
		return;
	gp = genbuf;
	for (a1 = addr1; a1 <= addr2; a1++) {
		lp = getline(a1->cur);
		while (*gp = *lp++)
			if (gp++ > &genbuf[LBSIZE-1])
				(void) error(27);
	}
	lp = linebuf;
	gp = genbuf;
	while (*lp++ = *gp++);
	addr1->cur = putline();
	if (addr1 < addr2)
		rdelete(addr1+1, addr2);
	dot = addr1;
}

static void
substitute(int inglob)
{
	int nl;
	LINE a1;
	long *markp;
	int ingsav;		/* For saving arg. */

	ingsav = inglob;
	ocerr2 = 0;
	gsubf = compsub();
	for (a1 = addr1; a1 <= addr2; a1++) {
		if (execute(0, a1) == 0)
			continue;
		numpass = 0;
		ocerr1 = 0;
		inglob |= 01;
		dosub();
		if (gsubf) {
			while (*loc2) {
				if (execute(1, (LINE)0) == 0)
					break;
				dosub();
			}
		}
		if (ocerr1 == 0)continue;	/* Don't put out-not changed. */
		subnewa = putline();
		a1->cur &= ~01;
		if (anymarks) {
			for (markp = names; markp < &names[26]; markp++)
				if (*markp == a1->cur)
					*markp = subnewa;
		}
		a1->cur = subnewa;
		append(getsub, a1);
		nl = nline;
		a1 += nl;
		addr2 += nl;
	}
	if (ingsav)
		return;	/* Was in global-no error msg allowed. */
	if (inglob == 0)
		(void) error(35);	/* Not in global, but not found. */
	if (ocerr2 == 0)
		(void) error(35); /* RE found, but occurrence match failed. */
}

static int
compsub(void)
{
	int c;
	wchar_t seof;
	char *p;
	char multic[MB_LEN_MAX];
	int n;
	static char remem[RHSIZE];
	static int remflg = -1;
	int i;

	if ((n = _mbftowc(multic, &seof, getchr, &peekc)) <= 0)
		(void) error(67);
	if (seof == '\n' || seof == ' ')
		(void) error(36);
	comple(seof);
	p = rhsbuf;
	for (;;) {
		wchar_t cl;
		if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
			(void) error(67);
		if (cl == '\\') {
			*p++ = '\\';
			if (p >= &rhsbuf[RHSIZE])
				(void) error(38);
			if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
				(void) error(67);
		} else if (cl == '\n') {
			if (nodelim == 1) {
				nodelim = 0;
				(void) error(36);
			}
			if (!(globp && globp[0])) {
				UNGETC('\n');
				pflag++;
				break;
			}
		} else if (cl == seof)
			break;
		if (p + n > &rhsbuf[RHSIZE])
			(void) error(38);
		(void) strncpy(p, multic, n);
		p += n;
	}
	*p++ = 0;
	if (rhsbuf[0] == '%' && rhsbuf[1] == 0)
		/*
		 * If there isn't a remembered string, it is an error;
		 * otherwise the right hand side is the previous right
		 * hand side.
		 */

		if (remflg == -1)
			(void) error(55);
		else
			strcpy(rhsbuf, remem);
	else {
		strcpy(remem, rhsbuf);
		remflg = 0;
	}
	c = 0;
	peekc = getchr();	/* Gets char after third delimiter. */
	if (peekc == 'g') {
		c = LBSIZE; peekc = 0;
	}
	if (peekc >= '1' && peekc <= '9') {
		c = peekc-'0';
		peekc = 0;	/* Allows getchr() to get next char. */
		while (1) {
			i = getchr();
			if (i < '0' || i > '9')
				break;
			c = c*10 + i-'0';
			if (c > LBSIZE-1)
				(void) error(20);	/* "Illegal suffix" */
			}
		peekc = i;	/* Effectively an unget. */
		}
	newline();
	return (c);

	/*
	 * Returns occurrence value. 0 & 1 both do first occurrence
	 * only: c = 0 if ordinary substitute; c = 1
	 * if use 1 in global sub(s/a/b/1). 0 in global form is illegal.
	 */
}

static int
getsub(void)
{
	char *p1, *p2;

	p1 = linebuf;
	if ((p2 = linebp) == 0)
		return (EOF);
	while (*p1++ = *p2++);
	linebp = 0;
	return (0);
}

static void
dosub(void)
{
	char *lp, *sp, *rp;
	int c;

	if (gsubf > 0 && gsubf < LBSIZE) {
		numpass++;
		if (gsubf != numpass)
			return;
	}
	ocerr1++;
	ocerr2++;
	lp = linebuf;
	sp = genbuf;
	rp = rhsbuf;
	while (lp < loc1)
		*sp++ = *lp++;
	while (c = *rp++) {
		if (c == '&') {
			sp = place(sp, loc1, loc2);
			continue;
		} else if (c == '\\') {
			c = *rp++;
			if (c >= '1' && c < nbra + '1') {
			sp = place(sp, braslist[c-'1'], braelist[c-'1']);
				continue;
			}
		}
		*sp++ = c;
		if (sp >= &genbuf[LBSIZE])
			(void) error(27);
	}
	lp = loc2;
	loc2 = sp - genbuf + linebuf;
	while (*sp++ = *lp++)
		if (sp >= &genbuf[LBSIZE])
			(void) error(27);
	lp = linebuf;
	sp = genbuf;
	while (*lp++ = *sp++);
}

static char *
place(char *sp, char *l1, char *l2)
{

	while (l1 < l2) {
		*sp++ = *l1++;
		if (sp >= &genbuf[LBSIZE])
			(void) error(27);
	}
	return (sp);
}

static void
comple(wchar_t seof)
{
	int cclass = 0;
	wchar_t c;
	int n;
	char *cp = genbuf;
	char multic[MB_LEN_MAX];

	while (1) {
		if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
			error1(67);
		if (n == 0 || c == '\n') {
			if (cclass)
				error1(49);
			else
				break;
		}
		if (c == seof && !cclass)
			break;
		if (cclass && c == ']') {
			cclass = 0;
			if (cp > &genbuf[LBSIZE-1])
				error1(50);
			*cp++ = ']';
			continue;
		}
		if (c == '[' && !cclass) {
			cclass = 1;
			if (cp > &genbuf[LBSIZE-1])
				error1(50);
			*cp++ = '[';
			if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
				error1(67);
			if (n == 0 || c == '\n')
				error1(49);
		}
		if (c == '\\' && !cclass) {
			if (cp > &genbuf[LBSIZE-1])
				error1(50);
			*cp++ = '\\';
			if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
				error1(67);
			if (n == 0 || c == '\n')
				error1(36);
		}
		if (cp + n > &genbuf[LBSIZE-1])
			error1(50);
		(void) strncpy(cp, multic, n);
		cp += n;
	}
	*cp = '\0';
	if (n != 0 && c == '\n')
		UNGETC('\n');
	if (n == 0 || c == '\n')
		nodelim = 1;

	/*
	 * NULL RE: do not compile a null regular expression; but process
	 * input with last regular expression encountered
	 */

	if (genbuf[0] != '\0') {
		if (expbuf)
			free(expbuf);
		expbuf = compile(genbuf, (char *)0, (char *)0);
	}
	if (regerrno)
		error1(regerrno);
}

static void
move(int cflag)
{
	LINE adt, ad1, ad2;

	setdot();
	nonzero();
	if ((adt = address()) == 0)
		(void) error(39);
	newline();
	if (!globflg) save();
	if (cflag) {
		ad1 = dol;
		append(getcopy, ad1++);
		ad2 = dol;
	} else {
		ad2 = addr2;
		for (ad1 = addr1; ad1 <= ad2; )
			(ad1++)->cur &= ~01;
		ad1 = addr1;
	}
	ad2++;
	if (adt < ad1) {
		dot = adt + (ad2-ad1);
		if ((++adt) == ad1)
			return;
		reverse(adt, ad1);
		reverse(ad1, ad2);
		reverse(adt, ad2);
	} else if (adt >= ad2) {
		dot = adt++;
		reverse(ad1, ad2);
		reverse(ad2, adt);
		reverse(ad1, adt);
	} else
		(void) error(39);
	fchange = 1;
}

static void
reverse(LINE a1, LINE a2)
{
	long t;

	for (;;) {
		t = (--a2)->cur;
		if (a2 <= a1)
			return;
		a2->cur = a1->cur;
		(a1++)->cur = t;
	}
}

static int
getcopy(void)
{

	if (addr1 > addr2)
		return (EOF);
	(void) getline((addr1++)->cur);
	return (0);
}


/*
 * Handles error code returned from comple() routine: regular expression
 * compile and match routines
 */

static void
error1(int code)
{
	nbra = 0;
	(void) error(code);
}


static int
execute(int gf, LINE addr)
{
	char *p1;
	int c;

	for (c = 0; c < nbra; c++) {
		braslist[c] = 0;
		braelist[c] = 0;
	}
	if (gf)
		locs = p1 = loc2;
	else {
		if (addr == zero)
			return (0);
		p1 = getline(addr->cur);
		locs = 0;
	}
	return (step(p1, expbuf));
}


static void
putd()
{
	int r;

	r = (int)(count%10);
	count /= 10;
	if (count)
		putd();
	putchr(r + '0');
}


int
puts(const char *sp)
{
	int n;
	wchar_t c;
	int sz, i;
	if (fss.Ffill && (listf == 0)) {

		/* deliberate attempt to remove constness of sp because */
		/* it needs to be expanded */

		if ((i = expnd((char *)sp, funny, &sz, &fss)) == -1) {
			write(1, funny, fss.Flim & 0377);
			putchr('\n');
			write(1, gettext("too long"),
				strlen(gettext("too long")));
		}
		else
			write(1, funny, sz);
		putchr('\n');
		if (i == -2)
			write(1, gettext("tab count\n"),
				strlen(gettext("tab count\n")));
		return (0);
	}
	col = 0;
	while (*sp) {
		n = mbtowc(&c, sp, MB_LEN_MAX);
		if (listf) {
			if (n < 1)
				(void) error(28);
			else if (n == 1)
				putchr((unsigned char)*sp++);
			else {
				sp += n;
				putwchr(c);
			}
		} else {
			putchr((unsigned char)*sp++);
		}
	}
#ifndef XPG6
	if (listf)
		putchr('$');    /* end of line is marked with a $ */
#else
	if (listf) {
	/* xpg6 - ensure that the end of line $ is not preceeded with a "\" */
	/* by doing a putchr() with listf=0, thereby avoiding the $ case */
	/* statement  in putchr() */
		listf = 0;
		putchr('$');    /* end of line is marked with a $ */
		listf++;
	}
#endif
	putchr('\n');
	return (1);
}


static void
putwchr(wchar_t ac)
{
	char buf[MB_LEN_MAX], *p;
	char *lp;
	wchar_t c;
	short len;

	lp = linp;
	c = ac;
	if (listf) {
		if (!iswprint(c)) {
			p = &buf[0];
			if ((len = wctomb(p, c)) <= 0) {
				*p = (unsigned char)c;
				len = 1;
			};
			while (len--) {
				if (col + 4 >= 72) {
					col = 0;
					*lp++ = '\\';
					*lp++ = '\n';
				}
				(void) sprintf(lp, "\\%03o",
					*(unsigned char *)p++);
				col += 4;
				lp += 4;
			}
		} else {
			if ((len = wcwidth(c)) <= 0)
				len = 0;
			if (col + len >= 72) {
				col = 0;
				*lp++ = '\\';
				*lp++ = '\n';
			}
			col += len;
			if ((len = wctomb(lp, c)) <= 0) {
				*lp = (unsigned char)c;
				len = 1;
			}
			lp += len;
		}
	} else {
		if ((len = wctomb(lp, c)) <= 0) {
			*lp = (unsigned char)c;
			len = 1;
		}
		lp += len;
	}
	if (c == '\n' || lp >= &line[64]) {
		linp = line;
		len = lp - line;
		write(1, line, len);
		return;
	}
	linp = lp;
}


static void
putchr(unsigned char c)
{
	char *lp;
	int len;

	lp = linp;
	if (listf && c != '\n') {
		switch (c) {
			case '\\' :
				*lp++ = '\\';
				*lp++ = '\\';
				col += 2;
				break;
			case '\007' :
				*lp++ = '\\';
				*lp++ = 'a';
				col += 2;
				break;
			case '\b' :
				*lp++ = '\\';
				*lp++ = 'b';
				col += 2;
				break;
			case '\f' :
				*lp++ = '\\';
				*lp++ = 'f';
				col += 2;
				break;
			case '\r' :
				*lp++ = '\\';
				*lp++ = 'r';
				col += 2;
				break;
			case '\t' :
				*lp++ = '\\';
				*lp++ = 't';
				col += 2;
				break;
			case '\v' :
				*lp++ = '\\';
				*lp++ = 'v';
				col += 2;
				break;
#ifdef XPG6
		/* if $ characters are within the line preceed with \ */
			case '$' :
				*lp++ = '\\';
				*lp++ = '$';
				col += 2;
				break;
#endif
			default:
				if (isprint(c)) {
					*lp++ = c;
					col += 1;
				} else {
					(void) sprintf(lp, "\\%03o", c);
					col += 4;
					lp += 4;
				}
				break;
		}

	/*
	 * long lines are folded w/ pt of folding indicated by writing
	 * backslash/newline character
	 */

		if (col + 1 >= 72) {
			col = 0;
			*lp++ = '\\';
			*lp++ = '\n';
		}
	} else
		*lp++ = c;
	if (c == '\n' || lp >= &line[64]) {
		linp = line;
		len = lp - line;
		(void) write(1, line, len);
		return;
	}
	linp = lp;
}


static char *
getkey(const char *prompt)
{
	struct termio b;
	int save;
	void (*sig)();
	static char key[KSIZE+1];
	char *p;
	int c;

	sig = signal(SIGINT, SIG_IGN);
	ioctl(0, TCGETA, &b);
	save = b.c_lflag;
	b.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
	ioctl(0, TCSETAW, &b);
	write(1, gettext(prompt), strlen(gettext(prompt)));
	p = key;
	while (((c = getchr()) != EOF) && (c != '\n')) {
		if (p < &key[KSIZE])
			*p++ = c;
	}
	*p = 0;
	write(1, "\n", 1);
	b.c_lflag = save;
	ioctl(0, TCSETAW, &b);
	signal(SIGINT, sig);
	return (key);
}


static void
globaln(int k)
{
	char *gp;
	int c;
	int n;
	wchar_t cl;
	LINE a1;
	int  nfirst;
	char globuf[LBSIZE];
	char multic[MB_LEN_MAX];
	int	len;
	int pflag_save = 0;
	int listf_save = 0;
	int listn_save = 0;

	if (globp)
		(void) error(33);
	setall();
	nonzero();
	if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
		(void) error(67);
	if (cl == '\n')
		(void) error(19);
	save();
	comple(cl);
	for (a1 = zero; a1 <= dol; a1++) {
		a1->cur &= ~01;
		if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
			a1->cur |= 01;
	}
	nfirst = 0;
	newline();
	/*
	 * preserve the p, l, and n suffix commands of the G and V
	 * commands during the interactive section and restore
	 * on completion of the G and V command.
	 */
	pflag_save = pflag;
	listf_save = listf;
	listn_save = listn;
	pflag = 0;
	listf = 0;
	listn = 0;
	for (a1 = zero; a1 <= dol; a1++) {
		if (a1->cur & 01) {
			a1->cur &= ~01;
			dot = a1;
			puts(getline(a1->cur));
			if ((c = get_wchr()) == EOF)
				(void) error(52);
			if (c == 'a' || c == 'i' || c == 'c')
				(void) error(53);
			if (c == '\n') {
				a1 = zero;
				continue;
			}
			if (c != '&') {
				gp = globuf;
				if ((len = wctomb(gp, c)) <= 0) {
					*gp = (unsigned char)c;
					len = 1;
				}
				gp += len;
				while ((c = get_wchr()) != '\n') {

			/* '\\' has special meaning only if preceding a '\n' */
					if (c == '\\') {
						c = get_wchr();
						if (c != '\n')
							*gp++ = '\\';
					}
					if ((gp + (unsigned int)MB_CUR_MAX) >=
							&globuf[LBSIZE-1])
						(void) error(34);

					if ((len = wctomb(gp, c)) <= 0) {
						*gp = (unsigned char)c;
						len = 1;
					}
					gp += len;
				}
				*gp++ = '\n';
				*gp++ = 0;
				nfirst = 1;
			} else if ((c = get_wchr()) != '\n')
				(void) error(54);
			globp = globuf;
			if (nfirst) {
				globflg = 1;
				commands();
				globflg = 0;
			} else
				(void) error(56);
			globp = 0;
			a1 = zero;
		}
	}
	pflag = pflag_save;
	listf = listf_save;
	listn = listn_save;
}


static int
eopen(char *string, int rw)
{
#define	w_or_r(a, b) (rw ? a : b)
	int pf[2];
	pid_t i;
	int io;
	int chcount;	/* # of char read. */

	if (rflg) {	/* restricted shell */
		if (Xqt) {
			Xqt = 0;
			(void) error(6);
		}
	}
	if (!Xqt) {
		if ((io = open(string, rw)) >= 0) {
			if (fflg) {
				chcount = read(io, crbuf, LBSIZE);
				if (crflag == -1) {
					if (isencrypt(crbuf, chcount))
						crflag = 2;
					else
						crflag = -2;
				}
				if (crflag > 0)
				if (run_crypt(0L, crbuf, chcount, perm) == -1)
						(void) error(63);
				if (fspec(crbuf, &fss, 0) < 0) {
					fss.Ffill = 0;
					fflg = 0;
					(void) error(4);
				}
				lseek(io, 0L, 0);
			}
		}
		fflg = 0;
		return (io);
	}
	if (pipe(pf) < 0)
xerr:		(void) error(0);
	if ((i = fork()) == 0) {
		signal(SIGHUP, oldhup);
		signal(SIGQUIT, oldquit);
		signal(SIGPIPE, oldpipe);
		signal(SIGINT, (void (*)()) 0);
		close(w_or_r(pf[1], pf[0]));
		close(w_or_r(0, 1));
		dup(w_or_r(pf[0], pf[1]));
		close(w_or_r(pf[0], pf[1]));
		if (__xpg4 == 0) {	/* not XPG4 */
			shpath = "/usr/bin/sh";
		} else {
			/* XPG4 */
			shpath = "/usr/xpg4/bin/sh";
		}
		execlp((const char *)shpath, "sh", "-c", string, (char *)0);
		exit(1);
	}
	if (i == (pid_t)-1)
		goto xerr;
	close(w_or_r(pf[0], pf[1]));
	return (w_or_r(pf[1], pf[0]));
}


static void
eclose(int f)
{
	close(f);
	if (Xqt)
		Xqt = 0, wait((int *)0);
}


static void
mkfunny(void)
{
	char *p, *p1, *p2;

	p2 = p1 = funny;
	p = file;
	/*
	 * Go to end of file name
	 */
	while (*p)
		p++;
	while (*--p  == '/')	/* delete trailing slashes */
		*p = '\0';
	/*
	 * go back to beginning of file
	 */
	p = file;
	/*
	 * Copy file name to funny setting p2 at
	 * basename of file.
	 */
	while (*p1++ = *p)
		if (*p++ == '/') p2 = p1;
	/*
	 * Set p1 to point to basename of tfname.
	 */
	p1 = strrchr(tfname, '/');
	if (strlen(tfname) > (size_t)6)
		p1 = &tfname[strlen(tfname)-6];
	p1++;
	*p2 = '\007'; /* add unprintable char for funny  a unique name */
	/*
	 * Copy tfname to file.
	 */
	while (*++p2 = *p1++);
}


static void
getime(void) /* get modified time of file and save */
{
	if (stat(file, &Fl) < 0)
		savtime = 0;
	else
		savtime = Fl.st_mtime;
}


static void
chktime(void) /* check saved mod time against current mod time */
{
	if (savtime != 0 && Fl.st_mtime != 0) {
		if (savtime != Fl.st_mtime)
			(void) error(58);
	}
}


static void
newtime(void) /* get new mod time and save */
{
	stat(file, &Fl);
	savtime = Fl.st_mtime;
}


static void
red(char *op)	/* restricted - check for '/' in name */
		/* and delete trailing '/' */
{
	char *p;

	p = op;
	while (*p)
		if (*p++ == '/'&& rflg) {
			*op = 0;
			(void) error(6);
		}
	/* delete trailing '/' */
	while (p > op) {
		if (*--p == '/')
			*p = '\0';
		else break;
	}
}


/*
 * Searches thru beginning of file looking for a string of the form
 *	<: values... :>
 *
 * where "values" are
 *
 *	\b      ignored
 *	s<num>  sets the Flim to <num>
 *	t???    sets tab stop stuff
 *	d       ignored
 *	m<num>  ignored
 *	e       ignored
 */

static int
fspec(char line[], struct Fspec *f, int up)
{
	struct termio arg;
	int havespec, n;
	int	len;

	if (!up) clear(f);

	havespec = fsprtn = 0;
	for (fsp = line; *fsp && *fsp != '\n'; fsp += len) {
		if ((len = mblen(fsp, MB_CUR_MAX)) <= 0)
			len = 1;
		switch (*fsp) {

			case '<':	if (havespec)
						return (-1);
					if (*(fsp+1) == ':') {
						havespec = 1;
						clear(f);
						if (!ioctl(1, TCGETA, &arg) &&
						((arg.c_oflag&TAB3) == TAB3))
						    f->Ffill = 1;
						fsp++;
						continue;
					}

			case ' ':	continue;

			case 's':	if (havespec && (n = numb()) >= 0)
						f->Flim = n;
					continue;

			case 't':	if (havespec) targ(f);
					continue;

			case 'd':	continue;

			case 'm':	if (havespec)  n = numb();
					continue;

			case 'e':	continue;
			case ':':	if (!havespec) continue;
					if (*(fsp+1) != '>') fsprtn = -1;
					return (fsprtn);

			default:	if (!havespec) continue;
					return (-1);
		}
	}
	return (1);
}


static int
numb(void)
{
	int n;

	n = 0;
	while (*++fsp >= '0' && *fsp <= '9')
		n = 10*n + *fsp-'0';
	fsp--;
	return (n);
}


static void
targ(struct Fspec *f)
{

	if (*++fsp == '-') {
		if (*(fsp + 1) >= '0' && *(fsp+1) <= '9') tincr(numb(), f);
		else tstd(f);
		return;
	}
	if (*fsp >= '0' && *fsp <= '9') {
		tlist(f);
		return;
	}
	fsprtn = -1;
	fsp--;
}


static void
tincr(int n, struct Fspec *f)
{
	int l, i;

	l = 1;
	for (i = 0; i < 20; i++)
		f->Ftabs[i] = l += n;
	f->Ftabs[i] = 0;
}


static void
tstd(struct Fspec *f)
{
	char std[3];

	std[0] = *++fsp;
	if (*(fsp+1) >= '0' && *(fsp+1) <= '9')  {
						std[1] = *++fsp;
						std[2] = '\0';
	} else std[1] = '\0';
	fsprtn = stdtab(std, f->Ftabs);
}


static void
tlist(struct Fspec *f)
{
	int n, last, i;

	fsp--;
	last = i = 0;

	do {
		if ((n = numb()) <= last || i >= 20) {
			fsprtn = -1;
			return;
		}
		f->Ftabs[i++] = last = n;
	} while (*++fsp == ',');

	f->Ftabs[i] = 0;
	fsp--;
}


static int
expnd(char line[], char buf[], int *sz, struct Fspec *f)
{
	char *l, *t;
	int b;

	l = line - 1;
	b = 1;
	t = f->Ftabs;
	fsprtn = 0;

	while (*++l && *l != '\n' && b < 511) {
		if (*l == '\t') {
			while (*t && b >= *t) t++;
			if (*t == 0) fsprtn = -2;
			do buf[b-1] = ' '; while (++b < *t);
		} else buf[b++ - 1] = *l;
	}

	buf[b] = '\0';
	*sz = b;
	if (*l != '\0' && *l != '\n') {
		buf[b-1] = '\n';
		return (-1);
	}
	buf[b-1] = *l;
	if (f->Flim && (b-1 > (int)f->Flim))
		return (-1);
	return (fsprtn);
}


static void
clear(struct Fspec *f)
{
	f->Ftabs[0] = f->Fdel = f->Fmov = f->Ffill = 0;
	f->Flim = 0;
}


static int
lenchk(char line[], struct Fspec *f)
{
	char *l, *t;
	int b;

	l = line - 1;
	b = 1;
	t = f->Ftabs;

	while (*++l && *l != '\n' && b < 511) {
		if (*l == '\t') {
			while (*t && b >= *t) t++;
			while (++b < *t);
		} else b++;
	}

	if ((*l != '\0' && *l != '\n') || (f->Flim && (b-1 > (int)f->Flim)))
		return (-1);
	return (0);
}
#define	NTABS 21


/*
 *	stdtabs: standard tabs table
 *	format: option code letter(s), null, tabs, null
 */

static char stdtabs[] = {
'a', 0, 1, 10, 16, 36, 72, 0,			/* IBM 370 Assembler */
'a', '2', 0, 1, 10, 16, 40, 72, 0,		/* IBM Assembler alternative */
'c', 0, 1, 8, 12, 16, 20, 55, 0,		/* COBOL, normal */
'c', '2', 0, 1, 6, 10, 14, 49, 0,		/* COBOL, crunched */
'c', '3', 0, 1, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50,
	54, 58, 62, 67, 0,
'f', 0, 1, 7, 11, 15, 19, 23, 0,		/* FORTRAN */
'p', 0, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 0,
						/* PL/I */
's', 0, 1, 10, 55, 0,    			/* SNOBOL */
'u', 0, 1, 12, 20, 44, 0, 			/* UNIVAC ASM */
0 };


/*
 *	stdtab: return tab list for any "canned" tab option.
 *		entry: option points to null-terminated option string
 *		tabvect points to vector to be filled in
 *	exit: return (0) if legal, tabvect filled, ending with zero
 *		return (-1) if unknown option
 */


static int
stdtab(char option[], char tabvect[NTABS])
{
	char *scan;
	tabvect[0] = 0;
	scan = stdtabs;
	while (*scan) {
		if (strequal(&scan, option)) {
			strcopy(scan, tabvect);
			break;
		} else
			while (*scan++);    /* skip over tab specs */
	}

/*	later: look up code in /etc/something */
	return (tabvect[0] ? 0 : -1);
}


/*
 *	strequal: checks strings for equality
 *		entry: scan1 points to scan pointer, str points to string
 *	exit: return (1) if equal, return (0) if not
 *		*scan1 is advanced to next nonzero byte after null
 */


static int
strequal(char **scan1, char *str)
{
	char c, *scan;
	scan = *scan1;
	while ((c = *scan++) == *str && c) str++;
	*scan1 = scan;
	if (c == 0 && *str == 0)
		return (1);
	if (c)
		while (*scan++);
			*scan1 = scan;
	return (0);
}


/*	strcopy: copy source to destination */


static void
strcopy(char *source, char *dest)
{
	while (*dest++ = *source++);
}


/* This is called before a buffer modifying command so that the */
/* current array of line ptrs is saved in sav and dot and dol are saved */


static void
save(void) {
	LINE i;
	int	j;

	savdot = dot;
	savdol = dol;
	for (j = 0; j <= 25; j++)
		savnames[j] = names[j];

	for (i = zero + 1; i <= dol; i++)
		i->sav = i->cur;
	initflg = 0;
}


/* The undo command calls this to restore the previous ptr array sav */
/* and swap with cur - dot and dol are swapped also. This allows user to */
/* undo an undo */


static void
undo(void) {
	int j;
	long tmp;
	LINE i, tmpdot, tmpdol;

	tmpdot = dot; dot = savdot; savdot = tmpdot;
	tmpdol = dol; dol = savdol; savdol = tmpdol;
	/* swap arrays using the greater of dol or savdol as upper limit */
	for (i = zero + 1; i <= ((dol > savdol) ? dol : savdol); i++) {
		tmp = i->cur;
		i->cur = i->sav;
		i->sav = tmp;
	}
		/*
		 * If the current text lines are swapped with the
		 * text lines in the save buffer, then swap the current
		 * marks with those in the save area.
		 */

		for (j = 0; j <= 25; j++) {
			tmp = names[j];
			names[j] = savnames[j];
			savnames[j] = tmp;
		}
}

static wchar_t
get_wchr()
{
	wchar_t	wc;
	char	multi[MB_LEN_MAX];

	if (_mbftowc(multi, &wc, getchr, &peekc) <= 0)
		wc = getchr();
	return (wc);
}