/*
* Maketool - GTK-based front end for gmake
* Copyright (c) 1999-2003 Greg Banks
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "maketool.h"
#include "filter.h"
#include "log.h"
#include "util.h"
#include "ps.h"
CVSID("$Id: log.c,v 1.57 2003/10/21 14:36:35 gnb Exp $");
#ifndef GTK_CTREE_IS_EMPTY
#define GTK_CTREE_IS_EMPTY(_ctree_) \
(gtk_ctree_node_nth(GTK_CTREE(_ctree_), 0) == 0)
#endif
#include "error.xpm"
#include "warning.xpm"
#include "info.xpm"
typedef struct
{
GdkPixmap *open_pm;
GdkBitmap *open_mask;
GdkPixmap *closed_pm;
GdkBitmap *closed_mask;
} NodeIcons;
#define DO_FONTS 0 /* TODO: support different fonts */
static GtkWidget *logwin; /* a GtkCTree widget */
static int num_errors;
static int num_warnings;
static gboolean num_ew_changed = FALSE;
static void (*log_count_callback)(int, int);
static GList *log; /* list of LogRecs */
static GList *log_tail; /* tail of `log' list */
static GHashTable *dir_hash; /* hashtable of all normalised directories */
static GPtrArray *dir_stack; /* stack of GList of entries in dir_hash */
static int dir_max_depth = 0;
static int dir_stack_count = 0;
#define stackhead(i) g_ptr_array_index(dir_stack, i)
static gboolean context_dirty = FALSE;
static GList *log_node_stack = 0; /* stack of LogRec's */
#if DO_FONTS
static GdkFont *fonts[L_MAX];
#endif
static GdkColor foregrounds[L_MAX];
static gboolean foreground_set[L_MAX];
static GdkColor backgrounds[L_MAX];
static gboolean background_set[L_MAX];
static NodeIcons icons[L_MAX];
static void log_context_unref(LogContext *con);
static LogContext *log_context_ref(LogContext *con);
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
static void
filter_result_init(FilterResult *res)
{
res->code = FR_UNDEFINED;
res->file = 0;
res->line = 0;
res->column = 0;
res->summary = 0;
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
static const char *
log_unique_dir(const char *dir)
{
char *norm = file_normalise(dir);
char *uniq;
if ((uniq = g_hash_table_lookup(dir_hash, norm)) != 0)
{
g_free(norm);
return uniq;
}
g_hash_table_insert(dir_hash, norm, norm);
return norm;
}
#if DEBUG
static void
log_dump_stack(void)
{
int i;
int n = 0;
GList *iter;
fprintf(stderr, "dir_stack = {\n");
for (i = 0 ; i < dir_max_depth ; i++)
{
fprintf(stderr, "[%d] ", i+1);
for (iter = stackhead(i) ; iter != 0 ; iter = iter->next)
{
const char *dir = (const char *)iter->data;
fprintf(stderr, " %s", dir);
n++;
}
fprintf(stderr, "\n");
}
fprintf(stderr, "}\n");
assert(n == dir_stack_count);
}
#endif
static void
log_change_dir(const char *dir)
{
#if 0
dir = log_unique_dir(dir);
#if DEBUG
fprintf(stderr, "log_change_dir(\"%s\") -> \"%s\"\n", dir, norm);
#endif
if (log_directory_stack == 0)
log_directory_stack = g_list_append(log_directory_stack, norm);
else
{
g_free((char*)log_directory_stack->data);
log_directory_stack->data = norm;
}
#endif
assert(0 && "log_change_dir not implemented");
}
static void
log_push_dir(int depth, const char *dir)
{
dir = log_unique_dir(dir);
#if DEBUG
fprintf(stderr, "log_push_dir(%d, \"%s\")\n", depth, dir);
#endif
if (depth < 1 || depth > dir_max_depth+1)
{
fprintf(stderr, "maketool: bad pushdir(%d, \"%s\")\n", depth, dir);
return;
}
if (depth == dir_max_depth+1)
{
dir_max_depth = depth;
g_ptr_array_set_size(dir_stack, dir_max_depth);
}
stackhead(depth-1) = g_list_prepend(stackhead(depth-1), (gpointer)dir);
dir_stack_count++;
context_dirty = TRUE;
#if DEBUG
log_dump_stack();
#endif
}
static void
log_pop_dir(int depth, const char *dir)
{
GList *iter, *next;
dir = log_unique_dir(dir);
#if DEBUG
fprintf(stderr, "log_pop_dir(%d, \"%s\")\n", depth, dir);
#endif
if (depth < 1 || depth > dir_max_depth)
{
fprintf(stderr, "maketool: bad popdir(%d, \"%s\")\n", depth, dir);
return;
}
for (iter = stackhead(depth-1) ; iter != 0 ; iter = next)
{
next = iter->next;
/* uniqueness property allows pointer comparison */
if ((char*)iter->data == dir)
{
stackhead(depth-1) = g_list_remove_link(stackhead(depth-1), iter);
dir_stack_count--;
context_dirty = TRUE;
break;
}
}
if (depth == dir_max_depth && stackhead(depth-1) == 0)
dir_max_depth--;
#if DEBUG
log_dump_stack();
#endif
}
static void
log_clear_dirs(void)
{
int i;
for (i = 0 ; i < dir_max_depth ; i++)
{
while (stackhead(i) != 0)
stackhead(i) = g_list_remove_link(stackhead(i), stackhead(i));
}
dir_max_depth = 0;
dir_stack_count = 0;
context_dirty = TRUE;
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
#if DEBUG
static void
log_context_dump(LogContext *con)
{
unsigned int i;
fprintf(stderr, "context {\n");
fprintf(stderr, " refcount = %d\n", con->refcount);
fprintf(stderr, " dirs = {");
for (i = 0 ; i < con->num_dirs ; i++)
fprintf(stderr, " %s", con->dirs[i]);
fprintf(stderr, "}\n");
}
#endif
static void
log_context_unref(LogContext *con)
{
if (--con->refcount == 0)
{
g_free(con->dirs);
g_free(con);
}
}
static LogContext *
log_context_ref(LogContext *con)
{
con->refcount++;
return con;
}
static LogContext *
log_context_new(int n)
{
LogContext *con;
con = g_new(LogContext, 1);
con->refcount = 1;
con->num_dirs = n;
con->dirs = g_new(const char*, n);
return con;
}
static LogContext *
log_get_context(void)
{
int i;
GList *iter;
const char **p;
static LogContext *context = 0;
if (!context_dirty && context != 0)
return context;
if (context != 0)
log_context_unref(context);
/* TODO: in !jmode save just the stack top */
context = log_context_new(dir_stack_count);
p = context->dirs;
for (i = dir_max_depth-1 ; i >= 0 ; i--)
{
for (iter = stackhead(i) ; iter != 0 ; iter = iter->next)
*p++ = (const char *)iter->data;
}
assert(p == context->dirs+dir_stack_count);
context_dirty = FALSE;
return context;
}
char **
log_get_filenames(LogRec *lr)
{
unsigned int i;
unsigned int j;
int nfiles = 0;
char **files;
char *ff;
if (lr->res.file[0] == '/')
{
/* absolute filename in error message, don't need to use context */
#if DEBUG
fprintf(stderr, "log_get_filenames: trying \"%s\"\n", lr->res.file);
#endif
if (!file_exists(lr->res.file))
return 0;
files = g_new(char*, 2);
files[0] = g_strdup(lr->res.file);
files[1] = 0;
return files;
}
if (lr->context == 0 || lr->context->num_dirs == 0)
{
/* no directories, but could exist in current directory */
ff = file_normalise(lr->res.file);
if (file_exists(ff))
{
files = g_new(char*, 2);
files[0] = ff;
files[1] = 0;
return files;
}
g_free(ff);
return 0;
}
files = g_new(char*, lr->context->num_dirs+1);
#if DEBUG
fprintf(stderr, "log_get_filenames: ");
log_context_dump(lr->context);
#endif
for (i = 0 ; i < lr->context->num_dirs ; i++)
{
/* check for duplicate directories -- this is a legitimate case */
for (j = 0 ; j < i ; j++)
{
if (lr->context->dirs[i] == lr->context->dirs[j])
break;
}
if (j < i)
continue; /* skip dup dir in context */
ff = g_strconcat(lr->context->dirs[i], "/", lr->res.file, 0);
#if DEBUG
fprintf(stderr, "log_get_filenames: trying \"%s\"\n", ff);
#endif
if (file_exists(ff))
files[nfiles++] = ff;
else
g_free(ff);
}
files[nfiles] = 0;
if (!nfiles)
{
g_free(files);
return 0;
}
return files;
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
static void
log_change_node(LogRec *lr)
{
if (log_node_stack == 0)
log_node_stack = g_list_append(log_node_stack, lr);
else
{
log_node_stack->data = lr;
}
}
static void
log_push_node(LogRec *lr)
{
log_node_stack = g_list_prepend(log_node_stack, lr);
}
static void
log_pop_node(void)
{
if (log_node_stack != 0)
{
log_node_stack = g_list_remove_link(log_node_stack, log_node_stack);
}
}
static void
log_clear_nodes(void)
{
while (log_node_stack != 0)
{
log_node_stack = g_list_remove_link(log_node_stack, log_node_stack);
}
}
/*
* Returns the node nearest the bottom of the stack (i.e.
* the rootmost in the log window's tree representation)
* with the same directory as the top of the stack. This
* trick prevents recursive make's which don't change
* directory from appearing as deeper levels in the tree.
*/
static LogRec *
log_current_unique_node(void)
{
GList *link;
LogRec *top;
if (log_node_stack == 0)
return 0;
top = (LogRec *)log_node_stack->data;
if (log_node_stack->next == 0 ||
(top->res.code != FR_PUSHDIR && top->res.code != FR_CHANGEDIR))
return top;
for (link = log_node_stack->next ; link != 0 ; link = link->next)
{
LogRec *lr = (LogRec *)link->data;
if ((lr->res.code != FR_PUSHDIR && lr->res.code != FR_CHANGEDIR) ||
strcmp(lr->res.file, top->res.file))
return (LogRec*)link->prev->data;
}
/*
* This should never happen 'cos the bottom-most in
* the stack should always be a FR_BUILDSTART.
*/
return 0;
}
static LogRec *
log_rootmost_node(void)
{
GList *tail = g_list_last(log_node_stack);
return (tail == 0 ? 0 : (LogRec *)tail->data);
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
static LogRec *
log_add_rec(char *line, const FilterResult *res, LogContext *con)
{
LogRec *lr;
lr = g_new(LogRec, 1);
memset(lr, 0, sizeof(LogRec));
lr->res = *res;
lr->line = line;
lr->expanded = TRUE;
if (con != 0)
lr->context = log_context_ref(con);
switch (lr->res.code)
{
case FR_WARNING:
num_warnings++;
num_ew_changed = TRUE;
break;
case FR_ERROR:
num_errors++;
num_ew_changed = TRUE;
break;
default:
break;
}
if (log == 0)
log = log_tail = g_list_append(0, lr);
else
{
g_list_append(log_tail, lr);
log_tail = log_tail->next;
}
return lr;
}
static void
log_del_rec(LogRec *lr)
{
if (lr->res.file != 0)
g_free(lr->res.file);
if (lr->res.summary != 0)
g_free(lr->res.summary);
g_free(lr->line);
if (lr->context)
log_context_unref(lr->context);
g_free(lr);
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
const char *
log_get_text(const LogRec *lr)
{
return ((prefs.log_flags & LF_SUMMARISE) && lr->res.summary != 0 ?
lr->res.summary : lr->line);
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
static void
log_show_rec(LogRec *lr)
{
gboolean was_empty = GTK_CTREE_IS_EMPTY(logwin);
LogSeverity sev = L_INFO;
LogRec *parent_rec = log_current_unique_node();
char *text;
gboolean is_leaf = TRUE;
lr->node = 0;
switch (lr->res.code)
{
case FR_UNDEFINED: /* same as INFORMATION */
case FR_INFORMATION:
/* use default font, fgnd, bgnd */
if (!(prefs.log_flags & LF_SHOW_INFO))
return;
if ((prefs.log_flags & LF_SUMMARISE) && lr->res.summary != 0)
sev = L_SUMMARY;
break;
case FR_WARNING:
sev = L_WARNING;
if (!(prefs.log_flags & LF_SHOW_WARNINGS))
return;
break;
case FR_ERROR:
sev = L_ERROR;
if (!(prefs.log_flags & LF_SHOW_ERRORS))
return;
break;
case FR_BUILDSTART:
log_clear_nodes();
log_push_node(lr);
parent_rec = 0;
is_leaf = FALSE;
break;
case FR_CHANGEDIR:
if (prefs.log_flags & LF_INDENT_DIRS)
{
log_change_node(lr);
is_leaf = FALSE;
}
break;
case FR_PUSHDIR:
if (prefs.log_flags & LF_INDENT_DIRS)
{
log_push_node(lr);
is_leaf = FALSE;
}
break;
case FR_POPDIR:
if (prefs.log_flags & LF_INDENT_DIRS)
log_pop_node();
break;
default:
break;
}
text = lr->line;
if ((prefs.log_flags & LF_SUMMARISE) && lr->res.summary != 0)
{
if (*lr->res.summary == '\0')
return; /* summary="" allows summarised lines to disappear */
else
text = lr->res.summary;
}
text = (char *)log_get_text(lr);
/* TODO: freeze & thaw if it redraws the wrong colour 1st */
lr->node = gtk_ctree_insert_node(GTK_CTREE(logwin),
(parent_rec == 0 ? 0 : parent_rec->node),/* parent */
(GtkCTreeNode*)0, /* sibling */
&text, /* text[] */
0, /* spacing */
icons[sev].closed_pm,
icons[sev].closed_mask, /* pixmap_closed,mask_closed */
icons[sev].open_pm,
icons[sev].open_mask, /* pixmap_opened,mask_opened */
is_leaf, /* is_leaf */
lr->expanded); /* expanded */
gtk_ctree_node_set_row_data(GTK_CTREE(logwin), lr->node, (gpointer)lr);
/* TODO: support different fonts */
if (foreground_set[sev] != 0)
gtk_ctree_node_set_foreground(GTK_CTREE(logwin), lr->node, &foregrounds[sev]);
if (background_set[sev] != 0)
gtk_ctree_node_set_background(GTK_CTREE(logwin), lr->node, &backgrounds[sev]);
if (was_empty)
grey_menu_items(); /* log window just became non-empty */
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
static void
log_update_build_start(void)
{
LogRec *bs;
estring text;
int n = 0;
if (!num_ew_changed || (num_errors == 0 && num_warnings == 0))
return;
if ((bs = log_rootmost_node()) == 0)
return; /* yeah like that's going to happen */
assert(bs->node != 0);
estring_init(&text);
estring_append_string(&text, bs->line);
estring_append_string(&text, " (");
if (num_errors > 0)
{
if (n)
estring_append_string(&text, ", ");
estring_append_printf(&text, _("%d errors"), num_errors);
n++;
}
if (num_warnings > 0)
{
if (n)
estring_append_string(&text, ", ");
estring_append_printf(&text, _("%d warnings"), num_warnings);
n++;
}
estring_append_string(&text, ")");
gtk_ctree_node_set_text(GTK_CTREE(logwin), bs->node, /*column*/0, text.data);
estring_free(&text);
if (log_count_callback)
(*log_count_callback)(num_errors, num_warnings);
num_ew_changed = FALSE;
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
#define MAX_PENDING 16
LogRec *
log_add_line(const char *line)
{
static FilterResult res;
static char *pending[MAX_PENDING];
static int num_pending;
LogRec *lr = 0;
int i;
LogContext *con = 0;
if (!(res.code & FR_PENDING))
filter_result_init(&res);
filter_apply(line, &res);
#if DEBUG
fprintf(stderr, "filter_apply(\"%s\") -> {code=%d file=\"%s\" line=%d summary=\"%s\"}\n",
line, (int)res.code, safe_str(res.file), res.line, res.summary);
#endif
/* add to the pending lines array */
assert(num_pending < MAX_PENDING);
pending[num_pending++] = g_strdup(line);
if (res.code & FR_PENDING)
return 0; /* go back and see some more lines */
switch (res.code)
{
case FR_UNDEFINED:
res.code = FR_INFORMATION;
break;
case FR_CHANGEDIR:
log_change_dir(res.file);
break;
case FR_PUSHDIR:
log_push_dir(res.line, res.file);
if (res.summary != 0)
g_free(res.summary);
res.summary = file_denormalise(res.file, DEN_ALL);
break;
case FR_POPDIR:
log_pop_dir(res.line, res.file);
break;
default:
if (res.file != 0 && *res.file != '/')
con = log_get_context();
break;
}
/* drain from the pending lines array, usually has exactly 1 element */
for (i = 0 ; i < num_pending ; i++)
{
if (i > 0)
{
/*
* Only the first line has a summary, others disappear in
* summary mode. This is both to save memory and also to
* make the summary presentation more compact.
*/
res.summary = g_strdup("");
/*
* Only the first line is an error or warning, others are
* informational. This is so the error count presented
* to the user is accurate.
*/
res.code = FR_INFORMATION;
}
lr = log_add_rec(pending[i], &res, con);
log_show_rec(lr);
/* TODO: do this exactly once when loading from file */
log_update_build_start();
}
num_pending = 0;
return lr;
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
void
log_clear(void)
{
/* delete all LogRecs */
while (log != 0)
{
log_del_rec((LogRec *)log->data);
log = g_list_remove_link(log, log);
}
log_tail = 0;
/* clear the log window */
gtk_clist_clear(GTK_CLIST(logwin));
/* update sensitivity of menu items */
grey_menu_items();
/* TODO: empty dir_hash */
}
gboolean
log_is_empty(void)
{
return GTK_CTREE_IS_EMPTY(logwin);
}
void
log_collapse_all(void)
{
GList *list;
for (list = log ; list != 0 ; list = list->next)
{
LogRec *lr = (LogRec *)list->data;
if (lr->res.code == FR_BUILDSTART)
gtk_ctree_collapse(GTK_CTREE(logwin), lr->node);
}
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/* BEGIN: pinched from gtkclist.c */
/* this defigns the base grid spacing */
#define CELL_SPACING 1
/* returns the row index from a y pixel location in the
* context of the clist's voffset */
#define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
((clist)->row_height + CELL_SPACING))
/* END: pinched from gtkclist.c */
static void
log_repopulate(void)
{
GList *list;
LogRec *first = 0;
gtk_clist_freeze(GTK_CLIST(logwin));
/* figure out which log record is the first-visible in the ctree */
if (!log_is_empty())
{
first = (LogRec *)gtk_ctree_node_get_row_data(
GTK_CTREE(logwin),
gtk_ctree_node_nth(
GTK_CTREE(logwin),
ROW_FROM_YPIXEL(GTK_CLIST(logwin), 0)));
#if DEBUG > 10
fprintf(stderr, "old first = \"%s\"\n", first->line);
#endif
}
/* remove all the rows and build new ones according to new flags */
gtk_clist_clear(GTK_CLIST(logwin));
for (list=log ; list!=0 ; list=list->next)
log_show_rec((LogRec *)list->data);
if (first != 0)
{
/* figure out which row is to be the new first-visible */
GList *firstl = g_list_find(log, first);
for (list = firstl ;
list != 0 && ((LogRec *)list->data)->node == 0 ;
list = list->next)
;
if (list == 0)
for (list = firstl ;
list != 0 && ((LogRec *)list->data)->node == 0 ;
list = list->prev)
;
if (list != 0)
{
first = (LogRec *)list->data;
#if DEBUG > 10
fprintf(stderr, "new first = \"%s\"\n", first->line);
#endif
gtk_ctree_node_moveto(GTK_CTREE(logwin), first->node, 0,
/* row_align */0.0, /* col_align */0.0);
}
}
gtk_clist_thaw(GTK_CLIST(logwin));
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/* TODO: rewrite log_save() and log_open() in terms of the
* existing functions log_apply(), log_start_build(), log_add_line()
* and new functions log_freeze() and log_thaw().
*/
void
log_save(const char *file, Progress *p)
{
GList *list;
FILE *fp;
int n = 0;
if ((fp = fopen(file, "w")) == 0)
{
/* Note: assumes g_strerror() does i18n of system error messages. */
message("%s: %s", file, g_strerror(errno));
return;
}
progress_start(p, g_list_length(log));
for (list=log ; list!=0 ; list=list->next)
{
LogRec *lr = (LogRec *)list->data;
fputs(lr->line, fp);
fputc('\n', fp);
if (++n == 32)
{
progress_delta(p, n);
n = 0;
}
}
progress_delta(p, n);
fclose(fp);
progress_end(p);
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
void
log_open(const char *file, Progress *p)
{
FILE *fp;
char *dfile;
int len;
estring leftover;
char *startstr, buf[1025];
struct stat sb;
if ((fp = fopen(file, "r")) == 0)
{
/* Note: assumes g_strerror() does i18n of system error messages. */
message("%s: %s", file, g_strerror(errno));
return;
}
if (fstat(fileno(fp), &sb) < 0)
{
message("can't fstat %s: %s", file, g_strerror(errno));
return;
}
progress_start(p, sb.st_size);
gtk_clist_freeze(GTK_CLIST(logwin));
dfile = file_denormalise(file, DEN_HOME);
startstr = g_strdup_printf(_("Log file %s"), dfile);
log_start_build(startstr);
g_free(startstr);
g_free(dfile);
estring_init(&leftover);
while ((len = fread(buf, 1, sizeof(buf)-1, fp)) > 0)
{
char *start = buf, *end;
buf[len] = '\0';
progress_delta(p, len);
while (len > 0)
{
end = strchr(start, '\n');
if (end == 0)
{
/* only a part of a line left - append to leftover */
estring_append_string(&leftover, start);
len = 0;
}
else
{
/* got an end-of-line - isolate the line & feed it to log_add_line() */
*end = '\0';
estring_append_string(&leftover, start);
log_add_line(leftover.data);
estring_truncate(&leftover);
len -= (end - start);
start = ++end;
}
}
}
/* handle case where last line is not terminated with '\n' */
if (leftover.length > 0) {
progress_delta(p, leftover.length);
log_add_line(leftover.data);
}
estring_free(&leftover);
fclose(fp);
gtk_clist_thaw(GTK_CLIST(logwin));
filter_post();
progress_end(p);
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
static int
log_get_indent_level(const LogRec *lr)
{
int indent = 0;
GtkCTreeNode *node = lr->node;
for (;;)
{
GtkCTreeNode *parent = GTK_CTREE_ROW(node)->parent;
if (parent == 0)
return indent;
indent++;
node = parent;
}
return 0; /* NOTREACHED */
}
void
log_print(FILE *fp)
{
GList *list;
PsDocument *ps;
int i;
ps = ps_begin(fp);
/* TODO: other std comments */
ps_title(ps, "Maketool log"); /* TODO: more details */
i = 0;
for (list=log ; list!=0 ; list=list->next)
{
LogRec *lr = (LogRec *)list->data;
if (lr->node != 0)
i++;
}
ps_num_lines(ps, i);
for (i=0 ; i<L_MAX ; i++)
{
if (foreground_set[i])
ps_foreground(ps, i,
(float)foregrounds[i].red / 65535.0,
(float)foregrounds[i].green / 65535.0,
(float)foregrounds[i].blue / 65535.0);
if (background_set[i])
ps_background(ps, i,
(float)backgrounds[i].red / 65535.0,
(float)backgrounds[i].green / 65535.0,
(float)backgrounds[i].blue / 65535.0);
}
ps_prologue(ps);
for (list=log ; list!=0 ; list=list->next)
{
LogRec *lr = (LogRec *)list->data;
LogSeverity sev = L_INFO;
/*
* We only want to print visible log records. LogRec visibility
* was determined in log_show_rec() using various rules which
* we'd rather not reproduce here.
*/
if (lr->node == 0)
continue;
/*
* Calculate severity level for display purposes.
* TODO: this is already done once by log_show_rec(),
* should store the result.
*/
switch (lr->res.code)
{
case FR_INFORMATION:
if ((prefs.log_flags & LF_SUMMARISE) && lr->res.summary != 0)
sev = L_SUMMARY;
break;
case FR_WARNING:
sev = L_WARNING;
break;
case FR_ERROR:
sev = L_ERROR;
break;
default:
break; /* shut up Mr gcc */
}
ps_line(ps, log_get_text(lr), sev, log_get_indent_level(lr));
}
ps_end(ps);
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
LogRec *
log_selected(void)
{
if (GTK_CLIST(logwin)->selection == 0)
return 0;
return (LogRec *)gtk_ctree_node_get_row_data(GTK_CTREE(logwin),
GTK_CTREE_NODE(GTK_CLIST(logwin)->selection->data));
}
void
log_set_selected(LogRec *lr)
{
if (lr->node != 0)
{
gtk_ctree_select(GTK_CTREE(logwin), lr->node);
if (gtk_ctree_node_is_visible(GTK_CTREE(logwin), lr->node) != GTK_VISIBILITY_FULL)
gtk_ctree_node_moveto(GTK_CTREE(logwin), lr->node, 0, 0.5, 0.0);
}
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
void
log_ensure_visible(const LogRec *lr)
{
if (lr->node != 0 &&
gtk_ctree_node_is_visible(GTK_CTREE(logwin), lr->node) != GTK_VISIBILITY_FULL)
gtk_ctree_node_moveto(GTK_CTREE(logwin), lr->node, 0, 0.5, 0.0);
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
int
log_get_flags(void)
{
return prefs.log_flags;
}
void
log_set_flags(int f)
{
prefs.log_flags = f;
log_repopulate();
preferences_save();
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
int
log_num_errors(void)
{
return num_errors;
}
int
log_num_warnings(void)
{
return num_warnings;
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
static gboolean
_log_load_color(
GdkColormap *colormap,
const char *color_name,
GdkColor *col)
{
if (color_name == 0)
return FALSE;
if (!gdk_color_parse(color_name, col))
return FALSE;
if (!gdk_colormap_alloc_color(colormap, col, FALSE, TRUE))
return FALSE;
return TRUE;
}
static void
_log_setup_colors(void)
{
GdkColormap *colormap = gtk_widget_get_colormap(toplevel);
/* L_INFO */
foreground_set[L_INFO] = _log_load_color(colormap, prefs.colors[COL_FG_INFO],
&foregrounds[L_INFO]);
background_set[L_INFO] = _log_load_color(colormap, prefs.colors[COL_BG_INFO],
&backgrounds[L_INFO]);
/* L_WARNING */
foreground_set[L_WARNING] = _log_load_color(colormap, prefs.colors[COL_FG_WARNING],
&foregrounds[L_WARNING]);
background_set[L_WARNING] = _log_load_color(colormap, prefs.colors[COL_BG_WARNING],
&backgrounds[L_WARNING]);
/* L_ERROR */
foreground_set[L_ERROR] = _log_load_color(colormap, prefs.colors[COL_FG_ERROR],
&foregrounds[L_ERROR]);
background_set[L_ERROR] = _log_load_color(colormap, prefs.colors[COL_BG_ERROR],
&backgrounds[L_ERROR]);
/* L_SUMMARY */
foreground_set[L_SUMMARY] = _log_load_color(colormap, prefs.colors[COL_FG_SUMMARY],
&foregrounds[L_SUMMARY]);
background_set[L_SUMMARY] = _log_load_color(colormap, prefs.colors[COL_BG_SUMMARY],
&backgrounds[L_SUMMARY]);
}
static void
_log_free_colors(void)
{
GdkColormap *colormap = gtk_widget_get_colormap(toplevel);
if (foreground_set[L_INFO])
gdk_colormap_free_colors(colormap, &foregrounds[L_INFO], 1);
if (background_set[L_INFO])
gdk_colormap_free_colors(colormap, &backgrounds[L_INFO], 1);
if (foreground_set[L_WARNING])
gdk_colormap_free_colors(colormap, &foregrounds[L_INFO], 1);
if (background_set[L_WARNING])
gdk_colormap_free_colors(colormap, &backgrounds[L_INFO], 1);
if (foreground_set[L_ERROR])
gdk_colormap_free_colors(colormap, &foregrounds[L_INFO], 1);
if (background_set[L_ERROR])
gdk_colormap_free_colors(colormap, &backgrounds[L_INFO], 1);
}
void
log_init(GtkWidget *w)
{
GdkWindow *win = toplevel->window;
/* L_INFO */
#if DO_FONTS
fonts[L_INFO] = 0;
#endif
icons[L_INFO].closed_pm = gdk_pixmap_create_from_xpm_d(win,
&icons[L_INFO].closed_mask, 0, info_xpm);
icons[L_INFO].open_pm = icons[L_INFO].closed_pm;
icons[L_INFO].open_mask = icons[L_INFO].closed_mask;
/* L_WARNING */
#if DO_FONTS
fonts[L_WARNING] = 0;
#endif
icons[L_WARNING].closed_pm = gdk_pixmap_create_from_xpm_d(win,
&icons[L_WARNING].closed_mask, 0, warning_xpm);
icons[L_WARNING].open_pm = icons[L_WARNING].closed_pm;
icons[L_WARNING].open_mask = icons[L_WARNING].closed_mask;
/* L_ERROR */
#if DO_FONTS
fonts[L_ERROR] = 0;
#endif
icons[L_ERROR].closed_pm = gdk_pixmap_create_from_xpm_d(win,
&icons[L_ERROR].closed_mask, 0, error_xpm);
icons[L_ERROR].open_pm = icons[L_ERROR].closed_pm;
icons[L_ERROR].open_mask = icons[L_ERROR].closed_mask;
/* L_SUMMARY */
#if DO_FONTS
fonts[L_SUMMARY] = 0;
#endif
icons[L_SUMMARY].closed_pm = icons[L_INFO].closed_pm;
icons[L_SUMMARY].closed_mask = icons[L_INFO].closed_mask;
icons[L_SUMMARY].open_pm = icons[L_SUMMARY].closed_pm;
icons[L_SUMMARY].open_mask = icons[L_SUMMARY].closed_mask;
_log_setup_colors();
logwin = w;
filter_load();
dir_hash = g_hash_table_new(g_str_hash, g_str_equal);
dir_stack = g_ptr_array_new();
}
void
log_set_count_callback(void (*callback)(int, int))
{
log_count_callback = callback;
}
void
log_colors_changed(void)
{
_log_free_colors();
_log_setup_colors();
log_repopulate();
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
void
log_start_build(const char *message)
{
FilterResult res;
log_clear_dirs();
num_errors = 0;
num_warnings = 0;
num_ew_changed = FALSE;
filter_init();
filter_result_init(&res);
res.code = FR_BUILDSTART;
log_show_rec(log_add_rec(g_strdup(message), &res, 0));
}
void
log_end_build(const char *target)
{
filter_post();
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
static LogRec *
log_find_error_aux(LogRec *lr, gboolean forward)
{
GList *list;
if (lr == 0)
/* begin at start or end */
list = (forward ? log : g_list_last(log));
else
{
/* skip the current logrec */
list = g_list_find(log, lr);
list = (forward ? list->next : list->prev);
}
for ( ; list != 0 ; list = (forward ? list->next : list->prev))
{
lr = (LogRec *)list->data;
if ((lr->res.code == FR_ERROR || lr->res.code == FR_WARNING) &&
lr->res.file != 0)
return lr;
}
return 0;
}
LogRec *
log_next_error(LogRec *lr)
{
return log_find_error_aux(lr, TRUE);
}
LogRec *
log_prev_error(LogRec *lr)
{
return log_find_error_aux(lr, FALSE);
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
#define FIRST() \
(forward ? log : g_list_last(log))
#define NEXT(l) \
(forward ? (l)->next : (l)->prev)
void
log_apply_after(
LogApplyFunc func,
gboolean forward,
LogRec *start,
gpointer user_data)
{
GList *list;
if (start == 0)
{
list = FIRST();
}
else
{
list = g_list_find(log, start);
if (list == 0)
list = FIRST();
else
list = NEXT(list);
}
for ( ; list!=0 ; list=NEXT(list))
{
LogRec *lr = (LogRec *)list->data;
if (!(*func)(lr, user_data))
return;
}
}
void
log_apply(LogApplyFunc func, gpointer user_data)
{
log_apply_after(func, TRUE, 0, user_data);
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
void
log_get_icon(
LogSeverity level,
GdkPixmap **open_pmp,
GdkBitmap **open_maskp,
GdkPixmap **closed_pmp,
GdkBitmap **closed_maskp)
{
if (open_pmp != 0)
*open_pmp = icons[level].open_pm;
if (open_maskp != 0)
*open_maskp = icons[level].open_mask;
if (closed_pmp != 0)
*closed_pmp = icons[level].closed_pm;
if (closed_maskp != 0)
*closed_maskp = icons[level].closed_mask;
}
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/*END*/
syntax highlighted by Code2HTML, v. 0.9.1