[master] 507a101 Add VSL Query/Dispatch structured log messages facility

Martin Blix Grydeland martin at varnish-cache.org
Wed May 15 14:46:14 CEST 2013


commit 507a101956fcd27f2e6e3ba1e403684b32040bc6
Author: Martin Blix Grydeland <martin at varnish-software.com>
Date:   Wed Apr 24 09:49:42 2013 +0200

    Add VSL Query/Dispatch structured log messages facility
    
    Move vtree.h from varnishtop to include directory so it's available for anyone.
    
    Simple proof-of-concept single regex against any log line query
    implementation.

diff --git a/bin/varnishtop/Makefile.am b/bin/varnishtop/Makefile.am
index 1a6ecc4..b2f5c89 100644
--- a/bin/varnishtop/Makefile.am
+++ b/bin/varnishtop/Makefile.am
@@ -8,8 +8,7 @@ dist_man_MANS = varnishtop.1
 
 varnishtop_SOURCES = varnishtop.c \
 	$(top_builddir)/lib/libvarnish/vas.c \
-	$(top_builddir)/lib/libvarnish/version.c \
-	vtree.h
+	$(top_builddir)/lib/libvarnish/version.c
 
 varnishtop_LDADD = \
 	$(top_builddir)/lib/libvarnishcompat/libvarnishcompat.la \
diff --git a/bin/varnishtop/vtree.h b/bin/varnishtop/vtree.h
deleted file mode 100644
index 7692d12..0000000
--- a/bin/varnishtop/vtree.h
+++ /dev/null
@@ -1,763 +0,0 @@
-/*	$NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $	*/
-/*	$OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $	*/
-/* $FreeBSD: release/9.0.0/sys/sys/tree.h 189204 2009-03-01 04:57:23Z bms $ */
-
-/*-
- * Copyright 2002 Niels Provos <provos at citi.umich.edu>
- * All rights reserved.
- *
- * 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 ``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 THE AUTHOR 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.
- */
-
-#ifndef	_VTREE_H_
-#define	_VTREE_H_
-
-/*
- * This file defines data structures for different types of trees:
- * splay trees and red-black trees.
- *
- * A splay tree is a self-organizing data structure.  Every operation
- * on the tree causes a splay to happen.  The splay moves the requested
- * node to the root of the tree and partly rebalances it.
- *
- * This has the benefit that request locality causes faster lookups as
- * the requested nodes move to the top of the tree.  On the other hand,
- * every lookup causes memory writes.
- *
- * The Balance Theorem bounds the total access time for m operations
- * and n inserts on an initially empty tree as O((m + n)lg n).  The
- * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
- *
- * A red-black tree is a binary search tree with the node color as an
- * extra attribute.  It fulfills a set of conditions:
- *	- every search path from the root to a leaf consists of the
- *	  same number of black nodes,
- *	- each red node (except for the root) has a black parent,
- *	- each leaf node is black.
- *
- * Every operation on a red-black tree is bounded as O(lg n).
- * The maximum height of a red-black tree is 2lg (n+1).
- */
-
-#define VSPLAY_HEAD(name, type)						\
-struct name {								\
-	struct type *sph_root; /* root of the tree */			\
-}
-
-#define VSPLAY_INITIALIZER(root)						\
-	{ NULL }
-
-#define VSPLAY_INIT(root) do {						\
-	(root)->sph_root = NULL;					\
-} while (/*CONSTCOND*/ 0)
-
-#define VSPLAY_ENTRY(type)						\
-struct {								\
-	struct type *spe_left; /* left element */			\
-	struct type *spe_right; /* right element */			\
-}
-
-#define VSPLAY_LEFT(elm, field)		(elm)->field.spe_left
-#define VSPLAY_RIGHT(elm, field)		(elm)->field.spe_right
-#define VSPLAY_ROOT(head)		(head)->sph_root
-#define VSPLAY_EMPTY(head)		(VSPLAY_ROOT(head) == NULL)
-
-/* VSPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold VSPLAY_{RIGHT,LEFT} */
-#define VSPLAY_ROTATE_RIGHT(head, tmp, field) do {			\
-	VSPLAY_LEFT((head)->sph_root, field) = VSPLAY_RIGHT(tmp, field);	\
-	VSPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
-	(head)->sph_root = tmp;						\
-} while (/*CONSTCOND*/ 0)
-
-#define VSPLAY_ROTATE_LEFT(head, tmp, field) do {			\
-	VSPLAY_RIGHT((head)->sph_root, field) = VSPLAY_LEFT(tmp, field);	\
-	VSPLAY_LEFT(tmp, field) = (head)->sph_root;			\
-	(head)->sph_root = tmp;						\
-} while (/*CONSTCOND*/ 0)
-
-#define VSPLAY_LINKLEFT(head, tmp, field) do {				\
-	VSPLAY_LEFT(tmp, field) = (head)->sph_root;			\
-	tmp = (head)->sph_root;						\
-	(head)->sph_root = VSPLAY_LEFT((head)->sph_root, field);		\
-} while (/*CONSTCOND*/ 0)
-
-#define VSPLAY_LINKRIGHT(head, tmp, field) do {				\
-	VSPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
-	tmp = (head)->sph_root;						\
-	(head)->sph_root = VSPLAY_RIGHT((head)->sph_root, field);	\
-} while (/*CONSTCOND*/ 0)
-
-#define VSPLAY_ASSEMBLE(head, node, left, right, field) do {		\
-	VSPLAY_RIGHT(left, field) = VSPLAY_LEFT((head)->sph_root, field);	\
-	VSPLAY_LEFT(right, field) = VSPLAY_RIGHT((head)->sph_root, field);\
-	VSPLAY_LEFT((head)->sph_root, field) = VSPLAY_RIGHT(node, field);	\
-	VSPLAY_RIGHT((head)->sph_root, field) = VSPLAY_LEFT(node, field);	\
-} while (/*CONSTCOND*/ 0)
-
-/* Generates prototypes and inline functions */
-
-#define VSPLAY_PROTOTYPE(name, type, field, cmp)				\
-void name##_VSPLAY(struct name *, struct type *);			\
-void name##_VSPLAY_MINMAX(struct name *, int);				\
-struct type *name##_VSPLAY_INSERT(struct name *, struct type *);		\
-struct type *name##_VSPLAY_REMOVE(struct name *, struct type *);		\
-									\
-/* Finds the node with the same key as elm */				\
-static __inline struct type *						\
-name##_VSPLAY_FIND(struct name *head, struct type *elm)			\
-{									\
-	if (VSPLAY_EMPTY(head))						\
-		return(NULL);						\
-	name##_VSPLAY(head, elm);					\
-	if ((cmp)(elm, (head)->sph_root) == 0)				\
-		return (head->sph_root);				\
-	return (NULL);							\
-}									\
-									\
-static __inline struct type *						\
-name##_VSPLAY_NEXT(struct name *head, struct type *elm)			\
-{									\
-	name##_VSPLAY(head, elm);					\
-	if (VSPLAY_RIGHT(elm, field) != NULL) {				\
-		elm = VSPLAY_RIGHT(elm, field);				\
-		while (VSPLAY_LEFT(elm, field) != NULL) {		\
-			elm = VSPLAY_LEFT(elm, field);			\
-		}							\
-	} else								\
-		elm = NULL;						\
-	return (elm);							\
-}									\
-									\
-static __inline struct type *						\
-name##_VSPLAY_MIN_MAX(struct name *head, int val)			\
-{									\
-	name##_VSPLAY_MINMAX(head, val);					\
-        return (VSPLAY_ROOT(head));					\
-}
-
-/* Main splay operation.
- * Moves node close to the key of elm to top
- */
-#define VSPLAY_GENERATE(name, type, field, cmp)				\
-struct type *								\
-name##_VSPLAY_INSERT(struct name *head, struct type *elm)		\
-{									\
-    if (VSPLAY_EMPTY(head)) {						\
-	    VSPLAY_LEFT(elm, field) = VSPLAY_RIGHT(elm, field) = NULL;	\
-    } else {								\
-	    int __comp;							\
-	    name##_VSPLAY(head, elm);					\
-	    __comp = (cmp)(elm, (head)->sph_root);			\
-	    if(__comp < 0) {						\
-		    VSPLAY_LEFT(elm, field) = VSPLAY_LEFT((head)->sph_root, field);\
-		    VSPLAY_RIGHT(elm, field) = (head)->sph_root;		\
-		    VSPLAY_LEFT((head)->sph_root, field) = NULL;		\
-	    } else if (__comp > 0) {					\
-		    VSPLAY_RIGHT(elm, field) = VSPLAY_RIGHT((head)->sph_root, field);\
-		    VSPLAY_LEFT(elm, field) = (head)->sph_root;		\
-		    VSPLAY_RIGHT((head)->sph_root, field) = NULL;	\
-	    } else							\
-		    return ((head)->sph_root);				\
-    }									\
-    (head)->sph_root = (elm);						\
-    return (NULL);							\
-}									\
-									\
-struct type *								\
-name##_VSPLAY_REMOVE(struct name *head, struct type *elm)		\
-{									\
-	struct type *__tmp;						\
-	if (VSPLAY_EMPTY(head))						\
-		return (NULL);						\
-	name##_VSPLAY(head, elm);					\
-	if ((cmp)(elm, (head)->sph_root) == 0) {			\
-		if (VSPLAY_LEFT((head)->sph_root, field) == NULL) {	\
-			(head)->sph_root = VSPLAY_RIGHT((head)->sph_root, field);\
-		} else {						\
-			__tmp = VSPLAY_RIGHT((head)->sph_root, field);	\
-			(head)->sph_root = VSPLAY_LEFT((head)->sph_root, field);\
-			name##_VSPLAY(head, elm);			\
-			VSPLAY_RIGHT((head)->sph_root, field) = __tmp;	\
-		}							\
-		return (elm);						\
-	}								\
-	return (NULL);							\
-}									\
-									\
-void									\
-name##_VSPLAY(struct name *head, struct type *elm)			\
-{									\
-	struct type __node, *__left, *__right, *__tmp;			\
-	int __comp;							\
-\
-	VSPLAY_LEFT(&__node, field) = VSPLAY_RIGHT(&__node, field) = NULL;\
-	__left = __right = &__node;					\
-\
-	while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) {		\
-		if (__comp < 0) {					\
-			__tmp = VSPLAY_LEFT((head)->sph_root, field);	\
-			if (__tmp == NULL)				\
-				break;					\
-			if ((cmp)(elm, __tmp) < 0){			\
-				VSPLAY_ROTATE_RIGHT(head, __tmp, field);	\
-				if (VSPLAY_LEFT((head)->sph_root, field) == NULL)\
-					break;				\
-			}						\
-			VSPLAY_LINKLEFT(head, __right, field);		\
-		} else if (__comp > 0) {				\
-			__tmp = VSPLAY_RIGHT((head)->sph_root, field);	\
-			if (__tmp == NULL)				\
-				break;					\
-			if ((cmp)(elm, __tmp) > 0){			\
-				VSPLAY_ROTATE_LEFT(head, __tmp, field);	\
-				if (VSPLAY_RIGHT((head)->sph_root, field) == NULL)\
-					break;				\
-			}						\
-			VSPLAY_LINKRIGHT(head, __left, field);		\
-		}							\
-	}								\
-	VSPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
-}									\
-									\
-/* Splay with either the minimum or the maximum element			\
- * Used to find minimum or maximum element in tree.			\
- */									\
-void name##_VSPLAY_MINMAX(struct name *head, int __comp) \
-{									\
-	struct type __node, *__left, *__right, *__tmp;			\
-\
-	VSPLAY_LEFT(&__node, field) = VSPLAY_RIGHT(&__node, field) = NULL;\
-	__left = __right = &__node;					\
-\
-	while (1) {							\
-		if (__comp < 0) {					\
-			__tmp = VSPLAY_LEFT((head)->sph_root, field);	\
-			if (__tmp == NULL)				\
-				break;					\
-			if (__comp < 0){				\
-				VSPLAY_ROTATE_RIGHT(head, __tmp, field);	\
-				if (VSPLAY_LEFT((head)->sph_root, field) == NULL)\
-					break;				\
-			}						\
-			VSPLAY_LINKLEFT(head, __right, field);		\
-		} else if (__comp > 0) {				\
-			__tmp = VSPLAY_RIGHT((head)->sph_root, field);	\
-			if (__tmp == NULL)				\
-				break;					\
-			if (__comp > 0) {				\
-				VSPLAY_ROTATE_LEFT(head, __tmp, field);	\
-				if (VSPLAY_RIGHT((head)->sph_root, field) == NULL)\
-					break;				\
-			}						\
-			VSPLAY_LINKRIGHT(head, __left, field);		\
-		}							\
-	}								\
-	VSPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
-}
-
-#define VSPLAY_NEGINF	-1
-#define VSPLAY_INF	1
-
-#define VSPLAY_INSERT(name, x, y)	name##_VSPLAY_INSERT(x, y)
-#define VSPLAY_REMOVE(name, x, y)	name##_VSPLAY_REMOVE(x, y)
-#define VSPLAY_FIND(name, x, y)		name##_VSPLAY_FIND(x, y)
-#define VSPLAY_NEXT(name, x, y)		name##_VSPLAY_NEXT(x, y)
-#define VSPLAY_MIN(name, x)		(VSPLAY_EMPTY(x) ? NULL	\
-					: name##_VSPLAY_MIN_MAX(x, VSPLAY_NEGINF))
-#define VSPLAY_MAX(name, x)		(VSPLAY_EMPTY(x) ? NULL	\
-					: name##_VSPLAY_MIN_MAX(x, VSPLAY_INF))
-
-#define VSPLAY_FOREACH(x, name, head)					\
-	for ((x) = VSPLAY_MIN(name, head);				\
-	     (x) != NULL;						\
-	     (x) = VSPLAY_NEXT(name, head, x))
-
-/* Macros that define a red-black tree */
-#define VRB_HEAD(name, type)						\
-struct name {								\
-	struct type *rbh_root; /* root of the tree */			\
-}
-
-#define VRB_INITIALIZER(root)						\
-	{ NULL }
-
-#define VRB_INIT(root) do {						\
-	(root)->rbh_root = NULL;					\
-} while (/*CONSTCOND*/ 0)
-
-#define VRB_BLACK	0
-#define VRB_RED		1
-#define VRB_ENTRY(type)							\
-struct {								\
-	struct type *rbe_left;		/* left element */		\
-	struct type *rbe_right;		/* right element */		\
-	struct type *rbe_parent;	/* parent element */		\
-	int rbe_color;			/* node color */		\
-}
-
-#define VRB_LEFT(elm, field)		(elm)->field.rbe_left
-#define VRB_RIGHT(elm, field)		(elm)->field.rbe_right
-#define VRB_PARENT(elm, field)		(elm)->field.rbe_parent
-#define VRB_COLOR(elm, field)		(elm)->field.rbe_color
-#define VRB_ROOT(head)			(head)->rbh_root
-#define VRB_EMPTY(head)			(VRB_ROOT(head) == NULL)
-
-#define VRB_SET(elm, parent, field) do {					\
-	VRB_PARENT(elm, field) = parent;					\
-	VRB_LEFT(elm, field) = VRB_RIGHT(elm, field) = NULL;		\
-	VRB_COLOR(elm, field) = VRB_RED;					\
-} while (/*CONSTCOND*/ 0)
-
-#define VRB_SET_BLACKRED(black, red, field) do {				\
-	VRB_COLOR(black, field) = VRB_BLACK;				\
-	VRB_COLOR(red, field) = VRB_RED;					\
-} while (/*CONSTCOND*/ 0)
-
-#ifndef VRB_AUGMENT
-#define VRB_AUGMENT(x)	do {} while (0)
-#endif
-
-#define VRB_ROTATE_LEFT(head, elm, tmp, field) do {			\
-	(tmp) = VRB_RIGHT(elm, field);					\
-	if ((VRB_RIGHT(elm, field) = VRB_LEFT(tmp, field)) != NULL) {	\
-		VRB_PARENT(VRB_LEFT(tmp, field), field) = (elm);		\
-	}								\
-	VRB_AUGMENT(elm);						\
-	if ((VRB_PARENT(tmp, field) = VRB_PARENT(elm, field)) != NULL) {	\
-		if ((elm) == VRB_LEFT(VRB_PARENT(elm, field), field))	\
-			VRB_LEFT(VRB_PARENT(elm, field), field) = (tmp);	\
-		else							\
-			VRB_RIGHT(VRB_PARENT(elm, field), field) = (tmp);	\
-	} else								\
-		(head)->rbh_root = (tmp);				\
-	VRB_LEFT(tmp, field) = (elm);					\
-	VRB_PARENT(elm, field) = (tmp);					\
-	VRB_AUGMENT(tmp);						\
-	if ((VRB_PARENT(tmp, field)))					\
-		VRB_AUGMENT(VRB_PARENT(tmp, field));			\
-} while (/*CONSTCOND*/ 0)
-
-#define VRB_ROTATE_RIGHT(head, elm, tmp, field) do {			\
-	(tmp) = VRB_LEFT(elm, field);					\
-	if ((VRB_LEFT(elm, field) = VRB_RIGHT(tmp, field)) != NULL) {	\
-		VRB_PARENT(VRB_RIGHT(tmp, field), field) = (elm);		\
-	}								\
-	VRB_AUGMENT(elm);						\
-	if ((VRB_PARENT(tmp, field) = VRB_PARENT(elm, field)) != NULL) {	\
-		if ((elm) == VRB_LEFT(VRB_PARENT(elm, field), field))	\
-			VRB_LEFT(VRB_PARENT(elm, field), field) = (tmp);	\
-		else							\
-			VRB_RIGHT(VRB_PARENT(elm, field), field) = (tmp);	\
-	} else								\
-		(head)->rbh_root = (tmp);				\
-	VRB_RIGHT(tmp, field) = (elm);					\
-	VRB_PARENT(elm, field) = (tmp);					\
-	VRB_AUGMENT(tmp);						\
-	if ((VRB_PARENT(tmp, field)))					\
-		VRB_AUGMENT(VRB_PARENT(tmp, field));			\
-} while (/*CONSTCOND*/ 0)
-
-/* Generates prototypes and inline functions */
-#define	VRB_PROTOTYPE(name, type, field, cmp)				\
-	VRB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
-#define	VRB_PROTOTYPE_STATIC(name, type, field, cmp)			\
-	VRB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
-#define VRB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr)		\
-attr void name##_VRB_INSERT_COLOR(struct name *, struct type *);		\
-attr void name##_VRB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
-attr struct type *name##_VRB_REMOVE(struct name *, struct type *);	\
-attr struct type *name##_VRB_INSERT(struct name *, struct type *);	\
-attr struct type *name##_VRB_FIND(struct name *, struct type *);		\
-attr struct type *name##_VRB_NFIND(struct name *, struct type *);	\
-attr struct type *name##_VRB_NEXT(struct type *);			\
-attr struct type *name##_VRB_PREV(struct type *);			\
-attr struct type *name##_VRB_MINMAX(struct name *, int);			\
-									\
-
-/* Main rb operation.
- * Moves node close to the key of elm to top
- */
-#define	VRB_GENERATE(name, type, field, cmp)				\
-	VRB_GENERATE_INTERNAL(name, type, field, cmp,)
-#define	VRB_GENERATE_STATIC(name, type, field, cmp)			\
-	VRB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
-#define VRB_GENERATE_INTERNAL(name, type, field, cmp, attr)		\
-attr void								\
-name##_VRB_INSERT_COLOR(struct name *head, struct type *elm)		\
-{									\
-	struct type *parent, *gparent, *tmp;				\
-	while ((parent = VRB_PARENT(elm, field)) != NULL &&		\
-	    VRB_COLOR(parent, field) == VRB_RED) {			\
-		gparent = VRB_PARENT(parent, field);			\
-		if (parent == VRB_LEFT(gparent, field)) {		\
-			tmp = VRB_RIGHT(gparent, field);			\
-			if (tmp && VRB_COLOR(tmp, field) == VRB_RED) {	\
-				VRB_COLOR(tmp, field) = VRB_BLACK;	\
-				VRB_SET_BLACKRED(parent, gparent, field);\
-				elm = gparent;				\
-				continue;				\
-			}						\
-			if (VRB_RIGHT(parent, field) == elm) {		\
-				VRB_ROTATE_LEFT(head, parent, tmp, field);\
-				tmp = parent;				\
-				parent = elm;				\
-				elm = tmp;				\
-			}						\
-			VRB_SET_BLACKRED(parent, gparent, field);	\
-			VRB_ROTATE_RIGHT(head, gparent, tmp, field);	\
-		} else {						\
-			tmp = VRB_LEFT(gparent, field);			\
-			if (tmp && VRB_COLOR(tmp, field) == VRB_RED) {	\
-				VRB_COLOR(tmp, field) = VRB_BLACK;	\
-				VRB_SET_BLACKRED(parent, gparent, field);\
-				elm = gparent;				\
-				continue;				\
-			}						\
-			if (VRB_LEFT(parent, field) == elm) {		\
-				VRB_ROTATE_RIGHT(head, parent, tmp, field);\
-				tmp = parent;				\
-				parent = elm;				\
-				elm = tmp;				\
-			}						\
-			VRB_SET_BLACKRED(parent, gparent, field);	\
-			VRB_ROTATE_LEFT(head, gparent, tmp, field);	\
-		}							\
-	}								\
-	VRB_COLOR(head->rbh_root, field) = VRB_BLACK;			\
-}									\
-									\
-attr void								\
-name##_VRB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
-{									\
-	struct type *tmp;						\
-	while ((elm == NULL || VRB_COLOR(elm, field) == VRB_BLACK) &&	\
-	    elm != VRB_ROOT(head)) {					\
-		if (VRB_LEFT(parent, field) == elm) {			\
-			tmp = VRB_RIGHT(parent, field);			\
-			if (VRB_COLOR(tmp, field) == VRB_RED) {		\
-				VRB_SET_BLACKRED(tmp, parent, field);	\
-				VRB_ROTATE_LEFT(head, parent, tmp, field);\
-				tmp = VRB_RIGHT(parent, field);		\
-			}						\
-			if ((VRB_LEFT(tmp, field) == NULL ||		\
-			    VRB_COLOR(VRB_LEFT(tmp, field), field) == VRB_BLACK) &&\
-			    (VRB_RIGHT(tmp, field) == NULL ||		\
-			    VRB_COLOR(VRB_RIGHT(tmp, field), field) == VRB_BLACK)) {\
-				VRB_COLOR(tmp, field) = VRB_RED;		\
-				elm = parent;				\
-				parent = VRB_PARENT(elm, field);		\
-			} else {					\
-				if (VRB_RIGHT(tmp, field) == NULL ||	\
-				    VRB_COLOR(VRB_RIGHT(tmp, field), field) == VRB_BLACK) {\
-					struct type *oleft;		\
-					if ((oleft = VRB_LEFT(tmp, field)) \
-					    != NULL)			\
-						VRB_COLOR(oleft, field) = VRB_BLACK;\
-					VRB_COLOR(tmp, field) = VRB_RED;	\
-					VRB_ROTATE_RIGHT(head, tmp, oleft, field);\
-					tmp = VRB_RIGHT(parent, field);	\
-				}					\
-				VRB_COLOR(tmp, field) = VRB_COLOR(parent, field);\
-				VRB_COLOR(parent, field) = VRB_BLACK;	\
-				if (VRB_RIGHT(tmp, field))		\
-					VRB_COLOR(VRB_RIGHT(tmp, field), field) = VRB_BLACK;\
-				VRB_ROTATE_LEFT(head, parent, tmp, field);\
-				elm = VRB_ROOT(head);			\
-				break;					\
-			}						\
-		} else {						\
-			tmp = VRB_LEFT(parent, field);			\
-			if (VRB_COLOR(tmp, field) == VRB_RED) {		\
-				VRB_SET_BLACKRED(tmp, parent, field);	\
-				VRB_ROTATE_RIGHT(head, parent, tmp, field);\
-				tmp = VRB_LEFT(parent, field);		\
-			}						\
-			if ((VRB_LEFT(tmp, field) == NULL ||		\
-			    VRB_COLOR(VRB_LEFT(tmp, field), field) == VRB_BLACK) &&\
-			    (VRB_RIGHT(tmp, field) == NULL ||		\
-			    VRB_COLOR(VRB_RIGHT(tmp, field), field) == VRB_BLACK)) {\
-				VRB_COLOR(tmp, field) = VRB_RED;		\
-				elm = parent;				\
-				parent = VRB_PARENT(elm, field);		\
-			} else {					\
-				if (VRB_LEFT(tmp, field) == NULL ||	\
-				    VRB_COLOR(VRB_LEFT(tmp, field), field) == VRB_BLACK) {\
-					struct type *oright;		\
-					if ((oright = VRB_RIGHT(tmp, field)) \
-					    != NULL)			\
-						VRB_COLOR(oright, field) = VRB_BLACK;\
-					VRB_COLOR(tmp, field) = VRB_RED;	\
-					VRB_ROTATE_LEFT(head, tmp, oright, field);\
-					tmp = VRB_LEFT(parent, field);	\
-				}					\
-				VRB_COLOR(tmp, field) = VRB_COLOR(parent, field);\
-				VRB_COLOR(parent, field) = VRB_BLACK;	\
-				if (VRB_LEFT(tmp, field))		\
-					VRB_COLOR(VRB_LEFT(tmp, field), field) = VRB_BLACK;\
-				VRB_ROTATE_RIGHT(head, parent, tmp, field);\
-				elm = VRB_ROOT(head);			\
-				break;					\
-			}						\
-		}							\
-	}								\
-	if (elm)							\
-		VRB_COLOR(elm, field) = VRB_BLACK;			\
-}									\
-									\
-attr struct type *							\
-name##_VRB_REMOVE(struct name *head, struct type *elm)			\
-{									\
-	struct type *child, *parent, *old = elm;			\
-	int color;							\
-	if (VRB_LEFT(elm, field) == NULL)				\
-		child = VRB_RIGHT(elm, field);				\
-	else if (VRB_RIGHT(elm, field) == NULL)				\
-		child = VRB_LEFT(elm, field);				\
-	else {								\
-		struct type *left;					\
-		elm = VRB_RIGHT(elm, field);				\
-		while ((left = VRB_LEFT(elm, field)) != NULL)		\
-			elm = left;					\
-		child = VRB_RIGHT(elm, field);				\
-		parent = VRB_PARENT(elm, field);				\
-		color = VRB_COLOR(elm, field);				\
-		if (child)						\
-			VRB_PARENT(child, field) = parent;		\
-		if (parent) {						\
-			if (VRB_LEFT(parent, field) == elm)		\
-				VRB_LEFT(parent, field) = child;		\
-			else						\
-				VRB_RIGHT(parent, field) = child;	\
-			VRB_AUGMENT(parent);				\
-		} else							\
-			VRB_ROOT(head) = child;				\
-		if (VRB_PARENT(elm, field) == old)			\
-			parent = elm;					\
-		(elm)->field = (old)->field;				\
-		if (VRB_PARENT(old, field)) {				\
-			if (VRB_LEFT(VRB_PARENT(old, field), field) == old)\
-				VRB_LEFT(VRB_PARENT(old, field), field) = elm;\
-			else						\
-				VRB_RIGHT(VRB_PARENT(old, field), field) = elm;\
-			VRB_AUGMENT(VRB_PARENT(old, field));		\
-		} else							\
-			VRB_ROOT(head) = elm;				\
-		VRB_PARENT(VRB_LEFT(old, field), field) = elm;		\
-		if (VRB_RIGHT(old, field))				\
-			VRB_PARENT(VRB_RIGHT(old, field), field) = elm;	\
-		if (parent) {						\
-			left = parent;					\
-			do {						\
-				VRB_AUGMENT(left);			\
-			} while ((left = VRB_PARENT(left, field)) != NULL); \
-		}							\
-		goto color;						\
-	}								\
-	parent = VRB_PARENT(elm, field);					\
-	color = VRB_COLOR(elm, field);					\
-	if (child)							\
-		VRB_PARENT(child, field) = parent;			\
-	if (parent) {							\
-		if (VRB_LEFT(parent, field) == elm)			\
-			VRB_LEFT(parent, field) = child;			\
-		else							\
-			VRB_RIGHT(parent, field) = child;		\
-		VRB_AUGMENT(parent);					\
-	} else								\
-		VRB_ROOT(head) = child;					\
-color:									\
-	if (color == VRB_BLACK)						\
-		name##_VRB_REMOVE_COLOR(head, parent, child);		\
-	return (old);							\
-}									\
-									\
-/* Inserts a node into the RB tree */					\
-attr struct type *							\
-name##_VRB_INSERT(struct name *head, struct type *elm)			\
-{									\
-	struct type *tmp;						\
-	struct type *parent = NULL;					\
-	int comp = 0;							\
-	tmp = VRB_ROOT(head);						\
-	while (tmp) {							\
-		parent = tmp;						\
-		comp = (cmp)(elm, parent);				\
-		if (comp < 0)						\
-			tmp = VRB_LEFT(tmp, field);			\
-		else if (comp > 0)					\
-			tmp = VRB_RIGHT(tmp, field);			\
-		else							\
-			return (tmp);					\
-	}								\
-	VRB_SET(elm, parent, field);					\
-	if (parent != NULL) {						\
-		if (comp < 0)						\
-			VRB_LEFT(parent, field) = elm;			\
-		else							\
-			VRB_RIGHT(parent, field) = elm;			\
-		VRB_AUGMENT(parent);					\
-	} else								\
-		VRB_ROOT(head) = elm;					\
-	name##_VRB_INSERT_COLOR(head, elm);				\
-	return (NULL);							\
-}									\
-									\
-/* Finds the node with the same key as elm */				\
-attr struct type *							\
-name##_VRB_FIND(struct name *head, struct type *elm)			\
-{									\
-	struct type *tmp = VRB_ROOT(head);				\
-	int comp;							\
-	while (tmp) {							\
-		comp = cmp(elm, tmp);					\
-		if (comp < 0)						\
-			tmp = VRB_LEFT(tmp, field);			\
-		else if (comp > 0)					\
-			tmp = VRB_RIGHT(tmp, field);			\
-		else							\
-			return (tmp);					\
-	}								\
-	return (NULL);							\
-}									\
-									\
-/* Finds the first node greater than or equal to the search key */	\
-attr struct type *							\
-name##_VRB_NFIND(struct name *head, struct type *elm)			\
-{									\
-	struct type *tmp = VRB_ROOT(head);				\
-	struct type *res = NULL;					\
-	int comp;							\
-	while (tmp) {							\
-		comp = cmp(elm, tmp);					\
-		if (comp < 0) {						\
-			res = tmp;					\
-			tmp = VRB_LEFT(tmp, field);			\
-		}							\
-		else if (comp > 0)					\
-			tmp = VRB_RIGHT(tmp, field);			\
-		else							\
-			return (tmp);					\
-	}								\
-	return (res);							\
-}									\
-									\
-/* ARGSUSED */								\
-attr struct type *							\
-name##_VRB_NEXT(struct type *elm)					\
-{									\
-	if (VRB_RIGHT(elm, field)) {					\
-		elm = VRB_RIGHT(elm, field);				\
-		while (VRB_LEFT(elm, field))				\
-			elm = VRB_LEFT(elm, field);			\
-	} else {							\
-		if (VRB_PARENT(elm, field) &&				\
-		    (elm == VRB_LEFT(VRB_PARENT(elm, field), field)))	\
-			elm = VRB_PARENT(elm, field);			\
-		else {							\
-			while (VRB_PARENT(elm, field) &&			\
-			    (elm == VRB_RIGHT(VRB_PARENT(elm, field), field)))\
-				elm = VRB_PARENT(elm, field);		\
-			elm = VRB_PARENT(elm, field);			\
-		}							\
-	}								\
-	return (elm);							\
-}									\
-									\
-/* ARGSUSED */								\
-attr struct type *							\
-name##_VRB_PREV(struct type *elm)					\
-{									\
-	if (VRB_LEFT(elm, field)) {					\
-		elm = VRB_LEFT(elm, field);				\
-		while (VRB_RIGHT(elm, field))				\
-			elm = VRB_RIGHT(elm, field);			\
-	} else {							\
-		if (VRB_PARENT(elm, field) &&				\
-		    (elm == VRB_RIGHT(VRB_PARENT(elm, field), field)))	\
-			elm = VRB_PARENT(elm, field);			\
-		else {							\
-			while (VRB_PARENT(elm, field) &&			\
-			    (elm == VRB_LEFT(VRB_PARENT(elm, field), field)))\
-				elm = VRB_PARENT(elm, field);		\
-			elm = VRB_PARENT(elm, field);			\
-		}							\
-	}								\
-	return (elm);							\
-}									\
-									\
-attr struct type *							\
-name##_VRB_MINMAX(struct name *head, int val)				\
-{									\
-	struct type *tmp = VRB_ROOT(head);				\
-	struct type *parent = NULL;					\
-	while (tmp) {							\
-		parent = tmp;						\
-		if (val < 0)						\
-			tmp = VRB_LEFT(tmp, field);			\
-		else							\
-			tmp = VRB_RIGHT(tmp, field);			\
-	}								\
-	return (parent);						\
-}
-
-#define VRB_NEGINF	-1
-#define VRB_INF	1
-
-#define VRB_INSERT(name, x, y)	name##_VRB_INSERT(x, y)
-#define VRB_REMOVE(name, x, y)	name##_VRB_REMOVE(x, y)
-#define VRB_FIND(name, x, y)	name##_VRB_FIND(x, y)
-#define VRB_NFIND(name, x, y)	name##_VRB_NFIND(x, y)
-#define VRB_NEXT(name, x, y)	name##_VRB_NEXT(y)
-#define VRB_PREV(name, x, y)	name##_VRB_PREV(y)
-#define VRB_MIN(name, x)		name##_VRB_MINMAX(x, VRB_NEGINF)
-#define VRB_MAX(name, x)		name##_VRB_MINMAX(x, VRB_INF)
-
-#define VRB_FOREACH(x, name, head)					\
-	for ((x) = VRB_MIN(name, head);					\
-	     (x) != NULL;						\
-	     (x) = name##_VRB_NEXT(x))
-
-#define VRB_FOREACH_FROM(x, name, y)					\
-	for ((x) = (y);							\
-	    ((x) != NULL) && ((y) = name##_VRB_NEXT(x), (x) != NULL);	\
-	     (x) = (y))
-
-#define VRB_FOREACH_SAFE(x, name, head, y)				\
-	for ((x) = VRB_MIN(name, head);					\
-	    ((x) != NULL) && ((y) = name##_VRB_NEXT(x), (x) != NULL);	\
-	     (x) = (y))
-
-#define VRB_FOREACH_REVERSE(x, name, head)				\
-	for ((x) = VRB_MAX(name, head);					\
-	     (x) != NULL;						\
-	     (x) = name##_VRB_PREV(x))
-
-#define VRB_FOREACH_REVERSE_FROM(x, name, y)				\
-	for ((x) = (y);							\
-	    ((x) != NULL) && ((y) = name##_VRB_PREV(x), (x) != NULL);	\
-	     (x) = (y))
-
-#define VRB_FOREACH_REVERSE_SAFE(x, name, head, y)			\
-	for ((x) = VRB_MAX(name, head);					\
-	    ((x) != NULL) && ((y) = name##_VRB_PREV(x), (x) != NULL);	\
-	     (x) = (y))
-
-#endif	/* _VTREE_H_ */
diff --git a/include/Makefile.am b/include/Makefile.am
index 6c3f687..ff33292 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -57,7 +57,8 @@ nobase_noinst_HEADERS = \
 	vsub.h \
 	vss.h \
 	vtcp.h \
-	vtim.h
+	vtim.h \
+	vtree.h
 
 # Headers for use with vmods
 pkgdataincludedir = $(pkgdatadir)/include
diff --git a/include/vapi/vsl.h b/include/vapi/vsl.h
index 54014cd..023b65f 100644
--- a/include/vapi/vsl.h
+++ b/include/vapi/vsl.h
@@ -1,9 +1,10 @@
 /*-
  * Copyright (c) 2006 Verdens Gang AS
- * Copyright (c) 2006-2011 Varnish Software 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
@@ -48,9 +49,27 @@
 			VSL_x_USAGE
 
 struct VSL_data;
+struct VSLQ;
 
 struct VSL_cursor {
 	const uint32_t		*ptr; /* Record pointer */
+
+	/* If not -1, the vxid of all records in this set */
+	int32_t			vxid;
+
+	/* For set cursors, the depth level of these records */
+	unsigned level;
+
+	/* Nonzero if pointer values from this cursor are still valid
+	   after next call to VSL_Next */
+	unsigned shmptr_ok;
+};
+
+enum VSL_grouping_e {
+	VSL_g_raw,
+	VSL_g_vxid,
+	VSL_g_request,
+	VSL_g_session,
 };
 
 extern const char *VSL_tags[256];
@@ -155,13 +174,138 @@ int VSL_Match(struct VSL_data *vsl, const struct VSL_cursor *c);
 	 *	0:	No match
 	 */
 
-int VSL_Print(struct VSL_data *vsl, const struct VSL_cursor *c, void *file);
+int VSL_PrintVXID(struct VSL_data *vsl, const struct VSL_cursor *c, void *fo);
+	/*
+	 * Print the log record pointed to by cursor to stream.
+	 *
+	 * Format: <vxid> <tag> <type> <data>
+	 *
+	 * Arguments:
+	 *   vsl: The VSL_data context
+	 *     c: A VSL_cursor
+	 *    fo: A FILE* pointer
+	 *
+	 * Return values:
+	 *	0:	OK
+	 *     -5:	I/O write error - see errno
+	 */
+
+int VSL_PrintLevel(struct VSL_data *vsl, const struct VSL_cursor *c, void *fo);
 	/*
 	 * Print the log record pointed to by cursor to stream.
 	 *
+	 * Format: <level> <tag> <type> <data>
+	 *
+	 * Arguments:
+	 *   vsl: The VSL_data context
+	 *     c: A VSL_cursor
+	 *    fo: A FILE* pointer
+	 *
 	 * Return values:
 	 *	0:	OK
 	 *     -5:	I/O write error - see errno
 	 */
 
+int VSL_PrintAll(struct VSL_data *vsl, struct VSL_cursor *c, void *fo);
+	/*
+	 * Calls VSL_Next on c until c is exhausted. In turn calls
+	 * prints all records where VSL_Match returns true.
+	 *
+	 * If c->vxid == -1, calls VSL_PrintVXID on each record. Else
+	 * prints a VXID header and calls VSL_PrintLevel on each record.
+	 *
+	 * Arguments:
+	 *   vsl: The VSL_data context
+	 *     c: A VSL_cursor
+	 *    fo: A FILE* pointer, stdout if NULL
+	 *
+	 * Return values:
+	 *	0:	OK
+	 *    !=0:	Return value from either VSL_Next or VSL_Print
+	 */
+
+int VSL_PrintSet(struct VSL_data *vsl, struct VSL_cursor *cp[], void *fo);
+	/*
+	 * Calls VSL_PrintAll on each cursor in cp[]. If any cursor in cp
+	 * has vxid != -1 it will end with a double line break as a set
+	 * delimeter.
+	 *
+	 * Arguments:
+	 *   vsl: The VSL_data context
+	 *    cp: A NULL-terminated array of VSL_cursor pointers
+	 *    fo: A FILE* pointer, stdout if NULL
+	 *
+	 * Return values:
+	 *	0:	OK
+	 *    !=0:	Return value from either VSL_Next or VSL_PrintAll
+	 */
+
+struct VSLQ *VSLQ_New(struct VSL_data *vsl, struct VSL_cursor **cp,
+    enum VSL_grouping_e grouping, const char *query);
+	/*
+	 * Create a new query context using cp. On success cp is NULLed,
+	 * and will be deleted when deleting the query.
+	 *
+	 * Arguments:
+	 *       vsl: The VSL_data context
+	 *        cp: The cursor to use
+	 *  grouping: VXID grouping to report on
+	 *     query: Query match expression
+	 *
+	 * Return values:
+	 *  non-NULL: OK
+	 *      NULL: Error - see VSL_Error
+	 */
+
+void VSLQ_Delete(struct VSLQ **pvslq);
+	/*
+	 * Delete the query pointed to by pvslq, freeing up the resources
+	 */
+
+typedef int VSLQ_dispatch_f(struct VSL_data *vsl, struct VSL_cursor *cp[],
+    void *priv);
+	/*
+	 * The callback function type for use with VSLQ_Dispatch.
+	 *
+	 * Arguments:
+	 *   vsl: The VSL_data context
+	 *  cp[]: A NULL terminated array of pointer to cursors. Each cursor
+	 *        will iterate over the log records of a single VXID
+	 *  priv: The priv argument from VSL_Dispatch
+	 *
+	 * Return value:
+	 *     0: OK - continue
+	 *   !=0: Makes VSLQ_Dispatch return with this return value immediatly
+	 */
+
+int VSLQ_Dispatch(struct VSLQ *vslq, VSLQ_dispatch_f *func, void *priv);
+	/*
+	 * Process log and call func for each set matching the specified
+	 * query
+	 *
+	 * Arguments:
+	 *  vslq: The VSLQ query
+	 *  func: The callback function to call. Can be NULL to ignore records.
+	 *  priv: An argument passed to func
+	 *
+	 * Return values:
+	 *     0: No more log records available
+	 *   !=0: The return value from either VSL_Next() or func
+	 */
+
+int VSLQ_Flush(struct VSLQ *vslq, VSLQ_dispatch_f *func, void *priv);
+	/*
+	 * Flush any pending record sets from the query.
+	 *
+	 * Arguments:
+	 *  vslq: The VSL context
+	 *  func: The callback function to call. Pass NULL to discard the
+	 *        pending messages
+	 *  priv: An argument passed to func
+	 *
+	 * Return values:
+	 *     0: OK
+	 *   !=0: The return value from func
+	 */
+
 #endif /* VAPI_VSL_H_INCLUDED */
diff --git a/include/vtree.h b/include/vtree.h
new file mode 100644
index 0000000..7692d12
--- /dev/null
+++ b/include/vtree.h
@@ -0,0 +1,763 @@
+/*	$NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $	*/
+/*	$OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $	*/
+/* $FreeBSD: release/9.0.0/sys/sys/tree.h 189204 2009-03-01 04:57:23Z bms $ */
+
+/*-
+ * Copyright 2002 Niels Provos <provos at citi.umich.edu>
+ * All rights reserved.
+ *
+ * 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 ``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 THE AUTHOR 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.
+ */
+
+#ifndef	_VTREE_H_
+#define	_VTREE_H_
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure.  Every operation
+ * on the tree causes a splay to happen.  The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree.  On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n).  The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute.  It fulfills a set of conditions:
+ *	- every search path from the root to a leaf consists of the
+ *	  same number of black nodes,
+ *	- each red node (except for the root) has a black parent,
+ *	- each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define VSPLAY_HEAD(name, type)						\
+struct name {								\
+	struct type *sph_root; /* root of the tree */			\
+}
+
+#define VSPLAY_INITIALIZER(root)						\
+	{ NULL }
+
+#define VSPLAY_INIT(root) do {						\
+	(root)->sph_root = NULL;					\
+} while (/*CONSTCOND*/ 0)
+
+#define VSPLAY_ENTRY(type)						\
+struct {								\
+	struct type *spe_left; /* left element */			\
+	struct type *spe_right; /* right element */			\
+}
+
+#define VSPLAY_LEFT(elm, field)		(elm)->field.spe_left
+#define VSPLAY_RIGHT(elm, field)		(elm)->field.spe_right
+#define VSPLAY_ROOT(head)		(head)->sph_root
+#define VSPLAY_EMPTY(head)		(VSPLAY_ROOT(head) == NULL)
+
+/* VSPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold VSPLAY_{RIGHT,LEFT} */
+#define VSPLAY_ROTATE_RIGHT(head, tmp, field) do {			\
+	VSPLAY_LEFT((head)->sph_root, field) = VSPLAY_RIGHT(tmp, field);	\
+	VSPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
+	(head)->sph_root = tmp;						\
+} while (/*CONSTCOND*/ 0)
+
+#define VSPLAY_ROTATE_LEFT(head, tmp, field) do {			\
+	VSPLAY_RIGHT((head)->sph_root, field) = VSPLAY_LEFT(tmp, field);	\
+	VSPLAY_LEFT(tmp, field) = (head)->sph_root;			\
+	(head)->sph_root = tmp;						\
+} while (/*CONSTCOND*/ 0)
+
+#define VSPLAY_LINKLEFT(head, tmp, field) do {				\
+	VSPLAY_LEFT(tmp, field) = (head)->sph_root;			\
+	tmp = (head)->sph_root;						\
+	(head)->sph_root = VSPLAY_LEFT((head)->sph_root, field);		\
+} while (/*CONSTCOND*/ 0)
+
+#define VSPLAY_LINKRIGHT(head, tmp, field) do {				\
+	VSPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
+	tmp = (head)->sph_root;						\
+	(head)->sph_root = VSPLAY_RIGHT((head)->sph_root, field);	\
+} while (/*CONSTCOND*/ 0)
+
+#define VSPLAY_ASSEMBLE(head, node, left, right, field) do {		\
+	VSPLAY_RIGHT(left, field) = VSPLAY_LEFT((head)->sph_root, field);	\
+	VSPLAY_LEFT(right, field) = VSPLAY_RIGHT((head)->sph_root, field);\
+	VSPLAY_LEFT((head)->sph_root, field) = VSPLAY_RIGHT(node, field);	\
+	VSPLAY_RIGHT((head)->sph_root, field) = VSPLAY_LEFT(node, field);	\
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+
+#define VSPLAY_PROTOTYPE(name, type, field, cmp)				\
+void name##_VSPLAY(struct name *, struct type *);			\
+void name##_VSPLAY_MINMAX(struct name *, int);				\
+struct type *name##_VSPLAY_INSERT(struct name *, struct type *);		\
+struct type *name##_VSPLAY_REMOVE(struct name *, struct type *);		\
+									\
+/* Finds the node with the same key as elm */				\
+static __inline struct type *						\
+name##_VSPLAY_FIND(struct name *head, struct type *elm)			\
+{									\
+	if (VSPLAY_EMPTY(head))						\
+		return(NULL);						\
+	name##_VSPLAY(head, elm);					\
+	if ((cmp)(elm, (head)->sph_root) == 0)				\
+		return (head->sph_root);				\
+	return (NULL);							\
+}									\
+									\
+static __inline struct type *						\
+name##_VSPLAY_NEXT(struct name *head, struct type *elm)			\
+{									\
+	name##_VSPLAY(head, elm);					\
+	if (VSPLAY_RIGHT(elm, field) != NULL) {				\
+		elm = VSPLAY_RIGHT(elm, field);				\
+		while (VSPLAY_LEFT(elm, field) != NULL) {		\
+			elm = VSPLAY_LEFT(elm, field);			\
+		}							\
+	} else								\
+		elm = NULL;						\
+	return (elm);							\
+}									\
+									\
+static __inline struct type *						\
+name##_VSPLAY_MIN_MAX(struct name *head, int val)			\
+{									\
+	name##_VSPLAY_MINMAX(head, val);					\
+        return (VSPLAY_ROOT(head));					\
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define VSPLAY_GENERATE(name, type, field, cmp)				\
+struct type *								\
+name##_VSPLAY_INSERT(struct name *head, struct type *elm)		\
+{									\
+    if (VSPLAY_EMPTY(head)) {						\
+	    VSPLAY_LEFT(elm, field) = VSPLAY_RIGHT(elm, field) = NULL;	\
+    } else {								\
+	    int __comp;							\
+	    name##_VSPLAY(head, elm);					\
+	    __comp = (cmp)(elm, (head)->sph_root);			\
+	    if(__comp < 0) {						\
+		    VSPLAY_LEFT(elm, field) = VSPLAY_LEFT((head)->sph_root, field);\
+		    VSPLAY_RIGHT(elm, field) = (head)->sph_root;		\
+		    VSPLAY_LEFT((head)->sph_root, field) = NULL;		\
+	    } else if (__comp > 0) {					\
+		    VSPLAY_RIGHT(elm, field) = VSPLAY_RIGHT((head)->sph_root, field);\
+		    VSPLAY_LEFT(elm, field) = (head)->sph_root;		\
+		    VSPLAY_RIGHT((head)->sph_root, field) = NULL;	\
+	    } else							\
+		    return ((head)->sph_root);				\
+    }									\
+    (head)->sph_root = (elm);						\
+    return (NULL);							\
+}									\
+									\
+struct type *								\
+name##_VSPLAY_REMOVE(struct name *head, struct type *elm)		\
+{									\
+	struct type *__tmp;						\
+	if (VSPLAY_EMPTY(head))						\
+		return (NULL);						\
+	name##_VSPLAY(head, elm);					\
+	if ((cmp)(elm, (head)->sph_root) == 0) {			\
+		if (VSPLAY_LEFT((head)->sph_root, field) == NULL) {	\
+			(head)->sph_root = VSPLAY_RIGHT((head)->sph_root, field);\
+		} else {						\
+			__tmp = VSPLAY_RIGHT((head)->sph_root, field);	\
+			(head)->sph_root = VSPLAY_LEFT((head)->sph_root, field);\
+			name##_VSPLAY(head, elm);			\
+			VSPLAY_RIGHT((head)->sph_root, field) = __tmp;	\
+		}							\
+		return (elm);						\
+	}								\
+	return (NULL);							\
+}									\
+									\
+void									\
+name##_VSPLAY(struct name *head, struct type *elm)			\
+{									\
+	struct type __node, *__left, *__right, *__tmp;			\
+	int __comp;							\
+\
+	VSPLAY_LEFT(&__node, field) = VSPLAY_RIGHT(&__node, field) = NULL;\
+	__left = __right = &__node;					\
+\
+	while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) {		\
+		if (__comp < 0) {					\
+			__tmp = VSPLAY_LEFT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if ((cmp)(elm, __tmp) < 0){			\
+				VSPLAY_ROTATE_RIGHT(head, __tmp, field);	\
+				if (VSPLAY_LEFT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			VSPLAY_LINKLEFT(head, __right, field);		\
+		} else if (__comp > 0) {				\
+			__tmp = VSPLAY_RIGHT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if ((cmp)(elm, __tmp) > 0){			\
+				VSPLAY_ROTATE_LEFT(head, __tmp, field);	\
+				if (VSPLAY_RIGHT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			VSPLAY_LINKRIGHT(head, __left, field);		\
+		}							\
+	}								\
+	VSPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
+}									\
+									\
+/* Splay with either the minimum or the maximum element			\
+ * Used to find minimum or maximum element in tree.			\
+ */									\
+void name##_VSPLAY_MINMAX(struct name *head, int __comp) \
+{									\
+	struct type __node, *__left, *__right, *__tmp;			\
+\
+	VSPLAY_LEFT(&__node, field) = VSPLAY_RIGHT(&__node, field) = NULL;\
+	__left = __right = &__node;					\
+\
+	while (1) {							\
+		if (__comp < 0) {					\
+			__tmp = VSPLAY_LEFT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if (__comp < 0){				\
+				VSPLAY_ROTATE_RIGHT(head, __tmp, field);	\
+				if (VSPLAY_LEFT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			VSPLAY_LINKLEFT(head, __right, field);		\
+		} else if (__comp > 0) {				\
+			__tmp = VSPLAY_RIGHT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if (__comp > 0) {				\
+				VSPLAY_ROTATE_LEFT(head, __tmp, field);	\
+				if (VSPLAY_RIGHT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			VSPLAY_LINKRIGHT(head, __left, field);		\
+		}							\
+	}								\
+	VSPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
+}
+
+#define VSPLAY_NEGINF	-1
+#define VSPLAY_INF	1
+
+#define VSPLAY_INSERT(name, x, y)	name##_VSPLAY_INSERT(x, y)
+#define VSPLAY_REMOVE(name, x, y)	name##_VSPLAY_REMOVE(x, y)
+#define VSPLAY_FIND(name, x, y)		name##_VSPLAY_FIND(x, y)
+#define VSPLAY_NEXT(name, x, y)		name##_VSPLAY_NEXT(x, y)
+#define VSPLAY_MIN(name, x)		(VSPLAY_EMPTY(x) ? NULL	\
+					: name##_VSPLAY_MIN_MAX(x, VSPLAY_NEGINF))
+#define VSPLAY_MAX(name, x)		(VSPLAY_EMPTY(x) ? NULL	\
+					: name##_VSPLAY_MIN_MAX(x, VSPLAY_INF))
+
+#define VSPLAY_FOREACH(x, name, head)					\
+	for ((x) = VSPLAY_MIN(name, head);				\
+	     (x) != NULL;						\
+	     (x) = VSPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-black tree */
+#define VRB_HEAD(name, type)						\
+struct name {								\
+	struct type *rbh_root; /* root of the tree */			\
+}
+
+#define VRB_INITIALIZER(root)						\
+	{ NULL }
+
+#define VRB_INIT(root) do {						\
+	(root)->rbh_root = NULL;					\
+} while (/*CONSTCOND*/ 0)
+
+#define VRB_BLACK	0
+#define VRB_RED		1
+#define VRB_ENTRY(type)							\
+struct {								\
+	struct type *rbe_left;		/* left element */		\
+	struct type *rbe_right;		/* right element */		\
+	struct type *rbe_parent;	/* parent element */		\
+	int rbe_color;			/* node color */		\
+}
+
+#define VRB_LEFT(elm, field)		(elm)->field.rbe_left
+#define VRB_RIGHT(elm, field)		(elm)->field.rbe_right
+#define VRB_PARENT(elm, field)		(elm)->field.rbe_parent
+#define VRB_COLOR(elm, field)		(elm)->field.rbe_color
+#define VRB_ROOT(head)			(head)->rbh_root
+#define VRB_EMPTY(head)			(VRB_ROOT(head) == NULL)
+
+#define VRB_SET(elm, parent, field) do {					\
+	VRB_PARENT(elm, field) = parent;					\
+	VRB_LEFT(elm, field) = VRB_RIGHT(elm, field) = NULL;		\
+	VRB_COLOR(elm, field) = VRB_RED;					\
+} while (/*CONSTCOND*/ 0)
+
+#define VRB_SET_BLACKRED(black, red, field) do {				\
+	VRB_COLOR(black, field) = VRB_BLACK;				\
+	VRB_COLOR(red, field) = VRB_RED;					\
+} while (/*CONSTCOND*/ 0)
+
+#ifndef VRB_AUGMENT
+#define VRB_AUGMENT(x)	do {} while (0)
+#endif
+
+#define VRB_ROTATE_LEFT(head, elm, tmp, field) do {			\
+	(tmp) = VRB_RIGHT(elm, field);					\
+	if ((VRB_RIGHT(elm, field) = VRB_LEFT(tmp, field)) != NULL) {	\
+		VRB_PARENT(VRB_LEFT(tmp, field), field) = (elm);		\
+	}								\
+	VRB_AUGMENT(elm);						\
+	if ((VRB_PARENT(tmp, field) = VRB_PARENT(elm, field)) != NULL) {	\
+		if ((elm) == VRB_LEFT(VRB_PARENT(elm, field), field))	\
+			VRB_LEFT(VRB_PARENT(elm, field), field) = (tmp);	\
+		else							\
+			VRB_RIGHT(VRB_PARENT(elm, field), field) = (tmp);	\
+	} else								\
+		(head)->rbh_root = (tmp);				\
+	VRB_LEFT(tmp, field) = (elm);					\
+	VRB_PARENT(elm, field) = (tmp);					\
+	VRB_AUGMENT(tmp);						\
+	if ((VRB_PARENT(tmp, field)))					\
+		VRB_AUGMENT(VRB_PARENT(tmp, field));			\
+} while (/*CONSTCOND*/ 0)
+
+#define VRB_ROTATE_RIGHT(head, elm, tmp, field) do {			\
+	(tmp) = VRB_LEFT(elm, field);					\
+	if ((VRB_LEFT(elm, field) = VRB_RIGHT(tmp, field)) != NULL) {	\
+		VRB_PARENT(VRB_RIGHT(tmp, field), field) = (elm);		\
+	}								\
+	VRB_AUGMENT(elm);						\
+	if ((VRB_PARENT(tmp, field) = VRB_PARENT(elm, field)) != NULL) {	\
+		if ((elm) == VRB_LEFT(VRB_PARENT(elm, field), field))	\
+			VRB_LEFT(VRB_PARENT(elm, field), field) = (tmp);	\
+		else							\
+			VRB_RIGHT(VRB_PARENT(elm, field), field) = (tmp);	\
+	} else								\
+		(head)->rbh_root = (tmp);				\
+	VRB_RIGHT(tmp, field) = (elm);					\
+	VRB_PARENT(elm, field) = (tmp);					\
+	VRB_AUGMENT(tmp);						\
+	if ((VRB_PARENT(tmp, field)))					\
+		VRB_AUGMENT(VRB_PARENT(tmp, field));			\
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+#define	VRB_PROTOTYPE(name, type, field, cmp)				\
+	VRB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
+#define	VRB_PROTOTYPE_STATIC(name, type, field, cmp)			\
+	VRB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
+#define VRB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr)		\
+attr void name##_VRB_INSERT_COLOR(struct name *, struct type *);		\
+attr void name##_VRB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+attr struct type *name##_VRB_REMOVE(struct name *, struct type *);	\
+attr struct type *name##_VRB_INSERT(struct name *, struct type *);	\
+attr struct type *name##_VRB_FIND(struct name *, struct type *);		\
+attr struct type *name##_VRB_NFIND(struct name *, struct type *);	\
+attr struct type *name##_VRB_NEXT(struct type *);			\
+attr struct type *name##_VRB_PREV(struct type *);			\
+attr struct type *name##_VRB_MINMAX(struct name *, int);			\
+									\
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define	VRB_GENERATE(name, type, field, cmp)				\
+	VRB_GENERATE_INTERNAL(name, type, field, cmp,)
+#define	VRB_GENERATE_STATIC(name, type, field, cmp)			\
+	VRB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
+#define VRB_GENERATE_INTERNAL(name, type, field, cmp, attr)		\
+attr void								\
+name##_VRB_INSERT_COLOR(struct name *head, struct type *elm)		\
+{									\
+	struct type *parent, *gparent, *tmp;				\
+	while ((parent = VRB_PARENT(elm, field)) != NULL &&		\
+	    VRB_COLOR(parent, field) == VRB_RED) {			\
+		gparent = VRB_PARENT(parent, field);			\
+		if (parent == VRB_LEFT(gparent, field)) {		\
+			tmp = VRB_RIGHT(gparent, field);			\
+			if (tmp && VRB_COLOR(tmp, field) == VRB_RED) {	\
+				VRB_COLOR(tmp, field) = VRB_BLACK;	\
+				VRB_SET_BLACKRED(parent, gparent, field);\
+				elm = gparent;				\
+				continue;				\
+			}						\
+			if (VRB_RIGHT(parent, field) == elm) {		\
+				VRB_ROTATE_LEFT(head, parent, tmp, field);\
+				tmp = parent;				\
+				parent = elm;				\
+				elm = tmp;				\
+			}						\
+			VRB_SET_BLACKRED(parent, gparent, field);	\
+			VRB_ROTATE_RIGHT(head, gparent, tmp, field);	\
+		} else {						\
+			tmp = VRB_LEFT(gparent, field);			\
+			if (tmp && VRB_COLOR(tmp, field) == VRB_RED) {	\
+				VRB_COLOR(tmp, field) = VRB_BLACK;	\
+				VRB_SET_BLACKRED(parent, gparent, field);\
+				elm = gparent;				\
+				continue;				\
+			}						\
+			if (VRB_LEFT(parent, field) == elm) {		\
+				VRB_ROTATE_RIGHT(head, parent, tmp, field);\
+				tmp = parent;				\
+				parent = elm;				\
+				elm = tmp;				\
+			}						\
+			VRB_SET_BLACKRED(parent, gparent, field);	\
+			VRB_ROTATE_LEFT(head, gparent, tmp, field);	\
+		}							\
+	}								\
+	VRB_COLOR(head->rbh_root, field) = VRB_BLACK;			\
+}									\
+									\
+attr void								\
+name##_VRB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
+{									\
+	struct type *tmp;						\
+	while ((elm == NULL || VRB_COLOR(elm, field) == VRB_BLACK) &&	\
+	    elm != VRB_ROOT(head)) {					\
+		if (VRB_LEFT(parent, field) == elm) {			\
+			tmp = VRB_RIGHT(parent, field);			\
+			if (VRB_COLOR(tmp, field) == VRB_RED) {		\
+				VRB_SET_BLACKRED(tmp, parent, field);	\
+				VRB_ROTATE_LEFT(head, parent, tmp, field);\
+				tmp = VRB_RIGHT(parent, field);		\
+			}						\
+			if ((VRB_LEFT(tmp, field) == NULL ||		\
+			    VRB_COLOR(VRB_LEFT(tmp, field), field) == VRB_BLACK) &&\
+			    (VRB_RIGHT(tmp, field) == NULL ||		\
+			    VRB_COLOR(VRB_RIGHT(tmp, field), field) == VRB_BLACK)) {\
+				VRB_COLOR(tmp, field) = VRB_RED;		\
+				elm = parent;				\
+				parent = VRB_PARENT(elm, field);		\
+			} else {					\
+				if (VRB_RIGHT(tmp, field) == NULL ||	\
+				    VRB_COLOR(VRB_RIGHT(tmp, field), field) == VRB_BLACK) {\
+					struct type *oleft;		\
+					if ((oleft = VRB_LEFT(tmp, field)) \
+					    != NULL)			\
+						VRB_COLOR(oleft, field) = VRB_BLACK;\
+					VRB_COLOR(tmp, field) = VRB_RED;	\
+					VRB_ROTATE_RIGHT(head, tmp, oleft, field);\
+					tmp = VRB_RIGHT(parent, field);	\
+				}					\
+				VRB_COLOR(tmp, field) = VRB_COLOR(parent, field);\
+				VRB_COLOR(parent, field) = VRB_BLACK;	\
+				if (VRB_RIGHT(tmp, field))		\
+					VRB_COLOR(VRB_RIGHT(tmp, field), field) = VRB_BLACK;\
+				VRB_ROTATE_LEFT(head, parent, tmp, field);\
+				elm = VRB_ROOT(head);			\
+				break;					\
+			}						\
+		} else {						\
+			tmp = VRB_LEFT(parent, field);			\
+			if (VRB_COLOR(tmp, field) == VRB_RED) {		\
+				VRB_SET_BLACKRED(tmp, parent, field);	\
+				VRB_ROTATE_RIGHT(head, parent, tmp, field);\
+				tmp = VRB_LEFT(parent, field);		\
+			}						\
+			if ((VRB_LEFT(tmp, field) == NULL ||		\
+			    VRB_COLOR(VRB_LEFT(tmp, field), field) == VRB_BLACK) &&\
+			    (VRB_RIGHT(tmp, field) == NULL ||		\
+			    VRB_COLOR(VRB_RIGHT(tmp, field), field) == VRB_BLACK)) {\
+				VRB_COLOR(tmp, field) = VRB_RED;		\
+				elm = parent;				\
+				parent = VRB_PARENT(elm, field);		\
+			} else {					\
+				if (VRB_LEFT(tmp, field) == NULL ||	\
+				    VRB_COLOR(VRB_LEFT(tmp, field), field) == VRB_BLACK) {\
+					struct type *oright;		\
+					if ((oright = VRB_RIGHT(tmp, field)) \
+					    != NULL)			\
+						VRB_COLOR(oright, field) = VRB_BLACK;\
+					VRB_COLOR(tmp, field) = VRB_RED;	\
+					VRB_ROTATE_LEFT(head, tmp, oright, field);\
+					tmp = VRB_LEFT(parent, field);	\
+				}					\
+				VRB_COLOR(tmp, field) = VRB_COLOR(parent, field);\
+				VRB_COLOR(parent, field) = VRB_BLACK;	\
+				if (VRB_LEFT(tmp, field))		\
+					VRB_COLOR(VRB_LEFT(tmp, field), field) = VRB_BLACK;\
+				VRB_ROTATE_RIGHT(head, parent, tmp, field);\
+				elm = VRB_ROOT(head);			\
+				break;					\
+			}						\
+		}							\
+	}								\
+	if (elm)							\
+		VRB_COLOR(elm, field) = VRB_BLACK;			\
+}									\
+									\
+attr struct type *							\
+name##_VRB_REMOVE(struct name *head, struct type *elm)			\
+{									\
+	struct type *child, *parent, *old = elm;			\
+	int color;							\
+	if (VRB_LEFT(elm, field) == NULL)				\
+		child = VRB_RIGHT(elm, field);				\
+	else if (VRB_RIGHT(elm, field) == NULL)				\
+		child = VRB_LEFT(elm, field);				\
+	else {								\
+		struct type *left;					\
+		elm = VRB_RIGHT(elm, field);				\
+		while ((left = VRB_LEFT(elm, field)) != NULL)		\
+			elm = left;					\
+		child = VRB_RIGHT(elm, field);				\
+		parent = VRB_PARENT(elm, field);				\
+		color = VRB_COLOR(elm, field);				\
+		if (child)						\
+			VRB_PARENT(child, field) = parent;		\
+		if (parent) {						\
+			if (VRB_LEFT(parent, field) == elm)		\
+				VRB_LEFT(parent, field) = child;		\
+			else						\
+				VRB_RIGHT(parent, field) = child;	\
+			VRB_AUGMENT(parent);				\
+		} else							\
+			VRB_ROOT(head) = child;				\
+		if (VRB_PARENT(elm, field) == old)			\
+			parent = elm;					\
+		(elm)->field = (old)->field;				\
+		if (VRB_PARENT(old, field)) {				\
+			if (VRB_LEFT(VRB_PARENT(old, field), field) == old)\
+				VRB_LEFT(VRB_PARENT(old, field), field) = elm;\
+			else						\
+				VRB_RIGHT(VRB_PARENT(old, field), field) = elm;\
+			VRB_AUGMENT(VRB_PARENT(old, field));		\
+		} else							\
+			VRB_ROOT(head) = elm;				\
+		VRB_PARENT(VRB_LEFT(old, field), field) = elm;		\
+		if (VRB_RIGHT(old, field))				\
+			VRB_PARENT(VRB_RIGHT(old, field), field) = elm;	\
+		if (parent) {						\
+			left = parent;					\
+			do {						\
+				VRB_AUGMENT(left);			\
+			} while ((left = VRB_PARENT(left, field)) != NULL); \
+		}							\
+		goto color;						\
+	}								\
+	parent = VRB_PARENT(elm, field);					\
+	color = VRB_COLOR(elm, field);					\
+	if (child)							\
+		VRB_PARENT(child, field) = parent;			\
+	if (parent) {							\
+		if (VRB_LEFT(parent, field) == elm)			\
+			VRB_LEFT(parent, field) = child;			\
+		else							\
+			VRB_RIGHT(parent, field) = child;		\
+		VRB_AUGMENT(parent);					\
+	} else								\
+		VRB_ROOT(head) = child;					\
+color:									\
+	if (color == VRB_BLACK)						\
+		name##_VRB_REMOVE_COLOR(head, parent, child);		\
+	return (old);							\
+}									\
+									\
+/* Inserts a node into the RB tree */					\
+attr struct type *							\
+name##_VRB_INSERT(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp;						\
+	struct type *parent = NULL;					\
+	int comp = 0;							\
+	tmp = VRB_ROOT(head);						\
+	while (tmp) {							\
+		parent = tmp;						\
+		comp = (cmp)(elm, parent);				\
+		if (comp < 0)						\
+			tmp = VRB_LEFT(tmp, field);			\
+		else if (comp > 0)					\
+			tmp = VRB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	VRB_SET(elm, parent, field);					\
+	if (parent != NULL) {						\
+		if (comp < 0)						\
+			VRB_LEFT(parent, field) = elm;			\
+		else							\
+			VRB_RIGHT(parent, field) = elm;			\
+		VRB_AUGMENT(parent);					\
+	} else								\
+		VRB_ROOT(head) = elm;					\
+	name##_VRB_INSERT_COLOR(head, elm);				\
+	return (NULL);							\
+}									\
+									\
+/* Finds the node with the same key as elm */				\
+attr struct type *							\
+name##_VRB_FIND(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp = VRB_ROOT(head);				\
+	int comp;							\
+	while (tmp) {							\
+		comp = cmp(elm, tmp);					\
+		if (comp < 0)						\
+			tmp = VRB_LEFT(tmp, field);			\
+		else if (comp > 0)					\
+			tmp = VRB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	return (NULL);							\
+}									\
+									\
+/* Finds the first node greater than or equal to the search key */	\
+attr struct type *							\
+name##_VRB_NFIND(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp = VRB_ROOT(head);				\
+	struct type *res = NULL;					\
+	int comp;							\
+	while (tmp) {							\
+		comp = cmp(elm, tmp);					\
+		if (comp < 0) {						\
+			res = tmp;					\
+			tmp = VRB_LEFT(tmp, field);			\
+		}							\
+		else if (comp > 0)					\
+			tmp = VRB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	return (res);							\
+}									\
+									\
+/* ARGSUSED */								\
+attr struct type *							\
+name##_VRB_NEXT(struct type *elm)					\
+{									\
+	if (VRB_RIGHT(elm, field)) {					\
+		elm = VRB_RIGHT(elm, field);				\
+		while (VRB_LEFT(elm, field))				\
+			elm = VRB_LEFT(elm, field);			\
+	} else {							\
+		if (VRB_PARENT(elm, field) &&				\
+		    (elm == VRB_LEFT(VRB_PARENT(elm, field), field)))	\
+			elm = VRB_PARENT(elm, field);			\
+		else {							\
+			while (VRB_PARENT(elm, field) &&			\
+			    (elm == VRB_RIGHT(VRB_PARENT(elm, field), field)))\
+				elm = VRB_PARENT(elm, field);		\
+			elm = VRB_PARENT(elm, field);			\
+		}							\
+	}								\
+	return (elm);							\
+}									\
+									\
+/* ARGSUSED */								\
+attr struct type *							\
+name##_VRB_PREV(struct type *elm)					\
+{									\
+	if (VRB_LEFT(elm, field)) {					\
+		elm = VRB_LEFT(elm, field);				\
+		while (VRB_RIGHT(elm, field))				\
+			elm = VRB_RIGHT(elm, field);			\
+	} else {							\
+		if (VRB_PARENT(elm, field) &&				\
+		    (elm == VRB_RIGHT(VRB_PARENT(elm, field), field)))	\
+			elm = VRB_PARENT(elm, field);			\
+		else {							\
+			while (VRB_PARENT(elm, field) &&			\
+			    (elm == VRB_LEFT(VRB_PARENT(elm, field), field)))\
+				elm = VRB_PARENT(elm, field);		\
+			elm = VRB_PARENT(elm, field);			\
+		}							\
+	}								\
+	return (elm);							\
+}									\
+									\
+attr struct type *							\
+name##_VRB_MINMAX(struct name *head, int val)				\
+{									\
+	struct type *tmp = VRB_ROOT(head);				\
+	struct type *parent = NULL;					\
+	while (tmp) {							\
+		parent = tmp;						\
+		if (val < 0)						\
+			tmp = VRB_LEFT(tmp, field);			\
+		else							\
+			tmp = VRB_RIGHT(tmp, field);			\
+	}								\
+	return (parent);						\
+}
+
+#define VRB_NEGINF	-1
+#define VRB_INF	1
+
+#define VRB_INSERT(name, x, y)	name##_VRB_INSERT(x, y)
+#define VRB_REMOVE(name, x, y)	name##_VRB_REMOVE(x, y)
+#define VRB_FIND(name, x, y)	name##_VRB_FIND(x, y)
+#define VRB_NFIND(name, x, y)	name##_VRB_NFIND(x, y)
+#define VRB_NEXT(name, x, y)	name##_VRB_NEXT(y)
+#define VRB_PREV(name, x, y)	name##_VRB_PREV(y)
+#define VRB_MIN(name, x)		name##_VRB_MINMAX(x, VRB_NEGINF)
+#define VRB_MAX(name, x)		name##_VRB_MINMAX(x, VRB_INF)
+
+#define VRB_FOREACH(x, name, head)					\
+	for ((x) = VRB_MIN(name, head);					\
+	     (x) != NULL;						\
+	     (x) = name##_VRB_NEXT(x))
+
+#define VRB_FOREACH_FROM(x, name, y)					\
+	for ((x) = (y);							\
+	    ((x) != NULL) && ((y) = name##_VRB_NEXT(x), (x) != NULL);	\
+	     (x) = (y))
+
+#define VRB_FOREACH_SAFE(x, name, head, y)				\
+	for ((x) = VRB_MIN(name, head);					\
+	    ((x) != NULL) && ((y) = name##_VRB_NEXT(x), (x) != NULL);	\
+	     (x) = (y))
+
+#define VRB_FOREACH_REVERSE(x, name, head)				\
+	for ((x) = VRB_MAX(name, head);					\
+	     (x) != NULL;						\
+	     (x) = name##_VRB_PREV(x))
+
+#define VRB_FOREACH_REVERSE_FROM(x, name, y)				\
+	for ((x) = (y);							\
+	    ((x) != NULL) && ((y) = name##_VRB_PREV(x), (x) != NULL);	\
+	     (x) = (y))
+
+#define VRB_FOREACH_REVERSE_SAFE(x, name, head, y)			\
+	for ((x) = VRB_MAX(name, head);					\
+	    ((x) != NULL) && ((y) = name##_VRB_PREV(x), (x) != NULL);	\
+	     (x) = (y))
+
+#endif	/* _VTREE_H_ */
diff --git a/lib/libvarnishapi/Makefile.am b/lib/libvarnishapi/Makefile.am
index 6069534..ac4fabc 100644
--- a/lib/libvarnishapi/Makefile.am
+++ b/lib/libvarnishapi/Makefile.am
@@ -27,6 +27,8 @@ libvarnishapi_la_SOURCES = \
 	vsm.c \
 	vsl_arg.c \
 	vsl_cursor.c \
+	vsl_dispatch.c \
+	vsl_query.c \
 	vsl.c \
 	vsc.c \
 	libvarnishapi.map
diff --git a/lib/libvarnishapi/libvarnishapi.map b/lib/libvarnishapi/libvarnishapi.map
index a25aa94..6099c4e 100644
--- a/lib/libvarnishapi/libvarnishapi.map
+++ b/lib/libvarnishapi/libvarnishapi.map
@@ -104,6 +104,13 @@ LIBVARNISHAPI_1.3 {
 	VSL_DeleteCursor;
 	VSL_Next;
 	VSL_Match;
-	VSL_Print;
+	VSL_PrintVXID;
+	VSL_PrintLevel;
+	VSL_PrintAll;
+	VSL_PrintSet;
+	VSLQ_New;
+	VSLQ_Delete;
+	VSLQ_Dispatch;
+	VSLQ_Flush;
 	# Variables:
 } LIBVARNISHAPI_1.0;
diff --git a/lib/libvarnishapi/vsl.c b/lib/libvarnishapi/vsl.c
index 963afce..994d76d 100644
--- a/lib/libvarnishapi/vsl.c
+++ b/lib/libvarnishapi/vsl.c
@@ -155,7 +155,7 @@ VSL_Match(struct VSL_data *vsl, const struct VSL_cursor *c)
 }
 
 int
-VSL_Print(struct VSL_data *vsl, const struct VSL_cursor *c, void *fo)
+VSL_PrintVXID(struct VSL_data *vsl, const struct VSL_cursor *c, void *fo)
 {
 	enum VSL_tag_e tag;
 	uint32_t vxid;
@@ -200,3 +200,101 @@ VSL_Print(struct VSL_data *vsl, const struct VSL_cursor *c, void *fo)
 		return (-5);
 	return (0);
 }
+
+int
+VSL_PrintLevel(struct VSL_data *vsl, const struct VSL_cursor *c, void *fo)
+{
+	enum VSL_tag_e tag;
+	unsigned len, lvl;
+	const char *data;
+	int type;
+	int i;
+
+	CHECK_OBJ_NOTNULL(vsl, VSL_MAGIC);
+	if (c == NULL || c->ptr == NULL)
+		return (0);
+	if (fo == NULL)
+		fo = stdout;
+
+	tag = VSL_TAG(c->ptr);
+	len = VSL_LEN(c->ptr);
+	type = VSL_CLIENT(c->ptr) ? 'c' : VSL_BACKEND(c->ptr) ? 'b' : '-';
+	data = VSL_CDATA(c->ptr);
+	lvl = c->level;
+	if (tag == SLT_Debug) {
+		i = fprintf(fo, "%2u %-15s %c \"", lvl, VSL_tags[tag],
+		    type);
+		if (i < 0)
+			return (-5);
+		while (len-- > 0) {
+			if (*data >= ' ' && *data <= '~')
+				i = fprintf(fo, "%c", *data);
+			else
+				i = fprintf(fo, "%%%02x",
+				    (unsigned char)*data);
+			if (i < 0)
+				return (-5);
+			data++;
+		}
+		i = fprintf(fo, "\"\n");
+		if (i < 0)
+			return (-5);
+		return (0);
+	}
+	i = fprintf(fo, "%2u %-15s %c %.*s\n",
+	    lvl, VSL_tags[tag], type, (int)len, data);
+	if (i < 0)
+		return (-5);
+	return (0);
+}
+
+int
+VSL_PrintAll(struct VSL_data *vsl, struct VSL_cursor *c, void *fo)
+{
+	int i;
+
+	if (c == NULL)
+		return (0);
+	if (c->vxid >= 0) {
+		i = fprintf(fo, "vv VXID: %11u vv\n", c->vxid);
+		if (i < 0)
+			return (-5);
+	}
+	while (1) {
+		i = VSL_Next(c);
+		if (i <= 0)
+			return (i);
+		if (!VSL_Match(vsl, c))
+			continue;
+		if (c->vxid < 0)
+			i = VSL_PrintVXID(vsl, c, fo);
+		else
+			i = VSL_PrintLevel(vsl, c, fo);
+		if (i != 0)
+			return (i);
+	}
+}
+
+int
+VSL_PrintSet(struct VSL_data *vsl, struct VSL_cursor *cp[], void *fo)
+{
+	int i;
+	int delim = 0;
+	struct VSL_cursor *c;
+
+	c = cp[0];
+	while (c) {
+		if (c->vxid >= 0)
+			delim = 1;
+		i = VSL_PrintAll(vsl, c, fo);
+		if (i)
+			return (i);
+		c = *++cp;
+	}
+	if (delim) {
+		i = fprintf(fo, "\n");
+		if (i < 0)
+			return (-5);
+	}
+	return (0);
+}
diff --git a/lib/libvarnishapi/vsl_api.h b/lib/libvarnishapi/vsl_api.h
index f03812e..a03aef7 100644
--- a/lib/libvarnishapi/vsl_api.h
+++ b/lib/libvarnishapi/vsl_api.h
@@ -37,10 +37,12 @@
 
 int vsl_diag(struct VSL_data *vsl, const char *fmt, ...)
     __printflike(2, 3);
+int vsl_skip(struct VSL_cursor *c, ssize_t words);
 
 typedef void vslc_delete_f(void *);
 typedef int vslc_next_f(void *);
 typedef int vslc_reset_f(void *);
+typedef int vslc_skip_f(void *, ssize_t words);
 
 struct vslc {
 	struct VSL_cursor		c;
@@ -50,6 +52,7 @@ struct vslc {
 	vslc_delete_f			*delete;
 	vslc_next_f			*next;
 	vslc_reset_f			*reset;
+	vslc_skip_f			*skip;
 };
 
 struct VSL_data {
@@ -66,3 +69,10 @@ struct VSL_data {
 	struct vbitmap			*vbm_select;
 	struct vbitmap			*vbm_supress;
 };
+
+/* vsl_query.c */
+struct vslq_query;
+struct vslq_query *vslq_newquery(struct VSL_data *vsl,
+    enum VSL_grouping_e grouping, const char *query);
+void vslq_deletequery(struct vslq_query **pquery);
+int vslq_runquery(struct vslq_query *query, struct VSL_cursor *cp[]);
diff --git a/lib/libvarnishapi/vsl_cursor.c b/lib/libvarnishapi/vsl_cursor.c
index f0d535b..bf4d1ec 100644
--- a/lib/libvarnishapi/vsl_cursor.c
+++ b/lib/libvarnishapi/vsl_cursor.c
@@ -169,6 +169,23 @@ vslc_vsm_reset(void *cursor)
 	return (0);
 }
 
+static int
+vslc_vsm_skip(void *cursor, ssize_t words)
+{
+	struct vslc_vsm *c;
+
+	CAST_OBJ_NOTNULL(c, cursor, VSLC_VSM_MAGIC);
+	if (words < 0)
+		return (-1);
+
+	c->next += words;
+	assert(c->next >= c->head->log);
+	assert(c->next < c->end);
+	c->c.c.ptr = NULL;
+
+	return (0);
+}
+
 struct VSL_cursor *
 VSL_CursorVSM(struct VSL_data *vsl, struct VSM_data *vsm, int tail)
 {
@@ -199,10 +216,13 @@ VSL_CursorVSM(struct VSL_data *vsl, struct VSM_data *vsm, int tail)
 		vsl_diag(vsl, "Out of memory\n");
 		return (NULL);
 	}
+	c->c.c.vxid = -1;	/* N/A to this cursor type */
+	c->c.c.shmptr_ok = 1;
 	c->c.magic = VSLC_MAGIC;
 	c->c.delete = vslc_vsm_delete;
 	c->c.next = vslc_vsm_next;
 	c->c.reset = vslc_vsm_reset;
+	c->c.skip = vslc_vsm_skip;
 
 	c->vsm = vsm;
 	c->vf = vf;
@@ -347,6 +367,7 @@ VSL_CursorFile(struct VSL_data *vsl, const char *name)
 		vsl_diag(vsl, "Out of memory\n");
 		return (NULL);
 	}
+	c->c.c.vxid = -1;	/* N/A to this cursor type */
 	c->c.magic = VSLC_MAGIC;
 	c->c.delete = vslc_file_delete;
 	c->c.next = vslc_file_next;
@@ -391,3 +412,14 @@ VSL_Next(struct VSL_cursor *cursor)
 	AN(c->next);
 	return ((c->next)(c));
 }
+
+int
+vsl_skip(struct VSL_cursor *cursor, ssize_t words)
+{
+	struct vslc *c;
+
+	CAST_OBJ_NOTNULL(c, (void *)cursor, VSLC_MAGIC);
+	if (c->skip == NULL)
+		return (-1);
+	return ((c->skip)(c, words));
+}
diff --git a/lib/libvarnishapi/vsl_dispatch.c b/lib/libvarnishapi/vsl_dispatch.c
new file mode 100644
index 0000000..61b0518
--- /dev/null
+++ b/lib/libvarnishapi/vsl_dispatch.c
@@ -0,0 +1,924 @@
+/*-
+ * 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 <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+#include "vas.h"
+#include "miniobj.h"
+#include "vqueue.h"
+#include "vtree.h"
+#include "vtim.h"
+
+#include "vapi/vsl.h"
+#include "vsl_api.h"
+
+#define VTX_CACHE 10
+#define VTX_BUFSIZE_MIN 64
+
+enum vtx_type_e {
+	vtx_t_unknown,
+	vtx_t_sess,
+	vtx_t_req,
+	vtx_t_esireq,
+	vtx_t_bereq,
+};
+
+enum vtx_link_e {
+	vtx_l_sess,
+	vtx_l_req,
+	vtx_l_esireq,
+	vtx_l_bereq,
+};
+
+struct vtx_key {
+	unsigned		vxid;
+	VRB_ENTRY(vtx_key)	entry;
+};
+VRB_HEAD(vtx_tree, vtx_key);
+
+struct vtx {
+	struct vtx_key		key;
+	unsigned		magic;
+#define VTX_MAGIC		0xACC21D09
+	VTAILQ_ENTRY(vtx)	list_child;
+	VTAILQ_ENTRY(vtx)	list_incomplete;
+
+	double			t_start;
+	unsigned		flags;
+#define VTX_F_SHM		0x1
+#define VTX_F_COMPLETE		0x2
+#define VTX_F_READY		0x4
+
+	enum vtx_type_e		type;
+
+	struct vtx		*parent;
+	VTAILQ_HEAD(,vtx)	child;
+	unsigned		n_child;
+	unsigned		n_childready;
+	unsigned		n_descend;
+
+	const uint32_t		*start;
+	ssize_t			len;
+	unsigned		index;
+
+	uint32_t		*buf;
+	ssize_t			bufsize;
+};
+
+struct vslc_raw {
+	struct vslc		c;
+	unsigned		magic;
+#define VSLC_RAW_MAGIC		0x247EBD44
+
+	const uint32_t		*start;
+	ssize_t			len;
+	const uint32_t		*next;
+};
+
+struct vslc_vtx {
+	struct vslc		c;
+	unsigned		magic;
+#define VSLC_VTX_MAGIC		0x74C6523F
+
+	struct vtx		*vtx;
+	const uint32_t		*next;
+};
+
+struct VSLQ {
+	unsigned		magic;
+#define VSLQ_MAGIC		0x23A8BE97
+
+	struct VSL_data		*vsl;
+	struct VSL_cursor	*c;
+	struct vslq_query	*query;
+
+	enum VSL_grouping_e	grouping;
+
+	struct vtx_tree		tree;
+
+	VTAILQ_HEAD(,vtx)	incomplete;
+	unsigned		n_incomplete;
+
+	VTAILQ_HEAD(,vtx)	cache;
+	unsigned		n_cache;
+};
+
+static inline int
+vtx_keycmp(const struct vtx_key *a, const struct vtx_key *b)
+{
+	if (a->vxid < b->vxid)
+		return (-1);
+	if (a->vxid > b->vxid)
+		return (1);
+	return (0);
+}
+
+VRB_PROTOTYPE(vtx_tree, vtx_key, entry, vtx_keycmp);
+VRB_GENERATE(vtx_tree, vtx_key, entry, vtx_keycmp);
+
+static int
+vtx_diag(struct vtx *vtx, const char *fmt, ...)
+{
+	va_list ap;
+
+	/* XXX: Prepend diagnostic message on vtx as a synthetic log
+	   record. For now print to stderr */
+	fprintf(stderr, "vtx_diag <%u>: ", vtx->key.vxid);
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+
+	return (-1);
+}
+
+static int
+vtx_diag_tag(struct vtx *vtx, const uint32_t *ptr, const char *reason)
+{
+	return (vtx_diag(vtx, "%s (%s: %.*s)", reason, VSL_tags[VSL_TAG(ptr)],
+		(int)VSL_LEN(ptr), VSL_CDATA(ptr)));
+}
+
+static int
+vslc_raw_next(void *cursor)
+{
+	struct vslc_raw *c;
+
+	CAST_OBJ_NOTNULL(c, cursor, VSLC_RAW_MAGIC);
+
+	assert(c->next >= c->start);
+	assert(c->next <= c->start + c->len);
+	if (c->next < c->start + c->len) {
+		c->c.c.ptr = c->next;
+		c->next = VSL_NEXT(c->next);
+		return (1);
+	}
+	return (0);
+}
+
+static int
+vslc_raw_reset(void *cursor)
+{
+	struct vslc_raw *c;
+
+	CAST_OBJ_NOTNULL(c, cursor, VSLC_RAW_MAGIC);
+
+	assert(c->next >= c->start);
+	assert(c->next <= c->start + c->len);
+	c->next = c->start;
+	c->c.c.ptr = NULL;
+
+	return (0);
+}
+
+static int
+vslc_vtx_next(void *cursor)
+{
+	struct vslc_vtx *c;
+
+	CAST_OBJ_NOTNULL(c, cursor, VSLC_VTX_MAGIC);
+
+	assert(c->next >= c->vtx->start);
+	assert(c->next <= c->vtx->start + c->vtx->len);
+	if (c->next < c->vtx->start + c->vtx->len) {
+		c->c.c.ptr = c->next;
+		c->next = VSL_NEXT(c->next);
+		return (1);
+	}
+	return (0);
+}
+
+static int
+vslc_vtx_reset(void *cursor)
+{
+	struct vslc_vtx *c;
+
+	CAST_OBJ_NOTNULL(c, cursor, VSLC_VTX_MAGIC);
+
+	assert(c->next >= c->vtx->start);
+	assert(c->next <= c->vtx->start + c->vtx->len);
+	c->next = c->vtx->start;
+	c->c.c.ptr = NULL;
+
+	return (0);
+}
+
+static void
+vslc_vtx_setup(struct vslc_vtx *c, struct vtx *vtx, unsigned level)
+{
+	AN(c);
+	AN(vtx);
+
+	memset(c, 0, sizeof *c);
+	c->c.c.vxid = vtx->key.vxid;
+	c->c.c.level = level;
+	c->c.magic = VSLC_MAGIC;
+	c->c.next = vslc_vtx_next;
+	c->c.reset = vslc_vtx_reset;
+
+	c->magic = VSLC_VTX_MAGIC;
+	c->vtx = vtx;
+	c->next = c->vtx->start;
+}
+
+static struct vtx *
+vtx_new(struct VSLQ *vslq)
+{
+	struct vtx *vtx;
+
+	AN(vslq);
+	if (vslq->n_cache) {
+		AZ(VTAILQ_EMPTY(&vslq->cache));
+		vtx = VTAILQ_FIRST(&vslq->cache);
+		VTAILQ_REMOVE(&vslq->cache, vtx, list_child);
+		vslq->n_cache--;
+	} else {
+		ALLOC_OBJ(vtx, VTX_MAGIC);
+		AN(vtx);
+	}
+
+	vtx->key.vxid = 0;
+	vtx->t_start = VTIM_mono();
+	vtx->flags = 0;
+	vtx->type = vtx_t_unknown;
+	vtx->parent = NULL;
+	VTAILQ_INIT(&vtx->child);
+	vtx->n_child = 0;
+	vtx->n_childready = 0;
+	vtx->n_descend = 0;
+	vtx->start = vtx->buf;
+	vtx->len = 0;
+	vtx->index = 0;
+
+	VTAILQ_INSERT_TAIL(&vslq->incomplete, vtx, list_incomplete);
+	vslq->n_incomplete++;
+
+	return (vtx);
+}
+
+static void
+vtx_free(struct vtx **pvtx)
+{
+	struct vtx *vtx;
+
+	AN(pvtx);
+	vtx = *pvtx;
+	*pvtx = NULL;
+
+	free(vtx->buf);
+	FREE_OBJ(vtx);
+}
+
+static void
+vtx_retire(struct VSLQ *vslq, struct vtx **pvtx)
+{
+	struct vtx *vtx;
+	struct vtx *child;
+
+	AN(vslq);
+	AN(pvtx);
+
+	vtx = *pvtx;
+	*pvtx = NULL;
+	CHECK_OBJ_NOTNULL(vtx, VTX_MAGIC);
+
+	AN(vtx->flags & VTX_F_COMPLETE);
+	AN(vtx->flags & VTX_F_READY);
+	AZ(vtx->parent);
+
+	while (!VTAILQ_EMPTY(&vtx->child)) {
+		child = VTAILQ_FIRST(&vtx->child);
+		assert(child->parent == vtx);
+		AN(vtx->n_child);
+		assert(vtx->n_descend >= child->n_descend + 1);
+		VTAILQ_REMOVE(&vtx->child, child, list_child);
+		child->parent = NULL;
+		vtx->n_child--;
+		vtx->n_descend -= child->n_descend + 1;
+		vtx_retire(vslq, &child);
+		AZ(child);
+	}
+	AZ(vtx->n_child);
+	AZ(vtx->n_descend);
+	AN(VRB_REMOVE(vtx_tree, &vslq->tree, &vtx->key));
+
+	if (vslq->n_cache < VTX_CACHE) {
+		VTAILQ_INSERT_HEAD(&vslq->cache, vtx, list_child);
+		vslq->n_cache++;
+	} else {
+		vtx_free(&vtx);
+		AZ(vtx);
+	}
+}
+
+static struct vtx *
+vtx_lori(struct VSLQ *vslq, unsigned vxid)
+{
+	struct vtx *vtx;
+	struct vtx_key lkey, *key;
+
+	AN(vslq);
+	lkey.vxid = vxid;
+	key = VRB_FIND(vtx_tree, &vslq->tree, &lkey);
+	if (key != NULL) {
+		CAST_OBJ_NOTNULL(vtx, (void *)key, VTX_MAGIC);
+		return (vtx);
+	}
+
+	vtx = vtx_new(vslq);
+	AN(vtx);
+	vtx->key.vxid = vxid;
+	AZ(VRB_INSERT(vtx_tree, &vslq->tree, &vtx->key));
+	return (vtx);
+}
+
+static void
+vtx_append(struct vtx *vtx, const uint32_t *ptr, ssize_t len, int shmptr_ok)
+{
+	ssize_t bufsize;
+	const uint32_t *ptr2;
+	ssize_t len2;
+
+	AN(vtx);
+
+	if (vtx->flags & VTX_F_SHM) {
+		assert(vtx->start != vtx->buf);
+		ptr2 = vtx->start;
+		vtx->start = vtx->buf;
+		len2 = vtx->len;
+		vtx->len = 0;
+		vtx->flags &= ~VTX_F_SHM;
+		vtx_append(vtx, ptr2, len2, 0);
+	}
+
+	if (len == 0)
+		return;
+	AN(ptr);
+
+	if (shmptr_ok && vtx->len == 0) {
+		vtx->start = ptr;
+		vtx->len = len;
+		vtx->flags |= VTX_F_SHM;
+		return;
+	}
+
+	bufsize = vtx->bufsize;
+	if (bufsize == 0)
+		bufsize = VTX_BUFSIZE_MIN;
+	while (vtx->len + len > bufsize)
+		bufsize *= 2;
+	if (bufsize != vtx->bufsize) {
+		vtx->buf = realloc(vtx->buf, 4 * bufsize);
+		AN(vtx->buf);
+		vtx->bufsize = bufsize;
+		vtx->start = vtx->buf;
+	}
+	memcpy(&vtx->buf[vtx->len], ptr, 4 * len);
+	vtx->len += len;
+}
+
+static struct vtx *
+vtx_check_ready(struct VSLQ *vslq, struct vtx *vtx)
+{
+	struct vtx *ready;
+
+	AN(vslq);
+	AN(vtx->flags & VTX_F_COMPLETE);
+	AZ(vtx->flags & VTX_F_READY);
+
+	if (vtx->type == vtx_t_unknown)
+		vtx_diag(vtx, "vtx of unknown type marked complete");
+
+	ready = vtx;
+	while (1) {
+		if (ready->flags & VTX_F_COMPLETE &&
+		    ready->n_child == ready->n_childready)
+			ready->flags |= VTX_F_READY;
+		else
+			break;
+		if (ready->parent == NULL)
+			break;
+		ready = ready->parent;
+		ready->n_childready++;
+		assert(ready->n_child >= ready->n_childready);
+	}
+
+	if (ready->flags & VTX_F_READY && ready->parent == NULL)
+		/* Top level vtx ready */
+		return (ready);
+
+	if (vtx->flags & VTX_F_SHM) {
+		/* Not ready, append zero to make sure it's not a shm
+		   reference */
+		vtx_append(vtx, NULL, 0, 0);
+		AZ(vtx->flags & VTX_F_SHM);
+	}
+
+	return (NULL);
+}
+
+static int
+vtx_parsetag_bl(const char *str, unsigned strlen, enum vtx_type_e *ptype,
+    unsigned *pvxid)
+{
+	char ibuf[strlen + 1];
+	char tbuf[7];
+	unsigned vxid;
+	int i;
+	enum vtx_type_e type = vtx_t_unknown;
+
+	AN(str);
+	memcpy(ibuf, str, strlen);
+	ibuf[strlen] = '\0';
+	i = sscanf(ibuf, "%6s %u", tbuf, &vxid);
+	if (i < 1)
+		return (-1);
+	if (!strcmp(tbuf, "sess"))
+		type = vtx_t_sess;
+	else if (!strcmp(tbuf, "req"))
+		type = vtx_t_req;
+	else if (!strcmp(tbuf, "esireq"))
+		type = vtx_t_esireq;
+	else if (!strcmp(tbuf, "bereq"))
+		type = vtx_t_bereq;
+	else
+		return (-1);
+	if (i == 1)
+		vxid = 0;
+	if (ptype)
+		*ptype = type;
+	if (pvxid)
+		*pvxid = vxid;
+	return (i);
+}
+
+static void
+vtx_set_parent(struct vtx *parent, struct vtx *child)
+{
+
+	AN(parent);
+	AN(child);
+	AZ(child->parent);
+	child->parent = parent;
+	VTAILQ_INSERT_TAIL(&parent->child, child, list_child);
+	parent->n_child++;
+	do
+		parent->n_descend += 1 + child->n_descend;
+	while ((parent = parent->parent));
+}
+
+static int
+vtx_scan_begintag(struct VSLQ *vslq, struct vtx *vtx, const uint32_t *ptr)
+{
+	int i;
+	enum vtx_type_e p_type;
+	unsigned p_vxid;
+	struct vtx *p_vtx;
+
+	assert(VSL_TAG(ptr) == SLT_Begin);
+
+	if (vtx->flags & VTX_F_READY)
+		return (vtx_diag_tag(vtx, ptr, "link too late"));
+
+	i = vtx_parsetag_bl(VSL_CDATA(ptr), VSL_LEN(ptr), &p_type, &p_vxid);
+	if (i < 1)
+		return (vtx_diag_tag(vtx, ptr, "parse error"));
+
+	/* Check/set vtx type */
+	assert(p_type != vtx_t_unknown);
+	if (vtx->type != vtx_t_unknown && vtx->type != p_type)
+		return (vtx_diag_tag(vtx, ptr, "type mismatch"));
+	vtx->type = p_type;
+
+	if (i == 1 || p_vxid == 0)
+		return (0);
+
+	/* Lookup and check parent vtx */
+	p_vtx = vtx_lori(vslq, p_vxid);
+	AN(p_vtx);
+	if (vtx->parent == p_vtx)
+		/* Link already exists */
+		return (0);
+	if (vtx->parent != NULL)
+		return (vtx_diag_tag(vtx, ptr, "duplicate link"));
+	if (p_vtx->flags & VTX_F_READY)
+		return (vtx_diag_tag(vtx, ptr, "link too late"));
+
+	vtx_set_parent(p_vtx, vtx);
+
+	return (0);
+}
+
+static int
+vtx_scan_linktag(struct VSLQ *vslq, struct vtx *vtx, const uint32_t *ptr)
+{
+	int i;
+	enum vtx_type_e c_type;
+	unsigned c_vxid;
+	struct vtx *c_vtx;
+
+	assert(VSL_TAG(ptr) == SLT_Link);
+
+	if (vtx->flags & VTX_F_READY)
+		return (vtx_diag_tag(vtx, ptr, "link too late"));
+
+	i = vtx_parsetag_bl(VSL_CDATA(ptr), VSL_LEN(ptr), &c_type, &c_vxid);
+	if (i < 2)
+		return (vtx_diag_tag(vtx, ptr, "parse error"));
+	assert(i == 2);
+
+	/* Lookup and check child vtx */
+	c_vtx = vtx_lori(vslq, c_vxid);
+	AN(c_vtx);
+	if (c_vtx->parent == vtx)
+		/* Link already exists */
+		return (0);
+	if (c_vtx->parent != NULL)
+		return (vtx_diag_tag(vtx, ptr, "duplicate link"));
+	if (c_vtx->flags & VTX_F_READY)
+		return (vtx_diag_tag(vtx, ptr, "link too late"));
+	assert(c_type != vtx_t_unknown);
+	if (c_vtx->type != vtx_t_unknown && c_vtx->type != c_type)
+		return (vtx_diag_tag(vtx, ptr, "type mismatch"));
+	c_vtx->type = c_type;
+
+	vtx_set_parent(vtx, c_vtx);
+
+	return (0);
+}
+
+static struct vtx *
+vtx_scan(struct VSLQ *vslq, struct vtx *vtx)
+{
+	const uint32_t *ptr;
+	enum VSL_tag_e tag;
+	int complete;
+
+	complete = (vtx->flags & VTX_F_COMPLETE ? 1 : 0);
+	ptr = vtx->start + vtx->index;
+	assert(ptr <= vtx->start + vtx->len);
+	for (; ptr < vtx->start + vtx->len; ptr = VSL_NEXT(ptr)) {
+		tag = VSL_TAG(ptr);
+
+		if (complete) {
+			vtx_diag(vtx, "late log rec");
+			continue;
+		}
+
+		if (vtx->type == vtx_t_unknown && tag != SLT_Begin)
+			vtx_diag_tag(vtx, ptr, "early log rec");
+
+		switch (tag) {
+		case SLT_Begin:
+			(void)vtx_scan_begintag(vslq, vtx, ptr);
+			break;
+
+		case SLT_Link:
+			(void)vtx_scan_linktag(vslq, vtx, ptr);
+			break;
+
+		case SLT_End:
+			complete = 1;
+			break;
+
+		default:
+			break;
+		}
+	}
+	vtx->index = ptr - vtx->start;
+	assert(vtx->index <= vtx->len);
+
+	if (!complete && vtx->flags & VTX_F_SHM) {
+		/* Append zero to make sure it's not a shm reference */
+		vtx_append(vtx, NULL, 0, 0);
+		AZ(vtx->flags & VTX_F_SHM);
+	}
+
+	if (complete) {
+		VTAILQ_REMOVE(&vslq->incomplete, vtx, list_incomplete);
+		vtx->flags |= VTX_F_COMPLETE;
+		AN(vslq->n_incomplete);
+		vslq->n_incomplete--;
+		return (vtx_check_ready(vslq, vtx));
+	}
+
+	return (NULL);
+}
+
+static struct vtx *
+vtx_force(struct VSLQ *vslq, struct vtx *vtx, const char *reason)
+{
+	AZ(vtx->flags & VTX_F_COMPLETE);
+	AZ(vtx->flags & VTX_F_READY);
+	vtx_diag(vtx, reason);
+
+	VTAILQ_REMOVE(&vslq->incomplete, vtx, list_incomplete);
+	vtx->flags |= VTX_F_COMPLETE;
+	AN(vslq->n_incomplete);
+	vslq->n_incomplete--;
+
+	return (vtx_check_ready(vslq, vtx));
+}
+
+static int
+vslq_callback(struct VSLQ *vslq, struct vtx *vtx, VSLQ_dispatch_f *func,
+    void *priv)
+{
+	unsigned n = vtx->n_descend + 1;
+	unsigned i, j;
+	struct vslc_vtx c[n];
+	struct VSL_cursor *cp[n + 1];
+
+	AN(vslq);
+	CHECK_OBJ_NOTNULL(vtx, VTX_MAGIC);
+
+	if (func == NULL)
+		return (0);
+	if (vslq->grouping == VSL_g_session &&
+	    vtx->type != vtx_t_sess)
+		return (0);
+	if (vslq->grouping == VSL_g_request &&
+	    vtx->type != vtx_t_req)
+		return (0);
+
+	i = j = 0;
+	vslc_vtx_setup(&c[i], vtx, 0);
+	i++;
+	while (j < i) {
+		vtx = VTAILQ_FIRST(&c[j].vtx->child);
+		while (vtx) {
+			assert(i < n);
+			vslc_vtx_setup(&c[i], vtx, c[j].c.c.level + 1);
+			i++;
+			vtx = VTAILQ_NEXT(vtx, list_child);
+		}
+		j++;
+	}
+	assert(i == n);
+
+	/* Reverse order */
+	for (i = 0; i < n; i++)
+		cp[i] = &c[n - i - 1].c.c;
+	cp[i] = NULL;
+
+	/* Query test goes here */
+	if (vslq->query == NULL ? 1 : vslq_runquery(vslq->query, cp))
+		return ((func)(vslq->vsl, cp, priv));
+	else
+		return (0);
+}
+
+struct VSLQ *
+VSLQ_New(struct VSL_data *vsl, struct VSL_cursor **cp,
+    enum VSL_grouping_e grouping, const char *querystring)
+{
+	struct vslq_query *query;
+	struct VSLQ *vslq;
+
+	CHECK_OBJ_NOTNULL(vsl, VSL_MAGIC);
+	AN(cp);
+	if (grouping > VSL_g_session) {
+		(void)vsl_diag(vsl, "Illegal query grouping");
+		return (NULL);
+	}
+	if (querystring != NULL) {
+		query = vslq_newquery(vsl, grouping, querystring);
+		if (query == NULL)
+			return (NULL);
+	} else
+		query = NULL;
+
+	ALLOC_OBJ(vslq, VSLQ_MAGIC);
+	AN(vslq);
+	vslq->vsl = vsl;
+	vslq->c = *cp;
+	*cp = NULL;
+	vslq->grouping = grouping;
+	vslq->query = query;
+	VRB_INIT(&vslq->tree);
+	VTAILQ_INIT(&vslq->incomplete);
+	VTAILQ_INIT(&vslq->cache);
+
+	return (vslq);
+}
+
+void
+VSLQ_Delete(struct VSLQ **pvslq)
+{
+	struct VSLQ *vslq;
+	struct vtx *vtx;
+
+	AN(pvslq);
+	vslq = *pvslq;
+	*pvslq = NULL;
+	CHECK_OBJ_NOTNULL(vslq, VSLQ_MAGIC);
+
+	(void)VSLQ_Flush(vslq, NULL, NULL);
+	AZ(vslq->n_incomplete);
+	VSL_DeleteCursor(vslq->c);
+	vslq->c = NULL;
+
+	if (vslq->query != NULL)
+		vslq_deletequery(&vslq->query);
+	AZ(vslq->query);
+
+	while (!VTAILQ_EMPTY(&vslq->cache)) {
+		AN(vslq->n_cache);
+		vtx = VTAILQ_FIRST(&vslq->cache);
+		VTAILQ_REMOVE(&vslq->cache, vtx, list_child);
+		vslq->n_cache--;
+		vtx_free(&vtx);
+		AZ(vtx);
+	}
+
+	FREE_OBJ(vslq);
+}
+
+static int
+vslq_raw(struct VSLQ *vslq, VSLQ_dispatch_f *func, void *priv)
+{
+	struct vslc_raw rawc;
+	struct VSL_cursor *c;
+	struct VSL_cursor *pc[2];
+	int i;
+
+	assert(vslq->grouping == VSL_g_raw);
+	c = vslq->c;
+
+	memset(&rawc, 0, sizeof rawc);
+	rawc.c.c.vxid = -1;
+	rawc.c.magic = VSLC_MAGIC;
+	rawc.c.next = vslc_raw_next;
+	rawc.c.reset = vslc_raw_reset;
+	rawc.magic = VSLC_RAW_MAGIC;
+	pc[0] = &rawc.c.c;
+	pc[1] = NULL;
+
+	while (1) {
+		i = VSL_Next(c);
+		if (i <= 0)
+			break;
+		AN(c->ptr);
+		if (func == NULL)
+			continue;
+		rawc.start = c->ptr;
+		rawc.len = VSL_NEXT(c->ptr) - c->ptr;
+		rawc.next = rawc.start;
+		rawc.c.c.ptr = NULL;
+
+		/* Query check goes here */
+		i = 0;
+		if (vslq->query == NULL ? 1 : vslq_runquery(vslq->query, pc))
+			i = (func)(vslq->vsl, pc, priv);
+		if (i)
+			break;
+	}
+
+	return (i);
+}
+
+int
+VSLQ_Dispatch(struct VSLQ *vslq, VSLQ_dispatch_f *func, void *priv)
+{
+	struct VSL_cursor *c;
+	int i;
+	enum VSL_tag_e tag;
+	const uint32_t *ptr;
+	ssize_t len;
+	unsigned vxid;
+	struct vtx *vtx;
+	double now;
+
+	CHECK_OBJ_NOTNULL(vslq, VSLQ_MAGIC);
+
+	if (vslq->grouping == VSL_g_raw)
+		return (vslq_raw(vslq, func, priv));
+
+	c = vslq->c;
+	while (1) {
+		i = VSL_Next(c);
+		if (i != 1)
+			break;
+		tag = VSL_TAG(c->ptr);
+		if (tag == SLT__Batch) {
+			ptr = VSL_NEXT(c->ptr);
+			len = VSL_WORDS(c->ptr[1]);
+			AZ(vsl_skip(c, len));
+		} else {
+			ptr = c->ptr;
+			len = VSL_NEXT(ptr) - ptr;
+		}
+		vxid = VSL_ID(ptr);
+		if (vxid == 0)
+			continue;
+		vtx = vtx_lori(vslq, vxid);
+		AN(vtx);
+		vtx_append(vtx, ptr, len, c->shmptr_ok);
+		vtx = vtx_scan(vslq, vtx);
+		if (vtx) {
+			AN(vtx->flags & VTX_F_READY);
+			i = vslq_callback(vslq, vtx, func, priv);
+			vtx_retire(vslq, &vtx);
+			AZ(vtx);
+			if (i)
+				break;
+		}
+	}
+	if (i)
+		return (i);
+
+	now = VTIM_mono();
+	while ((vtx = VTAILQ_FIRST(&vslq->incomplete)) &&
+	    now - vtx->t_start > 120.) {
+		/* XXX: Make timeout configurable through options and
+		   provide a sane default */
+		AZ(vtx->flags & VTX_F_COMPLETE);
+		vtx = vtx_force(vslq, vtx, "incomplete - timeout");
+		if (vtx) {
+			AN(vtx->flags & VTX_F_READY);
+			i = vslq_callback(vslq, vtx, func, priv);
+			vtx_retire(vslq, &vtx);
+			AZ(vtx);
+			if (i)
+				break;
+		}
+	}
+	if (i)
+		return (i);
+
+	while (vslq->n_incomplete > 1000) {
+		/* XXX: Make limit configurable through options and
+		   provide a sane default */
+		vtx = VTAILQ_FIRST(&vslq->incomplete);
+		AN(vtx);
+		AZ(vtx->flags & VTX_F_COMPLETE);
+		vtx = vtx_force(vslq, vtx, "incomplete - store overflow");
+		if (vtx) {
+			AN(vtx->flags & VTX_F_READY);
+			i = vslq_callback(vslq, vtx, func, priv);
+			vtx_retire(vslq, &vtx);
+			AZ(vtx);
+			if (i)
+				break;
+		}
+	}
+
+	return (i);
+}
+
+int
+VSLQ_Flush(struct VSLQ *vslq, VSLQ_dispatch_f *func, void *priv)
+{
+	struct vtx *vtx;
+	int i = 0;
+
+	CHECK_OBJ_NOTNULL(vslq, VSLQ_MAGIC);
+
+	while (vslq->n_incomplete) {
+		vtx = VTAILQ_FIRST(&vslq->incomplete);
+		AN(vtx);
+		AZ(vtx->flags & VTX_F_COMPLETE);
+		vtx = vtx_force(vslq, vtx, "incomplete - flushing");
+		if (vtx) {
+			AN(vtx->flags & VTX_F_READY);
+			i = vslq_callback(vslq, vtx, func, priv);
+			vtx_retire(vslq, &vtx);
+			AZ(vtx);
+			if (i)
+				break;
+		}
+	}
+	return (i);
+}
diff --git a/lib/libvarnishapi/vsl_query.c b/lib/libvarnishapi/vsl_query.c
new file mode 100644
index 0000000..b4a403c
--- /dev/null
+++ b/lib/libvarnishapi/vsl_query.c
@@ -0,0 +1,122 @@
+/*-
+ * 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 <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "vas.h"
+#include "miniobj.h"
+#include "vre.h"
+
+#include "vapi/vsl.h"
+#include "vsl_api.h"
+
+struct vslq_query {
+	unsigned		magic;
+#define VSLQ_QUERY_MAGIC	0x122322A5
+
+	vre_t			*regex;
+};
+
+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;
+
+	(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);
+	query->regex = regex;
+	return (query);
+}
+
+void
+vslq_deletequery(struct vslq_query **pquery)
+{
+	struct vslq_query *query;
+
+	AN(pquery);
+	query = *pquery;
+	*pquery = NULL;
+	CHECK_OBJ_NOTNULL(query, VSLQ_QUERY_MAGIC);
+
+	AN(query->regex);
+	VRE_free(&query->regex);
+	AZ(query->regex);
+
+	FREE_OBJ(query);
+}
+
+int
+vslq_runquery(struct vslq_query *query, struct VSL_cursor *cp[])
+{
+	struct VSL_cursor *c;
+	int i, len;
+	const char *data;
+
+	CHECK_OBJ_NOTNULL(query, VSLQ_QUERY_MAGIC);
+	AN(query->regex);
+
+	c = cp[0];
+	while (c) {
+		while (1) {
+			i = VSL_Next(c);
+			if (i == 0)
+				break;
+			assert(i == 1);
+			AN(c->ptr);
+			len = VSL_LEN(c->ptr);
+			data = VSL_CDATA(c->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));
+		c = *++cp;
+	}
+
+	return (0);
+}



More information about the varnish-commit mailing list