commit 014c66b699bcbd16589b535fc06203c4a9e5110c from: Omar Polo date: Sun Jun 25 08:40:55 2023 UTC initial import commit - /dev/null commit + 014c66b699bcbd16589b535fc06203c4a9e5110c blob - /dev/null blob + 18c2b7b613f81c234cadfc659cc40c7e5804d9bf (mode 644) --- /dev/null +++ Makefile @@ -0,0 +1,115 @@ +include config.mk + +# -- options -- + +PREFIX = /usr/local +SBINDIR = ${PREFIX}/sbin +MANDIR = ${PREFIX}/man +WWWDIR = /var/www/htdocs + +# -- build-related variables -- + +PROG = pkg_fcgi +VERSION = 0.1 +DISTNAME = ${PROG}-${VERSION} + +SRCS = pkg_fcgi.c fcgi.c log.c server.c xmalloc.c + +COBJS = ${COMPATS:.c=.o} +OBJS = ${SRCS:.c=.o} ${COBJS} + +MAN = ${PROG}.conf.5 ${PROG}.8 + +# -- public targets -- + +all: ${PROG} +.PHONY: all clean distclean install uninstall + +clean: + rm -f *.[do] compat/*.[do] tests/*.[do] ui.c ${PROG} + ${MAKE} -C template clean + +distclean: clean + rm -f config.h config.h.old config.mk config.log config.log.old + # ${MAKE} -C template distclean + +install: + mkdir -p ${DESTDIR}${MANDIR}/man5 + mkdir -p ${DESTDIR}${MANDIR}/man8 + mkdir -p ${DESTDIR}${SBINDIR} + mkdir -p ${DESTDIR}${WWWDIR} + ${INSTALL_MAN} galileo.conf.5 ${DESTDIR}${MANDIR}/man5/${PROG}.conf.5 + ${INSTALL_MAN} galileo.8 ${DESTDIR}${MANDIR}/man8/${PROG}.8 + ${INSTALL_PROGRAM} ${PROG} ${DESTDIR}${SBINDIR} + ${INSTALL_DATA} galileo.css ${DESTDIR}${WWWDIR} + +uninstall: + rm ${DESTDIR}${MANDIR}/man5/${PROG}.conf.5 + rm ${DESTDIR}${MANDIR}/man8/${PROG}.8 + rm ${DESTDIR}${SBINDIR}/${PROG} + rm ${DESTDIR}${WWWDIR}/galileo.css + +# -- internal build targets -- + +${PROG}: ${OBJS} + ${CC} -o $@ ${OBJS} ${LIBS} ${LDFLAGS} + +#ui.c: ui.tmpl +# ${MAKE} -C template +# ./template/template -o $@ ui.tmpl + +.c.o: + ${CC} ${CFLAGS} -c $< -o $@ + +# -- maintainer targets -- + +PRIVKEY = set-PRIVKEY +DISTFILES = CHANGES \ + Makefile \ + README \ + configure \ + fcgi.c \ + pkg_fcgi.8 \ + pkg_fcgi.c \ + pkg_fcgi.css \ + pkg_fcgi.h \ + log.c \ + log.h \ + server.c \ + ui.c \ + ui.tmpl \ + xmalloc.c \ + xmalloc.h \ + +.PHONY: release dist + +release: ${DISTNAME}.sha256.sig +dist: ${DISTNAME}.sha256 + +${DISTNAME}.sha256.sig: ${DISTNAME}.sha256 + signify -S -e -m ${DISTNAME}.sha256 -s ${PRIVKEY} + +${DISTNAME}.sha256: ${DISTNAME}.tar.gz + sha256 ${DISTNAME}.tar.gz > $@ + +${DISTNAME}.tar.gz: ${DISTFILES} + mkdir -p .dist/${DISTNAME}/ + ${INSTALL} -m 0644 ${DISTFILES} .dist/${DISTNAME} + ${MAKE} -C compat DESTDIR=${PWD}/.dist/${DISTNAME}/compat dist + ${MAKE} -C keys DESTDIR=${PWD}/.dist/${DISTNAME}/keys dist + ${MAKE} -C template DESTDIR=${PWD}/.dist/${DISTNAME}/template dist + ${MAKE} -C tests DESTDIR=${PWD}/.dist/${DISTNAME}/tests dist + cd .dist/${DISTNAME} && chmod 755 configure template/configure + cd .dist && tar czf ../$@ ${DISTNAME} + rm -rf .dist/ + +.PHONY: ${DISTNAME}.tar.gz + +# -- dependencies -- + +-include fcgi.d +-include log.d +-include pgk_fcgi.d +-include server.d +-include ui.d +-include xmalloc.d blob - /dev/null blob + 16f8ad634de95e716e525b0c0bb4fc6afd255f4a (mode 644) --- /dev/null +++ acme.guide @@ -0,0 +1,4 @@ +# win + +fn m { make $* && doas ./pkg_fcgi -dv -j1 } +fn g { gg -d all gemini://localhost/$"* } blob - /dev/null blob + 65aa7d9e4b3189c0b560fe906708dc0c142dac51 (mode 644) --- /dev/null +++ compat/Makefile @@ -0,0 +1,35 @@ +DISTFILES = Makefile \ + bufferevent_read_pressure_cb.c \ + err.c \ + event.h \ + event_asr_run.c \ + freezero.c \ + getdtablecount.c \ + getdtablesize.c \ + getprogname.c \ + pledge.c \ + reallocarray.c \ + recallocarray.c \ + setproctitle.c \ + setresgid.c \ + setresuid.c \ + stdlib.h \ + string.h \ + strlcat.c \ + strlcpy.c \ + strtonum.c \ + unistd.h \ + unveil.c \ + vasprintf.c + +all: + false + +dist: ${DISTFILES} + mkdir -p ${DESTDIR}/ + ${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/ + ${MAKE} -C imsg DESTDIR=${DESTDIR}/imsg dist + ${MAKE} -C sys DESTDIR=${DESTDIR}/sys dist + +.PHONY: all dist +include ../config.mk blob - /dev/null blob + cb22b6f52cad3fa05f5724b548c6977192539497 (mode 644) --- /dev/null +++ compat/err.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +static void vwarn_impl(const char*, va_list); +static void vwarnx_impl(const char*, va_list); + +static void +vwarn_impl(const char *fmt, va_list ap) +{ + fprintf(stderr, "%s: ", getprogname()); + vfprintf(stderr, fmt, ap); + fprintf(stderr, ": %s\n", strerror(errno)); +} + +static void +vwarnx_impl(const char *fmt, va_list ap) +{ + fprintf(stderr, "%s: ", getprogname()); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); +} + +void +err(int ret, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarn_impl(fmt, ap); + va_end(ap); + exit(ret); +} + +void +errx(int ret, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx_impl(fmt, ap); + va_end(ap); + exit(ret); +} + +void +warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarn_impl(fmt, ap); + va_end(ap); +} + +void +warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx_impl(fmt, ap); + va_end(ap); +} blob - /dev/null blob + c89fc77a8db4036318b9dde961b9a30e9212af89 (mode 644) --- /dev/null +++ compat/event.h @@ -0,0 +1,13 @@ +#include_next "event.h" + +#include "../config.h" + +#if !HAVE_EVENT_ASR_RUN +struct asr_query; +struct asr_result; +struct event_asr; + +struct event_asr *event_asr_run(struct asr_query *, + void (*cb)(struct asr_result *, void *), void *); +void event_asr_abort(struct event_asr *); +#endif blob - /dev/null blob + 6366503ab43a6917d9958656c6c7f5c381f3ef80 (mode 644) --- /dev/null +++ compat/event_asr_run.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2000-2004 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +/* + * Libevent glue for ASR. + */ +struct event_asr { + struct event ev; + struct asr_query *async; + void (*cb)(struct asr_result *, void *); + void *arg; +}; + +static void +event_asr_dispatch(int fd __attribute__((__unused__)), + short ev __attribute__((__unused__)), void *arg) +{ + struct event_asr *eva = arg; + struct asr_result ar; + struct timeval tv; + + event_del(&eva->ev); + + if (asr_run(eva->async, &ar)) { + eva->cb(&ar, eva->arg); + free(eva); + } else { + event_set(&eva->ev, ar.ar_fd, + ar.ar_cond == ASR_WANT_READ ? EV_READ : EV_WRITE, + event_asr_dispatch, eva); + tv.tv_sec = ar.ar_timeout / 1000; + tv.tv_usec = (ar.ar_timeout % 1000) * 1000; + event_add(&eva->ev, &tv); + } +} + +struct event_asr * +event_asr_run(struct asr_query *async, void (*cb)(struct asr_result *, void *), + void *arg) +{ + struct event_asr *eva; + struct timeval tv; + + eva = calloc(1, sizeof *eva); + if (eva == NULL) + return (NULL); + eva->async = async; + eva->cb = cb; + eva->arg = arg; + tv.tv_sec = 0; + tv.tv_usec = 0; + evtimer_set(&eva->ev, event_asr_dispatch, eva); + evtimer_add(&eva->ev, &tv); + return (eva); +} + +void +event_asr_abort(struct event_asr *eva) +{ + asr_abort(eva->async); + event_del(&eva->ev); + free(eva); +} blob - /dev/null blob + 0f83c59fffbfacccfe8af6db827e93f0bb9997ed (mode 644) --- /dev/null +++ compat/freezero.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +void +freezero(void *ptr, size_t len) +{ + if (ptr == NULL) + return; + + memset(ptr, 0, len); + free(ptr); +} blob - /dev/null blob + f6fe14a6f40035e080311b006af986bf1b9a3131 (mode 644) --- /dev/null +++ compat/getdtablecount.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * XXX: on linux it's possible to glob("/proc/$pid/fd/ *") to know the + * dtablecount. + */ + +#include "../config.h" + +int getdtablecount(void); + +int +getdtablecount(void) +{ + return 0; +} blob - /dev/null blob + c0a18609fdaa36944e1a72debf4fd70cf2a9e450 (mode 644) --- /dev/null +++ compat/getdtablesize.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +int getdtablesize(void); + +int +getdtablesize(void) +{ + return sysconf(_SC_OPEN_MAX); +} blob - /dev/null blob + 4aff31e40afec0da1d5879697964a3032efab9c5 (mode 644) --- /dev/null +++ compat/getprogname.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../config.h" + +const char *getprogname(void); + +#if HAVE___PROGNAME + +const char * +getprogname(void) +{ + extern const char *__progname; + + return __progname; +} + +#elif HAVE_GETEXECNAME + +const char * +getprogname(void) +{ + return getexecname(); +} + +#else + +const char * +getprogname(void) +{ + return "galileo"; +} + +#endif blob - /dev/null blob + e564f5e6f17add39aa7f62c26706c7f33020f747 (mode 644) --- /dev/null +++ compat/pledge.c @@ -0,0 +1,7 @@ +int pledge(const char *, const char *); + +int +pledge(const char *promises, const char *execpromises) +{ + return 0; +} blob - /dev/null blob + 43f0b69158ac2a290dec1f45075d7b6f6a18678b (mode 644) --- /dev/null +++ compat/reallocarray.c @@ -0,0 +1,38 @@ +/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} blob - /dev/null blob + 763552dc901ea6b8a38268ec0c436a8319de0182 (mode 644) --- /dev/null +++ compat/recallocarray.c @@ -0,0 +1,88 @@ +/* $OpenBSD: recallocarray.c,v 1.2 2021/03/18 11:16:58 claudio Exp $ */ +/* + * Copyright (c) 2008, 2017 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +/* + * Even though specified in POSIX, the PAGESIZE and PAGE_SIZE + * macros have very poor portability. Since we only use this + * to avoid free() overhead for small shrinking, simply pick + * an arbitrary number. + */ +#define getpagesize() (1UL << 12) + +void * +recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) +{ + size_t oldsize, newsize; + void *newptr; + + if (ptr == NULL) + return calloc(newnmemb, size); + + if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + newnmemb > 0 && SIZE_MAX / newnmemb < size) { + errno = ENOMEM; + return NULL; + } + newsize = newnmemb * size; + + if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + oldnmemb > 0 && SIZE_MAX / oldnmemb < size) { + errno = EINVAL; + return NULL; + } + oldsize = oldnmemb * size; + + /* + * Don't bother too much if we're shrinking just a bit, + * we do not shrink for series of small steps, oh well. + */ + if (newsize <= oldsize) { + size_t d = oldsize - newsize; + + if (d < oldsize / 2 && d < (size_t)getpagesize()) { + memset((char *)ptr + newsize, 0, d); + return ptr; + } + } + + newptr = malloc(newsize); + if (newptr == NULL) + return NULL; + + if (newsize > oldsize) { + memcpy(newptr, ptr, oldsize); + memset((char *)newptr + oldsize, 0, newsize - oldsize); + } else + memcpy(newptr, ptr, newsize); + + explicit_bzero(ptr, oldsize); + free(ptr); + + return newptr; +} blob - /dev/null blob + 4504b69fb16b99333a1028491adda8c8bb0de5a5 (mode 644) --- /dev/null +++ compat/setproctitle.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include + +#include "../config.h" + +void setproctitle(const char *, ...); + +#if HAVE_PR_SET_NAME + +#include + +void +setproctitle(const char *fmt, ...) +{ + char title[16], name[16], *cp; + va_list ap; + int used; + + va_start(ap, fmt); + vsnprintf(title, sizeof title, fmt, ap); + va_end(ap); + + used = snprintf(name, sizeof name, "%s: %s", getprogname(), title); + if (used >= (int)sizeof name) { + cp = strrchr(name, ' '); + if (cp != NULL) + *cp = '\0'; + } + prctl(PR_SET_NAME, name); +} +#else +void +setproctitle(const char *fmt, ...) +{ + (void)fmt; +} +#endif blob - /dev/null blob + ec625cd0abbff37a6be1700a1cd73e0f67bd2079 (mode 644) --- /dev/null +++ compat/setresgid.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2004, 2005 Darren Tucker (dtucker at zip com au). + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +int +setresgid(gid_t rgid, gid_t egid, gid_t sgid) +{ + /* this is the only configuration tested */ + + if (rgid != egid || egid != sgid) + return -1; + + if (setregid(rgid, egid) == -1) + return -1; + + return 0; +} blob - /dev/null blob + a033d99ab249e10501c2f99de973d2d3242757f8 (mode 644) --- /dev/null +++ compat/setresuid.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2004, 2005 Darren Tucker (dtucker at zip com au). + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +int +setresuid(uid_t ruid, uid_t euid, uid_t suid) +{ + uid_t ouid; + int ret = -1; + + /* Allow only the tested configuration. */ + + if (ruid != euid || euid != suid) { + errno = ENOSYS; + return -1; + } + ouid = getuid(); + + if ((ret = setreuid(euid, euid)) == -1) + return -1; + + /* + * When real, effective and saved uids are the same and we have + * changed uids, sanity check that we cannot restore the old uid. + */ + + if (ruid == euid && euid == suid && ouid != ruid && + setuid(ouid) != -1 && seteuid(ouid) != -1) { + errno = EINVAL; + return -1; + } + + /* + * Finally, check that the real and effective uids are what we + * expect. + */ + if (getuid() != ruid || geteuid() != euid) { + errno = EACCES; + return -1; + } + + return ret; +} blob - /dev/null blob + a9bac69f267bb7ef4314d165ddbf70dedcdc5393 (mode 644) --- /dev/null +++ compat/stdlib.h @@ -0,0 +1,31 @@ +#include_next "stdlib.h" + +#include "../config.h" + +#ifndef __dead +# define __dead __attribute__((noreturn)) +#endif + +#if !HAVE_GETPROGNAME +const char *getprogname(void); +#endif + +#if !HAVE_SETPROCTITLE +const char *setproctitle(const char *, ...); +#endif + +#if !HAVE_FREEZERO +void freezero(void *, size_t); +#endif + +#if !HAVE_REALLOCARRAY +void *reallocarray(void *, size_t, size_t); +#endif + +#if !HAVE_RECALLOCARRAY +void *recallocarray(void *, size_t, size_t, size_t); +#endif + +#if !HAVE_STRTONUM +long long strtonum(const char *, long long, long long, const char **); +#endif blob - /dev/null blob + 94e0bbb402c943b022f5b444df9d90f0081a8d66 (mode 644) --- /dev/null +++ compat/string.h @@ -0,0 +1,11 @@ +#include_next "string.h" + +#include "../config.h" + +#if !HAVE_STRLCPY +size_t strlcpy(char *, const char *, size_t); +#endif + +#if !HAVE_STRLCAT +size_t strlcat(char *, const char *, size_t); +#endif blob - /dev/null blob + 1c19062bf839da6e099ffca1dddcdb4070a44edf (mode 644) --- /dev/null +++ compat/strlcat.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1998, 2015 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t dsize) +{ + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') + dst++; + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) + return(dlen + strlen(src)); + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; + n--; + } + src++; + } + *dst = '\0'; + + return(dlen + (src - osrc)); /* count does not include NUL */ +} blob - /dev/null blob + 177ff7269492ec85c2196e31edc425107a958e4b (mode 644) --- /dev/null +++ compat/strlcpy.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1998, 2015 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return(src - osrc - 1); /* count does not include NUL */ +} blob - /dev/null blob + e1807693138c2fc6b662ac9d8859fa0f2431c360 (mode 644) --- /dev/null +++ compat/strtonum.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2004 Ted Unangst and Todd Miller + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#define INVALID 1 +#define TOOSMALL 2 +#define TOOLARGE 3 + +long long +strtonum(const char *numstr, long long minval, long long maxval, + const char **errstrp) +{ + long long ll = 0; + int error = 0; + char *ep; + struct errval { + const char *errstr; + int err; + } ev[4] = { + { NULL, 0 }, + { "invalid", EINVAL }, + { "too small", ERANGE }, + { "too large", ERANGE }, + }; + + ev[0].err = errno; + errno = 0; + if (minval > maxval) { + error = INVALID; + } else { + ll = strtoll(numstr, &ep, 10); + if (numstr == ep || *ep != '\0') + error = INVALID; + else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) + error = TOOSMALL; + else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) + error = TOOLARGE; + } + if (errstrp != NULL) + *errstrp = ev[error].errstr; + errno = ev[error].err; + if (error) + ll = 0; + + return (ll); +} blob - /dev/null blob + 360ee3a33402c4945921f43c2fecb15dc54c305a (mode 644) --- /dev/null +++ compat/sys/Makefile @@ -0,0 +1,13 @@ +DISTFILES = Makefile \ + queue.h \ + tree.h + +all: + false + +dist: ${DISTFILES} + mkdir -p ${DESTDIR}/ + ${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/ + +.PHONY: all dist +include ../../config.mk blob - /dev/null blob + bc1568be67482bf033c0da484f8bd9fd41426d03 (mode 644) --- /dev/null +++ compat/sys/queue.h @@ -0,0 +1,631 @@ +/* $OpenBSD: queue.h,v 1.46 2020/12/30 13:33:12 millert Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues and XOR simple queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * An XOR simple queue is used in the same way as a regular simple queue. + * The difference is that the head structure also includes a "cookie" that + * is XOR'd with the queue pointer (first, last or next) to generate the + * real pointer value. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) +#define _Q_INVALID ((void *)-1) +#define _Q_INVALIDATE(a) (a) = _Q_INVALID +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST(head); \ + (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ + _Q_INVALIDATE((elm)->field.sle_next); \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods. + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST(head); \ + (var) && ((tvar) = LIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SIMPLEQ_FIRST(head); \ + (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_CONCAT(head1, head2) do { \ + if (!SIMPLEQ_EMPTY((head2))) { \ + *(head1)->sqh_last = (head2)->sqh_first; \ + (head1)->sqh_last = (head2)->sqh_last; \ + SIMPLEQ_INIT((head2)); \ + } \ +} while (0) + +/* + * XOR Simple queue definitions. + */ +#define XSIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqx_first; /* first element */ \ + struct type **sqx_last; /* addr of last next element */ \ + unsigned long sqx_cookie; \ +} + +#define XSIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqx_next; /* next element */ \ +} + +/* + * XOR Simple queue access methods. + */ +#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ + (unsigned long)(ptr))) +#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) +#define XSIMPLEQ_END(head) NULL +#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) +#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) + + +#define XSIMPLEQ_FOREACH(var, head, field) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) != XSIMPLEQ_END(head); \ + (var) = XSIMPLEQ_NEXT(head, var, field)) + +#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ + (var) = (tvar)) + +/* + * XOR Simple queue functions. + */ +#define XSIMPLEQ_INIT(head) do { \ + arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqx_next = (head)->sqx_first) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ + *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + +#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ + (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ + (elm)->field.sqx_next)->field.sqx_next) \ + == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = \ + XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue access methods. + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_END(head) NULL +#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head)) +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = STAILQ_FIRST(head); \ + (var) != STAILQ_END(head); \ + (var) = STAILQ_NEXT(var, field)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST(head); \ + (var) && ((tvar) = STAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((elm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((elm), field) = (elm); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ +} while (0) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - offsetof(struct type, field)))) + +#endif /* !_SYS_QUEUE_H_ */ blob - /dev/null blob + fb56e32567bed50a2455b9b8fb87e809bba9acd0 (mode 644) --- /dev/null +++ compat/sys/tree.h @@ -0,0 +1,1012 @@ +/* $OpenBSD: tree.h,v 1.30 2020/10/10 18:03:41 otto Exp $ */ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +#include + +/* + * Local modifications: + * - dropped __unused + * - __inline -> inline + */ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root))) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)))\ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)))\ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field))) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field))); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ + (x) = (y)) + + +/* + * Copyright (c) 2016 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct rb_type { + int (*t_compare)(const void *, const void *); + void (*t_augment)(void *); + unsigned int t_offset; /* offset of rb_entry in type */ +}; + +struct rb_tree { + struct rb_entry *rbt_root; +}; + +struct rb_entry { + struct rb_entry *rbt_parent; + struct rb_entry *rbt_left; + struct rb_entry *rbt_right; + unsigned int rbt_color; +}; + +#define RBT_HEAD(_name, _type) \ +struct _name { \ + struct rb_tree rbh_root; \ +} + +#define RBT_ENTRY(_type) struct rb_entry + +static inline void +_rb_init(struct rb_tree *rbt) +{ + rbt->rbt_root = NULL; +} + +static inline int +_rb_empty(struct rb_tree *rbt) +{ + return (rbt->rbt_root == NULL); +} + +void *_rb_insert(const struct rb_type *, struct rb_tree *, void *); +void *_rb_remove(const struct rb_type *, struct rb_tree *, void *); +void *_rb_find(const struct rb_type *, struct rb_tree *, const void *); +void *_rb_nfind(const struct rb_type *, struct rb_tree *, const void *); +void *_rb_root(const struct rb_type *, struct rb_tree *); +void *_rb_min(const struct rb_type *, struct rb_tree *); +void *_rb_max(const struct rb_type *, struct rb_tree *); +void *_rb_next(const struct rb_type *, void *); +void *_rb_prev(const struct rb_type *, void *); +void *_rb_left(const struct rb_type *, void *); +void *_rb_right(const struct rb_type *, void *); +void *_rb_parent(const struct rb_type *, void *); +void _rb_set_left(const struct rb_type *, void *, void *); +void _rb_set_right(const struct rb_type *, void *, void *); +void _rb_set_parent(const struct rb_type *, void *, void *); +void _rb_poison(const struct rb_type *, void *, unsigned long); +int _rb_check(const struct rb_type *, void *, unsigned long); + +#define RBT_INITIALIZER(_head) { { NULL } } + +#define RBT_PROTOTYPE(_name, _type, _field, _cmp) \ +extern const struct rb_type *const _name##_RBT_TYPE; \ + \ +static inline void \ +_name##_RBT_INIT(struct _name *head) \ +{ \ + _rb_init(&head->rbh_root); \ +} \ + \ +static inline struct _type * \ +_name##_RBT_INSERT(struct _name *head, struct _type *elm) \ +{ \ + return _rb_insert(_name##_RBT_TYPE, &head->rbh_root, elm); \ +} \ + \ +static inline struct _type * \ +_name##_RBT_REMOVE(struct _name *head, struct _type *elm) \ +{ \ + return _rb_remove(_name##_RBT_TYPE, &head->rbh_root, elm); \ +} \ + \ +static inline struct _type * \ +_name##_RBT_FIND(struct _name *head, const struct _type *key) \ +{ \ + return _rb_find(_name##_RBT_TYPE, &head->rbh_root, key); \ +} \ + \ +static inline struct _type * \ +_name##_RBT_NFIND(struct _name *head, const struct _type *key) \ +{ \ + return _rb_nfind(_name##_RBT_TYPE, &head->rbh_root, key); \ +} \ + \ +static inline struct _type * \ +_name##_RBT_ROOT(struct _name *head) \ +{ \ + return _rb_root(_name##_RBT_TYPE, &head->rbh_root); \ +} \ + \ +static inline int \ +_name##_RBT_EMPTY(struct _name *head) \ +{ \ + return _rb_empty(&head->rbh_root); \ +} \ + \ +static inline struct _type * \ +_name##_RBT_MIN(struct _name *head) \ +{ \ + return _rb_min(_name##_RBT_TYPE, &head->rbh_root); \ +} \ + \ +static inline struct _type * \ +_name##_RBT_MAX(struct _name *head) \ +{ \ + return _rb_max(_name##_RBT_TYPE, &head->rbh_root); \ +} \ + \ +static inline struct _type * \ +_name##_RBT_NEXT(struct _type *elm) \ +{ \ + return _rb_next(_name##_RBT_TYPE, elm); \ +} \ + \ +static inline struct _type * \ +_name##_RBT_PREV(struct _type *elm) \ +{ \ + return _rb_prev(_name##_RBT_TYPE, elm); \ +} \ + \ +static inline struct _type * \ +_name##_RBT_LEFT(struct _type *elm) \ +{ \ + return _rb_left(_name##_RBT_TYPE, elm); \ +} \ + \ +static inline struct _type * \ +_name##_RBT_RIGHT(struct _type *elm) \ +{ \ + return _rb_right(_name##_RBT_TYPE, elm); \ +} \ + \ +static inline struct _type * \ +_name##_RBT_PARENT(struct _type *elm) \ +{ \ + return _rb_parent(_name##_RBT_TYPE, elm); \ +} \ + \ +static inline void \ +_name##_RBT_SET_LEFT(struct _type *elm, struct _type *left) \ +{ \ + _rb_set_left(_name##_RBT_TYPE, elm, left); \ +} \ + \ +static inline void \ +_name##_RBT_SET_RIGHT(struct _type *elm, struct _type *right) \ +{ \ + _rb_set_right(_name##_RBT_TYPE, elm, right); \ +} \ + \ +static inline void \ +_name##_RBT_SET_PARENT(struct _type *elm, struct _type *parent) \ +{ \ + _rb_set_parent(_name##_RBT_TYPE, elm, parent); \ +} \ + \ +static inline void \ +_name##_RBT_POISON(struct _type *elm, unsigned long poison) \ +{ \ + _rb_poison(_name##_RBT_TYPE, elm, poison); \ +} \ + \ +static inline int \ +_name##_RBT_CHECK(struct _type *elm, unsigned long poison) \ +{ \ + return _rb_check(_name##_RBT_TYPE, elm, poison); \ +} + +#define RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _aug) \ +static int \ +_name##_RBT_COMPARE(const void *lptr, const void *rptr) \ +{ \ + const struct _type *l = lptr, *r = rptr; \ + return _cmp(l, r); \ +} \ +static const struct rb_type _name##_RBT_INFO = { \ + _name##_RBT_COMPARE, \ + _aug, \ + offsetof(struct _type, _field), \ +}; \ +const struct rb_type *const _name##_RBT_TYPE = &_name##_RBT_INFO + +#define RBT_GENERATE_AUGMENT(_name, _type, _field, _cmp, _aug) \ +static void \ +_name##_RBT_AUGMENT(void *ptr) \ +{ \ + struct _type *p = ptr; \ + return _aug(p); \ +} \ +RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _name##_RBT_AUGMENT) + +#define RBT_GENERATE(_name, _type, _field, _cmp) \ + RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, NULL) + +#define RBT_INIT(_name, _head) _name##_RBT_INIT(_head) +#define RBT_INSERT(_name, _head, _elm) _name##_RBT_INSERT(_head, _elm) +#define RBT_REMOVE(_name, _head, _elm) _name##_RBT_REMOVE(_head, _elm) +#define RBT_FIND(_name, _head, _key) _name##_RBT_FIND(_head, _key) +#define RBT_NFIND(_name, _head, _key) _name##_RBT_NFIND(_head, _key) +#define RBT_ROOT(_name, _head) _name##_RBT_ROOT(_head) +#define RBT_EMPTY(_name, _head) _name##_RBT_EMPTY(_head) +#define RBT_MIN(_name, _head) _name##_RBT_MIN(_head) +#define RBT_MAX(_name, _head) _name##_RBT_MAX(_head) +#define RBT_NEXT(_name, _elm) _name##_RBT_NEXT(_elm) +#define RBT_PREV(_name, _elm) _name##_RBT_PREV(_elm) +#define RBT_LEFT(_name, _elm) _name##_RBT_LEFT(_elm) +#define RBT_RIGHT(_name, _elm) _name##_RBT_RIGHT(_elm) +#define RBT_PARENT(_name, _elm) _name##_RBT_PARENT(_elm) +#define RBT_SET_LEFT(_name, _elm, _l) _name##_RBT_SET_LEFT(_elm, _l) +#define RBT_SET_RIGHT(_name, _elm, _r) _name##_RBT_SET_RIGHT(_elm, _r) +#define RBT_SET_PARENT(_name, _elm, _p) _name##_RBT_SET_PARENT(_elm, _p) +#define RBT_POISON(_name, _elm, _p) _name##_RBT_POISON(_elm, _p) +#define RBT_CHECK(_name, _elm, _p) _name##_RBT_CHECK(_elm, _p) + +#define RBT_FOREACH(_e, _name, _head) \ + for ((_e) = RBT_MIN(_name, (_head)); \ + (_e) != NULL; \ + (_e) = RBT_NEXT(_name, (_e))) + +#define RBT_FOREACH_SAFE(_e, _name, _head, _n) \ + for ((_e) = RBT_MIN(_name, (_head)); \ + (_e) != NULL && ((_n) = RBT_NEXT(_name, (_e)), 1); \ + (_e) = (_n)) + +#define RBT_FOREACH_REVERSE(_e, _name, _head) \ + for ((_e) = RBT_MAX(_name, (_head)); \ + (_e) != NULL; \ + (_e) = RBT_PREV(_name, (_e))) + +#define RBT_FOREACH_REVERSE_SAFE(_e, _name, _head, _n) \ + for ((_e) = RBT_MAX(_name, (_head)); \ + (_e) != NULL && ((_n) = RBT_PREV(_name, (_e)), 1); \ + (_e) = (_n)) + +#endif /* _SYS_TREE_H_ */ blob - /dev/null blob + 6452b2bf184aa60b92f3c0f69f6f9e3e50b52dcc (mode 644) --- /dev/null +++ compat/unistd.h @@ -0,0 +1,27 @@ +#include_next "unistd.h" + +#include "../config.h" + +#if !HAVE_PLEGDE +int pledge(const char *, const char *); +#endif + +#if !HAVE_UNVEIL +int unveil(const char *, const char *); +#endif + +#if !HAVE_GETDTABLECOUNT +int getdtablecount(void); +#endif + +#if !HAVE_GETDTABLESIZE +int getdtablesize(void); +#endif + +#if !HAVE_SETRESGID +int setresgid(gid_t, gid_t, gid_t); +#endif + +#if !HAVE_SETRESUID +int setresuid(uid_t, uid_t, uid_t); +#endif blob - /dev/null blob + a6fd562ca3f2e3974dba47e0b5029eabffd51e9e (mode 644) --- /dev/null +++ compat/unveil.c @@ -0,0 +1,7 @@ +int unveil(const char *, const char *); + +int +unveil(const char *path, const char *permissions) +{ + return 0; +} blob - /dev/null blob + 8d012e94124300b10529531bbfb0ea93cf36eca3 (mode 644) --- /dev/null +++ compat/vasprintf.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015 Ingo Schwarze + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * This fallback implementation is not efficient: + * It does the formatting twice. + * Short of fiddling with the unknown internals of the system's + * printf(3) or completely reimplementing printf(3), i can't think + * of another portable solution. + */ + +#include +#include +#include + +int +vasprintf(char **ret, const char *format, va_list ap) +{ + char buf[2]; + va_list ap2; + int sz; + + va_copy(ap2, ap); + sz = vsnprintf(buf, sizeof(buf), format, ap2); + va_end(ap2); + + if (sz != -1 && (*ret = malloc(sz + 1)) != NULL) { + if (vsnprintf(*ret, sz + 1, format, ap) == sz) + return sz; + free(*ret); + } + *ret = NULL; + return -1; +} blob - /dev/null blob + 2cef3cda79960358a8bf5b2f9be655931973aa2b (mode 755) --- /dev/null +++ configure @@ -0,0 +1,402 @@ +#!/bin/sh +# +# Copyright (c) 2014, 2015, 2016 Ingo Schwarze +# Copyright (c) 2017, 2018 Kristaps Dzonsons +# Copyright (c) 2022 Omar Polo +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +set -e + +RELEASE="${RELEASE:-no}" + +usage() +{ + echo "usage: $0 [--help] [--prefix=prefix] [OPTION=VALUE...]" >&2 + exit 1 +} + +if command -v yacc 2>/dev/null >&2; then + YACC=yacc +elif command -v bison 2>/dev/null >&2; then + YACC=bison +else + # assume yacc by default. Make will fail building parse.y if + # not from a release tarball, but at least it'll have a decent + # error message. + YACC=yacc +fi + +if command -v pkg-config 2>/dev/null >&2; then + pkgconfig=pkg-config +else + pkgconfig= +fi + +db= +user= +sock= +while [ $# -gt 0 ]; do + key="${1%%=*}" + val="${1#*=}" + + if [ "$key" = --help ]; then + usage + fi + + if [ "$key" = --prefix ]; then + key=PREFIX + if [ "$1" = --prefix ]; then # no =, look at next arg + if !shift 2>&1 >/dev/null; then + echo "$0: missing value for --prefix" >&2 + exit 1 + fi + val="$1" + fi + fi + + if [ "$1" = "$key" ]; then + echo "$0: invalid key-value: $1" >&2 + exit 1 + fi + + case "$key" in + CC) + CC="$val" ;; + CFLAGS) + CFLAGS="$val" ;; + DB) + db="$val" ;; + LDADD) + LDADD="$val" ;; + LDADD_LIBEVENT) + LDADD_LIBEVENT="$val" ;; + LDADD_LIBSQLITE3) + LDADD_LIBSQLITE3="$val" ;; + LDADD_LIBSOCKET) + LDADD_LIBSOCKET="$val" ;; + PKG_CONFIG) + pkgconfig="$val" ;; + SOCK) + sock="$sock" ;; + USER) + user="$val" ;; + YACC) + YACC="$val" ;; + esac + + shift +done + +CDIAGFLAGS= +CDIAGFLAGS="${CDIAGFLAGS} -Wall -Wextra -Wpointer-arith -Wuninitialized" +CDIAGFLAGS="${CDIAGFLAGS} -Wstrict-prototypes -Wmissing-prototypes -Wunused" +CDIAGFLAGS="${CDIAGFLAGS} -Wsign-compare -Wshadow -Wno-unused-parameter" +CDIAGFLAGS="${CDIAGFLAGS} -Wno-missing-field-initializers" +CDIAGFLAGS="${CDIAGFLAGS} -Wno-pointer-sign" + +# don't ship releases with -Werror +test "$RELEASE" = no && CDIAGFLAGS="${CDIAGFLAGS} -Werror" + +CFLAGS="${CFLAGS:--O2 -pipe} ${CDIAGFLAGS}" +CC="${CC:-cc}" +LIBS="${LIBS:-}" +LDFLAGS="${LDFLAGS:-}" + +HOSTCC="${HOSTCC:-${CC}}" +HOSTCFLAGS="${HOSTCFLAGS:-${CFLAGS}}" + +# echo "running configure for \`template':" >&2 +# (cd ./template && ./configure CC="$HOSTCC" CFLAGS="$HOSTCFLAGS" YACC="$YACC") +# echo "returning to the configure for \`pkg_fcgi':" >&2 + +CFLAGS="${CFLAGS} -I. -Itemplate" +test -n "$db" && CFLAGS="${CFLAGS} -DPKG_FCGI_DB=\"\\\"$conf\\\"\"" +test -n "$user" && CFLAGS="${CFLAGS} -DPKG_FCGI_USER=\"\\\"$user\\\"\"" +test -n "$sock" && CFLAGS="${CFLAGS} -DPKG_FCGI_SOCK=\"\\\"$conf\\\"\"" + +[ -w config.log ] && mv config.log config.log.old + +exec 3> config.log +echo "config.log: writing..." + +COMPATS= + +HAVE_ACCEPT4= +HAVE_ASR_RUN= +HAVE_ERR= +HAVE_EVENT_ASR_RUN= +HAVE_FREEZERO= +HAVE_GETDTABLECOUNT= +HAVE_GETDTABLESIZE= +HAVE_GETEXECNAME= +HAVE_GETPROGNAME= +HAVE_LIBEVENT= +HAVE_LIBSQLITE3= +HAVE_PLEDGE= +HAVE_REALLOCARRAY= +HAVE_RECALLOCARRAY= +HAVE_SETGROUPS= +HAVE_SETPROCTITLE= +HAVE_SETRESGID= +HAVE_SETRESUID= +HAVE_STRLCAT= +HAVE_STRLCPY= +HAVE_STRTONUM= +HAVE_SYS_QUEUE= +HAVE_SYS_TREE= +HAVE_UNVEIL= +HAVE_VASPRINTF= +HAVE_WAIT_ANY= +HAVE___PROGNAME= + +# singletest name var extra-cflags extra-libs msg +singletest() { + msg="$5" + if [ -z "$msg" ]; then + if [ -n "$3" -a "$4" ]; then + msg=" ($3 $4)" + elif [ -n "$3" ]; then + msg=" ($3)" + elif [ -n "$4" ]; then + msg=" ($4)" + fi + elif [ "$msg" = no ]; then + msg="" + fi + + cat >&3 <&3 2>&3; then + rm -f test-${1} test-${1}.d + + echo "${1}: $CC$msg succeeded" >&3 + echo "${1}$msg: yes" + echo >&3 + + return 0 + fi + + echo "${1}: $CC$msg failed with $?" >&3 + echo "${1}$msg: no" + echo >&3 + + return 1 +} + +# deptest name var +deptest() { + if singletest "$1" "$2" "${CFLAGS}" "${LIBS}" no; then + eval HAVE_${2}=1 + return 0 + fi + + if [ -f compat/${1}.c ]; then + COMPATS="compat/${1}.c $COMPATS" + fi + + eval HAVE_${2}=0 + return 1 +} + +# runtest name var extra-cflags extra-libs pkgconfig-name +runtest() { + if singletest "$1" "$2" "" ""; then + eval HAVE_${2}=1 + return 0 + fi + + if [ -n "$3" -o -n "$4" ]; then + echo "retrying with ${3+$3 }$4" >&3 + if singletest "$1" "$2" "$3" "$4"; then + if [ -n "$3" ]; then + CFLAGS="$CFLAGS $3" + fi + if [ -n "${4}" ]; then + LIBS="$LIBS $4" + fi + eval HAVE_${2}=1 + return 0 + fi + fi + + if [ -n "$5" -a -n "$pkgconfig" ]; then + if $pkgconfig "$5"; then + cflags="$($pkgconfig --cflags "$5")" + ldflags="$($pkgconfig --libs "$5")" + echo "retrying with pkg-config" >&3 + if singletest "$1" "$2" "$3" "$cflags" "$ldflags"; then + CFLAGS="$CFLAGS $cflags" + LIBS="$LIBS $ldflags" + eval HAVE_${2}=1 + return 0 + fi + fi + fi + + if [ -f compat/${1}.c ]; then + COMPATS="compat/${1}.c $COMPATS" + fi + + eval HAVE_${2}=0 + return 1 +} + +if singletest MMD _MMD -MMD >/dev/null; then + CFLAGS="${CFLAGS} -MMD" + echo "adding -MMD to CFLAGS" >&2 + echo "adding -MMD to CFLAGS" >&3 +fi + +if ! singletest WAIT_ANY WAIT_ANY; then + CFLAGS="${CFLAGS} -DWAIT_ANY=-1" +fi + +runtest accept4 ACCEPT4 -D_GNU_SOURCE || true +runtest asr_run ASR_RUN "" "-lasr" || true +runtest err ERR || true +runtest freezero FREEZERO || true +runtest getdtablecount GETDTABLECOUNT || true +runtest getdtablesize GETDTABLESIZE || true +runtest getexecname GETEXECNAME || true +runtest getprogname GETPROGNAME || true +runtest libevent LIBEVENT "" "-levent" libevent_core || true +runtest libsqlite3 LIBSQLITE3 "-I/usr/local/include" "-L/usr/local/lib -lsqlite3" sqlite3 || true +runtest pledge PLEDGE || true +runtest reallocarray REALLOCARRAY -D_OPENBSD_SOURCE || true +runtest recallocarray RECALLOCARRAY -D_OPENBSD_SOURCE || true +runtest setgroups SETGROUPS -D_BSD_SOURCE || true +runtest setproctitle SETPROCTITLE || true +runtest setresgid SETRESGID -D_GNU_SOURCE || true +runtest setresuid SETRESUID -D_GNU_SOURCE || true +runtest strlcat STRLCAT || true +runtest strlcpy STRLCPY || true +runtest strtonum STRTONUM || true +runtest sys_queue SYS_QUEUE || true +runtest sys_tree SYS_TREE || true +runtest unveil UNVEIL || true +runtest vasprintf VASPRINTF -D_GNU_SOURCE || true +runtest __progname __PROGNAME || true + +deptest event_asr_run EVENT_ASR_RUN || true + +# mandatory things: + +if [ "${HAVE_ACCEPT4}" -eq 0 ]; then + echo "Fatal: missing accept4(2)" >&2 + echo "Fatal: missing accept4(2)" >&3 + exit 1 +fi + +if [ "${HAVE_ASR_RUN}" -eq 0 ]; then + echo "Fatal: missing libasr" >&2 + echo "Fatal: missing libasr" >&3 + exit 1 +fi + +if [ "${HAVE_LIBEVENT}" -eq 0 ]; then + echo "Fatal: missing libevent" >&2 + echo "Fatal: missing libevent" >&3 + exit 1 +fi + +if [ "${HAVE_LIBSQLITE3}" -eq 0 ]; then + echo "Fatal: missing libsqlite3" >&2 + echo "Fatal: missing libsqlite3" >&3 + exit 1 +fi + +if [ "${HAVE_SETGROUPS}" -eq 0 ]; then + echo "Fatal: missing setgroups(2)" >&2 + echo "Fatal: missing setgroups(2)" >&3 + exit 1 +fi + +# things we can provide by ourselves: + +if [ "${HAVE_SYS_QUEUE}" -eq 0 -o "${HAVE_SYS_TREE}" -eq 0 ]; then + CFLAGS="-I compat/sys ${CFLAGS}" +fi + +if [ -n "${COMPATS}" ]; then + CFLAGS="-I compat/ ${CFLAGS}" +fi + +exec > config.h +echo "config.h: writing..." >&2 + +cat < config.mk +echo "config.mk: writing..." >&2 + +cat <&2 + +echo >&2 +echo "Now run \`make' to compile." >&2 +echo >&2 blob - /dev/null blob + 166777374961a77ac558fb8d7c3be442a6e1333c (mode 644) --- /dev/null +++ fcgi.c @@ -0,0 +1,788 @@ +/* + * Copyright (c) 2022 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "pkg.h" +#include "tmpl.h" + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +struct fcgi_header { + unsigned char version; + unsigned char type; + unsigned char req_id1; + unsigned char req_id0; + unsigned char content_len1; + unsigned char content_len0; + unsigned char padding; + unsigned char reserved; +} __attribute__((packed)); + +/* + * number of bytes in a FCGI_HEADER. Future version of the protocol + * will not reduce this number. + */ +#define FCGI_HEADER_LEN 8 + +/* + * values for the version component + */ +#define FCGI_VERSION_1 1 + +/* + * values for the type component + */ +#define FCGI_BEGIN_REQUEST 1 +#define FCGI_ABORT_REQUEST 2 +#define FCGI_END_REQUEST 3 +#define FCGI_PARAMS 4 +#define FCGI_STDIN 5 +#define FCGI_STDOUT 6 +#define FCGI_STDERR 7 +#define FCGI_DATA 8 +#define FCGI_GET_VALUES 9 +#define FCGI_GET_VALUES_RESULT 10 +#define FCGI_UNKNOWN_TYPE 11 +#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) + +struct fcgi_begin_req { + unsigned char role1; + unsigned char role0; + unsigned char flags; + unsigned char reserved[5]; +}; + +struct fcgi_begin_req_record { + struct fcgi_header header; + struct fcgi_begin_req body; +}; + +/* + * mask for flags; + */ +#define FCGI_KEEP_CONN 1 + +/* + * values for the role + */ +#define FCGI_RESPONDER 1 +#define FCGI_AUTHORIZER 2 +#define FCGI_FILTER 3 + +struct fcgi_end_req_body { + unsigned char app_status3; + unsigned char app_status2; + unsigned char app_status1; + unsigned char app_status0; + unsigned char proto_status; + unsigned char reserved[3]; +}; + +/* + * values for proto_status + */ +#define FCGI_REQUEST_COMPLETE 0 +#define FCGI_CANT_MPX_CONN 1 +#define FCGI_OVERLOADED 2 +#define FCGI_UNKNOWN_ROLE 3 + +/* + * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT + * records. + */ +#define FCGI_MAX_CONNS "FCGI_MAX_CONNS" +#define FCGI_MAX_REQS "FCGI_MAX_REQS" +#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS" + +#define CAT(f0, f1) ((f0) + ((f1) << 8)) + +enum { + FCGI_RECORD_HEADER, + FCGI_RECORD_BODY, +}; + +volatile int fcgi_inflight; +int32_t fcgi_id; + +int accept_reserve(int, struct sockaddr *, socklen_t *, int, + volatile int *); + +static int +fcgi_send_end_req(struct fcgi *fcgi, int id, int as, int ps) +{ + struct bufferevent *bev = fcgi->fcg_bev; + struct fcgi_header hdr; + struct fcgi_end_req_body end; + + memset(&hdr, 0, sizeof(hdr)); + memset(&end, 0, sizeof(end)); + + hdr.version = FCGI_VERSION_1; + hdr.type = FCGI_END_REQUEST; + hdr.req_id0 = (id & 0xFF); + hdr.req_id1 = (id >> 8); + hdr.content_len0 = sizeof(end); + + end.app_status0 = (unsigned char)as; + end.proto_status = (unsigned char)ps; + + if (bufferevent_write(bev, &hdr, sizeof(hdr)) == -1) + return (-1); + if (bufferevent_write(bev, &end, sizeof(end)) == -1) + return (-1); + return (0); +} + +static int +end_request(struct client *clt, int status, int proto_status) +{ + struct fcgi *fcgi = clt->clt_fcgi; + int r; + + if (clt_flush(clt) == -1) + return (-1); + + r = fcgi_send_end_req(fcgi, clt->clt_id, status, + proto_status); + if (r == -1) { + fcgi_error(fcgi->fcg_bev, EV_WRITE, fcgi); + return (-1); + } + + SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt); + server_client_free(clt); + + if (!fcgi->fcg_keep_conn) + fcgi->fcg_done = 1; + + return (0); +} + +int +fcgi_end_request(struct client *clt, int status) +{ + return (end_request(clt, status, FCGI_REQUEST_COMPLETE)); +} + +int +fcgi_abort_request(struct client *clt) +{ + return (end_request(clt, 1, FCGI_OVERLOADED)); +} + +static void +fcgi_inflight_dec(const char *why) +{ + fcgi_inflight--; + log_debug("%s: fcgi inflight decremented, now %d, %s", + __func__, fcgi_inflight, why); +} + +void +fcgi_accept(int fd, short event, void *arg) +{ + struct env *env = arg; + struct fcgi *fcgi = NULL; + socklen_t slen; + struct sockaddr_storage ss; + int s = -1; + + event_add(&env->env_pausev, NULL); + if ((event & EV_TIMEOUT)) + return; + + slen = sizeof(ss); + if ((s = accept_reserve(env->env_sockfd, (struct sockaddr *)&ss, + &slen, FD_RESERVE, &fcgi_inflight)) == -1) { + /* + * Pause accept if we are out of file descriptors, or + * libevent will haunt us here too. + */ + if (errno == ENFILE || errno == EMFILE) { + struct timeval evtpause = { 1, 0 }; + + event_del(&env->env_sockev); + evtimer_add(&env->env_pausev, &evtpause); + log_debug("%s: deferring connections", __func__); + } + return; + } + + if ((fcgi = calloc(1, sizeof(*fcgi))) == NULL) + goto err; + + fcgi->fcg_id = ++fcgi_id; + fcgi->fcg_s = s; + fcgi->fcg_env = env; + fcgi->fcg_want = FCGI_RECORD_HEADER; + fcgi->fcg_toread = sizeof(struct fcgi_header); + SPLAY_INIT(&fcgi->fcg_clients); + + /* assume it's enabled until we get a FCGI_BEGIN_REQUEST */ + fcgi->fcg_keep_conn = 1; + + fcgi->fcg_bev = bufferevent_new(fcgi->fcg_s, fcgi_read, fcgi_write, + fcgi_error, fcgi); + if (fcgi->fcg_bev == NULL) + goto err; + + bufferevent_enable(fcgi->fcg_bev, EV_READ | EV_WRITE); + return; + +err: + if (s != -1) { + close(s); + free(fcgi); + fcgi_inflight_dec(__func__); + } +} + +static int +parse_len(struct fcgi *fcgi, struct evbuffer *src) +{ + unsigned char c, x[3]; + + fcgi->fcg_toread--; + evbuffer_remove(src, &c, 1); + if (c >> 7 == 0) + return (c); + + if (fcgi->fcg_toread < 3) + return (-1); + + fcgi->fcg_toread -= 3; + evbuffer_remove(src, x, sizeof(x)); + return (((c & 0x7F) << 24) | (x[0] << 16) | (x[1] << 8) | x[2]); +} + +static int +fcgi_parse_params(struct fcgi *fcgi, struct evbuffer *src, struct client *clt) +{ + char pname[32]; + char server[HOST_NAME_MAX + 1]; + char path[PATH_MAX]; + char query[GEMINI_MAXLEN]; + char method[8]; + int nlen, vlen; + + while (fcgi->fcg_toread > 0) { + if ((nlen = parse_len(fcgi, src)) < 0 || + (vlen = parse_len(fcgi, src)) < 0) + return (-1); + + if (fcgi->fcg_toread < nlen + vlen) + return (-1); + + if ((size_t)nlen > sizeof(pname) - 1) { + /* ignore this parameter */ + fcgi->fcg_toread -= nlen - vlen; + evbuffer_drain(src, nlen + vlen); + continue; + } + + fcgi->fcg_toread -= nlen; + evbuffer_remove(src, &pname, nlen); + pname[nlen] = '\0'; + + if (!strcmp(pname, "SERVER_NAME") && + (size_t)vlen < sizeof(server)) { + fcgi->fcg_toread -= vlen; + evbuffer_remove(src, &server, vlen); + server[vlen] = '\0'; + + free(clt->clt_server_name); + if ((clt->clt_server_name = strdup(server)) == NULL) + return (-1); + DPRINTF("clt %d: server_name: %s", clt->clt_id, + clt->clt_server_name); + continue; + } + + if (!strcmp(pname, "SCRIPT_NAME") && + (size_t)vlen < sizeof(path)) { + fcgi->fcg_toread -= vlen; + evbuffer_remove(src, &path, vlen); + path[vlen] = '\0'; + + free(clt->clt_script_name); + clt->clt_script_name = NULL; + + if (vlen == 0 || path[vlen - 1] != '/') + asprintf(&clt->clt_script_name, "%s/", path); + else + clt->clt_script_name = strdup(path); + + if (clt->clt_script_name == NULL) + return (-1); + + DPRINTF("clt %d: script_name: %s", clt->clt_id, + clt->clt_script_name); + continue; + } + + /* XXX: fix gmid */ + if (/*!strcmp(pname, "PATH_INFO") && */ + !strcmp(pname, "GEMINI_URL_PATH") && + (size_t)vlen < sizeof(path)) { + fcgi->fcg_toread -= vlen; + evbuffer_remove(src, &path, vlen); + path[vlen] = '\0'; + + free(clt->clt_path_info); + clt->clt_path_info = NULL; + + if (*path != '/') + asprintf(&clt->clt_path_info, "/%s", path); + else + clt->clt_path_info = strdup(path); + + if (clt->clt_path_info == NULL) + return (-1); + + DPRINTF("clt %d: path_info: %s", clt->clt_id, + clt->clt_path_info); + continue; + } + + if (!strcmp(pname, "QUERY_STRING") && + (size_t)vlen < sizeof(query) && + vlen > 0) { + fcgi->fcg_toread -= vlen; + evbuffer_remove(src, &query, vlen); + query[vlen] = '\0'; + + free(clt->clt_query); + if ((clt->clt_query = strdup(query)) == NULL) + return (-1); + + DPRINTF("clt %d: query: %s", clt->clt_id, + clt->clt_query); + continue; + } + + if (!strcmp(pname, "REQUEST_METHOD") && + (size_t)vlen < sizeof(method)) { + fcgi->fcg_toread -= vlen; + evbuffer_remove(src, &method, vlen); + method[vlen] = '\0'; + + if (!strcasecmp(method, "GET")) + clt->clt_method = METHOD_GET; + if (!strcasecmp(method, "POST")) + clt->clt_method = METHOD_POST; + + continue; + } + + fcgi->fcg_toread -= vlen; + evbuffer_drain(src, vlen); + } + + return (0); +} + +void +fcgi_read(struct bufferevent *bev, void *d) +{ + struct fcgi *fcgi = d; + struct env *env = fcgi->fcg_env; + struct evbuffer *src = EVBUFFER_INPUT(bev); + struct fcgi_header hdr; + struct fcgi_begin_req breq; + struct client *clt, q; + int role; + + memset(&q, 0, sizeof(q)); + + for (;;) { + if (EVBUFFER_LENGTH(src) < (size_t)fcgi->fcg_toread) + return; + + if (fcgi->fcg_want == FCGI_RECORD_HEADER) { + fcgi->fcg_want = FCGI_RECORD_BODY; + bufferevent_read(bev, &hdr, sizeof(hdr)); + + DPRINTF("header: v=%d t=%d id=%d len=%d p=%d", + hdr.version, hdr.type, + CAT(hdr.req_id0, hdr.req_id1), + CAT(hdr.content_len0, hdr.content_len1), + hdr.padding); + + if (hdr.version != FCGI_VERSION_1) { + log_warnx("unknown fastcgi version: %d", + hdr.version); + fcgi_error(bev, EV_READ, d); + return; + } + + fcgi->fcg_toread = CAT(hdr.content_len0, + hdr.content_len1); + if (fcgi->fcg_toread < 0) { + log_warnx("invalid record length: %d", + fcgi->fcg_toread); + fcgi_error(bev, EV_READ, d); + return; + } + + fcgi->fcg_padding = hdr.padding; + if (fcgi->fcg_padding < 0) { + log_warnx("invalid padding: %d", + fcgi->fcg_padding); + fcgi_error(bev, EV_READ, d); + return; + } + + fcgi->fcg_type = hdr.type; + fcgi->fcg_rec_id = CAT(hdr.req_id0, hdr.req_id1); + continue; + } + + q.clt_id = fcgi->fcg_rec_id; + clt = SPLAY_FIND(client_tree, &fcgi->fcg_clients, &q); + + switch (fcgi->fcg_type) { + case FCGI_BEGIN_REQUEST: + if (sizeof(breq) != fcgi->fcg_toread) { + log_warnx("unexpected size for " + "FCGI_BEGIN_REQUEST"); + fcgi_error(bev, EV_READ, d); + return; + } + + evbuffer_remove(src, &breq, sizeof(breq)); + + role = CAT(breq.role0, breq.role1); + if (role != FCGI_RESPONDER) { + log_warnx("unknown fastcgi role: %d", + role); + if (fcgi_send_end_req(fcgi, fcgi->fcg_rec_id, + 1, FCGI_UNKNOWN_ROLE) == -1) { + fcgi_error(bev, EV_READ, d); + return; + } + break; + } + + if (!fcgi->fcg_keep_conn) { + log_warnx("trying to reuse the fastcgi " + "socket without marking it as so."); + fcgi_error(bev, EV_READ, d); + return; + } + fcgi->fcg_keep_conn = breq.flags & FCGI_KEEP_CONN; + + if (clt != NULL) { + log_warnx("ignoring attemp to re-use an " + "active request id (%d)", + fcgi->fcg_rec_id); + break; + } + + if ((clt = calloc(1, sizeof(*clt))) == NULL) { + log_warnx("calloc"); + break; + } + +#if template + clt->clt_tp = template(clt, clt_tp_puts, clt_tp_putc); + if (clt->clt_tp == NULL) { + free(clt); + log_warn("template"); + break; + } +#endif + + clt->clt_id = fcgi->fcg_rec_id; + clt->clt_fd = -1; + clt->clt_fcgi = fcgi; + SPLAY_INSERT(client_tree, &fcgi->fcg_clients, clt); + break; + case FCGI_PARAMS: + if (clt == NULL) { + log_warnx("got FCGI_PARAMS for inactive id " + "(%d)", fcgi->fcg_rec_id); + evbuffer_drain(src, fcgi->fcg_toread); + break; + } + if (fcgi->fcg_toread == 0) { + evbuffer_drain(src, fcgi->fcg_toread); + if (server_handle(env, clt) == -1) + return; + break; + } + if (fcgi_parse_params(fcgi, src, clt) == -1) { + log_warnx("fcgi_parse_params failed"); + fcgi_error(bev, EV_READ, d); + return; + } + break; + case FCGI_STDIN: + /* not interested in reading stdin */ + evbuffer_drain(src, fcgi->fcg_toread); + break; + case FCGI_ABORT_REQUEST: + if (clt == NULL) { + log_warnx("got FCGI_ABORT_REQUEST for inactive" + " id (%d)", fcgi->fcg_rec_id); + evbuffer_drain(src, fcgi->fcg_toread); + break; + } + if (fcgi_end_request(clt, 1) == -1) { + /* calls fcgi_error on failure */ + return; + } + break; + default: + log_warnx("unknown fastcgi record type %d", + fcgi->fcg_type); + evbuffer_drain(src, fcgi->fcg_toread); + break; + } + + /* Prepare for the next record. */ + evbuffer_drain(src, fcgi->fcg_padding); + fcgi->fcg_want = FCGI_RECORD_HEADER; + fcgi->fcg_toread = sizeof(struct fcgi_header); + } +} + +void +fcgi_write(struct bufferevent *bev, void *d) +{ + struct fcgi *fcgi = d; + struct evbuffer *out = EVBUFFER_OUTPUT(bev); + + if (fcgi->fcg_done && EVBUFFER_LENGTH(out) == 0) + fcgi_error(bev, EVBUFFER_EOF, fcgi); +} + +void +fcgi_error(struct bufferevent *bev, short event, void *d) +{ + struct fcgi *fcgi = d; + struct env *env = fcgi->fcg_env; + struct client *clt; + + log_debug("fcgi failure, shutting down connection (ev: %x)", + event); + fcgi_inflight_dec(__func__); + + while ((clt = SPLAY_MIN(client_tree, &fcgi->fcg_clients)) != NULL) { + SPLAY_REMOVE(client_tree, &fcgi->fcg_clients, clt); + server_client_free(clt); + } + + SPLAY_REMOVE(fcgi_tree, &env->env_fcgi_socks, fcgi); + fcgi_free(fcgi); + + return; +} + +void +fcgi_free(struct fcgi *fcgi) +{ + close(fcgi->fcg_s); + bufferevent_free(fcgi->fcg_bev); + free(fcgi); +} + +int +clt_flush(struct client *clt) +{ + struct fcgi *fcgi = clt->clt_fcgi; + struct bufferevent *bev = fcgi->fcg_bev; + struct fcgi_header hdr; + + if (clt->clt_buflen == 0) + return (0); + + memset(&hdr, 0, sizeof(hdr)); + hdr.version = FCGI_VERSION_1; + hdr.type = FCGI_STDOUT; + hdr.req_id0 = (clt->clt_id & 0xFF); + hdr.req_id1 = (clt->clt_id >> 8); + hdr.content_len0 = (clt->clt_buflen & 0xFF); + hdr.content_len1 = (clt->clt_buflen >> 8); + + if (bufferevent_write(bev, &hdr, sizeof(hdr)) == -1 || + bufferevent_write(bev, clt->clt_buf, clt->clt_buflen) == -1) { + fcgi_error(bev, EV_WRITE, fcgi); + return (-1); + } + + clt->clt_buflen = 0; + + return (0); +} + +int +clt_write(struct client *clt, const uint8_t *buf, size_t len) +{ + size_t left, copy; + + while (len > 0) { + left = sizeof(clt->clt_buf) - clt->clt_buflen; + if (left == 0) { + if (clt_flush(clt) == -1) + return (-1); + left = sizeof(clt->clt_buf); + } + + copy = MIN(left, len); + + memcpy(&clt->clt_buf[clt->clt_buflen], buf, copy); + clt->clt_buflen += copy; + buf += copy; + len -= copy; + } + + return (0); +} + +int +clt_putc(struct client *clt, char ch) +{ + return (clt_write(clt, &ch, 1)); +} + +int +clt_puts(struct client *clt, const char *str) +{ + return (clt_write(clt, str, strlen(str))); +} + +int +clt_write_bufferevent(struct client *clt, struct bufferevent *bev) +{ + struct evbuffer *src = EVBUFFER_INPUT(bev); + size_t len, left, copy; + + len = EVBUFFER_LENGTH(src); + while (len > 0) { + left = sizeof(clt->clt_buf) - clt->clt_buflen; + if (left == 0) { + if (clt_flush(clt) == -1) + return (-1); + left = sizeof(clt->clt_buf); + } + + copy = bufferevent_read(bev, &clt->clt_buf[clt->clt_buflen], + MIN(left, len)); + clt->clt_buflen += copy; + + len = EVBUFFER_LENGTH(src); + } + + return (0); +} + +int +clt_printf(struct client *clt, const char *fmt, ...) +{ + struct fcgi *fcgi = clt->clt_fcgi; + struct bufferevent *bev = fcgi->fcg_bev; + char *str; + va_list ap; + int r; + + va_start(ap, fmt); + r = vasprintf(&str, fmt, ap); + va_end(ap); + if (r == -1) { + fcgi_error(bev, EV_WRITE, fcgi); + return (-1); + } + + r = clt_write(clt, str, r); + free(str); + return (r); +} + +#if template +int +clt_tp_puts(struct template *tp, const char *str) +{ + struct client *clt = tp->tp_arg; + + if (clt_puts(clt, str) == -1) + return (-1); + + return (0); +} + +int +clt_tp_putc(struct template *tp, int c) +{ + struct client *clt = tp->tp_arg; + + if (clt_putc(clt, c) == -1) + return (-1); + + return (0); +} +#endif + +int +fcgi_cmp(struct fcgi *a, struct fcgi *b) +{ + return ((int)a->fcg_id - b->fcg_id); +} + +int +fcgi_client_cmp(struct client *a, struct client *b) +{ + return ((int)a->clt_id - b->clt_id); +} + +int +accept_reserve(int sockfd, struct sockaddr *addr, socklen_t *addrlen, + int reserve, volatile int *counter) +{ + int ret; + + if (getdtablecount() + reserve + *counter >= getdtablesize()) { + errno = EMFILE; + return (-1); + } + + if ((ret = accept4(sockfd, addr, addrlen, SOCK_NONBLOCK)) > -1) { + (*counter)++; + log_debug("%s: inflight incremented, now %d", __func__, + *counter); + } + + return (ret); +} + +SPLAY_GENERATE(fcgi_tree, fcgi, fcg_nodes, fcgi_cmp); +SPLAY_GENERATE(client_tree, client, clt_nodes, fcgi_client_cmp); blob - /dev/null blob + c0baa8e37797771914fd6765192f98b8e55961fb (mode 644) --- /dev/null +++ keys/Makefile @@ -0,0 +1,18 @@ +NEXTV = 03 + +DISTFILES = Makefile \ + pkg_fcgi-01.pub \ + pkg_fcgi-02.pub + +all: + false + +newkey: + signify -G -p pkg_fcgi-${NEXTV}.pub -s pkg_fcgi-${NEXTV}.sec + +dist: ${DISTFILES} + mkdir -p ${DESTDIR}/ + ${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/ + +.PHONY: all dist +include ../config.mk blob - /dev/null blob + 179235943ad862e0426e472d7b0922bf3fef0371 (mode 644) --- /dev/null +++ log.c @@ -0,0 +1,197 @@ +/* $OpenBSD: log.c,v 1.1 2018/07/10 16:39:54 florian Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" + +static int debug; +static int verbose; +static const char *log_procname; + +void +log_init(int n_debug, int facility) +{ + debug = n_debug; + verbose = n_debug; + log_procinit(getprogname()); + + if (!debug) + openlog(getprogname(), LOG_PID | LOG_NDELAY, facility); + + tzset(); +} + +void +log_procinit(const char *procname) +{ + if (procname != NULL) + log_procname = procname; +} + +void +log_setverbose(int v) +{ + verbose = v; +} + +int +log_getverbose(void) +{ + return (verbose); +} + +void +logit(int pri, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog(pri, fmt, ap); + va_end(ap); +} + +void +vlog(int pri, const char *fmt, va_list ap) +{ + char *nfmt; + int saved_errno = errno; + + if (debug) { + /* best effort in out of mem situations */ + if (asprintf(&nfmt, "%s\n", fmt) == -1) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } else { + vfprintf(stderr, nfmt, ap); + free(nfmt); + } + fflush(stderr); + } else + vsyslog(pri, fmt, ap); + + errno = saved_errno; +} + +void +log_warn(const char *emsg, ...) +{ + char *nfmt; + va_list ap; + int saved_errno = errno; + + /* best effort to even work in out of memory situations */ + if (emsg == NULL) + logit(LOG_ERR, "%s", strerror(saved_errno)); + else { + va_start(ap, emsg); + + if (asprintf(&nfmt, "%s: %s", emsg, + strerror(saved_errno)) == -1) { + /* we tried it... */ + vlog(LOG_ERR, emsg, ap); + logit(LOG_ERR, "%s", strerror(saved_errno)); + } else { + vlog(LOG_ERR, nfmt, ap); + free(nfmt); + } + va_end(ap); + } + + errno = saved_errno; +} + +void +log_warnx(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_ERR, emsg, ap); + va_end(ap); +} + +void +log_info(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_INFO, emsg, ap); + va_end(ap); +} + +void +log_debug(const char *emsg, ...) +{ + va_list ap; + + if (verbose) { + va_start(ap, emsg); + vlog(LOG_DEBUG, emsg, ap); + va_end(ap); + } +} + +static void +vfatalc(int code, const char *emsg, va_list ap) +{ + static char s[BUFSIZ]; + const char *sep; + + if (emsg != NULL) { + (void)vsnprintf(s, sizeof(s), emsg, ap); + sep = ": "; + } else { + s[0] = '\0'; + sep = ""; + } + if (code) + logit(LOG_CRIT, "fatal in %s: %s%s%s", + log_procname, s, sep, strerror(code)); + else + logit(LOG_CRIT, "fatal in %s%s%s", log_procname, sep, s); +} + +void +fatal(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vfatalc(errno, emsg, ap); + va_end(ap); + exit(1); +} + +void +fatalx(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vfatalc(0, emsg, ap); + va_end(ap); + exit(1); +} blob - /dev/null blob + 0fa046fc3afbe8c893d40f492481a4d3055dd2bc (mode 644) --- /dev/null +++ log.h @@ -0,0 +1,45 @@ +/* $OpenBSD: log.h,v 1.2 2021/12/13 18:28:40 deraadt Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef LOG_H +#define LOG_H + +#include + +void log_init(int, int); +void log_procinit(const char *); +void log_setverbose(int); +int log_getverbose(void); +void log_warn(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_warnx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_info(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_debug(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void logit(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void vlog(int, const char *, va_list) + __attribute__((__format__ (printf, 2, 0))); +__dead void fatal(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +__dead void fatalx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + +#endif /* LOG_H */ blob - /dev/null blob + 1a72b2d68dfc7f6b91ebb75ab847109230bb5ad2 (mode 644) --- /dev/null +++ pkg.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2022 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define FD_RESERVE 5 +#define GEMINI_MAXLEN 1025 /* including NUL */ + +#ifdef DEBUG +#define DPRINTF log_debug +#else +#define DPRINTF(x...) do {} while (0) +#endif + +struct bufferevent; +struct event; +struct fcgi; +struct sqlite3; +struct sqlite3_stmt; + +enum { + METHOD_UNKNOWN, + METHOD_GET, + METHOD_POST, +}; + +struct client { + uint32_t clt_id; + int clt_fd; + struct fcgi *clt_fcgi; + char *clt_server_name; + char *clt_script_name; + char *clt_path_info; + char *clt_query; + int clt_method; +#if template + struct template *clt_tp; +#endif + char clt_buf[1024]; + size_t clt_buflen; + + SPLAY_ENTRY(client) clt_nodes; +}; +SPLAY_HEAD(client_tree, client); + +struct fcgi { + uint32_t fcg_id; + int fcg_s; + struct client_tree fcg_clients; + struct bufferevent *fcg_bev; + int fcg_toread; + int fcg_want; + int fcg_padding; + int fcg_type; + int fcg_rec_id; + int fcg_keep_conn; + int fcg_done; + + struct env *fcg_env; + + SPLAY_ENTRY(fcgi) fcg_nodes; +}; +SPLAY_HEAD(fcgi_tree, fcgi); + +struct env { + int env_sockfd; + struct event env_sockev; + struct event env_pausev; + struct fcgi_tree env_fcgi_socks; + + struct sqlite3 *env_db; + struct sqlite3_stmt *env_qsearch; + struct sqlite3_stmt *env_qfullpkgpath; + struct sqlite3_stmt *env_qcats; + struct sqlite3_stmt *env_qbycat; +}; + +/* fcgi.c */ +int fcgi_end_request(struct client *, int); +int fcgi_abort_request(struct client *); +void fcgi_accept(int, short, void *); +void fcgi_read(struct bufferevent *, void *); +void fcgi_write(struct bufferevent *, void *); +void fcgi_error(struct bufferevent *, short, void *); +void fcgi_free(struct fcgi *); +int clt_putc(struct client *, char); +int clt_puts(struct client *, const char *); +int clt_write_bufferevent(struct client *, struct bufferevent *); +int clt_flush(struct client *); +int clt_write(struct client *, const uint8_t *, size_t); +int clt_printf(struct client *, const char *, ...) + __attribute__((__format__(printf, 2, 3))) + __attribute__((__nonnull__(2))); +#if template +int clt_tp_puts(struct template *, const char *); +int clt_tp_putc(struct template *, int); +#endif +int fcgi_cmp(struct fcgi *, struct fcgi *); +int fcgi_client_cmp(struct client *, struct client *); + +/* server.c */ +int server_main(const char *); +int server_handle(struct env *, struct client *); +void server_client_free(struct client *); + +#if template +/* ui.tmpl */ +int tp_home(struct template *); +#endif + +SPLAY_PROTOTYPE(client_tree, client, clt_nodes, fcgi_client_cmp); +SPLAY_PROTOTYPE(fcgi_tree, fcgi, fcg_nodes, fcgi_cmp); blob - /dev/null blob + d70e224c5dc760f294495d3f54a40a8693f56fbc (mode 644) --- /dev/null +++ pkg_fcgi.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2022 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "pkg.h" + +#ifndef PKG_FCGI_DB +#define PKG_FCGI_DB "/pkg_fcgi/pkgs.sqlite3" +#endif + +#ifndef PKG_FCGI_SOCK +#define PKG_FCGI_SOCK "/run/pkg_fcgi.sock" +#endif + +#ifndef PKG_FCGI_USER +#define PKG_FCGI_USER "www" +#endif + +#define MAX_CHILDREN 32 + +static const char *argv0; +static pid_t pids[MAX_CHILDREN]; +static int children = 3; + +static volatile sig_atomic_t got_sigchld; + +static void +handle_sigchld(int sig) +{ + int i, saved_errno; + + if (got_sigchld) + return; + + got_sigchld = 1; + saved_errno = errno; + + for (i = 0; i < children; ++i) + (void) kill(pids[i], SIGTERM); + + errno = saved_errno; +} + +static int +bind_socket(const char *path, struct passwd *pw) +{ + struct sockaddr_un sun; + int fd, old_umask; + + if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0)) == -1) { + log_warn("%s: socket", __func__); + return (-1); + } + + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + + if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >= + sizeof(sun.sun_path)) { + log_warnx("%s: path too long: %s", __func__, path); + close(fd); + return (-1); + } + + if (unlink(path) == -1 && errno != ENOENT) { + log_warn("%s: unlink %s", __func__, path); + close(fd); + return (-1); + } + + old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); + if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { + log_warn("%s: bind: %s (%d)", __func__, path, geteuid()); + close(fd); + umask(old_umask); + return (-1); + } + umask(old_umask); + + if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) { + log_warn("%s: chmod %s", __func__, path); + close(fd); + (void) unlink(path); + return (-1); + } + + if (chown(path, pw->pw_uid, pw->pw_gid) == -1) { + log_warn("%s: chown %s %s", __func__, pw->pw_name, path); + close(fd); + (void) unlink(path); + return (-1); + } + + if (listen(fd, 5) == -1) { + log_warn("%s: listen", __func__); + close(fd); + (void) unlink(path); + return (-1); + } + + return (fd); +} + +static pid_t +start_child(const char *root, const char *user, const char *db, + int daemonize, int verbose, int fd) +{ + char *argv[10]; + int argc = 0; + pid_t pid; + + switch (pid = fork()) { + case -1: + fatal("cannot fork"); + case 0: + break; + default: + close(fd); + return (pid); + } + + if (fd != 3) { + if (dup2(fd, 3) == -1) + fatal("cannot setup imsg fd"); + } else if (fcntl(fd, F_SETFD, 0) == -1) + fatal("cannot setup imsg fd"); + + argv[argc++] = (char *)argv0; + argv[argc++] = (char *)"-S"; + argv[argc++] = (char *)"-p"; argv[argc++] = (char *)root; + argv[argc++] = (char *)"-u"; argv[argc++] = (char *)user; + if (!daemonize) + argv[argc++] = (char *)"-d"; + if (verbose) + argv[argc++] = (char *)"-v"; + argv[argc++] = (char *)db; + argv[argc++] = NULL; + + execvp(argv0, argv); + fatal("execvp"); +} + +static void __dead +usage(void) +{ + fprintf(stderr, + "usage: %s [-dv] [-j n] [-p path] [-s socket] [-u user] [db]\n", + getprogname()); + exit(1); +} + +int +main(int argc, char **argv) +{ + struct stat sb; + struct passwd *pw; + pid_t pid; + char path[PATH_MAX]; + const char *cause; + const char *root = NULL; + const char *sock = PKG_FCGI_SOCK; + const char *user = PKG_FCGI_USER; + const char *db = PKG_FCGI_DB; + const char *errstr; + int ch, i, daemonize = 1, verbosity = 0; + int server = 0, fd = -1; + int status; + + /* + * Ensure we have fds 0-2 open so that we have no issue with + * calling bind_socket before daemon(3). + */ + for (i = 0; i < 3; ++i) { + if (fstat(i, &sb) == -1) { + if ((fd = open("/dev/null", O_RDWR)) != -1) { + if (dup2(fd, i) == -1) + exit(1); + if (fd > i) + close(fd); + } else + exit(1); + } + } + + log_init(1, LOG_DAEMON); /* Log to stderr until daemonized. */ + + if ((argv0 = argv[0]) == NULL) + fatalx("argv[0] is NULL"); + + while ((ch = getopt(argc, argv, "dj:p:Ss:u:v")) != -1) { + switch (ch) { + case 'd': + daemonize = 0; + break; + case 'j': + children = strtonum(optarg, 1, MAX_CHILDREN, &errstr); + if (errstr) + fatalx("number of children is %s: %s", + errstr, optarg); + break; + case 'p': + root = optarg; + break; + case 'S': + server = 1; + break; + case 's': + sock = optarg; + break; + case 'u': + user = optarg; + break; + case 'v': + verbosity++; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc > 1) + usage(); + if (argc == 1) + db = argv[0]; + + if (geteuid()) + fatalx("need root privileges"); + + pw = getpwnam(user); + if (pw == NULL) + fatalx("user %s not found", user); + if (pw->pw_uid == 0) + fatalx("cannot run as %s: must not be the superuser", user); + + if (root == NULL) + root = pw->pw_dir; + + if (!server) { + int ret; + + ret = snprintf(path, sizeof(path), "%s/%s", root, sock); + if (ret < 0 || (size_t)ret >= sizeof(path)) + fatalx("socket path too long"); + + if ((fd = bind_socket(path, pw)) == -1) + fatalx("failed to open socket %s", sock); + + for (i = 0; i < children; ++i) { + int d; + + if ((d = dup(fd)) == -1) + fatalx("dup"); + pids[i] = start_child(root, user, db, + daemonize, verbosity, d); + log_debug("forking child %d (pid %lld)", i, + (long long)pids[i]); + } + + signal(SIGCHLD, handle_sigchld); + } + + if (chroot(root) == -1) + fatal("chroot %s", root); + if (chdir("/") == -1) + fatal("chdir /"); + + if (setgroups(1, &pw->pw_gid) == -1 || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) + fatal("failed to drop privileges"); + + log_init(daemonize ? 0 : 1, LOG_DAEMON); + log_setverbose(verbosity); + + if (server) + exit(server_main(db)); + + if (daemonize && daemon(1, 0) == -1) + fatal("daemon"); + + if (pledge("stdio proc", NULL) == -1) + fatal("pledge"); + + for (;;) { + do { + pid = waitpid(WAIT_ANY, &status, 0); + } while (pid != -1 || errno == EINTR); + + if (pid == -1) { + if (errno == ECHILD) + break; + fatal("waitpid failed"); + } + + if (WIFSIGNALED(status)) + cause = "was terminated"; + else if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) + cause = "exited abnormally"; + else + cause = "exited successfully"; + } else + cause = "died"; + + log_warnx("child process %lld %s", (long long)pid, cause); + } + + return (1); +} blob - /dev/null blob + 77f8c6984b87b2064b3a8c6431b66eea2ad1c5ad (mode 644) --- /dev/null +++ server.c @@ -0,0 +1,624 @@ +/* + * Copyright (c) 2022 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "log.h" +#include "pkg.h" +#include "tmpl.h" + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +char dbpath[PATH_MAX]; + +void server_sig_handler(int, short, void *); +void server_open_db(struct env *); +void server_close_db(struct env *); +__dead void server_shutdown(struct env *); +int server_reply(struct client *, int, const char *); + +int route_dispatch(struct env *, struct client *); +int route_home(struct env *, struct client *); +int route_search(struct env *, struct client *); +int route_categories(struct env *, struct client *); +int route_listing(struct env *, struct client *); +int route_port(struct env *, struct client *); + +typedef int (*route_t)(struct env *, struct client *); + +static const struct route { + const char *r_path; + route_t r_fn; +} routes[] = { + { "/", route_home }, + { "/search", route_search }, + { "/all", route_categories }, + { "/*", route_port }, +}; + +void +server_sig_handler(int sig, short ev, void *arg) +{ + struct env *env = arg; + + /* + * Normal signal handler rules don't apply because libevent + * decouples for us. + */ + + switch (sig) { + case SIGHUP: + log_info("re-opening the db"); + server_close_db(env); + server_open_db(env); + break; + case SIGTERM: + case SIGINT: + server_shutdown(env); + break; + default: + fatalx("unexpected signal %d", sig); + } +} + +static inline void +loadstmt(sqlite3 *db, sqlite3_stmt **stmt, const char *sql) +{ + int err; + + err = sqlite3_prepare_v2(db, sql, -1, stmt, NULL); + if (err != SQLITE_OK) + fatalx("failed prepare statement \"%s\": %s", + sql, sqlite3_errstr(err)); +} + +void +server_open_db(struct env *env) +{ + int err; + + err = sqlite3_open_v2(dbpath, &env->env_db, + SQLITE_OPEN_READONLY, NULL); + if (err != SQLITE_OK) + fatalx("can't open database %s: %s", dbpath, + sqlite3_errmsg(env->env_db)); + + /* load prepared statements */ + loadstmt(env->env_db, &env->env_qsearch, + "select webpkg_fts.pkgstem, webpkg_fts.comment, paths.fullpkgpath" + " from webpkg_fts" + " join _ports p on p.fullpkgpath = webpkg_fts.id" + " join _paths paths on paths.id = webpkg_fts.id" + " where webpkg_fts match ?" + " order by bm25(webpkg_fts)"); + + loadstmt(env->env_db, &env->env_qfullpkgpath, + "select p.fullpkgpath, pp.pkgstem, pp.comment, pp.pkgname," + " d.value, e.value, r.value, pp.homepage" + " from _paths p" + " join _descr d on d.fullpkgpath = p.id" + " join _ports pp on pp.fullpkgpath = p.id" + " join _email e on e.keyref = pp.maintainer" + " left join _readme r on r.fullpkgpath = p.id" + " where p.fullpkgpath = ?"); + + loadstmt(env->env_db, &env->env_qcats, + "select distinct value from categories order by value"); + + loadstmt(env->env_db, &env->env_qbycat, + "select fullpkgpath from categories where value = ?" + " order by fullpkgpath"); +} + +void +server_close_db(struct env *env) +{ + int err; + + sqlite3_finalize(env->env_qsearch); + sqlite3_finalize(env->env_qfullpkgpath); + sqlite3_finalize(env->env_qcats); + sqlite3_finalize(env->env_qbycat); + + if ((err = sqlite3_close(env->env_db)) != SQLITE_OK) + log_warnx("sqlite3_close %s", sqlite3_errstr(err)); +} + +int +server_main(const char *db) +{ + struct env env; + struct event sighup; + struct event sigint; + struct event sigterm; + + signal(SIGPIPE, SIG_IGN); + + memset(&env, 0, sizeof(env)); + + if (pledge("stdio rpath flock unix", NULL) == -1) + fatal("pledge"); + + if (realpath(db, dbpath) == NULL) + fatal("realpath %s", db); + + server_open_db(&env); + + event_init(); + + env.env_sockfd = 3; + + event_set(&env.env_sockev, env.env_sockfd, EV_READ | EV_PERSIST, + fcgi_accept, &env); + event_add(&env.env_sockev, NULL); + + evtimer_set(&env.env_pausev, fcgi_accept, &env); + + signal_set(&sighup, SIGHUP, server_sig_handler, &env); + signal_set(&sigint, SIGINT, server_sig_handler, &env); + signal_set(&sigterm, SIGTERM, server_sig_handler, &env); + + signal_add(&sighup, NULL); + signal_add(&sigint, NULL); + signal_add(&sigterm, NULL); + + log_info("ready"); + event_dispatch(); + + server_shutdown(&env); +} + +void __dead +server_shutdown(struct env *env) +{ + log_info("shutting down"); + server_close_db(env); + exit(0); +} + +int +server_reply(struct client *clt, int status, const char *ctype) +{ + if (clt_printf(clt, "%02d %s\r\n", status, ctype) == -1) + return (-1); + return (0); +} + +int +server_handle(struct env *env, struct client *clt) +{ + return (route_dispatch(env, clt)); +} + +void +server_client_free(struct client *clt) +{ +#if template + template_free(clt->clt_tp); +#endif + free(clt->clt_server_name); + free(clt->clt_script_name); + free(clt->clt_path_info); + free(clt->clt_query); + free(clt); +} + +static inline int +unquote(char *str) +{ + char *p, *q; + char hex[3]; + unsigned long x; + + hex[2] = '\0'; + p = q = str; + while (*p) { + switch (*p) { + case '%': + if (!isxdigit((unsigned char)p[1]) || + !isxdigit((unsigned char)p[2]) || + (p[1] == '0' && p[2] == '0')) + return (-1); + + hex[0] = p[1]; + hex[1] = p[2]; + + x = strtoul(hex, NULL, 16); + *q++ = (char)x; + p += 3; + break; + default: + *q++ = *p++; + break; + } + } + *q = '\0'; + return (0); +} + +static inline int +fts_escape(const char *p, char *buf, size_t bufsize) +{ + char *q; + + /* + * split p into words and quote them into buf. + * quoting means wrapping each word into "..." and + * replace every " with "". + * i.e. 'C++ "framework"' -> '"C++" """framework"""' + * flatting all the whitespaces seems fine too. + */ + + q = buf; + while (bufsize != 0) { + p += strspn(p, " \f\n\r\t\v"); + if (*p == '\0') + break; + + *q++ = '"'; + bufsize--; + while (*p && !isspace((unsigned char)*p) && bufsize != 0) { + if (*p == '"') { /* double the quote character */ + *q++ = '"'; + bufsize--; + if (bufsize == 0) + break; + } + *q++ = *p++; + bufsize--; + } + + if (bufsize < 2) + break; + *q++ = '"'; + *q++ = ' '; + bufsize -= 2; + } + if ((*p == '\0') && bufsize != 0) { + *q = '\0'; + return (0); + } + + return (-1); +} + +int +route_dispatch(struct env *env, struct client *clt) +{ + const struct route *r; + size_t i; + + log_debug("path info is %s", clt->clt_path_info); + + for (i = 0; i < nitems(routes); ++i) { + r = &routes[i]; + + if (fnmatch(r->r_path, clt->clt_path_info, 0) != 0) + continue; + return (r->r_fn(env, clt)); + } + + if (server_reply(clt, 51, "not found") == -1) + return (-1); + return (fcgi_end_request(clt, 0)); +} + +int +route_home(struct env *env, struct client *clt) +{ + if (server_reply(clt, 20, "text/gemini") == -1) + return (-1); + +#if 1 + if (clt_printf(clt, "# pkg_fcgi\n\n") == -1) + return (-1); + if (clt_printf(clt, "Welcome to pkg_fcgi, the Gemini interface " + "for the OpenBSD ports collection.\n\n") == -1) + return (-1); + if (clt_printf(clt, "=> /search Search for a package\n") == -1) + return (-1); + if (clt_printf(clt, "=> /all/ All categories\n") == -1) + return (-1); + if (clt_printf(clt, "\n") == -1) + return (-1); + if (clt_printf(clt, "What you search will be matched against the " + "package name (pkgstem), comment, DESCR and maintainer.\n") == -1) + return (-1); +#else + if (tp_home(clt->clt_tp) == -1) + return (-1); +#endif + + return (fcgi_end_request(clt, 0)); +} + +int +route_search(struct env *env, struct client *clt) +{ + const char *stem, *comment, *fullpkgpath; + char *query = clt->clt_query; + char equery[1024]; + int err; + int found = 0; + + if (query == NULL || *query == '\0') { + if (server_reply(clt, 10, "search for a package") == -1) + return (-1); + return (fcgi_end_request(clt, 0)); + } + + if (unquote(query) == -1 || + fts_escape(query, equery, sizeof(equery)) == -1) { + if (server_reply(clt, 59, "bad request") == -1) + return (-1); + return (fcgi_end_request(clt, 1)); + } + + log_debug("searching for %s", equery); + + err = sqlite3_bind_text(env->env_qsearch, 1, equery, -1, NULL); + if (err != SQLITE_OK) { + log_warnx("%s: sqlite3_bind_text \"%s\": %s", __func__, + query, sqlite3_errstr(err)); + sqlite3_reset(env->env_qsearch); + + if (server_reply(clt, 42, "internal error") == -1) + return (-1); + return (fcgi_end_request(clt, 1)); + } + + if (server_reply(clt, 20, "text/gemini") == -1) + goto err; + + if (clt_printf(clt, "# search results for %s\n\n", query) == -1) + goto err; + + for (;;) { + err = sqlite3_step(env->env_qsearch); + if (err == SQLITE_DONE) + break; + if (err != SQLITE_ROW) { + log_warnx("%s: sqlite3_step %s", __func__, + sqlite3_errstr(err)); + break; + } + found = 1; + + stem = sqlite3_column_text(env->env_qsearch, 0); + comment = sqlite3_column_text(env->env_qsearch, 1); + fullpkgpath = sqlite3_column_text(env->env_qsearch, 2); + + /* XXX fix URL */ + if (clt_printf(clt, "=> /%s %s: %s\n", fullpkgpath, + stem, comment) == -1) + goto err; + } + + sqlite3_reset(env->env_qsearch); + + if (!found && clt_printf(clt, "No ports found\n") == -1) + return (-1); + + return (fcgi_end_request(clt, 0)); + + err: + sqlite3_reset(env->env_qsearch); + return (-1); +} + +int +route_categories(struct env *env, struct client *clt) +{ + const char *fullpkgpath; + int err; + + if (server_reply(clt, 20, "text/gemini") == -1) + return (-1); + if (clt_printf(clt, "# list of all categories\n") == -1) + return (-1); + + if (clt_puts(clt, "\n") == -1) + return (-1); + + for (;;) { + err = sqlite3_step(env->env_qcats); + if (err == SQLITE_DONE) + break; + if (err != SQLITE_ROW) { + log_warnx("%s: sqlite3_step %s", __func__, + sqlite3_errstr(err)); + break; + } + + fullpkgpath = sqlite3_column_text(env->env_qcats, 0); + + /* XXX fix URL! */ + if (clt_printf(clt, "=> /%s %s\n", fullpkgpath, fullpkgpath) + == -1) { + sqlite3_reset(env->env_qcats); + return (-1); + } + } + + sqlite3_reset(env->env_qcats); + return (fcgi_end_request(clt, 0)); +} + +int +route_listing(struct env *env, struct client *clt) +{ + char buf[128], *s; + const char *path = clt->clt_path_info + 1; + const char *fullpkgpath; + int err; + + strlcpy(buf, path, sizeof(buf)); + while ((s = strrchr(buf, '/')) != NULL) + *s = '\0'; + + err = sqlite3_bind_text(env->env_qbycat, 1, buf, -1, NULL); + if (err != SQLITE_OK) { + log_warnx("%s: sqlite3_bind_text \"%s\": %s", __func__, + path, sqlite3_errstr(err)); + sqlite3_reset(env->env_qbycat); + + if (server_reply(clt, 42, "internal error") == -1) + return (-1); + return (fcgi_end_request(clt, 1)); + } + + if (server_reply(clt, 20, "text/gemini") == -1) + goto err; + + if (clt_printf(clt, "# port(s) under %s\n\n", path) == -1) + goto err; + + for (;;) { + err = sqlite3_step(env->env_qbycat); + if (err == SQLITE_DONE) + break; + if (err != SQLITE_ROW) { + log_warnx("%s: sqlite3_step %s", __func__, + sqlite3_errstr(err)); + break; + } + + fullpkgpath = sqlite3_column_text(env->env_qbycat, 0); + + /* XXX fix URL */ + if (clt_printf(clt, "=> /%s %s\n", fullpkgpath, fullpkgpath) + == -1) { + sqlite3_reset(env->env_qbycat); + return (-1); + } + } + + sqlite3_reset(env->env_qbycat); + return (fcgi_end_request(clt, 0)); + + err: + sqlite3_reset(env->env_qbycat); + return (-1); +} + +int +route_port(struct env *env, struct client *clt) +{ + const char *path = clt->clt_path_info + 1; + const char *fullpkgpath, *stem, *pkgname, *descr; + const char *comment, *maintainer, *readme, *www; + const char *version; + int err; + + err = sqlite3_bind_text(env->env_qfullpkgpath, 1, path, -1, NULL); + if (err != SQLITE_OK) { + log_warnx("%s: sqlite3_bind_text \"%s\": %s", __func__, + path, sqlite3_errstr(err)); + sqlite3_reset(env->env_qfullpkgpath); + + if (server_reply(clt, 42, "internal error") == -1) + return (-1); + return (fcgi_end_request(clt, 1)); + } + + err = sqlite3_step(env->env_qfullpkgpath); + if (err == SQLITE_DONE) { + /* No rows, retry as a category */ + sqlite3_reset(env->env_qfullpkgpath); + return (route_listing(env, clt)); + } + + if (err != SQLITE_ROW) { + log_warnx("%s: sqlite3_step %s", __func__, + sqlite3_errstr(err)); + if (server_reply(clt, 42, "internal error") == -1) + goto err; + goto done; + } + + fullpkgpath = sqlite3_column_text(env->env_qfullpkgpath, 0); + stem = sqlite3_column_text(env->env_qfullpkgpath, 1); + comment = sqlite3_column_text(env->env_qfullpkgpath, 2); + pkgname = sqlite3_column_text(env->env_qfullpkgpath, 3); + descr = sqlite3_column_text(env->env_qfullpkgpath, 4); + maintainer = sqlite3_column_text(env->env_qfullpkgpath, 5); + readme = sqlite3_column_text(env->env_qfullpkgpath, 6); + www = sqlite3_column_text(env->env_qfullpkgpath, 7); + + if ((version = strrchr(pkgname, '-')) != NULL) + version++; + else + version = " unknown"; + + if (server_reply(clt, 20, "text/gemini") == -1) + goto err; + + if (clt_printf(clt, "# %s v%s\n", path, version) == -1 || + clt_puts(clt, "\n") == -1 || + clt_printf(clt, "``` Command to install the package %s\n", + stem) == -1 || + clt_printf(clt, "# pkg_add %s\n", stem) == -1 || + clt_printf(clt, "```\n") == -1 || + clt_printf(clt, "\n") == -1 || + clt_printf(clt, "> %s\n", comment) == -1 || + clt_printf(clt, "\n") == -1 || + clt_printf(clt, "=> https://cvsweb.openbsd.org/ports/%s " + "CVS Web\n", fullpkgpath) == -1) + goto err; + + if (www && *www != '\0' && + clt_printf(clt, "=> %s Port Homepage (WWW)\n", www) == -1) + goto err; + + if (clt_printf(clt, "\n") == -1 || + clt_printf(clt, "Maintainer: %s\n", maintainer) == -1 || + clt_printf(clt, "## Description\n") == -1 || + clt_printf(clt, "``` %s description\n", stem) == -1 || + clt_puts(clt, descr) == -1 || + clt_puts(clt, "```\n") == -1 || + clt_puts(clt, "\n") == -1) + goto err; + + if (readme && *readme != '\0') { + if (clt_puts(clt, "## Readme\n") == -1 || + clt_puts(clt, "\n") == -1 || + clt_printf(clt, "``` README for %s\n", stem) == -1 || + clt_puts(clt, readme) == -1 || + clt_puts(clt, "\n") == -1) + goto err; + } + + done: + sqlite3_reset(env->env_qfullpkgpath); + return (fcgi_end_request(clt, 0)); + + err: + sqlite3_reset(env->env_qfullpkgpath); + return (-1); +} blob - /dev/null blob + e93001fad9cec3417a04f6241f9f121d3b3ae9b0 (mode 644) --- /dev/null +++ template/Makefile @@ -0,0 +1,33 @@ +PROG = template +SRCS = template.c y.tab.c +OBJS = ${SRCS:.c=.o} ${COBJS} + +DISTFILES = Makefile \ + configure \ + parse.y \ + template.c \ + tmpl.c \ + tmpl.h \ + y.tab.c + +all: ${PROG} + +include config.mk + +${PROG}: ${OBJS} + ${CC} -o $@ ${OBJS} + +y.tab.c: parse.y + ${YACC} -b y parse.y + +clean: + rm -rf *.o y.tab.* ${PROG} + +distclean: clean + rm -f config.h config.h.old config.mk config.log config.log.old + +dist: ${DISTFILES} + mkdir -p ${DESTDIR}/ + ${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/ + +.PHONY: all clean distclean dist blob - /dev/null blob + db1e39d22231e7c7f03b56af10ee2aac607e7a15 (mode 755) --- /dev/null +++ template/configure @@ -0,0 +1,233 @@ +#!/bin/sh +# +# Copyright (c) 2014, 2015, 2016 Ingo Schwarze +# Copyright (c) 2017, 2018 Kristaps Dzonsons +# Copyright (c) 2022 Omar Polo +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +set -e + +if command -v yacc 2>/dev/null >&2; then + YACC=yacc +elif command -v bison 2>/dev/null >&2; then + YACC=bison +else + YACC= +fi + +while [ $# -gt 0 ]; do + key="${1%%=*}" + val="${1#*=}" + + if [ "$1" = "$key" ]; then + echo "$0: invalid key-value: $1" >&2 + exit 1 + fi + + case "$key" in + CC) + CC="$val" ;; + CFLAGS) + CFLAGS="$val" ;; + YACC) + YACC="$val" ;; + esac + + shift +done + +if [ -z "$YACC" ]; then + echo "fatal: can't find yacc or bison." >&2 + exit 1 +fi + +[ -w config.log ] && mv config.log config.log.old + +exec 3> config.log +echo "config.log: writing..." + +CC="${CC:-cc}" +CFLAGS="${CFLAGS:--O2 -pipe} -I." +LIBS="${LIBS:-}" +LDFLAGS="${LDFLAGS:-}" + +COMPATS= + +HAVE_ERR= +HAVE_FREEZERO= +HAVE_GETEXECNAME= +HAVE_GETPROGNAME= +HAVE_PLEDGE= +HAVE_REALLOCARRAY= +HAVE_STRLCAT= +HAVE_STRLCPY= +HAVE_STRTONUM= +HAVE_SYS_QUEUE= +HAVE_UNVEIL= +HAVE_VASPRINTF= +HAVE___PROGNAME= + +# singletest message var extra-cflags extra-libs +singletest() { + cat >&3 <&3 2>&3; then + rm -f test-${1} test-${1}.d + + if [ -n "$3" ]; then + echo "${1}: $CC $3 succeeded" >&3 + echo "${1} ($3): yes" + elif [ -n "${4}" ]; then + echo "${1}: $CC $4 succeeded" >&3 + echo "${1} ($4): yes" + else + echo "${1}: $CC succeeded" >&3 + echo "${1}: yes" + fi + echo >&3 + + return 0 + fi + + if [ -n "$3" ]; then + echo "${1}: $CC $3 failed with $?" >&3 + echo "${1} ($3): no" + elif [ -n "${4}" ]; then + echo "${1}: $CC $4 failed with $?" >&3 + echo "${1} ($4): no" + else + echo "${1}: $CC failed with $?" >&3 + echo "${1}: no" + fi + echo >&3 + + return 1 +} + +# runtest message var extra-cflags extra-libs pkgconfig-name +runtest() { + if singletest "$1" "$2" "" ""; then + eval HAVE_${2}=1 + return 0 + fi + + if [ -n "$3" -o -n "$4" ]; then + echo "retrying with ${3+$3 }$4" >&3 + if singletest "$1" "$2" "$3" "$4"; then + if [ -n "$3" ]; then + CFLAGS="$CFLAGS $3" + fi + if [ -n "${4}" ]; then + LIBS="$LIBS $4" + fi + eval HAVE_${2}=1 + return 0 + fi + fi + + if [ -f ./../compat/${1}.c ]; then + COMPATS="${1}.o $COMPATS" + fi + + eval HAVE_${2}=0 + return 1 +} + +if runtest MMD _MMD -MMD >/dev/null; then + echo "adding -MMD to CFLAGS" >&2 + echo "adding -MMD to CFLAGS" >&3 +fi + +runtest err ERR || true +runtest freezero FREEZERO || true +runtest getexecname GETEXECNAME || true +runtest getprogname GETPROGNAME || true +runtest pledge PLEDGE || true +runtest reallocarray REALLOCARRAY -D_OPENBSD_SOURCE || true +runtest strlcat STRLCAT || true +runtest strlcpy STRLCPY || true +runtest strtonum STRTONUM || true +runtest sys_queue SYS_QUEUE || true +runtest unveil UNVEIL || true +runtest vasprintf VASPRINTF -D_GNU_SOURCE || true +runtest __progname __PROGNAME || true + +if [ "${HAVE_SYS_QUEUE}" -eq 0 ]; then + CFLAGS="-I ./../compat/sys ${CFLAGS}" +fi + +if [ -n "${COMPATS}" ]; then + CFLAGS="-I ./../compat/ ${CFLAGS}" +fi + +exec > config.h +echo "config.h: writing..." >&2 + +cat < config.mk +echo "config.mk: writing..." >&2 + +cat <&2 +echo >&2 blob - /dev/null blob + 7f67c56c3f92aa2c93b0deefc10c48f978e63b9f (mode 644) --- /dev/null +++ template/foo.tmpl @@ -0,0 +1,3 @@ +{{define x}} +{{ " | " }} +{{ end }} blob - /dev/null blob + 98ddc5d32e22f76091257f6a124fd957744c9974 (mode 644) --- /dev/null +++ template/parse.y @@ -0,0 +1,760 @@ +/* + * Copyright (c) 2022 Omar Polo + * Copyright (c) 2007-2016 Reyk Floeter + * Copyright (c) 2004, 2005 Esben Norby + * Copyright (c) 2004 Ryan McBride + * Copyright (c) 2002, 2003, 2004 Henning Brauer + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. + * Copyright (c) 2001 Theo de Raadt. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +%{ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define YYERROR_VERBOSE 1 + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); +static struct file { + TAILQ_ENTRY(file) entry; + FILE *stream; + char *name; + size_t ungetpos; + size_t ungetsize; + unsigned char *ungetbuf; + int eof_reached; + int lineno; + int errors; +} *file, *topfile; +int parse(FILE *, const char *); +struct file *pushfile(const char *, int); +int popfile(void); +int yyparse(void); +int yylex(void); +int yyerror(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) + __attribute__((__nonnull__ (1))); +int kw_cmp(const void *, const void *); +int lookup(char *); +int igetc(void); +int lgetc(int); +void lungetc(int); +int findeol(void); + +void dbg(void); +void printq(const char *); + +extern int nodebug; + +static FILE *fp; + +static int block; +static int in_define; +static int errors; +static int lastline = -1; + +typedef struct { + union { + char *string; + } v; + int lineno; +} YYSTYPE; + +%} + +%token DEFINE ELSE END ERROR FINALLY FOR IF INCLUDE PRINTF +%token RENDER TQFOREACH UNSAFE URLESCAPE WHILE +%token STRING +%type string +%type stringy + +%% + +grammar : /* empty */ + | grammar include + | grammar verbatim + | grammar block + | grammar error { file->errors++; } + ; + +include : INCLUDE STRING { + struct file *nfile; + + if ((nfile = pushfile($2, 0)) == NULL) { + yyerror("failed to include file %s", $2); + free($2); + YYERROR; + } + free($2); + + file = nfile; + lungetc('\n'); + } + ; + +verbatim : '!' verbatim1 '!' { + if (in_define) { + /* TODO: check template status and exit in case */ + } + } + ; + +verbatim1 : /* empty */ + | verbatim1 STRING { + if (*$2 != '\0') { + dbg(); + fprintf(fp, "%s\n", $2); + } + free($2); + } + ; + +verbatims : /* empty */ + | verbatims verbatim + ; + +raw : STRING { + dbg(); + fprintf(fp, "if ((tp_ret = tp->tp_puts(tp, "); + printq($1); + fputs(")) == -1) goto err;\n", fp); + + free($1); + } + ; + +block : define body end { + fputs("err:\n", fp); + fputs("return tp_ret;\n", fp); + fputs("}\n", fp); + in_define = 0; + } + | define body finally end { + fputs("return tp_ret;\n", fp); + fputs("}\n", fp); + in_define = 0; + } + ; + +define : '{' DEFINE string '}' { + in_define = 1; + + dbg(); + fprintf(fp, "int\n%s\n{\n", $3); + fputs("int tp_ret = 0;\n", fp); + + free($3); + } + ; + +body : /* empty */ + | body verbatim + | body raw + | body special + ; + +special : '{' RENDER string '}' { + dbg(); + fprintf(fp, "if ((tp_ret = %s) == -1) goto err;\n", + $3); + free($3); + } + | printf + | if body endif { fputs("}\n", fp); } + | loop + | '{' string '|' UNSAFE '}' { + dbg(); + fprintf(fp, + "if ((tp_ret = tp->tp_puts(tp, %s)) == -1)\n", + $2); + fputs("goto err;\n", fp); + free($2); + } + | '{' string '|' URLESCAPE '}' { + dbg(); + fprintf(fp, + "if ((tp_ret = tp_urlescape(tp, %s)) == -1)\n", + $2); + fputs("goto err;\n", fp); + free($2); + } + | '{' string '}' { + dbg(); + fprintf(fp, + "if ((tp_ret = tp->tp_escape(tp, %s)) == -1)\n", + $2); + fputs("goto err;\n", fp); + free($2); + } + ; + +printf : '{' PRINTF { + dbg(); + fprintf(fp, "if (asprintf(&tp->tp_tmp, "); + } printfargs '}' { + fputs(") == -1)\n", fp); + fputs("goto err;\n", fp); + fputs("if ((tp_ret = tp->tp_escape(tp, tp->tp_tmp)) " + "== -1)\n", fp); + fputs("goto err;\n", fp); + fputs("free(tp->tp_tmp);\n", fp); + fputs("tp->tp_tmp = NULL;\n", fp); + } + ; + +printfargs : /* empty */ + | printfargs STRING { + fprintf(fp, " %s", $2); + free($2); + } + ; + +if : '{' IF stringy '}' { + dbg(); + fprintf(fp, "if (%s) {\n", $3); + free($3); + } + ; + +endif : end + | else body end + | elsif body endif + ; + +elsif : '{' ELSE IF stringy '}' { + dbg(); + fprintf(fp, "} else if (%s) {\n", $4); + free($4); + } + ; + +else : '{' ELSE '}' { + dbg(); + fputs("} else {\n", fp); + } + ; + +loop : '{' FOR stringy '}' { + fprintf(fp, "for (%s) {\n", $3); + free($3); + } body end { + fputs("}\n", fp); + } + | '{' TQFOREACH STRING STRING STRING '}' { + fprintf(fp, "TAILQ_FOREACH(%s, %s, %s) {\n", + $3, $4, $5); + free($3); + free($4); + free($5); + } body end { + fputs("}\n", fp); + } + | '{' WHILE stringy '}' { + fprintf(fp, "while (%s) {\n", $3); + free($3); + } body end { + fputs("}\n", fp); + } + ; + +end : '{' END '}' + ; + +finally : '{' FINALLY '}' { + dbg(); + fputs("err:\n", fp); + } verbatims + ; + +string : STRING string { + if (asprintf(&$$, "%s %s", $1, $2) == -1) + err(1, "asprintf"); + free($1); + free($2); + } + | STRING + ; + +stringy : STRING + | STRING stringy { + if (asprintf(&$$, "%s %s", $1, $2) == -1) + err(1, "asprintf"); + free($1); + free($2); + } + | '|' stringy { + if (asprintf(&$$, "|%s", $2) == -1) + err(1, "asprintf"); + free($2); + } + ; + +%% + +struct keywords { + const char *k_name; + int k_val; +}; + +int +yyerror(const char *fmt, ...) +{ + va_list ap; + char *msg; + + file->errors++; + va_start(ap, fmt); + if (vasprintf(&msg, fmt, ap) == -1) + err(1, "yyerror vasprintf"); + va_end(ap); + fprintf(stderr, "%s:%d: %s\n", file->name, yylval.lineno, msg); + free(msg); + return (0); +} + +int +kw_cmp(const void *k, const void *e) +{ + return (strcmp(k, ((const struct keywords *)e)->k_name)); +} + +int +lookup(char *s) +{ + /* this has to be sorted always */ + static const struct keywords keywords[] = { + { "define", DEFINE }, + { "else", ELSE }, + { "end", END }, + { "finally", FINALLY }, + { "for", FOR }, + { "if", IF }, + { "include", INCLUDE }, + { "printf", PRINTF }, + { "render", RENDER }, + { "tailq-foreach", TQFOREACH }, + { "unsafe", UNSAFE }, + { "urlescape", URLESCAPE }, + { "while", WHILE }, + }; + const struct keywords *p; + + p = bsearch(s, keywords, nitems(keywords), sizeof(keywords[0]), + kw_cmp); + + if (p) + return (p->k_val); + else { fprintf(stderr, "found string >%s<\n", s); + return (STRING); } +} + +#define START_EXPAND 1 +#define DONE_EXPAND 2 + +static int expanding; + +int +igetc(void) +{ + int c; + + while (1) { + if (file->ungetpos > 0) + c = file->ungetbuf[--file->ungetpos]; + else + c = getc(file->stream); + + if (c == START_EXPAND) + expanding = 1; + else if (c == DONE_EXPAND) + expanding = 0; + else + break; + } + return (c); +} + +int +lgetc(int quotec) +{ + int c; + + if (quotec) { + if ((c = igetc()) == EOF) { + yyerror("reached end of filewhile parsing " + "quoted string"); + if (file == topfile || popfile() == EOF) + return (EOF); + return (quotec); + } + return (c); + } + + c = igetc(); +#if 0 + if (c == '\t' || c == ' ') { + /* Compress blanks to a sigle space. */ + do { + c = getc(file->stream); + } while (c == '\t' || c == ' '); + ungetc(c, file->stream); + c = ' '; + } +#endif + + if (c == EOF) { + /* + * Fake EOL when hit EOF for the first time. This gets line + * count rigchtif last line included file is syntactically + * invalid and has no newline. + */ + if (file->eof_reached == 0) { + file->eof_reached = 1; + return ('\n'); + } + while (c == EOF) { + if (file == topfile || popfile() == EOF) + return (EOF); + c = igetc(); + } + } + return (c); +} + +void +lungetc(int c) +{ + if (c == EOF) + return; + + if (file->ungetpos >= file->ungetsize) { + void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); + if (p == NULL) + err(1, "reallocarray"); + file->ungetbuf = p; + file->ungetsize *= 2; + } + file->ungetbuf[file->ungetpos++] = c; +} + +int +findeol(void) +{ + int c; + + /* skip to either EOF or the first real EOL */ + while (1) { + c = lgetc(0); + if (c == '\n') { + file->lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +int +yylex(void) +{ + char buf[8096]; + char *p = buf; + int c; + int token; + int starting = 0; + int ending = 0; + int quote = 0; + + if (!in_define && block == 0) { + while ((c = lgetc(0)) != '{' && c != EOF) { + if (c == '\n') + file->lineno++; + } + + if (c == EOF) + return (0); + +newblock: + c = lgetc(0); + if (c == '{' || c == '!') { + if (c == '{') + block = '}'; + else + block = c; + + int x; + while ((x = lgetc(0)) == ' ' || x == '\t' || x == '\n') + if (x == '\n') + file->lineno++; + lungetc(x); + + return (c); + } + if (c == '\n') + file->lineno++; + } + +#if 0 + while ((c = lgetc(0)) == ' ' || c == '\t' || c == '\n') { + if (c == '\n') + file->lineno++; + } +#else + if ((c = lgetc(0)) == '\n') + file->lineno++; +#endif + + if (c == EOF) { + yyerror("unterminated block"); + return (0); + } + + yylval.lineno = file->lineno; + + if (block != 0 && c == block) { + if ((c = lgetc(0)) == '}') { + if (block == '!') { + block = 0; + return ('!'); + } + block = 0; + return ('}'); + } + lungetc(c); + c = block; + } + + if (in_define && block == 0) { + if (c == '{') + goto newblock; + + do { + if (starting) { + if (c == '!' || c == '{') { + lungetc(c); + lungetc('{'); + break; + } + starting = 0; + lungetc(c); + c = '{'; + } else if (c == '{') { + starting = 1; + continue; + } + + *p++ = c; + if ((size_t)(p - buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && c != '\n'); + *p = '\0'; + if (c == EOF) { + yyerror("unterminated block"); + return (0); + } + if (c == '\n') + file->lineno++; + if ((yylval.v.string = strdup(buf)) == NULL) + err(1, "strdup"); + return (STRING); + } + + if (block == '!') { + do { + if (ending) { + if (c == '}') { + lungetc(c); + lungetc(block); + break; + } + ending = 0; + lungetc(c); + c = block; + } else if (c == '!') { + ending = 1; + continue; + } + + *p++ = c; + if ((size_t)(p - buf) >= sizeof(buf)) { + yyerror("line too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && c != '\n'); + *p = '\0'; + + if (c == EOF) { + yyerror("unterminated block"); + return (0); + } + if (c == '\n') + file->lineno++; + + if ((yylval.v.string = strdup(buf)) == NULL) + err(1, "strdup"); + return (STRING); + } + + if (c == '|') + return (c); + + do { + if (!quote && isspace((unsigned char)c)) + break; + + if (c == '"') + quote = !quote; + + if (!quote && c == '|') { + lungetc(c); + break; + } + + if (ending) { + if (c == '}') { + lungetc(c); + lungetc('}'); + break; + } + ending = 0; + lungetc(c); + c = block; + } else if (!quote && c == '}') { + ending = 1; + continue; + } + + *p++ = c; + if ((size_t)(p - buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF); + *p = '\0'; + + if (c == EOF) { + yyerror(quote ? "unterminated quote" : "unterminated block"); + return (0); + } + if (c == '\n') + file->lineno++; + if ((token = lookup(buf)) == STRING) + if ((yylval.v.string = strdup(buf)) == NULL) + err(1, "strdup"); + return (token); +} + +struct file * +pushfile(const char *name, int secret) +{ + struct file *nfile; + + if ((nfile = calloc(1, sizeof(*nfile))) == NULL) + err(1, "calloc"); + if ((nfile->name = strdup(name)) == NULL) + err(1, "strdup"); + if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { + warn("can't open %s", nfile->name); + free(nfile->name); + free(nfile); + return (NULL); + } + nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; + nfile->ungetsize = 16; + nfile->ungetbuf = malloc(nfile->ungetsize); + if (nfile->ungetbuf == NULL) + err(1, "malloc"); + TAILQ_INSERT_TAIL(&files, nfile, entry); + return (nfile); +} + +int +popfile(void) +{ + struct file *prev; + + if ((prev = TAILQ_PREV(file, files, entry)) != NULL) + prev->errors += file->errors; + + TAILQ_REMOVE(&files, file, entry); + fclose(file->stream); + free(file->name); + free(file->ungetbuf); + free(file); + file = prev; + return (file ? 0 : EOF); +} + +int +parse(FILE *outfile, const char *filename) +{ + fp = outfile; + + if ((file = pushfile(filename, 0)) == 0) + return (-1); + topfile = file; + + yyparse(); + errors = file->errors; + popfile(); + + return (errors ? -1 : 0); +} + +void +dbg(void) +{ + if (nodebug) + return; + + if (yylval.lineno == lastline + 1) { + lastline = yylval.lineno; + return; + } + lastline = yylval.lineno; + + fprintf(fp, "#line %d ", yylval.lineno); + printq(file->name); + putc('\n', fp); +} + +void +printq(const char *str) +{ + putc('"', fp); + for (; *str; ++str) { + if (*str == '"') + putc('\\', fp); + putc(*str, fp); + } + putc('"', fp); +} blob - /dev/null blob + e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (mode 644) blob - /dev/null blob + c2d0fad50754e9ec3740571ad5e77d7dc475ab26 (mode 644) --- /dev/null +++ template/regress/01-noise-only.tmpl @@ -0,0 +1,2 @@ +only +noise blob - /dev/null blob + c35618a4287faab518c419c4bbbe06ecbca7f157 (mode 644) --- /dev/null +++ template/regress/02-only-verbatim.tmpl @@ -0,0 +1,17 @@ +{! +#include + +#include "tmpl.h" +!} + +noise {! /* woops */ !} +here + +{! +int +main(void) +{ + puts("hello, world!"); + return (0); +} +!} blob - /dev/null blob + 270c611ee72c567bc1b2abec4cbc345bab9f15ba (mode 644) --- /dev/null +++ template/regress/02.expected @@ -0,0 +1 @@ +hello, world! blob - /dev/null blob + d866f69531b2f1248c5bb6940f9b8c40adf87c82 (mode 644) --- /dev/null +++ template/regress/03-block.tmpl @@ -0,0 +1,22 @@ +{! +#include + +#include "tmpl.h" +!} + +{{ define base(struct template *tp, const char *title) }} +{! char *foo = NULL; !} + + + + {{ title }} + + {! /* TODO: frobnicate this line! */ !} +

{{ title }}

+ {{ " | " }} + {{ "other stuff" }} + + +{{ finally }} +{! free(foo); !} +{{ end }} blob - /dev/null blob + 0f766820b9e5de1343ca8034bea3b2b0a4418221 (mode 644) --- /dev/null +++ template/regress/03.expected @@ -0,0 +1,2 @@ + *hello*

*hello*

| other stuff +<hello>

<hello>

| other stuff blob - /dev/null blob + aa8c5edc9b8934fea4803f8b465a7f00fb389e23 (mode 644) --- /dev/null +++ template/regress/04-flow.tmpl @@ -0,0 +1,31 @@ +{! +#include +#include + +#include "tmpl.h" +!} + +{{ define base(struct template *tp, const char *title) }} +{! char *foo = NULL; !} + + + + {{ title }} + + +

{{ title }}

+ {{ if strchr(title, '*') != NULL }} +

"{{ title }}" has a '*' in it

+ {{ if 1 }} +

tautology!

+ {{ end }} + {{ else if strchr(title, '=') != NULL }} +

"{{ title }}" has a '=' in it!

+ {{ else }} +

"{{ title }}" doesn't have a '*' in it

+ {{ end }} + + +{{ finally }} +{! free(foo); !} +{{ end }} blob - /dev/null blob + 32240e27d9a6dbeeb395f7e760e2a043e29b939c (mode 644) --- /dev/null +++ template/regress/04.expected @@ -0,0 +1,2 @@ + *hello*

*hello*

" *hello* " has a '*' in it

tautology!

+<hello>

<hello>

"<hello>" doesn't have a '*' in it

blob - /dev/null blob + d47673dc5bf15202d96329fe8d971d117bc930eb (mode 644) --- /dev/null +++ template/regress/05-loop.tmpl @@ -0,0 +1,42 @@ +{! +#include +#include +#include "lists.h" +#include "tmpl.h" + +int list(struct template *, struct tailhead *); + +!} + +{{ define base(struct template *tp, struct tailhead *head) }} + + + + {{ render list(tp, head) }} + + +{{ end }} + +{{ define list(struct template *tp, struct tailhead *head) }} +{! + struct entry *np; + int i; +!} + {{ if !TAILQ_EMPTY(head) }} +

items:

+
    + {{ tailq-foreach np head entries }} +
  • {{ np->text }}
  • + {{ end }} +
+ {{ else }} +

no items

+ {{ end }} + +

+ {{ for i = 0; i < 3; ++i }} + hello{{ " " }} + {{ end }} + world! +

+{{ end }} blob - /dev/null blob + d4c20d67eeee5e6e0217d8abd4c19f2d440ba6d9 (mode 644) --- /dev/null +++ template/regress/05.expected @@ -0,0 +1,2 @@ +

items:

  • 1
  • 2

hello hello hello world!

+

no items

hello hello hello world!

blob - /dev/null blob + f5450fbc2e7134e6fb5f5798e14983545f6347d4 (mode 644) --- /dev/null +++ template/regress/06-escape.tmpl @@ -0,0 +1,17 @@ +{! +#include + +#include "tmpl.h" +!} + +{{ define base(struct template *tp, const char *title) }} + + + + {{ title | urlescape }} + + +

{{ title | unsafe }}

+ + +{{ end }} blob - /dev/null blob + 6a9d734b454093206236753143743d95d5e473af (mode 644) --- /dev/null +++ template/regress/06.expected @@ -0,0 +1,2 @@ +%20*hello*%20

*hello*

+<hello>

blob - /dev/null blob + 8b48d50f1257a72de04bbd33038320dd05edca49 (mode 644) --- /dev/null +++ template/regress/07-printf.tmpl @@ -0,0 +1,10 @@ +{! +#include +#include + +#include "tmpl.h" +!} + +{{ define base(struct template *tp, const char *title) }} +{{ printf "%.2s:\t%d\n", title, 42 }} +{{ end }} blob - /dev/null blob + 681daf3915b708a380ddc75467b47beceff9b367 (mode 644) --- /dev/null +++ template/regress/07.expected @@ -0,0 +1,4 @@ + *: 42 + +<h: 42 + blob - /dev/null blob + 5b41a3b36ca0223dab174dafa40e73c572f9a203 (mode 644) --- /dev/null +++ template/regress/Makefile @@ -0,0 +1,60 @@ +REGRESS_TARGETS = 00-empty \ + 01-noise-only \ + 02-only-verbatim \ + 03-block \ + 04-flow \ + 05-loop \ + 06-escape \ + 07-printf + +REGRESS_SETUP_ONCE = setup-comp +REGRESS_CLEANUP = clean-comp +NO_OBJ = Yes + +CFLAGS += -I${.CURDIR}/../ + +setup-comp: + cp ${.CURDIR}/../tmpl.c . + ln -f ${.CURDIR}/../template template || \ + ln -f ${.CURDIR}/../obj/template template + +clean-comp: + rm template + rm -f t got 0*.[cdo] runbase.[do] runlist.[do] tmpl.* + +.SUFFIXES: .tmpl .c .o + +.tmpl.c: + ./template -o $@ $? + +00-empty: + ./template 00-empty.tmpl >/dev/null + +01-noise-only: + ./template 01-noise-only.tmpl >/dev/null + +02-only-verbatim: 02-only-verbatim.o tmpl.o + ${CC} 02-only-verbatim.o tmpl.o -o t && ./t > got + diff -u ${.CURDIR}/02.expected got + +03-block: 03-block.o runbase.o tmpl.o + ${CC} 03-block.o runbase.o tmpl.o -o t && ./t > got + diff -u ${.CURDIR}/03.expected got + +04-flow: 04-flow.o runbase.o tmpl.o + ${CC} 04-flow.o runbase.o tmpl.o -o t && ./t > got + diff -u ${.CURDIR}/04.expected got + +05-loop: 05-loop.o runlist.o tmpl.o + ${CC} 05-loop.o runlist.o tmpl.o -o t && ./t > got + diff -u ${.CURDIR}/05.expected got + +06-escape: 06-escape.o runbase.o tmpl.o + ${CC} 06-escape.o runbase.o tmpl.o -o t && ./t > got + diff -u ${.CURDIR}/06.expected got + +07-printf: 07-printf.o runbase.o tmpl.o + ${CC} 07-printf.o runbase.o tmpl.o -o t && ./t > got + diff -u ${.CURDIR}/07.expected got + +.include blob - /dev/null blob + 7229706ecd6bfb0bb2f38be92950b7082f7a6c41 (mode 644) --- /dev/null +++ template/regress/lists.h @@ -0,0 +1,7 @@ +#include + +TAILQ_HEAD(tailhead, entry); +struct entry { + char *text; + TAILQ_ENTRY(entry) entries; +}; blob - /dev/null blob + bde2b4cb20be96fc0b83abde12d5c55a2a7a4ac4 (mode 644) --- /dev/null +++ template/regress/runbase.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "tmpl.h" + +int base(struct template *, const char *title); + +int +my_putc(struct template *tp, int c) +{ + FILE *fp = tp->tp_arg; + + if (putc(c, fp) < 0) + return (-1); + + return (0); +} + +int +my_puts(struct template *tp, const char *s) +{ + FILE *fp = tp->tp_arg; + + if (fputs(s, fp) < 0) + return (-1); + + return (0); +} + +int +main(int argc, char **argv) +{ + struct template *tp; + + if ((tp = template(stdout, my_puts, my_putc)) == NULL) + err(1, "template"); + + if (base(tp, " *hello* ") == -1) + return (1); + puts(""); + + if (base(tp, "") == -1) + return (1); + puts(""); + + free(tp); + return (0); +} blob - /dev/null blob + cafb14beb743d07677cd510ccfae45e5be4fd612 (mode 644) --- /dev/null +++ template/regress/runlist.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "tmpl.h" +#include "lists.h" + +int base(struct template *, struct tailhead *); + +int +my_putc(struct template *tp, int c) +{ + FILE *fp = tp->tp_arg; + + if (putc(c, fp) < 0) + return (-1); + + return (0); +} + +int +my_puts(struct template *tp, const char *s) +{ + FILE *fp = tp->tp_arg; + + if (fputs(s, fp) < 0) + return (-1); + + return (0); +} + +int +main(int argc, char **argv) +{ + struct template *tp; + struct tailhead head; + struct entry *np; + int i; + + if ((tp = template(stdout, my_puts, my_putc)) == NULL) + err(1, "template"); + + TAILQ_INIT(&head); + for (i = 0; i < 2; ++i) { + if ((np = calloc(1, sizeof(*np))) == NULL) + err(1, "calloc"); + if (asprintf(&np->text, "%d", i+1) == -1) + err(1, "asprintf"); + TAILQ_INSERT_TAIL(&head, np, entries); + } + + if (base(tp, &head) == -1) + return (1); + puts(""); + + while ((np = TAILQ_FIRST(&head))) { + TAILQ_REMOVE(&head, np, entries); + free(np->text); + free(np); + } + + if (base(tp, &head) == -1) + return (1); + puts(""); + + free(tp); + + return (0); +} blob - /dev/null blob + 48765fbe52f01bd15fa5126aa7f5d4029d94172e (mode 644) --- /dev/null +++ template/template.1 @@ -0,0 +1,85 @@ +.\" Copyright (c) 2022 Omar Polo +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd January 06, 2022 +.Dt TEMPLATE 1 +.Os +.Sh NAME +.Nm template +.Nd templating system compiler +.Sh SYNOPSIS +.Nm +.Op Fl G +.Op Fl o Ar out +.Op Ar +.Sh DESCRIPTION +.Nm +is an utility that converts files written in the +.Xr template 7 +format format to a set of routine writtens in the C programming +language. +.Nm +converts the files given as arguments or from standard input, and +writes to standard output. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl G +Do not emit debug info in the generated source. +It's disabled by default, unless +.Nm +is reading from standard input. +.It Fl o Ar out +Write output to file. +.Ar out +will be created or truncated if exists and will be removed if +.Nm +encounters any error. +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr template 7 +.Sh AUTHORS +.An -nosplit +The +.Nm +utility was written by +.An Omar Polo Aq Mt op@openbsd.org . +.Sh CAVEATS +The compiler is very naive, so there are quite a few shortcomings: +.Bl -bullet -compact +.It +No attempt is made to validate the C code provided inline, nor the +validity of the arguments to many constructs. +.It +The generated code assumes that a variable called +.Va tp +of type +.Vt struct template * +is in scope inside each block. +.It +Each block may have additional variables used for the template +generation implicitly defined: to avoid clashes, don't name variables +or arguments with the +.Sq tp_ +prefix. +.It +Blanks are, in most cases, trimmed. +Normally this is not a problem, but a workaround is needed in case +they need to be preserved, for e.g.: +.Bd -literal -offset indent +Name: {{ " " }} {{ render name_field(tp) }} +.Ed +.El blob - /dev/null blob + c344796aaae84d607d9948f2ace4f85729f3c7d8 (mode 644) --- /dev/null +++ template/template.7 @@ -0,0 +1,127 @@ +.\" Copyright (c) 2022 Omar Polo +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd January 06, 2022 +.Dt TEMPLATE 7 +.Os +.Sh NAME +.Nm template +.Nd templating language +.Sh DESCRIPTION +.Nm +is a language used to define programs that output data in some way. +These programs are called +.Dq templates . +A +.Nm +file is assumed to be compiled using the +.Xr template 1 +utility into C code, to be further compiled as part of a bigger +application. +The language itself is format-agnostic and can thus be used to produce +various type of outputs. +.Pp +There are two special sequences: +.Bl -tag -width 9m +.It Cm {{ Ar ... Cm }} +used for +.Nm +special syntax. +.It Cm {! Ar ... Cm !} +used to include literal C code. +This is the only special syntax permitted as top-level, except for block +definition and includes. +.El +.Pp +The basic unit of a +.Nm +file is the block. +Each block is turned into a C function that output its content via some +provided functions. +Here's an example of a block: +.Bd -literal -offset indent +{{ define tp_base(struct template *tp, const char *title) }} + + + + {{ title }} + + + {{ render tp->tp_body(tp) }} + + +{{ end }} +.Ed +.Ss SPECIAL SYNTAX +This section is a reference for all the special syntaxes supported. +.Bl -tag -width Ds +.It Cm {{ Ic include Ar file Cm }} +Include additional template files. +.It Cm {{ Ic define Ar name Ns ( Ar arguments ... ) Cm }} Ar body Cm {{ Ic end Cm }} +Defines the block +.Ar name +with the given +.Ar arguments . +.Ar body +will be outputted as-is via the provided functions +.Pq i.e.\& is still escaped eventually +and can contain all the special syntaxes documented here except +.Ic include +and +.Ic define . +.It Cm {{ Ic render Ar expression() Cm }} +Executes +.Ar expression() +and terminate the template if it returns -1. +It's used to render (call) another template. +.It Cm {{ Ic printf Ar fmt , Ar arguments ... Cm }} +Outputs the string that would be produced by calling +.Xr printf 3 +with the given +.Ar fmt +format string and the given +.Ar arguments . +.It Cm {{ Ic if Ar expr Cm }} Ar ... Cm {{ Ic elseif Ar expr Cm }} Ar ... Cm {{ Ic else Cm }} Ar ... Cm {{ Ic end Cm }} +Conditional evaluation. +.Ic elseif +can be provided zero or more times, +.Ic else +only zero or one time and always for last. +.It Cm {{ Ic for Ar ... ; Ar ... ; Ar ... Cm }} Ar ... Cm {{ Ic end Cm }} +Looping construct similar to the C for loop. +.It Cm {{ Ic tailq-foreach Ar var head fieldname Cm }} Ar .. Cm {{ Ic end Cm }} +Looping construct similar to the queue.h macro TAILQ_FOREACH. +.It Cm {{ Ic while Ar ... Cm }} Ar ... Cm {{ Ic end Cm }} +Looping construct similar to the C while loop. +.It Cm {{ Ar expression Cm \&| Ic unsafe Cm }} +Output +.Ar expression +as-is. +.It Cm {{ Ar expression Cm \&| Ic urlescape Cm }} +Output +.Ar expression +escaped in a way that can be made part of an URL. +.It Cm {{ Ar expression Cm }} +Output +.Ar expression +with the default escaping. +.El +.Sh SEE ALSO +.Xr template 1 +.Sh AUTHORS +.An -nosplit +The +.Nm +reference was written by +.An Omar Polo Aq Mt op@openbsd.org . blob - /dev/null blob + d8f3e5383a96a5faf569ee9b566218d8f14189c1 (mode 644) --- /dev/null +++ template/template.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +int parse(FILE *, const char *); + +int nodebug; + +static void __dead +usage(void) +{ + fprintf(stderr, "usage: %s [file...]\n", + getprogname()); + exit(1); +} + +int +main(int argc, char **argv) +{ + FILE *fp = stdout; + const char *out = NULL; + int ch, i; + + while ((ch = getopt(argc, argv, "Go:")) != -1) { + switch (ch) { + case 'G': + nodebug = 1; + break; + case 'o': + out = optarg; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (out && (fp = fopen(out, "w")) == NULL) + err(1, "can't open %s", out); + + if (out && unveil(out, "wc") == -1) + err(1, "unveil %s", out); + if (unveil("/", "r") == -1) + err(1, "unveil /"); + if (pledge(out ? "stdio rpath cpath" : "stdio rpath", NULL) == -1) + err(1, "pledge"); + + if (argc == 0) { + nodebug = 1; + if (parse(fp, "/dev/stdin") == -1) + goto err; + } else { + for (i = 0; i < argc; ++i) + if (parse(fp, argv[i]) == -1) + goto err; + } + + if (ferror(fp)) + goto err; + + if (fclose(fp) == -1) { + fp = NULL; + goto err; + } + + return (0); + +err: + if (fp) + fclose(fp); + if (out && unlink(out) == -1) + err(1, "unlink %s", out); + return (1); +} blob - /dev/null blob + f125c861a661eea5d4cb494eb708b2fb02003b88 (mode 644) --- /dev/null +++ template/template.d @@ -0,0 +1 @@ +template.o: template.c blob - /dev/null blob + 18ee428516dc3b90ef925c5ac135e90278c0d5f9 (mode 644) --- /dev/null +++ template/tmpl.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2022 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "tmpl.h" + +int +tp_urlescape(struct template *tp, const char *str) +{ + int r; + char tmp[4]; + + if (str == NULL) + return (0); + + for (; *str; ++str) { + if (iscntrl((unsigned char)*str) || + isspace((unsigned char)*str) || + *str == '\'' || *str == '"' || *str == '\\') { + r = snprintf(tmp, sizeof(tmp), "%%%2X", *str); + if (r < 0 || (size_t)r >= sizeof(tmp)) + return (0); + if (tp->tp_puts(tp, tmp) == -1) + return (-1); + } else { + if (tp->tp_putc(tp, *str) == -1) + return (-1); + } + } + + return (0); +} + +int +tp_htmlescape(struct template *tp, const char *str) +{ + int r; + + if (str == NULL) + return (0); + + for (; *str; ++str) { + switch (*str) { + case '<': + r = tp->tp_puts(tp, "<"); + break; + case '>': + r = tp->tp_puts(tp, ">"); + break; + case '&': + r = tp->tp_puts(tp, "&"); + break; + case '"': + r = tp->tp_puts(tp, """); + break; + case '\'': + r = tp->tp_puts(tp, "'"); + break; + default: + r = tp->tp_putc(tp, *str); + break; + } + + if (r == -1) + return (-1); + } + + return (0); +} + +struct template * +template(void *arg, tmpl_puts putsfn, tmpl_putc putcfn) +{ + struct template *tp; + + if ((tp = calloc(1, sizeof(*tp))) == NULL) + return (NULL); + + tp->tp_arg = arg; + tp->tp_escape = tp_htmlescape; + tp->tp_puts = putsfn; + tp->tp_putc = putcfn; + + return (tp); +} + +void +template_free(struct template *tp) +{ + free(tp->tp_tmp); + free(tp); +} blob - /dev/null blob + c1e0f61eabf6578836278219e4b359dedea12681 (mode 644) --- /dev/null +++ template/tmpl.d @@ -0,0 +1 @@ +template/tmpl.o: template/tmpl.c template/tmpl.h blob - /dev/null blob + 4c8de903c9b7957de3c6bc1ed2058fa9a5db553b (mode 644) --- /dev/null +++ template/tmpl.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef TMPL_H +#define TMPL_H + +struct template; + +typedef int (*tmpl_puts)(struct template *, const char *); +typedef int (*tmpl_putc)(struct template *, int); + +struct template { + void *tp_arg; + char *tp_tmp; + tmpl_puts tp_escape; + tmpl_puts tp_puts; + tmpl_putc tp_putc; +}; + +int tp_urlescape(struct template *, const char *); +int tp_htmlescape(struct template *, const char *); + +struct template *template(void *, tmpl_puts, tmpl_putc); +void template_free(struct template *); + +#endif blob - /dev/null blob + 95485db80176bf6c2746a0ef6ed0107bcd87e1bc (mode 644) --- /dev/null +++ tests/MMD.c @@ -0,0 +1,5 @@ +int +main(void) +{ + return 0; +} blob - /dev/null blob + eeea39ab2f7055479d97e4cc3295a54e92afa194 (mode 644) --- /dev/null +++ tests/Makefile @@ -0,0 +1,43 @@ +DISTFILES = Makefile \ + MMD.c \ + WAIT_ANY.c \ + __progname.c \ + accept4.c \ + asr_run.c \ + bufferevent_read_pressure_cb.c \ + err.c \ + event_asr_run.c \ + freezero.c \ + getdtablecount.c \ + getdtablesize.c \ + getexecname.c \ + getprogname.c \ + imsg.c \ + libevent.c \ + libevent2.c \ + libsqlite3.c \ + libtls.c \ + pledge.c \ + reallocarray.c \ + recallocarray.c \ + setgroups.c \ + setproctitle.c \ + setresgid.c \ + setresuid.c \ + strlcat.c \ + strlcpy.c \ + strtonum.c \ + sys_queue.c \ + sys_tree.c \ + unveil.c \ + vasprintf.c + +all: + false + +dist: ${DISTFILES} + mkdir -p ${DESTDIR}/ + ${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/ + +.PHONY: all dist +include ../config.mk blob - /dev/null blob + 970e15419a8408b4772603b33c51259415d75d4e (mode 644) --- /dev/null +++ tests/WAIT_ANY.c @@ -0,0 +1,7 @@ +#include + +int +main(void) +{ + return WAIT_ANY; +} blob - /dev/null blob + 1964c2b48a27a9f94f4a1fa7427b63f653b7b3c2 (mode 644) --- /dev/null +++ tests/__progname.c @@ -0,0 +1,10 @@ +#include + +extern const char *__progname; + +int +main(void) +{ + puts(__progname); + return 0; +} blob - /dev/null blob + 3e2af5d5e26a4a2ceabfae40f10bbf66bee748bf (mode 644) --- /dev/null +++ tests/accept4.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +int +main(void) +{ + accept4(0, NULL, 0, SOCK_NONBLOCK); + return 0; +} blob - /dev/null blob + 3478809e685017e20e2b20667fac8c9e40cf19d8 (mode 644) --- /dev/null +++ tests/asr_run.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include +#include +#include + +int +main(void) +{ + struct asr_query *query; + struct asr_result ar; + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + query = getaddrinfo_async("openbsd.org", "www", &hints, NULL); + if (query == NULL) + return 1; + + asr_run_sync(query, &ar); + + freeaddrinfo(ar.ar_addrinfo); + return 0; +} blob - /dev/null blob + 93eb816d939712b0bb93d05c88810f247ead41f7 (mode 644) --- /dev/null +++ tests/err.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +int +main(void) +{ + warnx("%d. warnx", 1); + warn("%d. warn", 2); + err(0, "%d. err", 3); + return 1; +} blob - /dev/null blob + 060264e7c011ca4961c63fdeda4d7af53e14cf8c (mode 644) --- /dev/null +++ tests/event_asr_run.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include +#include +#include +#include + +int +main(void) +{ + struct asr_query *query; + struct addrinfo hints; + + event_init(); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + query = getaddrinfo_async("openbsd.org", "www", &hints, NULL); + if (query == NULL) + return 1; + + /* just testing whether it compiles */ + event_asr_run(query, NULL, NULL); + + return 0; +} blob - /dev/null blob + 1b15cbfca63d973754b5a508ee3f2dd5def3bbe9 (mode 644) --- /dev/null +++ tests/freezero.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +int +main(void) +{ + freezero(NULL, 0); + return 0; +} blob - /dev/null blob + 67558279c11e1dd7ba8d29d10ba83b9b00e3fd68 (mode 644) --- /dev/null +++ tests/getdtablecount.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +int +main(void) +{ + return getdtablecount() == 0; +} blob - /dev/null blob + 86fdae393a79943e168e0bef67adb83e5751218c (mode 644) --- /dev/null +++ tests/getdtablesize.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +int +main(void) +{ + return getdtablesize() == 0; +} blob - /dev/null blob + 2c53cab9c23171ab5aeb4c66a5851dba8d12de51 (mode 644) --- /dev/null +++ tests/getexecname.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +int +main(void) +{ + const char *progname; + + progname = getexecname(); + return (progname == NULL); +} blob - /dev/null blob + 57c0e2b059de0ac1a0662f96d0fe211a8f30e3db (mode 644) --- /dev/null +++ tests/getprogname.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +int +main(void) +{ + return getprogname() == NULL; +} blob - /dev/null blob + e0b63cd9b08fac391097b56a76185ca4816924eb (mode 644) --- /dev/null +++ tests/libevent.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +int +main(void) +{ + event_init(); + return 0; +} blob - /dev/null blob + 8a4b11f39666df7c2f5c94447ca54b285e1d7bc2 (mode 644) --- /dev/null +++ tests/libsqlite3.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +int +main(void) +{ + struct sqlite3 *db; + int err; + + err = sqlite3_open_v2(":memory:", &db, SQLITE_OPEN_READONLY, NULL); + if (err != SQLITE_OK) + return (1); + + err = sqlite3_close(db); + if (err != SQLITE_OK) + return (1); + + return (0); +} blob - /dev/null blob + 8878b01d6c97df185cfb7031e92b28545e0025a9 (mode 644) --- /dev/null +++ tests/pledge.c @@ -0,0 +1,10 @@ +#include +#include + +int +main(void) +{ + if (pledge("stdio", NULL) == -1) + return 1; + return 0; +} blob - /dev/null blob + f99e685d8350c6a98901404fbc059b61607c14f9 (mode 644) --- /dev/null +++ tests/reallocarray.c @@ -0,0 +1,7 @@ +#include + +int +main(void) +{ + return !reallocarray(NULL, 2, 2); +} blob - /dev/null blob + e0c60d7118f8b94719e18fa5efadb3daf9b36833 (mode 644) --- /dev/null +++ tests/recallocarray.c @@ -0,0 +1,11 @@ +#include + +int +main(void) +{ + void *p; + + if ((p = calloc(2, 2)) == NULL) + return 1; + return !recallocarray(p, 2, 3, 2); +} blob - /dev/null blob + 3c972c73ae245c44960d431c192d4cc52f95994a (mode 644) --- /dev/null +++ tests/setgroups.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +int +main(void) +{ + struct passwd *pw; + + pw = getpwnam("www"); + if (pw == NULL) + return 1; + + setgroups(1, &pw->pw_gid); + return 0; +} blob - /dev/null blob + d54a63e74996e9df0d6a249669510f7cad852ecd (mode 644) --- /dev/null +++ tests/setproctitle.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * FreeBSD has setproctitle in a different header than OpenBSD. + */ + +#include + +#include +#include + +int +main(void) +{ + setproctitle("%s", "frobnicator"); + return 0; +} blob - /dev/null blob + 616458f82280192cdb500175616ed252dfe19928 (mode 644) --- /dev/null +++ tests/setresgid.c @@ -0,0 +1,8 @@ +#include +#include + +int +main(void) +{ + return setresgid(-1, -1, -1) == -1; +} blob - /dev/null blob + 0f3f65c864ef1d839583399c33676500daf4c72a (mode 644) --- /dev/null +++ tests/setresuid.c @@ -0,0 +1,8 @@ +#include +#include + +int +main(void) +{ + return setresuid(-1, -1, -1) == -1; +} blob - /dev/null blob + 0a005c57f59ab5c0b9c5cb707940cb875ce21dbb (mode 644) --- /dev/null +++ tests/strlcat.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +int +main(void) +{ + char buf[3] = "a"; + return ! (strlcat(buf, "b", sizeof(buf)) == 2 && + buf[0] == 'a' && buf[1] == 'b' && buf[2] == '\0'); +} blob - /dev/null blob + d1789354e72631744c3a953289693faa450da222 (mode 644) --- /dev/null +++ tests/strlcpy.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +int +main(void) +{ + char buf[2] = ""; + return ! (strlcpy(buf, "a", sizeof(buf)) == 1 && + buf[0] == 'a' && buf[1] == '\0'); +} blob - /dev/null blob + 6e2aad2efbc46b09473e200b9a648019bdde8554 (mode 644) --- /dev/null +++ tests/strtonum.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +int +main(void) +{ + const char *str = "42", *errstr; + int res; + + res = strtonum(str, 1, 64, &errstr); + return res != 42 || errstr != NULL; +} blob - /dev/null blob + 81d3c6166446ea4d4da6944141bfff27fe02d9a5 (mode 644) --- /dev/null +++ tests/sys_queue.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +TAILQ_HEAD(tailhead, entry) head; +struct entry { + TAILQ_ENTRY(entry) entries; +} *np, *nt; + +int +main(void) +{ + TAILQ_INIT(&head); + TAILQ_FOREACH_SAFE(np, &head, entries, nt) { + /* nop */; + } + + return 0; +} blob - /dev/null blob + 795c578610adb6931f3ba912d4a0152822d044c2 (mode 644) --- /dev/null +++ tests/sys_tree.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +struct node { + int id; + SPLAY_ENTRY(node) nodes; +}; +SPLAY_HEAD(node_tree, node); + +int +node_cmp(struct node *a, struct node *b) +{ + return (a->id - b->id); +} + +SPLAY_PROTOTYPE(node_tree, node, nodes, node_cmp); +SPLAY_GENERATE(node_tree, node, nodes, node_cmp); + +int +main(void) +{ + return 0; +} blob - /dev/null blob + 0582be2a1f74f3d5d0728115dd45ac33a069ee81 (mode 644) --- /dev/null +++ tests/unveil.c @@ -0,0 +1,10 @@ +#include +#include + +int +main(void) +{ + if (unveil(".", "r") == -1) + return 1; + return 0; +} blob - /dev/null blob + 2d1ac0b1d15ccd93ef5b50e92cb00d65f009f3b0 (mode 644) --- /dev/null +++ tests/vasprintf.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +static int testfn(char **, const char*, ...); + +static int +testfn(char **ret, const char *fmt, ...) +{ + va_list ap; + int irc; + + va_start(ap, fmt); + irc = vasprintf(ret, fmt, ap); + va_end(ap); + + return irc; +} + +int +main(void) +{ + char *ret; + + if (testfn(&ret, "%s.", "Text") != 5) + return 1; + if (strcmp(ret, "Text.")) + return 2; + return 0; +} blob - /dev/null blob + b7db175dfd8ea2b00dc361998c459a66ad831a2d (mode 644) --- /dev/null +++ ui.tmpl @@ -0,0 +1,38 @@ +{! +/* + * Copyright (c) 2023 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* #include */ +#include + +#include +#include + +#include "pkg.h" +#include "tmpl.h" + +!} + +{{ define tp_home(struct template *tp) }} +# pkg_fcgi + +Welcome to pkg_fcgi, the Gemini interface for the OpenBSD ports collection! + +=> /search Search for a package +=> /all/ Browse all categories + +What you search will be matched against the package name (pkgstem), comment, description, and maintainer. +{{ end }} blob - /dev/null blob + f05ceadf51129ffe6559594eeea12fa517db5f69 (mode 644) --- /dev/null +++ xmalloc.c @@ -0,0 +1,100 @@ +/* $OpenBSD: xmalloc.c,v 1.4 2019/06/28 05:44:09 deraadt Exp $ */ +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "xmalloc.h" + +void * +xmalloc(size_t size) +{ + void *ptr; + + if (size == 0) + fatal("xmalloc: zero size"); + ptr = malloc(size); + if (ptr == NULL) + fatal("xmalloc: allocating %zu bytes", size); + return ptr; +} + +void * +xcalloc(size_t nmemb, size_t size) +{ + void *ptr; + + if (size == 0 || nmemb == 0) + fatal("xcalloc: zero size"); + ptr = calloc(nmemb, size); + if (ptr == NULL) + fatal("xcalloc: allocating %zu * %zu bytes", nmemb, size); + return ptr; +} + +void * +xreallocarray(void *ptr, size_t nmemb, size_t size) +{ + void *new_ptr; + + new_ptr = reallocarray(ptr, nmemb, size); + if (new_ptr == NULL) + fatal("xreallocarray: allocating %zu * %zu bytes", + nmemb, size); + return new_ptr; +} + +void * +xrecallocarray(void *ptr, size_t oldnmemb, size_t nmemb, size_t size) +{ + void *new_ptr; + + new_ptr = recallocarray(ptr, oldnmemb, nmemb, size); + if (new_ptr == NULL) + fatal("xrecallocarray: allocating %zu * %zu bytes", + nmemb, size); + return new_ptr; +} + +char * +xstrdup(const char *str) +{ + char *cp; + + if ((cp = strdup(str)) == NULL) + fatal("xstrdup"); + return cp; +} + +int +xasprintf(char **ret, const char *fmt, ...) +{ + va_list ap; + int i; + + va_start(ap, fmt); + i = vasprintf(ret, fmt, ap); + va_end(ap); + + if (i == -1) + fatal("xasprintf"); + + return i; +} blob - /dev/null blob + 5e11cb4e79d681af3c5b8787d9308302336a092f (mode 644) --- /dev/null +++ xmalloc.h @@ -0,0 +1,31 @@ +/* $OpenBSD: xmalloc.h,v 1.3 2015/11/17 18:25:03 tobias Exp $ */ + +/* + * Author: Tatu Ylonen + * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland + * All rights reserved + * Created: Mon Mar 20 22:09:17 1995 ylo + * + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef XMALLOC_H +#define XMALLOC_H + +void *xmalloc(size_t); +void *xcalloc(size_t, size_t); +void *xreallocarray(void *, size_t, size_t); +void *xrecallocarray(void *, size_t, size_t, size_t); +char *xstrdup(const char *); +int xasprintf(char **, const char *, ...) + __attribute__((__format__ (printf, 2, 3))) + __attribute__((__nonnull__ (2))); + +#endif /* XMALLOC_H */