root/sbin/ipf/libipf/var.c

/*
 * Copyright (C) 2012 by Darren Reed.
 *
 * See the IPFILTER.LICENCE file for details on licencing.
 *
 * $Id$
 */

#include <ctype.h>

#include "ipf.h"

typedef struct  variable        {
        struct  variable        *v_next;
        char    *v_name;
        char    *v_value;
} variable_t;

static  variable_t      *vtop = NULL;

static variable_t *find_var(char *);
static char *expand_string(char *, int);


static variable_t *find_var(char *name)
{
        variable_t *v;

        for (v = vtop; v != NULL; v = v->v_next)
                if (!strcmp(name, v->v_name))
                        return (v);
        return (NULL);
}


char *get_variable(char *string, char **after, int line)
{
        char c, *s, *t, *value;
        variable_t *v;

        s = string;

        if (*s == '{') {
                s++;
                for (t = s; *t != '\0'; t++)
                        if (*t == '}')
                                break;
                if (*t == '\0') {
                        fprintf(stderr, "%d: { without }\n", line);
                        return (NULL);
                }
        } else if (ISALPHA(*s)) {
                for (t = s + 1; *t != '\0'; t++)
                        if (!ISALPHA(*t) && !ISDIGIT(*t) && (*t != '_'))
                                break;
        } else {
                fprintf(stderr, "%d: variables cannot start with '%c'\n",
                        line, *s);
                return (NULL);
        }

        if (after != NULL)
                *after = t;
        c = *t;
        *t = '\0';
        v = find_var(s);
        *t = c;
        if (v == NULL) {
                fprintf(stderr, "%d: unknown variable '%s'\n", line, s);
                return (NULL);
        }

        s = strdup(v->v_value);
        value = expand_string(s, line);
        if (value != s)
                free(s);
        return (value);
}


static char *expand_string(char *oldstring, int line)
{
        char c, *s, *p1, *p2, *p3, *newstring, *value;
        int len;

        p3 = NULL;
        newstring = oldstring;

        for (s = oldstring; *s != '\0'; s++)
                if (*s == '$') {
                        *s = '\0';
                        s++;

                        switch (*s)
                        {
                        case '$' :
                                bcopy(s, s - 1, strlen(s));
                                break;
                        default :
                                c = *s;
                                if (c == '\0')
                                        return (newstring);

                                value = get_variable(s, &p3, line);
                                if (value == NULL)
                                        return (NULL);

                                p2 = expand_string(value, line);
                                if (p2 == NULL)
                                        return (NULL);

                                len = strlen(newstring) + strlen(p2);
                                if (p3 != NULL) {
                                        if (c == '{' && *p3 == '}')
                                                p3++;
                                        len += strlen(p3);
                                }
                                p1 = malloc(len + 1);
                                if (p1 == NULL)
                                        return (NULL);

                                *(s - 1) = '\0';
                                strcpy(p1, newstring);
                                strcat(p1, p2);
                                if (p3 != NULL)
                                        strcat(p1, p3);

                                s = p1 + len - strlen(p3) - 1;
                                if (newstring != oldstring)
                                        free(newstring);
                                newstring = p1;
                                break;
                        }
                }
        return (newstring);
}


void set_variable(char *name, char *value)
{
        variable_t *v;
        int len;

        if (name == NULL || value == NULL || *name == '\0')
                return;

        v = find_var(name);
        if (v != NULL) {
                free(v->v_value);
                v->v_value = strdup(value);
                return;
        }

        len = strlen(value);

        if ((*value == '"' && value[len - 1] == '"') ||
            (*value == '\'' && value[len - 1] == '\'')) {
                value[len - 1] = '\0';
                value++;
                len -=2;
        }

        v = (variable_t *)malloc(sizeof(*v));
        if (v == NULL)
                return;
        v->v_name = strdup(name);
        v->v_value = strdup(value);
        v->v_next = vtop;
        vtop = v;
}