[master] 00346d0 Add VSL expression parser and executor
Martin Blix Grydeland
martin at varnish-cache.org
Tue Oct 1 14:48:17 CEST 2013
commit 00346d0e7a8c2a20a55f092ddf07189445e2528e
Author: Martin Blix Grydeland <martin at varnish-software.com>
Date: Tue Jul 30 15:29:23 2013 +0200
Add VSL expression parser and executor
This is based on the libvcl parser
diff --git a/bin/varnishtest/tests/l00000.vtc b/bin/varnishtest/tests/l00000.vtc
index 90d9a7a..adf7c8f 100644
--- a/bin/varnishtest/tests/l00000.vtc
+++ b/bin/varnishtest/tests/l00000.vtc
@@ -29,7 +29,7 @@ logexpect l1 -v v1 -g session {
} -start
# Check with a query (this selects only the backend request)
-logexpect l2 -v v1 -g vxid -q "bereq 1001" {
+logexpect l2 -v v1 -g vxid -q "Begin eq 'bereq 1001'" {
expect 0 1002 Begin
expect * = End
} -start
diff --git a/lib/libvarnishapi/Makefile.am b/lib/libvarnishapi/Makefile.am
index ac4fabc..375ab29 100644
--- a/lib/libvarnishapi/Makefile.am
+++ b/lib/libvarnishapi/Makefile.am
@@ -11,6 +11,8 @@ libvarnishapi_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:0:0
libvarnishapi_la_SOURCES = \
vsm_api.h \
vsl_api.h \
+ vxp.h \
+ vxp_tokens.h \
\
../libvarnish/vas.c \
../libvarnish/vav.c \
@@ -31,6 +33,10 @@ libvarnishapi_la_SOURCES = \
vsl_query.c \
vsl.c \
vsc.c \
+ vxp.c \
+ vxp_parse.c \
+ vxp_lexer.c \
+ vxp_fixed_token.c \
libvarnishapi.map
libvarnishapi_la_CFLAGS = \
@@ -43,3 +49,31 @@ libvarnishapi_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libvarnishapi.map
else
libvarnishapi_la_LDFLAGS += -export-symbols-regex '^V'
endif
+
+EXTRA_DIST = \
+ generate.py
+
+BUILT_SOURCES = \
+ vxp_fixed_token.c \
+ vxp_tokens.h
+
+CLEANFILES = \
+ $(builddir)/vxp_fixed_token.c \
+ $(builddir)/vxp_tokens.h
+
+vxp_fixed_token.c vxp_tokens.h: \
+ $(srcdir)/generate.py
+ @PYTHON@ $(srcdir)/generate.py $(srcdir) $(top_builddir)
+
+EXTRA_PROGRAMS = vxp_test
+
+vxp_test_LDADD = @PCRE_LIBS@ \
+ ${RT_LIBS} ${LIBM} ${PTHREAD_LIBS}
+
+vxp_test_CFLAGS = \
+ -DVARNISH_STATE_DIR='"${VARNISH_STATE_DIR}"' \
+ -DVXP_DEBUG
+
+vxp_test_SOURCES = \
+ $(libvarnishapi_la_SOURCES) \
+ vxp_test.c
diff --git a/lib/libvarnishapi/generate.py b/lib/libvarnishapi/generate.py
new file mode 100755
index 0000000..1377f71
--- /dev/null
+++ b/lib/libvarnishapi/generate.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+#-
+# Copyright (c) 2006 Verdens Gang AS
+# Copyright (c) 2006-2013 Varnish Software AS
+# All rights reserved.
+#
+# Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+# Author: Martin Blix Grydeland <martin at varnish-software.com>
+#
+# 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.
+#
+# Generate various .c and .h files for the VSL query expression parser
+# and the interfaces for it.
+
+import sys
+import copy
+
+srcroot = "../.."
+buildroot = "../.."
+if len(sys.argv) == 3:
+ srcroot = sys.argv[1]
+ buildroot = sys.argv[2]
+
+#######################################################################
+# These are our tokens
+
+tokens = {
+ # Numerical comparisons
+ "T_EQ": "==",
+ "T_NEQ": "!=",
+ "T_LEQ": "<=",
+ "T_GEQ": ">=",
+
+ # String comparisons
+ "T_SEQ": "eq",
+ "T_SNEQ": "ne",
+
+ # Regular expression matching
+ "T_NOMATCH": "!~",
+
+ # Boolean operators
+ "T_NOT": "not",
+ "T_AND": "and",
+ "T_OR": "or",
+
+ # Miscellaneous
+ None: "<>~![]{}()",
+
+ # These have handwritten recognizers
+ "VAL": None,
+ "EOI": None,
+}
+
+#######################################################################
+# Emit a function to recognize tokens in a string
+
+def emit_vxp_fixed_token(fo, tokens):
+ recog = list()
+ emit = dict()
+ for i in tokens:
+ j = tokens[i]
+ if (j != None):
+ recog.append(j)
+ emit[j] = i
+
+ recog.sort()
+ rrecog = copy.copy(recog)
+ rrecog.sort(key = lambda x: -len(x))
+
+ fo.write("""
+unsigned
+vxp_fixed_token(const char *p, const char **q)
+{
+
+\tswitch (p[0]) {
+""")
+ last_initial = None
+ for i in recog:
+ if (i[0] == last_initial):
+ continue
+ last_initial = i[0]
+ fo.write("\tcase '%s':\n" % last_initial)
+ for j in rrecog:
+ if (j[0] != last_initial):
+ continue
+
+ fo.write("\t\tif (")
+ k = 1
+ l = len(j)
+ while (k < l):
+ fo.write("p[%d] == '%s'" % (k, j[k]))
+ fo.write(" &&\n\t\t ")
+ k += 1
+ fo.write("(isword(p[%d]) ? !isword(p[%d]) : 1)) {\n" %
+ (l - 1, l))
+ fo.write("\t\t\t*q = p + %d;\n" % l)
+ fo.write("\t\t\treturn (%s);\n" % emit[j])
+ fo.write("\t\t}\n");
+ fo.write("\t\treturn (0);\n")
+
+ fo.write("\tdefault:\n\t\treturn (0);\n\t}\n}\n")
+
+#######################################################################
+# Emit the vxp_tnames (token->string) conversion array
+
+def emit_vxp_tnames(fo, tokens):
+ fo.write("\nconst char * const vxp_tnames[256] = {\n")
+ l = list(tokens.keys())
+ l.sort()
+ for i in l:
+ j = tokens[i]
+ if j == None:
+ j = i
+ if i[0] == "'":
+ j = i
+ fo.write("\t[%s] = \"%s\",\n" % (i, j))
+ fo.write("};\n")
+
+#######################################################################
+
+def polish_tokens(tokens):
+ # Expand single char tokens
+ st = tokens[None]
+ del tokens[None]
+
+ for i in st:
+ tokens["'" + i + "'"] = i
+
+#######################################################################
+
+def file_header(fo):
+ fo.write("""/*
+ * NB: This file is machine generated, DO NOT EDIT!
+ *
+ * Edit and run generate.py instead
+ */
+""")
+
+#######################################################################
+
+polish_tokens(tokens)
+
+fo = open(buildroot + "/lib/libvarnishapi/vxp_tokens.h", "w")
+
+file_header(fo)
+
+j = 128
+l = list(tokens.keys())
+l.sort()
+for i in l:
+ if i[0] == "'":
+ continue
+ fo.write("#define\t%s %d\n" % (i, j))
+ j += 1
+ assert j < 256
+
+fo.close()
+
+#######################################################################
+
+fo = open(buildroot + "/lib/libvarnishapi/vxp_fixed_token.c", "w")
+
+file_header(fo)
+fo.write("""
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include "vxp.h"
+""")
+
+emit_vxp_fixed_token(fo, tokens)
+emit_vxp_tnames(fo, tokens)
+
+fo.close()
diff --git a/lib/libvarnishapi/vsl_query.c b/lib/libvarnishapi/vsl_query.c
index 436d7c9..09554cb 100644
--- a/lib/libvarnishapi/vsl_query.c
+++ b/lib/libvarnishapi/vsl_query.c
@@ -32,42 +32,121 @@
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
+#include <string.h>
#include "vas.h"
#include "miniobj.h"
#include "vre.h"
+#include "vsb.h"
#include "vapi/vsl.h"
#include "vsl_api.h"
+#include "vxp.h"
+
+#define NEEDLESS_RETURN(foo) return(foo)
struct vslq_query {
unsigned magic;
#define VSLQ_QUERY_MAGIC 0x122322A5
- vre_t *regex;
+ struct vex *vex;
};
+static int
+vslq_test(const struct vex *vex, struct VSL_transaction * const ptrans[])
+{
+ struct VSL_transaction *t;
+ int i, reclen, vallen;
+ const char *recdata;
+
+ CHECK_OBJ_NOTNULL(vex, VEX_MAGIC);
+ CHECK_OBJ_NOTNULL(vex->tag, VEX_TAG_MAGIC);
+ CHECK_OBJ_NOTNULL(vex->val, VEX_VAL_MAGIC);
+ AN(vex->val->val_string);
+
+ vallen = strlen(vex->val->val_string);
+ for (t = ptrans[0]; t != NULL; t = *++ptrans) {
+ AZ(VSL_ResetCursor(t->c));
+ while (1) {
+ i = VSL_Next(t->c);
+ if (i < 0)
+ return (i);
+ if (i == 0)
+ break;
+ assert(i == 1);
+ AN(t->c->rec.ptr);
+
+ if (vex->tag->tag != VSL_TAG(t->c->rec.ptr))
+ continue;
+
+ reclen = VSL_LEN(t->c->rec.ptr);
+ recdata = VSL_CDATA(t->c->rec.ptr);
+ if (reclen == vallen &&
+ !strncmp(vex->val->val_string, recdata, reclen))
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+vslq_exec(const struct vex *vex, struct VSL_transaction * const ptrans[])
+{
+ int r;
+
+ CHECK_OBJ_NOTNULL(vex, VEX_MAGIC);
+
+ switch (vex->tok) {
+ case T_OR:
+ AN(vex->a);
+ AN(vex->b);
+ r = vslq_exec(vex->a, ptrans);
+ if (r != 0)
+ return (r);
+ return (vslq_exec(vex->b, ptrans));
+ case T_AND:
+ AN(vex->a);
+ AN(vex->b);
+ r = vslq_exec(vex->a, ptrans);
+ if (r <= 0)
+ return (r);
+ return (vslq_exec(vex->b, ptrans));
+ case T_NOT:
+ AN(vex->a);
+ AZ(vex->b);
+ r = vslq_exec(vex->a, ptrans);
+ if (r < 0)
+ return (r);
+ return (!r);
+ default:
+ return (vslq_test(vex, ptrans));
+ }
+ NEEDLESS_RETURN(0);
+}
+
struct vslq_query *
vslq_newquery(struct VSL_data *vsl, enum VSL_grouping_e grouping,
const char *querystring)
{
- struct vslq_query *query;
- const char *error;
- int pos;
- vre_t *regex;
+ struct vsb *vsb;
+ struct vex *vex;
+ struct vslq_query *query = NULL;
(void)grouping;
AN(querystring);
- regex = VRE_compile(querystring, 0, &error, &pos);
- if (regex == NULL) {
- vsl_diag(vsl, "failed to compile regex at pos %d: %s",
- pos, error);
- return (NULL);
- }
- ALLOC_OBJ(query, VSLQ_QUERY_MAGIC);
- if (query != NULL)
- query->regex = regex;
+ vsb = VSB_new_auto();
+ AN(vsb);
+ vex = vex_New(querystring, vsb);
+ VSB_finish(vsb);
+ if (vex == NULL)
+ vsl_diag(vsl, "Query expression error:\n%s", VSB_data(vsb));
+ else {
+ ALLOC_OBJ(query, VSLQ_QUERY_MAGIC);
+ query->vex = vex;
+ }
+ VSB_delete(vsb);
return (query);
}
@@ -81,45 +160,24 @@ vslq_deletequery(struct vslq_query **pquery)
*pquery = NULL;
CHECK_OBJ_NOTNULL(query, VSLQ_QUERY_MAGIC);
- AN(query->regex);
- VRE_free(&query->regex);
- AZ(query->regex);
+ AN(query->vex);
+ vex_Free(&query->vex);
+ AZ(query->vex);
FREE_OBJ(query);
}
int
-vslq_runquery(const struct vslq_query *query, struct VSL_transaction * const ptrans[])
+vslq_runquery(const struct vslq_query *query,
+ struct VSL_transaction * const ptrans[])
{
struct VSL_transaction *t;
- struct VSL_cursor *c;
- int i, len;
- const char *data;
+ int r;
CHECK_OBJ_NOTNULL(query, VSLQ_QUERY_MAGIC);
- AN(query->regex);
- t = ptrans[0];
- while (t) {
- c = t->c;
- while (1) {
- i = VSL_Next(c);
- if (i == 0)
- break;
- assert(i == 1);
- AN(c->rec.ptr);
- len = VSL_LEN(c->rec.ptr);
- data = VSL_CDATA(c->rec.ptr);
- i = VRE_exec(query->regex, data, len, 0, 0, NULL, 0,
- NULL);
- if (i != VRE_ERROR_NOMATCH) {
- AZ(VSL_ResetCursor(c));
- return (1);
- }
- }
- AZ(VSL_ResetCursor(c));
- t = *++ptrans;
- }
-
- return (0);
+ r = vslq_exec(query->vex, ptrans);
+ for (t = ptrans[0]; t != NULL; t = *++ptrans)
+ AZ(VSL_ResetCursor(t->c));
+ return (r);
}
diff --git a/lib/libvarnishapi/vxp.c b/lib/libvarnishapi/vxp.c
new file mode 100644
index 0000000..fffa550
--- /dev/null
+++ b/lib/libvarnishapi/vxp.c
@@ -0,0 +1,232 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2013 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Martin Blix Grydeland <martin at varnish-software.com>
+ *
+ * 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.
+ *
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "vsb.h"
+#include "vas.h"
+#include "miniobj.h"
+
+#include "vxp.h"
+
+static void
+vxp_ErrToken(const struct vxp *vxp, const struct token *t)
+{
+
+ if (t->tok == EOI)
+ VSB_printf(vxp->sb, "end of input");
+ else
+ VSB_printf(vxp->sb, "'%.*s'", PF(t));
+}
+
+static void
+vxp_Pos(struct vxp *vxp, struct vsb *vsb, const struct token *t, int tokoff)
+{
+ unsigned pos;
+
+ AN(vxp);
+ AN(vsb);
+ AN(t);
+ assert(t->b >= vxp->b);
+ pos = (unsigned)(t->b - vxp->b);
+ if (tokoff > 0)
+ pos += tokoff;
+ VSB_printf(vsb, "(Pos %u)", pos + 1);
+}
+
+static void
+vxp_quote(struct vxp *vxp, const char *b, const char *e, int tokoff)
+{
+ const char *p;
+ char c;
+
+ assert(b <= e);
+ assert(b >= vxp->b);
+ assert(e <= vxp->e);
+ for (p = vxp->b; p < vxp->e; p++) {
+ if (isspace(*p))
+ VSB_bcat(vxp->sb, " ", 1);
+ else
+ VSB_bcat(vxp->sb, p, 1);
+ }
+ VSB_putc(vxp->sb, '\n');
+ for (p = vxp->b; p < vxp->e; p++) {
+ if (p >= b && p < e) {
+ if (p - b == tokoff)
+ c = '^';
+ else
+ c = '#';
+ } else
+ c = '-';
+ VSB_putc(vxp->sb, c);
+ }
+ VSB_putc(vxp->sb, '\n');
+}
+
+void
+vxp_ErrWhere(struct vxp *vxp, const struct token *t, int tokoff)
+{
+
+ AN(vxp);
+ AN(t);
+ vxp_Pos(vxp, vxp->sb, t, tokoff);
+ VSB_putc(vxp->sb, '\n');
+ vxp_quote(vxp, t->b, t->e, tokoff);
+ VSB_putc(vxp->sb, '\n');
+ vxp->err = 1;
+}
+
+void
+vxp_NextToken(struct vxp *vxp)
+{
+
+ AN(vxp->t);
+ vxp->t = VTAILQ_NEXT(vxp->t, list);
+ if (vxp->t == NULL) {
+ VSB_printf(vxp->sb,
+ "Ran out of input, something is missing or"
+ " maybe unbalanced parenthesis\n");
+ vxp->err = 1;
+ }
+}
+
+void
+vxp__Expect(struct vxp *vxp, unsigned tok)
+{
+
+ if (vxp->t->tok == tok)
+ return;
+ VSB_printf(vxp->sb, "Expected %s got ", vxp_tnames[tok]);
+ vxp_ErrToken(vxp, vxp->t);
+ VSB_putc(vxp->sb, ' ');
+ vxp_ErrWhere(vxp, vxp->t, -1);
+}
+
+static void
+vxp_DoFree(struct vxp *vxp, void *p)
+{
+ struct membit *mb;
+
+ mb = calloc(sizeof *mb, 1);
+ AN(mb);
+ mb->ptr = p;
+ VTAILQ_INSERT_TAIL(&vxp->membits, mb, list);
+}
+
+void *
+vxp_Alloc(struct vxp *vxp, unsigned len)
+{
+ void *p;
+
+ p = calloc(len, 1);
+ AN(p);
+ vxp_DoFree(vxp, p);
+ return (p);
+}
+
+static struct vxp *
+vxp_New(struct vsb *sb)
+{
+ struct vxp *vxp;
+
+ AN(sb);
+
+ ALLOC_OBJ(vxp, VXP_MAGIC);
+ AN(vxp);
+ VTAILQ_INIT(&vxp->membits);
+ VTAILQ_INIT(&vxp->tokens);
+ vxp->sb = sb;
+
+ return (vxp);
+}
+
+static void
+vxp_Delete(struct vxp **pvxp)
+{
+ struct vxp *vxp;
+ struct membit *mb;
+
+ AN(pvxp);
+ vxp = *pvxp;
+ *pvxp = NULL;
+ CHECK_OBJ_NOTNULL(vxp, VXP_MAGIC);
+
+ while (!VTAILQ_EMPTY(&vxp->membits)) {
+ mb = VTAILQ_FIRST(&vxp->membits);
+ VTAILQ_REMOVE(&vxp->membits, mb, list);
+ free(mb->ptr);
+ free(mb);
+ }
+
+ FREE_OBJ(vxp);
+}
+
+struct vex *
+vex_New(const char *query, struct vsb *sb)
+{
+ struct vxp *vxp;
+ struct vex *vex;
+
+ AN(query);
+ AN(sb);
+ vxp = vxp_New(sb);
+ vxp->b = query;
+ vxp->e = query + strlen(query);
+
+ vxp_Lexer(vxp);
+
+#ifdef VXP_DEBUG
+ vxp_PrintTokens(vxp);
+#endif
+
+ if (vxp->err) {
+ vxp_Delete(&vxp);
+ AZ(vxp);
+ return (NULL);
+ }
+
+ vex = vxp_Parse(vxp);
+
+#ifdef VXP_DEBUG
+ if (vex != NULL)
+ vex_PrintTree(vex);
+#endif
+
+ vxp_Delete(&vxp);
+ AZ(vxp);
+
+ return (vex);
+}
diff --git a/lib/libvarnishapi/vxp.h b/lib/libvarnishapi/vxp.h
new file mode 100644
index 0000000..9243e0b
--- /dev/null
+++ b/lib/libvarnishapi/vxp.h
@@ -0,0 +1,140 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2013 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Martin Blix Grydeland <martin at varnish-software.com>
+ *
+ * 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.
+ *
+ */
+
+#include <sys/types.h>
+
+#include "vqueue.h"
+#include "vre.h"
+
+#include "vxp_tokens.h"
+
+#define isword(c) \
+ (isalpha(c) || isdigit(c) || (c) == '_' || (c) == '-' || (c) == '.')
+
+#define PF(t) (int)((t)->e - (t)->b), (t)->b
+
+/* From vex_fixed_token.c */
+unsigned vxp_fixed_token(const char *p, const char **q);
+extern const char * const vxp_tnames[256];
+
+struct membit {
+ VTAILQ_ENTRY(membit) list;
+ void *ptr;
+};
+
+struct token {
+ unsigned tok;
+ const char *b;
+ const char *e;
+ VTAILQ_ENTRY(token) list;
+ unsigned cnt;
+ char *dec;
+};
+
+struct vxp {
+ unsigned magic;
+#define VXP_MAGIC 0x59C7F6AC
+
+ const char *src;
+ const char *b;
+ const char *e;
+
+ VTAILQ_HEAD(, token) tokens;
+ VTAILQ_HEAD(, membit) membits;
+ struct token *t;
+
+ struct vsb *sb;
+ int err;
+};
+
+struct vex;
+
+struct vex_tag {
+ unsigned magic;
+#define VEX_TAG_MAGIC 0x1AD3D78D
+ int tag;
+ int field;
+ int level_min;
+ int level_max;
+};
+
+enum vex_val_e {
+ VEX__UNSET,
+ VEX_INT,
+ VEX_FLOAT,
+ VEX_STRING,
+ VEX_REGEX,
+};
+
+struct vex_val {
+ unsigned magic;
+#define VEX_VAL_MAGIC 0x3F109965
+ enum vex_val_e type;
+ long long val_int;
+ double val_float;
+ char *val_string;
+ vre_t *val_regex;
+};
+
+struct vex {
+ unsigned magic;
+#define VEX_MAGIC 0xC7DB792D
+ unsigned tok;
+ struct vex *a, *b;
+ struct vex_tag *tag;
+ struct vex_val *val;
+};
+
+/* VXP internals */
+
+#define ERRCHK(tl) do { if ((tl)->err) return; } while (0)
+#define Expect(a, b) vxp__Expect(a, b)
+#define ExpectErr(a, b) \
+ do { vxp__Expect(a, b); ERRCHK(a); } while (0)
+#define SkipToken(a, b) \
+ do { vxp__Expect(a, b); ERRCHK(a); vxp_NextToken(a); } while (0)
+
+void vxp__Expect(struct vxp *vxp, unsigned tok);
+void vxp_ErrWhere(struct vxp *vxp, const struct token *t, int tokoff);
+void vxp_NextToken(struct vxp *vxp);
+void * vxp_Alloc(struct vxp *vxp, unsigned len);
+void vxp_Lexer(struct vxp *vxp);
+struct vex * vxp_Parse(struct vxp *vxp);
+
+/* API internal interface */
+
+struct vex * vex_New(const char *query, struct vsb *sb);
+void vex_Free(struct vex **pvex);
+
+/* Debug routines */
+#ifdef VXP_DEBUG
+void vxp_PrintTokens(const struct vxp *vxp);
+void vex_PrintTree(const struct vex *vex);
+#endif /* VXP_DEBUG */
diff --git a/lib/libvarnishapi/vxp_lexer.c b/lib/libvarnishapi/vxp_lexer.c
new file mode 100644
index 0000000..076edaa
--- /dev/null
+++ b/lib/libvarnishapi/vxp_lexer.c
@@ -0,0 +1,191 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2013 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Martin Blix Grydeland <martin at varnish-software.com>
+ *
+ * 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.
+ *
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "vsb.h"
+#include "vas.h"
+
+#include "vxp.h"
+
+static void
+vxp_add_token(struct vxp *vxp, unsigned tok, const char *b, const char *e)
+{
+ struct token *t;
+
+ t = vxp_Alloc(vxp, sizeof *t);
+ AN(t);
+ t->tok = tok;
+ t->b = b;
+ t->e = e;
+ if (vxp->t != NULL)
+ VTAILQ_INSERT_AFTER(&vxp->tokens, vxp->t, t, list);
+ else
+ VTAILQ_INSERT_TAIL(&vxp->tokens, t, list);
+ vxp->t = t;
+}
+
+static int
+vxp_decstr(struct vxp *vxp, int quoted)
+{
+ const char *b, *e, *p;
+ char *s;
+ unsigned l;
+ int esc = 0;
+
+ assert(vxp->t->tok == VAL);
+
+ b = vxp->t->b;
+ e = vxp->t->e;
+ if (quoted) {
+ assert(e - b >= 2);
+ b++;
+ e--;
+ }
+ l = e - b;
+ s = vxp->t->dec = vxp_Alloc(vxp, l + 1);
+ AN(vxp->t->dec);
+ for (p = b; p < e; p++) {
+ if (!esc && *p == '\\') {
+ esc = 1;
+ continue;
+ }
+ esc = 0;
+ *s++ = *p;
+ }
+ *s = '\0';
+ if (esc || p != e) {
+ VSB_printf(vxp->sb, "Syntax error ");
+ vxp_ErrWhere(vxp, vxp->t, -1);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Build a token list
+ */
+
+void
+vxp_Lexer(struct vxp *vxp)
+{
+ const char *p, *q;
+ unsigned u;
+ char quote;
+
+ for (p = vxp->b; p < vxp->e; ) {
+
+ /* Skip any whitespace */
+ if (isspace(*p)) {
+ p++;
+ continue;
+ }
+
+ /* Match for the fixed tokens */
+ u = vxp_fixed_token(p, &q);
+ if (u != 0) {
+ AN(q);
+ vxp_add_token(vxp, u, p, q);
+ p = q;
+ continue;
+ }
+
+ /* Match quoted strings */
+ if (*p == '"' || *p == '\'') {
+ quote = *p;
+ for (q = p + 1; q < vxp->e; q++) {
+ if (q[-1] == '\\')
+ continue;
+ if (*q == quote) {
+ q++;
+ quote = '\0';
+ break;
+ }
+ }
+ vxp_add_token(vxp, VAL, p, q);
+ if (quote != '\0') {
+ VSB_printf(vxp->sb, "Unterminated string ");
+ vxp_ErrWhere(vxp, vxp->t, q - p - 1);
+ return;
+ }
+ if (vxp_decstr(vxp, 1))
+ return;
+ p = q;
+ continue;
+ }
+
+ /* Match bareword */
+ if (isword(*p)) {
+ for (q = p; q < vxp->e; q++)
+ if (!isword(*q))
+ break;
+ vxp_add_token(vxp, VAL, p, q);
+ if (vxp_decstr(vxp, 0))
+ return;
+ p = q;
+ continue;
+ }
+
+ /* Error */
+ vxp_add_token(vxp, EOI, p, p + 1);
+ VSB_printf(vxp->sb, "Syntax error ");
+ vxp_ErrWhere(vxp, vxp->t, q - p);
+ return;
+ }
+
+ /* Finished */
+ vxp_add_token(vxp, EOI, vxp->e, vxp->e);
+}
+
+#ifdef VXP_DEBUG
+void
+vxp_PrintTokens(const struct vxp *vxp)
+{
+ struct token *t;
+
+ fprintf(stderr, "Token list:\n");
+ fprintf(stderr, " %-5s %-20s %s\n", "TOK", "SUBSTR", "DECODED");
+ VTAILQ_FOREACH(t, &vxp->tokens, list) {
+ fprintf(stderr, " ");
+ fprintf(stderr, "%-5s", vxp_tnames[t->tok]);
+ fprintf(stderr, " %-20.*s", (unsigned)(t->e - t->b), t->b);
+ if (t->dec)
+ fprintf(stderr, " '%s'", t->dec);
+ fprintf(stderr, "\n");
+ }
+ fprintf(stderr, "\n");
+}
+#endif /* VXP_DEBUG */
diff --git a/lib/libvarnishapi/vxp_parse.c b/lib/libvarnishapi/vxp_parse.c
new file mode 100644
index 0000000..390be8d
--- /dev/null
+++ b/lib/libvarnishapi/vxp_parse.c
@@ -0,0 +1,477 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2013 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Martin Blix Grydeland <martin at varnish-software.com>
+ *
+ * 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.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "vas.h"
+#include "vsb.h"
+#include "miniobj.h"
+#include "vapi/vsl.h"
+
+#include "vxp.h"
+
+static void vxp_expr_or(struct vxp *vxp, struct vex **pvex);
+
+static void
+vxp_expr_tag(struct vxp *vxp, struct vex_tag **ptag)
+{
+
+ /* XXX: Tag wildcards */
+ AN(ptag);
+ AZ(*ptag);
+ if (vxp->t->tok != VAL) {
+ VSB_printf(vxp->sb, "Expected VSL tag got '%.*s' ", PF(vxp->t));
+ vxp_ErrWhere(vxp, vxp->t, -1);
+ return;
+ }
+ ALLOC_OBJ(*ptag, VEX_TAG_MAGIC);
+ AN(*ptag);
+ (*ptag)->tag = VSL_Name2Tag(vxp->t->dec, -1);
+ if ((*ptag)->tag == -1) {
+ VSB_printf(vxp->sb, "Could not match '%.*s' to any tag ",
+ PF(vxp->t));
+ vxp_ErrWhere(vxp, vxp->t, -1);
+ return;
+ } else if ((*ptag)->tag == -2) {
+ VSB_printf(vxp->sb, "'%.*s' matches multiple tags ",
+ PF(vxp->t));
+ vxp_ErrWhere(vxp, vxp->t, -1);
+ return;
+ }
+ vxp_NextToken(vxp);
+
+ /* XXX: Tag limiting operators ([], {}) */
+}
+
+static void
+vxp_expr_num(struct vxp *vxp, struct vex_val **pval)
+{
+ char *endptr;
+
+ AN(pval);
+ AZ(*pval);
+ if (vxp->t->tok != VAL) {
+ VSB_printf(vxp->sb, "Expected number got '%.*s' ", PF(vxp->t));
+ vxp_ErrWhere(vxp, vxp->t, -1);
+ return;
+ }
+ AN(vxp->t->dec);
+ ALLOC_OBJ(*pval, VEX_VAL_MAGIC);
+ AN(*pval);
+ if (strchr(vxp->t->dec, '.')) {
+ (*pval)->type = VEX_FLOAT;
+ (*pval)->val_float = strtod(vxp->t->dec, &endptr);
+ while (isspace(*endptr))
+ endptr++;
+ if (*endptr != '\0') {
+ VSB_printf(vxp->sb, "Floating point parse error ");
+ vxp_ErrWhere(vxp, vxp->t, -1);
+ return;
+ }
+ } else {
+ (*pval)->type = VEX_INT;
+ (*pval)->val_int = strtoll(vxp->t->dec, &endptr, 0);
+ while (isspace(*endptr))
+ endptr++;
+ if (*endptr != '\0') {
+ VSB_printf(vxp->sb, "Integer parse error ");
+ vxp_ErrWhere(vxp, vxp->t, -1);
+ return;
+ }
+ }
+ vxp_NextToken(vxp);
+}
+
+static void
+vxp_expr_str(struct vxp *vxp, struct vex_val **pval)
+{
+
+ AN(pval);
+ AZ(*pval);
+ if (vxp->t->tok != VAL) {
+ VSB_printf(vxp->sb, "Expected string got '%.*s' ", PF(vxp->t));
+ vxp_ErrWhere(vxp, vxp->t, -1);
+ return;
+ }
+ AN(vxp->t->dec);
+ ALLOC_OBJ(*pval, VEX_VAL_MAGIC);
+ AN(*pval);
+ (*pval)->type = VEX_STRING;
+ (*pval)->val_string = strdup(vxp->t->dec);
+ AN((*pval)->val_string);
+ vxp_NextToken(vxp);
+}
+
+static void
+vxp_expr_regex(struct vxp *vxp, struct vex_val **pval)
+{
+ const char *errptr;
+ int erroff;
+
+ /* XXX: Caseless option */
+
+ AN(pval);
+ AZ(*pval);
+ if (vxp->t->tok != VAL) {
+ VSB_printf(vxp->sb, "Expected regular expression got '%.*s' ",
+ PF(vxp->t));
+ vxp_ErrWhere(vxp, vxp->t, -1);
+ return;
+ }
+ AN(vxp->t->dec);
+ ALLOC_OBJ(*pval, VEX_VAL_MAGIC);
+ AN(*pval);
+ (*pval)->type = VEX_REGEX;
+ (*pval)->val_string = strdup(vxp->t->dec);
+ (*pval)->val_regex = VRE_compile(vxp->t->dec, 0, &errptr, &erroff);
+ if ((*pval)->val_regex == NULL) {
+ AN(errptr);
+ VSB_printf(vxp->sb, "Regular expression error: %s ", errptr);
+ vxp_ErrWhere(vxp, vxp->t, erroff);
+ return;
+ }
+ vxp_NextToken(vxp);
+}
+
+/*
+ * SYNTAX:
+ * expr_cmp:
+ * tag
+ * tag <operator> num|str|regex
+ */
+
+static void
+vxp_expr_cmp(struct vxp *vxp, struct vex **pvex)
+{
+
+ AN(pvex);
+ AZ(*pvex);
+ ALLOC_OBJ(*pvex, VEX_MAGIC);
+ AN(*pvex);
+ vxp_expr_tag(vxp, &(*pvex)->tag);
+ ERRCHK(vxp);
+
+ /* Test operator */
+ switch (vxp->t->tok) {
+
+ /* Single tag expressions don't take any more tokens */
+ case EOI:
+ case T_AND:
+ case T_OR:
+ case ')':
+ return;
+
+ /* Valid operators */
+ case T_EQ: /* == */
+ case T_NEQ: /* != */
+ case T_SEQ: /* eq */
+ case T_SNEQ: /* ne */
+ case '~': /* ~ */
+ case T_NOMATCH: /* !~ */
+ (*pvex)->tok = vxp->t->tok;
+ break;
+
+ /* Error */
+ default:
+ VSB_printf(vxp->sb, "Expected operator got '%.*s' ",
+ PF(vxp->t));
+ vxp_ErrWhere(vxp, vxp->t, -1);
+ return;
+ }
+ vxp_NextToken(vxp);
+ ERRCHK(vxp);
+
+ /* Value */
+ switch((*pvex)->tok) {
+ case '\0':
+ WRONG("Missing token");
+ case T_EQ: /* == */
+ case T_GEQ: /* >= */
+ case T_LEQ: /* <= */
+ case T_NEQ: /* != */
+ vxp_expr_num(vxp, &(*pvex)->val);
+ break;
+ case T_SEQ: /* eq */
+ case T_SNEQ: /* ne */
+ vxp_expr_str(vxp, &(*pvex)->val);
+ break;
+ case '~': /* ~ */
+ case T_NOMATCH: /* !~ */
+ vxp_expr_regex(vxp, &(*pvex)->val);
+ break;
+ default:
+ INCOMPL();
+ }
+}
+
+/*
+ * SYNTAX:
+ * expr_group:
+ * '(' expr_or ')'
+ * expr_not
+ */
+
+static void
+vxp_expr_group(struct vxp *vxp, struct vex **pvex)
+{
+
+ AN(pvex);
+ AZ(*pvex);
+
+ if (vxp->t->tok == '(') {
+ SkipToken(vxp, '(');
+ vxp_expr_or(vxp, pvex);
+ ERRCHK(vxp);
+ SkipToken(vxp, ')');
+ return;
+ }
+
+ vxp_expr_cmp(vxp, pvex);
+}
+
+/*
+ * SYNTAX:
+ * expr_not:
+ * '!' expr_group
+ * expr_group
+ */
+
+static void
+vxp_expr_not(struct vxp *vxp, struct vex **pvex)
+{
+
+ AN(pvex);
+ AZ(*pvex);
+
+ if (vxp->t->tok == '!') {
+ ALLOC_OBJ(*pvex, VEX_MAGIC);
+ AN(*pvex);
+ (*pvex)->tok = vxp->t->tok;
+ vxp_NextToken(vxp);
+ vxp_expr_group(vxp, &(*pvex)->a);
+ return;
+ }
+
+ vxp_expr_group(vxp, pvex);
+ return;
+}
+
+/*
+ * SYNTAX:
+ * expr_and:
+ * expr_not { 'and' expr_not }*
+ */
+
+static void
+vxp_expr_and(struct vxp *vxp, struct vex **pvex)
+{
+ struct vex *a;
+
+ AN(pvex);
+ AZ(*pvex);
+ vxp_expr_not(vxp, pvex);
+ ERRCHK(vxp);
+ while (vxp->t->tok == T_AND) {
+ a = *pvex;
+ ALLOC_OBJ(*pvex, VEX_MAGIC);
+ AN(*pvex);
+ (*pvex)->tok = vxp->t->tok;
+ (*pvex)->a = a;
+ vxp_NextToken(vxp);
+ ERRCHK(vxp);
+ vxp_expr_not(vxp, &(*pvex)->b);
+ ERRCHK(vxp);
+ }
+}
+
+/*
+ * SYNTAX:
+ * expr_or:
+ * expr_and { 'or' expr_and }*
+ */
+
+static void
+vxp_expr_or(struct vxp *vxp, struct vex **pvex)
+{
+ struct vex *a;
+
+ AN(pvex);
+ AZ(*pvex);
+ vxp_expr_and(vxp, pvex);
+ ERRCHK(vxp);
+ while (vxp->t->tok == T_OR) {
+ a = *pvex;
+ ALLOC_OBJ(*pvex, VEX_MAGIC);
+ AN(*pvex);
+ (*pvex)->tok = vxp->t->tok;
+ (*pvex)->a = a;
+ vxp_NextToken(vxp);
+ ERRCHK(vxp);
+ vxp_expr_and(vxp, &(*pvex)->b);
+ ERRCHK(vxp);
+ }
+}
+
+/*
+ * SYNTAX:
+ * expr:
+ * expr_or EOI
+ */
+
+static void
+vxp_expr(struct vxp *vxp, struct vex **pvex)
+{
+ vxp_expr_or(vxp, pvex);
+ ERRCHK(vxp);
+ ExpectErr(vxp, EOI);
+}
+
+/*
+ * Build a struct vex tree from the token list in vxp
+ */
+
+struct vex *
+vxp_Parse(struct vxp *vxp)
+{
+ struct vex *vex = NULL;
+
+ vxp->t = VTAILQ_FIRST(&vxp->tokens);
+ if (vxp->t == NULL)
+ return (NULL);
+
+ vxp_expr(vxp, &vex);
+
+ if (vxp->err) {
+ if (vex)
+ vex_Free(&vex);
+ AZ(vex);
+ return (NULL);
+ }
+
+ return (vex);
+}
+
+/*
+ * Free a struct vex tree
+ */
+
+void
+vex_Free(struct vex **pvex)
+{
+
+ if ((*pvex)->tag != NULL)
+ FREE_OBJ((*pvex)->tag);
+ if ((*pvex)->val != NULL) {
+ if ((*pvex)->val->val_string)
+ free((*pvex)->val->val_string);
+ if ((*pvex)->val->val_regex)
+ VRE_free(&(*pvex)->val->val_regex);
+ FREE_OBJ((*pvex)->val);
+ }
+ if ((*pvex)->a != NULL) {
+ vex_Free(&(*pvex)->a);
+ AZ((*pvex)->a);
+ }
+ if ((*pvex)->b != NULL) {
+ vex_Free(&(*pvex)->b);
+ AZ((*pvex)->b);
+ }
+ FREE_OBJ(*pvex);
+ *pvex = NULL;
+}
+
+#ifdef VXP_DEBUG
+
+static void
+vex_print_val(const struct vex_val *val)
+{
+
+ CHECK_OBJ_NOTNULL(val, VEX_VAL_MAGIC);
+ switch (val->type) {
+ case VEX_INT:
+ fprintf(stderr, "INT=%jd", (intmax_t)val->val_int);
+ break;
+ case VEX_FLOAT:
+ fprintf(stderr, "FLOAT=%f", val->val_float);
+ break;
+ case VEX_STRING:
+ AN(val->val_string);
+ fprintf(stderr, "STRING='%s'", val->val_string);
+ break;
+ case VEX_REGEX:
+ AN(val->val_string);
+ AN(val->val_regex);
+ fprintf(stderr, "REGEX='%s'", val->val_string);
+ break;
+ default:
+ WRONG("value type");
+ break;
+ }
+}
+
+static void
+vex_print(const struct vex *vex, int indent)
+{
+ CHECK_OBJ_NOTNULL(vex, VEX_MAGIC);
+
+ fprintf(stderr, "%*s%s", indent, "", vxp_tnames[vex->tok]);
+ if (vex->tag != NULL) {
+ CHECK_OBJ_NOTNULL(vex->tag, VEX_TAG_MAGIC);
+ fprintf(stderr, " tag=%s", VSL_tags[vex->tag->tag]);
+ }
+ if (vex->val != NULL) {
+ fprintf(stderr, " ");
+ vex_print_val(vex->val);
+ }
+ fprintf(stderr, "\n");
+ if (vex->a != NULL)
+ vex_print(vex->a, indent + 2);
+ if (vex->b != NULL)
+ vex_print(vex->b, indent + 2);
+}
+
+void
+vex_PrintTree(const struct vex *vex)
+{
+
+ CHECK_OBJ_NOTNULL(vex, VEX_MAGIC);
+ fprintf(stderr, "VEX tree:\n");
+ vex_print(vex, 2);
+}
+
+#endif /* VXP_DEBUG */
diff --git a/lib/libvarnishapi/vxp_test.c b/lib/libvarnishapi/vxp_test.c
new file mode 100644
index 0000000..19be714
--- /dev/null
+++ b/lib/libvarnishapi/vxp_test.c
@@ -0,0 +1,41 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "vxp.h"
+#include "vas.h"
+#include "vsb.h"
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ unsigned l;
+ char *s;
+ struct vsb *vsb;
+ struct vex *vex;
+
+ l = 0;
+ for (i = 1; i < argc; i++)
+ l += strlen(argv[i]) + 1;
+ s = calloc(l + 1, sizeof (char));
+ for (i = 1; i < argc; strcat(s, " "), i++)
+ strcat(s, argv[i]);
+
+ vsb = VSB_new_auto();
+ AN(vsb);
+ vex = vex_New(s, vsb);
+
+ if (vex == NULL) {
+ VSB_finish(vsb);
+ fprintf(stderr, "Error:\n%s", VSB_data(vsb));
+ exit(1);
+ }
+ VSB_delete(vsb);
+
+ vex_Free(&vex);
+ AZ(vex);
+
+ return (0);
+}
More information about the varnish-commit
mailing list