main.c revision 7165
11553Srgrimes/* main.c: This file contains the main control and user-interface routines
21553Srgrimes   for the ed line editor. */
31553Srgrimes/*-
41553Srgrimes * Copyright (c) 1993 Andrew Moore, Talke Studio.
51553Srgrimes * All rights reserved.
61553Srgrimes *
71553Srgrimes * Redistribution and use in source and binary forms, with or without
81553Srgrimes * modification, are permitted provided that the following conditions
91553Srgrimes * are met:
101553Srgrimes * 1. Redistributions of source code must retain the above copyright
111553Srgrimes *    notice, this list of conditions and the following disclaimer.
121553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
13121300Sphk *    notice, this list of conditions and the following disclaimer in the
141553Srgrimes *    documentation and/or other materials provided with the distribution.
151553Srgrimes *
161553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
171553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
181553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
191553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
201553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
211553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
221553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
231553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
241553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261553Srgrimes * SUCH DAMAGE.
271553Srgrimes *
281553Srgrimes *	$Id: main.c,v 1.3 1994/09/24 02:55:28 davidg Exp $
291553Srgrimes */
30114601Sobrien
311553Srgrimes#ifndef lint
321553Srgrimeschar *copyright =
33114601Sobrien"@(#) Copyright (c) 1993 Andrew Moore, Talke Studio. \n\
3430027Scharnier All rights reserved.\n";
35114601Sobrien#endif /* not lint */
36114601Sobrien
371553Srgrimes#ifndef lint
381553Srgrimesstatic char *rcsid = "@(#)main.c,v 1.1 1994/02/01 00:34:42 alm Exp";
391553Srgrimes#endif /* not lint */
4030027Scharnier
4130027Scharnier/*
421553Srgrimes * CREDITS
431553Srgrimes *
4444303Swollman *	This program is based on the editor algorithm described in
4530027Scharnier *	Brian W. Kernighan and P. J. Plauger's book "Software Tools
4644303Swollman *	in Pascal," Addison-Wesley, 1981.
4744303Swollman *
4844303Swollman *	The buffering algorithm is attributed to Rodney Ruddock of
4944303Swollman *	the University of Guelph, Guelph, Ontario.
5044303Swollman *
5144303Swollman *	The cbc.c encryption code is adapted from
5244303Swollman *	the bdes program by Matt Bishop of Dartmouth College,
53100070Sdes *	Hanover, NH.
541553Srgrimes *
551553Srgrimes */
561553Srgrimes
571553Srgrimes#include <sys/ioctl.h>
581553Srgrimes#include <sys/wait.h>
591553Srgrimes#include <ctype.h>
601553Srgrimes#include <setjmp.h>
6130027Scharnier#include <pwd.h>
621553Srgrimes
6399802Salfred#include "ed.h"
641553Srgrimes
651553Srgrimes
661553Srgrimes#ifdef _POSIX_SOURCE
671553Srgrimessigjmp_buf env;
6866584Sphk#else
6966584Sphkjmp_buf env;
701553Srgrimes#endif
711553Srgrimes
721553Srgrimes/* static buffers */
73121299Sphkchar stdinbuf[1];		/* stdin buffer */
741553Srgrimeschar *shcmd;			/* shell command buffer */
75112214Srobertint shcmdsz;			/* shell command buffer size */
761553Srgrimesint shcmdi;			/* shell command buffer index */
77112214Srobertchar *ibuf;			/* ed command-line buffer */
7899802Salfredint ibufsz;			/* ed command-line buffer size */
7999802Salfredchar *ibufp;			/* pointer to ed command-line buffer */
8061749Sjoe
811553Srgrimes/* global flags */
821553Srgrimesint des = 0;			/* if set, use crypt(3) for i/o */
831553Srgrimesint garrulous = 0;		/* if set, print all error messages */
841553Srgrimesint isbinary;			/* if set, buffer contains ASCII NULs */
851553Srgrimesint isglobal;			/* if set, doing a global command */
861553Srgrimesint modified;			/* if set, buffer modified since last write */
871553Srgrimesint mutex = 0;			/* if set, signals set "sigflags" */
881553Srgrimesint red = 0;			/* if set, restrict shell/directory access */
891553Srgrimesint scripted = 0;		/* if set, suppress diagnostics */
901553Srgrimesint sigflags = 0;		/* if set, signals received while mutex set */
911553Srgrimesint sigactive = 0;		/* if set, signal handlers are enabled */
921553Srgrimes
931553Srgrimeschar old_filename[MAXPATHLEN + 1] = "";	/* default filename */
941553Srgrimeslong current_addr;		/* current address in editor buffer */
951553Srgrimeslong addr_last;			/* last address in editor buffer */
961553Srgrimesint lineno;			/* script line number */
971553Srgrimeschar *prompt;			/* command-line prompt */
981553Srgrimeschar *dps = "*";		/* default command-line prompt */
991553Srgrimes
1001553Srgrimeschar *usage = "usage: %s [-] [-sx] [-p string] [name]\n";
1011553Srgrimes
1021553Srgrimesextern char errmsg[];
1031553Srgrimesextern int optind;
1041553Srgrimesextern char *optarg;
1051553Srgrimes
1061553Srgrimes/* ed: line editor */
1071553Srgrimesint
1081553Srgrimesmain(argc, argv)
1091553Srgrimes	int argc;
1101553Srgrimes	char **argv;
11166584Sphk{
1121553Srgrimes	int c, n;
11366747Sphk	long status = 0;
1141553Srgrimes
1151553Srgrimes	red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r';
1161553Srgrimestop:
1171553Srgrimes	while ((c = getopt(argc, argv, "p:sx")) != EOF)
1181553Srgrimes		switch(c) {
1191553Srgrimes		case 'p':				/* set prompt */
12066584Sphk			prompt = optarg;
12138020Sbde			break;
1221553Srgrimes		case 's':				/* run script */
1231553Srgrimes			scripted = 1;
12466584Sphk			break;
1251553Srgrimes		case 'x':				/* use crypt */
1261553Srgrimes#ifdef DES
12766584Sphk			des = get_keyword();
1281553Srgrimes#else
12966584Sphk			fprintf(stderr, "crypt unavailable\n?\n");
1301553Srgrimes#endif
1311553Srgrimes			break;
1321553Srgrimes
1331553Srgrimes		default:
13466584Sphk			fprintf(stderr, usage, argv[0]);
13538020Sbde			exit(1);
1361553Srgrimes		}
1371553Srgrimes	argv += optind;
13866584Sphk	argc -= optind;
1391553Srgrimes	if (argc && **argv == '-') {
1401553Srgrimes		scripted = 1;
14166584Sphk		if (argc > 1) {
1421553Srgrimes			optind = 1;
14366584Sphk			goto top;
1441553Srgrimes		}
1451553Srgrimes		argv++;
1461553Srgrimes		argc--;
14766746Sphk	}
1481553Srgrimes	/* assert: reliable signals! */
1491553Srgrimes#ifdef SIGWINCH
15066584Sphk	handle_winch(SIGWINCH);
1511553Srgrimes	if (isatty(0)) signal(SIGWINCH, handle_winch);
1521553Srgrimes#endif
1531553Srgrimes	signal(SIGHUP, signal_hup);
15466584Sphk	signal(SIGQUIT, SIG_IGN);
1551553Srgrimes	signal(SIGINT, signal_int);
1561553Srgrimes#ifdef _POSIX_SOURCE
15766584Sphk	if ((status = sigsetjmp(env, 1)))
1581553Srgrimes#else
15966584Sphk	if ((status = setjmp(env)))
1601553Srgrimes#endif
1611553Srgrimes	{
1621553Srgrimes		fputs("\n?\n", stderr);
1631553Srgrimes		sprintf(errmsg, "interrupt");
1641553Srgrimes	} else {
16566584Sphk		init_buffers();
1661553Srgrimes		sigactive = 1;			/* enable signal handlers */
1671553Srgrimes		if (argc && **argv && is_legal_filename(*argv)) {
1681553Srgrimes			if (read_file(*argv, 0) < 0 && !isatty(0))
16966584Sphk				quit(2);
17066584Sphk			else if (**argv != '!')
1711553Srgrimes				strcpy(old_filename, *argv);
172100070Sdes		} else if (argc) {
173100070Sdes			fputs("?\n", stderr);
1741553Srgrimes			if (**argv == '\0')
1751553Srgrimes				sprintf(errmsg, "invalid filename");
1761553Srgrimes			if (!isatty(0))
1771553Srgrimes				quit(2);
1781553Srgrimes		}
1791553Srgrimes	}
1803052Sdg	for (;;) {
18118404Snate		if (status < 0 && garrulous)
18218404Snate			fprintf(stderr, "%s\n", errmsg);
1831553Srgrimes		if (prompt) {
18466584Sphk			printf("%s", prompt);
18518404Snate			fflush(stdout);
18666584Sphk		}
18718404Snate		if ((n = get_tty_line()) < 0) {
1881553Srgrimes			status = ERR;
1891553Srgrimes			continue;
19051705Sbillf		} else if (n == 0) {
1911553Srgrimes			if (modified && !scripted) {
1921553Srgrimes				fputs("?\n", stderr);
1931553Srgrimes				sprintf(errmsg, "warning: file modified");
1941553Srgrimes				if (!isatty(0)) {
1951553Srgrimes					fprintf(stderr, garrulous ?
1961553Srgrimes					    "script, line %d: %s\n" :
1971553Srgrimes					    "", lineno, errmsg);
1981553Srgrimes					quit(2);
1991553Srgrimes				}
2001553Srgrimes				clearerr(stdin);
2011553Srgrimes				modified = 0;
2021553Srgrimes				status = EMOD;
2031553Srgrimes				continue;
2041553Srgrimes			} else
2051553Srgrimes				quit(0);
20666584Sphk		} else if (ibuf[n - 1] != '\n') {
207112214Srobert			/* discard line */
208112194Stobez			sprintf(errmsg, "unexpected end-of-file");
2091553Srgrimes			clearerr(stdin);
2101553Srgrimes			status = ERR;
21151705Sbillf			continue;
21254375Sjoe		}
21354375Sjoe		isglobal = 0;
21454375Sjoe		if ((status = extract_addr_range()) >= 0 &&
21554375Sjoe		    (status = exec_command()) >= 0)
21654375Sjoe			if (!status ||
21754375Sjoe			    (status = display_lines(current_addr, current_addr,
21854375Sjoe			        status)) >= 0)
21954375Sjoe				continue;
22061749Sjoe		switch (status) {
22166584Sphk		case EOF:
22261749Sjoe			quit(0);
22361749Sjoe		case EMOD:
22461749Sjoe			modified = 0;
22566584Sphk			fputs("?\n", stderr);		/* give warning */
22661749Sjoe			sprintf(errmsg, "warning: file modified");
22761749Sjoe			if (!isatty(0)) {
22854375Sjoe				fprintf(stderr, garrulous ?
22954375Sjoe				    "script, line %d: %s\n" :
23066584Sphk				    "", lineno, errmsg);
23154375Sjoe				quit(2);
23254375Sjoe			}
23366584Sphk			break;
23454375Sjoe		case FATAL:
23566584Sphk			if (!isatty(0))
23654375Sjoe				fprintf(stderr, garrulous ?
23754375Sjoe				    "script, line %d: %s\n" : "",
23844303Swollman				    lineno, errmsg);
2396286Swollman			else
2409490Sphk				fprintf(stderr, garrulous ? "%s\n" : "",
2418857Srgrimes				    errmsg);
24244303Swollman			quit(3);
2436286Swollman		default:
2446286Swollman			fputs("?\n", stderr);
24566584Sphk			if (!isatty(0)) {
2466286Swollman				fprintf(stderr, garrulous ?
2476286Swollman				    "script, line %d: %s\n" : "",
2486286Swollman				    lineno, errmsg);
2496286Swollman				quit(2);
25066584Sphk			}
2516286Swollman			break;
2526286Swollman		}
2536286Swollman	}
2546286Swollman	/*NOTREACHED*/
25544303Swollman}
25644303Swollman
25744303Swollmanlong first_addr, second_addr, addr_cnt;
25844303Swollman
2596286Swollman/* extract_addr_range: get line addresses from the command buffer until an
26044303Swollman   illegal address is seen; return status */
26144303Swollmanint
26244303Swollmanextract_addr_range()
26366584Sphk{
26444303Swollman	long addr;
26544303Swollman
26644303Swollman	addr_cnt = 0;
26744303Swollman	first_addr = second_addr = current_addr;
268121300Sphk	while ((addr = next_addr()) >= 0) {
26966584Sphk		addr_cnt++;
27044303Swollman		first_addr = second_addr;
27144303Swollman		second_addr = addr;
27244303Swollman		if (*ibufp != ',' && *ibufp != ';')
27344303Swollman			break;
27444303Swollman		else if (*ibufp++ == ';')
27544303Swollman			current_addr = addr;
27644303Swollman	}
27744303Swollman	if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr)
27844303Swollman		first_addr = second_addr;
27944303Swollman	return (addr == ERR) ? ERR : 0;
28044303Swollman}
28166584Sphk
28244303Swollman
28344303Swollman#define	SKIP_BLANKS() while (isspace(*ibufp) && *ibufp != '\n') ibufp++
28444303Swollman
28544303Swollman#define MUST_BE_FIRST() \
28666584Sphk	if (!first) { sprintf(errmsg, "invalid address"); return ERR; }
28766584Sphk
28844303Swollman/*  next_addr: return the next line address in the command buffer */
28944303Swollmanlong
29044303Swollmannext_addr()
29144303Swollman{
29244303Swollman	char *hd;
29365812Ssheldonh	long addr = current_addr;
29465812Ssheldonh	long n;
2951553Srgrimes	int first = 1;
29666584Sphk	int c;
297112194Stobez
2981553Srgrimes	SKIP_BLANKS();
2991553Srgrimes	for (hd = ibufp;; first = 0)
3001553Srgrimes		switch (c = *ibufp) {
3011553Srgrimes		case '+':
30299802Salfred		case '\t':
303121299Sphk		case ' ':
3041553Srgrimes		case '-':
3051553Srgrimes		case '^':
3061553Srgrimes			ibufp++;
3071553Srgrimes			SKIP_BLANKS();
3081553Srgrimes			if (isdigit(*ibufp)) {
3091553Srgrimes				STRTOL(n, ibufp);
3101553Srgrimes				addr += (c == '-' || c == '^') ? -n : n;
3111553Srgrimes			} else if (!isspace(c))
3121553Srgrimes				addr += (c == '-' || c == '^') ? -1 : 1;
3131553Srgrimes			break;
3141553Srgrimes		case '0': case '1': case '2':
3151553Srgrimes		case '3': case '4': case '5':
3161553Srgrimes		case '6': case '7': case '8': case '9':
3171553Srgrimes			MUST_BE_FIRST();
3181553Srgrimes			STRTOL(addr, ibufp);
3191553Srgrimes			break;
3201553Srgrimes		case '.':
3211553Srgrimes		case '$':
3221553Srgrimes			MUST_BE_FIRST();
3231553Srgrimes			ibufp++;
3241553Srgrimes			addr = (c == '.') ? current_addr : addr_last;
3251553Srgrimes			break;
32699802Salfred		case '/':
327121299Sphk		case '?':
3281553Srgrimes			MUST_BE_FIRST();
3291553Srgrimes			if ((addr = get_matching_node_addr(
3301553Srgrimes			    get_compiled_pattern(), c == '/')) < 0)
3311553Srgrimes				return ERR;
3321553Srgrimes			else if (c == *ibufp)
3331553Srgrimes				ibufp++;
3341553Srgrimes			break;
3351553Srgrimes		case '\'':
3361553Srgrimes			MUST_BE_FIRST();
3371553Srgrimes			ibufp++;
3381553Srgrimes			if ((addr = get_marked_node_addr(*ibufp++)) < 0)
3391553Srgrimes				return ERR;
3401553Srgrimes			break;
3411553Srgrimes		case '%':
3421553Srgrimes		case ',':
3431553Srgrimes		case ';':
3441553Srgrimes			if (first) {
3451553Srgrimes				ibufp++;
3461553Srgrimes				addr_cnt++;
3471553Srgrimes				second_addr = (c == ';') ? current_addr : 1;
3481553Srgrimes				addr = addr_last;
3491553Srgrimes				break;
3501553Srgrimes			}
351121299Sphk			/* FALL THROUGH */
3521553Srgrimes		default:
3531553Srgrimes			if (ibufp == hd)
354121299Sphk				return EOF;
3551553Srgrimes			else if (addr < 0 || addr_last < addr) {
35636796Simp				sprintf(errmsg, "invalid address");
35730027Scharnier				return ERR;
3581553Srgrimes			} else
3591553Srgrimes				return addr;
3601553Srgrimes		}
361	/* NOTREACHED */
362}
363
364
365#ifdef BACKWARDS
366/* GET_THIRD_ADDR: get a legal address from the command buffer */
367#define GET_THIRD_ADDR(addr) \
368{ \
369	long ol1, ol2; \
370\
371	ol1 = first_addr, ol2 = second_addr; \
372	if (extract_addr_range() < 0) \
373		return ERR; \
374	else if (addr_cnt == 0) { \
375		sprintf(errmsg, "destination expected"); \
376		return ERR; \
377	} else if (second_addr < 0 || addr_last < second_addr) { \
378		sprintf(errmsg, "invalid address"); \
379		return ERR; \
380	} \
381	addr = second_addr; \
382	first_addr = ol1, second_addr = ol2; \
383}
384#else	/* BACKWARDS */
385/* GET_THIRD_ADDR: get a legal address from the command buffer */
386#define GET_THIRD_ADDR(addr) \
387{ \
388	long ol1, ol2; \
389\
390	ol1 = first_addr, ol2 = second_addr; \
391	if (extract_addr_range() < 0) \
392		return ERR; \
393	if (second_addr < 0 || addr_last < second_addr) { \
394		sprintf(errmsg, "invalid address"); \
395		return ERR; \
396	} \
397	addr = second_addr; \
398	first_addr = ol1, second_addr = ol2; \
399}
400#endif
401
402
403/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */
404#define GET_COMMAND_SUFFIX() { \
405	int done = 0; \
406	do { \
407		switch(*ibufp) { \
408		case 'p': \
409			gflag |= GPR, ibufp++; \
410			break; \
411		case 'l': \
412			gflag |= GLS, ibufp++; \
413			break; \
414		case 'n': \
415			gflag |= GNP, ibufp++; \
416			break; \
417		default: \
418			done++; \
419		} \
420	} while (!done); \
421	if (*ibufp++ != '\n') { \
422		sprintf(errmsg, "invalid command suffix"); \
423		return ERR; \
424	} \
425}
426
427
428/* sflags */
429#define SGG 001		/* complement previous global substitute suffix */
430#define SGP 002		/* complement previous print suffix */
431#define SGR 004		/* use last regex instead of last pat */
432#define SGF 010		/* repeat last substitution */
433
434int patlock = 0;	/* if set, pattern not freed by get_compiled_pattern() */
435
436long rows = 22;		/* scroll length: ws_row - 2 */
437
438/* exec_command: execute the next command in command buffer; return print
439   request, if any */
440int
441exec_command()
442{
443	extern long u_current_addr;
444	extern long u_addr_last;
445
446	static pattern_t *pat = NULL;
447	static int sgflag = 0;
448	static int sgnum = 0;
449
450	pattern_t *tpat;
451	char *fnp;
452	int gflag = 0;
453	int sflags = 0;
454	long addr = 0;
455	int n = 0;
456	int c;
457
458	SKIP_BLANKS();
459	switch(c = *ibufp++) {
460	case 'a':
461		GET_COMMAND_SUFFIX();
462		if (!isglobal) clear_undo_stack();
463		if (append_lines(second_addr) < 0)
464			return ERR;
465		break;
466	case 'c':
467		if (check_addr_range(current_addr, current_addr) < 0)
468			return ERR;
469		GET_COMMAND_SUFFIX();
470		if (!isglobal) clear_undo_stack();
471		if (delete_lines(first_addr, second_addr) < 0 ||
472		    append_lines(current_addr) < 0)
473			return ERR;
474		break;
475	case 'd':
476		if (check_addr_range(current_addr, current_addr) < 0)
477			return ERR;
478		GET_COMMAND_SUFFIX();
479		if (!isglobal) clear_undo_stack();
480		if (delete_lines(first_addr, second_addr) < 0)
481			return ERR;
482		else if ((addr = INC_MOD(current_addr, addr_last)) != 0)
483			current_addr = addr;
484		break;
485	case 'e':
486		if (modified && !scripted)
487			return EMOD;
488		/* fall through */
489	case 'E':
490		if (addr_cnt > 0) {
491			sprintf(errmsg, "unexpected address");
492			return ERR;
493		} else if (!isspace(*ibufp)) {
494			sprintf(errmsg, "unexpected command suffix");
495			return ERR;
496		} else if ((fnp = get_filename()) == NULL)
497			return ERR;
498		GET_COMMAND_SUFFIX();
499		if (delete_lines(1, addr_last) < 0)
500			return ERR;
501		clear_undo_stack();
502		if (close_sbuf() < 0)
503			return ERR;
504		else if (open_sbuf() < 0)
505			return FATAL;
506		if (*fnp && *fnp != '!') strcpy(old_filename, fnp);
507#ifdef BACKWARDS
508		if (*fnp == '\0' && *old_filename == '\0') {
509			sprintf(errmsg, "no current filename");
510			return ERR;
511		}
512#endif
513		if (read_file(*fnp ? fnp : old_filename, 0) < 0)
514			return ERR;
515		clear_undo_stack();
516		modified = 0;
517		u_current_addr = u_addr_last = -1;
518		break;
519	case 'f':
520		if (addr_cnt > 0) {
521			sprintf(errmsg, "unexpected address");
522			return ERR;
523		} else if (!isspace(*ibufp)) {
524			sprintf(errmsg, "unexpected command suffix");
525			return ERR;
526		} else if ((fnp = get_filename()) == NULL)
527			return ERR;
528		else if (*fnp == '!') {
529			sprintf(errmsg, "invalid redirection");
530			return ERR;
531		}
532		GET_COMMAND_SUFFIX();
533		if (*fnp) strcpy(old_filename, fnp);
534		printf("%s\n", strip_escapes(old_filename));
535		break;
536	case 'g':
537	case 'v':
538	case 'G':
539	case 'V':
540		if (isglobal) {
541			sprintf(errmsg, "cannot nest global commands");
542			return ERR;
543		} else if (check_addr_range(1, addr_last) < 0)
544			return ERR;
545		else if (build_active_list(c == 'g' || c == 'G') < 0)
546			return ERR;
547		else if ((n = (c == 'G' || c == 'V')))
548			GET_COMMAND_SUFFIX();
549		isglobal++;
550		if (exec_global(n, gflag) < 0)
551			return ERR;
552		break;
553	case 'h':
554		if (addr_cnt > 0) {
555			sprintf(errmsg, "unexpected address");
556			return ERR;
557		}
558		GET_COMMAND_SUFFIX();
559		if (*errmsg) fprintf(stderr, "%s\n", errmsg);
560		break;
561	case 'H':
562		if (addr_cnt > 0) {
563			sprintf(errmsg, "unexpected address");
564			return ERR;
565		}
566		GET_COMMAND_SUFFIX();
567		if ((garrulous = 1 - garrulous) && *errmsg)
568			fprintf(stderr, "%s\n", errmsg);
569		break;
570	case 'i':
571		if (second_addr == 0) {
572			sprintf(errmsg, "invalid address");
573			return ERR;
574		}
575		GET_COMMAND_SUFFIX();
576		if (!isglobal) clear_undo_stack();
577		if (append_lines(second_addr - 1) < 0)
578			return ERR;
579		break;
580	case 'j':
581		if (check_addr_range(current_addr, current_addr + 1) < 0)
582			return ERR;
583		GET_COMMAND_SUFFIX();
584		if (!isglobal) clear_undo_stack();
585		if (first_addr != second_addr &&
586		    join_lines(first_addr, second_addr) < 0)
587			return ERR;
588		break;
589	case 'k':
590		c = *ibufp++;
591		if (second_addr == 0) {
592			sprintf(errmsg, "invalid address");
593			return ERR;
594		}
595		GET_COMMAND_SUFFIX();
596		if (mark_line_node(get_addressed_line_node(second_addr), c) < 0)
597			return ERR;
598		break;
599	case 'l':
600		if (check_addr_range(current_addr, current_addr) < 0)
601			return ERR;
602		GET_COMMAND_SUFFIX();
603		if (display_lines(first_addr, second_addr, gflag | GLS) < 0)
604			return ERR;
605		gflag = 0;
606		break;
607	case 'm':
608		if (check_addr_range(current_addr, current_addr) < 0)
609			return ERR;
610		GET_THIRD_ADDR(addr);
611		if (first_addr <= addr && addr < second_addr) {
612			sprintf(errmsg, "invalid destination");
613			return ERR;
614		}
615		GET_COMMAND_SUFFIX();
616		if (!isglobal) clear_undo_stack();
617		if (move_lines(addr) < 0)
618			return ERR;
619		break;
620	case 'n':
621		if (check_addr_range(current_addr, current_addr) < 0)
622			return ERR;
623		GET_COMMAND_SUFFIX();
624		if (display_lines(first_addr, second_addr, gflag | GNP) < 0)
625			return ERR;
626		gflag = 0;
627		break;
628	case 'p':
629		if (check_addr_range(current_addr, current_addr) < 0)
630			return ERR;
631		GET_COMMAND_SUFFIX();
632		if (display_lines(first_addr, second_addr, gflag | GPR) < 0)
633			return ERR;
634		gflag = 0;
635		break;
636	case 'P':
637		if (addr_cnt > 0) {
638			sprintf(errmsg, "unexpected address");
639			return ERR;
640		}
641		GET_COMMAND_SUFFIX();
642		prompt = prompt ? NULL : optarg ? optarg : dps;
643		break;
644	case 'q':
645	case 'Q':
646		if (addr_cnt > 0) {
647			sprintf(errmsg, "unexpected address");
648			return ERR;
649		}
650		GET_COMMAND_SUFFIX();
651		gflag =  (modified && !scripted && c == 'q') ? EMOD : EOF;
652		break;
653	case 'r':
654		if (!isspace(*ibufp)) {
655			sprintf(errmsg, "unexpected command suffix");
656			return ERR;
657		} else if (addr_cnt == 0)
658			second_addr = addr_last;
659		if ((fnp = get_filename()) == NULL)
660			return ERR;
661		GET_COMMAND_SUFFIX();
662		if (!isglobal) clear_undo_stack();
663		if (*old_filename == '\0' && *fnp != '!')
664			strcpy(old_filename, fnp);
665#ifdef BACKWARDS
666		if (*fnp == '\0' && *old_filename == '\0') {
667			sprintf(errmsg, "no current filename");
668			return ERR;
669		}
670#endif
671		if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0)
672			return ERR;
673		else if (addr && addr != addr_last)
674			modified = 1;
675		break;
676	case 's':
677		do {
678			switch(*ibufp) {
679			case '\n':
680				sflags |=SGF;
681				break;
682			case 'g':
683				sflags |= SGG;
684				ibufp++;
685				break;
686			case 'p':
687				sflags |= SGP;
688				ibufp++;
689				break;
690			case 'r':
691				sflags |= SGR;
692				ibufp++;
693				break;
694			case '0': case '1': case '2': case '3': case '4':
695			case '5': case '6': case '7': case '8': case '9':
696				STRTOL(sgnum, ibufp);
697				sflags |= SGF;
698				sgflag &= ~GSG;		/* override GSG */
699				break;
700			default:
701				if (sflags) {
702					sprintf(errmsg, "invalid command suffix");
703					return ERR;
704				}
705			}
706		} while (sflags && *ibufp != '\n');
707		if (sflags && !pat) {
708			sprintf(errmsg, "no previous substitution");
709			return ERR;
710		} else if (sflags & SGG)
711			sgnum = 0;		/* override numeric arg */
712		if (*ibufp != '\n' && *(ibufp + 1) == '\n') {
713			sprintf(errmsg, "invalid pattern delimiter");
714			return ERR;
715		}
716		tpat = pat;
717		SPL1();
718		if ((!sflags || (sflags & SGR)) &&
719		    (tpat = get_compiled_pattern()) == NULL) {
720		 	SPL0();
721			return ERR;
722		} else if (tpat != pat) {
723			if (pat) {
724				regfree(pat);
725				free(pat);
726			}
727			pat = tpat;
728			patlock = 1;		/* reserve pattern */
729		}
730		SPL0();
731		if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0)
732			return ERR;
733		else if (isglobal)
734			sgflag |= GLB;
735		else
736			sgflag &= ~GLB;
737		if (sflags & SGG)
738			sgflag ^= GSG;
739		if (sflags & SGP)
740			sgflag ^= GPR, sgflag &= ~(GLS | GNP);
741		do {
742			switch(*ibufp) {
743			case 'p':
744				sgflag |= GPR, ibufp++;
745				break;
746			case 'l':
747				sgflag |= GLS, ibufp++;
748				break;
749			case 'n':
750				sgflag |= GNP, ibufp++;
751				break;
752			default:
753				n++;
754			}
755		} while (!n);
756		if (check_addr_range(current_addr, current_addr) < 0)
757			return ERR;
758		GET_COMMAND_SUFFIX();
759		if (!isglobal) clear_undo_stack();
760		if (search_and_replace(pat, sgflag, sgnum) < 0)
761			return ERR;
762		break;
763	case 't':
764		if (check_addr_range(current_addr, current_addr) < 0)
765			return ERR;
766		GET_THIRD_ADDR(addr);
767		GET_COMMAND_SUFFIX();
768		if (!isglobal) clear_undo_stack();
769		if (copy_lines(addr) < 0)
770			return ERR;
771		break;
772	case 'u':
773		if (addr_cnt > 0) {
774			sprintf(errmsg, "unexpected address");
775			return ERR;
776		}
777		GET_COMMAND_SUFFIX();
778		if (pop_undo_stack() < 0)
779			return ERR;
780		break;
781	case 'w':
782	case 'W':
783		if ((n = *ibufp) == 'q' || n == 'Q') {
784			gflag = EOF;
785			ibufp++;
786		}
787		if (!isspace(*ibufp)) {
788			sprintf(errmsg, "unexpected command suffix");
789			return ERR;
790		} else if ((fnp = get_filename()) == NULL)
791			return ERR;
792		if (addr_cnt == 0 && !addr_last)
793			first_addr = second_addr = 0;
794		else if (check_addr_range(1, addr_last) < 0)
795			return ERR;
796		GET_COMMAND_SUFFIX();
797		if (*old_filename == '\0' && *fnp != '!')
798			strcpy(old_filename, fnp);
799#ifdef BACKWARDS
800		if (*fnp == '\0' && *old_filename == '\0') {
801			sprintf(errmsg, "no current filename");
802			return ERR;
803		}
804#endif
805		if ((addr = write_file(*fnp ? fnp : old_filename,
806		    (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0)
807			return ERR;
808		else if (addr == addr_last)
809			modified = 0;
810		else if (modified && !scripted && n == 'q')
811			gflag = EMOD;
812		break;
813	case 'x':
814		if (addr_cnt > 0) {
815			sprintf(errmsg, "unexpected address");
816			return ERR;
817		}
818		GET_COMMAND_SUFFIX();
819#ifdef DES
820		des = get_keyword();
821#else
822		sprintf(errmsg, "crypt unavailable");
823		return ERR;
824#endif
825		break;
826	case 'z':
827#ifdef BACKWARDS
828		if (check_addr_range(first_addr = 1, current_addr + 1) < 0)
829#else
830		if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0)
831#endif
832			return ERR;
833		else if ('0' < *ibufp && *ibufp <= '9')
834			STRTOL(rows, ibufp);
835		GET_COMMAND_SUFFIX();
836		if (display_lines(second_addr, min(addr_last,
837		    second_addr + rows), gflag) < 0)
838			return ERR;
839		gflag = 0;
840		break;
841	case '=':
842		GET_COMMAND_SUFFIX();
843		printf("%ld\n", addr_cnt ? second_addr : addr_last);
844		break;
845	case '!':
846		if (addr_cnt > 0) {
847			sprintf(errmsg, "unexpected address");
848			return ERR;
849		} else if ((sflags = get_shell_command()) < 0)
850			return ERR;
851		GET_COMMAND_SUFFIX();
852		if (sflags) printf("%s\n", shcmd + 1);
853		system(shcmd + 1);
854		if (!scripted) printf("!\n");
855		break;
856	case '\n':
857#ifdef BACKWARDS
858		if (check_addr_range(first_addr = 1, current_addr + 1) < 0
859#else
860		if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0
861#endif
862		 || display_lines(second_addr, second_addr, 0) < 0)
863			return ERR;
864		break;
865	default:
866		sprintf(errmsg, "unknown command");
867		return ERR;
868	}
869	return gflag;
870}
871
872
873/* check_addr_range: return status of address range check */
874int
875check_addr_range(n, m)
876	long n, m;
877{
878	if (addr_cnt == 0) {
879		first_addr = n;
880		second_addr = m;
881	}
882	if (first_addr > second_addr || 1 > first_addr ||
883	    second_addr > addr_last) {
884		sprintf(errmsg, "invalid address");
885		return ERR;
886	}
887	return 0;
888}
889
890
891/* get_matching_node_addr: return the address of the next line matching a
892   pattern in a given direction.  wrap around begin/end of editor buffer if
893   necessary */
894long
895get_matching_node_addr(pat, dir)
896	pattern_t *pat;
897	int dir;
898{
899	char *s;
900	long n = current_addr;
901	line_t *lp;
902
903	if (!pat) return ERR;
904	do {
905	       if ((n = dir ? INC_MOD(n, addr_last) : DEC_MOD(n, addr_last))) {
906			lp = get_addressed_line_node(n);
907			if ((s = get_sbuf_line(lp)) == NULL)
908				return ERR;
909			if (isbinary)
910				NUL_TO_NEWLINE(s, lp->len);
911			if (!regexec(pat, s, 0, NULL, 0))
912				return n;
913	       }
914	} while (n != current_addr);
915	sprintf(errmsg, "no match");
916	return  ERR;
917}
918
919
920/* get_filename: return pointer to copy of filename in the command buffer */
921char *
922get_filename()
923{
924	static char *file = NULL;
925	static int filesz = 0;
926
927	int n;
928
929	if (*ibufp != '\n') {
930		SKIP_BLANKS();
931		if (*ibufp == '\n') {
932			sprintf(errmsg, "invalid filename");
933			return NULL;
934		} else if ((ibufp = get_extended_line(&n, 1)) == NULL)
935			return NULL;
936		else if (*ibufp == '!') {
937			ibufp++;
938			if ((n = get_shell_command()) < 0)
939				return NULL;
940			if (n) printf("%s\n", shcmd + 1);
941			return shcmd;
942		} else if (n - 1 > MAXPATHLEN) {
943			sprintf(errmsg, "filename too long");
944			return  NULL;
945		}
946	}
947#ifndef BACKWARDS
948	else if (*old_filename == '\0') {
949		sprintf(errmsg, "no current filename");
950		return  NULL;
951	}
952#endif
953	REALLOC(file, filesz, MAXPATHLEN + 1, NULL);
954	for (n = 0; *ibufp != '\n';)
955		file[n++] = *ibufp++;
956	file[n] = '\0';
957	return is_legal_filename(file) ? file : NULL;
958}
959
960
961/* get_shell_command: read a shell command from stdin; return substitution
962   status */
963int
964get_shell_command()
965{
966	static char *buf = NULL;
967	static int n = 0;
968
969	char *s;			/* substitution char pointer */
970	int i = 0;
971	int j = 0;
972
973	if (red) {
974		sprintf(errmsg, "shell access restricted");
975		return ERR;
976	} else if ((s = ibufp = get_extended_line(&j, 1)) == NULL)
977		return ERR;
978	REALLOC(buf, n, j + 1, ERR);
979	buf[i++] = '!';			/* prefix command w/ bang */
980	while (*ibufp != '\n')
981		switch (*ibufp) {
982		default:
983			REALLOC(buf, n, i + 2, ERR);
984			buf[i++] = *ibufp;
985			if (*ibufp++ == '\\')
986				buf[i++] = *ibufp++;
987			break;
988		case '!':
989			if (s != ibufp) {
990				REALLOC(buf, n, i + 1, ERR);
991				buf[i++] = *ibufp++;
992			}
993#ifdef BACKWARDS
994			else if (shcmd == NULL || *(shcmd + 1) == '\0')
995#else
996			else if (shcmd == NULL)
997#endif
998			{
999				sprintf(errmsg, "no previous command");
1000				return ERR;
1001			} else {
1002				REALLOC(buf, n, i + shcmdi, ERR);
1003				for (s = shcmd + 1; s < shcmd + shcmdi;)
1004					buf[i++] = *s++;
1005				s = ibufp++;
1006			}
1007			break;
1008		case '%':
1009			if (*old_filename  == '\0') {
1010				sprintf(errmsg, "no current filename");
1011				return ERR;
1012			}
1013			j = strlen(s = strip_escapes(old_filename));
1014			REALLOC(buf, n, i + j, ERR);
1015			while (j--)
1016				buf[i++] = *s++;
1017			s = ibufp++;
1018			break;
1019		}
1020	REALLOC(shcmd, shcmdsz, i + 1, ERR);
1021	memcpy(shcmd, buf, i);
1022	shcmd[shcmdi = i] = '\0';
1023	return *s == '!' || *s == '%';
1024}
1025
1026
1027/* append_lines: insert text from stdin to after line n; stop when either a
1028   single period is read or EOF; return status */
1029int
1030append_lines(n)
1031	long n;
1032{
1033	int l;
1034	char *lp = ibuf;
1035	char *eot;
1036	undo_t *up = NULL;
1037
1038	for (current_addr = n;;) {
1039		if (!isglobal) {
1040			if ((l = get_tty_line()) < 0)
1041				return ERR;
1042			else if (l == 0 || ibuf[l - 1] != '\n') {
1043				clearerr(stdin);
1044				return  l ? EOF : 0;
1045			}
1046			lp = ibuf;
1047		} else if (*(lp = ibufp) == '\0')
1048			return 0;
1049		else {
1050			while (*ibufp++ != '\n')
1051				;
1052			l = ibufp - lp;
1053		}
1054		if (l == 2 && lp[0] == '.' && lp[1] == '\n') {
1055			return 0;
1056		}
1057		eot = lp + l;
1058		SPL1();
1059		do {
1060			if ((lp = put_sbuf_line(lp)) == NULL) {
1061				SPL0();
1062				return ERR;
1063			} else if (up)
1064				up->t = get_addressed_line_node(current_addr);
1065			else if ((up = push_undo_stack(UADD, current_addr,
1066			    current_addr)) == NULL) {
1067				SPL0();
1068				return ERR;
1069			}
1070		} while (lp != eot);
1071		modified = 1;
1072		SPL0();
1073	}
1074	/* NOTREACHED */
1075}
1076
1077
1078/* join_lines: replace a range of lines with the joined text of those lines */
1079int
1080join_lines(from, to)
1081	long from;
1082	long to;
1083{
1084	static char *buf = NULL;
1085	static int n;
1086
1087	char *s;
1088	int size = 0;
1089	line_t *bp, *ep;
1090
1091	ep = get_addressed_line_node(INC_MOD(to, addr_last));
1092	bp = get_addressed_line_node(from);
1093	for (; bp != ep; bp = bp->q_forw) {
1094		if ((s = get_sbuf_line(bp)) == NULL)
1095			return ERR;
1096		REALLOC(buf, n, size + bp->len, ERR);
1097		memcpy(buf + size, s, bp->len);
1098		size += bp->len;
1099	}
1100	REALLOC(buf, n, size + 2, ERR);
1101	memcpy(buf + size, "\n", 2);
1102	if (delete_lines(from, to) < 0)
1103		return ERR;
1104	current_addr = from - 1;
1105	SPL1();
1106	if (put_sbuf_line(buf) == NULL ||
1107	    push_undo_stack(UADD, current_addr, current_addr) == NULL) {
1108		SPL0();
1109		return ERR;
1110	}
1111	modified = 1;
1112	SPL0();
1113	return 0;
1114}
1115
1116
1117/* move_lines: move a range of lines */
1118int
1119move_lines(addr)
1120	long addr;
1121{
1122	line_t *b1, *a1, *b2, *a2;
1123	long n = INC_MOD(second_addr, addr_last);
1124	long p = first_addr - 1;
1125	int done = (addr == first_addr - 1 || addr == second_addr);
1126
1127	SPL1();
1128	if (done) {
1129		a2 = get_addressed_line_node(n);
1130		b2 = get_addressed_line_node(p);
1131		current_addr = second_addr;
1132	} else if (push_undo_stack(UMOV, p, n) == NULL ||
1133	    push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) {
1134		SPL0();
1135		return ERR;
1136	} else {
1137		a1 = get_addressed_line_node(n);
1138		if (addr < first_addr) {
1139			b1 = get_addressed_line_node(p);
1140			b2 = get_addressed_line_node(addr);
1141					/* this get_addressed_line_node last! */
1142		} else {
1143			b2 = get_addressed_line_node(addr);
1144			b1 = get_addressed_line_node(p);
1145					/* this get_addressed_line_node last! */
1146		}
1147		a2 = b2->q_forw;
1148		REQUE(b2, b1->q_forw);
1149		REQUE(a1->q_back, a2);
1150		REQUE(b1, a1);
1151		current_addr = addr + ((addr < first_addr) ?
1152		    second_addr - first_addr + 1 : 0);
1153	}
1154	if (isglobal)
1155		unset_active_nodes(b2->q_forw, a2);
1156	modified = 1;
1157	SPL0();
1158	return 0;
1159}
1160
1161
1162/* copy_lines: copy a range of lines; return status */
1163int
1164copy_lines(addr)
1165	long addr;
1166{
1167	line_t *lp, *np = get_addressed_line_node(first_addr);
1168	undo_t *up = NULL;
1169	long n = second_addr - first_addr + 1;
1170	long m = 0;
1171
1172	current_addr = addr;
1173	if (first_addr <= addr && addr < second_addr) {
1174		n =  addr - first_addr + 1;
1175		m = second_addr - addr;
1176	}
1177	for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1))
1178		for (; n-- > 0; np = np->q_forw) {
1179			SPL1();
1180			if ((lp = dup_line_node(np)) == NULL) {
1181				SPL0();
1182				return ERR;
1183			}
1184			add_line_node(lp);
1185			if (up)
1186				up->t = lp;
1187			else if ((up = push_undo_stack(UADD, current_addr,
1188			    current_addr)) == NULL) {
1189				SPL0();
1190				return ERR;
1191			}
1192			modified = 1;
1193			SPL0();
1194		}
1195	return 0;
1196}
1197
1198
1199/* delete_lines: delete a range of lines */
1200int
1201delete_lines(from, to)
1202	long from, to;
1203{
1204	line_t *n, *p;
1205
1206	SPL1();
1207	if (push_undo_stack(UDEL, from, to) == NULL) {
1208		SPL0();
1209		return ERR;
1210	}
1211	n = get_addressed_line_node(INC_MOD(to, addr_last));
1212	p = get_addressed_line_node(from - 1);
1213					/* this get_addressed_line_node last! */
1214	if (isglobal)
1215		unset_active_nodes(p->q_forw, n);
1216	REQUE(p, n);
1217	addr_last -= to - from + 1;
1218	current_addr = from - 1;
1219	modified = 1;
1220	SPL0();
1221	return 0;
1222}
1223
1224
1225/* display_lines: print a range of lines to stdout */
1226int
1227display_lines(from, to, gflag)
1228	long from;
1229	long to;
1230	int gflag;
1231{
1232	line_t *bp;
1233	line_t *ep;
1234	char *s;
1235
1236	if (!from) {
1237		sprintf(errmsg, "invalid address");
1238		return ERR;
1239	}
1240	ep = get_addressed_line_node(INC_MOD(to, addr_last));
1241	bp = get_addressed_line_node(from);
1242	for (; bp != ep; bp = bp->q_forw) {
1243		if ((s = get_sbuf_line(bp)) == NULL)
1244			return ERR;
1245		if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0)
1246			return ERR;
1247	}
1248	return 0;
1249}
1250
1251
1252#define MAXMARK 26			/* max number of marks */
1253
1254line_t	*mark[MAXMARK];			/* line markers */
1255int markno;				/* line marker count */
1256
1257/* mark_line_node: set a line node mark */
1258int
1259mark_line_node(lp, n)
1260	line_t *lp;
1261	int n;
1262{
1263	if (!islower(n)) {
1264		sprintf(errmsg, "invalid mark character");
1265		return ERR;
1266	} else if (mark[n - 'a'] == NULL)
1267		markno++;
1268	mark[n - 'a'] = lp;
1269	return 0;
1270}
1271
1272
1273/* get_marked_node_addr: return address of a marked line */
1274long
1275get_marked_node_addr(n)
1276	int n;
1277{
1278	if (!islower(n)) {
1279		sprintf(errmsg, "invalid mark character");
1280		return ERR;
1281	}
1282	return get_line_node_addr(mark[n - 'a']);
1283}
1284
1285
1286/* unmark_line_node: clear line node mark */
1287void
1288unmark_line_node(lp)
1289	line_t *lp;
1290{
1291	int i;
1292
1293	for (i = 0; markno && i < MAXMARK; i++)
1294		if (mark[i] == lp) {
1295			mark[i] = NULL;
1296			markno--;
1297		}
1298}
1299
1300
1301/* dup_line_node: return a pointer to a copy of a line node */
1302line_t *
1303dup_line_node(lp)
1304	line_t *lp;
1305{
1306	line_t *np;
1307
1308	if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) {
1309		fprintf(stderr, "%s\n", strerror(errno));
1310		sprintf(errmsg, "out of memory");
1311		return NULL;
1312	}
1313	np->seek = lp->seek;
1314	np->len = lp->len;
1315	return np;
1316}
1317
1318
1319/* has_trailing_escape:  return the parity of escapes preceding a character
1320   in a string */
1321int
1322has_trailing_escape(s, t)
1323	char *s;
1324	char *t;
1325{
1326    return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1);
1327}
1328
1329
1330/* strip_escapes: return copy of escaped string of at most length MAXPATHLEN */
1331char *
1332strip_escapes(s)
1333	char *s;
1334{
1335	static char *file = NULL;
1336	static int filesz = 0;
1337
1338	int i = 0;
1339
1340	REALLOC(file, filesz, MAXPATHLEN + 1, NULL);
1341	/* assert: no trailing escape */
1342	while ((file[i++] = (*s == '\\') ? *++s : *s))
1343		s++;
1344	return file;
1345}
1346
1347
1348void
1349signal_hup(signo)
1350	int signo;
1351{
1352	if (mutex)
1353		sigflags |= (1 << (signo - 1));
1354	else	handle_hup(signo);
1355}
1356
1357
1358void
1359signal_int(signo)
1360	int signo;
1361{
1362	if (mutex)
1363		sigflags |= (1 << (signo - 1));
1364	else	handle_int(signo);
1365}
1366
1367
1368void
1369handle_hup(signo)
1370	int signo;
1371{
1372	char *hup = NULL;		/* hup filename */
1373	char *s;
1374	int n;
1375
1376	if (!sigactive)
1377		quit(1);
1378	sigflags &= ~(1 << (signo - 1));
1379	if (addr_last && write_file("ed.hup", "w", 1, addr_last) < 0 &&
1380	    (s = getenv("HOME")) != NULL &&
1381	    (n = strlen(s)) + 8 <= MAXPATHLEN &&	/* "ed.hup" + '/' */
1382	    (hup = (char *) malloc(n + 10)) != NULL) {
1383		strcpy(hup, s);
1384		if (hup[n - 1] != '/')
1385			hup[n] = '/', hup[n+1] = '\0';
1386		strcat(hup, "ed.hup");
1387		write_file(hup, "w", 1, addr_last);
1388	}
1389	quit(2);
1390}
1391
1392
1393void
1394handle_int(signo)
1395	int signo;
1396{
1397	if (!sigactive)
1398		quit(1);
1399	sigflags &= ~(1 << (signo - 1));
1400#ifdef _POSIX_SOURCE
1401	siglongjmp(env, -1);
1402#else
1403	longjmp(env, -1);
1404#endif
1405}
1406
1407
1408int cols = 72;				/* wrap column */
1409
1410void
1411handle_winch(signo)
1412	int signo;
1413{
1414	struct winsize ws;		/* window size structure */
1415
1416	sigflags &= ~(1 << (signo - 1));
1417	if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) {
1418		if (ws.ws_row > 2) rows = ws.ws_row - 2;
1419		if (ws.ws_col > 8) cols = ws.ws_col - 8;
1420	}
1421}
1422
1423
1424/* is_legal_filename: return a legal filename */
1425int
1426is_legal_filename(s)
1427	char *s;
1428{
1429	if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) {
1430		sprintf(errmsg, "shell access restricted");
1431		return 0;
1432	}
1433	return 1;
1434}
1435