/*
* Compiler error handler
* Copyright
* (C) 1992 Joseph H. Allen
*
* This file is part of JOE (Joe's Own Editor)
*/
#include "types.h"
/* Error database */
typedef struct error ERROR;
struct error {
LINK(ERROR) link; /* Linked list of errors */
off_t line; /* Target line number */
off_t org; /* Original target line number */
char *file; /* Target file name */
off_t src; /* Error-file line number */
char *msg; /* The message */
} errors = { { &errors, &errors} };
ERROR *errptr = &errors; /* Current error row */
B *errbuf = NULL; /* Buffer with error messages */
/* Function which allows stepping through all error buffers,
for multi-file search and replace. Give it a buffer. It finds next
buffer in error list. Look at 'berror' for error information. */
/* This is made to work like bafter: it does not increment refcount of buffer */
B *beafter(B *b)
{
struct error *e;
const char *name = b->name;
if (!name) name = "";
for (e = errors.link.next; e != &errors; e = e->link.next)
if (!zcmp(name, e->file))
break;
if (e == &errors) {
/* Given buffer is not in list? Return first buffer in list. */
e = errors.link.next;
}
while (e != &errors && !zcmp(name, e->file))
e = e->link.next;
berror = 0;
if (e != &errors) {
b = bfind(e->file);
/* bfind bumps refcount, so we have to unbump it */
if (b->count == 1)
b->orphan = 1; /* Oops */
else
--b->count;
return b;
}
return 0;
}
/* Insert and delete notices */
void inserr(const char *name, off_t where, off_t n, int bol)
{
ERROR *e;
if (!n)
return;
if (name) {
for (e = errors.link.next; e != &errors; e = e->link.next) {
if (!zcmp(e->file, name)) {
if (e->line > where)
e->line += n;
else if (e->line == where && bol)
e->line += n;
}
}
}
}
void delerr(const char *name, off_t where, off_t n)
{
ERROR *e;
if (!n)
return;
if (name) {
for (e = errors.link.next; e != &errors; e = e->link.next) {
if (!zcmp(e->file, name)) {
if (e->line > where + n)
e->line -= n;
else if (e->line > where)
e->line = where;
}
}
}
}
/* Abort notice */
void abrerr(const char *name)
{
ERROR *e;
if (name)
for (e = errors.link.next; e != &errors; e = e->link.next)
if (!zcmp(e->file, name))
e->line = e->org;
}
/* Save notice */
void saverr(const char *name)
{
ERROR *e;
if (name)
for (e = errors.link.next; e != &errors; e = e->link.next)
if (!zcmp(e->file, name))
e->org = e->line;
}
/* Pool of free error nodes */
ERROR errnodes = { {&errnodes, &errnodes} };
/* Free an error node */
static void freeerr(ERROR *n)
{
vsrm(n->file);
vsrm(n->msg);
enquef(ERROR, link, &errnodes, n);
}
/* Free all errors */
static int freeall(void)
{
int count = 0;
while (!qempty(ERROR, link, &errors)) {
freeerr(deque_f(ERROR, link, errors.link.next));
++count;
}
errptr = &errors;
return count;
}
/* Parse "make[1]: Entering directory '/home/..../foo'" message. */
static void parsedir(const char *s, char **rtn_dir)
{
const char *u, *v;
u = zstr(s, "Entering directory `");
if (u) {
u += zlen("Entering directory `");
for (v = u; *v && *v != '\''; ++v);
if (*v == '\'') {
char *t = *rtn_dir;
t = vstrunc(t, 0);
t = vsncpy(sv(t), u, v - u);
if (sLEN(t) && t[sLEN(t)-1] != '/')
t = vsadd(t, '/');
*rtn_dir = t;
}
}
}
/* Parse error messages into database */
/* From joe's joe 2.9 */
/* First word (allowing ., /, _ and -) with a . is the file name. Next number
is line number. Then there should be a ':' */
static void parseone(struct charmap *map,const char *s,char **rtn_name,off_t *rtn_line)
{
int flg;
int c;
char *name = NULL;
off_t line = -1;
const char *u, *v, *t;
v = s;
flg = 0;
if (s[0] == 'J' && s[1] == 'O' && s[2] == 'E' && s[3] == ':')
goto bye;
do {
/* Skip to first word */
for (u = v; *u && !((t = u), (c = fwrd_c(map, &t, NULL)), ((c >= 0 && joe_isalnum_(map, c)) || c == '.' || c == '/')); u = t) ;
/* Skip to end of first word */
for (v = u; (t = v), (c = fwrd_c(map, &t, NULL)), ((c >= 0 && joe_isalnum_(map, c)) || c == '.' || c == '/' || c == '-'); v = t)
if (c == '.')
flg = 1;
} while (!flg && u != v);
/* Save file name */
if (u != v)
name = vsncpy(NULL, 0, u, v - u);
/* Skip to first number */
for (u = v; *u && (*u < '0' || *u > '9'); ++u) ;
/* Skip to end of first number */
for (v = u; *v >= '0' && *v <= '9'; ++v) ;
/* Save line number */
if (u != v) {
line = ztoo(u);
}
if (line != -1)
--line;
/* Look for ':' */
flg = 0;
while (*v) {
/* Allow : anywhere on line: works for MIPS C compiler */
/*
for (y = 0; s[y];)
*/
if (*v == ':') {
flg = 1;
break;
}
++v;
}
bye:
if (!flg)
line = -1;
*rtn_name = name;
*rtn_line = line;
}
/* Parser for file name lists from grep, find and ls.
*
* filename
* filename:*
* filename:line-number:*
*/
void parseone_grep(struct charmap *map,const char *s,char **rtn_name,off_t *rtn_line)
{
int y;
char *name = NULL;
off_t line = -1;
if (s[0] == 'J' && s[1] == 'O' && s[2] == 'E' && s[3] == ':')
goto bye;
/* Skip to first : or end of line */
for (y = 0;s[y] && s[y] != ':';++y);
if (y) {
/* This should be the file name */
name = vsncpy(NULL,0,s,y);
line = 0;
if (s[y] == ':') {
/* Maybe there's a line number */
++y;
while (s[y] >= '0' && s[y] <= '9')
line = line * 10 + (s[y++] - '0');
--line;
if (line < 0 || s[y] != ':') {
/* Line number is only valid if there's a second : */
line = 0;
}
}
}
bye:
*rtn_name = name;
*rtn_line = line;
}
static int parseit(struct charmap *map,const char *s, off_t row,
void (*parseline)(struct charmap *map, const char *s, char **rtn_name, off_t *rtn_line), char *current_dir)
{
char *name = NULL;
off_t line = -1;
ERROR *err;
parseline(map,s,&name,&line);
if (name) {
if (line != -1) {
char *t;
/* We have an error */
err = (ERROR *) alitem(&errnodes, SIZEOF(ERROR));
err->file = name;
if (current_dir) {
err->file = vsncpy(NULL, 0, sv(current_dir));
err->file = vsncpy(sv(err->file), sv(name));
err->file = canonical(err->file);
vsrm(name);
} else {
err->file = name;
}
err->org = err->line = line;
err->src = row;
err->msg = vsncpy(NULL, 0, sc("\\i"));
t = duplicate_backslashes(sv(s));
err->msg = vsncpy(sv(err->msg), sv(t));
vsrm(t);
enqueb(ERROR, link, &errors, err);
return 1;
} else
vsrm(name);
}
return 0;
}
/* Parse the error output contained in a buffer */
void kill_ansi(char *s);
static off_t parserr(B *b)
{
if (markv(1)) {
char *curdir = 0;
P *p = pdup(markb, "parserr1");
P *q = pdup(markb, "parserr2");
off_t nerrs = 0;
errbuf = markb->b;
freeall();
p_goto_bol(p);
do {
char *s;
pset(q, p);
p_goto_eol(p);
s = brvs(q, p->byte - q->byte);
if (s) {
kill_ansi(s);
parsedir(s, &curdir);
nerrs += parseit(q->b->o.charmap, s, q->line, (q->b->parseone ? q->b->parseone : parseone), (curdir ? curdir : q->b->current_dir));
vsrm(s);
}
pgetc(p);
} while (p->byte < markk->byte);
vsrm(curdir);
prm(p);
prm(q);
return nerrs;
} else {
char *curdir = 0;
P *p = pdup(b->bof, "parserr3");
P *q = pdup(p, "parserr4");
off_t nerrs = 0;
errbuf = b;
freeall();
do {
char *s;
pset(q, p);
p_goto_eol(p);
s = brvs(q, p->byte - q->byte);
if (s) {
kill_ansi(s);
parsedir(s, &curdir);
nerrs += parseit(q->b->o.charmap, s, q->line, (q->b->parseone ? q->b->parseone : parseone), (curdir ? curdir : q->b->current_dir));
vsrm(s);
}
} while (pgetc(p) != NO_MORE_DATA);
vsrm(curdir);
prm(p);
prm(q);
return nerrs;
}
}
static BW *find_a_good_bw(B *b)
{
W *w;
BW *bw = 0;
/* Find lowest window with buffer */
if ((w = maint->topwin) != NULL) {
do {
if ((w->watom->what&TYPETW) && ((BW *)w->object)->b==b && w->y>=0)
bw = (BW *)w->object;
w = w->link.next;
} while (w != maint->topwin);
}
if (bw)
return bw;
/* Otherwise just find lowest window */
if ((w = maint->topwin) != NULL) {
do {
if ((w->watom->what&TYPETW) && w->y>=0)
bw = (BW *)w->object;
w = w->link.next;
} while (w != maint->topwin);
}
return bw;
}
int parserrb(B *b)
{
BW *bw;
off_t n;
freeall();
bw = find_a_good_bw(b);
unmark(bw->parent, 0);
n = parserr(b);
if (n)
joe_snprintf_1(msgbuf, JOE_MSGBUFSIZE, joe_gettext(_("%d messages found")), (int)n);
else
joe_snprintf_0(msgbuf, SIZEOF(msgbuf), joe_gettext(_("No messages found")));
msgnw(bw->parent, msgbuf);
return 0;
}
int urelease(W *w, int k)
{
BW *bw;
WIND_BW(bw, w);
bw->b->parseone = 0;
if (qempty(ERROR, link, &errors) && !errbuf) {
joe_snprintf_0(msgbuf, SIZEOF(msgbuf), joe_gettext(_("No messages")));
} else {
int count = freeall();
errbuf = NULL;
joe_snprintf_1(msgbuf, SIZEOF(msgbuf), joe_gettext(_("%d messages cleared")), count);
}
msgnw(bw->parent, msgbuf);
updall();
return 0;
}
int uparserr(W *w, int k)
{
off_t n;
BW *bw;
WIND_BW(bw, w);
freeall();
bw->b->parseone = parseone;
n = parserr(bw->b);
if (n)
joe_snprintf_1(msgbuf, JOE_MSGBUFSIZE, joe_gettext(_("%d messages found")), (int)n);
else
joe_snprintf_0(msgbuf, SIZEOF(msgbuf), joe_gettext(_("No messages found")));
msgnw(bw->parent, msgbuf);
return 0;
}
int ugparse(W *w, int k)
{
off_t n;
BW *bw;
WIND_BW(bw, w);
freeall();
bw->b->parseone = parseone_grep;
n = parserr(bw->b);
if (n)
joe_snprintf_1(msgbuf, JOE_MSGBUFSIZE, joe_gettext(_("%d messages found")), (int)n);
else
joe_snprintf_0(msgbuf, SIZEOF(msgbuf), joe_gettext(_("No messages found")));
msgnw(bw->parent, msgbuf);
return 0;
}
static int jump_to_file_line(BW *bw,char *file,off_t line,char *msg)
{
int omid;
if (!bw->b->name || zcmp(file, bw->b->name)) {
if (doswitch(bw->parent, vsdup(file), NULL, NULL))
return -1;
bw = (BW *) maint->curwin->object;
}
omid = opt_mid;
opt_mid = 1;
pline(bw->cursor, line);
dofollows();
opt_mid = omid;
bw->cursor->xcol = piscol(bw->cursor);
msgnw(bw->parent, msg);
return 0;
}
/* Show current message */
int ucurrent_msg(W *w, int k)
{
BW *bw;
WIND_BW(bw, w);
if (errptr != &errors) {
msgnw(bw->parent, errptr->msg);
return 0;
} else {
msgnw(bw->parent, joe_gettext(_("No messages")));
return -1;
}
}
/* Find line in error database: return pointer to message */
static ERROR *srcherr(BW *bw,char *file,off_t line)
{
ERROR *p;
for (p = errors.link.next; p != &errors; p=p->link.next)
if (!zcmp(p->file,file) && p->org == line) {
errptr = p;
setline(errbuf, errptr->src);
return errptr;
}
return 0;
}
/* Delete ansi formatting */
void kill_ansi(char *s)
{
char *d = s;
while (*s)
if (*s == 27) {
while (*s && (*s == 27 || *s == '[' || (*s >= '0' && *s <= '9') || *s == ';'))
++s;
if (*s)
++s;
} else
*d++ = *s++;
*d = 0;
}
int ujump(W *w, int k)
{
char *curdir = 0;
BW *bw;
int rtn = -1;
P *p;
P *q;
char *s;
WIND_BW(bw, w);
p = pdup(bw->b->bof, "ujump");
q = pdup(p, "ujump");
/* Parse entire file just to get current directory */
while (p->line != bw->cursor->line) {
pset(q, p);
p_goto_eol(p);
s = brvs(q, p->byte - q->byte);
if (s) {
kill_ansi(s);
parsedir(s, &curdir);
vsrm(s);
}
if (NO_MORE_DATA == pgetc(p))
break;
}
/* Parse the line with the cursor, look for an error */
pset(p, bw->cursor);
pset(q, bw->cursor);
p_goto_bol(p);
p_goto_eol(q);
s = brvs(p, q->byte - p->byte);
prm(p);
prm(q);
if (s) {
char *name = NULL;
char *fullname = NULL;
char *curd = get_cd(bw->parent);
off_t line = -1;
kill_ansi(s);
if (bw->b->parseone)
bw->b->parseone(bw->b->o.charmap,s,&name,&line);
else
parseone_grep(bw->b->o.charmap,s,&name,&line);
/* Prepend current directory.. */
if (curdir)
fullname = vsncpy(NULL, 0, sv(curdir));
else
fullname = vsncpy(NULL, 0, sv(curd));
fullname = vsncpy(sv(fullname), sv(name));
vsrm(name);
name = canonical(fullname);
if (name && line != -1) {
ERROR *er = srcherr(bw, name, line);
uprevw(bw->parent, 0);
/* Check that we made it to a tw */
if (er)
rtn = jump_to_file_line((BW *)maint->curwin->object,name,er->line,NULL /* p->msg */);
else
rtn = jump_to_file_line((BW *)maint->curwin->object,name,line,NULL);
vsrm(name);
}
vsrm(s);
}
vsrm(curdir);
return rtn;
}
int unxterr(W *w, int k)
{
BW *bw;
WIND_BW(bw, w);
if (errptr->link.next == &errors) {
msgnw(bw->parent, joe_gettext(_("No more errors")));
return -1;
}
errptr = errptr->link.next;
setline(errbuf, errptr->src);
return jump_to_file_line(bw,errptr->file,errptr->line,NULL /* errptr->msg */);
}
int uprverr(W *w, int k)
{
BW *bw;
WIND_BW(bw, w);
if (errptr->link.prev == &errors) {
msgnw(bw->parent, joe_gettext(_("No more errors")));
return -1;
}
errptr = errptr->link.prev;
setline(errbuf, errptr->src);
return jump_to_file_line(bw,errptr->file,errptr->line,NULL /* errptr->msg */);
}