/*-
 * Copyright (c) 2006 Verdens Gang AS
 * Copyright (c) 2006-2008 Linpro AS
 * All rights reserved.
 *
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS 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 AUTHOR OR 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.
 *
 * $Id: vcc_var.c 3586 2009-02-05 09:17:38Z tfheen $
 */

#include "config.h"

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

#include "vsb.h"

#include "vcc_priv.h"
#include "vcc_compile.h"
#include "libvarnish.h"

/*--------------------------------------------------------------------*/

static struct var *
HeaderVar(struct tokenlist *tl, const struct token *t, const struct var *vh)
{
	char *p;
	struct var *v;
	int i;

	(void)tl;

	v = TlAlloc(tl, sizeof *v);
	assert(v != NULL);
	i = t->e - t->b;
	p = TlAlloc(tl, i + 1);
	assert(p != NULL);
	memcpy(p, t->b, i);
	p[i] = '\0';
	v->name = p;
	v->access = V_RW;
	v->fmt = STRING;
	v->hdr = vh->hdr;
	v->methods = vh->methods;
	asprintf(&p, "VRT_GetHdr(sp, %s, \"\\%03o%s:\")", v->hdr,
	    (unsigned)(strlen(v->name + vh->len) + 1), v->name + vh->len);
	AN(p);
	TlFree(tl, p);
	v->rname = p;
	asprintf(&p, "VRT_SetHdr(sp, %s, \"\\%03o%s:\", ", v->hdr,
	    (unsigned)(strlen(v->name + vh->len) + 1), v->name + vh->len);
	AN(p);
	TlFree(tl, p);
	v->lname = p;
	return (v);
}

/*--------------------------------------------------------------------*/

/* Here, the hdr is not really the header but just the arg to pmatch */

static struct var *
PmatchVar(struct tokenlist *tl, const struct token *t, const struct var *vh)
{
	char *p;
	struct var *v;
	int i;
	long npmatch;
	struct procpm *pm;

	(void)tl;

	v = TlAlloc(tl, sizeof *v);
	assert(v != NULL);
	i = t->e - t->b;
	p = TlAlloc(tl, i + 1);
	assert(p != NULL);
	memcpy(p, t->b, i);
	p[i] = '\0';
	v->name = p;
	v->access = V_RO;
	v->fmt = STRING;
	v->hdr = vh->hdr;
	v->methods = vh->methods;

	errno = 0;
	npmatch = strtol(v->name + vh->len, NULL, 10);

	if (errno) {
		vsb_printf(tl->sb, "pmatch backreference must be numerical ");
		goto err;
	}
	if (npmatch > INT_MAX) {
		vsb_printf(tl->sb, "pmatch backreference is too long a value ");
		goto err;
	}

	asprintf(&p, "VRT_r_pmatch(sp, &local_pmatch, %d)", (int)npmatch);
	AN(p);
	TlFree(tl, p);
	v->rname = p;

	/*
	 * if there's no pm, this is a pmatch reference with no preceeding match
	 * operator
	 */
	   
	pm = VTAILQ_LAST(&tl->curproc->pmatches, procpmh);
	if (pm && (npmatch > pm->max_pmatch))
		pm->max_pmatch = npmatch;

	return (v);
	
  err:
	vcc_ErrToken(tl, t);
	vsb_cat(tl->sb, "\nAt: ");
	vcc_ErrWhere(tl, t);
	return (NULL);
}


/*--------------------------------------------------------------------*/

struct var *
vcc_FindVar(struct tokenlist *tl, const struct token *t, struct var *vl)
{
	struct var *v;

	for (v = vl; v->name != NULL; v++) {
		int isspecial = ((v->fmt == HEADER) || (v->fmt == PMATCH));

		if (isspecial  && (t->e - t->b) <= v->len)
			continue;
		if (!isspecial && t->e - t->b != v->len)
			continue;
		if (memcmp(t->b, v->name, v->len))
			continue;
		vcc_AddUses(tl, v);
		if (!isspecial)
			return (v);
		switch (v->fmt) {
		case HEADER:
			return (HeaderVar(tl, t, v));
		case PMATCH:
			return (PmatchVar(tl, t, v));
		default:
			break;
		}
	}
	vsb_printf(tl->sb, "Unknown variable ");
	vcc_ErrToken(tl, t);
	vsb_cat(tl->sb, "\nAt: ");
	vcc_ErrWhere(tl, t);
	return (NULL);
}

