/*
 * 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;
}