/* elmo - ELectronic Mail Operator Copyright (C) 2003 rzyjontko 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; version 2. 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. ---------------------------------------------------------------------- error display system An error message must be visible. It has to remain on the screen sufficiently long, so that a user is able to read it. The message should then disappear. Sometimes one error may generate next errors. A configreader may be a perfect example. This is why messages are queued for display. Other MUAs display error messages enough long to read it, but they don't clear them, what has always been annoying me. */ #define _GNU_SOURCE 1 /**************************************************************************** * IMPLEMENTATION HEADERS ****************************************************************************/ #include #include #include #include #include #include #include #include "ecurses.h" #include "error.h" #include "xmalloc.h" #include "str.h" #include "cmd.h" #include "elmo.h" #include "interface.h" /**************************************************************************** * IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS ****************************************************************************/ /* DELAY is a minimal number of seconds, that the message MUST remain on the screen. REMAIN is a maximal number of seconds, that the message MAY remain on the screen. */ #define DELAY 1 #define REMAIN 4 /**************************************************************************** * IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES ****************************************************************************/ struct list { struct list *next; char *msg; }; struct queue { struct list *list; /* first element of the queue */ struct list *last; /* last element of the queue */ time_t time; /* the time, when last message was displayed */ int visible; /* if anything is being displayed now */ }; /**************************************************************************** * IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID) ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE DATA ****************************************************************************/ /* The message queue. */ static struct queue msg_queue; /* 0 before error_init, and after error_free_resources when 1 - errors are displayed in cmd_win when 0 - errors are written to stderr */ static int initialized = 0; /**************************************************************************** * INTERFACE DATA ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTIONS ****************************************************************************/ static void queue_insert (char *msg) { struct list *elem = xmalloc (sizeof (struct list)); elem->next = NULL; elem->msg = msg; if (msg_queue.last == NULL){ msg_queue.list = msg_queue.last = elem; } else { msg_queue.last->next = elem; msg_queue.last = elem; } } static char * queue_get (void) { char *result; struct list *tmp; if (msg_queue.list == NULL) return NULL; tmp = msg_queue.list; result = tmp->msg; msg_queue.list = tmp->next; if (msg_queue.list == NULL) msg_queue.last = NULL; xfree (tmp); return result; } static char * prepare_error_msg (int errnum, const char *fmt, va_list ap) { str_t *str = str_create (); str_vsprintf (str, fmt, ap); if (errnum){ str_sprintf (str, ": %s", strerror (errnum)); } return str_finished (str); } static void display_msg (const char *msg) { if (initialized){ cmd_error_color (); window_mvaddstr (cmd_win, 0, 0, msg); wclrtoeol (cmd_win); wnoutrefresh (cmd_win); } else { fprintf (stderr, "%s\n", msg); } } /* There are 2 things to do each time this function is called. 1 - Clear the messages that are displayed too long. 2 - Display new messages if the last one was being displayed long enough. */ static void error_hook (int nothing) { char *msg; time_t now; if (msg_queue.visible || msg_queue.list){ now = time (NULL); if (now - msg_queue.time > DELAY){ msg = queue_get (); if (msg){ display_msg (msg); msg_queue.time = now; msg_queue.visible = 1; xfree (msg); } } if (now - msg_queue.time > REMAIN){ display_msg (""); msg_queue.visible = 0; } } } /**************************************************************************** * INTERFACE FUNCTIONS ****************************************************************************/ void error_init (void) { cmd_add_timeout_handler (error_hook); memset (&msg_queue, '\0', sizeof (msg_queue)); initialized = 1; } void error_critical (int status, int errnum, const char *msg) { elmo_finish (0); if (errnum) fprintf (stderr, "%s: %s\n", msg, strerror (errnum)); else fprintf (stderr, "%s\n", msg); exit (status); } void error_ (int errnum, const char *fmt, ...) { va_list ap; char *msg; va_start (ap, fmt); msg = prepare_error_msg (errnum, fmt, ap); va_end (ap); queue_insert (msg); } void error_regex (int errcode, regex_t *compiled, const char *re) { int length = regerror (errcode, compiled, NULL, 0); char *buffer = xmalloc (length + 1); regerror (errcode, compiled, buffer, length); if (re == NULL){ queue_insert (buffer); } else { error_ (0, "%s: %s", buffer, re); xfree (buffer); } } void error_free_resources (void) { struct list *list; struct list *next; for (list = msg_queue.list; list; list = next){ next = list->next; if (list->msg) xfree (list->msg); xfree (list); } initialized = 0; } /**************************************************************************** * INTERFACE CLASS BODIES ****************************************************************************/ /**************************************************************************** * * END MODULE error.c * ****************************************************************************/