4.4BSD/usr/src/contrib/ed/main.c

Compare this file to the similar file:
Show the results in this format:

/*-
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Rodney Ruddock of the University of Guelph.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef lint
static char copyright[] =
"@(#) Copyright (c) 1992, 1993\n\
	The Regents of the University of California.  All rights reserved.\n";
#endif /* not lint */

#ifndef lint
static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 5/31/93";
#endif /* not lint */

#include <sys/types.h>
#include <sys/ioctl.h>

#include <limits.h>
#include <regex.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifdef DBI
#include <db.h>
#endif

#include "ed.h"
#include "extern.h"

/*
 * This is where all of the "global" variables are declared. They are
 * set for extern in the ed.h header file (so everyone can get them).
 */

int nn_max, nn_max_flag, Start_default, End_default, address_flag;
int zsnum, filename_flag, add_flag=0, join_flag=0;
int help_flag=0, gut_num=-1;
#ifdef STDIO
FILE *fhtmp;
int file_seek;
#endif

#ifdef DBI
DB *dbhtmp;
#endif

LINE *nn_max_start, *nn_max_end;

struct MARK mark_matrix[26]; /* in init set all to null */ 

char *text;
LINE **gut=NULL;
char *filename_current, *prompt_string=NULL, help_msg[130];
char *template=NULL;
int prompt_str_flg=0, start_up_flag=0, name_set=0;

LINE *top, *current, *bottom, *Start, *End; 
struct u_layer *u_stk;
struct d_layer *d_stk;
LINE *u_current, *u_top, *u_bottom;
int u_set;
regex_t RE_comp;
regmatch_t RE_match[RE_SEC];
int RE_sol=0, RE_flag=0;
char *RE_patt=NULL;

int ss; /* for the getc() */
int explain_flag=1, g_flag=0, GV_flag=0, printsfx=0, exit_code=0;
long change_flag=0L;
int line_length;
jmp_buf ctrl_position, ctrl_position2, ctrl_position3; /* For SIGnal handling. */
int sigint_flag, sighup_flag, sigspecial=0, sigspecial2=0, sigspecial3=0;

static void sigint_handler __P((int));
static void sighup_handler __P((int));

/*
 * Starts the whole show going. Set things up as the arguments spec
 * in the shell and set a couple of the global variables.
 *
 * Note naming viol'n with errnum for consistancy.
 */
int
main(argc, argv)
	int argc;
	char *argv[];
{
	int l_num, errnum=0, l_err=0;
	char *l_fnametmp, *l_col, buf[2];
	struct winsize win;

	setbuffer(stdin, buf, 1);
	line_length = ((l_col = getenv("COLUMNS")) == NULL ? 0 : atoi(l_col));
	if ((line_length == 0 && isatty(STDOUT_FILENO) &&
		ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1))
		line_length = win.ws_col;
	if (line_length == 0)
		line_length = 78;
	line_length -= 3;	/* for the octal to break properly in 'l' */

	Start = End = NULL;
	top = bottom = NULL;
	current = NULL;
	nn_max_flag = 0;
	nn_max_start = nn_max_end = NULL;
	l_fnametmp = calloc(FILENAME_LEN, sizeof(char));
	if (l_fnametmp == NULL)
		ed_exit(4);
	text = calloc(NN_MAX_START + 2, sizeof(char));
	if (text == NULL)
		ed_exit(4);
	Start_default = End_default = 0;
	zsnum = 22;		/* for the 'z' command */
	help_msg[0] = '\0';
	u_stk = NULL;
	d_stk = NULL;
	u_current = u_top = u_bottom = NULL;
	u_set = 0;		/* for in d after a j */
	filename_flag = 0;
	filename_current = NULL;

	l_num = 1;
	for (;;) {
		/* Process the command line options */
		if (l_num >= argc)
			break;
		switch (argv[l_num][0]) {
		case '-':
			switch (argv[l_num][1]) {
			case '\0':	/* this is why 'getopt' not used */
			case 's':
				explain_flag = 0;
				break;
			case 'p':
				if (++l_num < argc) {
					prompt_string =
					    calloc(strlen(argv[l_num]),
					    sizeof(char));
					if (prompt_string == NULL)
						ed_exit(4);
					strcpy(prompt_string, argv[l_num]);
					prompt_str_flg = 1;
					break;
				}
				l_err = 1;
			case 'v':
#ifdef BSD
				(void)printf("ed: in BSD mode:\n");
#endif
#ifdef POSIX
				(void)printf("ed: in POSIX mode:\n");
#endif
				break;
			default:
				l_err++;
				ed_exit(l_err);
			}
			break;
		default:
			if (name_set)
				ed_exit(3);
			strcpy(l_fnametmp, argv[l_num]);
			filename_current = l_fnametmp;
			name_set = 1;
			if (prompt_str_flg)
				break;
			/* default ed prompt */
			prompt_string = (char *) calloc(3, sizeof(char));
			strcpy(prompt_string, "*");
			break;
		}
		l_num++;
	}

	start_up_flag = 1;
	cmd_loop(stdin, &errnum);
	/* NOTREACHED */
}

/*
 * The command loop. What the command is that the user has specified
 * is determined here. This is not just for commands coming from
 * the terminal but any standard i/o stream; see the global commands.
 * Some of the commands are handled within here (i.e. 'H') while most
 * are handled in their own functions (as called).
 */
void
cmd_loop(inputt, errnum)
	FILE *inputt;
	int *errnum;
{
	LINE *l_tempp;
	int l_last, l_jmp_flag;

	l_last = 0; /* value in l_last may be clobbered (reset to = 0) by longjump, but that's okay */

	if (g_flag == 0) {	/* big, BIG trouble if we don't check! think. */
		/* set the jump point for the signals */
		l_jmp_flag = setjmp(ctrl_position);
		signal(SIGINT, sigint_handler);
		signal(SIGHUP, sighup_handler);
		switch (l_jmp_flag) {
		case JMP_SET:
			break;
		/* Some general cleanup not specific to the jmp pt. */
		case INTERUPT:
			sigint_flag = 0;
			GV_flag = 0;	/* safest place to do these flags */
			g_flag = 0;
			(void)printf("\n?\n");
			break;
		case HANGUP:		/* shouldn't get here. */
			break;
		default:
			(void)fprintf(stderr, "Signal jump problem\n");
		}
		/* Only do this once! */
		if (start_up_flag) {
			start_up_flag = 0;
			/* simulate the 'e' at startup */
			e2(inputt, errnum);
			if (*errnum == 0)
				goto errmsg2;
		}
	}
	for (;;) {
		if (prompt_str_flg == 1)
			(void)printf("%s", prompt_string);
		ss = getc(inputt);
		*errnum = 0;
		l_tempp = Start = End = NULL;
		Start_default = End_default = 1;

		/*
		 * This isn't nice and alphabetical mainly because of
		 * restrictions with 'G' and 'V' (see ed(1)).
		 */
		for (;;) {
			switch (ss) {
			case 'd':
				d(inputt, errnum);
				break;
			case 'e':
			case 'E':
				e(inputt, errnum);
				if (*errnum == 0)
					goto errmsg2;
				break;
			case 'f':
				f(inputt, errnum);
				break;
			case 'a':
			case 'c':
			case 'i':
			case 'g':
			case 'G':
			case 'v':
			case 'V':
				if (GV_flag == 1) {
					(void)sprintf(help_msg,
					    "command `%c' illegal in G/V", ss);
					*errnum = -1;
					break;
				}
				switch (ss) {
				case 'a':
					a(inputt, errnum);
					break;
				case 'c':
					c(inputt, errnum);
					break;
				case 'i':
					i(inputt, errnum);
					break;
				default:
					g(inputt, errnum);
				}
				break;
			case 'h':
				if (rol(inputt, errnum))
					break;
				if (help_msg[0])
					(void)printf("%s\n", help_msg);
				*errnum = 1;
				break;
			case 'H':
				if (rol(inputt, errnum))
					break;
				if (help_flag == 0) {
					help_flag = 1;
					if (help_msg[0])
						(void)printf("%s\n",
						    help_msg);
				} else
					help_flag = 0;
				*errnum = 1;
				break;
			case 'j':
				j(inputt, errnum);
				break;
			case 'k':
				set_mark(inputt, errnum);
				break;
			case 'l':
				l(inputt, errnum);
				break;
			case 'm':
				m(inputt, errnum);
				break;
#ifdef POSIX
				/* In POSIX-land 'P' toggles the prompt. */
			case 'P':
				if (rol(inputt, errnum))
					break;
				prompt_str_flg = prompt_str_flg ? 0 : 1;
				*errnum = 1;
				break;
#endif
			case '\n':
				if (GV_flag == 1)
					return;
				/* For 'p' to consume. */
				ungetc(ss, inputt);
				if ((current == bottom) && (End == NULL)) {
					strcpy(help_msg, "at end of buffer");
					*errnum = -1;
					break;
				}
				current = current->below;
#ifdef BSD
				/* In BSD 'P'=='p'. */
			case 'P':
#endif
			case 'p':
				p(inputt, errnum, 0);
				break;
			case 'n':
				p(inputt, errnum, 1);
				break;
			/*
			 * An EOF means 'q' unless we're still in the middle
			 * of a global command, in which case it was just the
			 * end of the command list found.
			 */
			case EOF:
				clearerr(inputt);
				if (g_flag > 0)
					return;
				/*ss = 'q';*/
			case 'q':
			case 'Q':
				if ((!isatty(STDIN_FILENO)) && (ss == 'q'))
					ss = 'Q';
				q(inputt, errnum);
				break;
			case 'r':
				r(inputt, errnum);
				if (*errnum == 0)
					goto errmsg2;
				break;
			case 's':
				s(inputt, errnum);
				break;
			case 't':
				t(inputt, errnum);
				break;
			case 'u':
				u(inputt, errnum);
				break;
			case 'w':
			case 'W':
				w(inputt, errnum);
				break;
			case 'z':
				z(inputt, errnum);
				break;
			case '!':
				bang(inputt, errnum);
				break;
			case '=':
				equal(inputt, errnum);
				break;
			/*
			 * Control of address forms from here down.
			 *
			 * It's a head-game to understand why ";" and "," look
			 * as they do below, but a lot of it has to do with ";"
			 * and "," being special address pair forms themselves
			 * and the compatibility for address "chains".
			 */
			case ';':
				if (End_default == 1 && Start_default == 1) {
					Start = current;
					End = bottom;
					Start_default = End_default = 0;
				} else {
					Start = current = End;
					Start_default = 0;
					End_default = 1;
				}
				l_tempp = NULL;
				break;
			/*
			 * Note address ".,x" where x is a cmd is legal; not a
			 * bug - for backward compatability.
			 */
			case ',':
				if (End_default == 1 && Start_default == 1) {
					Start = top;
					End = bottom;
					Start_default = End_default = 0;
				} else {
					Start = End;
					Start_default = 0;
					End_default = 1;
				}
				l_tempp = NULL;
				break;
			case '%':
				if (End_default == 0) {
					strcpy(help_msg,
					    "'%' is an address pair");
					*errnum = -1;
					break;
				}
				Start = top;
				End = bottom;
				Start_default = End_default = 0;
				l_tempp = NULL;
				break;
			/*
			 * Within address_conv => l_last = '+', foobar, but
			 * historical and now POSIX...
			 */
			case ' ':
				break;
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
			case '-':
			case '^':
			case '+':
			case '\'':
			case '$':
			case '?':
			case '/':
			case '.':
				ungetc(ss, inputt);
				if (Start_default == 0 && End_default == 0) {
					strcpy(help_msg,
					    "badly formed address");
					*errnum = -1;
					break;
				}
				ss = l_last;
				l_tempp = address_conv(l_tempp, inputt, errnum);
				if (*errnum < 0)
					break;
				End = l_tempp;
				End_default = 0;
				if (Start_default == 0)
					*errnum = address_check(Start, End);
				break;
			default:
				*errnum = -1;
				strcpy(help_msg, "unknown command");
				break;
			}	/* end-switch(ss) */

			/* Things came out okay with the last command. */
			if (*errnum > 0) {
				if (GV_flag == 1)
					return;
				/* Do the suffixes if there were any. */
				if (printsfx > 0) {
					Start = End = current;
					ungetc(ss, inputt);
					if (printsfx == 1)
						p(inputt, errnum, 0);
					else
						if (printsfx == 2)
							p(inputt, errnum, 1);
						else if (printsfx == 4)
							l(inputt, errnum);
					/* Unlikely it's needed, but... */
					if (*errnum < 0)
						goto errmsg;
				}
				break;
			}
			/* There was a problem with the last command. */
			else if (*errnum < 0) {
errmsg:				while (((ss = getc(inputt)) != '\n') &&
				    (ss != EOF));
				(void)printf("?\n");
errmsg2:			if (help_flag)
					(void)printf("%s\n", help_msg);
				exit_code = 4;
/* for people wanting scripts to carry on after a cmd error, then
 * define NOENDONSCRIPT on the compile line.
 */
#ifndef NOENDONSCRIPT
				if (!isatty(STDIN_FILENO)) {
					ss = 'Q';
					ungetc('\n', inputt);
					q(inputt, errnum);
				}
#endif
				break;
			}
			l_last = ss;
			ss = getc(inputt);
		}
	}
}

/*
 * Exits ed and prints an appropriate message about the command line
 * being malformed (see below).
 */
void
ed_exit(err)
	int err;
{
	switch (err) {
          case 1:
		(void)fprintf(stderr, "ed: illegal option\n");
		break;
          case 2:
		(void)fprintf(stderr, "ed: missing promptstring\n");
		break;
          case 3:
		(void)fprintf(stderr, "ed: too many filenames\n");
		break;
          case 4:
		(void)fprintf(stderr, "ed: out of memory error\n");
		break;
	  case 5:
		(void)fprintf(stderr, "ed: unable to create buffer\n");
		break;
          default:
		(void)fprintf(stderr, "ed: command line error\n");
		break;
        }
	(void)fprintf(stderr,
	    "ed: ed [ -s ] [ -p promptstring ] [ filename ]\n");
	exit(1);
}

/*
 * SIGINT is never turned off. We flag it happened and then pay attention
 * to it at certain logical locations in the code we don't do more here
 * cause some of our buffer pointer's may be in an inbetween state at the
 * time of the SIGINT. So we flag it happened, let the local fn handle it
 * and do a jump back to the cmd_loop
 */
static void
sigint_handler(signo)
	int signo;
{
	sigint_flag = 1;
	if (sigspecial3) {
		sigspecial3 = 0;
		SIGINT_ILACTION;
	}
	else
		if (sigspecial);
		else
			SIGINT_ACTION;
}

static void
sighup_handler(signo)
	int signo;
{
	sighup_flag = 1;
	undo();
	do_hup();
	/* NOTREACHED */

	SIGHUP_ACTION;
}