changeset 948:21a91311c8ea

cmake: switch back, GNU make is painful
author David Demelier <markand@malikania.fr>
date Sat, 16 Jan 2021 17:58:46 +0100
parents 95201fd9ad88
children b4e8551e2064
files CMakeLists.txt Makefile config.mk extern/libcompat/CMakeLists.txt extern/libcompat/Makefile extern/libcompat/include/compat.h extern/libcompat/src/compat.h.in extern/libcompat/src/null.c extern/libcompat/src/vsyslog.c extern/libcompat/tests/CMakeLists.txt extern/libcompat/tests/test-bsd.c extern/libcompat/tests/test-dirent.c extern/libcompat/tests/test-dlfcn.c extern/libcompat/tests/test-posix.c extern/libcompat/trycompile.sh extern/libcompat/tryinclude.sh extern/libcompat/trylib.sh extern/libcompat/win/dirent/dirent.h extern/libcompat/win/dlfcn/CMakeLists.txt extern/libcompat/win/dlfcn/dlfcn.c extern/libcompat/win/dlfcn/dlfcn.h extern/libduktape/CMakeLists.txt extern/libduktape/Makefile extern/libgreatest/CMakeLists.txt irccd/CMakeLists.txt irccdctl/CMakeLists.txt lib/CMakeLists.txt lib/irccd/config.h.in lib/irccd/irccd.c lib/irccd/js-plugin.c lib/irccd/jsapi-file.c lib/irccd/jsapi-plugin.c lib/irccd/jsapi-server.c lib/irccd/jsapi-system.c lib/irccd/jsapi-system.h lib/irccd/peer.c tests/CMakeLists.txt
diffstat 36 files changed, 2556 insertions(+), 704 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CMakeLists.txt	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,54 @@
+#
+# CMakeLists.txt -- CMake build for irccd
+#
+# Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or 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.
+#
+
+cmake_minimum_required(VERSION 3.12)
+project(irccd)
+
+set_property(GLOBAL PROPERTY USE_FOLDERS On)
+
+set(IRCCD_VERSION_MAJOR 4)
+set(IRCCD_VERSION_MINOR 0)
+set(IRCCD_VERSION_PATCH 0)
+
+option(IRCCD_WITH_JS "Enable Javascript" On)
+option(IRCCD_WITH_SSL "Enable SSL support" On)
+option(IRCCD_WITH_IRCCD "Enable irccd daemon" On)
+option(IRCCD_WITH_IRCCDCTL "Enable irccdctl utility" On)
+option(IRCCD_WITH_TESTS "Enable unit tests" Off)
+
+if (CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
+	set(CMAKE_C_FLAGS "-Wall -Wextra")
+endif ()
+
+include(GNUInstallDirs)
+
+add_subdirectory(extern/libcompat)
+
+if (IRCCD_WITH_JS)
+	add_subdirectory(extern/libduktape)
+endif ()
+
+add_subdirectory(lib)
+add_subdirectory(irccd)
+add_subdirectory(irccdctl)
+
+if (IRCCD_WITH_TESTS)
+	enable_testing()
+	add_subdirectory(extern/libgreatest)
+	add_subdirectory(tests)
+endif ()
--- a/Makefile	Sat Jan 16 09:45:33 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,172 +0,0 @@
-#
-# Makefile -- POSIX makefile for irccd
-#
-# Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
-#
-# Permission to use, copy, modify, and/or 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.
-#
-
-.SUFFIXES:
-.SUFFIXES: .o .c
-
-include config.mk
-
-MAJOR=                  4
-MINOR=                  0
-PATCH=                  0
-
-IRCCD=                  irccd/irccd
-IRCCD_SRCS=             irccd/main.c
-IRCCD_OBJS=             ${IRCCD_SRCS:.c=.o}
-IRCCD_DEPS=             ${IRCCD_SRCS:.c=.d}
-
-IRCCDCTL=               irccdctl/irccdctl
-IRCCDCTL_SRCS=          irccdctl/main.c
-IRCCDCTL_OBJS=          ${IRCCDCTL_SRCS:.c=.o}
-IRCCDCTL_DEPS=          ${IRCCDCTL_SRCS:.c=.d}
-
-LIBCOMPAT=              extern/libcompat/libirccd-compat.a
-
-ifeq (${WITH_JS},yes)
-LIBDUKTAPE=             extern/libduktape/libirccd-duktape.a
-endif
-
-LIBIRCCD=               lib/libirccd.a
-LIBIRCCD_SRCS=          lib/irccd/channel.c
-LIBIRCCD_SRCS+=         lib/irccd/dl-plugin.c
-LIBIRCCD_SRCS+=         lib/irccd/irccd.c
-LIBIRCCD_SRCS+=         lib/irccd/log.c
-LIBIRCCD_SRCS+=         lib/irccd/peer.c
-LIBIRCCD_SRCS+=         lib/irccd/plugin.c
-LIBIRCCD_SRCS+=         lib/irccd/rule.c
-LIBIRCCD_SRCS+=         lib/irccd/server.c
-LIBIRCCD_SRCS+=         lib/irccd/subst.c
-LIBIRCCD_SRCS+=         lib/irccd/transport.c
-LIBIRCCD_SRCS+=         lib/irccd/util.c
-
-ifeq (${WITH_JS},yes)
-LIBIRCCD_SRCS+=         lib/irccd/js-plugin.c
-LIBIRCCD_SRCS+=         lib/irccd/jsapi-chrono.c
-LIBIRCCD_SRCS+=         lib/irccd/jsapi-file.c
-LIBIRCCD_SRCS+=         lib/irccd/jsapi-irccd.c
-LIBIRCCD_SRCS+=         lib/irccd/jsapi-logger.c
-LIBIRCCD_SRCS+=         lib/irccd/jsapi-plugin.c
-LIBIRCCD_SRCS+=         lib/irccd/jsapi-server.c
-LIBIRCCD_SRCS+=         lib/irccd/jsapi-system.c
-LIBIRCCD_SRCS+=         lib/irccd/jsapi-timer.c
-LIBIRCCD_SRCS+=         lib/irccd/jsapi-unicode.c
-LIBIRCCD_SRCS+=         lib/irccd/unicode.c
-endif
-
-LIBIRCCD_OBJS=          ${LIBIRCCD_SRCS:.c=.o}
-LIBIRCCD_DEPS=          ${LIBIRCCD_SRCS:.c=.d}
-
-TESTS=                  tests/test-channel.c
-TESTS+=                 tests/test-dl-plugin.c
-TESTS+=                 tests/test-log.c
-TESTS+=                 tests/test-rule.c
-TESTS+=                 tests/test-subst.c
-TESTS+=                 tests/test-util.c
-TESTS_OBJS=             ${TESTS:.c=}
-
-DEFINES=                -D_BSD_SOURCE
-DEFINES+=               -DSOURCEDIR=\"`pwd`\"
-
-INCS=                   -I extern/libcompat/include
-ifeq (${WITH_JS},yes)
-INCS+=                  -I extern/libduktape
-endif
-INCS+=                  -I extern/libgreatest
-INCS+=                  -I lib
-
-LIBS=                   -L extern/libcompat
-ifeq (${WITH_JS},yes)
-LIBS+=                  -L extern/libduktape
-endif
-LIBS+=                  -L lib
-
-LIBS+=                  -l irccd-compat
-ifeq (${WITH_JS},yes)
-LIBS+=                  -l irccd-duktape
-endif
-LIBS+=                  -l irccd
-
-ifeq (${WITH_SSL},yes)
-LIBS+=                  -l ssl -l crypto
-endif
-
-all: ${IRCCD} ${IRCCDCTL}
-
-.c.o:
-	${CMD.cc}
-
--include ${LIBIRCCD_DEPS}
--include ${IRCCD_DEPS}
-
-${LIBCOMPAT}:
-	${MAKE} CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" -C extern/libcompat
-
-ifeq (${WITH_JS},yes)
-${LIBDUKTAPE}:
-	${MAKE} CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" -C extern/libduktape
-endif
-
-ifneq (${WITH_JS},yes)
-EXTRA_SEDS+=    -e "/IRCCD_WITH_JS/d"
-endif
-
-ifneq (${WITH_SSL},yes)
-EXTRA_SEDS+=    -e "/IRCCD_WITH_SSL/d"
-endif
-
-lib/irccd/config.h: lib/irccd/config.h.in Makefile config.mk
-	sed -e "s/@IRCCD_VERSION_MAJOR@/${MAJOR}/" \
-	    -e "s/@IRCCD_VERSION_MINOR@/${MINOR}/" \
-	    -e "s/@IRCCD_VERSION_PATCH@/${PATCH}/" \
-	    ${EXTRA_SEDS} < $< > $@
-
-${LIBIRCCD_OBJS}: ${LIBCOMPAT} lib/irccd/config.h
-
-${LIBIRCCD}: ${LIBIRCCD_OBJS} ${LIBDUKTAPE}
-	${CMD.ar}
-
-${IRCCD}: ${IRCCD_OBJS} ${LIBCOMPAT} ${LIBDUKTAPE} ${LIBIRCCD}
-	${CMD.ccld}
-
-${IRCCDCTL}: ${IRCCDCTL_OBJS}
-	${CMD.ccld}
-
-# Unit tests.
-tests/test-%: tests/test-%.c
-	${CC} ${DEFINES} ${INCS} ${CFLAGS} -o $@ $< ${LIBS} ${LDFLAGS}
-
-${TESTS_OBJS}: ${LIBIRCCD}
-
-# Sample plugin for test-dl-plugin.
-tests/example-dl-plugin${EXT.shared}: tests/example-dl-plugin.o
-	${CMD.ld-shared}
-
-tests/test-dl-plugin: tests/example-dl-plugin${EXT.shared}
-
-tests: ${TESTS_OBJS}
-	for t in ${TESTS_OBJS}; do ./$$t; done
-
-clean:
-	${MAKE} -C extern/libcompat clean
-	${MAKE} -C extern/libduktape clean
-	rm -f ${LIBIRCCD} ${LIBIRCCD_OBJS} ${LIBIRCCD_DEPS}
-	rm -f ${IRCCD} ${IRCCD_OBJS} ${IRCCD_DEPS}
-	rm -f tests/example-dl-plugin${EXT.shared} tests/example-dl-plugin.o tests/example-dl-plugin.d
-	rm -f ${TESTS_OBJS}
-
-.PHONY: all clean tests
--- a/config.mk	Sat Jan 16 09:45:33 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-#
-# config.mk -- POSIX makefile for irccd
-#
-# Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
-#
-# Permission to use, copy, modify, and/or 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.
-#
-
-# Build tools.
-CC=                     cc
-AR=                     ar
-
-CFLAGS=                 -g -O0 -fsanitize=address,undefined
-LDFLAGS=                -fsanitize=address,undefined
-
-# Installation paths.
-PREFIX=                 /usr/local
-BINDIR=                 bin
-
-# User options.
-WITH_JS=                yes
-WITH_SSL=               no
-
-# System dependant macros.
-OS:=                    $(shell uname -s)
-
-# Standard commands.
-CMD.cc=                 ${CC} ${DEFINES} ${INCS} ${CFLAGS} -MMD -c $< -o $@
-CMD.ccld=               ${CC} ${DEFINES} ${INCS} ${CFLAGS} -o $@ $< ${LIBS} ${LDFLAGS}
-CMD.ar=                 ${AR} -rc $@ $^
-
-# Determine shared library extension and command to generate.
-ifeq (Darwin,${OS})
-EXT.shared=             .dylib
-CMD.ld-shared=          ${CC} -dynamiclib -o $@ $^ ${LDFLAGS}
-else
-EXT.shared=             .so
-CMD.ld-shared=          ${CC} -shared -o $@ $^ ${LDFLAGS}
-endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extern/libcompat/CMakeLists.txt	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,170 @@
+#
+# CMakeLists.txt -- CMake build system for libcompat
+#
+# Copyright (c) 2020 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or 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.
+#
+
+cmake_minimum_required(VERSION 3.10)
+project(libirccd-compat)
+
+option(COMPAT_WITH_TESTS "Enable tests" Off)
+
+include(CheckFunctionExists)
+include(CheckIncludeFile)
+include(CheckStructHasMember)
+
+set(
+	SOURCES
+	${libirccd-compat_SOURCE_DIR}/src/compat.c
+	${libirccd-compat_SOURCE_DIR}/src/compat.h.in
+)
+
+set(
+	FUNCTIONS
+	# BSD extensions.
+	err
+	errc
+	errx
+	pledge
+	reallocarray
+	recallocarray
+	strlcat
+	strlcpy
+	strsep
+	verr
+	verrc
+	verrx
+	vsyslog
+	vwarn
+	vwarnc
+	vwarnx
+	warn
+	warnc
+	warnx
+	# POSIX functions.
+	basename
+	dirname
+	getopt
+	strdup
+	strndup
+	strnlen
+)
+
+set(
+	INCLUDES
+	# BSD extensions.
+	err.h
+	# POSIX extensions.
+	unistd.h
+	libgen.h
+)
+
+foreach (f ${FUNCTIONS})
+	string(TOUPPER ${f} var)
+
+	check_function_exists(${f} COMPAT_HAVE_${var})
+
+	if (NOT COMPAT_HAVE_${var})
+		list(APPEND SOURCES ${libirccd-compat_SOURCE_DIR}/src/${f}.c)
+	endif ()
+endforeach ()
+
+# Functionalities we can't replace
+check_include_file(syslog.h COMPAT_HAVE_SYSLOG_H)
+
+add_library(libirccd-compat STATIC ${SOURCES})
+target_include_directories(
+	libirccd-compat
+	PUBLIC
+		$<BUILD_INTERFACE:${libirccd-compat_BINARY_DIR}>
+)
+set_target_properties(libirccd-compat PROPERTIES PREFIX "")
+
+# Remove useless C4996 warning about "deprecated" functions from MSVC.
+if (CMAKE_C_COMPILER_ID MATCHES MSVC)
+	target_compile_options(libirccd-compat PUBLIC /wd4996)
+endif ()
+
+foreach (i ${INCLUDES})
+	string(TOUPPER ${i} var)
+	string(REGEX REPLACE "\\." "_" var ${var})
+
+	check_include_file(${i} COMPAT_HAVE_${var})
+
+	if (NOT COMPAT_HAVE_${var})
+		file(WRITE ${libirccd-compat_BINARY_DIR}/${i} "/* Empty stub for ${i}. */\n")
+	endif ()
+endforeach ()
+
+# POSIX dirent.h
+check_include_file(dirent.h COMPAT_HAVE_DIRENT_H)
+
+if (NOT COMPAT_HAVE_DIRENT_H)
+	if (CMAKE_SYSTEM_NAME MATCHES "Windows")
+		target_include_directories(
+			libirccd-compat
+			PUBLIC
+				$<BUILD_INTERFACE:${libirccd-compat_SOURCE_DIR}/win/dirent>
+		)
+	endif ()
+endif ()
+
+# Math library
+find_library(COMPAT_HAVE_LIBM m)
+
+if (NOT COMPAT_HAVE_LIBM)
+	add_library(m INTERFACE)
+endif ()
+
+# POSIX dlfcn.h
+check_include_file(dlfcn.h COMPAT_HAVE_DLFCN_H)
+
+if (NOT COMPAT_HAVE_DLFCN_H)
+	if (CMAKE_SYSTEM_NAME MATCHES "Windows")
+		add_subdirectory(win/dlfcn)
+	endif ()
+else ()
+	# Some systems may not provide the dl library.
+	find_library(COMPAT_HAVE_LIBDL dl)
+
+	if (NOT COMPAT_HAVE_LIBDL)
+		add_library(dl INTERFACE)
+	endif ()
+endif ()
+
+# struct stat fields
+check_struct_has_member("struct stat" st_atime sys/stat.h COMPAT_HAVE_STAT_ST_ATIME)
+check_struct_has_member("struct stat" st_blksize sys/stat.h COMPAT_HAVE_STAT_ST_BLKSIZE)
+check_struct_has_member("struct stat" st_blocks sys/stat.h COMPAT_HAVE_STAT_ST_BLOCKS)
+check_struct_has_member("struct stat" st_ctime sys/stat.h COMPAT_HAVE_STAT_ST_CTIME)
+check_struct_has_member("struct stat" st_dev sys/stat.h COMPAT_HAVE_STAT_ST_DEV)
+check_struct_has_member("struct stat" st_gid sys/stat.h COMPAT_HAVE_STAT_ST_GID)
+check_struct_has_member("struct stat" st_ino sys/stat.h COMPAT_HAVE_STAT_ST_INO)
+check_struct_has_member("struct stat" st_mode sys/stat.h COMPAT_HAVE_STAT_ST_MODE)
+check_struct_has_member("struct stat" st_mtime sys/stat.h COMPAT_HAVE_STAT_ST_MTIME)
+check_struct_has_member("struct stat" st_nlink sys/stat.h COMPAT_HAVE_STAT_ST_NLINK)
+check_struct_has_member("struct stat" st_rdev sys/stat.h COMPAT_HAVE_STAT_ST_RDEV)
+check_struct_has_member("struct stat" st_size sys/stat.h COMPAT_HAVE_STAT_ST_SIZE)
+check_struct_has_member("struct stat" st_uid sys/stat.h COMPAT_HAVE_STAT_ST_UID)
+
+if (COMPAT_WITH_TESTS)
+	enable_testing()
+	add_subdirectory(tests)
+endif ()
+
+configure_file(
+	${libirccd-compat_SOURCE_DIR}/src/compat.h.in
+	${libirccd-compat_BINARY_DIR}/compat.h
+)
--- a/extern/libcompat/Makefile	Sat Jan 16 09:45:33 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-#
-# Makefile -- POSIX make for libcompat
-#
-# Copyright (c) 2020 David Demelier <markand@malikania.fr>
-#
-# Permission to use, copy, modify, and/or 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.
-#
-
-.POSIX:
-
-.SUFFIXES:
-.SUFFIXES: .c .o .h .sh
-
-FUNCTIONS_SRCS= src/basename.c \
-                src/dirname.c \
-                src/err.c \
-                src/errc.c \
-                src/errx.c \
-                src/getopt.c \
-                src/pledge.c \
-                src/reallocarray.c \
-                src/recallocarray.c \
-                src/strdup.c \
-                src/strlcat.c \
-                src/strlcpy.c \
-                src/strndup.c \
-                src/strnlen.c \
-                src/strsep.c \
-                src/verr.c \
-                src/verrc.c \
-                src/verrx.c \
-                src/vwarn.c \
-                src/vwarnc.c \
-                src/vwarnx.c \
-                src/warn.c \
-                src/warnc.c \
-                src/warnx.c
-
-INCLUDES_SRCS=  include/err.h \
-                include/libgen.h \
-                include/unistd.h
-
-LIBS_SRCS=      lib/libm.a \
-                lib/libdl.a \
-                lib/libintl.a
-
-FUNCTIONS_OBJS= ${FUNCTIONS_SRCS:.c=.o}
-FUNCTIONS_HDRS= ${FUNCTIONS_SRCS:.c=.h}
-
-LIB=            libirccd-compat.a
-
-all: ${LIB}
-
-.sh:
-	@cp $< $@
-	@chmod +x $@
-
-.c.o:
-	@CC="${CC}" CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" ./trycompile -c ${*F}
-
-.c.h:
-	CC="${CC}" CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" ./trycompile ${*F}
-
-${FUNCTIONS_HDRS} ${FUNCTIONS_OBJS}: trycompile
-
-include: Makefile tryinclude
-	@for h in ${INCLUDES_SRCS}; do CC="${CC}" CFLAGS="${CFLAGS}" ./tryinclude `basename $$h`; done
-	@touch include
-
-lib: Makefile trylib
-	@for l in ${LIBS_SRCS}; do AR="${AR}" CC="${CC}" CFLAGS="${CFLAGS}" ./trylib `basename $$l`; done
-	@touch lib
-
-${LIB}: include lib ${FUNCTIONS_HDRS} ${FUNCTIONS_OBJS}
-	@mkdir -p ${@D}
-	@${AR} -rc $@ ${FUNCTIONS_OBJS} >/dev/null 2>&1
-
-clean:
-	@rm -f trycompile tryinclude trylib
-	@rm -f ${LIB}
-	@rm -f ${FUNCTIONS_OBJS}
-	@rm -f ${FUNCTIONS_HDRS}
-	@rm -f ${INCLUDES_SRCS}
-
-.PHONY: all clean
--- a/extern/libcompat/include/compat.h	Sat Jan 16 09:45:33 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +0,0 @@
-#ifndef LIBCOMPAT_COMPAT_H
-#define LIBCOMPAT_COMPAT_H
-
-#include "../src/basename.h"
-#include "../src/dirname.h"
-#include "../src/err.h"
-#include "../src/errc.h"
-#include "../src/errx.h"
-#include "../src/getopt.h"
-#include "../src/pledge.h"
-#include "../src/reallocarray.h"
-#include "../src/recallocarray.h"
-#include "../src/strdup.h"
-#include "../src/strlcat.h"
-#include "../src/strlcpy.h"
-#include "../src/strndup.h"
-#include "../src/strnlen.h"
-#include "../src/strsep.h"
-#include "../src/verr.h"
-#include "../src/verrc.h"
-#include "../src/verrx.h"
-#include "../src/vwarn.h"
-#include "../src/vwarnc.h"
-#include "../src/vwarnx.h"
-#include "../src/warn.h"
-#include "../src/warnc.h"
-#include "../src/warnx.h"
-
-#include <stdarg.h>
-#include <stddef.h>
-
-#ifndef COMPAT_HAVE_BASENAME
-char *
-basename(char *);
-#endif
-
-#ifndef COMPAT_HAVE_DIRNAME
-char *
-dirname(char *);
-#endif
-
-#ifndef COMPAT_HAVE_ERR
-void
-err(int, const char *, ...);
-#endif
-
-#ifndef COMPAT_HAVE_ERRC
-void
-errc(int, int, const char *, ...);
-#endif
-
-#ifndef COMPAT_HAVE_ERRX
-void
-errx(int, const char *, ...);
-#endif
-
-#ifndef COMPAT_HAVE_VERR
-void
-verr(int, const char *, va_list);
-#endif
-
-#ifndef COMPAT_HAVE_VERRC
-void
-verrc(int, int, const char *, va_list);
-#endif
-
-#ifndef COMPAT_HAVE_VERRX
-void
-verrx(int, const char *, va_list);
-#endif
-
-#ifndef COMPAT_HAVE_VWARN
-void
-vwarn(const char *, va_list);
-#endif
-
-#ifndef COMPAT_HAVE_VWARNC
-void
-vwarnc(int, const char *, va_list);
-#endif
-
-#ifndef COMPAT_HAVE_VWARNX
-void
-vwarnx(const char *, va_list);
-#endif
-
-#ifndef COMPAT_HAVE_WARN
-void
-warn(const char *, ...);
-#endif
-
-#ifndef COMPAT_HAVE_WARNC
-void
-warnc(int, const char *, ...);
-#endif
-
-#ifndef COMPAT_HAVE_WARNX
-void
-warnx(const char *, ...);
-#endif
-
-#ifndef COMPAT_HAVE_GETOPT
-extern int opterr;
-extern int optind;
-extern int optopt;
-extern int optreset;
-extern char *optarg;
-
-int
-getopt(int, char **, const char *);
-#endif
-
-#ifndef COMPAT_HAVE_PLEDGE
-int
-pledge(const char *, const char *);
-#endif
-
-#ifndef COMPAT_HAVE_REALLOCARRAY
-void *
-reallocarray(void *, size_t, size_t);
-#endif
-
-#ifndef COMPAT_HAVE_RECALLOCARRAY
-void *
-recallocarray(void *, size_t, size_t, size_t);
-#endif
-
-#ifndef COMPAT_HAVE_STRDUP
-char *
-strdup(const char *);
-#endif
-
-#ifndef COMPAT_HAVE_STRLCAT
-size_t
-strlcat(char *, const char *, size_t);
-#endif
-
-#ifndef COMPAT_HAVE_STRLCPY
-size_t
-strlcpy(char *, const char *, size_t);
-#endif
-
-#ifndef COMPAT_HAVE_STRNDUP
-char *
-strndup(const char *, size_t);
-#endif
-
-#ifndef COMPAT_HAVE_STRNLEN
-size_t
-strnlen(const char *, size_t);
-#endif
-
-#ifndef COMPAT_HAVE_STRSEP
-char *
-strsep(char **, const char *);
-#endif
-
-#endif /* !LIBCOMPAT_COMPAT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extern/libcompat/src/compat.h.in	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,172 @@
+#ifndef LIBCOMPAT_COMPAT_H
+#define LIBCOMPAT_COMPAT_H
+
+#cmakedefine COMPAT_HAVE_BASENAME
+#cmakedefine COMPAT_HAVE_DIRNAME
+#cmakedefine COMPAT_HAVE_ERR
+#cmakedefine COMPAT_HAVE_ERRC
+#cmakedefine COMPAT_HAVE_ERRX
+#cmakedefine COMPAT_HAVE_GETOPT
+#cmakedefine COMPAT_HAVE_PLEDGE
+#cmakedefine COMPAT_HAVE_REALLOCARRAY
+#cmakedefine COMPAT_HAVE_RECALLOCARRAY
+#cmakedefine COMPAT_HAVE_STRDUP
+#cmakedefine COMPAT_HAVE_STRLCAT
+#cmakedefine COMPAT_HAVE_STRLCPY
+#cmakedefine COMPAT_HAVE_STRNDUP
+#cmakedefine COMPAT_HAVE_STRNLEN
+#cmakedefine COMPAT_HAVE_STRSEP
+#cmakedefine COMPAT_HAVE_VERR
+#cmakedefine COMPAT_HAVE_VERRC
+#cmakedefine COMPAT_HAVE_VERRX
+#cmakedefine COMPAT_HAVE_VWARN
+#cmakedefine COMPAT_HAVE_VWARNC
+#cmakedefine COMPAT_HAVE_VWARNX
+#cmakedefine COMPAT_HAVE_WARN
+#cmakedefine COMPAT_HAVE_WARNC
+#cmakedefine COMPAT_HAVE_WARNX
+
+#cmakedefine COMPAT_HAVE_STAT_ST_ATIME
+#cmakedefine COMPAT_HAVE_STAT_ST_BLKSIZE
+#cmakedefine COMPAT_HAVE_STAT_ST_BLOCKS
+#cmakedefine COMPAT_HAVE_STAT_ST_CTIME
+#cmakedefine COMPAT_HAVE_STAT_ST_DEV
+#cmakedefine COMPAT_HAVE_STAT_ST_GID
+#cmakedefine COMPAT_HAVE_STAT_ST_INO
+#cmakedefine COMPAT_HAVE_STAT_ST_MODE
+#cmakedefine COMPAT_HAVE_STAT_ST_MTIME
+#cmakedefine COMPAT_HAVE_STAT_ST_NLINK
+#cmakedefine COMPAT_HAVE_STAT_ST_RDEV
+#cmakedefine COMPAT_HAVE_STAT_ST_SIZE
+#cmakedefine COMPAT_HAVE_STAT_ST_UID
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#ifndef COMPAT_HAVE_BASENAME
+char *
+basename(char *);
+#endif
+
+#ifndef COMPAT_HAVE_DIRNAME
+char *
+dirname(char *);
+#endif
+
+#ifndef COMPAT_HAVE_ERR
+void
+err(int, const char *, ...);
+#endif
+
+#ifndef COMPAT_HAVE_ERRC
+void
+errc(int, int, const char *, ...);
+#endif
+
+#ifndef COMPAT_HAVE_ERRX
+void
+errx(int, const char *, ...);
+#endif
+
+#ifndef COMPAT_HAVE_VERR
+void
+verr(int, const char *, va_list);
+#endif
+
+#ifndef COMPAT_HAVE_VERRC
+void
+verrc(int, int, const char *, va_list);
+#endif
+
+#ifndef COMPAT_HAVE_VERRX
+void
+verrx(int, const char *, va_list);
+#endif
+
+#ifndef COMPAT_HAVE_VWARN
+void
+vwarn(const char *, va_list);
+#endif
+
+#ifndef COMPAT_HAVE_VWARNC
+void
+vwarnc(int, const char *, va_list);
+#endif
+
+#ifndef COMPAT_HAVE_VWARNX
+void
+vwarnx(const char *, va_list);
+#endif
+
+#ifndef COMPAT_HAVE_WARN
+void
+warn(const char *, ...);
+#endif
+
+#ifndef COMPAT_HAVE_WARNC
+void
+warnc(int, const char *, ...);
+#endif
+
+#ifndef COMPAT_HAVE_WARNX
+void
+warnx(const char *, ...);
+#endif
+
+#ifndef COMPAT_HAVE_GETOPT
+extern int opterr;
+extern int optind;
+extern int optopt;
+extern int optreset;
+extern char *optarg;
+
+int
+getopt(int, char **, const char *);
+#endif
+
+#ifndef COMPAT_HAVE_PLEDGE
+int
+pledge(const char *, const char *);
+#endif
+
+#ifndef COMPAT_HAVE_REALLOCARRAY
+void *
+reallocarray(void *, size_t, size_t);
+#endif
+
+#ifndef COMPAT_HAVE_RECALLOCARRAY
+void *
+recallocarray(void *, size_t, size_t, size_t);
+#endif
+
+#ifndef COMPAT_HAVE_STRDUP
+char *
+strdup(const char *);
+#endif
+
+#ifndef COMPAT_HAVE_STRLCAT
+size_t
+strlcat(char *, const char *, size_t);
+#endif
+
+#ifndef COMPAT_HAVE_STRLCPY
+size_t
+strlcpy(char *, const char *, size_t);
+#endif
+
+#ifndef COMPAT_HAVE_STRNDUP
+char *
+strndup(const char *, size_t);
+#endif
+
+#ifndef COMPAT_HAVE_STRNLEN
+size_t
+strnlen(const char *, size_t);
+#endif
+
+#ifndef COMPAT_HAVE_STRSEP
+char *
+strsep(char **, const char *);
+#endif
+
+#endif /* !LIBCOMPAT_COMPAT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extern/libcompat/src/vsyslog.c	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,39 @@
+/*
+ * vsyslog.c -- fallback implementation
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or 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"
+
+#if defined(COMPAT_HAVE_SYSLOG_H)
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <syslog.h>
+
+void
+vsyslog(int priority, const char *msg, va_list ap)
+{
+	assert(msg);
+
+	char buf[1024] = {0};
+
+	if (vsnprintf(buf, sizeof (buf), fmt, ap) >= 0)
+		syslog(priority, "%s", buf);
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extern/libcompat/tests/CMakeLists.txt	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,38 @@
+#
+# CMakeLists.txt -- CMake build system for libcompat
+#
+# Copyright (c) 2020 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or 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.
+#
+
+project(tests)
+
+function(define_test name)
+	add_executable(test-${name} test-${name}.c)
+	add_test(NAME test-${name} COMMAND test-${name})
+	target_link_libraries(test-${name} libcompat)
+endfunction()
+
+# POSIX dlfcn/dlopen/dlsym.
+define_test(dlfcn)
+set_target_properties(test-dlfcn PROPERTIES ENABLE_EXPORTS On)
+target_link_libraries(test-dlfcn dl)
+
+# POSIX opendir/readdir.
+define_test(dirent)
+target_compile_definitions(test-dirent PRIVATE DIRECTORY="${tests_SOURCE_DIR}")
+
+# BSD and POSIX functions.
+define_test(bsd)
+define_test(posix)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extern/libcompat/tests/test-bsd.c	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,50 @@
+/*
+ * test-bsd.c -- test BSD extensions
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or 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.
+ */
+
+#define _BSD_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <compat.h>
+
+int
+main(void)
+{
+	int *ints = NULL;
+	char buf[BUFSIZ];
+	char *token, *p;
+
+	pledge("stdio", NULL);
+
+	ints = reallocarray(ints, 5, sizeof (*ints));
+	ints = recallocarray(ints, 5, 10, sizeof (*ints));
+
+	strlcpy(buf, "Hello", sizeof (buf));
+	strlcat(buf, " world", sizeof (buf));
+
+	p = token = buf;
+
+	while ((token = strsep(&p, " \t")))
+		printf("%s\n", token);
+
+	free(ints);
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extern/libcompat/tests/test-dirent.c	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,41 @@
+/*
+ * dirent.c -- test opendir/readdir
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or 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 <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+int
+main(void)
+{
+	DIR *dp;
+	struct dirent *entry;
+
+	if (!(dp = opendir(DIRECTORY))) {
+		fprintf(stderr, "abort: %s\n", strerror(errno));
+		return 1;
+	}
+
+	while ((entry = readdir(dp)))
+		printf("%s\n", entry->d_name);
+
+	closedir(dp);
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extern/libcompat/tests/test-dlfcn.c	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,53 @@
+/*
+ * test-dlfcn.c -- test dlopen/dlsym/dlclose
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or 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 <dlfcn.h>
+#include <stdio.h>
+
+#if defined(_WIN32)
+#       define EXPORT __declspec(dllexport)
+#else
+#       define EXPORT
+#endif
+
+EXPORT int
+hello(void)
+{
+	return 0;
+}
+
+int
+main(void)
+{
+	void *handle;
+	int (*func)(void);
+	int ret = 1;
+
+	if (!(handle = dlopen(NULL, RTLD_NOW)) ||
+	    !(func = dlsym(handle, "hello"))) {
+		fprintf(stderr, "%s\n", dlerror());
+		goto end;
+	}
+
+	ret = func();
+
+	dlclose(handle);
+
+end:
+	return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extern/libcompat/tests/test-posix.c	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,64 @@
+/*
+ * test-posix.c -- test POSIX functions
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or 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.
+ */
+
+#define _BSD_SOURCE
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <compat.h>
+
+int
+main(int argc, char **argv)
+{
+	char path1[FILENAME_MAX] = "/usr/local/bin/vim";
+	char path2[FILENAME_MAX] = "/usr/local/bin/vim";
+	char tmppath[FILENAME_MAX];
+	char *s;
+
+	for (int ch; (ch = getopt(argc, argv, "l:v")) != -1; ) {
+		switch (ch) {
+		case 'l':
+			printf("log level: %s\n", optarg);
+			break;
+		case 'v':
+			printf("verbosity set\n");
+			break;
+		default:
+			break;
+		}
+	}
+
+	s = strdup("Hello World");
+	printf("%s\n", s);
+	free(s);
+
+	s = strndup("Hello World, what's up?", 11);
+	printf("%s\n", s);
+	free(s);
+
+	snprintf(tmppath, sizeof (tmppath), "%s", basename(path1));
+	puts(tmppath);
+
+	snprintf(tmppath, sizeof (tmppath), "%s", dirname(path1));
+	puts(tmppath);
+
+	printf("max len: %zu\n", strnlen("francis", 3));
+}
--- a/extern/libcompat/trycompile.sh	Sat Jan 16 09:45:33 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-#!/bin/sh
-
-prog=$(basename $0)
-top=$(dirname $0)
-compile=0
-src=$top/src/null.c
-
-usage()
-{
-	echo "usage: $prog [-c] function" 1>&2
-	exit 1
-}
-
-code()
-{
-	cat <<-EOF
-	void $1();
-
-	int
-	main(void)
-	{
-		$1();
-	}
-	EOF
-}
-
-var()
-{
-	printf "$1" | tr "[:lower:]" "[:upper:]"
-}
-
-while getopts "c" opt; do
-	case $opt in
-	c)
-		compile=1 ;;
-	*)
-		usage ;;
-	esac
-done
-
-shift $((OPTIND - 1))
-
-if [ $# -eq 0 ]; then
-	usage
-fi
-
-trap "rm -f test-$1 test-$1.c" QUIT INT TERM EXIT
-
-code $1 > test-$1.c
-
-if [ $compile -eq 0 ]; then
-	printf "Checking for function $1: "
-fi
-
-if ! $CC $CFLAGS -o test-$1 test-$1.c >/dev/null 2>&1; then
-	if [ $compile -eq 0 ]; then
-		printf "no\n"
-	fi
-
-	src=$top/src/$1.c
-	touch $top/src/$1.h
-else
-	if [ $compile -eq 0 ]; then
-		printf "yes\n"
-	fi
-
-	printf "#define COMPAT_HAVE_%s\n" $(var $1) > $top/src/$1.h
-fi
-
-if [ $compile -eq 1 ]; then
-	$CC $CFLAGS -I $top -c $src -o $top/src/$1.o
-fi
--- a/extern/libcompat/tryinclude.sh	Sat Jan 16 09:45:33 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-#!/bin/sh
-
-prog=$(basename $0)
-top=$(dirname $0)
-
-usage()
-{
-	echo "usage: $prog include" 1>&2
-	exit 1
-}
-
-code()
-{
-	cat <<-EOF
-	#include <$1>
-
-	int
-	main(void)
-	{
-	}
-	EOF
-}
-
-trap "rm -f test-$1 test-$1.c" QUIT INT TERM EXIT
-
-code $1 > test-$1.c
-
-printf "Checking for include file $1: "
-
-if ! $CC $CFLAGS -o test-$1 test-$1.c; then
-	printf "no\n"
-	touch $top/include/$1
-else
-	printf "yes\n"
-fi
--- a/extern/libcompat/trylib.sh	Sat Jan 16 09:45:33 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-#!/bin/sh
-
-prog=$(basename $0)
-top=$(dirname $0)
-
-usage()
-{
-	echo "usage: $prog lib" 1>&2
-	exit 1
-}
-
-code()
-{
-	cat <<-EOF
-	int
-	main(void)
-	{
-	}
-	EOF
-}
-
-lib()
-{
-	printf "$1" | sed 's|^lib||;s|\.a||'
-}
-
-name=$(lib $1)
-
-trap "rm -f test-$name test-$name.c" QUIT INT TERM EXIT
-
-code > test-$name.c
-
-printf "Checking for library $name: "
-
-if ! $CC $CFLAGS -o test-$name test-$name.c $LDFLAGS -l$name; then
-	printf "no\n"
-	$CC $CFLAGS -c $top/src/null.c -o $top/src/null.o
-	$AR -rc $top/lib/lib$name.a $top/src/null.o
-	rm -f $top/src/null.o
-else
-	printf "yes\n"
-fi
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extern/libcompat/win/dirent/dirent.h	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,1027 @@
+/*
+ * Dirent interface for Microsoft Visual Studio
+ *
+ * Copyright (C) 1998-2019 Toni Ronkko
+ * This file is part of dirent.  Dirent may be freely distributed
+ * under the MIT license.  For all details and documentation, see
+ * https://github.com/tronkko/dirent
+ */
+#ifndef DIRENT_H
+#define DIRENT_H
+
+/* Hide warnings about unreferenced local functions */
+#if defined(__clang__)
+#	pragma clang diagnostic ignored "-Wunused-function"
+#elif defined(_MSC_VER)
+#	pragma warning(disable:4505)
+#elif defined(__GNUC__)
+#	pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+/*
+ * Include windows.h without Windows Sockets 1.1 to prevent conflicts with
+ * Windows Sockets 2.0.
+ */
+#ifndef WIN32_LEAN_AND_MEAN
+#	define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <wchar.h>
+#include <string.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <ctype.h>
+
+/* Indicates that d_type field is available in dirent structure */
+#define _DIRENT_HAVE_D_TYPE
+
+/* Indicates that d_namlen field is available in dirent structure */
+#define _DIRENT_HAVE_D_NAMLEN
+
+/* Entries missing from MSVC 6.0 */
+#if !defined(FILE_ATTRIBUTE_DEVICE)
+#	define FILE_ATTRIBUTE_DEVICE 0x40
+#endif
+
+/* File type and permission flags for stat(), general mask */
+#if !defined(S_IFMT)
+#	define S_IFMT _S_IFMT
+#endif
+
+/* Directory bit */
+#if !defined(S_IFDIR)
+#	define S_IFDIR _S_IFDIR
+#endif
+
+/* Character device bit */
+#if !defined(S_IFCHR)
+#	define S_IFCHR _S_IFCHR
+#endif
+
+/* Pipe bit */
+#if !defined(S_IFFIFO)
+#	define S_IFFIFO _S_IFFIFO
+#endif
+
+/* Regular file bit */
+#if !defined(S_IFREG)
+#	define S_IFREG _S_IFREG
+#endif
+
+/* Read permission */
+#if !defined(S_IREAD)
+#	define S_IREAD _S_IREAD
+#endif
+
+/* Write permission */
+#if !defined(S_IWRITE)
+#	define S_IWRITE _S_IWRITE
+#endif
+
+/* Execute permission */
+#if !defined(S_IEXEC)
+#	define S_IEXEC _S_IEXEC
+#endif
+
+/* Pipe */
+#if !defined(S_IFIFO)
+#	define S_IFIFO _S_IFIFO
+#endif
+
+/* Block device */
+#if !defined(S_IFBLK)
+#	define S_IFBLK 0
+#endif
+
+/* Link */
+#if !defined(S_IFLNK)
+#	define S_IFLNK 0
+#endif
+
+/* Socket */
+#if !defined(S_IFSOCK)
+#	define S_IFSOCK 0
+#endif
+
+/* Read user permission */
+#if !defined(S_IRUSR)
+#	define S_IRUSR S_IREAD
+#endif
+
+/* Write user permission */
+#if !defined(S_IWUSR)
+#	define S_IWUSR S_IWRITE
+#endif
+
+/* Execute user permission */
+#if !defined(S_IXUSR)
+#	define S_IXUSR 0
+#endif
+
+/* Read group permission */
+#if !defined(S_IRGRP)
+#	define S_IRGRP 0
+#endif
+
+/* Write group permission */
+#if !defined(S_IWGRP)
+#	define S_IWGRP 0
+#endif
+
+/* Execute group permission */
+#if !defined(S_IXGRP)
+#	define S_IXGRP 0
+#endif
+
+/* Read others permission */
+#if !defined(S_IROTH)
+#	define S_IROTH 0
+#endif
+
+/* Write others permission */
+#if !defined(S_IWOTH)
+#	define S_IWOTH 0
+#endif
+
+/* Execute others permission */
+#if !defined(S_IXOTH)
+#	define S_IXOTH 0
+#endif
+
+/* Maximum length of file name */
+#if !defined(PATH_MAX)
+#	define PATH_MAX MAX_PATH
+#endif
+#if !defined(FILENAME_MAX)
+#	define FILENAME_MAX MAX_PATH
+#endif
+#if !defined(NAME_MAX)
+#	define NAME_MAX FILENAME_MAX
+#endif
+
+/* File type flags for d_type */
+#define DT_UNKNOWN 0
+#define DT_REG S_IFREG
+#define DT_DIR S_IFDIR
+#define DT_FIFO S_IFIFO
+#define DT_SOCK S_IFSOCK
+#define DT_CHR S_IFCHR
+#define DT_BLK S_IFBLK
+#define DT_LNK S_IFLNK
+
+/* Macros for converting between st_mode and d_type */
+#define IFTODT(mode) ((mode) & S_IFMT)
+#define DTTOIF(type) (type)
+
+/*
+ * File type macros.  Note that block devices, sockets and links cannot be
+ * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are
+ * only defined for compatibility.  These macros should always return false
+ * on Windows.
+ */
+#if !defined(S_ISFIFO)
+#	define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISDIR)
+#	define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG)
+#	define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISLNK)
+#	define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK)
+#	define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISCHR)
+#	define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISBLK)
+#	define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
+#endif
+
+/* Return the exact length of the file name without zero terminator */
+#define _D_EXACT_NAMLEN(p) ((p)->d_namlen)
+
+/* Return the maximum size of a file name */
+#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1)
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Wide-character version */
+struct _wdirent {
+	/* Always zero */
+	long d_ino;
+
+	/* File position within stream */
+	long d_off;
+
+	/* Structure size */
+	unsigned short d_reclen;
+
+	/* Length of name without \0 */
+	size_t d_namlen;
+
+	/* File type */
+	int d_type;
+
+	/* File name */
+	wchar_t d_name[PATH_MAX+1];
+};
+typedef struct _wdirent _wdirent;
+
+struct _WDIR {
+	/* Current directory entry */
+	struct _wdirent ent;
+
+	/* Private file data */
+	WIN32_FIND_DATAW data;
+
+	/* True if data is valid */
+	int cached;
+
+	/* Win32 search handle */
+	HANDLE handle;
+
+	/* Initial directory name */
+	wchar_t *patt;
+};
+typedef struct _WDIR _WDIR;
+
+/* Multi-byte character version */
+struct dirent {
+	/* Always zero */
+	long d_ino;
+
+	/* File position within stream */
+	long d_off;
+
+	/* Structure size */
+	unsigned short d_reclen;
+
+	/* Length of name without \0 */
+	size_t d_namlen;
+
+	/* File type */
+	int d_type;
+
+	/* File name */
+	char d_name[PATH_MAX+1];
+};
+typedef struct dirent dirent;
+
+struct DIR {
+	struct dirent ent;
+	struct _WDIR *wdirp;
+};
+typedef struct DIR DIR;
+
+
+/* Dirent functions */
+static DIR *opendir(const char *dirname);
+static _WDIR *_wopendir(const wchar_t *dirname);
+
+static struct dirent *readdir(DIR *dirp);
+static struct _wdirent *_wreaddir(_WDIR *dirp);
+
+static int readdir_r(
+	DIR *dirp, struct dirent *entry, struct dirent **result);
+static int _wreaddir_r(
+	_WDIR *dirp, struct _wdirent *entry, struct _wdirent **result);
+
+static int closedir(DIR *dirp);
+static int _wclosedir(_WDIR *dirp);
+
+static void rewinddir(DIR* dirp);
+static void _wrewinddir(_WDIR* dirp);
+
+static int scandir(const char *dirname, struct dirent ***namelist,
+	int (*filter)(const struct dirent*),
+	int (*compare)(const struct dirent**, const struct dirent**));
+
+static int alphasort(const struct dirent **a, const struct dirent **b);
+
+static int versionsort(const struct dirent **a, const struct dirent **b);
+
+static int strverscmp(const char *a, const char *b);
+
+/* For compatibility with Symbian */
+#define wdirent _wdirent
+#define WDIR _WDIR
+#define wopendir _wopendir
+#define wreaddir _wreaddir
+#define wclosedir _wclosedir
+#define wrewinddir _wrewinddir
+
+/* Compatibility with older Microsoft compilers and non-Microsoft compilers */
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+#	define wcstombs_s dirent_wcstombs_s
+#	define mbstowcs_s dirent_mbstowcs_s
+#endif
+
+/* Optimize dirent_set_errno() away on modern Microsoft compilers */
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#	define dirent_set_errno _set_errno
+#endif
+
+
+/* Internal utility functions */
+static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp);
+static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp);
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static int dirent_mbstowcs_s(
+	size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords,
+	const char *mbstr, size_t count);
+#endif
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static int dirent_wcstombs_s(
+	size_t *pReturnValue, char *mbstr, size_t sizeInBytes,
+	const wchar_t *wcstr, size_t count);
+#endif
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static void dirent_set_errno(int error);
+#endif
+
+
+/*
+ * Open directory stream DIRNAME for read and return a pointer to the
+ * internal working area that is used to retrieve individual directory
+ * entries.
+ */
+static _WDIR *_wopendir(const wchar_t *dirname)
+{
+	wchar_t *p;
+
+	/* Must have directory name */
+	if (dirname == NULL || dirname[0] == '\0') {
+		dirent_set_errno(ENOENT);
+		return NULL;
+	}
+
+	/* Allocate new _WDIR structure */
+	_WDIR *dirp = (_WDIR*) malloc(sizeof(struct _WDIR));
+	if (!dirp)
+		return NULL;
+
+	/* Reset _WDIR structure */
+	dirp->handle = INVALID_HANDLE_VALUE;
+	dirp->patt = NULL;
+	dirp->cached = 0;
+
+	/*
+	 * Compute the length of full path plus zero terminator
+	 *
+	 * Note that on WinRT there's no way to convert relative paths
+	 * into absolute paths, so just assume it is an absolute path.
+	 */
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+	/* Desktop */
+	DWORD n = GetFullPathNameW(dirname, 0, NULL, NULL);
+#else
+	/* WinRT */
+	size_t n = wcslen(dirname);
+#endif
+
+	/* Allocate room for absolute directory name and search pattern */
+	dirp->patt = (wchar_t*) malloc(sizeof(wchar_t) * n + 16);
+	if (dirp->patt == NULL)
+		goto exit_closedir;
+
+	/*
+	 * Convert relative directory name to an absolute one.  This
+	 * allows rewinddir() to function correctly even when current
+	 * working directory is changed between opendir() and rewinddir().
+	 *
+	 * Note that on WinRT there's no way to convert relative paths
+	 * into absolute paths, so just assume it is an absolute path.
+	 */
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+	/* Desktop */
+	n = GetFullPathNameW(dirname, n, dirp->patt, NULL);
+	if (n <= 0)
+		goto exit_closedir;
+#else
+	/* WinRT */
+	wcsncpy_s(dirp->patt, n+1, dirname, n);
+#endif
+
+	/* Append search pattern \* to the directory name */
+	p = dirp->patt + n;
+	switch (p[-1]) {
+	case '\\':
+	case '/':
+	case ':':
+		/* Directory ends in path separator, e.g. c:\temp\ */
+		/*NOP*/;
+		break;
+
+	default:
+		/* Directory name doesn't end in path separator */
+		*p++ = '\\';
+	}
+	*p++ = '*';
+	*p = '\0';
+
+	/* Open directory stream and retrieve the first entry */
+	if (!dirent_first(dirp))
+		goto exit_closedir;
+
+	/* Success */
+	return dirp;
+
+	/* Failure */
+exit_closedir:
+	_wclosedir(dirp);
+	return NULL;
+}
+
+/*
+ * Read next directory entry.
+ *
+ * Returns pointer to static directory entry which may be overwritten by
+ * subsequent calls to _wreaddir().
+ */
+static struct _wdirent *_wreaddir(_WDIR *dirp)
+{
+	/*
+	 * Read directory entry to buffer.  We can safely ignore the return
+	 * value as entry will be set to NULL in case of error.
+	 */
+	struct _wdirent *entry;
+	(void) _wreaddir_r(dirp, &dirp->ent, &entry);
+
+	/* Return pointer to statically allocated directory entry */
+	return entry;
+}
+
+/*
+ * Read next directory entry.
+ *
+ * Returns zero on success.  If end of directory stream is reached, then sets
+ * result to NULL and returns zero.
+ */
+static int _wreaddir_r(
+	_WDIR *dirp, struct _wdirent *entry, struct _wdirent **result)
+{
+	/* Read next directory entry */
+	WIN32_FIND_DATAW *datap = dirent_next(dirp);
+	if (!datap) {
+		/* Return NULL to indicate end of directory */
+		*result = NULL;
+		return /*OK*/0;
+	}
+
+	/*
+	 * Copy file name as wide-character string.  If the file name is too
+	 * long to fit in to the destination buffer, then truncate file name
+	 * to PATH_MAX characters and zero-terminate the buffer.
+	 */
+	size_t n = 0;
+	while (n < PATH_MAX && datap->cFileName[n] != 0) {
+		entry->d_name[n] = datap->cFileName[n];
+		n++;
+	}
+	entry->d_name[n] = 0;
+
+	/* Length of file name excluding zero terminator */
+	entry->d_namlen = n;
+
+	/* File type */
+	DWORD attr = datap->dwFileAttributes;
+	if ((attr & FILE_ATTRIBUTE_DEVICE) != 0)
+		entry->d_type = DT_CHR;
+	else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
+		entry->d_type = DT_DIR;
+	else
+		entry->d_type = DT_REG;
+
+	/* Reset dummy fields */
+	entry->d_ino = 0;
+	entry->d_off = 0;
+	entry->d_reclen = sizeof(struct _wdirent);
+
+	/* Set result address */
+	*result = entry;
+	return /*OK*/0;
+}
+
+/*
+ * Close directory stream opened by opendir() function.  This invalidates the
+ * DIR structure as well as any directory entry read previously by
+ * _wreaddir().
+ */
+static int _wclosedir(_WDIR *dirp)
+{
+	if (!dirp) {
+		dirent_set_errno(EBADF);
+		return /*failure*/-1;
+	}
+
+	/* Release search handle */
+	if (dirp->handle != INVALID_HANDLE_VALUE)
+		FindClose(dirp->handle);
+
+	/* Release search pattern */
+	free(dirp->patt);
+
+	/* Release directory structure */
+	free(dirp);
+	return /*success*/0;
+}
+
+/*
+ * Rewind directory stream such that _wreaddir() returns the very first
+ * file name again.
+ */
+static void _wrewinddir(_WDIR* dirp)
+{
+	if (!dirp)
+		return;
+
+	/* Release existing search handle */
+	if (dirp->handle != INVALID_HANDLE_VALUE)
+		FindClose(dirp->handle);
+
+	/* Open new search handle */
+	dirent_first(dirp);
+}
+
+/* Get first directory entry */
+static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp)
+{
+	if (!dirp)
+		return NULL;
+
+	/* Open directory and retrieve the first entry */
+	dirp->handle = FindFirstFileExW(
+		dirp->patt, FindExInfoStandard, &dirp->data,
+		FindExSearchNameMatch, NULL, 0);
+	if (dirp->handle == INVALID_HANDLE_VALUE)
+		goto error;
+
+	/* A directory entry is now waiting in memory */
+	dirp->cached = 1;
+	return &dirp->data;
+
+error:
+	/* Failed to open directory: no directory entry in memory */
+	dirp->cached = 0;
+
+	/* Set error code */
+	DWORD errorcode = GetLastError();
+	switch (errorcode) {
+	case ERROR_ACCESS_DENIED:
+		/* No read access to directory */
+		dirent_set_errno(EACCES);
+		break;
+
+	case ERROR_DIRECTORY:
+		/* Directory name is invalid */
+		dirent_set_errno(ENOTDIR);
+		break;
+
+	case ERROR_PATH_NOT_FOUND:
+	default:
+		/* Cannot find the file */
+		dirent_set_errno(ENOENT);
+	}
+	return NULL;
+}
+
+/* Get next directory entry */
+static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp)
+{
+	/* Is the next directory entry already in cache? */
+	if (dirp->cached) {
+		/* Yes, a valid directory entry found in memory */
+		dirp->cached = 0;
+		return &dirp->data;
+	}
+
+	/* No directory entry in cache */
+	if (dirp->handle == INVALID_HANDLE_VALUE)
+		return NULL;
+
+	/* Read the next directory entry from stream */
+	if (FindNextFileW(dirp->handle, &dirp->data) == FALSE)
+		goto exit_close;
+
+	/* Success */
+	return &dirp->data;
+
+	/* Failure */
+exit_close:
+	FindClose(dirp->handle);
+	dirp->handle = INVALID_HANDLE_VALUE;
+	return NULL;
+}
+
+/* Open directory stream using plain old C-string */
+static DIR *opendir(const char *dirname)
+{
+	/* Must have directory name */
+	if (dirname == NULL || dirname[0] == '\0') {
+		dirent_set_errno(ENOENT);
+		return NULL;
+	}
+
+	/* Allocate memory for DIR structure */
+	struct DIR *dirp = (DIR*) malloc(sizeof(struct DIR));
+	if (!dirp)
+		return NULL;
+
+	/* Convert directory name to wide-character string */
+	wchar_t wname[PATH_MAX + 1];
+	size_t n;
+	int error = mbstowcs_s(&n, wname, PATH_MAX + 1, dirname, PATH_MAX+1);
+	if (error)
+		goto exit_failure;
+
+	/* Open directory stream using wide-character name */
+	dirp->wdirp = _wopendir(wname);
+	if (!dirp->wdirp)
+		goto exit_failure;
+
+	/* Success */
+	return dirp;
+
+	/* Failure */
+exit_failure:
+	free(dirp);
+	return NULL;
+}
+
+/* Read next directory entry */
+static struct dirent *readdir(DIR *dirp)
+{
+	/*
+	 * Read directory entry to buffer.  We can safely ignore the return
+	 * value as entry will be set to NULL in case of error.
+	 */
+	struct dirent *entry;
+	(void) readdir_r(dirp, &dirp->ent, &entry);
+
+	/* Return pointer to statically allocated directory entry */
+	return entry;
+}
+
+/*
+ * Read next directory entry into called-allocated buffer.
+ *
+ * Returns zero on success.  If the end of directory stream is reached, then
+ * sets result to NULL and returns zero.
+ */
+static int readdir_r(
+	DIR *dirp, struct dirent *entry, struct dirent **result)
+{
+	/* Read next directory entry */
+	WIN32_FIND_DATAW *datap = dirent_next(dirp->wdirp);
+	if (!datap) {
+		/* No more directory entries */
+		*result = NULL;
+		return /*OK*/0;
+	}
+
+	/* Attempt to convert file name to multi-byte string */
+	size_t n;
+	int error = wcstombs_s(
+		&n, entry->d_name, PATH_MAX + 1,
+		datap->cFileName, PATH_MAX + 1);
+
+	/*
+	 * If the file name cannot be represented by a multi-byte string, then
+	 * attempt to use old 8+3 file name.  This allows the program to
+	 * access files although file names may seem unfamiliar to the user.
+	 *
+	 * Be ware that the code below cannot come up with a short file name
+	 * unless the file system provides one.  At least VirtualBox shared
+	 * folders fail to do this.
+	 */
+	if (error && datap->cAlternateFileName[0] != '\0') {
+		error = wcstombs_s(
+			&n, entry->d_name, PATH_MAX + 1,
+			datap->cAlternateFileName, PATH_MAX + 1);
+	}
+
+	if (!error) {
+		/* Length of file name excluding zero terminator */
+		entry->d_namlen = n - 1;
+
+		/* File attributes */
+		DWORD attr = datap->dwFileAttributes;
+		if ((attr & FILE_ATTRIBUTE_DEVICE) != 0)
+			entry->d_type = DT_CHR;
+		else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
+			entry->d_type = DT_DIR;
+		else
+			entry->d_type = DT_REG;
+
+		/* Reset dummy fields */
+		entry->d_ino = 0;
+		entry->d_off = 0;
+		entry->d_reclen = sizeof(struct dirent);
+	} else {
+		/*
+		 * Cannot convert file name to multi-byte string so construct
+		 * an erroneous directory entry and return that.  Note that
+		 * we cannot return NULL as that would stop the processing
+		 * of directory entries completely.
+		 */
+		entry->d_name[0] = '?';
+		entry->d_name[1] = '\0';
+		entry->d_namlen = 1;
+		entry->d_type = DT_UNKNOWN;
+		entry->d_ino = 0;
+		entry->d_off = -1;
+		entry->d_reclen = 0;
+	}
+
+	/* Return pointer to directory entry */
+	*result = entry;
+	return /*OK*/0;
+}
+
+/* Close directory stream */
+static int closedir(DIR *dirp)
+{
+	int ok;
+
+	if (!dirp)
+		goto exit_failure;
+
+	/* Close wide-character directory stream */
+	ok = _wclosedir(dirp->wdirp);
+	dirp->wdirp = NULL;
+
+	/* Release multi-byte character version */
+	free(dirp);
+	return ok;
+
+exit_failure:
+	/* Invalid directory stream */
+	dirent_set_errno(EBADF);
+	return /*failure*/-1;
+}
+
+/* Rewind directory stream to beginning */
+static void rewinddir(DIR* dirp)
+{
+	if (!dirp)
+		return;
+
+	/* Rewind wide-character string directory stream */
+	_wrewinddir(dirp->wdirp);
+}
+
+/* Scan directory for entries */
+static int scandir(
+	const char *dirname, struct dirent ***namelist,
+	int (*filter)(const struct dirent*),
+	int (*compare)(const struct dirent**, const struct dirent**))
+{
+	int result;
+
+	/* Open directory stream */
+	DIR *dir = opendir(dirname);
+	if (!dir) {
+		/* Cannot open directory */
+		return /*Error*/ -1;
+	}
+
+	/* Read directory entries to memory */
+	struct dirent *tmp = NULL;
+	struct dirent **files = NULL;
+	size_t size = 0;
+	size_t allocated = 0;
+	while (1) {
+		/* Allocate room for a temporary directory entry */
+		if (!tmp) {
+			tmp = (struct dirent*) malloc(sizeof(struct dirent));
+			if (!tmp)
+				goto exit_failure;
+		}
+
+		/* Read directory entry to temporary area */
+		struct dirent *entry;
+		if (readdir_r(dir, tmp, &entry) != /*OK*/0)
+			goto exit_failure;
+
+		/* Stop if we already read the last directory entry */
+		if (entry == NULL)
+			goto exit_success;
+
+		/* Determine whether to include the entry in results */
+		if (filter && !filter(tmp))
+			continue;
+
+		/* Enlarge pointer table to make room for another pointer */
+		if (size >= allocated) {
+			/* Compute number of entries in the new table */
+			size_t num_entries = size * 2 + 16;
+
+			/* Allocate new pointer table or enlarge existing */
+			void *p = realloc(files, sizeof(void*) * num_entries);
+			if (!p)
+				goto exit_failure;
+
+			/* Got the memory */
+			files = (dirent**) p;
+			allocated = num_entries;
+		}
+
+		/* Store the temporary entry to ptr table */
+		files[size++] = tmp;
+		tmp = NULL;
+	}
+
+exit_failure:
+	/* Release allocated file entries */
+	for (size_t i = 0; i < size; i++) {
+		free(files[i]);
+	}
+
+	/* Release the pointer table */
+	free(files);
+	files = NULL;
+
+	/* Exit with error code */
+	result = /*error*/ -1;
+	goto exit_status;
+
+exit_success:
+	/* Sort directory entries */
+	qsort(files, size, sizeof(void*),
+		(int (*) (const void*, const void*)) compare);
+
+	/* Pass pointer table to caller */
+	if (namelist)
+		*namelist = files;
+
+	/* Return the number of directory entries read */
+	result = (int) size;
+
+exit_status:
+	/* Release temporary directory entry, if we had one */
+	free(tmp);
+
+	/* Close directory stream */
+	closedir(dir);
+	return result;
+}
+
+/* Alphabetical sorting */
+static int alphasort(const struct dirent **a, const struct dirent **b)
+{
+	return strcoll((*a)->d_name, (*b)->d_name);
+}
+
+/* Sort versions */
+static int versionsort(const struct dirent **a, const struct dirent **b)
+{
+	return strverscmp((*a)->d_name, (*b)->d_name);
+}
+
+/* Compare strings */
+static int strverscmp(const char *a, const char *b)
+{
+	size_t i = 0;
+	size_t j;
+
+	/* Find first difference */
+	while (a[i] == b[i]) {
+		if (a[i] == '\0') {
+			/* No difference */
+			return 0;
+		}
+		++i;
+	}
+
+	/* Count backwards and find the leftmost digit */
+	j = i;
+	while (j > 0 && isdigit(a[j-1])) {
+		--j;
+	}
+
+	/* Determine mode of comparison */
+	if (a[j] == '0' || b[j] == '0') {
+		/* Find the next non-zero digit */
+		while (a[j] == '0' && a[j] == b[j]) {
+			j++;
+		}
+
+		/* String with more digits is smaller, e.g 002 < 01 */
+		if (isdigit(a[j])) {
+			if (!isdigit(b[j])) {
+				return -1;
+			}
+		} else if (isdigit(b[j])) {
+			return 1;
+		}
+	} else if (isdigit(a[j]) && isdigit(b[j])) {
+		/* Numeric comparison */
+		size_t k1 = j;
+		size_t k2 = j;
+
+		/* Compute number of digits in each string */
+		while (isdigit(a[k1])) {
+			k1++;
+		}
+		while (isdigit(b[k2])) {
+			k2++;
+		}
+
+		/* Number with more digits is bigger, e.g 999 < 1000 */
+		if (k1 < k2)
+			return -1;
+		else if (k1 > k2)
+			return 1;
+	}
+
+	/* Alphabetical comparison */
+	return (int) ((unsigned char) a[i]) - ((unsigned char) b[i]);
+}
+
+/* Convert multi-byte string to wide character string */
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static int dirent_mbstowcs_s(
+	size_t *pReturnValue, wchar_t *wcstr,
+	size_t sizeInWords, const char *mbstr, size_t count)
+{
+	/* Older Visual Studio or non-Microsoft compiler */
+	size_t n = mbstowcs(wcstr, mbstr, sizeInWords);
+	if (wcstr && n >= count)
+		return /*error*/ 1;
+
+	/* Zero-terminate output buffer */
+	if (wcstr && sizeInWords) {
+		if (n >= sizeInWords)
+			n = sizeInWords - 1;
+		wcstr[n] = 0;
+	}
+
+	/* Length of multi-byte string with zero terminator */
+	if (pReturnValue) {
+		*pReturnValue = n + 1;
+	}
+
+	/* Success */
+	return 0;
+}
+#endif
+
+/* Convert wide-character string to multi-byte string */
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static int dirent_wcstombs_s(
+	size_t *pReturnValue, char *mbstr,
+	size_t sizeInBytes, const wchar_t *wcstr, size_t count)
+{
+	/* Older Visual Studio or non-Microsoft compiler */
+	size_t n = wcstombs(mbstr, wcstr, sizeInBytes);
+	if (mbstr && n >= count)
+		return /*error*/1;
+
+	/* Zero-terminate output buffer */
+	if (mbstr && sizeInBytes) {
+		if (n >= sizeInBytes) {
+			n = sizeInBytes - 1;
+		}
+		mbstr[n] = '\0';
+	}
+
+	/* Length of resulting multi-bytes string WITH zero-terminator */
+	if (pReturnValue) {
+		*pReturnValue = n + 1;
+	}
+
+	/* Success */
+	return 0;
+}
+#endif
+
+/* Set errno variable */
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+static void dirent_set_errno(int error)
+{
+	/* Non-Microsoft compiler or older Microsoft compiler */
+	errno = error;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*DIRENT_H*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extern/libcompat/win/dlfcn/CMakeLists.txt	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,26 @@
+project(dl)
+add_library(
+	dl
+	STATIC
+	${dl_SOURCE_DIR}/dlfcn.h
+	${dl_SOURCE_DIR}/dlfcn.c
+)
+
+# Correctly export the location of installed includes in the target
+target_include_directories(
+	dl
+	PUBLIC
+		$<BUILD_INTERFACE:${dl_SOURCE_DIR}>
+		$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+
+if (CMAKE_C_COMPILER_ID MATCHES "MSVC")
+	target_compile_definitions(dl PRIVATE _CRT_SECURE_NO_WARNINGS)
+endif ()
+
+install(
+	TARGETS dl
+	RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+	LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+	ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extern/libcompat/win/dlfcn/dlfcn.c	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,478 @@
+/*
+ * dlfcn-win32
+ * Copyright (c) 2007 Ramiro Polla
+ * Copyright (c) 2015 Tiancheng "Timothy" Gu
+ * Copyright (c) 2019 Pali Rohár <pali.rohar@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+#ifdef _DEBUG
+#define _CRTDBG_MAP_ALLOC
+#include <stdlib.h>
+#include <crtdbg.h>
+#endif
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _MSC_VER
+/* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */
+#pragma intrinsic(_ReturnAddress)
+#else
+/* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */
+#ifndef _ReturnAddress
+#define _ReturnAddress() (__builtin_extract_return_addr(__builtin_return_address(0)))
+#endif
+#endif
+
+#ifdef SHARED
+#define DLFCN_WIN32_EXPORTS
+#endif
+#include "dlfcn.h"
+
+/* Note:
+ * MSDN says these functions are not thread-safe. We make no efforts to have
+ * any kind of thread safety.
+ */
+
+typedef struct local_object {
+    HMODULE hModule;
+    struct local_object *previous;
+    struct local_object *next;
+} local_object;
+
+static local_object first_object;
+
+/* These functions implement a double linked list for the local objects. */
+static local_object *local_search( HMODULE hModule )
+{
+    local_object *pobject;
+
+    if( hModule == NULL )
+        return NULL;
+
+    for( pobject = &first_object; pobject; pobject = pobject->next )
+        if( pobject->hModule == hModule )
+            return pobject;
+
+    return NULL;
+}
+
+static BOOL local_add( HMODULE hModule )
+{
+    local_object *pobject;
+    local_object *nobject;
+
+    if( hModule == NULL )
+        return TRUE;
+
+    pobject = local_search( hModule );
+
+    /* Do not add object again if it's already on the list */
+    if( pobject )
+        return TRUE;
+
+    for( pobject = &first_object; pobject->next; pobject = pobject->next );
+
+    nobject = (local_object*) malloc( sizeof( local_object ) );
+
+    if( !nobject )
+    {
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        return FALSE;
+    }
+
+    pobject->next = nobject;
+    nobject->next = NULL;
+    nobject->previous = pobject;
+    nobject->hModule = hModule;
+
+    return TRUE;
+}
+
+static void local_rem( HMODULE hModule )
+{
+    local_object *pobject;
+
+    if( hModule == NULL )
+        return;
+
+    pobject = local_search( hModule );
+
+    if( !pobject )
+        return;
+
+    if( pobject->next )
+        pobject->next->previous = pobject->previous;
+    if( pobject->previous )
+        pobject->previous->next = pobject->next;
+
+    free( pobject );
+}
+
+/* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one
+ * static buffer.
+ * MSDN says the buffer cannot be larger than 64K bytes, so we set it to
+ * the limit.
+ */
+static char error_buffer[65535];
+static BOOL error_occurred;
+
+static void save_err_str( const char *str )
+{
+    DWORD dwMessageId;
+    DWORD ret;
+    size_t pos, len;
+
+    dwMessageId = GetLastError( );
+
+    if( dwMessageId == 0 )
+        return;
+
+    len = strlen( str );
+    if( len > sizeof( error_buffer ) - 5 )
+        len = sizeof( error_buffer ) - 5;
+
+    /* Format error message to:
+     * "<argument to function that failed>": <Windows localized error message>
+      */
+    pos = 0;
+    error_buffer[pos++] = '"';
+    memcpy( error_buffer+pos, str, len );
+    pos += len;
+    error_buffer[pos++] = '"';
+    error_buffer[pos++] = ':';
+    error_buffer[pos++] = ' ';
+
+    ret = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwMessageId,
+        MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
+        error_buffer+pos, (DWORD) (sizeof(error_buffer)-pos), NULL );
+    pos += ret;
+
+    /* When FormatMessageA() fails it returns zero and does not touch buffer
+     * so add trailing null byte */
+    if( ret == 0 )
+        error_buffer[pos] = '\0';
+
+    if( pos > 1 )
+    {
+        /* POSIX says the string must not have trailing <newline> */
+        if( error_buffer[pos-2] == '\r' && error_buffer[pos-1] == '\n' )
+            error_buffer[pos-2] = '\0';
+    }
+
+    error_occurred = TRUE;
+}
+
+static void save_err_ptr_str( const void *ptr )
+{
+    char ptr_buf[19]; /* 0x<pointer> up to 64 bits. */
+
+#ifdef _MSC_VER
+/* Supress warning C4996: 'sprintf': This function or variable may be unsafe */
+#pragma warning( suppress: 4996 )
+#endif
+    sprintf( ptr_buf, "0x%p", ptr );
+
+    save_err_str( ptr_buf );
+}
+
+/* Load Psapi.dll at runtime, this avoids linking caveat */
+static BOOL MyEnumProcessModules( HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded )
+{
+    static BOOL (WINAPI *EnumProcessModulesPtr)(HANDLE, HMODULE *, DWORD, LPDWORD);
+    HMODULE psapi;
+
+    if( !EnumProcessModulesPtr )
+    {
+        psapi = LoadLibraryA( "Psapi.dll" );
+        if( psapi )
+            EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( psapi, "EnumProcessModules" );
+        if( !EnumProcessModulesPtr )
+            return 0;
+    }
+
+    return EnumProcessModulesPtr( hProcess, lphModule, cb, lpcbNeeded );
+}
+
+void *dlopen( const char *file, int mode )
+{
+    HMODULE hModule;
+    UINT uMode;
+
+    error_occurred = FALSE;
+
+    /* Do not let Windows display the critical-error-handler message box */
+    uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
+
+    if( file == 0 )
+    {
+        /* POSIX says that if the value of file is 0, a handle on a global
+         * symbol object must be provided. That object must be able to access
+         * all symbols from the original program file, and any objects loaded
+         * with the RTLD_GLOBAL flag.
+         * The return value from GetModuleHandle( ) allows us to retrieve
+         * symbols only from the original program file. EnumProcessModules() is
+         * used to access symbols from other libraries. For objects loaded
+         * with the RTLD_LOCAL flag, we create our own list later on. They are
+         * excluded from EnumProcessModules() iteration.
+         */
+        hModule = GetModuleHandle( NULL );
+
+        if( !hModule )
+            save_err_str( "(null)" );
+    }
+    else
+    {
+        HANDLE hCurrentProc;
+        DWORD dwProcModsBefore, dwProcModsAfter;
+        char lpFileName[MAX_PATH];
+        size_t i, len;
+
+        len = strlen( file );
+
+        if( len >= sizeof( lpFileName ) )
+        {
+            SetLastError( ERROR_FILENAME_EXCED_RANGE );
+            save_err_str( file );
+            hModule = NULL;
+        }
+        else
+        {
+            /* MSDN says backslashes *must* be used instead of forward slashes. */
+            for( i = 0; i < len; i++ )
+            {
+                if( file[i] == '/' )
+                    lpFileName[i] = '\\';
+                else
+                    lpFileName[i] = file[i];
+            }
+            lpFileName[len] = '\0';
+
+            hCurrentProc = GetCurrentProcess( );
+
+            if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsBefore ) == 0 )
+                dwProcModsBefore = 0;
+
+            /* POSIX says the search path is implementation-defined.
+             * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
+             * to UNIX's search paths (start with system folders instead of current
+             * folder).
+             */
+            hModule = LoadLibraryExA( lpFileName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
+
+            if( !hModule )
+            {
+                save_err_str( lpFileName );
+            }
+            else
+            {
+                if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsAfter ) == 0 )
+                    dwProcModsAfter = 0;
+
+                /* If the object was loaded with RTLD_LOCAL, add it to list of local
+                 * objects, so that its symbols cannot be retrieved even if the handle for
+                 * the original program file is passed. POSIX says that if the same
+                 * file is specified in multiple invocations, and any of them are
+                 * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the
+                 * symbols will remain global. If number of loaded modules was not
+                 * changed after calling LoadLibraryEx(), it means that library was
+                 * already loaded.
+                 */
+                if( (mode & RTLD_LOCAL) && dwProcModsBefore != dwProcModsAfter )
+                {
+                    if( !local_add( hModule ) )
+                    {
+                        save_err_str( lpFileName );
+                        FreeLibrary( hModule );
+                        hModule = NULL;
+                    }
+                }
+                else if( !(mode & RTLD_LOCAL) && dwProcModsBefore == dwProcModsAfter )
+                {
+                    local_rem( hModule );
+                }
+            }
+        }
+    }
+
+    /* Return to previous state of the error-mode bit flags. */
+    SetErrorMode( uMode );
+
+    return (void *) hModule;
+}
+
+int dlclose( void *handle )
+{
+    HMODULE hModule = (HMODULE) handle;
+    BOOL ret;
+
+    error_occurred = FALSE;
+
+    ret = FreeLibrary( hModule );
+
+    /* If the object was loaded with RTLD_LOCAL, remove it from list of local
+     * objects.
+     */
+    if( ret )
+        local_rem( hModule );
+    else
+        save_err_ptr_str( handle );
+
+    /* dlclose's return value in inverted in relation to FreeLibrary's. */
+    ret = !ret;
+
+    return (int) ret;
+}
+
+__declspec(noinline) /* Needed for _ReturnAddress() */
+void *dlsym( void *handle, const char *name )
+{
+    FARPROC symbol;
+    HMODULE hCaller;
+    HMODULE hModule;
+    HANDLE hCurrentProc;
+
+    error_occurred = FALSE;
+
+    symbol = NULL;
+    hCaller = NULL;
+    hModule = GetModuleHandle( NULL );
+    hCurrentProc = GetCurrentProcess( );
+
+    if( handle == RTLD_DEFAULT )
+    {
+        /* The symbol lookup happens in the normal global scope; that is,
+         * a search for a symbol using this handle would find the same
+         * definition as a direct use of this symbol in the program code.
+         * So use same lookup procedure as when filename is NULL.
+         */
+        handle = hModule;
+    }
+    else if( handle == RTLD_NEXT )
+    {
+        /* Specifies the next object after this one that defines name.
+         * This one refers to the object containing the invocation of dlsym().
+         * The next object is the one found upon the application of a load
+         * order symbol resolution algorithm. To get caller function of dlsym()
+         * use _ReturnAddress() intrinsic. To get HMODULE of caller function
+         * use standard GetModuleHandleExA() function.
+         */
+        if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) _ReturnAddress( ), &hCaller ) )
+            goto end;
+    }
+
+    if( handle != RTLD_NEXT )
+    {
+        symbol = GetProcAddress( (HMODULE) handle, name );
+
+        if( symbol != NULL )
+            goto end;
+    }
+
+    /* If the handle for the original program file is passed, also search
+     * in all globally loaded objects.
+     */
+
+    if( hModule == handle || handle == RTLD_NEXT )
+    {
+        HMODULE *modules;
+        DWORD cbNeeded;
+        DWORD dwSize;
+        size_t i;
+
+        /* GetModuleHandle( NULL ) only returns the current program file. So
+         * if we want to get ALL loaded module including those in linked DLLs,
+         * we have to use EnumProcessModules( ).
+         */
+        if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwSize ) != 0 )
+        {
+            modules = malloc( dwSize );
+            if( modules )
+            {
+                if( MyEnumProcessModules( hCurrentProc, modules, dwSize, &cbNeeded ) != 0 && dwSize == cbNeeded )
+                {
+                    for( i = 0; i < dwSize / sizeof( HMODULE ); i++ )
+                    {
+                        if( handle == RTLD_NEXT && hCaller )
+                        {
+                            /* Next modules can be used for RTLD_NEXT */
+                            if( hCaller == modules[i] )
+                                hCaller = NULL;
+                            continue;
+                        }
+                        if( local_search( modules[i] ) )
+                            continue;
+                        symbol = GetProcAddress( modules[i], name );
+                        if( symbol != NULL )
+                        {
+                            free( modules );
+                            goto end;
+                        }
+                    }
+
+                }
+                free( modules );
+            }
+            else
+            {
+                SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+                goto end;
+            }
+        }
+    }
+
+end:
+    if( symbol == NULL )
+    {
+        if( GetLastError() == 0 )
+            SetLastError( ERROR_PROC_NOT_FOUND );
+        save_err_str( name );
+    }
+
+    return *(void **) (&symbol);
+}
+
+char *dlerror( void )
+{
+    /* If this is the second consecutive call to dlerror, return NULL */
+    if( !error_occurred )
+        return NULL;
+
+    /* POSIX says that invoking dlerror( ) a second time, immediately following
+     * a prior invocation, shall result in NULL being returned.
+     */
+    error_occurred = FALSE;
+
+    return error_buffer;
+}
+
+#ifdef SHARED
+BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
+{
+    (void) hinstDLL;
+    (void) fdwReason;
+    (void) lpvReserved;
+    return TRUE;
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extern/libcompat/win/dlfcn/dlfcn.h	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,78 @@
+/*
+ * dlfcn-win32
+ * Copyright (c) 2007 Ramiro Polla
+ *
+ * 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.
+ */
+
+#ifndef DLFCN_H
+#define DLFCN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(DLFCN_BUILD_DLL)
+#   define DLFCN_EXPORT __declspec(dllexport)
+#else
+#   define DLFCN_EXPORT
+#endif
+
+/* Relocations are performed when the object is loaded. */
+#define RTLD_NOW    0
+
+/* Relocations are performed at an implementation-defined time.
+ * Windows API does not support lazy symbol resolving (when first reference
+ * to a given symbol occurs). So RTLD_LAZY implementation is same as RTLD_NOW.
+ */
+#define RTLD_LAZY   RTLD_NOW
+
+/* All symbols are available for relocation processing of other modules. */
+#define RTLD_GLOBAL (1 << 1)
+
+/* All symbols are not made available for relocation processing by other modules. */
+#define RTLD_LOCAL  (1 << 2)
+
+/* These two were added in The Open Group Base Specifications Issue 6.
+ * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant.
+ */
+
+/* The symbol lookup happens in the normal global scope. */
+#define RTLD_DEFAULT    ((void *)0)
+
+/* Specifies the next object after this one that defines name. */
+#define RTLD_NEXT       ((void *)-1)
+
+/* Open a symbol table handle. */
+DLFCN_EXPORT void *dlopen(const char *file, int mode);
+
+/* Close a symbol table handle. */
+DLFCN_EXPORT int dlclose(void *handle);
+
+/* Get the address of a symbol from a symbol table handle. */
+DLFCN_EXPORT void *dlsym(void *handle, const char *name);
+
+/* Get diagnostic information. */
+DLFCN_EXPORT char *dlerror(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DLFCN_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extern/libduktape/CMakeLists.txt	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,31 @@
+#
+# CMakeLists.txt -- CMake build system for duktape
+#
+# Copyright (c) 2016-2019 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or 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.
+#
+
+cmake_minimum_required(VERSION 3.0)
+project(libirccd-duktape)
+add_library(libirccd-duktape duk_config.h duktape.c duktape.h)
+target_include_directories(
+	libirccd-duktape
+	PUBLIC
+		$<BUILD_INTERFACE:${libirccd-duktape_SOURCE_DIR}>
+)
+if (BUILD_SHARED_LIBS)
+	target_compile_definitions(libirccd-duktape PUBLIC DUK_F_DLL_BUILD)
+endif ()
+
+set_target_properties(libirccd-duktape PROPERTIES PREFIX "")
--- a/extern/libduktape/Makefile	Sat Jan 16 09:45:33 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-#
-# Makefile -- POSIX makefile for irccd
-#
-# Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
-#
-# Permission to use, copy, modify, and/or 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 ../../config.mk
-
-all: libirccd-duktape.a
-
-duktape.o: duktape.c duktape.h duk_config.h
-	${CC} ${CFLAGS} -c $< -o $@
-
-libirccd-duktape.a: duktape.o
-	${CMD.ar}
-
-clean:
-	rm -f libirccd-duktape.a duktape.o
-
-.PHONY: clean
--- a/extern/libgreatest/CMakeLists.txt	Sat Jan 16 09:45:33 2021 +0100
+++ b/extern/libgreatest/CMakeLists.txt	Sat Jan 16 17:58:46 2021 +0100
@@ -17,11 +17,11 @@
 #
 
 cmake_minimum_required(VERSION 3.0)
-project(libgreatest)
-add_library(libgreatest INTERFACE)
+project(libirccd-greatest)
+add_library(libirccd-greatest INTERFACE)
 target_include_directories(
-	libgreatest
+	libirccd-greatest
 	INTERFACE
-		$<BUILD_INTERFACE:${libgreatest_SOURCE_DIR}>
+		$<BUILD_INTERFACE:${libirccd-greatest_SOURCE_DIR}>
 )
-target_sources(libgreatest INTERFACE ${libgreatest_SOURCE_DIR}/greatest.h)
+target_sources(libirccd-greatest INTERFACE ${libirccd-greatest_SOURCE_DIR}/greatest.h)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/irccd/CMakeLists.txt	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,21 @@
+#
+# CMakeLists.txt -- CMake build for irccd
+#
+# Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or 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.
+#
+
+project(irccd)
+add_executable(irccd main.c)
+target_link_libraries(irccd libirccd)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/irccdctl/CMakeLists.txt	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,21 @@
+#
+# CMakeLists.txt -- CMake build for irccd
+#
+# Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or 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.
+#
+
+project(irccdctl)
+add_executable(irccdctl main.c)
+target_link_libraries(irccdctl libirccd)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/CMakeLists.txt	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,100 @@
+#
+# CMakeLists.txt -- CMake build for irccd
+#
+# Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or 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.
+#
+
+project(libirccd)
+
+set(
+	SOURCES
+	CMakeLists.txt
+	irccd/channel.c
+	irccd/channel.h
+	irccd/config.h.in
+	irccd/dl-plugin.c
+	irccd/dl-plugin.h
+	irccd/event.h
+	irccd/irccd.c
+	irccd/irccd.h
+	irccd/limits.h
+	irccd/log.c
+	irccd/log.h
+	irccd/peer.c
+	irccd/peer.h
+	irccd/plugin.c
+	irccd/plugin.h
+	irccd/rule.c
+	irccd/rule.h
+	irccd/server.c
+	irccd/server.h
+	irccd/set.h
+	irccd/subst.c
+	irccd/subst.h
+	irccd/transport.c
+	irccd/transport.h
+	irccd/util.c
+	irccd/util.h
+)
+
+if (IRCCD_WITH_JS)
+	list(
+		APPEND SOURCES
+		irccd/unicode.c
+		irccd/unicode.h
+		irccd/js-plugin.c
+		irccd/js-plugin.h
+		irccd/jsapi-chrono.c
+		irccd/jsapi-chrono.h
+		irccd/jsapi-file.c
+		irccd/jsapi-file.h
+		irccd/jsapi-irccd.c
+		irccd/jsapi-irccd.h
+		irccd/jsapi-logger.c
+		irccd/jsapi-logger.h
+		irccd/jsapi-plugin.c
+		irccd/jsapi-plugin.h
+		irccd/jsapi-server.c
+		irccd/jsapi-server.h
+		irccd/jsapi-system.c
+		irccd/jsapi-system.h
+		irccd/jsapi-timer.c
+		irccd/jsapi-timer.h
+		irccd/jsapi-unicode.c
+		irccd/jsapi-unicode.h
+	)
+endif ()
+
+configure_file(
+	${libirccd_SOURCE_DIR}/irccd/config.h.in
+	${libirccd_BINARY_DIR}/irccd/config.h
+)
+
+add_library(libirccd ${SOURCES})
+set_target_properties(libirccd PROPERTIES PREFIX "")
+
+target_include_directories(
+	libirccd
+	PUBLIC
+		$<BUILD_INTERFACE:${libirccd_SOURCE_DIR}>
+		$<BUILD_INTERFACE:${libirccd_BINARY_DIR}>
+	PRIVATE
+		$<BUILD_INTERFACE:${libirccd_BINARY_DIR}>/irccd
+)
+target_link_libraries(libirccd libirccd-compat)
+
+if (IRCCD_WITH_JS)
+	target_link_libraries(libirccd libirccd-duktape)
+endif ()
--- a/lib/irccd/config.h.in	Sat Jan 16 09:45:33 2021 +0100
+++ b/lib/irccd/config.h.in	Sat Jan 16 17:58:46 2021 +0100
@@ -5,7 +5,7 @@
 #define IRCCD_VERSION_MINOR @IRCCD_VERSION_MINOR@
 #define IRCCD_VERSION_PATCH @IRCCD_VERSION_PATCH@
 
-#define IRCCD_WITH_JS
-#define IRCCD_WITH_SSL
+#cmakedefine IRCCD_WITH_JS
+#cmakedefine IRCCD_WITH_SSL
 
 #endif /* !IRCCD_CONFIG_H */
--- a/lib/irccd/irccd.c	Sat Jan 16 09:45:33 2021 +0100
+++ b/lib/irccd/irccd.c	Sat Jan 16 17:58:46 2021 +0100
@@ -50,12 +50,6 @@
 static int pipes[2];
 
 static int
-cmp_server(const struct irc_server *s1, const struct irc_server *s2)
-{
-	return strcmp(s1->name, s2->name);
-}
-
-static int
 cmp_plugin(const struct irc_plugin *p1, const struct irc_plugin *p2)
 {
 	return strcmp(p1->name, p2->name);
--- a/lib/irccd/js-plugin.c	Sat Jan 16 09:45:33 2021 +0100
+++ b/lib/irccd/js-plugin.c	Sat Jan 16 17:58:46 2021 +0100
@@ -453,7 +453,6 @@
 	assert(plg);
 	assert(path);
 
-	bool ret = false;
 	char *script = NULL;
 
 	if (!(script = eat(path))) {
--- a/lib/irccd/jsapi-file.c	Sat Jan 16 09:45:33 2021 +0100
+++ b/lib/irccd/jsapi-file.c	Sat Jan 16 17:58:46 2021 +0100
@@ -25,6 +25,7 @@
 
 #include <duktape.h>
 
+#include "compat.h"
 #include "jsapi-file.h"
 #include "jsapi-system.h"
 #include "util.h"
@@ -96,56 +97,56 @@
 {
 	duk_push_object(ctx);
 
-#if defined(IRCCD_HAVE_STAT_ST_ATIME)
-	duk_push_int(ctx, st.st_atime);
+#if defined(COMPAT_HAVE_STAT_ST_ATIME)
+	duk_push_int(ctx, st->st_atime);
 	duk_put_prop_string(ctx, -2, "atime");
 #endif
-#if defined(IRCCD_HAVE_STAT_ST_BLKSIZE)
-	duk_push_int(ctx, st.st_blksize);
+#if defined(COMPAT_HAVE_STAT_ST_BLKSIZE)
+	duk_push_int(ctx, st->st_blksize);
 	duk_put_prop_string(ctx, -2, "blksize");
 #endif
-#if defined(IRCCD_HAVE_STAT_ST_BLOCKS)
-	duk_push_int(ctx, st.st_blocks);
+#if defined(COMPAT_HAVE_STAT_ST_BLOCKS)
+	duk_push_int(ctx, st->st_blocks);
 	duk_put_prop_string(ctx, -2, "blocks");
 #endif
-#if defined(IRCCD_HAVE_STAT_ST_CTIME)
-	duk_push_int(ctx, st.st_ctime);
+#if defined(COMPAT_HAVE_STAT_ST_CTIME)
+	duk_push_int(ctx, st->st_ctime);
 	duk_put_prop_string(ctx, -2, "ctime");
 #endif
-#if defined(IRCCD_HAVE_STAT_ST_DEV)
-	duk_push_int(ctx, st.st_dev);
+#if defined(COMPAT_HAVE_STAT_ST_DEV)
+	duk_push_int(ctx, st->st_dev);
 	duk_put_prop_string(ctx, -2, "dev");
 #endif
-#if defined(IRCCD_HAVE_STAT_ST_GID)
-	duk_push_int(ctx, st.st_gid);
+#if defined(COMPAT_HAVE_STAT_ST_GID)
+	duk_push_int(ctx, st->st_gid);
 	duk_put_prop_string(ctx, -2, "gid");
 #endif
-#if defined(IRCCD_HAVE_STAT_ST_INO)
-	duk_push_int(ctx, st.st_ino);
+#if defined(COMPAT_HAVE_STAT_ST_INO)
+	duk_push_int(ctx, st->st_ino);
 	duk_put_prop_string(ctx, -2, "ino");
 #endif
-#if defined(IRCCD_HAVE_STAT_ST_MODE)
-	duk_push_int(ctx, st.st_mode);
+#if defined(COMPAT_HAVE_STAT_ST_MODE)
+	duk_push_int(ctx, st->st_mode);
 	duk_put_prop_string(ctx, -2, "mode");
 #endif
-#if defined(IRCCD_HAVE_STAT_ST_MTIME)
-	duk_push_int(ctx, st.st_mtime);
+#if defined(COMPAT_HAVE_STAT_ST_MTIME)
+	duk_push_int(ctx, st->st_mtime);
 	duk_put_prop_string(ctx, -2, "mtime");
 #endif
-#if defined(IRCCD_HAVE_STAT_ST_NLINK)
-	duk_push_int(ctx, st.st_nlink);
+#if defined(COMPAT_HAVE_STAT_ST_NLINK)
+	duk_push_int(ctx, st->st_nlink);
 	duk_put_prop_string(ctx, -2, "nlink");
 #endif
-#if defined(IRCCD_HAVE_STAT_ST_RDEV)
-	duk_push_int(ctx, st.st_rdev);
+#if defined(COMPAT_HAVE_STAT_ST_RDEV)
+	duk_push_int(ctx, st->st_rdev);
 	duk_put_prop_string(ctx, -2, "rdev");
 #endif
-#if defined(IRCCD_HAVE_STAT_ST_SIZE)
-	duk_push_int(ctx, st.st_size);
+#if defined(COMPAT_HAVE_STAT_ST_SIZE)
+	duk_push_int(ctx, st->st_size);
 	duk_put_prop_string(ctx, -2, "size");
 #endif
-#if defined(IRCCD_HAVE_STAT_ST_UID)
-	duk_push_int(ctx, st.st_uid);
+#if defined(COMPAT_HAVE_STAT_ST_UID)
+	duk_push_int(ctx, st->st_uid);
 	duk_put_prop_string(ctx, -2, "uid");
 #endif
 }
@@ -239,7 +240,7 @@
 		irc_jsapi_system_raise(ctx);
 	}
 
-	return amount == -1
+	return amount == -1U
 	    ? read_until_eof(ctx, file)
 	    : read_amount(ctx, file, amount);
 }
--- a/lib/irccd/jsapi-plugin.c	Sat Jan 16 09:45:33 2021 +0100
+++ b/lib/irccd/jsapi-plugin.c	Sat Jan 16 17:58:46 2021 +0100
@@ -172,18 +172,24 @@
 static duk_ret_t
 Plugin_load(duk_context *ctx)
 {
+	(void)ctx;
+
 	return 0;
 }
 
 static duk_ret_t
 Plugin_reload(duk_context *ctx)
 {
+	(void)ctx;
+
 	return 0;
 }
 
 static duk_ret_t
 Plugin_unload(duk_context *ctx)
 {
+	(void)ctx;
+
 	return 0;
 }
 
--- a/lib/irccd/jsapi-server.c	Sat Jan 16 09:45:33 2021 +0100
+++ b/lib/irccd/jsapi-server.c	Sat Jan 16 17:58:46 2021 +0100
@@ -136,6 +136,7 @@
 static duk_ret_t
 Server_prototype_isSelf(duk_context *ctx)
 {
+	(void)ctx;
 #if 0
 	return wrap(ctx, [] (auto ctx) {
 		return duk::push(ctx, self(ctx)->is_self(duk::require<std::string>(ctx, 0)));
@@ -340,9 +341,7 @@
 static duk_ret_t
 Server_prototype_whois(duk_context *ctx)
 {
-	struct irc_server *s = self(ctx);
-	const char *target = duk_require_string(ctx, 0);
-
+	(void)ctx;
 #if 0
 	if (target.empty())
 		throw server_error(server_error::invalid_nickname);
@@ -474,6 +473,8 @@
 	return 0;
 }
 
+#if 0
+
 static duk_ret_t
 Server_destructor(duk_context *ctx)
 {
@@ -490,11 +491,15 @@
 	return 0;
 }
 
+#endif
+
 static duk_ret_t
 Server_add(duk_context *ctx)
 {
 	struct irc_server *sv = require(ctx, 0);
 
+	irc_bot_add_server(sv);
+
 	return 0;
 }
 
--- a/lib/irccd/jsapi-system.c	Sat Jan 16 09:45:33 2021 +0100
+++ b/lib/irccd/jsapi-system.c	Sat Jan 16 17:58:46 2021 +0100
@@ -226,7 +226,7 @@
 	{ NULL,         NULL,           0 }
 };
 
-void
+void noreturn
 irc_jsapi_system_raise(duk_context *ctx)
 {
 	duk_get_global_string(ctx, "Irccd");
--- a/lib/irccd/jsapi-system.h	Sat Jan 16 09:45:33 2021 +0100
+++ b/lib/irccd/jsapi-system.h	Sat Jan 16 17:58:46 2021 +0100
@@ -19,9 +19,11 @@
 #ifndef IRCCD_JSAPI_SYSTEM_H
 #define IRCCD_JSAPI_SYSTEM_H
 
+#include <stdnoreturn.h>
+
 #include <duktape.h>
 
-void
+void noreturn
 irc_jsapi_system_raise(duk_context *);
 
 void
--- a/lib/irccd/peer.c	Sat Jan 16 09:45:33 2021 +0100
+++ b/lib/irccd/peer.c	Sat Jan 16 17:58:46 2021 +0100
@@ -244,6 +244,8 @@
 static int
 cmd_server_list(struct irc_peer *p, char *line)
 {
+	(void)line;
+
 	char out[IRC_BUF_MAX] = "OK ";
 
 	for (struct irc_server *s = irc.servers; s; s = s->next) {
@@ -300,6 +302,7 @@
 	const char *name;
 	int (*call)(struct irc_peer *, char *);
 } cmds[] = {
+	{ "SERVER-DISCONNECT",  cmd_server_disconnect   },
 	{ "SERVER-INVITE",      cmd_server_invite       },
 	{ "SERVER-JOIN",        cmd_server_join         },
 	{ "SERVER-KICK",        cmd_server_kick         },
@@ -389,7 +392,7 @@
 	if ((ns = send(p->fd, p->out, len, 0)) < 0)
 		return false;
 
-	if (ns >= len)
+	if ((size_t)ns >= len)
 		memset(p->out, 0, sizeof (p->out));
 	else
 		memmove(p->out, p->out + ns, sizeof (p->out) - ns);
@@ -443,8 +446,6 @@
 	assert(p);
 	assert(fd);
 
-	char buf[IRC_BUF_MAX];
-
 	if (fd->fd != p->fd)
 		return true;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/CMakeLists.txt	Sat Jan 16 17:58:46 2021 +0100
@@ -0,0 +1,36 @@
+#
+# CMakeLists.txt -- CMake build for irccd
+#
+# Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or 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.
+#
+
+project(tests)
+
+set(
+	TESTS
+	test-channel
+	#test-dl-plugin
+	test-log
+	test-rule
+	test-subst
+	test-util
+)
+
+foreach (t ${TESTS})
+	add_executable(${t} ${t}.c)
+	add_test(${t} ${t})
+	target_link_libraries(${t} libirccd libirccd-greatest)
+	set_target_properties(${t} PROPERTIES FOLDER "tests")
+endforeach ()