ed: Deal signals in a reliable way

The signal handlers were calling longjmp() but as the code was calling
non signal safe functions the behaviour was very unpredictable generating
segmentation faults and dead lock. This commit changes the signal handlers
to only set a variable that is checked in safe places where long loops
happen.
This commit is contained in:
Roberto E. Vargas Caballero 2023-09-22 23:10:30 +02:00
parent 95b9603975
commit d3780956a9
2 changed files with 110 additions and 43 deletions

1
TODO
View File

@ -32,7 +32,6 @@ ed
g/^line/a \
line1
.
* Signal handling is broken.
* cat <<EOF | ed
0a
int radix = 16;

152
ed.c
View File

@ -71,6 +71,8 @@ static struct undo udata;
static int newcmd;
int eol, bol;
static sig_atomic_t intr, hup;
static void
discard(void)
{
@ -119,6 +121,17 @@ prevln(int line)
return (line < 0) ? lastln : line;
}
static String *
string(String *s)
{
free(s->str);
s->str = NULL;
s->siz = 0;
s->cap = 0;
return s;
}
static char *
addchar(char c, String *s)
{
@ -136,6 +149,8 @@ addchar(char c, String *s)
return t;
}
static void chksignals(void);
static int
input(void)
{
@ -146,6 +161,9 @@ input(void)
if ((c = getchar()) != EOF)
addchar(c, &cmdline);
chksignals();
return c;
}
@ -455,6 +473,8 @@ search(int way)
i = curln;
do {
chksignals();
i = (way == '?') ? prevln(i) : nextln(i);
if (i > 0 && match(i))
return i;
@ -636,12 +656,66 @@ deflines(int def1, int def2)
error("invalid address");
}
static void
quit(void)
{
clearbuf();
exit(exstatus);
}
static void dowrite(const char *, int);
static void
dump(void)
{
char *home;
line1 = nextln(0);
line2 = lastln;
if (!setjmp(savesp)) {
dowrite("ed.hup", 1);
return;
}
home = getenv("HOME");
if (!home || chdir(home) < 0)
return;
if (!setjmp(savesp))
dowrite("ed.hup", 1);
}
static void
chksignals(void)
{
if (hup) {
if (modflag)
dump();
exstatus = 1;
quit();
}
if (intr) {
intr = 0;
clearerr(stdin);
error("Interrupt");
}
}
static void
dowrite(const char *fname, int trunc)
{
FILE *fp;
size_t bytecount = 0;
int i, r, line, sh;
int i, r, line;
FILE *aux;
static int sh;
static FILE *fp;
if (fp) {
sh ? pclose(fp) : fclose(fp);
fp = NULL;
}
if(fname[0] == '!') {
sh = 1;
@ -656,6 +730,8 @@ dowrite(const char *fname, int trunc)
line = curln;
for (i = line1; i <= line2; ++i) {
chksignals();
gettxt(i);
bytecount += text.siz - 1;
fwrite(text.str, 1, text.siz - 1, fp);
@ -663,7 +739,9 @@ dowrite(const char *fname, int trunc)
curln = line2;
r = sh ? pclose(fp) : fclose(fp);
aux = fp;
fp = NULL;
r = sh ? pclose(aux) : fclose(aux);
if (r)
error("input/output error");
strcpy(savfname, fname);
@ -691,6 +769,7 @@ doread(const char *fname)
curln = line2;
for (cnt = 0; (n = getline(&s, &len, fp)) > 0; cnt += (size_t)n) {
chksignals();
if (s[n-1] != '\n') {
if (len == SIZE_MAX || !(p = realloc(s, ++len)))
error("out of memory");
@ -718,6 +797,7 @@ doprint(void)
if (line1 <= 0 || line2 > lastln)
error("incorrect address");
for (i = line1; i <= line2; ++i) {
chksignals();
if (pflag == 'n')
printf("%d\t", i);
for (s = gettxt(i); (c = *s) != '\n'; ++s) {
@ -867,11 +947,11 @@ join(void)
{
int i;
char *t, c;
String s;
static String s;
s.str = NULL;
s.siz = s.cap = 0;
string(&s);
for (i = line1;; i = nextln(i)) {
chksignals();
for (t = gettxt(i); (c = *t) != '\n'; ++t)
addchar(*t, &s);
if (i == line2)
@ -898,6 +978,7 @@ scroll(int num)
if (max > lastln)
max = lastln;
for (cnt = line1; cnt < max; cnt++) {
chksignals();
fputs(gettxt(ln), stdout);
ln = nextln(ln);
}
@ -913,6 +994,7 @@ copy(int where)
curln = where;
while (line1 <= line2) {
chksignals();
inject(gettxt(line1), AFTER);
if (line2 >= curln)
line2 = nextln(line2);
@ -922,13 +1004,6 @@ copy(int where)
}
}
static void
quit(void)
{
clearbuf();
exit(exstatus);
}
static void
execsh(void)
{
@ -939,7 +1014,7 @@ execsh(void)
skipblank();
if ((c = input()) != '!') {
back(c);
cmd.siz = 0;
string(&cmd);
} else if (cmd.siz) {
--cmd.siz;
repl = 1;
@ -973,9 +1048,7 @@ getrhs(int delim)
int c;
static String s;
free(s.str);
s.str = NULL;
s.siz = s.cap = 0;
string(&s);
while ((c = input()) != '\n' && c != EOF && c != delim)
addchar(c, &s);
addchar('\0', &s);
@ -1079,8 +1152,10 @@ subline(int num, int nth)
int i, m, changed;
static String s;
i = changed = s.siz = 0;
string(&s);
i = changed = 0;
for (m = match(num); m; m = rematch(num)) {
chksignals();
addpre(&s);
changed |= addsub(&s, nth, ++i);
if (eol || bol)
@ -1099,8 +1174,10 @@ subst(int nth)
{
int i;
for (i = line1; i <= line2; ++i)
for (i = line1; i <= line2; ++i) {
chksignals();
subline(i, nth);
}
}
static void
@ -1362,6 +1439,7 @@ chkglobal(void)
compile(delim);
for (i = 1; i <= lastln; ++i) {
chksignals();
if (i >= line1 && i <= line2)
v = match(i) == dir;
else
@ -1378,13 +1456,14 @@ doglobal(void)
int cnt, ln, k;
skipblank();
cmdline.siz = 0;
string(&cmdline);
gflag = 1;
if (uflag)
chkprint(0);
ln = line1;
for (cnt = 0; cnt < lastln; ) {
chksignals();
k = getindex(ln);
if (zero[k].global) {
zero[k].global = 0;
@ -1414,30 +1493,13 @@ usage(void)
static void
sigintr(int n)
{
signal(SIGINT, sigintr);
error("interrupt");
intr = 1;
}
static void
sighup(int dummy)
{
int n;
char *home = getenv("HOME"), fname[FILENAME_MAX];
if (modflag) {
line1 = nextln(0);
line2 = lastln;
if (!setjmp(savesp)) {
dowrite("ed.hup", 1);
} else if (home && !setjmp(savesp)) {
n = snprintf(fname,
sizeof(fname), "%s/%s", home, "ed.hup");
if (n < sizeof(fname) && n > 0)
dowrite(fname, 1);
}
}
exstatus = 1;
quit();
hup = 1;
}
static void
@ -1492,9 +1554,15 @@ main(int argc, char *argv[])
usage();
if (!setjmp(savesp)) {
signal(SIGINT, sigintr);
signal(SIGHUP, sighup);
signal(SIGQUIT, SIG_IGN);
sigaction(SIGINT,
&(struct sigaction) {.sa_handler = sigintr},
NULL);
sigaction(SIGHUP,
&(struct sigaction) {.sa_handler = sighup},
NULL);
sigaction(SIGQUIT,
&(struct sigaction) {.sa_handler = SIG_IGN},
NULL);
init(*argv);
}
edit();