/*
**  OSSP var -- Variable Expansion
**  Copyright (c) 2001-2004 Ralf S. Engelschall <rse@engelschall.com>
**  Copyright (c) 2001-2004 The OSSP Project (http://www.ossp.org/)
**  Copyright (c) 2001-2004 Cable & Wireless (http://www.cw.com/)
**
**  This file is part of OSSP var, a variable expansion
**  library which can be found at http://www.ossp.org/pkg/lib/var/.
**
**  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.
**
**  var_play.c: library command line playing tool
*/

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

#include "var.h"

static var_rc_t lookup(
    var_t *var, void *ctx,
    const char  *var_ptr, size_t  var_len, int     var_idx,
    const char **val_ptr, size_t *val_len, size_t *val_size)
{
    char tmp[256];

    if (var_idx != 0)
        return VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED;
    if (var_len > sizeof(tmp) - 1)
        return VAR_ERR_OUT_OF_MEMORY;
    memcpy(tmp, var_ptr, var_len);
    tmp[var_len] = '\0';
    if ((*val_ptr = getenv(tmp)) == NULL)
        return VAR_ERR_UNDEFINED_VARIABLE;
    *val_len = strlen(*val_ptr);
    *val_size = 0;
    return VAR_OK;
}

static void die(const char *context, var_t *var, var_rc_t rc)
{
    char *error;

    var_strerror(var, rc, &error);
    fprintf(stderr, "ERROR: %s: %s (%d)\n", context, error, rc);
    var_destroy(var);
    exit(1);
}

int main(int argc, char *argv[])
{
    var_t *var;
    var_rc_t rc;
    char *src_ptr;
    char *dst_ptr;
    size_t src_len;
    size_t dst_len;
    var_syntax_t syntax = { '\\', '$', '{', '}', '[', ']', '#', "a-zA-Z0-9_" };

    /* command line handling */
    if (argc != 2)
        die("command line", NULL, VAR_ERR_INVALID_ARGUMENT);
    src_ptr = argv[1];
    src_len = strlen(src_ptr);
    fprintf(stdout, "input:     \"%s\"\n", src_ptr);

    /* establish variable expansion context */
    if ((rc = var_create(&var)) != VAR_OK)
        die("create context", NULL, rc);
    if ((rc = var_config(var, VAR_CONFIG_SYNTAX, &syntax)) != VAR_OK)
        die("configure syntax", var, rc);
    if ((rc = var_config(var, VAR_CONFIG_CB_VALUE, lookup, NULL)) != VAR_OK)
        die("configure callback", var, rc);

    /* unescape known escape sequences (in place) */
    if ((rc = var_unescape(var, src_ptr, src_len, src_ptr, src_len+1, 0)) != VAR_OK)
        die("unescape known escape sequences", var, rc);
    src_len = strlen(src_ptr);
    fprintf(stdout, "unescaped: \"%s\"\n", src_ptr);

    /* expand variable constructs (force expansion) */
    if ((rc = var_expand(var, src_ptr, src_len, &dst_ptr, &dst_len, 1)) != VAR_OK) {
        if (rc != VAR_ERR_INVALID_ARGUMENT && rc != VAR_ERR_OUT_OF_MEMORY) {
            fprintf(stdout, "parsing:   \"%s\"\n", dst_ptr);
            fprintf(stdout, "             %*s\n", dst_len, "^");
        }
        die("variable expansion", var, rc);
    }
    fprintf(stdout, "expanded:  \"%s\"\n", dst_ptr);

    /* unescape new and unknown escape sequences (in place) */
    if ((rc = var_unescape(var, dst_ptr, dst_len, dst_ptr, dst_len+1, 1)) != VAR_OK)
        die("unescape new and unknown escape sequences", var, rc);
    fprintf(stdout, "output:    \"%s\"\n", dst_ptr);
    free(dst_ptr);

    /* destroy variable expansion context */
    if ((rc = var_destroy(var)) != VAR_OK)
        die("destroy context", var, rc);

    return 0;
}

