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