static char rcsid[] = "@(#)$Id: debug.c,v 1.23 2006/04/09 07:37:08 hurtta Exp $";
/******************************************************************************
* The Elm (ME+) Mail System - $Revision: 1.23 $ $State: Exp $
*
* Author: Kari Hurtta <hurtta+elm@posti.FMI.FI> (was hurtta+elm@ozone.FMI.FI)
*****************************************************************************/
#include "headers.h"
#include "s_elm.h"
#include "patchlevel.h"
static char *us2s P_((unsigned char *str));
static char *us2s(str)
unsigned char *str;
{
return (char *)str;
}
struct debug_target {
int debug_fd;
CONST char * file;
struct class_level_pair {
CONST char * class;
int level;
} * match;
int match_count;
struct debug_target * next;
/* In startup quite huge number of debug output can be
needed to be buffered */
char buffer[4000];
VOLATILE int buffer_ptr;
} * debug_list = NULL;
static CONST char * DEBUG_base = "ELMLIB";
/* Given arhument must be statically alloced */
void init_debugfile(progname)
CONST char *progname;
{
DEBUG_base = progname;
}
static int flush_debug_buffer P_((struct debug_target *a));
static void init_debug P_((struct debug_target * A));
static void init_debug(A)
struct debug_target * A;
{
DEBUG_VAR(temp,__FILE__,"");
char * filename = elm_message(FRM("%s/%s"),home,A->file);
char * pp;
int fd = -1;
temp.target = A;
/* For compability of old method
"ELM:debug.info" is renamed to "ELM:debug.last"
*/
if (NULL != (pp = strrchr(A->file,':')) &&
0 == strcmp(pp,":debug.info") &&
access(filename, ACCESS_EXISTS) == 0) { /* already one! */
char *newfname = elm_message(FRM("%s/%s:debug.last"),
home,DEBUG_base);
if (0 == rename(filename, newfname)) {
debug_action_call(&temp,
"RENAMEd old debug file %s to %s\n",
filename,newfname);
}
free(newfname);
}
if (0 == can_open(filename,"a"))
fd = open(filename,O_CREAT|O_WRONLY|O_APPEND,00600);
if (-1 == fd) {
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCouldNotOpenDebugFile,
"Could not open file %s for debug output!\n"),
filename);
goto out;
}
if (-1 != A->debug_fd)
close(A->debug_fd);
A->debug_fd = fd;
elm_chown(filename, userid, groupid); /* file owned by user */
if (0 != A->buffer_ptr)
debug_action_call(&temp,
"\n======================================================\n");
debug_action_call(&temp,
"Debug output of the ELM library. Version %s PL%s\n\n",
VERSION,PATCHLEVEL);
out:
free(filename);
}
static void close_all(void);
static void close_all()
{
DEBUG_VAR(temp,__FILE__,"");
struct debug_target * A;
for (A = debug_list; A; A = A->next) {
temp.target = A;
debug_action_call(&temp,"\nClosing debug file...\n");
flush_debug_buffer(A);
if (-1 != A->debug_fd)
close(A->debug_fd);
A->debug_fd = -1;
}
}
int set_debugging(arg)
CONST char *arg;
{
struct debug_target * X = NULL;
char * match_filename = NULL;
struct class_level_pair C;
struct class_level_pair *tmp1;
DEBUG_VAR(temp,__FILE__,"");
char * ptr;
long l;
C.class = "";
C.level = 0;
l = strtol(arg,&ptr,10);
if ('\0' == *ptr)
C.level = l;
else if (NULL == (ptr = strpbrk(arg,":="))) {
C.class = arg;
C.level = 1;
} else {
char *ptr1;
char * tmp__1 = safe_strdup(arg);
tmp__1[ptr-arg] = '\0';
C.class = tmp__1;
if ('=' == *ptr) {
match_filename = safe_strdup(ptr+1);
ptr = strchr(match_filename,':');
if (ptr) {
*ptr = '\0';
ptr++;
}
} else
ptr++;
if (!ptr)
C.level = 1;
else {
l = strtol(ptr,&ptr1,10);
if ('\0' != *ptr1) {
if (match_filename)
free(match_filename);
return 0;
}
C.level = l;
}
}
if (!match_filename)
match_filename = elm_message(FRM("%s:debug.info"),
DEBUG_base);
for (X = debug_list; X; X = X -> next)
if (0 == strcmp(X->file,match_filename))
break;
if (X) {
free(match_filename);
match_filename = NULL;
} else {
X = safe_malloc(sizeof (*X));
X->debug_fd = -1;
X->file = match_filename;
X->next = debug_list;
X->match = NULL;
X->match_count = 0;
X->buffer[0] = '\0';
X->buffer_ptr = 0;
match_filename = NULL;
if (home[0])
init_debug(X);
debug_list = X;
}
/* safe_realloc() calls DPRINT and therefore may refere X->match
before it is assigned .. so we do not use safe_realloc()
on here ... */
tmp1 = realloc(X->match,
(1 + X->match_count) * sizeof (X->match[0]));
if (tmp1) {
X->match = tmp1;
X->match[X->match_count++] = C;
} else {
debug_action_call(&temp,
"Can't alloc memory for class %s at debug level %d (size %d)\n",
C.class,C.level,
(1 + X->match_count) * sizeof (X->match[0]));
}
if (!debug_list)
atexit(close_all);
temp.target = X;
debug_action_call(&temp,
"Debug output for class %s at debug level %d\n",
C.class,C.level);
return 1;
}
void debug_user_init()
{
struct debug_target * A;
/* frm calls user_init() several times ... */
static int init_done = 0;
if (init_done)
return;
init_done = 1;
for (A = debug_list; A; A = A->next)
init_debug(A);
}
void debug_level_check(a)
struct debug_struct *a;
{
struct debug_target * A;
for (A = debug_list; A; A = A->next) {
int i;
for (i = 0; i < A->match_count; i++) {
if ('\0' == A->match[i].class[0] ||
0 == strcmp(a->class,A->match[i].class)) {
a->target = A;
a->active = A->match[i].level;
if (a->active > 0) {
/* Not really called from signal handler, but
better safe and anyway this for this
debug_action_sigcall() is enough
*/
debug_action_sigcall(a,"\n");
debug_action_sigcall(a,
"\n======================================================\n");
debug_action_sigcall(a,"Class: %.10s File: %s\n",
a->class,a->file);
debug_action_sigcall(a," : %s\n",a->ID);
debug_action_sigcall(a,
"======================================================\n");
}
return;
}
}
}
/* If user initialized, mark that debug_level_check() need
not recalled */
if ('\0' != home[0])
a->active = 0;
}
/* can be called from signal handler!!
RACE condition: May lose characters
*/
static int flush_debug_buffer(a)
struct debug_target *a;
{
if (a->debug_fd != -1) {
int X = write(a->debug_fd,a->buffer,a->buffer_ptr);
if (X > 0) {
int len = a->buffer_ptr;
len -= X;
if (len > 0)
memmove(a->buffer,a->buffer+X,len);
if (len >= 0)
a->buffer_ptr = len;
else
a->buffer_ptr = 0;
return 1;
}
}
return 0;
}
#define INC(p) { (p)++; if (!*(p)) break; }
/* RACE condition:
May lose some characters when called from signal handler
*/
static void PUTC P_((struct debug_target *a, int c));
#if __GNUC__
__inline__
#endif
static void PUTC(a,c)
struct debug_target *a;
int c;
{
int x;
if (a->buffer_ptr >= sizeof a->buffer)
if (!flush_debug_buffer(a))
return; /* Buffer full -- can't flush */
x = a->buffer_ptr++;
if (x < sizeof a->buffer) {
a->buffer[x] = c;
if ('\n' == c) flush_debug_buffer(a);
}
}
static void print_prefix P_((struct debug_struct *a));
static void print_prefix(a)
struct debug_struct *a;
{
/* Prefix with file name */
if (0 == a->target->buffer_ptr ||
a->target->buffer_ptr > 0 &&
a->target->buffer_ptr < sizeof (a->target->buffer) &&
'\n' == a->target->buffer[a->target->buffer_ptr-1]) {
int i;
for (i = 0; i < 10 && a->file[i]; i++) {
PUTC(a->target,a->file[i]);
}
for (;i < 10; i++) {
PUTC(a->target,' ');
}
PUTC(a->target,':');
PUTC(a->target,' ');
}
}
#define SKIP_TO_ARG(a,s) \
if (*s != '%') { PUTC(a->target,*s); \
if ('\n' == *s && '\0' != *(s+1)) { print_prefix(a); } \
continue; } \
INC(s); \
if ('%' == *s) { PUTC(a->target,*s); continue; }
#define SKIP_WIDTH(a,s) \
if ('0' == *s) { INC(s); } \
else if ('-' == *s) { INC(s); } \
else if ('+' == *s) { INC(s); } \
if (*s >= '1' && *s <= '9') { \
while (*s >= '0' && *s <= '9') { INC(s); } \
if ('$' == *s) { PUTC(a,'?'); break; } \
} else if ('*' == *s) { INC(s); } \
if ('.' == *s) { \
INC(s); \
if (*s >= '1' && *s <= '9') { \
while (*s >= '0' && *s <= '9') { INC(s); } \
} else if ('*' == *s) { INC(s); } \
} \
if ('l' == *s) { INC(s); }
#define MAX_ARGS 5
#define MAX_WIDTH 128
/* This may called from signal handler, so do not malloc
memory on here!
*/
static int handle_common P_((struct debug_target *target,
struct format_elem *elems, int pos));
static int handle_common(target,elems,pos)
struct debug_target *target;
struct format_elem *elems;
int pos;
{
char buffer[MAX_WIDTH];
int len;
switch (elems[pos].format_chr) {
int l;
char *str, *a;
int quote;
case 'c':
if (V_chr_val != elems[pos].type) {
PUTC(target,'?');
PUTC(target,'t');
PUTC(target,'?');
return 0;
}
l = 1;
while (l < elems[pos].val1 && !elems[pos].left) {
PUTC(target,elems[pos].fill); l++;
}
PUTC(target,elems[pos].value.chr_val);
while (l < elems[pos].val1 && elems[pos].left) {
PUTC(target,' '); l++;
}
return 1;
case 'p':
case 'd': case'i': case 'x': case 'X': case 'o': case 'u':
case 'f':
len = convert_number(buffer, sizeof buffer, &(elems[pos]));
if (0 == len) {
PUTC(target,'?');
PUTC(target,'l');
PUTC(target,'?');
return 0;
}
for (l = 0; l < len; l++) {
PUTC(target,buffer[l]);
}
return 1;
case 's': case 'Q':
if (V_str_val != elems[pos].type) {
PUTC(target,'?');
return 0;
}
str = elems[pos].value.str_val;
quote = ('Q' == elems[pos].format_chr);
if (!str) {
PUTC(target,'?');
PUTC(target,'t');
PUTC(target,'?');
return 0;
}
l = strlen(str);
if (quote) {
l += 2;
for (a=str; *a; a++) {
if (*a == '\\' || *a == '"')
l++;
}
}
if (elems[pos].val2 <= 0)
elems[pos].val2 = l;
if (quote)
elems[pos].val2 -= 2;
while (l < elems[pos].val1 && !elems[pos].left) {
PUTC(target,elems[pos].fill); l++;
}
if (quote) {
PUTC(target,'"');
}
for (a = str; *a && a - str < elems[pos].val2; a++) {
if (quote && (*a == '\\' || *a == '"')) {
PUTC(target,'\\');
}
PUTC(target,*a);
}
if (quote) {
PUTC(target,'"');
}
while (l < elems[pos].val1 && elems[pos].left) {
PUTC(target,' '); l++;
}
return 1;
}
return 0;
}
/* Must NOT be called from signal handler ... */
void debug_action_call(
#if ANSI_C
struct debug_struct *a, const char * format, ...
#else
a, format, va_alist
#endif
)
#if !ANSI_C
struct debug_struct *a;
CONST char * format;
va_dcl
#endif
{
int count, x, max_elems;
char * format_error = NULL;
int pos;
struct format_elem A[MAX_ARGS], *elems;
CONST char * s;
va_list vl;
if (!a->target)
return;
print_prefix(a);
Va_start(vl, format); /* defined in defs.h */
x = 0;
for (s = format; *s; s++)
if ('%' == *s && '%' != *(s+1))
x++;
if (x > MAX_ARGS &&
/* Allow failure of allocation! */
NULL != (elems = malloc(x * sizeof (*elems))))
max_elems = x;
else {
elems = A;
max_elems = MAX_ARGS;
}
count = parse_format_args (elems,max_elems, format,vl, &format_error);
if (format_error) {
PUTC(a->target,'?');
PUTC(a->target,'\n');
for (s = format_error; *s; s++) {
PUTC(a->target,*s);
}
PUTC(a->target,'\n');
}
pos = -1;
for (s = format; *s; s++) {
SKIP_TO_ARG(a,s);
pos++;
SKIP_WIDTH(a->target,s);
if (pos < 0 || pos >= count) {
PUTC(a->target,'?');
PUTC(a->target,'O');
PUTC(a->target,'?');
break;
}
if (elems[pos].format_chr != *s) {
PUTC(a->target,'?');
PUTC(a->target,'f');
PUTC(a->target,'?');
break;
}
if (!handle_common(a->target,elems,pos))
switch(*s) {
struct string * S, *S1;
struct charset_state *ch;
char *temp;
int l,X;
char *p;
case 'C':
if (V_cs_val != elems[pos].type) {
PUTC(a->target,'?');
break;
}
ch = elems[pos].value.cs_val;
S = new_string(display_charset);
add_state_to_string(S,ch);
temp = us2s(stream_from_string(S,1,NULL));
l = 1;
while (l < elems[pos].val1 && !elems[pos].left) {
PUTC(a->target,elems[pos].fill); l++;
}
for (p=temp; *p; p++) {
PUTC(a->target,*p);
}
while (l < elems[pos].val1 && elems[pos].left) {
PUTC(a->target,' '); l++;
}
free(temp);
free_string(&S);
break;
case 'S':
if (V_string_val != elems[pos].type) {
PUTC(a->target,'?');
break;
}
S = elems[pos].value.string_val;
if (!S) {
PUTC(a->target,'?');
break;
}
if (!verify_string(S)) {
PUTC(a->target,'?');
break;
}
S1 = convert_string(display_charset,S,1);
if (!verify_string(S1)) {
PUTC(a->target,'?');
break;
}
l = string_len(S1);
if (elems[pos].val2 <= 0)
elems[pos].val2 = l;
X = 0;
temp = us2s(streamclip_from_string(S1,&X,elems[pos].val2,
NULL,NULL)); /* TODO: Should use printable_len ? */
while (l < elems[pos].val1 && !elems[pos].left) {
PUTC(a->target,elems[pos].fill); l++;
}
for (p=temp; *p; p++) {
PUTC(a->target,*p);
}
free(temp);
free_string(&S1);
while (l < elems[pos].val1 && elems[pos].left) {
PUTC(a->target,' '); l++;
}
break;
default:
PUTC(a->target,'?');
PUTC(a->target,'u');
PUTC(a->target,'?');
break;
}
}
if (elems != A) {
free(elems);
elems = NULL;
}
va_end(vl);
}
/* resulting FILE * can be fclosed after use */
FILE * debug_to_FILE(a)
struct debug_struct *a;
{
if (!a->target)
return NULL;
flush_debug_buffer(a->target);
if (a->target->debug_fd != -1) {
int fd = dup(a->target->debug_fd);
return fdopen(fd,"w");
}
return NULL;
}
/* This IS called from signal handler */
void debug_action_sigcall(
#if ANSI_C
struct debug_struct *a, const char * format, ...
#else
a, format, va_alist
#endif
)
#if !ANSI_C
struct debug_struct *a;
CONST char * format;
va_dcl
#endif
{
int count, pos;
char * format_error = NULL;
CONST char *s;
struct format_elem A[MAX_ARGS];
va_list vl;
if (!a->target)
return;
print_prefix(a);
Va_start(vl, format); /* defined in defs.h */
count = parse_format_args (A,MAX_ARGS, format,vl, &format_error);
if (format_error) {
PUTC(a->target,'?');
PUTC(a->target,'\n');
for (s = format_error; *s; s++) {
PUTC(a->target,*s);
}
PUTC(a->target,'\n');
}
pos = -1;
for (s = format; *s; s++) {
SKIP_TO_ARG(a,s);
pos++;
SKIP_WIDTH(a->target,s);
if (pos < 0 || pos >= count) {
PUTC(a->target,'?');
PUTC(a->target,'O');
PUTC(a->target,'?');
break;
}
if (A[pos].format_chr != *s) {
PUTC(a->target,'?');
PUTC(a->target,'f');
PUTC(a->target,'?');
break;
}
if (!handle_common(a->target,A,pos)) {
PUTC(a->target,'?');
PUTC(a->target,'u');
PUTC(a->target,'?');
}
}
va_end(vl);
}
/* This IS called from signal handler -- used on panic(),
returns largest debug_level
*/
int panic_dprint(
#if ANSI_C
const char * format, ...
#else
format, va_alist
#endif
)
#if !ANSI_C
CONST char * format;
va_dcl
#endif
{
int level = 0;
int count;
char * format_error = NULL;
struct format_elem A[MAX_ARGS];
struct debug_target *ptr;
va_list vl;
Va_start(vl, format); /* defined in defs.h */
count = parse_format_args (A,MAX_ARGS, format,vl, &format_error);
for (ptr = debug_list; ptr; ptr= ptr-> next) {
int i;
CONST char *s;
int pos;
DEBUG_VAR(temp,__FILE__,"");
temp.target = ptr;
for (i = 0; i < ptr->match_count; i++) {
if (ptr->match[i].level > level)
level = ptr->match[i].level;
}
if (format_error) {
PUTC(ptr,'?');
PUTC(ptr,'\n');
for (s = format_error; *s; s++) {
PUTC(ptr,*s);
}
PUTC(ptr,'\n');
}
pos = -1;
for (s = format; *s; s++) {
SKIP_TO_ARG((&temp),s);
pos++;
SKIP_WIDTH(ptr,s);
if (pos < 0 || pos >= count) {
PUTC(ptr,'?');
PUTC(ptr,'o');
PUTC(ptr,'?');
break;
}
if (A[pos].format_chr != *s) {
PUTC(ptr,'?');
PUTC(ptr,'f');
PUTC(ptr,'?');
break;
}
if (!handle_common(ptr,A,pos)) {
PUTC(ptr,'?');
PUTC(ptr,'u');
PUTC(ptr,'?');
}
}
}
va_end(vl);
return level;
}
/*
* Local Variables:
* mode:c
* c-basic-offset:4
* buffer-file-coding-system: iso-8859-1
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1