/*
 * Copyright (c) 2002, Peter Bentley
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 *   Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 
 *   Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 *   The name Peter Bentley may not be used to endorse or promote
 * products derived from this software without specific prior
 * written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * NOTE: The implementation in this file is a crock, just to get
 * the other stuff up and running.  Needs a complete rewrite.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "slog.h"

CVSID( "$Id: slog.c,v 1.9 2003/04/08 19:07:29 pete Exp $" );

/*
 * Private names for log levels (XXX: I11N)
 */
static const char *levelname[] = {
    "DETAIL",
    "TRACE",
    "DEBUG",
    "INFO",
    "WARNING",
    "ERROR",
    "FATAL",
};


void out_default( int level, const char *msg );

/*
 * Private structure for a log handler
 */
struct log_handler {
    int			level;
    slog_handler	outfunc;
    struct log_handler	*next;
};

/*
 * Linked list end marker
 */
static struct log_handler list_end = {
    0,
    NULL,
    &list_end,
};

/*
 * log_handler for default output handler
 */
static struct log_handler default_handler = {
    LOG_DEBUG,
    out_default,
    &list_end,
};

/*
 * Handler list - just default_handler which points to list_end
 */
static struct log_handler *handlers = &default_handler;


/*****************************************************************************
 * Get the first handler in the chain
 */

void *slog_get_handler()
{
    return (void *) handlers;
}


/*****************************************************************************
 * Set the level of a handler
 */

void slog_set_handler_level( void *handler, int level )
{
    struct log_handler *h = (struct log_handler *) handler;

    if( level >= 0 && level <= LOG_MAX )
    {
	h->level = level;
    }
}

/*****************************************************************************
 * Replace handler chain with a single handler
 */

void *slog_set_handler( int level, slog_handler handler )
{
    /* XXX free this somewhere */
    struct log_handler *h = malloc( sizeof( sizeof( struct log_handler )));
    if( h )
    {
        h->level = level;
        h->outfunc = handler;
        h->next = &list_end;
        
        handlers = h;
    }
    return h;
}


/*****************************************************************************
 * Add a new handler on the end of a chain 
 */
void *slog_add_handler( int level, slog_handler handler )
{
    /* XXX free this somewhere */
    struct log_handler *h = malloc( sizeof( sizeof( struct log_handler )));
    if( h )
    {
        h->level = level;
        h->outfunc = handler;
        h->next = handlers;
        
        handlers = h;
    }
    return h;
}

/*****************************************************************************
 * slog() - actually log something
 */

#define MAXLOG 512	/* Nasty hard coded max */
void slog( int level, const char *fmt, ... )
{
    struct log_handler *handler;
    char	buffer[ MAXLOG + 1 ];
    va_list	args;
    int		len, max;

    
    if( level >= 0 && level <= LOG_MAX )
    {
	sprintf( buffer, "%s: ", levelname[level] );
	len = strlen( buffer );
	max = MAXLOG - len;
	
	va_start( args, fmt );
	if( vsnprintf( buffer + len, max, fmt, args ) >= MAXLOG )
	{
	    buffer[MAXLOG] = '\0';
	    slog( LOG_WARNING, "Overly long log message - truncated!" );
	}

	for( handler = handlers; handler != &list_end; handler = handler->next)
	{
	    if( handler->level <= level )
	    {
		handler->outfunc( level, buffer );
	    }
	}
    }
    else
    {
	slog( LOG_WARNING, "Log message with illegal level %d", level );
    }
}


/*****************************************************************************
 * slog_hex
 */

void slog_hex( int level, const char *prefix,
	       const unsigned char *data, int size )
{
    struct log_handler *handler;
    int i, rowsize, offset;
    char *bufpos, buffer[128];	/* XXX handle buffer overflow */
  
    for( offset = 0; size;  ) {
	rowsize = ( size > 16 ) ? 16 : size;
	sprintf( buffer, "%s%04x: ", prefix, offset );
	bufpos = buffer + strlen( buffer );
	
	for( i = 0; i < rowsize; i++ )
	{
	    sprintf( bufpos, "%02x ", data[i] );
	    bufpos += 3;
	}

	for( ; i < 16; i++ )
	{
	    strcat( buffer, "   " );
	}

	strcat( buffer, "  " );
	bufpos = buffer + strlen( buffer );
	for( i = 0; i < rowsize; i++, bufpos++ )
	{
	    sprintf( bufpos, "%c", isprint( data[i] ) ? data[i] : '.' );
	}

	size -= rowsize;
	data += rowsize;
	offset += rowsize;

	for( handler = handlers; handler != &list_end; handler = handler->next)
	{
	    if( handler->level <= level )
	    {
		handler->outfunc( level, buffer );
	    }
	}
    }
}


/*****************************************************************************
 * Default output handler - just print to stderr
 */

void out_default( int level, const char *msg ) {

    fprintf( stderr, "%s\n", msg );
}

