/*
**  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_main.c: main API
*/

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

#include "cfg.h"
#include "cfg_global.h"
#include "cfg_fmt.h"
#include "cfg_main.h"
#include "cfg_data.h"
#include "cfg_node.h"
#include "cfg_syn.h"
#define _CFG_VERS_C_AS_HEADER_
#include "cfg_vers.c"
#undef  _CFG_VERS_C_AS_HEADER_

long cfg_version(void)
{
    return __cfg_version.v_hex;
}

cfg_rc_t cfg_create(cfg_t **cfg)
{
    cfg_rc_t rc;

    if (cfg == NULL)
        return CFG_ERR_ARG;
    if ((*cfg = malloc(sizeof(cfg_t))) == NULL)
        return CFG_ERR_SYS;
    (*cfg)->root = NULL;
    (*cfg)->szError[0]     = '\0';
    (*cfg)->szErrorInfo[0] = '\0';
    (*cfg)->rcErrorInfo    = CFG_OK;
    if ((rc = cfg_grid_create(&((*cfg)->grid_nodes), sizeof(cfg_node_t), 512)) != CFG_OK) {
        free(*cfg);
        return rc;
    }
    if ((rc = cfg_grid_create(&((*cfg)->grid_tokens), 8192, 1)) != CFG_OK) {
        free(*cfg);
        cfg_grid_destroy((*cfg)->grid_nodes);
        return rc;
    }
    return CFG_OK;
}

cfg_rc_t cfg_destroy(cfg_t *cfg)
{
    if (cfg == NULL)
        return CFG_ERR_ARG;
    /* FIXME: on each node, free token string first */
    cfg_grid_destroy(cfg->grid_nodes);
    cfg_grid_destroy(cfg->grid_tokens);
    free(cfg);
    return CFG_OK;
}

cfg_rc_t cfg_error_info(cfg_t *cfg, cfg_rc_t rv, const char *fmt, ...)
{
    va_list ap;

    /* argument sanity check */
    if (cfg == NULL || rv == CFG_OK || fmt == NULL)
        return CFG_ERR_ARG;

    /* remember error information */
    va_start(ap, fmt);
    cfg_fmt_vsprintf(cfg->szErrorInfo, sizeof(cfg->szErrorInfo), fmt, ap);
    cfg->rcErrorInfo = rv;
    va_end(ap);

    return CFG_OK;
}

cfg_rc_t cfg_error(cfg_t *cfg, cfg_rc_t rv, char **error)
{
    char *sz;
    char *cpBuf;
    int nBuf;
    int n;

    /* argument sanity check */
    if (cfg == NULL || error == NULL)
        return CFG_ERR_ARG;

    /* start at begin of buffer */
    cpBuf = cfg->szError;
    nBuf  = sizeof(cfg->szError);

    /* translate result value into corresponding string */
    if      (rv == CFG_OK)      sz = "everything ok";
    else if (rv == CFG_ERR_ARG) sz = "invalid argument";
    else if (rv == CFG_ERR_USE) sz = "invalid use";
    else if (rv == CFG_ERR_MEM) sz = "no more memory available";
    else if (rv == CFG_ERR_SYS) sz = "operating system error";
    else if (rv == CFG_ERR_FMT) sz = "formatting error";
    else if (rv == CFG_ERR_INT) sz = "internal error";
    else if (rv == CFG_ERR_SYN) sz = "syntax error";
    else if (rv == CFG_ERR_NDE) sz = "node reference error";
    else                        sz = "unknown error";
    n = cfg_fmt_sprintf(cpBuf, nBuf, "%s", sz);
    cpBuf += n;
    nBuf  -= n;

    /* optionally annotate with error information */
    if (rv == cfg->rcErrorInfo && cfg->szErrorInfo[0] != '\0') {
        n = cfg_fmt_sprintf(cpBuf, nBuf, "; %s", cfg->szErrorInfo);
        cpBuf += n;
        nBuf  -= n;
    }

    /* optionally annotate with operating system error information */
    if (rv == CFG_ERR_SYS) {
        n = cfg_fmt_sprintf(cpBuf, nBuf, "; %s (%d)", strerror(errno), errno);
        cpBuf += n;
        nBuf  -= n;
    }

    /* return pointer to internal buffer */
    *error = cfg->szError;

    return CFG_OK;
}

cfg_rc_t cfg_import(cfg_t *cfg, cfg_node_t *node, cfg_fmt_t fmt, const char *in_ptr, size_t in_len)
{
    cfg_rc_t rv;
    char error[1024];
    cfg_node_t *root;

    if (cfg == NULL || in_ptr == NULL || in_len == 0)
        return CFG_ERR_ARG;
    if (node == NULL)
        node = cfg->root;
    if (fmt == CFG_FMT_CFG) {
        if ((rv = cfg_syn_import(cfg, &root, in_ptr, in_len, error, sizeof(error))) != CFG_OK) {
            cfg_error_info(cfg, rv, "%s", error);
            return rv;
        }
    }
    /* FIXME: CFG_FMT_XML */
    else
        return CFG_ERR_INT;

    /* FIXME: merge: cfg->root[node] := cfg->root[node] + root */
    cfg->root = root;

    return CFG_OK;
}

cfg_rc_t cfg_export(cfg_t *cfg, cfg_node_t *node, cfg_fmt_t fmt, char **ex_ptr, size_t ex_len)
{
    cfg_rc_t rv;
    char *output;
    size_t n;

    if (cfg == NULL || ex_ptr == NULL)
        return CFG_ERR_ARG;
    if (node == NULL)
        node = cfg->root;
    if (fmt == CFG_FMT_CFG) {
        if ((rv = cfg_syn_export(cfg, node, &output)) != CFG_OK)
            return rv;
    }
    /* FIXME: CFG_FMT_XML */
    else
        return CFG_ERR_INT;

    /* provide output */
    if (ex_len == 0)
        *ex_ptr = output;
    else {
        n = strlen(output);
        if (n >= ex_len)
            return CFG_ERR_MEM;
        memmove(*ex_ptr, output, n+1);
        free(output);
    }

    return CFG_OK;
}

