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