/*
 * Management of the debugging channels
 *
 * Copyright 2000 Alexandre Julliard
 */

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>

struct dll
{
    struct dll   *next;        /* linked list of dlls */
    struct dll   *prev;
    char * const *channels;    /* array of channels */
    int           nb_channels; /* number of channels in array */
};

static struct dll *first_dll;

struct debug_option
{
    struct debug_option *next;       /* next option in list */
    unsigned char        set;        /* bits to set */
    unsigned char        clear;      /* bits to clear */
    char                 name[14];   /* channel name, or empty for "all" */
};

static struct debug_option *first_option;
static struct debug_option *last_option;


static int cmp_name( const void *p1, const void *p2 )
{
    const char *name = p1;
    const char * const *chan = p2;
    return strcmp( name, *chan + 1 );
}

/* apply a debug option to the channels of a given dll */
static void apply_option( struct dll *dll, const struct debug_option *opt )
{
    if (opt->name[0])
    {
        char **dbch = bsearch( opt->name, dll->channels, dll->nb_channels,
                               sizeof(*dll->channels), cmp_name );
        if (dbch) **dbch = (**dbch & ~opt->clear) | opt->set;
    }
    else /* all */
    {
        int i;
        for (i = 0; i < dll->nb_channels; i++)
            dll->channels[i][0] = (dll->channels[i][0] & ~opt->clear) | opt->set;
    }
}

/* register a new set of channels for a dll */
void *__wine_dbg_register( char * const *channels, int nb )
{
    struct debug_option *opt = first_option;
    struct dll *dll = malloc( sizeof(*dll) );
    if (dll)
    {
        dll->channels = channels;
        dll->nb_channels = nb;
        dll->prev = NULL;
        if ((dll->next = first_dll)) dll->next->prev = dll;
        first_dll = dll;

        /* apply existing options to this dll */
        while (opt)
        {
            apply_option( dll, opt );
            opt = opt->next;
        }
    }
    return dll;
}


/* unregister a set of channels; must pass the pointer obtained from wine_dbg_register */
void __wine_dbg_unregister( void *channel )
{
    struct dll *dll = channel;
    if (dll)
    {
        if (dll->next) dll->next->prev = dll->prev;
        if (dll->prev) dll->prev->next = dll->next;
        else first_dll = dll->next;
        free( dll );
    }
}


/* add a new debug option at the end of the option list */
void wine_dbg_add_option( const char *name, unsigned char set, unsigned char clear )
{
    struct dll *dll = first_dll;
    struct debug_option *opt;

    if (!(opt = malloc( sizeof(*opt) ))) return;
    opt->next  = NULL;
    opt->set   = set;
    opt->clear = clear;
    strncpy( opt->name, name, sizeof(opt->name) );
    opt->name[sizeof(opt->name)-1] = 0;
    if (last_option) last_option->next = opt;
    else first_option = opt;
    last_option = opt;

    /* apply option to all existing dlls */
    while (dll)
    {
        apply_option( dll, opt );
        dll = dll->next;
    }
}
