main.c revision 59797
133965Sjdp/* main.c: This file contains the main control and user-interface routines
233965Sjdp   for the ed line editor. */
333965Sjdp/*-
433965Sjdp * Copyright (c) 1993 Andrew Moore, Talke Studio.
533965Sjdp * All rights reserved.
633965Sjdp *
733965Sjdp * Redistribution and use in source and binary forms, with or without
833965Sjdp * modification, are permitted provided that the following conditions
933965Sjdp * are met:
1033965Sjdp * 1. Redistributions of source code must retain the above copyright
1133965Sjdp *    notice, this list of conditions and the following disclaimer.
1233965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1333965Sjdp *    notice, this list of conditions and the following disclaimer in the
1433965Sjdp *    documentation and/or other materials provided with the distribution.
1533965Sjdp *
1633965Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1733965Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1833965Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1933965Sjdp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2033965Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2133965Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2233965Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2333965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2433965Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2533965Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2633965Sjdp * SUCH DAMAGE.
2733965Sjdp */
2833965Sjdp
2933965Sjdp#ifndef lint
3033965Sjdpstatic char * const copyright =
3133965Sjdp"@(#) Copyright (c) 1993 Andrew Moore, Talke Studio. \n\
3233965Sjdp All rights reserved.\n";
3333965Sjdp#endif /* not lint */
3433965Sjdp
3533965Sjdp#ifndef lint
3633965Sjdp#if 0
3733965Sjdpstatic char * const rcsid = "@(#)main.c,v 1.1 1994/02/01 00:34:42 alm Exp";
3833965Sjdp#else
3933965Sjdpstatic char * const rcsid =
4033965Sjdp  "$FreeBSD: head/bin/ed/main.c 59797 2000-04-30 20:46:14Z joe $";
4133965Sjdp#endif
4233965Sjdp#endif /* not lint */
4333965Sjdp
4433965Sjdp/*
4533965Sjdp * CREDITS
4633965Sjdp *
4733965Sjdp *	This program is based on the editor algorithm described in
4833965Sjdp *	Brian W. Kernighan and P. J. Plauger's book "Software Tools
4933965Sjdp *	in Pascal," Addison-Wesley, 1981.
5033965Sjdp *
5133965Sjdp *	The buffering algorithm is attributed to Rodney Ruddock of
5233965Sjdp *	the University of Guelph, Guelph, Ontario.
5333965Sjdp *
5433965Sjdp *	The cbc.c encryption code is adapted from
5533965Sjdp *	the bdes program by Matt Bishop of Dartmouth College,
5633965Sjdp *	Hanover, NH.
5733965Sjdp *
5833965Sjdp */
5933965Sjdp
6033965Sjdp#include <sys/ioctl.h>
6133965Sjdp#include <sys/wait.h>
6233965Sjdp#include <ctype.h>
6333965Sjdp#include <locale.h>
6433965Sjdp#include <pwd.h>
6533965Sjdp#include <setjmp.h>
6633965Sjdp
6733965Sjdp#include "ed.h"
6833965Sjdp
6933965Sjdp
7033965Sjdp#ifdef _POSIX_SOURCE
7133965Sjdpsigjmp_buf env;
7233965Sjdp#else
7333965Sjdpjmp_buf env;
7433965Sjdp#endif
7533965Sjdp
7633965Sjdp/* static buffers */
7733965Sjdpchar stdinbuf[1];		/* stdin buffer */
7833965Sjdpchar *shcmd;			/* shell command buffer */
7933965Sjdpint shcmdsz;			/* shell command buffer size */
8033965Sjdpint shcmdi;			/* shell command buffer index */
8133965Sjdpchar *ibuf;			/* ed command-line buffer */
8233965Sjdpint ibufsz;			/* ed command-line buffer size */
8333965Sjdpchar *ibufp;			/* pointer to ed command-line buffer */
8433965Sjdp
8533965Sjdp/* global flags */
8633965Sjdpint des = 0;			/* if set, use crypt(3) for i/o */
8733965Sjdpint garrulous = 0;		/* if set, print all error messages */
8833965Sjdpint isbinary;			/* if set, buffer contains ASCII NULs */
8933965Sjdpint isglobal;			/* if set, doing a global command */
9033965Sjdpint modified;			/* if set, buffer modified since last write */
9133965Sjdpint mutex = 0;			/* if set, signals set "sigflags" */
9233965Sjdpint red = 0;			/* if set, restrict shell/directory access */
9333965Sjdpint scripted = 0;		/* if set, suppress diagnostics */
9433965Sjdpint sigflags = 0;		/* if set, signals received while mutex set */
9533965Sjdpint sigactive = 0;		/* if set, signal handlers are enabled */
9633965Sjdp
9733965Sjdpchar old_filename[MAXPATHLEN + 1] = "";	/* default filename */
9833965Sjdplong current_addr;		/* current address in editor buffer */
9933965Sjdplong addr_last;			/* last address in editor buffer */
10033965Sjdpint lineno;			/* script line number */
10133965Sjdpchar *prompt;			/* command-line prompt */
10233965Sjdpchar *dps = "*";		/* default command-line prompt */
10333965Sjdp
10433965Sjdpchar *usage = "usage: %s [-] [-sx] [-p string] [name]\n";
10533965Sjdp
10633965Sjdp/* ed: line editor */
10733965Sjdpint
10833965Sjdpmain(argc, argv)
10933965Sjdp	int argc;
11033965Sjdp	char **argv;
11133965Sjdp{
11233965Sjdp	int c, n;
11333965Sjdp	long status = 0;
11433965Sjdp#if __GNUC__
11533965Sjdp	/* Avoid longjmp clobbering */
11633965Sjdp	(void) &argc;
11733965Sjdp	(void) &argv;
11833965Sjdp#endif
11933965Sjdp
12033965Sjdp	(void)setlocale(LC_ALL, "");
12133965Sjdp
12233965Sjdp	red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r';
12333965Sjdptop:
12433965Sjdp	while ((c = getopt(argc, argv, "p:sx")) != -1)
12533965Sjdp		switch(c) {
12633965Sjdp		case 'p':				/* set prompt */
12733965Sjdp			prompt = optarg;
12833965Sjdp			break;
12933965Sjdp		case 's':				/* run script */
13033965Sjdp			scripted = 1;
13133965Sjdp			break;
13233965Sjdp		case 'x':				/* use crypt */
13333965Sjdp#ifdef DES
13433965Sjdp			des = get_keyword();
13533965Sjdp#else
13633965Sjdp			fprintf(stderr, "crypt unavailable\n?\n");
13733965Sjdp#endif
13833965Sjdp			break;
13933965Sjdp
14033965Sjdp		default:
14133965Sjdp			fprintf(stderr, usage, argv[0]);
14233965Sjdp			exit(1);
14333965Sjdp		}
14433965Sjdp	argv += optind;
14533965Sjdp	argc -= optind;
14633965Sjdp	if (argc && **argv == '-') {
14733965Sjdp		scripted = 1;
14833965Sjdp		if (argc > 1) {
14933965Sjdp			optind = 1;
15033965Sjdp			goto top;
15133965Sjdp		}
15233965Sjdp		argv++;
15333965Sjdp		argc--;
15433965Sjdp	}
15533965Sjdp	/* assert: reliable signals! */
15633965Sjdp#ifdef SIGWINCH
15733965Sjdp	handle_winch(SIGWINCH);
15833965Sjdp	if (isatty(0)) signal(SIGWINCH, handle_winch);
15933965Sjdp#endif
16033965Sjdp	signal(SIGHUP, signal_hup);
16133965Sjdp	signal(SIGQUIT, SIG_IGN);
16233965Sjdp	signal(SIGINT, signal_int);
16333965Sjdp#ifdef _POSIX_SOURCE
16433965Sjdp	if ((status = sigsetjmp(env, 1)))
16533965Sjdp#else
16633965Sjdp	if ((status = setjmp(env)))
16733965Sjdp#endif
16833965Sjdp	{
16933965Sjdp		fputs("\n?\n", stderr);
17033965Sjdp		sprintf(errmsg, "interrupt");
17133965Sjdp	} else {
17233965Sjdp		init_buffers();
17333965Sjdp		sigactive = 1;			/* enable signal handlers */
17433965Sjdp		if (argc && **argv && is_legal_filename(*argv)) {
17533965Sjdp			if (read_file(*argv, 0) < 0 && !isatty(0))
17633965Sjdp				quit(2);
17733965Sjdp			else if (**argv != '!')
17833965Sjdp				if (strlcpy(old_filename, *argv, sizeof(old_filename))
17933965Sjdp				    >= sizeof(old_filename))
18033965Sjdp					quit(2);
18133965Sjdp		} else if (argc) {
18233965Sjdp			fputs("?\n", stderr);
18333965Sjdp			if (**argv == '\0')
18433965Sjdp				sprintf(errmsg, "invalid filename");
18533965Sjdp			if (!isatty(0))
18633965Sjdp				quit(2);
18733965Sjdp		}
18833965Sjdp	}
18933965Sjdp	for (;;) {
19033965Sjdp		if (status < 0 && garrulous)
19133965Sjdp			fprintf(stderr, "%s\n", errmsg);
19233965Sjdp		if (prompt) {
19333965Sjdp			printf("%s", prompt);
19433965Sjdp			fflush(stdout);
19533965Sjdp		}
19633965Sjdp		if ((n = get_tty_line()) < 0) {
19733965Sjdp			status = ERR;
19833965Sjdp			continue;
19933965Sjdp		} else if (n == 0) {
20033965Sjdp			if (modified && !scripted) {
20133965Sjdp				fputs("?\n", stderr);
20233965Sjdp				sprintf(errmsg, "warning: file modified");
20333965Sjdp				if (!isatty(0)) {
20433965Sjdp					fprintf(stderr, garrulous ?
20533965Sjdp					    "script, line %d: %s\n" :
20633965Sjdp					    "", lineno, errmsg);
20733965Sjdp					quit(2);
20833965Sjdp				}
20933965Sjdp				clearerr(stdin);
21033965Sjdp				modified = 0;
21133965Sjdp				status = EMOD;
21233965Sjdp				continue;
21333965Sjdp			} else
21433965Sjdp				quit(0);
21533965Sjdp		} else if (ibuf[n - 1] != '\n') {
21633965Sjdp			/* discard line */
21733965Sjdp			sprintf(errmsg, "unexpected end-of-file");
21833965Sjdp			clearerr(stdin);
21933965Sjdp			status = ERR;
22033965Sjdp			continue;
22133965Sjdp		}
22233965Sjdp		isglobal = 0;
22333965Sjdp		if ((status = extract_addr_range()) >= 0 &&
22433965Sjdp		    (status = exec_command()) >= 0)
22533965Sjdp			if (!status ||
22633965Sjdp			    (status = display_lines(current_addr, current_addr,
22733965Sjdp			        status)) >= 0)
22833965Sjdp				continue;
22933965Sjdp		switch (status) {
23033965Sjdp		case EOF:
23133965Sjdp			quit(0);
23233965Sjdp		case EMOD:
23333965Sjdp			modified = 0;
23433965Sjdp			fputs("?\n", stderr);		/* give warning */
23533965Sjdp			sprintf(errmsg, "warning: file modified");
23633965Sjdp			if (!isatty(0)) {
23733965Sjdp				fprintf(stderr, garrulous ?
23833965Sjdp				    "script, line %d: %s\n" :
23933965Sjdp				    "", lineno, errmsg);
24033965Sjdp				quit(2);
24133965Sjdp			}
24233965Sjdp			break;
24333965Sjdp		case FATAL:
24433965Sjdp			if (!isatty(0))
24533965Sjdp				fprintf(stderr, garrulous ?
24633965Sjdp				    "script, line %d: %s\n" : "",
24733965Sjdp				    lineno, errmsg);
24833965Sjdp			else
24933965Sjdp				fprintf(stderr, garrulous ? "%s\n" : "",
25033965Sjdp				    errmsg);
25133965Sjdp			quit(3);
25233965Sjdp		default:
25333965Sjdp			fputs("?\n", stderr);
25433965Sjdp			if (!isatty(0)) {
25533965Sjdp				fprintf(stderr, garrulous ?
25633965Sjdp				    "script, line %d: %s\n" : "",
25733965Sjdp				    lineno, errmsg);
25833965Sjdp				quit(2);
25933965Sjdp			}
26033965Sjdp			break;
26133965Sjdp		}
26233965Sjdp	}
26333965Sjdp	/*NOTREACHED*/
26433965Sjdp}
26533965Sjdp
26633965Sjdplong first_addr, second_addr, addr_cnt;
26733965Sjdp
26833965Sjdp/* extract_addr_range: get line addresses from the command buffer until an
26933965Sjdp   illegal address is seen; return status */
27033965Sjdpint
27133965Sjdpextract_addr_range()
27233965Sjdp{
27333965Sjdp	long addr;
27433965Sjdp
27533965Sjdp	addr_cnt = 0;
27633965Sjdp	first_addr = second_addr = current_addr;
27733965Sjdp	while ((addr = next_addr()) >= 0) {
27833965Sjdp		addr_cnt++;
27933965Sjdp		first_addr = second_addr;
28033965Sjdp		second_addr = addr;
28133965Sjdp		if (*ibufp != ',' && *ibufp != ';')
28233965Sjdp			break;
28333965Sjdp		else if (*ibufp++ == ';')
28433965Sjdp			current_addr = addr;
28533965Sjdp	}
28633965Sjdp	if ((addr_cnt = min(addr_cnt, 2)) == 1 || second_addr != addr)
28733965Sjdp		first_addr = second_addr;
28833965Sjdp	return (addr == ERR) ? ERR : 0;
28933965Sjdp}
29033965Sjdp
29133965Sjdp
29233965Sjdp#define SKIP_BLANKS() while (isspace((unsigned char)*ibufp) && *ibufp != '\n') ibufp++
29333965Sjdp
29433965Sjdp#define MUST_BE_FIRST() \
29533965Sjdp	if (!first) { sprintf(errmsg, "invalid address"); return ERR; }
29633965Sjdp
29733965Sjdp/*  next_addr: return the next line address in the command buffer */
29833965Sjdplong
29933965Sjdpnext_addr()
30033965Sjdp{
30133965Sjdp	char *hd;
30233965Sjdp	long addr = current_addr;
30333965Sjdp	long n;
30433965Sjdp	int first = 1;
30533965Sjdp	int c;
30633965Sjdp
30733965Sjdp	SKIP_BLANKS();
30833965Sjdp	for (hd = ibufp;; first = 0)
30933965Sjdp		switch (c = *ibufp) {
31033965Sjdp		case '+':
31133965Sjdp		case '\t':
31233965Sjdp		case ' ':
31333965Sjdp		case '-':
31433965Sjdp		case '^':
31533965Sjdp			ibufp++;
31633965Sjdp			SKIP_BLANKS();
31733965Sjdp			if (isdigit((unsigned char)*ibufp)) {
31833965Sjdp				STRTOL(n, ibufp);
31933965Sjdp				addr += (c == '-' || c == '^') ? -n : n;
32033965Sjdp			} else if (!isspace((unsigned char)c))
32133965Sjdp				addr += (c == '-' || c == '^') ? -1 : 1;
32233965Sjdp			break;
32333965Sjdp		case '0': case '1': case '2':
32433965Sjdp		case '3': case '4': case '5':
32533965Sjdp		case '6': case '7': case '8': case '9':
32633965Sjdp			MUST_BE_FIRST();
32733965Sjdp			STRTOL(addr, ibufp);
32833965Sjdp			break;
32933965Sjdp		case '.':
33033965Sjdp		case '$':
33133965Sjdp			MUST_BE_FIRST();
33233965Sjdp			ibufp++;
33333965Sjdp			addr = (c == '.') ? current_addr : addr_last;
33433965Sjdp			break;
33533965Sjdp		case '/':
33633965Sjdp		case '?':
33733965Sjdp			MUST_BE_FIRST();
33833965Sjdp			if ((addr = get_matching_node_addr(
33933965Sjdp			    get_compiled_pattern(), c == '/')) < 0)
34033965Sjdp				return ERR;
34133965Sjdp			else if (c == *ibufp)
34233965Sjdp				ibufp++;
34333965Sjdp			break;
34433965Sjdp		case '\'':
34533965Sjdp			MUST_BE_FIRST();
34633965Sjdp			ibufp++;
34733965Sjdp			if ((addr = get_marked_node_addr(*ibufp++)) < 0)
34833965Sjdp				return ERR;
34933965Sjdp			break;
35033965Sjdp		case '%':
35133965Sjdp		case ',':
35233965Sjdp		case ';':
35333965Sjdp			if (first) {
35433965Sjdp				ibufp++;
35533965Sjdp				addr_cnt++;
35633965Sjdp				second_addr = (c == ';') ? current_addr : 1;
35733965Sjdp				addr = addr_last;
35833965Sjdp				break;
35933965Sjdp			}
36033965Sjdp			/* FALL THROUGH */
36133965Sjdp		default:
36233965Sjdp			if (ibufp == hd)
36333965Sjdp				return EOF;
36433965Sjdp			else if (addr < 0 || addr_last < addr) {
36533965Sjdp				sprintf(errmsg, "invalid address");
36633965Sjdp				return ERR;
36733965Sjdp			} else
36833965Sjdp				return addr;
36933965Sjdp		}
37033965Sjdp	/* NOTREACHED */
37133965Sjdp}
37233965Sjdp
37333965Sjdp
37433965Sjdp#ifdef BACKWARDS
37533965Sjdp/* GET_THIRD_ADDR: get a legal address from the command buffer */
37633965Sjdp#define GET_THIRD_ADDR(addr) \
37733965Sjdp{ \
37833965Sjdp	long ol1, ol2; \
37933965Sjdp\
38033965Sjdp	ol1 = first_addr, ol2 = second_addr; \
38133965Sjdp	if (extract_addr_range() < 0) \
38233965Sjdp		return ERR; \
38333965Sjdp	else if (addr_cnt == 0) { \
38433965Sjdp		sprintf(errmsg, "destination expected"); \
38533965Sjdp		return ERR; \
38633965Sjdp	} else if (second_addr < 0 || addr_last < second_addr) { \
38733965Sjdp		sprintf(errmsg, "invalid address"); \
38833965Sjdp		return ERR; \
38933965Sjdp	} \
39033965Sjdp	addr = second_addr; \
39133965Sjdp	first_addr = ol1, second_addr = ol2; \
39233965Sjdp}
39333965Sjdp#else	/* BACKWARDS */
39433965Sjdp/* GET_THIRD_ADDR: get a legal address from the command buffer */
39533965Sjdp#define GET_THIRD_ADDR(addr) \
39633965Sjdp{ \
39733965Sjdp	long ol1, ol2; \
39833965Sjdp\
39933965Sjdp	ol1 = first_addr, ol2 = second_addr; \
40033965Sjdp	if (extract_addr_range() < 0) \
40133965Sjdp		return ERR; \
40233965Sjdp	if (second_addr < 0 || addr_last < second_addr) { \
40333965Sjdp		sprintf(errmsg, "invalid address"); \
40433965Sjdp		return ERR; \
40533965Sjdp	} \
40633965Sjdp	addr = second_addr; \
40733965Sjdp	first_addr = ol1, second_addr = ol2; \
40833965Sjdp}
40933965Sjdp#endif
41033965Sjdp
41133965Sjdp
41233965Sjdp/* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */
41333965Sjdp#define GET_COMMAND_SUFFIX() { \
41433965Sjdp	int done = 0; \
41533965Sjdp	do { \
41633965Sjdp		switch(*ibufp) { \
41733965Sjdp		case 'p': \
41833965Sjdp			gflag |= GPR, ibufp++; \
41933965Sjdp			break; \
42033965Sjdp		case 'l': \
42133965Sjdp			gflag |= GLS, ibufp++; \
42233965Sjdp			break; \
42333965Sjdp		case 'n': \
42433965Sjdp			gflag |= GNP, ibufp++; \
42533965Sjdp			break; \
42633965Sjdp		default: \
42733965Sjdp			done++; \
42833965Sjdp		} \
42933965Sjdp	} while (!done); \
43033965Sjdp	if (*ibufp++ != '\n') { \
43133965Sjdp		sprintf(errmsg, "invalid command suffix"); \
43233965Sjdp		return ERR; \
43333965Sjdp	} \
43433965Sjdp}
43533965Sjdp
43633965Sjdp
43733965Sjdp/* sflags */
43833965Sjdp#define SGG 001		/* complement previous global substitute suffix */
43933965Sjdp#define SGP 002		/* complement previous print suffix */
44033965Sjdp#define SGR 004		/* use last regex instead of last pat */
44133965Sjdp#define SGF 010		/* repeat last substitution */
44233965Sjdp
44333965Sjdpint patlock = 0;	/* if set, pattern not freed by get_compiled_pattern() */
44433965Sjdp
44533965Sjdplong rows = 22;		/* scroll length: ws_row - 2 */
44633965Sjdp
44733965Sjdp/* exec_command: execute the next command in command buffer; return print
44833965Sjdp   request, if any */
44933965Sjdpint
45033965Sjdpexec_command()
45133965Sjdp{
45233965Sjdp	extern long u_current_addr;
45333965Sjdp	extern long u_addr_last;
45433965Sjdp
45533965Sjdp	static pattern_t *pat = NULL;
45633965Sjdp	static int sgflag = 0;
45733965Sjdp	static long sgnum = 0;
45833965Sjdp
45933965Sjdp	pattern_t *tpat;
46033965Sjdp	char *fnp;
46133965Sjdp	int gflag = 0;
46233965Sjdp	int sflags = 0;
46333965Sjdp	long addr = 0;
46433965Sjdp	int n = 0;
46533965Sjdp	int c;
46633965Sjdp
46733965Sjdp	SKIP_BLANKS();
46833965Sjdp	switch(c = *ibufp++) {
46933965Sjdp	case 'a':
47033965Sjdp		GET_COMMAND_SUFFIX();
47133965Sjdp		if (!isglobal) clear_undo_stack();
47233965Sjdp		if (append_lines(second_addr) < 0)
47333965Sjdp			return ERR;
47433965Sjdp		break;
47533965Sjdp	case 'c':
47633965Sjdp		if (check_addr_range(current_addr, current_addr) < 0)
47733965Sjdp			return ERR;
47833965Sjdp		GET_COMMAND_SUFFIX();
47933965Sjdp		if (!isglobal) clear_undo_stack();
48033965Sjdp		if (delete_lines(first_addr, second_addr) < 0 ||
48133965Sjdp		    append_lines(current_addr) < 0)
48233965Sjdp			return ERR;
48333965Sjdp		break;
48433965Sjdp	case 'd':
48533965Sjdp		if (check_addr_range(current_addr, current_addr) < 0)
48633965Sjdp			return ERR;
48733965Sjdp		GET_COMMAND_SUFFIX();
48833965Sjdp		if (!isglobal) clear_undo_stack();
48933965Sjdp		if (delete_lines(first_addr, second_addr) < 0)
49033965Sjdp			return ERR;
49133965Sjdp		else if ((addr = INC_MOD(current_addr, addr_last)) != 0)
49233965Sjdp			current_addr = addr;
49333965Sjdp		break;
49433965Sjdp	case 'e':
49533965Sjdp		if (modified && !scripted)
49633965Sjdp			return EMOD;
49733965Sjdp		/* fall through */
49833965Sjdp	case 'E':
49933965Sjdp		if (addr_cnt > 0) {
50033965Sjdp			sprintf(errmsg, "unexpected address");
50133965Sjdp			return ERR;
50233965Sjdp		} else if (!isspace((unsigned char)*ibufp)) {
50333965Sjdp			sprintf(errmsg, "unexpected command suffix");
50433965Sjdp			return ERR;
50533965Sjdp		} else if ((fnp = get_filename()) == NULL)
50633965Sjdp			return ERR;
50733965Sjdp		GET_COMMAND_SUFFIX();
50833965Sjdp		if (delete_lines(1, addr_last) < 0)
50933965Sjdp			return ERR;
51033965Sjdp		clear_undo_stack();
51133965Sjdp		if (close_sbuf() < 0)
51233965Sjdp			return ERR;
51333965Sjdp		else if (open_sbuf() < 0)
51433965Sjdp			return FATAL;
51533965Sjdp		if (*fnp && *fnp != '!') strcpy(old_filename, fnp);
51633965Sjdp#ifdef BACKWARDS
51733965Sjdp		if (*fnp == '\0' && *old_filename == '\0') {
51833965Sjdp			sprintf(errmsg, "no current filename");
51933965Sjdp			return ERR;
52033965Sjdp		}
52133965Sjdp#endif
52233965Sjdp		if (read_file(*fnp ? fnp : old_filename, 0) < 0)
52333965Sjdp			return ERR;
52433965Sjdp		clear_undo_stack();
52533965Sjdp		modified = 0;
52633965Sjdp		u_current_addr = u_addr_last = -1;
52733965Sjdp		break;
52833965Sjdp	case 'f':
52933965Sjdp		if (addr_cnt > 0) {
53033965Sjdp			sprintf(errmsg, "unexpected address");
53133965Sjdp			return ERR;
53233965Sjdp		} else if (!isspace((unsigned char)*ibufp)) {
53333965Sjdp			sprintf(errmsg, "unexpected command suffix");
53433965Sjdp			return ERR;
53533965Sjdp		} else if ((fnp = get_filename()) == NULL)
53633965Sjdp			return ERR;
53733965Sjdp		else if (*fnp == '!') {
53833965Sjdp			sprintf(errmsg, "invalid redirection");
53933965Sjdp			return ERR;
54033965Sjdp		}
54133965Sjdp		GET_COMMAND_SUFFIX();
54233965Sjdp		if (*fnp) strcpy(old_filename, fnp);
54333965Sjdp		printf("%s\n", strip_escapes(old_filename));
54433965Sjdp		break;
54533965Sjdp	case 'g':
54633965Sjdp	case 'v':
54733965Sjdp	case 'G':
54833965Sjdp	case 'V':
54933965Sjdp		if (isglobal) {
55033965Sjdp			sprintf(errmsg, "cannot nest global commands");
55133965Sjdp			return ERR;
55233965Sjdp		} else if (check_addr_range(1, addr_last) < 0)
55333965Sjdp			return ERR;
55433965Sjdp		else if (build_active_list(c == 'g' || c == 'G') < 0)
55533965Sjdp			return ERR;
55633965Sjdp		else if ((n = (c == 'G' || c == 'V')))
55733965Sjdp			GET_COMMAND_SUFFIX();
55833965Sjdp		isglobal++;
55933965Sjdp		if (exec_global(n, gflag) < 0)
56033965Sjdp			return ERR;
56133965Sjdp		break;
56233965Sjdp	case 'h':
56333965Sjdp		if (addr_cnt > 0) {
56433965Sjdp			sprintf(errmsg, "unexpected address");
56533965Sjdp			return ERR;
56633965Sjdp		}
56733965Sjdp		GET_COMMAND_SUFFIX();
56833965Sjdp		if (*errmsg) fprintf(stderr, "%s\n", errmsg);
56933965Sjdp		break;
57033965Sjdp	case 'H':
57133965Sjdp		if (addr_cnt > 0) {
57233965Sjdp			sprintf(errmsg, "unexpected address");
57333965Sjdp			return ERR;
57433965Sjdp		}
57533965Sjdp		GET_COMMAND_SUFFIX();
57633965Sjdp		if ((garrulous = 1 - garrulous) && *errmsg)
57733965Sjdp			fprintf(stderr, "%s\n", errmsg);
57833965Sjdp		break;
57933965Sjdp	case 'i':
58033965Sjdp		if (second_addr == 0) {
58133965Sjdp			sprintf(errmsg, "invalid address");
58233965Sjdp			return ERR;
58333965Sjdp		}
58433965Sjdp		GET_COMMAND_SUFFIX();
58533965Sjdp		if (!isglobal) clear_undo_stack();
58633965Sjdp		if (append_lines(second_addr - 1) < 0)
58733965Sjdp			return ERR;
58833965Sjdp		break;
58933965Sjdp	case 'j':
59033965Sjdp		if (check_addr_range(current_addr, current_addr + 1) < 0)
59133965Sjdp			return ERR;
59233965Sjdp		GET_COMMAND_SUFFIX();
59333965Sjdp		if (!isglobal) clear_undo_stack();
59433965Sjdp		if (first_addr != second_addr &&
59533965Sjdp		    join_lines(first_addr, second_addr) < 0)
59633965Sjdp			return ERR;
59733965Sjdp		break;
59833965Sjdp	case 'k':
59933965Sjdp		c = *ibufp++;
60033965Sjdp		if (second_addr == 0) {
60133965Sjdp			sprintf(errmsg, "invalid address");
60233965Sjdp			return ERR;
60333965Sjdp		}
60433965Sjdp		GET_COMMAND_SUFFIX();
60533965Sjdp		if (mark_line_node(get_addressed_line_node(second_addr), c) < 0)
60633965Sjdp			return ERR;
60733965Sjdp		break;
60833965Sjdp	case 'l':
60933965Sjdp		if (check_addr_range(current_addr, current_addr) < 0)
61033965Sjdp			return ERR;
61133965Sjdp		GET_COMMAND_SUFFIX();
61233965Sjdp		if (display_lines(first_addr, second_addr, gflag | GLS) < 0)
61333965Sjdp			return ERR;
61433965Sjdp		gflag = 0;
61533965Sjdp		break;
61633965Sjdp	case 'm':
61733965Sjdp		if (check_addr_range(current_addr, current_addr) < 0)
61833965Sjdp			return ERR;
61933965Sjdp		GET_THIRD_ADDR(addr);
62033965Sjdp		if (first_addr <= addr && addr < second_addr) {
62133965Sjdp			sprintf(errmsg, "invalid destination");
62233965Sjdp			return ERR;
62333965Sjdp		}
62433965Sjdp		GET_COMMAND_SUFFIX();
62533965Sjdp		if (!isglobal) clear_undo_stack();
62633965Sjdp		if (move_lines(addr) < 0)
62733965Sjdp			return ERR;
62833965Sjdp		break;
62933965Sjdp	case 'n':
63033965Sjdp		if (check_addr_range(current_addr, current_addr) < 0)
63133965Sjdp			return ERR;
63233965Sjdp		GET_COMMAND_SUFFIX();
63333965Sjdp		if (display_lines(first_addr, second_addr, gflag | GNP) < 0)
63433965Sjdp			return ERR;
63533965Sjdp		gflag = 0;
63633965Sjdp		break;
63733965Sjdp	case 'p':
63833965Sjdp		if (check_addr_range(current_addr, current_addr) < 0)
63933965Sjdp			return ERR;
64033965Sjdp		GET_COMMAND_SUFFIX();
64133965Sjdp		if (display_lines(first_addr, second_addr, gflag | GPR) < 0)
64233965Sjdp			return ERR;
64333965Sjdp		gflag = 0;
64433965Sjdp		break;
64533965Sjdp	case 'P':
64633965Sjdp		if (addr_cnt > 0) {
64733965Sjdp			sprintf(errmsg, "unexpected address");
64833965Sjdp			return ERR;
64933965Sjdp		}
65033965Sjdp		GET_COMMAND_SUFFIX();
65133965Sjdp		prompt = prompt ? NULL : optarg ? optarg : dps;
65233965Sjdp		break;
65333965Sjdp	case 'q':
65433965Sjdp	case 'Q':
65533965Sjdp		if (addr_cnt > 0) {
65633965Sjdp			sprintf(errmsg, "unexpected address");
65733965Sjdp			return ERR;
65833965Sjdp		}
65933965Sjdp		GET_COMMAND_SUFFIX();
66033965Sjdp		gflag =  (modified && !scripted && c == 'q') ? EMOD : EOF;
66133965Sjdp		break;
66233965Sjdp	case 'r':
66333965Sjdp		if (!isspace((unsigned char)*ibufp)) {
66433965Sjdp			sprintf(errmsg, "unexpected command suffix");
66533965Sjdp			return ERR;
66633965Sjdp		} else if (addr_cnt == 0)
66733965Sjdp			second_addr = addr_last;
66833965Sjdp		if ((fnp = get_filename()) == NULL)
66933965Sjdp			return ERR;
67033965Sjdp		GET_COMMAND_SUFFIX();
67133965Sjdp		if (!isglobal) clear_undo_stack();
67233965Sjdp		if (*old_filename == '\0' && *fnp != '!')
67333965Sjdp			strcpy(old_filename, fnp);
67433965Sjdp#ifdef BACKWARDS
67533965Sjdp		if (*fnp == '\0' && *old_filename == '\0') {
67633965Sjdp			sprintf(errmsg, "no current filename");
67733965Sjdp			return ERR;
67833965Sjdp		}
67933965Sjdp#endif
68033965Sjdp		if ((addr = read_file(*fnp ? fnp : old_filename, second_addr)) < 0)
68133965Sjdp			return ERR;
68233965Sjdp		else if (addr && addr != addr_last)
68333965Sjdp			modified = 1;
68433965Sjdp		break;
68533965Sjdp	case 's':
68633965Sjdp		do {
68733965Sjdp			switch(*ibufp) {
68833965Sjdp			case '\n':
68933965Sjdp				sflags |=SGF;
69033965Sjdp				break;
69133965Sjdp			case 'g':
69233965Sjdp				sflags |= SGG;
69333965Sjdp				ibufp++;
69433965Sjdp				break;
69533965Sjdp			case 'p':
69633965Sjdp				sflags |= SGP;
69733965Sjdp				ibufp++;
69833965Sjdp				break;
69933965Sjdp			case 'r':
70033965Sjdp				sflags |= SGR;
70133965Sjdp				ibufp++;
70233965Sjdp				break;
70333965Sjdp			case '0': case '1': case '2': case '3': case '4':
70433965Sjdp			case '5': case '6': case '7': case '8': case '9':
70533965Sjdp				STRTOL(sgnum, ibufp);
70633965Sjdp				sflags |= SGF;
70733965Sjdp				sgflag &= ~GSG;		/* override GSG */
70833965Sjdp				break;
70933965Sjdp			default:
71033965Sjdp				if (sflags) {
71133965Sjdp					sprintf(errmsg, "invalid command suffix");
71233965Sjdp					return ERR;
71333965Sjdp				}
71433965Sjdp			}
71533965Sjdp		} while (sflags && *ibufp != '\n');
71633965Sjdp		if (sflags && !pat) {
71733965Sjdp			sprintf(errmsg, "no previous substitution");
71833965Sjdp			return ERR;
71933965Sjdp		} else if (sflags & SGG)
72033965Sjdp			sgnum = 0;		/* override numeric arg */
72133965Sjdp		if (*ibufp != '\n' && *(ibufp + 1) == '\n') {
72233965Sjdp			sprintf(errmsg, "invalid pattern delimiter");
72333965Sjdp			return ERR;
72433965Sjdp		}
72533965Sjdp		tpat = pat;
72633965Sjdp		SPL1();
72733965Sjdp		if ((!sflags || (sflags & SGR)) &&
72833965Sjdp		    (tpat = get_compiled_pattern()) == NULL) {
72933965Sjdp		 	SPL0();
73033965Sjdp			return ERR;
73133965Sjdp		} else if (tpat != pat) {
73233965Sjdp			if (pat) {
73333965Sjdp				regfree(pat);
73433965Sjdp				free(pat);
73533965Sjdp			}
73633965Sjdp			pat = tpat;
73733965Sjdp			patlock = 1;		/* reserve pattern */
73833965Sjdp		}
73933965Sjdp		SPL0();
74033965Sjdp		if (!sflags && extract_subst_tail(&sgflag, &sgnum) < 0)
74133965Sjdp			return ERR;
74233965Sjdp		else if (isglobal)
74333965Sjdp			sgflag |= GLB;
74433965Sjdp		else
74533965Sjdp			sgflag &= ~GLB;
74633965Sjdp		if (sflags & SGG)
74733965Sjdp			sgflag ^= GSG;
74833965Sjdp		if (sflags & SGP)
74933965Sjdp			sgflag ^= GPR, sgflag &= ~(GLS | GNP);
75033965Sjdp		do {
75133965Sjdp			switch(*ibufp) {
75233965Sjdp			case 'p':
75333965Sjdp				sgflag |= GPR, ibufp++;
75433965Sjdp				break;
75533965Sjdp			case 'l':
75633965Sjdp				sgflag |= GLS, ibufp++;
75733965Sjdp				break;
75833965Sjdp			case 'n':
75933965Sjdp				sgflag |= GNP, ibufp++;
76033965Sjdp				break;
76133965Sjdp			default:
76233965Sjdp				n++;
76333965Sjdp			}
76433965Sjdp		} while (!n);
76533965Sjdp		if (check_addr_range(current_addr, current_addr) < 0)
76633965Sjdp			return ERR;
76733965Sjdp		GET_COMMAND_SUFFIX();
76833965Sjdp		if (!isglobal) clear_undo_stack();
76933965Sjdp		if (search_and_replace(pat, sgflag, sgnum) < 0)
77033965Sjdp			return ERR;
77133965Sjdp		break;
77233965Sjdp	case 't':
77333965Sjdp		if (check_addr_range(current_addr, current_addr) < 0)
77433965Sjdp			return ERR;
77533965Sjdp		GET_THIRD_ADDR(addr);
77633965Sjdp		GET_COMMAND_SUFFIX();
77733965Sjdp		if (!isglobal) clear_undo_stack();
77833965Sjdp		if (copy_lines(addr) < 0)
77933965Sjdp			return ERR;
78033965Sjdp		break;
78133965Sjdp	case 'u':
78233965Sjdp		if (addr_cnt > 0) {
78333965Sjdp			sprintf(errmsg, "unexpected address");
78433965Sjdp			return ERR;
78533965Sjdp		}
78633965Sjdp		GET_COMMAND_SUFFIX();
78733965Sjdp		if (pop_undo_stack() < 0)
78833965Sjdp			return ERR;
78933965Sjdp		break;
79033965Sjdp	case 'w':
79133965Sjdp	case 'W':
79233965Sjdp		if ((n = *ibufp) == 'q' || n == 'Q') {
79333965Sjdp			gflag = EOF;
79433965Sjdp			ibufp++;
79533965Sjdp		}
79633965Sjdp		if (!isspace((unsigned char)*ibufp)) {
79733965Sjdp			sprintf(errmsg, "unexpected command suffix");
79833965Sjdp			return ERR;
79933965Sjdp		} else if ((fnp = get_filename()) == NULL)
80033965Sjdp			return ERR;
80133965Sjdp		if (addr_cnt == 0 && !addr_last)
80233965Sjdp			first_addr = second_addr = 0;
80333965Sjdp		else if (check_addr_range(1, addr_last) < 0)
80433965Sjdp			return ERR;
80533965Sjdp		GET_COMMAND_SUFFIX();
80633965Sjdp		if (*old_filename == '\0' && *fnp != '!')
80733965Sjdp			strcpy(old_filename, fnp);
80833965Sjdp#ifdef BACKWARDS
80933965Sjdp		if (*fnp == '\0' && *old_filename == '\0') {
81033965Sjdp			sprintf(errmsg, "no current filename");
81133965Sjdp			return ERR;
81233965Sjdp		}
81333965Sjdp#endif
81433965Sjdp		if ((addr = write_file(*fnp ? fnp : old_filename,
81533965Sjdp		    (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0)
81633965Sjdp			return ERR;
81733965Sjdp		else if (addr == addr_last)
81833965Sjdp			modified = 0;
81933965Sjdp		else if (modified && !scripted && n == 'q')
82033965Sjdp			gflag = EMOD;
82133965Sjdp		break;
82233965Sjdp	case 'x':
82333965Sjdp		if (addr_cnt > 0) {
82433965Sjdp			sprintf(errmsg, "unexpected address");
82533965Sjdp			return ERR;
82633965Sjdp		}
82733965Sjdp		GET_COMMAND_SUFFIX();
82833965Sjdp#ifdef DES
82933965Sjdp		des = get_keyword();
83033965Sjdp#else
83133965Sjdp		sprintf(errmsg, "crypt unavailable");
83233965Sjdp		return ERR;
83333965Sjdp#endif
83433965Sjdp		break;
83533965Sjdp	case 'z':
83633965Sjdp#ifdef BACKWARDS
83733965Sjdp		if (check_addr_range(first_addr = 1, current_addr + 1) < 0)
83833965Sjdp#else
83933965Sjdp		if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0)
84033965Sjdp#endif
84133965Sjdp			return ERR;
84233965Sjdp		else if ('0' < *ibufp && *ibufp <= '9')
84333965Sjdp			STRTOL(rows, ibufp);
84433965Sjdp		GET_COMMAND_SUFFIX();
84533965Sjdp		if (display_lines(second_addr, min(addr_last,
84633965Sjdp		    second_addr + rows), gflag) < 0)
84733965Sjdp			return ERR;
84833965Sjdp		gflag = 0;
84933965Sjdp		break;
85033965Sjdp	case '=':
85133965Sjdp		GET_COMMAND_SUFFIX();
85233965Sjdp		printf("%ld\n", addr_cnt ? second_addr : addr_last);
85333965Sjdp		break;
85433965Sjdp	case '!':
85533965Sjdp		if (addr_cnt > 0) {
85633965Sjdp			sprintf(errmsg, "unexpected address");
85733965Sjdp			return ERR;
85833965Sjdp		} else if ((sflags = get_shell_command()) < 0)
85933965Sjdp			return ERR;
86033965Sjdp		GET_COMMAND_SUFFIX();
86133965Sjdp		if (sflags) printf("%s\n", shcmd + 1);
86233965Sjdp		system(shcmd + 1);
86333965Sjdp		if (!scripted) printf("!\n");
86433965Sjdp		break;
86533965Sjdp	case '\n':
86633965Sjdp#ifdef BACKWARDS
86733965Sjdp		if (check_addr_range(first_addr = 1, current_addr + 1) < 0
86833965Sjdp#else
86933965Sjdp		if (check_addr_range(first_addr = 1, current_addr + !isglobal) < 0
87033965Sjdp#endif
87133965Sjdp		 || display_lines(second_addr, second_addr, 0) < 0)
87233965Sjdp			return ERR;
87333965Sjdp		break;
87433965Sjdp	default:
87533965Sjdp		sprintf(errmsg, "unknown command");
87633965Sjdp		return ERR;
87733965Sjdp	}
87833965Sjdp	return gflag;
87933965Sjdp}
88033965Sjdp
88133965Sjdp
88233965Sjdp/* check_addr_range: return status of address range check */
88333965Sjdpint
88433965Sjdpcheck_addr_range(n, m)
88533965Sjdp	long n, m;
88633965Sjdp{
88733965Sjdp	if (addr_cnt == 0) {
88833965Sjdp		first_addr = n;
88933965Sjdp		second_addr = m;
89033965Sjdp	}
89133965Sjdp	if (first_addr > second_addr || 1 > first_addr ||
89233965Sjdp	    second_addr > addr_last) {
89333965Sjdp		sprintf(errmsg, "invalid address");
89433965Sjdp		return ERR;
89533965Sjdp	}
89633965Sjdp	return 0;
89733965Sjdp}
89833965Sjdp
89933965Sjdp
90033965Sjdp/* get_matching_node_addr: return the address of the next line matching a
90133965Sjdp   pattern in a given direction.  wrap around begin/end of editor buffer if
90233965Sjdp   necessary */
90333965Sjdplong
90433965Sjdpget_matching_node_addr(pat, dir)
90533965Sjdp	pattern_t *pat;
90633965Sjdp	int dir;
90733965Sjdp{
90833965Sjdp	char *s;
90933965Sjdp	long n = current_addr;
91033965Sjdp	line_t *lp;
91133965Sjdp
91233965Sjdp	if (!pat) return ERR;
91333965Sjdp	do {
91433965Sjdp	       if ((n = dir ? INC_MOD(n, addr_last) : DEC_MOD(n, addr_last))) {
91533965Sjdp			lp = get_addressed_line_node(n);
91633965Sjdp			if ((s = get_sbuf_line(lp)) == NULL)
91733965Sjdp				return ERR;
91833965Sjdp			if (isbinary)
91933965Sjdp				NUL_TO_NEWLINE(s, lp->len);
92033965Sjdp			if (!regexec(pat, s, 0, NULL, 0))
92133965Sjdp				return n;
92233965Sjdp	       }
92333965Sjdp	} while (n != current_addr);
92433965Sjdp	sprintf(errmsg, "no match");
92533965Sjdp	return  ERR;
92633965Sjdp}
92733965Sjdp
92833965Sjdp
92933965Sjdp/* get_filename: return pointer to copy of filename in the command buffer */
93033965Sjdpchar *
93133965Sjdpget_filename()
93233965Sjdp{
93333965Sjdp	static char *file = NULL;
93433965Sjdp	static int filesz = 0;
93533965Sjdp
93633965Sjdp	int n;
93733965Sjdp
93833965Sjdp	if (*ibufp != '\n') {
93933965Sjdp		SKIP_BLANKS();
94033965Sjdp		if (*ibufp == '\n') {
94133965Sjdp			sprintf(errmsg, "invalid filename");
94233965Sjdp			return NULL;
94333965Sjdp		} else if ((ibufp = get_extended_line(&n, 1)) == NULL)
94433965Sjdp			return NULL;
94533965Sjdp		else if (*ibufp == '!') {
94633965Sjdp			ibufp++;
94733965Sjdp			if ((n = get_shell_command()) < 0)
94833965Sjdp				return NULL;
94933965Sjdp			if (n) printf("%s\n", shcmd + 1);
95033965Sjdp			return shcmd;
95133965Sjdp		} else if (n - 1 > MAXPATHLEN) {
95233965Sjdp			sprintf(errmsg, "filename too long");
95333965Sjdp			return  NULL;
95433965Sjdp		}
95533965Sjdp	}
95633965Sjdp#ifndef BACKWARDS
95733965Sjdp	else if (*old_filename == '\0') {
95833965Sjdp		sprintf(errmsg, "no current filename");
95933965Sjdp		return  NULL;
96033965Sjdp	}
96133965Sjdp#endif
96233965Sjdp	REALLOC(file, filesz, MAXPATHLEN + 1, NULL);
96333965Sjdp	for (n = 0; *ibufp != '\n';)
96433965Sjdp		file[n++] = *ibufp++;
96533965Sjdp	file[n] = '\0';
96633965Sjdp	return is_legal_filename(file) ? file : NULL;
96733965Sjdp}
96833965Sjdp
96933965Sjdp
97033965Sjdp/* get_shell_command: read a shell command from stdin; return substitution
97133965Sjdp   status */
97233965Sjdpint
97333965Sjdpget_shell_command()
97433965Sjdp{
97533965Sjdp	static char *buf = NULL;
97633965Sjdp	static int n = 0;
97733965Sjdp
97833965Sjdp	char *s;			/* substitution char pointer */
97933965Sjdp	int i = 0;
98033965Sjdp	int j = 0;
98133965Sjdp
98233965Sjdp	if (red) {
98333965Sjdp		sprintf(errmsg, "shell access restricted");
98433965Sjdp		return ERR;
98533965Sjdp	} else if ((s = ibufp = get_extended_line(&j, 1)) == NULL)
98633965Sjdp		return ERR;
98733965Sjdp	REALLOC(buf, n, j + 1, ERR);
98833965Sjdp	buf[i++] = '!';			/* prefix command w/ bang */
98933965Sjdp	while (*ibufp != '\n')
99033965Sjdp		switch (*ibufp) {
99133965Sjdp		default:
99233965Sjdp			REALLOC(buf, n, i + 2, ERR);
99333965Sjdp			buf[i++] = *ibufp;
99433965Sjdp			if (*ibufp++ == '\\')
99533965Sjdp				buf[i++] = *ibufp++;
99633965Sjdp			break;
99733965Sjdp		case '!':
99833965Sjdp			if (s != ibufp) {
99933965Sjdp				REALLOC(buf, n, i + 1, ERR);
100033965Sjdp				buf[i++] = *ibufp++;
100133965Sjdp			}
100233965Sjdp#ifdef BACKWARDS
100333965Sjdp			else if (shcmd == NULL || *(shcmd + 1) == '\0')
100433965Sjdp#else
100533965Sjdp			else if (shcmd == NULL)
100633965Sjdp#endif
100733965Sjdp			{
100833965Sjdp				sprintf(errmsg, "no previous command");
100933965Sjdp				return ERR;
101033965Sjdp			} else {
101133965Sjdp				REALLOC(buf, n, i + shcmdi, ERR);
101233965Sjdp				for (s = shcmd + 1; s < shcmd + shcmdi;)
101333965Sjdp					buf[i++] = *s++;
101433965Sjdp				s = ibufp++;
101533965Sjdp			}
101633965Sjdp			break;
101733965Sjdp		case '%':
101833965Sjdp			if (*old_filename  == '\0') {
101933965Sjdp				sprintf(errmsg, "no current filename");
102033965Sjdp				return ERR;
102133965Sjdp			}
102233965Sjdp			j = strlen(s = strip_escapes(old_filename));
102333965Sjdp			REALLOC(buf, n, i + j, ERR);
102433965Sjdp			while (j--)
102533965Sjdp				buf[i++] = *s++;
102633965Sjdp			s = ibufp++;
102733965Sjdp			break;
102833965Sjdp		}
102933965Sjdp	REALLOC(shcmd, shcmdsz, i + 1, ERR);
103033965Sjdp	memcpy(shcmd, buf, i);
103133965Sjdp	shcmd[shcmdi = i] = '\0';
103233965Sjdp	return *s == '!' || *s == '%';
103333965Sjdp}
103433965Sjdp
103533965Sjdp
103633965Sjdp/* append_lines: insert text from stdin to after line n; stop when either a
103733965Sjdp   single period is read or EOF; return status */
103833965Sjdpint
103933965Sjdpappend_lines(n)
104033965Sjdp	long n;
104133965Sjdp{
104233965Sjdp	int l;
104333965Sjdp	char *lp = ibuf;
104433965Sjdp	char *eot;
104533965Sjdp	undo_t *up = NULL;
104633965Sjdp
104733965Sjdp	for (current_addr = n;;) {
104833965Sjdp		if (!isglobal) {
104933965Sjdp			if ((l = get_tty_line()) < 0)
105033965Sjdp				return ERR;
105133965Sjdp			else if (l == 0 || ibuf[l - 1] != '\n') {
105233965Sjdp				clearerr(stdin);
105333965Sjdp				return  l ? EOF : 0;
105433965Sjdp			}
105533965Sjdp			lp = ibuf;
105633965Sjdp		} else if (*(lp = ibufp) == '\0')
105733965Sjdp			return 0;
105833965Sjdp		else {
105933965Sjdp			while (*ibufp++ != '\n')
106033965Sjdp				;
106133965Sjdp			l = ibufp - lp;
106233965Sjdp		}
106333965Sjdp		if (l == 2 && lp[0] == '.' && lp[1] == '\n') {
106433965Sjdp			return 0;
106533965Sjdp		}
106633965Sjdp		eot = lp + l;
106733965Sjdp		SPL1();
106833965Sjdp		do {
106933965Sjdp			if ((lp = put_sbuf_line(lp)) == NULL) {
107033965Sjdp				SPL0();
107133965Sjdp				return ERR;
107233965Sjdp			} else if (up)
107333965Sjdp				up->t = get_addressed_line_node(current_addr);
107433965Sjdp			else if ((up = push_undo_stack(UADD, current_addr,
107533965Sjdp			    current_addr)) == NULL) {
107633965Sjdp				SPL0();
107733965Sjdp				return ERR;
107833965Sjdp			}
107933965Sjdp		} while (lp != eot);
108033965Sjdp		modified = 1;
108133965Sjdp		SPL0();
108233965Sjdp	}
108333965Sjdp	/* NOTREACHED */
108433965Sjdp}
108533965Sjdp
108633965Sjdp
108733965Sjdp/* join_lines: replace a range of lines with the joined text of those lines */
108833965Sjdpint
108933965Sjdpjoin_lines(from, to)
109033965Sjdp	long from;
109133965Sjdp	long to;
109233965Sjdp{
109333965Sjdp	static char *buf = NULL;
109433965Sjdp	static int n;
109533965Sjdp
109633965Sjdp	char *s;
109733965Sjdp	int size = 0;
109833965Sjdp	line_t *bp, *ep;
109933965Sjdp
110033965Sjdp	ep = get_addressed_line_node(INC_MOD(to, addr_last));
110133965Sjdp	bp = get_addressed_line_node(from);
110233965Sjdp	for (; bp != ep; bp = bp->q_forw) {
110333965Sjdp		if ((s = get_sbuf_line(bp)) == NULL)
110433965Sjdp			return ERR;
110533965Sjdp		REALLOC(buf, n, size + bp->len, ERR);
110633965Sjdp		memcpy(buf + size, s, bp->len);
110733965Sjdp		size += bp->len;
110833965Sjdp	}
110933965Sjdp	REALLOC(buf, n, size + 2, ERR);
111033965Sjdp	memcpy(buf + size, "\n", 2);
111133965Sjdp	if (delete_lines(from, to) < 0)
111233965Sjdp		return ERR;
111333965Sjdp	current_addr = from - 1;
111433965Sjdp	SPL1();
111533965Sjdp	if (put_sbuf_line(buf) == NULL ||
111633965Sjdp	    push_undo_stack(UADD, current_addr, current_addr) == NULL) {
111733965Sjdp		SPL0();
111833965Sjdp		return ERR;
111933965Sjdp	}
112033965Sjdp	modified = 1;
112133965Sjdp	SPL0();
112233965Sjdp	return 0;
112333965Sjdp}
112433965Sjdp
112533965Sjdp
112633965Sjdp/* move_lines: move a range of lines */
112733965Sjdpint
112833965Sjdpmove_lines(addr)
112933965Sjdp	long addr;
113033965Sjdp{
113133965Sjdp	line_t *b1, *a1, *b2, *a2;
113233965Sjdp	long n = INC_MOD(second_addr, addr_last);
113333965Sjdp	long p = first_addr - 1;
113433965Sjdp	int done = (addr == first_addr - 1 || addr == second_addr);
113533965Sjdp
113633965Sjdp	SPL1();
113733965Sjdp	if (done) {
113833965Sjdp		a2 = get_addressed_line_node(n);
113933965Sjdp		b2 = get_addressed_line_node(p);
114033965Sjdp		current_addr = second_addr;
114133965Sjdp	} else if (push_undo_stack(UMOV, p, n) == NULL ||
114233965Sjdp	    push_undo_stack(UMOV, addr, INC_MOD(addr, addr_last)) == NULL) {
114333965Sjdp		SPL0();
114433965Sjdp		return ERR;
114533965Sjdp	} else {
114633965Sjdp		a1 = get_addressed_line_node(n);
114733965Sjdp		if (addr < first_addr) {
114833965Sjdp			b1 = get_addressed_line_node(p);
114933965Sjdp			b2 = get_addressed_line_node(addr);
115033965Sjdp					/* this get_addressed_line_node last! */
115133965Sjdp		} else {
115233965Sjdp			b2 = get_addressed_line_node(addr);
115333965Sjdp			b1 = get_addressed_line_node(p);
115433965Sjdp					/* this get_addressed_line_node last! */
115533965Sjdp		}
115633965Sjdp		a2 = b2->q_forw;
115733965Sjdp		REQUE(b2, b1->q_forw);
115833965Sjdp		REQUE(a1->q_back, a2);
115933965Sjdp		REQUE(b1, a1);
116033965Sjdp		current_addr = addr + ((addr < first_addr) ?
116133965Sjdp		    second_addr - first_addr + 1 : 0);
116233965Sjdp	}
116333965Sjdp	if (isglobal)
116433965Sjdp		unset_active_nodes(b2->q_forw, a2);
116533965Sjdp	modified = 1;
116633965Sjdp	SPL0();
116733965Sjdp	return 0;
116833965Sjdp}
116933965Sjdp
117033965Sjdp
117133965Sjdp/* copy_lines: copy a range of lines; return status */
117233965Sjdpint
117333965Sjdpcopy_lines(addr)
117433965Sjdp	long addr;
117533965Sjdp{
117633965Sjdp	line_t *lp, *np = get_addressed_line_node(first_addr);
117733965Sjdp	undo_t *up = NULL;
117833965Sjdp	long n = second_addr - first_addr + 1;
117933965Sjdp	long m = 0;
118033965Sjdp
118133965Sjdp	current_addr = addr;
118233965Sjdp	if (first_addr <= addr && addr < second_addr) {
118333965Sjdp		n =  addr - first_addr + 1;
118433965Sjdp		m = second_addr - addr;
118533965Sjdp	}
118633965Sjdp	for (; n > 0; n=m, m=0, np = get_addressed_line_node(current_addr + 1))
118733965Sjdp		for (; n-- > 0; np = np->q_forw) {
118833965Sjdp			SPL1();
118933965Sjdp			if ((lp = dup_line_node(np)) == NULL) {
119033965Sjdp				SPL0();
119133965Sjdp				return ERR;
119233965Sjdp			}
119333965Sjdp			add_line_node(lp);
119433965Sjdp			if (up)
119533965Sjdp				up->t = lp;
119633965Sjdp			else if ((up = push_undo_stack(UADD, current_addr,
119733965Sjdp			    current_addr)) == NULL) {
119833965Sjdp				SPL0();
119933965Sjdp				return ERR;
120033965Sjdp			}
120133965Sjdp			modified = 1;
120233965Sjdp			SPL0();
120333965Sjdp		}
120433965Sjdp	return 0;
120533965Sjdp}
120633965Sjdp
120733965Sjdp
120833965Sjdp/* delete_lines: delete a range of lines */
120933965Sjdpint
121033965Sjdpdelete_lines(from, to)
121133965Sjdp	long from, to;
121233965Sjdp{
121333965Sjdp	line_t *n, *p;
1214
1215	SPL1();
1216	if (push_undo_stack(UDEL, from, to) == NULL) {
1217		SPL0();
1218		return ERR;
1219	}
1220	n = get_addressed_line_node(INC_MOD(to, addr_last));
1221	p = get_addressed_line_node(from - 1);
1222					/* this get_addressed_line_node last! */
1223	if (isglobal)
1224		unset_active_nodes(p->q_forw, n);
1225	REQUE(p, n);
1226	addr_last -= to - from + 1;
1227	current_addr = from - 1;
1228	modified = 1;
1229	SPL0();
1230	return 0;
1231}
1232
1233
1234/* display_lines: print a range of lines to stdout */
1235int
1236display_lines(from, to, gflag)
1237	long from;
1238	long to;
1239	int gflag;
1240{
1241	line_t *bp;
1242	line_t *ep;
1243	char *s;
1244
1245	if (!from) {
1246		sprintf(errmsg, "invalid address");
1247		return ERR;
1248	}
1249	ep = get_addressed_line_node(INC_MOD(to, addr_last));
1250	bp = get_addressed_line_node(from);
1251	for (; bp != ep; bp = bp->q_forw) {
1252		if ((s = get_sbuf_line(bp)) == NULL)
1253			return ERR;
1254		if (put_tty_line(s, bp->len, current_addr = from++, gflag) < 0)
1255			return ERR;
1256	}
1257	return 0;
1258}
1259
1260
1261#define MAXMARK 26			/* max number of marks */
1262
1263line_t	*mark[MAXMARK];			/* line markers */
1264int markno;				/* line marker count */
1265
1266/* mark_line_node: set a line node mark */
1267int
1268mark_line_node(lp, n)
1269	line_t *lp;
1270	int n;
1271{
1272	if (!islower((unsigned char)n)) {
1273		sprintf(errmsg, "invalid mark character");
1274		return ERR;
1275	} else if (mark[n - 'a'] == NULL)
1276		markno++;
1277	mark[n - 'a'] = lp;
1278	return 0;
1279}
1280
1281
1282/* get_marked_node_addr: return address of a marked line */
1283long
1284get_marked_node_addr(n)
1285	int n;
1286{
1287	if (!islower((unsigned char)n)) {
1288		sprintf(errmsg, "invalid mark character");
1289		return ERR;
1290	}
1291	return get_line_node_addr(mark[n - 'a']);
1292}
1293
1294
1295/* unmark_line_node: clear line node mark */
1296void
1297unmark_line_node(lp)
1298	line_t *lp;
1299{
1300	int i;
1301
1302	for (i = 0; markno && i < MAXMARK; i++)
1303		if (mark[i] == lp) {
1304			mark[i] = NULL;
1305			markno--;
1306		}
1307}
1308
1309
1310/* dup_line_node: return a pointer to a copy of a line node */
1311line_t *
1312dup_line_node(lp)
1313	line_t *lp;
1314{
1315	line_t *np;
1316
1317	if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) {
1318		fprintf(stderr, "%s\n", strerror(errno));
1319		sprintf(errmsg, "out of memory");
1320		return NULL;
1321	}
1322	np->seek = lp->seek;
1323	np->len = lp->len;
1324	return np;
1325}
1326
1327
1328/* has_trailing_escape:  return the parity of escapes preceding a character
1329   in a string */
1330int
1331has_trailing_escape(s, t)
1332	char *s;
1333	char *t;
1334{
1335    return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape(s, t - 1);
1336}
1337
1338
1339/* strip_escapes: return copy of escaped string of at most length MAXPATHLEN */
1340char *
1341strip_escapes(s)
1342	char *s;
1343{
1344	static char *file = NULL;
1345	static int filesz = 0;
1346
1347	int i = 0;
1348
1349	REALLOC(file, filesz, MAXPATHLEN + 1, NULL);
1350	while (i < filesz - 1	/* Worry about a possible trailing escape */
1351	       && (file[i++] = (*s == '\\') ? *++s : *s))
1352		s++;
1353	return file;
1354}
1355
1356
1357void
1358signal_hup(signo)
1359	int signo;
1360{
1361	if (mutex)
1362		sigflags |= (1 << (signo - 1));
1363	else
1364		handle_hup(signo);
1365}
1366
1367
1368void
1369signal_int(signo)
1370	int signo;
1371{
1372	if (mutex)
1373		sigflags |= (1 << (signo - 1));
1374	else
1375		handle_int(signo);
1376}
1377
1378
1379void
1380handle_hup(signo)
1381	int signo;
1382{
1383	char *hup = NULL;		/* hup filename */
1384	char *s;
1385	int n;
1386
1387	if (!sigactive)
1388		quit(1);
1389	sigflags &= ~(1 << (signo - 1));
1390	if (addr_last && write_file("ed.hup", "w", 1, addr_last) < 0 &&
1391	    (s = getenv("HOME")) != NULL &&
1392	    (n = strlen(s)) + 8 <= MAXPATHLEN &&	/* "ed.hup" + '/' */
1393	    (hup = (char *) malloc(n + 10)) != NULL) {
1394		strcpy(hup, s);
1395		if (hup[n - 1] != '/')
1396			hup[n] = '/', hup[n+1] = '\0';
1397		strcat(hup, "ed.hup");
1398		write_file(hup, "w", 1, addr_last);
1399	}
1400	quit(2);
1401}
1402
1403
1404void
1405handle_int(signo)
1406	int signo;
1407{
1408	if (!sigactive)
1409		quit(1);
1410	sigflags &= ~(1 << (signo - 1));
1411#ifdef _POSIX_SOURCE
1412	siglongjmp(env, -1);
1413#else
1414	longjmp(env, -1);
1415#endif
1416}
1417
1418
1419int cols = 72;				/* wrap column */
1420
1421void
1422handle_winch(signo)
1423	int signo;
1424{
1425	int save_errno = errno;
1426
1427	struct winsize ws;		/* window size structure */
1428
1429	sigflags &= ~(1 << (signo - 1));
1430	if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) {
1431		if (ws.ws_row > 2) rows = ws.ws_row - 2;
1432		if (ws.ws_col > 8) cols = ws.ws_col - 8;
1433	}
1434	errno = save_errno;
1435}
1436
1437
1438/* is_legal_filename: return a legal filename */
1439int
1440is_legal_filename(s)
1441	char *s;
1442{
1443	if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) {
1444		sprintf(errmsg, "shell access restricted");
1445		return 0;
1446	}
1447	return 1;
1448}
1449