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