/*
**  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_buf.c: auto-resizing buffer
*/

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

#include "cfg.h"
#include "cfg_global.h"
#include "cfg_buf.h"
#include "cfg_fmt.h"

struct cfg_buf_st {
    char   *buf_ptr;
    size_t  buf_size;
    size_t  buf_len;
};

cfg_rc_t cfg_buf_create(cfg_buf_t **buf)
{
    if (buf == NULL)
        return CFG_ERR_ARG;
    if ((*buf = (cfg_buf_t *)malloc(sizeof(cfg_buf_t))) == NULL)
        return CFG_ERR_SYS;
    (*buf)->buf_ptr  = NULL;
    (*buf)->buf_len  = 0;
    (*buf)->buf_size = 0;
    return CFG_OK;
}

cfg_rc_t cfg_buf_resize(cfg_buf_t *buf, signed int n)
{
    char *cp;

    if (buf == NULL)
        return CFG_ERR_ARG;
    if (n == 0) {
        /* destroy buffer */
        if (buf->buf_ptr != NULL)
            free(buf->buf_ptr);
        buf->buf_ptr  = NULL;
        buf->buf_len  = 0;
        buf->buf_size = 0;
    }
    else {
        /* shrink or grow buffer */
        if (buf->buf_ptr == NULL) {
            if ((buf->buf_ptr = malloc(n+1)) == NULL)
                return CFG_ERR_SYS;
            buf->buf_size = n+1;
            buf->buf_len  = 0;
        }
        else {
            if ((cp = realloc(buf->buf_ptr, buf->buf_size+n)) == NULL)
                return CFG_ERR_SYS;
            buf->buf_ptr = cp;
            buf->buf_size += n;
            if (buf->buf_len >= buf->buf_size) {
                buf->buf_len = buf->buf_size-1;
                *(buf->buf_ptr + buf->buf_len) = '\0';
            }
        }
    }
    return CFG_OK;
}

cfg_rc_t cfg_buf_format(cfg_buf_t *buf, const char *fmt, ...)
{
    va_list ap;
    cfg_rc_t rc;

    va_start(ap, fmt);
    rc = cfg_buf_vformat(buf, fmt, ap);
    va_end(ap);
    return rc;
}

cfg_rc_t cfg_buf_vformat(cfg_buf_t *buf, const char *fmt, va_list ap)
{
    cfg_rc_t rc;
    int n;

    if (buf == NULL || fmt == NULL)
        return CFG_ERR_ARG;
    if ((n = cfg_fmt_vsprintf(NULL, -1, fmt, ap)) == -1)
        return CFG_ERR_FMT;
    if ((rc = cfg_buf_resize(buf, n)) != CFG_OK)
        return rc;
    if ((n = cfg_fmt_vsprintf(buf->buf_ptr + buf->buf_len,
                              buf->buf_size - buf->buf_len,
                              fmt, ap)) == -1)
        return CFG_ERR_FMT;
    buf->buf_len += n;
    return CFG_OK;
}

cfg_rc_t cfg_buf_content(cfg_buf_t *buf, char **ptr, size_t *len, size_t *size)
{
    if (buf == NULL)
        return CFG_ERR_ARG;
    if (ptr != NULL) {
        *ptr  = buf->buf_ptr;
        buf->buf_ptr = NULL;
    }
    if (len != NULL)
        *len  = buf->buf_len;
    if (size != NULL)
        *size = buf->buf_size;
    return CFG_OK;
}

cfg_rc_t cfg_buf_destroy(cfg_buf_t *buf)
{
    if (buf == NULL)
        return CFG_ERR_ARG;
    if (buf->buf_ptr != NULL)
        free(buf->buf_ptr);
    free(buf);
    return CFG_OK;
}

