/*
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 <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#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
*
****************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1