xref: /illumos-gate/usr/src/cmd/ed/ed.c (revision 501bc5c03a221e7e601a0cfa80ab0972bcf20330)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved	*/
28 
29 /*
30  * Editor
31  */
32 
33 #include	<crypt.h>
34 #include	<libgen.h>
35 #include	<wait.h>
36 #include	<string.h>
37 #include	<sys/types.h>
38 #include	<locale.h>
39 #include	<regexpr.h>
40 #include	<regex.h>
41 #include	<errno.h>
42 #include	<paths.h>
43 
44 static const	char	*msgtab[] =
45 {
46 	"write or open on pipe failed",			/*  0 */
47 	"warning: expecting `w'",			/*  1 */
48 	"mark not lower case ascii",			/*  2 */
49 	"Cannot open input file",			/*  3 */
50 	"PWB spec problem",				/*  4 */
51 	"nothing to undo",				/*  5 */
52 	"restricted shell",				/*  6 */
53 	"cannot create output file",			/*  7 */
54 	"filesystem out of space!",			/*  8 */
55 	"cannot open file",				/*  9 */
56 	"cannot link",					/* 10 */
57 	"Range endpoint too large",			/* 11 */
58 	"unknown command",				/* 12 */
59 	"search string not found",			/* 13 */
60 	"-",						/* 14 */
61 	"line out of range",				/* 15 */
62 	"bad number",					/* 16 */
63 	"bad range",					/* 17 */
64 	"Illegal address count",			/* 18 */
65 	"incomplete global expression",			/* 19 */
66 	"illegal suffix",				/* 20 */
67 	"illegal or missing filename",			/* 21 */
68 	"no space after command",			/* 22 */
69 	"fork failed - try again",			/* 23 */
70 	"maximum of 64 characters in file names",	/* 24 */
71 	"`\\digit' out of range",			/* 25 */
72 	"interrupt",					/* 26 */
73 	"line too long",				/* 27 */
74 	"illegal character in input file",		/* 28 */
75 	"write error",					/* 29 */
76 	"out of memory for append",			/* 30 */
77 	"temp file too big",				/* 31 */
78 	"I/O error on temp file",			/* 32 */
79 	"multiple globals not allowed",			/* 33 */
80 	"global too long",				/* 34 */
81 	"no match",					/* 35 */
82 	"illegal or missing delimiter",			/* 36 */
83 	"-",						/* 37 */
84 	"replacement string too long",			/* 38 */
85 	"illegal move destination",			/* 39 */
86 	"-",						/* 40 */
87 	"no remembered search string",			/* 41 */
88 	"'\\( \\)' imbalance",				/* 42 */
89 	"Too many `\\(' s",				/* 43 */
90 	"more than 2 numbers given",			/* 44 */
91 	"'\\}' expected",				/* 45 */
92 	"first number exceeds second",			/* 46 */
93 	"incomplete substitute",			/* 47 */
94 	"newline unexpected",				/* 48 */
95 	"'[ ]' imbalance",				/* 49 */
96 	"regular expression overflow",			/* 50 */
97 	"regular expression error",			/* 51 */
98 	"command expected",				/* 52 */
99 	"a, i, or c not allowed in G",			/* 53 */
100 	"end of line expected",				/* 54 */
101 	"no remembered replacement string",		/* 55 */
102 	"no remembered command",			/* 56 */
103 	"illegal redirection",				/* 57 */
104 	"possible concurrent update",			/* 58 */
105 	"-",						/* 59 */
106 	"the x command has become X (upper case)",	/* 60 */
107 	"Warning: 'w' may destroy input file "
108 	"(due to `illegal char' read earlier)",
109 							/* 61 */
110 	"Caution: 'q' may lose data in buffer;"
111 	" 'w' may destroy input file",
112 							/* 62 */
113 	"Encryption of string failed",			/* 63 */
114 	"Encryption facility not available",		/* 64 */
115 	"Cannot encrypt temporary file",		/* 65 */
116 	"Enter key:",					/* 66 */
117 	"Illegal byte sequence",			/* 67 */
118 	"File does not exist",				/* 68 */
119 	"tempnam failed",				/* 69 */
120 	"Cannot open temporary file",			/* 70 */
121 	0
122 };
123 
124 #include <stdlib.h>
125 #include <limits.h>
126 #include <stdio.h>
127 #include <signal.h>
128 #include <sys/types.h>
129 #include <sys/stat.h>
130 #include <sys/statvfs.h>
131 #include <unistd.h>
132 #include <termio.h>
133 #include <ctype.h>
134 #include <setjmp.h>
135 #include <fcntl.h>
136 #include <wchar.h>	/* I18N */
137 #include <wctype.h>	/* I18N */
138 #include <widec.h>	/* I18N */
139 
140 #define	FTYPE(A)	(A.st_mode)
141 #define	FMODE(A)	(A.st_mode)
142 #define	IDENTICAL(A, B)	(A.st_dev == B.st_dev && A.st_ino == B.st_ino)
143 #define	ISBLK(A)	((A.st_mode & S_IFMT) == S_IFBLK)
144 #define	ISCHR(A)	((A.st_mode & S_IFMT) == S_IFCHR)
145 #define	ISDIR(A)	((A.st_mode & S_IFMT) == S_IFDIR)
146 #define	ISFIFO(A)	((A.st_mode & S_IFMT) == S_IFIFO)
147 #define	ISREG(A)	((A.st_mode & S_IFMT) == S_IFREG)
148 
149 #define	PUTM()	if (xcode >= 0) puts(gettext(msgtab[xcode]))
150 #define	UNGETC(c)	(peekc = c)
151 #define	FNSIZE	PATH_MAX
152 #define	LBSIZE	LINE_MAX
153 
154 /* size of substitution replacement pattern buffer */
155 #define	RHSIZE	(LINE_MAX*2)
156 
157 #define	KSIZE	8
158 
159 #define	READ	0
160 #define	WRITE	1
161 
162 extern  char	*optarg;	/* Value of argument */
163 extern  int	optind;		/* Indicator of argument */
164 
165 struct  Fspec   {
166 	char    Ftabs[22];
167 	char    Fdel;
168 	unsigned char   Flim;
169 	char    Fmov;
170 	char    Ffill;
171 };
172 static struct  Fspec   fss;
173 
174 static char	*fsp;
175 static int	fsprtn;
176 static char	line[70];
177 static char	*linp = line;
178 static int	sig;
179 static int	Xqt = 0;
180 static int	lastc;
181 static char	savedfile[FNSIZE];
182 static char	file[FNSIZE];
183 static char	funny[FNSIZE];
184 static int	funlink = 0;
185 static char	linebuf[LBSIZE];
186 static char	*tstring = linebuf;
187 
188 static char *expbuf;
189 
190 static char	rhsbuf[RHSIZE];
191 struct	lin	{
192 	long cur;
193 	long sav;
194 };
195 typedef struct lin *LINE;
196 static LINE	zero;
197 static LINE	dot;
198 static LINE	dol;
199 static LINE	endcore;
200 static LINE	fendcore;
201 static LINE	addr1;
202 static LINE	addr2;
203 static LINE	savdol, savdot;
204 static int	globflg;
205 static int	initflg;
206 static char	genbuf[LBSIZE];
207 static long	count;
208 static int	numpass;	/* Number of passes thru dosub(). */
209 static int	gsubf;		/* Occurrence value. LBSIZE-1=all. */
210 static int	ocerr1;	/* Allows lines NOT changed by dosub() to NOT be put */
211 			/* out. Retains last line changed as current line. */
212 static int	ocerr2;	/* Flags if ANY line changed by substitute(). 0=nc. */
213 static char	*nextip;
214 static char	*linebp;
215 static int	ninbuf;
216 static int	peekc;
217 static int	io;
218 static void	(*oldhup)(), (*oldintr)();
219 static void	(*oldquit)(), (*oldpipe)();
220 static void	quit(int) __NORETURN;
221 static int	vflag = 1;
222 static int	xflag;
223 static int	xtflag;
224 static int	kflag;
225 static int	crflag;
226 		/* Flag for determining if file being read is encrypted */
227 static int	hflag;
228 static int	xcode = -1;
229 static char	crbuf[LBSIZE];
230 static	int	perm[2];
231 static int	tperm[2];
232 static int	permflag;
233 static int	tpermflag;
234 static int	col;
235 static char	*globp;
236 static int	tfile = -1;
237 static int	tline;
238 static char	*tfname;
239 extern char	*locs;
240 static char	ibuff[LBSIZE];
241 static int	iblock = -1;
242 static char	obuff[LBSIZE];
243 static int	oblock = -1;
244 static int	ichanged;
245 static int	nleft;
246 static long	savnames[26], names[26];
247 static int	anymarks;
248 static long	subnewa;
249 static int	fchange;
250 static int	nline;
251 static int	fflg, shflg;
252 static char	prompt[16] = "*";
253 static int	rflg;
254 static int	readflg;
255 static int	eflg;
256 static int	qflg = 0;
257 static int	ncflg;
258 static int	listn;
259 static int	listf;
260 static int	pflag;
261 static int	flag28 = 0; /* Prevents write after a partial read */
262 static int	save28 = 0; /* Flag whether buffer empty at start of read */
263 static long	savtime;
264 static char	*name = "SHELL";
265 static char	*rshell = "/usr/lib/rsh";
266 static char	*val;
267 static char	*home;
268 static int	nodelim;
269 
270 int	makekey(int *);
271 int	_mbftowc(char *, wchar_t *, int (*)(), int *);
272 static int	error(int code);
273 static void	tlist(struct Fspec *);
274 static void	tstd(struct Fspec *);
275 static void	gdelete(void);
276 static void	delete(void);
277 static void	exfile(void);
278 static void	filename(int comm);
279 static void	newline(void);
280 static int	gettty(void);
281 static void	commands(void);
282 static void	undo(void);
283 static void	save(void);
284 static void	strcopy(char *source, char *dest);
285 static int	strequal(char **scan1, char *str);
286 static int	stdtab(char *, char *);
287 static int	lenchk(char *, struct Fspec *);
288 static void	clear(struct Fspec *);
289 static int	expnd(char *, char *, int *, struct Fspec *);
290 static void	tincr(int, struct Fspec *);
291 static void	targ(struct Fspec *);
292 static int	numb(void);
293 static int	fspec(char *, struct Fspec *, int);
294 static void	red(char *);
295 static void	newtime(void);
296 static void	chktime(void);
297 static void	getime(void);
298 static void	mkfunny(void);
299 static int	eopen(char *, int);
300 static void	eclose(int f);
301 static void	globaln(int);
302 static char	*getkey(const char *);
303 static int	execute(int, LINE);
304 static void	error1(int);
305 static int	getcopy(void);
306 static void	move(int);
307 static void	dosub(void);
308 static int	getsub(void);
309 static int	compsub(void);
310 static void	substitute(int);
311 static void	join(void);
312 static void	global(int);
313 static void	init(void);
314 static void	rdelete(LINE, LINE);
315 static void	append(int (*)(void), LINE);
316 static int	getfile(void);
317 static void	putfile(void);
318 static void	onpipe(int);
319 static void	onhup(int);
320 static void	onintr(int);
321 static void	setdot(void);
322 static void	setall(void);
323 static void	setnoaddr(void);
324 static void	nonzero(void);
325 static void	setzeroasone(void);
326 static long	putline(void);
327 static LINE	address(void);
328 static char	*getaline(long);
329 static char	*getblock(long, long);
330 static char	*place(char *, char *, char *);
331 static void	comple(wchar_t);
332 static void	putchr(unsigned char);
333 static void	putwchr(wchar_t);
334 static int	getchr(void);
335 static void	unixcom(void);
336 static void	blkio(int, char *, ssize_t (*)());
337 static void	reverse(LINE, LINE);
338 static void	putd();
339 static wchar_t	get_wchr(void);
340 
341 static struct stat	Fl, Tf;
342 #ifndef RESEARCH
343 static struct statvfs	U;
344 static int	Short = 0;
345 static mode_t	oldmask; /* No umask while writing */
346 #endif
347 static jmp_buf	savej;
348 
349 #ifdef	NULLS
350 int	nulls;	/* Null count */
351 #endif
352 static long	ccount;
353 
354 static int	errcnt = 0;
355 
356 
357 static void
358 onpipe(int sig)
359 {
360 	(int)error(0);
361 }
362 
363 int
364 main(int argc, char **argv)
365 {
366 	char *p1, *p2;
367 	int c;
368 
369 	(void) setlocale(LC_ALL, "");
370 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
371 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
372 #endif
373 	(void) textdomain(TEXT_DOMAIN);
374 
375 	oldquit = signal(SIGQUIT, SIG_IGN);
376 	oldhup = signal(SIGHUP, SIG_IGN);
377 	oldintr = signal(SIGINT, SIG_IGN);
378 	oldpipe = signal(SIGPIPE, onpipe);
379 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
380 		signal(SIGTERM, quit);
381 	p1 = *argv;
382 	while (*p1++)
383 		;
384 	while (--p1 >= *argv)
385 		if (*p1 == '/')
386 			break;
387 	*argv = p1 + 1;
388 	/* if SHELL set in environment and is /usr/lib/rsh, set rflg */
389 	if ((val = getenv(name)) != NULL)
390 		if (strcmp(val, rshell) == 0)
391 			rflg++;
392 	if (**argv == 'r')
393 		rflg++;
394 	home = getenv("HOME");
395 	while (1) {
396 		while ((c = getopt(argc, argv, "sp:qxC")) != EOF) {
397 			switch (c) {
398 
399 			case 's':
400 				vflag = 0;
401 				break;
402 
403 			case 'p':
404 				strncpy(prompt, optarg, sizeof (prompt)-1);
405 				shflg = 1;
406 				break;
407 
408 			case 'q':
409 				signal(SIGQUIT, SIG_DFL);
410 				vflag = 1;
411 				break;
412 
413 			case 'x':
414 				crflag = -1;
415 				xflag = 1;
416 				break;
417 
418 			case 'C':
419 				crflag = 1;
420 				xflag = 1;
421 				break;
422 
423 			case '?':
424 				(void) fprintf(stderr, gettext(
425 		"Usage:   ed [- | -s] [-p string] [-x] [-C] [file]\n"
426 		"	  red [- | -s] [-p string] [-x] [-C] [file]\n"));
427 				exit(2);
428 			}
429 		}
430 		if (argv[optind] && strcmp(argv[optind], "-") == 0 &&
431 		    strcmp(argv[optind-1], "--") != 0) {
432 			vflag = 0;
433 			optind++;
434 			continue;
435 		}
436 		break;
437 	}
438 	argc = argc - optind;
439 	argv = &argv[optind];
440 
441 	if (xflag) {
442 		if (permflag)
443 			crypt_close(perm);
444 		permflag = 1;
445 		kflag = run_setkey(&perm[0], getkey(msgtab[66]));
446 		if (kflag == -1) {
447 			puts(gettext(msgtab[64]));
448 			xflag = 0;
449 			kflag = 0;
450 		}
451 		if (kflag == 0)
452 			crflag = 0;
453 	}
454 
455 	if (argc > 0) {
456 		p1 = *argv;
457 		if (strlen(p1) >= (size_t)FNSIZE) {
458 			puts(gettext("file name too long"));
459 			if (kflag)
460 				crypt_close(perm);
461 			exit(2);
462 		}
463 		p2 = savedfile;
464 		while (*p2++ = *p1++)
465 			;
466 		globp = "e";
467 		fflg++;
468 	} else	/* editing with no file so set savtime to 0 */
469 		savtime = 0;
470 	eflg++;
471 	if ((tfname = tempnam("", "ea")) == NULL) {
472 		puts(gettext(msgtab[69]));
473 		exit(2);
474 	}
475 
476 	fendcore = (LINE)sbrk(0);
477 	init();
478 	if (oldintr != SIG_IGN)
479 		signal(SIGINT, onintr);
480 	if (oldhup != SIG_IGN)
481 		signal(SIGHUP, onhup);
482 	setjmp(savej);
483 	commands();
484 	quit(sig);
485 	return (0);
486 }
487 
488 static void
489 commands(void)
490 {
491 	LINE a1;
492 	int c;
493 	char *p1, *p2;
494 	int fsave, m, n;
495 
496 	for (;;) {
497 	nodelim = 0;
498 	if (pflag) {
499 		pflag = 0;
500 		addr1 = addr2 = dot;
501 		goto print;
502 	}
503 	if (shflg && globp == 0)
504 		write(1, gettext(prompt), strlen(gettext(prompt)));
505 	addr1 = 0;
506 	addr2 = 0;
507 	if ((c = getchr()) == ',') {
508 		addr1 = zero + 1;
509 		addr2 = dol;
510 #ifdef XPG6
511 	/* XPG4 - it was an error if the second address was */
512 	/* input and the first address was ommitted	*/
513 	/* Parse second address	*/
514 		if ((a1 = address()) != 0) {
515 			addr2 = a1;
516 		}
517 #endif
518 		c = getchr();
519 		goto swch;
520 	} else if (c == ';') {
521 		addr1 = dot;
522 		addr2 = dol;
523 #ifdef XPG6
524 	/* XPG4 - it was an error if the second address was */
525 	/* input and the first address was ommitted	*/
526 	/* Parse second address	*/
527 		if ((a1 = address()) != 0) {
528 			addr2 = a1;
529 		}
530 #endif
531 		c = getchr();
532 		goto swch;
533 	} else
534 		peekc = c;
535 	do {
536 		addr1 = addr2;
537 		if ((a1 = address()) == 0) {
538 			c = getchr();
539 			break;
540 		}
541 		addr2 = a1;
542 		if ((c = getchr()) == ';') {
543 			c = ',';
544 			dot = a1;
545 		}
546 	} while (c == ',');
547 	if (addr1 == 0)
548 		addr1 = addr2;
549 swch:
550 	switch (c) {
551 
552 	case 'a':
553 		setdot();
554 		newline();
555 		if (!globflg) save();
556 		append(gettty, addr2);
557 		continue;
558 
559 	case 'c':
560 #ifdef XPG6
561 		setzeroasone();
562 #endif
563 		delete();
564 		append(gettty, addr1-1);
565 
566 		/* XPG4 - If no new lines are inserted, then the current */
567 		/* line becomes the line after the lines deleted. */
568 
569 		if (((linebuf[0] != '.') || (dot == (addr1-1))) &&
570 		    (addr2 <= dol))
571 			dot = addr1;
572 		continue;
573 
574 	case 'd':
575 		delete();
576 		continue;
577 
578 	case 'E':
579 		fchange = 0;
580 		c = 'e';
581 		/* FALLTHROUGH */
582 	case 'e':
583 		fflg++;
584 		setnoaddr();
585 		if (vflag && fchange) {
586 			fchange = 0;
587 			(void) error(1);
588 		}
589 		filename(c);
590 		eflg++;
591 		init();
592 		addr2 = zero;
593 		goto caseread;
594 
595 	case 'f':
596 		setnoaddr();
597 		filename(c);
598 		if (!ncflg)  /* there is a filename */
599 			getime();
600 		else
601 			ncflg--;
602 		puts(savedfile);
603 		continue;
604 
605 	case 'g':
606 		global(1);
607 		continue;
608 	case 'G':
609 		globaln(1);
610 		continue;
611 
612 	case 'h':
613 		newline();
614 		setnoaddr();
615 		PUTM();
616 		continue;
617 
618 	case 'H':
619 		newline();
620 		setnoaddr();
621 		if (!hflag) {
622 			hflag = 1;
623 			PUTM();
624 		}
625 		else
626 			hflag = 0;
627 		continue;
628 
629 	case 'i':
630 #ifdef XPG6
631 		setzeroasone();
632 #endif
633 		setdot();
634 		nonzero();
635 		newline();
636 		if (!globflg) save();
637 		append(gettty, addr2-1);
638 		if (dot == addr2-1)
639 			dot += 1;
640 		continue;
641 
642 	case 'j':
643 		if (addr2 == 0) {
644 			addr1 = dot;
645 			addr2 = dot+1;
646 		}
647 		setdot();
648 		newline();
649 		nonzero();
650 		if (!globflg) save();
651 		join();
652 		continue;
653 
654 	case 'k':
655 		if ((c = getchr()) < 'a' || c > 'z')
656 			(void) error(2);
657 		newline();
658 		setdot();
659 		nonzero();
660 		names[c-'a'] = addr2->cur & ~01;
661 		anymarks |= 01;
662 		continue;
663 
664 	case 'm':
665 		move(0);
666 		continue;
667 
668 	case '\n':
669 		if (addr2 == 0)
670 			addr2 = dot+1;
671 		addr1 = addr2;
672 		goto print;
673 
674 	case 'n':
675 		listn++;
676 		newline();
677 		goto print;
678 
679 	case 'l':
680 		listf++;
681 		/* FALLTHROUGH */
682 	case 'p':
683 		newline();
684 	print:
685 		setdot();
686 		nonzero();
687 		a1 = addr1;
688 		do {
689 			if (listn) {
690 				count = a1 - zero;
691 				putd();
692 				putchr('\t');
693 			}
694 			puts(getaline((a1++)->cur));
695 		} while (a1 <= addr2);
696 		dot = addr2;
697 		pflag = 0;
698 		listn = 0;
699 		listf = 0;
700 		continue;
701 
702 	case 'Q':
703 		fchange = 0;
704 		/* FALLTHROUGH */
705 	case 'q':
706 		setnoaddr();
707 		newline();
708 		quit(sig);
709 
710 	case 'r':
711 		filename(c);
712 	caseread:
713 		readflg = 1;
714 		save28 = (dol != fendcore);
715 		if (crflag == 2 || crflag == -2)
716 			crflag = -1; /* restore crflag for next file */
717 		errno = 0;
718 		if ((io = eopen(file, O_RDONLY)) < 0) {
719 			lastc = '\n';
720 			/* if first entering editor and file does not exist */
721 			/* set saved access time to 0 */
722 			if (eflg) {
723 				savtime = 0;
724 				eflg  = 0;
725 				if (c == 'e' && vflag == 0)
726 					qflg = 1;
727 			}
728 			if (errno == ENOENT) {
729 				(void) error(68);
730 			} else {
731 				(void) error(3);
732 			}
733 		}
734 		/* get last mod time of file */
735 		/* eflg - entered editor with ed or e  */
736 		if (eflg) {
737 			eflg = 0;
738 			getime();
739 		}
740 		setall();
741 		ninbuf = 0;
742 		n = zero != dol;
743 #ifdef NULLS
744 		nulls = 0;
745 #endif
746 		if (!globflg && (c == 'r')) save();
747 		append(getfile, addr2);
748 		exfile();
749 		readflg = 0;
750 		fchange = n;
751 		continue;
752 
753 	case 's':
754 		setdot();
755 		nonzero();
756 		if (!globflg) save();
757 		substitute(globp != 0);
758 		continue;
759 
760 	case 't':
761 		move(1);
762 		continue;
763 
764 	case 'u':
765 		setdot();
766 		newline();
767 		if (!initflg)
768 			undo();
769 		else
770 			(void) error(5);
771 		fchange = 1;
772 		continue;
773 
774 	case 'v':
775 		global(0);
776 		continue;
777 	case 'V':
778 		globaln(0);
779 		continue;
780 
781 	case 'W':
782 	case 'w':
783 		if (flag28) {
784 			flag28 = 0;
785 			fchange = 0;
786 			(void) error(61);
787 		}
788 		setall();
789 
790 		/* on NULL-RE condition do not generate error */
791 
792 		if ((linebuf[0] != '.') && (zero != dol) &&
793 		    (addr1 <= zero || addr2 > dol))
794 			(void) error(15);
795 		filename(c);
796 		if (Xqt) {
797 			io = eopen(file, O_WRONLY);
798 			n = 1;	/* set n so newtime will not execute */
799 		} else {
800 			struct stat lFl;
801 			fstat(tfile, &Tf);
802 			if (stat(file, &Fl) < 0) {
803 				if ((io = creat(file, S_IRUSR|S_IWUSR|S_IRGRP
804 				    |S_IWGRP|S_IROTH|S_IWOTH)) < 0)
805 					(void) error(7);
806 				fstat(io, &Fl);
807 				Fl.st_mtime = 0;
808 				lFl = Fl;
809 				close(io);
810 			} else {
811 #ifndef	RESEARCH
812 				oldmask = umask(0);
813 				/*
814 				 * Must determine if file is
815 				 * a symbolic link
816 				 */
817 				lstat(file, &lFl);
818 #endif
819 			}
820 #ifndef RESEARCH
821 			/*
822 			 * Determine if there are enough free blocks on system
823 			 */
824 			if (!Short && statvfs(file, &U) == 0 &&
825 			    U.f_bfree < ((Tf.st_size/U.f_frsize) + 100)) {
826 				Short = 1;
827 				(void) error(8);
828 			}
829 			Short = 0;
830 #endif
831 			p1 = savedfile;		/* The current filename */
832 			p2 = file;
833 			m = strcmp(p1, p2);
834 			if (c == 'w' && Fl.st_nlink == 1 && ISREG(lFl)) {
835 				if (close(open(file, O_WRONLY)) < 0)
836 					(void) error(9);
837 				if (!(n = m))
838 					chktime();
839 				mkfunny();
840 				/*
841 				 * If funlink equals one it means that
842 				 * funny points to a valid file which must
843 				 * be unlinked when interrupted.
844 				 */
845 
846 				funlink = 1;
847 				if ((io = creat(funny, FMODE(Fl))) >= 0) {
848 					chown(funny, Fl.st_uid, Fl.st_gid);
849 					chmod(funny, FMODE(Fl));
850 					putfile();
851 					exfile();
852 
853 					if (rename(funny, file))
854 						(void) error(10);
855 					funlink = 0;
856 					/* if filenames are the same */
857 					if (!n)
858 						newtime();
859 					/* check if entire buffer was written */
860 					fsave = fchange;
861 					if (((addr1 == zero) ||
862 					    (addr1 == (zero + 1))) &&
863 					    (addr2 == dol))
864 						fchange = 0;
865 					else
866 						fchange = 1;
867 					if (fchange == 1 && m != 0)
868 						fchange = fsave;
869 					continue;
870 				}
871 			} else {
872 				n = 1;	/* set n so newtime will not execute */
873 			}
874 			if ((io = open(file,
875 			    (c == 'w') ? O_WRONLY|O_CREAT|O_TRUNC
876 			    : O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR
877 			    |S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0)
878 				(void) error(7);
879 		}
880 		putfile();
881 		exfile();
882 		if (!n)
883 			newtime();
884 		fsave = fchange;
885 		fchange = (((addr1 == zero) || (addr1 == (zero + 1))) &&
886 		    (addr2 == dol)) ? 0 : 1;
887 	/* Leave fchange alone if partial write was to another file */
888 		if (fchange == 1 && m != 0) fchange = fsave;
889 		continue;
890 
891 	case 'C':
892 		crflag = 1;
893 			/*
894 			 * C is same as X, but always assume input files are
895 			 * ciphertext
896 			 */
897 		goto encrypt;
898 
899 	case 'X':
900 		crflag = -1;
901 encrypt:
902 		setnoaddr();
903 		newline();
904 		xflag = 1;
905 		if (permflag)
906 			(void) crypt_close(perm);
907 		permflag = 1;
908 		if ((kflag = run_setkey(&perm[0], getkey(msgtab[66]))) == -1) {
909 			xflag = 0;
910 			kflag = 0;
911 			crflag = 0;
912 			(void) error(64);
913 		}
914 		if (kflag == 0)
915 			crflag = 0;
916 		continue;
917 
918 	case '=':
919 		setall();
920 		newline();
921 		count = (addr2-zero)&077777;
922 		putd();
923 		putchr('\n');
924 		continue;
925 
926 	case '!':
927 		unixcom();
928 		continue;
929 
930 	case EOF:
931 		return;
932 
933 	case 'P':
934 		setnoaddr();
935 		newline();
936 		if (shflg)
937 			shflg = 0;
938 		else
939 			shflg++;
940 		continue;
941 	}
942 	if (c == 'x')
943 		(void) error(60);
944 	else
945 		(void) error(12);
946 	}
947 }
948 
949 LINE
950 address(void)
951 {
952 	int minus, c;
953 	LINE a1;
954 	int n, relerr, retval;
955 
956 	minus = 0;
957 	a1 = 0;
958 	for (;;) {
959 		c = getchr();
960 		if ('0' <= c && c <= '9') {
961 			n = 0;
962 			do {
963 				n *= 10;
964 				n += c - '0';
965 			} while ((c = getchr()) >= '0' && c <= '9');
966 			peekc = c;
967 			if (a1 == 0)
968 				a1 = zero;
969 			if (minus < 0)
970 				n = -n;
971 			a1 += n;
972 			minus = 0;
973 			continue;
974 		}
975 		relerr = 0;
976 		if (a1 || minus)
977 			relerr++;
978 		switch (c) {
979 		case ' ':
980 		case '\t':
981 			continue;
982 
983 		case '+':
984 			minus++;
985 			if (a1 == 0)
986 				a1 = dot;
987 			continue;
988 
989 		case '-':
990 		case '^':
991 			minus--;
992 			if (a1 == 0)
993 				a1 = dot;
994 			continue;
995 
996 		case '?':
997 		case '/':
998 			comple(c);
999 			a1 = dot;
1000 			for (;;) {
1001 				if (c == '/') {
1002 					a1++;
1003 					if (a1 > dol)
1004 						a1 = zero;
1005 				} else {
1006 					a1--;
1007 					if (a1 < zero)
1008 						a1 = dol;
1009 				}
1010 
1011 				if (execute(0, a1))
1012 					break;
1013 				if (a1 == dot)
1014 					(void) error(13);
1015 			}
1016 			break;
1017 
1018 		case '$':
1019 			a1 = dol;
1020 			break;
1021 
1022 		case '.':
1023 			a1 = dot;
1024 			break;
1025 
1026 		case '\'':
1027 			if ((c = getchr()) < 'a' || c > 'z')
1028 				(void) error(2);
1029 			for (a1 = zero; a1 <= dol; a1++)
1030 				if (names[c-'a'] == (a1->cur & ~01))
1031 					break;
1032 			break;
1033 
1034 		default:
1035 			peekc = c;
1036 			if (a1 == 0)
1037 				return (0);
1038 			a1 += minus;
1039 
1040 			/* on NULL-RE condition do not generate error */
1041 
1042 			if ((linebuf[0] != '.') && (a1 < zero || a1 > dol))
1043 				(void) error(15);
1044 			return (a1);
1045 		}
1046 		if (relerr)
1047 			(void) error(16);
1048 	}
1049 }
1050 
1051 static void
1052 setdot(void)
1053 {
1054 	if (addr2 == 0)
1055 		addr1 = addr2 = dot;
1056 	if (addr1 > addr2)
1057 		(void) error(17);
1058 }
1059 
1060 static void
1061 setall(void)
1062 {
1063 	if (addr2 == 0) {
1064 		addr1 = zero+1;
1065 		addr2 = dol;
1066 		if (dol == zero)
1067 			addr1 = zero;
1068 	}
1069 	setdot();
1070 }
1071 
1072 static void
1073 setnoaddr(void)
1074 {
1075 	if (addr2)
1076 		(void) error(18);
1077 }
1078 
1079 static void
1080 nonzero(void)
1081 {
1082 	/* on NULL-RE condition do not generate error */
1083 
1084 	if ((linebuf[0] != '.') && (addr1 <= zero || addr2 > dol))
1085 		(void) error(15);
1086 }
1087 
1088 static void
1089 setzeroasone(void)
1090 {
1091 /* for the c and i commands 0 equal to 1 address */
1092 	if (addr1 == zero) {
1093 		addr1 = zero+1;
1094 	}
1095 	if (addr2 == zero) {
1096 		addr2 = zero+1;
1097 	}
1098 }
1099 
1100 
1101 static void
1102 newline(void)
1103 {
1104 	int c;
1105 
1106 	if ((c = getchr()) == '\n')
1107 		return;
1108 	if (c == 'p' || c == 'l' || c == 'n') {
1109 		pflag++;
1110 		if (c == 'l') listf++;
1111 		if (c == 'n') listn++;
1112 		if ((c = getchr()) == '\n')
1113 			return;
1114 	}
1115 	(void) error(20);
1116 }
1117 
1118 static void
1119 filename(int comm)
1120 {
1121 	char *p1, *p2;
1122 	int c;
1123 	int i = 0;
1124 
1125 	count = 0;
1126 	c = getchr();
1127 	if (c == '\n' || c == EOF) {
1128 		p1 = savedfile;
1129 		if (*p1 == 0 && comm != 'f')
1130 			(void) error(21);
1131 		/* ncflg set means do not get mod time of file */
1132 		/* since no filename followed f */
1133 		if (comm == 'f')
1134 			ncflg++;
1135 		p2 = file;
1136 		while (*p2++ = *p1++)
1137 			;
1138 		red(savedfile);
1139 		return;
1140 	}
1141 	if (c != ' ')
1142 		(void) error(22);
1143 	while ((c = getchr()) == ' ')
1144 		;
1145 	if (c == '!')
1146 		++Xqt, c = getchr();
1147 	if (c == '\n')
1148 		(void) error(21);
1149 	p1 = file;
1150 	do {
1151 		if (++i >= FNSIZE)
1152 			(void) error(24);
1153 		*p1++ = c;
1154 		if (c == EOF || (c == ' ' && !Xqt))
1155 			(void) error(21);
1156 	} while ((c = getchr()) != '\n');
1157 	*p1++ = 0;
1158 	if (Xqt)
1159 		if (comm == 'f') {
1160 			--Xqt;
1161 			(void) error(57);
1162 		}
1163 		else
1164 			return;
1165 	if (savedfile[0] == 0 || comm == 'e' || comm == 'f') {
1166 		p1 = savedfile;
1167 		p2 = file;
1168 		while (*p1++ = *p2++)
1169 			;
1170 	}
1171 	red(file);
1172 }
1173 
1174 
1175 static void
1176 exfile(void)
1177 {
1178 #ifdef NULLS
1179 	int c;
1180 #endif
1181 
1182 #ifndef RESEARCH
1183 	if (oldmask) {
1184 		umask(oldmask);
1185 		oldmask = 0;
1186 	}
1187 #endif
1188 	eclose(io);
1189 	io = -1;
1190 	if (vflag) {
1191 		putd();
1192 		putchr('\n');
1193 #ifdef NULLS
1194 		if (nulls) {
1195 			c = count;
1196 			count = nulls;
1197 			nulls = 0;
1198 			putd();
1199 			puts(gettext(" nulls replaced by '\\0'"));
1200 			count = c;
1201 		}
1202 #endif
1203 	}
1204 }
1205 
1206 static void
1207 onintr(int sig)
1208 {
1209 	signal(SIGINT, onintr);
1210 	putchr('\n');
1211 	lastc = '\n';
1212 	globflg = 0;
1213 	if (funlink) unlink(funny); /* remove tmp file */
1214 	/* if interrupted a read, only part of file may be in buffer */
1215 	if (readflg) {
1216 		sprintf(tstring, "\007read may be incomplete - beware!\007");
1217 		puts(gettext(tstring));
1218 		fchange = 0;
1219 	}
1220 	(void) error(26);
1221 }
1222 
1223 static void
1224 onhup(int sig)
1225 {
1226 	signal(SIGINT, SIG_IGN);
1227 	signal(SIGHUP, SIG_IGN);
1228 	/*
1229 	 * if there are lines in file and file was not written
1230 	 * since last update, save in ed.hup, or $HOME/ed.hup
1231 	 */
1232 	if (dol > zero && fchange == 1) {
1233 		addr1 = zero+1;
1234 		addr2 = dol;
1235 		io = creat("ed.hup",
1236 		    S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
1237 		if (io < 0 && home) {
1238 			char	*fn;
1239 
1240 			fn = (char *)calloc(strlen(home) + 8, sizeof (char));
1241 			if (fn) {
1242 				strcpy(fn, home);
1243 				strcat(fn, "/ed.hup");
1244 				io = creat(fn, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
1245 				    |S_IROTH|S_IWOTH);
1246 				free(fn);
1247 			}
1248 		}
1249 		if (io > 0)
1250 			putfile();
1251 	}
1252 	fchange = 0;
1253 	++errcnt;
1254 	quit(sig);
1255 }
1256 
1257 static int
1258 error(int code)
1259 {
1260 	int c;
1261 
1262 	if (code == 28 && save28 == 0) {
1263 		fchange = 0;
1264 		flag28++;
1265 	}
1266 	readflg = 0;
1267 	++errcnt;
1268 	listf = listn = 0;
1269 	pflag = 0;
1270 #ifndef RESEARCH
1271 	if (oldmask) {
1272 		umask(oldmask);
1273 		oldmask = 0;
1274 	}
1275 #endif
1276 #ifdef NULLS	/* Not really nulls, but close enough */
1277 	/* This is a bug because of buffering */
1278 	if (code == 28) /* illegal char. */
1279 		putd();
1280 #endif
1281 	/* Cant open file or file does not exist */
1282 	if ((code == 3) || (code == 68)) {
1283 		if (qflg == 0) {
1284 			putchr('?');
1285 			puts(file);
1286 		}
1287 		else
1288 			qflg = 0;
1289 	}
1290 	else
1291 	{
1292 		putchr('?');
1293 		putchr('\n');
1294 	}
1295 	count = 0;
1296 	lseek(0, (long)0, 2);
1297 	if (globp)
1298 		lastc = '\n';
1299 	globp = 0;
1300 	peekc = lastc;
1301 	if (lastc)
1302 		while ((c = getchr()) != '\n' && c != EOF)
1303 			;
1304 	if (io) {
1305 		eclose(io);
1306 		io = -1;
1307 	}
1308 	xcode = code;
1309 	if (hflag)
1310 		PUTM();
1311 	if (code == 4)
1312 		return (0);	/* Non-fatal error. */
1313 	longjmp(savej, 1);
1314 	/* NOTREACHED */
1315 }
1316 
1317 static int
1318 getchr(void)
1319 {
1320 	char c;
1321 	if (lastc = peekc) {
1322 		peekc = 0;
1323 		return (lastc);
1324 	}
1325 	if (globp) {
1326 		if ((lastc = (unsigned char)*globp++) != 0)
1327 			return (lastc);
1328 		globp = 0;
1329 		return (EOF);
1330 	}
1331 	if (read(0, &c, 1) <= 0)
1332 		return (lastc = EOF);
1333 	lastc = (unsigned char)c;
1334 	return (lastc);
1335 }
1336 
1337 static int
1338 gettty(void)
1339 {
1340 	int c;
1341 	char *gf;
1342 	char *p;
1343 
1344 	p = linebuf;
1345 	gf = globp;
1346 	while ((c = getchr()) != '\n') {
1347 		if (c == EOF) {
1348 			if (gf)
1349 				peekc = c;
1350 			return (c);
1351 		}
1352 		if (c == 0)
1353 			continue;
1354 		*p++ = c;
1355 
1356 		if (p > &linebuf[LBSIZE-1])
1357 			(void) error(27);
1358 	}
1359 	*p++ = 0;
1360 	if (linebuf[0] == '.' && linebuf[1] == 0)
1361 		return (EOF);
1362 
1363 	/*
1364 	 * POSIX.2/XPG4 explicitly says no to this:
1365 	 *
1366 	 * in Solaris backslash followed by special character "." is
1367 	 * special character "." itself; (so terminating input mode can be
1368 	 * "\.\n").
1369 	 *
1370 	 * however, POSIX2/XPG4 says, input mode is terminated by
1371 	 * entering line consisting of only 2 characters: ".\n"
1372 	 *
1373 	 * if (linebuf[0]=='\\' && linebuf[1]=='.' && linebuf[2]==0) {
1374 	 *	linebuf[0] = '.';
1375 	 *	linebuf[1] = 0;
1376 	 * }
1377 	 */
1378 	return (0);
1379 }
1380 
1381 static int
1382 getfile(void)
1383 {
1384 	char c;
1385 	char *lp, *fp;
1386 
1387 	lp = linebuf;
1388 	fp = nextip;
1389 	do {
1390 		if (--ninbuf < 0) {
1391 			if ((ninbuf = read(io, genbuf, LBSIZE)-1) < 0)
1392 				if (lp > linebuf) {
1393 					puts(gettext("'\\n' appended"));
1394 					*genbuf = '\n';
1395 				}
1396 				else
1397 					return (EOF);
1398 			if (crflag == -1) {
1399 				if (isencrypt(genbuf, ninbuf + 1))
1400 					crflag = 2;
1401 				else
1402 					crflag = -2;
1403 			}
1404 			fp = genbuf;
1405 			if (crflag > 0)
1406 			if (run_crypt(count, genbuf, ninbuf+1, perm) == -1)
1407 				(void) error(63);
1408 		}
1409 		if (lp >= &linebuf[LBSIZE]) {
1410 			lastc = '\n';
1411 			(void) error(27);
1412 		}
1413 		if ((*lp++ = c = *fp++) == 0) {
1414 #ifdef NULLS
1415 			lp[-1] = '\\';
1416 			*lp++ = '0';
1417 			nulls++;
1418 #else
1419 			lp--;
1420 			continue;
1421 #endif
1422 		}
1423 		count++;
1424 	} while (c != '\n');
1425 	*--lp = 0;
1426 	nextip = fp;
1427 	if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) {
1428 		write(1, gettext("line too long: lno = "),
1429 		    strlen(gettext("line too long: lno = ")));
1430 		ccount = count;
1431 		count = (++dot-zero)&077777;
1432 		dot--;
1433 		putd();
1434 		count = ccount;
1435 		putchr('\n');
1436 	}
1437 	return (0);
1438 }
1439 
1440 static void
1441 putfile(void)
1442 {
1443 	int n;
1444 	LINE a1;
1445 	char *fp, *lp;
1446 	int nib;
1447 
1448 	nib = LBSIZE;
1449 	fp = genbuf;
1450 	a1 = addr1;
1451 	do {
1452 		lp = getaline(a1++->cur);
1453 		if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) {
1454 			write(1, gettext("line too long: lno = "),
1455 			    strlen(gettext("line too long: lno = ")));
1456 			ccount = count;
1457 			count = (a1-zero-1)&077777;
1458 			putd();
1459 			count = ccount;
1460 			putchr('\n');
1461 		}
1462 		for (;;) {
1463 			if (--nib < 0) {
1464 				n = fp-genbuf;
1465 				if (kflag)
1466 				if (run_crypt(count-n, genbuf, n, perm) == -1)
1467 					(void) error(63);
1468 				if (write(io, genbuf, n) != n)
1469 					(void) error(29);
1470 				nib = LBSIZE - 1;
1471 				fp = genbuf;
1472 			}
1473 			if (dol->cur == 0L)break; /* Allow write of null file */
1474 			count++;
1475 			if ((*fp++ = *lp++) == 0) {
1476 				fp[-1] = '\n';
1477 				break;
1478 			}
1479 		}
1480 	} while (a1 <= addr2);
1481 	n = fp-genbuf;
1482 	if (kflag)
1483 		if (run_crypt(count-n, genbuf, n, perm) == -1)
1484 			(void) error(63);
1485 	if (write(io, genbuf, n) != n)
1486 		(void) error(29);
1487 }
1488 
1489 static void
1490 append(int (*f)(void), LINE a)
1491 {
1492 	LINE a1, a2, rdot;
1493 	long tl;
1494 
1495 	nline = 0;
1496 	dot = a;
1497 	while ((*f)() == 0) {
1498 		if (dol >= endcore) {
1499 			if ((int)sbrk(512 * sizeof (struct lin)) == -1) {
1500 				lastc = '\n';
1501 				(void) error(30);
1502 			}
1503 			endcore += 512;
1504 		}
1505 		tl = putline();
1506 		nline++;
1507 		a1 = ++dol;
1508 		a2 = a1+1;
1509 		rdot = ++dot;
1510 		while (a1 > rdot)
1511 			(--a2)->cur = (--a1)->cur;
1512 		rdot->cur = tl;
1513 	}
1514 }
1515 
1516 static void
1517 unixcom(void)
1518 {
1519 	void (*savint)();
1520 	pid_t	pid, rpid;
1521 	int retcode;
1522 	static char savcmd[LBSIZE];	/* last command */
1523 	char curcmd[LBSIZE];		/* current command */
1524 	char *psavcmd, *pcurcmd, *psavedfile;
1525 	int endflg = 1, shflg = 0;
1526 	wchar_t	c;
1527 	int	len;
1528 
1529 	setnoaddr();
1530 	if (rflg)
1531 		(void) error(6);
1532 	pcurcmd = curcmd;
1533 	/* read command til end */
1534 
1535 	/*
1536 	 * a '!' found in beginning of command is replaced with the saved
1537 	 * command.  a '%' found in command is replaced with the current
1538 	 * filename
1539 	 */
1540 
1541 	c = getchr();
1542 	if (c == '!') {
1543 		if (savcmd[0] == 0)
1544 			(void) error(56);
1545 		else {
1546 			psavcmd = savcmd;
1547 			while (*pcurcmd++ = *psavcmd++)
1548 				;
1549 			--pcurcmd;
1550 			shflg = 1;
1551 		}
1552 	} else
1553 		UNGETC(c);  /* put c back */
1554 	while (endflg == 1) {
1555 		while ((c = get_wchr()) != '\n' && c != '%' && c != '\\') {
1556 			if ((len = wctomb(pcurcmd, c)) <= 0) {
1557 				*pcurcmd = (unsigned char)c;
1558 				len = 1;
1559 			}
1560 			pcurcmd += len;
1561 		}
1562 
1563 		if (c == '%') {
1564 			if (savedfile[0] == 0)
1565 				(void) error(21);
1566 			else {
1567 				psavedfile = savedfile;
1568 				while (pcurcmd < curcmd + LBSIZE &&
1569 				    (*pcurcmd++ = *psavedfile++))
1570 					;
1571 				--pcurcmd;
1572 				shflg = 1;
1573 			}
1574 		} else if (c == '\\') {
1575 			c = get_wchr();
1576 			if (c != '%')
1577 				*pcurcmd++ = '\\';
1578 			if ((len = wctomb(pcurcmd, c)) <= 0) {
1579 				*pcurcmd = (unsigned char)c;
1580 				len = 1;
1581 			}
1582 			pcurcmd += len;
1583 		}
1584 		else
1585 			/* end of command hit */
1586 			endflg = 0;
1587 	}
1588 	*pcurcmd++ = 0;
1589 	if (shflg == 1)
1590 		puts(curcmd);
1591 	/* save command */
1592 	strcpy(savcmd, curcmd);
1593 
1594 	if ((pid = fork()) == 0) {
1595 		signal(SIGHUP, oldhup);
1596 		signal(SIGQUIT, oldquit);
1597 		close(tfile);
1598 		execlp(_PATH_BSHELL, "sh", "-c", curcmd, NULL);
1599 		exit(0100);
1600 	}
1601 	savint = signal(SIGINT, SIG_IGN);
1602 	while ((rpid = wait(&retcode)) != pid && rpid != (pid_t)-1)
1603 		;
1604 	signal(SIGINT, savint);
1605 	if (vflag) puts("!");
1606 }
1607 
1608 static void
1609 quit(int sig)
1610 {
1611 	if (vflag && fchange) {
1612 		fchange = 0;
1613 		if (flag28) {
1614 			flag28 = 0;
1615 			(void) error(62);
1616 		}
1617 
1618 		/*
1619 		 * For case where user reads in BOTH a good
1620 		 * file & a bad file
1621 		 */
1622 		(void) error(1);
1623 	}
1624 	unlink(tfname);
1625 	if (kflag)
1626 		crypt_close(perm);
1627 	if (xtflag)
1628 		crypt_close(tperm);
1629 	exit(errcnt? 2: 0);
1630 }
1631 
1632 static void
1633 delete(void)
1634 {
1635 	setdot();
1636 	newline();
1637 	nonzero();
1638 	if (!globflg)
1639 		save();
1640 	rdelete(addr1, addr2);
1641 }
1642 
1643 static void
1644 rdelete(LINE ad1, LINE ad2)
1645 {
1646 	LINE a1, a2, a3;
1647 
1648 	a1 = ad1;
1649 	a2 = ad2+1;
1650 	a3 = dol;
1651 	dol -= a2 - a1;
1652 	do {
1653 		(a1++)->cur = (a2++)->cur;
1654 	} while (a2 <= a3);
1655 	a1 = ad1;
1656 	if (a1 > dol)
1657 		a1 = dol;
1658 	dot = a1;
1659 	fchange = 1;
1660 }
1661 
1662 static void
1663 gdelete(void)
1664 {
1665 	LINE a1, a2, a3;
1666 
1667 	a3 = dol;
1668 	for (a1 = zero+1; (a1->cur&01) == 0; a1++)
1669 		if (a1 >= a3)
1670 			return;
1671 	for (a2 = a1 + 1; a2 <= a3; ) {
1672 		if (a2->cur & 01) {
1673 			a2++;
1674 			dot = a1;
1675 		} else
1676 			(a1++)->cur = (a2++)->cur;
1677 	}
1678 	dol = a1-1;
1679 	if (dot > dol)
1680 		dot = dol;
1681 	fchange = 1;
1682 }
1683 
1684 static char *
1685 getaline(long tl)
1686 {
1687 	char *bp, *lp;
1688 	int nl;
1689 
1690 	lp = linebuf;
1691 	bp = getblock(tl, READ);
1692 	nl = nleft;
1693 	tl &= ~0377;
1694 	while (*lp++ = *bp++)
1695 		if (--nl == 0) {
1696 			bp = getblock(tl += 0400, READ);
1697 			nl = nleft;
1698 		}
1699 	return (linebuf);
1700 }
1701 
1702 static long
1703 putline(void)
1704 {
1705 	char *bp, *lp;
1706 	int nl;
1707 	long tl;
1708 
1709 	fchange = 1;
1710 	lp = linebuf;
1711 	tl = tline;
1712 	bp = getblock(tl, WRITE);
1713 	nl = nleft;
1714 	tl &= ~0377;
1715 	while (*bp = *lp++) {
1716 		if (*bp++ == '\n') {
1717 			*--bp = 0;
1718 			linebp = lp;
1719 			break;
1720 		}
1721 		if (--nl == 0) {
1722 			bp = getblock(tl += 0400, WRITE);
1723 			nl = nleft;
1724 		}
1725 	}
1726 	nl = tline;
1727 	tline += (((lp-linebuf)+03)>>1)&077776;
1728 	return (nl);
1729 }
1730 
1731 static char *
1732 getblock(long atl, long iof)
1733 {
1734 	int bno, off;
1735 	char *p1, *p2;
1736 	int n;
1737 
1738 	bno = atl >> 8;
1739 	off = (atl<<1)&0774;
1740 
1741 	/* bno is limited to 16 bits */
1742 	if (bno >= 65535) {
1743 		lastc = '\n';
1744 		(void) error(31);
1745 	}
1746 	nleft = 512 - off;
1747 	if (bno == iblock) {
1748 		ichanged |= iof;
1749 		return (ibuff+off);
1750 	}
1751 	if (bno == oblock)
1752 		return (obuff+off);
1753 	if (iof == READ) {
1754 		if (ichanged) {
1755 			if (xtflag)
1756 				if (run_crypt(0L, ibuff, 512, tperm) == -1)
1757 					(void) error(63);
1758 			blkio(iblock, ibuff, write);
1759 		}
1760 		ichanged = 0;
1761 		iblock = bno;
1762 		blkio(bno, ibuff, read);
1763 		if (xtflag)
1764 			if (run_crypt(0L, ibuff, 512, tperm) == -1)
1765 				(void) error(63);
1766 		return (ibuff+off);
1767 	}
1768 	if (oblock >= 0) {
1769 		if (xtflag) {
1770 			p1 = obuff;
1771 			p2 = crbuf;
1772 			n = 512;
1773 			while (n--)
1774 				*p2++ = *p1++;
1775 			if (run_crypt(0L, crbuf, 512, tperm) == -1)
1776 				(void) error(63);
1777 			blkio(oblock, crbuf, write);
1778 		} else
1779 			blkio(oblock, obuff, write);
1780 	}
1781 	oblock = bno;
1782 	return (obuff+off);
1783 }
1784 
1785 static void
1786 blkio(int b, char *buf, ssize_t (*iofcn)())
1787 {
1788 	lseek(tfile, (long)b<<9, 0);
1789 	if ((*iofcn)(tfile, buf, 512) != 512) {
1790 		if (dol != zero)
1791 			(void) error(32); /* Bypass this if writing null file */
1792 	}
1793 }
1794 
1795 static void
1796 init(void)
1797 {
1798 	long *markp;
1799 	mode_t omask;
1800 
1801 	if (tfile != -1) {
1802 		(void) close(tfile);
1803 		(void) unlink(tfname);
1804 	}
1805 
1806 	tline = 2;
1807 	for (markp = names; markp < &names[26]; )
1808 		*markp++ = 0L;
1809 	subnewa = 0L;
1810 	anymarks = 0;
1811 	iblock = -1;
1812 	oblock = -1;
1813 	ichanged = 0;
1814 	initflg = 1;
1815 	omask = umask(0);
1816 
1817 	if ((tfile = open(tfname, O_CREAT|O_EXCL|O_RDWR,
1818 	    S_IRUSR|S_IWUSR)) < 0) {
1819 		puts(gettext(msgtab[70]));
1820 		exit(2);
1821 	}
1822 
1823 	umask(omask);
1824 	if (xflag) {
1825 		xtflag = 1;
1826 		if (tpermflag)
1827 			(void) crypt_close(tperm);
1828 		tpermflag = 1;
1829 		if (makekey(tperm)) {
1830 			xtflag = 0;
1831 			puts(gettext(msgtab[65]));
1832 		}
1833 	}
1834 	brk((char *)fendcore);
1835 	dot = zero = dol = savdot = savdol = fendcore;
1836 	flag28 = save28 = 0;
1837 	endcore = fendcore - sizeof (struct lin);
1838 }
1839 
1840 static void
1841 global(int k)
1842 {
1843 	char *gp;
1844 	wchar_t l;
1845 	char multic[MB_LEN_MAX];
1846 	wchar_t c;
1847 	LINE a1;
1848 	char globuf[LBSIZE];
1849 	int n;
1850 	int	len;
1851 
1852 	if (globp)
1853 		(void) error(33);
1854 	setall();
1855 	nonzero();
1856 	if ((n = _mbftowc(multic, &l, getchr, &peekc)) <= 0)
1857 		(void) error(67);
1858 	if (l == '\n')
1859 		(void) error(19);
1860 	save();
1861 	comple(l);
1862 	gp = globuf;
1863 	while ((c = get_wchr()) != '\n') {
1864 		if (c == EOF)
1865 			(void) error(19);
1866 
1867 		/* '\\' has special meaning only if preceding a '\n' */
1868 		if (c == '\\') {
1869 			c = get_wchr();
1870 			if (c != '\n')
1871 				*gp++ = '\\';
1872 		}
1873 		if ((gp + (unsigned int)MB_CUR_MAX) >= &globuf[LBSIZE-1])
1874 			(void) error(34);
1875 		if ((len = wctomb(gp, c)) <= 0) {
1876 			*gp = (unsigned char)c;
1877 			len = 1;
1878 		}
1879 		gp += len;
1880 	}
1881 	if (gp == globuf)
1882 		*gp++ = 'p';
1883 	*gp++ = '\n';
1884 	*gp++ = 0;
1885 	for (a1 = zero; a1 <= dol; a1++) {
1886 		a1->cur &= ~01;
1887 		if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
1888 			a1->cur |= 01;
1889 	}
1890 	/*
1891 	 * Special case: g/.../d (avoid n^2 algorithm)
1892 	 */
1893 	if (globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == '\0') {
1894 		gdelete();
1895 		return;
1896 	}
1897 	for (a1 = zero; a1 <= dol; a1++) {
1898 		if (a1->cur & 01) {
1899 			a1->cur &= ~01;
1900 			dot = a1;
1901 			globp = globuf;
1902 			globflg = 1;
1903 			commands();
1904 			globflg = 0;
1905 			a1 = zero;
1906 		}
1907 	}
1908 }
1909 
1910 static void
1911 join(void)
1912 {
1913 	char *gp, *lp;
1914 	LINE a1;
1915 
1916 	if (addr1 == addr2)
1917 		return;
1918 	gp = genbuf;
1919 	for (a1 = addr1; a1 <= addr2; a1++) {
1920 		lp = getaline(a1->cur);
1921 		while (*gp = *lp++)
1922 			if (gp++ > &genbuf[LBSIZE-1])
1923 				(void) error(27);
1924 	}
1925 	lp = linebuf;
1926 	gp = genbuf;
1927 	while (*lp++ = *gp++)
1928 		;
1929 	addr1->cur = putline();
1930 	if (addr1 < addr2)
1931 		rdelete(addr1+1, addr2);
1932 	dot = addr1;
1933 }
1934 
1935 static void
1936 substitute(int inglob)
1937 {
1938 	int nl;
1939 	LINE a1;
1940 	long *markp;
1941 	int ingsav;		/* For saving arg. */
1942 
1943 	ingsav = inglob;
1944 	ocerr2 = 0;
1945 	gsubf = compsub();
1946 	for (a1 = addr1; a1 <= addr2; a1++) {
1947 		if (execute(0, a1) == 0)
1948 			continue;
1949 		numpass = 0;
1950 		ocerr1 = 0;
1951 		inglob |= 01;
1952 		dosub();
1953 		if (gsubf) {
1954 			while (*loc2) {
1955 				if (execute(1, (LINE)0) == 0)
1956 					break;
1957 				dosub();
1958 			}
1959 		}
1960 		if (ocerr1 == 0)continue;	/* Don't put out-not changed. */
1961 		subnewa = putline();
1962 		a1->cur &= ~01;
1963 		if (anymarks) {
1964 			for (markp = names; markp < &names[26]; markp++)
1965 				if (*markp == a1->cur)
1966 					*markp = subnewa;
1967 		}
1968 		a1->cur = subnewa;
1969 		append(getsub, a1);
1970 		nl = nline;
1971 		a1 += nl;
1972 		addr2 += nl;
1973 	}
1974 	if (ingsav)
1975 		return;	/* Was in global-no error msg allowed. */
1976 	if (inglob == 0)
1977 		(void) error(35);	/* Not in global, but not found. */
1978 	if (ocerr2 == 0)
1979 		(void) error(35); /* RE found, but occurrence match failed. */
1980 }
1981 
1982 static int
1983 compsub(void)
1984 {
1985 	int c;
1986 	wchar_t seof;
1987 	char *p;
1988 	char multic[MB_LEN_MAX];
1989 	int n;
1990 	static char remem[RHSIZE];
1991 	static int remflg = -1;
1992 	int i;
1993 
1994 	if ((n = _mbftowc(multic, &seof, getchr, &peekc)) <= 0)
1995 		(void) error(67);
1996 	if (seof == '\n' || seof == ' ')
1997 		(void) error(36);
1998 	comple(seof);
1999 	p = rhsbuf;
2000 	for (;;) {
2001 		wchar_t cl;
2002 		if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
2003 			(void) error(67);
2004 		if (cl == '\\') {
2005 			*p++ = '\\';
2006 			if (p >= &rhsbuf[RHSIZE])
2007 				(void) error(38);
2008 			if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
2009 				(void) error(67);
2010 		} else if (cl == '\n') {
2011 			if (nodelim == 1) {
2012 				nodelim = 0;
2013 				(void) error(36);
2014 			}
2015 			if (!(globp && globp[0])) {
2016 				UNGETC('\n');
2017 				pflag++;
2018 				break;
2019 			}
2020 		} else if (cl == seof)
2021 			break;
2022 		if (p + n > &rhsbuf[RHSIZE])
2023 			(void) error(38);
2024 		(void) strncpy(p, multic, n);
2025 		p += n;
2026 	}
2027 	*p++ = 0;
2028 	if (rhsbuf[0] == '%' && rhsbuf[1] == 0)
2029 		/*
2030 		 * If there isn't a remembered string, it is an error;
2031 		 * otherwise the right hand side is the previous right
2032 		 * hand side.
2033 		 */
2034 
2035 		if (remflg == -1)
2036 			(void) error(55);
2037 		else
2038 			strcpy(rhsbuf, remem);
2039 	else {
2040 		strcpy(remem, rhsbuf);
2041 		remflg = 0;
2042 	}
2043 	c = 0;
2044 	peekc = getchr();	/* Gets char after third delimiter. */
2045 	if (peekc == 'g') {
2046 		c = LBSIZE; peekc = 0;
2047 	}
2048 	if (peekc >= '1' && peekc <= '9') {
2049 		c = peekc-'0';
2050 		peekc = 0;	/* Allows getchr() to get next char. */
2051 		while (1) {
2052 			i = getchr();
2053 			if (i < '0' || i > '9')
2054 				break;
2055 			c = c*10 + i-'0';
2056 			if (c > LBSIZE-1)
2057 				(void) error(20);	/* "Illegal suffix" */
2058 			}
2059 		peekc = i;	/* Effectively an unget. */
2060 		}
2061 	newline();
2062 	return (c);
2063 
2064 	/*
2065 	 * Returns occurrence value. 0 & 1 both do first occurrence
2066 	 * only: c = 0 if ordinary substitute; c = 1
2067 	 * if use 1 in global sub(s/a/b/1). 0 in global form is illegal.
2068 	 */
2069 }
2070 
2071 static int
2072 getsub(void)
2073 {
2074 	char *p1, *p2;
2075 
2076 	p1 = linebuf;
2077 	if ((p2 = linebp) == 0)
2078 		return (EOF);
2079 	while (*p1++ = *p2++)
2080 		;
2081 	linebp = 0;
2082 	return (0);
2083 }
2084 
2085 static void
2086 dosub(void)
2087 {
2088 	char *lp, *sp, *rp;
2089 	int c;
2090 
2091 	if (gsubf > 0 && gsubf < LBSIZE) {
2092 		numpass++;
2093 		if (gsubf != numpass)
2094 			return;
2095 	}
2096 	ocerr1++;
2097 	ocerr2++;
2098 	lp = linebuf;
2099 	sp = genbuf;
2100 	rp = rhsbuf;
2101 	while (lp < loc1)
2102 		*sp++ = *lp++;
2103 	while (c = *rp++) {
2104 		if (c == '&') {
2105 			sp = place(sp, loc1, loc2);
2106 			continue;
2107 		} else if (c == '\\') {
2108 			c = *rp++;
2109 			if (c >= '1' && c < nbra + '1') {
2110 				sp = place(sp, braslist[c-'1'],
2111 				    braelist[c-'1']);
2112 				continue;
2113 			}
2114 		}
2115 		*sp++ = c;
2116 		if (sp >= &genbuf[LBSIZE])
2117 			(void) error(27);
2118 	}
2119 	lp = loc2;
2120 	loc2 = sp - genbuf + linebuf;
2121 	while (*sp++ = *lp++)
2122 		if (sp >= &genbuf[LBSIZE])
2123 			(void) error(27);
2124 	lp = linebuf;
2125 	sp = genbuf;
2126 	while (*lp++ = *sp++)
2127 		;
2128 }
2129 
2130 static char *
2131 place(char *sp, char *l1, char *l2)
2132 {
2133 
2134 	while (l1 < l2) {
2135 		*sp++ = *l1++;
2136 		if (sp >= &genbuf[LBSIZE])
2137 			(void) error(27);
2138 	}
2139 	return (sp);
2140 }
2141 
2142 static void
2143 comple(wchar_t seof)
2144 {
2145 	int cclass = 0;
2146 	wchar_t c;
2147 	int n;
2148 	char *cp = genbuf;
2149 	char multic[MB_LEN_MAX];
2150 
2151 	while (1) {
2152 		if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2153 			error1(67);
2154 		if (n == 0 || c == '\n') {
2155 			if (cclass)
2156 				error1(49);
2157 			else
2158 				break;
2159 		}
2160 		if (c == seof && !cclass)
2161 			break;
2162 		if (cclass && c == ']') {
2163 			cclass = 0;
2164 			if (cp > &genbuf[LBSIZE-1])
2165 				error1(50);
2166 			*cp++ = ']';
2167 			continue;
2168 		}
2169 		if (c == '[' && !cclass) {
2170 			cclass = 1;
2171 			if (cp > &genbuf[LBSIZE-1])
2172 				error1(50);
2173 			*cp++ = '[';
2174 			if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2175 				error1(67);
2176 			if (n == 0 || c == '\n')
2177 				error1(49);
2178 		}
2179 		if (c == '\\' && !cclass) {
2180 			if (cp > &genbuf[LBSIZE-1])
2181 				error1(50);
2182 			*cp++ = '\\';
2183 			if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2184 				error1(67);
2185 			if (n == 0 || c == '\n')
2186 				error1(36);
2187 		}
2188 		if (cp + n > &genbuf[LBSIZE-1])
2189 			error1(50);
2190 		(void) strncpy(cp, multic, n);
2191 		cp += n;
2192 	}
2193 	*cp = '\0';
2194 	if (n != 0 && c == '\n')
2195 		UNGETC('\n');
2196 	if (n == 0 || c == '\n')
2197 		nodelim = 1;
2198 
2199 	/*
2200 	 * NULL RE: do not compile a null regular expression; but process
2201 	 * input with last regular expression encountered
2202 	 */
2203 
2204 	if (genbuf[0] != '\0') {
2205 		if (expbuf)
2206 			free(expbuf);
2207 		expbuf = compile(genbuf, (char *)0, (char *)0);
2208 	}
2209 	if (regerrno)
2210 		error1(regerrno);
2211 }
2212 
2213 static void
2214 move(int cflag)
2215 {
2216 	LINE adt, ad1, ad2;
2217 
2218 	setdot();
2219 	nonzero();
2220 	if ((adt = address()) == 0)
2221 		(void) error(39);
2222 	newline();
2223 	if (!globflg) save();
2224 	if (cflag) {
2225 		ad1 = dol;
2226 		append(getcopy, ad1++);
2227 		ad2 = dol;
2228 	} else {
2229 		ad2 = addr2;
2230 		for (ad1 = addr1; ad1 <= ad2; )
2231 			(ad1++)->cur &= ~01;
2232 		ad1 = addr1;
2233 	}
2234 	ad2++;
2235 	if (adt < ad1) {
2236 		dot = adt + (ad2-ad1);
2237 		if ((++adt) == ad1)
2238 			return;
2239 		reverse(adt, ad1);
2240 		reverse(ad1, ad2);
2241 		reverse(adt, ad2);
2242 	} else if (adt >= ad2) {
2243 		dot = adt++;
2244 		reverse(ad1, ad2);
2245 		reverse(ad2, adt);
2246 		reverse(ad1, adt);
2247 	} else
2248 		(void) error(39);
2249 	fchange = 1;
2250 }
2251 
2252 static void
2253 reverse(LINE a1, LINE a2)
2254 {
2255 	long t;
2256 
2257 	for (;;) {
2258 		t = (--a2)->cur;
2259 		if (a2 <= a1)
2260 			return;
2261 		a2->cur = a1->cur;
2262 		(a1++)->cur = t;
2263 	}
2264 }
2265 
2266 static int
2267 getcopy(void)
2268 {
2269 
2270 	if (addr1 > addr2)
2271 		return (EOF);
2272 	(void) getaline((addr1++)->cur);
2273 	return (0);
2274 }
2275 
2276 
2277 /*
2278  * Handles error code returned from comple() routine: regular expression
2279  * compile and match routines
2280  */
2281 
2282 static void
2283 error1(int code)
2284 {
2285 	nbra = 0;
2286 	(void) error(code);
2287 }
2288 
2289 
2290 static int
2291 execute(int gf, LINE addr)
2292 {
2293 	char *p1;
2294 	int c;
2295 
2296 	for (c = 0; c < nbra; c++) {
2297 		braslist[c] = 0;
2298 		braelist[c] = 0;
2299 	}
2300 	if (gf)
2301 		locs = p1 = loc2;
2302 	else {
2303 		if (addr == zero)
2304 			return (0);
2305 		p1 = getaline(addr->cur);
2306 		locs = 0;
2307 	}
2308 	return (step(p1, expbuf));
2309 }
2310 
2311 
2312 static void
2313 putd()
2314 {
2315 	int r;
2316 
2317 	r = (int)(count%10);
2318 	count /= 10;
2319 	if (count)
2320 		putd();
2321 	putchr(r + '0');
2322 }
2323 
2324 
2325 int
2326 puts(const char *sp)
2327 {
2328 	int n;
2329 	wchar_t c;
2330 	int sz, i;
2331 	if (fss.Ffill && (listf == 0)) {
2332 
2333 		/* deliberate attempt to remove constness of sp because */
2334 		/* it needs to be expanded */
2335 
2336 		if ((i = expnd((char *)sp, funny, &sz, &fss)) == -1) {
2337 			write(1, funny, fss.Flim & 0377);
2338 			putchr('\n');
2339 			write(1, gettext("too long"),
2340 			    strlen(gettext("too long")));
2341 		}
2342 		else
2343 			write(1, funny, sz);
2344 		putchr('\n');
2345 		if (i == -2)
2346 			write(1, gettext("tab count\n"),
2347 			    strlen(gettext("tab count\n")));
2348 		return (0);
2349 	}
2350 	col = 0;
2351 	while (*sp) {
2352 		n = mbtowc(&c, sp, MB_LEN_MAX);
2353 		if (listf) {
2354 			if (n < 1)
2355 				(void) error(28);
2356 			else if (n == 1)
2357 				putchr((unsigned char)*sp++);
2358 			else {
2359 				sp += n;
2360 				putwchr(c);
2361 			}
2362 		} else {
2363 			putchr((unsigned char)*sp++);
2364 		}
2365 	}
2366 #ifndef XPG6
2367 	if (listf)
2368 		putchr('$');    /* end of line is marked with a $ */
2369 #else
2370 	if (listf) {
2371 	/* xpg6 - ensure that the end of line $ is not preceeded with a "\" */
2372 	/* by doing a putchr() with listf=0, thereby avoiding the $ case */
2373 	/* statement  in putchr() */
2374 		listf = 0;
2375 		putchr('$');    /* end of line is marked with a $ */
2376 		listf++;
2377 	}
2378 #endif
2379 	putchr('\n');
2380 	return (1);
2381 }
2382 
2383 
2384 static void
2385 putwchr(wchar_t ac)
2386 {
2387 	char buf[MB_LEN_MAX], *p;
2388 	char *lp;
2389 	wchar_t c;
2390 	short len;
2391 
2392 	lp = linp;
2393 	c = ac;
2394 	if (listf) {
2395 		if (!iswprint(c)) {
2396 			p = &buf[0];
2397 			if ((len = wctomb(p, c)) <= 0) {
2398 				*p = (unsigned char)c;
2399 				len = 1;
2400 			};
2401 			while (len--) {
2402 				if (col + 4 >= 72) {
2403 					col = 0;
2404 					*lp++ = '\\';
2405 					*lp++ = '\n';
2406 				}
2407 				(void) sprintf(lp, "\\%03o",
2408 				    *(unsigned char *)p++);
2409 				col += 4;
2410 				lp += 4;
2411 			}
2412 		} else {
2413 			if ((len = wcwidth(c)) <= 0)
2414 				len = 0;
2415 			if (col + len >= 72) {
2416 				col = 0;
2417 				*lp++ = '\\';
2418 				*lp++ = '\n';
2419 			}
2420 			col += len;
2421 			if ((len = wctomb(lp, c)) <= 0) {
2422 				*lp = (unsigned char)c;
2423 				len = 1;
2424 			}
2425 			lp += len;
2426 		}
2427 	} else {
2428 		if ((len = wctomb(lp, c)) <= 0) {
2429 			*lp = (unsigned char)c;
2430 			len = 1;
2431 		}
2432 		lp += len;
2433 	}
2434 	if (c == '\n' || lp >= &line[64]) {
2435 		linp = line;
2436 		len = lp - line;
2437 		write(1, line, len);
2438 		return;
2439 	}
2440 	linp = lp;
2441 }
2442 
2443 
2444 static void
2445 putchr(unsigned char c)
2446 {
2447 	char *lp;
2448 	int len;
2449 
2450 	lp = linp;
2451 	if (listf && c != '\n') {
2452 		switch (c) {
2453 			case '\\' :
2454 				*lp++ = '\\';
2455 				*lp++ = '\\';
2456 				col += 2;
2457 				break;
2458 			case '\007' :
2459 				*lp++ = '\\';
2460 				*lp++ = 'a';
2461 				col += 2;
2462 				break;
2463 			case '\b' :
2464 				*lp++ = '\\';
2465 				*lp++ = 'b';
2466 				col += 2;
2467 				break;
2468 			case '\f' :
2469 				*lp++ = '\\';
2470 				*lp++ = 'f';
2471 				col += 2;
2472 				break;
2473 			case '\r' :
2474 				*lp++ = '\\';
2475 				*lp++ = 'r';
2476 				col += 2;
2477 				break;
2478 			case '\t' :
2479 				*lp++ = '\\';
2480 				*lp++ = 't';
2481 				col += 2;
2482 				break;
2483 			case '\v' :
2484 				*lp++ = '\\';
2485 				*lp++ = 'v';
2486 				col += 2;
2487 				break;
2488 #ifdef XPG6
2489 		/* if $ characters are within the line preceed with \ */
2490 			case '$' :
2491 				*lp++ = '\\';
2492 				*lp++ = '$';
2493 				col += 2;
2494 				break;
2495 #endif
2496 			default:
2497 				if (isprint(c)) {
2498 					*lp++ = c;
2499 					col += 1;
2500 				} else {
2501 					(void) sprintf(lp, "\\%03o", c);
2502 					col += 4;
2503 					lp += 4;
2504 				}
2505 				break;
2506 		}
2507 
2508 	/*
2509 	 * long lines are folded w/ pt of folding indicated by writing
2510 	 * backslash/newline character
2511 	 */
2512 
2513 		if (col + 1 >= 72) {
2514 			col = 0;
2515 			*lp++ = '\\';
2516 			*lp++ = '\n';
2517 		}
2518 	} else
2519 		*lp++ = c;
2520 	if (c == '\n' || lp >= &line[64]) {
2521 		linp = line;
2522 		len = lp - line;
2523 		(void) write(1, line, len);
2524 		return;
2525 	}
2526 	linp = lp;
2527 }
2528 
2529 
2530 static char *
2531 getkey(const char *prompt)
2532 {
2533 	struct termio b;
2534 	int save;
2535 	void (*sig)();
2536 	static char key[KSIZE+1];
2537 	char *p;
2538 	int c;
2539 
2540 	sig = signal(SIGINT, SIG_IGN);
2541 	ioctl(0, TCGETA, &b);
2542 	save = b.c_lflag;
2543 	b.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
2544 	ioctl(0, TCSETAW, &b);
2545 	write(1, gettext(prompt), strlen(gettext(prompt)));
2546 	p = key;
2547 	while (((c = getchr()) != EOF) && (c != '\n')) {
2548 		if (p < &key[KSIZE])
2549 			*p++ = c;
2550 	}
2551 	*p = 0;
2552 	write(1, "\n", 1);
2553 	b.c_lflag = save;
2554 	ioctl(0, TCSETAW, &b);
2555 	signal(SIGINT, sig);
2556 	return (key);
2557 }
2558 
2559 
2560 static void
2561 globaln(int k)
2562 {
2563 	char *gp;
2564 	int c;
2565 	int n;
2566 	wchar_t cl;
2567 	LINE a1;
2568 	int  nfirst;
2569 	char globuf[LBSIZE];
2570 	char multic[MB_LEN_MAX];
2571 	int	len;
2572 	int pflag_save = 0;
2573 	int listf_save = 0;
2574 	int listn_save = 0;
2575 
2576 	if (globp)
2577 		(void) error(33);
2578 	setall();
2579 	nonzero();
2580 	if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
2581 		(void) error(67);
2582 	if (cl == '\n')
2583 		(void) error(19);
2584 	save();
2585 	comple(cl);
2586 	for (a1 = zero; a1 <= dol; a1++) {
2587 		a1->cur &= ~01;
2588 		if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
2589 			a1->cur |= 01;
2590 	}
2591 	nfirst = 0;
2592 	newline();
2593 	/*
2594 	 * preserve the p, l, and n suffix commands of the G and V
2595 	 * commands during the interactive section and restore
2596 	 * on completion of the G and V command.
2597 	 */
2598 	pflag_save = pflag;
2599 	listf_save = listf;
2600 	listn_save = listn;
2601 	pflag = 0;
2602 	listf = 0;
2603 	listn = 0;
2604 	for (a1 = zero; a1 <= dol; a1++) {
2605 		if (a1->cur & 01) {
2606 			a1->cur &= ~01;
2607 			dot = a1;
2608 			puts(getaline(a1->cur));
2609 			if ((c = get_wchr()) == EOF)
2610 				(void) error(52);
2611 			if (c == 'a' || c == 'i' || c == 'c')
2612 				(void) error(53);
2613 			if (c == '\n') {
2614 				a1 = zero;
2615 				continue;
2616 			}
2617 			if (c != '&') {
2618 				gp = globuf;
2619 				if ((len = wctomb(gp, c)) <= 0) {
2620 					*gp = (unsigned char)c;
2621 					len = 1;
2622 				}
2623 				gp += len;
2624 				while ((c = get_wchr()) != '\n') {
2625 
2626 			/* '\\' has special meaning only if preceding a '\n' */
2627 					if (c == '\\') {
2628 						c = get_wchr();
2629 						if (c != '\n')
2630 							*gp++ = '\\';
2631 					}
2632 					if ((gp + (unsigned int)MB_CUR_MAX) >=
2633 					    &globuf[LBSIZE-1])
2634 						(void) error(34);
2635 
2636 					if ((len = wctomb(gp, c)) <= 0) {
2637 						*gp = (unsigned char)c;
2638 						len = 1;
2639 					}
2640 					gp += len;
2641 				}
2642 				*gp++ = '\n';
2643 				*gp++ = 0;
2644 				nfirst = 1;
2645 			} else if ((c = get_wchr()) != '\n')
2646 				(void) error(54);
2647 			globp = globuf;
2648 			if (nfirst) {
2649 				globflg = 1;
2650 				commands();
2651 				globflg = 0;
2652 			} else
2653 				(void) error(56);
2654 			globp = 0;
2655 			a1 = zero;
2656 		}
2657 	}
2658 	pflag = pflag_save;
2659 	listf = listf_save;
2660 	listn = listn_save;
2661 }
2662 
2663 
2664 static int
2665 eopen(char *string, int rw)
2666 {
2667 #define	w_or_r(a, b) (rw ? a : b)
2668 	int pf[2];
2669 	pid_t i;
2670 	int io;
2671 	int chcount;	/* # of char read. */
2672 
2673 	if (rflg) {	/* restricted shell */
2674 		if (Xqt) {
2675 			Xqt = 0;
2676 			(void) error(6);
2677 		}
2678 	}
2679 	if (!Xqt) {
2680 		if ((io = open(string, rw)) >= 0) {
2681 			if (fflg) {
2682 				chcount = read(io, crbuf, LBSIZE);
2683 				if (crflag == -1) {
2684 					if (isencrypt(crbuf, chcount))
2685 						crflag = 2;
2686 					else
2687 						crflag = -2;
2688 				}
2689 				if (crflag > 0)
2690 				if (run_crypt(0L, crbuf, chcount, perm) == -1)
2691 						(void) error(63);
2692 				if (fspec(crbuf, &fss, 0) < 0) {
2693 					fss.Ffill = 0;
2694 					fflg = 0;
2695 					(void) error(4);
2696 				}
2697 				lseek(io, 0L, 0);
2698 			}
2699 		}
2700 		fflg = 0;
2701 		return (io);
2702 	}
2703 	if (pipe(pf) < 0)
2704 xerr:		(void) error(0);
2705 	if ((i = fork()) == 0) {
2706 		signal(SIGHUP, oldhup);
2707 		signal(SIGQUIT, oldquit);
2708 		signal(SIGPIPE, oldpipe);
2709 		signal(SIGINT, (void (*)()) 0);
2710 		close(w_or_r(pf[1], pf[0]));
2711 		close(w_or_r(0, 1));
2712 		dup(w_or_r(pf[0], pf[1]));
2713 		close(w_or_r(pf[0], pf[1]));
2714 		execlp(_PATH_BSHELL, "sh", "-c", string, (char *)0);
2715 		exit(1);
2716 	}
2717 	if (i == (pid_t)-1)
2718 		goto xerr;
2719 	close(w_or_r(pf[0], pf[1]));
2720 	return (w_or_r(pf[1], pf[0]));
2721 }
2722 
2723 
2724 static void
2725 eclose(int f)
2726 {
2727 	close(f);
2728 	if (Xqt)
2729 		Xqt = 0, wait((int *)0);
2730 }
2731 
2732 
2733 static void
2734 mkfunny(void)
2735 {
2736 	char *p, *p1, *p2;
2737 
2738 	p2 = p1 = funny;
2739 	p = file;
2740 	/*
2741 	 * Go to end of file name
2742 	 */
2743 	while (*p)
2744 		p++;
2745 	while (*--p  == '/')	/* delete trailing slashes */
2746 		*p = '\0';
2747 	/*
2748 	 * go back to beginning of file
2749 	 */
2750 	p = file;
2751 	/*
2752 	 * Copy file name to funny setting p2 at
2753 	 * basename of file.
2754 	 */
2755 	while (*p1++ = *p)
2756 		if (*p++ == '/')
2757 			p2 = p1;
2758 	/*
2759 	 * Set p1 to point to basename of tfname.
2760 	 */
2761 	p1 = strrchr(tfname, '/');
2762 	if (strlen(tfname) > (size_t)6)
2763 		p1 = &tfname[strlen(tfname)-6];
2764 	p1++;
2765 	*p2 = '\007'; /* add unprintable char for funny  a unique name */
2766 	/*
2767 	 * Copy tfname to file.
2768 	 */
2769 	while (*++p2 = *p1++)
2770 		;
2771 }
2772 
2773 
2774 static void
2775 getime(void) /* get modified time of file and save */
2776 {
2777 	if (stat(file, &Fl) < 0)
2778 		savtime = 0;
2779 	else
2780 		savtime = Fl.st_mtime;
2781 }
2782 
2783 
2784 static void
2785 chktime(void) /* check saved mod time against current mod time */
2786 {
2787 	if (savtime != 0 && Fl.st_mtime != 0) {
2788 		if (savtime != Fl.st_mtime)
2789 			(void) error(58);
2790 	}
2791 }
2792 
2793 
2794 static void
2795 newtime(void) /* get new mod time and save */
2796 {
2797 	stat(file, &Fl);
2798 	savtime = Fl.st_mtime;
2799 }
2800 
2801 
2802 /*
2803  * restricted - check for '/' in name and delete trailing '/'
2804  */
2805 static void
2806 red(char *op)
2807 {
2808 	char *p;
2809 
2810 	p = op;
2811 	while (*p)
2812 		if (*p++ == '/'&& rflg) {
2813 			*op = 0;
2814 			(void) error(6);
2815 		}
2816 	/* delete trailing '/' */
2817 	while (p > op) {
2818 		if (*--p == '/')
2819 			*p = '\0';
2820 		else break;
2821 	}
2822 }
2823 
2824 
2825 /*
2826  * Searches thru beginning of file looking for a string of the form
2827  *	<: values... :>
2828  *
2829  * where "values" are
2830  *
2831  *	\b      ignored
2832  *	s<num>  sets the Flim to <num>
2833  *	t???    sets tab stop stuff
2834  *	d       ignored
2835  *	m<num>  ignored
2836  *	e       ignored
2837  */
2838 
2839 static int
2840 fspec(char line[], struct Fspec *f, int up)
2841 {
2842 	struct termio arg;
2843 	int havespec, n;
2844 	int	len;
2845 
2846 	if (!up) clear(f);
2847 
2848 	havespec = fsprtn = 0;
2849 	for (fsp = line; *fsp && *fsp != '\n'; fsp += len) {
2850 		if ((len = mblen(fsp, MB_CUR_MAX)) <= 0)
2851 			len = 1;
2852 		switch (*fsp) {
2853 
2854 			case '<':	if (havespec)
2855 						return (-1);
2856 					if (*(fsp+1) == ':') {
2857 						havespec = 1;
2858 						clear(f);
2859 						if (!ioctl(1, TCGETA, &arg) &&
2860 						    ((arg.c_oflag & TAB3) ==
2861 						    TAB3))
2862 							f->Ffill = 1;
2863 						fsp++;
2864 					}
2865 					continue;
2866 
2867 			case ' ':	continue;
2868 
2869 			case 's':	if (havespec && (n = numb()) >= 0)
2870 						f->Flim = n;
2871 					continue;
2872 
2873 			case 't':	if (havespec) targ(f);
2874 					continue;
2875 
2876 			case 'd':	continue;
2877 
2878 			case 'm':	if (havespec)  n = numb();
2879 					continue;
2880 
2881 			case 'e':	continue;
2882 			case ':':	if (!havespec) continue;
2883 					if (*(fsp+1) != '>') fsprtn = -1;
2884 					return (fsprtn);
2885 
2886 			default:	if (!havespec) continue;
2887 					return (-1);
2888 		}
2889 	}
2890 	return (1);
2891 }
2892 
2893 
2894 static int
2895 numb(void)
2896 {
2897 	int n;
2898 
2899 	n = 0;
2900 	while (*++fsp >= '0' && *fsp <= '9')
2901 		n = 10*n + *fsp-'0';
2902 	fsp--;
2903 	return (n);
2904 }
2905 
2906 
2907 static void
2908 targ(struct Fspec *f)
2909 {
2910 
2911 	if (*++fsp == '-') {
2912 		if (*(fsp + 1) >= '0' && *(fsp+1) <= '9') tincr(numb(), f);
2913 		else tstd(f);
2914 		return;
2915 	}
2916 	if (*fsp >= '0' && *fsp <= '9') {
2917 		tlist(f);
2918 		return;
2919 	}
2920 	fsprtn = -1;
2921 	fsp--;
2922 }
2923 
2924 
2925 static void
2926 tincr(int n, struct Fspec *f)
2927 {
2928 	int l, i;
2929 
2930 	l = 1;
2931 	for (i = 0; i < 20; i++)
2932 		f->Ftabs[i] = l += n;
2933 	f->Ftabs[i] = 0;
2934 }
2935 
2936 
2937 static void
2938 tstd(struct Fspec *f)
2939 {
2940 	char std[3];
2941 
2942 	std[0] = *++fsp;
2943 	if (*(fsp+1) >= '0' && *(fsp+1) <= '9')  {
2944 						std[1] = *++fsp;
2945 						std[2] = '\0';
2946 	} else {
2947 		std[1] = '\0';
2948 	}
2949 	fsprtn = stdtab(std, f->Ftabs);
2950 }
2951 
2952 
2953 static void
2954 tlist(struct Fspec *f)
2955 {
2956 	int n, last, i;
2957 
2958 	fsp--;
2959 	last = i = 0;
2960 
2961 	do {
2962 		if ((n = numb()) <= last || i >= 20) {
2963 			fsprtn = -1;
2964 			return;
2965 		}
2966 		f->Ftabs[i++] = last = n;
2967 	} while (*++fsp == ',');
2968 
2969 	f->Ftabs[i] = 0;
2970 	fsp--;
2971 }
2972 
2973 
2974 static int
2975 expnd(char line[], char buf[], int *sz, struct Fspec *f)
2976 {
2977 	char *l, *t;
2978 	int b;
2979 
2980 	l = line - 1;
2981 	b = 1;
2982 	t = f->Ftabs;
2983 	fsprtn = 0;
2984 
2985 	while (*++l && *l != '\n' && b < 511) {
2986 		if (*l == '\t') {
2987 			while (*t && b >= *t)
2988 				t++;
2989 			if (*t == 0)
2990 				fsprtn = -2;
2991 			do {
2992 				buf[b-1] = ' ';
2993 			} while (++b < *t);
2994 		} else {
2995 			buf[b++ - 1] = *l;
2996 		}
2997 	}
2998 
2999 	buf[b] = '\0';
3000 	*sz = b;
3001 	if (*l != '\0' && *l != '\n') {
3002 		buf[b-1] = '\n';
3003 		return (-1);
3004 	}
3005 	buf[b-1] = *l;
3006 	if (f->Flim && (b-1 > (int)f->Flim))
3007 		return (-1);
3008 	return (fsprtn);
3009 }
3010 
3011 
3012 static void
3013 clear(struct Fspec *f)
3014 {
3015 	f->Ftabs[0] = f->Fdel = f->Fmov = f->Ffill = 0;
3016 	f->Flim = 0;
3017 }
3018 
3019 
3020 static int
3021 lenchk(char line[], struct Fspec *f)
3022 {
3023 	char *l, *t;
3024 	int b;
3025 
3026 	l = line - 1;
3027 	b = 1;
3028 	t = f->Ftabs;
3029 
3030 	while (*++l && *l != '\n' && b < 511) {
3031 		if (*l == '\t') {
3032 			while (*t && b >= *t)
3033 				t++;
3034 			while (++b < *t)
3035 				;
3036 		} else {
3037 			b++;
3038 		}
3039 	}
3040 
3041 	if ((*l != '\0' && *l != '\n') || (f->Flim && (b-1 > (int)f->Flim)))
3042 		return (-1);
3043 	return (0);
3044 }
3045 #define	NTABS 21
3046 
3047 
3048 /*
3049  *	stdtabs: standard tabs table
3050  *	format: option code letter(s), null, tabs, null
3051  */
3052 
3053 static char stdtabs[] = {
3054 'a', 0, 1, 10, 16, 36, 72, 0,			/* IBM 370 Assembler */
3055 'a', '2', 0, 1, 10, 16, 40, 72, 0,		/* IBM Assembler alternative */
3056 'c', 0, 1, 8, 12, 16, 20, 55, 0,		/* COBOL, normal */
3057 'c', '2', 0, 1, 6, 10, 14, 49, 0,		/* COBOL, crunched */
3058 'c', '3', 0, 1, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50,
3059 	54, 58, 62, 67, 0,
3060 'f', 0, 1, 7, 11, 15, 19, 23, 0,		/* FORTRAN */
3061 'p', 0, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 0,
3062 						/* PL/I */
3063 's', 0, 1, 10, 55, 0,				/* SNOBOL */
3064 'u', 0, 1, 12, 20, 44, 0,			/* UNIVAC ASM */
3065 0 };
3066 
3067 
3068 /*
3069  *	stdtab: return tab list for any "canned" tab option.
3070  *		entry: option points to null-terminated option string
3071  *		tabvect points to vector to be filled in
3072  *	exit: return (0) if legal, tabvect filled, ending with zero
3073  *		return (-1) if unknown option
3074  */
3075 
3076 
3077 static int
3078 stdtab(char *option, char *tabvect)
3079 {
3080 	char *scan;
3081 
3082 	tabvect[0] = '\0';
3083 	scan = stdtabs;
3084 	while (*scan) {
3085 		if (strequal(&scan, option)) {
3086 			strcopy(scan, tabvect);
3087 			break;
3088 		} else
3089 			while (*scan++)		/* skip over tab specs */
3090 				;
3091 	}
3092 
3093 /*	later: look up code in /etc/something */
3094 	return (tabvect[0] ? 0 : -1);
3095 }
3096 
3097 
3098 /*
3099  *	strequal: checks strings for equality
3100  *		entry: scan1 points to scan pointer, str points to string
3101  *	exit: return (1) if equal, return (0) if not
3102  *		*scan1 is advanced to next nonzero byte after null
3103  */
3104 
3105 
3106 static int
3107 strequal(char **scan1, char *str)
3108 {
3109 	char c, *scan;
3110 	scan = *scan1;
3111 	while ((c = *scan++) == *str && c)
3112 		str++;
3113 	*scan1 = scan;
3114 	if (c == 0 && *str == 0)
3115 		return (1);
3116 	if (c)
3117 		while (*scan++)
3118 			;
3119 	*scan1 = scan;
3120 	return (0);
3121 }
3122 
3123 
3124 /*	strcopy: copy source to destination */
3125 
3126 
3127 static void
3128 strcopy(char *source, char *dest)
3129 {
3130 	while (*dest++ = *source++)
3131 		;
3132 }
3133 
3134 
3135 /* This is called before a buffer modifying command so that the */
3136 /* current array of line ptrs is saved in sav and dot and dol are saved */
3137 
3138 
3139 static void
3140 save(void)
3141 {
3142 	LINE i;
3143 	int	j;
3144 
3145 	savdot = dot;
3146 	savdol = dol;
3147 	for (j = 0; j <= 25; j++)
3148 		savnames[j] = names[j];
3149 
3150 	for (i = zero + 1; i <= dol; i++)
3151 		i->sav = i->cur;
3152 	initflg = 0;
3153 }
3154 
3155 
3156 /* The undo command calls this to restore the previous ptr array sav */
3157 /* and swap with cur - dot and dol are swapped also. This allows user to */
3158 /* undo an undo */
3159 
3160 
3161 static void
3162 undo(void)
3163 {
3164 	int j;
3165 	long tmp;
3166 	LINE i, tmpdot, tmpdol;
3167 
3168 	tmpdot = dot; dot = savdot; savdot = tmpdot;
3169 	tmpdol = dol; dol = savdol; savdol = tmpdol;
3170 	/* swap arrays using the greater of dol or savdol as upper limit */
3171 	for (i = zero + 1; i <= ((dol > savdol) ? dol : savdol); i++) {
3172 		tmp = i->cur;
3173 		i->cur = i->sav;
3174 		i->sav = tmp;
3175 	}
3176 		/*
3177 		 * If the current text lines are swapped with the
3178 		 * text lines in the save buffer, then swap the current
3179 		 * marks with those in the save area.
3180 		 */
3181 
3182 		for (j = 0; j <= 25; j++) {
3183 			tmp = names[j];
3184 			names[j] = savnames[j];
3185 			savnames[j] = tmp;
3186 		}
3187 }
3188 
3189 static wchar_t
3190 get_wchr(void)
3191 {
3192 	wchar_t	wc;
3193 	char	multi[MB_LEN_MAX];
3194 
3195 	if (_mbftowc(multi, &wc, getchr, &peekc) <= 0)
3196 		wc = getchr();
3197 	return (wc);
3198 }
3199