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