Commit Diff


commit - cd32e8cf4864504cb356198701ca1aaeae0aa94b
commit + bbcba3edad343a751fe9bebefddfc812a26cc057
blob - 9bed9c14e5443d7a8d3b464cfde197fd0ad7abf9
blob + 24c237eaf92cd5bf99b729025b58fab050d58ded
--- .gitignore
+++ .gitignore
@@ -7,6 +7,20 @@
 **/parse.c
 **/parse.h
 
+**/Makefile
+**/*.in
+**/.deps
+**/.dirstamp
+
+aclocal.m4
+autom4te.cache
+config.h
+config.log
+config.status
+configure
+etc
+stamp-h1
+
 kamictl/kamictl
 kamid/kamid
 kamiftp/kamiftp
blob - 1b2fbf9ebe7ea86acf1e7d76453c86314a33db66 (mode 644)
blob + /dev/null
--- Makefile
+++ /dev/null
@@ -1,30 +0,0 @@
-SUBDIR = 
-SUBDIR += kamictl
-SUBDIR += kamid
-SUBDIR += kamiftp
-SUBDIR += kamirepl
-SUBDIR += ninepscript
-
-.if make(regress) || make(obj) || make(clean) || make(release)
-SUBDIR += regress
-.endif
-
-.if make(tags) || make(cleandir)
-SUBDIR += lib
-.endif
-
-.include "kamid-version.mk"
-
-release: clean
-	sed -i -e 's/_RELEASE=No/_RELEASE=Yes/' kamid-version.mk
-	${MAKE} dist
-	sed -i -e 's/_RELEASE=Yes/_RELEASE=No/' kamid-version.mk
-
-dist: clean
-	mkdir /tmp/kamid-${KAMID_VERSION}
-	pax -rw * /tmp/kamid-${KAMID_VERSION}
-	find /tmp/kamid-${KAMID_VERSION} -name obj -type d -delete
-	tar -C /tmp -zcf kamid-${KAMID_VERSION}.tar.gz kamid-${KAMID_VERSION}
-	rm -rf /tmp/kamid-${KAMID_VERSION}
-
-.include <bsd.subdir.mk>
blob - /dev/null
blob + c9929c120717464cab46b8833c4e0eac75ae6c2c (mode 644)
--- /dev/null
+++ Makefile.am
@@ -0,0 +1,19 @@
+SUBDIRS = kamictl kamid kamiftp kamirepl ninepscript
+
+AM_CPPFLAGS += -DKAMID_VERSION='"@VERSION"' \
+	-I$(top_srcdir)/lib \
+	-I$(top_srcdir)/compat
+
+LDADD = $(LIBOBJS)
+
+AM_CFLAGS += -Wall
+AM_CFLAGS += -Wextra
+AM_CFLAGS += -Wmissing-prototypes
+AM_CFLAGS += -Wstrict-prototypes
+AM_CFLAGS += -Wwrite-strings
+AM_CFLAGS += -Wno-unused-parameter
+
+test:
+	@echo XXX: figure it out something
+	@false
+#$(MAKE) -C regress 
blob - /dev/null
blob + 785467871ee34cbb0f2a4792c0ea48ebafe2da68 (mode 755)
--- /dev/null
+++ autogen.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+die()
+{
+    echo "$@" >&2
+    exit $2
+}
+
+autoreconf -f -i -v || die "autoreconf failed" $?
blob - 9074383dc2bbfc282ccb63be5fc10a3585b6c817 (mode 644)
blob + /dev/null
--- kamictl/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-.PATH:${.CURDIR}/../lib
-
-.include "../kamid-version.mk"
-
-PROG=		kamictl
-SRCS=		ctl_parser.c kamictl.c log.c sandbox.c
-MAN=		kamictl.8
-
-CPPFLAGS=	-I${.CURDIR}/../lib -I${.CURDIR}/../kamid/ -I${.CURDIR}
-
-LDADD+= -lutil
-DPADD+= ${LIBUTIL}
-
-.if ${KAMID_RELEASE} != Yes
-NOMAN= Yes
-.endif
-
-.include <bsd.prog.mk>
blob - /dev/null
blob + ea6df133cafc1cc4e88119b1054d4d3e0b2978eb (mode 644)
--- /dev/null
+++ kamictl/Makefile.am
@@ -0,0 +1,21 @@
+bin_PROGRAMS = kamictl
+
+kamictl_SOURCES=ctl_parser.c			\
+		ctl_parser.h			\
+		kamictl.c			\
+		$(top_srcdir)/compat.h		\
+		$(top_srcdir)/lib/log.c		\
+		$(top_srcdir)/lib/log.h		\
+		$(top_srcdir)/lib/sandbox.c	\
+		$(top_srcdir)/lib/sandbox.h
+
+man8_MANS =	kamictl.8
+
+LDADD =		$(LIBOBJS)
+
+AM_CPPFLAGS +=	-DKAMID_VERSION='"@VERSION@"'	\
+		-I$(top_srcdir)/		\
+		-I$(top_srcdir)/compat		\
+		-I$(top_srcdir)/lib		\
+		-I$(top_srcdir)/kamictl		\
+		-I$(top_srcdir)/kamid
blob - b0515084f057d8663be62be76614f58434f45c2c
blob + b0346a8d6354022bdcf2f51f0cf0786afe344f86
--- kamictl/ctl_parser.c
+++ kamictl/ctl_parser.c
@@ -15,15 +15,11 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/uio.h>
+#include "compat.h"
 
-#include <event.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
-#include <imsg.h>
 
 #include "ctl_parser.h"
 #include "kamid.h"
blob - be14f113641e49a974eaa5f420de82e4b67dccfa
blob + 34607ae1e6d70d9065d0da1719a00985a289f58a
--- kamictl/kamictl.c
+++ kamictl/kamictl.c
@@ -14,20 +14,19 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "compat.h"
+
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/uio.h>
 #include <sys/un.h>
 
-#include <err.h>
-#include <event.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
-#include <imsg.h>
 
 #include "ctl_parser.h"
 #include "kamid.h"
blob - /dev/null
blob + 70d2ea27dc6aebc565bc7ae98b4afe074d88045f (mode 644)
--- /dev/null
+++ compat/asprintf.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2006 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "compat.h"
+
+int
+asprintf(char **ret, const char *fmt, ...)
+{
+	va_list	ap;
+	int	n;
+
+	va_start(ap, fmt);
+	n = vasprintf(ret, fmt, ap);
+	va_end(ap);
+
+	return (n);
+}
+
+int
+vasprintf(char **ret, const char *fmt, va_list ap)
+{
+	int	 n;
+	va_list  ap2;
+
+	va_copy(ap2, ap);
+
+	if ((n = vsnprintf(NULL, 0, fmt, ap)) < 0)
+		goto error;
+
+	if ((*ret = malloc(n + 1)) == NULL)
+		goto error;
+	if ((n = vsnprintf(*ret, n + 1, fmt, ap2)) < 0) {
+		free(*ret);
+		goto error;
+	}
+	va_end(ap2);
+
+	return (n);
+
+error:
+	va_end(ap2);
+	*ret = NULL;
+	return (-1);
+}
blob - /dev/null
blob + 244909c09b1829522fc3429f5e207de7edd34bc2 (mode 644)
--- /dev/null
+++ compat/err.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "compat.h"
+
+static void vwarn(const char*, va_list);
+static void vwarnx(const char*, va_list);
+
+static void
+vwarn(const char *fmt, va_list ap)
+{
+	fprintf(stderr, "%s: ", getprogname());
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, ": %s\n", strerror(errno));
+}
+
+static void
+vwarnx(const char *fmt, va_list ap)
+{
+	fprintf(stderr, "%s: ", getprogname());
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
+}
+
+void
+err(int ret, const char *fmt, ...)
+{
+	va_list	ap;
+
+	va_start(ap, fmt);
+	vwarn(fmt, ap);
+	va_end(ap);
+	exit(ret);
+}
+
+void
+errx(int ret, const char *fmt, ...)
+{
+	va_list	ap;
+
+	va_start(ap, fmt);
+	vwarnx(fmt, ap);
+	va_end(ap);
+	exit(ret);
+}
+
+void
+warn(const char *fmt, ...)
+{
+	va_list	ap;
+
+	va_start(ap, fmt);
+	vwarn(fmt, ap);
+	va_end(ap);
+}
+
+void
+warnx(const char *fmt, ...)
+{
+	va_list	ap;
+
+	va_start(ap, fmt);
+	vwarnx(fmt, ap);
+	va_end(ap);
+}
blob - /dev/null
blob + 1b2d5f59da6911eb007cd5a1fae0d58fc2fce889 (mode 644)
--- /dev/null
+++ compat/fmt_scaled.c
@@ -0,0 +1,159 @@
+/*	$OpenBSD: fmt_scaled.c,v 1.19 2020/10/12 22:08:34 deraadt Exp $	*/
+
+/*
+ * Copyright (c) 2001, 2002, 2003 Ian F. Darwin.  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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+/*
+ * fmt_scaled: Format numbers scaled for human comprehension
+ * scan_scaled: Scan numbers in this format.
+ *
+ * "Human-readable" output uses 4 digits max, and puts a unit suffix at
+ * the end.  Makes output compact and easy-to-read esp. on huge disks.
+ * Formatting code was originally in OpenBSD "df", converted to library routine.
+ * Scanning code written for OpenBSD libutil.
+ */
+
+#include "compat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+typedef enum {
+	NONE = 0, KILO = 1, MEGA = 2, GIGA = 3, TERA = 4, PETA = 5, EXA = 6
+} unit_type;
+
+/* These three arrays MUST be in sync!  XXX make a struct */
+static const unit_type units[] = { NONE, KILO, MEGA, GIGA, TERA, PETA, EXA };
+static const char scale_chars[] = "BKMGTPE";
+static const long long scale_factors[] = {
+	1LL,
+	1024LL,
+	1024LL*1024,
+	1024LL*1024*1024,
+	1024LL*1024*1024*1024,
+	1024LL*1024*1024*1024*1024,
+	1024LL*1024*1024*1024*1024*1024,
+};
+#define	SCALE_LENGTH (sizeof(units)/sizeof(units[0]))
+
+#define MAX_DIGITS (SCALE_LENGTH * 3)	/* XXX strlen(sprintf("%lld", -1)? */
+
+/* Format the given "number" into human-readable form in "result".
+ * Result must point to an allocated buffer of length FMT_SCALED_STRSIZE.
+ * Return 0 on success, -1 and errno set if error.
+ */
+int
+fmt_scaled(long long number, char *result)
+{
+	long long abval, fract = 0;
+	unsigned int i;
+	unit_type unit = NONE;
+
+	/* Not every negative long long has a positive representation. */
+	if (number == LLONG_MIN) {
+		errno = ERANGE;
+		return -1;
+	}
+
+	abval = llabs(number);
+
+	/* Also check for numbers that are just too darned big to format. */
+	if (abval / 1024 >= scale_factors[SCALE_LENGTH-1]) {
+		errno = ERANGE;
+		return -1;
+	}
+
+	/* scale whole part; get unscaled fraction */
+	for (i = 0; i < SCALE_LENGTH; i++) {
+		if (abval/1024 < scale_factors[i]) {
+			unit = units[i];
+			fract = (i == 0) ? 0 : abval % scale_factors[i];
+			number /= scale_factors[i];
+			if (i > 0)
+				fract /= scale_factors[i - 1];
+			break;
+		}
+	}
+
+	fract = (10 * fract + 512) / 1024;
+	/* if the result would be >= 10, round main number */
+	if (fract >= 10) {
+		if (number >= 0)
+			number++;
+		else
+			number--;
+		fract = 0;
+	} else if (fract < 0) {
+		/* shouldn't happen */
+		fract = 0;
+	}
+
+	if (number == 0)
+		strlcpy(result, "0B", FMT_SCALED_STRSIZE);
+	else if (unit == NONE || number >= 100 || number <= -100) {
+		if (fract >= 5) {
+			if (number >= 0)
+				number++;
+			else
+				number--;
+		}
+		(void)snprintf(result, FMT_SCALED_STRSIZE, "%lld%c",
+			number, scale_chars[unit]);
+	} else
+		(void)snprintf(result, FMT_SCALED_STRSIZE, "%lld.%1lld%c",
+			number, fract, scale_chars[unit]);
+
+	return 0;
+}
+
+#ifdef	MAIN
+/*
+ * This is the original version of the program in the man page.
+ * Copy-and-paste whatever you need from it.
+ */
+int
+main(int argc, char **argv)
+{
+	char *cinput = "1.5K", buf[FMT_SCALED_STRSIZE];
+	long long ninput = 10483892, result;
+
+	if (scan_scaled(cinput, &result) == 0)
+		printf("\"%s\" -> %lld\n", cinput, result);
+	else
+		perror(cinput);
+
+	if (fmt_scaled(ninput, buf) == 0)
+		printf("%lld -> \"%s\"\n", ninput, buf);
+	else
+		fprintf(stderr, "%lld invalid (%s)\n", ninput, strerror(errno));
+
+	return 0;
+}
+#endif
blob - /dev/null
blob + 9929f01e8cc1fccffb64b4699ec6fdf5ad566446 (mode 644)
--- /dev/null
+++ compat/freezero.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "../compat.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+void
+freezero(void *ptr, size_t len)
+{
+	if (ptr == NULL)
+		return;
+
+	memset(ptr, 0, len);
+	free(ptr);
+}
blob - /dev/null
blob + 1d75ae2114b24059d3b4f0fe18e8d925db430b5d (mode 644)
--- /dev/null
+++ compat/getdtablecount.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* XXX: on linux it's possible to glob("/proc/$pid/fd/ *") to know the
+ * dtablecount. */
+
+#include "compat.h"
+
+int
+getdtablecount(void)
+{
+	return 0;
+}
blob - /dev/null
blob + a9a8e9d52cba2c1315734677e41e7c83d6399f4b (mode 644)
--- /dev/null
+++ compat/getdtablesize.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "compat.h"
+
+#include <unistd.h>
+
+int
+getdtablesize(void)
+{
+	return sysconf(_SC_OPEN_MAX);
+}
blob - /dev/null
blob + 1b50f0c49503e60bd7ce9207d9cc07e7686673c2 (mode 644)
--- /dev/null
+++ compat/getprogname.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "compat.h"
+
+#ifdef HAVE_PROGRAM_INVOCATION_SHORT_NAME
+
+#include <errno.h>
+
+extern char *program_invocation_short_name;
+
+const char *
+getprogname(void)
+{
+	return program_invocation_short_name;
+}
+
+#else
+
+const char *
+getprogname(void)
+{
+	return "kamid";
+}
+
+#endif
blob - /dev/null
blob + 7bd827d6354db030b1fbffc9a365200e017a557e (mode 644)
--- /dev/null
+++ compat/imsg-buffer.c
@@ -0,0 +1,310 @@
+/*	$OpenBSD: imsg-buffer.c,v 1.12 2019/01/20 02:50:03 bcook Exp $	*/
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "compat.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "imsg.h"
+
+static int	ibuf_realloc(struct ibuf *, size_t);
+static void	ibuf_enqueue(struct msgbuf *, struct ibuf *);
+static void	ibuf_dequeue(struct msgbuf *, struct ibuf *);
+
+struct ibuf *
+ibuf_open(size_t len)
+{
+	struct ibuf	*buf;
+
+	if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
+		return (NULL);
+	if ((buf->buf = malloc(len)) == NULL) {
+		free(buf);
+		return (NULL);
+	}
+	buf->size = buf->max = len;
+	buf->fd = -1;
+
+	return (buf);
+}
+
+struct ibuf *
+ibuf_dynamic(size_t len, size_t max)
+{
+	struct ibuf	*buf;
+
+	if (max < len)
+		return (NULL);
+
+	if ((buf = ibuf_open(len)) == NULL)
+		return (NULL);
+
+	if (max > 0)
+		buf->max = max;
+
+	return (buf);
+}
+
+static int
+ibuf_realloc(struct ibuf *buf, size_t len)
+{
+	unsigned char	*b;
+
+	/* on static buffers max is eq size and so the following fails */
+	if (buf->wpos + len > buf->max) {
+		errno = ERANGE;
+		return (-1);
+	}
+
+	b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1);
+	if (b == NULL)
+		return (-1);
+	buf->buf = b;
+	buf->size = buf->wpos + len;
+
+	return (0);
+}
+
+int
+ibuf_add(struct ibuf *buf, const void *data, size_t len)
+{
+	if (buf->wpos + len > buf->size)
+		if (ibuf_realloc(buf, len) == -1)
+			return (-1);
+
+	memcpy(buf->buf + buf->wpos, data, len);
+	buf->wpos += len;
+	return (0);
+}
+
+void *
+ibuf_reserve(struct ibuf *buf, size_t len)
+{
+	void	*b;
+
+	if (buf->wpos + len > buf->size)
+		if (ibuf_realloc(buf, len) == -1)
+			return (NULL);
+
+	b = buf->buf + buf->wpos;
+	buf->wpos += len;
+	return (b);
+}
+
+void *
+ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
+{
+	/* only allowed to seek in already written parts */
+	if (pos + len > buf->wpos)
+		return (NULL);
+
+	return (buf->buf + pos);
+}
+
+size_t
+ibuf_size(struct ibuf *buf)
+{
+	return (buf->wpos);
+}
+
+size_t
+ibuf_left(struct ibuf *buf)
+{
+	return (buf->max - buf->wpos);
+}
+
+void
+ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+	ibuf_enqueue(msgbuf, buf);
+}
+
+int
+ibuf_write(struct msgbuf *msgbuf)
+{
+	struct iovec	 iov[IOV_MAX];
+	struct ibuf	*buf;
+	unsigned int	 i = 0;
+	ssize_t	n;
+
+	memset(&iov, 0, sizeof(iov));
+	TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+		if (i >= IOV_MAX)
+			break;
+		iov[i].iov_base = buf->buf + buf->rpos;
+		iov[i].iov_len = buf->wpos - buf->rpos;
+		i++;
+	}
+
+again:
+	if ((n = writev(msgbuf->fd, iov, i)) == -1) {
+		if (errno == EINTR)
+			goto again;
+		if (errno == ENOBUFS)
+			errno = EAGAIN;
+		return (-1);
+	}
+
+	if (n == 0) {			/* connection closed */
+		errno = 0;
+		return (0);
+	}
+
+	msgbuf_drain(msgbuf, n);
+
+	return (1);
+}
+
+void
+ibuf_free(struct ibuf *buf)
+{
+	if (buf == NULL)
+		return;
+	freezero(buf->buf, buf->size);
+	free(buf);
+}
+
+void
+msgbuf_init(struct msgbuf *msgbuf)
+{
+	msgbuf->queued = 0;
+	msgbuf->fd = -1;
+	TAILQ_INIT(&msgbuf->bufs);
+}
+
+void
+msgbuf_drain(struct msgbuf *msgbuf, size_t n)
+{
+	struct ibuf	*buf, *next;
+
+	for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
+	    buf = next) {
+		next = TAILQ_NEXT(buf, entry);
+		if (buf->rpos + n >= buf->wpos) {
+			n -= buf->wpos - buf->rpos;
+			ibuf_dequeue(msgbuf, buf);
+		} else {
+			buf->rpos += n;
+			n = 0;
+		}
+	}
+}
+
+void
+msgbuf_clear(struct msgbuf *msgbuf)
+{
+	struct ibuf	*buf;
+
+	while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
+		ibuf_dequeue(msgbuf, buf);
+}
+
+int
+msgbuf_write(struct msgbuf *msgbuf)
+{
+	struct iovec	 iov[IOV_MAX];
+	struct ibuf	*buf;
+	unsigned int	 i = 0;
+	ssize_t		 n;
+	struct msghdr	 msg;
+	struct cmsghdr	*cmsg;
+	union {
+		struct cmsghdr	hdr;
+		char		buf[CMSG_SPACE(sizeof(int))];
+	} cmsgbuf;
+
+	memset(&iov, 0, sizeof(iov));
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmsgbuf, 0, sizeof(cmsgbuf));
+	TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+		if (i >= IOV_MAX)
+			break;
+		iov[i].iov_base = buf->buf + buf->rpos;
+		iov[i].iov_len = buf->wpos - buf->rpos;
+		i++;
+		if (buf->fd != -1)
+			break;
+	}
+
+	msg.msg_iov = iov;
+	msg.msg_iovlen = i;
+
+	if (buf != NULL && buf->fd != -1) {
+		msg.msg_control = (caddr_t)&cmsgbuf.buf;
+		msg.msg_controllen = sizeof(cmsgbuf.buf);
+		cmsg = CMSG_FIRSTHDR(&msg);
+		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+		cmsg->cmsg_level = SOL_SOCKET;
+		cmsg->cmsg_type = SCM_RIGHTS;
+		*(int *)CMSG_DATA(cmsg) = buf->fd;
+	}
+
+again:
+	if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
+		if (errno == EINTR)
+			goto again;
+		if (errno == ENOBUFS)
+			errno = EAGAIN;
+		return (-1);
+	}
+
+	if (n == 0) {			/* connection closed */
+		errno = 0;
+		return (0);
+	}
+
+	/*
+	 * assumption: fd got sent if sendmsg sent anything
+	 * this works because fds are passed one at a time
+	 */
+	if (buf != NULL && buf->fd != -1) {
+		close(buf->fd);
+		buf->fd = -1;
+	}
+
+	msgbuf_drain(msgbuf, n);
+
+	return (1);
+}
+
+static void
+ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+	TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
+	msgbuf->queued++;
+}
+
+static void
+ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+	TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
+
+	if (buf->fd != -1)
+		close(buf->fd);
+
+	msgbuf->queued--;
+	ibuf_free(buf);
+}
blob - /dev/null
blob + 6c746751450bd523a4fb6736a4347900fd01d5bb (mode 644)
--- /dev/null
+++ compat/imsg.c
@@ -0,0 +1,303 @@
+/*	$OpenBSD: imsg.c,v 1.16 2017/12/14 09:27:44 kettenis Exp $	*/
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "compat.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "imsg.h"
+
+int	 imsg_fd_overhead = 0;
+
+static int	 imsg_get_fd(struct imsgbuf *);
+
+void
+imsg_init(struct imsgbuf *ibuf, int fd)
+{
+	msgbuf_init(&ibuf->w);
+	memset(&ibuf->r, 0, sizeof(ibuf->r));
+	ibuf->fd = fd;
+	ibuf->w.fd = fd;
+	ibuf->pid = getpid();
+	TAILQ_INIT(&ibuf->fds);
+}
+
+ssize_t
+imsg_read(struct imsgbuf *ibuf)
+{
+	struct msghdr		 msg;
+	struct cmsghdr		*cmsg;
+	union {
+		struct cmsghdr hdr;
+		char	buf[CMSG_SPACE(sizeof(int) * 1)];
+	} cmsgbuf;
+	struct iovec		 iov;
+	ssize_t			 n = -1;
+	int			 fd;
+	struct imsg_fd		*ifd;
+
+	memset(&msg, 0, sizeof(msg));
+	memset(&cmsgbuf, 0, sizeof(cmsgbuf));
+
+	iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
+	iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = &cmsgbuf.buf;
+	msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+	if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
+		return (-1);
+
+again:
+	if (getdtablecount() + imsg_fd_overhead +
+	    (int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))
+	    >= getdtablesize()) {
+		errno = EAGAIN;
+		free(ifd);
+		return (-1);
+	}
+
+	if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
+		if (errno == EINTR)
+			goto again;
+		goto fail;
+	}
+
+	ibuf->r.wpos += n;
+
+	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+		if (cmsg->cmsg_level == SOL_SOCKET &&
+		    cmsg->cmsg_type == SCM_RIGHTS) {
+			int i;
+			int j;
+
+			/*
+			 * We only accept one file descriptor.  Due to C
+			 * padding rules, our control buffer might contain
+			 * more than one fd, and we must close them.
+			 */
+			j = ((char *)cmsg + cmsg->cmsg_len -
+			    (char *)CMSG_DATA(cmsg)) / sizeof(int);
+			for (i = 0; i < j; i++) {
+				fd = ((int *)CMSG_DATA(cmsg))[i];
+				if (ifd != NULL) {
+					ifd->fd = fd;
+					TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
+					    entry);
+					ifd = NULL;
+				} else
+					close(fd);
+			}
+		}
+		/* we do not handle other ctl data level */
+	}
+
+fail:
+	free(ifd);
+	return (n);
+}
+
+ssize_t
+imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
+{
+	size_t			 av, left, datalen;
+
+	av = ibuf->r.wpos;
+
+	if (IMSG_HEADER_SIZE > av)
+		return (0);
+
+	memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
+	if (imsg->hdr.len < IMSG_HEADER_SIZE ||
+	    imsg->hdr.len > MAX_IMSGSIZE) {
+		errno = ERANGE;
+		return (-1);
+	}
+	if (imsg->hdr.len > av)
+		return (0);
+	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+	ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
+	if (datalen == 0)
+		imsg->data = NULL;
+	else if ((imsg->data = malloc(datalen)) == NULL)
+		return (-1);
+
+	if (imsg->hdr.flags & IMSGF_HASFD)
+		imsg->fd = imsg_get_fd(ibuf);
+	else
+		imsg->fd = -1;
+
+	memcpy(imsg->data, ibuf->r.rptr, datalen);
+
+	if (imsg->hdr.len < av) {
+		left = av - imsg->hdr.len;
+		memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
+		ibuf->r.wpos = left;
+	} else
+		ibuf->r.wpos = 0;
+
+	return (datalen + IMSG_HEADER_SIZE);
+}
+
+int
+imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
+    int fd, const void *data, uint16_t datalen)
+{
+	struct ibuf	*wbuf;
+
+	if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+		return (-1);
+
+	if (imsg_add(wbuf, data, datalen) == -1)
+		return (-1);
+
+	wbuf->fd = fd;
+
+	imsg_close(ibuf, wbuf);
+
+	return (1);
+}
+
+int
+imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
+    int fd, const struct iovec *iov, int iovcnt)
+{
+	struct ibuf	*wbuf;
+	int		 i, datalen = 0;
+
+	for (i = 0; i < iovcnt; i++)
+		datalen += iov[i].iov_len;
+
+	if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+		return (-1);
+
+	for (i = 0; i < iovcnt; i++)
+		if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
+			return (-1);
+
+	wbuf->fd = fd;
+
+	imsg_close(ibuf, wbuf);
+
+	return (1);
+}
+
+/* ARGSUSED */
+struct ibuf *
+imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
+    uint16_t datalen)
+{
+	struct ibuf	*wbuf;
+	struct imsg_hdr	 hdr;
+
+	datalen += IMSG_HEADER_SIZE;
+	if (datalen > MAX_IMSGSIZE) {
+		errno = ERANGE;
+		return (NULL);
+	}
+
+	hdr.type = type;
+	hdr.flags = 0;
+	hdr.peerid = peerid;
+	if ((hdr.pid = pid) == 0)
+		hdr.pid = ibuf->pid;
+	if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
+		return (NULL);
+	}
+	if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
+		return (NULL);
+
+	return (wbuf);
+}
+
+int
+imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
+{
+	if (datalen)
+		if (ibuf_add(msg, data, datalen) == -1) {
+			ibuf_free(msg);
+			return (-1);
+		}
+	return (datalen);
+}
+
+void
+imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
+{
+	struct imsg_hdr	*hdr;
+
+	hdr = (struct imsg_hdr *)msg->buf;
+
+	hdr->flags &= ~IMSGF_HASFD;
+	if (msg->fd != -1)
+		hdr->flags |= IMSGF_HASFD;
+
+	hdr->len = (uint16_t)msg->wpos;
+
+	ibuf_close(&ibuf->w, msg);
+}
+
+void
+imsg_free(struct imsg *imsg)
+{
+	freezero(imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE);
+}
+
+static int
+imsg_get_fd(struct imsgbuf *ibuf)
+{
+	int		 fd;
+	struct imsg_fd	*ifd;
+
+	if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
+		return (-1);
+
+	fd = ifd->fd;
+	TAILQ_REMOVE(&ibuf->fds, ifd, entry);
+	free(ifd);
+
+	return (fd);
+}
+
+int
+imsg_flush(struct imsgbuf *ibuf)
+{
+	while (ibuf->w.queued)
+		if (msgbuf_write(&ibuf->w) <= 0)
+			return (-1);
+	return (0);
+}
+
+void
+imsg_clear(struct imsgbuf *ibuf)
+{
+	int	fd;
+
+	msgbuf_clear(&ibuf->w);
+	while ((fd = imsg_get_fd(ibuf)) != -1)
+		close(fd);
+}
blob - /dev/null
blob + 5b092cfcfd53b5b09a2d5932010f421d6795e0aa (mode 644)
--- /dev/null
+++ compat/imsg.h
@@ -0,0 +1,113 @@
+/*	$OpenBSD: imsg.h,v 1.5 2019/01/20 02:50:03 bcook Exp $	*/
+
+/*
+ * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
+ * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IMSG_H_
+#define _IMSG_H_
+
+#include <stdint.h>
+
+#define IBUF_READ_SIZE		65535
+#define IMSG_HEADER_SIZE	sizeof(struct imsg_hdr)
+#define MAX_IMSGSIZE		16384
+
+struct ibuf {
+	TAILQ_ENTRY(ibuf)	 entry;
+	unsigned char		*buf;
+	size_t			 size;
+	size_t			 max;
+	size_t			 wpos;
+	size_t			 rpos;
+	int			 fd;
+};
+
+struct msgbuf {
+	TAILQ_HEAD(, ibuf)	 bufs;
+	uint32_t		 queued;
+	int			 fd;
+};
+
+struct ibuf_read {
+	unsigned char		 buf[IBUF_READ_SIZE];
+	unsigned char		*rptr;
+	size_t			 wpos;
+};
+
+struct imsg_fd {
+	TAILQ_ENTRY(imsg_fd)	entry;
+	int			fd;
+};
+
+struct imsgbuf {
+	TAILQ_HEAD(, imsg_fd)	 fds;
+	struct ibuf_read	 r;
+	struct msgbuf		 w;
+	int			 fd;
+	pid_t			 pid;
+};
+
+#define IMSGF_HASFD	1
+
+struct imsg_hdr {
+	uint32_t	 type;
+	uint16_t	 len;
+	uint16_t	 flags;
+	uint32_t	 peerid;
+	uint32_t	 pid;
+};
+
+struct imsg {
+	struct imsg_hdr	 hdr;
+	int		 fd;
+	void		*data;
+};
+
+
+/* buffer.c */
+struct ibuf	*ibuf_open(size_t);
+struct ibuf	*ibuf_dynamic(size_t, size_t);
+int		 ibuf_add(struct ibuf *, const void *, size_t);
+void		*ibuf_reserve(struct ibuf *, size_t);
+void		*ibuf_seek(struct ibuf *, size_t, size_t);
+size_t		 ibuf_size(struct ibuf *);
+size_t		 ibuf_left(struct ibuf *);
+void		 ibuf_close(struct msgbuf *, struct ibuf *);
+int		 ibuf_write(struct msgbuf *);
+void		 ibuf_free(struct ibuf *);
+void		 msgbuf_init(struct msgbuf *);
+void		 msgbuf_clear(struct msgbuf *);
+int		 msgbuf_write(struct msgbuf *);
+void		 msgbuf_drain(struct msgbuf *, size_t);
+
+/* imsg.c */
+void	 imsg_init(struct imsgbuf *, int);
+ssize_t	 imsg_read(struct imsgbuf *);
+ssize_t	 imsg_get(struct imsgbuf *, struct imsg *);
+int	 imsg_compose(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
+	    const void *, uint16_t);
+int	 imsg_composev(struct imsgbuf *, uint32_t, uint32_t,  pid_t, int,
+	    const struct iovec *, int);
+struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, uint16_t);
+int	 imsg_add(struct ibuf *, const void *, uint16_t);
+void	 imsg_close(struct imsgbuf *, struct ibuf *);
+void	 imsg_free(struct imsg *);
+int	 imsg_flush(struct imsgbuf *);
+void	 imsg_clear(struct imsgbuf *);
+
+#endif
blob - /dev/null
blob + 9b11960def28ef34941658a0091836c135a3d8b4 (mode 644)
--- /dev/null
+++ compat/memmem.c
@@ -0,0 +1,183 @@
+/*	$OpenBSD: memmem.c,v 1.5 2020/04/16 12:39:28 claudio Exp $ */
+
+/*
+ * Copyright (c) 2005-2020 Rich Felker, et al.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <string.h>
+#include <stdint.h>
+
+static char *
+twobyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
+{
+	uint16_t nw = n[0]<<8 | n[1], hw = h[0]<<8 | h[1];
+	for (h+=2, k-=2; k; k--, hw = hw<<8 | *h++)
+		if (hw == nw) return (char *)h-2;
+	return hw == nw ? (char *)h-2 : 0;
+}
+
+static char *
+threebyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
+{
+	uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8;
+	uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8;
+	for (h+=3, k-=3; k; k--, hw = (hw|*h++)<<8)
+		if (hw == nw) return (char *)h-3;
+	return hw == nw ? (char *)h-3 : 0;
+}
+
+static char *
+fourbyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
+{
+	uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8 | n[3];
+	uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8 | h[3];
+	for (h+=4, k-=4; k; k--, hw = hw<<8 | *h++)
+		if (hw == nw) return (char *)h-4;
+	return hw == nw ? (char *)h-4 : 0;
+}
+
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+#define BITOP(a,b,op) \
+ ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
+
+/*
+ * Maxime Crochemore and Dominique Perrin, Two-way string-matching,
+ * Journal of the ACM, 38(3):651-675, July 1991.
+ */
+static char *
+twoway_memmem(const unsigned char *h, const unsigned char *z,
+    const unsigned char *n, size_t l)
+{
+	size_t i, ip, jp, k, p, ms, p0, mem, mem0;
+	size_t byteset[32 / sizeof(size_t)] = { 0 };
+	size_t shift[256];
+
+	/* Computing length of needle and fill shift table */
+	for (i=0; i<l; i++)
+		BITOP(byteset, n[i], |=), shift[n[i]] = i+1;
+
+	/* Compute maximal suffix */
+	ip = -1; jp = 0; k = p = 1;
+	while (jp+k<l) {
+		if (n[ip+k] == n[jp+k]) {
+			if (k == p) {
+				jp += p;
+				k = 1;
+			} else k++;
+		} else if (n[ip+k] > n[jp+k]) {
+			jp += k;
+			k = 1;
+			p = jp - ip;
+		} else {
+			ip = jp++;
+			k = p = 1;
+		}
+	}
+	ms = ip;
+	p0 = p;
+
+	/* And with the opposite comparison */
+	ip = -1; jp = 0; k = p = 1;
+	while (jp+k<l) {
+		if (n[ip+k] == n[jp+k]) {
+			if (k == p) {
+				jp += p;
+				k = 1;
+			} else k++;
+		} else if (n[ip+k] < n[jp+k]) {
+			jp += k;
+			k = 1;
+			p = jp - ip;
+		} else {
+			ip = jp++;
+			k = p = 1;
+		}
+	}
+	if (ip+1 > ms+1) ms = ip;
+	else p = p0;
+
+	/* Periodic needle? */
+	if (memcmp(n, n+p, ms+1)) {
+		mem0 = 0;
+		p = MAX(ms, l-ms-1) + 1;
+	} else mem0 = l-p;
+	mem = 0;
+
+	/* Search loop */
+	for (;;) {
+		/* If remainder of haystack is shorter than needle, done */
+		if (z-h < l) return 0;
+
+		/* Check last byte first; advance by shift on mismatch */
+		if (BITOP(byteset, h[l-1], &)) {
+			k = l-shift[h[l-1]];
+			if (k) {
+				if (k < mem) k = mem;
+				h += k;
+				mem = 0;
+				continue;
+			}
+		} else {
+			h += l;
+			mem = 0;
+			continue;
+		}
+
+		/* Compare right half */
+		for (k=MAX(ms+1,mem); k<l && n[k] == h[k]; k++);
+		if (k < l) {
+			h += k-ms;
+			mem = 0;
+			continue;
+		}
+		/* Compare left half */
+		for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--);
+		if (k <= mem) return (char *)h;
+		h += p;
+		mem = mem0;
+	}
+}
+
+void *
+memmem(const void *h0, size_t k, const void *n0, size_t l)
+{
+	const unsigned char *h = h0, *n = n0;
+
+	/* Return immediately on empty needle */
+	if (!l) return (void *)h;
+
+	/* Return immediately when needle is longer than haystack */
+	if (k<l) return 0;
+
+	/* Use faster algorithms for short needles */
+	h = memchr(h0, *n, k);
+	if (!h || l==1) return (void *)h;
+	k -= h - (const unsigned char *)h0;
+	if (k<l) return 0;
+	if (l==2) return twobyte_memmem(h, k, n);
+	if (l==3) return threebyte_memmem(h, k, n);
+	if (l==4) return fourbyte_memmem(h, k, n);
+
+	return twoway_memmem(h, h+k, n, l);
+}
blob - /dev/null
blob + 578765401498b3c2b58b1994596d4204ba17d7a5 (mode 644)
--- /dev/null
+++ compat/ohash.c
@@ -0,0 +1,329 @@
+/* $OpenBSD: ohash.c,v 1.1 2014/06/02 18:52:03 deraadt Exp $ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "compat.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include "ohash.h"
+
+struct _ohash_record {
+	uint32_t	hv;
+	const char	*p;
+};
+
+#define DELETED		((const char *)h)
+#define NONE		(h->size)
+
+/* Don't bother changing the hash table if the change is small enough.  */
+#define MINSIZE		(1UL << 4)
+#define MINDELETED	4
+
+static void ohash_resize(struct ohash *);
+
+
+/* This handles the common case of variable length keys, where the
+ * key is stored at the end of the record.
+ */
+void *
+ohash_create_entry(struct ohash_info *i, const char *start, const char **end)
+{
+	char *p;
+
+	if (!*end)
+		*end = start + strlen(start);
+	p = (i->alloc)(i->key_offset + (*end - start) + 1, i->data);
+	if (p) {
+		memcpy(p+i->key_offset, start, *end-start);
+		p[i->key_offset + (*end - start)] = '\0';
+	}
+	return (void *)p;
+}
+
+/* hash_delete only frees the hash structure. Use hash_first/hash_next
+ * to free entries as well.  */
+void
+ohash_delete(struct ohash *h)
+{
+	(h->info.free)(h->t, h->info.data);
+#ifndef NDEBUG
+	h->t = NULL;
+#endif
+}
+
+static void
+ohash_resize(struct ohash *h)
+{
+	struct _ohash_record *n;
+	size_t ns;
+	unsigned int	j;
+	unsigned int	i, incr;
+
+	if (4 * h->deleted < h->total) {
+		if (h->size >= (UINT_MAX >> 1U))
+			ns = UINT_MAX;
+		else
+			ns = h->size << 1U;
+	} else if (3 * h->deleted > 2 * h->total)
+		ns = h->size >> 1U;
+	else
+		ns = h->size;
+	if (ns < MINSIZE)
+		ns = MINSIZE;
+#ifdef STATS_HASH
+	STAT_HASH_EXPAND++;
+	STAT_HASH_SIZE += ns - h->size;
+#endif
+
+	n = (h->info.calloc)(ns, sizeof(struct _ohash_record), h->info.data);
+	if (!n)
+		return;
+
+	for (j = 0; j < h->size; j++) {
+		if (h->t[j].p != NULL && h->t[j].p != DELETED) {
+			i = h->t[j].hv % ns;
+			incr = ((h->t[j].hv % (ns - 2)) & ~1) + 1;
+			while (n[i].p != NULL) {
+				i += incr;
+				if (i >= ns)
+					i -= ns;
+			}
+			n[i].hv = h->t[j].hv;
+			n[i].p = h->t[j].p;
+		}
+	}
+	(h->info.free)(h->t, h->info.data);
+	h->t = n;
+	h->size = ns;
+	h->total -= h->deleted;
+	h->deleted = 0;
+}
+
+void *
+ohash_remove(struct ohash *h, unsigned int i)
+{
+	void		*result = (void *)h->t[i].p;
+
+	if (result == NULL || result == DELETED)
+		return NULL;
+
+#ifdef STATS_HASH
+	STAT_HASH_ENTRIES--;
+#endif
+	h->t[i].p = DELETED;
+	h->deleted++;
+	if (h->deleted >= MINDELETED && 4 * h->deleted > h->total)
+		ohash_resize(h);
+	return result;
+}
+
+void *
+ohash_find(struct ohash *h, unsigned int i)
+{
+	if (h->t[i].p == DELETED)
+		return NULL;
+	else
+		return (void *)h->t[i].p;
+}
+
+void *
+ohash_insert(struct ohash *h, unsigned int i, void *p)
+{
+#ifdef STATS_HASH
+	STAT_HASH_ENTRIES++;
+#endif
+	if (h->t[i].p == DELETED) {
+		h->deleted--;
+		h->t[i].p = p;
+	} else {
+		h->t[i].p = p;
+		/* Arbitrary resize boundary.  Tweak if not efficient enough.  */
+		if (++h->total * 4 > h->size * 3)
+			ohash_resize(h);
+	}
+	return p;
+}
+
+unsigned int
+ohash_entries(struct ohash *h)
+{
+	return h->total - h->deleted;
+}
+
+void *
+ohash_first(struct ohash *h, unsigned int *pos)
+{
+	*pos = 0;
+	return ohash_next(h, pos);
+}
+
+void *
+ohash_next(struct ohash *h, unsigned int *pos)
+{
+	for (; *pos < h->size; (*pos)++)
+		if (h->t[*pos].p != DELETED && h->t[*pos].p != NULL)
+			return (void *)h->t[(*pos)++].p;
+	return NULL;
+}
+
+void
+ohash_init(struct ohash *h, unsigned int size, struct ohash_info *info)
+{
+	h->size = 1UL << size;
+	if (h->size < MINSIZE)
+		h->size = MINSIZE;
+#ifdef STATS_HASH
+	STAT_HASH_CREATION++;
+	STAT_HASH_SIZE += h->size;
+#endif
+	/* Copy info so that caller may free it.  */
+	h->info.key_offset = info->key_offset;
+	h->info.calloc = info->calloc;
+	h->info.free = info->free;
+	h->info.alloc = info->alloc;
+	h->info.data = info->data;
+	h->t = (h->info.calloc)(h->size, sizeof(struct _ohash_record),
+		    h->info.data);
+	h->total = h->deleted = 0;
+}
+
+uint32_t
+ohash_interval(const char *s, const char **e)
+{
+	uint32_t k;
+
+	if (!*e)
+		*e = s + strlen(s);
+	if (s == *e)
+		k = 0;
+	else
+		k = *s++;
+	while (s != *e)
+		k =  ((k << 2) | (k >> 30)) ^ *s++;
+	return k;
+}
+
+unsigned int
+ohash_lookup_interval(struct ohash *h, const char *start, const char *end,
+    uint32_t hv)
+{
+	unsigned int	i, incr;
+	unsigned int	empty;
+
+#ifdef STATS_HASH
+	STAT_HASH_LOOKUP++;
+#endif
+	empty = NONE;
+	i = hv % h->size;
+	incr = ((hv % (h->size-2)) & ~1) + 1;
+	while (h->t[i].p != NULL) {
+#ifdef STATS_HASH
+		STAT_HASH_LENGTH++;
+#endif
+		if (h->t[i].p == DELETED) {
+			if (empty == NONE)
+				empty = i;
+		} else if (h->t[i].hv == hv &&
+		    strncmp(h->t[i].p+h->info.key_offset, start,
+			end - start) == 0 &&
+		    (h->t[i].p+h->info.key_offset)[end-start] == '\0') {
+			if (empty != NONE) {
+				h->t[empty].hv = hv;
+				h->t[empty].p = h->t[i].p;
+				h->t[i].p = DELETED;
+				return empty;
+			} else {
+#ifdef STATS_HASH
+				STAT_HASH_POSITIVE++;
+#endif
+				return i;
+			}
+		}
+		i += incr;
+		if (i >= h->size)
+			i -= h->size;
+	}
+
+	/* Found an empty position.  */
+	if (empty != NONE)
+		i = empty;
+	h->t[i].hv = hv;
+	return i;
+}
+
+unsigned int
+ohash_lookup_memory(struct ohash *h, const char *k, size_t size, uint32_t hv)
+{
+	unsigned int	i, incr;
+	unsigned int	empty;
+
+#ifdef STATS_HASH
+	STAT_HASH_LOOKUP++;
+#endif
+	empty = NONE;
+	i = hv % h->size;
+	incr = ((hv % (h->size-2)) & ~1) + 1;
+	while (h->t[i].p != NULL) {
+#ifdef STATS_HASH
+		STAT_HASH_LENGTH++;
+#endif
+		if (h->t[i].p == DELETED) {
+			if (empty == NONE)
+				empty = i;
+		} else if (h->t[i].hv == hv &&
+		    memcmp(h->t[i].p+h->info.key_offset, k, size) == 0) {
+			if (empty != NONE) {
+				h->t[empty].hv = hv;
+				h->t[empty].p = h->t[i].p;
+				h->t[i].p = DELETED;
+				return empty;
+			} else {
+#ifdef STATS_HASH
+				STAT_HASH_POSITIVE++;
+#endif
+			}	return i;
+		}
+		i += incr;
+		if (i >= h->size)
+			i -= h->size;
+	}
+
+	/* Found an empty position.  */
+	if (empty != NONE)
+		i = empty;
+	h->t[i].hv = hv;
+	return i;
+}
+
+unsigned int
+ohash_qlookup(struct ohash *h, const char *s)
+{
+	const char *e = NULL;
+	return ohash_qlookupi(h, s, &e);
+}
+
+unsigned int
+ohash_qlookupi(struct ohash *h, const char *s, const char **e)
+{
+	uint32_t hv;
+
+	hv = ohash_interval(s, e);
+	return ohash_lookup_interval(h, s, *e, hv);
+}
blob - /dev/null
blob + 3f070e2c7257456fa8401d225eea13c7eea93d1c (mode 644)
--- /dev/null
+++ compat/ohash.h
@@ -0,0 +1,71 @@
+/* $OpenBSD: ohash.h,v 1.2 2014/06/02 18:52:03 deraadt Exp $ */
+
+/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef OHASH_H
+#define OHASH_H
+
+/* Open hashing support. 
+ * Open hashing was chosen because it is much lighter than other hash
+ * techniques, and more efficient in most cases.
+ */
+
+/* user-visible data structure */
+struct ohash_info {
+	ptrdiff_t key_offset;
+	void *data;	/* user data */
+	void *(*calloc)(size_t, size_t, void *);
+	void (*free)(void *, void *);
+	void *(*alloc)(size_t, void *);
+};
+
+struct _ohash_record;
+
+/* private structure. It's there just so you can do a sizeof */
+struct ohash {
+	struct _ohash_record 	*t;
+	struct ohash_info 	info;
+	unsigned int 		size;
+	unsigned int 		total;
+	unsigned int 		deleted;
+};
+
+/* For this to be tweakable, we use small primitives, and leave part of the
+ * logic to the client application.  e.g., hashing is left to the client
+ * application.  We also provide a simple table entry lookup that yields
+ * a hashing table index (opaque) to be used in find/insert/remove.
+ * The keys are stored at a known position in the client data.
+ */
+void ohash_init(struct ohash *, unsigned, struct ohash_info *);
+void ohash_delete(struct ohash *);
+
+unsigned int ohash_lookup_interval(struct ohash *, const char *,
+	    const char *, uint32_t);
+unsigned int ohash_lookup_memory(struct ohash *, const char *,
+	    size_t, uint32_t);
+void *ohash_find(struct ohash *, unsigned int);
+void *ohash_remove(struct ohash *, unsigned int);
+void *ohash_insert(struct ohash *, unsigned int, void *);
+void *ohash_first(struct ohash *, unsigned int *);
+void *ohash_next(struct ohash *, unsigned int *);
+unsigned int ohash_entries(struct ohash *);
+
+void *ohash_create_entry(struct ohash_info *, const char *, const char **);
+uint32_t ohash_interval(const char *, const char **);
+
+unsigned int ohash_qlookupi(struct ohash *, const char *, const char **);
+unsigned int ohash_qlookup(struct ohash *, const char *);
+#endif
blob - /dev/null
blob + bc1568be67482bf033c0da484f8bd9fd41426d03 (mode 644)
--- /dev/null
+++ compat/queue.h
@@ -0,0 +1,631 @@
+/*	$OpenBSD: queue.h,v 1.46 2020/12/30 13:33:12 millert Exp $	*/
+/*	$NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $	*/
+
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS 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.
+ *
+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef	_SYS_QUEUE_H_
+#define	_SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues and XOR simple queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * An XOR simple queue is used in the same way as a regular simple queue.
+ * The difference is that the head structure also includes a "cookie" that
+ * is XOR'd with the queue pointer (first, last or next) to generate the
+ * real pointer value.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
+#define _Q_INVALID ((void *)-1)
+#define _Q_INVALIDATE(a) (a) = _Q_INVALID
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type)						\
+struct name {								\
+	struct type *slh_first;	/* first element */			\
+}
+
+#define	SLIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define SLIST_ENTRY(type)						\
+struct {								\
+	struct type *sle_next;	/* next element */			\
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define	SLIST_FIRST(head)	((head)->slh_first)
+#define	SLIST_END(head)		NULL
+#define	SLIST_EMPTY(head)	(SLIST_FIRST(head) == SLIST_END(head))
+#define	SLIST_NEXT(elm, field)	((elm)->field.sle_next)
+
+#define	SLIST_FOREACH(var, head, field)					\
+	for((var) = SLIST_FIRST(head);					\
+	    (var) != SLIST_END(head);					\
+	    (var) = SLIST_NEXT(var, field))
+
+#define	SLIST_FOREACH_SAFE(var, head, field, tvar)			\
+	for ((var) = SLIST_FIRST(head);				\
+	    (var) && ((tvar) = SLIST_NEXT(var, field), 1);		\
+	    (var) = (tvar))
+
+/*
+ * Singly-linked List functions.
+ */
+#define	SLIST_INIT(head) {						\
+	SLIST_FIRST(head) = SLIST_END(head);				\
+}
+
+#define	SLIST_INSERT_AFTER(slistelm, elm, field) do {			\
+	(elm)->field.sle_next = (slistelm)->field.sle_next;		\
+	(slistelm)->field.sle_next = (elm);				\
+} while (0)
+
+#define	SLIST_INSERT_HEAD(head, elm, field) do {			\
+	(elm)->field.sle_next = (head)->slh_first;			\
+	(head)->slh_first = (elm);					\
+} while (0)
+
+#define	SLIST_REMOVE_AFTER(elm, field) do {				\
+	(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next;	\
+} while (0)
+
+#define	SLIST_REMOVE_HEAD(head, field) do {				\
+	(head)->slh_first = (head)->slh_first->field.sle_next;		\
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do {			\
+	if ((head)->slh_first == (elm)) {				\
+		SLIST_REMOVE_HEAD((head), field);			\
+	} else {							\
+		struct type *curelm = (head)->slh_first;		\
+									\
+		while (curelm->field.sle_next != (elm))			\
+			curelm = curelm->field.sle_next;		\
+		curelm->field.sle_next =				\
+		    curelm->field.sle_next->field.sle_next;		\
+	}								\
+	_Q_INVALIDATE((elm)->field.sle_next);				\
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type)						\
+struct name {								\
+	struct type *lh_first;	/* first element */			\
+}
+
+#define LIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List access methods.
+ */
+#define	LIST_FIRST(head)		((head)->lh_first)
+#define	LIST_END(head)			NULL
+#define	LIST_EMPTY(head)		(LIST_FIRST(head) == LIST_END(head))
+#define	LIST_NEXT(elm, field)		((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field)					\
+	for((var) = LIST_FIRST(head);					\
+	    (var)!= LIST_END(head);					\
+	    (var) = LIST_NEXT(var, field))
+
+#define	LIST_FOREACH_SAFE(var, head, field, tvar)			\
+	for ((var) = LIST_FIRST(head);				\
+	    (var) && ((tvar) = LIST_NEXT(var, field), 1);		\
+	    (var) = (tvar))
+
+/*
+ * List functions.
+ */
+#define	LIST_INIT(head) do {						\
+	LIST_FIRST(head) = LIST_END(head);				\
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do {			\
+	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\
+		(listelm)->field.le_next->field.le_prev =		\
+		    &(elm)->field.le_next;				\
+	(listelm)->field.le_next = (elm);				\
+	(elm)->field.le_prev = &(listelm)->field.le_next;		\
+} while (0)
+
+#define	LIST_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.le_prev = (listelm)->field.le_prev;		\
+	(elm)->field.le_next = (listelm);				\
+	*(listelm)->field.le_prev = (elm);				\
+	(listelm)->field.le_prev = &(elm)->field.le_next;		\
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do {				\
+	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\
+		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+	(head)->lh_first = (elm);					\
+	(elm)->field.le_prev = &(head)->lh_first;			\
+} while (0)
+
+#define LIST_REMOVE(elm, field) do {					\
+	if ((elm)->field.le_next != NULL)				\
+		(elm)->field.le_next->field.le_prev =			\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = (elm)->field.le_next;			\
+	_Q_INVALIDATE((elm)->field.le_prev);				\
+	_Q_INVALIDATE((elm)->field.le_next);				\
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do {				\
+	if (((elm2)->field.le_next = (elm)->field.le_next) != NULL)	\
+		(elm2)->field.le_next->field.le_prev =			\
+		    &(elm2)->field.le_next;				\
+	(elm2)->field.le_prev = (elm)->field.le_prev;			\
+	*(elm2)->field.le_prev = (elm2);				\
+	_Q_INVALIDATE((elm)->field.le_prev);				\
+	_Q_INVALIDATE((elm)->field.le_next);				\
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *sqh_first;	/* first element */			\
+	struct type **sqh_last;	/* addr of last next element */		\
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type)						\
+struct {								\
+	struct type *sqe_next;	/* next element */			\
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define	SIMPLEQ_FIRST(head)	    ((head)->sqh_first)
+#define	SIMPLEQ_END(head)	    NULL
+#define	SIMPLEQ_EMPTY(head)	    (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define	SIMPLEQ_NEXT(elm, field)    ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field)				\
+	for((var) = SIMPLEQ_FIRST(head);				\
+	    (var) != SIMPLEQ_END(head);					\
+	    (var) = SIMPLEQ_NEXT(var, field))
+
+#define	SIMPLEQ_FOREACH_SAFE(var, head, field, tvar)			\
+	for ((var) = SIMPLEQ_FIRST(head);				\
+	    (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1);		\
+	    (var) = (tvar))
+
+/*
+ * Simple queue functions.
+ */
+#define	SIMPLEQ_INIT(head) do {						\
+	(head)->sqh_first = NULL;					\
+	(head)->sqh_last = &(head)->sqh_first;				\
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)	\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(head)->sqh_first = (elm);					\
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.sqe_next = NULL;					\
+	*(head)->sqh_last = (elm);					\
+	(head)->sqh_last = &(elm)->field.sqe_next;			\
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+	(listelm)->field.sqe_next = (elm);				\
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do {			\
+	if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+		(head)->sqh_last = &(head)->sqh_first;			\
+} while (0)
+
+#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do {			\
+	if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
+	    == NULL)							\
+		(head)->sqh_last = &(elm)->field.sqe_next;		\
+} while (0)
+
+#define SIMPLEQ_CONCAT(head1, head2) do {				\
+	if (!SIMPLEQ_EMPTY((head2))) {					\
+		*(head1)->sqh_last = (head2)->sqh_first;		\
+		(head1)->sqh_last = (head2)->sqh_last;			\
+		SIMPLEQ_INIT((head2));					\
+	}								\
+} while (0)
+
+/*
+ * XOR Simple queue definitions.
+ */
+#define XSIMPLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *sqx_first;	/* first element */			\
+	struct type **sqx_last;	/* addr of last next element */		\
+	unsigned long sqx_cookie;					\
+}
+
+#define XSIMPLEQ_ENTRY(type)						\
+struct {								\
+	struct type *sqx_next;	/* next element */			\
+}
+
+/*
+ * XOR Simple queue access methods.
+ */
+#define XSIMPLEQ_XOR(head, ptr)	    ((__typeof(ptr))((head)->sqx_cookie ^ \
+					(unsigned long)(ptr)))
+#define	XSIMPLEQ_FIRST(head)	    XSIMPLEQ_XOR(head, ((head)->sqx_first))
+#define	XSIMPLEQ_END(head)	    NULL
+#define	XSIMPLEQ_EMPTY(head)	    (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
+#define	XSIMPLEQ_NEXT(head, elm, field)    XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
+
+
+#define XSIMPLEQ_FOREACH(var, head, field)				\
+	for ((var) = XSIMPLEQ_FIRST(head);				\
+	    (var) != XSIMPLEQ_END(head);				\
+	    (var) = XSIMPLEQ_NEXT(head, var, field))
+
+#define	XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar)			\
+	for ((var) = XSIMPLEQ_FIRST(head);				\
+	    (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1);	\
+	    (var) = (tvar))
+
+/*
+ * XOR Simple queue functions.
+ */
+#define	XSIMPLEQ_INIT(head) do {					\
+	arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
+	(head)->sqx_first = XSIMPLEQ_XOR(head, NULL);			\
+	(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first);	\
+} while (0)
+
+#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.sqx_next = (head)->sqx_first) ==		\
+	    XSIMPLEQ_XOR(head, NULL))					\
+		(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
+	(head)->sqx_first = XSIMPLEQ_XOR(head, (elm));			\
+} while (0)
+
+#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL);		\
+	*(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
+	(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next);	\
+} while (0)
+
+#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.sqx_next = (listelm)->field.sqx_next) ==	\
+	    XSIMPLEQ_XOR(head, NULL))					\
+		(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
+	(listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm));		\
+} while (0)
+
+#define XSIMPLEQ_REMOVE_HEAD(head, field) do {				\
+	if (((head)->sqx_first = XSIMPLEQ_XOR(head,			\
+	    (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
+		(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
+} while (0)
+
+#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do {			\
+	if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head,			\
+	    (elm)->field.sqx_next)->field.sqx_next)			\
+	    == XSIMPLEQ_XOR(head, NULL))				\
+		(head)->sqx_last = 					\
+		    XSIMPLEQ_XOR(head, &(elm)->field.sqx_next);		\
+} while (0)
+
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+}
+
+#define TAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+}
+
+/*
+ * Tail queue access methods.
+ */
+#define	TAILQ_FIRST(head)		((head)->tqh_first)
+#define	TAILQ_END(head)			NULL
+#define	TAILQ_NEXT(elm, field)		((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname)					\
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field)				\
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define	TAILQ_EMPTY(head)						\
+	(TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field)					\
+	for((var) = TAILQ_FIRST(head);					\
+	    (var) != TAILQ_END(head);					\
+	    (var) = TAILQ_NEXT(var, field))
+
+#define	TAILQ_FOREACH_SAFE(var, head, field, tvar)			\
+	for ((var) = TAILQ_FIRST(head);					\
+	    (var) != TAILQ_END(head) &&					\
+	    ((tvar) = TAILQ_NEXT(var, field), 1);			\
+	    (var) = (tvar))
+
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field)		\
+	for((var) = TAILQ_LAST(head, headname);				\
+	    (var) != TAILQ_END(head);					\
+	    (var) = TAILQ_PREV(var, headname, field))
+
+#define	TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar)	\
+	for ((var) = TAILQ_LAST(head, headname);			\
+	    (var) != TAILQ_END(head) &&					\
+	    ((tvar) = TAILQ_PREV(var, headname, field), 1);		\
+	    (var) = (tvar))
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_INIT(head) do {						\
+	(head)->tqh_first = NULL;					\
+	(head)->tqh_last = &(head)->tqh_first;				\
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
+		(head)->tqh_first->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(head)->tqh_first = (elm);					\
+	(elm)->field.tqe_prev = &(head)->tqh_first;			\
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do {			\
+	(elm)->field.tqe_next = NULL;					\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &(elm)->field.tqe_next;			\
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(listelm)->field.tqe_next = (elm);				\
+	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
+} while (0)
+
+#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	(elm)->field.tqe_next = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do {				\
+	if (((elm)->field.tqe_next) != NULL)				\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
+	_Q_INVALIDATE((elm)->field.tqe_prev);				\
+	_Q_INVALIDATE((elm)->field.tqe_next);				\
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do {			\
+	if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)	\
+		(elm2)->field.tqe_next->field.tqe_prev =		\
+		    &(elm2)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm2)->field.tqe_next;		\
+	(elm2)->field.tqe_prev = (elm)->field.tqe_prev;			\
+	*(elm2)->field.tqe_prev = (elm2);				\
+	_Q_INVALIDATE((elm)->field.tqe_prev);				\
+	_Q_INVALIDATE((elm)->field.tqe_next);				\
+} while (0)
+
+#define TAILQ_CONCAT(head1, head2, field) do {				\
+	if (!TAILQ_EMPTY(head2)) {					\
+		*(head1)->tqh_last = (head2)->tqh_first;		\
+		(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last;	\
+		(head1)->tqh_last = (head2)->tqh_last;			\
+		TAILQ_INIT((head2));					\
+	}								\
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define	STAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *stqh_first;	/* first element */		\
+	struct type **stqh_last;	/* addr of last next element */	\
+}
+
+#define	STAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).stqh_first }
+
+#define	STAILQ_ENTRY(type)						\
+struct {								\
+	struct type *stqe_next;	/* next element */			\
+}
+
+/*
+ * Singly-linked Tail queue access methods.
+ */
+#define	STAILQ_FIRST(head)	((head)->stqh_first)
+#define	STAILQ_END(head)	NULL
+#define	STAILQ_EMPTY(head)	(STAILQ_FIRST(head) == STAILQ_END(head))
+#define	STAILQ_NEXT(elm, field)	((elm)->field.stqe_next)
+
+#define STAILQ_FOREACH(var, head, field)				\
+	for ((var) = STAILQ_FIRST(head);				\
+	    (var) != STAILQ_END(head);					\
+	    (var) = STAILQ_NEXT(var, field))
+
+#define	STAILQ_FOREACH_SAFE(var, head, field, tvar)			\
+	for ((var) = STAILQ_FIRST(head);				\
+	    (var) && ((tvar) = STAILQ_NEXT(var, field), 1);		\
+	    (var) = (tvar))
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define	STAILQ_INIT(head) do {						\
+	STAILQ_FIRST((head)) = NULL;					\
+	(head)->stqh_last = &STAILQ_FIRST((head));			\
+} while (0)
+
+#define	STAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL)	\
+		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\
+	STAILQ_FIRST((head)) = (elm);					\
+} while (0)
+
+#define	STAILQ_INSERT_TAIL(head, elm, field) do {			\
+	STAILQ_NEXT((elm), field) = NULL;				\
+	*(head)->stqh_last = (elm);					\
+	(head)->stqh_last = &STAILQ_NEXT((elm), field);			\
+} while (0)
+
+#define	STAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((elm), field)) == NULL)\
+		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\
+	STAILQ_NEXT((elm), field) = (elm);				\
+} while (0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do {                            \
+	if ((STAILQ_FIRST((head)) =					\
+	    STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)		\
+		(head)->stqh_last = &STAILQ_FIRST((head));		\
+} while (0)
+
+#define STAILQ_REMOVE_AFTER(head, elm, field) do {                      \
+	if ((STAILQ_NEXT(elm, field) =					\
+	    STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL)	\
+		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\
+} while (0)
+
+#define	STAILQ_REMOVE(head, elm, type, field) do {			\
+	if (STAILQ_FIRST((head)) == (elm)) {				\
+		STAILQ_REMOVE_HEAD((head), field);			\
+	} else {							\
+		struct type *curelm = (head)->stqh_first;		\
+		while (STAILQ_NEXT(curelm, field) != (elm))		\
+			curelm = STAILQ_NEXT(curelm, field);		\
+		STAILQ_REMOVE_AFTER(head, curelm, field);		\
+	}								\
+} while (0)
+
+#define	STAILQ_CONCAT(head1, head2) do {				\
+	if (!STAILQ_EMPTY((head2))) {					\
+		*(head1)->stqh_last = (head2)->stqh_first;		\
+		(head1)->stqh_last = (head2)->stqh_last;		\
+		STAILQ_INIT((head2));					\
+	}								\
+} while (0)
+
+#define	STAILQ_LAST(head, type, field)					\
+	(STAILQ_EMPTY((head)) ?	NULL :					\
+	        ((struct type *)(void *)				\
+		((char *)((head)->stqh_last) - offsetof(struct type, field))))
+
+#endif	/* !_SYS_QUEUE_H_ */
blob - /dev/null
blob + bbdbefedb14cf927c971d7f6f37b7192baeac977 (mode 644)
--- /dev/null
+++ compat/recallocarray.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "compat.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
+ * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
+ */
+#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
+
+/*
+ * Even though specified in POSIX, the PAGESIZE and PAGE_SIZE
+ * macros have very poor portability.  Since we only use this
+ * to avoid free() overhead for small shrinking, simply pick
+ * an arbitrary number.
+ */
+#define getpagesize()	(1UL << 12)
+
+void *
+recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size)
+{
+	size_t oldsize, newsize;
+	void *newptr;
+
+	if (ptr == NULL)
+		return calloc(newnmemb, size);
+
+	if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+	    newnmemb > 0 && SIZE_MAX / newnmemb < size) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	newsize = newnmemb * size;
+
+	if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+	    oldnmemb > 0 && SIZE_MAX / oldnmemb < size) {
+		errno = EINVAL;
+		return NULL;
+	}
+	oldsize = oldnmemb * size;
+
+	/*
+	 * Don't bother too much if we're shrinking just a bit,
+	 * we do not shrink for series of small steps, oh well.
+	 */
+	if (newsize <= oldsize) {
+		size_t d = oldsize - newsize;
+
+		if (d < oldsize / 2 && d < getpagesize()) {
+			memset((char *)ptr + newsize, 0, d);
+			return ptr;
+		}
+	}
+
+	newptr = malloc(newsize);
+	if (newptr == NULL)
+		return NULL;
+
+	if (newsize > oldsize) {
+		memcpy(newptr, ptr, oldsize);
+		memset((char *)newptr + oldsize, 0, newsize - oldsize);
+	} else
+		memcpy(newptr, ptr, newsize);
+
+	explicit_bzero(ptr, oldsize);
+	free(ptr);
+
+	return newptr;
+}
blob - /dev/null
blob + 15c6f0991321ddd8d80d0f6ad75937e68c84a810 (mode 644)
--- /dev/null
+++ compat/setproctitle.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016 Nicholas Marriott <nicholas.marriott@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "compat.h"
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#if HAVE_PR_SET_NAME
+
+#include <sys/prctl.h>
+
+void
+setproctitle(const char *fmt, ...)
+{
+	char	title[16], name[16], *cp;
+	va_list	ap;
+	int	used;
+
+	va_start(ap, fmt);
+	vsnprintf(title, sizeof title, fmt, ap);
+	va_end(ap);
+
+	used = snprintf(name, sizeof name, "%s: %s", getprogname(), title);
+	if (used >= (int)sizeof name) {
+		cp = strrchr(name, ' ');
+		if (cp != NULL)
+			*cp = '\0';
+	}
+	prctl(PR_SET_NAME, name);
+}
+#else
+void
+setproctitle(const char *fmt, ...)
+{
+	(void)fmt;
+}
+#endif
blob - /dev/null
blob + ddfd2925e5893cc1e9112bbe443911c66a82e3b1 (mode 644)
--- /dev/null
+++ compat/setprogname.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "../compat.h"
+
+void
+setprogname(const char *name)
+{
+	return;
+}
blob - /dev/null
blob + c096bc91af0bd03b8a9700bdfe642541a00e2d1b (mode 644)
--- /dev/null
+++ compat/strlcat.c
@@ -0,0 +1,57 @@
+/*	$OpenBSD: strlcat.c,v 1.19 2019/01/25 00:19:25 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "compat.h"
+
+/*
+ * Appends src to string dst of size dsize (unlike strncat, dsize is the
+ * full size of dst, not space left).  At most dsize-1 characters
+ * will be copied.  Always NUL terminates (unless dsize <= strlen(dst)).
+ * Returns strlen(src) + MIN(dsize, strlen(initial dst)).
+ * If retval >= dsize, truncation occurred.
+ */
+size_t
+strlcat(char *dst, const char *src, size_t dsize)
+{
+	const char *odst = dst;
+	const char *osrc = src;
+	size_t n = dsize;
+	size_t dlen;
+
+	/* Find the end of dst and adjust bytes left but don't go past end. */
+	while (n-- != 0 && *dst != '\0')
+		dst++;
+	dlen = dst - odst;
+	n = dsize - dlen;
+
+	if (n-- == 0)
+		return(dlen + strlen(src));
+	while (*src != '\0') {
+		if (n != 0) {
+			*dst++ = *src;
+			n--;
+		}
+		src++;
+	}
+	*dst = '\0';
+
+	return(dlen + (src - osrc));	/* count does not include NUL */
+}
blob - /dev/null
blob + 5d17ee7e953b68de4fd0eb168e96e38b362631f8 (mode 644)
--- /dev/null
+++ compat/strlcpy.c
@@ -0,0 +1,52 @@
+/*	$OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "compat.h"
+
+/*
+ * Copy string src to buffer dst of size dsize.  At most dsize-1
+ * chars will be copied.  Always NUL terminates (unless dsize == 0).
+ * Returns strlen(src); if retval >= dsize, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t dsize)
+{
+	const char *osrc = src;
+	size_t nleft = dsize;
+
+	/* Copy as many bytes as will fit. */
+	if (nleft != 0) {
+		while (--nleft != 0) {
+			if ((*dst++ = *src++) == '\0')
+				break;
+		}
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src. */
+	if (nleft == 0) {
+		if (dsize != 0)
+			*dst = '\0';		/* NUL-terminate dst */
+		while (*src++)
+			;
+	}
+
+	return(src - osrc - 1);	/* count does not include NUL */
+}
blob - /dev/null
blob + ea71d7bba91e7b03ad3b9049d129e90752f3c457 (mode 644)
--- /dev/null
+++ compat/strsep.c
@@ -0,0 +1,72 @@
+/*	$OpenBSD: strsep.c,v 1.8 2015/08/31 02:53:57 guenther Exp $	*/
+
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS 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 "compat.h"
+
+#include <string.h>
+
+/*
+ * Get next token from string *stringp, where tokens are possibly-empty
+ * strings separated by characters from delim.  
+ *
+ * Writes NULs into the string at *stringp to end tokens.
+ * delim need not remain constant from call to call.
+ * On return, *stringp points past the last NUL written (if there might
+ * be further tokens), or is NULL (if there are definitely no more tokens).
+ *
+ * If *stringp is NULL, strsep returns NULL.
+ */
+char *
+strsep(char **stringp, const char *delim)
+{
+	char *s;
+	const char *spanp;
+	int c, sc;
+	char *tok;
+
+	if ((s = *stringp) == NULL)
+		return (NULL);
+	for (tok = s;;) {
+		c = *s++;
+		spanp = delim;
+		do {
+			if ((sc = *spanp++) == c) {
+				if (c == 0)
+					s = NULL;
+				else
+					s[-1] = 0;
+				*stringp = s;
+				return (tok);
+			}
+		} while (sc != 0);
+	}
+	/* NOTREACHED */
+}
blob - /dev/null
blob + a3d382ca22b6753ad4c1f7ab8bddaa9fa2eb4336 (mode 644)
--- /dev/null
+++ compat/strtonum.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2004 Ted Unangst and Todd Miller
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "compat.h"
+
+#define	INVALID		1
+#define	TOOSMALL	2
+#define	TOOLARGE	3
+
+long long
+strtonum(const char *numstr, long long minval, long long maxval,
+    const char **errstrp)
+{
+	long long ll = 0;
+	int error = 0;
+	char *ep;
+	struct errval {
+		const char *errstr;
+		int err;
+	} ev[4] = {
+		{ NULL,		0 },
+		{ "invalid",	EINVAL },
+		{ "too small",	ERANGE },
+		{ "too large",	ERANGE },
+	};
+
+	ev[0].err = errno;
+	errno = 0;
+	if (minval > maxval) {
+		error = INVALID;
+	} else {
+		ll = strtoll(numstr, &ep, 10);
+		if (numstr == ep || *ep != '\0')
+			error = INVALID;
+		else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
+			error = TOOSMALL;
+		else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
+			error = TOOLARGE;
+	}
+	if (errstrp != NULL)
+		*errstrp = ev[error].errstr;
+	errno = ev[error].err;
+	if (error)
+		ll = 0;
+
+	return (ll);
+}
blob - /dev/null
blob + fb56e32567bed50a2455b9b8fb87e809bba9acd0 (mode 644)
--- /dev/null
+++ compat/tree.h
@@ -0,0 +1,1012 @@
+/*	$OpenBSD: tree.h,v 1.30 2020/10/10 18:03:41 otto Exp $	*/
+/*
+ * Copyright 2002 Niels Provos <provos@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	_SYS_TREE_H_
+#define	_SYS_TREE_H_
+
+#include <stddef.h>
+
+/*
+ * Local modifications:
+ *  - dropped __unused
+ *  - __inline -> inline
+ */
+
+/*
+ * 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 SPLAY_HEAD(name, type)						\
+struct name {								\
+	struct type *sph_root; /* root of the tree */			\
+}
+
+#define SPLAY_INITIALIZER(root)						\
+	{ NULL }
+
+#define SPLAY_INIT(root) do {						\
+	(root)->sph_root = NULL;					\
+} while (0)
+
+#define SPLAY_ENTRY(type)						\
+struct {								\
+	struct type *spe_left; /* left element */			\
+	struct type *spe_right; /* right element */			\
+}
+
+#define SPLAY_LEFT(elm, field)		(elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field)		(elm)->field.spe_right
+#define SPLAY_ROOT(head)		(head)->sph_root
+#define SPLAY_EMPTY(head)		(SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {			\
+	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);	\
+	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
+	(head)->sph_root = tmp;						\
+} while (0)
+
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do {			\
+	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);	\
+	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
+	(head)->sph_root = tmp;						\
+} while (0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do {				\
+	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
+	tmp = (head)->sph_root;						\
+	(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);		\
+} while (0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do {				\
+	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
+	tmp = (head)->sph_root;						\
+	(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);	\
+} while (0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do {		\
+	SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field);	\
+	SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
+	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field);	\
+	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field);	\
+} while (0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp)				\
+void name##_SPLAY(struct name *, struct type *);			\
+void name##_SPLAY_MINMAX(struct name *, int);				\
+struct type *name##_SPLAY_INSERT(struct name *, struct type *);		\
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *);		\
+									\
+/* Finds the node with the same key as elm */				\
+static inline struct type *						\
+name##_SPLAY_FIND(struct name *head, struct type *elm)			\
+{									\
+	if (SPLAY_EMPTY(head))						\
+		return(NULL);						\
+	name##_SPLAY(head, elm);					\
+	if ((cmp)(elm, (head)->sph_root) == 0)				\
+		return (head->sph_root);				\
+	return (NULL);							\
+}									\
+									\
+static inline struct type *						\
+name##_SPLAY_NEXT(struct name *head, struct type *elm)			\
+{									\
+	name##_SPLAY(head, elm);					\
+	if (SPLAY_RIGHT(elm, field) != NULL) {				\
+		elm = SPLAY_RIGHT(elm, field);				\
+		while (SPLAY_LEFT(elm, field) != NULL) {		\
+			elm = SPLAY_LEFT(elm, field);			\
+		}							\
+	} else								\
+		elm = NULL;						\
+	return (elm);							\
+}									\
+									\
+static inline struct type *						\
+name##_SPLAY_MIN_MAX(struct name *head, int val)			\
+{									\
+	name##_SPLAY_MINMAX(head, val);					\
+        return (SPLAY_ROOT(head));					\
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp)				\
+struct type *								\
+name##_SPLAY_INSERT(struct name *head, struct type *elm)		\
+{									\
+    if (SPLAY_EMPTY(head)) {						\
+	    SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;	\
+    } else {								\
+	    int __comp;							\
+	    name##_SPLAY(head, elm);					\
+	    __comp = (cmp)(elm, (head)->sph_root);			\
+	    if(__comp < 0) {						\
+		    SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
+		    SPLAY_RIGHT(elm, field) = (head)->sph_root;		\
+		    SPLAY_LEFT((head)->sph_root, field) = NULL;		\
+	    } else if (__comp > 0) {					\
+		    SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
+		    SPLAY_LEFT(elm, field) = (head)->sph_root;		\
+		    SPLAY_RIGHT((head)->sph_root, field) = NULL;	\
+	    } else							\
+		    return ((head)->sph_root);				\
+    }									\
+    (head)->sph_root = (elm);						\
+    return (NULL);							\
+}									\
+									\
+struct type *								\
+name##_SPLAY_REMOVE(struct name *head, struct type *elm)		\
+{									\
+	struct type *__tmp;						\
+	if (SPLAY_EMPTY(head))						\
+		return (NULL);						\
+	name##_SPLAY(head, elm);					\
+	if ((cmp)(elm, (head)->sph_root) == 0) {			\
+		if (SPLAY_LEFT((head)->sph_root, field) == NULL) {	\
+			(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
+		} else {						\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
+			name##_SPLAY(head, elm);			\
+			SPLAY_RIGHT((head)->sph_root, field) = __tmp;	\
+		}							\
+		return (elm);						\
+	}								\
+	return (NULL);							\
+}									\
+									\
+void									\
+name##_SPLAY(struct name *head, struct type *elm)			\
+{									\
+	struct type __node, *__left, *__right, *__tmp;			\
+	int __comp;							\
+\
+	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+	__left = __right = &__node;					\
+\
+	while ((__comp = (cmp)(elm, (head)->sph_root))) {		\
+		if (__comp < 0) {					\
+			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if ((cmp)(elm, __tmp) < 0){			\
+				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
+				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKLEFT(head, __right, field);		\
+		} else if (__comp > 0) {				\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if ((cmp)(elm, __tmp) > 0){			\
+				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
+				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKRIGHT(head, __left, field);		\
+		}							\
+	}								\
+	SPLAY_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##_SPLAY_MINMAX(struct name *head, int __comp) \
+{									\
+	struct type __node, *__left, *__right, *__tmp;			\
+\
+	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+	__left = __right = &__node;					\
+\
+	while (1) {							\
+		if (__comp < 0) {					\
+			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if (__comp < 0){				\
+				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
+				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKLEFT(head, __right, field);		\
+		} else if (__comp > 0) {				\
+			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
+			if (__tmp == NULL)				\
+				break;					\
+			if (__comp > 0) {				\
+				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
+				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+					break;				\
+			}						\
+			SPLAY_LINKRIGHT(head, __left, field);		\
+		}							\
+	}								\
+	SPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
+}
+
+#define SPLAY_NEGINF	-1
+#define SPLAY_INF	1
+
+#define SPLAY_INSERT(name, x, y)	name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y)	name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y)		name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y)		name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x)		(SPLAY_EMPTY(x) ? NULL	\
+					: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x)		(SPLAY_EMPTY(x) ? NULL	\
+					: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head)					\
+	for ((x) = SPLAY_MIN(name, head);				\
+	     (x) != NULL;						\
+	     (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-black tree */
+#define RB_HEAD(name, type)						\
+struct name {								\
+	struct type *rbh_root; /* root of the tree */			\
+}
+
+#define RB_INITIALIZER(root)						\
+	{ NULL }
+
+#define RB_INIT(root) do {						\
+	(root)->rbh_root = NULL;					\
+} while (0)
+
+#define RB_BLACK	0
+#define RB_RED		1
+#define RB_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 RB_LEFT(elm, field)		(elm)->field.rbe_left
+#define RB_RIGHT(elm, field)		(elm)->field.rbe_right
+#define RB_PARENT(elm, field)		(elm)->field.rbe_parent
+#define RB_COLOR(elm, field)		(elm)->field.rbe_color
+#define RB_ROOT(head)			(head)->rbh_root
+#define RB_EMPTY(head)			(RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do {					\
+	RB_PARENT(elm, field) = parent;					\
+	RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;		\
+	RB_COLOR(elm, field) = RB_RED;					\
+} while (0)
+
+#define RB_SET_BLACKRED(black, red, field) do {				\
+	RB_COLOR(black, field) = RB_BLACK;				\
+	RB_COLOR(red, field) = RB_RED;					\
+} while (0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x)	do {} while (0)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do {			\
+	(tmp) = RB_RIGHT(elm, field);					\
+	if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) {		\
+		RB_PARENT(RB_LEFT(tmp, field), field) = (elm);		\
+	}								\
+	RB_AUGMENT(elm);						\
+	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) {		\
+		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
+			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
+		else							\
+			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
+	} else								\
+		(head)->rbh_root = (tmp);				\
+	RB_LEFT(tmp, field) = (elm);					\
+	RB_PARENT(elm, field) = (tmp);					\
+	RB_AUGMENT(tmp);						\
+	if ((RB_PARENT(tmp, field)))					\
+		RB_AUGMENT(RB_PARENT(tmp, field));			\
+} while (0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do {			\
+	(tmp) = RB_LEFT(elm, field);					\
+	if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) {		\
+		RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);		\
+	}								\
+	RB_AUGMENT(elm);						\
+	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) {		\
+		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
+			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
+		else							\
+			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
+	} else								\
+		(head)->rbh_root = (tmp);				\
+	RB_RIGHT(tmp, field) = (elm);					\
+	RB_PARENT(elm, field) = (tmp);					\
+	RB_AUGMENT(tmp);						\
+	if ((RB_PARENT(tmp, field)))					\
+		RB_AUGMENT(RB_PARENT(tmp, field));			\
+} while (0)
+
+/* Generates prototypes and inline functions */
+#define	RB_PROTOTYPE(name, type, field, cmp)				\
+	RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
+#define	RB_PROTOTYPE_STATIC(name, type, field, cmp)			\
+	RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
+#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr)		\
+attr void name##_RB_INSERT_COLOR(struct name *, struct type *);		\
+attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+attr struct type *name##_RB_REMOVE(struct name *, struct type *);	\
+attr struct type *name##_RB_INSERT(struct name *, struct type *);	\
+attr struct type *name##_RB_FIND(struct name *, struct type *);		\
+attr struct type *name##_RB_NFIND(struct name *, struct type *);	\
+attr struct type *name##_RB_NEXT(struct type *);			\
+attr struct type *name##_RB_PREV(struct type *);			\
+attr struct type *name##_RB_MINMAX(struct name *, int);			\
+									\
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define	RB_GENERATE(name, type, field, cmp)				\
+	RB_GENERATE_INTERNAL(name, type, field, cmp,)
+#define	RB_GENERATE_STATIC(name, type, field, cmp)			\
+	RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
+#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr)		\
+attr void								\
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm)		\
+{									\
+	struct type *parent, *gparent, *tmp;				\
+	while ((parent = RB_PARENT(elm, field)) &&			\
+	    RB_COLOR(parent, field) == RB_RED) {			\
+		gparent = RB_PARENT(parent, field);			\
+		if (parent == RB_LEFT(gparent, field)) {		\
+			tmp = RB_RIGHT(gparent, field);			\
+			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
+				RB_COLOR(tmp, field) = RB_BLACK;	\
+				RB_SET_BLACKRED(parent, gparent, field);\
+				elm = gparent;				\
+				continue;				\
+			}						\
+			if (RB_RIGHT(parent, field) == elm) {		\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				tmp = parent;				\
+				parent = elm;				\
+				elm = tmp;				\
+			}						\
+			RB_SET_BLACKRED(parent, gparent, field);	\
+			RB_ROTATE_RIGHT(head, gparent, tmp, field);	\
+		} else {						\
+			tmp = RB_LEFT(gparent, field);			\
+			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
+				RB_COLOR(tmp, field) = RB_BLACK;	\
+				RB_SET_BLACKRED(parent, gparent, field);\
+				elm = gparent;				\
+				continue;				\
+			}						\
+			if (RB_LEFT(parent, field) == elm) {		\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				tmp = parent;				\
+				parent = elm;				\
+				elm = tmp;				\
+			}						\
+			RB_SET_BLACKRED(parent, gparent, field);	\
+			RB_ROTATE_LEFT(head, gparent, tmp, field);	\
+		}							\
+	}								\
+	RB_COLOR(head->rbh_root, field) = RB_BLACK;			\
+}									\
+									\
+attr void								\
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
+{									\
+	struct type *tmp;						\
+	while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) &&	\
+	    elm != RB_ROOT(head)) {					\
+		if (RB_LEFT(parent, field) == elm) {			\
+			tmp = RB_RIGHT(parent, field);			\
+			if (RB_COLOR(tmp, field) == RB_RED) {		\
+				RB_SET_BLACKRED(tmp, parent, field);	\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				tmp = RB_RIGHT(parent, field);		\
+			}						\
+			if ((RB_LEFT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+			    (RB_RIGHT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+				RB_COLOR(tmp, field) = RB_RED;		\
+				elm = parent;				\
+				parent = RB_PARENT(elm, field);		\
+			} else {					\
+				if (RB_RIGHT(tmp, field) == NULL ||	\
+				    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
+					struct type *oleft;		\
+					if ((oleft = RB_LEFT(tmp, field)))\
+						RB_COLOR(oleft, field) = RB_BLACK;\
+					RB_COLOR(tmp, field) = RB_RED;	\
+					RB_ROTATE_RIGHT(head, tmp, oleft, field);\
+					tmp = RB_RIGHT(parent, field);	\
+				}					\
+				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+				RB_COLOR(parent, field) = RB_BLACK;	\
+				if (RB_RIGHT(tmp, field))		\
+					RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
+				RB_ROTATE_LEFT(head, parent, tmp, field);\
+				elm = RB_ROOT(head);			\
+				break;					\
+			}						\
+		} else {						\
+			tmp = RB_LEFT(parent, field);			\
+			if (RB_COLOR(tmp, field) == RB_RED) {		\
+				RB_SET_BLACKRED(tmp, parent, field);	\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				tmp = RB_LEFT(parent, field);		\
+			}						\
+			if ((RB_LEFT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+			    (RB_RIGHT(tmp, field) == NULL ||		\
+			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+				RB_COLOR(tmp, field) = RB_RED;		\
+				elm = parent;				\
+				parent = RB_PARENT(elm, field);		\
+			} else {					\
+				if (RB_LEFT(tmp, field) == NULL ||	\
+				    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
+					struct type *oright;		\
+					if ((oright = RB_RIGHT(tmp, field)))\
+						RB_COLOR(oright, field) = RB_BLACK;\
+					RB_COLOR(tmp, field) = RB_RED;	\
+					RB_ROTATE_LEFT(head, tmp, oright, field);\
+					tmp = RB_LEFT(parent, field);	\
+				}					\
+				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+				RB_COLOR(parent, field) = RB_BLACK;	\
+				if (RB_LEFT(tmp, field))		\
+					RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
+				RB_ROTATE_RIGHT(head, parent, tmp, field);\
+				elm = RB_ROOT(head);			\
+				break;					\
+			}						\
+		}							\
+	}								\
+	if (elm)							\
+		RB_COLOR(elm, field) = RB_BLACK;			\
+}									\
+									\
+attr struct type *							\
+name##_RB_REMOVE(struct name *head, struct type *elm)			\
+{									\
+	struct type *child, *parent, *old = elm;			\
+	int color;							\
+	if (RB_LEFT(elm, field) == NULL)				\
+		child = RB_RIGHT(elm, field);				\
+	else if (RB_RIGHT(elm, field) == NULL)				\
+		child = RB_LEFT(elm, field);				\
+	else {								\
+		struct type *left;					\
+		elm = RB_RIGHT(elm, field);				\
+		while ((left = RB_LEFT(elm, field)))			\
+			elm = left;					\
+		child = RB_RIGHT(elm, field);				\
+		parent = RB_PARENT(elm, field);				\
+		color = RB_COLOR(elm, field);				\
+		if (child)						\
+			RB_PARENT(child, field) = parent;		\
+		if (parent) {						\
+			if (RB_LEFT(parent, field) == elm)		\
+				RB_LEFT(parent, field) = child;		\
+			else						\
+				RB_RIGHT(parent, field) = child;	\
+			RB_AUGMENT(parent);				\
+		} else							\
+			RB_ROOT(head) = child;				\
+		if (RB_PARENT(elm, field) == old)			\
+			parent = elm;					\
+		(elm)->field = (old)->field;				\
+		if (RB_PARENT(old, field)) {				\
+			if (RB_LEFT(RB_PARENT(old, field), field) == old)\
+				RB_LEFT(RB_PARENT(old, field), field) = elm;\
+			else						\
+				RB_RIGHT(RB_PARENT(old, field), field) = elm;\
+			RB_AUGMENT(RB_PARENT(old, field));		\
+		} else							\
+			RB_ROOT(head) = elm;				\
+		RB_PARENT(RB_LEFT(old, field), field) = elm;		\
+		if (RB_RIGHT(old, field))				\
+			RB_PARENT(RB_RIGHT(old, field), field) = elm;	\
+		if (parent) {						\
+			left = parent;					\
+			do {						\
+				RB_AUGMENT(left);			\
+			} while ((left = RB_PARENT(left, field)));	\
+		}							\
+		goto color;						\
+	}								\
+	parent = RB_PARENT(elm, field);					\
+	color = RB_COLOR(elm, field);					\
+	if (child)							\
+		RB_PARENT(child, field) = parent;			\
+	if (parent) {							\
+		if (RB_LEFT(parent, field) == elm)			\
+			RB_LEFT(parent, field) = child;			\
+		else							\
+			RB_RIGHT(parent, field) = child;		\
+		RB_AUGMENT(parent);					\
+	} else								\
+		RB_ROOT(head) = child;					\
+color:									\
+	if (color == RB_BLACK)						\
+		name##_RB_REMOVE_COLOR(head, parent, child);		\
+	return (old);							\
+}									\
+									\
+/* Inserts a node into the RB tree */					\
+attr struct type *							\
+name##_RB_INSERT(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp;						\
+	struct type *parent = NULL;					\
+	int comp = 0;							\
+	tmp = RB_ROOT(head);						\
+	while (tmp) {							\
+		parent = tmp;						\
+		comp = (cmp)(elm, parent);				\
+		if (comp < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else if (comp > 0)					\
+			tmp = RB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	RB_SET(elm, parent, field);					\
+	if (parent != NULL) {						\
+		if (comp < 0)						\
+			RB_LEFT(parent, field) = elm;			\
+		else							\
+			RB_RIGHT(parent, field) = elm;			\
+		RB_AUGMENT(parent);					\
+	} else								\
+		RB_ROOT(head) = elm;					\
+	name##_RB_INSERT_COLOR(head, elm);				\
+	return (NULL);							\
+}									\
+									\
+/* Finds the node with the same key as elm */				\
+attr struct type *							\
+name##_RB_FIND(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp = RB_ROOT(head);				\
+	int comp;							\
+	while (tmp) {							\
+		comp = cmp(elm, tmp);					\
+		if (comp < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else if (comp > 0)					\
+			tmp = RB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	return (NULL);							\
+}									\
+									\
+/* Finds the first node greater than or equal to the search key */	\
+attr struct type *							\
+name##_RB_NFIND(struct name *head, struct type *elm)			\
+{									\
+	struct type *tmp = RB_ROOT(head);				\
+	struct type *res = NULL;					\
+	int comp;							\
+	while (tmp) {							\
+		comp = cmp(elm, tmp);					\
+		if (comp < 0) {						\
+			res = tmp;					\
+			tmp = RB_LEFT(tmp, field);			\
+		}							\
+		else if (comp > 0)					\
+			tmp = RB_RIGHT(tmp, field);			\
+		else							\
+			return (tmp);					\
+	}								\
+	return (res);							\
+}									\
+									\
+/* ARGSUSED */								\
+attr struct type *							\
+name##_RB_NEXT(struct type *elm)					\
+{									\
+	if (RB_RIGHT(elm, field)) {					\
+		elm = RB_RIGHT(elm, field);				\
+		while (RB_LEFT(elm, field))				\
+			elm = RB_LEFT(elm, field);			\
+	} else {							\
+		if (RB_PARENT(elm, field) &&				\
+		    (elm == RB_LEFT(RB_PARENT(elm, field), field)))	\
+			elm = RB_PARENT(elm, field);			\
+		else {							\
+			while (RB_PARENT(elm, field) &&			\
+			    (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
+				elm = RB_PARENT(elm, field);		\
+			elm = RB_PARENT(elm, field);			\
+		}							\
+	}								\
+	return (elm);							\
+}									\
+									\
+/* ARGSUSED */								\
+attr struct type *							\
+name##_RB_PREV(struct type *elm)					\
+{									\
+	if (RB_LEFT(elm, field)) {					\
+		elm = RB_LEFT(elm, field);				\
+		while (RB_RIGHT(elm, field))				\
+			elm = RB_RIGHT(elm, field);			\
+	} else {							\
+		if (RB_PARENT(elm, field) &&				\
+		    (elm == RB_RIGHT(RB_PARENT(elm, field), field)))	\
+			elm = RB_PARENT(elm, field);			\
+		else {							\
+			while (RB_PARENT(elm, field) &&			\
+			    (elm == RB_LEFT(RB_PARENT(elm, field), field)))\
+				elm = RB_PARENT(elm, field);		\
+			elm = RB_PARENT(elm, field);			\
+		}							\
+	}								\
+	return (elm);							\
+}									\
+									\
+attr struct type *							\
+name##_RB_MINMAX(struct name *head, int val)				\
+{									\
+	struct type *tmp = RB_ROOT(head);				\
+	struct type *parent = NULL;					\
+	while (tmp) {							\
+		parent = tmp;						\
+		if (val < 0)						\
+			tmp = RB_LEFT(tmp, field);			\
+		else							\
+			tmp = RB_RIGHT(tmp, field);			\
+	}								\
+	return (parent);						\
+}
+
+#define RB_NEGINF	-1
+#define RB_INF	1
+
+#define RB_INSERT(name, x, y)	name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y)	name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y)	name##_RB_FIND(x, y)
+#define RB_NFIND(name, x, y)	name##_RB_NFIND(x, y)
+#define RB_NEXT(name, x, y)	name##_RB_NEXT(y)
+#define RB_PREV(name, x, y)	name##_RB_PREV(y)
+#define RB_MIN(name, x)		name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x)		name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head)					\
+	for ((x) = RB_MIN(name, head);					\
+	     (x) != NULL;						\
+	     (x) = name##_RB_NEXT(x))
+
+#define RB_FOREACH_SAFE(x, name, head, y)				\
+	for ((x) = RB_MIN(name, head);					\
+	    ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1);		\
+	     (x) = (y))
+
+#define RB_FOREACH_REVERSE(x, name, head)				\
+	for ((x) = RB_MAX(name, head);					\
+	     (x) != NULL;						\
+	     (x) = name##_RB_PREV(x))
+
+#define RB_FOREACH_REVERSE_SAFE(x, name, head, y)			\
+	for ((x) = RB_MAX(name, head);					\
+	    ((x) != NULL) && ((y) = name##_RB_PREV(x), 1);		\
+	     (x) = (y))
+
+
+/*
+ * Copyright (c) 2016 David Gwynne <dlg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+struct rb_type {
+	int		(*t_compare)(const void *, const void *);
+	void		(*t_augment)(void *);
+	unsigned int	  t_offset;	/* offset of rb_entry in type */
+};
+
+struct rb_tree {
+	struct rb_entry	*rbt_root;
+};
+
+struct rb_entry {
+	struct rb_entry	 *rbt_parent;
+	struct rb_entry	 *rbt_left;
+	struct rb_entry	 *rbt_right;
+	unsigned int	  rbt_color;
+};
+
+#define RBT_HEAD(_name, _type)						\
+struct _name {								\
+	struct rb_tree rbh_root;					\
+}
+
+#define RBT_ENTRY(_type)	struct rb_entry
+
+static inline void
+_rb_init(struct rb_tree *rbt)
+{
+	rbt->rbt_root = NULL;
+}
+
+static inline int
+_rb_empty(struct rb_tree *rbt)
+{
+	return (rbt->rbt_root == NULL);
+}
+
+void	*_rb_insert(const struct rb_type *, struct rb_tree *, void *);
+void	*_rb_remove(const struct rb_type *, struct rb_tree *, void *);
+void	*_rb_find(const struct rb_type *, struct rb_tree *, const void *);
+void	*_rb_nfind(const struct rb_type *, struct rb_tree *, const void *);
+void	*_rb_root(const struct rb_type *, struct rb_tree *);
+void	*_rb_min(const struct rb_type *, struct rb_tree *);
+void	*_rb_max(const struct rb_type *, struct rb_tree *);
+void	*_rb_next(const struct rb_type *, void *);
+void	*_rb_prev(const struct rb_type *, void *);
+void	*_rb_left(const struct rb_type *, void *);
+void	*_rb_right(const struct rb_type *, void *);
+void	*_rb_parent(const struct rb_type *, void *);
+void	 _rb_set_left(const struct rb_type *, void *, void *);
+void	 _rb_set_right(const struct rb_type *, void *, void *);
+void	 _rb_set_parent(const struct rb_type *, void *, void *);
+void	 _rb_poison(const struct rb_type *, void *, unsigned long);
+int	 _rb_check(const struct rb_type *, void *, unsigned long);
+
+#define RBT_INITIALIZER(_head)	{ { NULL } }
+
+#define RBT_PROTOTYPE(_name, _type, _field, _cmp)			\
+extern const struct rb_type *const _name##_RBT_TYPE;			\
+									\
+static inline void							\
+_name##_RBT_INIT(struct _name *head)					\
+{									\
+	_rb_init(&head->rbh_root);					\
+}									\
+									\
+static inline struct _type *						\
+_name##_RBT_INSERT(struct _name *head, struct _type *elm)		\
+{									\
+	return _rb_insert(_name##_RBT_TYPE, &head->rbh_root, elm);	\
+}									\
+									\
+static inline struct _type *						\
+_name##_RBT_REMOVE(struct _name *head, struct _type *elm)		\
+{									\
+	return _rb_remove(_name##_RBT_TYPE, &head->rbh_root, elm);	\
+}									\
+									\
+static inline struct _type *						\
+_name##_RBT_FIND(struct _name *head, const struct _type *key)		\
+{									\
+	return _rb_find(_name##_RBT_TYPE, &head->rbh_root, key);	\
+}									\
+									\
+static inline struct _type *						\
+_name##_RBT_NFIND(struct _name *head, const struct _type *key)		\
+{									\
+	return _rb_nfind(_name##_RBT_TYPE, &head->rbh_root, key);	\
+}									\
+									\
+static inline struct _type *						\
+_name##_RBT_ROOT(struct _name *head)					\
+{									\
+	return _rb_root(_name##_RBT_TYPE, &head->rbh_root);		\
+}									\
+									\
+static inline int							\
+_name##_RBT_EMPTY(struct _name *head)					\
+{									\
+	return _rb_empty(&head->rbh_root);				\
+}									\
+									\
+static inline struct _type *						\
+_name##_RBT_MIN(struct _name *head)					\
+{									\
+	return _rb_min(_name##_RBT_TYPE, &head->rbh_root);		\
+}									\
+									\
+static inline struct _type *						\
+_name##_RBT_MAX(struct _name *head)					\
+{									\
+	return _rb_max(_name##_RBT_TYPE, &head->rbh_root);		\
+}									\
+									\
+static inline struct _type *						\
+_name##_RBT_NEXT(struct _type *elm)					\
+{									\
+	return _rb_next(_name##_RBT_TYPE, elm);				\
+}									\
+									\
+static inline struct _type *						\
+_name##_RBT_PREV(struct _type *elm)					\
+{									\
+	return _rb_prev(_name##_RBT_TYPE, elm);				\
+}									\
+									\
+static inline struct _type *						\
+_name##_RBT_LEFT(struct _type *elm)					\
+{									\
+	return _rb_left(_name##_RBT_TYPE, elm);				\
+}									\
+									\
+static inline struct _type *						\
+_name##_RBT_RIGHT(struct _type *elm)					\
+{									\
+	return _rb_right(_name##_RBT_TYPE, elm);			\
+}									\
+									\
+static inline struct _type *						\
+_name##_RBT_PARENT(struct _type *elm)					\
+{									\
+	return _rb_parent(_name##_RBT_TYPE, elm);			\
+}									\
+									\
+static inline void							\
+_name##_RBT_SET_LEFT(struct _type *elm, struct _type *left)		\
+{									\
+	_rb_set_left(_name##_RBT_TYPE, elm, left);			\
+}									\
+									\
+static inline void							\
+_name##_RBT_SET_RIGHT(struct _type *elm, struct _type *right)		\
+{									\
+	_rb_set_right(_name##_RBT_TYPE, elm, right);			\
+}									\
+									\
+static inline void							\
+_name##_RBT_SET_PARENT(struct _type *elm, struct _type *parent)		\
+{									\
+	_rb_set_parent(_name##_RBT_TYPE, elm, parent);			\
+}									\
+									\
+static inline void							\
+_name##_RBT_POISON(struct _type *elm, unsigned long poison)		\
+{									\
+	_rb_poison(_name##_RBT_TYPE, elm, poison);			\
+}									\
+									\
+static inline int							\
+_name##_RBT_CHECK(struct _type *elm, unsigned long poison)		\
+{									\
+	return _rb_check(_name##_RBT_TYPE, elm, poison);		\
+}
+
+#define RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _aug)		\
+static int								\
+_name##_RBT_COMPARE(const void *lptr, const void *rptr)			\
+{									\
+	const struct _type *l = lptr, *r = rptr;			\
+	return _cmp(l, r);						\
+}									\
+static const struct rb_type _name##_RBT_INFO = {			\
+	_name##_RBT_COMPARE,						\
+	_aug,								\
+	offsetof(struct _type, _field),					\
+};									\
+const struct rb_type *const _name##_RBT_TYPE = &_name##_RBT_INFO
+
+#define RBT_GENERATE_AUGMENT(_name, _type, _field, _cmp, _aug)		\
+static void								\
+_name##_RBT_AUGMENT(void *ptr)						\
+{									\
+	struct _type *p = ptr;						\
+	return _aug(p);							\
+}									\
+RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _name##_RBT_AUGMENT)
+
+#define RBT_GENERATE(_name, _type, _field, _cmp)			\
+    RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, NULL)
+
+#define RBT_INIT(_name, _head)		_name##_RBT_INIT(_head)
+#define RBT_INSERT(_name, _head, _elm)	_name##_RBT_INSERT(_head, _elm)
+#define RBT_REMOVE(_name, _head, _elm)	_name##_RBT_REMOVE(_head, _elm)
+#define RBT_FIND(_name, _head, _key)	_name##_RBT_FIND(_head, _key)
+#define RBT_NFIND(_name, _head, _key)	_name##_RBT_NFIND(_head, _key)
+#define RBT_ROOT(_name, _head)		_name##_RBT_ROOT(_head)
+#define RBT_EMPTY(_name, _head)		_name##_RBT_EMPTY(_head)
+#define RBT_MIN(_name, _head)		_name##_RBT_MIN(_head)
+#define RBT_MAX(_name, _head)		_name##_RBT_MAX(_head)
+#define RBT_NEXT(_name, _elm)		_name##_RBT_NEXT(_elm)
+#define RBT_PREV(_name, _elm)		_name##_RBT_PREV(_elm)
+#define RBT_LEFT(_name, _elm)		_name##_RBT_LEFT(_elm)
+#define RBT_RIGHT(_name, _elm)		_name##_RBT_RIGHT(_elm)
+#define RBT_PARENT(_name, _elm)		_name##_RBT_PARENT(_elm)
+#define RBT_SET_LEFT(_name, _elm, _l)	_name##_RBT_SET_LEFT(_elm, _l)
+#define RBT_SET_RIGHT(_name, _elm, _r)	_name##_RBT_SET_RIGHT(_elm, _r)
+#define RBT_SET_PARENT(_name, _elm, _p)	_name##_RBT_SET_PARENT(_elm, _p)
+#define RBT_POISON(_name, _elm, _p)	_name##_RBT_POISON(_elm, _p)
+#define RBT_CHECK(_name, _elm, _p)	_name##_RBT_CHECK(_elm, _p)
+
+#define RBT_FOREACH(_e, _name, _head)					\
+	for ((_e) = RBT_MIN(_name, (_head));				\
+	     (_e) != NULL;						\
+	     (_e) = RBT_NEXT(_name, (_e)))
+
+#define RBT_FOREACH_SAFE(_e, _name, _head, _n)				\
+	for ((_e) = RBT_MIN(_name, (_head));				\
+	     (_e) != NULL && ((_n) = RBT_NEXT(_name, (_e)), 1);	\
+	     (_e) = (_n))
+
+#define RBT_FOREACH_REVERSE(_e, _name, _head)				\
+	for ((_e) = RBT_MAX(_name, (_head));				\
+	     (_e) != NULL;						\
+	     (_e) = RBT_PREV(_name, (_e)))
+
+#define RBT_FOREACH_REVERSE_SAFE(_e, _name, _head, _n)			\
+	for ((_e) = RBT_MAX(_name, (_head));				\
+	     (_e) != NULL && ((_n) = RBT_PREV(_name, (_e)), 1);	\
+	     (_e) = (_n))
+
+#endif	/* _SYS_TREE_H_ */
blob - /dev/null
blob + 1b8ec6ca2b31e2f06cfed55fcc17f69e53f30573 (mode 644)
--- /dev/null
+++ compat/vis.c
@@ -0,0 +1,242 @@
+/*	$OpenBSD: vis.c,v 1.25 2015/09/13 11:32:51 guenther Exp $ */
+/*-
+ * Copyright (c) 1989, 1993
+ *	The Regents of the University of California.  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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS 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 "compat.h"
+
+#include <sys/types.h>
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define	isoctal(c)	(((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
+#define	isvisible(c,flag)						\
+	(((c) == '\\' || (flag & VIS_ALL) == 0) &&			\
+	(((u_int)(c) <= UCHAR_MAX && isascii((u_char)(c)) &&		\
+	(((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') ||	\
+		(flag & VIS_GLOB) == 0) && isgraph((u_char)(c))) ||	\
+	((flag & VIS_SP) == 0 && (c) == ' ') ||				\
+	((flag & VIS_TAB) == 0 && (c) == '\t') ||			\
+	((flag & VIS_NL) == 0 && (c) == '\n') ||			\
+	((flag & VIS_SAFE) && ((c) == '\b' ||				\
+		(c) == '\007' || (c) == '\r' ||				\
+		isgraph((u_char)(c))))))
+
+/*
+ * vis - visually encode characters
+ */
+char *
+vis(char *dst, int c, int flag, int nextc)
+{
+	if (isvisible(c, flag)) {
+		if ((c == '"' && (flag & VIS_DQ) != 0) ||
+		    (c == '\\' && (flag & VIS_NOSLASH) == 0))
+			*dst++ = '\\';
+		*dst++ = c;
+		*dst = '\0';
+		return (dst);
+	}
+
+	if (flag & VIS_CSTYLE) {
+		switch(c) {
+		case '\n':
+			*dst++ = '\\';
+			*dst++ = 'n';
+			goto done;
+		case '\r':
+			*dst++ = '\\';
+			*dst++ = 'r';
+			goto done;
+		case '\b':
+			*dst++ = '\\';
+			*dst++ = 'b';
+			goto done;
+		case '\a':
+			*dst++ = '\\';
+			*dst++ = 'a';
+			goto done;
+		case '\v':
+			*dst++ = '\\';
+			*dst++ = 'v';
+			goto done;
+		case '\t':
+			*dst++ = '\\';
+			*dst++ = 't';
+			goto done;
+		case '\f':
+			*dst++ = '\\';
+			*dst++ = 'f';
+			goto done;
+		case ' ':
+			*dst++ = '\\';
+			*dst++ = 's';
+			goto done;
+		case '\0':
+			*dst++ = '\\';
+			*dst++ = '0';
+			if (isoctal(nextc)) {
+				*dst++ = '0';
+				*dst++ = '0';
+			}
+			goto done;
+		}
+	}
+	if (((c & 0177) == ' ') || (flag & VIS_OCTAL) ||
+	    ((flag & VIS_GLOB) && (c == '*' || c == '?' || c == '[' || c == '#'))) {
+		*dst++ = '\\';
+		*dst++ = ((u_char)c >> 6 & 07) + '0';
+		*dst++ = ((u_char)c >> 3 & 07) + '0';
+		*dst++ = ((u_char)c & 07) + '0';
+		goto done;
+	}
+	if ((flag & VIS_NOSLASH) == 0)
+		*dst++ = '\\';
+	if (c & 0200) {
+		c &= 0177;
+		*dst++ = 'M';
+	}
+	if (iscntrl((u_char)c)) {
+		*dst++ = '^';
+		if (c == 0177)
+			*dst++ = '?';
+		else
+			*dst++ = c + '@';
+	} else {
+		*dst++ = '-';
+		*dst++ = c;
+	}
+done:
+	*dst = '\0';
+	return (dst);
+}
+
+/*
+ * strvis, strnvis, strvisx - visually encode characters from src into dst
+ *	
+ *	Dst must be 4 times the size of src to account for possible
+ *	expansion.  The length of dst, not including the trailing NULL,
+ *	is returned. 
+ *
+ *	Strnvis will write no more than siz-1 bytes (and will NULL terminate).
+ *	The number of bytes needed to fully encode the string is returned.
+ *
+ *	Strvisx encodes exactly len bytes from src into dst.
+ *	This is useful for encoding a block of data.
+ */
+int
+strvis(char *dst, const char *src, int flag)
+{
+	char c;
+	char *start;
+
+	for (start = dst; (c = *src);)
+		dst = vis(dst, c, flag, *++src);
+	*dst = '\0';
+	return (dst - start);
+}
+
+int
+strnvis(char *dst, const char *src, size_t siz, int flag)
+{
+	char *start, *end;
+	char tbuf[5];
+	int c, i;
+
+	i = 0;
+	for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) {
+		if (isvisible(c, flag)) {
+			if ((c == '"' && (flag & VIS_DQ) != 0) ||
+			    (c == '\\' && (flag & VIS_NOSLASH) == 0)) {
+				/* need space for the extra '\\' */
+				if (dst + 1 >= end) {
+					i = 2;
+					break;
+				}
+				*dst++ = '\\';
+			}
+			i = 1;
+			*dst++ = c;
+			src++;
+		} else {
+			i = vis(tbuf, c, flag, *++src) - tbuf;
+			if (dst + i <= end) {
+				memcpy(dst, tbuf, i);
+				dst += i;
+			} else {
+				src--;
+				break;
+			}
+		}
+	}
+	if (siz > 0)
+		*dst = '\0';
+	if (dst + i > end) {
+		/* adjust return value for truncation */
+		while ((c = *src))
+			dst += vis(tbuf, c, flag, *++src) - tbuf;
+	}
+	return (dst - start);
+}
+
+int
+stravis(char **outp, const char *src, int flag)
+{
+	char *buf;
+	int len, serrno;
+
+	buf = reallocarray(NULL, 4, strlen(src) + 1);
+	if (buf == NULL)
+		return -1;
+	len = strvis(buf, src, flag);
+	serrno = errno;
+	*outp = realloc(buf, len + 1);
+	if (*outp == NULL) {
+		*outp = buf;
+		errno = serrno;
+	}
+	return (len);
+}
+
+int
+strvisx(char *dst, const char *src, size_t len, int flag)
+{
+	char c;
+	char *start;
+
+	for (start = dst; len > 1; len--) {
+		c = *src;
+		dst = vis(dst, c, flag, *++src);
+	}
+	if (len)
+		dst = vis(dst, *src, flag, '\0');
+	*dst = '\0';
+	return (dst - start);
+}
blob - /dev/null
blob + cb1b9bd93046bd4c4738c07cece523bd7af294aa (mode 644)
--- /dev/null
+++ compat/vis.h
@@ -0,0 +1,88 @@
+/*	$OpenBSD: vis.h,v 1.15 2015/07/20 01:52:27 millert Exp $	*/
+/*	$NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $	*/
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS 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.
+ *
+ *	@(#)vis.h	5.9 (Berkeley) 4/3/91
+ */
+
+#ifndef _VIS_H_
+#define	_VIS_H_
+
+/*
+ * to select alternate encoding format
+ */
+#define	VIS_OCTAL	0x01	/* use octal \ddd format */
+#define	VIS_CSTYLE	0x02	/* use \[nrft0..] where appropriate */
+
+/*
+ * to alter set of characters encoded (default is to encode all
+ * non-graphic except space, tab, and newline).
+ */
+#define	VIS_SP		0x04	/* also encode space */
+#define	VIS_TAB		0x08	/* also encode tab */
+#define	VIS_NL		0x10	/* also encode newline */
+#define	VIS_WHITE	(VIS_SP | VIS_TAB | VIS_NL)
+#define	VIS_SAFE	0x20	/* only encode "unsafe" characters */
+#define	VIS_DQ		0x200	/* backslash-escape double quotes */
+#define	VIS_ALL		0x400	/* encode all characters */
+
+/*
+ * other
+ */
+#define	VIS_NOSLASH	0x40	/* inhibit printing '\' */
+#define	VIS_GLOB	0x100	/* encode glob(3) magics and '#' */
+
+/*
+ * unvis return codes
+ */
+#define	UNVIS_VALID	 1	/* character valid */
+#define	UNVIS_VALIDPUSH	 2	/* character valid, push back passed char */
+#define	UNVIS_NOCHAR	 3	/* valid sequence, no character produced */
+#define	UNVIS_SYNBAD	-1	/* unrecognized escape sequence */
+#define	UNVIS_ERROR	-2	/* decoder in unknown state (unrecoverable) */
+
+/*
+ * unvis flags
+ */
+#define	UNVIS_END	1	/* no more characters */
+
+char	*vis(char *, int, int, int);
+int	strvis(char *, const char *, int);
+int	stravis(char **, const char *, int);
+int	strnvis(char *, const char *, size_t, int)
+		__attribute__ ((__bounded__(__string__,1,3)));
+int	strvisx(char *, const char *, size_t, int)
+		__attribute__ ((__bounded__(__string__,1,3)));
+int	strunvis(char *, const char *);
+int	unvis(char *, char, int *, int);
+ssize_t strnunvis(char *, const char *, size_t)
+		__attribute__ ((__bounded__(__string__,1,3)));
+
+#endif /* !_VIS_H_ */
blob - ade79764eaa3d1b8896ff9c6e16ee466ae08cbc5 (mode 644)
blob + /dev/null
--- kamid/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-.PATH:${.CURDIR}/../lib
-
-.include "../kamid-version.mk"
-
-PROG=		kamid
-SRCS=		client.c control.c kamid.c listener.c log.c parse.y sandbox.c \
-		table.c table_static.c utils.c
-MAN=		kamid.conf.5 9p.7 kamid.8
-
-CPPFLAGS=	-I${.CURDIR}/../lib -I${.CURDIR}
-
-LDADD+= -levent -lutil -ltls
-DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBTLS}
-
-.if ${KAMID_RELEASE} != Yes
-NOMAN= Yes
-.endif
-
-.include <bsd.prog.mk>
blob - /dev/null
blob + cb4f7b0fb3c5e182699212e636caebdaea64eab9 (mode 644)
--- /dev/null
+++ kamid/Makefile.am
@@ -0,0 +1,33 @@
+bin_PROGRAMS = kamid
+
+kamid_SOURCES =	client.c			\
+		client.h			\
+		control.c			\
+		control.h			\
+		kami.h				\
+		kamid.c				\
+		kamid.h				\
+		listener.c			\
+		listener.h			\
+		parse.y				\
+		table.c				\
+		table.h				\
+		table_static.c			\
+		$(top_srcdir)/lib/log.c		\
+		$(top_srcdir)/lib/log.h		\
+		$(top_srcdir)/lib/sandbox.c	\
+		$(top_srcdir)/lib/sandbox.h	\
+		$(top_srcdir)/lib/utils.c	\
+		$(top_srcdir)/lib/utils.h
+
+man5_MANS =	kamid.conf.5
+man7_MANS =	9p.7
+man8_MANS =	kamid.8
+
+LDADD =		$(LIBOBJS)
+
+AM_CPPFLAGS +=	-DKAMID_VERSION='"@VERSION@"'	\
+		-I$(top_srcdir)/		\
+		-I$(top_srcdir)/compat		\
+		-I$(top_srcdir)/lib		\
+		-I$(top_srcdir)/kamid
blob - 74734329db79092dc0f40c306b3c44998dd8cf27
blob + 0b178a42ff7d5f9f95ce9ded30db61b7162aee21
--- kamid/client.c
+++ kamid/client.c
@@ -14,16 +14,15 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "compat.h"
+
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/queue.h>
 #include <sys/uio.h>
 
 #include <dirent.h>
 #include <endian.h>
-#include <err.h>
 #include <errno.h>
-#include <event.h>
 #include <fcntl.h>
 #include <pwd.h>
 #include <signal.h>
@@ -32,7 +31,6 @@
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
-#include <imsg.h>
 
 #include "client.h"
 #include "kami.h"
blob - 5d8abc786358b8715dec9ab680a868c59bf80c0d
blob + 2b6985542b6d7383f5f452adbe52744d5ef88ad6
--- kamid/control.c
+++ kamid/control.c
@@ -14,10 +14,11 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "compat.h"
+
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
-#include <sys/queue.h>
 #include <sys/uio.h>
 #include <sys/un.h>
 
@@ -25,12 +26,10 @@
 #include <net/if.h>
 
 #include <errno.h>
-#include <event.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <imsg.h>
 
 #include "control.h"
 #include "kamid.h"
blob - cd27289756b18d4d7bceb853c1b5d5f210686808
blob + 0c963c740a461c315d0027210d578d980db92bc8
--- kamid/kamid.c
+++ kamid/kamid.c
@@ -18,9 +18,10 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "compat.h"
+
 #include <sys/socket.h>
 #include <sys/types.h>
-#include <sys/queue.h>
 #include <sys/uio.h>
 #include <sys/wait.h>
 
@@ -28,7 +29,6 @@
 #include <netinet/in.h>
 
 #include <errno.h>
-#include <event.h>
 #include <fcntl.h>
 #include <pwd.h>
 #include <signal.h>
@@ -38,7 +38,6 @@
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
-#include <imsg.h>
 
 #include "client.h"
 #include "control.h"
blob - 655c05f33499a226890eee263131d3efa95be0b9
blob + 8df8890afda9b5ad12af7a81c69dc416216b8185
--- kamid/listener.c
+++ kamid/listener.c
@@ -18,15 +18,14 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "compat.h"
+
 #include <sys/socket.h>
 #include <sys/types.h>
-#include <sys/tree.h>
-#include <sys/queue.h>
 #include <sys/uio.h>
 
 #include <endian.h>
 #include <errno.h>
-#include <event.h>
 #include <inttypes.h>
 #include <pwd.h>
 #include <signal.h>
@@ -36,7 +35,6 @@
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
-#include <imsg.h>
 
 #include "control.h"
 #include "kami.h"
blob - 342a0800da97c13c5140ccfbd7fe240b178499b9
blob + eb8b81009eb6c2d5754323f777bf98fbc29478fb
--- kamid/parse.y
+++ kamid/parse.y
@@ -23,14 +23,12 @@
 
 %{
 
-#include <sys/types.h>
-#include <sys/queue.h>
+#include "compat.h"
+
 #include <sys/stat.h>
 
 #include <ctype.h>
-#include <err.h>
 #include <errno.h>
-#include <event.h>
 #include <inttypes.h>
 #include <limits.h>
 #include <stdarg.h>
@@ -39,7 +37,6 @@
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
-#include <imsg.h>
 
 #include "log.h"
 #include "kamid.h"
blob - e7c8a5965571c231fcca030c4048a11e0f7bfd28
blob + 8a4ae8b015c96849c181dd8c0178ff43c0a8c502
--- kamid/table.c
+++ kamid/table.c
@@ -14,15 +14,13 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <sys/types.h>
-#include <sys/queue.h>
+#include "compat.h"
+
 #include <sys/uio.h>
 
-#include <event.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
-#include <imsg.h>
 
 #include "log.h"
 #include "utils.h"
blob - e83954e7280640d8f36108cc971e7028a7600c9c
blob + 550bb623efc8f59242d5ce988876022df5e09e01
--- kamid/table_static.c
+++ kamid/table_static.c
@@ -14,15 +14,12 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <sys/queue.h>
+#include "compat.h"
 
-#include <event.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
-#include <ohash.h>
-#include <imsg.h>
 
 #include "utils.h"
 #include "kamid.h"
blob - /dev/null
blob + 84b60242793c3a21ddc50fae638a208fb4f5ee29 (mode 644)
--- /dev/null
+++ compat.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef COMPAT_H
+#define COMPAT_H
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef __dead
+#define __dead __attribute__((noreturn))
+#endif
+
+#if HAVE_EVENT2
+# include <event2/event.h>
+# include <event2/event_compat.h>
+# include <event2/event_struct.h>
+# include <event2/buffer.h>
+# include <event2/buffer_compat.h>
+# include <event2/bufferevent.h>
+# include <event2/bufferevent_struct.h>
+# include <event2/bufferevent_compat.h>
+#else
+# include <event.h>
+#endif
+
+#ifdef HAVE_QUEUE_H
+# include <sys/queue.h>
+#else
+# include "compat/queue.h"
+#endif
+
+#ifdef HAVE_SYS_TREE_H
+# include <sys/tree.h>
+#else
+# include "compat/tree.h"
+#endif
+
+#ifdef HAVE_LIBUTIL
+# include <imsg.h>
+# include <ohash.h>
+# include <util.h>
+#else
+# include "compat/imsg.h"
+# include "compat/ohash.h"
+# define FMT_SCALED_STRSIZE	7 /* minus sign, 4 digits, suffix, NUL */
+int	fmt_scaled(long long, char *);
+#endif
+
+#ifndef HAVE_ARC4RANDOM
+# include <stdint.h>
+uint32_t	 arc4random(void);
+void		 arc4random_buf(void *, size_t);
+uint32_t	 arc4random_uniform(uint32_t);
+#endif
+
+#ifndef HAVE_ASPRINTF
+int		 asprintf(char **, const char *, ...);
+int		 vasprintf(char **, const char *, ...);
+#endif
+
+#ifndef HAVE_ERR
+void		 err(int, const char *, ...);
+void		 errx(int, const char *, ...);
+void		 warn(int, const char *, ...);
+void		 warnx(int, const char *, ...);
+#else
+#include <err.h>
+#endif
+
+#ifndef FREEZERO
+void		 freezero(void *, size_t);
+#endif
+
+#ifndef HAVE_GETDTABLECOUNT
+int		 getdtablecount(void);
+#endif
+
+#ifndef HAVE_GETDTABLESIZE
+int		 getdtablesize(void);
+#endif
+
+#ifndef HAVE_GETPROGNAME
+const char	*getprogname(void);
+#endif
+
+#ifndef HAVE_MEMMEM
+void		*memmem(const void *, size_t, const void *, size_t);
+#endif
+
+#ifndef HAVE_RECALLOCARRAY
+void		*recallocarray(void *, size_t, size_t, size_t);
+#endif
+
+#ifndef HAVE_SETPROCTITLE
+void		 setproctitle(const char *, ...);
+#endif
+
+#ifndef HAVE_SETPROGNAME
+void		 setprogname(const char *);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t		 strlcat(char *, const char *, size_t);
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t		 strlcpy(char *, const char *, size_t);
+#endif
+
+#ifndef HAVE_STRSEP
+char		*strsep(char **, const char *);
+#endif
+
+#ifndef HAVE_STRTONUM
+long long	 strtonum(const char *, long long, long long, const char **);
+#endif
+
+#ifdef HAVE_VIS
+# include <vis.h>
+#else
+# include "compat/vis.h"
+#endif
+
+#endif	/* COMPAT_H */
blob - /dev/null
blob + 88f814bd083282ec3980e7633b13701cdafa4656 (mode 644)
--- /dev/null
+++ configure.ac
@@ -0,0 +1,153 @@
+AC_INIT([kamid-portable], [0.1], [kamid@omarpolo.com], [],
+	[https://kamid.omarpolo.com])
+AC_CONFIG_AUX_DIR(etc)
+AC_CONFIG_LIBOBJ_DIR(compat)
+AM_INIT_AUTOMAKE([foreign subdir-objects])
+
+KAMID_RELEASE=No
+
+AC_DEFINE_UNQUOTED(VERSION, "$VERSION")
+AC_SUBST(VERSION)
+AC_SUBST(KAMID_RELEASE)
+
+AC_CANONICAL_HOST
+
+# When CFLAGS isn't set at this stage and gcc is detected by the macro below,
+# autoconf will automatically use CFLAGS="-O2 -g". Prevent that by using an
+# empty default.
+: ${CFLAGS=""}
+
+# Save user CPPFLAGS, CFLAGS and LDFLAGS. We need to change them because
+# AC_CHECK_HEADER doesn't give us any other way to update the include
+# paths. But for Makefile.am we want to use AM_CPPFLAGS and friends.
+SAVED_CFLAGS="$CFLAGS"
+SAVED_CPPFLAGS="$CPPFLAGS"
+SAVED_LDFLAGS="$LDFLAGS"
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AC_PROG_YACC
+PKG_PROG_PKG_CONFIG
+AC_USE_SYSTEM_EXTENSIONS
+
+# Some functions can be in libbsd.  Thanks to lldpb for the inspiration :)
+AC_ARG_WITH([libbsd],
+  AS_HELP_STRING([--with-libbsd], [Use libbsd @<:@default=auto@:>@]),
+  [],
+  [with_libbsd=auto])
+if test x"$with_libbsd" != x"no"; then
+  PKG_CHECK_MODULES([libbsd], [libbsd-overlay libbsd-ctor], [
+    _save_AM_CFLAGS="$AM_CFLAGS"
+    _save_LIBS="$LIBS"
+    AM_CFLAGS="$AM_CFLAGS $libbsd_CFLAGS"
+    LIBS="$LIBS $libbsd_LIBS"
+  ], [
+    if test x"$with_libbsd" = x"yes"; then
+       AC_MSG_FAILURE([*** no libbsd support found])
+    fi
+    with_libbsd=no
+  ])
+fi
+
+# Checks for library functions.
+AC_SEARCH_LIBS([arc4random], [],
+	[AC_DEFINE([HAVE_ARC4RANDOM], 1, [arc4random])],
+	[AC_DEFINE([HAVE_ARC4RANDOM], 0, [arc4random])])
+
+AC_REPLACE_FUNCS([
+	asprintf	\
+	err		\
+	freezero	\
+	getdtablecount	\
+	getdtablesize	\
+	getprogname	\
+	memmem		\
+	recallocarray	\
+	setproctitle	\
+	setprogname	\
+	strlcat		\
+	strlcpy		\
+	strsep		\
+	strtonum	\
+	vis		\
+])
+
+AC_MSG_CHECKING([for sys/queue.h with TAILQ_FOREACH_SAFE and STAILQ_ENTRY])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+#include <sys/queue.h>
+#include <stddef.h>
+], [
+	TAILQ_HEAD(tailhead, entry) head;
+	struct entry {
+		TAILQ_ENTRY(entry) entries;
+	} *np, *nt;
+	TAILQ_INIT(&head);
+	TAILQ_FOREACH_SAFE(np, &head, entries, nt) {
+		/* nop */ ;
+	}
+
+	STAILQ_HEAD(listhead, qentry) qhead = STAILQ_HEAD_INITIALIZER(qhead);
+	struct qentry {
+		STAILQ_ENTRY(qentry) entries;
+	} foo;
+
+	return 0;
+])], [
+	AC_MSG_RESULT(yes)
+	AC_DEFINE([HAVE_QUEUE_H], 1, [QUEUE_H])
+], AC_MSG_RESULT(no))
+
+AC_CHECK_HEADERS([sys/tree.h])
+
+AC_CHECK_DECL(PR_SET_NAME, AC_DEFINE([HAVE_PR_SET_NAME], 1, [pr_set_name]), [],
+	[[#include <sys/prctl.h>]])
+
+AC_CHECK_LIB([crypto], [RAND_add], [], [
+	AC_MSG_ERROR([requires openssl])
+])
+
+AC_CHECK_LIB(tls, tls_init, [], [
+	AC_MSG_ERROR([requires libtls])
+])
+
+AS_CASE([$host_os],
+	[*openbsd*], [AC_CHECK_LIB([event], [event_init], [],
+			[AC_MSG_ERROR([requires libevent])])],
+	[PKG_CHECK_MODULES([libevent2], [libevent_core >= 2],
+		[
+			AC_DEFINE([HAVE_EVENT2], 1, [1 if using event2])
+			AM_CFLAGS="$libevent2_CFLAGS $AM_CFLAGS"
+			LIBS="$libevent2_LIBS $LIBS"
+		], [AC_MSG_ERROR([requires libevent])])])
+
+AC_CHECK_LIB(util, imsg_init, [], [
+	AC_LIBOBJ(fmt_scaled)
+	AC_LIBOBJ(imsg)
+	AC_LIBOBJ(imsg-buffer)
+	AC_LIBOBJ(ohash)
+])
+
+# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user
+# variables.
+AC_SUBST(AM_CPPFLAGS)
+CPPFLAGS="$SAVED_CPPFLAGS"
+AC_SUBST(AM_CFLAGS)
+CFLAGS="$SAVED_CFLAGS"
+AC_SUBST(AM_LDFLAGS)
+LDFLAGS="$SAVED_LDFLAGS"
+
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_FILES([
+	Makefile
+	kamictl/Makefile
+	kamid/Makefile
+	kamiftp/Makefile
+	kamirepl/Makefile
+	ninepscript/Makefile
+])
+
+AC_OUTPUT
blob - 16c2148ba9b4b560dcccba205a1395e1a88f2537 (mode 644)
blob + /dev/null
--- kamiftp/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-.PATH:${.CURDIR}/../lib
-
-.include "../kamid-version.mk"
-
-PROG=		kamiftp
-SRCS=		9pclib.c ftp.c log.c utils.c
-MAN=		kamiftp.1
-
-CPPFLAGS=	-I${.CURDIR}/../lib -I${.CURDIR}
-
-LDADD+= -levent -lutil -ltls -lreadline
-DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBTLS}
-
-.if ${KAMID_RELEASE} != Yes
-NOMAN= Yes
-.endif
-
-.include <bsd.prog.mk>
blob - /dev/null
blob + db48a7233c4f6f374cf5263aa19af899f5bf900b (mode 644)
--- /dev/null
+++ kamiftp/Makefile.am
@@ -0,0 +1,19 @@
+bin_PROGRAMS =	kamiftp
+
+kamiftp_SOURCES=ftp.c				\
+		$(top_srcdir)/lib/9pclib.c	\
+		$(top_srcdir)/lib/9pclib.h	\
+		$(top_srcdir)/lib/kami.h	\
+		$(top_srcdir)/lib/log.c		\
+		$(top_srcdir)/lib/log.h		\
+		$(top_srcdir)/lib/utils.c	\
+		$(top_srcdir)/lib/utils.h
+
+man1_MANS =	kamiftp.1
+
+LDADD =		$(LIBOBJS)
+
+AM_CPPFLAGS +=	-DKAMID_VERSION='"@VERSION@"'	\
+		-I$(top_srcdir)/		\
+		-I$(top_srcdir)/compat		\
+		-I$(top_srcdir)/lib
blob - 186902f6c09f87dfd6fbbe1ee59bc283ee986c5a
blob + 96e71e43189f2deed268092a49e080c462282053
--- kamiftp/ftp.c
+++ kamiftp/ftp.c
@@ -14,14 +14,14 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "compat.h"
+
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
 #include <assert.h>
-#include <err.h>
 #include <errno.h>
-#include <event.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <netdb.h>
@@ -33,7 +33,6 @@
 #include <syslog.h>
 #include <tls.h>
 #include <unistd.h>
-#include <util.h>
 
 #include <readline/readline.h>
 #include <readline/history.h>
blob - 7de065f7200b6f32f7f8e5fd9ada3f4e07f51ad2 (mode 644)
blob + /dev/null
--- kamirepl/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-.PATH:${.CURDIR}/../lib
-
-.include "../kamid-version.mk"
-
-PROG=		kamirepl
-SRCS=		9pclib.c kamirepl.c log.c utils.c
-
-CPPFLAGS=	-I${.CURDIR}/../lib -I${.CURDIR}
-
-LDADD+= -levent -lutil -ltls
-DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBTLS}
-
-.if ${KAMID_RELEASE} != Yes
-NOMAN= Yes
-.endif
-
-.include <bsd.prog.mk>
blob - /dev/null
blob + afaf551856b3f43121405abebde6a8eb70725594 (mode 644)
--- /dev/null
+++ kamirepl/Makefile.am
@@ -0,0 +1,19 @@
+bin_PROGRAMS =	kamirepl
+
+kamirepl_SOURCES=kamirepl.c			\
+		$(top_srcdir)/lib/9pclib.c	\
+		$(top_srcdir)/lib/9pclib.h	\
+		$(top_srcdir)/lib/kami.h	\
+		$(top_srcdir)/lib/log.c		\
+		$(top_srcdir)/lib/log.h		\
+		$(top_srcdir)/lib/utils.c	\
+		$(top_srcdir)/lib/utils.h
+
+man1_MANS =	kamirepl.1
+
+LDADD =		$(LIBOBJS)
+
+AM_CPPFLAGS +=	-DKAMID_VERSION='"@VERSION@"'	\
+		-I$(top_srcdir)/		\
+		-I$(top_srcdir)/compat		\
+		-I$(top_srcdir)/lib
blob - d811e395f1890a7cf094ee8612574ff27b3e5763
blob + 4e8ce1ff3db895b71d5e0264b8eb3e1ca270020c
--- kamirepl/kamirepl.c
+++ kamirepl/kamirepl.c
@@ -14,6 +14,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "compat.h"
+
 #include <sys/types.h>
 #include <sys/socket.h>
 
@@ -21,9 +23,7 @@
 
 #include <assert.h>
 #include <endian.h>
-#include <err.h>
 #include <errno.h>
-#include <event.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <signal.h>
@@ -33,7 +33,6 @@
 #include <syslog.h>
 #include <tls.h>
 #include <unistd.h>
-#include <vis.h>
 
 #include "9pclib.h"
 #include "kami.h"
blob - 41698f3563582a33b0dcd1b46aac1564ca3e56c0
blob + c9cc93ca244ecef5eedfab354ec8bc7c3ce57f98
--- lib/9pclib.c
+++ lib/9pclib.c
@@ -14,8 +14,9 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "compat.h"
+
 #include <endian.h>
-#include <event.h>
 #include <inttypes.h>
 #include <string.h>
 
blob - 362b9c4cbfe5998442528d99f3faaa2315f44972
blob + 859eb2b7dc29fb815517252d515596e8a2c801f3
--- lib/log.c
+++ lib/log.c
@@ -14,6 +14,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "compat.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
blob - 408dec92e050085f6a48c675e9cb0dde00869cd4
blob + ad34f705b7da07948450ea1d2a3ee510061e9cc2
--- lib/log.h
+++ lib/log.h
@@ -18,7 +18,6 @@
 #define LOG_H
 
 #include <stdarg.h>
-#include <sys/cdefs.h>
 
 void	log_init(int, int);
 void	log_procinit(const char *);
blob - cf1ac0e9438622950da2943dd9476e842bc8531c
blob + 03d5397f7dfc5cb6f74e0789bf83c8db9e0395b0
--- lib/sandbox.c
+++ lib/sandbox.c
@@ -14,6 +14,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "compat.h"
+
 #include "log.h"
 #include "sandbox.h"
 
blob - 19c5accb28cf9b5a472ffc64462f0fd1e645a970
blob + 1f3e5488dc446718a1b1729cd834f81a70446bb8
--- lib/utils.c
+++ lib/utils.c
@@ -14,17 +14,16 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "compat.h"
+
 #include <sys/types.h>
-#include <sys/queue.h>
 #include <sys/uio.h>
 
-#include <event.h>
 #include <ctype.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <imsg.h>
 
 #include "kami.h"
 #include "log.h"
blob - be7be3e1e7d43208a380f99283ed303709a37d0f
blob + ab28e8217ef5a3cca017a41636e3c3a11bbcc1b6
--- lib/utils.h
+++ lib/utils.h
@@ -17,8 +17,6 @@
 #ifndef UTILS_H
 #define UTILS_H
 
-#include <imsg.h>
-
 #define MIN(a, b) ((a) < (b) ? (a) : (b))
 
 struct imsgev {
blob - fb2fec0c8c16141e0cb61f5dcb1150d73bfd9b31 (mode 644)
blob + /dev/null
--- ninepscript/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-.PATH:${.CURDIR}/../lib
-.PATH:${.CURDIR}/../kamid
-
-.include "../kamid-version.mk"
-
-PROG=		ninepscript
-SRCS=		client.c log.c parse.y sandbox.c script.c utils.c
-MAN=		ninepscript.5 ninepscript.8
-
-CPPFLAGS=	-I${.CURDIR}/../kamid/ \
-		-I${.CURDIR}/../lib -I${.CURDIR}
-
-LDADD+= -levent -lutil -ltls
-DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBTLS}
-
-NOMAN = Yes
-
-.include <bsd.prog.mk>
blob - /dev/null
blob + b943b44b5a4b84b2577dabacf43adacb05fbfe0b (mode 644)
--- /dev/null
+++ ninepscript/Makefile.am
@@ -0,0 +1,26 @@
+noinst_PROGRAMS =	ninepscript
+
+ninepscript_SOURCES=parse.y			\
+		script.c			\
+		script.h			\
+		$(top_srcdir)/kamid/client.c	\
+		$(top_srcdir)/kamid/client.h	\
+		$(top_srcdir)/kamid/kamid.h	\
+		$(top_srcdir)/lib/kami.h	\
+		$(top_srcdir)/lib/log.c		\
+		$(top_srcdir)/lib/log.h		\
+		$(top_srcdir)/lib/sandbox.c	\
+		$(top_srcdir)/lib/sandbox.h	\
+		$(top_srcdir)/lib/utils.c	\
+		$(top_srcdir)/lib/utils.h
+
+man5_MANS =	ninepscript.5
+man8_MANS =	ninepscript.8
+
+LDADD =		$(LIBOBJS)
+
+AM_CPPFLAGS +=	-DKAMID_VERSION='"@VERSION@"'	\
+		-I$(top_srcdir)/		\
+		-I$(top_srcdir)/compat		\
+		-I$(top_srcdir)/lib		\
+		-I$(top_srcdir)/kamid
blob - 4eaa64c699eaf0005fe49c0997355c654b84c84d
blob + 6e080e055abaead5828c1c95ebd85c1aa530add6
--- ninepscript/parse.y
+++ ninepscript/parse.y
@@ -23,12 +23,10 @@
 
 %{
 
-#include <sys/queue.h>
+#include "compat.h"
 
 #include <ctype.h>
-#include <err.h>
 #include <errno.h>
-#include <event.h>
 #include <inttypes.h>
 #include <limits.h>
 #include <limits.h>
blob - 74b0934f6ed8a3dc82f3aaef45fb00f3b3fcac40
blob + 28f514eaba85c9b0e3434d9803cdeffbdee9f12d
--- ninepscript/script.c
+++ ninepscript/script.c
@@ -14,16 +14,15 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <sys/queue.h>
+#include "compat.h"
+
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 
 #include <assert.h>
 #include <endian.h>
-#include <err.h>
 #include <errno.h>
-#include <event.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <poll.h>
blob - c0aa58ac4c6f1883a9f4d63cc216253cbf5ff1a0 (mode 644)
blob + /dev/null
--- regress/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-HAVE_LISP ?= No
-
-SUBDIR = ninepscript
-
-.if ${HAVE_LISP:L} == yes
-SUBDIR += lisp
-.endif
-
-.include <bsd.subdir.mk>