longjmp fixes
[unix-history] / usr / src / contrib / ed / main.c
/*-
* Copyright (c) 1992 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.
*
* %sccs.include.redist.c%
*/
#ifndef lint
static char sccsid[] = "@(#)main.c 5.5 (Berkeley) %G%";
#endif /* not lint */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <regex.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.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;
#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;
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;
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;
struct winsize win;
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;
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 */
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;
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;
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);
}
}
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);
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;
(void)printf("%s\n", help_msg);
*errnum = 1;
break;
case 'H':
if (rol(inputt, errnum))
break;
if (help_flag == 0) {
help_flag = 1;
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':
q(inputt, errnum);
break;
case 'r':
r(inputt, errnum);
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));
if (help_flag == 1)
printf("%?: %s\n", help_msg);
else
printf("?\n");
if (g_flag > 0)
return;
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;
}
if (sigspecial2) {
sigspecial2 = 0;
SIGINT_ALACTION;
}
else
if (sigspecial);
else
SIGINT_ACTION;
}
static void
sighup_handler(signo)
int signo;
{
(void)fprintf(stderr,"\n SIGHUP \n");
sighup_flag = 1;
undo();
do_hup();
/* NOTREACHED */
SIGHUP_ACTION;
}