<commit_before>/*-
 * Copyright (c) 2006 Verdens Gang AS
 * Copyright (c) 2006-2015 Varnish Software AS
 * All rights reserved.
 *
 * Author: <NAME> <<EMAIL>>
 *
 * 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.
 *
 * Runtime support for compiled VCL programs
 */

#include "config.h"

#include "cache_varnishd.h"

#include "cache_objhead.h"
#include "vav.h"
#include "vcl.h"
#include "vct.h"
#include "vrt_obj.h"
#include "vsa.h"
#include "vtcp.h"
#include "vtim.h"
#include "vcc_interface.h"

#include "common/heritage.h"
#include "common/vsmw.h"

const void * const vrt_magic_string_end = &vrt_magic_string_end;
const void * const vrt_magic_string_unset = &vrt_magic_string_unset;

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

VCL_VOID
VRT_synth(VRT_CTX, VCL_INT code, VCL_STRING reason)
{

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	assert(ctx->req != NULL || ctx->bo != NULL);
	if (code < 100 || code > 65535)
		code = 503;

	if (ctx->req == NULL) {
		CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC);
		ctx->bo->err_code = (uint16_t)code;
		ctx->bo->err_reason = reason ? reason
		    : http_Status2Reason(ctx->bo->err_code % 1000, NULL);
		return;
	}

	ctx->req->err_code = (uint16_t)code;
	ctx->req->err_reason = reason ? reason
	    : http_Status2Reason(ctx->req->err_code % 1000, NULL);
}

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

void
VRT_acl_log(VRT_CTX, const char *msg)
{

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	AN(msg);
	if (ctx->vsl != NULL)
		VSLb(ctx->vsl, SLT_VCL_acl, "%s", msg);
	else
		VSL(SLT_VCL_acl, 0, "%s", msg);
}

int
VRT_acl_match(VRT_CTX, VCL_ACL acl, VCL_IP ip)
{

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	CHECK_OBJ_NOTNULL(acl, VRT_ACL_MAGIC);
	if (ip == NULL) {
		VRT_fail(ctx, "Cannot match a null IP address");
		return (0);
	}
	assert(VSA_Sane(ip));
	return (acl->match(ctx, ip));
}

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

VCL_VOID
VRT_hit_for_pass(VRT_CTX, VCL_DURATION d)
{
	struct objcore *oc;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	if (ctx->bo == NULL) {
		VSLb(ctx->vsl, SLT_Error,
		    "Note: Ignoring DURATION argument to return(pass);");
		return;
	}
	CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC);
	oc = ctx->bo->fetch_objcore;
	oc->ttl = d;
	oc->grace = 0.0;
	oc->keep = 0.0;
	VSLb(ctx->vsl, SLT_TTL, "HFP %.0f %.0f %.0f %.0f uncacheable",
	    oc->ttl, oc->grace, oc->keep, oc->t_origin);
}

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

VCL_HTTP
VRT_selecthttp(VRT_CTX, enum gethdr_e where)
{
	struct http *hp;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	switch (where) {
	case HDR_REQ:
		hp = ctx->http_req;
		break;
	case HDR_REQ_TOP:
		hp = ctx->http_req_top;
		break;
	case HDR_BEREQ:
		hp = ctx->http_bereq;
		break;
	case HDR_BERESP:
		hp = ctx->http_beresp;
		break;
	case HDR_RESP:
		hp = ctx->http_resp;
		break;
	default:
		WRONG("VRT_selecthttp 'where' invalid");
	}
	return (hp);
}

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

VCL_STRING
VRT_GetHdr(VRT_CTX, const struct gethdr_s *hs)
{
	const char *p;
	struct http *hp;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	if (hs->where == HDR_OBJ) {
		CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC);
		CHECK_OBJ_NOTNULL(ctx->req->objcore, OBJCORE_MAGIC);
		return (HTTP_GetHdrPack(ctx->req->wrk, ctx->req->objcore,
		    hs->what));
	}
	hp = VRT_selecthttp(ctx, hs->where);
	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
	if (!http_GetHdr(hp, hs->what, &p))
		return (NULL);
	return (p);
}

/*--------------------------------------------------------------------
 * Build STRANDS from what is essentially a STRING_LIST
 */

VCL_STRANDS
VPI_BundleStrands(int n, struct strands *s, char const **d, const char *f, ...)
{
	va_list ap;

	assert(n > 0);
	s->n = n;
	s->p = d;
	*d++ = f;
	va_start(ap, f);
	while(--n)
		*d++ = va_arg(ap, const char *);
	assert(va_arg(ap, const char *) == vrt_magic_string_end);
	va_end(ap);
	return (s);
}

/*--------------------------------------------------------------------
 * Compare two STRANDS
 */

int
VRT_CompareStrands(VCL_STRANDS a, VCL_STRANDS b)
{
	const char *pa = NULL, *pb = NULL;
	int na = 0, nb = 0;

	while (1) {
		if (pa != NULL && *pa == '\0')
			pa = NULL;
		if (pb != NULL && *pb == '\0')
			pb = NULL;
		if (pa == NULL && na < a->n)
			pa = a->p[na++];
		else if (pb == NULL && nb < b->n)
			pb = b->p[nb++];
		else if (pa == NULL && pb == NULL)
			return (0);
		else if (pa == NULL)
			return (-1);
		else if (pb == NULL)
			return (1);
		else if (*pa == '\0')
			pa = NULL;
		else if (*pb == '\0')
			pb = NULL;
		else if (*pa != *pb)
			return (*pa - *pb);
		else {
			pa++;
			pb++;
		}
	}
}

/*--------------------------------------------------------------------
 * STRANDS to BOOL
 */

VCL_BOOL
VRT_Strands2Bool(VCL_STRANDS s)
{
	int i;

	AN(s);
	for (i = 0; i < s->n; i++)
		if (s->p[i] != NULL)
			return (1);
	return (0);
}

/*--------------------------------------------------------------------
 * Collapse a STRING_LIST in the space provided, or return NULL
 */

char *
VRT_StringList(char *d, unsigned dl, const char *p, va_list ap)
{
	char *b, *e;
	unsigned x;

	b = d;
	e = b + dl;
	while (p != vrt_magic_string_end && b < e) {
		if (p != NULL && *p != '\0') {
			x = strlen(p);
			if (b + x < e)
				memcpy(b, p, x);
			b += x;
		}
		p = va_arg(ap, const char *);
	}
	if (b >= e)
		return (NULL);
	*b++ = '\0';
	return (b);
}

/*--------------------------------------------------------------------
 * Copy and merge a STRING_LIST into a workspace.
 */

const char *
VRT_String(struct ws *ws, const char *h, const char *p, va_list ap)
{
	char *b, *e;
	const char *q;
	unsigned u, x;
	va_list aq;

	u = WS_ReserveAll(ws);
	e = b = ws->f;
	e += u;

	va_copy(aq, ap);
	do
		q = va_arg(aq, const char *);
	while (q == NULL || (q != vrt_magic_string_end && *q == '\0'));

	if (h != NULL && p == NULL && q == vrt_magic_string_end &&
	    WS_Inside(ws, h, NULL)) {
		va_end(aq);
		WS_Release(ws, 0);
		return (h);
	}

	if (h == NULL && p != NULL && q == vrt_magic_string_end &&
	    WS_Inside(ws, p, NULL)) {
		va_end(aq);
		WS_Release(ws, 0);
		return (p);
	}

	if (h == NULL && p == NULL) {
		if (q == vrt_magic_string_end) {
			va_end(aq);
			WS_Release(ws, 0);
			return ("");
		}
		do
			p = va_arg(aq, const char *);
		while (p == NULL || (p != vrt_magic_string_end && *p == '\0'));
		if (p == vrt_magic_string_end && WS_Inside(ws, q, NULL)) {
			va_end(aq);
			WS_Release(ws, 0);
			return (q);
		}
		p = NULL;
		va_end(aq);
	}

	if (h != NULL) {
		x = strlen(h);
		if (b + x < e)
			memcpy(b, h, x);
		b += x;
		if (b < e)
			*b = ' ';
		b++;
	}
	b = VRT_StringList(b, e > b ? e - b : 0, p, ap);
	if (b == NULL || b == e) {
		WS_MarkOverflow(ws);
		WS_Release(ws, 0);
		return (NULL);
	}
	e = b;
	b = ws->f;
	WS_Release(ws, e - b);
	return (b);
}

/*--------------------------------------------------------------------
 * Copy and merge a STRING_LIST on the current workspace
 */

VCL_STRING
VRT_CollectString(VRT_CTX, const char *p, ...)
{
	va_list ap;
	const char *b;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
	va_start(ap, p);
	b = VRT_String(ctx->ws, NULL, p, ap);
	va_end(ap);
	if (b == NULL)
		VRT_fail(ctx, "Workspace overflow");
	return (b);
}

/*--------------------------------------------------------------------
 * Collapse STRANDS into the space provided, or return NULL
 */

char *
VRT_Strands(char *d, size_t dl, VCL_STRANDS s)
{
	char *b;
	const char *e;
	unsigned x;

	AN(d);
	AN(s);
	b = d;
	e = b + dl;
	for (int i = 0; i < s->n; i++)
		if (s->p[i] != NULL && *s->p[i] != '\0') {
			x = strlen(s->p[i]);
			if (b + x >= e)
				return (NULL);
			memcpy(b, s->p[i], x);
			b += x;
		}
	assert(b < e);
	*b++ = '\0';
	return (b);
}

/*--------------------------------------------------------------------
 * Copy and merge STRANDS into a workspace.
 */

VCL_STRING
VRT_StrandsWS(struct ws *ws, const char *h, VCL_STRANDS s)
{
	char *b;
	const char *q = NULL, *e;
	VCL_STRING r;
	unsigned u, x;
	int i;

	AN(s);
	u = WS_ReserveAll(ws);

	for (i = 0; i < s->n; i++)
		if (s->p[i] != NULL && *s->p[i] != '\0') {
			q = s->p[i];
			break;
		}

	if (h != NULL && q == NULL && WS_Inside(ws, h, NULL)) {
		WS_Release(ws, 0);
		return (h);
	}

	if (h == NULL) {
		if (q == NULL) {
			WS_Release(ws, 0);
			return ("");
		}
		if (WS_Inside(ws, q, NULL)) {
			for (i++; i < s->n; i++)
				if (s->p[i] != NULL && *s->p[i] != '\0')
					break;
			if (i == s->n) {
				WS_Release(ws, 0);
				return (q);
			}
		}
	}

	b = WS_Front(ws);
	e = b + u;

	if (h != NULL) {
		x = strlen(h);
		if (b + x < e)
			memcpy(b, h, x);
		b += x;
		if (b < e)
			*b = ' ';
		b++;
	}
	r = VRT_Strands(b, e > b ? e - b : 0, s);
	if (r == NULL || r == e) {
		WS_MarkOverflow(ws);
		WS_Release(ws, 0);
		return (NULL);
	}
	b = WS_Front(ws);
	WS_Release(ws, r - b);
	return (b);
}

/*--------------------------------------------------------------------
 * Copy and merge STRANDS on the current workspace
 */

VCL_STRING
VRT_CollectStrands(VRT_CTX, VCL_STRANDS s)
{
	const char *b;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
	b = VRT_StrandsWS(ctx->ws, NULL, s);
	if (b == NULL)
		VRT_fail(ctx, "Workspace overflow");
	return (b);
}

/*--------------------------------------------------------------------
 * upper/lower-case STRANDS (onto workspace)
 */

#include <stdio.h>

VCL_STRING
VRT_UpperLowerStrands(VRT_CTX, VCL_STRANDS s, int up)
{
	unsigned u;
	char *b, *e, *r;
	const char *p, *q = NULL;
	int i, copy = 0;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
	AN(s);
	u = WS_ReserveAll(ctx->ws);
	r = b = WS_Front(ctx->ws);
	e = b + u;
	for (i = 0; i < s->n; i++) {
		if (s->p[i] == NULL || s->p[i][0] == '\0')
			continue;
		if (q != NULL)
			copy = 1;
		p = q = s->p[i];
		for(p = q = s->p[i]; *p != '\0'; p++) {
			if ((up && vct_islower(*p)) ||
			    (!up && vct_isupper(*p))) {
				*b++ = *p ^ 0x20;
				copy = 1;
			} else if (b < e) {
				*b++ = *p;
			}
			if (copy && b == e)
				break;
		}
		if (copy && b == e) {
			WS_Release(ctx->ws, 0);
			VRT_fail(ctx, "Workspace overflow");
			return (NULL);
		}
	}
	assert(b <= e);
	if (!copy) {
		WS_Release(ctx->ws, 0);
		return (q);
	}
	assert(b < e);
	*b++ = '\0';
	assert(b <= e);
	WS_ReleaseP(ctx->ws, b);
	return (r);
}

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

VCL_VOID
VRT_SetHdr(VRT_CTX , const struct gethdr_s *hs,
    const char *p, ...)
{
	struct http *hp;
	va_list ap;
	const char *b;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	AN(hs);
	AN(hs->what);
	hp = VRT_selecthttp(ctx, hs->where);
	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
	va_start(ap, p);
	if (p == vrt_magic_string_unset) {
		http_Unset(hp, hs->what);
	} else {
		b = VRT_String(hp->ws, hs->what + 1, p, ap);
		if (b == NULL) {
			VSLb(ctx->vsl, SLT_LostHeader, "%s", hs->what + 1);
		} else {
			http_Unset(hp, hs->what);<commit_msg>Use the VCL_HTTP type in VRT code<commit_after>/*-
 * Copyright (c) 2006 Verdens Gang AS
 * Copyright (c) 2006-2015 Varnish Software AS
 * All rights reserved.
 *
 * Author: <NAME> <<EMAIL>>
 *
 * 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.
 *
 * Runtime support for compiled VCL programs
 */

#include "config.h"

#include "cache_varnishd.h"

#include "cache_objhead.h"
#include "vav.h"
#include "vcl.h"
#include "vct.h"
#include "vrt_obj.h"
#include "vsa.h"
#include "vtcp.h"
#include "vtim.h"
#include "vcc_interface.h"

#include "common/heritage.h"
#include "common/vsmw.h"

const void * const vrt_magic_string_end = &vrt_magic_string_end;
const void * const vrt_magic_string_unset = &vrt_magic_string_unset;

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

VCL_VOID
VRT_synth(VRT_CTX, VCL_INT code, VCL_STRING reason)
{

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	assert(ctx->req != NULL || ctx->bo != NULL);
	if (code < 100 || code > 65535)
		code = 503;

	if (ctx->req == NULL) {
		CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC);
		ctx->bo->err_code = (uint16_t)code;
		ctx->bo->err_reason = reason ? reason
		    : http_Status2Reason(ctx->bo->err_code % 1000, NULL);
		return;
	}

	ctx->req->err_code = (uint16_t)code;
	ctx->req->err_reason = reason ? reason
	    : http_Status2Reason(ctx->req->err_code % 1000, NULL);
}

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

void
VRT_acl_log(VRT_CTX, const char *msg)
{

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	AN(msg);
	if (ctx->vsl != NULL)
		VSLb(ctx->vsl, SLT_VCL_acl, "%s", msg);
	else
		VSL(SLT_VCL_acl, 0, "%s", msg);
}

int
VRT_acl_match(VRT_CTX, VCL_ACL acl, VCL_IP ip)
{

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	CHECK_OBJ_NOTNULL(acl, VRT_ACL_MAGIC);
	if (ip == NULL) {
		VRT_fail(ctx, "Cannot match a null IP address");
		return (0);
	}
	assert(VSA_Sane(ip));
	return (acl->match(ctx, ip));
}

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

VCL_VOID
VRT_hit_for_pass(VRT_CTX, VCL_DURATION d)
{
	struct objcore *oc;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	if (ctx->bo == NULL) {
		VSLb(ctx->vsl, SLT_Error,
		    "Note: Ignoring DURATION argument to return(pass);");
		return;
	}
	CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC);
	oc = ctx->bo->fetch_objcore;
	oc->ttl = d;
	oc->grace = 0.0;
	oc->keep = 0.0;
	VSLb(ctx->vsl, SLT_TTL, "HFP %.0f %.0f %.0f %.0f uncacheable",
	    oc->ttl, oc->grace, oc->keep, oc->t_origin);
}

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

VCL_HTTP
VRT_selecthttp(VRT_CTX, enum gethdr_e where)
{
	VCL_HTTP hp;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	switch (where) {
	case HDR_REQ:
		hp = ctx->http_req;
		break;
	case HDR_REQ_TOP:
		hp = ctx->http_req_top;
		break;
	case HDR_BEREQ:
		hp = ctx->http_bereq;
		break;
	case HDR_BERESP:
		hp = ctx->http_beresp;
		break;
	case HDR_RESP:
		hp = ctx->http_resp;
		break;
	default:
		WRONG("VRT_selecthttp 'where' invalid");
	}
	return (hp);
}

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

VCL_STRING
VRT_GetHdr(VRT_CTX, const struct gethdr_s *hs)
{
	VCL_HTTP hp;
	const char *p;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	if (hs->where == HDR_OBJ) {
		CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC);
		CHECK_OBJ_NOTNULL(ctx->req->objcore, OBJCORE_MAGIC);
		return (HTTP_GetHdrPack(ctx->req->wrk, ctx->req->objcore,
		    hs->what));
	}
	hp = VRT_selecthttp(ctx, hs->where);
	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
	if (!http_GetHdr(hp, hs->what, &p))
		return (NULL);
	return (p);
}

/*--------------------------------------------------------------------
 * Build STRANDS from what is essentially a STRING_LIST
 */

VCL_STRANDS
VPI_BundleStrands(int n, struct strands *s, char const **d, const char *f, ...)
{
	va_list ap;

	assert(n > 0);
	s->n = n;
	s->p = d;
	*d++ = f;
	va_start(ap, f);
	while(--n)
		*d++ = va_arg(ap, const char *);
	assert(va_arg(ap, const char *) == vrt_magic_string_end);
	va_end(ap);
	return (s);
}

/*--------------------------------------------------------------------
 * Compare two STRANDS
 */

int
VRT_CompareStrands(VCL_STRANDS a, VCL_STRANDS b)
{
	const char *pa = NULL, *pb = NULL;
	int na = 0, nb = 0;

	while (1) {
		if (pa != NULL && *pa == '\0')
			pa = NULL;
		if (pb != NULL && *pb == '\0')
			pb = NULL;
		if (pa == NULL && na < a->n)
			pa = a->p[na++];
		else if (pb == NULL && nb < b->n)
			pb = b->p[nb++];
		else if (pa == NULL && pb == NULL)
			return (0);
		else if (pa == NULL)
			return (-1);
		else if (pb == NULL)
			return (1);
		else if (*pa == '\0')
			pa = NULL;
		else if (*pb == '\0')
			pb = NULL;
		else if (*pa != *pb)
			return (*pa - *pb);
		else {
			pa++;
			pb++;
		}
	}
}

/*--------------------------------------------------------------------
 * STRANDS to BOOL
 */

VCL_BOOL
VRT_Strands2Bool(VCL_STRANDS s)
{
	int i;

	AN(s);
	for (i = 0; i < s->n; i++)
		if (s->p[i] != NULL)
			return (1);
	return (0);
}

/*--------------------------------------------------------------------
 * Collapse a STRING_LIST in the space provided, or return NULL
 */

char *
VRT_StringList(char *d, unsigned dl, const char *p, va_list ap)
{
	char *b, *e;
	unsigned x;

	b = d;
	e = b + dl;
	while (p != vrt_magic_string_end && b < e) {
		if (p != NULL && *p != '\0') {
			x = strlen(p);
			if (b + x < e)
				memcpy(b, p, x);
			b += x;
		}
		p = va_arg(ap, const char *);
	}
	if (b >= e)
		return (NULL);
	*b++ = '\0';
	return (b);
}

/*--------------------------------------------------------------------
 * Copy and merge a STRING_LIST into a workspace.
 */

const char *
VRT_String(struct ws *ws, const char *h, const char *p, va_list ap)
{
	char *b, *e;
	const char *q;
	unsigned u, x;
	va_list aq;

	u = WS_ReserveAll(ws);
	e = b = ws->f;
	e += u;

	va_copy(aq, ap);
	do
		q = va_arg(aq, const char *);
	while (q == NULL || (q != vrt_magic_string_end && *q == '\0'));

	if (h != NULL && p == NULL && q == vrt_magic_string_end &&
	    WS_Inside(ws, h, NULL)) {
		va_end(aq);
		WS_Release(ws, 0);
		return (h);
	}

	if (h == NULL && p != NULL && q == vrt_magic_string_end &&
	    WS_Inside(ws, p, NULL)) {
		va_end(aq);
		WS_Release(ws, 0);
		return (p);
	}

	if (h == NULL && p == NULL) {
		if (q == vrt_magic_string_end) {
			va_end(aq);
			WS_Release(ws, 0);
			return ("");
		}
		do
			p = va_arg(aq, const char *);
		while (p == NULL || (p != vrt_magic_string_end && *p == '\0'));
		if (p == vrt_magic_string_end && WS_Inside(ws, q, NULL)) {
			va_end(aq);
			WS_Release(ws, 0);
			return (q);
		}
		p = NULL;
		va_end(aq);
	}

	if (h != NULL) {
		x = strlen(h);
		if (b + x < e)
			memcpy(b, h, x);
		b += x;
		if (b < e)
			*b = ' ';
		b++;
	}
	b = VRT_StringList(b, e > b ? e - b : 0, p, ap);
	if (b == NULL || b == e) {
		WS_MarkOverflow(ws);
		WS_Release(ws, 0);
		return (NULL);
	}
	e = b;
	b = ws->f;
	WS_Release(ws, e - b);
	return (b);
}

/*--------------------------------------------------------------------
 * Copy and merge a STRING_LIST on the current workspace
 */

VCL_STRING
VRT_CollectString(VRT_CTX, const char *p, ...)
{
	va_list ap;
	const char *b;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
	va_start(ap, p);
	b = VRT_String(ctx->ws, NULL, p, ap);
	va_end(ap);
	if (b == NULL)
		VRT_fail(ctx, "Workspace overflow");
	return (b);
}

/*--------------------------------------------------------------------
 * Collapse STRANDS into the space provided, or return NULL
 */

char *
VRT_Strands(char *d, size_t dl, VCL_STRANDS s)
{
	char *b;
	const char *e;
	unsigned x;

	AN(d);
	AN(s);
	b = d;
	e = b + dl;
	for (int i = 0; i < s->n; i++)
		if (s->p[i] != NULL && *s->p[i] != '\0') {
			x = strlen(s->p[i]);
			if (b + x >= e)
				return (NULL);
			memcpy(b, s->p[i], x);
			b += x;
		}
	assert(b < e);
	*b++ = '\0';
	return (b);
}

/*--------------------------------------------------------------------
 * Copy and merge STRANDS into a workspace.
 */

VCL_STRING
VRT_StrandsWS(struct ws *ws, const char *h, VCL_STRANDS s)
{
	char *b;
	const char *q = NULL, *e;
	VCL_STRING r;
	unsigned u, x;
	int i;

	AN(s);
	u = WS_ReserveAll(ws);

	for (i = 0; i < s->n; i++)
		if (s->p[i] != NULL && *s->p[i] != '\0') {
			q = s->p[i];
			break;
		}

	if (h != NULL && q == NULL && WS_Inside(ws, h, NULL)) {
		WS_Release(ws, 0);
		return (h);
	}

	if (h == NULL) {
		if (q == NULL) {
			WS_Release(ws, 0);
			return ("");
		}
		if (WS_Inside(ws, q, NULL)) {
			for (i++; i < s->n; i++)
				if (s->p[i] != NULL && *s->p[i] != '\0')
					break;
			if (i == s->n) {
				WS_Release(ws, 0);
				return (q);
			}
		}
	}

	b = WS_Front(ws);
	e = b + u;

	if (h != NULL) {
		x = strlen(h);
		if (b + x < e)
			memcpy(b, h, x);
		b += x;
		if (b < e)
			*b = ' ';
		b++;
	}
	r = VRT_Strands(b, e > b ? e - b : 0, s);
	if (r == NULL || r == e) {
		WS_MarkOverflow(ws);
		WS_Release(ws, 0);
		return (NULL);
	}
	b = WS_Front(ws);
	WS_Release(ws, r - b);
	return (b);
}

/*--------------------------------------------------------------------
 * Copy and merge STRANDS on the current workspace
 */

VCL_STRING
VRT_CollectStrands(VRT_CTX, VCL_STRANDS s)
{
	const char *b;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
	b = VRT_StrandsWS(ctx->ws, NULL, s);
	if (b == NULL)
		VRT_fail(ctx, "Workspace overflow");
	return (b);
}

/*--------------------------------------------------------------------
 * upper/lower-case STRANDS (onto workspace)
 */

#include <stdio.h>

VCL_STRING
VRT_UpperLowerStrands(VRT_CTX, VCL_STRANDS s, int up)
{
	unsigned u;
	char *b, *e, *r;
	const char *p, *q = NULL;
	int i, copy = 0;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
	AN(s);
	u = WS_ReserveAll(ctx->ws);
	r = b = WS_Front(ctx->ws);
	e = b + u;
	for (i = 0; i < s->n; i++) {
		if (s->p[i] == NULL || s->p[i][0] == '\0')
			continue;
		if (q != NULL)
			copy = 1;
		p = q = s->p[i];
		for(p = q = s->p[i]; *p != '\0'; p++) {
			if ((up && vct_islower(*p)) ||
			    (!up && vct_isupper(*p))) {
				*b++ = *p ^ 0x20;
				copy = 1;
			} else if (b < e) {
				*b++ = *p;
			}
			if (copy && b == e)
				break;
		}
		if (copy && b == e) {
			WS_Release(ctx->ws, 0);
			VRT_fail(ctx, "Workspace overflow");
			return (NULL);
		}
	}
	assert(b <= e);
	if (!copy) {
		WS_Release(ctx->ws, 0);
		return (q);
	}
	assert(b < e);
	*b++ = '\0';
	assert(b <= e);
	WS_ReleaseP(ctx->ws, b);
	return (r);
}

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

VCL_VOID
VRT_SetHdr(VRT_CTX , const struct gethdr_s *hs,
    const char *p, ...)
{
	VCL_HTTP hp;
	va_list ap;
	const char *b;

	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
	AN(hs);
	AN(hs->what);
	hp = VRT_selecthttp(ctx, hs->where);
	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
	va_start(ap, p);
	if (p == vrt_magic_string_unset) {
		http_Unset(hp, hs->what);
	} else {
		b = VRT_String(hp->ws, hs->what + 1, p, ap);
		if (b == NULL) {
			VSLb(ctx->vsl, SLT_LostHeader, "%s", hs->what + 1);
		} else {
			http_Unset(hp, hs->what);
{'fraction_non_alphanumeric': 0.25307175622542594, 'fraction_numerical': 0.0081913499344692, 'mean_word_length': 2.2686746987951807, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 211, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7223674', 'n_tokens_mistral': 10627, 'n_tokens_neox': 9694, 'n_words': 2566}