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