/*
**  OSSP cfg - Configuration Parsing
**  Copyright (c) 2002-2003 Ralf S. Engelschall <rse@engelschall.com>
**  Copyright (c) 2002-2003 The OSSP Project (http://www.ossp.org/)
**  Copyright (c) 2002-2003 Cable & Wireless Deutschland (http://www.cw.com/de/)
**
**  This file is part of OSSP cfg, a configuration parsing
**  library which can be found at http://www.ossp.org/pkg/lib/cfg/.
**
**  Permission to use, copy, modify, and distribute this software for
**  any purpose with or without fee is hereby granted, provided that
**  the above copyright notice and this permission notice appear in all
**  copies.
**
**  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 AUTHORS AND COPYRIGHT HOLDERS AND THEIR
**  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.
**
**  cfg_data.c: configuration annotational data
*/

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#include "cfg.h"
#include "cfg_global.h"
#include "cfg_data.h"

cfg_rc_t cfg_data_create(cfg_data_t **data)
{
    cfg_rc_t rc;

    if (data == NULL)
        return CFG_ERR_ARG;
    if ((*data = (cfg_data_t *)malloc(sizeof(cfg_data_t))) == NULL)
        return CFG_ERR_SYS;
    if ((rc = cfg_data_init(*data)) != CFG_OK) {
        free(*data);
        return rc;
    }
    return CFG_OK;
}

cfg_rc_t cfg_data_init(cfg_data_t *data)
{
    if (data == NULL)
        return CFG_ERR_ARG;
    data->type    = CFG_DATA_TYPE_INT;
    data->value.i = 0;
    data->ctrl    = cfg_data_ctrl;
    return CFG_OK;
}

cfg_rc_t cfg_data_kill(cfg_data_t *data)
{
    cfg_rc_t rc;

    if (data == NULL)
        return CFG_ERR_ARG;
    if (data->ctrl != NULL)
        if ((rc = data->ctrl(data, CFG_DATA_CTRL_DESTROY)) != CFG_OK)
            return rc;
    return CFG_OK;
}

cfg_rc_t cfg_data_destroy(cfg_data_t *data)
{
    cfg_rc_t rc;

    if (data == NULL)
        return CFG_ERR_ARG;
    if ((rc = cfg_data_kill(data)) != CFG_OK)
        return rc;
    free(data);
    return CFG_OK;
}

cfg_rc_t cfg_data_copy(cfg_data_t *data, cfg_data_t *copy)
{
    cfg_rc_t rc;

    if (data == NULL || copy == NULL)
        return CFG_ERR_ARG;
    copy->type = data->type;
    copy->ctrl = data->ctrl;
    if ((rc = data->ctrl(data, CFG_DATA_CTRL_CLONE, copy)) != CFG_OK)
        return rc;
    return CFG_OK;
}

cfg_rc_t cfg_data_clone(cfg_data_t *data, cfg_data_t **clone)
{
    cfg_rc_t rc;

    if (data == NULL || clone == NULL)
        return CFG_ERR_ARG;
    if ((*clone = (cfg_data_t *)malloc(sizeof(cfg_data_t))) == NULL)
        return CFG_ERR_SYS;
    if ((rc = cfg_data_copy(data, *clone)) != CFG_OK)
        return rc;
    return CFG_OK;
}

cfg_rc_t cfg_data_ctrl(cfg_data_t *data, cfg_data_ctrl_t ctrl, ...)
{
    cfg_data_t *data2;
    va_list ap;

    va_start(ap, ctrl);
    if (data == NULL)
        return CFG_ERR_ARG;
    if (ctrl == CFG_DATA_CTRL_CLONE) {
        if ((data2 = (cfg_data_t *)va_arg(ap, void *)) == NULL)
            return CFG_ERR_ARG;
        if (data->type == CFG_DATA_TYPE_STR) {
            if (data->value.s != NULL)
                data2->value.s = strdup(data->value.s);
            else
                data2->value.s = NULL;
        }
        else {
            memcpy((void *)&(data2->value),
                   (void *)&(data->value), sizeof(cfg_data_value_t));
        }
    }
    else if (ctrl == CFG_DATA_CTRL_DESTROY) {
        if (data->type == CFG_DATA_TYPE_STR)
            if (data->value.s != NULL)
                free(data->value.s);
    }
    va_end(ap);
    return CFG_OK;
}

cfg_rc_t cfg_data_set(cfg_data_t *data, cfg_data_attr_t id, ...)
{
    va_list ap;

    if (data == NULL)
        return CFG_ERR_ARG;
    va_start(ap, id);
    switch (id) {
        case CFG_DATA_ATTR_TYPE: {
            cfg_data_type_t t = (cfg_data_type_t)va_arg(ap, int);
            if (!(   t == CFG_DATA_TYPE_PTR
                  || t == CFG_DATA_TYPE_STR
                  || t == CFG_DATA_TYPE_INT
                  || t == CFG_DATA_TYPE_FLT))
                return CFG_ERR_ARG;
            data->type = t;
            break;
        }
        case CFG_DATA_ATTR_VALUE: {
            switch (data->type) {
                case CFG_DATA_TYPE_PTR: {
                    data->value.p = (void *)va_arg(ap, void *);
                    break;
                }
                case CFG_DATA_TYPE_STR: {
                    char *s = (char *)va_arg(ap, void *);
                    if (data->value.s != NULL)
                        free(data->value.s);
                    if (s != NULL)
                        s = strdup(s);
                    data->value.s = s;
                    break;
                }
                case CFG_DATA_TYPE_INT: {
                    data->value.i = (int)va_arg(ap, int);
                    break;
                }
                case CFG_DATA_TYPE_FLT: {
                    data->value.f = (double)va_arg(ap, double);
                    break;
                }
            }
            break;
        }
        case CFG_DATA_ATTR_CTRL: {
            cfg_data_cb_t ctrl = (cfg_data_cb_t)va_arg(ap, cfg_data_cb_t);
            data->ctrl = ctrl;
            break;
        }
    }
    va_end(ap);
    return CFG_OK;
}

cfg_rc_t cfg_data_get(cfg_data_t *data, cfg_data_attr_t id, ...)
{
    va_list ap;

    if (data == NULL)
        return CFG_ERR_ARG;
    va_start(ap, id);
    switch (id) {
        case CFG_DATA_ATTR_TYPE: {
            cfg_data_type_t *pt = (cfg_data_type_t *)va_arg(ap, int *);
            if (pt == NULL)
                return CFG_ERR_ARG;
            *pt = data->type;
            break;
        }
        case CFG_DATA_ATTR_VALUE: {
            switch (data->type) {
                case CFG_DATA_TYPE_PTR: {
                    void **p = (void **)va_arg(ap, void *);
                    if (p == NULL)
                        return CFG_ERR_ARG;
                    *p = data->value.p;
                    break;
                }
                case CFG_DATA_TYPE_STR: {
                    char **p = (char **)va_arg(ap, void *);
                    if (p == NULL)
                        return CFG_ERR_ARG;
                    *p = data->value.s;
                    break;
                }
                case CFG_DATA_TYPE_INT: {
                    int *p = (int *)va_arg(ap, void *);
                    if (p == NULL)
                        return CFG_ERR_ARG;
                    *p = data->value.i;
                    break;
                }
                case CFG_DATA_TYPE_FLT: {
                    double *p = (double *)va_arg(ap, void *);
                    if (p == NULL)
                        return CFG_ERR_ARG;
                    *p = data->value.f;
                    break;
                }
            }
            break;
        }
        case CFG_DATA_ATTR_CTRL: {
            cfg_data_cb_t *pcb = (cfg_data_cb_t *)va_arg(ap, void *);
            if (pcb == NULL)
                return CFG_ERR_ARG;
            *pcb = data->ctrl;
            break;
        }
    }
    va_end(ap);
    return CFG_OK;
}

