xref: /386bsd/usr/src/bin/ed/ed.c (revision a2142627)
1 /* ed.c: This file contains the main control and user-interface routines
2    for the ed line editor. */
3 /*-
4  * Copyright (c) 1993 The Regents of the University of California.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley
8  * by Andrew Moore, Talke Studio.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 /*-
39  * Kernighan/Plauger, "Software Tools in Pascal," (c) 1981 by
40  * Addison-Wesley Publishing Company, Inc.  Reprinted with permission of
41  * the publisher.
42  */
43 
44 #ifndef lint
45 char copyright1[] =
46 "@(#) Copyright (c) 1993 The Regents of the University of California.\n\
47  All rights reserved.\n";
48 char copyright2[] =
49 "@(#) Kernighan/Plauger, Software Tools in Pascal, (c) 1981 by\n\
50  Addison-Wesley Publishing Company, Inc.  Reprinted with permission of\n\
51  the publisher.\n";
52 #endif /* not lint */
53 
54 #ifndef lint
55 static char sccsid[] = "@(#)ed.c	5.5 (Berkeley) 3/28/93";
56 #endif /* not lint */
57 
58 /*
59  * CREDITS
60  *	The buf.c algorithm is attributed to Rodney Ruddock of
61  *	the University of Guelph, Guelph, Ontario.
62  *
63  *	The cbc.c encryption code is adapted from
64  *	the bdes program by Matt Bishop of Dartmouth College,
65  *	Hanover, NH.
66  *
67  *	Addison-Wesley Publishing Company generously granted
68  *	permission to distribute this program over Internet.
69  *
70  */
71 
72 #include <unistd.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <ctype.h>
77 #include <setjmp.h>
78 #include <pwd.h>
79 #include <sys/ioctl.h>
80 
81 #include "ed.h"
82 
83 #ifdef _POSIX_SOURCE
84 sigjmp_buf env;
85 #else
86 jmp_buf env;
87 #endif
88 
89 /* static buffers */
90 char *shcmd;			/* shell command buffer */
91 int shcmdsz;			/* shell command buffer size */
92 int shcmdi;			/* shell command buffer index */
93 char *cvbuf;			/* global command buffer */
94 int cvbufsz;			/* global command buffer size */
95 char *lhbuf;			/* lhs buffer */
96 int lhbufsz;			/* lhs buffer size */
97 char *rhbuf;			/* rhs buffer */
98 int rhbufsz;			/* rhs buffer size */
99 int rhbufi;			/* rhs buffer index */
100 char *rbuf;			/* regsub buffer */
101 int rbufsz;			/* regsub buffer size */
102 char *sbuf;			/* file i/o buffer */
103 int sbufsz;			/* file i/o buffer size */
104 char *ibuf;			/* ed command-line buffer */
105 int ibufsz;			/* ed command-line buffer size */
106 char *ibufp;			/* pointer to ed command-line buffer */
107 
108 /* global flags */
109 int isbinary;			/* if set, buffer contains ASCII NULs */
110 int modified;			/* if set, buffer modified since last write */
111 int garrulous = 0;		/* if set, print all error messages */
112 int scripted = 0;		/* if set, suppress diagnostics */
113 int des = 0;			/* if set, use crypt(3) for i/o */
114 int mutex = 0;			/* if set, signals set "sigflags" */
115 int sigflags = 0;		/* if set, signals received while mutex set */
116 int sigactive = 0;		/* if set, signal handlers are enabled */
117 int red = 0;			/* if set, restrict shell/directory access */
118 
119 char dfn[MAXFNAME + 1] = "";	/* default filename */
120 long curln;			/* current address */
121 long lastln;			/* last address */
122 int lineno;			/* script line number */
123 char *prompt;			/* command-line prompt */
124 char *dps = "*";		/* default command-line prompt */
125 
126 char *usage = "usage: %s [-] [-sx] [-p string] [name]\n";
127 
128 extern char errmsg[];
129 extern int optind;
130 extern char *optarg;
131 
132 /* ed: line editor */
main(argc,argv)133 main(argc, argv)
134 	int argc;
135 	char **argv;
136 {
137 	int c, n;
138 	long status = 0;
139 
140 	red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r';
141 top:
142 	while ((c = getopt(argc, argv, "p:sx")) != EOF)
143 		switch(c) {
144 		case 'p':				/* set prompt */
145 			prompt = optarg;
146 			break;
147 		case 's':				/* run script */
148 			scripted = 1;
149 			break;
150 		case 'x':				/* use crypt */
151 #ifdef DES
152 			des = getkey();
153 #else
154 			fprintf(stderr, "crypt unavailable\n?\n");
155 #endif
156 			break;
157 
158 		default:
159 			fprintf(stderr, usage, argv[0]);
160 			exit(1);
161 		}
162 	argv += optind;
163 	argc -= optind;
164 	if (argc && **argv == '-') {
165 		scripted = 1;
166 		if (argc > 1) {
167 			optind = 1;
168 			goto top;
169 		}
170 		argv++;
171 		argc--;
172 	}
173 	/* assert: reliable signals! */
174 #ifdef SIGWINCH
175 	dowinch(SIGWINCH);
176 	if (isatty(0)) signal(SIGWINCH, dowinch);
177 #endif
178 	signal(SIGHUP, onhup);
179 	signal(SIGQUIT, SIG_IGN);
180 	signal(SIGINT, onintr);
181 #ifdef _POSIX_SOURCE
182 	if (status = sigsetjmp(env, 1))
183 #else
184 	if (status = setjmp(env))
185 #endif
186 	{
187 		fputs("\n?\n", stderr);
188 		sprintf(errmsg, "interrupt");
189 	} else {
190 		init_buf();
191 		sigactive = 1;			/* enable signal handlers */
192 		if (argc && **argv && ckfn(*argv)) {
193 			if (doread(0, *argv) < 0 && !isatty(0))
194 				quit(2);
195 			else if (**argv != '!')
196 				strcpy(dfn, *argv);
197 		} else if (argc) {
198 			fputs("?\n", stderr);
199 			if (**argv == '\0')
200 				sprintf(errmsg, "invalid filename");
201 			if (!isatty(0))
202 				quit(2);
203 		}
204 	}
205 	for (;;) {
206 		if (status < 0 && garrulous)
207 			fprintf(stderr, "%s\n", errmsg);
208 		if (prompt) {
209 			printf("%s", prompt);
210 			fflush(stdout);
211 		}
212 		if ((n = getline()) < 0) {
213 			status = ERR;
214 			continue;
215 		} else if (n == 0) {
216 			if (modified && !scripted) {
217 				fputs("?\n", stderr);
218 				sprintf(errmsg, "warning: file modified");
219 				if (!isatty(0)) {
220 					fprintf(stderr, garrulous ? "script, line %d: %s\n"
221 						: "", lineno, errmsg);
222 					quit(2);
223 				}
224 				clearerr(stdin);
225 				modified = 0;
226 				status = EMOD;
227 				continue;
228 			} else
229 				quit(0);
230 		} else if (ibuf[n - 1] != '\n') {
231 			/* discard line */
232 			sprintf(errmsg, "unexpected end-of-file");
233 			clearerr(stdin);
234 			status = ERR;
235 			continue;
236 		}
237 		if ((n = getlist()) >= 0 && (status = ckglob()) != 0) {
238 			if (status > 0 && (status = doglob(status)) >= 0) {
239 				curln = status;
240 				continue;
241 			}
242 		} else if ((status = n) >= 0 && (status = docmd(0)) >= 0) {
243 			if (!status || status
244 			 && (status = doprint(curln, curln, status)) >= 0)
245 				continue;
246 		}
247 		switch (status) {
248 		case EOF:
249 			quit(0);
250 		case EMOD:
251 			modified = 0;
252 			fputs("?\n", stderr);		/* give warning */
253 			sprintf(errmsg, "warning: file modified");
254 			if (!isatty(0)) {
255 				fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg);
256 				quit(2);
257 			}
258 			break;
259 		case FATAL:
260 			if (!isatty(0))
261 				fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg);
262 			else
263 				fprintf(stderr, garrulous ? "%s\n" : "", errmsg);
264 			quit(3);
265 		default:
266 			fputs("?\n", stderr);
267 			if (!isatty(0)) {
268 				fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg);
269 				quit(2);
270 			}
271 			break;
272 		}
273 	}
274 	/*NOTREACHED*/
275 }
276 
277 
278 long line1, line2, nlines;
279 
280 /* getlist: get line numbers from the command buffer until an illegal
281    address is seen.  return range status */
getlist()282 getlist()
283 {
284 	long num;
285 
286 	nlines = line2 = 0;
287 	while ((num = getone()) >= 0) {
288 		line1 = line2;
289 		line2 = num;
290 		nlines++;
291 		if (*ibufp != ',' && *ibufp != ';')
292 			break;
293 		else if (*ibufp++ == ';')
294 			curln = num;
295 	}
296 	nlines = min(nlines, 2);
297 	if (nlines == 0)
298 		line2 = curln;
299 	if (nlines <= 1)
300 		line1 = line2;
301 	return  (num == ERR) ? ERR : nlines;
302 }
303 
304 
305 /*  getone: return the next line number in the command buffer */
306 long
getone()307 getone()
308 {
309 	int c;
310 	long i, num;
311 
312 	if ((num = getnum(1)) < 0)
313 		return num;
314 	for (;;) {
315 		c = isspace(*ibufp);
316 		skipblanks();
317 		c = c && isdigit(*ibufp);
318 		if (!c && *ibufp != '+' && *ibufp != '-' && *ibufp != '^')
319 			break;
320 		c = c ? '+' : *ibufp++;
321 		if ((i = getnum(0)) < 0) {
322 			sprintf(errmsg, "invalid address");
323 			return  i;
324 		}
325 		if (c == '+')
326 			num += i;
327 		else	num -= i;
328 	}
329 	if (num > lastln || num < 0) {
330 		sprintf(errmsg, "invalid address");
331 		return ERR;
332 	}
333 	return num;
334 }
335 
336 
337 /* getnum:  return a relative line number from the command buffer */
338 long
getnum(first)339 getnum(first)
340 	int first;
341 {
342 	pattern_t *pat;
343 	char c;
344 
345 	skipblanks();
346 	if (isdigit(*ibufp))
347 		return strtol(ibufp, &ibufp, 10);
348 	switch(c = *ibufp) {
349 	case '.':
350 		ibufp++;
351 		return first ? curln : ERR;
352 	case '$':
353 		ibufp++;
354 		return first ? lastln : ERR;
355 	case '/':
356 	case '?':
357 		if ((pat = optpat()) == NULL)
358 			return ERR;
359 		else if (*ibufp == c)
360 			ibufp++;
361 		return first ? patscan(pat, (c == '/') ? 1 : 0) : ERR;
362 	case '^':
363 	case '-':
364 	case '+':
365 		return first ? curln : 1;
366 	case '\'':
367 		ibufp++;
368 		return first ? getmark(*ibufp++) : ERR;
369 	case '%':
370 	case ',':
371 	case ';':
372 		if (first) {
373 			ibufp++;
374 			line2 = (c == ';') ? curln : 1;
375 			nlines++;
376 			return lastln;
377 		}
378 		return 1;
379 	default:
380 		return  first ? EOF : 1;
381 	}
382 }
383 
384 
385 /* gflags */
386 #define GLB 001		/* global command */
387 #define GPR 002		/* print after command */
388 #define GLS 004		/* list after command */
389 #define GNP 010		/* enumerate after command */
390 #define GSG 020		/* global substitute */
391 
392 
393 /* VRFYCMD: verify the command suffix in the command buffer */
394 #define VRFYCMD() { \
395 	int done = 0; \
396 	do { \
397 		switch(*ibufp) { \
398 		case 'p': \
399 			gflag |= GPR, ibufp++; \
400 			break; \
401 		case 'l': \
402 			gflag |= GLS, ibufp++; \
403 			break; \
404 		case 'n': \
405 			gflag |= GNP, ibufp++; \
406 			break; \
407 		default: \
408 			done++; \
409 		} \
410 	} while (!done); \
411 	if (*ibufp++ != '\n') { \
412 		sprintf(errmsg, "invalid command suffix"); \
413 		return ERR; \
414 	} \
415 }
416 
417 
418 /* ckglob:  set lines matching a pattern in the command buffer; return
419    global status  */
ckglob()420 ckglob()
421 {
422 	pattern_t *pat;
423 	char c, delim;
424 	char *s;
425 	int nomatch;
426 	long n;
427 	line_t *lp;
428 	int gflag = 0;			/* print suffix of interactive cmd */
429 
430 	if ((c = *ibufp) == 'V' || c == 'G')
431 		gflag = GLB;
432 	else if (c != 'g' && c != 'v')
433 		return 0;
434 	if (ckrange(1, lastln) < 0)
435 		return ERR;
436 	else if ((delim = *++ibufp) == ' ' || delim == '\n') {
437 		sprintf(errmsg, "invalid pattern delimiter");
438 		return ERR;
439 	} else if ((pat = optpat()) == NULL)
440 		return ERR;
441 	else if (*ibufp == delim)
442 		ibufp++;
443 	if (gflag)
444 		VRFYCMD();			/* get print suffix */
445 	for (lp = getlp(n = 1); n <= lastln; n++, lp = lp->next) {
446 		if ((s = gettxt(lp)) == NULL)
447 			return ERR;
448 		lp->len &= ~ACTV;			/* zero ACTV  bit */
449 		if (isbinary)
450 			s = nultonl(s, lp->len & ~ACTV);
451 		if (line1 <= n && n <= line2
452 		 && (!(nomatch = regexec(pat, s, 0, NULL, 0))
453 		 && (c == 'g'  || c == 'G')
454 		 || nomatch && (c == 'v' || c == 'V')))
455 			lp->len |= ACTV;
456 	}
457 	return gflag | GSG;
458 }
459 
460 
461 /* doglob: apply command list in the command buffer to the active
462    lines in a range; return command status */
463 long
doglob(gflag)464 doglob(gflag)
465 	int gflag;
466 {
467 	static char *ocmd = NULL;
468 	static int ocmdsz = 0;
469 
470 	line_t *lp = NULL;
471 	long lc;
472 	int status;
473 	int n;
474 	int interact = gflag & ~GSG;		/* GLB & gflag ? */
475 	char *cmd = NULL;
476 
477 #ifdef BACKWARDS
478 	if (!interact)
479 		if (!strcmp(ibufp, "\n"))
480 			cmd = "p\n";		/* null cmd-list == `p' */
481 		else if ((cmd = getcmdv(&n, 0)) == NULL)
482 			return ERR;
483 #else
484 	if (!interact && (cmd = getcmdv(&n, 0)) == NULL)
485 		return ERR;
486 #endif
487 	ureset();
488 	for (;;) {
489 		for (lp = getlp(lc = 1); lc <= lastln; lc++, lp = lp->next)
490 			if (lp->len & ACTV)		/* active line */
491 				break;
492 		if (lc > lastln)
493 			break;
494 		lp->len ^= ACTV;			/* zero ACTV bit */
495 		curln = lc;
496 		if (interact) {
497 			/* print curln and get a command in global syntax */
498 			if (doprint(curln, curln, 0) < 0)
499 				return ERR;
500 			while ((n = getline()) > 0
501 			    && ibuf[n - 1] != '\n')
502 				clearerr(stdin);
503 			if (n < 0)
504 				return ERR;
505 			else if (n == 0) {
506 				sprintf(errmsg, "unexpected end-of-file");
507 				return ERR;
508 			} else if (n == 1 && !strcmp(ibuf, "\n"))
509 				continue;
510 			else if (n == 2 && !strcmp(ibuf, "&\n")) {
511 				if (cmd == NULL) {
512 					sprintf(errmsg, "no previous command");
513 					return ERR;
514 				} else cmd = ocmd;
515 			} else if ((cmd = getcmdv(&n, 0)) == NULL)
516 				return ERR;
517 			else {
518 				CKBUF(ocmd, ocmdsz, n + 1, ERR);
519 				memcpy(ocmd, cmd, n + 1);
520 				cmd = ocmd;
521 			}
522 
523 		}
524 		ibufp = cmd;
525 		for (; *ibufp;)
526 			if ((status = getlist()) < 0
527 			 || (status = docmd(1)) < 0
528 			 || (status > 0
529 			 && (status = doprint(curln, curln, status)) < 0))
530 				return status;
531 	}
532 	return ((interact & ~GLB ) && doprint(curln, curln, interact) < 0) ? ERR : curln;
533 }
534 
535 
536 #ifdef BACKWARDS
537 /* GETLINE3: get a legal address from the command buffer */
538 #define GETLINE3(num) \
539 { \
540 	long ol1, ol2; \
541 \
542 	ol1 = line1, ol2 = line2; \
543 	if (getlist() < 0) \
544 		return ERR; \
545 	else if (nlines == 0) { \
546 		sprintf(errmsg, "destination expected"); \
547 		return ERR; \
548 	} else if (line2 < 0 || lastln < line2) { \
549 		sprintf(errmsg, "invalid address"); \
550 		return ERR; \
551 	} \
552 	num = line2; \
553 	line1 = ol1, line2 = ol2; \
554 }
555 #else	/* BACKWARDS */
556 /* GETLINE3: get a legal address from the command buffer */
557 #define GETLINE3(num) \
558 { \
559 	long ol1, ol2; \
560 \
561 	ol1 = line1, ol2 = line2; \
562 	if (getlist() < 0) \
563 		return ERR; \
564 	if (line2 < 0 || lastln < line2) { \
565 		sprintf(errmsg, "invalid address"); \
566 		return ERR; \
567 	} \
568 	num = line2; \
569 	line1 = ol1, line2 = ol2; \
570 }
571 #endif
572 
573 /* sgflags */
574 #define SGG 001		/* complement previous global substitute suffix */
575 #define SGP 002		/* complement previous print suffix */
576 #define SGR 004		/* use last regex instead of last pat */
577 #define SGF 010		/* newline found */
578 
579 long ucurln = -1;	/* if >= 0, undo enabled */
580 long ulastln = -1;	/* if >= 0, undo enabled */
581 int patlock = 0;	/* if set, pattern not released by optpat() */
582 
583 long rows = 22;		/* scroll length: ws_row - 2 */
584 
585 /* docmd: execute the next command in command buffer; return print
586    request, if any */
docmd(glob)587 docmd(glob)
588 	int glob;
589 {
590 	static pattern_t *pat = NULL;
591 	static int sgflag = 0;
592 
593 	pattern_t *tpat;
594 	char *fnp;
595 	int gflag = 0;
596 	int sflags = 0;
597 	long num = 0;
598 	int n = 0;
599 	int c;
600 
601 	skipblanks();
602 	switch(c = *ibufp++) {
603 	case 'a':
604 		VRFYCMD();
605 		if (!glob) ureset();
606 		if (append(line2, glob) < 0)
607 			return ERR;
608 		break;
609 	case 'c':
610 		if (ckrange(curln, curln) < 0)
611 			return ERR;
612 		VRFYCMD();
613 		if (!glob) ureset();
614 		if (lndelete(line1, line2) < 0 || append(curln, glob) < 0)
615 			return ERR;
616 		break;
617 	case 'd':
618 		if (ckrange(curln, curln) < 0)
619 			return ERR;
620 		VRFYCMD();
621 		if (!glob) ureset();
622 		if (lndelete(line1, line2) < 0)
623 			return ERR;
624 		else if (nextln(curln, lastln) != 0)
625 			curln = nextln(curln, lastln);
626 		modified = 1;
627 		break;
628 	case 'e':
629 		if (modified && !scripted)
630 			return EMOD;
631 		/* fall through */
632 	case 'E':
633 		if (nlines > 0) {
634 			sprintf(errmsg, "unexpected address");
635 			return ERR;
636 		} else if (!isspace(*ibufp)) {
637 			sprintf(errmsg, "unexpected command suffix");
638 			return ERR;
639 		} else if ((fnp = getfn()) == NULL)
640 			return ERR;
641 		VRFYCMD();
642 		if (lndelete(1, lastln) < 0)
643 			return ERR;
644 		ureset();
645 		if (sbclose() < 0)
646 			return ERR;
647 		else if (sbopen() < 0)
648 			return FATAL;
649 		if (*fnp && *fnp != '!') strcpy(dfn, fnp);
650 #ifdef BACKWARDS
651 		if (*fnp == '\0' && *dfn == '\0') {
652 			sprintf(errmsg, "no current filename");
653 			return ERR;
654 		}
655 #endif
656 		if (doread(0, *fnp ? fnp : dfn) < 0)
657 			return ERR;
658 		ureset();
659 		modified = 0;
660 		ucurln = ulastln = -1;
661 		break;
662 	case 'f':
663 		if (nlines > 0) {
664 			sprintf(errmsg, "unexpected address");
665 			return ERR;
666 		} else if (!isspace(*ibufp)) {
667 			sprintf(errmsg, "unexpected command suffix");
668 			return ERR;
669 		} else if ((fnp = getfn()) == NULL)
670 			return ERR;
671 		else if (*fnp == '!') {
672 			sprintf(errmsg, "invalid redirection");
673 			return ERR;
674 		}
675 		VRFYCMD();
676 		if (*fnp) strcpy(dfn, fnp);
677 		printf("%s\n", esctos(dfn));
678 		break;
679 	case 'g':
680 	case 'G':
681 		sprintf(errmsg, "cannot nest global commands");
682 		return ERR;
683 	case 'h':
684 		if (nlines > 0) {
685 			sprintf(errmsg, "unexpected address");
686 			return ERR;
687 		}
688 		VRFYCMD();
689 		if (*errmsg) fprintf(stderr, "%s\n", errmsg);
690 		break;
691 	case 'H':
692 		if (nlines > 0) {
693 			sprintf(errmsg, "unexpected address");
694 			return ERR;
695 		}
696 		VRFYCMD();
697 		if ((garrulous = 1 - garrulous) && *errmsg)
698 			fprintf(stderr, "%s\n", errmsg);
699 		break;
700 	case 'i':
701 		if (line2 == 0) {
702 			sprintf(errmsg, "invalid address");
703 			return ERR;
704 		}
705 		VRFYCMD();
706 		if (!glob) ureset();
707 		if (append(prevln(line2, lastln), glob) < 0)
708 			return ERR;
709 		break;
710 	case 'j':
711 		if (ckrange(curln, curln + 1) < 0)
712 			return ERR;
713 		VRFYCMD();
714 		if (!glob) ureset();
715 		if (line1 != line2 && join(line1, line2) < 0)
716 			return ERR;
717 		break;
718 	case 'k':
719 		c = *ibufp++;
720 		if (line2 == 0) {
721 			sprintf(errmsg, "invalid address");
722 			return ERR;
723 		}
724 		VRFYCMD();
725 		if (putmark(c, getlp(line2)) < 0)
726 			return ERR;
727 		break;
728 	case 'l':
729 		if (ckrange(curln, curln) < 0)
730 			return ERR;
731 		VRFYCMD();
732 		if (doprint(line1, line2, gflag | GLS) < 0)
733 			return ERR;
734 		gflag = 0;
735 		break;
736 	case 'm':
737 		if (ckrange(curln, curln) < 0)
738 			return ERR;
739 		GETLINE3(num);
740 		if (line1 <= num && num < line2) {
741 			sprintf(errmsg, "invalid destination");
742 			return ERR;
743 		}
744 		VRFYCMD();
745 		if (!glob) ureset();
746 		if (move(num, glob) < 0)
747 			return ERR;
748 		else
749 			modified = 1;
750 		break;
751 	case 'n':
752 		if (ckrange(curln, curln) < 0)
753 			return ERR;
754 		VRFYCMD();
755 		if (doprint(line1, line2, gflag | GNP) < 0)
756 			return ERR;
757 		gflag = 0;
758 		break;
759 	case 'p':
760 		if (ckrange(curln, curln) < 0)
761 			return ERR;
762 		VRFYCMD();
763 		if (doprint(line1, line2, gflag | GPR) < 0)
764 			return ERR;
765 		gflag = 0;
766 		break;
767 	case 'P':
768 		if (nlines > 0) {
769 			sprintf(errmsg, "unexpected address");
770 			return ERR;
771 		}
772 		VRFYCMD();
773 		prompt = prompt ? NULL : optarg ? optarg : dps;
774 		break;
775 	case 'q':
776 	case 'Q':
777 		if (nlines > 0) {
778 			sprintf(errmsg, "unexpected address");
779 			return ERR;
780 		}
781 		VRFYCMD();
782 		gflag =  (modified && !scripted && c == 'q') ? EMOD : EOF;
783 		break;
784 	case 'r':
785 		if (!isspace(*ibufp)) {
786 			sprintf(errmsg, "unexpected command suffix");
787 			return ERR;
788 		} else if (nlines == 0)
789 			line2 = lastln;
790 		if ((fnp = getfn()) == NULL)
791 			return ERR;
792 		VRFYCMD();
793 		if (!glob) ureset();
794 		if (*dfn == '\0' && *fnp != '!') strcpy(dfn, fnp);
795 #ifdef BACKWARDS
796 		if (*fnp == '\0' && *dfn == '\0') {
797 			sprintf(errmsg, "no current filename");
798 			return ERR;
799 		}
800 #endif
801 		if ((num = doread(line2, *fnp ? fnp : dfn)) < 0)
802 			return ERR;
803 		else if (num && num != lastln)
804 			modified = 1;
805 		break;
806 	case 's':
807 		do {
808 			switch(*ibufp) {
809 			case '\n':
810 				sflags |=SGF;
811 				break;
812 			case 'g':
813 				sflags |= SGG;
814 				ibufp++;
815 				break;
816 			case 'p':
817 				sflags |= SGP;
818 				ibufp++;
819 				break;
820 			case 'r':
821 				sflags |= SGR;
822 				ibufp++;
823 				break;
824 			default:
825 				if (sflags) {
826 					sprintf(errmsg, "invalid command suffix");
827 					return ERR;
828 				}
829 			}
830 		} while (sflags && *ibufp != '\n');
831 		if (sflags && !pat) {
832 			sprintf(errmsg, "no previous substitution");
833 			return ERR;
834 		} else if (!(sflags & SGF))
835 			sgflag &= 0xff;
836 		if (*ibufp != '\n' && *(ibufp + 1) == '\n') {
837 			sprintf(errmsg, "invalid pattern delimiter");
838 			return ERR;
839 		}
840 		tpat = pat;
841 		spl1();
842 		if ((!sflags || (sflags & SGR))
843 		 && (tpat = optpat()) == NULL)
844 			return ERR;
845 		else if (tpat != pat) {
846 			if (pat) {
847 				 regfree(pat);
848 				 free(pat);
849 			 }
850 			pat = tpat;
851 			patlock = 1;		/* reserve pattern */
852 		} else if (pat == NULL) {
853 			/* NOTREACHED */
854 			sprintf(errmsg, "no previous substitution");
855 			return ERR;
856 		}
857 		spl0();
858 		if (!sflags && (sgflag = getrhs(glob)) < 0)
859 			return ERR;
860 		else if (glob)
861 			sgflag |= GLB;
862 		else
863 			sgflag &= ~GLB;
864 		if (sflags & SGG)
865 			sgflag ^= GSG;
866 		if (sflags & SGP)
867 			sgflag ^= GPR, sgflag &= ~(GLS | GNP);
868 		do {
869 			switch(*ibufp) {
870 			case 'p':
871 				sgflag |= GPR, ibufp++;
872 				break;
873 			case 'l':
874 				sgflag |= GLS, ibufp++;
875 				break;
876 			case 'n':
877 				sgflag |= GNP, ibufp++;
878 				break;
879 			default:
880 				n++;
881 			}
882 		} while (!n);
883 		if (ckrange(curln, curln) < 0)
884 			return ERR;
885 		VRFYCMD();
886 		if (!glob) ureset();
887 		if ((n = subst(pat, sgflag)) < 0)
888 			return ERR;
889 		else if (n)
890 			modified = 1;
891 		break;
892 	case 't':
893 		if (ckrange(curln, curln) < 0)
894 			return ERR;
895 		GETLINE3(num);
896 		VRFYCMD();
897 		if (!glob) ureset();
898 		if (transfer(num) < 0)
899 			return ERR;
900 		modified = 1;
901 		break;
902 	case 'u':
903 		if (nlines > 0) {
904 			sprintf(errmsg, "unexpected address");
905 			return ERR;
906 		}
907 		VRFYCMD();
908 		if (undo(glob) < 0)
909 			return ERR;
910 		break;
911 	case 'v':
912 	case 'V':
913 		sprintf(errmsg, "cannot nest global commands");
914 		return ERR;
915 	case 'w':
916 	case 'W':
917 		if ((n = *ibufp) == 'q' || n == 'Q') {
918 			gflag = EOF;
919 			ibufp++;
920 		}
921 		if (!isspace(*ibufp)) {
922 			sprintf(errmsg, "unexpected command suffix");
923 			return ERR;
924 		} else if ((fnp = getfn()) == NULL)
925 			return ERR;
926 		if (nlines == 0 && !lastln)
927 			line1 = line2 = 0;
928 		else if (ckrange(1, lastln) < 0)
929 			return ERR;
930 		VRFYCMD();
931 		if (*dfn == '\0' && *fnp != '!') strcpy(dfn, fnp);
932 #ifdef BACKWARDS
933 		if (*fnp == '\0' && *dfn == '\0') {
934 			sprintf(errmsg, "no current filename");
935 			return ERR;
936 		}
937 #endif
938 		if ((num = dowrite(line1, line2, *fnp ? fnp : dfn, (c == 'W') ? "a" : "w")) < 0)
939 			return ERR;
940 		else if (num == lastln)
941 			modified = 0;
942 		else if (modified && !scripted && n == 'q')
943 			gflag = EMOD;
944 		break;
945 	case 'x':
946 		if (nlines > 0) {
947 			sprintf(errmsg, "unexpected address");
948 			return ERR;
949 		}
950 		VRFYCMD();
951 #ifdef DES
952 		des = getkey();
953 #else
954 		sprintf(errmsg, "crypt unavailable");
955 		return ERR;
956 #endif
957 		break;
958 	case 'z':
959 #ifdef BACKWARDS
960 		if (ckrange(line1 = 1, curln + 1) < 0)
961 #else
962 		if (ckrange(line1 = 1, curln + !glob) < 0)
963 #endif
964 			return ERR;
965 		else if ('0' < *ibufp && *ibufp <= '9')
966 			rows = strtol(ibufp, &ibufp, 10);
967 		VRFYCMD();
968 		if (doprint(line2, min(lastln, line2 + rows - 1), gflag) < 0)
969 			return ERR;
970 		gflag = 0;
971 		break;
972 	case '=':
973 		VRFYCMD();
974 		printf("%d\n", nlines ? line2 : lastln);
975 		break;
976 	case '!':
977 #ifndef VI_BANG
978 		if (nlines > 0) {
979 			sprintf(errmsg, "unexpected address");
980 			return ERR;
981 		}
982 #endif
983 		if ((sflags = getshcmd()) < 0)
984 			return ERR;
985 		VRFYCMD();
986 		if (sflags) printf("%s\n", shcmd + 1);
987 #ifdef VI_BANG
988 		if (nlines == 0) {
989 #endif
990 			system(shcmd + 1);
991 			if (!scripted) printf("!\n");
992 			break;
993 #ifdef VI_BANG
994 		}
995 		if (!lastln && !line1 && !line2) {
996 			if (!glob) ureset();
997 		} else if (ckrange(curln, curln) < 0)
998 			return ERR;
999 		else {
1000 			if (!glob) ureset();
1001 			if (lndelete(line1, line2) < 0)
1002 				return ERR;
1003 			line2 = curln;
1004 			modified = 1;
1005 		}
1006 		if ((num = doread(line2, shcmd)) < 0)
1007 			return ERR;
1008 		else if (num && num != lastln)
1009 			modified = 1;
1010 		break;
1011 #endif
1012 	case '\n':
1013 #ifdef BACKWARDS
1014 		if (ckrange(line1 = 1, curln + 1) < 0
1015 #else
1016 		if (ckrange(line1 = 1, curln + !glob) < 0
1017 #endif
1018 		 || doprint(line2, line2, 0) < 0)
1019 			return ERR;
1020 		break;
1021 	default:
1022 		sprintf(errmsg, "unknown command");
1023 		return ERR;
1024 	}
1025 	return gflag;
1026 }
1027 
1028 
1029 /* ckrange: return status of line number range check */
ckrange(def1,def2)1030 ckrange(def1, def2)
1031 	long def1, def2;
1032 {
1033 	if (nlines == 0) {
1034 		line1 = def1;
1035 		line2 = def2;
1036 	}
1037 	if (line1 > line2 || 1 > line1 || line2 > lastln) {
1038 		sprintf(errmsg, "invalid address");
1039 		return ERR;
1040 	}
1041 	return 0;
1042 }
1043 
1044 
1045 /* patscan: return the number of the next line matching a pattern in a
1046    given direction.  wrap around begin/end of line queue if necessary */
1047 long
patscan(pat,dir)1048 patscan(pat, dir)
1049 	pattern_t *pat;
1050 	int dir;
1051 {
1052 	char *s;
1053 	long n = curln;
1054 	line_t *lp;
1055 
1056 	do {
1057 		if (n = dir ? nextln(n, lastln) : prevln(n, lastln)) {
1058 			if ((s = gettxt(lp = getlp(n))) == NULL)
1059 				return ERR;
1060 			if (isbinary)
1061 				s = nultonl(s, lp->len & ~ACTV);
1062 			if (!regexec(pat, s, 0, NULL, 0))
1063 				return n;
1064 		}
1065 	} while (n != curln);
1066 	sprintf(errmsg, "no match");
1067 	return  ERR;
1068 }
1069 
1070 
1071 /* getfn: return pointer to copy of filename in the command buffer */
1072 char *
getfn()1073 getfn()
1074 {
1075 	static char *file = NULL;
1076 	static int filesz = 0;
1077 
1078 	int n;
1079 
1080 	if (*ibufp != '\n') {
1081 		skipblanks();
1082 		if (*ibufp == '\n') {
1083 			sprintf(errmsg, "invalid filename");
1084 			return NULL;
1085 		} else if ((ibufp = getcmdv(&n, 1)) == NULL)
1086 			return NULL;
1087 #ifdef VI_BANG
1088 		else if (*ibufp == '!') {
1089 			ibufp++;
1090 			if ((n = getshcmd()) < 0)
1091 				return NULL;
1092 			if (n) printf("%s\n", shcmd + 1);
1093 			return shcmd;
1094 		}
1095 #endif
1096 		else if (n - 1 > MAXFNAME) {
1097 			sprintf(errmsg, "filename too long");
1098 			return  NULL;
1099 		}
1100 	}
1101 #ifndef BACKWARDS
1102 	else if (*dfn == '\0') {
1103 		sprintf(errmsg, "no current filename");
1104 		return  NULL;
1105 	}
1106 #endif
1107 	CKBUF(file, filesz, MAXFNAME + 1, NULL);
1108 	for (n = 0; *ibufp != '\n';)
1109 		file[n++] = *ibufp++;
1110 	file[n] = '\0';
1111 	return ckfn(file);
1112 }
1113 
1114 
1115 /* getrhs: extract substitution template from the command buffer */
getrhs(glob)1116 getrhs(glob)
1117 	int glob;
1118 {
1119 	char delim;
1120 
1121 	if ((delim = *ibufp) == '\n') {
1122 		rhbufi = 0;
1123 		return GPR;
1124 	} else if (makesub(glob) == NULL)
1125 		return  ERR;
1126 	else if (*ibufp == '\n')
1127 		return GPR;
1128 	else if (*ibufp == delim)
1129 		ibufp++;
1130 	if ('1' <= *ibufp && *ibufp <= '9')
1131 		return (int) strtol(ibufp, &ibufp, 10) << 8;
1132 	else if (*ibufp == 'g') {
1133 		ibufp++;
1134 		return GSG;
1135 	}
1136 	return  0;
1137 }
1138 
1139 
1140 /* makesub: return pointer to copy of substitution template in the command
1141    buffer */
1142 char *
makesub(glob)1143 makesub(glob)
1144 	int glob;
1145 {
1146 	int n = 0;
1147 	int i = 0;
1148 	char delim = *ibufp++;
1149 	char c;
1150 
1151 	if (*ibufp == '%' && *(ibufp + 1) == delim) {
1152 		ibufp++;
1153 		if (!rhbuf) sprintf(errmsg, "no previous substitution");
1154 		return rhbuf;
1155 	}
1156 	while (*ibufp != delim) {
1157 		CKBUF(rhbuf, rhbufsz, i + 2, NULL);
1158 		if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0') {
1159 			i--, ibufp--;
1160 			break;
1161 		} else if (c != '\\')
1162 			;
1163 		else if ((rhbuf[i++] = *ibufp++) != '\n')
1164 			;
1165 		else if (!glob) {
1166 			while ((n = getline()) == 0
1167 			    || n > 0 && ibuf[n - 1] != '\n')
1168 				clearerr(stdin);
1169 			if (n < 0)
1170 				return NULL;
1171 		} else
1172 			/*NOTREACHED*/
1173 			;
1174 	}
1175 	CKBUF(rhbuf, rhbufsz, i + 1, NULL);
1176 	rhbuf[rhbufi = i] = '\0';
1177 	return  rhbuf;
1178 }
1179 
1180 
1181 /* getshcmd: read a shell command up a maximum size from stdin; return
1182    substitution status */
1183 int
getshcmd()1184 getshcmd()
1185 {
1186 	static char *buf = NULL;
1187 	static int n = 0;
1188 
1189 	char *s;			/* substitution char pointer */
1190 	int i = 0;
1191 	int j = 0;
1192 
1193 	if (red) {
1194 		sprintf(errmsg, "shell access restricted");
1195 		return ERR;
1196 	} else if ((s = ibufp = getcmdv(&j, 1)) == NULL)
1197 		return ERR;
1198 	CKBUF(buf, n, j + 1, ERR);
1199 	buf[i++] = '!';			/* prefix command w/ bang */
1200 	while (*ibufp != '\n')
1201 		switch (*ibufp) {
1202 		default:
1203 			CKBUF(buf, n, i + 2, ERR);
1204 			buf[i++] = *ibufp;
1205 			if (*ibufp++ == '\\')
1206 				buf[i++] = *ibufp++;
1207 			break;
1208 		case '!':
1209 			if (s != ibufp) {
1210 				CKBUF(buf, n, i + 1, ERR);
1211 				buf[i++] = *ibufp++;
1212 			}
1213 #ifdef BACKWARDS
1214 			else if (shcmd == NULL || *(shcmd + 1) == '\0')
1215 #else
1216 			else if (shcmd == NULL)
1217 #endif
1218 			{
1219 				sprintf(errmsg, "no previous command");
1220 				return ERR;
1221 			} else {
1222 				CKBUF(buf, n, i + shcmdi, ERR);
1223 				for (s = shcmd + 1; s < shcmd + shcmdi;)
1224 					buf[i++] = *s++;
1225 				s = ibufp++;
1226 			}
1227 			break;
1228 		case '%':
1229 			if (*dfn  == '\0') {
1230 				sprintf(errmsg, "no current filename");
1231 				return ERR;
1232 			}
1233 			j = strlen(s = esctos(dfn));
1234 			CKBUF(buf, n, i + j, ERR);
1235 			while (j--)
1236 				buf[i++] = *s++;
1237 			s = ibufp++;
1238 			break;
1239 		}
1240 	CKBUF(shcmd, shcmdsz, i + 1, ERR);
1241 	memcpy(shcmd, buf, i);
1242 	shcmd[shcmdi = i] = '\0';
1243 	return *s == '!' || *s == '%';
1244 }
1245 
1246 
1247 /* append: insert text from stdin to after line n; stop when either a
1248    single period is read or EOF; return status */
append(n,glob)1249 append(n, glob)
1250 	long n;
1251 	int  glob;
1252 {
1253 	int l;
1254 	char *lp = ibuf;
1255 	char *eot;
1256 	undo_t *up = NULL;
1257 
1258 	for (curln = n;;) {
1259 		if (!glob) {
1260 			if ((l = getline()) < 0)
1261 				return ERR;
1262 			else if (l == 0 || ibuf[l - 1] != '\n') {
1263 				clearerr(stdin);
1264 				return  l ? EOF : 0;
1265 			}
1266 			lp = ibuf;
1267 		} else if (*(lp = ibufp) == '\0')
1268 			return 0;
1269 		else {
1270 			while (*ibufp++ != '\n')
1271 				;
1272 			l = ibufp - lp;
1273 		}
1274 		if (l == 2 && lp[0] == '.' && lp[1] == '\n') {
1275 			return 0;
1276 		}
1277 		eot = lp + l;
1278 		spl1();
1279 		do {
1280 			if ((lp = puttxt(lp)) == NULL) {
1281 				spl0();
1282 				return ERR;
1283 			} else if (up)
1284 				up->t = getlp(curln);
1285 			else if ((up = upush(UADD, curln, curln)) == NULL) {
1286 				spl0();
1287 				return ERR;
1288 			}
1289 		} while (lp != eot);
1290 		spl0();
1291 		modified = 1;
1292 	}
1293 }
1294 
1295 
1296 /* subst: change all text matching a pattern in a range of lines according to
1297    a substitution template; return status  */
subst(pat,gflag)1298 subst(pat, gflag)
1299 	pattern_t *pat;
1300 	int gflag;
1301 {
1302 	undo_t *up;
1303 	char *txt;
1304 	char *eot;
1305 	long lc;
1306 	int nsubs = 0;
1307 	line_t *lp;
1308 	int len;
1309 
1310 	curln = prevln(line1, lastln);
1311 	for (lc = 0; lc <= line2 - line1; lc++) {
1312 		lp = getlp(curln = nextln(curln, lastln));
1313 		if ((len = regsub(pat, lp, gflag)) < 0)
1314 			return ERR;
1315 		else if (len) {
1316 			up = NULL;
1317 			if (lndelete(curln, curln) < 0)
1318 				return ERR;
1319 			txt = rbuf;
1320 			eot = rbuf + len;
1321 			spl1();
1322 			do {
1323 				if ((txt = puttxt(txt)) == NULL) {
1324 					spl0();
1325 					return ERR;
1326 				} else if (up)
1327 					up->t = getlp(curln);
1328 				else if ((up = upush(UADD, curln, curln)) == NULL) {
1329 					spl0();
1330 					return ERR;
1331 				}
1332 			} while (txt != eot);
1333 			spl0();
1334 			nsubs++;
1335 		}
1336 	}
1337 	if  (nsubs == 0 && !(gflag & GLB)) {
1338 		sprintf(errmsg, "no match");
1339 		return ERR;
1340 	} else if ((gflag & (GPR | GLS | GNP))
1341 	 && doprint(curln, curln, gflag) < 0)
1342 		return ERR;
1343 	return 1;
1344 }
1345 
1346 
1347 /* regsub: replace text matched by a pattern according to a substitution
1348    template; return pointer to the modified text */
regsub(pat,lp,gflag)1349 regsub(pat, lp, gflag)
1350 	pattern_t *pat;
1351 	line_t *lp;
1352 	int gflag;
1353 {
1354 	int off = 0;
1355 	int kth = gflag >> 8;		/* substitute kth match only */
1356 	int chngd = 0;
1357 	int matchno = 0;
1358 	int len;
1359 	int i = 0;
1360 	regmatch_t rm[SE_MAX];
1361 	char *txt;
1362 	char *eot;
1363 
1364 	if ((txt = gettxt(lp)) == NULL)
1365 		return ERR;
1366 	len = lp->len & ~ACTV;
1367 	eot = txt + len;
1368 	if (isbinary) txt = nultonl(txt, len);
1369 	if (!regexec(pat, txt, SE_MAX, rm, 0)) {
1370 		do {
1371 			if (!kth || kth == ++matchno) {
1372 				chngd++;
1373 				i = rm[0].rm_so;
1374 				CKBUF(rbuf, rbufsz, off + i, ERR);
1375 				if (isbinary) txt = nltonul(txt, rm[0].rm_eo);
1376 				memcpy(rbuf + off, txt, i);
1377 				if ((off = catsub(txt, rm, off += i)) < 0)
1378 					return ERR;
1379 			} else {
1380 				i = rm[0].rm_eo;
1381 				CKBUF(rbuf, rbufsz, off + i, ERR);
1382 				if (isbinary) txt = nltonul(txt, i);
1383 				memcpy(rbuf + off, txt, i);
1384 				off += i;
1385 			}
1386 			txt += rm[0].rm_eo;
1387 		} while (*txt && (!chngd || (gflag & GSG) && rm[0].rm_eo)
1388 		      && !regexec(pat, txt, SE_MAX, rm, REG_NOTBOL));
1389 		i = eot - txt;
1390 		CKBUF(rbuf, rbufsz, off + i + 2, ERR);
1391 		if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) {
1392 			sprintf(errmsg, "infinite substitution loop");
1393 			return  ERR;
1394 		}
1395 		if (isbinary) txt = nltonul(txt, i);
1396 		memcpy(rbuf + off, txt, i);
1397 		memcpy(rbuf + off + i, "\n", 2);
1398 	}
1399 	return chngd ? off + i + 1 : 0;
1400 }
1401 
1402 
1403 /* join: replace a range of lines with the joined text of those lines */
join(from,to)1404 join(from, to)
1405 	long from;
1406 	long to;
1407 {
1408 	static char *buf = NULL;
1409 	static int n;
1410 
1411 	char *s;
1412 	int len = 0;
1413 	int size = 0;
1414 	line_t *bp, *ep;
1415 
1416 	ep = getlp(nextln(to, lastln));
1417 	for (bp = getlp(from); bp != ep; bp = bp->next, size += len) {
1418 		if ((s = gettxt(bp)) == NULL)
1419 			return ERR;
1420 		len = bp->len & ~ACTV;
1421 		CKBUF(buf, n, size + len, ERR);
1422 		memcpy(buf + size, s, len);
1423 	}
1424 	CKBUF(buf, n, size + 2, ERR);
1425 	memcpy(buf + size, "\n", 2);
1426 	if (lndelete(from, to) < 0)
1427 		return ERR;
1428 	curln = from - 1;
1429 	spl1();
1430 	if (puttxt(buf) == NULL
1431 	 || upush(UADD, curln, curln) == NULL) {
1432 		spl0();
1433 		return ERR;
1434 	}
1435 	spl0();
1436 	modified = 1;
1437 	return 0;
1438 }
1439 
1440 
1441 /* move: move a range of lines */
move(num,glob)1442 move(num, glob)
1443 	long num;
1444 	int glob;
1445 {
1446 	line_t *b1, *a1, *b2, *a2, *lp;
1447 	long n = nextln(line2, lastln);
1448 	long p = prevln(line1, lastln);
1449 	int done = (num == line1 - 1 || num == line2);
1450 
1451 	spl1();
1452 	if (done) {
1453 		a2 = getlp(n);
1454 		b2 = getlp(p);
1455 		curln = line2;
1456 	} else if (upush(UMOV, p, n) == NULL
1457 	 || upush(UMOV, num, nextln(num, lastln)) == NULL) {
1458 		spl0();
1459 		return ERR;
1460 	} else {
1461 		a1 = getlp(n);
1462 		if (num < line1)
1463 			b1 = getlp(p), b2 = getlp(num);	/* this getlp last! */
1464 		else	b2 = getlp(num), b1 = getlp(p);	/* this getlp last! */
1465 		a2 = b2->next;
1466 		requeue(b2, b1->next);
1467 		requeue(a1->prev, a2);
1468 		requeue(b1, a1);
1469 		curln = num + ((num < line1) ? line2 - line1 + 1 : 0);
1470 	}
1471 	if (glob)
1472 		for (lp = b2->next; lp != a2; lp = lp->next)
1473 			lp->len &= ~ACTV;		/* zero ACTV  bit */
1474 	spl0();
1475 	return 0;
1476 }
1477 
1478 
1479 /* transfer: copy a range of lines; return status */
transfer(num)1480 transfer(num)
1481 	long num;
1482 {
1483 	line_t *lp;
1484 	long nl, nt, lc;
1485 	long mid = (num < line2) ? num : line2;
1486 	undo_t *up = NULL;
1487 
1488 	curln = num;
1489 	for (nt = 0, nl = line1; nl <= mid; nl++, nt++) {
1490 		spl1();
1491 		if ((lp = lpdup(getlp(nl))) == NULL) {
1492 			spl0();
1493 			return ERR;
1494 		}
1495 		lpqueue(lp);
1496 		if (up)
1497 			up->t = lp;
1498 		else if ((up = upush(UADD, curln, curln)) == NULL) {
1499 			spl0();
1500 			return ERR;
1501 		}
1502 		spl0();
1503 	}
1504 	for (nl += nt, lc = line2 + nt; nl <= lc; nl += 2, lc++) {
1505 		spl1();
1506 		if ((lp = lpdup(getlp(nl))) == NULL) {
1507 			spl0();
1508 			return ERR;
1509 		}
1510 		lpqueue(lp);
1511 		if (up)
1512 			up->t = lp;
1513 		else if ((up = upush(UADD, curln, curln)) == NULL) {
1514 			spl0();
1515 			return ERR;
1516 		}
1517 		spl0();
1518 	}
1519 	return 0;
1520 }
1521 
1522 
1523 /* lndelete: delete a range of lines */
lndelete(from,to)1524 lndelete(from, to)
1525 	long from, to;
1526 {
1527 	line_t *before, *after;
1528 
1529 	spl1();
1530 	if (upush(UDEL, from, to) == NULL) {
1531 		spl0();
1532 		return ERR;
1533 	}
1534 	after = getlp(nextln(to, lastln));
1535 	before = getlp(prevln(from, lastln));		/* this getlp last! */
1536 	requeue(before, after);
1537 	lastln -= to - from + 1;
1538 	curln = prevln(from, lastln);
1539 	spl0();
1540 	return 0;
1541 }
1542 
1543 
1544 /* catsub: modify text according to a substitution template;
1545    return offset to end of modified text */
catsub(boln,rm,off)1546 catsub(boln, rm, off)
1547 	char *boln;
1548 	regmatch_t *rm;
1549 	int off;
1550 {
1551 	int j = 0;
1552 	int k = 0;
1553 	char *sub = rhbuf;
1554 
1555 	for (; sub - rhbuf < rhbufi; sub++)
1556 		if (*sub == '&') {
1557 			j = rm[0].rm_so;
1558 			k = rm[0].rm_eo;
1559 			CKBUF(rbuf, rbufsz, off + k - j, ERR);
1560 			while (j < k)
1561 				rbuf[off++] = boln[j++];
1562 		} else if (*sub == '\\' && '1' <= *++sub && *sub <= '9'
1563 		      && rm[*sub - '0'].rm_so >= 0
1564 		      && rm[*sub - '0'].rm_eo >= 0) {
1565 			j = rm[*sub - '0'].rm_so;
1566 			k = rm[*sub - '0'].rm_eo;
1567 			CKBUF(rbuf, rbufsz, off + k - j, ERR);
1568 			while (j < k)
1569 				rbuf[off++] = boln[j++];
1570 		} else {
1571 			CKBUF(rbuf, rbufsz, off + 1, ERR);
1572 			rbuf[off++] = *sub;
1573 		}
1574 	CKBUF(rbuf, rbufsz, off + 1, ERR);
1575 	rbuf[off] = '\0';
1576 	return off;
1577 }
1578 
1579 /* doprint: print a range of lines to stdout */
doprint(from,to,gflag)1580 doprint(from, to, gflag)
1581 	long from;
1582 	long to;
1583 	int gflag;
1584 {
1585 	line_t *bp;
1586 	line_t *ep;
1587 	char *s;
1588 
1589 	if (!from) {
1590 		sprintf(errmsg, "invalid address");
1591 		return ERR;
1592 	}
1593 	ep = getlp(nextln(to, lastln));
1594 	for (bp = getlp(from); bp != ep; bp = bp->next) {
1595 		if ((s = gettxt(bp)) == NULL)
1596 			return ERR;
1597 		putstr(s, bp->len & ~ACTV, curln = from++, gflag);
1598 	}
1599 	return 0;
1600 }
1601 
1602 
1603 int cols = 72;		/* wrap column: ws_col - 8 */
1604 
1605 /* putstr: print text to stdout */
1606 void
putstr(s,l,n,gflag)1607 putstr(s, l, n, gflag)
1608 	char *s;
1609 	int l;
1610 	long n;
1611 	int gflag;
1612 {
1613 	int col = 0;
1614 
1615 	if (gflag & GNP) {
1616 		printf("%ld\t", n);
1617 		col = 8;
1618 	}
1619 	for (; l--; s++) {
1620 		if ((gflag & GLS) && ++col > cols) {
1621 			fputs("\\\n", stdout);
1622 			col = 1;
1623 		}
1624 		if (gflag & GLS) {
1625 			switch (*s) {
1626 			case '\b':
1627 				fputs("\\b", stdout);
1628 				break;
1629 			case '\f':
1630 				fputs("\\f", stdout);
1631 				break;
1632 			case '\n':
1633 				fputs("\\n", stdout);
1634 				break;
1635 			case '\r':
1636 				fputs("\\r", stdout);
1637 				break;
1638 			case '\t':
1639 				fputs("\\t", stdout);
1640 				break;
1641 			case '\v':
1642 				fputs("\\v", stdout);
1643 				break;
1644 			default:
1645 				if (*s < 32 || 126 < *s) {
1646 					putchar('\\');
1647 					putchar((((unsigned char) *s & 0300) >> 6) + '0');
1648 					putchar((((unsigned char) *s & 070) >> 3) + '0');
1649 					putchar(((unsigned char) *s & 07) + '0');
1650 					col += 2;
1651 				} else if (*s == '\\')
1652 					fputs("\\\\", stdout);
1653 				else {
1654 					putchar(*s);
1655 					col--;
1656 				}
1657 			}
1658 			col++;
1659 		} else
1660 			putchar(*s);
1661 	}
1662 #ifndef BACKWARDS
1663 	if (gflag & GLS)
1664 		putchar('$');
1665 #endif
1666 	putchar('\n');
1667 }
1668 
1669 
1670 int newline_added;		/* set if newline appended to input file */
1671 
1672 /* doread: read a text file into the editor buffer; return line count */
1673 long
doread(n,fn)1674 doread(n, fn)
1675 	long n;
1676 	char *fn;
1677 {
1678 	FILE *fp;
1679 	line_t *lp = getlp(n);
1680 	unsigned long size = 0;
1681 	undo_t *up = NULL;
1682 	int len;
1683 
1684 	isbinary = newline_added = 0;
1685 	if ((fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(esctos(fn), "r")) == NULL) {
1686 		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1687 		sprintf(errmsg, "cannot open input file");
1688 		return ERR;
1689 	} else if (des)
1690 		desinit();
1691 	for (curln = n; (len = sgetline(fp)) > 0; size += len) {
1692 		spl1();
1693 		if (puttxt(sbuf) == NULL) {
1694 			spl0();
1695 			return ERR;
1696 		}
1697 		lp = lp->next;
1698 		if (up)
1699 			up->t = lp;
1700 		else if ((up = upush(UADD, curln, curln)) == NULL) {
1701 			spl0();
1702 			return ERR;
1703 		}
1704 		spl0();
1705 	}
1706 	if (((*fn == '!') ?  pclose(fp) : fclose(fp)) < 0) {
1707 		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1708 		sprintf(errmsg, "cannot close input file");
1709 		return ERR;
1710 	}
1711 	if (newline_added && !isbinary)
1712 		fputs("newline appended\n", stderr);
1713 	if (des) size += 8 - size % 8;
1714 	fprintf(stderr, !scripted ? "%lu\n" : "", size);
1715 	return  (len < 0) ? ERR : curln - n;
1716 }
1717 
1718 
1719 /* dowrite: write the text of a range of lines to a file; return line count */
1720 long
dowrite(n,m,fn,mode)1721 dowrite(n, m, fn, mode)
1722 	long n;
1723 	long m;
1724 	char *fn;
1725 	char *mode;
1726 {
1727 	FILE *fp;
1728 	line_t *lp;
1729 	unsigned long size = 0;
1730 	long lc = n ? m - n + 1 : 0;
1731 	char *s = NULL;
1732 	int len;
1733 	int ct;
1734 
1735 	if ((fp = ((*fn == '!') ? popen(fn + 1, "w") : fopen(esctos(fn), mode))) == NULL) {
1736 		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1737 		sprintf(errmsg, "cannot open output file");
1738 		return ERR;
1739 	} else if (des)
1740 		desinit();
1741 	if (n && !des)
1742 		for (lp = getlp(n); n <= m; n++, lp = lp->next) {
1743 			if ((s = gettxt(lp)) == NULL)
1744 				return ERR;
1745 			len = lp->len & ~ACTV;
1746 			if (n != lastln || !isbinary || !newline_added)
1747 				s[len++] = '\n';
1748 			if ((ct = fwrite(s, sizeof(char), len, fp)) < 0 || ct != len) {
1749 				fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1750 				sprintf(errmsg, "cannot write file");
1751 				return ERR;
1752 			}
1753 			size += len;
1754 		}
1755 	else if (n)
1756 		for (lp = getlp(n); n <= m; n++, lp = lp->next) {
1757 			if ((s = gettxt(lp)) == NULL)
1758 				return ERR;
1759 			len = lp->len & ~ACTV;
1760 			while (len--) {
1761 				if (desputc(*s++, fp) == EOF && ferror(fp)) {
1762 					fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1763 					sprintf(errmsg, "cannot write file");
1764 					return ERR;
1765 				}
1766 			}
1767 			if (n != lastln || !isbinary || !newline_added) {
1768 				if (desputc('\n', fp) < 0) {
1769 					fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1770 					sprintf(errmsg, "cannot write file");
1771 					return ERR;
1772 				}
1773 				size++;			/* for '\n' */
1774 			}
1775 			size += (lp->len & ~ACTV);
1776 		}
1777 	if (des) {
1778 		desflush(fp);				/* flush buffer */
1779 		size += 8 - size % 8;			/* adjust DES size */
1780 	}
1781 	if (((*fn == '!') ?  pclose(fp) : fclose(fp)) < 0) {
1782 		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
1783 		sprintf(errmsg, "cannot close output file");
1784 		return ERR;
1785 	}
1786 	fprintf(stderr, !scripted ? "%lu\n" : "", size);
1787 	return  lc;
1788 }
1789 
1790 
1791 #define USIZE 100				/* undo stack size */
1792 undo_t *ustack = NULL;				/* undo stack */
1793 long usize = 0;					/* stack size variable */
1794 long u_p = 0;					/* undo stack pointer */
1795 
1796 /* upush: return pointer to intialized undo node */
1797 undo_t *
upush(type,from,to)1798 upush(type, from, to)
1799 	int type;
1800 	long from;
1801 	long to;
1802 {
1803 	undo_t *t;
1804 
1805 #if defined(sun) || defined(NO_REALLOC_NULL)
1806 	if (ustack == NULL
1807 	 && (ustack = (undo_t *) malloc((usize = USIZE) * sizeof(undo_t))) == NULL) {
1808 		fprintf(stderr, "%s\n", strerror(errno));
1809 		sprintf(errmsg, "out of memory");
1810 		return NULL;
1811 	}
1812 #endif
1813 	t = ustack;
1814 	if (u_p < usize
1815 	 || (t = (undo_t *) realloc(ustack, (usize += USIZE) * sizeof(undo_t))) != NULL) {
1816 		ustack = t;
1817 		ustack[u_p].type = type;
1818 		ustack[u_p].t = getlp(to);
1819 		ustack[u_p].h = getlp(from);
1820 		return ustack + u_p++;
1821 	}
1822 	/* out of memory - release undo stack */
1823 	fprintf(stderr, "%s\n", strerror(errno));
1824 	sprintf(errmsg, "out of memory");
1825 	ureset();
1826 	free(ustack);
1827 	ustack = NULL;
1828 	usize = 0;
1829 	return NULL;
1830 }
1831 
1832 
1833 /* USWAP: swap undo nodes */
1834 #define USWAP(x,y) { \
1835 	undo_t utmp; \
1836 	utmp = x, x = y, y = utmp; \
1837 }
1838 
1839 
1840 /* undo: undo last change to the editor buffer */
undo(glob)1841 undo(glob)
1842 	int glob;
1843 {
1844 	long n;
1845 	long ocurln = curln;
1846 	long olastln = lastln;
1847 	line_t *lp, *np;
1848 
1849 	if (ucurln == -1 || ulastln == -1) {
1850 		sprintf(errmsg, "nothing to undo");
1851 		return ERR;
1852 	} else if (u_p)
1853 		modified = 1;
1854 	getlp(0);				/* this getlp last! */
1855 	spl1();
1856 	for (n = u_p; n-- > 0;) {
1857 		switch(ustack[n].type) {
1858 		case UADD:
1859 			requeue(ustack[n].h->prev, ustack[n].t->next);
1860 			break;
1861 		case UDEL:
1862 			requeue(ustack[n].h->prev, ustack[n].h);
1863 			requeue(ustack[n].t, ustack[n].t->next);
1864 			break;
1865 		case UMOV:
1866 		case VMOV:
1867 			requeue(ustack[n - 1].h, ustack[n].h->next);
1868 			requeue(ustack[n].t->prev, ustack[n - 1].t);
1869 			requeue(ustack[n].h, ustack[n].t);
1870 			n--;
1871 			break;
1872 		default:
1873 			/*NOTREACHED*/
1874 			;
1875 		}
1876 		ustack[n].type ^= 1;
1877 	}
1878 	/* reverse undo order */
1879 	for (n = u_p; n-- > (u_p + 1)/ 2;)
1880 		USWAP(ustack[n], ustack[u_p - 1 - n]);
1881 	if (glob)
1882 		for (lp = np = getlp(0); (lp = lp->next) != np;)
1883 			lp->len &= ~ACTV;		/* zero ACTV bit */
1884 	curln = ucurln, ucurln = ocurln;
1885 	lastln = ulastln, ulastln = olastln;
1886 	spl0();
1887 	return 0;
1888 }
1889 
1890 
1891 /* ureset: clear the undo stack */
1892 void
ureset()1893 ureset()
1894 {
1895 	line_t *lp, *ep, *tl;
1896 
1897 	while (u_p--)
1898 		if (ustack[u_p].type == UDEL) {
1899 			ep = ustack[u_p].t->next;
1900 			for (lp = ustack[u_p].h; lp != ep; lp = tl) {
1901 				clrmark(lp);
1902 				tl = lp->next;
1903 				free(lp);
1904 			}
1905 		}
1906 	u_p = 0;
1907 	ucurln = curln;
1908 	ulastln = lastln;
1909 }
1910 
1911 
1912 #define MAXMARK 26			/* max number of marks */
1913 
1914 line_t	*mark[MAXMARK];			/* line markers */
1915 int markno;				/* line marker count */
1916 
1917 /* getmark: return address of a marked line */
1918 long
getmark(n)1919 getmark(n)
1920 	int n;
1921 {
1922 	if (!islower(n)) {
1923 		sprintf(errmsg, "invalid mark character");
1924 		return ERR;
1925 	}
1926 	return getaddr(mark[n - 'a']);
1927 }
1928 
1929 
1930 /* putmark: set a line node mark */
1931 int
putmark(n,lp)1932 putmark(n, lp)
1933 	int n;
1934 	line_t *lp;
1935 {
1936 	if (!islower(n)) {
1937 		sprintf(errmsg, "invalid mark character");
1938 		return ERR;
1939 	} else if (mark[n - 'a'] == NULL)
1940 		markno++;
1941 	mark[n - 'a'] = lp;
1942 	return 0;
1943 }
1944 
1945 
1946 /* clrmark: clear line node marks */
1947 void
clrmark(lp)1948 clrmark(lp)
1949 	line_t *lp;
1950 {
1951 	int i;
1952 
1953 	if (markno)
1954 		for (i = 0; i < MAXMARK; i++)
1955 			if (mark[i] == lp) {
1956 				mark[i] = NULL;
1957 				markno--;
1958 			}
1959 }
1960 
1961 
1962 /* sgetline: read a line of text up a maximum size from a file; return
1963    line length */
sgetline(fp)1964 sgetline(fp)
1965 	FILE *fp;
1966 {
1967 	register int c;
1968 	register int i = 0;
1969 
1970 	while (((c = des ? desgetc(fp) : getc(fp)) != EOF || !feof(fp) && !ferror(fp)) && c != '\n') {
1971 		CKBUF(sbuf, sbufsz, i + 1, ERR);
1972 		if (!(sbuf[i++] = c)) isbinary = 1;
1973 	}
1974 	CKBUF(sbuf, sbufsz, i + 2, ERR);
1975 	if (c == '\n')
1976 		sbuf[i++] = c;
1977 	else if (feof(fp) && i) {
1978 		sbuf[i++] = '\n';
1979 		newline_added = 1;
1980 	} else if (ferror(fp)) {
1981 		fprintf(stderr, "%s\n", strerror(errno));
1982 		sprintf(errmsg, "cannot read input file");
1983 		return ERR;
1984 	}
1985 	sbuf[i] = '\0';
1986 	return (isbinary && newline_added && i) ? --i : i;
1987 }
1988 
1989 
1990 /* getline: read a line of text up a maximum size from stdin; return
1991    line length */
getline()1992 getline()
1993 {
1994 	register int i = 0;
1995 	register int oi = 0;
1996 	char c;
1997 
1998 	/* Read one character at a time to avoid i/o contention with shell
1999 	   escapes invoked by nonterminal input, e.g.,
2000 	   ed - <<EOF
2001 	   !cat
2002 	   hello, world
2003 	   EOF */
2004 	for (;;)
2005 		switch (read(0, &c, 1)) {
2006 		default:
2007 			oi = 0;
2008 			CKBUF(ibuf, ibufsz, i + 2, ERR);
2009 			if (!(ibuf[i++] = c)) isbinary = 1;
2010 			if (c != '\n')
2011 				continue;
2012 			lineno++;		/* script line no. */
2013 			ibuf[i] = '\0';
2014 			ibufp = ibuf;
2015 			return i;
2016 		case 0:
2017 			if (i != oi) {
2018 				oi = i;
2019 				continue;
2020 			} else if (i)
2021 				ibuf[i] = '\0';
2022 			ibufp = ibuf;
2023 			return i;
2024 		case -1:
2025 			fprintf(stderr, "%s\n", strerror(errno));
2026 			sprintf(errmsg, "cannot read standard input");
2027 			clearerr(stdin);
2028 			ibufp = NULL;
2029 			return ERR;
2030 		}
2031 }
2032 
2033 
2034 /* getcmdv: get a command vector */
2035 char *
getcmdv(sizep,nonl)2036 getcmdv(sizep, nonl)
2037 	int *sizep;
2038 	int nonl;
2039 {
2040 	int l, n;
2041 	char *t = ibufp;
2042 
2043 	while (*t++ != '\n')
2044 		;
2045 	if ((l = t - ibufp) < 2 || !oddesc(ibufp, ibufp + l - 1)) {
2046 		*sizep = l;
2047 		return ibufp;
2048 	}
2049 	*sizep = -1;
2050 	CKBUF(cvbuf, cvbufsz, l, NULL);
2051 	memcpy(cvbuf, ibufp, l);
2052 	*(cvbuf + --l - 1) = '\n'; 	/* strip trailing esc */
2053 	if (nonl) l--; 			/* strip newline */
2054 	for (;;) {
2055 		if ((n = getline()) < 0)
2056 			return NULL;
2057 		else if (n == 0 || ibuf[n - 1] != '\n') {
2058 			sprintf(errmsg, "unexpected end-of-file");
2059 			return NULL;
2060 		}
2061 		CKBUF(cvbuf, cvbufsz, l + n, NULL);
2062 		memcpy(cvbuf + l, ibuf, n);
2063 		l += n;
2064 		if (n < 2 || !oddesc(cvbuf, cvbuf + l - 1))
2065 			break;
2066 		*(cvbuf + --l - 1) = '\n'; 	/* strip trailing esc */
2067 		if (nonl) l--; 			/* strip newline */
2068 	}
2069 	CKBUF(cvbuf, cvbufsz, l + 1, NULL);
2070 	cvbuf[l] = '\0';
2071 	*sizep = l;
2072 	return cvbuf;
2073 }
2074 
2075 
2076 /* lpdup: return a pointer to a copy of a line node */
2077 line_t *
lpdup(lp)2078 lpdup(lp)
2079 	line_t *lp;
2080 {
2081 	line_t *np;
2082 
2083 	if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) {
2084 		fprintf(stderr, "%s\n", strerror(errno));
2085 		sprintf(errmsg, "out of memory");
2086 		return NULL;
2087 	}
2088 	np->seek = lp->seek;
2089 	np->len = (lp->len & ~ACTV);	/* zero ACTV bit */
2090 	return np;
2091 }
2092 
2093 
2094 /* oddesc:  return the parity of escapes preceding a character in a
2095    string */
oddesc(s,t)2096 oddesc(s, t)
2097 	char *s;
2098 	char *t;
2099 {
2100     return (s == t || *(t - 1) != '\\') ? 0 : !oddesc(s, t - 1);
2101 }
2102 
2103 
2104 /* esctos: return copy of escaped string */
2105 char *
esctos(s)2106 esctos(s)
2107 	char *s;
2108 {
2109 	static char *file = NULL;
2110 	static int filesz = 0;
2111 
2112 	int i = 0;
2113 
2114 	CKBUF(file, filesz, MAXFNAME + 1, NULL);
2115 	/* assert: no trailing escape */
2116 	while (file[i++] = (*s == '\\') ? *++s : *s)
2117 		s++;
2118 	return file;
2119 }
2120 
2121 
2122 void
onhup(signo)2123 onhup(signo)
2124 	int signo;
2125 {
2126 	if (mutex)
2127 		sigflags |= (1 << signo);
2128 	else	dohup(signo);
2129 }
2130 
2131 
2132 void
onintr(signo)2133 onintr(signo)
2134 	int signo;
2135 {
2136 	if (mutex)
2137 		sigflags |= (1 << signo);
2138 	else	dointr(signo);
2139 }
2140 
2141 
2142 void
dohup(signo)2143 dohup(signo)
2144 	int signo;
2145 {
2146 	char *hup = NULL;		/* hup filename */
2147 	char *s;
2148 	int n;
2149 
2150 	if (!sigactive)
2151 		quit(1);
2152 	sigflags &= ~(1 << signo);
2153 	if (lastln && dowrite(1, lastln, "ed.hup", "w") < 0
2154 	 && (s = getenv("HOME")) != NULL
2155 	 && (n = strlen(s)) + 8 <= MAXFNAME	/* "ed.hup" + '/' */
2156 	 && (hup = (char *) malloc(n + 10)) != NULL) {
2157 		strcpy(hup, s);
2158 		if (hup[n - 1] != '/')
2159 			hup[n] = '/', hup[n+1] = '\0';
2160 		strcat(hup, "ed.hup");
2161 		dowrite(1, lastln, hup, "w");
2162 	}
2163 	quit(2);
2164 }
2165 
2166 
2167 void
dointr(signo)2168 dointr(signo)
2169 	int signo;
2170 {
2171 	if (!sigactive)
2172 		quit(1);
2173 	sigflags &= ~(1 << signo);
2174 #ifdef _POSIX_SOURCE
2175 	siglongjmp(env, -1);
2176 #else
2177 	longjmp(env, -1);
2178 #endif
2179 }
2180 
2181 
2182 struct winsize ws;		/* window size structure */
2183 
2184 void
dowinch(signo)2185 dowinch(signo)
2186 	int signo;
2187 {
2188 	sigflags &= ~(1 << signo);
2189 	if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) {
2190 		if (ws.ws_row > 2) rows = ws.ws_row - 2;
2191 		if (ws.ws_col > 8) cols = ws.ws_col - 8;
2192 	}
2193 }
2194 
2195 
2196 /* ckfn: return a legal filename */
2197 char *
ckfn(s)2198 ckfn(s)
2199 	char *s;
2200 {
2201 	if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) {
2202 		sprintf(errmsg, "shell access restricted");
2203 		return NULL;
2204 	}
2205 	return s;
2206 }
2207