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